From ca5edbbdfa3281528377960b532545755029919b Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Mon, 6 Jan 2025 18:40:32 +0400 Subject: [PATCH 1/6] names sorter completed --- solutions/14_names_sorter.py | 74 ++++++++++++++++++++++++++++ solutions/tests/test_names_sorter.py | 74 ++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 solutions/14_names_sorter.py create mode 100644 solutions/tests/test_names_sorter.py diff --git a/solutions/14_names_sorter.py b/solutions/14_names_sorter.py new file mode 100644 index 000000000..a6dbce990 --- /dev/null +++ b/solutions/14_names_sorter.py @@ -0,0 +1,74 @@ +""" +A module for sorting and displaying names from a dictionary based on first names. +The names of the team's members (Group-25) are the data utilized to run this program. + +Module contents: + - get_first_name(item): Extracts the first name from a dictionary item. + - display_sorted_names(names_dict): Displays names sorted by first names. + +Sorting rules: + - Names are sorted alphabetically by first names, then by last names if first names are identical. +""" + +from typing import Dict, Tuple + +def get_first_name(item: Tuple[str, str]) -> str: + """ + Extract the first name from a dictionary item. + + Args: + item (Tuple[str, str]): A tuple containing (last_name, first_name). + Returns: + str: The first name. + Raises: + TypeError: If 'item' is not a tuple. + ValueError: If 'item' does not contain exactly two elements. + """ + if not isinstance(item, tuple): + raise TypeError("Input item must be a tuple") + if len(item) != 2: + raise ValueError("Input tuple must contain exactly two elements (last_name, first_name)") + return item[1] + +def display_sorted_names(names_dict: Dict[str, str]) -> None: + """ + Display names in alphabetical order of first names. + + Args: + names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. + Returns: + None. Prints names in format: + 1. FirstName LastName + 2. FirstName LastName + """ + if not isinstance(names_dict, dict): + raise TypeError("names_dict must be a dictionary") + for key, value in names_dict.items(): + if not isinstance(key, str) or not isinstance(value, str): + raise TypeError("Both keys and values must be strings") + + if not names_dict: + print("No names to display.") + return + + sorted_names = sorted(names_dict.items(), key=lambda item: (item[1].lower(), item[0].lower())) + + for i, (last_name, first_name) in enumerate(sorted_names, 1): + print(f"{i}. {first_name} {last_name}") + +# Define group names, then main block +group_names = { + "Abd Elraheem": "Amin", + "Mohammed": "Razan", + "Seedahmed": "Abdalrahman", + "Solomon": "Alexander", + "Ibrahim": "Azza", + "El-Waleed": "Tamir", + "Saeed": "Mohamed", + "Alaa": "Mohamed", + "Albashityalshaer": "Kefah", + "Makki": "Mohamed" +} + +if __name__ == "__main__": + display_sorted_names(group_names) \ No newline at end of file diff --git a/solutions/tests/test_names_sorter.py b/solutions/tests/test_names_sorter.py new file mode 100644 index 000000000..b5e164e78 --- /dev/null +++ b/solutions/tests/test_names_sorter.py @@ -0,0 +1,74 @@ +import unittest +from names_sorter import get_first_name, display_sorted_names + + +class TestNameSorting(unittest.TestCase): + """Unit tests for name sorting module.""" + + def test_get_first_name_valid(self): + """Test get_first_name with valid inputs.""" + self.assertEqual(get_first_name(("Yusif", "Arwa")), "Arwa") + self.assertEqual(get_first_name(("Smith", "John")), "John") + + def test_get_first_name_invalid(self): + """Test get_first_name with invalid inputs.""" + with self.assertRaises(TypeError): + get_first_name(["Smith", "John"]) # Not a tuple + with self.assertRaises(ValueError): + get_first_name(("Smith",)) # Tuple with fewer than 2 elements + with self.assertRaises(ValueError): + get_first_name( + ("Smith", "John", "Extra") + ) # Tuple with more than 2 elements + + def test_display_sorted_names_valid(self): + """Test display_sorted_names with valid data.""" + names_dict = {"Yusif": "Arwa", "Smith": "John"} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "1. Arwa Yusif\n2. John Smith\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_tie(self): + """Test display_sorted_names with duplicate first names.""" + names_dict = {"Smith": "John", "Johnson": "John"} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "1. John Johnson\n2. John Smith\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_empty(self): + """Test display_sorted_names with an empty dictionary.""" + names_dict = {} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "No names to display.\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_invalid(self): + """Test display_sorted_names with invalid inputs.""" + with self.assertRaises(TypeError): + display_sorted_names(["Smith", "John"]) # Not a dictionary + with self.assertRaises(TypeError): + display_sorted_names({"Smith": 123}) # Non-string value + with self.assertRaises(TypeError): + display_sorted_names({123: "John"}) # Non-string key + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From 42030d808fbe34b17f5226f707ff789c1b15eca6 Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Fri, 10 Jan 2025 23:53:07 +0400 Subject: [PATCH 2/6] Add sorting functionality and tests --- solutions/15-finding-the-prime-numbers.py | 75 ++++++++++++++++++ solutions/is_leap_year.py | 6 +- solutions/prime_num_generator.py | 65 ++++++++++++++++ solutions/str_sorting.py | 70 +++++++++++++++++ solutions/tests/test_prime_num_generator.py | 86 +++++++++++++++++++++ solutions/tests/test_str_sorting.py | 74 ++++++++++++++++++ 6 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 solutions/15-finding-the-prime-numbers.py create mode 100644 solutions/prime_num_generator.py create mode 100644 solutions/str_sorting.py create mode 100644 solutions/tests/test_prime_num_generator.py create mode 100644 solutions/tests/test_str_sorting.py diff --git a/solutions/15-finding-the-prime-numbers.py b/solutions/15-finding-the-prime-numbers.py new file mode 100644 index 000000000..ae3a0f066 --- /dev/null +++ b/solutions/15-finding-the-prime-numbers.py @@ -0,0 +1,75 @@ +""" +A module for generating prime numbers up to a given limit. + +Module contents: + - generate_primes(limit): Generates a list of prime numbers up to a given limit. + - get_valid_input(prompt): Prompts the user for valid input and validates it as a positive number. + +Prime number generation rules: + - A prime number is a natural number greater than 1 that is not divisible by any number other than 1 and itself. +""" + + +def generate_primes(limit): + """ + Generate a series of prime numbers up to a given limit. + + Args: + limit (float): The upper limit up to which prime numbers are generated. + + Returns: + list: A list of prime numbers up to the specified limit. + + Raises: + AssertionError: + - If 'limit' is not a float or integer. + - If 'limit' is less than 2. + + Example: + >>> generate_primes(10) + [2, 3, 5, 7] + """ + # Input validation + assert isinstance(limit, (int, float)), "Limit must be a float or an integer" + assert limit >= 2, "Limit must be greater than or equal to 2" + + primes = [] + for num in range(2, int(limit) + 1): + if all(num % i != 0 for i in range(2, int(num**0.5) + 1)): + primes.append(num) + return primes + + +def get_valid_input(prompt): + """ + Prompt the user for input and validate it as a positive number (integer or float). + + Args: + prompt (str): The message displayed to the user. + + Returns: + float: A valid positive number input. + + Raises: + AssertionError: If the prompt is not a string. + + Example: + >>> get_valid_input("Enter a number: ") + # User enters 10 + 10.0 + """ + assert isinstance(prompt, str), "Prompt must be a string" + + while True: + try: + value = float(input(prompt)) # Accept input as float + if value > 0: # Ensure the value is positive + return value + print("The number must be greater than 0.") + except ValueError: + print("Invalid input. Please enter a valid number.") + + +if __name__ == "__main__": + user_limit = get_valid_input("Enter the limit for prime numbers: ") + print(f"Prime numbers up to {int(user_limit)}: {generate_primes(user_limit)}") diff --git a/solutions/is_leap_year.py b/solutions/is_leap_year.py index 50dfc5cd9..584599079 100644 --- a/solutions/is_leap_year.py +++ b/solutions/is_leap_year.py @@ -41,8 +41,8 @@ def is_leap_year(year: int) -> bool: """ assert isinstance(year, int), "year must be an integer" - assert ( - year >= 1583 - ), "year must be greater than or equal to 1583 (1582 is start of Gregorian Calendar)" + assert year >= 1583, ( + "year must be greater than or equal to 1583 (1582 is start of Gregorian Calendar)" + ) return (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0) diff --git a/solutions/prime_num_generator.py b/solutions/prime_num_generator.py new file mode 100644 index 000000000..68b0c7d11 --- /dev/null +++ b/solutions/prime_num_generator.py @@ -0,0 +1,65 @@ +""" +A module for generating prime numbers up to a given limit. +Module contents: + - generate_primes(limit): Generates a list of prime numbers up to a given limit. + - get_valid_input(prompt): Prompts the user for valid input and validates it as a positive number. +Prime number generation rules: + - A prime number is a natural number greater than 1 that is not divisible by any number other than 1 and itself. +""" + + +def generate_primes(limit): + """ + Generate a series of prime numbers up to a given limit. + Args: + limit (float): The upper limit up to which prime numbers are generated. + Returns: + list: A list of prime numbers up to the specified limit. + Raises: + AssertionError: + - If 'limit' is not a float or integer. + - If 'limit' is less than 2. + Example: + >>> generate_primes(10) + [2, 3, 5, 7] + """ + # Input validation + assert isinstance(limit, (int, float)), "Limit must be a float or an integer" + assert limit >= 2, "Limit must be greater than or equal to 2" + + primes = [] + for num in range(2, int(limit) + 1): + if all(num % i != 0 for i in range(2, int(num**0.5) + 1)): + primes.append(num) + return primes + + +def get_valid_input(prompt): + """ + Prompt the user for input and validate it as a positive number (integer or float). + Args: + prompt (str): The message displayed to the user. + Returns: + float: A valid positive number input. + Raises: + AssertionError: If the prompt is not a string. + Example: + >>> get_valid_input("Enter a number: ") + # User enters 10 + 10.0 + """ + assert isinstance(prompt, str), "Prompt must be a string" + + while True: + try: + value = float(input(prompt)) # Accept input as float + if value > 0: # Ensure the value is positive + return value + print("The number must be greater than 0.") + except ValueError: + print("Invalid input. Please enter a valid number.") + + +if __name__ == "__main__": + user_limit = get_valid_input("Enter the limit for prime numbers: ") + print(f"Prime numbers up to {int(user_limit)}: {generate_primes(user_limit)}") diff --git a/solutions/str_sorting.py b/solutions/str_sorting.py new file mode 100644 index 000000000..20b49d211 --- /dev/null +++ b/solutions/str_sorting.py @@ -0,0 +1,70 @@ +""" +A module for sorting and displaying names from a dictionary based on first names. +The names of the team's members (Group-25) are the data utilized to run this program. +Module contents: + - get_first_name(item): Extracts the first name from a dictionary item. + - display_sorted_names(names_dict): Displays names sorted by first names. +Sorting rules: + - Names are sorted alphabetically by first names, then by last names if first names are identical. +""" + +from typing import Dict, Tuple + +def get_first_name(item: Tuple[str, str]) -> str: + """ + Extract the first name from a dictionary item. + Args: + item (Tuple[str, str]): A tuple containing (last_name, first_name). + Returns: + str: The first name. + Raises: + TypeError: If 'item' is not a tuple. + ValueError: If 'item' does not contain exactly two elements. + """ + if not isinstance(item, tuple): + raise TypeError("Input item must be a tuple") + if len(item) != 2: + raise ValueError("Input tuple must contain exactly two elements (last_name, first_name)") + return item[1] + +def display_sorted_names(names_dict: Dict[str, str]) -> None: + """ + Display names in alphabetical order of first names. + Args: + names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. + Returns: + None. Prints names in format: + 1. FirstName LastName + 2. FirstName LastName + """ + if not isinstance(names_dict, dict): + raise TypeError("names_dict must be a dictionary") + for key, value in names_dict.items(): + if not isinstance(key, str) or not isinstance(value, str): + raise TypeError("Both keys and values must be strings") + + if not names_dict: + print("No names to display.") + return + + sorted_names = sorted(names_dict.items(), key=lambda item: (item[1].lower(), item[0].lower())) + + for i, (last_name, first_name) in enumerate(sorted_names, 1): + print(f"{i}. {first_name} {last_name}") + +# Define group names, then main block +group_names = { + "Abd Elraheem": "Amin", + "Mohammed": "Razan", + "Seedahmed": "Abdalrahman", + "Solomon": "Alexander", + "Ibrahim": "Azza", + "El-Waleed": "Tamir", + "Saeed": "Mohamed", + "Alaa": "Mohamed", + "Albashityalshaer": "Kefah", + "Makki": "Mohamed" +} + +if __name__ == "__main__": + display_sorted_names(group_names) diff --git a/solutions/tests/test_prime_num_generator.py b/solutions/tests/test_prime_num_generator.py new file mode 100644 index 000000000..b755902a4 --- /dev/null +++ b/solutions/tests/test_prime_num_generator.py @@ -0,0 +1,86 @@ +""" +This module contains unit tests for the max_integer function. +It tests various scenarios including ordered and unordered lists, +lists with floats, strings, and edge cases. +""" + +import unittest + + +def generate_primes(limit): + """Generate a list of prime numbers up to the given limit.""" + + if not isinstance(limit, int) or limit < 2: + raise AssertionError("Limit must be an integer greater than or equal to 2.") + + primes = [] + + for num in range(2, limit + 1): + is_prime = all(num % i != 0 for i in range(2, int(num**0.5) + 1)) + + if is_prime: + primes.append(num) + + return primes + + +def get_valid_input(prompt): + """Get a valid numeric input from the user.""" + + while True: + try: + value = float(input(prompt)) + + if value < 0: + raise ValueError("The number must be non-negative.") + + return value + + except ValueError as e: + print(e) + + +# from prime_generator import generate_primes, get_valid_input + + +class TestPrimeGenerator(unittest.TestCase): + """Tests for the prime number generator and input validation functions.""" + + def test_generate_primes_valid(self): + """Test valid prime number generation.""" + self.assertEqual(generate_primes(10), [2, 3, 5, 7]) + self.assertEqual(generate_primes(2), [2]) + self.assertEqual(generate_primes(20), [2, 3, 5, 7, 11, 13, 17, 19]) + + def test_generate_primes_invalid_limit(self): + """Test invalid limit for prime generation.""" + with self.assertRaises(AssertionError): + generate_primes(1) # Below valid range + with self.assertRaises(AssertionError): + generate_primes("ten") # Non-numeric input + with self.assertRaises(AssertionError): + generate_primes(-5) # Negative number + + def test_get_valid_input(self): + """Test get_valid_input with valid and invalid inputs.""" + # Since `input()` is used, this requires user simulation or mocking. + # Mocking would replace `input()` with predefined values for testing. + import builtins + + original_input = builtins.input + + # Test valid input + builtins.input = lambda _: "10" + self.assertEqual(get_valid_input("Enter a number: "), 10.0) + + # Test invalid inputs (mocking a sequence of attempts) + attempts = iter(["-5", "abc", "2.5"]) + builtins.input = lambda _: next(attempts) + self.assertEqual(get_valid_input("Enter a number: "), 2.5) + + # Restore original input + builtins.input = original_input + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_str_sorting.py b/solutions/tests/test_str_sorting.py new file mode 100644 index 000000000..5653bea7e --- /dev/null +++ b/solutions/tests/test_str_sorting.py @@ -0,0 +1,74 @@ +import unittest +from names_sorter import get_first_name, display_sorted_names + + +class TestNameSorting(unittest.TestCase): + """Unit tests for name sorting module.""" + + def test_get_first_name_valid(self): + """Test get_first_name with valid inputs.""" + self.assertEqual(get_first_name(("Yusif", "Arwa")), "Arwa") + self.assertEqual(get_first_name(("Smith", "John")), "John") + + def test_get_first_name_invalid(self): + """Test get_first_name with invalid inputs.""" + with self.assertRaises(TypeError): + get_first_name(["Smith", "John"]) # Not a tuple + with self.assertRaises(ValueError): + get_first_name(("Smith",)) # Tuple with fewer than 2 elements + with self.assertRaises(ValueError): + get_first_name( + ("Smith", "John", "Extra") + ) # Tuple with more than 2 elements + + def test_display_sorted_names_valid(self): + """Test display_sorted_names with valid data.""" + names_dict = {"Yusif": "Arwa", "Smith": "John"} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "1. Arwa Yusif\n2. John Smith\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_tie(self): + """Test display_sorted_names with duplicate first names.""" + names_dict = {"Smith": "John", "Johnson": "John"} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "1. John Johnson\n2. John Smith\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_empty(self): + """Test display_sorted_names with an empty dictionary.""" + names_dict = {} + import io + from contextlib import redirect_stdout + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "No names to display.\n" + self.assertEqual(output.getvalue(), expected_output) + + def test_display_sorted_names_invalid(self): + """Test display_sorted_names with invalid inputs.""" + with self.assertRaises(TypeError): + display_sorted_names(["Smith", "John"]) # Not a dictionary + with self.assertRaises(TypeError): + display_sorted_names({"Smith": 123}) # Non-string value + with self.assertRaises(TypeError): + display_sorted_names({123: "John"}) # Non-string key + + +if __name__ == "__main__": + unittest.main() From 4d132fd16b610cfc11a7bcfd4449a289d6148489 Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Sat, 11 Jan 2025 00:59:41 +0400 Subject: [PATCH 3/6] Fix import issues, update docstrings, and ensure code works without bugs --- solutions/names_sorter.py | 81 ++++++++++++++++++++++++++++ solutions/tests/test_names_sorter.py | 30 +++++++---- 2 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 solutions/names_sorter.py diff --git a/solutions/names_sorter.py b/solutions/names_sorter.py new file mode 100644 index 000000000..84c9b31e6 --- /dev/null +++ b/solutions/names_sorter.py @@ -0,0 +1,81 @@ +""" +A module for sorting and displaying names from a dictionary based on first names. +The names of the team's members (Group-25) are the data utilized to run this program. + +Module contents: + - get_first_name(item): Extracts the first name from a dictionary item. + - display_sorted_names(names_dict): Displays names sorted by first names. + +Sorting rules: + - Names are sorted alphabetically by first names, then by last names if first names are identical. +""" + +from typing import Dict, Tuple + + +def get_first_name(item: Tuple[str, str]) -> str: + """ + Extract the first name from a dictionary item. + + Args: + item (Tuple[str, str]): A tuple containing (last_name, first_name). + Returns: + str: The first name. + Raises: + TypeError: If 'item' is not a tuple. + ValueError: If 'item' does not contain exactly two elements. + """ + if not isinstance(item, tuple): + raise TypeError("Input item must be a tuple") + if len(item) != 2: + raise ValueError( + "Input tuple must contain exactly two elements (last_name, first_name)" + ) + return item[1] + + +def display_sorted_names(names_dict: Dict[str, str]) -> None: + """ + Display names in alphabetical order of first names. + + Args: + names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. + Returns: + None. Prints names in format: + 1. FirstName LastName + 2. FirstName LastName + """ + if not isinstance(names_dict, dict): + raise TypeError("names_dict must be a dictionary") + for key, value in names_dict.items(): + if not isinstance(key, str) or not isinstance(value, str): + raise TypeError("Both keys and values must be strings") + + if not names_dict: + print("No names to display.") + return + + sorted_names = sorted( + names_dict.items(), key=lambda item: (item[1].lower(), item[0].lower()) + ) + + for i, (last_name, first_name) in enumerate(sorted_names, 1): + print(f"{i}. {first_name} {last_name}") + + +# Define group names, then main block +group_names = { + "Abd Elraheem": "Amin", + "Mohammed": "Razan", + "Seedahmed": "Abdalrahman", + "Solomon": "Alexander", + "Ibrahim": "Azza", + "El-Waleed": "Tamir", + "Saeed": "Mohamed", + "Alaa": "Mohamed", + "Albashityalshaer": "Kefah", + "Makki": "Mohamed", +} + +if __name__ == "__main__": + display_sorted_names(group_names) diff --git a/solutions/tests/test_names_sorter.py b/solutions/tests/test_names_sorter.py index b5e164e78..47fd071ef 100644 --- a/solutions/tests/test_names_sorter.py +++ b/solutions/tests/test_names_sorter.py @@ -1,28 +1,33 @@ import unittest -from names_sorter import get_first_name, display_sorted_names +from solutions.names_sorter import get_first_name, display_sorted_names class TestNameSorting(unittest.TestCase): - """Unit tests for name sorting module.""" + """Unit tests for the name sorting module.""" def test_get_first_name_valid(self): """Test get_first_name with valid inputs.""" + # Should return the first name when given a valid tuple self.assertEqual(get_first_name(("Yusif", "Arwa")), "Arwa") self.assertEqual(get_first_name(("Smith", "John")), "John") def test_get_first_name_invalid(self): """Test get_first_name with invalid inputs.""" + # Should raise TypeError when input is not a tuple with self.assertRaises(TypeError): - get_first_name(["Smith", "John"]) # Not a tuple + get_first_name(["Smith", "John"]) # Invalid: Not a tuple + + # Should raise ValueError when tuple does not have exactly two elements with self.assertRaises(ValueError): - get_first_name(("Smith",)) # Tuple with fewer than 2 elements + get_first_name(("Smith",)) # Invalid: Tuple with fewer than 2 elements with self.assertRaises(ValueError): get_first_name( ("Smith", "John", "Extra") - ) # Tuple with more than 2 elements + ) # Invalid: Tuple with more than 2 elements def test_display_sorted_names_valid(self): - """Test display_sorted_names with valid data.""" + """Test display_sorted_names with valid input data.""" + # Should display names sorted by first name names_dict = {"Yusif": "Arwa", "Smith": "John"} import io from contextlib import redirect_stdout @@ -35,7 +40,8 @@ def test_display_sorted_names_valid(self): self.assertEqual(output.getvalue(), expected_output) def test_display_sorted_names_tie(self): - """Test display_sorted_names with duplicate first names.""" + """Test display_sorted_names when duplicate first names are present.""" + # Should sort by last name when first names are identical names_dict = {"Smith": "John", "Johnson": "John"} import io from contextlib import redirect_stdout @@ -49,6 +55,7 @@ def test_display_sorted_names_tie(self): def test_display_sorted_names_empty(self): """Test display_sorted_names with an empty dictionary.""" + # Should handle empty input gracefully names_dict = {} import io from contextlib import redirect_stdout @@ -62,13 +69,14 @@ def test_display_sorted_names_empty(self): def test_display_sorted_names_invalid(self): """Test display_sorted_names with invalid inputs.""" + # Should raise TypeError for non-dictionary inputs with self.assertRaises(TypeError): - display_sorted_names(["Smith", "John"]) # Not a dictionary + display_sorted_names(["Smith", "John"]) # Invalid: Not a dictionary with self.assertRaises(TypeError): - display_sorted_names({"Smith": 123}) # Non-string value + display_sorted_names({"Smith": 123}) # Invalid: Non-string value with self.assertRaises(TypeError): - display_sorted_names({123: "John"}) # Non-string key + display_sorted_names({123: "John"}) # Invalid: Non-string key if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() From c0eea6f5b28fdb9e1decfc7a0d6c8d860e5fb8b7 Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Sat, 11 Jan 2025 01:07:59 +0400 Subject: [PATCH 4/6] Remove obsolete file and add pyproject.toml --- solutions/14_names_sorter.py | 74 ------------------------------------ solutions/pyproject.toml | 6 +++ 2 files changed, 6 insertions(+), 74 deletions(-) delete mode 100644 solutions/14_names_sorter.py create mode 100644 solutions/pyproject.toml diff --git a/solutions/14_names_sorter.py b/solutions/14_names_sorter.py deleted file mode 100644 index a6dbce990..000000000 --- a/solutions/14_names_sorter.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -A module for sorting and displaying names from a dictionary based on first names. -The names of the team's members (Group-25) are the data utilized to run this program. - -Module contents: - - get_first_name(item): Extracts the first name from a dictionary item. - - display_sorted_names(names_dict): Displays names sorted by first names. - -Sorting rules: - - Names are sorted alphabetically by first names, then by last names if first names are identical. -""" - -from typing import Dict, Tuple - -def get_first_name(item: Tuple[str, str]) -> str: - """ - Extract the first name from a dictionary item. - - Args: - item (Tuple[str, str]): A tuple containing (last_name, first_name). - Returns: - str: The first name. - Raises: - TypeError: If 'item' is not a tuple. - ValueError: If 'item' does not contain exactly two elements. - """ - if not isinstance(item, tuple): - raise TypeError("Input item must be a tuple") - if len(item) != 2: - raise ValueError("Input tuple must contain exactly two elements (last_name, first_name)") - return item[1] - -def display_sorted_names(names_dict: Dict[str, str]) -> None: - """ - Display names in alphabetical order of first names. - - Args: - names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. - Returns: - None. Prints names in format: - 1. FirstName LastName - 2. FirstName LastName - """ - if not isinstance(names_dict, dict): - raise TypeError("names_dict must be a dictionary") - for key, value in names_dict.items(): - if not isinstance(key, str) or not isinstance(value, str): - raise TypeError("Both keys and values must be strings") - - if not names_dict: - print("No names to display.") - return - - sorted_names = sorted(names_dict.items(), key=lambda item: (item[1].lower(), item[0].lower())) - - for i, (last_name, first_name) in enumerate(sorted_names, 1): - print(f"{i}. {first_name} {last_name}") - -# Define group names, then main block -group_names = { - "Abd Elraheem": "Amin", - "Mohammed": "Razan", - "Seedahmed": "Abdalrahman", - "Solomon": "Alexander", - "Ibrahim": "Azza", - "El-Waleed": "Tamir", - "Saeed": "Mohamed", - "Alaa": "Mohamed", - "Albashityalshaer": "Kefah", - "Makki": "Mohamed" -} - -if __name__ == "__main__": - display_sorted_names(group_names) \ No newline at end of file diff --git a/solutions/pyproject.toml b/solutions/pyproject.toml new file mode 100644 index 000000000..b76e4c4ef --- /dev/null +++ b/solutions/pyproject.toml @@ -0,0 +1,6 @@ +[tool.ruff] +exclude = [ + "solutions/is_leap_year.py" # Exclude nonexistent file +] +line-length = 88 # Optional: set a line-length limit +select = ["E", "F", "W"] # Optional: select specific error codes to check From fe8bdc936d86ae128ddc76c4ae6b4e8860d00bfc Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Sat, 11 Jan 2025 02:00:52 +0400 Subject: [PATCH 5/6] Fix errors and update branch with latest changes --- ls | 30 ++++++++++++++++++++++++++++ solutions/names_sorter.py | 13 +++++++----- solutions/tests/test_names_sorter.py | 10 +++++++--- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 ls diff --git a/ls b/ls new file mode 100644 index 000000000..73902af55 --- /dev/null +++ b/ls @@ -0,0 +1,30 @@ + 14-sorting-strings-in-alphabetical-order + 15-finding-the-prime-numbers +* 55-prime_num_generator + main + remotes/origin/11-upload-base-readme-file + remotes/origin/14-sorting-strings-in-alphabetical-order + remotes/origin/15-finding-the-prime-numbers + remotes/origin/32-checking-prime-numbers + remotes/origin/34-missing-number + remotes/origin/55-prime_num_generator + remotes/origin/6-longest-substring + remotes/origin/7-anagram-checker + remotes/origin/8-is-number-odd + remotes/origin/9-factorial + remotes/origin/GP25_Group-Norms + remotes/origin/HEAD -> origin/main + remotes/origin/Max-Integer + remotes/origin/Min-Integer + remotes/origin/binary-search + remotes/origin/communication + remotes/origin/constraints + remotes/origin/count-capitalized-words + remotes/origin/is-leap-year + remotes/origin/is-palindrome + remotes/origin/ka-branch + remotes/origin/learning-goals + remotes/origin/main + remotes/origin/missing-numbers + remotes/origin/multiplication-table + remotes/origin/revert-44-missing-numbers diff --git a/solutions/names_sorter.py b/solutions/names_sorter.py index 84c9b31e6..5a32eb20e 100644 --- a/solutions/names_sorter.py +++ b/solutions/names_sorter.py @@ -3,11 +3,13 @@ The names of the team's members (Group-25) are the data utilized to run this program. Module contents: - - get_first_name(item): Extracts the first name from a dictionary item. + - get_first_name(item): + Extracts the first name from a dictionary item. - display_sorted_names(names_dict): Displays names sorted by first names. Sorting rules: - - Names are sorted alphabetically by first names, then by last names if first names are identical. + - Names are sorted alphabetically by first names, + - then by last names if first names are identical. """ from typing import Dict, Tuple @@ -39,11 +41,12 @@ def display_sorted_names(names_dict: Dict[str, str]) -> None: Display names in alphabetical order of first names. Args: - names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. + names_dict (Dict[str, str]): Dictionary with last names as keys and + first names as values. Returns: None. Prints names in format: - 1. FirstName LastName - 2. FirstName LastName + 1. FirstName LastName + 2. FirstName LastName """ if not isinstance(names_dict, dict): raise TypeError("names_dict must be a dictionary") diff --git a/solutions/tests/test_names_sorter.py b/solutions/tests/test_names_sorter.py index 47fd071ef..b4238d935 100644 --- a/solutions/tests/test_names_sorter.py +++ b/solutions/tests/test_names_sorter.py @@ -1,5 +1,9 @@ import unittest -from solutions.names_sorter import get_first_name, display_sorted_names + +from solutions.names_sorter import ( + display_sorted_names, + get_first_name, +) class TestNameSorting(unittest.TestCase): @@ -36,7 +40,7 @@ def test_display_sorted_names_valid(self): with redirect_stdout(output): display_sorted_names(names_dict) - expected_output = "1. Arwa Yusif\n2. John Smith\n" + expected_output = "1. Arwa Yusif\\n2. John Smith\\n" self.assertEqual(output.getvalue(), expected_output) def test_display_sorted_names_tie(self): @@ -50,7 +54,7 @@ def test_display_sorted_names_tie(self): with redirect_stdout(output): display_sorted_names(names_dict) - expected_output = "1. John Johnson\n2. John Smith\n" + expected_output = "1. John Johnson\\n2. John Smith\\n" self.assertEqual(output.getvalue(), expected_output) def test_display_sorted_names_empty(self): From edcc99fec4c02bfac186ada0d13000da52a3a972 Mon Sep 17 00:00:00 2001 From: mohd-makki Date: Sun, 12 Jan 2025 20:16:02 +0400 Subject: [PATCH 6/6] code duly updated --- solutions/names_sorter.py | 31 +++++++++++++++------ solutions/tests/test_names_sorter.py | 41 +++++++++++++++++++++------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/solutions/names_sorter.py b/solutions/names_sorter.py index 5a32eb20e..6e924ead2 100644 --- a/solutions/names_sorter.py +++ b/solutions/names_sorter.py @@ -1,9 +1,11 @@ """ -A module for sorting and displaying names from a dictionary based on first names. -The names of the team's members (Group-25) are the data utilized to run this program. +A module for sorting and displaying names from a dictionary based on +first names. +The names of the team's members (Group-25) are the data utilized to +run this program. Module contents: - - get_first_name(item): + - extract_first_name(item): Extracts the first name from a dictionary item. - display_sorted_names(names_dict): Displays names sorted by first names. @@ -15,7 +17,7 @@ from typing import Dict, Tuple -def get_first_name(item: Tuple[str, str]) -> str: +def extract_first_name(item: Tuple[str, str]) -> str: """ Extract the first name from a dictionary item. @@ -31,11 +33,24 @@ def get_first_name(item: Tuple[str, str]) -> str: raise TypeError("Input item must be a tuple") if len(item) != 2: raise ValueError( - "Input tuple must contain exactly two elements (last_name, first_name)" + "Input tuple must contain exactly two elements " + "(last_name, first_name)" ) return item[1] +def get_first_name(item: Tuple[str, str]) -> str: + """ + Get the first name from a tuple containing (last_name, first_name). + + Args: + item (Tuple[str, str]): A tuple containing (last_name, first_name). + Returns: + str: The first name. + """ + return extract_first_name(item) + + def display_sorted_names(names_dict: Dict[str, str]) -> None: """ Display names in alphabetical order of first names. @@ -43,10 +58,6 @@ def display_sorted_names(names_dict: Dict[str, str]) -> None: Args: names_dict (Dict[str, str]): Dictionary with last names as keys and first names as values. - Returns: - None. Prints names in format: - 1. FirstName LastName - 2. FirstName LastName """ if not isinstance(names_dict, dict): raise TypeError("names_dict must be a dictionary") @@ -68,6 +79,7 @@ def display_sorted_names(names_dict: Dict[str, str]) -> None: # Define group names, then main block group_names = { + # spell-checker: disable "Abd Elraheem": "Amin", "Mohammed": "Razan", "Seedahmed": "Abdalrahman", @@ -78,6 +90,7 @@ def display_sorted_names(names_dict: Dict[str, str]) -> None: "Alaa": "Mohamed", "Albashityalshaer": "Kefah", "Makki": "Mohamed", + # spell-checker: enable } if __name__ == "__main__": diff --git a/solutions/tests/test_names_sorter.py b/solutions/tests/test_names_sorter.py index b4238d935..4116fb961 100644 --- a/solutions/tests/test_names_sorter.py +++ b/solutions/tests/test_names_sorter.py @@ -1,4 +1,15 @@ +"""Unit tests for the name sorting module. + +This module contains unit tests for the functions `display_sorted_names` and +`get_first_name` in the `solutions.names_sorter` module. It tests various +scenarios, including valid and invalid inputs, empty input, ties in first +names, +and mixed-case names. +""" + import unittest +import io +from contextlib import redirect_stdout from solutions.names_sorter import ( display_sorted_names, @@ -23,7 +34,9 @@ def test_get_first_name_invalid(self): # Should raise ValueError when tuple does not have exactly two elements with self.assertRaises(ValueError): - get_first_name(("Smith",)) # Invalid: Tuple with fewer than 2 elements + get_first_name( + ("Smith",) + ) # Invalid: Tuple with fewer than 2 elements with self.assertRaises(ValueError): get_first_name( ("Smith", "John", "Extra") @@ -33,36 +46,30 @@ def test_display_sorted_names_valid(self): """Test display_sorted_names with valid input data.""" # Should display names sorted by first name names_dict = {"Yusif": "Arwa", "Smith": "John"} - import io - from contextlib import redirect_stdout output = io.StringIO() with redirect_stdout(output): display_sorted_names(names_dict) - expected_output = "1. Arwa Yusif\\n2. John Smith\\n" + expected_output = "1. Arwa Yusif\n2. John Smith\n" self.assertEqual(output.getvalue(), expected_output) def test_display_sorted_names_tie(self): """Test display_sorted_names when duplicate first names are present.""" # Should sort by last name when first names are identical names_dict = {"Smith": "John", "Johnson": "John"} - import io - from contextlib import redirect_stdout output = io.StringIO() with redirect_stdout(output): display_sorted_names(names_dict) - expected_output = "1. John Johnson\\n2. John Smith\\n" + expected_output = "1. John Johnson\n2. John Smith\n" self.assertEqual(output.getvalue(), expected_output) def test_display_sorted_names_empty(self): """Test display_sorted_names with an empty dictionary.""" # Should handle empty input gracefully names_dict = {} - import io - from contextlib import redirect_stdout output = io.StringIO() with redirect_stdout(output): @@ -75,12 +82,26 @@ def test_display_sorted_names_invalid(self): """Test display_sorted_names with invalid inputs.""" # Should raise TypeError for non-dictionary inputs with self.assertRaises(TypeError): - display_sorted_names(["Smith", "John"]) # Invalid: Not a dictionary + display_sorted_names( + ["Smith", "John"] + ) # Invalid: Not a dictionary with self.assertRaises(TypeError): display_sorted_names({"Smith": 123}) # Invalid: Non-string value with self.assertRaises(TypeError): display_sorted_names({123: "John"}) # Invalid: Non-string key + def test_display_sorted_names_mixed_case(self): + """Test display_sorted_names with mixed case names.""" + # Should sort names in a case-insensitive manner + names_dict = {"smith": "john", "Johnson": "john"} + + output = io.StringIO() + with redirect_stdout(output): + display_sorted_names(names_dict) + + expected_output = "1. john Johnson\n2. john smith\n" + self.assertEqual(output.getvalue().lower(), expected_output.lower()) + if __name__ == "__main__": unittest.main()