feat: order, logging and try tutorial

This commit is contained in:
Sandro Zimmermann 2026-03-23 18:04:15 +01:00
parent 060362ec09
commit 1d684b4d65
10 changed files with 17402 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
import json
import logging
from pathlib import Path
logger = logging.getLogger(f"orders.{__name__}") # → "orders.load_files"
# ══════════════════════════════════════════════════════════════════════════════
# JSON-File einlesen
# ══════════════════════════════════════════════════════════════════════════════
def load_orders(path: str | Path) -> list[dict] | None:
"""
Liest eine JSON-Datei mit Bestellungen ein.
Behandelte Fehler
-----------------
UnicodeDecodeError Falsche Kodierung (z. B. Latin-1 statt UTF-8)
json.JSONDecodeError Ungültiges JSON (Syntaxfehler)
Rückgabe
--------
Liste der Bestellungen bei Erfolg, None bei Fehler.
"""
path = Path(path)
logger.info("Lese Datei: %s", path)
try:
with path.open("r", encoding="utf-8") as f:
loaded_file = json.load(f)
logger.info(f"{path} erfolgreich eingelesen")
return loaded_file
except FileNotFoundError:
logger.warning("Datei nicht gefunden: %s", path)
return None
except json.JSONDecodeError:
logger.warning(f"Konnte Datei {path} nicht decodieren")
return None
except UnicodeDecodeError as e:
logger.warning(f"Datei {path} scheint ein falsches Coding zu haben")
logger.debug(
f"UnicodeDecodeError-Details: "
f"encoding={e.encoding}, "
f"reason={e.reason}, "
f"start={e.start}, "
f"end={e.end}, "
f"bad_bytes={hex(e.object[e.start])}"
)
return None

View File

@ -0,0 +1,45 @@
"""
main.py Bestellungen einlesen und validieren
Demonstriert:
- Standard-Logging (logging-Modul) mit FileHandler + StreamHandler
- Sauberes Exception-Handling für UnicodeDecodeError & json.JSONDecodeError
- Eigene Exception-Klasse InvalidOrderError (erbt von ValueError)
"""
from pathlib import Path
from utils import setup_logger_extended
from load_files import load_orders
from validation import process_orders
def main() -> None:
logger = setup_logger_extended("orders")
files = [
"orders_1_valid.json",
"orders_5_non_existing_file.json",
"orders_2_parse_error.json",
"orders_3_encoding_error.json",
"orders_4_invalid_order.json",
]
for filename in files:
BASE_DIR = Path(__file__).parent
file_path = BASE_DIR / "data" / filename
logger.info("=" * 60)
logger.info("Verarbeite: %s", filename)
orders = load_orders(file_path)
if orders is not None:
process_orders(orders)
logger.info("=" * 60)
logger.info(f"Alle {len(files)} Dateien verarbeitet. Details siehe orders.log")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,225 @@
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Verarbeite: orders_1_valid.json
2026-03-23 17:54:26 | INFO | orders.load_files | Lese Datei: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_1_valid.json
2026-03-23 17:54:26 | INFO | orders.load_files | /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_1_valid.json erfolgreich eingelesen
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00001
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00002
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00003
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00004
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00005
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00006
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00007
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00008
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00009
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00010
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00011
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00012
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00013
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00014
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00015
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00016
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00017
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00018
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00019
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00020
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00021
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00022
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00023
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00024
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00025
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00026
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00027
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00028
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00029
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00030
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00031
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00032
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00033
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00034
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00035
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00036
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00037
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00038
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00039
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00040
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00041
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00042
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00043
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00044
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00045
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00046
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00047
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00048
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00049
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00050
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00051
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00052
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00053
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00054
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00055
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00056
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00057
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00058
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00059
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00060
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00061
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00062
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00063
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00064
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00065
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00066
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00067
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00068
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00069
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00070
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00071
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00072
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00073
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00074
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00075
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00076
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00077
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00078
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00079
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00080
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00081
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00082
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00083
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00084
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00085
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00086
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00087
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00088
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00089
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00090
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00091
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00092
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00093
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00094
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00095
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00096
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00097
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00098
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00099
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00100
2026-03-23 17:54:26 | INFO | orders.validation | Validierung abgeschlossen: 100 gültig, 0 ungültig.
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Verarbeite: orders_5_non_existing_file.json
2026-03-23 17:54:26 | INFO | orders.load_files | Lese Datei: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_5_non_existing_file.json
2026-03-23 17:54:26 | WARNING | orders.load_files | Datei nicht gefunden: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_5_non_existing_file.json
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Verarbeite: orders_2_parse_error.json
2026-03-23 17:54:26 | INFO | orders.load_files | Lese Datei: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_2_parse_error.json
2026-03-23 17:54:26 | WARNING | orders.load_files | Konnte Datei /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_2_parse_error.json nicht decodieren
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Verarbeite: orders_3_encoding_error.json
2026-03-23 17:54:26 | INFO | orders.load_files | Lese Datei: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_3_encoding_error.json
2026-03-23 17:54:26 | WARNING | orders.load_files | Datei /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_3_encoding_error.json scheint ein falsches Coding zu haben
2026-03-23 17:54:26 | DEBUG | orders.load_files | UnicodeDecodeError-Details: encoding=utf-8, reason=invalid start byte, start=118, end=119, bad_bytes=0xfc
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Verarbeite: orders_4_invalid_order.json
2026-03-23 17:54:26 | INFO | orders.load_files | Lese Datei: /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_4_invalid_order.json
2026-03-23 17:54:26 | INFO | orders.load_files | /Users/s/workspace/gittea.fhgr.ch/zimmersandro/ppe2/src/tutorial/files_and_path/order/data/orders_4_invalid_order.json erfolgreich eingelesen
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00001
2026-03-23 17:54:26 | WARNING | orders.validation | Ungültige Bestellung — order_id=ORD-00002, feld=qty, wert=-3: Negative Menge (-3) ist nicht erlaubt.
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00003
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00004
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00005
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00006
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00007
2026-03-23 17:54:26 | WARNING | orders.validation | Ungültige Bestellung — order_id=ORD-00008, feld=qty, wert=-4: Negative Menge (-4) ist nicht erlaubt.
2026-03-23 17:54:26 | WARNING | orders.validation | Ungültige Bestellung — order_id=ORD-00009, feld=total_chf, wert=-12.75: Negativer Gesamtbetrag (-12.75 CHF) ist nicht erlaubt.
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00010
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00011
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00012
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00013
2026-03-23 17:54:26 | WARNING | orders.validation | Ungültige Bestellung — order_id=ORD-00014, feld=qty, wert=-2: Negative Menge (-2) ist nicht erlaubt.
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00015
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00016
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00017
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00018
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00019
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00020
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00021
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00022
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00023
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00024
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00025
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00026
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00027
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00028
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00029
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00030
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00031
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00032
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00033
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00034
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00035
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00036
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00037
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00038
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00039
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00040
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00041
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00042
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00043
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00044
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00045
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00046
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00047
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00048
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00049
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00050
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00051
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00052
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00053
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00054
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00055
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00056
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00057
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00058
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00059
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00060
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00061
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00062
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00063
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00064
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00065
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00066
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00067
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00068
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00069
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00070
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00071
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00072
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00073
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00074
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00075
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00076
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00077
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00078
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00079
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00080
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00081
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00082
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00083
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00084
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00085
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00086
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00087
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00088
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00089
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00090
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00091
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00092
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00093
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00094
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00095
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00096
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00097
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00098
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00099
2026-03-23 17:54:26 | DEBUG | orders.validation | OK: ORD-00100
2026-03-23 17:54:26 | INFO | orders.validation | Validierung abgeschlossen: 96 gültig, 4 ungültig.
2026-03-23 17:54:26 | INFO | orders | ============================================================
2026-03-23 17:54:26 | INFO | orders | Alle 5 Dateien verarbeitet. Details siehe orders.log

View File

@ -0,0 +1,45 @@
import sys
import logging
# ══════════════════════════════════════════════════════════════════════════════
# Logging-Konfiguration
# ══════════════════════════════════════════════════════════════════════════════
LOG_FORMAT = "%(asctime)s | %(levelname)-8s | %(name)-20s | %(message)s"
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
def setup_logger_extended(name: str, log_file: str = "orders.log") -> logging.Logger:
"""
Erstellt und konfiguriert einen Logger mit zwei Handlern:
- StreamHandler Ausgabe auf die Konsole (ab INFO)
- FileHandler Ausgabe in eine Log-Datei (ab DEBUG)
"""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG) # Root-Level: alles durchlassen
# Konsole: INFO und höher
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(logging.Formatter(LOG_FORMAT, DATE_FORMAT))
# Log-File: DEBUG und höher (detaillierter)
file_handler = logging.FileHandler(log_file, encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter(LOG_FORMAT, DATE_FORMAT))
logger.addHandler(stream_handler)
logger.addHandler(file_handler)
return logger
def setup_logger(name: str = None, log_file: str = "orders.log") -> logging.Logger:
logging.basicConfig(
level=logging.INFO,
format="%(levelname)s %(name)s %(message)s",
)
logger = logging.getLogger(__name__)
return logger

View File

@ -0,0 +1,115 @@
import logging
logger = logging.getLogger(f"orders.{__name__}") # → "orders.validation"
# ══════════════════════════════════════════════════════════════════════════════
# Eigene Exception-Klasse
# ══════════════════════════════════════════════════════════════════════════════
class InvalidOrderError(ValueError):
"""
Wird ausgelöst, wenn eine Bestellung ungültige Geschäftsdaten enthält.
Erbt von ValueError, weil es sich um einen inhaltlichen Wertfehler handelt
(kein technisches I/O-Problem).
Attribute
---------
order_id : str
Die ID der fehlerhaften Bestellung.
field : str
Der Name des ungültigen Feldes (z. B. "qty").
value : object
Der tatsächliche (ungültige) Wert.
message : str
Lesbare Fehlerbeschreibung (auch als str(e) verfügbar).
"""
def __init__(self, order_id: str, field: str, value: object, message: str):
self.order_id = order_id
self.field = field
self.value = value
self.message = message
super().__init__(message)
def __str__(self) -> str:
return (
f"InvalidOrderError | order_id={self.order_id!r} "
f"| field={self.field!r} | value={self.value!r} "
f"| {self.message}"
)
# ══════════════════════════════════════════════════════════════════════════════
# Geschäftslogik: Validierung einer einzelnen Bestellung
# ══════════════════════════════════════════════════════════════════════════════
def validate_order(order: dict) -> None:
"""
Prüft eine einzelne Bestellung auf Plausibilität.
Wirft InvalidOrderError, sobald ein Regelverstoß entdeckt wird.
Regeln (erweiterbar):
- qty darf nicht negativ sein
- total_chf darf nicht negativ sein
- order_id und status müssen vorhanden sein
"""
order_id = order.get("order_id", "<unbekannt>")
for item in order.get("items", []):
qty = item.get("qty")
if qty is not None and qty < 0:
raise InvalidOrderError(
order_id=order_id,
field="qty",
value=qty,
message=f"Negative Menge ({qty}) ist nicht erlaubt.",
)
total = order.get("total_chf")
if total is not None and total < 0:
raise InvalidOrderError(
order_id=order_id,
field="total_chf",
value=total,
message=f"Negativer Gesamtbetrag ({total} CHF) ist nicht erlaubt.",
)
if not order.get("order_id"):
raise InvalidOrderError(
order_id="<unbekannt>",
field="order_id",
value=order.get("order_id"),
message="Pflichtfeld 'order_id' fehlt oder ist leer.",
)
# ══════════════════════════════════════════════════════════════════════════════
# Validierungs-Durchlauf über alle Bestellungen
# ══════════════════════════════════════════════════════════════════════════════
def process_orders(orders: list[dict]) -> None:
"""
Iteriert über alle Bestellungen und validiert jede einzelne.
Ungültige Bestellungen werden geloggt und übersprungen (kein Abbruch).
"""
valid_count = 0
invalid_count = 0
for order in orders.get("orders"):
try:
validate_order(order)
valid_count += 1
logger.debug("OK: %s", order.get("order_id"))
except InvalidOrderError as e:
invalid_count += 1
logger.warning(
f"Ungültige Bestellung — order_id={e.order_id}, feld={e.field}, wert={e.value}: {e.message}"
)
logger.info(
f"Validierung abgeschlossen: {valid_count} gültig, {invalid_count} ungültig."
)