environment_monitoring_app/lib/models/in_situ_sampling_data.dart

410 lines
15 KiB
Dart

// 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<String, dynamic>? secondSampler;
String? samplingDate;
String? samplingTime;
String? samplingType;
String? sampleIdCode;
String? selectedStateName;
String? selectedCategoryName;
Map<String, dynamic>? 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;
// --- 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<String, bool> 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<File>().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<String, dynamic> 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.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<String, bool>.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 single JSON object with all submission data for offline storage.
Map<String, dynamic> toDbJson() {
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,
'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,
};
}
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();
}
Map<String, String> toApiFormData() {
final Map<String, String> 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<String, File?> 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,
};
}
}