367 lines
14 KiB
Dart
367 lines
14 KiB
Dart
// 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<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 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']);
|
|
|
|
|
|
// --- START: 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']);
|
|
// --- END: Deserialization for NPE Fields ---
|
|
|
|
return data;
|
|
}
|
|
|
|
// ... (generateTelegramAlertMessage method remains unchanged) ...
|
|
|
|
// ... (toApiFormData method remains unchanged) ...
|
|
|
|
// ... (toApiImageFiles method remains unchanged) ...
|
|
|
|
/// 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,
|
|
// ... (all other existing fields)
|
|
'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,
|
|
|
|
// --- START: Serialization for NPE Fields ---
|
|
'npe_field_observations': npeFieldObservations,
|
|
'npe_others_observation_remark': npeOthersObservationRemark,
|
|
'npe_possible_source': npePossibleSource,
|
|
// Note: Image file paths are handled separately by the LocalStorageService
|
|
// and are not part of this JSON object directly.
|
|
// --- END: Serialization for NPE Fields ---
|
|
};
|
|
}
|
|
|
|
// --- Methods from the original file ---
|
|
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,
|
|
};
|
|
}
|
|
} |