Files
HartOMat/backend/app/utils/seed_templates.py
T
Hartmut 9f54bc3ab1 feat(phase4+5): role hierarchy, tenant config, fallback material, dead code removal
Phase 4.1 — Role Hierarchy:
  - UserRole enum: add global_admin (platform operator) + tenant_admin
    (per-tenant admin); keep legacy 'admin' for backward compat
  - Role sets: ADMIN_ROLES, TENANT_ADMIN_ROLES, PM_ROLES, RLS_BYPASS_ROLES
  - New auth guards: require_global_admin(), require_tenant_admin_or_above(),
    require_pm_or_above(), is_admin(), is_privileged()
  - Legacy require_admin / require_admin_or_pm now check both old+new roles
  - Migration 049: ADD VALUE global_admin + tenant_admin with AUTOCOMMIT
    workaround; backfills admin → global_admin
  - Seed: new admin users created with global_admin role

Phase 4.3 — RLS bypass updated for global_admin in get_db + set_tenant_context

Phase 4.4 — Tenant Feature Flags:
  - Migration 050: tenant_config JSONB on tenants table
  - Tenant model: tenant_config field + get_config() accessor
  - Defaults: max_concurrent_renders=3, fallback_material, invoice_prefix etc.

Phase 5.1 — Fallback Material:
  - blender_render.py: replace PALETTE_LINEAR/PALETTE_HEX/_assign_palette_material
    with _assign_failed_material() → SCHAEFFLER_059999_FailedMaterial (magenta)
  - Unmatched parts now logged explicitly before rendering

Phase 5.2 — Remove EEVEE fallback:
  - render_blender.py: EEVEE→Cycles silent retry removed; hard failure on EEVEE error

Phase 5.3 — Remove Blender version check:
  - render_blender.py: deleted MIN_BLENDER_VERSION = (5, 0, 1) constant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 19:42:10 +01:00

185 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Seed database with 7 Schaeffler product category templates."""
import asyncio
import uuid
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy import select
STANDARD_FIELDS = {
"0": {"label": "Ebene1", "required": True},
"1": {"label": "Ebene2", "required": True},
"2": {"label": "Baureihe", "required": True},
"3": {"label": "PIM-ID (Klasse)", "required": False},
"4": {"label": "Produkt (Baureihe)", "required": False},
"5": {"label": "[Separator]", "required": False, "skip": True},
"6": {"label": "Gewähltes Produkt", "required": True},
"7": {"label": "Name CAD-Modell", "required": True},
"8": {"label": "Gewünschte Bildnummer", "required": False},
"9": {"label": "Lagertyp", "required": False},
"10": {"label": "Medias-Rendering", "required": False},
}
TEMPLATES = [
{
"name": "Tapered Roller Bearings (TRB)",
"category_key": "TRB",
"description": "Kegelrollenlager Tapered roller bearings",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Innenring / Inner ring", "required": False},
{"component_type": "Innenring / Inner ring 2", "required": False},
{"component_type": "Innenring / Inner ring 3", "required": False},
{"component_type": "Außenring / Outer ring", "required": False},
{"component_type": "Außenring / Outer ring 2", "required": False},
{"component_type": "Außenring / Outer ring 3", "required": False},
{"component_type": "Außenring / Outer ring 4", "required": False},
{"component_type": "Käfig / Cage", "required": False},
{"component_type": "Wälzkörper / Rolling Element", "required": False},
{"component_type": "Dichtungskern/Dichtungsträger", "required": False},
{"component_type": "Dichtung Außen / Dichtlippe", "required": False},
]
},
},
{
"name": "Kugellager (Ball Bearings)",
"category_key": "Kugellager",
"description": "Kugellager Ball bearings",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Innenring / Inner ring", "required": False},
{"component_type": "Außenring / Outer ring", "required": False},
{"component_type": "Wälzkörper / Rolling Element", "required": True},
{"component_type": "Käfig / Cage", "required": False},
{"component_type": "Dichtungskern/Dichtungsträger", "required": False},
{"component_type": "Axial - WS", "required": False},
{"component_type": "Axial - GS", "required": False},
]
},
},
{
"name": "Gleitlager (Plain Bearings)",
"category_key": "Gleitlager",
"description": "Gleitlager Plain / sliding bearings",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Innenring / Inner ring", "required": False},
{"component_type": "Außenring / Outer ring", "required": False},
{"component_type": "Gehause / Housing", "required": False},
{"component_type": "Sliding Layer", "required": False},
{"component_type": "Dichtungsträger / Sealing carrier", "required": False},
{"component_type": "Dichtlippe / Sealing lip", "required": False},
]
},
},
{
"name": "Spherical / Toroidal Roller Bearings (SRB/TORB)",
"category_key": "SRB_TORB",
"description": "Pendelrollenlager / Toroidalrollenlager SRB and TORB bearings",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Innenring / Inner ring", "required": False},
{"component_type": "Außenring / Outer ring", "required": False},
{"component_type": "Käfig / Cage", "required": False},
{"component_type": "Wälzkörper / Rolling element", "required": False},
{"component_type": "Bordscheibe IR / Loose Lip IR", "required": False},
{"component_type": "Dichtungsträger / Sealing carrier", "required": False},
]
},
},
{
"name": "Cylindrical Roller Bearings (CRB)",
"category_key": "CRB",
"description": "Zylinderrollenlager Cylindrical roller bearings",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Innenring", "required": False},
{"component_type": "Außenring", "required": False},
{"component_type": "Rollen", "required": False},
{"component_type": "Käfig", "required": False},
{"component_type": "Dichtung", "required": False},
{"component_type": "Halteringe", "required": False},
{"component_type": "Bordscheibe", "required": False},
]
},
},
{
"name": "Linear Guide Rails",
"category_key": "Linear_schiene",
"description": "Linearsysteme Linear guide rail systems",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Rail", "required": True},
]
},
},
{
"name": "End Plates (Anschlagplatten)",
"category_key": "Anschlagplatten",
"description": "Anschlagplatten End plates for guide rails",
"standard_fields": STANDARD_FIELDS,
"component_schema": {
"pairs": [
{"component_type": "Platte / Plate", "required": True},
{"component_type": "Schraube / Screw", "required": False},
{"component_type": "Nut BZ", "required": False},
]
},
},
]
async def seed(db_url: str, admin_email: str = "admin@schaeffler.com", admin_password: str = "Admin1234!"):
from app.models.template import Template
from app.models.user import User, UserRole
from app.utils.auth import hash_password
engine = create_async_engine(db_url, echo=False)
session_factory = async_sessionmaker(engine, expire_on_commit=False)
async with session_factory() as session:
# Seed templates
for tpl_data in TEMPLATES:
result = await session.execute(
select(Template).where(Template.category_key == tpl_data["category_key"])
)
existing = result.scalar_one_or_none()
if not existing:
tpl = Template(**tpl_data)
session.add(tpl)
print(f" + Template: {tpl_data['category_key']}")
else:
print(f" ~ Template already exists: {tpl_data['category_key']}")
# Seed admin user
result = await session.execute(select(User).where(User.email == admin_email))
if not result.scalar_one_or_none():
admin = User(
email=admin_email,
password_hash=hash_password(admin_password),
role=UserRole.global_admin,
full_name="Schaeffler Admin",
)
session.add(admin)
print(f" + Admin user: {admin_email}")
else:
print(f" ~ Admin user already exists: {admin_email}")
await session.commit()
await engine.dispose()
print("Seed complete.")
if __name__ == "__main__":
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from app.config import settings
asyncio.run(seed(settings.database_url))