Add optional subtitle to recipes, display under title, omit from TOC
This commit is contained in:
+26
-11
@@ -291,6 +291,13 @@ def init_db():
|
|||||||
if not cursor.fetchone():
|
if not cursor.fetchone():
|
||||||
cursor.execute("ALTER TABLE recipes ADD COLUMN printed_at DATETIME NULL DEFAULT NULL AFTER image_url")
|
cursor.execute("ALTER TABLE recipes ADD COLUMN printed_at DATETIME NULL DEFAULT NULL AFTER image_url")
|
||||||
|
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'recipes' AND COLUMN_NAME = 'subtitle'
|
||||||
|
""", (MARIADB_NAME,))
|
||||||
|
if not cursor.fetchone():
|
||||||
|
cursor.execute("ALTER TABLE recipes ADD COLUMN subtitle VARCHAR(255) DEFAULT '' AFTER title")
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'recipes' AND COLUMN_NAME = 'has_portion_table'
|
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'recipes' AND COLUMN_NAME = 'has_portion_table'
|
||||||
@@ -375,6 +382,7 @@ def init_db():
|
|||||||
CREATE TABLE IF NOT EXISTS recipes (
|
CREATE TABLE IF NOT EXISTS recipes (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
|
subtitle TEXT DEFAULT '',
|
||||||
category TEXT NOT NULL,
|
category TEXT NOT NULL,
|
||||||
chapter_number INTEGER NOT NULL,
|
chapter_number INTEGER NOT NULL,
|
||||||
effort TEXT NOT NULL DEFAULT 'mittel',
|
effort TEXT NOT NULL DEFAULT 'mittel',
|
||||||
@@ -395,6 +403,8 @@ def init_db():
|
|||||||
# Migration check for SQLite
|
# Migration check for SQLite
|
||||||
cursor.execute("PRAGMA table_info(recipes)")
|
cursor.execute("PRAGMA table_info(recipes)")
|
||||||
existing_cols = [c[1] for c in cursor.fetchall()]
|
existing_cols = [c[1] for c in cursor.fetchall()]
|
||||||
|
if "subtitle" not in existing_cols:
|
||||||
|
cursor.execute("ALTER TABLE recipes ADD COLUMN subtitle TEXT DEFAULT ''")
|
||||||
if "source" not in existing_cols:
|
if "source" not in existing_cols:
|
||||||
cursor.execute("ALTER TABLE recipes ADD COLUMN source TEXT DEFAULT ''")
|
cursor.execute("ALTER TABLE recipes ADD COLUMN source TEXT DEFAULT ''")
|
||||||
if "printed_at" not in existing_cols:
|
if "printed_at" not in existing_cols:
|
||||||
@@ -713,9 +723,11 @@ def add_recipe(
|
|||||||
source: str = "",
|
source: str = "",
|
||||||
image_url: str = "",
|
image_url: str = "",
|
||||||
printed_at: Optional[str] = None,
|
printed_at: Optional[str] = None,
|
||||||
has_portion_table: int = 0
|
has_portion_table: int = 0,
|
||||||
|
subtitle: str = ""
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
title = title.strip()
|
title = title.strip()
|
||||||
|
subtitle = (subtitle or "").strip()
|
||||||
category = category.strip()
|
category = category.strip()
|
||||||
effort = effort.strip().lower()
|
effort = effort.strip().lower()
|
||||||
source = source.strip()
|
source = source.strip()
|
||||||
@@ -732,10 +744,10 @@ def add_recipe(
|
|||||||
|
|
||||||
res = execute_query(
|
res = execute_query(
|
||||||
"""
|
"""
|
||||||
INSERT INTO recipes (title, category, chapter_number, effort, prep_time, cook_time, portions, has_portion_table, ingredients, instructions, notes, source, image_url, printed_at)
|
INSERT INTO recipes (title, subtitle, category, chapter_number, effort, prep_time, cook_time, portions, has_portion_table, ingredients, instructions, notes, source, image_url, printed_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(title, category, chapter_number, effort, prep_time, cook_time, portions, int(bool(has_portion_table)), ingredients, instructions, notes, source, image_url, printed_at),
|
(title, subtitle, category, chapter_number, effort, prep_time, cook_time, portions, int(bool(has_portion_table)), ingredients, instructions, notes, source, image_url, printed_at),
|
||||||
fetch="none",
|
fetch="none",
|
||||||
commit=True
|
commit=True
|
||||||
)
|
)
|
||||||
@@ -758,7 +770,8 @@ def update_recipe(
|
|||||||
source: str = "",
|
source: str = "",
|
||||||
image_url: Optional[str] = None,
|
image_url: Optional[str] = None,
|
||||||
printed_at: Optional[str] = None,
|
printed_at: Optional[str] = None,
|
||||||
has_portion_table: Optional[int] = None
|
has_portion_table: Optional[int] = None,
|
||||||
|
subtitle: Optional[str] = None
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
title = title.strip()
|
title = title.strip()
|
||||||
category = category.strip()
|
category = category.strip()
|
||||||
@@ -772,6 +785,8 @@ def update_recipe(
|
|||||||
if not existing:
|
if not existing:
|
||||||
raise KeyError(f"Rezept #{recipe_id} nicht gefunden.")
|
raise KeyError(f"Rezept #{recipe_id} nicht gefunden.")
|
||||||
|
|
||||||
|
use_subtitle = (subtitle if subtitle is not None else existing.get("subtitle", "")).strip()
|
||||||
|
|
||||||
instructions = clean_instructions_text(instructions)
|
instructions = clean_instructions_text(instructions)
|
||||||
|
|
||||||
use_portion_table = int(bool(has_portion_table)) if has_portion_table is not None else existing.get("has_portion_table", 0)
|
use_portion_table = int(bool(has_portion_table)) if has_portion_table is not None else existing.get("has_portion_table", 0)
|
||||||
@@ -779,17 +794,17 @@ def update_recipe(
|
|||||||
if image_url is not None:
|
if image_url is not None:
|
||||||
query = """
|
query = """
|
||||||
UPDATE recipes
|
UPDATE recipes
|
||||||
SET title = ?, category = ?, chapter_number = ?, effort = ?, prep_time = ?, cook_time = ?, portions = ?, has_portion_table = ?, ingredients = ?, instructions = ?, notes = ?, source = ?, image_url = ?, printed_at = ?
|
SET title = ?, subtitle = ?, category = ?, chapter_number = ?, effort = ?, prep_time = ?, cook_time = ?, portions = ?, has_portion_table = ?, ingredients = ?, instructions = ?, notes = ?, source = ?, image_url = ?, printed_at = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
"""
|
"""
|
||||||
params = (title, category, chapter_number, effort, prep_time, cook_time, portions, use_portion_table, ingredients, instructions, notes, source, image_url, printed_at, recipe_id)
|
params = (title, use_subtitle, category, chapter_number, effort, prep_time, cook_time, portions, use_portion_table, ingredients, instructions, notes, source, image_url, printed_at, recipe_id)
|
||||||
else:
|
else:
|
||||||
query = """
|
query = """
|
||||||
UPDATE recipes
|
UPDATE recipes
|
||||||
SET title = ?, category = ?, chapter_number = ?, effort = ?, prep_time = ?, cook_time = ?, portions = ?, has_portion_table = ?, ingredients = ?, instructions = ?, notes = ?, source = ?, printed_at = ?
|
SET title = ?, subtitle = ?, category = ?, chapter_number = ?, effort = ?, prep_time = ?, cook_time = ?, portions = ?, has_portion_table = ?, ingredients = ?, instructions = ?, notes = ?, source = ?, printed_at = ?
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
"""
|
"""
|
||||||
params = (title, category, chapter_number, effort, prep_time, cook_time, portions, use_portion_table, ingredients, instructions, notes, source, printed_at, recipe_id)
|
params = (title, use_subtitle, category, chapter_number, effort, prep_time, cook_time, portions, use_portion_table, ingredients, instructions, notes, source, printed_at, recipe_id)
|
||||||
|
|
||||||
res = execute_query(query, params, fetch="none", commit=True)
|
res = execute_query(query, params, fetch="none", commit=True)
|
||||||
return get_recipe(recipe_id)
|
return get_recipe(recipe_id)
|
||||||
@@ -826,9 +841,9 @@ def get_all_recipes(
|
|||||||
params.append(category.strip())
|
params.append(category.strip())
|
||||||
|
|
||||||
if search and search.strip():
|
if search and search.strip():
|
||||||
query += " AND (title LIKE ? OR ingredients LIKE ? OR instructions LIKE ?)"
|
query += " AND (title LIKE ? OR subtitle LIKE ? OR ingredients LIKE ? OR instructions LIKE ?)"
|
||||||
s = f"%{search.strip()}%"
|
s = f"%{search.strip()}%"
|
||||||
params.extend([s, s, s])
|
params.extend([s, s, s, s])
|
||||||
|
|
||||||
if printed_status == "unprinted":
|
if printed_status == "unprinted":
|
||||||
query += " AND printed_at IS NULL"
|
query += " AND printed_at IS NULL"
|
||||||
|
|||||||
@@ -580,6 +580,7 @@ def api_get_recipe(recipe_id: int):
|
|||||||
@app.post("/api/recipes")
|
@app.post("/api/recipes")
|
||||||
async def api_create_recipe(
|
async def api_create_recipe(
|
||||||
title: str = Form(...),
|
title: str = Form(...),
|
||||||
|
subtitle: str = Form(""),
|
||||||
chapter_number: int = Form(...),
|
chapter_number: int = Form(...),
|
||||||
effort: str = Form("mittel"),
|
effort: str = Form("mittel"),
|
||||||
prep_time: str = Form(""),
|
prep_time: str = Form(""),
|
||||||
@@ -604,6 +605,7 @@ async def api_create_recipe(
|
|||||||
try:
|
try:
|
||||||
recipe = add_recipe(
|
recipe = add_recipe(
|
||||||
title=title,
|
title=title,
|
||||||
|
subtitle=subtitle,
|
||||||
category=category_title,
|
category=category_title,
|
||||||
chapter_number=chapter_number,
|
chapter_number=chapter_number,
|
||||||
effort=effort,
|
effort=effort,
|
||||||
@@ -636,6 +638,7 @@ async def api_create_recipe(
|
|||||||
async def api_update_recipe(
|
async def api_update_recipe(
|
||||||
recipe_id: int,
|
recipe_id: int,
|
||||||
title: str = Form(...),
|
title: str = Form(...),
|
||||||
|
subtitle: Optional[str] = Form(None),
|
||||||
chapter_number: int = Form(...),
|
chapter_number: int = Form(...),
|
||||||
effort: str = Form("mittel"),
|
effort: str = Form("mittel"),
|
||||||
prep_time: str = Form(""),
|
prep_time: str = Form(""),
|
||||||
@@ -661,6 +664,7 @@ async def api_update_recipe(
|
|||||||
recipe = update_recipe(
|
recipe = update_recipe(
|
||||||
recipe_id=recipe_id,
|
recipe_id=recipe_id,
|
||||||
title=title,
|
title=title,
|
||||||
|
subtitle=subtitle,
|
||||||
category=category_title,
|
category=category_title,
|
||||||
chapter_number=chapter_number,
|
chapter_number=chapter_number,
|
||||||
effort=effort,
|
effort=effort,
|
||||||
|
|||||||
@@ -542,6 +542,14 @@
|
|||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recipe-subtitle {
|
||||||
|
font-family: var(--font-hand);
|
||||||
|
font-size: 1.55rem;
|
||||||
|
color: #4a453f;
|
||||||
|
margin: -4px 0 12px 0;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
.recipe-meta-row {
|
.recipe-meta-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -945,6 +953,13 @@
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recipe-subtitle {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin-top: -4px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
.recipe-meta-row {
|
.recipe-meta-row {
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
@@ -290,6 +290,14 @@
|
|||||||
color: var(--tannengruen) !important;
|
color: var(--tannengruen) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recipe-subtitle {
|
||||||
|
font-family: var(--font-hand) !important;
|
||||||
|
font-size: 1.05rem !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
margin: -0.5mm 0 1.5mm 0 !important;
|
||||||
|
color: #4a453f !important;
|
||||||
|
}
|
||||||
|
|
||||||
.recipe-meta-row {
|
.recipe-meta-row {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-wrap: wrap !important;
|
flex-wrap: wrap !important;
|
||||||
@@ -685,6 +693,9 @@
|
|||||||
<!-- Header (Ohne Kapitelangabe & ohne Quelle im Titel) -->
|
<!-- Header (Ohne Kapitelangabe & ohne Quelle im Titel) -->
|
||||||
<header class="recipe-header">
|
<header class="recipe-header">
|
||||||
<h3 class="recipe-title">{{ recipe.title }}</h3>
|
<h3 class="recipe-title">{{ recipe.title }}</h3>
|
||||||
|
{% if recipe.subtitle and recipe.subtitle.strip() %}
|
||||||
|
<div class="recipe-subtitle">{{ recipe.subtitle.strip() }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="recipe-meta-row">
|
<div class="recipe-meta-row">
|
||||||
{% if recipe.effort %}
|
{% if recipe.effort %}
|
||||||
@@ -1174,6 +1185,12 @@
|
|||||||
<input type="text" id="m-title" name="title" class="cb-input" placeholder="z. B. Klassische Waffeln" required autocomplete="off">
|
<input type="text" id="m-title" name="title" class="cb-input" placeholder="z. B. Klassische Waffeln" required autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Untertitel -->
|
||||||
|
<div class="cb-form-group">
|
||||||
|
<label for="m-subtitle" class="cb-label">Untertitel (optional)</label>
|
||||||
|
<input type="text" id="m-subtitle" name="subtitle" class="cb-input" placeholder="z. B. mit knusprigen Croutons" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Kapitel -->
|
<!-- Kapitel -->
|
||||||
<div class="cb-form-group">
|
<div class="cb-form-group">
|
||||||
<label for="m-chapter_number" class="cb-label">Kategorie / Kapitel *</label>
|
<label for="m-chapter_number" class="cb-label">Kategorie / Kapitel *</label>
|
||||||
|
|||||||
@@ -63,6 +63,9 @@
|
|||||||
<!-- Header (Ohne Kapitelangabe & ohne Quelle im Titel) -->
|
<!-- Header (Ohne Kapitelangabe & ohne Quelle im Titel) -->
|
||||||
<header class="recipe-header">
|
<header class="recipe-header">
|
||||||
<h2 class="recipe-title">{{ recipe.title }}</h2>
|
<h2 class="recipe-title">{{ recipe.title }}</h2>
|
||||||
|
{% if recipe.subtitle and recipe.subtitle.strip() %}
|
||||||
|
<div class="recipe-subtitle">{{ recipe.subtitle.strip() }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="recipe-meta-row">
|
<div class="recipe-meta-row">
|
||||||
{% if recipe.effort %}
|
{% if recipe.effort %}
|
||||||
|
|||||||
@@ -65,6 +65,14 @@
|
|||||||
placeholder="z. B. Klassische Spaghetti Carbonara" required autocomplete="off">
|
placeholder="z. B. Klassische Spaghetti Carbonara" required autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Untertitel -->
|
||||||
|
<div class="cb-form-group">
|
||||||
|
<label for="subtitle" class="cb-label">Untertitel (optional)</label>
|
||||||
|
<input type="text" id="subtitle" name="subtitle" class="cb-input"
|
||||||
|
value="{{ recipe.subtitle if recipe and recipe.subtitle else '' }}"
|
||||||
|
placeholder="z. B. mit knusprigen Croutons" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Kategorie / Kapitel -->
|
<!-- Kategorie / Kapitel -->
|
||||||
<div class="cb-form-group">
|
<div class="cb-form-group">
|
||||||
<label for="chapter_number" class="cb-label">Kategorie / Kapitel *</label>
|
<label for="chapter_number" class="cb-label">Kategorie / Kapitel *</label>
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from app.database import get_connection, MARIADB_NAME
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
|
conn = get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = %s AND TABLE_NAME = 'recipes' AND COLUMN_NAME = 'subtitle'
|
||||||
|
""", (MARIADB_NAME,))
|
||||||
|
|
||||||
|
if not cur.fetchone():
|
||||||
|
cur.execute("ALTER TABLE recipes ADD COLUMN subtitle VARCHAR(255) DEFAULT '' AFTER title")
|
||||||
|
print("Column 'subtitle' added to MariaDB 'recipes' table.")
|
||||||
|
else:
|
||||||
|
print("Column 'subtitle' already exists in MariaDB 'recipes' table.")
|
||||||
|
|
||||||
|
cur.execute("DESCRIBE recipes")
|
||||||
|
for col in cur.fetchall():
|
||||||
|
print(f" {col['Field']}: {col['Type']}")
|
||||||
|
|
||||||
|
conn.close()
|
||||||
@@ -41,6 +41,24 @@ def test_routes():
|
|||||||
assert len(matches) == 0, f"Found {len(matches)} double numbers in /cookbook: {matches[:3]}"
|
assert len(matches) == 0, f"Found {len(matches)} double numbers in /cookbook: {matches[:3]}"
|
||||||
print("✓ Zero double numbers in rendered /cookbook steps confirmed!")
|
print("✓ Zero double numbers in rendered /cookbook steps confirmed!")
|
||||||
|
|
||||||
|
# Verify subtitle behavior:
|
||||||
|
# 1. Kressesuppe is in TOC, without subtitle
|
||||||
|
toc_titles = re.findall(r'<span class="toc-rec-title">([^<]+)</span>', res_cb.text)
|
||||||
|
assert "Kressesuppe" in toc_titles, "Kressesuppe not found in TOC titles!"
|
||||||
|
assert "Kressesuppe mit Croutons" not in toc_titles, "Old combined title still in TOC!"
|
||||||
|
print("✓ TOC Title check: Only 'Kressesuppe' appears in TOC, not the subtitle.")
|
||||||
|
|
||||||
|
# 2. Recipe page renders title and subtitle
|
||||||
|
assert '<h3 class="recipe-title">Kressesuppe</h3>' in res_cb.text
|
||||||
|
assert '<div class="recipe-subtitle">mit Croutons</div>' in res_cb.text
|
||||||
|
print("✓ Cookbook recipe page check: Title is Kressesuppe, Subtitle is mit Croutons.")
|
||||||
|
|
||||||
|
# 3. Recipe detail page renders title and subtitle
|
||||||
|
res_detail = client.get("/cookbook/recipe/51")
|
||||||
|
assert '<h2 class="recipe-title">Kressesuppe</h2>' in res_detail.text
|
||||||
|
assert '<div class="recipe-subtitle">mit Croutons</div>' in res_detail.text
|
||||||
|
print("✓ Recipe detail page check: Title and subtitle present.")
|
||||||
|
|
||||||
print("\nAll route tests passed successfully!")
|
print("\nAll route tests passed successfully!")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Apply titles and optional subtitles to recipes in MariaDB.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from app.database import get_connection
|
||||||
|
|
||||||
|
sys.stdout.reconfigure(encoding='utf-8')
|
||||||
|
|
||||||
|
# Mapping of ID -> (new_title, new_subtitle)
|
||||||
|
SUBTITLE_UPDATES = {
|
||||||
|
# Specifically requested by user:
|
||||||
|
51: ("Kressesuppe", "mit Croutons"),
|
||||||
|
|
||||||
|
# Parenthetical subtitles:
|
||||||
|
9: ("Gebratene Kasknödel", "Käseknödel aus der Pfanne"),
|
||||||
|
24: ("Galaktoboureko", "Griechischer Grießauflauf"),
|
||||||
|
30: ("Heidelbeer-Tiramisu", "fruchtig-frisch ohne Ei"),
|
||||||
|
29: ("Zitronen-Tiramisu", "mit Lemon Curd (ohne rohes Ei)"),
|
||||||
|
37: ("Donauwelle", "Super Easy vom Blech"),
|
||||||
|
39: ("Fudgy Kidneybohnen-Brownies", "ohne Mehl"),
|
||||||
|
82: ("Blumenkohl-Curry mit Reis", "Cauliflower Curry"),
|
||||||
|
87: ("Pla Pad Krapao", "Thai-Basilikum-Fischfilet mit Reis"),
|
||||||
|
92: ("Schmackefatz", "Süßer Nudel-Quark-Auflauf"),
|
||||||
|
95: ("Saftiger Karottenkuchen", "Rüblikuchen"),
|
||||||
|
107: ("Jian Baos", "Gebratene Bao-Taschen"),
|
||||||
|
108: ("Okonomiyaki", "Japanischer Kohlpfannkuchen"),
|
||||||
|
109: ("Klassische Dim Sum", "Gyoza / Dumplings"),
|
||||||
|
112: ("Soft Cinnamon Rolls", "Amerikanische Zimtschnecken"),
|
||||||
|
113: ("Zimtschneckenkuchen", "ohne Hefe"),
|
||||||
|
|
||||||
|
# Long composite titles with "mit ..." / "auf ..." / "aus ...":
|
||||||
|
3: ("Champignons wie vom Weihnachtsmarkt", "mit Knoblauchsauce"),
|
||||||
|
7: ("Spaghetti in Zitronenbutter", "und Lachs mit Macadamiakruste"),
|
||||||
|
8: ("Orzo-Halloumi-Salat", "mit Granatapfel & Honig-Senf-Dressing"),
|
||||||
|
11: ("Cremige Zucchini-Bohnenpfanne", "mit Erdnussbutter"),
|
||||||
|
12: ("Blätterteigpäckchen", "mit grünem Spargel & Pecorino"),
|
||||||
|
14: ("Traditionelle italienische Focaccia", "mit Rosmarin"),
|
||||||
|
15: ("Fluffige Bao Buns", "mit marinierten Champignons & Edamame"),
|
||||||
|
16: ("Schnelle Ramen", "mit Miso-Dashibrühe & Räuchertofu"),
|
||||||
|
17: ("Traditionelle Ramen", "mit Shoyu-Tare & knusprigem Hack"),
|
||||||
|
18: ("One-Pot Mie-Nudeln", "mit Kokos, Curry & Erdnussbutter"),
|
||||||
|
19: ("Gemüse nach Gonbao-Art", "mit gerösteten Erdnüssen"),
|
||||||
|
20: ("Knuspriger Orangentofu", "mit Duftreis"),
|
||||||
|
21: ("Lachs in Thai-Curry-Soße", "mit Zucchini & goldenem Reis"),
|
||||||
|
22: ("American Pancakes", "mit Beeren & Ahornsirup"),
|
||||||
|
25: ("Crema Catalana", "mit feiner Zuckerkruste"),
|
||||||
|
26: ("Cremiges veganes Schokomousse", "aus Seidentofu"),
|
||||||
|
27: ("Mango-Limetten-Triffle", "mit weißer Schokolade"),
|
||||||
|
28: ("Lebkuchen-Mousse", "mit Glühweinkirschen"),
|
||||||
|
32: ("Zimt-Biskuitrolle", "mit karamellisierten Pflaumen & Krokant"),
|
||||||
|
34: ("Apfel-Salzkaramell-Tarte", "mit Erdnüssen"),
|
||||||
|
36: ("Bounty®-Kuchen", "vom Blech mit Kokoscreme"),
|
||||||
|
38: ("Gedeckter Apfelkuchen", "mit Streuseln"),
|
||||||
|
53: ("Blätterteig Lauchstrudel", "mit Speck"),
|
||||||
|
56: ("Klassische Senfeier", "mit Schnittlauch & Kresse"),
|
||||||
|
57: ("Würzige Fischboulette", "in Kräuterpanade"),
|
||||||
|
59: ("Maultaschen", "auf warmem Wurzelgemüse-Salat"),
|
||||||
|
60: ("Allgäuer Käsenudeln", "mit Tomatensauce"),
|
||||||
|
63: ("Spaghetti Napoletana", "mit knusprigen Butterbröseln"),
|
||||||
|
65: ("Thunfischsauce", "mit Orecchiette"),
|
||||||
|
68: ("Glasierte Garnelen", "auf Umami-Kartoffelstampf"),
|
||||||
|
71: ("Korean Glazed Potatoes", "mit Chili-Öl & Gurkensalat"),
|
||||||
|
72: ("Teriyaki-Fischfilet", "auf Ingwer-Süßkartoffelstampf"),
|
||||||
|
74: ("Schummel-Paella", "mit Meeresfrüchten & Hähnchen"),
|
||||||
|
75: ("Vegane Bowl", "mit Sesam-Süßkartoffel & Avocado"),
|
||||||
|
77: ("Hausgemachtes Naan-Brot", "mit Knoblauchbutter"),
|
||||||
|
79: ("Rahm-Fladen", "mit Schmand, Speck & Frühlingszwiebeln"),
|
||||||
|
80: ("Schwäbische Dinnete", "mit Kartoffeln und Käse"),
|
||||||
|
81: ("Veggie Po-Boy Sandwich", "mit crunchy Champignons"),
|
||||||
|
83: ("Curry-Pfannkuchen", "mit würziger Bohnenfüllung"),
|
||||||
|
85: ("Indisches rotes Linsen-Dal", "mit Kokos"),
|
||||||
|
86: ("Pad Thai", "mit veganem Hähnchen"),
|
||||||
|
88: ("Shredded Tofu-Linsen-Curry", "mit Kokosmilch"),
|
||||||
|
89: ("Umami-Kräuterseitlinge", "mit Sesam & Frühlingszwiebeln"),
|
||||||
|
90: ("Apple Crumble", "mit Haferflocken-Streuseln"),
|
||||||
|
91: ("Palatschinken", "mit feiner Walnussfüllung"),
|
||||||
|
97: ("Rhabarberkuchen", "mit Quarkcreme und Streuseln"),
|
||||||
|
98: ("Zwetschgenkuchen", "mit feinen Zimtstreuseln"),
|
||||||
|
103: ("Soba „Sommerrollen“-Salat", "mit Tofu und Erdnussdressing"),
|
||||||
|
111: ("Zimt-Apfel-Blondies", "mit Mandelkruste")
|
||||||
|
}
|
||||||
|
|
||||||
|
def main():
|
||||||
|
conn = get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for rid, (title, subtitle) in SUBTITLE_UPDATES.items():
|
||||||
|
cur.execute("UPDATE recipes SET title = %s, subtitle = %s WHERE id = %s", (title, subtitle, rid))
|
||||||
|
count += 1
|
||||||
|
print(f"Updated #{rid}: '{title}' | Untertitel: '{subtitle}'")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print(f"\nSuccessfully updated {count} recipes with titles and subtitles.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user