"""Seed: Power-Profil 'Herb' + PAROL6-Demo-Projekt. Legt (idempotent) an: 1. Ein Power-User-Profil 'Herb' (rolle='power', kein Tier). PIN wird beim ersten Anlegen generiert + ausgegeben; bei spaeteren Laeufen unveraendert. 2. Das Demo-Projekt 'PAROL6 — 6-Achsen-Roboterarm': - Macher: Herb (Power) - Drucker: P1S #1 - Status: abgeschlossen + freigegeben (Power -> direkt im Katalog, als Vorlage waehlbar) - Cover: aus der 3MF extrahiertes Render (thumbnail_middle.png) - Dateien: GitHub-Repo (Link) + Bauanleitung-PDF (BLOB) - Die 3MF selbst wird NICHT gespeichert (nur verlinkt via GitHub-STLs). Aufruf: python scripts/seed_demo_parol6.py <3mf-pfad> Das 3MF wird nur zum Cover-Extrahieren gelesen, nicht als BLOB abgelegt. """ import sys import zipfile import secrets from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from database import get_db from services.auth import pin_hashen GITHUB_URL = "https://github.com/PCrnjak/PAROL6-Desktop-robot-arm" PROJEKT_TITEL = "PAROL6 — 6-Achsen-Roboterarm" AG_ID = "lessing-3d-ag" BESCHREIBUNG = """Ein **6-Achsen-Roboterarm zum Selberbauen** — Open Source, komplett 3D-druckbar. Das **PAROL6** ist ein Roboterarm fuer den Schreibtisch mit 6 Bewegungsachsen — so wie die grossen Industrie-Roboter, nur klein. Entwickelt von Petar Crnjak, Lizenz GPL-3.0 (frei nutzbar). ## Was dazugehoert - **3D-Druck:** alle Teile sind druckbar (hier vorbereitet fuer den P1S). Die STLs liegen im GitHub-Repo. - **Elektronik:** Schrittmotoren + eine eigene Steuerplatine (muss bestellt werden). - **Steuerung:** ueber eine Python-API, optional mit ROS2/MoveIt simulierbar. ## Schwierigkeit: anspruchsvoll Das ist ein **Langzeit-Projekt**. Drucken ist nur der erste Schritt — danach kommen Zusammenbau, Elektronik und Programmierung. Nichts fuer einen Nachmittag, aber ein tolles Ziel, wenn du tiefer einsteigen willst. ## Quellen - **Projektseite (GitHub):** alle Dateien, Stueckliste, Doku — siehe Link unten. - **Bauanleitung:** das angehaengte PDF (81 Seiten).""" COVER_KANDIDATEN = [ "Auxiliaries/.thumbnails/thumbnail_middle.png", "Metadata/top_1.png", "Metadata/plate_1.png", ] def hole_cover(dreimf_pfad: str): """Extrahiert das beste Render aus der 3MF. Returns (blob, mime) oder (None, None).""" try: with zipfile.ZipFile(dreimf_pfad) as z: namen = set(z.namelist()) for kandidat in COVER_KANDIDATEN: if kandidat in namen: return (z.read(kandidat), "image/png") except Exception as e: print(f" [warn] Cover-Extraktion fehlgeschlagen: {e}") return (None, None) def hole_pdf(pdf_pfad: str): """Liest das PDF als BLOB. Returns (blob, dateiname) oder (None, None).""" try: with open(pdf_pfad, "rb") as f: return (f.read(), Path(pdf_pfad).name) except Exception as e: print(f" [warn] PDF-Lesen fehlgeschlagen: {e}") return (None, None) def main(): if len(sys.argv) < 3: print("Aufruf: python scripts/seed_demo_parol6.py <3mf-pfad> ") sys.exit(1) dreimf_pfad, pdf_pfad = sys.argv[1], sys.argv[2] conn = get_db() # --- 1. Power-Profil 'Herb' --- herb = conn.execute( "SELECT id, vorname, rolle FROM profil WHERE vorname = ? AND rolle = 'power'", ("Herb",) ).fetchone() if herb: herb_id = herb["id"] print(f"[seed] Power-Profil 'Herb' existiert bereits (id={herb_id}).") else: pin = f"{secrets.randbelow(10000):04d}" cur = conn.execute( "INSERT INTO profil (vorname, tier, pin_hash, ag_id, aktiv, rolle) " "VALUES (?, 'power', ?, ?, 1, 'power')", ("Herb", pin_hashen(pin), AG_ID) ) conn.commit() herb_id = cur.lastrowid print(f"[seed] Power-Profil 'Herb' angelegt (id={herb_id}). PIN: {pin} <-- AUFSCHREIBEN!") # --- 2. PAROL6-Projekt --- schon = conn.execute( "SELECT id FROM projekt WHERE profil_id = ? AND titel = ?", (herb_id, PROJEKT_TITEL) ).fetchone() if schon: print(f"[seed] Projekt '{PROJEKT_TITEL}' existiert bereits (id={schon['id']}) — nichts zu tun.") conn.close() return # Drucker P1S #1 drucker = conn.execute("SELECT id FROM drucker WHERE name = 'P1S #1'").fetchone() drucker_id = drucker["id"] if drucker else None cover_blob, cover_mime = hole_cover(dreimf_pfad) cur = conn.execute( """INSERT INTO projekt (profil_id, titel, beschreibung_md, status_lebenszyklus, abgeschlossen_am, selbsteinschaetzung, selbsteinschaetzung_planung, selbsteinschaetzung_ausfuehrung, was_gelernt, freigabe_status, freigegeben_am, sichtbar_in_ag, drucker_id_used, cover_blob, cover_mime) VALUES (?, ?, ?, 'abgeschlossen', CURRENT_TIMESTAMP, 'zufrieden', 'gut', 'gut', 'Ein anspruchsvolles Open-Source-Projekt als Inspiration fuer Fortgeschrittene.', 'freigegeben', CURRENT_TIMESTAMP, 1, ?, ?, ?)""", (herb_id, PROJEKT_TITEL, BESCHREIBUNG, drucker_id, cover_blob, cover_mime) ) projekt_id = cur.lastrowid # GitHub-Link als "Datei" conn.execute( "INSERT INTO projekt_datei (projekt_id, dateiname, extern_url, art, hochgeladen_von) " "VALUES (?, ?, ?, 'link', ?)", (projekt_id, "PAROL6-Projekt auf GitHub (STLs + Doku)", GITHUB_URL, herb_id) ) # PDF als BLOB pdf_blob, pdf_name = hole_pdf(pdf_pfad) if pdf_blob: conn.execute( "INSERT INTO projekt_datei (projekt_id, dateiname, mime_type, groesse_bytes, inhalt, art, hochgeladen_von) " "VALUES (?, ?, 'application/pdf', ?, ?, 'sonstiges', ?)", (projekt_id, "Bauanleitung (81 Seiten, PDF)", len(pdf_blob), pdf_blob, herb_id) ) conn.commit() conn.close() print(f"[seed] Projekt '{PROJEKT_TITEL}' angelegt (id={projekt_id}).") print(f" Cover: {'ja' if cover_blob else 'NEIN'} | PDF: {'ja ('+str(len(pdf_blob)//1024)+' KB)' if pdf_blob else 'NEIN'} | GitHub-Link: ja | Drucker: P1S #1") if __name__ == "__main__": main()