environment_monitoring_app/lib/services/submission_ftp_service.dart

175 lines
7.1 KiB
Dart

// lib/services/submission_ftp_service.dart
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'dart:convert'; // Added for jsonEncode
import 'package:path/path.dart' as p; // Added for basename
import 'package:environment_monitoring_app/services/user_preferences_service.dart';
import 'package:environment_monitoring_app/services/ftp_service.dart';
import 'package:environment_monitoring_app/services/retry_service.dart';
// Import necessary services and models if needed for queueFtpTasksForSkippedAttempt
import 'package:environment_monitoring_app/services/zipping_service.dart';
//import 'package:environment_monitoring_app/services/api_service.dart'; // For DatabaseHelper
import 'package:environment_monitoring_app/services/database_helper.dart';
/// A generic, reusable service for handling the FTP submission process.
/// It respects user preferences for enabled destinations for any given module.
class SubmissionFtpService {
final UserPreferencesService _userPreferencesService = UserPreferencesService();
final FtpService _ftpService = FtpService();
final RetryService _retryService = RetryService();
// Add ZippingService and DatabaseHelper if queueFtpTasksForSkippedAttempt needs them
final ZippingService _zippingService = ZippingService();
final DatabaseHelper _dbHelper = DatabaseHelper();
/// Submits a file to all enabled FTP destinations for a given module.
///
/// This method works differently from the API service. It attempts to upload
/// to ALL enabled destinations. It returns a summary of success/failure for each.
/// If any upload fails, it is queued for individual retry.
/// The overall result is considered successful if there are no hard errors
/// during the process, even if some uploads are queued.
Future<Map<String, dynamic>> submit({
required String moduleName,
required File fileToUpload,
required String remotePath,
}) async {
final destinations = await _userPreferencesService.getEnabledFtpConfigsForModule(moduleName);
if (destinations.isEmpty) {
debugPrint("SubmissionFtpService: No enabled FTP destinations for module '$moduleName'. Skipping.");
// Return success with a specific status indicating no config
return {
'success': true, // Process succeeded because there was nothing to do
'message': 'No FTP destinations enabled for this module.',
'statuses': [{'status': 'Not Configured', 'message': 'No destinations enabled.', 'success': true}]
};
}
final List<Map<String, dynamic>> statuses = [];
bool allSucceededOrNotConfigured = true; // Track if all attempts either succeeded or weren't configured
for (final dest in destinations) {
final configName = dest['config_name'] as String? ?? 'Unknown FTP';
final int? configId = dest['ftp_config_id'] as int?; // Get the config ID
// Skip if config ID is missing (should not happen with DB data)
if (configId == null) {
debugPrint("SubmissionFtpService: Skipping destination '$configName' due to missing config ID.");
statuses.add({
'config_name': configName,
'status': 'Error',
'success': false,
'message': 'Configuration ID missing.',
});
allSucceededOrNotConfigured = false;
continue;
}
debugPrint("SubmissionFtpService: Attempting to upload to '$configName' (ID: $configId)");
final result = await _ftpService.uploadFile(
config: dest,
fileToUpload: fileToUpload,
remotePath: remotePath,
);
statuses.add({
'config_name': configName,
'ftp_config_id': configId, // Include ID in status
'success': result['success'],
'message': result['message'],
'status': result['success'] ? 'Success' : 'Failed', // Add status text
});
if (result['success'] != true) {
allSucceededOrNotConfigured = false;
// If an individual upload fails, queue it for manual retry.
debugPrint("SubmissionFtpService: Upload to '$configName' (ID: $configId) failed. Queuing for retry.");
// --- START FIX: Add ftpConfigId ---
await _retryService.addFtpToQueue(
localFilePath: fileToUpload.path,
remotePath: remotePath,
ftpConfigId: configId, // Pass the specific config ID
);
// --- END FIX ---
}
}
if (allSucceededOrNotConfigured) {
return {
'success': true,
'message': 'File successfully uploaded to all enabled FTP destinations.',
'statuses': statuses,
};
} else {
return {
'success': true, // The process itself succeeded (attempted all), even if some uploads were queued.
'message': 'One or more FTP uploads failed and have been queued for retry.',
'statuses': statuses,
};
}
}
/// Manually queues FTP tasks when the initial FTP attempt is skipped (e.g., due to session expiry).
Future<void> queueFtpTasksForSkippedAttempt({
required String moduleName,
required Map<String, dynamic> dataJson, // The data model converted to JSON (toDbJson)
required Map<String, File> imageFiles,
required String baseFileName, // Base name for zip files
}) async {
debugPrint("Manually queuing FTP tasks for skipped attempt (Module: $moduleName).");
final ftpConfigs = await _dbHelper.loadFtpConfigs() ?? [];
if (ftpConfigs.isEmpty) {
debugPrint("Cannot queue skipped FTP tasks: No FTP configurations found.");
return;
}
// 1. Create Data ZIP (in temp directory)
final dataZip = await _zippingService.createDataZip(
// Adapt jsonDataMap based on module if needed, using dataJson
jsonDataMap: {'db.json': jsonEncode(dataJson)}, // Default, adjust per module if needed
baseFileName: baseFileName,
destinationDir: null, // Save to temp dir
);
// 2. Create Image ZIP (in temp directory)
File? imageZip;
if (imageFiles.isNotEmpty) {
imageZip = await _zippingService.createImageZip(
imageFiles: imageFiles.values.toList(),
baseFileName: baseFileName,
destinationDir: null, // Save to temp dir
);
}
// 3. Queue uploads for each config
for (final config in ftpConfigs) {
final configId = config['ftp_config_id'];
if (configId != null) {
// Queue data zip upload
if (dataZip != null) {
await _retryService.addFtpToQueue(
localFilePath: dataZip.path,
remotePath: '/${p.basename(dataZip.path)}',
ftpConfigId: configId
);
debugPrint("Queued skipped data ZIP upload for FTP config ID $configId");
}
// Queue image zip upload
if (imageZip != null) {
await _retryService.addFtpToQueue(
localFilePath: imageZip.path,
remotePath: '/${p.basename(imageZip.path)}',
ftpConfigId: configId
);
debugPrint("Queued skipped image ZIP upload for FTP config ID $configId");
}
}
}
// Temporary ZIP files will be cleaned up by OS eventually, or handled by retry logic upon success/failure.
}
}