philippurbschat.de/plants/api.js

173 lines
8.5 KiB
JavaScript

// Version: 01.10.2025 11:45 (FINAL FIX)
const API_URL = 'api.php';
// --- Hilfsfunktion für Fehlerbehandlung ---
async function handleResponse(response) {
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unbekannter API-Fehler' }));
throw new Error(errorData.error || `HTTP-Fehler! Status: ${response.status}`);
}
return response.json();
}
// --- Nutzer-Funktionen ---
export async function getUserSettings() {
const response = await fetch(`${API_URL}?action=get_user_settings`);
return handleResponse(response);
}
export async function updateUserSettings(settings) {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ request_type: 'update_user_settings', settings: settings }),
});
return handleResponse(response);
}
// --- Lexikon-Funktionen ---
export async function getLexiconEntries() {
const response = await fetch(`${API_URL}?action=get_lexicon_entries`);
return handleResponse(response);
}
export async function getLexiconEntryDetails(latinName) {
const response = await fetch(`${API_URL}?action=get_lexicon_entry_details&latin_name=${encodeURIComponent(latinName)}`);
return handleResponse(response);
}
// --- Pflanzen-Funktionen ---
export async function getPlants() {
const response = await fetch(`${API_URL}?action=get_plants`);
return handleResponse(response);
}
export async function savePlant(formData) {
const response = await fetch(API_URL, { method: 'POST', body: formData });
return handleResponse(response);
}
export async function deletePlant(plantId) {
const response = await fetch(`${API_URL}?id=${plantId}`, { method: 'DELETE' });
return handleResponse(response);
}
export async function recordCareAction(plantId, action) {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ request_type: 'care_action', id: plantId, action }),
});
return handleResponse(response);
}
export async function setProfilePicture(plantId, photoPath) {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ request_type: 'set_profile_picture', plant_id: plantId, photo_path: photoPath }),
});
return handleResponse(response);
}
// --- Tagebuch-Funktionen ---
export async function getDiaryEntries(plantId) {
const response = await fetch(`${API_URL}?action=get_diary&plant_id=${plantId}`);
return handleResponse(response);
}
export async function addDiaryEntry(formData) {
formData.append('action', 'add_diary_entry');
const response = await fetch(API_URL, { method: 'POST', body: formData });
return handleResponse(response);
}
export async function deleteDiaryEntry(entryId) {
const response = await fetch(`${API_URL}?diary_entry_id=${entryId}`, { method: 'DELETE' });
return handleResponse(response);
}
// --- KI-Funktionen ---
// Schritt 1: Identifikation & Foto-Analyse
export async function performInitialAnalysis(base64Image, excludedLatinNames = []) {
let prompt = `
Du bist ein Botaniker und Foto-Analyst. Führe ZWEI Aufgaben aus und gib das Ergebnis als EINEN einzigen, sauberen JSON-String zurück. Das JSON-Objekt muss zwei Hauptschlüssel haben: "identification" und "photo_analysis".
1. **Im "identification"-Objekt:** Identifiziere die Pflanze.
- "name": Der gebräuchlichste Name, so wie er im deutschen Einzelhandel genutzt wird.
- "latin_name": Der korrekte, zweiteilige botanische Name.
- "confidence_score": Bewerte deine Sicherheit KRITISCH (0-100).
- "identification_notes": Kurze Notiz zur Identifizierung.
- REGEL: Falls dein confidence_score < 75 ist, setze alle Werte auf "Unbekannt" oder 0.
2. **Im "photo_analysis"-Objekt:** Analysiere die Fotoqualität.
- "photo_quality_score" (integer): Bewerte die Qualität KRITISCH (0-100).
- "is_high_quality" (boolean): true, wenn photo_quality_score > 70 ist.
- "rejection_reason" (string): Kurzer Grund, falls is_high_quality false ist, sonst null.
- "photo_type" (string): 'whole_plant', 'leaf_detail', 'flower_detail', oder 'other'.
- "focal_point" (object): Ein Objekt mit "x"- und "y"-Koordinaten (Prozent von 0-100) des visuellen Mittelpunkts.
- "dominant_color_hex" (string): Der dominante Farbton des Hauptmotivs als HEX-Code.
`;
if (excludedLatinNames.length > 0) {
prompt += ` Ignoriere dabei diese falschen Vorschläge: ${excludedLatinNames.join(', ')}.`;
}
const payload = {
request_type: 'ai_identification',
payload: { "contents": [{ "parts": [ { "text": prompt }, { "inline_data": { "mime_type": "image/jpeg", "data": base64Image } } ] }] }
};
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await handleResponse(response);
const resultText = result.candidates[0].content.parts[0].text;
return JSON.parse(resultText.replace(/```json|```/g, '').trim());
}
// Schritt 2: Detaillierte Anreicherung
export async function aiEnrich(latinName) {
const prompt = `
Du bist ein Botanik-Experte. Gib detaillierte Pflegedaten für "${latinName}" als sauberen JSON-String zurück.
ANWEISUNGEN FÜR DEN INHALT:
- Für "description_full": Erstelle einen ausführlichen, enzyklopädischen Text. **Verwende KEINE einleitenden Grußformeln, Willkommenssätze oder persönlichen Anreden wie "Du"**. Gliedere den Text in Abschnitte mit Titeln (z.B. 'Standort & Licht:') und spreche den Leser neutral an (z.B. "Diese Pflanze benötigt..."). Die Titel dürfen KEIN Markdown enthalten.
- Für "care_tips": Erstelle genau drei kurze, prägnante Tipps aus der Ich-Perspektive der Pflanze.
- Für "tags": Gib 3-5 passende Schlagwörter als kommaseparierten String an (z.B. "Anfängerfreundlich, Haustier-sicher, Buntblatt").
Das JSON-Objekt MUSS die folgenden Schlüssel enthalten, auch wenn der Wert null ist: "plant_family", "plant_genus", "description_full", "watering_interval_days", "fertilizing_interval_days", "care_tips", "light_requirement", "water_requirement", "humidity_requirement", "care_difficulty", "growth_habit", "mature_size", "toxicity", "edibility", "temperature_preference", "flowering_period", "substrate_recommendation", "native_to", "tags".
WICHTIGE REGELN FÜR KATEGORIEN:
- Für "substrate_recommendation": Wähle einen oder MEHRERE passende Werte aus der Liste und gib sie als kommaseparierten String zurück: 'Universal-Erde', 'Sukkulenten/Kakteen-Erde', 'Anzucht-Erde', 'Kräuter-Erde', 'Saurer Boden (Moorbeet-Erde)', 'Orchideen-Substrat', 'Hydrokultur/Pon', 'Erde-frei (Aroid-Mix)'.
- Für die folgenden Felder, wähle EXAKT EINEN der angegebenen Werte in Kleinbuchstaben:
- "light_requirement": 'wenig', 'mittel', 'viel'.
- "water_requirement": 'niedrig', 'mittel', 'hoch'.
- "humidity_requirement": 'normal', 'erhöht'.
- "care_difficulty": 'einfach', 'mittel', 'schwer'.
- "growth_habit": 'kletternd', 'hängend', 'aufrecht', 'buschig', 'rosette'.
- "mature_size": 'klein', 'mittel', 'groß'.
- "toxicity": 'ungiftig', 'leicht giftig', 'giftig'.
- "edibility": 'essbar', 'nicht essbar', 'zierfrüchte'.
- "temperature_preference": 'zimmerwarm', 'kühl', 'winterhart'.
- "flowering_period": 'frühling', 'sommer', 'herbst', 'winter', 'ganzjährig', 'selten', oder null.
FORMATIERUNGSREGELN für "care_tips":
1. Jeder Tipp MUSS mit einem passenden Emoji und einem Titel beginnen (z.B. "☀️ Licht:").
2. Zwischen den Tipps MUSS sich ein doppelter Zeilenumbruch (\\n\\n) befinden.`;
const payload = {
request_type: 'ai_enrichment',
payload: { "contents": [{ "parts": [ { "text": prompt } ] }] }
};
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await handleResponse(response);
const resultText = result.candidates[0].content.parts[0].text;
return JSON.parse(resultText.replace(/```json|```/g, '').trim());
}