diff --git a/.gitignore b/.gitignore index 3c3629e64..f9cca446e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +**/.venv/ \ No newline at end of file diff --git a/sprint5-prep-exercises/classes_and_objects.py b/sprint5-prep-exercises/classes_and_objects.py new file mode 100644 index 000000000..0f6575603 --- /dev/null +++ b/sprint5-prep-exercises/classes_and_objects.py @@ -0,0 +1,22 @@ +class Person: + def __init__(self, name: str, age: int, preferred_operating_system: str) -> None: + self.name = name + self.age = age + self.preferred_operating_system = preferred_operating_system + + +def is_adult(person: Person) -> bool: + return person.age >= 18 + + +def get_postal_address(person: Person) -> str: + return person.address + + +imran = Person("Imran", 22, "Ubuntu") +eliza = Person("Eliza", 34, "Arch Linux") + +print(imran.name) +print(eliza.name) +print(is_adult(imran)) +print(is_adult(eliza)) \ No newline at end of file diff --git a/sprint5-prep-exercises/dataclass_exercise.py b/sprint5-prep-exercises/dataclass_exercise.py new file mode 100644 index 000000000..fb1bd70e3 --- /dev/null +++ b/sprint5-prep-exercises/dataclass_exercise.py @@ -0,0 +1,30 @@ +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 = date.today() + has_had_birthday_this_year = (today.month, today.day) >= ( + self.date_of_birth.month, + self.date_of_birth.day, + ) + age = today.year - self.date_of_birth.year + if not has_had_birthday_this_year: + age -= 1 + return age >= 18 + + +imran = Person("Imran", date(2002, 6, 15), "Ubuntu") +imran2 = Person("Imran", date(2002, 6, 15), "Ubuntu") +eliza = Person("Eliza", date(1990, 11, 20), "Arch Linux") + +print(imran) +print(imran == imran2) +print(imran.is_adult()) +print(eliza.is_adult()) \ No newline at end of file diff --git a/sprint5-prep-exercises/enums.py b/sprint5-prep-exercises/enums.py new file mode 100644 index 000000000..d4b334b2c --- /dev/null +++ b/sprint5-prep-exercises/enums.py @@ -0,0 +1,108 @@ +from dataclasses import dataclass +from enum import Enum +import sys +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 + + +def parse_age(raw_age: str) -> int: + age = int(raw_age) + if age <= 0: + raise ValueError("Age must be a positive integer") + return age + + +def parse_operating_system(raw_operating_system: str) -> OperatingSystem: + normalised_value = raw_operating_system.strip().lower() + for operating_system in OperatingSystem: + if normalised_value == operating_system.value.lower() or normalised_value == operating_system.name.lower(): + return operating_system + + valid_values = ", ".join(operating_system.value for operating_system in OperatingSystem) + raise ValueError(f"Preferred operating system must be one of: {valid_values}") + + +def create_person_from_input() -> Person: + name = input("Name: ").strip() + if not name: + raise ValueError("Name cannot be empty") + + raw_age = input("Age: ").strip() + raw_operating_system = input("Preferred operating system (Ubuntu, Arch Linux, macOS): ").strip() + + age = parse_age(raw_age) + preferred_operating_system = parse_operating_system(raw_operating_system) + + return Person(name=name, age=age, preferred_operating_system=preferred_operating_system) + + +def count_laptops_for_operating_system(laptops: List[Laptop], operating_system: OperatingSystem) -> int: + return sum(1 for laptop in laptops if laptop.operating_system == operating_system) + + +def find_most_available_operating_system(laptops: List[Laptop]) -> OperatingSystem: + counts = { + operating_system: count_laptops_for_operating_system(laptops, operating_system) + for operating_system in OperatingSystem + } + return max(counts, key=lambda operating_system: counts[operating_system]) + +def main() -> None: + 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), + ] + + try: + person = create_person_from_input() + except ValueError as error: + print(error, file=sys.stderr) + raise SystemExit(1) + + possible_laptops = find_possible_laptops(laptops, person) + preferred_count = len(possible_laptops) + print(f"The library has {preferred_count} laptop(s) with {person.preferred_operating_system.value}.") + + best_operating_system = find_most_available_operating_system(laptops) + best_count = count_laptops_for_operating_system(laptops, best_operating_system) + + if best_operating_system != person.preferred_operating_system and best_count > preferred_count: + print( + f"If you're willing to accept {best_operating_system.value}, " + f"you're more likely to get a laptop ({best_count} available)." + ) + + +if __name__ == "__main__": + main() diff --git a/sprint5-prep-exercises/generics_exercise.py b/sprint5-prep-exercises/generics_exercise.py new file mode 100644 index 000000000..4b7fb5d5d --- /dev/null +++ b/sprint5-prep-exercises/generics_exercise.py @@ -0,0 +1,35 @@ +from dataclasses import dataclass +from datetime import date +from typing import List + + +@dataclass(frozen=True) +class Person: + name: str + date_of_birth: date + children: List["Person"] + + @property + def age(self) -> int: + today = date.today() + has_had_birthday_this_year = (today.month, today.day) >= ( + self.date_of_birth.month, + self.date_of_birth.day, + ) + years = today.year - self.date_of_birth.year + if not has_had_birthday_this_year: + years -= 1 + return years + + +fatma = Person(name="Fatma", date_of_birth=date(2017, 5, 3), children=[]) +aisha = Person(name="Aisha", date_of_birth=date(2019, 9, 12), children=[]) + +imran = Person(name="Imran", date_of_birth=date(1991, 2, 10), children=[fatma, aisha]) + +def print_family_tree(person: Person) -> None: + print(person.name) + for child in person.children: + print(f"- {child.name} ({child.age})") + +print_family_tree(imran) \ No newline at end of file diff --git a/sprint5-prep-exercises/inheritance.py b/sprint5-prep-exercises/inheritance.py new file mode 100644 index 000000000..4ee428edb --- /dev/null +++ b/sprint5-prep-exercises/inheritance.py @@ -0,0 +1,47 @@ +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: list[str] = [] + + def change_last_name(self, last_name: str) -> 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()) +if isinstance(person2, Child): + print(person2.get_full_name()) + person2.change_last_name("Tyurina") + print(person2.get_name()) + print(person2.get_full_name()) +else: + print("Parent does not have get_full_name() or change_last_name().") + +# Predictions vs actual output (play computer): +# 1) Child inherits get_name() from Parent. +# 2) Child-specific get_full_name() shows the maiden name after changing last name. +# 3) Parent supports get_name() only; Child-only methods are unavailable on Parent. + +""" When I played computer, I predicted that Child would inherit get_name() from Parent, so person1.get_name() should work exactly like in Parent, while person1.get_full_name() would only exist on Child and add extra detail after a last-name change. Running the code confirmed this: before changing the name, both methods show “Elizaveta Alekseeva”, and after change_last_name("Tyurina"), get_full_name() includes the maiden-name suffix (née Alekseeva). For person2 (a Parent), only get_name() is available, which shows that inheritance gives Child all parent behavior plus additional child-specific methods, but not the other way around. """ diff --git a/sprint5-prep-exercises/methods.py b/sprint5-prep-exercises/methods.py new file mode 100644 index 000000000..67f5981d9 --- /dev/null +++ b/sprint5-prep-exercises/methods.py @@ -0,0 +1,28 @@ +from datetime import date + + +class Person: + def __init__(self, name: str, date_of_birth: date, preferred_operating_system: str) -> None: + self.name = name + self.date_of_birth = date_of_birth + self.preferred_operating_system = preferred_operating_system + + def is_adult(self) -> bool: + today = date.today() + has_had_birthday_this_year = (today.month, today.day) >= ( + self.date_of_birth.month, + self.date_of_birth.day, + ) + age = today.year - self.date_of_birth.year + if not has_had_birthday_this_year: + age -= 1 + return age >= 18 + + +imran = Person("Imran", date(2002, 6, 15), "Ubuntu") +eliza = Person("Eliza", date(1990, 11, 20), "Arch Linux") + +print(imran.name) +print(imran.is_adult()) +print(eliza.name) +print(eliza.is_adult()) diff --git a/sprint5-prep-exercises/type_checking.py b/sprint5-prep-exercises/type_checking.py new file mode 100644 index 000000000..76e29a45c --- /dev/null +++ b/sprint5-prep-exercises/type_checking.py @@ -0,0 +1,30 @@ +def open_account(balances: dict[str, int], name: str, amount: int) -> None: + balances[name] = amount + +def sum_balances(accounts: dict[str, int]) -> 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", 913) +open_account(balances, "Olya", 713) + +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/sprint5-prep-exercises/type_guided_refactorings.py b/sprint5-prep-exercises/type_guided_refactorings.py new file mode 100644 index 000000000..a07a4663e --- /dev/null +++ b/sprint5-prep-exercises/type_guided_refactorings.py @@ -0,0 +1,42 @@ +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", "Ubuntu"]), +] + +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}: {possible_laptops}") \ No newline at end of file