feat: order, logging and try tutorial
This commit is contained in:
parent
060362ec09
commit
1d684b4d65
0
src/tutorial/files_and_path/order/__init__.py
Normal file
0
src/tutorial/files_and_path/order/__init__.py
Normal file
4253
src/tutorial/files_and_path/order/data/orders_1_valid.json
Normal file
4253
src/tutorial/files_and_path/order/data/orders_1_valid.json
Normal file
File diff suppressed because it is too large
Load Diff
4254
src/tutorial/files_and_path/order/data/orders_2_parse_error.json
Normal file
4254
src/tutorial/files_and_path/order/data/orders_2_parse_error.json
Normal file
File diff suppressed because it is too large
Load Diff
4163
src/tutorial/files_and_path/order/data/orders_3_encoding_error.json
Normal file
4163
src/tutorial/files_and_path/order/data/orders_3_encoding_error.json
Normal file
File diff suppressed because it is too large
Load Diff
4253
src/tutorial/files_and_path/order/data/orders_4_invalid_order.json
Normal file
4253
src/tutorial/files_and_path/order/data/orders_4_invalid_order.json
Normal file
File diff suppressed because it is too large
Load Diff
49
src/tutorial/files_and_path/order/load_files.py
Normal file
49
src/tutorial/files_and_path/order/load_files.py
Normal 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
|
||||||
45
src/tutorial/files_and_path/order/main.py
Normal file
45
src/tutorial/files_and_path/order/main.py
Normal 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()
|
||||||
225
src/tutorial/files_and_path/order/orders.log
Normal file
225
src/tutorial/files_and_path/order/orders.log
Normal 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
|
||||||
45
src/tutorial/files_and_path/order/utils.py
Normal file
45
src/tutorial/files_and_path/order/utils.py
Normal 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
|
||||||
115
src/tutorial/files_and_path/order/validation.py
Normal file
115
src/tutorial/files_and_path/order/validation.py
Normal 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."
|
||||||
|
)
|
||||||
Loading…
x
Reference in New Issue
Block a user