feat: tag management — scrape, edit, search existing, import to Mealie/Tandoor

- Scraper extracts tags from mindmegette.hu (<a class="tag">) and schema.org keywords
- Tag editor UI with removable chips, search/autocomplete for existing tags, custom add
- Mealie: auto-create tags via POST /api/organizers/tags, include in recipe PATCH
- Tandoor: include keywords in recipe POST (auto-created by name)
- New GET /tags endpoint returns existing tags from both services for search

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 12:42:54 +01:00
parent 458b1e362a
commit bbd0889471
6 changed files with 314 additions and 8 deletions
+19
View File
@@ -42,6 +42,7 @@ def scrape(url: str) -> dict:
"image_url": str | None,
"ingredients": [{"quantity": str, "unit": str, "food": str, "extra": str}, ...],
"instructions": [str, ...],
"tags": [str, ...],
"original_url": str,
}
@@ -139,12 +140,22 @@ def _parse_mindmegette(soup: BeautifulSoup, url: str) -> dict:
if txt:
instructions.append(txt)
# --- Tags ---
tags = []
tag_wrapper = soup.select_one("div.desktop-wrapper")
if tag_wrapper:
for a in tag_wrapper.select("a.tag"):
tag_text = a.get_text(strip=True)
if tag_text:
tags.append(tag_text)
return {
"title": title or "Ismeretlen recept",
"description": description,
"image_url": image_url,
"ingredients": ingredients,
"instructions": instructions,
"tags": tags,
"original_url": url,
}
@@ -161,6 +172,7 @@ def _parse_generic(soup: BeautifulSoup, url: str) -> dict:
ingredients = []
instructions = []
tags = []
# Try schema.org JSON-LD
for script in soup.find_all("script", type="application/ld+json"):
@@ -180,6 +192,12 @@ def _parse_generic(soup: BeautifulSoup, url: str) -> dict:
instructions.append(item)
elif isinstance(item, dict):
instructions.append(item.get("text", ""))
# Extract keywords
kw = data.get("keywords", "")
if isinstance(kw, str):
tags = [k.strip() for k in kw.split(",") if k.strip()]
elif isinstance(kw, list):
tags = [str(k).strip() for k in kw if str(k).strip()]
break
except (json.JSONDecodeError, TypeError, AttributeError):
continue
@@ -190,6 +208,7 @@ def _parse_generic(soup: BeautifulSoup, url: str) -> dict:
"image_url": image_url,
"ingredients": ingredients,
"instructions": instructions,
"tags": tags,
"original_url": url,
}