From 002a78b5fd740eaaf40463c75978caf25e23b161 Mon Sep 17 00:00:00 2001 From: Emilianouz <135679131+Emilianouz@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:39:34 +0000 Subject: [PATCH 1/3] Sprint 5 Prep exercises. --- .gitignore | 4 + prep-sprint5/class-object-person.py | 40 +++++++++ prep-sprint5/dataclass-person.py | 27 ++++++ prep-sprint5/enums.py | 108 ++++++++++++++++++++++++ prep-sprint5/generics.py | 21 +++++ prep-sprint5/inheritance.py | 47 +++++++++++ prep-sprint5/methods-person.py | 24 ++++++ prep-sprint5/mypy-checking.py | 30 +++++++ prep-sprint5/type-guided-refactoring.py | 52 ++++++++++++ 9 files changed, 353 insertions(+) create mode 100644 prep-sprint5/class-object-person.py create mode 100644 prep-sprint5/dataclass-person.py create mode 100644 prep-sprint5/enums.py create mode 100644 prep-sprint5/generics.py create mode 100644 prep-sprint5/inheritance.py create mode 100644 prep-sprint5/methods-person.py create mode 100644 prep-sprint5/mypy-checking.py create mode 100644 prep-sprint5/type-guided-refactoring.py diff --git a/.gitignore b/.gitignore index 3c3629e64..ec685bab8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ node_modules +implement-cowsay/.venv +**/package.json +**/package-lock.json +implement-cowsay/requirements.txt \ No newline at end of file diff --git a/prep-sprint5/class-object-person.py b/prep-sprint5/class-object-person.py new file mode 100644 index 000000000..a025ed0f3 --- /dev/null +++ b/prep-sprint5/class-object-person.py @@ -0,0 +1,40 @@ +''' +Run it through mypy - notice that no errors are reported - mypy understands +that Person has a property named age so is happy with the function. +Write a new function in the file that accepts a Person as a parameter +and tries to access a property that doesn't exist. Run it through mypy +and check that it does report an error. +''' + +class Person: + def __init__(self, name: str, age: int, preferred_operating_system: str): + self.name = name + self.age = age + self.preferred_operating_system = preferred_operating_system + +imran = Person("Imran", 22, "Ubuntu") +print(imran.name) +#print(imran.address) + +eliza = Person("Eliza", 34, "Arch Linux") +print(eliza.name) +#print(eliza.address) + +def is_adult(person: Person) -> bool: + return person.age >= 18 + +print(is_adult(imran)) + +''' adding the function:''' +def read_genre(person: Person) -> str: + if person.genre == "M": + return "Male" + else: + return "Female" + +''' returns: +cyf@cyfs-MacBook-Pro prep-sprint5 % mypy class-object-person.py +class-object-person.py:29: error: "Person" has no attribute "genre" [attr-defined] +Found 1 error in 1 file (checked 1 source file) + +''' diff --git a/prep-sprint5/dataclass-person.py b/prep-sprint5/dataclass-person.py new file mode 100644 index 000000000..c1c6a9acc --- /dev/null +++ b/prep-sprint5/dataclass-person.py @@ -0,0 +1,27 @@ +'''Write a Person class using @datatype which uses a datetime.date for date of birth, +rather than an int for age. +Re-add the is_adult method to it.''' +from dataclasses import dataclass +from datetime import date + +@dataclass(frozen=True) +class Person: + name: str + date_of_birth: date + preferred_operating_system: str + + def is_adult(self) -> bool: + today_year = date.today().year + birth_year = self.date_of_birth.year + age = today_year - birth_year + + if date.today().month > self.date_of_birth.month: + return age >= 18 + elif date.today().month == self.date_of_birth.month: + return date.today().day >= self.date_of_birth.day and age >= 18 + else: + return age - 1 >= 18 + +imran = Person("Imran", date(2002, 9, 21), "Ubuntu") +print(imran) +print("Is adult?", imran.is_adult()) \ No newline at end of file diff --git a/prep-sprint5/enums.py b/prep-sprint5/enums.py new file mode 100644 index 000000000..a288483e6 --- /dev/null +++ b/prep-sprint5/enums.py @@ -0,0 +1,108 @@ +''' +Write a program which: +1.Already has a list of Laptops that a library has to lend out. +2.Accepts user input to create a new Person - it should use the input function to +read a person's name, age, and preferred operating system. +3.Tells the user how many laptops the library has that have that operating system. +4.If there is an operating system that has more laptops available, tells the user +that if they're willing to accept that operating system they're more likely to get a laptop. + +You should convert the age and preferred operating system input from the user +into more constrained types as quickly as possible, and should output errors to +stderr and terminate the program with a non-zero exit code if the user input bad values. +''' + +from dataclasses import dataclass +from enum import Enum +from typing import List + +class OperatingSystem(Enum): + MACOS = "macOS" + ARCH = "Arch Linux" + UBUNTU = "Ubuntu" + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_system: OperatingSystem + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: OperatingSystem + + +def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: + possible_laptops = [] + for laptop in laptops: + if laptop.operating_system == person.preferred_operating_system: + possible_laptops.append(laptop) + return possible_laptops + + +people = [ + Person(name="Imran", age=22, preferred_operating_system=OperatingSystem.UBUNTU), + Person(name="Eliza", age=34, preferred_operating_system=OperatingSystem.ARCH), +] + +laptops = [ + Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH), + Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU), + Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU), + Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS), +] + +print("Library laptops:") # 1. list of laptops +for laptop in laptops: + print(f"- ID:{laptop.id} {laptop.operating_system.value} - {laptop.manufacturer} {laptop.model} Size: {laptop.screen_size_in_inches}") + +# receiving values from input to create person: +name = input("Enter your name: ") +age = int(input("Enter your age: ")) + +print("Choose preferred operating system:") +print("1. macOS") +print("2. Arch Linux") +print("3. Ubuntu") +choice = input(">> ") + +os_map = { + "1": OperatingSystem.MACOS, + "2": OperatingSystem.ARCH, + "3": OperatingSystem.UBUNTU, +} + +preferred_os = os_map.get(choice) + +person = Person(name=name, age=age, preferred_operating_system=preferred_os) + +print("Created person:", person) +# counts how many laptops there are with that OS +matches_count = sum( + 1 for laptop in laptops + if laptop.operating_system == person.preferred_operating_system +) +print(f"There are {matches_count} laptop(s) with {person.preferred_operating_system.value}.") + +count_laptops_by_os = { + os_type: sum(1 for laptop in laptops if laptop.operating_system == os_type) + for os_type in OperatingSystem +} + +best_os = max(count_laptops_by_os, key = count_laptops_by_os.get) +best_os_count = count_laptops_by_os[best_os] + +if best_os != person.preferred_operating_system: + print( + f"There are more laptops available with {best_os.value} " + f"({best_os_count} laptops). If you're willing to use that OS, " + "you're more likely to get a laptop." + ) +else: + print("Good choice! Your preferred OS has the highest availability.") + diff --git a/prep-sprint5/generics.py b/prep-sprint5/generics.py new file mode 100644 index 000000000..c5fc7a949 --- /dev/null +++ b/prep-sprint5/generics.py @@ -0,0 +1,21 @@ +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + age: int + children: List["Person"] + +fatma = Person(name="Fatma", age=20, children=[]) +aisha = Person(name="Aisha", age=30, children=[]) + +imran = Person(name="Imran", age=50, children=[fatma, aisha]) +maria = Person(name="maria", age=38,children=[fatma]) + +def print_family_tree(person: Person) -> None: + print(person.name) + for child in person.children: + print(f"- {child.name} ({child.age})") + +print_family_tree(maria) \ No newline at end of file diff --git a/prep-sprint5/inheritance.py b/prep-sprint5/inheritance.py new file mode 100644 index 000000000..d6ebb6235 --- /dev/null +++ b/prep-sprint5/inheritance.py @@ -0,0 +1,47 @@ +''' +Play computer with this code. Predict what you expect each line will do. +Then run the code and check your predictions. (If any lines cause errors, +you may need to comment them out to check later lines). +''' + +class Parent: + def __init__(self, first_name: str, last_name: str): + self.first_name = first_name + self.last_name = last_name + + def get_name(self) -> str: + return f"{self.first_name} {self.last_name}" + + +class Child(Parent): + def __init__(self, first_name: str, last_name: str): + super().__init__(first_name, last_name) + self.previous_last_names = [] + + def change_last_name(self, last_name) -> None: + self.previous_last_names.append(self.last_name) + self.last_name = last_name + + def get_full_name(self) -> str: + suffix = "" + if len(self.previous_last_names) > 0: + suffix = f" (née {self.previous_last_names[0]})" + return f"{self.first_name} {self.last_name}{suffix}" + +person1 = Child("Elizaveta", "Alekseeva") +print(person1.get_name()) +print(person1.get_full_name()) +person1.change_last_name("Tyurina") +print(person1.get_name()) +print(person1.get_full_name()) + +person2 = Parent("Elizaveta", "Alekseeva") +print(person2.get_name()) +#print(person2.get_full_name()) +#person2.change_last_name("Tyurina") +#print(person2.get_name()) +#print(person2.get_full_name()) + +# PREDICTION: +# Printing from person1 (Child) will work fine, as the Child class inherits from the Parent class and has access to its own methods. +# Printing from person2 (Parent) will get error because the Parent class does not have the methods get_full_name and change_last_name defined in the Child class. diff --git a/prep-sprint5/methods-person.py b/prep-sprint5/methods-person.py new file mode 100644 index 000000000..7a49086e2 --- /dev/null +++ b/prep-sprint5/methods-person.py @@ -0,0 +1,24 @@ + +from datetime import date + +class Person: + def __init__(self, name: str, date_of_birth: date, preferred_operating_system: str): + self.name = name + self.date_of_birth = date_of_birth + self.preferred_operating_system = preferred_operating_system + + def is_adult(self) -> bool: + today_year = date.today().year + birth_year = self.date_of_birth.year + age = today_year - birth_year + + if date.today().month > self.date_of_birth.month: + return age >= 18 + elif date.today().month == self.date_of_birth.month: + return date.today().day >= self.date_of_birth.day and age >= 18 + else: + return age - 1 >= 18 + + +imran = Person("Imran", date(2007,1,19), "Ubuntu") +print(imran.is_adult()) \ No newline at end of file diff --git a/prep-sprint5/mypy-checking.py b/prep-sprint5/mypy-checking.py new file mode 100644 index 000000000..fdf823734 --- /dev/null +++ b/prep-sprint5/mypy-checking.py @@ -0,0 +1,30 @@ +def open_account(balances:dict, name:str, amount:int) -> None: + balances[name] = amount + +def sum_balances(accounts) -> int: + total = 0 + for name, pence in accounts.items(): + print(f"{name} had balance {pence}") + total += pence + return total + +def format_pence_as_string(total_pence: int) -> str: + if total_pence < 100: + return f"{total_pence}p" + pounds = int(total_pence / 100) + pence = total_pence % 100 + return f"£{pounds}.{pence:02d}" + +balances = { + "Sima": 700, + "Linn": 545, + "Georg": 831, +} + +open_account(balances,"Tobi", int(9.13 * 100)) +open_account(balances,"Olya", int(7.13 * 100)) + +total_pence = sum_balances(balances) +total_string = format_pence_as_string(total_pence) + +print(f"The bank accounts total {total_string}") \ No newline at end of file diff --git a/prep-sprint5/type-guided-refactoring.py b/prep-sprint5/type-guided-refactoring.py new file mode 100644 index 000000000..1f47b6f38 --- /dev/null +++ b/prep-sprint5/type-guided-refactoring.py @@ -0,0 +1,52 @@ +'''Try changing the type annotation of Person.preferred_operating_system from str to List[str]. +Run mypy on the code. +It tells us different places that our code is now wrong, because we’re passing values of the wrong type. +We probably also want to rename our field - lists are plural. Rename the field to preferred_operating_systems. +Run mypy again. +Fix all of the places that mypy tells you need changing. Make sure the program works as you’d expect.''' + +from dataclasses import dataclass +from typing import List + +@dataclass(frozen=True) +class Person: + name: str + age: int + preferred_operating_systems: List[str] + + +@dataclass(frozen=True) +class Laptop: + id: int + manufacturer: str + model: str + screen_size_in_inches: float + operating_system: str + + +def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop]: + possible_laptops = [] + for laptop in laptops: + if laptop.operating_system in person.preferred_operating_systems: + possible_laptops.append(laptop) + return possible_laptops + + +people = [ + Person(name="Imran", age=22, preferred_operating_systems=["Ubuntu", "Arch Linux"]), + Person(name="Eliza", age=34, preferred_operating_systems=["Arch Linux"]), +] + +laptops = [ + Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system="Arch Linux"), + Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="Ubuntu"), + Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system="ubuntu"), + Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system="macOS"), +] + +for person in people: + possible_laptops = find_possible_laptops(laptops, person) + print(f"Possible laptops for {person.name}: ") + for each in possible_laptops: + print(f" - ID:{each.id} {each.manufacturer} {each.model} ({each.operating_system})") + print() From 4cd0d89d77c933fd9b93c00f747b40021cabbe6b Mon Sep 17 00:00:00 2001 From: Emiliano Uruena Date: Mon, 16 Feb 2026 10:50:45 +0000 Subject: [PATCH 2/3] Enhance age input validation and output display Added error handling for age input and improved output formatting. --- prep-sprint5/enums.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/prep-sprint5/enums.py b/prep-sprint5/enums.py index a288483e6..26e031ae5 100644 --- a/prep-sprint5/enums.py +++ b/prep-sprint5/enums.py @@ -15,6 +15,7 @@ from dataclasses import dataclass from enum import Enum from typing import List +import sys class OperatingSystem(Enum): MACOS = "macOS" @@ -63,7 +64,13 @@ def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop] # receiving values from input to create person: name = input("Enter your name: ") -age = int(input("Enter your age: ")) + +try: + age_input = input("Enter your age: ") + age = int(age_input) +except ValueError: + print("Error: Age must be a number.", file=sys.stderr) + sys.exit(1) print("Choose preferred operating system:") print("1. macOS") @@ -79,9 +86,17 @@ def find_possible_laptops(laptops: List[Laptop], person: Person) -> List[Laptop] preferred_os = os_map.get(choice) +if preferred_os is None: + print("Error: Invalid operating system selection.", file=sys.stderr) + sys.exit(1) + person = Person(name=name, age=age, preferred_operating_system=preferred_os) -print("Created person:", person) +print( + f"\nWelcome {person.name}! " + f"Age: {person.age}, " + f"Preferred OS: {person.preferred_operating_system.value}" +) # counts how many laptops there are with that OS matches_count = sum( 1 for laptop in laptops From 2223fb7209c6b85a802736eab1e87b9fd2cbdc8a Mon Sep 17 00:00:00 2001 From: Emiliano Uruena Date: Mon, 16 Feb 2026 11:02:34 +0000 Subject: [PATCH 3/3] Enhance print_family_tree with indentation Updated print_family_tree to include indentation and level. --- prep-sprint5/generics.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/prep-sprint5/generics.py b/prep-sprint5/generics.py index c5fc7a949..c742b97a4 100644 --- a/prep-sprint5/generics.py +++ b/prep-sprint5/generics.py @@ -13,9 +13,11 @@ class Person: imran = Person(name="Imran", age=50, children=[fatma, aisha]) maria = Person(name="maria", age=38,children=[fatma]) -def print_family_tree(person: Person) -> None: - print(person.name) +def print_family_tree(person: Person, level: int = 0) -> None: + indent = " " * level + print(f"{indent}{person.name} ({person.age})") + for child in person.children: - print(f"- {child.name} ({child.age})") + print_family_tree(child, level + 1) -print_family_tree(maria) \ No newline at end of file +print_family_tree(maria)