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>
60 lines
2.1 KiB
Dart
60 lines
2.1 KiB
Dart
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'),
|
||
);
|
||
});
|