Files
gartenmanager/backend/app/seeds/initial_data.py

172 lines
8.6 KiB
Python
Raw Normal View History

"""
Seed-Daten: Globale Pflanzenfamilien, Pflanzen und Kompatibilitäten.
Idempotent kann mehrfach ausgeführt werden ohne Fehler.
"""
import asyncio
import uuid
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.session import AsyncSessionLocal
from app.models.plant import (
CompatibilityRating,
NutrientDemand,
Plant,
PlantCompatibility,
PlantFamily,
WaterDemand,
)
FAMILIES = [
{"name": "Solanaceae", "latin_name": "Solanaceae"},
{"name": "Kreuzblütler", "latin_name": "Brassicaceae"},
{"name": "Doldenblütler", "latin_name": "Apiaceae"},
{"name": "Hülsenfrüchtler", "latin_name": "Fabaceae"},
{"name": "Kürbisgewächse", "latin_name": "Cucurbitaceae"},
{"name": "Korbblütler", "latin_name": "Asteraceae"},
{"name": "Lauchgewächse", "latin_name": "Alliaceae"},
{"name": "Gänsefußgewächse", "latin_name": "Amaranthaceae"},
{"name": "Lippenblütler", "latin_name": "Lamiaceae"},
{"name": "Süßgräser", "latin_name": "Poaceae"},
]
# (name, latin_name, family_name, nutrient, water, spacing_cm, sow_start, sow_end, rest_years)
PLANTS = [
# Solanaceae
("Tomate", "Solanum lycopersicum", "Solanaceae", NutrientDemand.STARK, WaterDemand.MITTEL, 60, 3, 4, 3),
("Paprika", "Capsicum annuum", "Solanaceae", NutrientDemand.STARK, WaterDemand.MITTEL, 45, 2, 3, 3),
("Aubergine", "Solanum melongena", "Solanaceae", NutrientDemand.STARK, WaterDemand.MITTEL, 50, 2, 3, 3),
# Kreuzblütler
("Brokkoli", "Brassica oleracea var. italica", "Kreuzblütler", NutrientDemand.STARK, WaterDemand.MITTEL, 45, 3, 4, 4),
("Weißkohl", "Brassica oleracea var. capitata", "Kreuzblütler", NutrientDemand.STARK, WaterDemand.MITTEL, 50, 3, 4, 4),
("Kohlrabi", "Brassica oleracea var. gongylodes", "Kreuzblütler", NutrientDemand.MITTEL, WaterDemand.MITTEL, 22, 3, 7, 3),
("Radieschen", "Raphanus sativus", "Kreuzblütler", NutrientDemand.SCHWACH, WaterDemand.MITTEL, 5, 3, 8, 2),
# Doldenblütler
("Möhre", "Daucus carota", "Doldenblütler", NutrientDemand.MITTEL, WaterDemand.WENIG, 5, 3, 7, 3),
("Petersilie", "Petroselinum crispum", "Doldenblütler", NutrientDemand.MITTEL, WaterDemand.MITTEL, 18, 3, 5, 2),
("Sellerie", "Apium graveolens", "Doldenblütler", NutrientDemand.STARK, WaterDemand.VIEL, 28, 2, 3, 3),
# Hülsenfrüchtler
("Buschbohne", "Phaseolus vulgaris", "Hülsenfrüchtler", NutrientDemand.SCHWACH, WaterDemand.MITTEL, 12, 5, 7, 3),
("Erbse", "Pisum sativum", "Hülsenfrüchtler", NutrientDemand.SCHWACH, WaterDemand.MITTEL, 8, 3, 5, 3),
# Kürbisgewächse
("Gurke", "Cucumis sativus", "Kürbisgewächse", NutrientDemand.STARK, WaterDemand.VIEL, 60, 4, 5, 2),
("Zucchini", "Cucurbita pepo", "Kürbisgewächse", NutrientDemand.STARK, WaterDemand.VIEL, 90, 4, 5, 2),
("Kürbis", "Cucurbita maxima", "Kürbisgewächse", NutrientDemand.STARK, WaterDemand.VIEL, 180, 4, 5, 2),
# Korbblütler
("Kopfsalat", "Lactuca sativa", "Korbblütler", NutrientDemand.SCHWACH, WaterDemand.MITTEL, 22, 3, 8, 2),
("Feldsalat", "Valerianella locusta", "Korbblütler", NutrientDemand.SCHWACH, WaterDemand.WENIG, 8, 8, 9, 2),
# Lauchgewächse
("Zwiebel", "Allium cepa", "Lauchgewächse", NutrientDemand.MITTEL, WaterDemand.WENIG, 12, 3, 4, 3),
("Lauch", "Allium porrum", "Lauchgewächse", NutrientDemand.MITTEL, WaterDemand.MITTEL, 12, 2, 3, 3),
("Knoblauch", "Allium sativum", "Lauchgewächse", NutrientDemand.SCHWACH, WaterDemand.WENIG, 10, 10, 11, 4),
("Schnittlauch", "Allium schoenoprasum", "Lauchgewächse", NutrientDemand.SCHWACH, WaterDemand.WENIG, 10, 3, 4, 3),
# Gänsefußgewächse
("Mangold", "Beta vulgaris var. cicla", "Gänsefußgewächse", NutrientDemand.MITTEL, WaterDemand.MITTEL, 28, 3, 6, 3),
("Spinat", "Spinacia oleracea", "Gänsefußgewächse", NutrientDemand.MITTEL, WaterDemand.MITTEL, 12, 3, 9, 3),
("Rote Bete", "Beta vulgaris var. conditiva", "Gänsefußgewächse", NutrientDemand.MITTEL, WaterDemand.MITTEL, 10, 4, 6, 3),
# Lippenblütler
("Basilikum", "Ocimum basilicum", "Lippenblütler", NutrientDemand.SCHWACH, WaterDemand.MITTEL, 20, 4, 5, 2),
("Thymian", "Thymus vulgaris", "Lippenblütler", NutrientDemand.SCHWACH, WaterDemand.WENIG, 25, 3, 4, 2),
("Minze", "Mentha spicata", "Lippenblütler", NutrientDemand.SCHWACH, WaterDemand.VIEL, 30, 3, 4, 2),
("Oregano", "Origanum vulgare", "Lippenblütler", NutrientDemand.SCHWACH, WaterDemand.WENIG, 25, 3, 4, 2),
]
# (plant_a_name, plant_b_name, rating, reason)
COMPATIBILITIES = [
("Tomate", "Basilikum", CompatibilityRating.GUT, "Basilikum fördert das Tomatenwachstum und hält Schädlinge fern."),
("Tomate", "Möhre", CompatibilityRating.GUT, "Gute Nachbarn, gegenseitige Förderung."),
("Tomate", "Petersilie", CompatibilityRating.GUT, "Petersilie stärkt die Tomaten."),
("Tomate", "Brokkoli", CompatibilityRating.SCHLECHT, "Kohl hemmt das Tomatenwachstum."),
("Tomate", "Weißkohl", CompatibilityRating.SCHLECHT, "Kohl hemmt das Tomatenwachstum."),
("Möhre", "Zwiebel", CompatibilityRating.GUT, "Zwiebeln halten die Möhrenfliege fern."),
("Möhre", "Lauch", CompatibilityRating.GUT, "Lauch schützt die Möhre vor der Möhrenfliege."),
("Möhre", "Erbse", CompatibilityRating.GUT, "Erbsen lockern den Boden für Möhren."),
("Gurke", "Buschbohne", CompatibilityRating.GUT, "Klassische gute Nachbarschaft."),
("Weißkohl", "Sellerie", CompatibilityRating.GUT, "Sellerie hält die Kohlfliege fern."),
("Brokkoli", "Sellerie", CompatibilityRating.GUT, "Sellerie hält Kohlschädlinge fern."),
("Spinat", "Radieschen", CompatibilityRating.GUT, "Platzsparende Kombination, gegenseitig förderlich."),
("Zwiebel", "Buschbohne", CompatibilityRating.SCHLECHT, "Zwiebeln hemmen das Bohnenwachstum."),
("Zwiebel", "Erbse", CompatibilityRating.SCHLECHT, "Zwiebeln und Hülsenfrüchtler vertragen sich nicht."),
("Zwiebel", "Weißkohl", CompatibilityRating.SCHLECHT, "Konkurrenz um Nährstoffe."),
]
async def seed_initial_data(db: AsyncSession) -> None:
# 1. Plant families
family_map: dict[str, PlantFamily] = {}
for f in FAMILIES:
result = await db.execute(select(PlantFamily).where(PlantFamily.name == f["name"]))
existing = result.scalar_one_or_none()
if existing:
family_map[f["name"]] = existing
else:
obj = PlantFamily(id=uuid.uuid4(), **f)
db.add(obj)
await db.flush()
family_map[f["name"]] = obj
# 2. Global plants
plant_map: dict[str, Plant] = {}
for (name, latin, family_name, nutrient, water, spacing, sow_start, sow_end, rest) in PLANTS:
result = await db.execute(
select(Plant).where(Plant.name == name, Plant.tenant_id == None) # noqa: E711
)
existing = result.scalar_one_or_none()
if existing:
plant_map[name] = existing
else:
obj = Plant(
id=uuid.uuid4(),
tenant_id=None,
family_id=family_map[family_name].id,
name=name,
latin_name=latin,
nutrient_demand=nutrient,
water_demand=water,
spacing_cm=spacing,
sowing_start_month=sow_start,
sowing_end_month=sow_end,
rest_years=rest,
)
db.add(obj)
await db.flush()
plant_map[name] = obj
# 3. Compatibilities (both directions)
for (name_a, name_b, rating, reason) in COMPATIBILITIES:
if name_a not in plant_map or name_b not in plant_map:
continue
id_a = plant_map[name_a].id
id_b = plant_map[name_b].id
result = await db.execute(
select(PlantCompatibility).where(
PlantCompatibility.plant_id_a == id_a,
PlantCompatibility.plant_id_b == id_b,
)
)
if not result.scalar_one_or_none():
db.add(PlantCompatibility(id=uuid.uuid4(), plant_id_a=id_a, plant_id_b=id_b, rating=rating, reason=reason))
# Reverse direction
result = await db.execute(
select(PlantCompatibility).where(
PlantCompatibility.plant_id_a == id_b,
PlantCompatibility.plant_id_b == id_a,
)
)
if not result.scalar_one_or_none():
db.add(PlantCompatibility(id=uuid.uuid4(), plant_id_a=id_b, plant_id_b=id_a, rating=rating, reason=reason))
await db.commit()
print("Seed-Daten erfolgreich eingespielt.")
async def main() -> None:
async with AsyncSessionLocal() as db:
await seed_initial_data(db)
if __name__ == "__main__":
asyncio.run(main())