refactor: rebrand project to HartOMat

This commit is contained in:
2026-04-06 12:45:47 +02:00
parent fa7093307a
commit b795f0e6d6
95 changed files with 608 additions and 497 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
script_location = alembic
prepend_sys_path = .
version_path_separator = os
sqlalchemy.url = postgresql://schaeffler:schaeffler@localhost:5432/schaeffler
sqlalchemy.url = postgresql://hartomat:hartomat@localhost:5432/hartomat
[post_write_hooks]
@@ -1,4 +1,4 @@
"""Schaeffler standard materials — add schaeffler_code column and seed 35 materials
"""HartOMat standard materials — add hartomat_code column and seed 35 materials
Revision ID: 019
Revises: 018
@@ -16,23 +16,23 @@ depends_on = None
def upgrade() -> None:
op.add_column("materials", sa.Column("schaeffler_code", sa.Integer(), nullable=True))
op.add_column("materials", sa.Column("hartomat_code", sa.Integer(), nullable=True))
from app.data.schaeffler_materials import SCHAEFFLER_MATERIALS
from app.data.hartomat_materials import HARTOMAT_MATERIALS
conn = op.get_bind()
now = datetime.utcnow().isoformat()
for mat in SCHAEFFLER_MATERIALS:
for mat in HARTOMAT_MATERIALS:
desc = mat["description"].replace("'", "''")
name = mat["name"].replace("'", "''")
conn.execute(sa.text(
f"INSERT INTO materials (id, name, description, source, schaeffler_code, created_at, updated_at) "
f"INSERT INTO materials (id, name, description, source, hartomat_code, created_at, updated_at) "
f"VALUES ('{uuid.uuid4()}', '{name}', '{desc}', '{mat['source']}', "
f"{mat['schaeffler_code']}, '{now}', '{now}') "
f"{mat['hartomat_code']}, '{now}', '{now}') "
f"ON CONFLICT (name) DO NOTHING"
))
def downgrade() -> None:
op.execute("DELETE FROM materials WHERE source = 'schaeffler_standard'")
op.drop_column("materials", "schaeffler_code")
op.execute("DELETE FROM materials WHERE source = 'hartomat_standard'")
op.drop_column("materials", "hartomat_code")
+1 -1
View File
@@ -28,7 +28,7 @@ def upgrade():
# Seed default tenant — all existing data will be assigned to this tenant
op.execute("""
INSERT INTO tenants (name, slug, is_active)
VALUES ('Schaeffler', 'schaeffler', true)
VALUES ('HartOMat', 'hartomat', true)
""")
+2 -2
View File
@@ -74,10 +74,10 @@ def upgrade():
),
)
# 2. Backfill with the default 'schaeffler' tenant
# 2. Backfill with the default 'hartomat' tenant
op.execute(
f"UPDATE {table} "
"SET tenant_id = (SELECT id FROM tenants WHERE slug = 'schaeffler')"
"SET tenant_id = (SELECT id FROM tenants WHERE slug = 'hartomat')"
)
# 3. Make NOT NULL now that every row has a value
@@ -20,7 +20,7 @@ _DEFAULT_CONFIG = """{
"max_concurrent_renders": 3,
"render_engines_allowed": ["cycles", "eevee"],
"max_order_size": 500,
"fallback_material": "SCHAEFFLER_059999_FailedMaterial",
"fallback_material": "HARTOMAT_059999_FailedMaterial",
"notifications_enabled": true,
"invoice_prefix": "INV"
}"""
@@ -0,0 +1,106 @@
"""Backfill persisted Schaeffler branding to HartOMat.
Revision ID: 063
Revises: 062
"""
from alembic import op
import sqlalchemy as sa
revision = "063"
down_revision = "062"
branch_labels = None
depends_on = None
def upgrade() -> None:
bind = op.get_bind()
inspector = sa.inspect(bind)
material_columns = {column["name"] for column in inspector.get_columns("materials")}
if "schaeffler_code" in material_columns and "hartomat_code" not in material_columns:
op.alter_column("materials", "schaeffler_code", new_column_name="hartomat_code")
op.execute(
"""
UPDATE materials
SET name = REPLACE(name, 'SCHAEFFLER_', 'HARTOMAT_')
WHERE name LIKE 'SCHAEFFLER_%'
"""
)
op.execute(
"""
UPDATE materials
SET source = 'hartomat_standard'
WHERE source = 'schaeffler_standard'
"""
)
op.execute(
"""
UPDATE tenants
SET name = 'HartOMat',
slug = 'hartomat'
WHERE lower(name) = 'schaeffler'
OR lower(slug) = 'schaeffler'
"""
)
op.execute(
"""
UPDATE tenants
SET tenant_config = jsonb_set(
tenant_config,
'{fallback_material}',
'\"HARTOMAT_059999_FailedMaterial\"'::jsonb,
true
)
WHERE tenant_config ? 'fallback_material'
"""
)
def downgrade() -> None:
bind = op.get_bind()
inspector = sa.inspect(bind)
material_columns = {column["name"] for column in inspector.get_columns("materials")}
op.execute(
"""
UPDATE materials
SET name = REPLACE(name, 'HARTOMAT_', 'SCHAEFFLER_')
WHERE name LIKE 'HARTOMAT_%'
"""
)
op.execute(
"""
UPDATE materials
SET source = 'schaeffler_standard'
WHERE source = 'hartomat_standard'
"""
)
op.execute(
"""
UPDATE tenants
SET name = 'Schaeffler',
slug = 'schaeffler'
WHERE lower(name) = 'hartomat'
OR lower(slug) = 'hartomat'
"""
)
op.execute(
"""
UPDATE tenants
SET tenant_config = jsonb_set(
tenant_config,
'{fallback_material}',
'\"SCHAEFFLER_059999_FailedMaterial\"'::jsonb,
true
)
WHERE tenant_config ? 'fallback_material'
"""
)
if "hartomat_code" in material_columns and "schaeffler_code" not in material_columns:
op.alter_column("materials", "hartomat_code", new_column_name="schaeffler_code")
+2 -2
View File
@@ -1014,9 +1014,9 @@ async def get_dashboard_stats(
if isinstance(entry, dict) and entry.get("material"):
all_mat_names.add(entry["material"])
# Library materials (name starts with SCHAEFFLER_)
# Library materials (name starts with HARTOMAT_)
lib_count_result = await db.execute(
select(func.count(Material.id)).where(Material.name.like("SCHAEFFLER_%"))
select(func.count(Material.id)).where(Material.name.like("HARTOMAT_%"))
)
library_material_count = lib_count_result.scalar() or 0
+1 -1
View File
@@ -89,7 +89,7 @@ async def get_material_pbr_map(db: AsyncSession = Depends(get_db)):
}
# Also index by aliases so frontend can look up by raw Excel names
# (e.g. "Steel--Stahl" → same PBR as "SCHAEFFLER_010101_Steel-Bare")
# (e.g. "Steel--Stahl" → same PBR as "HARTOMAT_010101_Steel-Bare")
# Bypass RLS — this is public data and aliases may have NULL tenant_id
if pbr_map:
await db.execute(text("SET LOCAL app.current_tenant_id = 'bypass'"))
+14 -14
View File
@@ -23,7 +23,7 @@ class MaterialOut(BaseModel):
name: str
description: str | None
source: str
schaeffler_code: int | None = None
hartomat_code: int | None = None
created_by_name: str | None = None
aliases: list[str] = []
created_at: datetime
@@ -42,7 +42,7 @@ class MaterialCreate(BaseModel):
name: str
description: str | None = None
source: str = "manual"
schaeffler_code: int | None = None
hartomat_code: int | None = None
class MaterialUpdate(BaseModel):
@@ -64,7 +64,7 @@ def _to_out(mat: Material) -> MaterialOut:
name=mat.name,
description=mat.description,
source=mat.source,
schaeffler_code=mat.schaeffler_code,
hartomat_code=mat.hartomat_code,
created_by_name=creator_name,
aliases=alias_names,
created_at=mat.created_at,
@@ -94,9 +94,9 @@ async def get_next_code(
range_end = prefix_int + 99
result = await db.execute(
select(func.max(Material.schaeffler_code)).where(
Material.schaeffler_code >= range_start,
Material.schaeffler_code <= range_end,
select(func.max(Material.hartomat_code)).where(
Material.hartomat_code >= range_start,
Material.hartomat_code <= range_end,
)
)
max_code = result.scalar_one_or_none()
@@ -113,16 +113,16 @@ async def get_next_code(
}
@router.post("/seed-schaeffler")
async def seed_schaeffler_materials(
@router.post("/seed-hartomat")
async def seed_hartomat_materials(
user: User = Depends(require_admin_or_pm),
db: AsyncSession = Depends(get_db),
):
"""Bulk-create the 35 standard Schaeffler materials. Skips existing by name."""
from app.data.schaeffler_materials import SCHAEFFLER_MATERIALS
"""Bulk-create the 35 standard HartOMat materials. Skips existing by name."""
from app.data.hartomat_materials import HARTOMAT_MATERIALS
inserted = 0
for mat_data in SCHAEFFLER_MATERIALS:
for mat_data in HARTOMAT_MATERIALS:
existing = await db.execute(
select(Material).where(Material.name == mat_data["name"])
)
@@ -132,14 +132,14 @@ async def seed_schaeffler_materials(
name=mat_data["name"],
description=mat_data["description"],
source=mat_data["source"],
schaeffler_code=mat_data["schaeffler_code"],
hartomat_code=mat_data["hartomat_code"],
created_by=user.id,
)
db.add(mat)
inserted += 1
await db.commit()
return {"inserted": inserted, "total": len(SCHAEFFLER_MATERIALS)}
return {"inserted": inserted, "total": len(HARTOMAT_MATERIALS)}
@router.post("/seed-aliases")
@@ -273,7 +273,7 @@ async def create_material(
name=body.name,
description=body.description,
source=body.source,
schaeffler_code=body.schaeffler_code,
hartomat_code=body.hartomat_code,
created_by=user.id,
)
db.add(mat)
+2 -2
View File
@@ -475,8 +475,8 @@ async def scale_workers(
compose_file = os.path.join(compose_dir, "docker-compose.yml")
# Derive project name from compose dir on host (directory name = project name).
# Inside the container the compose file is at /compose, but the host project
# dir name determines the container naming prefix (e.g. "schaefflerautomat").
compose_project = os.environ.get("COMPOSE_PROJECT_NAME", "schaefflerautomat")
# dir name determines the container naming prefix (e.g. "hartomat").
compose_project = os.environ.get("COMPOSE_PROJECT_NAME", "hartomat")
def _scale() -> subprocess.CompletedProcess:
return subprocess.run(
+3 -3
View File
@@ -4,9 +4,9 @@ from typing import Optional
class Settings(BaseSettings):
# Database
postgres_db: str = "schaeffler"
postgres_user: str = "schaeffler"
postgres_password: str = "schaeffler"
postgres_db: str = "hartomat"
postgres_user: str = "hartomat"
postgres_password: str = "hartomat"
postgres_host: str = "localhost"
postgres_port: int = 5432
+48
View File
@@ -0,0 +1,48 @@
"""HartOMat standard materials — single source of truth.
Naming convention: HARTOMAT_[TypeCode(2)][SubType(2)][Consecutive(2)]_[Name-Parts-Dashed]
Type codes: 01=Metals, 02=Coatings, 03=Non-metals, 04=Compounds, 05=Misc
"""
HARTOMAT_MATERIALS: list[dict] = [
# --- 01 Metals ---
{"name": "HARTOMAT_010101_Steel-Bare", "description": "Stahl / Stahl, glänzend / Stahl, konserviert", "hartomat_code": 10101, "source": "hartomat_standard"},
{"name": "HARTOMAT_010102_Steel-Burnished", "description": "Stahl, brüniert", "hartomat_code": 10102, "source": "hartomat_standard"},
{"name": "HARTOMAT_010103_Steel-Galvanized", "description": "Stahl, verzinkt", "hartomat_code": 10103, "source": "hartomat_standard"},
{"name": "HARTOMAT_010104_Steel-Casted", "description": "Stahl Körnung", "hartomat_code": 10104, "source": "hartomat_standard"},
{"name": "HARTOMAT_010105_Steel-Plate", "description": "Stahlblech", "hartomat_code": 10105, "source": "hartomat_standard"},
{"name": "HARTOMAT_010201_Niro", "description": "Niro", "hartomat_code": 10201, "source": "hartomat_standard"},
{"name": "HARTOMAT_010301_Tin", "description": "MU-Stahl, Zinnüberzug / MX-Stahl, Zinnüberzug", "hartomat_code": 10301, "source": "hartomat_standard"},
{"name": "HARTOMAT_010401_Aluminium", "description": "Aluminium", "hartomat_code": 10401, "source": "hartomat_standard"},
{"name": "HARTOMAT_010501_Brass", "description": "Messing", "hartomat_code": 10501, "source": "hartomat_standard"},
{"name": "HARTOMAT_010601_Bronze", "description": "MU-B, Bronze", "hartomat_code": 10601, "source": "hartomat_standard"},
# --- 02 Coatings ---
{"name": "HARTOMAT_020101_Durotect-Blue", "description": "Stahl, Durotect CMT", "hartomat_code": 20101, "source": "hartomat_standard"},
{"name": "HARTOMAT_020102_Durotect-Black", "description": "Stahl, Durotect M", "hartomat_code": 20102, "source": "hartomat_standard"},
{"name": "HARTOMAT_020201_Coat-Black", "description": "", "hartomat_code": 20201, "source": "hartomat_standard"},
# --- 03 Non-metals ---
{"name": "HARTOMAT_030101_Elastomer-Brown", "description": "Elastomer, braun", "hartomat_code": 30101, "source": "hartomat_standard"},
{"name": "HARTOMAT_030102_Elastomer-Green", "description": "Elastomer, grün", "hartomat_code": 30102, "source": "hartomat_standard"},
{"name": "HARTOMAT_030103_Elastomer-Black", "description": "Elastomer, schwarz", "hartomat_code": 30103, "source": "hartomat_standard"},
{"name": "HARTOMAT_030201_Plastic-Brown", "description": "Kunststoff, braun", "hartomat_code": 30201, "source": "hartomat_standard"},
{"name": "HARTOMAT_030202_Plastic-Green", "description": "Kunststoff, grün", "hartomat_code": 30202, "source": "hartomat_standard"},
{"name": "HARTOMAT_030203_Plastic-Black", "description": "Kunststoff, schwarz", "hartomat_code": 30203, "source": "hartomat_standard"},
{"name": "HARTOMAT_030204_Plastic-Blue", "description": "Kunststoff, blau", "hartomat_code": 30204, "source": "hartomat_standard"},
{"name": "HARTOMAT_030205_Plastic-White", "description": "Kunststoff, weiß", "hartomat_code": 30205, "source": "hartomat_standard"},
{"name": "HARTOMAT_030301_Plastic-Clear", "description": "Kunststoff, durchsichtig", "hartomat_code": 30301, "source": "hartomat_standard"},
{"name": "HARTOMAT_030302_Plastic-Translucent-White", "description": "", "hartomat_code": 30302, "source": "hartomat_standard"},
{"name": "HARTOMAT_030401_TPU-Blue", "description": "TPU, blau", "hartomat_code": 30401, "source": "hartomat_standard"},
{"name": "HARTOMAT_030501_Ceramic-Black", "description": "Keramik, schwarz", "hartomat_code": 30501, "source": "hartomat_standard"},
# --- 04 Compounds ---
{"name": "HARTOMAT_040101_E40", "description": "E40", "hartomat_code": 40101, "source": "hartomat_standard"},
{"name": "HARTOMAT_040102_E50", "description": "E50", "hartomat_code": 40102, "source": "hartomat_standard"},
{"name": "HARTOMAT_040201_Elgoglide", "description": "Elgoglide", "hartomat_code": 40201, "source": "hartomat_standard"},
{"name": "HARTOMAT_040202_Elgotex", "description": "Elgotex, schwarz", "hartomat_code": 40202, "source": "hartomat_standard"},
{"name": "HARTOMAT_040301_PTFE-Niro-Compound", "description": "PTFE-Compound, Niro-Verbund", "hartomat_code": 40301, "source": "hartomat_standard"},
{"name": "HARTOMAT_040302_PTFE-Foil", "description": "PTFE-Folie", "hartomat_code": 40302, "source": "hartomat_standard"},
{"name": "HARTOMAT_040303_PTFE-Compound-Black", "description": "PTFE-Verbund, schwarz", "hartomat_code": 40303, "source": "hartomat_standard"},
{"name": "HARTOMAT_040304_PTFE-Compound-Orange", "description": "PTFE-Verbundwerkstoff", "hartomat_code": 40304, "source": "hartomat_standard"},
{"name": "HARTOMAT_040305_GFK-PTFE-Compound", "description": "GFK+PTFE Verbundwerkstoff, schwarz / TPU, schwarz", "hartomat_code": 40305, "source": "hartomat_standard"},
# --- 05 Misc ---
{"name": "HARTOMAT_059999_FailedMaterial", "description": "", "hartomat_code": 59999, "source": "hartomat_standard"},
]
+36 -36
View File
@@ -1,9 +1,9 @@
"""Material alias seed data — derived from naming_scheme.xlsx Materialmapping sheet.
Each entry maps a SCHAEFFLER library material name to its known aliases:
Each entry maps a HARTOMAT library material name to its known aliases:
- German description (Col A from Materialmapping)
- Intermediate identifier (Col B, e.g. "Steel_black_oxided--Stahl_brueniert")
- Schaeffler code as string (e.g. "10102")
- HartOMat code as string (e.g. "10102")
- German variants (singular/plural, abbreviations, industry terms, DIN/EN standards)
- English equivalents commonly used in German engineering contexts
"""
@@ -13,7 +13,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
# --- 01 Metals ---
# =====================================================================
{
"material_name": "SCHAEFFLER_010101_Steel-Bare",
"material_name": "HARTOMAT_010101_Steel-Bare",
"aliases": [
"Stahl",
"Stahl, glänzend",
@@ -66,7 +66,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010102_Steel-Burnished",
"material_name": "HARTOMAT_010102_Steel-Burnished",
"aliases": [
"Stahl, brüniert",
"Steel_black_oxided--Stahl_brueniert",
@@ -94,7 +94,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010103_Steel-Galvanized",
"material_name": "HARTOMAT_010103_Steel-Galvanized",
"aliases": [
"Stahl, verzinkt",
"Steel_galvanized--Stahl_verzinkt",
@@ -130,7 +130,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010104_Steel-Casted",
"material_name": "HARTOMAT_010104_Steel-Casted",
"aliases": [
"Stahl Körnung",
"Guss",
@@ -169,7 +169,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010105_Steel-Plate",
"material_name": "HARTOMAT_010105_Steel-Plate",
"aliases": [
"Stahlblech",
"Steel_sheet--Stahlblech",
@@ -204,7 +204,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010201_Niro",
"material_name": "HARTOMAT_010201_Niro",
"aliases": [
"Niro",
"Steel_stainless--Niro",
@@ -248,7 +248,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010301_Tin",
"material_name": "HARTOMAT_010301_Tin",
"aliases": [
"Zinnüberzug",
"Tin--Zinn",
@@ -278,7 +278,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010401_Aluminium",
"material_name": "HARTOMAT_010401_Aluminium",
"aliases": [
"Aluminium",
"Aluminium--Aluminium",
@@ -319,7 +319,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010501_Brass",
"material_name": "HARTOMAT_010501_Brass",
"aliases": [
"Messing",
"Brass--Messing",
@@ -351,7 +351,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_010601_Bronze",
"material_name": "HARTOMAT_010601_Bronze",
"aliases": [
"MU-B; Bronze",
"Bronze",
@@ -393,7 +393,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
# --- 02 Coatings ---
# =====================================================================
{
"material_name": "SCHAEFFLER_020101_Durotect-Blue",
"material_name": "HARTOMAT_020101_Durotect-Blue",
"aliases": [
"Stahl, Durotect CMT",
"Durotect_CMT--Durotect_CMT",
@@ -414,7 +414,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_020102_Durotect-Black",
"material_name": "HARTOMAT_020102_Durotect-Black",
"aliases": [
"Stahl, Durotect M",
"Stahl; Durotect M",
@@ -435,7 +435,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_020201_Coat-Black",
"material_name": "HARTOMAT_020201_Coat-Black",
"aliases": [
"Stahl, schwarz",
"Steel_coated_black--Stahl_beschichtet_schwarz",
@@ -468,7 +468,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
# --- 03 Non-metals ---
# =====================================================================
{
"material_name": "SCHAEFFLER_030101_Elastomer-Brown",
"material_name": "HARTOMAT_030101_Elastomer-Brown",
"aliases": [
"Elastomer, braun",
"Elastomer_brown--Elastomer_braun",
@@ -493,7 +493,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030102_Elastomer-Green",
"material_name": "HARTOMAT_030102_Elastomer-Green",
"aliases": [
"Elastomer, grün",
"Elastomer_green--Elastomer_gruen",
@@ -518,7 +518,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030103_Elastomer-Black",
"material_name": "HARTOMAT_030103_Elastomer-Black",
"aliases": [
"Elastomer, schwarz",
"Eslastomer_black--Elastomer_schwarz",
@@ -557,7 +557,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030201_Plastic-Brown",
"material_name": "HARTOMAT_030201_Plastic-Brown",
"aliases": [
"Kunststoff, braun",
"Plastic_brown--Kunststoff_braun",
@@ -585,7 +585,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030202_Plastic-Green",
"material_name": "HARTOMAT_030202_Plastic-Green",
"aliases": [
"Kunststoff, grün",
"Plastic_green--Kunststoff_gruen",
@@ -612,7 +612,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030203_Plastic-Black",
"material_name": "HARTOMAT_030203_Plastic-Black",
"aliases": [
"Kunststoff, schwarz",
"Plastic_black--Kunststoff_schwarz",
@@ -642,7 +642,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030204_Plastic-Blue",
"material_name": "HARTOMAT_030204_Plastic-Blue",
"aliases": [
"Kunststoff, blau",
"Plastic_blue--Kunststoff_blau",
@@ -668,7 +668,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030205_Plastic-White",
"material_name": "HARTOMAT_030205_Plastic-White",
"aliases": [
"Kunststoff, weiß",
"Plastic_white--Kunststoff_weiss",
@@ -702,7 +702,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030301_Plastic-Clear",
"material_name": "HARTOMAT_030301_Plastic-Clear",
"aliases": [
"Kunststoff, durchsichtig",
"Plastic_clear--Kunststoff_durchsichtig",
@@ -738,7 +738,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030302_Plastic-Translucent-White",
"material_name": "HARTOMAT_030302_Plastic-Translucent-White",
"aliases": [
"Plastic_translucent_white--Kunststoff_transluzent_weiss",
"30302",
@@ -769,7 +769,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030401_TPU-Blue",
"material_name": "HARTOMAT_030401_TPU-Blue",
"aliases": [
"TPU, blau",
"Elastomer_blue--Elastomer_blau",
@@ -798,7 +798,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_030501_Ceramic-Black",
"material_name": "HARTOMAT_030501_Ceramic-Black",
"aliases": [
"Keramik, schwarz",
"Ceramics_black--Keramik_schwarz",
@@ -834,7 +834,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
# --- 04 Compounds ---
# =====================================================================
{
"material_name": "SCHAEFFLER_040101_E40",
"material_name": "HARTOMAT_040101_E40",
"aliases": [
"E40",
"E40--E40",
@@ -851,7 +851,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040102_E50",
"material_name": "HARTOMAT_040102_E50",
"aliases": [
"E50",
"E50--E50",
@@ -868,7 +868,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040201_Elgoglide",
"material_name": "HARTOMAT_040201_Elgoglide",
"aliases": [
"Elgoglide",
"Elgoglide--Elgoglide",
@@ -886,7 +886,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040202_Elgotex",
"material_name": "HARTOMAT_040202_Elgotex",
"aliases": [
"Elgotex, schwarz",
"Elgotex--Elgotex",
@@ -907,7 +907,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040301_PTFE-Niro-Compound",
"material_name": "HARTOMAT_040301_PTFE-Niro-Compound",
"aliases": [
"PTFE-Compound, Niro-Verbund",
"PTFE_compound_stainless_steel_composite--PTFE_Compound_Niro_Verbund",
@@ -933,7 +933,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040302_PTFE-Foil",
"material_name": "HARTOMAT_040302_PTFE-Foil",
"aliases": [
"PTFE-Folie",
"PTFE_film--PTFE_Folie",
@@ -962,7 +962,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040303_PTFE-Compound-Black",
"material_name": "HARTOMAT_040303_PTFE-Compound-Black",
"aliases": [
"PTFE-Verbund, schwarz",
"PTFE_compound_black--PTFE_Verbund_schwarz",
@@ -987,7 +987,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040304_PTFE-Compound-Orange",
"material_name": "HARTOMAT_040304_PTFE-Compound-Orange",
"aliases": [
"PTFE-Verbundwerkstoff",
"PTFE_composite_material_orange--PTFE_Verbundwerkstoff_orange",
@@ -1014,7 +1014,7 @@ MATERIAL_ALIAS_SEEDS: list[dict] = [
],
},
{
"material_name": "SCHAEFFLER_040305_GFK-PTFE-Compound",
"material_name": "HARTOMAT_040305_GFK-PTFE-Compound",
"aliases": [
"GFK+PTFE Verbundwerkstoff, schwarz",
"GFK_PTFE_compound--GFK_PTFE_Verbundwerkstoff",
-48
View File
@@ -1,48 +0,0 @@
"""Schaeffler standard materials — single source of truth.
Naming convention: SCHAEFFLER_[TypeCode(2)][SubType(2)][Consecutive(2)]_[Name-Parts-Dashed]
Type codes: 01=Metals, 02=Coatings, 03=Non-metals, 04=Compounds, 05=Misc
"""
SCHAEFFLER_MATERIALS: list[dict] = [
# --- 01 Metals ---
{"name": "SCHAEFFLER_010101_Steel-Bare", "description": "Stahl / Stahl, glänzend / Stahl, konserviert", "schaeffler_code": 10101, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010102_Steel-Burnished", "description": "Stahl, brüniert", "schaeffler_code": 10102, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010103_Steel-Galvanized", "description": "Stahl, verzinkt", "schaeffler_code": 10103, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010104_Steel-Casted", "description": "Stahl Körnung", "schaeffler_code": 10104, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010105_Steel-Plate", "description": "Stahlblech", "schaeffler_code": 10105, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010201_Niro", "description": "Niro", "schaeffler_code": 10201, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010301_Tin", "description": "MU-Stahl, Zinnüberzug / MX-Stahl, Zinnüberzug", "schaeffler_code": 10301, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010401_Aluminium", "description": "Aluminium", "schaeffler_code": 10401, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010501_Brass", "description": "Messing", "schaeffler_code": 10501, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_010601_Bronze", "description": "MU-B, Bronze", "schaeffler_code": 10601, "source": "schaeffler_standard"},
# --- 02 Coatings ---
{"name": "SCHAEFFLER_020101_Durotect-Blue", "description": "Stahl, Durotect CMT", "schaeffler_code": 20101, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_020102_Durotect-Black", "description": "Stahl, Durotect M", "schaeffler_code": 20102, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_020201_Coat-Black", "description": "", "schaeffler_code": 20201, "source": "schaeffler_standard"},
# --- 03 Non-metals ---
{"name": "SCHAEFFLER_030101_Elastomer-Brown", "description": "Elastomer, braun", "schaeffler_code": 30101, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030102_Elastomer-Green", "description": "Elastomer, grün", "schaeffler_code": 30102, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030103_Elastomer-Black", "description": "Elastomer, schwarz", "schaeffler_code": 30103, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030201_Plastic-Brown", "description": "Kunststoff, braun", "schaeffler_code": 30201, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030202_Plastic-Green", "description": "Kunststoff, grün", "schaeffler_code": 30202, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030203_Plastic-Black", "description": "Kunststoff, schwarz", "schaeffler_code": 30203, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030204_Plastic-Blue", "description": "Kunststoff, blau", "schaeffler_code": 30204, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030205_Plastic-White", "description": "Kunststoff, weiß", "schaeffler_code": 30205, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030301_Plastic-Clear", "description": "Kunststoff, durchsichtig", "schaeffler_code": 30301, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030302_Plastic-Translucent-White", "description": "", "schaeffler_code": 30302, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030401_TPU-Blue", "description": "TPU, blau", "schaeffler_code": 30401, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_030501_Ceramic-Black", "description": "Keramik, schwarz", "schaeffler_code": 30501, "source": "schaeffler_standard"},
# --- 04 Compounds ---
{"name": "SCHAEFFLER_040101_E40", "description": "E40", "schaeffler_code": 40101, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040102_E50", "description": "E50", "schaeffler_code": 40102, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040201_Elgoglide", "description": "Elgoglide", "schaeffler_code": 40201, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040202_Elgotex", "description": "Elgotex, schwarz", "schaeffler_code": 40202, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040301_PTFE-Niro-Compound", "description": "PTFE-Compound, Niro-Verbund", "schaeffler_code": 40301, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040302_PTFE-Foil", "description": "PTFE-Folie", "schaeffler_code": 40302, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040303_PTFE-Compound-Black", "description": "PTFE-Verbund, schwarz", "schaeffler_code": 40303, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040304_PTFE-Compound-Orange", "description": "PTFE-Verbundwerkstoff", "schaeffler_code": 40304, "source": "schaeffler_standard"},
{"name": "SCHAEFFLER_040305_GFK-PTFE-Compound", "description": "GFK+PTFE Verbundwerkstoff, schwarz / TPU, schwarz", "schaeffler_code": 40305, "source": "schaeffler_standard"},
# --- 05 Misc ---
{"name": "SCHAEFFLER_059999_FailedMaterial", "description": "", "schaeffler_code": 59999, "source": "schaeffler_standard"},
]
+1 -1
View File
@@ -17,7 +17,7 @@ class Material(Base):
name: Mapped[str] = mapped_column(String(200), nullable=False, unique=True)
description: Mapped[str] = mapped_column(Text, nullable=True)
source: Mapped[str] = mapped_column(String(20), nullable=False, default="manual")
schaeffler_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
hartomat_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
created_by: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
)
+8 -8
View File
@@ -1,7 +1,7 @@
"""Material alias resolution service.
Used from Celery tasks (sync context) to resolve raw material names
(from Excel / user input) to SCHAEFFLER library material names via aliases.
(from Excel / user input) to HARTOMAT library material names via aliases.
Resolution chain:
1. Alias lookup (case-insensitive) → use alias.material.name
@@ -31,7 +31,7 @@ def _get_engine():
def resolve_material_map(raw_map: dict[str, str]) -> dict[str, str]:
"""Resolve raw material names to SCHAEFFLER library names via aliases.
"""Resolve raw material names to HARTOMAT library names via aliases.
For each value in raw_map:
1. Alias lookup (case-insensitive) → return alias.material.name
@@ -66,7 +66,7 @@ def resolve_material_map(raw_map: dict[str, str]) -> dict[str, str]:
raw_lower = raw_material.lower()
# 1. Alias lookup first — aliases explicitly map intermediate/display names
# to the canonical SCHAEFFLER library names
# to the canonical HARTOMAT library names
if raw_lower in alias_lookup:
target = alias_lookup[raw_lower]
logger.info("resolved '%s''%s' (alias match)", raw_material, target)
@@ -147,7 +147,7 @@ async def find_unmapped_materials(
"""Find material names that have no alias or library match.
Returns a list of {"raw_name": str, "suggestions": [...]} for each
unmapped name. Suggestions are the top 5 SCHAEFFLER library materials
unmapped name. Suggestions are the top 5 HARTOMAT library materials
by string similarity.
"""
if not material_names:
@@ -159,8 +159,8 @@ async def find_unmapped_materials(
# Load all materials
mat_rows = (await db.execute(select(Material))).scalars().all()
# Library materials have a schaeffler_code
library_mats = [m for m in mat_rows if m.schaeffler_code is not None]
# Library materials have a hartomat_code
library_mats = [m for m in mat_rows if m.hartomat_code is not None]
# All material names (case-insensitive) for exact-match check
name_lookup: dict[str, Material] = {m.name.lower(): m for m in mat_rows}
@@ -179,7 +179,7 @@ async def find_unmapped_materials(
# 2. Exact name match with a library material → mapped
matched_mat = name_lookup.get(raw_lower)
if matched_mat and matched_mat.schaeffler_code is not None:
if matched_mat and matched_mat.hartomat_code is not None:
continue
# Unmapped — compute suggestions from library materials
@@ -194,7 +194,7 @@ async def find_unmapped_materials(
{
"id": str(m.id),
"name": m.name,
"schaeffler_code": str(m.schaeffler_code),
"hartomat_code": str(m.hartomat_code),
}
for _, m in scored[:5]
]
+1 -1
View File
@@ -235,7 +235,7 @@ def send_email_notification_stub(
from email.mime.text import MIMEText
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = subject
msg["From"] = cfg.get("smtp_from_address") or cfg.get("smtp_user", "noreply@schaeffler.com")
msg["From"] = cfg.get("smtp_from_address") or cfg.get("smtp_user", "noreply@hartomat.com")
msg["To"] = to_address
port = int(cfg.get("smtp_port", "587"))
with smtplib.SMTP(smtp_host, port) as smtp:
@@ -330,7 +330,7 @@ def generate_usd_master_task(self, cad_file_id: str) -> dict:
if part_name and raw_material:
raw_mat_map[part_name] = raw_material
# Resolve raw material names to SCHAEFFLER library names via aliases
# Resolve raw material names to HARTOMAT library names via aliases
material_map: dict[str, str] = {}
if raw_mat_map:
material_map = resolve_material_map(raw_mat_map)
@@ -244,7 +244,7 @@ def render_order_line_task(self, order_line_id: str):
if m.get("part_name") and m.get("material")
}
# Resolve raw material names to SCHAEFFLER library names via aliases
# Resolve raw material names to HARTOMAT library names via aliases
from app.services.material_service import resolve_material_map
material_map = resolve_material_map(material_map)
@@ -61,7 +61,7 @@ _STEP_DESCRIPTIONS: dict[StepName, str] = {
StepName.OCC_OBJECT_EXTRACT: "Extract part objects and metadata from the STEP file using cadquery/OCC",
StepName.OCC_GLB_EXPORT: "Convert STEP geometry to glTF/GLB via cadquery",
StepName.GLB_BBOX: "Compute bounding-box from the exported GLB for camera framing",
StepName.MATERIAL_MAP_RESOLVE: "Resolve raw part-material names to SCHAEFFLER library materials via alias table",
StepName.MATERIAL_MAP_RESOLVE: "Resolve raw part-material names to HARTOMAT library materials via alias table",
StepName.AUTO_POPULATE_MATERIALS: "Auto-create Material records for any newly discovered part names",
StepName.BLENDER_RENDER: "Render a thumbnail PNG using Blender (Cycles or EEVEE)",
StepName.THREEJS_RENDER: "Render a thumbnail PNG using Three.js / Playwright headless browser",
+1 -1
View File
@@ -9,7 +9,7 @@ _DEFAULT_TENANT_CONFIG = {
"max_concurrent_renders": 3,
"render_engines_allowed": ["cycles", "eevee"],
"max_order_size": 500,
"fallback_material": "SCHAEFFLER_059999_FailedMaterial",
"fallback_material": "HARTOMAT_059999_FailedMaterial",
"notifications_enabled": True,
"invoice_prefix": "INV",
# Azure AI validation (per-tenant)
+3 -3
View File
@@ -41,9 +41,9 @@ async def lifespan(app: FastAPI):
app = FastAPI(
title="Schaeffler Automat API",
title="HartOMat API",
version="0.1.0",
description="Media-creation pipeline for Schaeffler CAD/bearing product orders",
description="Media-creation pipeline for HartOMat CAD/bearing product orders",
lifespan=lifespan,
)
@@ -101,7 +101,7 @@ app.include_router(chat_router, prefix="/api")
@app.get("/health")
async def health():
return {"status": "ok", "service": "schaefflerautomat-backend"}
return {"status": "ok", "service": "hartomat-backend"}
@app.websocket("/api/ws")
+2 -2
View File
@@ -8,12 +8,12 @@ from pathlib import Path
logger = logging.getLogger(__name__)
VALIDATION_PROMPT = """You are a quality control expert for Schaeffler bearing product catalog images.
VALIDATION_PROMPT = """You are a quality control expert for HartOMat bearing product catalog images.
Analyze this thumbnail of a bearing/mechanical component and evaluate:
1. Is the component orientation correct for a standard product catalog? (typically isometric view, 30° elevation, 45° rotation)
2. Are the key features visible? (rolling elements, rings, cage if present)
3. Does it match standard Schaeffler catalog angle conventions?
3. Does it match standard HartOMat catalog angle conventions?
Respond in JSON with exactly these fields:
{
+9 -9
View File
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
# ── System prompt ────────────────────────────────────────────────────────────
SYSTEM_PROMPT = """You are the Schaeffler Automat AI assistant. You help users manage their automated render pipeline for Schaeffler product images.
SYSTEM_PROMPT = """You are the HartOMat AI assistant. You help users manage their automated render pipeline for HartOMat product images.
You can:
- List and search orders and products
@@ -39,8 +39,8 @@ RULES:
7. Respond in the same language the user writes in.
8. Be concise — short answers are better than long ones.
9. When the user says "beliebig", "any", "random", "irgendein" — just pick one yourself, don't ask back.
10. Material system: Materials have SCHAEFFLER library names (e.g. SCHAEFFLER_020101_Durotect-Blue). Common names like "Durotect", "Stahl", "Bronze" are aliases that map to these library names. When the user asks for a material by a common name, use list_materials to find the correct SCHAEFFLER name, then use that for material_override.
11. When setting material_override, always use the full SCHAEFFLER library name (e.g. SCHAEFFLER_020101_Durotect-Blue), never the alias.
10. Material system: Materials have HARTOMAT library names (e.g. HARTOMAT_020101_Durotect-Blue). Common names like "Durotect", "Stahl", "Bronze" are aliases that map to these library names. When the user asks for a material by a common name, use list_materials to find the correct HARTOMAT name, then use that for material_override.
11. When setting material_override, always use the full HARTOMAT library name (e.g. HARTOMAT_020101_Durotect-Blue), never the alias.
12. When mentioning a product, ALWAYS link to it: [ProductName](/products/UUID). When mentioning an order, link to it: [OrderNumber](/orders/UUID). This makes the response navigable.
13. Materials exist at TWO levels: (a) product_material — materials assigned to the product's CAD parts (from STEP/Excel import, e.g. "Durotect_M", "Stahl"), and (b) material_override — a single material applied to ALL parts at render time. When user asks for a product "with Durotect material", search product_material FIRST (products that naturally have Durotect parts). Only use material_override filter if they specifically say "override" or "alle Teile in".
14. NEVER say "no renders found" when renders DO exist. If no exact match, show the closest match and explain what's different."""
@@ -120,7 +120,7 @@ TOOLS = [
},
"material_override": {
"type": "string",
"description": "Optional SCHAEFFLER library material name to apply to all lines.",
"description": "Optional HARTOMAT library material name to apply to all lines.",
"default": "",
},
},
@@ -176,7 +176,7 @@ TOOLS = [
},
"material_name": {
"type": "string",
"description": "SCHAEFFLER library material name, or empty string to clear.",
"description": "HARTOMAT library material name, or empty string to clear.",
},
},
"required": ["order_id", "material_name"],
@@ -250,7 +250,7 @@ TOOLS = [
"type": "function",
"function": {
"name": "list_materials",
"description": "List available SCHAEFFLER library materials with their aliases. Use this to find the correct material name for material_override. Materials have names like SCHAEFFLER_010101_Steel-Bare. Aliases map common names (Stahl, Bronze, Durotect, etc.) to these library materials. When user asks for a material by a common name, search aliases to find the correct SCHAEFFLER library material name.",
"description": "List available HARTOMAT library materials with their aliases. Use this to find the correct material name for material_override. Materials have names like HARTOMAT_010101_Steel-Bare. Aliases map common names (Stahl, Bronze, Durotect, etc.) to these library materials. When user asks for a material by a common name, search aliases to find the correct HARTOMAT library material name.",
"parameters": {
"type": "object",
"properties": {
@@ -671,13 +671,13 @@ async def _tool_check_materials(db: AsyncSession, tenant_id: str, user_id: str =
async def _tool_list_materials(db: AsyncSession, tenant_id: str, query: str = "") -> str:
"""List library materials with their aliases."""
sql = """
SELECT m.id, m.name, m.schaeffler_code, m.description,
SELECT m.id, m.name, m.hartomat_code, m.description,
COALESCE(
(SELECT json_agg(ma.alias) FROM material_aliases ma WHERE ma.material_id = m.id),
'[]'::json
) AS aliases
FROM materials m
WHERE m.schaeffler_code IS NOT NULL
WHERE m.hartomat_code IS NOT NULL
"""
params: dict = {}
if query:
@@ -698,7 +698,7 @@ async def _tool_list_materials(db: AsyncSession, tenant_id: str, query: str = ""
aliases = r["aliases"] if isinstance(r["aliases"], list) else []
materials.append({
"name": r["name"],
"schaeffler_code": r["schaeffler_code"],
"hartomat_code": r["hartomat_code"],
"description": r["description"],
"aliases": aliases[:10], # cap to avoid token bloat
})
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Excel parser for Schaeffler CAD order lists.
Excel parser for HartOMat CAD order lists.
Supports two formats:
@@ -294,7 +294,7 @@ def _parse_material_mapping(wb) -> list[dict]:
def parse_excel(file_path: str | Path) -> ParsedExcel:
"""
Parse a Schaeffler order list Excel file.
Parse a HartOMat order list Excel file.
Returns a ParsedExcel with all data rows extracted.
Header-driven: finds "Ebene1" in any column within first 5 rows,
+2 -2
View File
@@ -25,9 +25,9 @@ def build_part_colors(
"""
Build {part_name: material_name} for Blender rendering.
Returns a mapping of part name → Schaeffler material name (e.g. SCHAEFFLER_010101_Steel-Bare).
Returns a mapping of part name → HartOMat material name (e.g. HARTOMAT_010101_Steel-Bare).
Parts with no material assignment are omitted; Blender will use the fallback material
(SCHAEFFLER_059999_FailedMaterial) for unrecognised parts.
(HARTOMAT_059999_FailedMaterial) for unrecognised parts.
Args:
cad_parsed_objects: List of part names from cad_file.parsed_objects["objects"].
+1 -1
View File
@@ -3,7 +3,7 @@ from celery.schedules import crontab
from app.config import settings
celery_app = Celery(
"schaefflerautomat",
"hartomat",
broker=settings.redis_url,
backend=settings.redis_url,
include=[
+3 -3
View File
@@ -1,4 +1,4 @@
"""Seed database with 7 Schaeffler product category templates."""
"""Seed database with 7 HartOMat product category templates."""
import asyncio
import uuid
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
@@ -133,7 +133,7 @@ TEMPLATES = [
]
async def seed(db_url: str, admin_email: str = "admin@schaeffler.com", admin_password: str = "Admin1234!"):
async def seed(db_url: str, admin_email: str = "admin@hartomat.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
@@ -162,7 +162,7 @@ async def seed(db_url: str, admin_email: str = "admin@schaeffler.com", admin_pas
email=admin_email,
password_hash=hash_password(admin_password),
role=UserRole.global_admin,
full_name="Schaeffler Admin",
full_name="HartOMat Admin",
)
session.add(admin)
print(f" + Admin user: {admin_email}")
+1 -1
View File
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
packages = ["app"]
[project]
name = "schaefflerautomat-backend"
name = "hartomat-backend"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
+2 -2
View File
@@ -1,5 +1,5 @@
"""
Pytest fixtures for the Schaeffler Automat backend test suite.
Pytest fixtures for the HartOMat backend test suite.
The tests in this suite are divided into:
- Unit tests (no DB / network required): excel_parser, models, schemas
@@ -122,7 +122,7 @@ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sess
TEST_DB_URL = os.environ.get(
"TEST_DATABASE_URL",
"postgresql+asyncpg://schaeffler:schaeffler@localhost:5432/schaeffler_test"
"postgresql+asyncpg://hartomat:hartomat@localhost:5432/hartomat_test"
)