#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Importiert das Bollwerk-Vorratsinventar in den Server (einmaliger PUT). Verwendet App-Default-Kategorien (IDs 1-7) und Default-Location Keller (ID 1). Aufruf: python import-inventar.py python import-inventar.py --url http://localhost:8080 """ import json import getpass import time import uuid import argparse import urllib.request import urllib.error BASE_URL = "https://bollwerk.online" ADMIN_USER = "admin" def api(method, path, body=None, token=None): url = BASE_URL + path # ensure_ascii=True kodiert alle Umlaute als \uXXXX – kein Encoding-Problem data = json.dumps(body, ensure_ascii=True).encode("ascii") if body else None headers = {"Content-Type": "application/json"} if token: headers["Authorization"] = f"Bearer {token}" req = urllib.request.Request(url, data=data, headers=headers, method=method) with urllib.request.urlopen(req) as resp: return json.loads(resp.read().decode("utf-8")) def new_item(name, cat_id, qty, unit, expiry=None, kcal=None, notes=""): now = int(time.time() * 1000) return { "id": str(uuid.uuid4()), "name": name, "categoryId": cat_id, "quantity": float(qty), "unit": unit, "unitPrice": 0.0, "kcalPerUnit": kcal, "expiryDate": expiry, "locationId": 1, "notes": notes or "", "lastUpdated": now, } # App-Default-Kategorien (IDs aus SeedDatabaseUseCase) CATEGORIES = [ {"id": 1, "name": "Lebensmittel"}, {"id": 2, "name": "Wasser"}, {"id": 3, "name": "Medikamente"}, {"id": 4, "name": "Ausrüstung"}, {"id": 5, "name": "Hygiene"}, {"id": 6, "name": "Energie & Licht"}, {"id": 7, "name": "Dokumente"}, ] # App-Default-Lagerort LOCATIONS = [{"id": 1, "name": "Keller"}] ITEMS = [ # --- Lebensmittel: Getreide & Hülsenfrüchte (cat 1) --------------------- new_item("Dinkelkorn", 1, 16, "Beutel", None, None, "Jahrgang 2019, vakuumiert"), new_item("Weizen (Naturland)", 1, 36, "kg", None, None, "Jahrgang 2023"), new_item("Dinkel", 1, 40, "kg", None, None, "Jahrgang 2023"), new_item("Bio-Weizen", 1, 18, "kg", None, None, "Jahrgang 2019"), new_item("Bio-Weizen (Neu)", 1, 200, "kg", None, None, "Jahrgang 05/2026, Kunststoffbehälter, O2-Absorber, Trockenmittel, lose luftdicht"), new_item("Grüne Erbsen", 1, 10, "kg", None, None, "Jahrgang 2019"), new_item("Gelbe Schälerbsen", 1, 10, "kg", None, None, "Jahrgang 2019"), new_item("Berglinsen (1 kg)", 1, 1, "Pkg.", "2021-12-31", None, "MHD abgelaufen"), new_item("Berglinsen (500 g)", 1, 4, "Pkg.", "2023-12-31", None, "MHD abgelaufen"), new_item("Weiße Bohnen", 1, 2, "Pkg.", "2022-12-31", None, "MHD abgelaufen"), new_item("Jodsalz", 1, 25, "kg", None, None, ""), # --- Lebensmittel: Konserven (cat 1) ------------------------------------ new_item("Kondensmilch gezuckert (Dovgan)", 1, 88, "Dosen", None, None, "MHD prüfen"), new_item("Corned Beef (Exeter)", 1, 48, "Dosen", None, None, "MHD prüfen"), new_item("Geschälte Tomaten", 1, 5, "Dosen", None, None, "2x 2,5 kg, MHD abgelaufen"), new_item("Tomatenmark (200 g)", 1, 24, "Dosen", None, None, "MHD abgelaufen"), new_item("Sardinen", 1, 5, "Dosen", None, None, "MHD abgelaufen"), new_item("Heringsfilet in Tomatensoße", 1, 11, "Dosen", None, None, "MHD abgelaufen"), new_item("Thunfisch groß (900 g)", 1, 3, "Dosen", None, None, "MHD abgelaufen"), new_item("Thunfisch klein", 1, 9, "Dosen", None, None, "MHD abgelaufen"), new_item("Brathering Filet", 1, 2, "Dosen", None, None, "MHD abgelaufen"), new_item("Dosenbrot", 1, 6, "Dosen", None, None, "MHD abgelaufen"), # --- Lebensmittel: Öle & Fette (cat 1) ---------------------------------- new_item("Olivenöl (750 ml)", 1, 3, "Flaschen", "2022-12-31", None, "MHD abgelaufen"), new_item("Avocadoöl (250 ml)", 1, 3, "Flaschen", "2026-12-31", None, "MHD 2026, prüfen"), new_item("Sonnenblumenöl (1 L)", 1, 3, "Flaschen", "2021-12-31", None, "MHD abgelaufen"), new_item("Sonnenblumenöl (500 ml)", 1, 1, "Flaschen", "2023-12-31", None, "MHD abgelaufen"), # --- Lebensmittel: Getränke (cat 1) ------------------------------------- new_item("Instant-Kaffee", 1, 12, "Stk.", "2024-12-31", None, "MHD abgelaufen"), new_item("Trek'N'Eat Peronin", 1, 8, "Stk.", "2024-12-31", None, "MHD abgelaufen"), # --- Lebensmittel: Notfallrationen (cat 1) ------------------------------ new_item("Conserva-Set", 1, 30, "Manntage", None, 2300, "à 2300 kcal/Tag, conserva.de"), new_item("NRG-5 Katadyn (Neubestand)", 1, 96, "Tagesrationen", "2036-04-30", 2300, "4x24, Produktion April 2026"), new_item("NRG-5 Katadyn (Altbestand)", 1, 19, "Tagesrationen", "2029-01-31", 2300, "Produktion 2019, 5 Rationen verbraucht"), # --- Medikamente (cat 3) ------------------------------------------------ new_item("Vitamine", 3, 960, "Stk.", None, None, "3x240 + 2x120 Tabletten"), # --- Ausrüstung (cat 4) ------------------------------------------------- new_item("PMR-Funkgerät (Midland G9 Pro)", 4, 2, "Stk.", None, None, ""), # --- Hygiene (cat 5) ---------------------------------------------------- new_item("Shampoo Mini", 5, 50, "Stk.", None, None, "à 20 ml"), new_item("Ethanol", 5, 30, "Liter", None, None, ""), new_item("Isopropanol", 5, 4, "Liter", None, None, ""), new_item("Methanol", 5, 1, "Liter", None, None, ""), new_item("Desinfektionsmittel", 5, 5, "Liter", None, None, ""), # --- Energie & Licht (cat 6) -------------------------------------------- new_item("Petroleum", 6, 50, "Liter", None, None, ""), ] def main(): global BASE_URL parser = argparse.ArgumentParser() parser.add_argument("--url", default=BASE_URL) parser.add_argument("--user", default=ADMIN_USER) args = parser.parse_args() BASE_URL = args.url.rstrip("/") password = getpass.getpass(f"Admin-Passwort für '{args.user}' @ {BASE_URL}: ") print("Logge ein...", end=" ", flush=True) auth = api("POST", "/api/auth/login", {"username": args.user, "password": password}) token = auth["accessToken"] print("OK") print("Prüfe vorhandenes Inventar...", end=" ", flush=True) existing = api("GET", "/api/inventory", token=token) count = len(existing.get("items", [])) print(f"{count} Artikel vorhanden.") if count > 0: confirm = input(f" WARNUNG: {count} Artikel vorhanden. Überschreiben? (ja/nein): ") if confirm.strip().lower() != "ja": print("Abgebrochen.") return inventory = { "version": 1, "categories": CATEGORIES, "locations": LOCATIONS, "items": ITEMS, "settings": [], "deletedItemIds": [], } print(f"Lade {len(ITEMS)} Artikel hoch...", end=" ", flush=True) result = api("PUT", "/api/inventory", inventory, token=token) print("OK") print() print("Fertig!") print(f" Artikel : {len(result.get('items', []))}") print(f" Kategorien : {len(result.get('categories', []))}") print(f" Lagerorte : {len(result.get('locations', []))}") if __name__ == "__main__": main()