- Updated the source_unit_choices_for_field function to include a custom option for user-defined conversion factors, improving flexibility in unit conversions. - Modified the AdminCsvTemplateEditorPage to support custom conversion factors, allowing users to input specific scaling factors for their data. - Added tests to ensure the custom option is correctly included in the source unit choices and functions as expected in the template editor.
116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
"""
|
|
Kanonische Speichereinheiten pro CSV-Zielfeld (module_registry: field.unit) und
|
|
abwählbare Quelleinheiten → Faktor für type_conversions.source_unit.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from csv_parser.module_registry import get_module_definition
|
|
|
|
# 1 kcal = 4.184 kJ (IEC/ISO 80000)
|
|
_KJ_TO_KCAL = 1.0 / 4.184
|
|
|
|
# — Energie (Ziel kcal) —
|
|
_ENERGY: list[dict[str, Any]] = [
|
|
{"id": "kcal", "label": "Kilokalorien (kcal), wie in DB", "factor": 1.0},
|
|
{"id": "kj", "label": "Kilojoule (kJ) → kcal", "factor": _KJ_TO_KCAL},
|
|
{"id": "j", "label": "Joule (J) → kcal", "factor": _KJ_TO_KCAL / 1000.0},
|
|
]
|
|
|
|
# — Masse klein (Ziel g): Makronährstoffe —
|
|
_GRAM: list[dict[str, Any]] = [
|
|
{"id": "g", "label": "Gramm (g), wie in DB", "factor": 1.0},
|
|
{"id": "kg", "label": "Kilogramm (kg) → g", "factor": 1000.0},
|
|
{"id": "mg", "label": "Milligramm (mg) → g", "factor": 0.001},
|
|
]
|
|
|
|
# — Körpergewicht (Ziel kg) —
|
|
_KG: list[dict[str, Any]] = [
|
|
{"id": "kg", "label": "Kilogramm (kg), wie in DB", "factor": 1.0},
|
|
{"id": "g", "label": "Gramm (g) → kg", "factor": 0.001},
|
|
{"id": "lb", "label": "Pfund / lb → kg", "factor": 0.45359237},
|
|
{"id": "oz", "label": "Unze / oz → kg", "factor": 0.028349523125},
|
|
]
|
|
|
|
# — Strecke (Ziel km) —
|
|
_KM: list[dict[str, Any]] = [
|
|
{"id": "km", "label": "Kilometer (km), wie in DB", "factor": 1.0},
|
|
{"id": "m", "label": "Meter (m) → km", "factor": 0.001},
|
|
{"id": "mi", "label": "Meilen (mi) → km", "factor": 1.609344},
|
|
]
|
|
|
|
_UNIT_FAMILY: dict[str, list[dict[str, Any]]] = {
|
|
"kcal": _ENERGY,
|
|
"g": _GRAM,
|
|
"kg": _KG,
|
|
"km": _KM,
|
|
}
|
|
|
|
|
|
def get_canonical_unit(module: str, db_field: str) -> str | None:
|
|
mod = get_module_definition(module)
|
|
if not mod:
|
|
return None
|
|
finfo: dict[str, Any] | None = mod.get("fields", {}).get(db_field)
|
|
if not finfo:
|
|
return None
|
|
u = finfo.get("unit")
|
|
return str(u) if u else None
|
|
|
|
|
|
def source_unit_choices_for_field(module: str, db_field: str) -> list[dict[str, Any]]:
|
|
"""Optionen für GUI: id, label, canonical_unit, is_canonical (Umrechnung serverseitig)."""
|
|
cu = get_canonical_unit(module, db_field)
|
|
if not cu:
|
|
return []
|
|
choices = _UNIT_FAMILY.get(cu)
|
|
if not choices:
|
|
return []
|
|
out: list[dict[str, Any]] = [
|
|
{
|
|
"id": c["id"],
|
|
"label": c["label"],
|
|
"canonical_unit": cu,
|
|
"is_canonical": c["id"] == cu,
|
|
}
|
|
for c in choices
|
|
]
|
|
out.append(
|
|
{
|
|
"id": "custom",
|
|
"label": "Benutzerdefiniert (Konvertierungsfaktor, z. B. ml→g je nach Dichte)",
|
|
"canonical_unit": cu,
|
|
"is_canonical": False,
|
|
}
|
|
)
|
|
return out
|
|
|
|
|
|
def factor_source_to_canonical(module: str, db_field: str, source_unit: str | None) -> float:
|
|
"""
|
|
Multiplikator: CSV-Zahl * Faktor → Wert in kanonischer DB-Einheit.
|
|
Unbekannte/None/leer/Passthrough → 1.0
|
|
|
|
``source_unit`` ``custom`` / ``none``: kein Registry-Faktor (1.0); freie Skalierung nur über
|
|
``conversion_factor`` in type_conversions (JSON).
|
|
"""
|
|
if source_unit is None:
|
|
return 1.0
|
|
su = str(source_unit).strip().lower()
|
|
if not su:
|
|
return 1.0
|
|
if su in ("custom", "none"):
|
|
return 1.0
|
|
cu = get_canonical_unit(module, db_field)
|
|
if not cu:
|
|
return 1.0
|
|
choices = _UNIT_FAMILY.get(cu)
|
|
if not choices:
|
|
return 1.0
|
|
for c in choices:
|
|
if str(c["id"]).lower() == su:
|
|
return float(c["factor"])
|
|
return 1.0
|