Compare commits
No commits in common. "master" and "uebung1" have entirely different histories.
149
orders.log
149
orders.log
File diff suppressed because one or more lines are too long
@ -3,4 +3,3 @@ black == 26.1.0
|
|||||||
pytest == 9.0.2
|
pytest == 9.0.2
|
||||||
pre-commit == 4.5.1
|
pre-commit == 4.5.1
|
||||||
|
|
||||||
pandas
|
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
def greet(name):
|
|
||||||
return f"Hello, {name} how are you doing today?"
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print(greet("John"))
|
|
||||||
@ -1,167 +0,0 @@
|
|||||||
"""
|
|
||||||
Codewars Kata:
|
|
||||||
https://www.codewars.com/kata/54fe05c4762e2e3047000add
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Ship:
|
|
||||||
def __init__(self, draft, crew):
|
|
||||||
self.draft = draft
|
|
||||||
self.crew = crew
|
|
||||||
|
|
||||||
def is_worth_it(self):
|
|
||||||
crew_weight = self.crew * 1.5
|
|
||||||
return self.draft - crew_weight > 20
|
|
||||||
|
|
||||||
|
|
||||||
Titanic = Ship(15, 10)
|
|
||||||
|
|
||||||
print(Titanic.is_worth_it())
|
|
||||||
|
|
||||||
"""
|
|
||||||
Codewars Kata:
|
|
||||||
https://www.codewars.com/kata/528d36d7cc451cd7e4000339
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Student:
|
|
||||||
all_students = []
|
|
||||||
|
|
||||||
def __init__(self, name, fives, tens, twenties):
|
|
||||||
self.name = name
|
|
||||||
self.fives = fives
|
|
||||||
self.tens = tens
|
|
||||||
self.twenties = twenties
|
|
||||||
Student.all_students.append(self)
|
|
||||||
|
|
||||||
def total_amount(self):
|
|
||||||
return self.fives * 5 + self.tens * 10 + self.twenties * 20
|
|
||||||
|
|
||||||
|
|
||||||
richest_student = None
|
|
||||||
multiple = False
|
|
||||||
|
|
||||||
for s in Student.all_students:
|
|
||||||
if richest_student is None:
|
|
||||||
richest_student = s
|
|
||||||
elif s.total_amount() > richest_student.total_amount():
|
|
||||||
richest_student = s
|
|
||||||
multiple = False
|
|
||||||
elif s.total_amount() == richest_student.total_amount():
|
|
||||||
multiple = True
|
|
||||||
|
|
||||||
if multiple:
|
|
||||||
print("all")
|
|
||||||
else:
|
|
||||||
print(richest_student.name)
|
|
||||||
|
|
||||||
"""
|
|
||||||
Codewars Kata:
|
|
||||||
https://www.codewars.com/kata/5a03af9606d5b65ff7000009
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class User:
|
|
||||||
def __init__(self, name: str, balance: int, checking_account: bool):
|
|
||||||
self.name = name
|
|
||||||
self.balance = balance
|
|
||||||
self.checking_account = checking_account
|
|
||||||
|
|
||||||
def withdraw(self, amount):
|
|
||||||
if amount > self.balance:
|
|
||||||
raise ValueError("overdraw")
|
|
||||||
self.balance -= amount
|
|
||||||
return f"{self.name} has {self.balance}."
|
|
||||||
|
|
||||||
def add_cash(self, amount):
|
|
||||||
self.balance += amount
|
|
||||||
return f"{self.name} has {self.balance}."
|
|
||||||
|
|
||||||
def check(self, issuer, amount):
|
|
||||||
if amount > issuer.balance:
|
|
||||||
raise ValueError("bad check")
|
|
||||||
if not issuer.checking_account:
|
|
||||||
raise ValueError("non checking-account")
|
|
||||||
self.balance += amount
|
|
||||||
issuer.balance -= amount
|
|
||||||
return f"{self.name} has {self.balance} and {issuer.name} has {issuer.balance}."
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Codewars Kata:
|
|
||||||
https://www.codewars.com/kata/5941c545f5c394fef900000c
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Warrior:
|
|
||||||
RANKS = [
|
|
||||||
("Greatest", 100),
|
|
||||||
("Master", 90),
|
|
||||||
("Champion", 80),
|
|
||||||
("Conqueror", 70),
|
|
||||||
("Elite", 60),
|
|
||||||
("Sage", 50),
|
|
||||||
("Veteran", 40),
|
|
||||||
("Warrior", 30),
|
|
||||||
("Fighter", 20),
|
|
||||||
("Novice", 10),
|
|
||||||
("Pushover", 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, level=1, rank="Pushover", experience=100, achievements=None):
|
|
||||||
self.level = level
|
|
||||||
self.rank = rank
|
|
||||||
self.experience = experience
|
|
||||||
self.achievements = achievements if achievements is not None else []
|
|
||||||
|
|
||||||
def battle(self, enemy):
|
|
||||||
if not enemy or enemy > 100:
|
|
||||||
return "Invalid level"
|
|
||||||
|
|
||||||
level_diff = enemy - self.level
|
|
||||||
rank_diff = (enemy // 10 + 1) - (self.level // 10 + 1)
|
|
||||||
if level_diff >= 5 and rank_diff >= 1:
|
|
||||||
return "You've been defeated"
|
|
||||||
else:
|
|
||||||
self.xp_won(level_diff)
|
|
||||||
if level_diff > 0:
|
|
||||||
return "An intense fight"
|
|
||||||
elif level_diff > -2:
|
|
||||||
return "A good fight"
|
|
||||||
else:
|
|
||||||
return "Easy fight"
|
|
||||||
|
|
||||||
def xp_won(self, diff: int):
|
|
||||||
xp_gained = 0
|
|
||||||
if diff == -1:
|
|
||||||
xp_gained = 5
|
|
||||||
elif diff == 0:
|
|
||||||
xp_gained = 10
|
|
||||||
elif diff > 0:
|
|
||||||
xp_gained = diff**2 * 20
|
|
||||||
|
|
||||||
if xp_gained:
|
|
||||||
self.add_xp(xp_gained)
|
|
||||||
|
|
||||||
def add_xp(self, xp_gained: int):
|
|
||||||
self.experience += xp_gained
|
|
||||||
|
|
||||||
if self.experience > 10000:
|
|
||||||
self.experience = 10000
|
|
||||||
|
|
||||||
if self.level != self.experience // 100:
|
|
||||||
self.level = self.experience // 100
|
|
||||||
|
|
||||||
for rank_name, max_level in self.RANKS:
|
|
||||||
if max_level <= self.level:
|
|
||||||
self.rank = rank_name
|
|
||||||
break
|
|
||||||
|
|
||||||
def training(self, training_arr):
|
|
||||||
description, xp_gained, min_level = training_arr
|
|
||||||
if self.level < min_level:
|
|
||||||
return "Not strong enough"
|
|
||||||
elif self.level >= min_level:
|
|
||||||
self.achievements.append(description)
|
|
||||||
self.add_xp(xp_gained)
|
|
||||||
return description
|
|
||||||
@ -1 +0,0 @@
|
|||||||
""" """
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
class Hund:
|
|
||||||
anzahl_hunde = 0
|
|
||||||
|
|
||||||
def __init__(self, name: str, rasse: str, alter: int, gewicht: float):
|
|
||||||
self.name = name
|
|
||||||
self.rasse = rasse
|
|
||||||
self.alter = alter
|
|
||||||
self.gewicht = gewicht
|
|
||||||
Hund.anzahl_hunde += 1
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"Hund(name='{self.name}', rasse='{self.rasse}',"
|
|
||||||
f"alter={self.alter}, gewicht={self.gewicht}kg"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name} ist ein {self.alter}-jähriger {self.rasse}"
|
|
||||||
|
|
||||||
def bellen(self, anzahl=1):
|
|
||||||
print("Wuff!" * anzahl)
|
|
||||||
return
|
|
||||||
|
|
||||||
def geburtstag(self):
|
|
||||||
self.alter += 1
|
|
||||||
print(
|
|
||||||
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
def ist_welpe(self):
|
|
||||||
return self.alter < 2
|
|
||||||
|
|
||||||
|
|
||||||
hund1 = Hund("Bello", "Labrador", 3, 28.5)
|
|
||||||
hund2 = Hund("BBarky", "Puddel", 4, 28.5)
|
|
||||||
|
|
||||||
print(repr(hund1))
|
|
||||||
print(hund1)
|
|
||||||
print(hund1.__dict__)
|
|
||||||
print(hund1.ist_welpe())
|
|
||||||
@ -1,121 +0,0 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class Hund(ABC):
|
|
||||||
anzahl_hunde = 0
|
|
||||||
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float):
|
|
||||||
self.name = name
|
|
||||||
self.alter = alter
|
|
||||||
self.gewicht = gewicht
|
|
||||||
Hund.anzahl_hunde += 1
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"Hund(name='{self.name}', rasse='{self.rasse}',"
|
|
||||||
f"alter={self.alter}, gewicht={self.gewicht}kg"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name} ist ein {self.alter}-jähriger {self.rasse}"
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def bellen(self, anzahl: int = 1) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def geburtstag(self):
|
|
||||||
self.alter += 1
|
|
||||||
print(
|
|
||||||
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class Pudel(Hund):
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float):
|
|
||||||
super().__init__(name, alter, gewicht)
|
|
||||||
self.rasse = "Pudel"
|
|
||||||
self.laut = "Wauw!"
|
|
||||||
|
|
||||||
def bellen(self, anzahl: int = 1) -> None:
|
|
||||||
print(self.laut * anzahl)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class Labrador(Hund):
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float):
|
|
||||||
super().__init__(name, alter, gewicht)
|
|
||||||
self.rasse = "Labrador"
|
|
||||||
self.laut = "Wuff!"
|
|
||||||
|
|
||||||
def bellen(self, anzahl: int = 1) -> None:
|
|
||||||
print(self.laut * anzahl)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class Bulldog(Hund):
|
|
||||||
def __init___(self, name: str, alter: int, gewicht: float):
|
|
||||||
super().__init__(name, alter, gewicht)
|
|
||||||
self.rasse = "Bulldog"
|
|
||||||
self.laut = "Wuffi!"
|
|
||||||
|
|
||||||
def bellen(self, anzahl: int = 1) -> None:
|
|
||||||
print(self.laut * anzahl)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
ohne abstract method
|
|
||||||
class Hund:
|
|
||||||
anzahl_hunde = 0
|
|
||||||
|
|
||||||
def __init__ (self, name: str, alter: int, gewicht: float):
|
|
||||||
self.name = name
|
|
||||||
self.alter = alter
|
|
||||||
self.gewicht = gewicht
|
|
||||||
Hund.anzahl_hunde += 1
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"Hund(name='{self.name}', rasse='{self.rasse}',"
|
|
||||||
f"alter={self.alter}, gewicht={self.gewicht}kg"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name} ist ein {self.alter}-jähriger {self.rasse}"
|
|
||||||
|
|
||||||
def bellen(self, anzahl=1):
|
|
||||||
if not self.laut:
|
|
||||||
print("Wuff!" * anzahl)
|
|
||||||
else:
|
|
||||||
print(self.laut * anzahl)
|
|
||||||
return
|
|
||||||
|
|
||||||
def geburtstag(self):
|
|
||||||
self.alter += 1
|
|
||||||
print(
|
|
||||||
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class Pudel(Hund):
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float):
|
|
||||||
super().__init__(name, alter, gewicht)
|
|
||||||
self.rasse = "Pudel"
|
|
||||||
self.laut = "Wauw!"
|
|
||||||
|
|
||||||
class Labrador(Hund):
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float):
|
|
||||||
super().__init__(name, alter, gewicht)
|
|
||||||
self.rasse = "Labrador"
|
|
||||||
self.laut = "Wuff!"
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
hund1 = Pudel("Bello", 1, 2)
|
|
||||||
hund2 = Labrador("Bello", 4, 3)
|
|
||||||
hund3 = Bulldog("Bello", 4, 3)
|
|
||||||
|
|
||||||
hund1.bellen(3)
|
|
||||||
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
@ -1,49 +0,0 @@
|
|||||||
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
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
"""
|
|
||||||
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")
|
|
||||||
# logger = setup_logger()
|
|
||||||
|
|
||||||
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()
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
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
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
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."
|
|
||||||
)
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
# Task: Schreibt für die folgende Aufgabe einige Unit-Test. Nutzt dazu für den Happy-Path einen parametrisierten Test für
|
|
||||||
# einige valide Inputs und Outputs
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_bmi(weight_kg: float, height_m: float) -> float:
|
|
||||||
"""
|
|
||||||
Berechnet den Body Mass Index (BMI).
|
|
||||||
BMI = Gewicht (kg) / Grösse (m)^2
|
|
||||||
"""
|
|
||||||
if weight_kg <= 0 or height_m <= 0:
|
|
||||||
raise ValueError("Gewicht und Grösse müssen positiv sein.")
|
|
||||||
return weight_kg / (height_m**2)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print(calculate_bmi(weight_kg=72, height_m=1.84))
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
# TASK: Schreibe sinnvolle Tests für die folgende Funktion
|
|
||||||
# Schreibe Tests für die korrekte Struktur des Outputs der Funktion
|
|
||||||
# Teste, ob bei Übergabe von 'falschem' Parameter data eine Exception geworfen wird
|
|
||||||
|
|
||||||
# https://docs.python.org/3/library/exceptions.html
|
|
||||||
# Passing arguments of the wrong type (e.g. passing a list when an int is expected) should result in a TypeError,
|
|
||||||
# but passing arguments with the wrong value (e.g. a number outside expected boundaries) should result in a ValueError.
|
|
||||||
|
|
||||||
|
|
||||||
def double_integers(data: list[int]) -> list[int]:
|
|
||||||
"""Doubles a list of given integers
|
|
||||||
|
|
||||||
:param data: list of integers
|
|
||||||
:return: list of doubled integers
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not isinstance(data, list):
|
|
||||||
raise TypeError("data must be a list")
|
|
||||||
if not all([isinstance(i, int) for i in data]):
|
|
||||||
raise TypeError("data may contain only integers")
|
|
||||||
|
|
||||||
return [i * 2 for i in data]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
data = [1, 2, 3]
|
|
||||||
result = double_integers(data)
|
|
||||||
print(result)
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
# https://www.codewars.com/kata/53dbd5315a3c69eed20002dd
|
|
||||||
# level: 7 kyu
|
|
||||||
|
|
||||||
# In this kata you will create a function that takes a list of non-negative integers and strings and returns a new
|
|
||||||
# list with the strings filtered out.
|
|
||||||
#
|
|
||||||
# Example
|
|
||||||
# filter_list([1,2,'a','b']) == [1,2]
|
|
||||||
# filter_list([1,'a','b',0,15]) == [1,0,15]
|
|
||||||
# filter_list([1,2,'aasf','1','123',123]) == [1,2,123]
|
|
||||||
|
|
||||||
|
|
||||||
def filter_list(list):
|
|
||||||
return [i for i in list if isinstance(i, int)]
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
# https://www.codewars.com/kata/5ea2baed9345eb001e8ce394
|
|
||||||
# 7 kyu
|
|
||||||
|
|
||||||
# Input parameters
|
|
||||||
|
|
||||||
# dataframe: pandas.DataFrame object
|
|
||||||
# col: target column
|
|
||||||
# func: filter function
|
|
||||||
|
|
||||||
# Task
|
|
||||||
# Your function must return a new pandas.DataFrame object with the same columns as the original input. However,
|
|
||||||
# include only the rows whose cell values in the designated column evaluate to False by func.
|
|
||||||
#
|
|
||||||
# Input DataFrame will never be empty. The target column will always be one of the dataframe columns. Filter function
|
|
||||||
# will be a valid one. Index value must remain the same.
|
|
||||||
|
|
||||||
# REMARK: leichte Modifikation -> es wird ausgegeben, was in der Funktion definiert wurde (True) und nicht umgekehrt.
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
|
|
||||||
def filter_dataframe(df, col, func):
|
|
||||||
if col not in df.columns:
|
|
||||||
raise ValueError(f"Column '{col}' is not present in the DataFrame.")
|
|
||||||
mask = df[col].apply(func)
|
|
||||||
return df[mask]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
|
|
||||||
df = pd.DataFrame(
|
|
||||||
{
|
|
||||||
"A": [1, 2, 3, 4, 5],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
print(filter_dataframe(df, "A", lambda x: x >= 4))
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# https://www.codewars.com/kata/54ff3102c1bad923760001f3
|
|
||||||
# level: 7 kyu
|
|
||||||
|
|
||||||
# Return the number (count) of vowels in the given string.
|
|
||||||
# We will consider a, e, i, o, u as vowels for this Kata (but not y).
|
|
||||||
# The input string will only consist of lower case letters and/or spaces.
|
|
||||||
|
|
||||||
|
|
||||||
def get_count(inputStr):
|
|
||||||
num_vowels = 0
|
|
||||||
for char in inputStr:
|
|
||||||
if char in "aeiouAEIOU":
|
|
||||||
num_vowels = num_vowels + 1
|
|
||||||
return num_vowels
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print(get_count("Hello World"))
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
def discount_price(price: float, percent: float) -> float:
|
|
||||||
if percent < 0:
|
|
||||||
raise ValueError("Discount percent cannot be negative")
|
|
||||||
if percent >= 100:
|
|
||||||
raise ValueError("Percent must be between 0% and 100%")
|
|
||||||
|
|
||||||
return price - price * percent / 100
|
|
||||||
|
|
||||||
|
|
||||||
print(discount_price(100.0, 20.0))
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
from src.u1_moduleA import addition
|
from src.moduleA import addition
|
||||||
|
|
||||||
|
|
||||||
def test_a():
|
def test_a():
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
from src.u6_tests.pricing import discount_price
|
|
||||||
|
|
||||||
|
|
||||||
def test_discount_price_reduces_price():
|
|
||||||
result = discount_price(100.0, 20.0)
|
|
||||||
assert result == 80.0
|
|
||||||
|
|
||||||
|
|
||||||
def test_discount_price_negative_discount():
|
|
||||||
try:
|
|
||||||
discount_price(100.0, -20.0)
|
|
||||||
assert False, "Expected ValueError"
|
|
||||||
except ValueError as e:
|
|
||||||
assert str(e) == "Discount percent cannot be negative"
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
from src.u6_tests.U6_Freitag_testing.catching_exeptions import double_integers
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_double_integers_with_invalid_input():
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
double_integers("not a list")
|
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
double_integers([1, 2, "not an int"])
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input_data, expected_output",
|
|
||||||
[([1, 2, 3], [2, 4, 6]), ([0, -1, -2], [0, -2, -4]), ([], [])],
|
|
||||||
)
|
|
||||||
def test_double_integers(input_data, expected_output):
|
|
||||||
assert double_integers(input_data) == expected_output
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from src.u6_tests.U6_Freitag_testing.kata_list_filtering import filter_list
|
|
||||||
|
|
||||||
|
|
||||||
def test_filter_list_with_mixed_types():
|
|
||||||
result = filter_list([1, 2, "a", "b"])
|
|
||||||
assert result == [1, 2]
|
|
||||||
|
|
||||||
|
|
||||||
def test_filter_list_with_only_strings():
|
|
||||||
result = filter_list(["a", "b", "c"])
|
|
||||||
assert result == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_filter_list_with_only_integers():
|
|
||||||
result = filter_list([1, 2, 3])
|
|
||||||
assert result == [1, 2, 3]
|
|
||||||
|
|
||||||
|
|
||||||
def test_filter_list_output_is_list():
|
|
||||||
result = filter_list([1, "a", 2])
|
|
||||||
assert isinstance(result, list)
|
|
||||||
|
|
||||||
|
|
||||||
def test_filter_list_assert_length():
|
|
||||||
|
|
||||||
l1 = [1, "a", 2, "b", 3]
|
|
||||||
l2 = [1, 2, 3]
|
|
||||||
l3 = [1 / 2, "a", 2.5, "b", 3]
|
|
||||||
|
|
||||||
result1 = filter_list(l1)
|
|
||||||
result2 = filter_list(l2)
|
|
||||||
result3 = filter_list(l3)
|
|
||||||
|
|
||||||
assert len(result1) == 3
|
|
||||||
assert len(result2) == 3
|
|
||||||
assert len(result3) == 1
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"input_list, expected_length",
|
|
||||||
[([1, "a", 2, "b", 3], 3), ([1, 2, 3], 3), ([1 / 2, "a", 2.5, "b", 3], 1)],
|
|
||||||
)
|
|
||||||
def test_filter_list_parametrized(input_list, expected_length):
|
|
||||||
result = filter_list(input_list)
|
|
||||||
assert len(result) == expected_length
|
|
||||||
Loading…
x
Reference in New Issue
Block a user