#!/usr/bin/env python3 """ Seed a curated starter set of beneficial & pest insects relevant to a temperate permaculture farm. Conservative, widely-documented entomological facts. Idempotent: skips insects whose slug already exists. Run: HERBAPI_TOKEN=... python3 seed_insects.py """ import json import os import re import urllib.error import urllib.request BASE = os.environ.get("HERBAPI_BASE", "http://herbapi01.corp.sub-net.at:8080/api/v1") TOKEN = os.environ.get("HERBAPI_TOKEN", "") def api(path, method="GET", data=None): r = urllib.request.Request( BASE + path, data=json.dumps(data).encode() if data is not None else None, method=method, ) r.add_header("Authorization", "Bearer " + TOKEN) r.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(r) as resp: return resp.status, json.loads(resp.read().decode()) except urllib.error.HTTPError as e: return e.code, e.read().decode()[:200] INSECTS = [ # ── Beneficials ── {"name_scientific": "Apis mellifera", "name_de": "Westliche Honigbiene", "name_en": "Western honey bee", "order_scientific": "Hymenoptera", "family_scientific": "Apidae", "role": "beneficial", "beneficial_roles": ["pollinator"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [3, 4, 5, 6, 7, 8, 9, 10], "description": "Eusocial honey bee; the principal managed pollinator of orchards and field crops.", "native_range": "Europe, Africa, Western Asia"}, {"name_scientific": "Bombus terrestris", "name_de": "Dunkle Erdhummel", "name_en": "Buff-tailed bumblebee", "order_scientific": "Hymenoptera", "family_scientific": "Apidae", "role": "beneficial", "beneficial_roles": ["pollinator"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [3, 4, 5, 6, 7, 8, 9], "description": "Buzz-pollinator, effective on tomatoes and early-season fruit in cool weather.", "native_range": "Europe"}, {"name_scientific": "Osmia bicornis", "name_de": "Rote Mauerbiene", "name_en": "Red mason bee", "order_scientific": "Hymenoptera", "family_scientific": "Megachilidae", "role": "beneficial", "beneficial_roles": ["pollinator"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [4, 5, 6], "description": "Solitary spring bee; excellent orchard pollinator, readily uses bee hotels.", "native_range": "Europe"}, {"name_scientific": "Coccinella septempunctata", "name_de": "Siebenpunkt-Marienkäfer", "name_en": "Seven-spot ladybird", "order_scientific": "Coleoptera", "family_scientific": "Coccinellidae", "role": "beneficial", "beneficial_roles": ["predator"], "preys_on": ["myzus-persicae"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [4, 5, 6, 7, 8, 9], "description": "Adults and larvae are voracious aphid predators.", "native_range": "Palearctic"}, {"name_scientific": "Chrysoperla carnea", "name_de": "Gemeine Florfliege", "name_en": "Green lacewing", "order_scientific": "Neuroptera", "family_scientific": "Chrysopidae", "role": "beneficial", "beneficial_roles": ["predator"], "preys_on": ["myzus-persicae"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [4, 5, 6, 7, 8, 9], "description": "Larvae ('aphid lions') prey on aphids, mites and small caterpillars.", "native_range": "Europe"}, {"name_scientific": "Episyrphus balteatus", "name_de": "Hainschwebfliege", "name_en": "Marmalade hoverfly", "order_scientific": "Diptera", "family_scientific": "Syrphidae", "role": "beneficial", "beneficial_roles": ["pollinator", "predator"], "preys_on": ["myzus-persicae"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [3, 4, 5, 6, 7, 8, 9, 10], "description": "Adults pollinate; larvae are major aphid predators.", "native_range": "Palearctic"}, {"name_scientific": "Aphidius colemani", "name_de": "Blattlaus-Schlupfwespe", "name_en": "Aphid parasitoid wasp", "order_scientific": "Hymenoptera", "family_scientific": "Braconidae", "role": "beneficial", "beneficial_roles": ["parasitoid"], "preys_on": ["myzus-persicae"], "life_cycle": "holometabolous", "description": "Tiny wasp that parasitises aphids; widely used in biocontrol.", "native_range": "Cosmopolitan (biocontrol)"}, {"name_scientific": "Carabus auratus", "name_de": "Goldlaufkäfer", "name_en": "Golden ground beetle", "order_scientific": "Coleoptera", "family_scientific": "Carabidae", "role": "beneficial", "beneficial_roles": ["predator"], "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [4, 5, 6, 7, 8], "description": "Ground-dwelling predator of slugs, caterpillars and other soil pests.", "native_range": "Western Europe"}, # ── Pests ── {"name_scientific": "Myzus persicae", "name_de": "Grüne Pfirsichblattlaus", "name_en": "Green peach aphid", "order_scientific": "Hemiptera", "family_scientific": "Aphididae", "role": "pest", "pest_severity": "major", "life_cycle": "hemimetabolous", "active_months": [4, 5, 6, 7, 8, 9, 10], "description": "Highly polyphagous aphid; sap-sucker and major virus vector.", "native_range": "Cosmopolitan"}, {"name_scientific": "Leptinotarsa decemlineata", "name_de": "Kartoffelkäfer", "name_en": "Colorado potato beetle", "order_scientific": "Coleoptera", "family_scientific": "Chrysomelidae", "role": "pest", "pest_severity": "major", "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [5, 6, 7, 8], "description": "Defoliates potato and other solanaceous crops; rapid resistance to insecticides.", "native_range": "North America (invasive in Europe)"}, {"name_scientific": "Pieris brassicae", "name_de": "Großer Kohlweißling", "name_en": "Large white", "order_scientific": "Lepidoptera", "family_scientific": "Pieridae", "role": "pest", "pest_severity": "moderate", "life_cycle": "holometabolous", "overwinters_as": "pupa", "active_months": [4, 5, 6, 7, 8, 9], "description": "Caterpillars defoliate brassicas (cabbage, kale, broccoli).", "native_range": "Europe, North Africa, Asia"}, {"name_scientific": "Plutella xylostella", "name_de": "Kohlmotte", "name_en": "Diamondback moth", "order_scientific": "Lepidoptera", "family_scientific": "Plutellidae", "role": "pest", "pest_severity": "moderate", "life_cycle": "holometabolous", "active_months": [5, 6, 7, 8, 9], "description": "Small moth whose larvae mine and skeletonise brassica leaves; resistance-prone.", "native_range": "Cosmopolitan"}, {"name_scientific": "Delia radicum", "name_de": "Kleine Kohlfliege", "name_en": "Cabbage root fly", "order_scientific": "Diptera", "family_scientific": "Anthomyiidae", "role": "pest", "pest_severity": "moderate", "life_cycle": "holometabolous", "overwinters_as": "pupa", "active_months": [4, 5, 6, 7, 8], "description": "Larvae feed on brassica roots, wilting and killing transplants.", "native_range": "Holarctic"}, {"name_scientific": "Phyllotreta nemorum", "name_de": "Gelbstreifiger Kohlerdfloh", "name_en": "Turnip flea beetle", "order_scientific": "Coleoptera", "family_scientific": "Chrysomelidae", "role": "pest", "pest_severity": "minor", "life_cycle": "holometabolous", "overwinters_as": "adult", "active_months": [4, 5, 6, 7], "description": "Adults shot-hole brassica seedlings; worst in warm dry spells.", "native_range": "Europe, Asia"}, {"name_scientific": "Forficula auricularia", "name_de": "Gemeiner Ohrwurm", "name_en": "European earwig", "order_scientific": "Dermaptera", "family_scientific": "Forficulidae", "role": "both", "beneficial_roles": ["predator"], "pest_severity": "minor", "preys_on": ["myzus-persicae"], "life_cycle": "hemimetabolous", "overwinters_as": "adult", "active_months": [5, 6, 7, 8, 9, 10], "description": "Eats aphids and codling-moth eggs (beneficial) but can damage soft fruit and seedlings.", "native_range": "Europe"}, ] def main(): if not TOKEN: raise SystemExit("HERBAPI_TOKEN not set") existing = {i["slug"] for i in api("/insects?per_page=100")[1]["data"]} created = skipped = errors = 0 for ins in INSECTS: slug = re.sub(r"[^a-z0-9]+", "-", ins["name_scientific"].lower()).strip("-") if slug in existing: print(f"skip {slug} (exists)") skipped += 1 continue code, resp = api("/insects", "POST", ins) if code == 200: print(f"created {slug}") created += 1 else: print(f"ERROR {slug}: {code} {resp}") errors += 1 print(f"\ncreated={created} skipped={skipped} errors={errors}") if __name__ == "__main__": main()