// lib/models/in_situ_sampling_data.dart import 'dart:io'; import 'dart:convert'; // Added for jsonEncode import 'package:environment_monitoring_app/models/marine_manual_npe_report_data.dart'; /// A data model class to hold all information for the multi-step /// In-Situ Sampling form. class InSituSamplingData { // --- Step 1: Sampling & Station Info --- String? firstSamplerName; int? firstSamplerUserId; Map? secondSampler; String? samplingDate; String? samplingTime; String? samplingType; String? sampleIdCode; String? selectedStateName; String? selectedCategoryName; Map? selectedStation; String? stationLatitude; String? stationLongitude; String? currentLatitude; String? currentLongitude; double? distanceDifferenceInKm; String? distanceDifferenceRemarks; // --- Step 2: Site Info & Photos --- String? weather; String? tideLevel; String? seaCondition; // String? tarball; // <-- REMOVED THIS PROPERTY String? eventRemarks; String? labRemarks; File? leftLandViewImage; File? rightLandViewImage; File? waterFillingImage; File? seawaterColorImage; File? phPaperImage; File? optionalImage1; String? optionalRemark1; File? optionalImage2; String? optionalRemark2; File? optionalImage3; String? optionalRemark3; File? optionalImage4; String? optionalRemark4; // --- Step 3: Data Capture --- String? sondeId; String? dataCaptureDate; String? dataCaptureTime; double? oxygenConcentration; double? oxygenSaturation; double? ph; double? salinity; double? electricalConductivity; double? temperature; double? tds; double? turbidity; double? tss; double? batteryVoltage; // --- Post-Submission Status --- String? submissionStatus; String? submissionMessage; String? reportId; // --- START: NPE Report Compatibility Fields --- /// Fields to hold data that can be transferred to an NPE Report. /// This makes the model compatible for auto-generating NPE reports in the future. // Corresponds to the checkboxes in the NPE form Map npeFieldObservations = { 'Oil slick on the water surface/ Oil spill': false, 'Discoloration of the sea water': false, 'Formation of foam on the surface': false, 'Coral bleaching or dead corals': false, 'Observation of tar balls': false, 'Excessive debris': false, 'Red tides or algae blooms': false, 'Silt plume': false, 'Foul smell': false, 'Others': false, }; // Corresponds to the "Others" text field in NPE observations String? npeOthersObservationRemark; // Corresponds to the "Possible Source" field in the NPE form String? npePossibleSource; // Holds the images to be attached to the NPE report File? npeImage1; File? npeImage2; File? npeImage3; File? npeImage4; // --- END: NPE Report Compatibility Fields --- InSituSamplingData({ this.samplingDate, this.samplingTime, }); /// Creates a pre-populated NPE Report object from the current In-Situ data. MarineManualNpeReportData toNpeReportData() { final npeData = MarineManualNpeReportData(); // Transfer Reporter & Event Info npeData.firstSamplerName = firstSamplerName; npeData.firstSamplerUserId = firstSamplerUserId; npeData.eventDate = samplingDate; npeData.eventTime = samplingTime; // Transfer Location Info npeData.selectedStation = selectedStation; npeData.latitude = currentLatitude; npeData.longitude = currentLongitude; // Transfer In-Situ Measurements relevant to NPE npeData.oxygenSaturation = oxygenSaturation; npeData.electricalConductivity = electricalConductivity; npeData.oxygenConcentration = oxygenConcentration; npeData.turbidity = turbidity; npeData.ph = ph; npeData.temperature = temperature; // Pre-populate possible source with event remarks as a starting point for the user npeData.possibleSource = eventRemarks; // Pre-populate some common observations based on data if ((turbidity ?? 0) > 50) { // Example threshold, adjust as needed npeData.fieldObservations['Silt plume'] = true; } if ((oxygenConcentration ?? 999) < 4) { // Example threshold for low oxygen npeData.fieldObservations['Foul smell'] = true; } // Transfer up to 4 available images final availableImages = [ leftLandViewImage, rightLandViewImage, waterFillingImage, seawaterColorImage, phPaperImage, optionalImage1, optionalImage2, optionalImage3, optionalImage4, ].where((img) => img != null).cast().toList(); if (availableImages.isNotEmpty) npeData.image1 = availableImages[0]; if (availableImages.length > 1) npeData.image2 = availableImages[1]; if (availableImages.length > 2) npeData.image3 = availableImages[2]; if (availableImages.length > 3) npeData.image4 = availableImages[3]; return npeData; } /// Creates an InSituSamplingData object from a JSON map. factory InSituSamplingData.fromJson(Map json) { double? doubleFromJson(dynamic value) { if (value is num) return value.toDouble(); if (value is String) return double.tryParse(value); return null; } int? intFromJson(dynamic value) { if (value is int) return value; if (value is String) return int.tryParse(value); return null; } File? fileFromPath(dynamic path) { return (path is String && path.isNotEmpty) ? File(path) : null; } final data = InSituSamplingData(); // Standard In-Situ Fields data.firstSamplerName = json['first_sampler_name']; data.firstSamplerUserId = intFromJson(json['first_sampler_user_id']); data.secondSampler = json['secondSampler'] ?? json['second_sampler']; data.samplingDate = json['sampling_date'] ?? json['man_date']; data.samplingTime = json['sampling_time'] ?? json['man_time']; data.samplingType = json['sampling_type']; // ... (all other existing fields) data.sampleIdCode = json['sample_id_code']; data.selectedStateName = json['selected_state_name']; data.selectedCategoryName = json['selected_category_name']; data.selectedStation = json['selectedStation']; data.stationLatitude = json['station_latitude']; data.stationLongitude = json['station_longitude']; data.currentLatitude = json['current_latitude']?.toString(); data.currentLongitude = json['current_longitude']?.toString(); data.distanceDifferenceInKm = doubleFromJson(json['distance_difference_in_km']); data.distanceDifferenceRemarks = json['distance_difference_remarks']; data.weather = json['weather']; data.tideLevel = json['tide_level']; data.seaCondition = json['sea_condition']; // data.tarball = json['tarball']; // <-- REMOVED DESERIALIZATION data.eventRemarks = json['event_remarks']; data.labRemarks = json['lab_remarks']; data.optionalRemark1 = json['man_optional_photo_01_remarks']; data.optionalRemark2 = json['man_optional_photo_02_remarks']; data.optionalRemark3 = json['man_optional_photo_03_remarks']; data.optionalRemark4 = json['man_optional_photo_04_remarks']; data.sondeId = json['sonde_id']; data.dataCaptureDate = json['data_capture_date']; data.dataCaptureTime = json['data_capture_time']; data.oxygenConcentration = doubleFromJson(json['oxygen_concentration']); data.oxygenSaturation = doubleFromJson(json['oxygen_saturation']); data.ph = doubleFromJson(json['ph']); data.salinity = doubleFromJson(json['salinity']); data.electricalConductivity = doubleFromJson(json['electrical_conductivity']); data.temperature = doubleFromJson(json['temperature']); data.tds = doubleFromJson(json['tds']); data.turbidity = doubleFromJson(json['turbidity']); data.tss = doubleFromJson(json['tss']); data.batteryVoltage = doubleFromJson(json['battery_voltage']); data.submissionStatus = json['submission_status']; data.submissionMessage = json['submission_message']; data.reportId = json['report_id']?.toString(); // Image paths (handled by LocalStorageService) data.leftLandViewImage = fileFromPath(json['man_left_side_land_view']); data.rightLandViewImage = fileFromPath(json['man_right_side_land_view']); // ... (all other existing images) data.waterFillingImage = fileFromPath(json['man_filling_water_into_sample_bottle']); data.seawaterColorImage = fileFromPath(json['man_seawater_in_clear_glass_bottle']); data.phPaperImage = fileFromPath(json['man_examine_preservative_ph_paper']); data.optionalImage1 = fileFromPath(json['man_optional_photo_01']); data.optionalImage2 = fileFromPath(json['man_optional_photo_02']); data.optionalImage3 = fileFromPath(json['man_optional_photo_03']); data.optionalImage4 = fileFromPath(json['man_optional_photo_04']); // --- Deserialization for NPE Fields --- if (json['npe_field_observations'] is Map) { data.npeFieldObservations = Map.from(json['npe_field_observations']); } data.npeOthersObservationRemark = json['npe_others_observation_remark']; data.npePossibleSource = json['npe_possible_source']; // NPE image paths data.npeImage1 = fileFromPath(json['npe_image_1']); data.npeImage2 = fileFromPath(json['npe_image_2']); data.npeImage3 = fileFromPath(json['npe_image_3']); data.npeImage4 = fileFromPath(json['npe_image_4']); return data; } /// Creates a Map object with all submission data for local logging. Map toMap() { return { 'first_sampler_name': firstSamplerName, 'first_sampler_user_id': firstSamplerUserId, 'secondSampler': secondSampler, 'sampling_date': samplingDate, 'sampling_time': samplingTime, 'sampling_type': samplingType, 'sample_id_code': sampleIdCode, 'selected_state_name': selectedStateName, 'selected_category_name': selectedCategoryName, 'selectedStation': selectedStation, 'station_latitude': stationLatitude, 'station_longitude': stationLongitude, 'current_latitude': currentLatitude, 'current_longitude': currentLongitude, 'distance_difference_in_km': distanceDifferenceInKm, 'distance_difference_remarks': distanceDifferenceRemarks, 'weather': weather, 'tide_level': tideLevel, 'sea_condition': seaCondition, // 'tarball': tarball, // <-- REMOVED 'event_remarks': eventRemarks, 'lab_remarks': labRemarks, 'man_optional_photo_01_remarks': optionalRemark1, 'man_optional_photo_02_remarks': optionalRemark2, 'man_optional_photo_03_remarks': optionalRemark3, 'man_optional_photo_04_remarks': optionalRemark4, 'sonde_id': sondeId, 'data_capture_date': dataCaptureDate, 'data_capture_time': dataCaptureTime, 'oxygen_concentration': oxygenConcentration, 'oxygen_saturation': oxygenSaturation, 'ph': ph, 'salinity': salinity, 'electrical_conductivity': electricalConductivity, 'temperature': temperature, 'tds': tds, 'turbidity': turbidity, 'tss': tss, 'battery_voltage': batteryVoltage, 'submission_status': submissionStatus, 'submission_message': submissionMessage, 'report_id': reportId, 'npe_field_observations': npeFieldObservations, 'npe_others_observation_remark': npeOthersObservationRemark, 'npe_possible_source': npePossibleSource, // Image paths will be added/updated by LocalStorageService during saving/updating }; } /// Creates a single JSON object with all submission data, mimicking 'db.json' String toDbJson() { // This is a direct conversion of the model's properties to a map, // with keys matching the expected 'db.json' file format. final data = { 'battery_cap': batteryVoltage == -999.0 ? null : batteryVoltage, 'device_name': sondeId, 'sampling_type': samplingType, 'report_id': reportId, 'sampler_2ndname': secondSampler?['first_name'], 'sample_state': selectedStateName, 'station_id': selectedStation?['man_station_code'], 'tech_id': firstSamplerUserId, 'tech_phonenum': null, // This field was not in the model 'tech_name': firstSamplerName, 'latitude': stationLatitude, 'longitude': stationLongitude, 'record_dt': '$samplingDate $samplingTime', 'do_mgl': oxygenConcentration == -999.0 ? null : oxygenConcentration, 'do_sat': oxygenSaturation == -999.0 ? null : oxygenSaturation, 'ph': ph == -999.0 ? null : ph, 'salinity': salinity == -999.0 ? null : salinity, 'tss': tss == -999.0 ? null : tss, 'temperature': temperature == -999.0 ? null : temperature, 'turbidity': turbidity == -999.0 ? null : turbidity, 'tds': tds == -999.0 ? null : tds, 'electric_conductivity': electricalConductivity == -999.0 ? null : electricalConductivity, 'sample_id': sampleIdCode, 'tarball': "", // <-- MODIFIED: Hardcoded to "N/A" 'weather': weather, 'tide_lvl': tideLevel, 'sea_cond': seaCondition, 'remarks_event': eventRemarks, 'remarks_lab': labRemarks, }; // Remove null values before encoding data.removeWhere((key, value) => value == null); return jsonEncode(data); } /// Creates a JSON object for basic form info, mimicking 'marine_insitu_basic_form.json'. String toBasicFormJson() { final data = { 'tech_name': firstSamplerName, 'sampler_2ndname': secondSampler?['first_name'], // Assuming 'first_name' key 'sample_date': samplingDate, 'sample_time': samplingTime, 'sampling_type': samplingType, 'sample_state': selectedStateName, 'station_id': selectedStation?['man_station_code'], // Marine-specific key 'station_latitude': stationLatitude, 'station_longitude': stationLongitude, 'latitude': currentLatitude, 'longitude': currentLongitude, 'sample_id': sampleIdCode, }; // Remove null values before encoding data.removeWhere((key, value) => value == null); return jsonEncode(data); } /// Creates a JSON object for sensor readings, mimicking 'marine_sampling_reading.json'. String toReadingJson() { final data = { 'do_mgl': oxygenConcentration == -999.0 ? null : oxygenConcentration, 'do_sat': oxygenSaturation == -999.0 ? null : oxygenSaturation, 'ph': ph == -999.0 ? null : ph, 'salinity': salinity == -999.0 ? null : salinity, 'tds': tds == -999.0 ? null : tds, 'tss': tss == -999.0 ? null : tss, 'temperature': temperature == -999.0 ? null : temperature, 'turbidity': turbidity == -999.0 ? null : turbidity, 'electric_conductivity': electricalConductivity == -999.0 ? null : electricalConductivity, 'date_sampling_reading': dataCaptureDate, 'time_sampling_reading': dataCaptureTime, }; // Remove null values before encoding data.removeWhere((key, value) => value == null); return jsonEncode(data); } /// Creates a JSON object for manual info, mimicking 'marine_manual_info.json'. String toManualInfoJson() { final data = { 'tarball': "", // <-- MODIFIED: Hardcoded to "N/A" 'weather': weather, 'tide_lvl': tideLevel, 'sea_cond': seaCondition, 'remarks_event': eventRemarks, 'remarks_lab': labRemarks, }; // Remove null values before encoding data.removeWhere((key, value) => value == null); return jsonEncode(data); } Map toApiFormData() { final Map map = {}; void add(String key, dynamic value) { if (value != null) { String stringValue; if (value is double) { if (value == -999.0) { stringValue = '-999'; } else { stringValue = value.toStringAsFixed(5); } } else { stringValue = value.toString(); } if (stringValue.isNotEmpty) { map[key] = stringValue; } } } add('station_id', selectedStation?['station_id']); add('man_date', samplingDate); add('man_time', samplingTime); add('first_sampler_user_id', firstSamplerUserId); add('man_second_sampler_id', secondSampler?['user_id']); add('man_type', samplingType); add('man_sample_id_code', sampleIdCode); add('man_current_latitude', currentLatitude); add('man_current_longitude', currentLongitude); add('man_distance_difference', distanceDifferenceInKm); add('man_distance_difference_remarks', distanceDifferenceRemarks); add('man_weather', weather); add('man_tide_level', tideLevel); add('man_sea_condition', seaCondition); add('man_event_remark', eventRemarks); add('man_lab_remark', labRemarks); add('man_optional_photo_01_remarks', optionalRemark1); add('man_optional_photo_02_remarks', optionalRemark2); add('man_optional_photo_03_remarks', optionalRemark3); add('man_optional_photo_04_remarks', optionalRemark4); add('man_sondeID', sondeId); add('data_capture_date', dataCaptureDate); add('data_capture_time', dataCaptureTime); add('man_oxygen_conc', oxygenConcentration); add('man_oxygen_sat', oxygenSaturation); add('man_ph', ph); add('man_salinity', salinity); add('man_conductivity', electricalConductivity); add('man_temperature', temperature); add('man_tds', tds); add('man_turbidity', turbidity); add('man_tss', tss); add('man_battery_volt', batteryVoltage); add('first_sampler_name', firstSamplerName); add('man_station_code', selectedStation?['man_station_code']); add('man_station_name', selectedStation?['man_station_name']); return map; } Map toApiImageFiles() { return { 'man_left_side_land_view': leftLandViewImage, 'man_right_side_land_view': rightLandViewImage, 'man_filling_water_into_sample_bottle': waterFillingImage, 'man_seawater_in_clear_glass_bottle': seawaterColorImage, 'man_examine_preservative_ph_paper': phPaperImage, 'man_optional_photo_01': optionalImage1, 'man_optional_photo_02': optionalImage2, 'man_optional_photo_03': optionalImage3, 'man_optional_photo_04': optionalImage4, }; } }