Files
herbapi/tools/enrichment/seed_herbapi.py
T

127 lines
8.4 KiB
Python

#!/usr/bin/env python3
"""Seed HerbAPI with common permaculture plant families and species via GBIF + API."""
import json, urllib.request, urllib.parse, time, sys
API = "http://herbapi01.corp.sub-net.at:8080/api/v1"
TOKEN = "km2WjhgyMTHlltwgch5TZADHQ-4uIg0NxBeowD-DHGk"
GBIF = "https://api.gbif.org/v1"
def api_post(path, data):
req = urllib.request.Request(f"{API}{path}",
data=json.dumps(data).encode(),
headers={"Content-Type": "application/json", "Authorization": f"Bearer {TOKEN}"})
try:
resp = urllib.request.urlopen(req)
return json.loads(resp.read())
except urllib.error.HTTPError as e:
print(f" ERR {e.code}: {e.read().decode()[:120]}", file=sys.stderr)
return None
def gbif_de_name(name):
"""Get German common name from GBIF."""
url = f"{GBIF}/species/match?name={urllib.parse.quote(name)}"
try:
match = json.loads(urllib.request.urlopen(url).read())
if not match.get("usageKey"): return None
url2 = f"{GBIF}/species/{match['usageKey']}/vernacularNames?limit=100"
data = json.loads(urllib.request.urlopen(url2).read())
for r in data.get("results", []):
if r.get("language") == "deu":
return r["vernacularName"]
except: pass
return None
FAMILIES = [
("Fabaceae", "Hülsenfrüchtler", "Legumes"),
("Rosaceae", "Rosengewächse", "Rose family"),
("Brassicaceae", "Kreuzblütler", "Cabbage family"),
("Apiaceae", "Doldenblütler", "Carrot family"),
("Lamiaceae", "Lippenblütler", "Mint family"),
("Asteraceae", "Korbblütler", "Daisy family"),
("Solanaceae", "Nachtschattengewächse", "Nightshade family"),
("Cucurbitaceae", "Kürbisgewächse", "Gourd family"),
("Poaceae", "Süßgräser", "Grass family"),
("Amaryllidaceae", "Amaryllisgewächse", "Amaryllis family"),
("Boraginaceae", "Raublattgewächse", "Borage family"),
("Adoxaceae", "Moschuskrautgewächse", "Moschatel family"),
("Betulaceae", "Birkengewächse", "Birch family"),
("Fagaceae", "Buchengewächse", "Beech family"),
("Juglandaceae", "Walnussgewächse", "Walnut family"),
("Caprifoliaceae", "Geißblattgewächse", "Honeysuckle family"),
("Grossulariaceae", "Stachelbeergewächse", "Gooseberry family"),
("Ericaceae", "Heidekrautgewächse", "Heath family"),
("Moraceae", "Maulbeergewächse", "Mulberry family"),
("Urticaceae", "Brennnesselgewächse", "Nettle family"),
("Malvaceae", "Malvengewächse", "Mallow family"),
("Polygonaceae", "Knöterichgewächse", "Buckwheat family"),
("Chenopodiaceae", "Gänsefußgewächse", "Goosefoot family"),
("Asparagaceae", "Spargelgewächse", "Asparagus family"),
("Plantaginaceae", "Wegerichgewächse", "Plantain family"),
]
SPECIES = [
("Sambucus nigra", "Adoxaceae", {"plant_layer": "understory", "nitrogen_fixer": False, "food_uses": "Flowers (cordial, fritters), berries (cooked — syrup, wine)", "medicinal_uses": "Cold/flu remedy, immune support, diaphoretic", "succession_stage": "secondary"}),
("Symphytum officinale", "Boraginaceae", {"plant_layer": "herbaceous", "dynamic_accumulator": True, "food_uses": "Young leaves (limited, contains pyrrolizidine alkaloids)", "medicinal_uses": "Wound healing, bone knitting (external only)", "other_uses": "Dynamic accumulator, mulch/compost activator, animal fodder"}),
("Trifolium pratense", "Fabaceae", {"plant_layer": "ground_cover", "nitrogen_fixer": True, "food_uses": "Flowers, young leaves", "medicinal_uses": "Respiratory, menopausal symptoms", "other_uses": "Green manure, nitrogen fixer, bee forage"}),
("Corylus avellana", "Betulaceae", {"plant_layer": "shrub", "food_uses": "Nuts", "other_uses": "Coppice wood, hedging, wildlife habitat", "succession_stage": "secondary"}),
("Ribes nigrum", "Grossulariaceae", {"plant_layer": "shrub", "food_uses": "Berries, leaves (tea)", "medicinal_uses": "High vitamin C, anti-inflammatory"}),
("Rubus idaeus", "Rosaceae", {"plant_layer": "shrub", "food_uses": "Berries, leaves (tea)", "medicinal_uses": "Leaf tea for pregnancy/digestion", "succession_stage": "pioneer"}),
("Urtica dioica", "Urticaceae", {"plant_layer": "herbaceous", "dynamic_accumulator": True, "food_uses": "Young leaves, seeds", "medicinal_uses": "Anti-inflammatory, prostate, allergies", "other_uses": "Compost activator, fibre, liquid fertiliser"}),
("Borago officinalis", "Boraginaceae", {"plant_layer": "herbaceous", "food_uses": "Flowers, young leaves", "other_uses": "Bee forage, companion plant", "attracts_pollinators": True}),
("Lavandula angustifolia", "Lamiaceae", {"plant_layer": "herbaceous", "food_uses": "Flowers", "medicinal_uses": "Calming, antiseptic, sleep aid", "other_uses": "Bee forage, pest repellent, fragrance", "attracts_pollinators": True}),
("Malus domestica", "Rosaceae", {"plant_layer": "canopy", "food_uses": "Fruit", "pollination_type": "Insect-pollinated"}),
("Prunus domestica", "Rosaceae", {"plant_layer": "canopy", "food_uses": "Fruit", "pollination_type": "Insect-pollinated"}),
("Juglans regia", "Juglandaceae", {"plant_layer": "canopy", "food_uses": "Nuts", "other_uses": "Timber, dye", "allelopathic": True}),
("Fragaria vesca", "Rosaceae", {"plant_layer": "ground_cover", "food_uses": "Berries, leaves (tea)", "ground_cover_quality": "Good"}),
("Allium ursinum", "Amaryllidaceae", {"plant_layer": "ground_cover", "food_uses": "Leaves, flowers, bulbs", "medicinal_uses": "Antimicrobial, blood pressure"}),
("Phacelia tanacetifolia", "Boraginaceae", {"plant_layer": "herbaceous", "other_uses": "Green manure, bee forage, cover crop", "attracts_pollinators": True}),
("Lupinus polyphyllus", "Fabaceae", {"plant_layer": "herbaceous", "nitrogen_fixer": True, "other_uses": "Nitrogen fixer, green manure, ornamental"}),
("Vicia faba", "Fabaceae", {"plant_layer": "herbaceous", "nitrogen_fixer": True, "food_uses": "Beans", "other_uses": "Nitrogen fixer, green manure"}),
("Solanum lycopersicum", "Solanaceae", {"plant_layer": "herbaceous", "food_uses": "Fruit"}),
("Cucurbita pepo", "Cucurbitaceae", {"plant_layer": "ground_cover", "food_uses": "Fruit, seeds, flowers"}),
("Beta vulgaris", "Chenopodiaceae", {"plant_layer": "herbaceous", "food_uses": "Roots, leaves"}),
("Daucus carota", "Apiaceae", {"plant_layer": "herbaceous", "food_uses": "Root"}),
("Calendula officinalis", "Asteraceae", {"plant_layer": "herbaceous", "food_uses": "Flowers", "medicinal_uses": "Wound healing, anti-inflammatory, skin care", "other_uses": "Companion plant, pest deterrent", "attracts_pollinators": True}),
("Melissa officinalis", "Lamiaceae", {"plant_layer": "herbaceous", "food_uses": "Leaves", "medicinal_uses": "Calming, antiviral, digestive", "attracts_pollinators": True}),
("Salvia officinalis", "Lamiaceae", {"plant_layer": "herbaceous", "food_uses": "Leaves", "medicinal_uses": "Sore throat, digestive, antimicrobial"}),
("Thymus vulgaris", "Lamiaceae", {"plant_layer": "ground_cover", "food_uses": "Leaves", "medicinal_uses": "Respiratory, antimicrobial, cough"}),
]
# Create families
print("=== Creating families ===")
family_map = {}
for sci, de, en in FAMILIES:
r = api_post("/families", {"name_scientific": sci, "name_de": de, "name_en": en})
if r:
family_map[sci] = r["id"]
print(f"{sci}")
time.sleep(0.05)
print(f"Created {len(family_map)} families\n")
# Create species
print("=== Creating species (with GBIF German names) ===")
created = 0
for sci_name, family_sci, extra in SPECIES:
fam_id = family_map.get(family_sci)
if not fam_id:
print(f"{sci_name} — family {family_sci} missing")
continue
de_name = gbif_de_name(sci_name)
data = {"name_scientific": sci_name, "name_de": de_name or "", "name_en": "", "family_id": fam_id, **extra}
r = api_post("/species", data)
if r:
created += 1
print(f"{sci_name}{de_name or '(no DE name)'}")
time.sleep(0.15)
print(f"Created {created} species\n")
# Create suppliers
print("=== Creating suppliers ===")
for name, url, country, organic, demeter, notes in [
("Reinsaat", "https://www.reinsaat.at", "AT", True, True, "Austrian biodynamic seed producer, open-pollinated varieties"),
("Magic Garden Seeds", "https://www.magicgardenseeds.com", "DE", False, False, "Specialist seed shop with rare and heritage varieties"),
]:
r = api_post("/suppliers", {"name": name, "url": url, "country": country, "is_organic": organic, "is_demeter": demeter, "notes": notes})
if r: print(f"{name}")
print("\nDone!")