From 548b127b0ddeef242ccabdf07603b5a0e546cc83 Mon Sep 17 00:00:00 2001 From: Daniele Fiocca Date: Thu, 13 Nov 2025 06:56:53 +0100 Subject: [PATCH 1/2] First solution 002-polygon-area-calculator --- projects/002-polygon-area-calculator/main.py | 20 --- .../python/main.py | 5 + .../python/poligon_calculator/__init__.py | 0 .../python/poligon_calculator/controller.py | 129 ++++++++++++++++++ .../python/poligon_calculator/input.py | 92 +++++++++++++ .../python/poligon_calculator/output.py | 19 +++ .../python/poligon_calculator/rectangle.py | 35 +++++ .../poligon_calculator/shape_calculator.py | 31 +++++ .../python/poligon_calculator/square.py | 18 +++ .../poligon_calculator/tests/__init__.py | 0 .../poligon_calculator/tests}/test_module.py | 6 +- .../shape_calculator.py | 6 - projects/__init__.py | 0 13 files changed, 332 insertions(+), 29 deletions(-) delete mode 100644 projects/002-polygon-area-calculator/main.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/__init__.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/controller.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/input.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/output.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/square.py create mode 100644 projects/002-polygon-area-calculator/python/poligon_calculator/tests/__init__.py rename projects/002-polygon-area-calculator/{ => python/poligon_calculator/tests}/test_module.py (95%) delete mode 100644 projects/002-polygon-area-calculator/shape_calculator.py create mode 100644 projects/__init__.py diff --git a/projects/002-polygon-area-calculator/main.py b/projects/002-polygon-area-calculator/main.py deleted file mode 100644 index d4354fa..0000000 --- a/projects/002-polygon-area-calculator/main.py +++ /dev/null @@ -1,20 +0,0 @@ -# This entrypoint file to be used in development. Start by reading README.md -import shape_calculator -from unittest import main - - -rect = shape_calculator.Rectangle(5, 10) -print(rect.get_area()) -rect.set_width(3) -print(rect.get_perimeter()) -print(rect) - -sq = shape_calculator.Square(9) -print(sq.get_area()) -sq.set_side(4) -print(sq.get_diagonal()) -print(sq) - - -# Run unit tests automatically -main(module='test_module', exit=False) \ No newline at end of file diff --git a/projects/002-polygon-area-calculator/python/main.py b/projects/002-polygon-area-calculator/python/main.py index e69de29..a505b2d 100644 --- a/projects/002-polygon-area-calculator/python/main.py +++ b/projects/002-polygon-area-calculator/python/main.py @@ -0,0 +1,5 @@ +from poligon_calculator.controller import Controller + +if __name__ == "__main__": + controller = Controller() + controller.start() \ No newline at end of file diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/__init__.py b/projects/002-polygon-area-calculator/python/poligon_calculator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py b/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py new file mode 100644 index 0000000..0976d5f --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py @@ -0,0 +1,129 @@ +from poligon_calculator.shape_calculator import ShapeCalculator +from poligon_calculator.input import Input +from poligon_calculator.output import Output + +class Controller: + def __init__(self): + self.shape_calculator = ShapeCalculator() + + def start(self): + + Output.welcome() + choice = Input.get_intro_menu_selection() + + + match choice: + case 1: + self.handle_rect() + case 2: + self.handle_square() + case 3: + self.handle_square_and_rect() + case _: + print("You didn't enter a valid choice") + return + self.second_menu() + + + + def handle_rect(self): + width, height = Input.create_rectangle() + self.shape_calculator.create_rectangle(width,height) + print("Rectangle create successfully") + + def handle_square(self): + side = Input.create_square() + self.shape_calculator.create_square(side) + print("Square create successfully") + + def handle_square_and_rect(self): + self.handle_rect() + self.handle_square() + + def second_menu(self): + while True: + choice = Input.menu_after_shape() + + match choice: + case 1: + self.show_specs() + case 2: + self.calculate_square_in_rect() + + case 3: + self.modify_shapes() + case 4: + print("Thank you for using this program") + break + case _: + print("You didn't enter a valid choice") + + + def show_specs(self): + shape_choice = Input.get_shape() + + + if shape_choice == 1: + if self.shape_calculator.rectangle: + Output.print_shape_specs(self.shape_calculator.rectangle) + else: + print("You didn't enter a valid shape") + elif shape_choice == 2: + if self.shape_calculator.square: + Output.print_shape_specs(self.shape_calculator.square) + else: + print("You didn't enter a valid shape") + + def calculate_square_in_rect(self): + try: + if not self.shape_calculator.rectangle: + choice = Input.if_not_rectangle() + if choice == "y": + self.handle_rect() + else: + return + + if not self.shape_calculator.square: + choice = Input.if_not_square() + if choice == 'y': + self.handle_square() + else: + return + + amount = self.shape_calculator.square_in_rectangle() + if amount is not None: + Output.print_how_square_in_rect(amount) + except Exception as e: + print(f"Error calculating square in rectangle: {e}") + + + + def modify_shapes(self): + try: + if not self.shape_calculator.rectangle and not self.shape_calculator.square: + print("No shape has been created.") + return + + result = Input.modify_measures() + + if result is None: + return + + if result[0] == "rectangle": + if self.shape_calculator.rectangle: + _, width, height = result + self.shape_calculator.set_width(width) + self.shape_calculator.set_height(height) + print("Rectangle modify successfully") + else: + print("You didn't enter a valid shape") + + elif result[0] == "square": + if self.shape_calculator.square: + _, side = result + self.shape_calculator.set_side(side) + print("Square modify successfully") + else: + print("You didn't enter a valid shape") + except Exception as e: + print(f"Error modifing shape: {e}") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/input.py b/projects/002-polygon-area-calculator/python/poligon_calculator/input.py new file mode 100644 index 0000000..8b60a7d --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/input.py @@ -0,0 +1,92 @@ +class Input: + + def create_rectangle(): + while True: + width = Input.validate_positive_number("Insert width: ") + height = Input.validate_positive_number("Insert height: ") + if width != height: + return width, height + else: + print("Width and height must be greater than or equal to two.") + + def create_square(): + width = Input.validate_positive_number("Insert side length: ") + return width + + + def get_intro_menu_selection(): + print("What shape do you want to work on?") + print("1. Rectangle") + print("2. Square") + print("3. Rectangle / Square\n") + try: + while (choice := (int(input("Enter your choice: ")))) not in (1, 2, 3): + print("Invalid input. Try again.") + return choice + except ValueError: + print("Please enter a number.") + + + def validate_positive_number(prompt): + while True: + try: + number = int(input(prompt)) + if number > 0: + return number + + print("Please enter a positive number") + except ValueError: + print("Please enter a positive number") + + def if_not_square(): + print("For use this function, you must create a square shape.") + while (choose := (input("Do you want create it?(y/n) "))) not in ("y", "n"): + print("Please enter y or n") + return choose + + def if_not_rectangle(): + print("For use this function, you must create a rectangle shape.") + while (choose := (input("Do you want create it?(y/n) "))) not in ("y", "n"): + print("Please enter y or n") + return choose + + def get_shape(): + print("Which shape do you want to view?") + print("1. Rectangle") + print("2. Square") + try: + while (choice := (int(input("Choose shape: ")))) not in (1, 2): + print("Invalid input. Try again.") + return choice + except ValueError: + print("Please enter a number.") + + def menu_after_shape(): + print("1. Calculate shape specs (Area, Perimeter, Diagonal)") + print("2. Calculate amount square inside rectangle") + print("3. Modify measures") + print("4. Exit") + try: + while (choice := (int(input("Enter your choice: ")))) not in (1, 2, 3, 4): + print("Invalid input. Try again.") + return choice + except ValueError: + print("Please enter a number.") + + def modify_measures(): + print("\nWhich shape do you want to modify?") + print("1. Rectangle") + print("2. Square") + + try: + while (choice := int(input("Choose shape: "))) not in (1, 2): + print("Please enter 1 or 2") + if choice == 1: + width = Input.validate_positive_number("Enter new width: ") + height = Input.validate_positive_number("Enter new height: ") + return "rectangle", width, height + elif choice == 2: + side = Input.validate_positive_number("Enter new side: ") + return "square", side + except ValueError: + print("Please enter a valid number") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/output.py b/projects/002-polygon-area-calculator/python/poligon_calculator/output.py new file mode 100644 index 0000000..6111f35 --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/output.py @@ -0,0 +1,19 @@ +class Output: + + def welcome(): + print("WELCOME TO THE SHAPE CALCULATOR\n") + + + def print_shape_specs(shape): + specs = f"{shape}:\n" + specs += f"Width: {shape.width} Height: {shape.height}\n" + specs += f"Area: {shape.get_area()}\n" + specs += f"Perimeter: {shape.get_perimeter()}\n" + specs += f"Diagonal: {shape.get_diagonal():.2f}\n" + specs += f"Pic:\n{shape.get_picture()}" + print(specs) + + + + def print_how_square_in_rect(amount): + print(f"In rectangle area can stay {amount} square units") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py b/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py new file mode 100644 index 0000000..db8f23d --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py @@ -0,0 +1,35 @@ +class Rectangle: + def __init__(self, width, height): + self.width = width + self.height = height + + def set_width(self, width): + self.width = width + + def set_height(self, height): + self.height = height + + def get_area(self): + return self.width * self.height + + def get_perimeter(self): + return 2 * self.width + 2 * self.height + + def get_diagonal(self): + return (self.width ** 2 + self.height ** 2) ** 0.5 + + def get_picture(self): + if self.width > 50 or self.height > 50: + return "Too big for picture." + design = "" + for i in range(self.height): + design += ("*" * self.width) + "\n" + return design + + def get_amount_inside(self, shape): + return (self.width // shape.width) * (self.height // shape.height) + + + def __str__(self): + return f"Rectangle (width={self.width}, height={self.height}" + diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py b/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py new file mode 100644 index 0000000..6b86a18 --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py @@ -0,0 +1,31 @@ +from poligon_calculator.rectangle import Rectangle +from poligon_calculator.square import Square + +class ShapeCalculator: + def __init__(self): + self.rectangle = None + self.square = None + + def create_rectangle(self, width, height): + self.rectangle = Rectangle(width, height) + + def create_square(self, side): + self.square = Square(side) + + + def set_height(self, height): + if self.rectangle: + self.rectangle.set_height(height) + + def set_width(self, width): + if self.rectangle: + self.rectangle.set_width(width) + def set_side(self, side): + if self.square: + self.square.set_side(side) + + def square_in_rectangle(self): + if self.rectangle is not None and self.square is not None: + amount = self.rectangle.get_amount_inside(self.square) + return amount + return None \ No newline at end of file diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/square.py b/projects/002-polygon-area-calculator/python/poligon_calculator/square.py new file mode 100644 index 0000000..a0be165 --- /dev/null +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/square.py @@ -0,0 +1,18 @@ +from poligon_calculator.rectangle import Rectangle + + +class Square(Rectangle): + def __init__(self, side): + super().__init__(height= side, width= side) + self.width = side + self.height = side + self.side = side + + def set_side(self, side): + self.height = side + self.width = side + + + + def __str__(self): + return f"Square(side={self.width}" diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/tests/__init__.py b/projects/002-polygon-area-calculator/python/poligon_calculator/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/002-polygon-area-calculator/test_module.py b/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py similarity index 95% rename from projects/002-polygon-area-calculator/test_module.py rename to projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py index d263a4b..52c5ce9 100644 --- a/projects/002-polygon-area-calculator/test_module.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py @@ -10,17 +10,17 @@ def setUp(self): def test_subclass(self): actual = issubclass(shape_calculator.Square, shape_calculator.Rectangle) expected = True - self.assertEqual(actual, expected, 'Expected Square class to be a subclass of the Rectangle class.') + self.assertEqual(actual, expected, 'Expected Square class_config to be a subclass of the Rectangle class_config.') def test_distinct_classes(self): actual = shape_calculator.Square is not shape_calculator.Rectangle expected = True - self.assertEqual(actual, expected, 'Expected Square class to be a distinct class from the Rectangle class.') + self.assertEqual(actual, expected, 'Expected Square class_config to be a distinct class_config from the Rectangle class_config.') def test_square_is_square_and_rectangle(self): actual = isinstance(self.sq, shape_calculator.Square) and isinstance(self.sq, shape_calculator.Rectangle) expected = True - self.assertEqual(actual, expected, 'Expected square object to be an instance of the Square class and the Rectangle class.') + self.assertEqual(actual, expected, 'Expected square object to be an instance of the Square class_config and the Rectangle class_config.') def test_rectangle_string(self): actual = str(self.rect) diff --git a/projects/002-polygon-area-calculator/shape_calculator.py b/projects/002-polygon-area-calculator/shape_calculator.py deleted file mode 100644 index e210be6..0000000 --- a/projects/002-polygon-area-calculator/shape_calculator.py +++ /dev/null @@ -1,6 +0,0 @@ -class Rectangle: - - - - -class Square: diff --git a/projects/__init__.py b/projects/__init__.py new file mode 100644 index 0000000..e69de29 From ac11525cf8e6fee9a3912628ca40bffb8d4bc556 Mon Sep 17 00:00:00 2001 From: Daniele Fiocca Date: Thu, 13 Nov 2025 22:27:11 +0100 Subject: [PATCH 2/2] refactor: add type hints and docstrings to all classes for clarity --- .../python/poligon_calculator/controller.py | 54 ++++++++---- .../python/poligon_calculator/input.py | 76 ++++++++++++++--- .../python/poligon_calculator/output.py | 18 +++- .../python/poligon_calculator/rectangle.py | 84 +++++++++++++++--- .../poligon_calculator/shape_calculator.py | 61 +++++++++++-- .../python/poligon_calculator/square.py | 30 +++++-- .../poligon_calculator/tests/test_module.py | 85 ++++++++++++++----- 7 files changed, 326 insertions(+), 82 deletions(-) diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py b/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py index 0976d5f..886d18d 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/controller.py @@ -1,17 +1,22 @@ -from poligon_calculator.shape_calculator import ShapeCalculator from poligon_calculator.input import Input from poligon_calculator.output import Output +from poligon_calculator.shape_calculator import ShapeCalculator + class Controller: + """Main controller class responsible for managing user interactions + and delegating shape-related operations to the ShapeCalculator. + """ + def __init__(self): + """Initialize the Controller instance and create a ShapeCalculator object.""" self.shape_calculator = ShapeCalculator() - def start(self): - + def start(self) -> None: + """Start the program by displaying the intro menu and handling user choices.""" Output.welcome() choice = Input.get_intro_menu_selection() - match choice: case 1: self.handle_rect() @@ -24,23 +29,29 @@ def start(self): return self.second_menu() + def handle_rect(self) -> None: + """Handle the creation of a rectangle by collecting user input and storing it.""" - - def handle_rect(self): width, height = Input.create_rectangle() - self.shape_calculator.create_rectangle(width,height) + self.shape_calculator.create_rectangle(width, height) print("Rectangle create successfully") - def handle_square(self): + def handle_square(self) -> None: + """Handle the creation of a square by collecting user input and storing it.""" + side = Input.create_square() self.shape_calculator.create_square(side) print("Square create successfully") - def handle_square_and_rect(self): + def handle_square_and_rect(self) -> None: + """Handle the creation of both a rectangle and a square.""" + self.handle_rect() self.handle_square() - def second_menu(self): + def second_menu(self) -> None: + """Display the secondary menu and handle subsequent user interactions.""" + while True: choice = Input.menu_after_shape() @@ -58,11 +69,11 @@ def second_menu(self): case _: print("You didn't enter a valid choice") + def show_specs(self) -> None: + """Display the specifications (area, perimeter, etc.) of the chosen shape.""" - def show_specs(self): shape_choice = Input.get_shape() - if shape_choice == 1: if self.shape_calculator.rectangle: Output.print_shape_specs(self.shape_calculator.rectangle) @@ -74,7 +85,13 @@ def show_specs(self): else: print("You didn't enter a valid shape") - def calculate_square_in_rect(self): + def calculate_square_in_rect(self) -> None: + """Calculate how many squares fit inside the rectangle. + + This method ensures that both shapes exist. If not, it prompts + the user to create them first. It then calls the calculator + to determine how many squares can fit within the rectangle. + """ try: if not self.shape_calculator.rectangle: choice = Input.if_not_rectangle() @@ -85,7 +102,7 @@ def calculate_square_in_rect(self): if not self.shape_calculator.square: choice = Input.if_not_square() - if choice == 'y': + if choice == "y": self.handle_square() else: return @@ -96,9 +113,12 @@ def calculate_square_in_rect(self): except Exception as e: print(f"Error calculating square in rectangle: {e}") + def modify_shapes(self) -> None: + """Allow the user to modify the measures of existing shapes. - - def modify_shapes(self): + Raises: + Exception: If an unexpected error occurs during modification. + """ try: if not self.shape_calculator.rectangle and not self.shape_calculator.square: print("No shape has been created.") @@ -126,4 +146,4 @@ def modify_shapes(self): else: print("You didn't enter a valid shape") except Exception as e: - print(f"Error modifing shape: {e}") + print(f"Error modifying shape: {e}") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/input.py b/projects/002-polygon-area-calculator/python/poligon_calculator/input.py index 8b60a7d..62bf3e5 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/input.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/input.py @@ -1,20 +1,35 @@ class Input: + """Class responsible for handling all user input operations.""" - def create_rectangle(): + def create_rectangle() -> tuple[int, int]: + """Prompt the user to input the dimensions of a rectangle. + + Returns: + tuple[int, int]: A tuple containing the width and height of the rectangle. + """ while True: - width = Input.validate_positive_number("Insert width: ") - height = Input.validate_positive_number("Insert height: ") + width: int = Input.validate_positive_number("Insert width: ") + height: int = Input.validate_positive_number("Insert height: ") if width != height: return width, height else: print("Width and height must be greater than or equal to two.") - def create_square(): - width = Input.validate_positive_number("Insert side length: ") + def create_square() -> int: + """Prompt the user to input the side length of a square. + + Returns: + int: The side length of the square. + """ + width: int = Input.validate_positive_number("Insert side length: ") return width + def get_intro_menu_selection() -> int | None: + """Display the main menu and get the user's choice of shape. - def get_intro_menu_selection(): + Returns: + int | None: The user's menu selection (1, 2, or 3), or None if invalid input is entered. + """ print("What shape do you want to work on?") print("1. Rectangle") print("2. Square") @@ -26,8 +41,15 @@ def get_intro_menu_selection(): except ValueError: print("Please enter a number.") + def validate_positive_number(prompt: str) -> int: + """Validate that the user input is a positive integer. + + Args: + prompt (str): The text prompt displayed to the user. - def validate_positive_number(prompt): + Returns: + int: A positive integer input by the user. + """ while True: try: number = int(input(prompt)) @@ -38,19 +60,35 @@ def validate_positive_number(prompt): except ValueError: print("Please enter a positive number") - def if_not_square(): + def if_not_square() -> str: + """Ask the user if they want to create a square when one does not exist. + + Returns: + str: 'y' if the user wants to create a square, 'n' otherwise. + """ + print("For use this function, you must create a square shape.") while (choose := (input("Do you want create it?(y/n) "))) not in ("y", "n"): print("Please enter y or n") return choose - def if_not_rectangle(): + def if_not_rectangle() -> str: + """Ask the user if they want to create a rectangle when one does not exist. + + Returns: + str: 'y' if the user wants to create a rectangle, 'n' otherwise. + """ print("For use this function, you must create a rectangle shape.") while (choose := (input("Do you want create it?(y/n) "))) not in ("y", "n"): print("Please enter y or n") return choose - def get_shape(): + def get_shape() -> int: + """Ask the user which shape they want to view. + + Returns: + int | None: 1 for rectangle, 2 for square, or None if invalid input is entered. + """ print("Which shape do you want to view?") print("1. Rectangle") print("2. Square") @@ -61,7 +99,12 @@ def get_shape(): except ValueError: print("Please enter a number.") - def menu_after_shape(): + def menu_after_shape() -> int: + """Display the post-creation menu and get the user's choice. + + Returns: + int | None: The user's menu selection (1–4), or None if invalid input is entered. + """ print("1. Calculate shape specs (Area, Perimeter, Diagonal)") print("2. Calculate amount square inside rectangle") print("3. Modify measures") @@ -73,7 +116,16 @@ def menu_after_shape(): except ValueError: print("Please enter a number.") - def modify_measures(): + def modify_measures() -> tuple[str, int, int] | tuple[str, int] | None: + """Allow the user to modify the dimensions of a shape. + + Returns: + tuple[str, int, int] | tuple[str, int] | None: + A tuple containing the shape type and its new dimensions, + or None if invalid input is entered. + - ("rectangle", width, height) + - ("square", side) + """ print("\nWhich shape do you want to modify?") print("1. Rectangle") print("2. Square") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/output.py b/projects/002-polygon-area-calculator/python/poligon_calculator/output.py index 6111f35..328273c 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/output.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/output.py @@ -1,10 +1,17 @@ class Output: + """Class responsible for displaying information to the user.""" - def welcome(): + def welcome() -> None: + """Display a welcome message at the start of the program.""" print("WELCOME TO THE SHAPE CALCULATOR\n") + def print_shape_specs(shape) -> None: + """Print the specifications of a given shape, including dimensions, + area, perimeter, diagonal, and a visual representation. - def print_shape_specs(shape): + Args: + shape: The shape object containing geometric data. + """ specs = f"{shape}:\n" specs += f"Width: {shape.width} Height: {shape.height}\n" specs += f"Area: {shape.get_area()}\n" @@ -13,7 +20,10 @@ def print_shape_specs(shape): specs += f"Pic:\n{shape.get_picture()}" print(specs) + def print_how_square_in_rect(amount: int) -> None: + """Print the number of squares that can fit inside a rectangle. - - def print_how_square_in_rect(amount): + Args: + amount (int): The number of squares that fit within the rectangle. + """ print(f"In rectangle area can stay {amount} square units") diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py b/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py index db8f23d..8406cfa 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/rectangle.py @@ -1,24 +1,70 @@ class Rectangle: - def __init__(self, width, height): - self.width = width - self.height = height + """Class representing a rectangle shape with width and height. + + Provides methods for computing geometric properties such as area, + perimeter, diagonal length, and ASCII visualization. + """ + + def __init__(self, width: int, height: int) -> None: + """Initialize a Rectangle instance. - def set_width(self, width): + Args: + width (int): The width of the rectangle. + height (int): The height of the rectangle. + """ + self.width: int = width + self.height: int = height + + def set_width(self, width: int) -> None: + """Set a new width for the rectangle. + + Args: + width (int): The new width value. + """ self.width = width - def set_height(self, height): + def set_height(self, height: int) -> None: + """Set a new height for the rectangle. + + Args: + height (int): The new height value. + """ self.height = height - def get_area(self): + def get_area(self) -> int: + """Calculate the area of the rectangle. + + Returns: + int: The area of the rectangle. + """ return self.width * self.height - def get_perimeter(self): + def get_perimeter(self) -> int: + """Calculate the perimeter of the rectangle. + + Returns: + int: The perimeter of the rectangle. + """ return 2 * self.width + 2 * self.height - def get_diagonal(self): - return (self.width ** 2 + self.height ** 2) ** 0.5 + def get_diagonal(self) -> float: + """Calculate the diagonal length of the rectangle. + + Returns: + float: The diagonal length, calculated using the Pythagorean theorem. + """ + return (self.width**2 + self.height**2) ** 0.5 + + def get_picture(self) -> str: + """Return a string representation (ASCII art) of the rectangle. + + If the rectangle is too large (width or height > 50), + a warning message is returned instead. - def get_picture(self): + Returns: + str: A multi-line string made of '*' representing the rectangle, + or a warning message if too large. + """ if self.width > 50 or self.height > 50: return "Too big for picture." design = "" @@ -26,10 +72,22 @@ def get_picture(self): design += ("*" * self.width) + "\n" return design - def get_amount_inside(self, shape): + def get_amount_inside(self, shape) -> int: + """Determine how many times another shape fits inside this rectangle. + + Args: + shape (Rectangle): Another shape (typically a Square) + to fit inside this one. + + Returns: + int: The total number of times the given shape can fit inside. + """ return (self.width // shape.width) * (self.height // shape.height) + def __str__(self) -> str: + """Return a human-readable string representation of the rectangle. - def __str__(self): + Returns: + str: A formatted description of the rectangle. + """ return f"Rectangle (width={self.width}, height={self.height}" - diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py b/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py index 6b86a18..861c4b8 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/shape_calculator.py @@ -1,31 +1,74 @@ +from typing import Optional + from poligon_calculator.rectangle import Rectangle from poligon_calculator.square import Square + class ShapeCalculator: - def __init__(self): + """Class responsible for managing and performing operations on geometric shapes. + + This class acts as a bridge between user input and geometric computations. + It supports creating, modifying, and calculating relationships between + rectangles and squares. + """ + + def __init__(self) -> None: + """Initialize the ShapeCalculator with no shapes created.""" self.rectangle = None self.square = None - def create_rectangle(self, width, height): + def create_rectangle(self, width: int, height: int) -> None: + """Create and store a new Rectangle object. + + Args: + width (int): The width of the rectangle. + height (int): The height of the rectangle. + """ self.rectangle = Rectangle(width, height) - def create_square(self, side): + def create_square(self, side: int) -> None: + """Create and store a new Square object. + + Args: + side (int): The side length of the square. + """ self.square = Square(side) + def set_height(self, height: int) -> None: + """Set the height of the current rectangle, if one exists. - def set_height(self, height): + Args: + height (int): The new height value. + """ if self.rectangle: self.rectangle.set_height(height) - def set_width(self, width): + def set_width(self, width: int) -> None: + """Set the width of the current rectangle, if one exists. + + Args: + width (int): The new width value. + """ if self.rectangle: self.rectangle.set_width(width) - def set_side(self, side): + + def set_side(self, side: int) -> None: + """Set the side length of the current square, if one exists. + + Args: + side (int): The new side length. + """ if self.square: self.square.set_side(side) - def square_in_rectangle(self): + def square_in_rectangle(self) -> Optional[int]: + """Calculate how many squares can fit inside the current rectangle. + + Returns: + Optional[int]: The number of squares that fit inside the rectangle, + or None if either shape has not been created yet. + """ if self.rectangle is not None and self.square is not None: - amount = self.rectangle.get_amount_inside(self.square) + amount: int = self.rectangle.get_amount_inside(self.square) return amount - return None \ No newline at end of file + return None diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/square.py b/projects/002-polygon-area-calculator/python/poligon_calculator/square.py index a0be165..d158f60 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/square.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/square.py @@ -2,17 +2,37 @@ class Square(Rectangle): - def __init__(self, side): - super().__init__(height= side, width= side) + """Class representing a square, a special type of rectangle + where all sides have equal length. + """ + + def __init__(self, side: int) -> None: + """Initialize a Square instance. + + Args: + side (int): The length of each side of the square. + """ + super().__init__(height=side, width=side) + self.width = side self.height = side self.side = side - def set_side(self, side): + def set_side(self, side: int) -> None: + """Set a new side length for the square. + + Updates both width and height to maintain equality. + + Args: + side (int): The new side length of the square. + """ self.height = side self.width = side + def __str__(self) -> str: + """Return a human-readable string representation of the square. - - def __str__(self): + Returns: + str: A formatted description of the square. + """ return f"Square(side={self.width}" diff --git a/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py b/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py index 52c5ce9..dbcfdf9 100644 --- a/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py +++ b/projects/002-polygon-area-calculator/python/poligon_calculator/tests/test_module.py @@ -1,4 +1,5 @@ import unittest + import shape_calculator @@ -10,52 +11,77 @@ def setUp(self): def test_subclass(self): actual = issubclass(shape_calculator.Square, shape_calculator.Rectangle) expected = True - self.assertEqual(actual, expected, 'Expected Square class_config to be a subclass of the Rectangle class_config.') + self.assertEqual( + actual, + expected, + "Expected Square class_config to be a subclass of the Rectangle class_config.", + ) def test_distinct_classes(self): actual = shape_calculator.Square is not shape_calculator.Rectangle expected = True - self.assertEqual(actual, expected, 'Expected Square class_config to be a distinct class_config from the Rectangle class_config.') + self.assertEqual( + actual, + expected, + "Expected Square class_config to be a distinct class_config from the Rectangle class_config.", + ) def test_square_is_square_and_rectangle(self): - actual = isinstance(self.sq, shape_calculator.Square) and isinstance(self.sq, shape_calculator.Rectangle) + actual = isinstance(self.sq, shape_calculator.Square) and isinstance( + self.sq, shape_calculator.Rectangle + ) expected = True - self.assertEqual(actual, expected, 'Expected square object to be an instance of the Square class_config and the Rectangle class_config.') + self.assertEqual( + actual, + expected, + "Expected square object to be an instance of the Square class_config and the Rectangle class_config.", + ) def test_rectangle_string(self): actual = str(self.rect) expected = "Rectangle(width=3, height=6)" - self.assertEqual(actual, expected, 'Expected string representation of rectangle to be "Rectangle(width=3, height=6)"') + self.assertEqual( + actual, + expected, + 'Expected string representation of rectangle to be "Rectangle(width=3, height=6)"', + ) def test_square_string(self): actual = str(self.sq) expected = "Square(side=5)" - self.assertEqual(actual, expected, 'Expected string representation of square to be "Square(side=5)"') + self.assertEqual( + actual, + expected, + 'Expected string representation of square to be "Square(side=5)"', + ) def test_area(self): actual = self.rect.get_area() expected = 18 - self.assertEqual(actual, expected, 'Expected area of rectangle to be 18') + self.assertEqual(actual, expected, "Expected area of rectangle to be 18") actual = self.sq.get_area() expected = 25 - self.assertEqual(actual, expected, 'Expected area of square to be 25') - + self.assertEqual(actual, expected, "Expected area of square to be 25") def test_perimeter(self): actual = self.rect.get_perimeter() expected = 18 - self.assertEqual(actual, expected, 'Expected perimeter of rectangle to be 18') + self.assertEqual(actual, expected, "Expected perimeter of rectangle to be 18") actual = self.sq.get_perimeter() expected = 20 - self.assertEqual(actual, expected, 'Expected perimeter of square to be 20') + self.assertEqual(actual, expected, "Expected perimeter of square to be 20") def test_diagonal(self): actual = self.rect.get_diagonal() expected = 6.708203932499369 - self.assertEqual(actual, expected, 'Expected diagonal of rectangle to be 6.708203932499369') + self.assertEqual( + actual, expected, "Expected diagonal of rectangle to be 6.708203932499369" + ) actual = self.sq.get_diagonal() expected = 7.0710678118654755 - self.assertEqual(actual, expected, 'Expected diagonal of square to be 7.0710678118654755') + self.assertEqual( + actual, expected, "Expected diagonal of square to be 7.0710678118654755" + ) def test_set_attributes(self): self.rect.set_width(7) @@ -63,27 +89,41 @@ def test_set_attributes(self): self.sq.set_side(2) actual = str(self.rect) expected = "Rectangle(width=7, height=8)" - self.assertEqual(actual, expected, 'Expected string representation of rectangle after setting new values to be "Rectangle(width=7, height=8)"') + self.assertEqual( + actual, + expected, + 'Expected string representation of rectangle after setting new values to be "Rectangle(width=7, height=8)"', + ) actual = str(self.sq) expected = "Square(side=2)" - self.assertEqual(actual, expected, 'Expected string representation of square after setting new values to be "Square(side=2)"') + self.assertEqual( + actual, + expected, + 'Expected string representation of square after setting new values to be "Square(side=2)"', + ) self.sq.set_width(4) actual = str(self.sq) expected = "Square(side=4)" - self.assertEqual(actual, expected, 'Expected string representation of square after setting width to be "Square(side=4)"') + self.assertEqual( + actual, + expected, + 'Expected string representation of square after setting width to be "Square(side=4)"', + ) def test_rectangle_picture(self): self.rect.set_width(7) self.rect.set_height(3) actual = self.rect.get_picture() expected = "*******\n*******\n*******\n" - self.assertEqual(actual, expected, 'Expected rectangle picture to be different.') + self.assertEqual( + actual, expected, "Expected rectangle picture to be different." + ) def test_square_picture(self): self.sq.set_side(2) actual = self.sq.get_picture() expected = "**\n**\n" - self.assertEqual(actual, expected, 'Expected square picture to be different.') + self.assertEqual(actual, expected, "Expected square picture to be different.") def test_big_picture(self): self.rect.set_width(51) @@ -97,19 +137,20 @@ def test_get_amount_inside(self): self.rect.set_width(15) actual = self.rect.get_amount_inside(self.sq) expected = 6 - self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 6.') + self.assertEqual(actual, expected, "Expected `get_amount_inside` to return 6.") def test_get_amount_inside_two_rectangles(self): rect2 = shape_calculator.Rectangle(4, 8) actual = rect2.get_amount_inside(self.rect) expected = 1 - self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 1.') + self.assertEqual(actual, expected, "Expected `get_amount_inside` to return 1.") def test_get_amount_inside_none(self): rect2 = shape_calculator.Rectangle(2, 3) actual = rect2.get_amount_inside(self.rect) expected = 0 - self.assertEqual(actual, expected, 'Expected `get_amount_inside` to return 0.') - + self.assertEqual(actual, expected, "Expected `get_amount_inside` to return 0.") + + if __name__ == "__main__": unittest.main()