Compare commits
No commits in common. "6b28a6ab1d4407e563be5578e8cb49a6282e4a1b" and "f0af4d7dd223c4fe74d91cb36f0ad75b6bc5448c" have entirely different histories.
6b28a6ab1d
...
f0af4d7dd2
@ -15,9 +15,9 @@ Repository for CDS-2020 Programming and Promt Engineering II
|
|||||||
|build|Build-System, Dependencies|build: update requirements.txt|
|
|build|Build-System, Dependencies|build: update requirements.txt|
|
||||||
|
|
||||||
# Codewars
|
# Codewars
|
||||||
|Title|Source (src/codewars/)|Test (test/test_codewars/)|URL|
|
|Title|Source (src/codewars/)|Test (test/codewars/)|URL|
|
||||||
|-|-|-|-|
|
|-|-|-|-|
|
||||||
|Find the force of gravity between two objects|kata_force_of_gravity.py|test_force_of_gravity.py|[5b609ebc8f47bd595e000627](https://www.codewars.com/kata/5b609ebc8f47bd595e000627)|
|
|Find the force of gravity between two objects|kata_force_of_gravity.py|test_force_of_gravity.py|[5b609ebc8f47bd595e000627](https://www.codewars.com/kata/5b609ebc8f47bd595e000627/)|
|
||||||
|The Lamp: Revisited|kata_the_lamp.py|test_the_lamp.py|[570e6e32de4dc8a8340016dd](https://www.codewars.com/kata/570e6e32de4dc8a8340016dd)|
|
|The Lamp: Revisited|kata_the_lamp.py|test_the_lamp.py|[570e6e32de4dc8a8340016dd](https://www.codewars.com/kata/570e6e32de4dc8a8340016dd)|
|
||||||
|OOP: Object Oriented Piracy|kata_object_oriented_piracy.py|test_object_oriented_piracy.py|[54fe05c4762e2e3047000add](https://www.codewars.com/kata/54fe05c4762e2e3047000add)|
|
|OOP: Object Oriented Piracy|kata_object_oriented_piracy.py|test_object_oriented_piracy.py|[54fe05c4762e2e3047000add](https://www.codewars.com/kata/54fe05c4762e2e3047000add)|
|
||||||
|Vigenère Cipher Helper|kata_vigenere_cipher_helper.py|test_vigenere_cipher_helper.py|[52d1bd3694d26f8d6e0000d3](https://www.codewars.com/kata/52d1bd3694d26f8d6e0000d3)|
|
|Vigenère Cipher Helper|kata_vigenere_cipher_helper.py|test_vigenere_cipher_helper.py|[52d1bd3694d26f8d6e0000d3](https://www.codewars.com/kata/52d1bd3694d26f8d6e0000d3)|
|
||||||
@ -26,8 +26,3 @@ Repository for CDS-2020 Programming and Promt Engineering II
|
|||||||
|Thinkful - Object Drills: Quarks|kata_thinkful_quarks.py|test_thinkful_quarks.py|[5882b052bdeafec15e0000e6](https://www.codewars.com/kata/5882b052bdeafec15e0000e6)|
|
|Thinkful - Object Drills: Quarks|kata_thinkful_quarks.py|test_thinkful_quarks.py|[5882b052bdeafec15e0000e6](https://www.codewars.com/kata/5882b052bdeafec15e0000e6)|
|
||||||
|Thinkful - Object Drills: Vectors|kata_thinkful_vectors.py|test_thinkful_vectors.py|[587f1e1f39d444cee6000ad4](https://www.codewars.com/kata/587f1e1f39d444cee6000ad4)|
|
|Thinkful - Object Drills: Vectors|kata_thinkful_vectors.py|test_thinkful_vectors.py|[587f1e1f39d444cee6000ad4](https://www.codewars.com/kata/587f1e1f39d444cee6000ad4)|
|
||||||
|Building blocks|kata_building_blocks.py|test_building_blocks.py|[55b75fcf67e558d3750000a3](https://www.codewars.com/kata/55b75fcf67e558d3750000a3)|
|
|Building blocks|kata_building_blocks.py|test_building_blocks.py|[55b75fcf67e558d3750000a3](https://www.codewars.com/kata/55b75fcf67e558d3750000a3)|
|
||||||
|PaginationHelper|kata_pagination_helper.py|test_pagination_helper.py|[515bb423de843ea99400000a](https://www.codewars.com/kata/515bb423de843ea99400000a)|
|
|
||||||
|Who has the most money?|kata_who_the_most_money.py|test_who_the_most_money.py|[528d36d7cc451cd7e4000339](https://www.codewars.com/kata/528d36d7cc451cd7e4000339)|
|
|
||||||
|Next bigger number with the same digits|kata_next_bigger_number_same_digits.py|test_next_bigger_number_same_digits.py|[55983863da40caa2c900004e](https://www.codewars.com/kata/55983863da40caa2c900004e)|
|
|
||||||
|Snail|kata_snail.py|test_snail.py|[521c2db8ddc89b9b7a0000c1](https://www.codewars.com/kata/521c2db8ddc89b9b7a0000c1)|
|
|
||||||
|Chinese Numeral Encoder|kata_chinese_numeral_encoder.py|test_chinese_numeral_encoder.py|[52608f5345d4a19bed000b31](https://www.codewars.com/kata/52608f5345d4a19bed000b31)|
|
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
class Block:
|
|
||||||
def __init__(self, dimensions: list):
|
|
||||||
self.width = dimensions[0]
|
|
||||||
self.length = dimensions[1]
|
|
||||||
self.height = dimensions[2]
|
|
||||||
|
|
||||||
def get_width(self):
|
|
||||||
return self.width
|
|
||||||
|
|
||||||
def get_length(self):
|
|
||||||
return self.length
|
|
||||||
|
|
||||||
def get_height(self):
|
|
||||||
return self.height
|
|
||||||
|
|
||||||
def get_volume(self):
|
|
||||||
return self.width * self.length * self.height
|
|
||||||
|
|
||||||
def get_surface_area(self):
|
|
||||||
return (
|
|
||||||
2 * self.length * self.height
|
|
||||||
+ 2 * self.width * self.height
|
|
||||||
+ 2 * self.width * self.length
|
|
||||||
)
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
numerals = {
|
|
||||||
"-": "负",
|
|
||||||
".": "点",
|
|
||||||
0: "零",
|
|
||||||
1: "一",
|
|
||||||
2: "二",
|
|
||||||
3: "三",
|
|
||||||
4: "四",
|
|
||||||
5: "五",
|
|
||||||
6: "六",
|
|
||||||
7: "七",
|
|
||||||
8: "八",
|
|
||||||
9: "九",
|
|
||||||
10: "十",
|
|
||||||
100: "百",
|
|
||||||
1000: "千",
|
|
||||||
10000: "万",
|
|
||||||
}
|
|
||||||
|
|
||||||
# "Ten^x" dictonary
|
|
||||||
T = {1: 0, 2: 10, 3: 100, 4: 1000, 5: 10000}
|
|
||||||
|
|
||||||
|
|
||||||
def to_chinese_numeral(n):
|
|
||||||
# Check if negative, turn positive and remeber state
|
|
||||||
if n < 0:
|
|
||||||
n = abs(n)
|
|
||||||
is_negative = True
|
|
||||||
else:
|
|
||||||
is_negative = False
|
|
||||||
|
|
||||||
# return numerals[n] if n in numerals dictonary
|
|
||||||
if n % 100 != 0:
|
|
||||||
try:
|
|
||||||
if is_negative:
|
|
||||||
return "".join([numerals["-"], numerals[n]])
|
|
||||||
else:
|
|
||||||
return numerals[n]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Split number in iteger list and fractional (None if not exists)
|
|
||||||
if type(n) is not int:
|
|
||||||
n = str(n).split(".")
|
|
||||||
integer = [int(x) for x in n[0]]
|
|
||||||
fractional = [int(x) for x in n[1]]
|
|
||||||
else:
|
|
||||||
integer = [int(x) for x in str(n)]
|
|
||||||
fractional = None
|
|
||||||
|
|
||||||
# Blueprint list will contain building instruction on how to encode the number: 90090 -> [9, 10000, 0, 9, 10]
|
|
||||||
blueprint = []
|
|
||||||
|
|
||||||
blueprint.append(integer[0]) # First digit won't be 0 it can just be appended
|
|
||||||
blueprint.append(T[len(integer)]) # Length of integer determines power of ten
|
|
||||||
integer.pop(0) # Pop first digit
|
|
||||||
|
|
||||||
# create rest of blueprint by looping rest of integer
|
|
||||||
for _ in range(0, len(integer)):
|
|
||||||
# 0 needs no power of ten
|
|
||||||
if integer[0] == 0:
|
|
||||||
blueprint.append(0)
|
|
||||||
else:
|
|
||||||
blueprint.append(integer[0])
|
|
||||||
blueprint.append(T[len(integer)])
|
|
||||||
integer.pop(0)
|
|
||||||
|
|
||||||
# Pop last digit in blueprint if it's a 0
|
|
||||||
for i in range(len(blueprint) - 1, 0, -1):
|
|
||||||
if blueprint[i] == 0:
|
|
||||||
blueprint.pop(i)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
# Remove grouped zeros
|
|
||||||
was_zero = False
|
|
||||||
index_to_remove = []
|
|
||||||
for j, i in enumerate(blueprint):
|
|
||||||
if i == 0 and not was_zero:
|
|
||||||
was_zero = True
|
|
||||||
elif i == 0 and was_zero:
|
|
||||||
index_to_remove.append(j)
|
|
||||||
elif i != 0 and was_zero:
|
|
||||||
was_zero = False
|
|
||||||
|
|
||||||
index_to_remove.sort(
|
|
||||||
reverse=True
|
|
||||||
) # Reverse indices to avoid index errors when applying pop()
|
|
||||||
[blueprint.pop(x) for x in index_to_remove]
|
|
||||||
|
|
||||||
# remove first item in blueprint if second item in blueprint is between 10 and 19. Skip step if index error
|
|
||||||
try:
|
|
||||||
if sum(blueprint[0:2]) == 11:
|
|
||||||
blueprint.pop(0)
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Add symbols - if n was negative and . if it was a fraction
|
|
||||||
if is_negative:
|
|
||||||
blueprint.insert(0, "-")
|
|
||||||
|
|
||||||
if fractional is not None:
|
|
||||||
blueprint.append(".")
|
|
||||||
[blueprint.append(x) for x in fractional]
|
|
||||||
|
|
||||||
# Apply chinese encoding to blueprint and fractional
|
|
||||||
encoding = [numerals[x] for x in blueprint]
|
|
||||||
|
|
||||||
return "".join(encoding)
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
def next_bigger(n):
|
|
||||||
# get number as digits in list
|
|
||||||
digits = [int(x) for x in list(str(n))]
|
|
||||||
|
|
||||||
# find index of pivot. return -1 if there is no pivot, meaning number is alreadig biggest
|
|
||||||
pivot = -1
|
|
||||||
for i in range(1, len(digits)):
|
|
||||||
if digits[-i] > digits[-i - 1]:
|
|
||||||
pivot = -i - 1
|
|
||||||
break
|
|
||||||
|
|
||||||
if pivot == -1:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
# find the smallest digit to the right that is bigger than the pivot
|
|
||||||
right = digits[pivot + 1 :]
|
|
||||||
swap = right.index(min([x for x in right if x > digits[pivot]]))
|
|
||||||
|
|
||||||
# swap pivot with found digit
|
|
||||||
digits[pivot], digits[len(digits) - len(right) + swap] = (
|
|
||||||
right[swap],
|
|
||||||
digits[pivot],
|
|
||||||
)
|
|
||||||
|
|
||||||
# sort right side of new swapped pivot and replace it at the end
|
|
||||||
right = digits[pivot + 1 :]
|
|
||||||
right.sort()
|
|
||||||
digits[pivot + 1 :] = right
|
|
||||||
|
|
||||||
# return number
|
|
||||||
return int("".join([str(x) for x in digits]))
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
class PaginationHelper:
|
|
||||||
|
|
||||||
# The constructor takes in an array of items and an integer indicating
|
|
||||||
# how many items fit within a single page
|
|
||||||
def __init__(self, collection, items_per_page):
|
|
||||||
self.collection = collection
|
|
||||||
self.items_per_page = items_per_page
|
|
||||||
|
|
||||||
# returns the number of items within the entire collection
|
|
||||||
def item_count(self):
|
|
||||||
return len(self.collection)
|
|
||||||
|
|
||||||
# returns the number of pages
|
|
||||||
def page_count(self):
|
|
||||||
return int(len(self.collection) / self.items_per_page) + (
|
|
||||||
len(self.collection) % self.items_per_page > 0
|
|
||||||
)
|
|
||||||
|
|
||||||
# returns the number of items on the given page. page_index is zero based
|
|
||||||
# this method should return -1 for page_index values that are out of range
|
|
||||||
def page_item_count(self, page_index):
|
|
||||||
if page_index < 0 or self.page_count() - 1 < page_index:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
if page_index == 0:
|
|
||||||
return len(self.collection[: self.items_per_page])
|
|
||||||
|
|
||||||
return len(
|
|
||||||
self.collection[
|
|
||||||
self.items_per_page * page_index : self.items_per_page * page_index
|
|
||||||
+ self.items_per_page
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# determines what page an item at the given index is on. Zero based indexes.
|
|
||||||
# this method should return -1 for item_index values that are out of range
|
|
||||||
def page_index(self, item_index):
|
|
||||||
try:
|
|
||||||
self.collection[item_index]
|
|
||||||
|
|
||||||
if item_index < 0:
|
|
||||||
raise IndexError
|
|
||||||
except IndexError:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
return item_index // self.items_per_page
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
def snail(snail_map):
|
|
||||||
# first list in array is always first. So add it to new list snail and pop it from snail_map
|
|
||||||
snail = [x for x in snail_map[0]]
|
|
||||||
snail_map.pop(0)
|
|
||||||
|
|
||||||
# transpone list and append first list to snail. Then pop firt item of snail_map. Loop till snail_map is empty
|
|
||||||
while len(snail_map) > 0:
|
|
||||||
snail_map = [list(reversed(x)) for x in snail_map]
|
|
||||||
snail_map = [list(row) for row in zip(*snail_map)]
|
|
||||||
|
|
||||||
[snail.append(x) for x in snail_map[0]]
|
|
||||||
snail_map.pop(0)
|
|
||||||
|
|
||||||
return snail
|
|
||||||
|
|
||||||
|
|
||||||
array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
||||||
array = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
|
|
||||||
print(snail(array))
|
|
||||||
|
|
||||||
"""
|
|
||||||
Konzept:
|
|
||||||
Ziel: 1 2 3 6 9 8 7 4 5
|
|
||||||
|
|
||||||
1 2 3
|
|
||||||
4 5 6
|
|
||||||
7 8 9
|
|
||||||
|
|
||||||
1 2 3
|
|
||||||
|
|
||||||
4 5 6
|
|
||||||
7 8 9
|
|
||||||
|
|
||||||
6 9
|
|
||||||
5 8
|
|
||||||
4 7
|
|
||||||
|
|
||||||
1 2 3 6 9
|
|
||||||
|
|
||||||
5 8
|
|
||||||
4 7
|
|
||||||
|
|
||||||
8 7
|
|
||||||
5 4
|
|
||||||
|
|
||||||
1 2 3 6 9 8 7
|
|
||||||
|
|
||||||
5 4
|
|
||||||
|
|
||||||
4
|
|
||||||
5
|
|
||||||
|
|
||||||
1 2 3 6 9 8 7 4
|
|
||||||
|
|
||||||
5
|
|
||||||
|
|
||||||
1 2 3 6 9 8 7 4 5
|
|
||||||
|
|
||||||
|
|
||||||
Ziel: 1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
|
|
||||||
|
|
||||||
1 2 3 4
|
|
||||||
5 6 7 8
|
|
||||||
9 10 11 12
|
|
||||||
13 14 15 16
|
|
||||||
|
|
||||||
1 2 3 4
|
|
||||||
|
|
||||||
5 6 7 8
|
|
||||||
9 10 11 12
|
|
||||||
13 14 15 16
|
|
||||||
|
|
||||||
8 12 16
|
|
||||||
7 11 15
|
|
||||||
6 10 14
|
|
||||||
5 9 13
|
|
||||||
|
|
||||||
1 2 3 4 8 12 16
|
|
||||||
|
|
||||||
7 11 15
|
|
||||||
6 10 14
|
|
||||||
5 9 13
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
"""
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
class Quark(object):
|
|
||||||
def __init__(self, color, flavor):
|
|
||||||
self.color = color
|
|
||||||
self.flavor = flavor
|
|
||||||
self.baryon_number = 1 / 3
|
|
||||||
|
|
||||||
def interact(self, other) -> None:
|
|
||||||
self.color, other.color = other.color, self.color
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
class Vector(object):
|
|
||||||
def __init__(self, x: int, y: int):
|
|
||||||
self.x = x
|
|
||||||
self.y = y
|
|
||||||
|
|
||||||
def add(self, other):
|
|
||||||
return Vector(self.x + other.x, self.y + other.y)
|
|
||||||
@ -1,47 +1,3 @@
|
|||||||
class VersionManager:
|
class VersionManager:
|
||||||
def __init__(self, version=None):
|
def __init__(self, version):
|
||||||
if not version:
|
pass
|
||||||
version = "0.0.1"
|
|
||||||
|
|
||||||
parts = version.split(".")[:3]
|
|
||||||
|
|
||||||
if not all(p.isdecimal() for p in parts):
|
|
||||||
raise ValueError("Error occured while parsing version!")
|
|
||||||
|
|
||||||
nums = [int(p) for p in parts]
|
|
||||||
|
|
||||||
while len(nums) < 3:
|
|
||||||
nums.append(0)
|
|
||||||
|
|
||||||
self.major_v, self.minor_v, self.patch_v = nums
|
|
||||||
self.history = []
|
|
||||||
|
|
||||||
def _save(self):
|
|
||||||
self.history.append((self.major_v, self.minor_v, self.patch_v))
|
|
||||||
|
|
||||||
def major(self):
|
|
||||||
self._save()
|
|
||||||
self.major_v += 1
|
|
||||||
self.minor_v = 0
|
|
||||||
self.patch_v = 0
|
|
||||||
return self
|
|
||||||
|
|
||||||
def minor(self):
|
|
||||||
self._save()
|
|
||||||
self.minor_v += 1
|
|
||||||
self.patch_v = 0
|
|
||||||
return self
|
|
||||||
|
|
||||||
def patch(self):
|
|
||||||
self._save()
|
|
||||||
self.patch_v += 1
|
|
||||||
return self
|
|
||||||
|
|
||||||
def rollback(self):
|
|
||||||
if not self.history:
|
|
||||||
raise Exception("Cannot rollback!")
|
|
||||||
self.major_v, self.minor_v, self.patch_v = self.history.pop()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def release(self):
|
|
||||||
return f"{self.major_v}.{self.minor_v}.{self.patch_v}"
|
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
class Student:
|
|
||||||
def __init__(self, name, fives, tens, twenties):
|
|
||||||
self.name = name
|
|
||||||
self.fives = fives
|
|
||||||
self.tens = tens
|
|
||||||
self.twenties = twenties
|
|
||||||
|
|
||||||
|
|
||||||
def most_money(students):
|
|
||||||
student_names = [x.name for x in students]
|
|
||||||
student_money = [x.fives * 5 + x.tens * 10 + x.twenties * 20 for x in students]
|
|
||||||
|
|
||||||
if len(set(student_money)) == 1 and len(student_money) > 1:
|
|
||||||
return "all"
|
|
||||||
return max(list(zip(student_names, student_money)), key=lambda x: x[1])[0]
|
|
||||||
@ -1,88 +1,41 @@
|
|||||||
from abc import ABC, abstractmethod
|
class Hund:
|
||||||
|
|
||||||
|
|
||||||
class Hund(ABC):
|
|
||||||
anzahl_hunde = 0
|
anzahl_hunde = 0
|
||||||
|
|
||||||
def __init__(self, name: str, alter: int, gewicht: float, bellgeraeusch: str):
|
def __init__(self, name: str, rasse: str, alter: int, gewicht: float):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.rasse = rasse
|
||||||
self.alter = alter
|
self.alter = alter
|
||||||
self.gewicht = gewicht
|
self.gewicht = gewicht
|
||||||
self.bellgeraeusch = bellgeraeusch
|
|
||||||
Hund.anzahl_hunde += 1
|
Hund.anzahl_hunde += 1
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self):
|
||||||
return f"Hund(name={self.name!r}, rasse={self.__class__.__name__}, alter={self.alter}, gewicht={self.gewicht}"
|
return f"Hund(name={self.name!r}, rasse={self.rasse}, alter={self.alter}, gewicht={self.gewicht}"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self):
|
||||||
return f"{self.name} ist ein {self.alter}-jähriger {self.__class__.__name__}"
|
return f"{self.name} ist ein {self.alter}-jähriger {self.rasse}"
|
||||||
|
|
||||||
@abstractmethod
|
def bellen(self, n=1) -> int:
|
||||||
def bellen(self, n: int = 1) -> None:
|
print(n * "Woof! ")
|
||||||
print(" ".join([self.bellgeraeusch] * n))
|
|
||||||
|
|
||||||
def geburtstag(self) -> None:
|
def geburtstag(self):
|
||||||
self.alter += 1
|
self.alter += 1
|
||||||
print(
|
print(
|
||||||
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
|
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
|
||||||
)
|
)
|
||||||
|
|
||||||
def ist_welpe(self) -> None:
|
def ist_welpe(self):
|
||||||
if self.alter < 2:
|
if self.alter < 2:
|
||||||
print(f"{self.name} ist ein {self.alter}-jähriger Welpe")
|
print(f"{self.name} ist ein {self.alter}-jähriger Welpe")
|
||||||
else:
|
else:
|
||||||
print(f"{self.name} ist ein {self.alter}-jähriger erwachsener Hund")
|
print(f"{self.name} ist ein {self.alter}-jähriger erwachsener Hund")
|
||||||
|
|
||||||
def __lt__(self, other): # less than: self < other
|
|
||||||
return self.alter < other.alter
|
|
||||||
|
|
||||||
def __le__(self, other): # less equal: self <= other
|
hund1 = Hund(name="Bello", rasse="Pudel", alter=99, gewicht=357)
|
||||||
return self.alter <= other.alter
|
hund2 = Hund(name="Dewy", rasse="Labrador", alter=-6, gewicht=1)
|
||||||
|
|
||||||
def __gt__(self, other): # greater than: self > other
|
|
||||||
return self.alter > other.alter
|
|
||||||
|
|
||||||
def __ge__(self, other): # greater equal: self >= other
|
|
||||||
return self.alter >= other.alter
|
|
||||||
|
|
||||||
def __eq__(self, other): # equal: self == other
|
|
||||||
return self.alter == other.alter
|
|
||||||
|
|
||||||
|
|
||||||
class Pudel(Hund):
|
|
||||||
def __init__(self, name, alter, gewicht):
|
|
||||||
super().__init__(name, alter, gewicht, "wau")
|
|
||||||
|
|
||||||
def bellen(self, n=1):
|
|
||||||
super().bellen(n)
|
|
||||||
|
|
||||||
|
|
||||||
class Labrador(Hund):
|
|
||||||
def __init__(self, name, alter, gewicht):
|
|
||||||
super().__init__(name, alter, gewicht, "wuff")
|
|
||||||
|
|
||||||
def bellen(self, n=1):
|
|
||||||
super().bellen(n)
|
|
||||||
|
|
||||||
|
|
||||||
class Bulldog(Hund):
|
|
||||||
def __init__(self, name, alter, gewicht):
|
|
||||||
super().__init__(name, alter, gewicht, "woff")
|
|
||||||
|
|
||||||
def bellen(self, n=1):
|
|
||||||
super().bellen(n)
|
|
||||||
|
|
||||||
|
|
||||||
hund1 = Labrador(name="Bello", alter=33, gewicht=27)
|
|
||||||
hund2 = Pudel(name="Dewy", alter=6, gewicht=1)
|
|
||||||
hund3 = Bulldog(name="Stone", alter=15, gewicht=1000)
|
|
||||||
|
|
||||||
print(repr(hund1))
|
print(repr(hund1))
|
||||||
print(hund2)
|
print(hund2)
|
||||||
hund2.bellen(3)
|
hund2.bellen(3)
|
||||||
hund1.bellen(2)
|
|
||||||
hund1.geburtstag()
|
hund1.geburtstag()
|
||||||
hund2.ist_welpe()
|
hund2.ist_welpe()
|
||||||
hund1.ist_welpe()
|
hund1.ist_welpe()
|
||||||
hund3.bellen(4)
|
|
||||||
print(hund3 > hund2)
|
|
||||||
|
|||||||
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,45 +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")
|
|
||||||
|
|
||||||
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,225 +0,0 @@
|
|||||||
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
|
|
||||||
@ -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,6 +0,0 @@
|
|||||||
from pathlib import Path
|
|
||||||
|
|
||||||
base_dir = Path("data")
|
|
||||||
file_path = base_dir / "raw" / "processed.json"
|
|
||||||
|
|
||||||
file_path.mkdir(parents=True, exist_ok=True)
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
# Ziel dieses Skripts:
|
|
||||||
#
|
|
||||||
# 1. Das Produkt von 6 * 7 berechnen → Erwartet: 42.0
|
|
||||||
# 2. Die Kreisfläche für Radius 5 berechnen → Erwartet: ~78.54
|
|
||||||
# 3. Den genauen Wert von PI ausgeben → Erwartet: 3.141592653589793
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# AUFGABE: Das Skript liefert falsche Ergebnisse. Finde den Fehler und
|
|
||||||
# korrigiere die Import-Anweisungen – ohne den restlichen Code
|
|
||||||
# zu verändern!
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
# from src.mathematik import berechne, PI # (1) Import aus mathematik
|
|
||||||
# from src.geometrie import berechne, PI # (2) Import aus geometrie
|
|
||||||
import src.geometrie as geo
|
|
||||||
import src.mathematik as mathe
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# --- Aufgabe 1: Produkt berechnen (nutzt mathematik.berechne) ---
|
|
||||||
produkt = mathe.berechne(6, 7) # Erwartet: 42.0 — bekommt aber einen Fehler!
|
|
||||||
print(f"6 × 7 = {produkt}")
|
|
||||||
|
|
||||||
# --- Aufgabe 2: Kreisfläche (nutzt geometrie.berechne) ---
|
|
||||||
flaeche = geo.berechne(5) # Erwartet: ~78.54
|
|
||||||
print(f"Kreisfläche (r=5): {flaeche:.4f}")
|
|
||||||
|
|
||||||
# --- Aufgabe 3: PI-Wert ---
|
|
||||||
print(f"PI = {mathe.PI}") # Erwartet: 3.141592653589793
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
# geometrie.py
|
|
||||||
# Modul für geometrische Berechnungen (Kreisflächen, Umfang usw.)
|
|
||||||
|
|
||||||
PI = 3.14 # Vereinfachter Wert (nur 2 Nachkommastellen!)
|
|
||||||
|
|
||||||
|
|
||||||
def berechne(radius: float) -> float:
|
|
||||||
"""Berechnet die Fläche eines Kreises: A = PI * r²"""
|
|
||||||
return PI * radius**2
|
|
||||||
|
|
||||||
|
|
||||||
def umfang(radius: float) -> float:
|
|
||||||
"""Berechnet den Umfang eines Kreises: U = 2 * PI * r"""
|
|
||||||
return 2 * PI * radius
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
# mathematik.py
|
|
||||||
# Modul für allgemeine mathematische Berechnungen
|
|
||||||
|
|
||||||
PI = 3.141592653589793 # Hochpräziser Wert
|
|
||||||
|
|
||||||
|
|
||||||
def berechne(a: float, b: float) -> float:
|
|
||||||
"""Berechnet das Produkt zweier Zahlen."""
|
|
||||||
return a * b
|
|
||||||
|
|
||||||
|
|
||||||
def potenz(basis: float, exponent: int) -> float:
|
|
||||||
"""Berechnet basis hoch exponent."""
|
|
||||||
return basis**exponent
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
# Ziel dieses Skripts:
|
|
||||||
#
|
|
||||||
# Testergebnisse einer Klasse auswerten und formatiert ausgeben.
|
|
||||||
#
|
|
||||||
# Gegeben: Punkte von 5 Studierenden (von max. 100 Punkten)
|
|
||||||
# Erwartet:
|
|
||||||
# Mittelwert → 73.5 (kaufm. gerundet → 74 → "Gut")
|
|
||||||
# Max. Punkte → 100 (aus statistik.MAX_WERT)
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# AUFGABE: Das Skript gibt falsche Werte aus. Es gibt keine Fehlermeldung
|
|
||||||
# vom Interpreter. Finde heraus, warum die Ergebnisse falsch sind
|
|
||||||
# und korrigiere die Import-Anweisungen!
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
from src.statistik import (
|
|
||||||
runde,
|
|
||||||
mittelwert,
|
|
||||||
bewerte,
|
|
||||||
MAX_WERT,
|
|
||||||
) # importiert: runde, mittelwert, bewerte, MAX_WERT, MIN_WERT
|
|
||||||
from src.formatierung import (
|
|
||||||
trennlinie,
|
|
||||||
) # importiert: runde, als_prozent, trennlinie, MAX_WERT, MIN_WERT
|
|
||||||
|
|
||||||
PUNKTE = [92, 85, 61, 48, 82]
|
|
||||||
|
|
||||||
|
|
||||||
def auswertung():
|
|
||||||
print(trennlinie("="))
|
|
||||||
print(" TESTERGEBNISSE")
|
|
||||||
print(trennlinie("="))
|
|
||||||
|
|
||||||
# --- Mittelwert berechnen und benoten ---
|
|
||||||
mw = mittelwert(PUNKTE)
|
|
||||||
mw_gerundet = runde(mw) # Soll kaufm. auf int runden → Erwartet: 74
|
|
||||||
note = bewerte(mw)
|
|
||||||
|
|
||||||
print(f" Mittelwert : {mw}")
|
|
||||||
print(f" Gerundet : {mw_gerundet} ← Erwartet: 74 (int)")
|
|
||||||
print(f" Durchschnittsnote: {note}")
|
|
||||||
|
|
||||||
print(trennlinie())
|
|
||||||
|
|
||||||
# --- Maximale Punktzahl aus dem Modul ---
|
|
||||||
print(f" Max. Punkte : {MAX_WERT} ← Erwartet: 100")
|
|
||||||
|
|
||||||
print(trennlinie())
|
|
||||||
|
|
||||||
# --- Einzelne Ergebnisse ---
|
|
||||||
print(" Einzelergebnisse:")
|
|
||||||
for p in PUNKTE:
|
|
||||||
print(f" {p:>3} Punkte → {bewerte(p)}")
|
|
||||||
|
|
||||||
print(trennlinie("="))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
auswertung()
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# formatierung.py
|
|
||||||
# Modul für Ausgabe-Formatierung und Darstellung
|
|
||||||
|
|
||||||
MAX_WERT = 255 # Maximaler RGB-Farbwert (0–255)
|
|
||||||
MIN_WERT = 0 # Minimaler RGB-Farbwert
|
|
||||||
|
|
||||||
|
|
||||||
def runde(wert: float, stellen: int = 2) -> float:
|
|
||||||
"""Rundet auf 'stellen' Nachkommastellen (für Anzeige-Formatierung)."""
|
|
||||||
faktor = 10**stellen
|
|
||||||
return int(wert * faktor + 0.5) / faktor
|
|
||||||
|
|
||||||
|
|
||||||
def als_prozent(wert: float, gesamt: float) -> str:
|
|
||||||
"""Gibt einen Wert als formatierten Prozentstring zurück."""
|
|
||||||
return f"{runde(wert / gesamt * 100)} %"
|
|
||||||
|
|
||||||
|
|
||||||
def trennlinie(zeichen: str = "-", laenge: int = 40) -> str:
|
|
||||||
"""Erzeugt eine Trennlinie für die Konsolenausgabe."""
|
|
||||||
return zeichen * laenge
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
# statistik.py
|
|
||||||
# Modul für statistische Berechnungen
|
|
||||||
|
|
||||||
MAX_WERT = 100 # Maximale Punktzahl in einem Test (0–100)
|
|
||||||
MIN_WERT = 0 # Minimale Punktzahl
|
|
||||||
|
|
||||||
|
|
||||||
def runde(wert: float) -> int:
|
|
||||||
"""Rundet kaufmännisch auf ganze Zahlen (für Testergebnisse)."""
|
|
||||||
return int(wert + 0.5)
|
|
||||||
|
|
||||||
|
|
||||||
def mittelwert(werte: list[float]) -> float:
|
|
||||||
"""Berechnet den arithmetischen Mittelwert einer Liste."""
|
|
||||||
return sum(werte) / len(werte)
|
|
||||||
|
|
||||||
|
|
||||||
def bewerte(punkte: float) -> str:
|
|
||||||
"""Gibt eine Note zurück basierend auf Punktzahl (0–100)."""
|
|
||||||
p = runde(punkte)
|
|
||||||
if p >= 90:
|
|
||||||
return "Sehr gut"
|
|
||||||
elif p >= 75:
|
|
||||||
return "Gut"
|
|
||||||
elif p >= 60:
|
|
||||||
return "Befriedigend"
|
|
||||||
elif p >= 45:
|
|
||||||
return "Ausreichend"
|
|
||||||
else:
|
|
||||||
return "Ungenügend"
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# random.py
|
|
||||||
#
|
|
||||||
# Dieses Modul wurde angelegt, um eigene Hilfsfunktionen für
|
|
||||||
# "Zufallsentscheidungen" in der Lotterie-Logik bereitzustellen.
|
|
||||||
#
|
|
||||||
# Es definiert einige der gleichen Funktionen wie das Standardmodul –
|
|
||||||
# aber mit vereinfachter (fehlerhafter) Implementierung.
|
|
||||||
|
|
||||||
|
|
||||||
def randint(a: int, b: int) -> int:
|
|
||||||
"""Gibt eine 'zufällige' Ganzzahl zwischen a und b zurück."""
|
|
||||||
return a # ← gibt IMMER den kleinsten Wert zurück!
|
|
||||||
|
|
||||||
|
|
||||||
def shuffle(lst: list) -> None:
|
|
||||||
"""Mischt eine Liste in-place."""
|
|
||||||
pass # ← tut gar nichts!
|
|
||||||
|
|
||||||
|
|
||||||
def sample(population: list, k: int) -> list:
|
|
||||||
"""Gibt k zufällige Elemente aus population zurück."""
|
|
||||||
return list(population)[:k] # ← gibt IMMER die ersten k Elemente zurück!
|
|
||||||
|
|
||||||
|
|
||||||
def seed(a=None):
|
|
||||||
"""Setzt den Zufallsgenerator-Seed."""
|
|
||||||
pass # ← keine Wirkung
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
# Lotterie-Simulation: Zieht 6 aus 45 Zahlen, dazu eine Zusatzzahl.
|
|
||||||
#
|
|
||||||
# Wiederholt die Ziehung 5× und gibt die Ergebnisse aus.
|
|
||||||
# Anschließend wird die Gewinnerliste zufällig gemischt.
|
|
||||||
#
|
|
||||||
# Erwartetes Verhalten:
|
|
||||||
# • Jede Ziehung liefert 6 VERSCHIEDENE, ZUFÄLLIGE Zahlen aus 1–45
|
|
||||||
# • Wiederholte Aufrufe liefern UNTERSCHIEDLICHE Ergebnisse
|
|
||||||
# • Die Teilnehmerliste ist nach dem Mischen in ZUFÄLLIGER Reihenfolge
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# AUFGABE: Das Programm läuft ohne Fehlermeldung, aber die Ergebnisse
|
|
||||||
# sind offensichtlich nicht zufällig. Finde die Ursache und
|
|
||||||
# behebe den Fehler – ohne ziehung.py oder statistik.py zu ändern!
|
|
||||||
#
|
|
||||||
# REMARK: Möglicherweise ist das Problem sogar abhängig von der verwendeten
|
|
||||||
# IDE... Lasst das main.py mal aus eurer IDE laufen sowie aus dem
|
|
||||||
# Terminal. Sind die Resultate gleich?
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
from ziehung import ziehe_zahlen, ziehe_zusatzzahl
|
|
||||||
from statistik import simuliere_ziehungen, mische_teilnehmer
|
|
||||||
|
|
||||||
TEILNEHMER = ["Alice", "Bob", "Carol", "Dave", "Eve"]
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
print("=" * 45)
|
|
||||||
print(" LOTTO-SIMULATION (6 aus 45)")
|
|
||||||
print("=" * 45)
|
|
||||||
|
|
||||||
print("\n--- 5 unabhängige Ziehungen ---")
|
|
||||||
for i, ziehung in enumerate(simuliere_ziehungen(5), 1):
|
|
||||||
print(f" Ziehung {i}: {ziehung}")
|
|
||||||
|
|
||||||
print("\n--- Einzelziehung mit Zusatzzahl ---")
|
|
||||||
haupt = ziehe_zahlen()
|
|
||||||
zusatz = ziehe_zusatzzahl(haupt)
|
|
||||||
print(f" Hauptzahlen : {haupt}")
|
|
||||||
print(f" Zusatzzahl : {zusatz}")
|
|
||||||
|
|
||||||
print("\n--- Gewinnerliste (zufällig gemischt) ---")
|
|
||||||
gemischt = mische_teilnehmer(TEILNEHMER)
|
|
||||||
for rang, name in enumerate(gemischt, 1):
|
|
||||||
print(f" Rang {rang}: {name}")
|
|
||||||
|
|
||||||
print("=" * 45)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
# Wertet mehrere Lottoziehungen statistisch aus
|
|
||||||
|
|
||||||
import custom_random
|
|
||||||
from ziehung import ziehe_zahlen
|
|
||||||
|
|
||||||
|
|
||||||
def simuliere_ziehungen(anzahl: int = 10) -> list[list[int]]:
|
|
||||||
"""Führt 'anzahl' unabhängige Lottoziehungen durch."""
|
|
||||||
ergebnisse = []
|
|
||||||
for _ in range(anzahl):
|
|
||||||
zahlen = ziehe_zahlen()
|
|
||||||
ergebnisse.append(zahlen)
|
|
||||||
return ergebnisse
|
|
||||||
|
|
||||||
|
|
||||||
def mische_teilnehmer(teilnehmer: list[str]) -> list[str]:
|
|
||||||
"""Mischt die Teilnehmerliste zufällig (z. B. für Gewinner-Reihenfolge)."""
|
|
||||||
kopie = teilnehmer[:]
|
|
||||||
custom_random.shuffle(kopie)
|
|
||||||
return kopie
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
# lotterie/ziehung.py
|
|
||||||
# Modul für die eigentliche Lottoziehung
|
|
||||||
|
|
||||||
import custom_random
|
|
||||||
|
|
||||||
ZAHLEN_POOL = list(range(1, 46)) # Zahlen 1–45
|
|
||||||
|
|
||||||
|
|
||||||
def ziehe_zahlen(anzahl: int = 6) -> list[int]:
|
|
||||||
"""Zieht 'anzahl' verschiedene Lottozahlen aus dem Pool."""
|
|
||||||
gezogen = custom_random.sample(ZAHLEN_POOL, anzahl)
|
|
||||||
gezogen.sort()
|
|
||||||
return gezogen
|
|
||||||
|
|
||||||
|
|
||||||
def ziehe_zusatzzahl(ausgeschlossen: list[int]) -> int:
|
|
||||||
"""Zieht eine Zusatzzahl, die nicht in der Hauptziehung vorkommt."""
|
|
||||||
pool = [z for z in ZAHLEN_POOL if z not in ausgeschlossen]
|
|
||||||
return custom_random.randint(1, len(pool) - 1) # ← Index in pool, dann Lookup
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
# Schulverwaltungs-Simulation:
|
|
||||||
# • Drei Schüler werden angelegt (mit Noten in zwei Kursen)
|
|
||||||
# • Zwei Kurse werden erstellt, Schüler werden eingeschrieben
|
|
||||||
# • Notenspiegel je Schüler und Kursbericht je Kurs wird ausgegeben
|
|
||||||
#
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
# AUFGABE: Das Programm lässt sich gar nicht erst starten – es gibt
|
|
||||||
# sofort einen ImportError.
|
|
||||||
#
|
|
||||||
# 1. Lies die Fehlermeldung sorgfältig und zeichne den
|
|
||||||
# Importgraphen auf: Welches Modul importiert welches?
|
|
||||||
#
|
|
||||||
# 2. Versuche durch eine geeignete Gegenmassnahme den Fehler zu
|
|
||||||
# beheben.
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
from src.kurs import Kurs
|
|
||||||
from src.schueler import Schueler
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# --- Schüler anlegen ---
|
|
||||||
anna = Schueler("Anna Meier", {"Mathematik": 2.5, "Deutsch": 3.0})
|
|
||||||
ben = Schueler("Ben Keller", {"Mathematik": 5.0, "Deutsch": 2.0})
|
|
||||||
clara = Schueler("Clara Huber", {"Mathematik": 3.5, "Deutsch": 4.5})
|
|
||||||
|
|
||||||
# --- Kurse anlegen und Schüler einschreiben ---
|
|
||||||
mathe = Kurs("Mathematik")
|
|
||||||
deutsch = Kurs("Deutsch")
|
|
||||||
|
|
||||||
for s in [anna, ben, clara]:
|
|
||||||
mathe.einschreiben(s)
|
|
||||||
deutsch.einschreiben(s)
|
|
||||||
|
|
||||||
# --- Ausgabe Notenspiegel ---
|
|
||||||
print("=" * 45)
|
|
||||||
print(" NOTENSPIEGEL")
|
|
||||||
print("=" * 45)
|
|
||||||
for s in [anna, ben, clara]:
|
|
||||||
print(s.notenspiegel())
|
|
||||||
print()
|
|
||||||
|
|
||||||
# --- Ausgabe Kursberichte ---
|
|
||||||
print("=" * 45)
|
|
||||||
print(" KURSBERICHTE")
|
|
||||||
print("=" * 45)
|
|
||||||
print(mathe.kursbericht())
|
|
||||||
print(deutsch.kursbericht())
|
|
||||||
print("=" * 45)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
# ---------------------------------------------------------------
|
|
||||||
# Gemeinsam genutzte Hilfsfunktion – wird auch in schueler.py
|
|
||||||
# über den Import von oben verwendet.
|
|
||||||
# ---------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def ist_bestanden(note: float) -> bool:
|
|
||||||
"""Gibt True zurück, wenn die Note ≤ 4.0 (bestanden) ist."""
|
|
||||||
return note <= 4.0
|
|
||||||
|
|
||||||
|
|
||||||
def berechne_klassendurchschnitt(noten: list[float]) -> float:
|
|
||||||
"""Berechnet den Notendurchschnitt einer Gruppe."""
|
|
||||||
if not noten:
|
|
||||||
return 0.0
|
|
||||||
return round(sum(noten) / len(noten), 2)
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# schulverwaltung/kurs.py
|
|
||||||
#
|
|
||||||
# Verwaltet Kurse und eingeschriebene Schüler.
|
|
||||||
# Benötigt die Schueler-Klasse aus schueler.py für die Einschreibung
|
|
||||||
# und Notenauswertung.
|
|
||||||
|
|
||||||
from notenrechner import ist_bestanden, berechne_klassendurchschnitt
|
|
||||||
from .schueler import Schueler
|
|
||||||
|
|
||||||
|
|
||||||
class Kurs:
|
|
||||||
def __init__(self, name: str):
|
|
||||||
self.name = name
|
|
||||||
self.schueler: list[Schueler] = []
|
|
||||||
|
|
||||||
def einschreiben(self, schueler: Schueler) -> None:
|
|
||||||
"""Schreibt einen Schüler in diesen Kurs ein."""
|
|
||||||
self.schueler.append(schueler)
|
|
||||||
|
|
||||||
def kursbericht(self) -> str:
|
|
||||||
"""Gibt eine Übersicht über alle Schüler und den Kursdurchschnitt aus."""
|
|
||||||
zeilen = [f"\n Kurs: {self.name} ({len(self.schueler)} Schüler)"]
|
|
||||||
noten_im_kurs = []
|
|
||||||
|
|
||||||
for s in self.schueler:
|
|
||||||
note = s.noten.get(self.name)
|
|
||||||
if note is not None:
|
|
||||||
status = "✓" if ist_bestanden(note) else "✗"
|
|
||||||
zeilen.append(f" {status} {s.name:<20} {note:.1f}")
|
|
||||||
noten_im_kurs.append(note)
|
|
||||||
|
|
||||||
if noten_im_kurs:
|
|
||||||
schnitt = berechne_klassendurchschnitt(noten_im_kurs)
|
|
||||||
zeilen.append(f" → Klassendurchschnitt: {schnitt:.2f}")
|
|
||||||
return "\n".join(zeilen)
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
# Verwaltet Schüler-Daten und prüft den Abschlussstatus.
|
|
||||||
# Benötigt ist_bestanden() aus kurs.py, um festzustellen ob
|
|
||||||
# ein Schüler alle Kurse bestanden hat.
|
|
||||||
|
|
||||||
from notenrechner import ist_bestanden
|
|
||||||
|
|
||||||
|
|
||||||
class Schueler:
|
|
||||||
def __init__(self, name: str, noten: dict[str, float]):
|
|
||||||
"""
|
|
||||||
name : Vollständiger Name des Schülers
|
|
||||||
noten : {Kursname: Note} z. B. {'Mathematik': 4.5, 'Deutsch': 3.0}
|
|
||||||
"""
|
|
||||||
self.name = name
|
|
||||||
self.noten = noten
|
|
||||||
|
|
||||||
def kann_abschliessen(self) -> bool:
|
|
||||||
"""Gibt True zurück, wenn alle Kursnoten bestanden sind (Note ≤ 4.0)."""
|
|
||||||
return all(ist_bestanden(note) for note in self.noten.values())
|
|
||||||
|
|
||||||
def notenspiegel(self) -> str:
|
|
||||||
"""Gibt eine formatierte Übersicht aller Noten zurück."""
|
|
||||||
zeilen = [f" Schüler: {self.name}"]
|
|
||||||
for kurs, note in self.noten.items():
|
|
||||||
status = "✓" if ist_bestanden(note) else "✗"
|
|
||||||
zeilen.append(f" {status} {kurs:<18} Note: {note:.1f}")
|
|
||||||
zeilen.append(f" → Abschluss möglich: {self.kann_abschliessen()}")
|
|
||||||
return "\n".join(zeilen)
|
|
||||||
@ -1,6 +1,2 @@
|
|||||||
def discount_price(price: float, percent: float) -> float:
|
def discount_price(price: float, percent: float) -> float:
|
||||||
if price < 0:
|
|
||||||
raise ValueError("Price must be >= 0")
|
|
||||||
if not 0 <= percent <= 100:
|
|
||||||
raise ValueError("Percent must be between 0 and 100")
|
|
||||||
return price - price * percent / 100
|
return price - price * percent / 100
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
from src.codewars.kata_building_blocks import Block
|
|
||||||
|
|
||||||
|
|
||||||
def test_block():
|
|
||||||
b1 = Block([2, 2, 2])
|
|
||||||
b2 = Block([41, 87, 67])
|
|
||||||
|
|
||||||
assert b1.get_width() == 2
|
|
||||||
assert b1.get_length() == 2
|
|
||||||
assert b1.get_height() == 2
|
|
||||||
assert b1.get_volume() == 8
|
|
||||||
assert b1.get_surface_area() == 24
|
|
||||||
assert b2.get_width() == 41
|
|
||||||
assert b2.get_length() == 87
|
|
||||||
assert b2.get_height() == 67
|
|
||||||
assert b2.get_volume() == 238989
|
|
||||||
assert b2.get_surface_area() == 24286
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
from src.codewars.kata_ceasar_cipher_helper import CaesarCipher
|
|
||||||
|
|
||||||
|
|
||||||
def test_cipher_helper():
|
|
||||||
c = CaesarCipher(5)
|
|
||||||
|
|
||||||
assert c.encode("Codewars") == "HTIJBFWX"
|
|
||||||
assert c.decode("HTIJBFWX") == "CODEWARS"
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from src.codewars.kata_chinese_numeral_encoder import to_chinese_numeral
|
|
||||||
|
|
||||||
|
|
||||||
def test_to_chinese():
|
|
||||||
assert to_chinese_numeral(9) == "九"
|
|
||||||
assert to_chinese_numeral(-5) == "负五"
|
|
||||||
assert to_chinese_numeral(0.5) == "零点五"
|
|
||||||
assert to_chinese_numeral(10) == "十"
|
|
||||||
assert to_chinese_numeral(110) == "一百一十"
|
|
||||||
assert to_chinese_numeral(111) == "一百一十一"
|
|
||||||
assert to_chinese_numeral(1000) == "一千"
|
|
||||||
assert to_chinese_numeral(10000) == "一万"
|
|
||||||
assert to_chinese_numeral(10006) == "一万零六"
|
|
||||||
assert to_chinese_numeral(10306.005) == "一万零三百零六点零零五"
|
|
||||||
assert to_chinese_numeral(-10.000001) == "负十点零零零零零一"
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from src.codewars.kata_force_of_gravity import solution
|
|
||||||
|
|
||||||
|
|
||||||
def test_a():
|
|
||||||
assert solution([1000, 1000, 100], ["g", "kg", "m"]) == 6.67e-12
|
|
||||||
assert solution([1000, 1000, 100], ["kg", "kg", "m"]) == 6.6699999999999995e-09
|
|
||||||
assert solution([1000, 1000, 100], ["kg", "kg", "cm"]) == 0.0000667
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from src.codewars.kata_next_bigger_number_same_digits import next_bigger
|
|
||||||
|
|
||||||
|
|
||||||
def test_next_bigger():
|
|
||||||
assert next_bigger(12) == 21
|
|
||||||
assert next_bigger(21) == -1
|
|
||||||
assert next_bigger(513) == 531
|
|
||||||
assert next_bigger(2017) == 2071
|
|
||||||
assert next_bigger(414) == 441
|
|
||||||
assert next_bigger(144) == 414
|
|
||||||
assert next_bigger(1234567890) == 1234567908
|
|
||||||
assert next_bigger(59884848459853) == 59884848483559
|
|
||||||
assert next_bigger(7600201336) == 7600201363
|
|
||||||
assert next_bigger(5113455566888) == 5113455568688
|
|
||||||
assert next_bigger(4769560370633) == 4769560373036
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from src.codewars.kata_object_oriented_piracy import Ship
|
|
||||||
|
|
||||||
|
|
||||||
def test_piracy():
|
|
||||||
empty_ship = Ship(0, 0)
|
|
||||||
assert not empty_ship.is_worth_it()
|
|
||||||
|
|
||||||
boat = Ship(15, 20)
|
|
||||||
assert not boat.is_worth_it()
|
|
||||||
|
|
||||||
worthy_ship = Ship(100, 20)
|
|
||||||
assert worthy_ship.is_worth_it()
|
|
||||||
|
|
||||||
big_boat = Ship(35, 20)
|
|
||||||
assert not big_boat.is_worth_it()
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
from src.codewars.kata_pagination_helper import PaginationHelper
|
|
||||||
|
|
||||||
|
|
||||||
def test_pagination():
|
|
||||||
collection = ["a", "b", "c", "d", "e", "f"]
|
|
||||||
helper = PaginationHelper(collection, 4)
|
|
||||||
|
|
||||||
assert helper.page_count() == 2
|
|
||||||
assert helper.item_count() == 6
|
|
||||||
assert helper.page_item_count(0) == 4
|
|
||||||
assert helper.page_item_count(1) == 2
|
|
||||||
assert helper.page_item_count(2) == -1
|
|
||||||
assert helper.page_index(5) == 1
|
|
||||||
assert helper.page_index(2) == 0
|
|
||||||
assert helper.page_index(20) == -1
|
|
||||||
assert helper.page_index(-10) == -1
|
|
||||||
|
|
||||||
empty = PaginationHelper([], 10)
|
|
||||||
|
|
||||||
assert empty.item_count() == 0
|
|
||||||
assert empty.page_count() == 0
|
|
||||||
assert empty.page_index(0) == -1
|
|
||||||
assert empty.page_index(1) == -1
|
|
||||||
assert empty.page_index(-1) == -1
|
|
||||||
assert empty.page_item_count(0) == -1
|
|
||||||
assert empty.page_item_count(1) == -1
|
|
||||||
assert empty.page_item_count(-1) == -1
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
from src.codewars.kata_snail import snail
|
|
||||||
|
|
||||||
|
|
||||||
def test_snail():
|
|
||||||
array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
|
||||||
|
|
||||||
assert snail(array) == [1, 2, 3, 6, 9, 8, 7, 4, 5]
|
|
||||||
|
|
||||||
array = [[1, 2, 3], [8, 9, 4], [7, 6, 5]]
|
|
||||||
assert snail(array) == [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
from src.codewars.kata_the_lamp import Lamp
|
|
||||||
|
|
||||||
|
|
||||||
def test_lamp():
|
|
||||||
my_lamp = Lamp("Blue")
|
|
||||||
|
|
||||||
assert my_lamp.color == "Blue"
|
|
||||||
assert not my_lamp.on
|
|
||||||
assert my_lamp.state() == "The lamp is off."
|
|
||||||
my_lamp.toggle_switch()
|
|
||||||
assert my_lamp.state() == "The lamp is on."
|
|
||||||
my_lamp.toggle_switch()
|
|
||||||
assert my_lamp.state() == "The lamp is off."
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from src.codewars.kata_thinkful_quarks import Quark
|
|
||||||
|
|
||||||
|
|
||||||
def test_quark():
|
|
||||||
q1 = Quark("red", "up")
|
|
||||||
q2 = Quark("blue", "strange")
|
|
||||||
|
|
||||||
assert q1.color == "red"
|
|
||||||
assert q2.flavor == "strange"
|
|
||||||
assert q2.baryon_number == 1 / 3
|
|
||||||
|
|
||||||
q1.interact(q2)
|
|
||||||
|
|
||||||
assert q1.color == "blue"
|
|
||||||
assert q2.color == "red"
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
from src.codewars.kata_thinkful_vectors import Vector
|
|
||||||
|
|
||||||
|
|
||||||
def test_vector():
|
|
||||||
v1 = Vector(3, 4)
|
|
||||||
v2 = Vector(1, 2)
|
|
||||||
|
|
||||||
assert v1.x == 3
|
|
||||||
assert v2.y == 2
|
|
||||||
assert hasattr(v1, "add")
|
|
||||||
assert isinstance(v1.add(v2), Vector)
|
|
||||||
assert (v1.add(v2)).x == 4
|
|
||||||
assert (v1.add(v2)).y == 6
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from src.codewars.kata_version_mamanger import VersionManager
|
|
||||||
|
|
||||||
|
|
||||||
def test_lamp():
|
|
||||||
v = VersionManager("1.1.1")
|
|
||||||
|
|
||||||
assert v.release() == "1.1.1"
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
from src.codewars.kata_vigenere_cipher_helper import VigenereCipher
|
|
||||||
|
|
||||||
|
|
||||||
def test_cipher_helper():
|
|
||||||
abc = "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
key = "password"
|
|
||||||
c = VigenereCipher(key, abc)
|
|
||||||
|
|
||||||
assert c.encode("codewars") == "rovwsoiv"
|
|
||||||
assert c.decode("rovwsoiv") == "codewars"
|
|
||||||
|
|
||||||
assert c.encode("waffles") == "laxxhsj"
|
|
||||||
assert c.decode("laxxhsj") == "waffles"
|
|
||||||
|
|
||||||
assert c.encode("CODEWARS") == "CODEWARS"
|
|
||||||
assert c.decode("CODEWARS") == "CODEWARS"
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from src.codewars.kata_who_the_most_money import Student, most_money
|
|
||||||
|
|
||||||
|
|
||||||
def test_most_money():
|
|
||||||
phil = Student("Phil", 2, 2, 1)
|
|
||||||
cam = Student("Cameron", 2, 2, 0)
|
|
||||||
geoff = Student("Geoff", 0, 3, 0)
|
|
||||||
|
|
||||||
assert most_money([cam, geoff, phil]) == "Phil"
|
|
||||||
|
|
||||||
phil = Student("Phil", 2, 2, 2)
|
|
||||||
cam = Student("Cameron", 2, 2, 2)
|
|
||||||
geoff = Student("Geoff", 2, 2, 2)
|
|
||||||
|
|
||||||
assert most_money([cam, geoff, phil]) == "all"
|
|
||||||
@ -1,21 +1,6 @@
|
|||||||
from src.tutorial.testing.shop.pricing import discount_price
|
from src.tutorial.testing.shop.pricing import discount_price
|
||||||
import pytest
|
|
||||||
|
|
||||||
|
|
||||||
def test_discount_price_reduces_price():
|
def test_discount_price_reduces_price():
|
||||||
result = discount_price(100.0, 20.0)
|
result = discount_price(100.0, 20.0)
|
||||||
assert result == 80
|
assert result == 80
|
||||||
|
|
||||||
assert discount_price(100.0, 50.0) == 50
|
|
||||||
assert discount_price(100.0, 0) == 100
|
|
||||||
assert discount_price(100.0, 100) == 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_negative_orice_raises_value_error() -> None:
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
discount_price(-100.0, 50.0) is ValueError
|
|
||||||
|
|
||||||
|
|
||||||
def test_percent_above_hundered_message() -> None:
|
|
||||||
with pytest.raises(ValueError, match="between 0 and 100"):
|
|
||||||
discount_price(100.0, 150.0)
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user