"""Recipe import: photo/screenshot (AI vision), pasted text (AI) and links. Every path ends in the normal recipe form, pre-filled as an editable draft and posted to recipes:create. Nothing is saved before the user presses "Speichern". """ from django import forms from django.contrib import messages from django.shortcuts import redirect, render from django.urls import reverse from django.views.decorators.http import require_POST from . import ai, images from .forms import MultipleFileInput, MultipleImageField, RecipeForm class PhotoImportForm(forms.Form): photos = MultipleImageField( label="Foto oder Screenshot", widget=MultipleFileInput(attrs={"accept": "image/*"}), help_text=f"Bis zu {ai.MAX_IMAGES} Bilder vom selben Rezept, z. B. Kochbuchseite oder Screenshot.", ) def clean_photos(self): photos = self.cleaned_data["photos"] if not photos: raise forms.ValidationError("Bitte wähle ein Bild aus.") if len(photos) > ai.MAX_IMAGES: raise forms.ValidationError(f"Höchstens {ai.MAX_IMAGES} Bilder.") return photos class TextImportForm(forms.Form): text = forms.CharField( label="Rezepttext", max_length=ai.MAX_INPUT_CHARS, widget=forms.Textarea(attrs={"rows": 8, "placeholder": "Rezept hier einfügen, z. B. die Beschreibung eines Instagram-Beitrags"}), ) source_url = forms.URLField(label="Quelle (optional)", required=False, max_length=1000, assume_scheme="https") def render_draft(request, initial, notice, ai_draft=None, extra_context=None): """Show the recipe form filled with a draft; it posts to recipes:create.""" form = RecipeForm(initial=initial) return render(request, "recipes/form.html", { "form": form, "is_new": True, "draft_notice": notice, "ai_draft": ai_draft, "form_action": reverse("recipes:create"), **(extra_context or {}), }) def import_start(request, photo_form=None, text_form=None): return render(request, "recipes/import.html", { "photo_form": photo_form or PhotoImportForm(), "text_form": text_form or TextImportForm(), }) @require_POST def import_photo(request): form = PhotoImportForm(request.POST, request.FILES) if not form.is_valid(): return import_start(request, photo_form=form) try: jpegs = [images.prepare_for_ai(f) for f in form.cleaned_data["photos"]] draft = ai.extract_from_images(request.user, jpegs) except (ai.AIError, images.ImageError) as error: messages.error(request, getattr(error, "user_message", str(error))) return redirect("recipes:import") return render_draft(request, draft.to_form_initial(), "Aus dem Bild erkannt", ai_draft=draft) @require_POST def import_text(request): form = TextImportForm(request.POST) if not form.is_valid(): return import_start(request, text_form=form) try: draft = ai.structure_text(request.user, form.cleaned_data["text"]) except ai.AIError as error: messages.error(request, error.user_message) return import_start(request, text_form=form) initial = {**draft.to_form_initial(), "source_url": form.cleaned_data["source_url"]} return render_draft(request, initial, "Aus dem Text erkannt", ai_draft=draft)