first commit
This commit is contained in:
108
webapp/services/blueprints.py
Normal file
108
webapp/services/blueprints.py
Normal file
@@ -0,0 +1,108 @@
|
||||
from __future__ import annotations
|
||||
import csv
|
||||
import os
|
||||
import json
|
||||
from typing import Optional, Dict
|
||||
from flask import current_app
|
||||
|
||||
# Cache: "name in lowercase" -> type_id
|
||||
_BP_NAME2ID: Dict[str, int] = {}
|
||||
# Cache: product type id -> (product name)
|
||||
_ID2NAME: Dict[int, str] = {}
|
||||
# Optional benutzerdefinierte Überschreibungen
|
||||
# Format: { "Structure Market Network": 123456, ... }
|
||||
_USER_MAP: Dict[str, int] = {}
|
||||
|
||||
|
||||
def _data_path(filename: str) -> str:
|
||||
return os.path.join(current_app.root_path, "data", filename)
|
||||
|
||||
|
||||
def _ensure_loaded() -> None:
|
||||
"""Liest typeIDs.csv einmalig ein und baut schnelle Nachschlage-Maps auf."""
|
||||
global _BP_NAME2ID, _ID2NAME, _USER_MAP
|
||||
if _BP_NAME2ID and _ID2NAME:
|
||||
return
|
||||
|
||||
# Benutzer-Overrides (optional)
|
||||
try:
|
||||
override = _data_path("blueprints.json")
|
||||
if os.path.isfile(override):
|
||||
with open(override, "r", encoding="utf-8") as f:
|
||||
_USER_MAP = json.load(f) or {}
|
||||
except Exception:
|
||||
_USER_MAP = {}
|
||||
|
||||
csv_path = _data_path("typeIDs.csv")
|
||||
if not os.path.isfile(csv_path):
|
||||
return
|
||||
|
||||
try:
|
||||
with open(csv_path, "r", encoding="utf-8", newline="") as f:
|
||||
# Datei ist ;-separiert: id;name;group_id;iconID;graphicID
|
||||
reader = csv.reader(f, delimiter=";")
|
||||
header = next(reader, None) # "id;name;group_id;iconID;graphicID"
|
||||
for row in reader:
|
||||
if not row or len(row) < 2:
|
||||
continue
|
||||
try:
|
||||
tid = int(row[0])
|
||||
except Exception:
|
||||
continue
|
||||
name = (row[1] or "").strip()
|
||||
if not name:
|
||||
continue
|
||||
_ID2NAME[tid] = name
|
||||
key = name.lower()
|
||||
# Merke auch die Blaupause separat (Name endet exakt auf " Blueprint")
|
||||
if key.endswith(" blueprint"):
|
||||
_BP_NAME2ID[key] = tid
|
||||
except Exception:
|
||||
# Map bleibt ggf. leer; Aufrufer bekommen dann None zurück
|
||||
pass
|
||||
|
||||
|
||||
def product_name_by_id(type_id: int) -> Optional[str]:
|
||||
"""Gibt den Produktnamen zur TypeID zurück (aus typeIDs.csv)."""
|
||||
_ensure_loaded()
|
||||
return _ID2NAME.get(type_id)
|
||||
|
||||
|
||||
def resolve_blueprint_id(product_name: str) -> Optional[int]:
|
||||
"""
|
||||
Ermittelt die Blueprint-TypeID zu einem *Produktnamen*.
|
||||
Regeln:
|
||||
1) Benutzer-Override (blueprints.json)
|
||||
2) Exakter Treffer "<Produktname> Blueprint" in typeIDs.csv
|
||||
3) Weicher Treffer (enthält Produktname & 'blueprint')
|
||||
"""
|
||||
if not product_name:
|
||||
return None
|
||||
|
||||
_ensure_loaded()
|
||||
|
||||
# 1) User-Override
|
||||
if product_name in _USER_MAP:
|
||||
return int(_USER_MAP[product_name])
|
||||
|
||||
wanted_exact = f"{product_name} Blueprint".lower()
|
||||
|
||||
# 2) Exakter Treffer
|
||||
if wanted_exact in _BP_NAME2ID:
|
||||
return _BP_NAME2ID[wanted_exact]
|
||||
|
||||
# 3) Weiche Suche
|
||||
prod_l = product_name.lower()
|
||||
for k, tid in _BP_NAME2ID.items():
|
||||
if prod_l in k and "blueprint" in k:
|
||||
return tid
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def resolve_blueprint_id_by_product_id(product_type_id: int) -> Optional[int]:
|
||||
"""Ermittelt die Blueprint-TypeID aus der *Produkt*-TypeID."""
|
||||
name = product_name_by_id(int(product_type_id))
|
||||
if not name:
|
||||
return None
|
||||
return resolve_blueprint_id(name)
|
||||
Reference in New Issue
Block a user