submitObservation method

Future<Observation?> submitObservation({
  1. required ClassificationResult localResult,
  2. required WebPredictionResult? webPrediction,
  3. required double latitude,
  4. required double longitude,
  5. required String notes,
  6. 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;
  }
}