// lib/models/in_situ_sampling_data.dart import 'dart:io'; import 'dart:convert'; // Added for jsonEncode /// 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? 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; // REPAIRED: Added a constructor to accept initial values. InSituSamplingData({ this.samplingDate, this.samplingTime, }); // --- ADDED: Factory constructor to create a new instance from a 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; } // Helper to create a File object from a path string File? fileFromPath(dynamic path) { return (path is String && path.isNotEmpty) ? File(path) : null; } return InSituSamplingData() ..firstSamplerName = json['first_sampler_name'] ..firstSamplerUserId = intFromJson(json['first_sampler_user_id']) ..secondSampler = json['secondSampler'] ..samplingDate = json['man_date'] ..samplingTime = json['man_time'] ..samplingType = json['man_type'] ..sampleIdCode = json['man_sample_id_code'] ..selectedStateName = json['selectedStateName'] ..selectedCategoryName = json['selectedCategoryName'] ..selectedStation = json['selectedStation'] ..stationLatitude = json['stationLatitude'] ..stationLongitude = json['stationLongitude'] ..currentLatitude = json['man_current_latitude']?.toString() ..currentLongitude = json['man_current_longitude']?.toString() ..distanceDifferenceInKm = doubleFromJson(json['man_distance_difference']) ..distanceDifferenceRemarks = json['man_distance_difference_remarks'] ..weather = json['man_weather'] ..tideLevel = json['man_tide_level'] ..seaCondition = json['man_sea_condition'] ..eventRemarks = json['man_event_remark'] ..labRemarks = json['man_lab_remark'] ..sondeId = json['man_sondeID'] ..dataCaptureDate = json['data_capture_date'] ..dataCaptureTime = json['data_capture_time'] ..oxygenConcentration = doubleFromJson(json['man_oxygen_conc']) ..oxygenSaturation = doubleFromJson(json['man_oxygen_sat']) ..ph = doubleFromJson(json['man_ph']) ..salinity = doubleFromJson(json['man_salinity']) ..electricalConductivity = doubleFromJson(json['man_conductivity']) ..temperature = doubleFromJson(json['man_temperature']) ..tds = doubleFromJson(json['man_tds']) ..turbidity = doubleFromJson(json['man_turbidity']) ..tss = doubleFromJson(json['man_tss']) ..batteryVoltage = doubleFromJson(json['man_battery_volt']) ..optionalRemark1 = json['man_optional_photo_01_remarks'] ..optionalRemark2 = json['man_optional_photo_02_remarks'] ..optionalRemark3 = json['man_optional_photo_03_remarks'] ..optionalRemark4 = json['man_optional_photo_04_remarks'] ..leftLandViewImage = fileFromPath(json['man_left_side_land_view']) ..rightLandViewImage = fileFromPath(json['man_right_side_land_view']) ..waterFillingImage = fileFromPath(json['man_filling_water_into_sample_bottle']) ..seawaterColorImage = fileFromPath(json['man_seawater_in_clear_glass_bottle']) ..phPaperImage = fileFromPath(json['man_examine_preservative_ph_paper']) ..optionalImage1 = fileFromPath(json['man_optional_photo_01']) ..optionalImage2 = fileFromPath(json['man_optional_photo_02']) ..optionalImage3 = fileFromPath(json['man_optional_photo_03']) ..optionalImage4 = fileFromPath(json['man_optional_photo_04']); } /// Generates a formatted Telegram alert message for successful submissions. String generateTelegramAlertMessage({required bool isDataOnly}) { final submissionType = isDataOnly ? "(Data Only)" : "(Data & Images)"; final stationName = selectedStation?['man_station_name'] ?? 'N/A'; final stationCode = selectedStation?['man_station_code'] ?? 'N/A'; final buffer = StringBuffer() ..writeln('✅ *In-Situ Sample $submissionType Submitted:*') ..writeln() ..writeln('*Station Name & Code:* $stationName ($stationCode)') ..writeln('*Date of Submission:* $samplingDate') ..writeln('*Submitted by User:* $firstSamplerName') ..writeln('*Sonde ID:* ${sondeId ?? "N/A"}') ..writeln('*Status of Submission:* Successful'); if (distanceDifferenceInKm != null && distanceDifferenceInKm! > 0) { buffer ..writeln() ..writeln('🔔 *Alert:*') ..writeln('*Distance from station:* ${(distanceDifferenceInKm! * 1000).toStringAsFixed(0)} meters'); if (distanceDifferenceRemarks != null && distanceDifferenceRemarks!.isNotEmpty) { buffer.writeln('*Remarks for distance:* $distanceDifferenceRemarks'); } } return buffer.toString(); } /// Converts the data model into a Map for the API form data. Map toApiFormData() { final Map map = {}; // Helper to add non-null values to the map void add(String key, dynamic value) { if (value != null && value.toString().isNotEmpty) { map[key] = value.toString(); } } // --- Required fields that were missing or incorrect --- add('station_id', selectedStation?['station_id']); add('man_date', samplingDate); add('man_time', samplingTime); add('first_sampler_user_id', firstSamplerUserId); // --- Other Step 1 Data --- 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); // --- Step 2 Data --- 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); // --- Step 3 Data --- 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); // --- Human-readable fields for server-side alerts --- add('first_sampler_name', firstSamplerName); add('man_station_code', selectedStation?['man_station_code']); add('man_station_name', selectedStation?['man_station_name']); return map; } /// Converts the image properties into a Map for the multipart API request. 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, }; } /// Creates a single JSON object with all submission data, mimicking 'db.json'. Map toDbJson() { return { 'first_sampler_name': firstSamplerName, 'first_sampler_user_id': firstSamplerUserId, 'second_sampler': secondSampler, 'sampling_date': samplingDate, 'sampling_time': samplingTime, 'sampling_type': samplingType, 'sample_id_code': sampleIdCode, 'selected_state_name': selectedStateName, 'selected_category_name': selectedCategoryName, 'selected_station': 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, 'event_remarks': eventRemarks, 'lab_remarks': labRemarks, '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, }; } /// Creates a JSON object for basic form info, mimicking 'basic_form.json'. Map toBasicFormJson() { return { 'tech_name': firstSamplerName, 'sampler_2ndname': secondSampler?['user_name'], 'sample_date': samplingDate, 'sample_time': samplingTime, 'sampling_type': samplingType, 'sample_state': selectedStateName, 'station_id': selectedStation?['man_station_code'], 'station_latitude': stationLatitude, 'station_longitude': stationLongitude, 'latitude': currentLatitude, 'longitude': currentLongitude, 'sample_id': sampleIdCode, }; } /// Creates a JSON object for sensor readings, mimicking 'reading.json'. Map toReadingJson() { return { 'do_mgl': oxygenConcentration, 'do_sat': oxygenSaturation, 'ph': ph, 'salinity': salinity, 'temperature': temperature, 'turbidity': turbidity, 'tds': tds, 'electric_conductivity': electricalConductivity, 'flowrate': null, // This is not collected in marine in-situ 'date_sampling_reading': dataCaptureDate, 'time_sampling_reading': dataCaptureTime, }; } /// Creates a JSON object for manual info, mimicking 'manual_info.json'. Map toManualInfoJson() { return { 'weather': weather, 'remarks_event': eventRemarks, 'remarks_lab': labRemarks, }; } }