From e79747c11ed34b80b45f86390c28b6d080a26d02 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Mon, 30 Dec 2024 21:06:46 +0100 Subject: [PATCH 01/52] Update README.md --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/README.md b/README.md index e69de29bb..436df2ced 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,66 @@ +# ZeroThroughTen 🚀 + +Welcome to **ZeroThroughTen**, +a dynamic group of passionate learners united by a shared enthusiasm for coding, collaboration, and creative problem-solving. 🌟 Together, we transform challenges into stepping stones for growth, innovation, and success. 💡 + +At ZeroThroughTen, every contribution matters, every voice is heard, and every member is an integral part of our journey. We believe in learning, growing, and achieving **as a team**—where the strength of one enhances the power of all. 🤝 + +--- + +## ✨ **About Us** + +We are **ZeroThroughTen**, a group of enthusiastic problem-solvers driven by curiosity, creativity, and a commitment to mastering programming skills. We don't just solve problems—we redefine them, turning challenges into opportunities to build impactful and meaningful solutions. 🚀 + +Our group thrives on collaboration, where every idea adds value and every challenge is tackled together. Whether it’s debugging code, brainstorming new ideas, or building innovative solutions, we’re here to **shine brighter, together. 🌈** + +--- + +## 🛠️ **Our Mission** + +- **Learn and Grow**: Sharpen programming and problem-solving skills through hands-on collaboration and experimentation. +- **Collaborate to Innovate**: Build impactful projects while fostering a culture of open communication and shared knowledge. +- **Master the Tools**: Enhance familiarity with GitHub workflows, version control, and best coding practices. +- **Support Each Other**: Cultivate an inclusive and inspiring team environment where every member feels valued. + +--- + +## 🔗 **Communication Channels** + +- **Slack**: Daily updates, task discussions, and real-time collaboration. +- **Weekly Meetings**: Thursdays at 7:00 PM CET (Central European Time) on Zoom. +- **GitHub**: Central hub for code contributions, pull requests, and documentation. + +> Remember, teamwork is the heart of ZeroThroughTen! 💬 + +--- + +## 🧩 **Our Values** + +1. **Collaboration**: Together, we achieve more. +2. **Respect**: Every idea matters, every voice counts. +3. **Curiosity**: Always stay curious, always stay creative. +4. **Accountability**: Take ownership of your tasks while supporting your teammates. +5. **Excellence**: Strive for clean, efficient, and impactful solutions. + +--- + +## 📝 **Our Goals** + +- Build a collaborative project where every member contributes meaningfully. +- Learn to review and improve code collaboratively. +- Explore new tools, frameworks, and technologies together. +- Develop solutions that are innovative, efficient, and impactful. + +--- + +## 🎉 **Acknowledgments** + +A huge **shoutout** to every member of **ZeroThroughTen** for their dedication, hard work, and creativity. 💪 +Each step we take, each line of code we write, brings us closer to our goals—**one breakthrough at a time. 🌟** + +Let’s continue to learn, grow, and shine together. ✨ +**Remember, from zero through ten, the possibilities are infinite. 🔢** + +--- + +Stay curious. Stay creative. Keep coding. 🚀 \ No newline at end of file From 5418cfe3ce257b2b6d93a90b9a08fdb74e7d2f0f Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Mon, 30 Dec 2024 21:19:38 +0100 Subject: [PATCH 02/52] Fix file and folder naming to follow snake_case rules --- .../20241222_meeting.md | 0 .../20241224_meeting.md | 0 .../default_minutes_of_meeting.md} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename collaboration/{minutes-of-meeting => minutes_of_meeting}/20241222_meeting.md (100%) rename collaboration/{minutes-of-meeting => minutes_of_meeting}/20241224_meeting.md (100%) rename collaboration/{minutes-of-meeting/default minutes_of_meeting.md => minutes_of_meeting/default_minutes_of_meeting.md} (100%) diff --git a/collaboration/minutes-of-meeting/20241222_meeting.md b/collaboration/minutes_of_meeting/20241222_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/20241222_meeting.md rename to collaboration/minutes_of_meeting/20241222_meeting.md diff --git a/collaboration/minutes-of-meeting/20241224_meeting.md b/collaboration/minutes_of_meeting/20241224_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/20241224_meeting.md rename to collaboration/minutes_of_meeting/20241224_meeting.md diff --git a/collaboration/minutes-of-meeting/default minutes_of_meeting.md b/collaboration/minutes_of_meeting/default_minutes_of_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/default minutes_of_meeting.md rename to collaboration/minutes_of_meeting/default_minutes_of_meeting.md From fc8bee83b8a1e3bbe6f6bdb14639883428189296 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Mon, 30 Dec 2024 22:09:21 +0100 Subject: [PATCH 03/52] markdown error fixed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 436df2ced..5b012cc2e 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,4 @@ Let’s continue to learn, grow, and shine together. ✨ --- -Stay curious. Stay creative. Keep coding. 🚀 \ No newline at end of file +Stay curious. Stay creative. Keep coding. 🚀 From e9cf4561ceac9ae150c5114108a9d533e751fcff Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 15:30:13 +0100 Subject: [PATCH 04/52] Add palindrome checker and update solutions structure --- solutions/README.md | 20 +++++++++++++++++ solutions/is_palindrome.py | 40 +++++++++++++++++++++++++++++++++ solutions/test_is_palindrome.py | 40 +++++++++++++++++++++++++++++++++ solutions/tests/__init__.py | 1 - 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 solutions/is_palindrome.py create mode 100644 solutions/test_is_palindrome.py delete mode 100644 solutions/tests/__init__.py diff --git a/solutions/README.md b/solutions/README.md index 9852346d2..232a38a45 100644 --- a/solutions/README.md +++ b/solutions/README.md @@ -1 +1,21 @@ # Solutions + +# Palindrome Checker + +This module checks if a given string is a palindrome. A palindrome reads the same backward as forward, ignoring case and spaces. + +## How to Use + +1. Run the `is_palindrome.py` file to use the function. +2. Use `test_is_palindrome.py` to test the functionality. + +### Examples + +```python +>>> from is_palindrome import is_palindrome +>>> is_palindrome("level") +True +>>> is_palindrome("hello") +False +>>> is_palindrome("A man a plan a canal Panama") +True \ No newline at end of file diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py new file mode 100644 index 000000000..17a2b4211 --- /dev/null +++ b/solutions/is_palindrome.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for checking if a string is a palindrome. + +Module contents: + - is_palindrome: Checks if a given string is a palindrome. + +Created on 2024-12-30 +Author: Emre Biyik +""" + +def is_palindrome(text: str) -> bool: + """Checks if a string is a palindrome. + + A palindrome is a word, phrase, or sequence that reads the same backward as forward, ignoring case and spaces. + + Parameters: + text: str, the input string to check. + + Returns -> bool: True if the input is a palindrome, False otherwise. + + Raises: + AssertionError: if input is not a string. + + Examples: + >>> is_palindrome("level") + True + >>> is_palindrome("hello") + False + >>> is_palindrome("A man a plan a canal Panama") + True + """ + assert isinstance(text, str), "Input must be a string" + + # Normalize the text: remove spaces and convert to lowercase + normalized = ''.join(char.lower() for char in text if char.isalnum()) + + # Check if the string is the same when reversed + return normalized == normalized[::-1] \ No newline at end of file diff --git a/solutions/test_is_palindrome.py b/solutions/test_is_palindrome.py new file mode 100644 index 000000000..f817b0e80 --- /dev/null +++ b/solutions/test_is_palindrome.py @@ -0,0 +1,40 @@ +import unittest +from is_palindrome import is_palindrome + +class TestIsPalindrome(unittest.TestCase): + """Test the is_palindrome function.""" + + def test_empty_string(self): + """It should return True for an empty string.""" + self.assertTrue(is_palindrome("")) + + def test_single_character(self): + """It should return True for single character strings.""" + self.assertTrue(is_palindrome("a")) + + def test_simple_palindrome(self): + """It should return True for simple palindromes.""" + self.assertTrue(is_palindrome("level")) + self.assertTrue(is_palindrome("radar")) + + def test_non_palindrome(self): + """It should return False for non-palindromes.""" + self.assertFalse(is_palindrome("hello")) + self.assertFalse(is_palindrome("world")) + + def test_mixed_case_palindrome(self): + """It should handle mixed case palindromes.""" + self.assertTrue(is_palindrome("RaceCar")) + + def test_palindrome_with_spaces(self): + """It should handle palindromes with spaces.""" + self.assertTrue(is_palindrome("A man a plan a canal Panama")) + + def test_not_a_string(self): + """It should raise AssertionError for non-string input.""" + with self.assertRaises(AssertionError): + is_palindrome(12321) + +if __name__ == "__main__": + unittest.main() + \ No newline at end of file diff --git a/solutions/tests/__init__.py b/solutions/tests/__init__.py deleted file mode 100644 index 8b1378917..000000000 --- a/solutions/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ - From c5eb6526eb936fb3b9ec57f3193502b7e9befdb4 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 17:48:25 +0100 Subject: [PATCH 05/52] Fix Markdown formatting errors in README.md --- solutions/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/solutions/README.md b/solutions/README.md index 232a38a45..ad5409559 100644 --- a/solutions/README.md +++ b/solutions/README.md @@ -1,15 +1,15 @@ # Solutions -# Palindrome Checker +## Palindrome Checker This module checks if a given string is a palindrome. A palindrome reads the same backward as forward, ignoring case and spaces. -## How to Use +### How to Use 1. Run the `is_palindrome.py` file to use the function. 2. Use `test_is_palindrome.py` to test the functionality. -### Examples +#### Examples ```python >>> from is_palindrome import is_palindrome @@ -18,4 +18,6 @@ True >>> is_palindrome("hello") False >>> is_palindrome("A man a plan a canal Panama") -True \ No newline at end of file +True + + From a4d6c90b95580ac2abec21120204f0bde77178a6 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 17:54:34 +0100 Subject: [PATCH 06/52] Fix multiple blank lines in README.md --- solutions/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/solutions/README.md b/solutions/README.md index ad5409559..420ed1e86 100644 --- a/solutions/README.md +++ b/solutions/README.md @@ -19,5 +19,3 @@ True False >>> is_palindrome("A man a plan a canal Panama") True - - From 17ee226faa1bef26c15ad106570074e6e66450ca Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 18:01:02 +0100 Subject: [PATCH 07/52] Fix formatting issues and run ruff checks --- solutions/is_palindrome.py | 2 +- solutions/test_is_palindrome.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py index 17a2b4211..9ce3853dd 100644 --- a/solutions/is_palindrome.py +++ b/solutions/is_palindrome.py @@ -37,4 +37,4 @@ def is_palindrome(text: str) -> bool: normalized = ''.join(char.lower() for char in text if char.isalnum()) # Check if the string is the same when reversed - return normalized == normalized[::-1] \ No newline at end of file + return normalized == normalized[::-1] diff --git a/solutions/test_is_palindrome.py b/solutions/test_is_palindrome.py index f817b0e80..18bfdb4d8 100644 --- a/solutions/test_is_palindrome.py +++ b/solutions/test_is_palindrome.py @@ -37,4 +37,5 @@ def test_not_a_string(self): if __name__ == "__main__": unittest.main() + \ No newline at end of file From 042d2c99819c13c71d3415a54d1c707f0d1092a0 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 19:29:18 +0100 Subject: [PATCH 08/52] Apply Ruff formatting to code --- solutions/is_palindrome.py | 17 +++++++++-------- solutions/test_is_palindrome.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py index 9ce3853dd..39d82bc15 100644 --- a/solutions/is_palindrome.py +++ b/solutions/is_palindrome.py @@ -10,19 +10,20 @@ Author: Emre Biyik """ + def is_palindrome(text: str) -> bool: """Checks if a string is a palindrome. - + A palindrome is a word, phrase, or sequence that reads the same backward as forward, ignoring case and spaces. - + Parameters: text: str, the input string to check. - + Returns -> bool: True if the input is a palindrome, False otherwise. - + Raises: AssertionError: if input is not a string. - + Examples: >>> is_palindrome("level") True @@ -32,9 +33,9 @@ def is_palindrome(text: str) -> bool: True """ assert isinstance(text, str), "Input must be a string" - + # Normalize the text: remove spaces and convert to lowercase - normalized = ''.join(char.lower() for char in text if char.isalnum()) - + normalized = "".join(char.lower() for char in text if char.isalnum()) + # Check if the string is the same when reversed return normalized == normalized[::-1] diff --git a/solutions/test_is_palindrome.py b/solutions/test_is_palindrome.py index 18bfdb4d8..352c85e89 100644 --- a/solutions/test_is_palindrome.py +++ b/solutions/test_is_palindrome.py @@ -1,41 +1,41 @@ import unittest from is_palindrome import is_palindrome + class TestIsPalindrome(unittest.TestCase): """Test the is_palindrome function.""" - + def test_empty_string(self): """It should return True for an empty string.""" self.assertTrue(is_palindrome("")) - + def test_single_character(self): """It should return True for single character strings.""" self.assertTrue(is_palindrome("a")) - + def test_simple_palindrome(self): """It should return True for simple palindromes.""" self.assertTrue(is_palindrome("level")) self.assertTrue(is_palindrome("radar")) - + def test_non_palindrome(self): """It should return False for non-palindromes.""" self.assertFalse(is_palindrome("hello")) self.assertFalse(is_palindrome("world")) - + def test_mixed_case_palindrome(self): """It should handle mixed case palindromes.""" self.assertTrue(is_palindrome("RaceCar")) - + def test_palindrome_with_spaces(self): """It should handle palindromes with spaces.""" self.assertTrue(is_palindrome("A man a plan a canal Panama")) - + def test_not_a_string(self): """It should raise AssertionError for non-string input.""" with self.assertRaises(AssertionError): is_palindrome(12321) + if __name__ == "__main__": unittest.main() - - \ No newline at end of file From 1ec0547039c8f325a726f2f46c21d4fa99dc7846 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 31 Dec 2024 19:32:05 +0100 Subject: [PATCH 09/52] Fix snake_case naming for collaboration folder --- .../20241222_meeting.md | 0 .../20241224_meeting.md | 0 .../default_minutes_of_meeting.md} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename collaboration/{minutes-of-meeting => minutes_of_meeting}/20241222_meeting.md (100%) rename collaboration/{minutes-of-meeting => minutes_of_meeting}/20241224_meeting.md (100%) rename collaboration/{minutes-of-meeting/default minutes_of_meeting.md => minutes_of_meeting/default_minutes_of_meeting.md} (100%) diff --git a/collaboration/minutes-of-meeting/20241222_meeting.md b/collaboration/minutes_of_meeting/20241222_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/20241222_meeting.md rename to collaboration/minutes_of_meeting/20241222_meeting.md diff --git a/collaboration/minutes-of-meeting/20241224_meeting.md b/collaboration/minutes_of_meeting/20241224_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/20241224_meeting.md rename to collaboration/minutes_of_meeting/20241224_meeting.md diff --git a/collaboration/minutes-of-meeting/default minutes_of_meeting.md b/collaboration/minutes_of_meeting/default_minutes_of_meeting.md similarity index 100% rename from collaboration/minutes-of-meeting/default minutes_of_meeting.md rename to collaboration/minutes_of_meeting/default_minutes_of_meeting.md From 7a2e13fb467f4a6d0da129f223fcdb84e54efadc Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Thu, 2 Jan 2025 07:42:37 +0100 Subject: [PATCH 10/52] structure of files created structure of files --- solutions/bubble_sort.py | 0 solutions/tests/test_bubble_sort.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 solutions/bubble_sort.py create mode 100644 solutions/tests/test_bubble_sort.py diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py new file mode 100644 index 000000000..e69de29bb diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py new file mode 100644 index 000000000..e69de29bb From c025d3fc1c4efcf0996d9852f65217cc4535f04b Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Thu, 2 Jan 2025 08:11:19 +0100 Subject: [PATCH 11/52] completed docstring completed docstring --- solutions/bubble_sort.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index e69de29bb..fc4f90a9e 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for sorting collection using bubble sort algorithm. + +Module contents: + - bubble_sort: sort the collection in ascending order. + +Created on 2024-01-02 +Author: Oleksandr Maksymikhin +""" + + +def bubble_sort(input_collection: list[int]) -> list[int]: + """Sort collection using bubble sort algorithm. + + Collection is a set of the same type data. + Bubble sort is a sorting algorithm that swaps elements moving larger elements to the end of collection. + + Parameters: + input_collection: list[int], collection of unsorted data type int. + + Returns -> list[int], collection of sorted data type int. + + Raises: + AssertionError: if collection contains elements of different type. + + Examples: + >>> bubble_sort(1) + [1] + >>> bubble_sort([1, 3, 2]]) + [1, 2, 3] + >>> bubble_sort([3, 2, 1000000, 1]]) + [1, 2, 3, 1000000] + """ + # add return statement to avoid lint errors + output_collection = input_collection + return output_collection From 70d45ad8b22f53705499a11de071d980ec154a65 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Thu, 2 Jan 2025 08:17:41 +0100 Subject: [PATCH 12/52] corrected pylint notifications --- solutions/bubble_sort.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index fc4f90a9e..b4089f1e4 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -14,8 +14,7 @@ def bubble_sort(input_collection: list[int]) -> list[int]: """Sort collection using bubble sort algorithm. - Collection is a set of the same type data. - Bubble sort is a sorting algorithm that swaps elements moving larger elements to the end of collection. + Sort elements in collection by swapping and moving larger elements to the end of collection. Parameters: input_collection: list[int], collection of unsorted data type int. From 6817f1394b4fc084c1336bf182eebff7ada61e3e Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Fri, 3 Jan 2025 10:04:56 +0100 Subject: [PATCH 13/52] sort collection of type(int) sort collection of type(int) with tests coverage --- solutions/bubble_sort.py | 27 +++++++++++++-- solutions/tests/test_bubble_sort.py | 51 +++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index b4089f1e4..c88834f61 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -11,6 +11,7 @@ """ +# def bubble_sort(input_collection: list[int]) -> list[int]: def bubble_sort(input_collection: list[int]) -> list[int]: """Sort collection using bubble sort algorithm. @@ -32,6 +33,26 @@ def bubble_sort(input_collection: list[int]) -> list[int]: >>> bubble_sort([3, 2, 1000000, 1]]) [1, 2, 3, 1000000] """ - # add return statement to avoid lint errors - output_collection = input_collection - return output_collection + + # copy collection to avoid side effect + collection = input_collection.copy() + # define collection length + collection_length = len(collection) + # first loop to traverse the collection + for current_item_index in range(collection_length): + # flag to break if the last run didn't swap any item + already_sorted = True + # second loop to compare item with adjacent one + for swap_index in range(collection_length - current_item_index - 1): + # swap items if next adjacent item is bigger + if collection[swap_index] > collection[swap_index + 1]: + (collection[swap_index], collection[swap_index + 1]) = ( + collection[swap_index + 1], + collection[swap_index], + ) + already_sorted = False + # break loop if the last run didn't swap any item + if already_sorted: + break + # return sorted collection + return collection diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py index e69de29bb..3ba00b638 100644 --- a/solutions/tests/test_bubble_sort.py +++ b/solutions/tests/test_bubble_sort.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for bubble_sort function. + +Test categories: + - Standard cases: typical lists with different lengths + - Edge cases: empty lists, single element + - Defensive tests: wrong input types, different input types + +Created on 2024-01-03 +Author: Oleksandr Maksymikhin +""" + +import unittest + +from ..bubble_sort import bubble_sort + + +class TestBubbleSort(unittest.TestCase): + """Test the bubble_sort function.""" + + def test_empty_list(self): + """It should return [] for input []""" + actual = bubble_sort([]) + expected = [] + self.assertEqual(actual, expected) + + def test_one_element_list(self): + """It should return [1] for input [1]""" + actual = bubble_sort([1]) + expected = [1] + self.assertEqual(actual, expected) + + def test_two_element_list(self): + """It should return [1, 2] for input [2, 1]""" + actual = bubble_sort([2, 1]) + expected = [1, 2] + self.assertEqual(actual, expected) + + def test_three_elements_list(self): + """It should return [1, 2, 3] for input [3, 2, 1]""" + actual = bubble_sort([3, 2, 1]) + expected = [1, 2, 3] + self.assertEqual(actual, expected) + + def test_four_elements_big_number_list(self): + """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" + actual = bubble_sort([2, 3, 100500, 1]) + expected = [1, 2, 3, 100500] + self.assertEqual(actual, expected) From daedfbe0ebc226b0b8fbb6636eb08a617936b6ec Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Fri, 3 Jan 2025 10:32:58 +0100 Subject: [PATCH 14/52] extended parameter data types to (int, char, string, bool) Extended parameter data types. Now module works with (int, char, string, bool). Tests cover all data types (int, char, string, bool) --- solutions/bubble_sort.py | 6 ++--- solutions/tests/test_bubble_sort.py | 36 +++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index c88834f61..8d7433af8 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -12,7 +12,7 @@ # def bubble_sort(input_collection: list[int]) -> list[int]: -def bubble_sort(input_collection: list[int]) -> list[int]: +def bubble_sort(input_collection: list[any]) -> list[any]: """Sort collection using bubble sort algorithm. Sort elements in collection by swapping and moving larger elements to the end of collection. @@ -33,9 +33,7 @@ def bubble_sort(input_collection: list[int]) -> list[int]: >>> bubble_sort([3, 2, 1000000, 1]]) [1, 2, 3, 1000000] """ - - # copy collection to avoid side effect - collection = input_collection.copy() + collection = input_collection # define collection length collection_length = len(collection) # first loop to traverse the collection diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py index 3ba00b638..2143f932f 100644 --- a/solutions/tests/test_bubble_sort.py +++ b/solutions/tests/test_bubble_sort.py @@ -20,32 +20,60 @@ class TestBubbleSort(unittest.TestCase): """Test the bubble_sort function.""" + # Uncomment for predictive stepping + # actual_1 = bubble_sort([]) + # actual_2 = bubble_sort([1]) + # actual_3 = bubble_sort([2, 1]) + # actual_4 = bubble_sort([3, 2, 1]) + # actual_5 = bubble_sort([2, 3, 100500, 1]) + # actual_6 = bubble_sort(["b", "a", "c"]) + # actual_7 = bubble_sort(["cabbage", "banana", "apple"]) + # actual_8 = bubble_sort([True, False, True, False]) + def test_empty_list(self): """It should return [] for input []""" actual = bubble_sort([]) expected = [] self.assertEqual(actual, expected) - def test_one_element_list(self): + def test_one_int_element_list(self): """It should return [1] for input [1]""" actual = bubble_sort([1]) expected = [1] self.assertEqual(actual, expected) - def test_two_element_list(self): + def test_two_int_element_list(self): """It should return [1, 2] for input [2, 1]""" actual = bubble_sort([2, 1]) expected = [1, 2] self.assertEqual(actual, expected) - def test_three_elements_list(self): + def test_three_int_elements_list(self): """It should return [1, 2, 3] for input [3, 2, 1]""" actual = bubble_sort([3, 2, 1]) expected = [1, 2, 3] self.assertEqual(actual, expected) - def test_four_elements_big_number_list(self): + def test_four_int_elements_big_number_list(self): """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" actual = bubble_sort([2, 3, 100500, 1]) expected = [1, 2, 3, 100500] self.assertEqual(actual, expected) + + def test_chars_list(self): + """It should return ["a", "b", "c"] for input ["b", "a", "c"]""" + actual = bubble_sort(["b", "a", "c"]) + expected = ["a", "b", "c"] + self.assertEqual(actual, expected) + + def test_string_list(self): + """It should return ["apple", "banana", "cabbage"] for input ["cabbage", "banana", "apple"]""" + actual = bubble_sort(["cabbage", "banana", "apple"]) + expected = ["apple", "banana", "cabbage"] + self.assertEqual(actual, expected) + + def test_bool_list(self): + """It should return [False, False, True, True] for input [True, False, True, False]""" + actual = bubble_sort([True, False, True, False]) + expected = [False, False, True, True] + self.assertEqual(actual, expected) From b192dff601fab1bea81d1754ff48cd17cbf32c23 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Fri, 3 Jan 2025 10:43:12 +0100 Subject: [PATCH 15/52] added protection from side effect, changes in function docstring description --- solutions/bubble_sort.py | 6 +++--- solutions/tests/test_bubble_sort.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index 8d7433af8..9c47f6f23 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -18,9 +18,9 @@ def bubble_sort(input_collection: list[any]) -> list[any]: Sort elements in collection by swapping and moving larger elements to the end of collection. Parameters: - input_collection: list[int], collection of unsorted data type int. + input_collection: list[any], collection of unsorted data of any type. - Returns -> list[int], collection of sorted data type int. + Returns -> list[any], collection of sorted elements with any data type. Raises: AssertionError: if collection contains elements of different type. @@ -33,7 +33,7 @@ def bubble_sort(input_collection: list[any]) -> list[any]: >>> bubble_sort([3, 2, 1000000, 1]]) [1, 2, 3, 1000000] """ - collection = input_collection + collection = input_collection.copy() # define collection length collection_length = len(collection) # first loop to traverse the collection diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py index 2143f932f..16bc48460 100644 --- a/solutions/tests/test_bubble_sort.py +++ b/solutions/tests/test_bubble_sort.py @@ -77,3 +77,10 @@ def test_bool_list(self): actual = bubble_sort([True, False, True, False]) expected = [False, False, True, True] self.assertEqual(actual, expected) + + def test_side_effect_protection(self): + """It should return [3, 2, 1] of initial input""" + input_list = [3, 2, 1] + copy_for_sorting = input_list.copy() + bubble_sort(copy_for_sorting) + self.assertEqual(input_list, [3, 2, 1]) From 5818073f0c232be3041d4dac67a4899f00e96e26 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Fri, 3 Jan 2025 11:14:47 +0100 Subject: [PATCH 16/52] Added defensive assertions (input is not list, input is non-homogeneous collection). Small corrections by commets and variable names --- solutions/bubble_sort.py | 23 +++++++++--- solutions/tests/test_bubble_sort.py | 58 ++++++++++++++++++----------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index 9c47f6f23..d81fcea46 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -18,21 +18,32 @@ def bubble_sort(input_collection: list[any]) -> list[any]: Sort elements in collection by swapping and moving larger elements to the end of collection. Parameters: - input_collection: list[any], collection of unsorted data of any type. + input_collection: list[any], collection of unsorted data of any homogeneous type. Returns -> list[any], collection of sorted elements with any data type. Raises: - AssertionError: if collection contains elements of different type. + AssertionError: if input is not a collection. + AssertionError: if collection contains elements of different data types (non-homogeneous). Examples: >>> bubble_sort(1) [1] >>> bubble_sort([1, 3, 2]]) [1, 2, 3] - >>> bubble_sort([3, 2, 1000000, 1]]) - [1, 2, 3, 1000000] + >>> bubble_sort([3, 2, 100500, 1]]) + [1, 2, 3, 100500] """ + + # defensive assertion to check that input is a collection list + assert isinstance(input_collection, list), "Input is not a collection" + + # defensive assertion to check that collection is homogeneous + assert all( + isinstance(item, type(input_collection[0])) for item in input_collection + ), "Collection is not homogeneous" + + # copy collection to avoid side effect collection = input_collection.copy() # define collection length collection_length = len(collection) @@ -40,9 +51,9 @@ def bubble_sort(input_collection: list[any]) -> list[any]: for current_item_index in range(collection_length): # flag to break if the last run didn't swap any item already_sorted = True - # second loop to compare item with adjacent one + # second loop to compare/swap item with adjacent one for swap_index in range(collection_length - current_item_index - 1): - # swap items if next adjacent item is bigger + # swap items if the next adjacent item is smaller if collection[swap_index] > collection[swap_index + 1]: (collection[swap_index], collection[swap_index + 1]) = ( collection[swap_index + 1], diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py index 16bc48460..bb5112650 100644 --- a/solutions/tests/test_bubble_sort.py +++ b/solutions/tests/test_bubble_sort.py @@ -1,12 +1,16 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -Test module for bubble_sort function. +Test module for bubble_sort() function. Test categories: - - Standard cases: typical lists with different lengths + - Standard cases: lists of type int with different lengths - Edge cases: empty lists, single element - - Defensive tests: wrong input types, different input types + - Different data type: char, string, bool + - Defensive tests: + - side effects protection + - input is not a collection + - different data types in the input collection (non-homogeneous) Created on 2024-01-03 Author: Oleksandr Maksymikhin @@ -21,66 +25,78 @@ class TestBubbleSort(unittest.TestCase): """Test the bubble_sort function.""" # Uncomment for predictive stepping - # actual_1 = bubble_sort([]) - # actual_2 = bubble_sort([1]) - # actual_3 = bubble_sort([2, 1]) - # actual_4 = bubble_sort([3, 2, 1]) - # actual_5 = bubble_sort([2, 3, 100500, 1]) - # actual_6 = bubble_sort(["b", "a", "c"]) - # actual_7 = bubble_sort(["cabbage", "banana", "apple"]) - # actual_8 = bubble_sort([True, False, True, False]) - - def test_empty_list(self): + # test_1 = bubble_sort([]) + # test_2 = bubble_sort([1]) + # test_3 = bubble_sort([2, 1]) + # test_4 = bubble_sort([3, 2, 1]) + # test_5 = bubble_sort([2, 3, 100500, 1]) + # test_6 = bubble_sort(["b", "a", "c"]) + # test_7 = bubble_sort(["cabbage", "banana", "apple"]) + # test_8 = bubble_sort([True, False, True, False]) + # test_10 = bubble_sort("apple") + # test_11 = bubble_sort([3, "two", 1]) + + def test_1_empty_list(self): """It should return [] for input []""" actual = bubble_sort([]) expected = [] self.assertEqual(actual, expected) - def test_one_int_element_list(self): + def test_2_one_int_element_list(self): """It should return [1] for input [1]""" actual = bubble_sort([1]) expected = [1] self.assertEqual(actual, expected) - def test_two_int_element_list(self): + def test_3_two_int_elements_list(self): """It should return [1, 2] for input [2, 1]""" actual = bubble_sort([2, 1]) expected = [1, 2] self.assertEqual(actual, expected) - def test_three_int_elements_list(self): + def test_4_three_int_elements_list(self): """It should return [1, 2, 3] for input [3, 2, 1]""" actual = bubble_sort([3, 2, 1]) expected = [1, 2, 3] self.assertEqual(actual, expected) - def test_four_int_elements_big_number_list(self): + def test_5_four_int_elements_list_big_number(self): """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" actual = bubble_sort([2, 3, 100500, 1]) expected = [1, 2, 3, 100500] self.assertEqual(actual, expected) - def test_chars_list(self): + def test_6_char_elements_list(self): """It should return ["a", "b", "c"] for input ["b", "a", "c"]""" actual = bubble_sort(["b", "a", "c"]) expected = ["a", "b", "c"] self.assertEqual(actual, expected) - def test_string_list(self): + def test_7_string_elements_list(self): """It should return ["apple", "banana", "cabbage"] for input ["cabbage", "banana", "apple"]""" actual = bubble_sort(["cabbage", "banana", "apple"]) expected = ["apple", "banana", "cabbage"] self.assertEqual(actual, expected) - def test_bool_list(self): + def test_8_bool_elements_list(self): """It should return [False, False, True, True] for input [True, False, True, False]""" actual = bubble_sort([True, False, True, False]) expected = [False, False, True, True] self.assertEqual(actual, expected) - def test_side_effect_protection(self): + def test_9_side_effect_protection(self): """It should return [3, 2, 1] of initial input""" input_list = [3, 2, 1] copy_for_sorting = input_list.copy() bubble_sort(copy_for_sorting) self.assertEqual(input_list, [3, 2, 1]) + + def test_10_non_collection_input(self): + """It should raise an assertion error if the input is not a collection""" + with self.assertRaises(AssertionError): + bubble_sort("apple") + + def test_11_non_homogeneous_collection_input(self): + """It should raise an assertion error if the collection is non-homogeneous""" + with self.assertRaises(AssertionError): + bubble_sort([3, "two", 1]) From fb2f5f6fcf0bebdb1bb762cc35c33572274141ac Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Fri, 3 Jan 2025 11:33:58 +0100 Subject: [PATCH 17/52] ready for review, removed unnecessary comments --- solutions/bubble_sort.py | 8 ++++---- solutions/tests/test_bubble_sort.py | 17 ++--------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index d81fcea46..3cfca7e68 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -27,11 +27,11 @@ def bubble_sort(input_collection: list[any]) -> list[any]: AssertionError: if collection contains elements of different data types (non-homogeneous). Examples: - >>> bubble_sort(1) + >>> bubble_sort([1]) [1] - >>> bubble_sort([1, 3, 2]]) + >>> bubble_sort([1, 3, 2]) [1, 2, 3] - >>> bubble_sort([3, 2, 100500, 1]]) + >>> bubble_sort([3, 2, 100500, 1]) [1, 2, 3, 100500] """ @@ -41,7 +41,7 @@ def bubble_sort(input_collection: list[any]) -> list[any]: # defensive assertion to check that collection is homogeneous assert all( isinstance(item, type(input_collection[0])) for item in input_collection - ), "Collection is not homogeneous" + ), "Input collection is not homogeneous" # copy collection to avoid side effect collection = input_collection.copy() diff --git a/solutions/tests/test_bubble_sort.py b/solutions/tests/test_bubble_sort.py index bb5112650..ec5e331cb 100644 --- a/solutions/tests/test_bubble_sort.py +++ b/solutions/tests/test_bubble_sort.py @@ -6,7 +6,7 @@ Test categories: - Standard cases: lists of type int with different lengths - Edge cases: empty lists, single element - - Different data type: char, string, bool + - Different data types: char, string, bool - Defensive tests: - side effects protection - input is not a collection @@ -24,18 +24,6 @@ class TestBubbleSort(unittest.TestCase): """Test the bubble_sort function.""" - # Uncomment for predictive stepping - # test_1 = bubble_sort([]) - # test_2 = bubble_sort([1]) - # test_3 = bubble_sort([2, 1]) - # test_4 = bubble_sort([3, 2, 1]) - # test_5 = bubble_sort([2, 3, 100500, 1]) - # test_6 = bubble_sort(["b", "a", "c"]) - # test_7 = bubble_sort(["cabbage", "banana", "apple"]) - # test_8 = bubble_sort([True, False, True, False]) - # test_10 = bubble_sort("apple") - # test_11 = bubble_sort([3, "two", 1]) - def test_1_empty_list(self): """It should return [] for input []""" actual = bubble_sort([]) @@ -87,8 +75,7 @@ def test_8_bool_elements_list(self): def test_9_side_effect_protection(self): """It should return [3, 2, 1] of initial input""" input_list = [3, 2, 1] - copy_for_sorting = input_list.copy() - bubble_sort(copy_for_sorting) + bubble_sort(input_list) self.assertEqual(input_list, [3, 2, 1]) def test_10_non_collection_input(self): From 9795d22c14fd3648408479b520bf00a05cc9c048 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Fri, 3 Jan 2025 19:08:18 +0100 Subject: [PATCH 18/52] Refactor file structure, update imports, and add new tests --- .vscode/settings.json | 4 +- solutions/__init__.py | 1 - solutions/is_palindrome.py | 1 + solutions/test_is_palindrome.py | 41 ------------------- solutions/tests/__init__.py | 0 solutions/tests/test_is_palindrome.py | 58 +++++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 44 deletions(-) delete mode 100644 solutions/test_is_palindrome.py create mode 100644 solutions/tests/__init__.py create mode 100644 solutions/tests/test_is_palindrome.py diff --git a/.vscode/settings.json b/.vscode/settings.json index bbda5188d..252022b48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -119,8 +119,8 @@ "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.ruff": true, - "source.organizeImports.ruff": true + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" } } } diff --git a/solutions/__init__.py b/solutions/__init__.py index 8b1378917..e69de29bb 100644 --- a/solutions/__init__.py +++ b/solutions/__init__.py @@ -1 +0,0 @@ - diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py index 39d82bc15..a84482e10 100644 --- a/solutions/is_palindrome.py +++ b/solutions/is_palindrome.py @@ -32,6 +32,7 @@ def is_palindrome(text: str) -> bool: >>> is_palindrome("A man a plan a canal Panama") True """ + # Ensure the input is of type string to avoid unexpected errors. assert isinstance(text, str), "Input must be a string" # Normalize the text: remove spaces and convert to lowercase diff --git a/solutions/test_is_palindrome.py b/solutions/test_is_palindrome.py deleted file mode 100644 index 352c85e89..000000000 --- a/solutions/test_is_palindrome.py +++ /dev/null @@ -1,41 +0,0 @@ -import unittest -from is_palindrome import is_palindrome - - -class TestIsPalindrome(unittest.TestCase): - """Test the is_palindrome function.""" - - def test_empty_string(self): - """It should return True for an empty string.""" - self.assertTrue(is_palindrome("")) - - def test_single_character(self): - """It should return True for single character strings.""" - self.assertTrue(is_palindrome("a")) - - def test_simple_palindrome(self): - """It should return True for simple palindromes.""" - self.assertTrue(is_palindrome("level")) - self.assertTrue(is_palindrome("radar")) - - def test_non_palindrome(self): - """It should return False for non-palindromes.""" - self.assertFalse(is_palindrome("hello")) - self.assertFalse(is_palindrome("world")) - - def test_mixed_case_palindrome(self): - """It should handle mixed case palindromes.""" - self.assertTrue(is_palindrome("RaceCar")) - - def test_palindrome_with_spaces(self): - """It should handle palindromes with spaces.""" - self.assertTrue(is_palindrome("A man a plan a canal Panama")) - - def test_not_a_string(self): - """It should raise AssertionError for non-string input.""" - with self.assertRaises(AssertionError): - is_palindrome(12321) - - -if __name__ == "__main__": - unittest.main() diff --git a/solutions/tests/__init__.py b/solutions/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/solutions/tests/test_is_palindrome.py b/solutions/tests/test_is_palindrome.py new file mode 100644 index 000000000..530b5af4a --- /dev/null +++ b/solutions/tests/test_is_palindrome.py @@ -0,0 +1,58 @@ +import unittest +from solutions.is_palindrome import is_palindrome + + +class TestIsPalindrome(unittest.TestCase): + """Test the is_palindrome function.""" + + def test_empty_string(self): + """It should return True for an empty string.""" + self.assertTrue(is_palindrome("")) + + def test_single_character(self): + """It should return True for single character strings.""" + self.assertTrue(is_palindrome("a")) + + def test_level_palindrome(self): + """It should return True for the palindrome 'level'.""" + self.assertTrue(is_palindrome("level")) + + def test_radar_palindrome(self): + """It should return True for the palindrome 'radar'.""" + self.assertTrue(is_palindrome("radar")) + + def test_hello_is_not_palindrome(self): + """It should return False for the non-palindrome 'hello'.""" + self.assertFalse(is_palindrome("hello")) + + def test_world_is_not_palindrome(self): + """It should return False for the non-palindrome 'world'.""" + self.assertFalse(is_palindrome("world")) + + def test_mixed_case_palindromes_return_true(self): + """It should return True for palindromes with mixed lower-case and upper-case characters.""" + self.assertTrue(is_palindrome("RaceCar")) + + def test_palindromes_with_spaces_return_true(self): + """It should return True for palindromes with spaces and mixed cases.""" + self.assertTrue(is_palindrome("A man a plan a canal Panama")) + + def test_palindrome_with_numbers_returns_true(self): + """It should return True for palindromes containing numbers.""" + self.assertTrue(is_palindrome("12321")) + + def test_non_palindrome_numbers_return_false(self): + """It should return False for non-palindrome numbers.""" + self.assertFalse(is_palindrome("12345")) + + def test_palindrome_with_special_characters_returns_true(self): + """It should return True for palindromes with special characters.""" + self.assertTrue(is_palindrome("!@#$%^&*()_+_)(*&^%$#@!")) + + def test_non_palindrome_with_special_characters_returns_false(self): + """It should return False for non-palindromes with special characters.""" + self.assertFalse(is_palindrome("hello!")) + + +if __name__ == "__main__": + unittest.main() From 6b1e0425f7369a511542cdec90af6d6efe600b43 Mon Sep 17 00:00:00 2001 From: colevandersWands <18554853+colevandersWands@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:22:14 -0500 Subject: [PATCH 19/52] fix config error --- .vscode/settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bbda5188d..252022b48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -119,8 +119,8 @@ "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.ruff": true, - "source.organizeImports.ruff": true + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" } } } From 358a9ebf9d583b5b76b72c67d18232392d93b9b4 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sat, 4 Jan 2025 00:46:32 +0100 Subject: [PATCH 20/52] Add register_user function and its tests with validation --- solutions/register_user.py | 44 ++++++++++++++++ solutions/tests/test_register_user.py | 72 +++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 solutions/register_user.py create mode 100644 solutions/tests/test_register_user.py diff --git a/solutions/register_user.py b/solutions/register_user.py new file mode 100644 index 000000000..b736d454f --- /dev/null +++ b/solutions/register_user.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for registering users with validated inputs. + +Module contents: + - register_user: Validates user details and returns a registration dictionary. + +Created on: 2025-01-03 +Author: Emre Biyik +""" + + +def register_user(name: str, age: int, email: str) -> dict: + """ + Registers a user with the given name, age, and email. + + Parameters: + name (str): User's name. Must contain only alphabetic characters. + age (int): User's age. Must be between 18 and 99 (inclusive). + email (str): User's email. Must be in a valid format. + + Returns: + dict: A dictionary containing the user's details. + + Raises: + ValueError: If `name`, `age`, or `email` is invalid. + + Examples: + >>> register_user("Alice", 25, "alice@example.com") + {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'} + + >>> register_user("Bob", 18, "bob@example.net") + {'name': 'Bob', 'age': 18, 'email': 'bob@example.net'} + + """ + if not name.isalpha(): + raise ValueError("Invalid name: Must contain only alphabetic characters.") + if not isinstance(age, int) or not (18 <= age <= 99): + raise ValueError("Invalid age: Must be an integer between 18 and 99.") + if "@" not in email or "." not in email.split("@")[-1]: + raise ValueError("Invalid email: Must be a valid email format.") + + return {"name": name, "age": age, "email": email} diff --git a/solutions/tests/test_register_user.py b/solutions/tests/test_register_user.py new file mode 100644 index 000000000..1fa143e13 --- /dev/null +++ b/solutions/tests/test_register_user.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for register_user function. + +Test categories: + - Standard cases: valid inputs + - Edge cases: minimum and maximum valid ages + - Defensive tests: invalid inputs for name, age, and email + +Created on: 2025-01-03 +Author: Your Name +""" + +import unittest +from solutions.register_user import register_user + + +class TestRegisterUser(unittest.TestCase): + """Test suite for the register_user function.""" + + # Standard cases + def test_valid_inputs(self): + """It should register a user with valid details.""" + result = register_user("Alice", 25, "alice@example.com") + expected = {"name": "Alice", "age": 25, "email": "alice@example.com"} + self.assertEqual(result, expected) + + # Edge cases + def test_minimum_age(self): + """It should allow the minimum valid age.""" + self.assertEqual( + register_user("Bob", 18, "bob@example.net"), + {"name": "Bob", "age": 18, "email": "bob@example.net"}, + ) + + def test_maximum_age(self): + """It should allow the maximum valid age.""" + self.assertEqual( + register_user("Charlie", 99, "charlie@example.org"), + {"name": "Charlie", "age": 99, "email": "charlie@example.org"}, + ) + + # Defensive tests + def test_invalid_name(self): + """It should raise ValueError for invalid names.""" + with self.assertRaises(ValueError): + register_user("Alice123", 25, "alice@example.com") + + def test_invalid_age_below_minimum(self): + """It should raise ValueError for ages below 18.""" + with self.assertRaises(ValueError): + register_user("Alice", 17, "alice@example.com") + + def test_invalid_age_above_maximum(self): + """It should raise ValueError for ages above 99.""" + with self.assertRaises(ValueError): + register_user("Alice", 100, "alice@example.com") + + def test_invalid_email_format(self): + """It should raise ValueError for invalid email formats.""" + with self.assertRaises(ValueError): + register_user("Alice", 25, "aliceexample.com") + + def test_multiple_invalid_inputs(self): + """It should raise ValueError for multiple invalid inputs.""" + with self.assertRaises(ValueError): + register_user("Alice123", 17, "aliceexample.com") + + +if __name__ == "__main__": + unittest.main() From 6571f48dd167d93c93642ef85d45803d606bf747 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sat, 4 Jan 2025 00:56:51 +0100 Subject: [PATCH 21/52] small update --- solutions/tests/test_register_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/tests/test_register_user.py b/solutions/tests/test_register_user.py index 1fa143e13..32ca95373 100644 --- a/solutions/tests/test_register_user.py +++ b/solutions/tests/test_register_user.py @@ -9,7 +9,7 @@ - Defensive tests: invalid inputs for name, age, and email Created on: 2025-01-03 -Author: Your Name +Author: Emre Biyik """ import unittest From dab436cf9afa0f0608cad705acde05fab233f903 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sat, 4 Jan 2025 23:58:53 +0100 Subject: [PATCH 22/52] Add learning goals for the group --- collaboration/learning_goals.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index 11c583d2b..474891167 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -1,5 +1,31 @@ # Learning Goals +## Group Objectives + +The primary goal of our group is to enhance our understanding and practical skills in software development by working collaboratively on exercises. Each member brings unique objectives, and we aim to create a supportive environment where these individual goals can be achieved. + ## Collective +1. **Enhance Problem-Solving Skills**: Apply data structures (DSs) and algorithms to solve complex problems effectively. +2. **Learn and Practice New Techniques**: Explore new programming language features and techniques to improve code quality. +3. **Focus on Readability and Documentation**: Develop solutions that are well-documented, clear, and maintainable. +4. **Improve Development Speed**: Practice strategies to write and debug code faster, aiming for efficiency. +5. **Collaborative Code Reviews**: Engage in multiple review cycles to learn from feedback and refine solutions collaboratively. + +--- + ## Individual + +### Emre Biyik + +- Review data structures and algorithms studied previously. +- Focus on writing more complete and readable solutions. +- Gain deeper experience with version control tools like Git and GitHub, especially in team projects. + +--- + +## Group Approach + +- Regularly review and reflect on progress towards these goals during meetings. +- Support each other in achieving individual and shared objectives through collaborative exercises. +- Use tools like GitHub Issues and Project Boards to ensure visibility and accountability. From 17848e23e5521bf2f386cbd86cfc57a5d72557b0 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 5 Jan 2025 00:28:45 +0100 Subject: [PATCH 23/52] Add constraints to the collaboration/constraints.md file --- collaboration/constraints.md | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index 24079505c..4a9c9538f 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -4,31 +4,39 @@ Some boundaries around our project. -## External - - - -## Internal: Involuntary - - - -## Internal: Voluntary - - +--- + +## **External** + + + +- **Deadline:** The project must be completed by **10 Jan 2025**. +- **Programming Language:** The programming language for this project is **Python** and cannot be changed. +- **Coding Standards:** All coding, documentation, and reviews must adhere to established standards and examples. +- **Restricted Pushes:** Direct pushes to the main branch are prohibited. +- **Branch Merging Rules:** Branches can only be merged after a pull request is reviewed and approved. +- **Team Size:** The team consists of 10 members. However, active participation will be observed throughout the process, and adjustments will be made as needed to ensure tasks are distributed effectively and project goals are met. This flexible approach allows the team to adapt based on member availability and engagement. + +--- + +## **Internal: Involuntary** + + + +- **Different Schedules:** Team members have varying commitments, making synchronous meetings difficult. +- **Learning Together:** Some members are still learning relevant skills, requiring collaboration and support. +- **Limited Availability:** Balancing this project with other responsibilities might reduce availability at times. +- **Knowledge Gaps:** Some members lack experience in specific technologies or concepts, which may slow progress. +- **Dependency on Others:** Certain tasks depend on the completion of others, potentially causing bottlenecks. +- **Limited Review Capacity:** Fewer reviewers might delay pull request approvals and overall progress. + +--- + +## **Internal: Voluntary** + + + +- **Scope Management:** The project scope is deliberately kept small to ensure timely completion and avoid unnecessary complexity. +- **Adherence to Rules:** The team agrees to follow coding style guides, documentation standards, and review conventions. +- **Collaboration Commitment:** All members are dedicated to equal participation and supporting one another. +- **Documentation Priority:** All tasks, decisions, and code will be properly documented to facilitate collaboration. From dbb4eaa7516fecfe8d29dea918a7b5f2f3e39143 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 5 Jan 2025 00:52:34 +0100 Subject: [PATCH 24/52] Add communication plan to collaboration/communication.md --- .vscode/settings.json | 4 +- collaboration/communication.md | 78 +++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bbda5188d..252022b48 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -119,8 +119,8 @@ "editor.defaultFormatter": "charliermarsh.ruff", "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.ruff": true, - "source.organizeImports.ruff": true + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" } } } diff --git a/collaboration/communication.md b/collaboration/communication.md index 484652e0f..7be823233 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -9,46 +9,74 @@ # Communication -______________________________________________________________________ +--- ## Communication Schedule -| Day | How | The topic of discussion | | --- | :-: | ----------------------- | -| | | | +| **Day** | **How** | **Topic of Discussion** | +|---------------|--------------------------------|---------------------------| +| Flexible | Slack (Daily) | Quick updates, general communication | +| Twice a week | Google Meet | Progress updates, task discussions, and important decisions | + +--- ## Communication Channels -how often will we get in touch on each channel, and what we will discuss there: +### Frequency and topics for each channel + +- **Slack**: + - **Purpose**: Daily communication, task updates, and sharing quick announcements. + - **Frequency**: Regularly active for asynchronous communication. + +- **Google Meet**: + - **Purpose**: Discuss progress, blockers, and important milestones. + - **Frequency**: Twice per week or as needed based on the project phase. -- **Issues**: -- **Pull Requests**: -- **Slack/Discord**: -- **Video Calls**: +- **GitHub Issues**: + - **Purpose**: Track tasks, report bugs, and plan milestones. + - **Frequency**: Active for task management. -______________________________________________________________________ +- **GitHub Pull Requests**: + - **Purpose**: Code reviews and collaboration. + - **Frequency**: Notified via Slack when a review is needed. + +--- ## Availability -### Availability for calling/messaging +### Team's General Availability + +The team works asynchronously due to different time zones and schedules. To facilitate effective communication: + +- Meeting times are determined by a poll or decided during prior meetings. +- Members are encouraged to stay active on Slack to ensure timely updates. +- Meetings are recorded when possible, and summaries are shared for those unable to attend. -| Day | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday | | ------- | :----: | :-----: | :-------: | :------: | :----: | :------: | :----: | -| _name_ | | | | | | | | +### Response Time -### How many hours everyone has per day +- **Messages**: Responses on Slack are expected within **12 hours**. +- **Code Reviews**: Pull requests are reviewed within **24 hours** whenever possible. -- name: _5h_; -- name: _6h_; -- name: _5h_; -- name: _4h_; -- name: _3h_; +--- ## Asking for Help -There's a fine line between confidently learning from your mistakes, and -stubbornly getting no where. Here is a general guide for when to ask for help -based on how long you've been stuck on the same problem: +Here’s a general guide for when to ask for help if you’re stuck: + +1. **0 -> 30 minutes**: Try solving the problem independently using available resources. +2. **30 -> 60 minutes**: Reach out to the group via Slack for input or support. +3. **60+ minutes**: If still unresolved, tag a coach or mentor on Slack or GitHub. + +### Key Note + +Don’t hesitate to ask for help when needed. Collaboration is about supporting each other to achieve the project goals. + +--- + +## Meeting Agendas + +Meeting agendas will be created before each scheduled meeting: -1. _0 -> 30 min_: Try on your own -1. _30 -> 60 min_: Ask your group for help -1. _60+ min_: Tag your coaches in Slack or GitHub +- Agendas are drafted using GitHub Issues, where members can add discussion points. +- Meeting minutes and action items are recorded and shared in the issue comments or via Slack. +- All agenda points are addressed before closing the meeting issue. From 3e9b4f1df2eb669bb12e9ae736e1767d31e59b60 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Mon, 6 Jan 2025 01:52:59 +0100 Subject: [PATCH 25/52] Created project structure (merge_sort.py, merge.py, test_merge_sort.py) Created project structure (merge_sort.py, merge.py, test_merge_sort.py). No logic added into the files: output = input --- solutions/merge.py | 41 ++++++++++++++++++++++++++++++ solutions/merge_sort.py | 41 ++++++++++++++++++++++++++++++ solutions/tests/test_merge_sort.py | 31 ++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 solutions/merge.py create mode 100644 solutions/merge_sort.py create mode 100644 solutions/tests/test_merge_sort.py diff --git a/solutions/merge.py b/solutions/merge.py new file mode 100644 index 000000000..712cf51d6 --- /dev/null +++ b/solutions/merge.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for merging of two sorted collection into one sorted collection. + +Module contents: + - merge: merge two sorted collections into one sorted collection. + +Created on 2024-01-06 +Author: Oleksandr Maksymikhin +""" + + +def merge(left: list[int], right: list[int]) -> list[int]: + """Merge two sorted collections into one sorted collection. + + Combine two sorted homogeneous collections into one sorted collection + + Parameters: + left: list[int], first sorted collection of type int. + right: list[int], second sorted collection of type int. + + Returns -> list[int], collection of sorted elements with type int. + + Raises: + AssertionError: left is not a collection. + AssertionError: right is not a collection. + AssertionError: left collection contains elements of different data types (non-homogeneous). + AssertionError: right collection contains elements of different data types (non-homogeneous). + AssertionError: data of left and right collections have different types (non-homogeneous). + + Examples: + >>> merge([2], [1]) + [1, 2] + >>> merge_sort([1, 5], [2, 6]) + [1, 2, 5, 6] + >>> merge_sort([2, 5, 100500], [1, 9]) + [1, 2, 5, 9, 100500] + """ + + return left + right diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py new file mode 100644 index 000000000..01e5f3160 --- /dev/null +++ b/solutions/merge_sort.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for sorting collection using merge sort algorithm. +Splitting and sorting function. + +Module contents: + - merge_sort: splitting the collection and launching the merge. + +Created on 2024-01-06 +Author: Oleksandr Maksymikhin +""" + +from .merge import merge + + +def merge_sort(input_collection: list[int]) -> list[int]: + """Sort collection using merge sort algorithm. + + Sort elements in collection by splitting the collection in two parts and launching the merge. + + Parameters: + input_collection: list[int], collection of unsorted data type int. + + Returns -> list[int], collection of sorted elements with type int. + + Raises: + AssertionError: if input is not a collection. + AssertionError: if collection contains elements of different data types (non-homogeneous). + + Examples: + >>> merge_sort([1]) + [1] + >>> merge_sort([1, 3, 2]) + [1, 2, 3] + >>> merge_sort([3, 2, 100500, 1]) + [1, 2, 3, 100500] + """ + + merge([1], [2]) + return input_collection diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py new file mode 100644 index 000000000..47f146c2f --- /dev/null +++ b/solutions/tests/test_merge_sort.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for merge_sort() function. + +Test categories: + - Standard cases: lists of type int with different lengths + - Edge cases: empty lists, single element + - Different data type: char, string, bool + - Defensive tests: + - side effects protection + - input is not a collection + - different data types in the input collection (non-homogeneous) + +Created on 2024-01-06 +Author: Oleksandr Maksymikhin +""" + +import unittest + +from ..merge_sort import merge_sort + + +class TestMergeSort(unittest.TestCase): + """Test the merge_sort function.""" + + def test_1_empty_list(self): + """It should return [] for input []""" + actual = merge_sort([]) + expected = [] + self.assertEqual(actual, expected) From 88e1fdca439b922f90b3b32850f9c3cc66be2679 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Mon, 6 Jan 2025 10:22:27 +0100 Subject: [PATCH 26/52] Basic functionality of sorting with type int is working. Tests are passed Basic functionality of sorting with type int is working. Tests are passed --- solutions/merge.py | 37 ++++++++++++++++++++++++++---- solutions/merge_sort.py | 16 +++++++++---- solutions/tests/test_merge_sort.py | 26 ++++++++++++++++++++- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/solutions/merge.py b/solutions/merge.py index 712cf51d6..8c81c9ce3 100644 --- a/solutions/merge.py +++ b/solutions/merge.py @@ -6,7 +6,7 @@ Module contents: - merge: merge two sorted collections into one sorted collection. -Created on 2024-01-06 +Created on 2024-01-05 Author: Oleksandr Maksymikhin """ @@ -32,10 +32,39 @@ def merge(left: list[int], right: list[int]) -> list[int]: Examples: >>> merge([2], [1]) [1, 2] - >>> merge_sort([1, 5], [2, 6]) + >>> merge([1, 5], [2, 6]) [1, 2, 5, 6] - >>> merge_sort([2, 5, 100500], [1, 9]) + >>> merge([2, 5, 100500], [1, 9]) [1, 2, 5, 9, 100500] """ + # check if left or right collection is empty + if len(left) == 0: + return right + if len(right) == 0: + return left - return left + right + # create empty collection for merge + merged_collection = [] + # create indexed to traverse collections + index_left = index_right = 0 + + # merge collections until length of merged collection is smaller + # then length of left and right collections + while len(merged_collection) < len(left) + len(right): + if left[index_left] <= right[index_right]: + merged_collection.append(left[index_left]) + index_left += 1 + else: + merged_collection.append(right[index_right]) + index_right += 1 + + # if left or right collection becomes empty + # add elements of the other collection and break loop + if index_left == len(left): + merged_collection += right[index_right:] + break + if index_right == len(right): + merged_collection += left[index_left:] + break + + return merged_collection diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py index 01e5f3160..b3869cd83 100644 --- a/solutions/merge_sort.py +++ b/solutions/merge_sort.py @@ -7,11 +7,11 @@ Module contents: - merge_sort: splitting the collection and launching the merge. -Created on 2024-01-06 +Created on 2024-01-05 Author: Oleksandr Maksymikhin """ -from .merge import merge +from solutions.merge import merge def merge_sort(input_collection: list[int]) -> list[int]: @@ -36,6 +36,14 @@ def merge_sort(input_collection: list[int]) -> list[int]: >>> merge_sort([3, 2, 100500, 1]) [1, 2, 3, 100500] """ + if len(input_collection) < 2: + return input_collection - merge([1], [2]) - return input_collection + # divide collection to two parts + split_index = len(input_collection) // 2 + + left = merge_sort(input_collection[:split_index]) + right = merge_sort(input_collection[split_index:]) + + # return merged collection, sorting left and right parts + return merge(left, right) diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py index 47f146c2f..6d9a7b6e3 100644 --- a/solutions/tests/test_merge_sort.py +++ b/solutions/tests/test_merge_sort.py @@ -12,7 +12,7 @@ - input is not a collection - different data types in the input collection (non-homogeneous) -Created on 2024-01-06 +Created on 2024-01-05 Author: Oleksandr Maksymikhin """ @@ -29,3 +29,27 @@ def test_1_empty_list(self): actual = merge_sort([]) expected = [] self.assertEqual(actual, expected) + + def test_2_one_int_element_list(self): + """It should return [1] for input [1]""" + actual = merge_sort([1]) + expected = [1] + self.assertEqual(actual, expected) + + def test_3_two_int_elements_list(self): + """It should return [1, 2] for input [2, 1]""" + actual = merge_sort([2, 1]) + expected = [1, 2] + self.assertEqual(actual, expected) + + def test_4_three_int_elements_list(self): + """It should return [1, 2, 3] for input [3, 2, 1]""" + actual = merge_sort([3, 2, 1]) + expected = [1, 2, 3] + self.assertEqual(actual, expected) + + def test_5_four_int_elements_list_big_number(self): + """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" + actual = merge_sort([2, 3, 100500, 1]) + expected = [1, 2, 3, 100500] + self.assertEqual(actual, expected) From 8b278aaf0c2ba8c04b0acd5ad533b14dfe893815 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Mon, 6 Jan 2025 10:47:26 +0100 Subject: [PATCH 27/52] Extended solution to work with any data type (int, float, char, string, bool). Test coverage Extended solution to work with any data type (int, float, char, string, bool). Test coverage. --- solutions/merge.py | 8 ++++---- solutions/merge_sort.py | 6 +++--- solutions/tests/test_merge_sort.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/solutions/merge.py b/solutions/merge.py index 8c81c9ce3..acfbe4aaf 100644 --- a/solutions/merge.py +++ b/solutions/merge.py @@ -11,16 +11,16 @@ """ -def merge(left: list[int], right: list[int]) -> list[int]: +def merge(left: list[any], right: list[any]) -> list[any]: """Merge two sorted collections into one sorted collection. Combine two sorted homogeneous collections into one sorted collection Parameters: - left: list[int], first sorted collection of type int. - right: list[int], second sorted collection of type int. + left: list[any], first sorted collection of type any. + right: list[any], second sorted collection of type any. - Returns -> list[int], collection of sorted elements with type int. + Returns -> list[any], collection of sorted elements with type any. Raises: AssertionError: left is not a collection. diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py index b3869cd83..4742366e7 100644 --- a/solutions/merge_sort.py +++ b/solutions/merge_sort.py @@ -14,15 +14,15 @@ from solutions.merge import merge -def merge_sort(input_collection: list[int]) -> list[int]: +def merge_sort(input_collection: list[any]) -> list[any]: """Sort collection using merge sort algorithm. Sort elements in collection by splitting the collection in two parts and launching the merge. Parameters: - input_collection: list[int], collection of unsorted data type int. + input_collection: list[any], collection of unsorted data type any. - Returns -> list[int], collection of sorted elements with type int. + Returns -> list[any], collection of sorted elements with type any. Raises: AssertionError: if input is not a collection. diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py index 6d9a7b6e3..6880aba3f 100644 --- a/solutions/tests/test_merge_sort.py +++ b/solutions/tests/test_merge_sort.py @@ -24,6 +24,18 @@ class TestMergeSort(unittest.TestCase): """Test the merge_sort function.""" + # Uncomment for predictive stepping + # test_1 = merge_sort([]) + # test_2 = merge_sort([1]) + # test_3 = merge_sort([2, 1]) + # test_4 = merge_sort([3, 2, 1]) + # test_5 = merge_sort([2, 3, 100500, 1]) + # test_6 = merge_sort(["b", "a", "c"]) + # test_7 = merge_sort(["daikon", "cabbage", "banana", "apple"]) + # test_8 = merge_sort([True, False, True, False]) + # test_10 = merge_sort("apple") + # test_11 = merge_sort([3, "two", 1]) + def test_1_empty_list(self): """It should return [] for input []""" actual = merge_sort([]) @@ -53,3 +65,21 @@ def test_5_four_int_elements_list_big_number(self): actual = merge_sort([2, 3, 100500, 1]) expected = [1, 2, 3, 100500] self.assertEqual(actual, expected) + + def test_6_char_elements_list(self): + """It should return ["a", "b", "c", "d"] for input ["b", "d", "a", "c"]""" + actual = merge_sort(["b", "d", "a", "c"]) + expected = ["a", "b", "c", "d"] + self.assertEqual(actual, expected) + + def test_7_string_elements_list(self): + """It should return ["apple", "banana", "cabbage", "daikon"] for input ["daikon", "cabbage", "banana", "apple"]""" + actual = merge_sort(["daikon", "cabbage", "banana", "apple"]) + expected = ["apple", "banana", "cabbage", "daikon"] + self.assertEqual(actual, expected) + + def test_8_bool_elements_list(self): + """It should return [False, False, True, True] for input [True, False, False, True]""" + actual = merge_sort([True, False, False, True]) + expected = [False, False, True, True] + self.assertEqual(actual, expected) From 9652dd2c37b151fca948faa73da0470bf1812635 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Mon, 6 Jan 2025 10:50:50 +0100 Subject: [PATCH 28/52] Added protection from side effects with test coverage Added protection from side effects with test coverage --- solutions/merge_sort.py | 9 +++++---- solutions/tests/test_merge_sort.py | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py index 4742366e7..e4ac1a13c 100644 --- a/solutions/merge_sort.py +++ b/solutions/merge_sort.py @@ -36,14 +36,15 @@ def merge_sort(input_collection: list[any]) -> list[any]: >>> merge_sort([3, 2, 100500, 1]) [1, 2, 3, 100500] """ - if len(input_collection) < 2: + collection = input_collection.copy() + if len(collection) < 2: return input_collection # divide collection to two parts - split_index = len(input_collection) // 2 + split_index = len(collection) // 2 - left = merge_sort(input_collection[:split_index]) - right = merge_sort(input_collection[split_index:]) + left = merge_sort(collection[:split_index]) + right = merge_sort(collection[split_index:]) # return merged collection, sorting left and right parts return merge(left, right) diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py index 6880aba3f..cf94a9b2b 100644 --- a/solutions/tests/test_merge_sort.py +++ b/solutions/tests/test_merge_sort.py @@ -83,3 +83,10 @@ def test_8_bool_elements_list(self): actual = merge_sort([True, False, False, True]) expected = [False, False, True, True] self.assertEqual(actual, expected) + + def test_9_side_effect_protection(self): + """It should return [3, 2, 1] of initial input""" + input_list = [3, 2, 1, 100500] + copy_for_sorting = input_list.copy() + merge_sort(copy_for_sorting) + self.assertEqual(input_list, [3, 2, 1, 100500]) From d30c5b38e0dea4d29095bbd941899f26b2e9ff9b Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Mon, 6 Jan 2025 12:22:21 +0100 Subject: [PATCH 29/52] Completed solution. Limited functionality to process data types (int, float, string, bool) --- solutions/merge.py | 24 ++++++++++-- solutions/merge_sort.py | 32 ++++++++++++++-- solutions/tests/test_merge_sort.py | 60 ++++++++++++++++-------------- 3 files changed, 80 insertions(+), 36 deletions(-) diff --git a/solutions/merge.py b/solutions/merge.py index acfbe4aaf..0862d06d0 100644 --- a/solutions/merge.py +++ b/solutions/merge.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """ A module for merging of two sorted collection into one sorted collection. +Processed data types: int, float, string, bool. Module contents: - merge: merge two sorted collections into one sorted collection. @@ -17,17 +18,17 @@ def merge(left: list[any], right: list[any]) -> list[any]: Combine two sorted homogeneous collections into one sorted collection Parameters: - left: list[any], first sorted collection of type any. - right: list[any], second sorted collection of type any. + left: list[any], first sorted collection of data types (int, float, string, bool). + right: list[any], second sorted collection of data types (int, float, string, bool). - Returns -> list[any], collection of sorted elements with type any. + Returns -> list[any], sorted collection of data types (int, float, string, bool). Raises: AssertionError: left is not a collection. AssertionError: right is not a collection. AssertionError: left collection contains elements of different data types (non-homogeneous). AssertionError: right collection contains elements of different data types (non-homogeneous). - AssertionError: data of left and right collections have different types (non-homogeneous). + AssertionError: left and right contain elements of different data types(non-homogeneous). Examples: >>> merge([2], [1]) @@ -37,6 +38,21 @@ def merge(left: list[any], right: list[any]) -> list[any]: >>> merge([2, 5, 100500], [1, 9]) [1, 2, 5, 9, 100500] """ + # defensive assertion to check that input is a collections are lists + assert isinstance(left, list), "Input left is not a collection" + assert isinstance(right, list), "Input right is not a collection" + + # defensive assertion to check that input collections are homogeneous + assert all( + isinstance(item, type(left[0])) for item in left + ), "Input left is not homogeneous" + assert all( + isinstance(item, type(right[0])) for item in right + ), "Input right is not homogeneous" + assert isinstance( + left[0], type(right[0]) + ), "Input left and right are not homogeneous" + # check if left or right collection is empty if len(left) == 0: return right diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py index e4ac1a13c..cf50ea138 100644 --- a/solutions/merge_sort.py +++ b/solutions/merge_sort.py @@ -1,11 +1,12 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -A module for sorting collection using merge sort algorithm. +A module for sorting non empty collection of build-in elementary Python data types +(int, float, string, bool) using merge sort algorithm. Splitting and sorting function. Module contents: - - merge_sort: splitting the collection and launching the merge. + - merge_sort: function that splits the collection and launching the merge. Created on 2024-01-05 Author: Oleksandr Maksymikhin @@ -20,12 +21,14 @@ def merge_sort(input_collection: list[any]) -> list[any]: Sort elements in collection by splitting the collection in two parts and launching the merge. Parameters: - input_collection: list[any], collection of unsorted data type any. + input_collection: list[any], collection of unsorted data of data types (int, float, string, bool). - Returns -> list[any], collection of sorted elements with type any. + Returns -> list[any], collection of sorted elements of data types (int, float, string, bool). Raises: AssertionError: if input is not a collection. + AssertionError: if input collection is empty. + AssertionError: if input is not a data type processed by module (int, float, string, bool). AssertionError: if collection contains elements of different data types (non-homogeneous). Examples: @@ -36,6 +39,27 @@ def merge_sort(input_collection: list[any]) -> list[any]: >>> merge_sort([3, 2, 100500, 1]) [1, 2, 3, 100500] """ + + # defensive assertion to check that input is a collection list + assert isinstance(input_collection, list), "Input is not a collection" + + # defensive assertion to check that input collection is not empty + assert len(input_collection) > 0, "Collection is empty" + + # defensive assertion to check that input data types are any of (int, float, string, bool) + assert ( + isinstance(input_collection[0], int) + or isinstance(input_collection[0], float) + or isinstance(input_collection[0], str) + or isinstance(input_collection[0], bool) + ), "Input data types is not processed. Processed data types (int, float, string, bool)" + + # defensive assertion to check that collection is homogeneous + assert all( + isinstance(item, type(input_collection[0])) for item in input_collection + ), "Collection is not homogeneous" + + # copy collection to avoid side effect collection = input_collection.copy() if len(collection) < 2: return input_collection diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py index cf94a9b2b..4870710f4 100644 --- a/solutions/tests/test_merge_sort.py +++ b/solutions/tests/test_merge_sort.py @@ -5,12 +5,14 @@ Test categories: - Standard cases: lists of type int with different lengths - - Edge cases: empty lists, single element - - Different data type: char, string, bool + - Edge cases: single element + - Different data type: string, bool - Defensive tests: - side effects protection - input is not a collection + - input of empty collection - different data types in the input collection (non-homogeneous) + - input data types are not processed by module Created on 2024-01-05 Author: Oleksandr Maksymikhin @@ -24,69 +26,71 @@ class TestMergeSort(unittest.TestCase): """Test the merge_sort function.""" - # Uncomment for predictive stepping - # test_1 = merge_sort([]) - # test_2 = merge_sort([1]) - # test_3 = merge_sort([2, 1]) - # test_4 = merge_sort([3, 2, 1]) - # test_5 = merge_sort([2, 3, 100500, 1]) - # test_6 = merge_sort(["b", "a", "c"]) - # test_7 = merge_sort(["daikon", "cabbage", "banana", "apple"]) - # test_8 = merge_sort([True, False, True, False]) - # test_10 = merge_sort("apple") - # test_11 = merge_sort([3, "two", 1]) - - def test_1_empty_list(self): - """It should return [] for input []""" - actual = merge_sort([]) - expected = [] - self.assertEqual(actual, expected) - - def test_2_one_int_element_list(self): + def test_1_one_int_element_list(self): """It should return [1] for input [1]""" actual = merge_sort([1]) expected = [1] self.assertEqual(actual, expected) - def test_3_two_int_elements_list(self): + def test_2_two_int_elements_list(self): """It should return [1, 2] for input [2, 1]""" actual = merge_sort([2, 1]) expected = [1, 2] self.assertEqual(actual, expected) - def test_4_three_int_elements_list(self): + def test_3_three_int_elements_list(self): """It should return [1, 2, 3] for input [3, 2, 1]""" actual = merge_sort([3, 2, 1]) expected = [1, 2, 3] self.assertEqual(actual, expected) - def test_5_four_int_elements_list_big_number(self): + def test_4_four_int_elements_list_big_number(self): """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" actual = merge_sort([2, 3, 100500, 1]) expected = [1, 2, 3, 100500] self.assertEqual(actual, expected) - def test_6_char_elements_list(self): + def test_5_char_elements_list(self): """It should return ["a", "b", "c", "d"] for input ["b", "d", "a", "c"]""" actual = merge_sort(["b", "d", "a", "c"]) expected = ["a", "b", "c", "d"] self.assertEqual(actual, expected) - def test_7_string_elements_list(self): + def test_6_string_elements_list(self): """It should return ["apple", "banana", "cabbage", "daikon"] for input ["daikon", "cabbage", "banana", "apple"]""" actual = merge_sort(["daikon", "cabbage", "banana", "apple"]) expected = ["apple", "banana", "cabbage", "daikon"] self.assertEqual(actual, expected) - def test_8_bool_elements_list(self): + def test_7_bool_elements_list(self): """It should return [False, False, True, True] for input [True, False, False, True]""" actual = merge_sort([True, False, False, True]) expected = [False, False, True, True] self.assertEqual(actual, expected) - def test_9_side_effect_protection(self): + def test_8_side_effect_protection(self): """It should return [3, 2, 1] of initial input""" input_list = [3, 2, 1, 100500] copy_for_sorting = input_list.copy() merge_sort(copy_for_sorting) self.assertEqual(input_list, [3, 2, 1, 100500]) + + def test_9_non_collection_input(self): + """It should raise an assertion error if the input is not a collection""" + with self.assertRaises(AssertionError): + merge_sort("banana") + + def test_10_non_homogeneous_collection_input(self): + """It should raise an assertion error if the collection is non-homogeneous""" + with self.assertRaises(AssertionError): + merge_sort([3, 2, "one"]) + + def test_11_input_of_empty_collection(self): + """It should raise an assertion error if the input data type is out of processed type""" + with self.assertRaises(AssertionError): + merge_sort([]) + + def test_12_input_of_non_processed_data_type(self): + """It should raise an assertion error if the input data type is out of processed type""" + with self.assertRaises(AssertionError): + merge_sort([{1, "one"}, {2, "two"}]) From a56cf0127d56c251ba5a5ae1bab498260e682760 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Mon, 6 Jan 2025 15:28:36 +0100 Subject: [PATCH 30/52] Fix special character handling and update test cases based on review --- solutions/is_palindrome.py | 13 ++++++++++++- solutions/tests/test_is_palindrome.py | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py index a84482e10..b55b46ea3 100644 --- a/solutions/is_palindrome.py +++ b/solutions/is_palindrome.py @@ -31,12 +31,23 @@ def is_palindrome(text: str) -> bool: False >>> is_palindrome("A man a plan a canal Panama") True + >>> is_palindrome("$+$") + True + >>> is_palindrome("$+#") + False """ # Ensure the input is of type string to avoid unexpected errors. assert isinstance(text, str), "Input must be a string" # Normalize the text: remove spaces and convert to lowercase - normalized = "".join(char.lower() for char in text if char.isalnum()) + normalized = "".join([char.lower() for char in text if not char.isspace()]) # Check if the string is the same when reversed return normalized == normalized[::-1] + + +if __name__ == "__main__": + # Example cases to test functionality + print(is_palindrome("$+$")) # Should return True + print(is_palindrome("$+#")) # Should return False + print(is_palindrome("A man a plan a canal Panama")) # Should return True diff --git a/solutions/tests/test_is_palindrome.py b/solutions/tests/test_is_palindrome.py index 530b5af4a..7a4a562ae 100644 --- a/solutions/tests/test_is_palindrome.py +++ b/solutions/tests/test_is_palindrome.py @@ -1,3 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Unit tests for the is_palindrome function. + +This module contains test cases for the is_palindrome function, +which checks if a string is a palindrome. The tests cover: + +- Empty strings +- Single-character strings +- Palindromes with lowercase, mixed-case, and spaces +- Palindromes containing numbers +- Palindromes containing special characters +- Non-palindromes + +Created on 2024-12-30 +Author: Emre Biyik +""" + import unittest from solutions.is_palindrome import is_palindrome @@ -53,6 +72,14 @@ def test_non_palindrome_with_special_characters_returns_false(self): """It should return False for non-palindromes with special characters.""" self.assertFalse(is_palindrome("hello!")) + def test_special_characters_palindrome_positive(self): + """It should return True for special character palindrome.""" + self.assertTrue(is_palindrome("$+$")) # Palindrome + + def test_special_characters_palindrome_negative(self): + """It should return False for non-palindrome special characters.""" + self.assertFalse(is_palindrome("$+#")) # Non-palindrome + if __name__ == "__main__": unittest.main() From 9d85d3a4c4373189aaefccae3fd5433f91050d10 Mon Sep 17 00:00:00 2001 From: colevandersWands <18554853+colevandersWands@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:35:14 -0500 Subject: [PATCH 31/52] clarify "no function calls" item --- .github/PULL_REQUEST_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6e269b4ad..eb18b4638 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -55,7 +55,8 @@ about: A template PR for code review with a checklist - [ ] The function's name describes it's behavior - [ ] The function's name matches the file name - [ ] The function has correct type annotations -- [ ] The function is not called in the function file +- [ ] The function is not called at the top level of the function file + - _Recursive solutions **can** call the function from **inside** the function body_ ## Strategy From 91ab5b86713479bb7fe7069920b1ce9655031203 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Tue, 7 Jan 2025 00:00:45 +0100 Subject: [PATCH 32/52] Refactor register_user function docstring and add comments for clarity --- solutions/register_user.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/solutions/register_user.py b/solutions/register_user.py index b736d454f..a26d45b20 100644 --- a/solutions/register_user.py +++ b/solutions/register_user.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -A module for registering users with validated inputs. +A module for validating user registration inputs. Module contents: - register_user: Validates user details and returns a registration dictionary. @@ -13,7 +13,7 @@ def register_user(name: str, age: int, email: str) -> dict: """ - Registers a user with the given name, age, and email. + Validates the parameters (name, age, email) provided for user registration. Parameters: name (str): User's name. Must contain only alphabetic characters. @@ -24,7 +24,9 @@ def register_user(name: str, age: int, email: str) -> dict: dict: A dictionary containing the user's details. Raises: - ValueError: If `name`, `age`, or `email` is invalid. + ValueError: If name contains non-alphabetic characters. + ValueError: If age is under 18 or over 99 years old. + ValueError: If email does not include symbols "@" and "." (in the part after the "@" separator). Examples: >>> register_user("Alice", 25, "alice@example.com") @@ -33,11 +35,18 @@ def register_user(name: str, age: int, email: str) -> dict: >>> register_user("Bob", 18, "bob@example.net") {'name': 'Bob', 'age': 18, 'email': 'bob@example.net'} + >>> register_user("Dave", 30, "dave@example.org") + {'name': 'Dave', 'age': 30, 'email': 'dave@example.org'} """ + # Check if name contains only alphabetic characters if not name.isalpha(): raise ValueError("Invalid name: Must contain only alphabetic characters.") + + # Check if age is within the valid range (18-99) if not isinstance(age, int) or not (18 <= age <= 99): raise ValueError("Invalid age: Must be an integer between 18 and 99.") + + # Check if email contains "@" and "." if "@" not in email or "." not in email.split("@")[-1]: raise ValueError("Invalid email: Must be a valid email format.") From 50a35d279bd92d18a2886bf55d9c483e8aa859b7 Mon Sep 17 00:00:00 2001 From: colevandersWands <18554853+colevandersWands@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:18:33 -0500 Subject: [PATCH 33/52] checklist updates post-Q&A --- .github/PULL_REQUEST_TEMPLATE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index eb18b4638..eba8a7be2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -54,6 +54,7 @@ about: A template PR for code review with a checklist - [ ] The function's name describes it's behavior - [ ] The function's name matches the file name + - _It's ok to have extra helper functions if necessary, like with mergesort_ - [ ] The function has correct type annotations - [ ] The function is not called at the top level of the function file - _Recursive solutions **can** call the function from **inside** the function body_ @@ -68,7 +69,7 @@ about: A template PR for code review with a checklist ### Don'ts -- [ ] The function's strategy _is not_ described in the documentation +- [ ] The function's strategy _is not_ described in any docstrings or tests - [ ] Comments explain the _strategy_, **not** the _implementation_ - [ ] The function _does not_ have more comments than code - If it does, consider finding a new strategy or a simpler implementation From 8d27f24a51073d4ca2e0971b5f1802bd9ffadeb4 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Wed, 8 Jan 2025 08:17:12 +0100 Subject: [PATCH 34/52] Added input types verification/ Processed types (int, float, str, bool) Added input types verification/ Processed types (int, float, str, bool) --- solutions/bubble_sort.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/solutions/bubble_sort.py b/solutions/bubble_sort.py index 3cfca7e68..acee5cb30 100644 --- a/solutions/bubble_sort.py +++ b/solutions/bubble_sort.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """ A module for sorting collection using bubble sort algorithm. +Processed types: int, float, str, bool Module contents: - bubble_sort: sort the collection in ascending order. @@ -10,17 +11,21 @@ Author: Oleksandr Maksymikhin """ +from typing import TypeVar + +T = TypeVar("T", int, float, str, bool) + # def bubble_sort(input_collection: list[int]) -> list[int]: -def bubble_sort(input_collection: list[any]) -> list[any]: +def bubble_sort(input_collection: list[T]) -> list[T]: """Sort collection using bubble sort algorithm. Sort elements in collection by swapping and moving larger elements to the end of collection. Parameters: - input_collection: list[any], collection of unsorted data of any homogeneous type. + input_collection: list[T], collection of unsorted data with data types (T): int, float, str, bool. - Returns -> list[any], collection of sorted elements with any data type. + Returns -> list[T], collection of sorted elements with data types (T): int, float, str, bool. Raises: AssertionError: if input is not a collection. From 6483d548db9844a221e72fc8147490ddb99a4d08 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Wed, 8 Jan 2025 15:08:04 +0100 Subject: [PATCH 35/52] Added TypeVar for specifying input data types (int, float, str, bool) Corrected code after code review. Added TypeVar for specifying input data types (int, float, str, bool). Small corrections by texting/comments --- solutions/merge.py | 86 -------------------------- solutions/merge_sort.py | 98 +++++++++++++++++++++++++++--- solutions/tests/test_merge_sort.py | 31 +++++----- 3 files changed, 105 insertions(+), 110 deletions(-) delete mode 100644 solutions/merge.py diff --git a/solutions/merge.py b/solutions/merge.py deleted file mode 100644 index 0862d06d0..000000000 --- a/solutions/merge.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -A module for merging of two sorted collection into one sorted collection. -Processed data types: int, float, string, bool. - -Module contents: - - merge: merge two sorted collections into one sorted collection. - -Created on 2024-01-05 -Author: Oleksandr Maksymikhin -""" - - -def merge(left: list[any], right: list[any]) -> list[any]: - """Merge two sorted collections into one sorted collection. - - Combine two sorted homogeneous collections into one sorted collection - - Parameters: - left: list[any], first sorted collection of data types (int, float, string, bool). - right: list[any], second sorted collection of data types (int, float, string, bool). - - Returns -> list[any], sorted collection of data types (int, float, string, bool). - - Raises: - AssertionError: left is not a collection. - AssertionError: right is not a collection. - AssertionError: left collection contains elements of different data types (non-homogeneous). - AssertionError: right collection contains elements of different data types (non-homogeneous). - AssertionError: left and right contain elements of different data types(non-homogeneous). - - Examples: - >>> merge([2], [1]) - [1, 2] - >>> merge([1, 5], [2, 6]) - [1, 2, 5, 6] - >>> merge([2, 5, 100500], [1, 9]) - [1, 2, 5, 9, 100500] - """ - # defensive assertion to check that input is a collections are lists - assert isinstance(left, list), "Input left is not a collection" - assert isinstance(right, list), "Input right is not a collection" - - # defensive assertion to check that input collections are homogeneous - assert all( - isinstance(item, type(left[0])) for item in left - ), "Input left is not homogeneous" - assert all( - isinstance(item, type(right[0])) for item in right - ), "Input right is not homogeneous" - assert isinstance( - left[0], type(right[0]) - ), "Input left and right are not homogeneous" - - # check if left or right collection is empty - if len(left) == 0: - return right - if len(right) == 0: - return left - - # create empty collection for merge - merged_collection = [] - # create indexed to traverse collections - index_left = index_right = 0 - - # merge collections until length of merged collection is smaller - # then length of left and right collections - while len(merged_collection) < len(left) + len(right): - if left[index_left] <= right[index_right]: - merged_collection.append(left[index_left]) - index_left += 1 - else: - merged_collection.append(right[index_right]) - index_right += 1 - - # if left or right collection becomes empty - # add elements of the other collection and break loop - if index_left == len(left): - merged_collection += right[index_right:] - break - if index_right == len(right): - merged_collection += left[index_left:] - break - - return merged_collection diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py index cf50ea138..ef9b7ef9b 100644 --- a/solutions/merge_sort.py +++ b/solutions/merge_sort.py @@ -2,28 +2,32 @@ # -*- coding: utf-8 -*- """ A module for sorting non empty collection of build-in elementary Python data types -(int, float, string, bool) using merge sort algorithm. -Splitting and sorting function. +(int, float, str, bool) using merge sorting algorithm. Module contents: - - merge_sort: function that splits the collection and launching the merge. + - merge_sort: function that splits the collection and launches the merge. + - merge: function that merges two sorted collections into one sorted collection. Created on 2024-01-05 Author: Oleksandr Maksymikhin """ -from solutions.merge import merge +from typing import TypeVar +# from solutions.merge import merge -def merge_sort(input_collection: list[any]) -> list[any]: - """Sort collection using merge sort algorithm. +T = TypeVar("T", int, float, str, bool) - Sort elements in collection by splitting the collection in two parts and launching the merge. + +def merge_sort(input_collection: list[T]) -> list[T]: + """Sort collection using merge sorting algorithm. + + Sort elements in collection by splitting the collection in two parts and launch merge. Parameters: - input_collection: list[any], collection of unsorted data of data types (int, float, string, bool). + input_collection: list[T], collection of unsorted elements of data types (int, float, string, bool). - Returns -> list[any], collection of sorted elements of data types (int, float, string, bool). + Returns -> list[T], collection of sorted elements of data types (int, float, string, bool). Raises: AssertionError: if input is not a collection. @@ -38,6 +42,8 @@ def merge_sort(input_collection: list[any]) -> list[any]: [1, 2, 3] >>> merge_sort([3, 2, 100500, 1]) [1, 2, 3, 100500] + >>> merge_sort(['banana', 'apple', 'cherry']) + ['apple', 'banana', 'cherry'] """ # defensive assertion to check that input is a collection list @@ -72,3 +78,77 @@ def merge_sort(input_collection: list[any]) -> list[any]: # return merged collection, sorting left and right parts return merge(left, right) + + +def merge(left: list[T], right: list[T]) -> list[T]: + """Merge two sorted collections into one sorted collection. + + Combine two sorted homogeneous collections into one sorted collection + + Parameters: + left: list[T], first sorted collection of data types (int, float, string, bool). + right: list[T], second sorted collection of data types (int, float, string, bool). + + Returns -> list[T], sorted collection of data types (int, float, string, bool). + + Raises: + AssertionError: left is not a collection. + AssertionError: right is not a collection. + AssertionError: left collection contains elements of different data types (non-homogeneous). + AssertionError: right collection contains elements of different data types (non-homogeneous). + AssertionError: left and right contain elements of different data types(non-homogeneous). + + Examples: + >>> merge([2], [1]) + [1, 2] + >>> merge([1, 5], [2, 6]) + [1, 2, 5, 6] + >>> merge([2, 5, 100500], [1, 9]) + [1, 2, 5, 9, 100500] + """ + # defensive assertion to check that input is a collections are lists + assert isinstance(left, list), "Input left is not a collection" + assert isinstance(right, list), "Input right is not a collection" + + # defensive assertion to check that input collections are homogeneous + assert all( + isinstance(item, type(left[0])) for item in left + ), "Input left is not homogeneous" + assert all( + isinstance(item, type(right[0])) for item in right + ), "Input right is not homogeneous" + assert isinstance( + left[0], type(right[0]) + ), "Input left and right are not homogeneous" + + # check if left or right collection is empty + if len(left) == 0: + return right + if len(right) == 0: + return left + + # create empty collection for merge + merged_collection = [] + # create indexed to traverse collections + index_left = index_right = 0 + + # merge collections until length of merged collection is smaller + # then length of left and right collections + while len(merged_collection) < len(left) + len(right): + if left[index_left] <= right[index_right]: + merged_collection.append(left[index_left]) + index_left += 1 + else: + merged_collection.append(right[index_right]) + index_right += 1 + + # if left or right collection becomes empty + # add elements of the other collection and break loop + if index_left == len(left): + merged_collection += right[index_right:] + break + if index_right == len(right): + merged_collection += left[index_left:] + break + + return merged_collection diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py index 4870710f4..062dd5d10 100644 --- a/solutions/tests/test_merge_sort.py +++ b/solutions/tests/test_merge_sort.py @@ -2,17 +2,18 @@ # -*- coding: utf-8 -*- """ Test module for merge_sort() function. +Test sorting of homogeneous collections types (int, float, string, bool) using merge algorithm. Test categories: - Standard cases: lists of type int with different lengths - - Edge cases: single element - - Different data type: string, bool + - Edge cases: single element type int + - Different data type: str, bool - Defensive tests: - side effects protection - input is not a collection - input of empty collection - different data types in the input collection (non-homogeneous) - - input data types are not processed by module + - input data types that are not processed by module Created on 2024-01-05 Author: Oleksandr Maksymikhin @@ -26,71 +27,71 @@ class TestMergeSort(unittest.TestCase): """Test the merge_sort function.""" - def test_1_one_int_element_list(self): + def test_single_int_element_list(self): """It should return [1] for input [1]""" actual = merge_sort([1]) expected = [1] self.assertEqual(actual, expected) - def test_2_two_int_elements_list(self): + def test_two_int_elements_list(self): """It should return [1, 2] for input [2, 1]""" actual = merge_sort([2, 1]) expected = [1, 2] self.assertEqual(actual, expected) - def test_3_three_int_elements_list(self): + def test_three_int_elements_list(self): """It should return [1, 2, 3] for input [3, 2, 1]""" actual = merge_sort([3, 2, 1]) expected = [1, 2, 3] self.assertEqual(actual, expected) - def test_4_four_int_elements_list_big_number(self): + def test_four_int_elements_list_big_number(self): """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" actual = merge_sort([2, 3, 100500, 1]) expected = [1, 2, 3, 100500] self.assertEqual(actual, expected) - def test_5_char_elements_list(self): + def test_char_elements_list(self): """It should return ["a", "b", "c", "d"] for input ["b", "d", "a", "c"]""" actual = merge_sort(["b", "d", "a", "c"]) expected = ["a", "b", "c", "d"] self.assertEqual(actual, expected) - def test_6_string_elements_list(self): + def test_str_elements_list(self): """It should return ["apple", "banana", "cabbage", "daikon"] for input ["daikon", "cabbage", "banana", "apple"]""" actual = merge_sort(["daikon", "cabbage", "banana", "apple"]) expected = ["apple", "banana", "cabbage", "daikon"] self.assertEqual(actual, expected) - def test_7_bool_elements_list(self): + def test_bool_elements_list(self): """It should return [False, False, True, True] for input [True, False, False, True]""" actual = merge_sort([True, False, False, True]) expected = [False, False, True, True] self.assertEqual(actual, expected) - def test_8_side_effect_protection(self): + def test_side_effect_protection(self): """It should return [3, 2, 1] of initial input""" input_list = [3, 2, 1, 100500] copy_for_sorting = input_list.copy() merge_sort(copy_for_sorting) self.assertEqual(input_list, [3, 2, 1, 100500]) - def test_9_non_collection_input(self): + def test_non_collection_input(self): """It should raise an assertion error if the input is not a collection""" with self.assertRaises(AssertionError): merge_sort("banana") - def test_10_non_homogeneous_collection_input(self): + def test_non_homogeneous_collection_input(self): """It should raise an assertion error if the collection is non-homogeneous""" with self.assertRaises(AssertionError): merge_sort([3, 2, "one"]) - def test_11_input_of_empty_collection(self): + def test_input_of_empty_collection(self): """It should raise an assertion error if the input data type is out of processed type""" with self.assertRaises(AssertionError): merge_sort([]) - def test_12_input_of_non_processed_data_type(self): + def test_input_of_non_processed_data_type(self): """It should raise an assertion error if the input data type is out of processed type""" with self.assertRaises(AssertionError): merge_sort([{1, "one"}, {2, "two"}]) From 53c89e9b0101a97911bfceb2dd19a3617d650298 Mon Sep 17 00:00:00 2001 From: AhmadHamedDehzad Date: Thu, 9 Jan 2025 14:59:42 +0100 Subject: [PATCH 36/52] Added documentation and unittest for word frequency counter --- solutions/__init__.py | 1 + solutions/tests/__init__.py | 1 + .../tests/test_word_frequency_counter.py | 62 +++++++++++++++++++ solutions/word_frequency_counter.py | 54 ++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 solutions/tests/test_word_frequency_counter.py create mode 100644 solutions/word_frequency_counter.py diff --git a/solutions/__init__.py b/solutions/__init__.py index e69de29bb..8b1378917 100644 --- a/solutions/__init__.py +++ b/solutions/__init__.py @@ -0,0 +1 @@ + diff --git a/solutions/tests/__init__.py b/solutions/tests/__init__.py index e69de29bb..8b1378917 100644 --- a/solutions/tests/__init__.py +++ b/solutions/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/solutions/tests/test_word_frequency_counter.py b/solutions/tests/test_word_frequency_counter.py new file mode 100644 index 000000000..900268d92 --- /dev/null +++ b/solutions/tests/test_word_frequency_counter.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on 01.01.2025 + +@author: Ahmad Hamed Dehzad +""" + +import unittest + +from solutions.word_frequency_counter import word_frequency_counter + + +class TestWordFrequencyCounter(unittest.TestCase): + """Test the word_frequency_counter function""" + + def test_empty_string(self): + """It should evaluate an empty string to an empty dictionary""" + actual = word_frequency_counter("") + expected = {} + self.assertEqual(actual, expected) + + def test_single_word(self): + """It should evaluate a single word correctly""" + actual = word_frequency_counter("Hello") + expected = {"hello": 1} + self.assertEqual(actual, expected) + + def test_multiple_words(self): + """It should count the frequency of multiple words""" + actual = word_frequency_counter("Hello world hello") + expected = {"hello": 2, "world": 1} + self.assertEqual(actual, expected) + + def test_case_insensitivity(self): + """It should treat words case-insensitively""" + actual = word_frequency_counter("Apple apple APPLE") + expected = {"apple": 3} + self.assertEqual(actual, expected) + + def test_with_punctuation(self): + """It should handle words with punctuation correctly""" + actual = word_frequency_counter("Hello, world! Hello.") + expected = {"hello,": 1, "world!": 1, "hello.": 1} + self.assertEqual(actual, expected) + + def test_numbers_in_string(self): + """It should handle strings with numbers""" + actual = word_frequency_counter("123 123 456") + expected = {"123": 2, "456": 1} + self.assertEqual(actual, expected) + + def test_non_string_input(self): + """It should raise a ValueError if the input is not a string""" + with self.assertRaises(ValueError): + word_frequency_counter(12345) + + def test_with_whitespace(self): + """It should handle extra spaces correctly""" + actual = word_frequency_counter(" Hello world ") + expected = {"hello": 1, "world": 1} + self.assertEqual(actual, expected) \ No newline at end of file diff --git a/solutions/word_frequency_counter.py b/solutions/word_frequency_counter.py new file mode 100644 index 000000000..8985268c6 --- /dev/null +++ b/solutions/word_frequency_counter.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for counting the frequency of words in a given sentence. + +Module contents: + - word_frequency_counter: calculates the frequency of each word in a sentence. + - display_word_frequencies: prints the word frequencies in a readable format. + +Created on 01.01.2025 +@author: Ahmad Hamed Dehzad +""" + +# def word_frequency_counter(sentence): +# if not isinstance(sentence, str): +# raise ValueError("Invalid input: Please enter a text string.") +# words = sentence.split() +# word_frequencies = {} +# for word in words: +# word = word.lower() +# if word in word_frequencies: +# word_frequencies[word] += 1 +# else: +# word_frequencies[word] = 1 +# return word_frequencies + + +# --- after documenting and testing --- + +def word_frequency_counter(sentence: str) -> dict[str, int]: + """Counts the frequency of each word in a given sentence. + + Parameters: + sentence: str, a sentence containing words separated by spaces. + + Returns: + A dictionary where keys are words (case-insensitive) and values are their frequency. + + Raises: + ValueError: If the input is not a string. + """ + if not isinstance(sentence, str): + raise ValueError("Invalid input: Please enter a text string.") + + words = sentence.split() + word_frequencies = {} + for word in words: + word = word.lower() + if word in word_frequencies: + word_frequencies[word] += 1 + else: + word_frequencies[word] = 1 + + return word_frequencies \ No newline at end of file From a8c11169d2724fb64c5d589d91ee86763bd01e7c Mon Sep 17 00:00:00 2001 From: AhmadHamedDehzad Date: Thu, 9 Jan 2025 15:59:45 +0100 Subject: [PATCH 37/52] Doc string for test module is added --- solutions/tests/test_word_frequency_counter.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/solutions/tests/test_word_frequency_counter.py b/solutions/tests/test_word_frequency_counter.py index 900268d92..29e421b55 100644 --- a/solutions/tests/test_word_frequency_counter.py +++ b/solutions/tests/test_word_frequency_counter.py @@ -1,6 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ +This is test module for counting the frequency of words in a given sentence. + +Test categories: + - Standard cases: typical strings with different lengths + - Edge cases: white space input + - Defensive tests: non string inputs + Created on 01.01.2025 @author: Ahmad Hamed Dehzad From ece29e2b47c5e081b344baf3bca33a6e129dda81 Mon Sep 17 00:00:00 2001 From: AhmadHamedDehzad Date: Thu, 9 Jan 2025 18:31:34 +0100 Subject: [PATCH 38/52] Added documentation and unittest for temperature converter --- solutions/__init__.py | 1 + solutions/temperature_converter.py | 71 ++++++++++++++++++ solutions/tests/__init__.py | 1 + solutions/tests/test_temperature_converter.py | 73 +++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 solutions/temperature_converter.py create mode 100644 solutions/tests/test_temperature_converter.py diff --git a/solutions/__init__.py b/solutions/__init__.py index e69de29bb..8b1378917 100644 --- a/solutions/__init__.py +++ b/solutions/__init__.py @@ -0,0 +1 @@ + diff --git a/solutions/temperature_converter.py b/solutions/temperature_converter.py new file mode 100644 index 000000000..fef471668 --- /dev/null +++ b/solutions/temperature_converter.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for converting temperatures between Celsius and Fahrenheit. + +Module contents: + - celsius_to_fahrenheit: Converts a temperature from Celsius to Fahrenheit. + - fahrenheit_to_celsius: Converts a temperature from Fahrenheit to Celsius. + +Created on 01.05.2025 +@author: Ahmad Hamed Dehzad +""" + +# def celsius_to_fahrenheit(celsius): +# return (celsius * 9/5) + 32 +# +# def fahrenheit_to_celsius(fahrenheit): +# return (fahrenheit - 32) * 5/9 + +def celsius_to_fahrenheit(celsius: float) -> float: + """Converts a temperature from Celsius to Fahrenheit. + + Parameters: + celsius: float, the temperature in Celsius. + + Returns: + float: The temperature in Fahrenheit. + + Raises: + ValueError: If the input is not a number. + + >>> celsius_to_fahrenheit(0) + 32.0 + + >>> celsius_to_fahrenheit(100) + 212.0 + + >>> celsius_to_fahrenheit(-40) + -40.0 + """ + if not isinstance(celsius, (int, float)): + raise ValueError("Invalid input: Temperature must be a number.") + + return (celsius * 9 / 5) + 32 + + +def fahrenheit_to_celsius(fahrenheit: float) -> float: + """Converts a temperature from Fahrenheit to Celsius. + + Parameters: + fahrenheit: float, the temperature in Fahrenheit. + + Returns: + float: The temperature in Celsius. + + Raises: + ValueError: If the input is not a number. + + >>> fahrenheit_to_celsius(32) + 0.0 + + >>> fahrenheit_to_celsius(212) + 100.0 + + >>> fahrenheit_to_celsius(-40) + -40.0 + """ + if not isinstance(fahrenheit, (int, float)): + raise ValueError("Invalid input: Temperature must be a number.") + + return (fahrenheit - 32) * 5 / 9 \ No newline at end of file diff --git a/solutions/tests/__init__.py b/solutions/tests/__init__.py index e69de29bb..8b1378917 100644 --- a/solutions/tests/__init__.py +++ b/solutions/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/solutions/tests/test_temperature_converter.py b/solutions/tests/test_temperature_converter.py new file mode 100644 index 000000000..a80160d30 --- /dev/null +++ b/solutions/tests/test_temperature_converter.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +This is a test module for converting temperatures between Celsius and Fahrenheit. + +Test categories: + - Standard cases: typical temperature values + - Edge cases: extreme temperatures and boundary values + - Defensive tests: non-numeric inputs + +Created on 01.05.2025 + +@author: Ahmad Hamed Dehzad +""" + +import unittest + +from ..temperature_converter import celsius_to_fahrenheit, fahrenheit_to_celsius + +class TestTemperatureConverter(unittest.TestCase): + """Test the temperature conversion functions""" + + # Tests for celsius_to_fahrenheit + def test_celsius_to_fahrenheit_zero(self): + """It should convert 0°C to 32°F""" + actual = celsius_to_fahrenheit(0) + expected = 32.0 + self.assertEqual(actual, expected) + + def test_celsius_to_fahrenheit_positive(self): + """It should convert 100°C to 212°F""" + actual = celsius_to_fahrenheit(100) + expected = 212.0 + self.assertEqual(actual, expected) + + def test_celsius_to_fahrenheit_negative(self): + """It should convert -40°C to -40°F""" + actual = celsius_to_fahrenheit(-40) + expected = -40.0 + self.assertEqual(actual, expected) + + def test_celsius_to_fahrenheit_non_numeric(self): + """It should raise a ValueError if the input is not a number""" + with self.assertRaises(ValueError): + celsius_to_fahrenheit("invalid") + + # Tests for fahrenheit_to_celsius + def test_fahrenheit_to_celsius_zero(self): + """It should convert 32°F to 0°C""" + actual = fahrenheit_to_celsius(32) + expected = 0.0 + self.assertEqual(actual, expected) + + def test_fahrenheit_to_celsius_positive(self): + """It should convert 212°F to 100°C""" + actual = fahrenheit_to_celsius(212) + expected = 100.0 + self.assertEqual(actual, expected) + + def test_fahrenheit_to_celsius_negative(self): + """It should convert -40°F to -40°C""" + actual = fahrenheit_to_celsius(-40) + expected = -40.0 + self.assertEqual(actual, expected) + + def test_fahrenheit_to_celsius_non_numeric(self): + """It should raise a ValueError if the input is not a number""" + with self.assertRaises(ValueError): + fahrenheit_to_celsius("invalid") + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file From efe11dbde17c0470b9bb6d987f3320932fb7dc55 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Jan 2025 01:09:24 +0100 Subject: [PATCH 39/52] ascending_order first commit --- solutions/ascending_order_18/__init__.py | 0 .../ascending_order_18/ascending_order.py | 32 +++++++++++++++++++ solutions/tests/test_ascending_order.py | 29 +++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 solutions/ascending_order_18/__init__.py create mode 100644 solutions/ascending_order_18/ascending_order.py create mode 100644 solutions/tests/test_ascending_order.py diff --git a/solutions/ascending_order_18/__init__.py b/solutions/ascending_order_18/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/solutions/ascending_order_18/ascending_order.py b/solutions/ascending_order_18/ascending_order.py new file mode 100644 index 000000000..6a2be56df --- /dev/null +++ b/solutions/ascending_order_18/ascending_order.py @@ -0,0 +1,32 @@ +"""this is a solution for the ascending order problem.""" + + +def sort_numbers(num_list): + """ + Sorts a list of numbers in ascending order. + + Args: + num_list (list): A list of numbers (integers or floats). + + Returns: + list: A new list containing the numbers sorted in ascending order. + """ + return sorted(num_list) + + +def get_input(): + """ + Takes a comma-separated string input from the user + converts it to a list of numbers, and sorts them. + + Returns: + list: A sorted list of numbers. + """ + user_input = input("Enter a list of numbers separated by commas: ") + num_list = [int(num.strip()) for num in user_input.split(",")] + return sort_numbers(num_list) + + +if __name__ == "__main__": + sorted_list = get_input() + print(f"Sorted list: {sorted_list}") diff --git a/solutions/tests/test_ascending_order.py b/solutions/tests/test_ascending_order.py new file mode 100644 index 000000000..cbccaf64f --- /dev/null +++ b/solutions/tests/test_ascending_order.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +"""This module contains the unit tests for the sort_numbers function.""" + +import unittest +from solutions.ascending_order_18.ascending_order import sort_numbers + + +class TestSortNumbers(unittest.TestCase): + """tests for the sort_numbers function""" + + def test_empty_list(self): + """Test if the function handles an empty list and returns an empty list.""" + self.assertEqual(sort_numbers([]), []) + + def test_sorted_list(self): + """Test if the function returns the correct sorted list.""" + self.assertEqual(sort_numbers([5, 2, 9, 1, 5, 6]), [1, 2, 5, 5, 6, 9]) + + def test_negative_and_positive_numbers(self): + """Test if the function handles negative and positive numbers correctly.""" + self.assertEqual(sort_numbers([5, -3, 9, -1, 0, 6]), [-3, -1, 0, 5, 6, 9]) + + def test_large_numbers(self): + """Test if the function handles large numbers correctly.""" + self.assertEqual( + sort_numbers([200000, 500000, 1000000]), [200000, 500000, 1000000] + ) From e37408715f642b98e3dace725557147823cfb625 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Jan 2025 02:02:14 +0100 Subject: [PATCH 40/52] ascending_order second --- solutions/merge_sort.py | 154 ---------------------------------------- 1 file changed, 154 deletions(-) delete mode 100644 solutions/merge_sort.py diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py deleted file mode 100644 index ef9b7ef9b..000000000 --- a/solutions/merge_sort.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -A module for sorting non empty collection of build-in elementary Python data types -(int, float, str, bool) using merge sorting algorithm. - -Module contents: - - merge_sort: function that splits the collection and launches the merge. - - merge: function that merges two sorted collections into one sorted collection. - -Created on 2024-01-05 -Author: Oleksandr Maksymikhin -""" - -from typing import TypeVar - -# from solutions.merge import merge - -T = TypeVar("T", int, float, str, bool) - - -def merge_sort(input_collection: list[T]) -> list[T]: - """Sort collection using merge sorting algorithm. - - Sort elements in collection by splitting the collection in two parts and launch merge. - - Parameters: - input_collection: list[T], collection of unsorted elements of data types (int, float, string, bool). - - Returns -> list[T], collection of sorted elements of data types (int, float, string, bool). - - Raises: - AssertionError: if input is not a collection. - AssertionError: if input collection is empty. - AssertionError: if input is not a data type processed by module (int, float, string, bool). - AssertionError: if collection contains elements of different data types (non-homogeneous). - - Examples: - >>> merge_sort([1]) - [1] - >>> merge_sort([1, 3, 2]) - [1, 2, 3] - >>> merge_sort([3, 2, 100500, 1]) - [1, 2, 3, 100500] - >>> merge_sort(['banana', 'apple', 'cherry']) - ['apple', 'banana', 'cherry'] - """ - - # defensive assertion to check that input is a collection list - assert isinstance(input_collection, list), "Input is not a collection" - - # defensive assertion to check that input collection is not empty - assert len(input_collection) > 0, "Collection is empty" - - # defensive assertion to check that input data types are any of (int, float, string, bool) - assert ( - isinstance(input_collection[0], int) - or isinstance(input_collection[0], float) - or isinstance(input_collection[0], str) - or isinstance(input_collection[0], bool) - ), "Input data types is not processed. Processed data types (int, float, string, bool)" - - # defensive assertion to check that collection is homogeneous - assert all( - isinstance(item, type(input_collection[0])) for item in input_collection - ), "Collection is not homogeneous" - - # copy collection to avoid side effect - collection = input_collection.copy() - if len(collection) < 2: - return input_collection - - # divide collection to two parts - split_index = len(collection) // 2 - - left = merge_sort(collection[:split_index]) - right = merge_sort(collection[split_index:]) - - # return merged collection, sorting left and right parts - return merge(left, right) - - -def merge(left: list[T], right: list[T]) -> list[T]: - """Merge two sorted collections into one sorted collection. - - Combine two sorted homogeneous collections into one sorted collection - - Parameters: - left: list[T], first sorted collection of data types (int, float, string, bool). - right: list[T], second sorted collection of data types (int, float, string, bool). - - Returns -> list[T], sorted collection of data types (int, float, string, bool). - - Raises: - AssertionError: left is not a collection. - AssertionError: right is not a collection. - AssertionError: left collection contains elements of different data types (non-homogeneous). - AssertionError: right collection contains elements of different data types (non-homogeneous). - AssertionError: left and right contain elements of different data types(non-homogeneous). - - Examples: - >>> merge([2], [1]) - [1, 2] - >>> merge([1, 5], [2, 6]) - [1, 2, 5, 6] - >>> merge([2, 5, 100500], [1, 9]) - [1, 2, 5, 9, 100500] - """ - # defensive assertion to check that input is a collections are lists - assert isinstance(left, list), "Input left is not a collection" - assert isinstance(right, list), "Input right is not a collection" - - # defensive assertion to check that input collections are homogeneous - assert all( - isinstance(item, type(left[0])) for item in left - ), "Input left is not homogeneous" - assert all( - isinstance(item, type(right[0])) for item in right - ), "Input right is not homogeneous" - assert isinstance( - left[0], type(right[0]) - ), "Input left and right are not homogeneous" - - # check if left or right collection is empty - if len(left) == 0: - return right - if len(right) == 0: - return left - - # create empty collection for merge - merged_collection = [] - # create indexed to traverse collections - index_left = index_right = 0 - - # merge collections until length of merged collection is smaller - # then length of left and right collections - while len(merged_collection) < len(left) + len(right): - if left[index_left] <= right[index_right]: - merged_collection.append(left[index_left]) - index_left += 1 - else: - merged_collection.append(right[index_right]) - index_right += 1 - - # if left or right collection becomes empty - # add elements of the other collection and break loop - if index_left == len(left): - merged_collection += right[index_right:] - break - if index_right == len(right): - merged_collection += left[index_left:] - break - - return merged_collection From 7209fc9e4c2fde0581b6eea3d33d2b48ebaeb9cf Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Jan 2025 02:05:25 +0100 Subject: [PATCH 41/52] ascending_order 3 --- solutions/tests/test_merge_sort.py | 97 ------------------------------ 1 file changed, 97 deletions(-) delete mode 100644 solutions/tests/test_merge_sort.py diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py deleted file mode 100644 index 062dd5d10..000000000 --- a/solutions/tests/test_merge_sort.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Test module for merge_sort() function. -Test sorting of homogeneous collections types (int, float, string, bool) using merge algorithm. - -Test categories: - - Standard cases: lists of type int with different lengths - - Edge cases: single element type int - - Different data type: str, bool - - Defensive tests: - - side effects protection - - input is not a collection - - input of empty collection - - different data types in the input collection (non-homogeneous) - - input data types that are not processed by module - -Created on 2024-01-05 -Author: Oleksandr Maksymikhin -""" - -import unittest - -from ..merge_sort import merge_sort - - -class TestMergeSort(unittest.TestCase): - """Test the merge_sort function.""" - - def test_single_int_element_list(self): - """It should return [1] for input [1]""" - actual = merge_sort([1]) - expected = [1] - self.assertEqual(actual, expected) - - def test_two_int_elements_list(self): - """It should return [1, 2] for input [2, 1]""" - actual = merge_sort([2, 1]) - expected = [1, 2] - self.assertEqual(actual, expected) - - def test_three_int_elements_list(self): - """It should return [1, 2, 3] for input [3, 2, 1]""" - actual = merge_sort([3, 2, 1]) - expected = [1, 2, 3] - self.assertEqual(actual, expected) - - def test_four_int_elements_list_big_number(self): - """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" - actual = merge_sort([2, 3, 100500, 1]) - expected = [1, 2, 3, 100500] - self.assertEqual(actual, expected) - - def test_char_elements_list(self): - """It should return ["a", "b", "c", "d"] for input ["b", "d", "a", "c"]""" - actual = merge_sort(["b", "d", "a", "c"]) - expected = ["a", "b", "c", "d"] - self.assertEqual(actual, expected) - - def test_str_elements_list(self): - """It should return ["apple", "banana", "cabbage", "daikon"] for input ["daikon", "cabbage", "banana", "apple"]""" - actual = merge_sort(["daikon", "cabbage", "banana", "apple"]) - expected = ["apple", "banana", "cabbage", "daikon"] - self.assertEqual(actual, expected) - - def test_bool_elements_list(self): - """It should return [False, False, True, True] for input [True, False, False, True]""" - actual = merge_sort([True, False, False, True]) - expected = [False, False, True, True] - self.assertEqual(actual, expected) - - def test_side_effect_protection(self): - """It should return [3, 2, 1] of initial input""" - input_list = [3, 2, 1, 100500] - copy_for_sorting = input_list.copy() - merge_sort(copy_for_sorting) - self.assertEqual(input_list, [3, 2, 1, 100500]) - - def test_non_collection_input(self): - """It should raise an assertion error if the input is not a collection""" - with self.assertRaises(AssertionError): - merge_sort("banana") - - def test_non_homogeneous_collection_input(self): - """It should raise an assertion error if the collection is non-homogeneous""" - with self.assertRaises(AssertionError): - merge_sort([3, 2, "one"]) - - def test_input_of_empty_collection(self): - """It should raise an assertion error if the input data type is out of processed type""" - with self.assertRaises(AssertionError): - merge_sort([]) - - def test_input_of_non_processed_data_type(self): - """It should raise an assertion error if the input data type is out of processed type""" - with self.assertRaises(AssertionError): - merge_sort([{1, "one"}, {2, "two"}]) From e11081e8dd4878e68cec9f665a65380dbe776afa Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 12 Jan 2025 00:41:23 +0100 Subject: [PATCH 42/52] Updated retrospective file with new insights --- collaboration/retrospective.md | 65 ++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/collaboration/retrospective.md b/collaboration/retrospective.md index 74e18813b..798f3b63a 100644 --- a/collaboration/retrospective.md +++ b/collaboration/retrospective.md @@ -1,23 +1,74 @@ - - # Retrospective +> “Regardless of what we discover, we understand and truly believe that everyone +> did the best job they could, given what they knew at the time, their skills +> and abilities, the resources available, and the situation at hand.” +> +> - [Norm Kerth](http://www.amazon.com/Project-Retrospectives-Handbook-Reviews-Dorset-ebook/dp/B00DY3KQJU/ref=tmm_kin_swatch_0?_encoding=UTF8&sr=&qid=) + +--- + ## Stop Doing +- **Challenges Faced:** + - Initial difficulties in understanding the complete GitHub workflow. + - Errors during the push process from local machines to GitHub, such as merge conflicts or incomplete changes. + - Limited initial familiarity with GitHub’s collaborative features, leading to delays in resolving issues. + +*Takeaway:* Invest more time in understanding GitHub tools and workflows at the start of the project to minimize errors and delays. + +--- + ## Continue Doing +- **What Worked Well:** + - Collaborative problem-solving improved over time, especially during CI/CD checks and error resolution. + - Regular document reviews and feedback fostered team learning and improved overall quality. + - Clear documentation and communication in Slack channels helped the team address challenges effectively. + +*Takeaway:* Continue building on strong collaboration and communication practices. + +--- + ## Start Doing +- **New Ideas for Future Projects:** + - Document the step-by-step GitHub workflow for all team members to refer to during the project. + - Establish a shared resource for documenting common errors and solutions (e.g., troubleshooting merge conflicts or CI errors). + - Schedule regular review sessions to identify errors earlier in the project lifecycle. + +*Takeaway:* Implement these strategies to ensure a smoother and more efficient workflow in future projects. + +--- + ## Lessons Learned -______________________________________________________________________ +- Working on the GitHub Team Project provided invaluable insights into: + - Pushing updates from local machines to GitHub and managing version control. + - Troubleshooting challenges like merge conflicts, rough draft errors, and CI/CD issues. + - Reviewing, commenting, and improving code collaboratively. + - Observing the step-by-step lifecycle of a project, from initiation to finalization. + - The importance of effective communication and documentation in ensuring project success. + +These lessons enhanced both technical and collaboration skills, laying the foundation for better teamwork in future projects. + +--- ## Strategy vs. Board -### What parts of your plan went as expected? +- **What parts of your plan went as expected?** + - GitHub collaboration improved over time with regular communication and troubleshooting. + - Document reviews and feedback loops were successfully implemented. + +- **What parts of your plan did not work out?** + - Early misunderstandings of GitHub workflows caused delays. + +- **Did you need to add things that weren't in your strategy?** + - Yes, additional time and resources were allocated for troubleshooting and learning GitHub features. -### What parts of your plan did not work out? +- **Or remove extra steps?** + - Simplifying the review and feedback process helped focus on key areas. -### Did you need to add things that weren't in your strategy? +--- -### Or remove extra steps? +This retrospective reflects the challenges, achievements, and lessons learned during the GitHub Team Project. The project provided an invaluable opportunity to see a software development process in its entirety, from local pushes to collaborative reviews and debugging. Moving forward, these experiences will guide us in delivering even more efficient and successful projects. Thank you all for your dedication and effort! From 30478b2d5e588ef377c9e9924f0534d84de9adb6 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 12 Jan 2025 00:54:10 +0100 Subject: [PATCH 43/52] Fixed line length issues in retrospective.md --- collaboration/retrospective.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/collaboration/retrospective.md b/collaboration/retrospective.md index 798f3b63a..62919c9e1 100644 --- a/collaboration/retrospective.md +++ b/collaboration/retrospective.md @@ -71,4 +71,7 @@ These lessons enhanced both technical and collaboration skills, laying the found --- -This retrospective reflects the challenges, achievements, and lessons learned during the GitHub Team Project. The project provided an invaluable opportunity to see a software development process in its entirety, from local pushes to collaborative reviews and debugging. Moving forward, these experiences will guide us in delivering even more efficient and successful projects. Thank you all for your dedication and effort! +This retrospective reflects the challenges, achievements, and lessons learned during the GitHub Team Project. +The project provided an invaluable opportunity to see a software development process in its entirety, from local pushes to collaborative reviews and debugging. +Moving forward, these experiences will guide us in delivering even more efficient and successful projects. +Thank you all for your dedication and effort! From 6e39c6c0a1131dc1141b36b5cd0ee99ba91730fc Mon Sep 17 00:00:00 2001 From: AhmadHamedDehzad Date: Sun, 12 Jan 2025 11:47:27 +0100 Subject: [PATCH 44/52] changes made according to review --- .../tests/test_word_frequency_counter.py | 20 +++---- solutions/word_frequency_counter.py | 52 +++++++++---------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/solutions/tests/test_word_frequency_counter.py b/solutions/tests/test_word_frequency_counter.py index 29e421b55..b7efabf64 100644 --- a/solutions/tests/test_word_frequency_counter.py +++ b/solutions/tests/test_word_frequency_counter.py @@ -1,21 +1,19 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -This is test module for counting the frequency of words in a given sentence. +This is a test module for counting the frequency of words in a given sentence. Test categories: - Standard cases: typical strings with different lengths - - Edge cases: white space input - - Defensive tests: non string inputs + - Edge cases: white space input and punctuation handling + - Defensive tests: non-string inputs Created on 01.01.2025 - @author: Ahmad Hamed Dehzad """ import unittest - -from solutions.word_frequency_counter import word_frequency_counter +from ..word_frequency_counter import word_frequency_counter class TestWordFrequencyCounter(unittest.TestCase): @@ -46,9 +44,9 @@ def test_case_insensitivity(self): self.assertEqual(actual, expected) def test_with_punctuation(self): - """It should handle words with punctuation correctly""" + """It should handle punctuation by ignoring it""" actual = word_frequency_counter("Hello, world! Hello.") - expected = {"hello,": 1, "world!": 1, "hello.": 1} + expected = {"hello": 2, "world": 1} # Updated expected result self.assertEqual(actual, expected) def test_numbers_in_string(self): @@ -66,4 +64,8 @@ def test_with_whitespace(self): """It should handle extra spaces correctly""" actual = word_frequency_counter(" Hello world ") expected = {"hello": 1, "world": 1} - self.assertEqual(actual, expected) \ No newline at end of file + self.assertEqual(actual, expected) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/solutions/word_frequency_counter.py b/solutions/word_frequency_counter.py index 8985268c6..97e644c51 100644 --- a/solutions/word_frequency_counter.py +++ b/solutions/word_frequency_counter.py @@ -3,38 +3,35 @@ """ A module for counting the frequency of words in a given sentence. -Module contents: - - word_frequency_counter: calculates the frequency of each word in a sentence. - - display_word_frequencies: prints the word frequencies in a readable format. +Features: + - Handles case-insensitivity + - Strips punctuation + - Validates input as a string Created on 01.01.2025 @author: Ahmad Hamed Dehzad -""" -# def word_frequency_counter(sentence): -# if not isinstance(sentence, str): -# raise ValueError("Invalid input: Please enter a text string.") -# words = sentence.split() -# word_frequencies = {} -# for word in words: -# word = word.lower() -# if word in word_frequencies: -# word_frequencies[word] += 1 -# else: -# word_frequencies[word] = 1 -# return word_frequencies +Examples: + >>> word_frequency_counter("Hello world hello") + {'hello': 2, 'world': 1} + + >>> word_frequency_counter("Hello, world! Hello.") + {'hello': 2, 'world': 1} +""" +import string -# --- after documenting and testing --- def word_frequency_counter(sentence: str) -> dict[str, int]: - """Counts the frequency of each word in a given sentence. + """ + Counts the frequency of each word in a given sentence. - Parameters: - sentence: str, a sentence containing words separated by spaces. + Args: + sentence (str): A sentence containing words. Returns: - A dictionary where keys are words (case-insensitive) and values are their frequency. + dict[str, int]: A dictionary where keys are words (case-insensitive) + and values are their frequency. Raises: ValueError: If the input is not a string. @@ -42,13 +39,14 @@ def word_frequency_counter(sentence: str) -> dict[str, int]: if not isinstance(sentence, str): raise ValueError("Invalid input: Please enter a text string.") - words = sentence.split() + # Remove punctuation and split into words + cleaned_sentence = sentence.translate(str.maketrans("", "", string.punctuation)) + words = cleaned_sentence.split() + + # Count word frequencies word_frequencies = {} for word in words: word = word.lower() - if word in word_frequencies: - word_frequencies[word] += 1 - else: - word_frequencies[word] = 1 + word_frequencies[word] = word_frequencies.get(word, 0) + 1 - return word_frequencies \ No newline at end of file + return word_frequencies From 570ea6729dfe49f1350dac63e687d149a9926aac Mon Sep 17 00:00:00 2001 From: emrebiyik <159007525+emrebiyik@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:09:47 +0100 Subject: [PATCH 45/52] Delete collaboration/constraints.md --- collaboration/constraints.md | 42 ------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 collaboration/constraints.md diff --git a/collaboration/constraints.md b/collaboration/constraints.md deleted file mode 100644 index 4a9c9538f..000000000 --- a/collaboration/constraints.md +++ /dev/null @@ -1,42 +0,0 @@ - - -# Constraints - -Some boundaries around our project. - ---- - -## **External** - - - -- **Deadline:** The project must be completed by **10 Jan 2025**. -- **Programming Language:** The programming language for this project is **Python** and cannot be changed. -- **Coding Standards:** All coding, documentation, and reviews must adhere to established standards and examples. -- **Restricted Pushes:** Direct pushes to the main branch are prohibited. -- **Branch Merging Rules:** Branches can only be merged after a pull request is reviewed and approved. -- **Team Size:** The team consists of 10 members. However, active participation will be observed throughout the process, and adjustments will be made as needed to ensure tasks are distributed effectively and project goals are met. This flexible approach allows the team to adapt based on member availability and engagement. - ---- - -## **Internal: Involuntary** - - - -- **Different Schedules:** Team members have varying commitments, making synchronous meetings difficult. -- **Learning Together:** Some members are still learning relevant skills, requiring collaboration and support. -- **Limited Availability:** Balancing this project with other responsibilities might reduce availability at times. -- **Knowledge Gaps:** Some members lack experience in specific technologies or concepts, which may slow progress. -- **Dependency on Others:** Certain tasks depend on the completion of others, potentially causing bottlenecks. -- **Limited Review Capacity:** Fewer reviewers might delay pull request approvals and overall progress. - ---- - -## **Internal: Voluntary** - - - -- **Scope Management:** The project scope is deliberately kept small to ensure timely completion and avoid unnecessary complexity. -- **Adherence to Rules:** The team agrees to follow coding style guides, documentation standards, and review conventions. -- **Collaboration Commitment:** All members are dedicated to equal participation and supporting one another. -- **Documentation Priority:** All tasks, decisions, and code will be properly documented to facilitate collaboration. From d28c33d79a9c0e22e40278dfb626ebc64c42baa3 Mon Sep 17 00:00:00 2001 From: emrebiyik <159007525+emrebiyik@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:10:06 +0100 Subject: [PATCH 46/52] Delete collaboration/learning_goals.md --- collaboration/learning_goals.md | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 collaboration/learning_goals.md diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md deleted file mode 100644 index 474891167..000000000 --- a/collaboration/learning_goals.md +++ /dev/null @@ -1,31 +0,0 @@ -# Learning Goals - -## Group Objectives - -The primary goal of our group is to enhance our understanding and practical skills in software development by working collaboratively on exercises. Each member brings unique objectives, and we aim to create a supportive environment where these individual goals can be achieved. - -## Collective - -1. **Enhance Problem-Solving Skills**: Apply data structures (DSs) and algorithms to solve complex problems effectively. -2. **Learn and Practice New Techniques**: Explore new programming language features and techniques to improve code quality. -3. **Focus on Readability and Documentation**: Develop solutions that are well-documented, clear, and maintainable. -4. **Improve Development Speed**: Practice strategies to write and debug code faster, aiming for efficiency. -5. **Collaborative Code Reviews**: Engage in multiple review cycles to learn from feedback and refine solutions collaboratively. - ---- - -## Individual - -### Emre Biyik - -- Review data structures and algorithms studied previously. -- Focus on writing more complete and readable solutions. -- Gain deeper experience with version control tools like Git and GitHub, especially in team projects. - ---- - -## Group Approach - -- Regularly review and reflect on progress towards these goals during meetings. -- Support each other in achieving individual and shared objectives through collaborative exercises. -- Use tools like GitHub Issues and Project Boards to ensure visibility and accountability. From 45fd5f9f6712a1ece14470b24207472b738751e6 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Sun, 12 Jan 2025 13:17:02 +0100 Subject: [PATCH 47/52] added Oleksandr's personal goals in learning_goals.md added Oleksandr's personal goals in learning_goals.md --- collaboration/learning_goals.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index 474891167..7df5105bf 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -22,6 +22,12 @@ The primary goal of our group is to enhance our understanding and practical skil - Focus on writing more complete and readable solutions. - Gain deeper experience with version control tools like Git and GitHub, especially in team projects. +### Oleksandr Maksymikhin + +- Study the Python programming language. +- Improve skills for participation in group projects using the version control system GitHub. +- Investigate the functionality of GitHub (Issues, Project Board, Actions, CI/CD). + --- ## Group Approach From e26e18f67d98508d603b7942a0bf88b6af12794c Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Sun, 12 Jan 2025 14:38:19 +0100 Subject: [PATCH 48/52] Changed the Group name in the collaboration/README.md Changed the Group name in the collaboration/README.md --- collaboration/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/collaboration/README.md b/collaboration/README.md index 869cc698d..6bd0cf3fa 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -1,11 +1,11 @@ # Collaboration -## Group “ET6-foundations-group-10” +## Group “ZeroThroughTen” -The group is focused on professional communication in developing the _**''“ET6-foundations-group-10”**_ project to implement the _Foundations Track_ syllabus of the [_MIT Emerging Talent_](https://emergingtalent.mit.edu/) program. +The group is focused on professional communication in developing the _**“ZeroThroughTen”**_ project to implement the _Foundations Track_ syllabus of the [_MIT Emerging Talent_](https://emergingtalent.mit.edu/) program. ### Norms of the group -1. We try to communicate on a professional level on issues related to the project “ET6-foundations-group-10” development and implementation of the Foundations Track syllabus of the MIT Emerging Talent program. We do not touch on issues of personal characteristics, religious beliefs, political views, or level of competence. -2. We communicate asynchronously using Slack and GitHub tools, so our communication needs to be documented and other group members can view the subject of the communication, the discussion itself, and the results of the communication. +1. We try to communicate on a professional level on the issues related to the project development and implementation according to the Foundations Track syllabus of the MIT Emerging Talent program. We do not touch on issues of personal characteristics, religious beliefs, political views, or level of competence. +2. We communicate asynchronously using Slack and GitHub tools, so our communication needs to be documented to allow other group members to view the subject of the communication, the discussion itself, and the results of the communication. 3. All group meetings are scheduled in advance so that other group members can organize their time. All group communication is documented. Personal communication can take place online. From 7a3563db0f3970f5d00906dee6e0fbbd2c17ecc6 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 12 Jan 2025 15:32:49 +0100 Subject: [PATCH 49/52] Restore constraints.md from main --- collaboration/constraints.md | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 collaboration/constraints.md diff --git a/collaboration/constraints.md b/collaboration/constraints.md new file mode 100644 index 000000000..4a9c9538f --- /dev/null +++ b/collaboration/constraints.md @@ -0,0 +1,42 @@ + + +# Constraints + +Some boundaries around our project. + +--- + +## **External** + + + +- **Deadline:** The project must be completed by **10 Jan 2025**. +- **Programming Language:** The programming language for this project is **Python** and cannot be changed. +- **Coding Standards:** All coding, documentation, and reviews must adhere to established standards and examples. +- **Restricted Pushes:** Direct pushes to the main branch are prohibited. +- **Branch Merging Rules:** Branches can only be merged after a pull request is reviewed and approved. +- **Team Size:** The team consists of 10 members. However, active participation will be observed throughout the process, and adjustments will be made as needed to ensure tasks are distributed effectively and project goals are met. This flexible approach allows the team to adapt based on member availability and engagement. + +--- + +## **Internal: Involuntary** + + + +- **Different Schedules:** Team members have varying commitments, making synchronous meetings difficult. +- **Learning Together:** Some members are still learning relevant skills, requiring collaboration and support. +- **Limited Availability:** Balancing this project with other responsibilities might reduce availability at times. +- **Knowledge Gaps:** Some members lack experience in specific technologies or concepts, which may slow progress. +- **Dependency on Others:** Certain tasks depend on the completion of others, potentially causing bottlenecks. +- **Limited Review Capacity:** Fewer reviewers might delay pull request approvals and overall progress. + +--- + +## **Internal: Voluntary** + + + +- **Scope Management:** The project scope is deliberately kept small to ensure timely completion and avoid unnecessary complexity. +- **Adherence to Rules:** The team agrees to follow coding style guides, documentation standards, and review conventions. +- **Collaboration Commitment:** All members are dedicated to equal participation and supporting one another. +- **Documentation Priority:** All tasks, decisions, and code will be properly documented to facilitate collaboration. From 6ba469a3c15e326270cdab4199bd22de0a8c8c06 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 12 Jan 2025 15:33:18 +0100 Subject: [PATCH 50/52] Remove constraints.md from add-communication branch --- collaboration/constraints.md | 42 ------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 collaboration/constraints.md diff --git a/collaboration/constraints.md b/collaboration/constraints.md deleted file mode 100644 index 4a9c9538f..000000000 --- a/collaboration/constraints.md +++ /dev/null @@ -1,42 +0,0 @@ - - -# Constraints - -Some boundaries around our project. - ---- - -## **External** - - - -- **Deadline:** The project must be completed by **10 Jan 2025**. -- **Programming Language:** The programming language for this project is **Python** and cannot be changed. -- **Coding Standards:** All coding, documentation, and reviews must adhere to established standards and examples. -- **Restricted Pushes:** Direct pushes to the main branch are prohibited. -- **Branch Merging Rules:** Branches can only be merged after a pull request is reviewed and approved. -- **Team Size:** The team consists of 10 members. However, active participation will be observed throughout the process, and adjustments will be made as needed to ensure tasks are distributed effectively and project goals are met. This flexible approach allows the team to adapt based on member availability and engagement. - ---- - -## **Internal: Involuntary** - - - -- **Different Schedules:** Team members have varying commitments, making synchronous meetings difficult. -- **Learning Together:** Some members are still learning relevant skills, requiring collaboration and support. -- **Limited Availability:** Balancing this project with other responsibilities might reduce availability at times. -- **Knowledge Gaps:** Some members lack experience in specific technologies or concepts, which may slow progress. -- **Dependency on Others:** Certain tasks depend on the completion of others, potentially causing bottlenecks. -- **Limited Review Capacity:** Fewer reviewers might delay pull request approvals and overall progress. - ---- - -## **Internal: Voluntary** - - - -- **Scope Management:** The project scope is deliberately kept small to ensure timely completion and avoid unnecessary complexity. -- **Adherence to Rules:** The team agrees to follow coding style guides, documentation standards, and review conventions. -- **Collaboration Commitment:** All members are dedicated to equal participation and supporting one another. -- **Documentation Priority:** All tasks, decisions, and code will be properly documented to facilitate collaboration. From 512b78e6522dc329489b0c67667a64fd36874ae0 Mon Sep 17 00:00:00 2001 From: emrebiyik Date: Sun, 12 Jan 2025 15:38:54 +0100 Subject: [PATCH 51/52] Restore constraints.md to match main --- collaboration/constraints.md | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 collaboration/constraints.md diff --git a/collaboration/constraints.md b/collaboration/constraints.md new file mode 100644 index 000000000..4a9c9538f --- /dev/null +++ b/collaboration/constraints.md @@ -0,0 +1,42 @@ + + +# Constraints + +Some boundaries around our project. + +--- + +## **External** + + + +- **Deadline:** The project must be completed by **10 Jan 2025**. +- **Programming Language:** The programming language for this project is **Python** and cannot be changed. +- **Coding Standards:** All coding, documentation, and reviews must adhere to established standards and examples. +- **Restricted Pushes:** Direct pushes to the main branch are prohibited. +- **Branch Merging Rules:** Branches can only be merged after a pull request is reviewed and approved. +- **Team Size:** The team consists of 10 members. However, active participation will be observed throughout the process, and adjustments will be made as needed to ensure tasks are distributed effectively and project goals are met. This flexible approach allows the team to adapt based on member availability and engagement. + +--- + +## **Internal: Involuntary** + + + +- **Different Schedules:** Team members have varying commitments, making synchronous meetings difficult. +- **Learning Together:** Some members are still learning relevant skills, requiring collaboration and support. +- **Limited Availability:** Balancing this project with other responsibilities might reduce availability at times. +- **Knowledge Gaps:** Some members lack experience in specific technologies or concepts, which may slow progress. +- **Dependency on Others:** Certain tasks depend on the completion of others, potentially causing bottlenecks. +- **Limited Review Capacity:** Fewer reviewers might delay pull request approvals and overall progress. + +--- + +## **Internal: Voluntary** + + + +- **Scope Management:** The project scope is deliberately kept small to ensure timely completion and avoid unnecessary complexity. +- **Adherence to Rules:** The team agrees to follow coding style guides, documentation standards, and review conventions. +- **Collaboration Commitment:** All members are dedicated to equal participation and supporting one another. +- **Documentation Priority:** All tasks, decisions, and code will be properly documented to facilitate collaboration. From f5342cd7fb7dbaa82275857ce3427f9d6e269943 Mon Sep 17 00:00:00 2001 From: oleksandr-maksymikhin Date: Sun, 12 Jan 2025 19:14:57 +0100 Subject: [PATCH 52/52] restore mistakenly deleted merge_sort.py and test_merge_sort.py restore mistakenly deleted merge_sort.py and test_merge_sort.py --- solutions/merge_sort.py | 156 +++++++++++++++++++++++++++++ solutions/tests/test_merge_sort.py | 97 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 solutions/merge_sort.py create mode 100644 solutions/tests/test_merge_sort.py diff --git a/solutions/merge_sort.py b/solutions/merge_sort.py new file mode 100644 index 000000000..14ccbac7e --- /dev/null +++ b/solutions/merge_sort.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +A module for sorting non empty collection of build-in elementary Python data types +(int, float, str, bool) using merge sorting algorithm. + +Module contents: + - merge_sort: function that splits the collection and launches the merge, + - merge: function that merges two sorted collections into one sorted collection. + +Created on 2024-01-05 +Author: Oleksandr Maksymikhin +""" + +from typing import TypeVar + +# from solutions.merge import merge + +T = TypeVar("T", int, float, str, bool) + + +def merge_sort(input_collection: list[T]) -> list[T]: + """Sort collection using merge sorting algorithm. + + Sort elements in collection by splitting the collection in two parts and launch merge. + + Parameters: + - input_collection: list[T], collection of unsorted elements of data types (int, float, string, bool). + + Returns -> list[T], collection of sorted elements of data types (int, float, string, bool). + + Raises: + - AssertionError: if input is not a collection. + - AssertionError: if input collection is empty. + - AssertionError: if input is not a data type processed by module (int, float, string, bool). + - AssertionError: if collection contains elements of different data types (non-homogeneous). + + Examples: + >>> merge_sort([1]) + [1] + >>> merge_sort([1, 3, 2]) + [1, 2, 3] + >>> merge_sort([3, 2, 100500, 1]) + [1, 2, 3, 100500] + >>> merge_sort(['banana', 'apple', 'cherry']) + ['apple', 'banana', 'cherry'] + """ + + # defensive assertion to check that input is a collection list + assert isinstance(input_collection, list), "Input is not a collection" + + # defensive assertion to check that input collection is not empty + assert len(input_collection) > 0, "Collection is empty" + + # defensive assertion to check that input data types are any of (int, float, string, bool) + assert ( + isinstance(input_collection[0], int) + or isinstance(input_collection[0], float) + or isinstance(input_collection[0], str) + or isinstance(input_collection[0], bool) + ), ( + "Input data types is not processed. Processed data types (int, float, string, bool)" + ) + + # defensive assertion to check that collection is homogeneous + assert all( + isinstance(item, type(input_collection[0])) for item in input_collection + ), "Collection is not homogeneous" + + # copy collection to avoid side effect + collection = input_collection.copy() + if len(collection) < 2: + return input_collection + + # divide collection to two parts + split_index = len(collection) // 2 + + left = merge_sort(collection[:split_index]) + right = merge_sort(collection[split_index:]) + + # return merged collection, sorting left and right parts + return merge(left, right) + + +def merge(left: list[T], right: list[T]) -> list[T]: + """Merge two sorted collections into one sorted collection. + + Combine two sorted homogeneous collections into one sorted collection + + Parameters: + - left: list[T], first sorted collection of data types (int, float, string, bool). + - right: list[T], second sorted collection of data types (int, float, string, bool). + + Returns -> list[T], sorted collection of data types (int, float, string, bool). + + Raises: + - AssertionError: left is not a collection. + - AssertionError: right is not a collection. + - AssertionError: left collection contains elements of different data types (non-homogeneous). + - AssertionError: right collection contains elements of different data types (non-homogeneous). + - AssertionError: left and right contain elements of different data types(non-homogeneous). + + Examples: + >>> merge([2], [1]) + [1, 2] + >>> merge([1, 5], [2, 6]) + [1, 2, 5, 6] + >>> merge([2, 5, 100500], [1, 9]) + [1, 2, 5, 9, 100500] + """ + # defensive assertion to check that input is a collections are lists + assert isinstance(left, list), "Input left is not a collection" + assert isinstance(right, list), "Input right is not a collection" + + # defensive assertion to check that input collections are homogeneous + assert all(isinstance(item, type(left[0])) for item in left), ( + "Input left is not homogeneous" + ) + assert all(isinstance(item, type(right[0])) for item in right), ( + "Input right is not homogeneous" + ) + assert isinstance(left[0], type(right[0])), ( + "Input left and right are not homogeneous" + ) + + # check if left or right collection is empty + if len(left) == 0: + return right + if len(right) == 0: + return left + + # create empty collection for merge + merged_collection = [] + # create indexed to traverse collections + index_left = index_right = 0 + + # merge collections until length of merged collection is smaller + # then length of left and right collections + while len(merged_collection) < len(left) + len(right): + if left[index_left] <= right[index_right]: + merged_collection.append(left[index_left]) + index_left += 1 + else: + merged_collection.append(right[index_right]) + index_right += 1 + + # if left or right collection becomes empty + # add elements of the other collection and break loop + if index_left == len(left): + merged_collection += right[index_right:] + break + if index_right == len(right): + merged_collection += left[index_left:] + break + + return merged_collection diff --git a/solutions/tests/test_merge_sort.py b/solutions/tests/test_merge_sort.py new file mode 100644 index 000000000..062dd5d10 --- /dev/null +++ b/solutions/tests/test_merge_sort.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Test module for merge_sort() function. +Test sorting of homogeneous collections types (int, float, string, bool) using merge algorithm. + +Test categories: + - Standard cases: lists of type int with different lengths + - Edge cases: single element type int + - Different data type: str, bool + - Defensive tests: + - side effects protection + - input is not a collection + - input of empty collection + - different data types in the input collection (non-homogeneous) + - input data types that are not processed by module + +Created on 2024-01-05 +Author: Oleksandr Maksymikhin +""" + +import unittest + +from ..merge_sort import merge_sort + + +class TestMergeSort(unittest.TestCase): + """Test the merge_sort function.""" + + def test_single_int_element_list(self): + """It should return [1] for input [1]""" + actual = merge_sort([1]) + expected = [1] + self.assertEqual(actual, expected) + + def test_two_int_elements_list(self): + """It should return [1, 2] for input [2, 1]""" + actual = merge_sort([2, 1]) + expected = [1, 2] + self.assertEqual(actual, expected) + + def test_three_int_elements_list(self): + """It should return [1, 2, 3] for input [3, 2, 1]""" + actual = merge_sort([3, 2, 1]) + expected = [1, 2, 3] + self.assertEqual(actual, expected) + + def test_four_int_elements_list_big_number(self): + """It should return [1, 2, 3, 100500] for input [2, 3, 100500, 1]""" + actual = merge_sort([2, 3, 100500, 1]) + expected = [1, 2, 3, 100500] + self.assertEqual(actual, expected) + + def test_char_elements_list(self): + """It should return ["a", "b", "c", "d"] for input ["b", "d", "a", "c"]""" + actual = merge_sort(["b", "d", "a", "c"]) + expected = ["a", "b", "c", "d"] + self.assertEqual(actual, expected) + + def test_str_elements_list(self): + """It should return ["apple", "banana", "cabbage", "daikon"] for input ["daikon", "cabbage", "banana", "apple"]""" + actual = merge_sort(["daikon", "cabbage", "banana", "apple"]) + expected = ["apple", "banana", "cabbage", "daikon"] + self.assertEqual(actual, expected) + + def test_bool_elements_list(self): + """It should return [False, False, True, True] for input [True, False, False, True]""" + actual = merge_sort([True, False, False, True]) + expected = [False, False, True, True] + self.assertEqual(actual, expected) + + def test_side_effect_protection(self): + """It should return [3, 2, 1] of initial input""" + input_list = [3, 2, 1, 100500] + copy_for_sorting = input_list.copy() + merge_sort(copy_for_sorting) + self.assertEqual(input_list, [3, 2, 1, 100500]) + + def test_non_collection_input(self): + """It should raise an assertion error if the input is not a collection""" + with self.assertRaises(AssertionError): + merge_sort("banana") + + def test_non_homogeneous_collection_input(self): + """It should raise an assertion error if the collection is non-homogeneous""" + with self.assertRaises(AssertionError): + merge_sort([3, 2, "one"]) + + def test_input_of_empty_collection(self): + """It should raise an assertion error if the input data type is out of processed type""" + with self.assertRaises(AssertionError): + merge_sort([]) + + def test_input_of_non_processed_data_type(self): + """It should raise an assertion error if the input data type is out of processed type""" + with self.assertRaises(AssertionError): + merge_sort([{1, "one"}, {2, "two"}])