172 lines
8.6 KiB
Python
172 lines
8.6 KiB
Python
|
|
"""
|
|||
|
|
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())
|