leafittome/lib/features/plants/data/plant_fit_service.dart
cschlaefke f0b420b656 V3: Stellplatz-Bewertung (Standort-Analyse per Foto + Eignungs-Sterne auf Abruf)
Letzter Roadmap-Punkt von V3. Foto-basierte Lichtverhältnis-Analyse pro
Stellplatz (Cloud Function analyzeLocation) und darauf aufbauende, rein
textbasierte Eignungs-Bewertung einer Pflanze (assessPlantFit) — beides
ausschließlich auf Abruf, nicht automatisch (Kostenkontrolle, eigener
Anthropic-Key). Neue Stellplatz-Detailseite, Verlinkung im Pflanzen-Detail,
Erkennung veralteter Bewertungen bei Umzug/Neuanalyse.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 00:17:18 +02:00

60 lines
2.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:cloud_functions/cloud_functions.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../locations/domain/plant_location.dart';
import '../domain/plant.dart';
/// Ein Fit-Ergebnis gilt als veraltet, wenn die Pflanze seither verschoben
/// oder der Stellplatz seither neu analysiert wurde — dann zeigt die UI
/// einen "neu prüfen"-Hinweis statt der (nicht mehr verlässlichen) Sterne.
bool isFitStale(Plant plant, PlantLocation location) {
if (plant.fitLocationId != location.id) return true;
final assessedAt = plant.fitAssessedAt;
final analyzedAt = location.analyzedAt;
if (assessedAt == null) return true;
if (analyzedAt != null && analyzedAt.isAfter(assessedAt)) return true;
return false;
}
/// Ergebnis der Eignungs-Bewertung (Cloud Function assessPlantFit).
class PlantFitResult {
const PlantFitResult({required this.stars, required this.reasoning});
final int stars;
final String reasoning;
}
class PlantFitService {
PlantFitService(this._functionsGetter);
// Lazy, damit Widget-Tests ohne initialisiertes Firebase auskommen —
// FirebaseFunctions.instanceFor wirft sonst schon beim Erzeugen.
final FirebaseFunctions Function() _functionsGetter;
/// Fragt Claude, wie gut [plant] an den (bereits analysierten) [location]
/// passt rein textbasiert, kein neues Foto nötig.
Future<PlantFitResult> assessFit({
required Plant plant,
required PlantLocation location,
}) async {
final callable = _functionsGetter().httpsCallable('assessPlantFit');
final response = await callable.call<Map<String, dynamic>>({
'species': plant.species,
'description': plant.description,
'careNotes': plant.careNotes,
'lightCategory': location.lightCategory ?? '',
'lightAssessment': location.lightAssessment ?? '',
});
final data = response.data;
return PlantFitResult(
stars: (data['stars'] as num?)?.toInt() ?? 0,
reasoning: data['reasoning'] as String? ?? '',
);
}
}
final plantFitServiceProvider = Provider<PlantFitService>((ref) {
return PlantFitService(
() => FirebaseFunctions.instanceFor(region: 'europe-west3'),
);
});