submitObservation method
- required ClassificationResult localResult,
- required WebPredictionResult? webPrediction,
- required double latitude,
- required double longitude,
- required String notes,
- required AppLocalizations localizations,
Submits an observation to the server with classification results.
Handles both local-only and web+local prediction submissions.
Parameters:
- localResult: The classification result from the local model
- webPrediction: Optional web prediction result (for hybrid submissions)
- latitude: Observation location latitude
- longitude: Observation location longitude
- notes: User-provided notes about the observation
- localizations: Used for localized error messages
Returns:
- Observation: The created observation if successful
- null: If submission fails
Updates:
- _state: Updates to submitting/submitted/error states
- _submissionResult: Contains the created observation on success
- _errorMessage: Contains error details if submission fails
Implementation
Future<Observation?> submitObservation({
required ClassificationResult localResult,
required WebPredictionResult? webPrediction,
required double latitude,
required double longitude,
required String notes,
required AppLocalizations localizations,
}) async {
_state = ClassificationState.submitting;
_errorMessage = null;
notifyListeners();
try {
final String userId = await _userService.getUserId();
final Map<String, dynamic> finalPayload;
if (webPrediction == null) {
print("Submitting observation with LOCAL prediction only.");
finalPayload = {
"species_scientific_name": localResult.species.name,
"count": 1,
"location": {"lat": latitude, "lng": longitude},
"observed_at": DateTime.now().toUtc().toIso8601String(),
"notes": notes.trim().isEmpty ? null : notes.trim(),
"user_id": userId,
"image_filename": localResult.imageFile.path.split('/').last,
"model_id": modelId, // Uses the modelId defined at the top of the class
"confidence": localResult.confidence,
"data_source": "culicidaelab_mobile",
"metadata": {
"submission_type": "local_only",
"local_prediction": {
"scientific_name": localResult.species.name,
"confidence": localResult.confidence,
"inference_time_ms": localResult.inferenceTime,
}
}
};
} else {
print("Submitting observation with WEB prediction.");
finalPayload = {
"species_scientific_name": webPrediction.scientificName,
"count": 1,
"location": {"lat": latitude, "lng": longitude},
"observed_at": DateTime.now().toUtc().toIso8601String(),
"notes": notes.trim().isEmpty ? null : notes.trim(),
"user_id": userId,
"image_filename": localResult.imageFile.path.split('/').last,
"model_id": webPrediction.modelId, // Use the web model's ID
"confidence": webPrediction.confidence,
"data_source": "culicidaelab_mobile",
"metadata": {
"submission_type": "web_and_local",
"web_prediction": {
"id": webPrediction.id,
"probabilities": webPrediction.probabilities,
},
"local_prediction": {
"scientific_name": localResult.species.name,
"confidence": localResult.confidence,
"inference_time_ms": localResult.inferenceTime,
}
}
};
}
final submission = await _repository.submitObservation(finalPayload: finalPayload);
_submissionResult = submission;
_state = ClassificationState.submitted;
notifyListeners();
return submission;
} catch (e) {
print("!!! ERROR submitting observation: $e");
_state = ClassificationState.error;
_errorMessage = localizations.errorSubmissionFailed(e.toString());
notifyListeners();
return null;
}
}