From c125c405964729f0a763d6bd6ab65d8d14a0b344 Mon Sep 17 00:00:00 2001 From: Malak Date: Thu, 26 Dec 2024 20:14:19 +0200 Subject: [PATCH 001/163] Updated the learning_goals.md file --- collaboration/learning_goals.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index 11c583d2b..734a8d628 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -2,4 +2,21 @@ ## Collective +### Technical Skills + +- Learn Python +- GitHub Expertise +- Visual Studio Code + +### Human Skills + +- Collaboration +- Communication +- Time Management +- Problem-Solving +- Cultural Integration +- Non-Formal Education + +--- + ## Individual From 7145b1ec2504792e60e94c343a14438c0ef20c1b Mon Sep 17 00:00:00 2001 From: Malak Date: Thu, 26 Dec 2024 21:09:42 +0200 Subject: [PATCH 002/163] Updated the README.md file with the About Us section and the intro. --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index e69de29bb..eeb448e38 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,17 @@ +# Keyboard Heroes 🌟 + +**Collaborate. Innovate. Illuminate.** + +Welcome to the repository of **Keyboard Heroes**, representing +Group 17 in the MIT Emerging Talent Program (Foundations Track for Computer Science +and Data Science program). + +--- + +## **About Us** + +We are a group of learners with diverse backgrounds working together to solve programming +challenges in Python. Our mission is to foster collaboration, build technical +skills, and grow as a team. + +--- From 8aa99e0c49174bf52129174ca67b646bb5b7ce28 Mon Sep 17 00:00:00 2001 From: Malak Date: Fri, 27 Dec 2024 01:53:44 +0200 Subject: [PATCH 003/163] Updated the External and Internal: Involuntary constraints - first draft --- collaboration/constraints.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index 24079505c..1240f48e8 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -4,24 +4,21 @@ Some boundaries around our project. -## External +## External - +- Deadline: The project must be finished by **January 10th**. +- Connectivity issues: **Limited internet access** affects some teammates, + especially during peak hours. +- Holiday availability: Team members celebrating **Christmas** may have fewer + hours to contribute this week. -## Internal: Involuntary +## Internal: Involuntary - +- **Students** are managing their time between this project + and their **final exams**. +- Some team members are new to **GitHub workflows** and may need extra guidance. +- Limited fluency in English among some members might make it harder to express + complex ideas or provide detailed feedback during discussions. ## Internal: Voluntary From 3a248959e1c85ddda23660a0c3d0305c191da76f Mon Sep 17 00:00:00 2001 From: Malak Date: Fri, 27 Dec 2024 02:32:21 +0200 Subject: [PATCH 004/163] Updated the group norms summary and list for the collaboration/README.md file --- collaboration/README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/collaboration/README.md b/collaboration/README.md index 20889b951..05b18adfc 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -1,5 +1,18 @@ -# Collaboration +# Group Norms Summary - +Our team thrives on openness, respect, and the shared belief that collaboration +makes us stronger. - + +- Feedback is given constructively, aiming to improve ideas rather + than criticize individuals. +- We communicate transparently and ensure all intentions are clear, + fostering trust among the team. +- If someone doesn’t have an answer, they openly admit it, and we work together + to find solutions. +- Concerns or ideas are raised immediately, ensuring smooth progress without + unnecessary delays. +- Every contribution is valuable, and all ideas are treated with respect and + consideration. +- Personal attacks are off-limits; we engage respectfully, focusing on ideas + rather than people. From db60b4a94c234be69bdb9c81bcb5520f0e7a7f86 Mon Sep 17 00:00:00 2001 From: Malak Date: Fri, 27 Dec 2024 02:38:01 +0200 Subject: [PATCH 005/163] Updated the group norms summary and list for the collaboration/README.md file --- collaboration/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/collaboration/README.md b/collaboration/README.md index 05b18adfc..da29c05a7 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -3,7 +3,8 @@ Our team thrives on openness, respect, and the shared belief that collaboration makes us stronger. - +## group norms list + - Feedback is given constructively, aiming to improve ideas rather than criticize individuals. - We communicate transparently and ensure all intentions are clear, From 71234e5f2a65bae09b09b21d1cb4306f05ccbda7 Mon Sep 17 00:00:00 2001 From: Malak Date: Fri, 27 Dec 2024 02:55:53 +0200 Subject: [PATCH 006/163] Added the collaboration heading --- collaboration/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/collaboration/README.md b/collaboration/README.md index da29c05a7..31176de4a 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -1,4 +1,6 @@ -# Group Norms Summary +# Collaboration + +## Group Norms Summary Our team thrives on openness, respect, and the shared belief that collaboration makes us stronger. From 94f00ab224dd0369b8757201681fb1b0e28edfd2 Mon Sep 17 00:00:00 2001 From: Malak Date: Mon, 30 Dec 2024 04:46:32 +0200 Subject: [PATCH 007/163] Added Evan's updates --- .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 1175fea407d97f88af06cd32ed4ce11e498fd048 Mon Sep 17 00:00:00 2001 From: Malak Date: Mon, 30 Dec 2024 06:25:08 +0200 Subject: [PATCH 008/163] Added Malak's Availability Times --- collaboration/communication.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 484652e0f..50819051a 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -13,8 +13,9 @@ ______________________________________________________________________ ## Communication Schedule -| Day | How | The topic of discussion | | --- | :-: | ----------------------- | -| | | | +| Day | How | The topic of discussion | +|-----------|------|-------------------------| +| | | | ## Communication Channels @@ -31,13 +32,13 @@ ______________________________________________________________________ ### Availability for calling/messaging -| Day | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday | | ------- | :----: | :-----: | :-------: | :------: | :----: | :------: | :----: | -| _name_ | | | | | | | | +| Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| +|--------------|--------|---------|----------|----------|--------|-------|------| +| Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | ### How many hours everyone has per day -- name: _5h_; +- Malak Battatt: _~2h_; - name: _6h_; - name: _5h_; - name: _4h_; From ea85b564f7daeee7c46d8d9ad3c65885df1ed5d5 Mon Sep 17 00:00:00 2001 From: Malak Date: Mon, 30 Dec 2024 06:36:30 +0200 Subject: [PATCH 009/163] Changed Group Name --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eeb448e38..7acc2d31f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Keyboard Heroes 🌟 +# MIT Avatars 🌟 **Collaborate. Innovate. Illuminate.** -Welcome to the repository of **Keyboard Heroes**, representing +Welcome to the repository of **MIT Avatars**, representing Group 17 in the MIT Emerging Talent Program (Foundations Track for Computer Science and Data Science program). From b177ffc23860bc4389600527826b2c7b43321594 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:48:57 +0200 Subject: [PATCH 010/163] Update communication.md --- collaboration/communication.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 50819051a..271997a42 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -1,12 +1,3 @@ - - # Communication ______________________________________________________________________ @@ -35,11 +26,12 @@ ______________________________________________________________________ | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| | Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | +| Abdulrahman Alsir| 7-9 PM| 2-5 PM | 4-8 PM | 7-9 PM | 7-9 PM | Unavailable | Unavailable | ### How many hours everyone has per day - Malak Battatt: _~2h_; -- name: _6h_; +- Abdulrahman Alsir: _~2-4h_; - name: _5h_; - name: _4h_; - name: _3h_; From 2cb342fe57e93272d7bba279798a8574f88efd9a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:21:04 +0200 Subject: [PATCH 011/163] Update communication.md --- collaboration/communication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 271997a42..db46df0ff 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -26,7 +26,7 @@ ______________________________________________________________________ | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| | Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | -| Abdulrahman Alsir| 7-9 PM| 2-5 PM | 4-8 PM | 7-9 PM | 7-9 PM | Unavailable | Unavailable | +| Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | ### How many hours everyone has per day From 48e30224d9c2d3c868ffd0428784b82afb34d484 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:24:47 +0200 Subject: [PATCH 012/163] Update communication.md --- collaboration/communication.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index db46df0ff..cb94e296e 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -4,18 +4,22 @@ ______________________________________________________________________ ## Communication Schedule -| Day | How | The topic of discussion | +| Day | How | The topic of discussion | |-----------|------|-------------------------| -| | | | +| Tuesday 31/12 | Whatsapp Group | Collaboration Documents | +| Thursday 2/1 | Zoom Online Meeting | Problems Delivery | +| Saturday 4/1 | GitHub | Problems Delivery Deadline | +| Tuesday 7/1 | GitHub | Solutions Delivery Deadline | +| Wednesday 8/1 | Zoom Online Meeting | Retrospective | ## Communication Channels how often will we get in touch on each channel, and what we will discuss there: -- **Issues**: -- **Pull Requests**: -- **Slack/Discord**: -- **Video Calls**: +- **Issues**: As Needed +- **Pull Requests**: Daily +- **Slack/Discord**: As Needed +- **Video Calls**: Every 3 Days ______________________________________________________________________ From d478e3458399a55d4eb65541831b37ebca1bed57 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:11:57 +0200 Subject: [PATCH 013/163] Update learning_goals.md --- collaboration/learning_goals.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index 734a8d628..b19c7bc61 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -4,18 +4,29 @@ ### Technical Skills -- Learn Python -- GitHub Expertise -- Visual Studio Code +- To learn the basics of Python and build a strong and solid foundation by +practicing what we have learned in the sessions. We aim to do this over the +next two weeks. +- To learn how to use GitHub and its features and gain acceptable expertise by +consistently practicing key GitHub workflows, collaborating on projects. +- Develop expertise in setting up, navigating, and customizing VS Code for +coding, predictive stepping, debugging, and collaboration. ### Human Skills -- Collaboration -- Communication -- Time Management -- Problem-Solving -- Cultural Integration -- Non-Formal Education +- Learn how to collaborate and work collectively towards achieving our project +goals. +- To enhance communication skills by consistently practicing clear and effective +verbal and written communication, actively listening, and adapting messages to +diverse audiences and situations. +- Enhance the ability to prioritize tasks, allocate time effectively, +and meet deadlines without compromising quality. +- Develop problem-solving skills and undertand how to approcah problems +systematically while exploring different ways to look at obstacles that we face. +- Get to know each other more and discover new things about different other +cultures. +- Embrace self-directed and problem based learning approaches to gain knowledge +and skills outside traditional formal settings. --- From c0033d39475d5bf1f0f27b81bcfe824bec090768 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:56:57 +0200 Subject: [PATCH 014/163] Update README.md I moved the about us part to the repository description --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 7acc2d31f..f59846891 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,4 @@ and Data Science program). --- -## **About Us** - -We are a group of learners with diverse backgrounds working together to solve programming -challenges in Python. Our mission is to foster collaboration, build technical -skills, and grow as a team. - --- From 52c3c937c60eaafd7ba3e370fb7cad2035ce1239 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:57:11 +0200 Subject: [PATCH 015/163] Update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index f59846891..5f59c8774 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,3 @@ Welcome to the repository of **MIT Avatars**, representing Group 17 in the MIT Emerging Talent Program (Foundations Track for Computer Science and Data Science program). - ---- - ---- From eb78ba270994801fcaebf931e26a6fc51ffb5929 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Thu, 2 Jan 2025 23:26:35 +0200 Subject: [PATCH 016/163] Update communication.md --- collaboration/communication.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index db46df0ff..68c6ca0dd 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -27,14 +27,21 @@ ______________________________________________________________________ |--------------|--------|---------|----------|----------|--------|-------|------| | Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | +| Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | +| Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | +| Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | + + ### How many hours everyone has per day - Malak Battatt: _~2h_; - Abdulrahman Alsir: _~2-4h_; -- name: _5h_; -- name: _4h_; -- name: _3h_; +- Maher: _~2h_; +- Maria: _~2h_; +- Muhannad: _~2h_; + + ## Asking for Help From 6adbcee783685254f01547945a43642a7f2a0276 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:20:43 +0200 Subject: [PATCH 017/163] Update communication.md --- collaboration/communication.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 68c6ca0dd..0c128d83e 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -27,11 +27,9 @@ ______________________________________________________________________ |--------------|--------|---------|----------|----------|--------|-------|------| | Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | -| Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | -| Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | -| Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | - - +| Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | +| Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | +| Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | ### How many hours everyone has per day @@ -41,8 +39,6 @@ ______________________________________________________________________ - Maria: _~2h_; - Muhannad: _~2h_; - - ## Asking for Help There's a fine line between confidently learning from your mistakes, and From df710bf5d4c8a073478337abb95223efab589cfb Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:26:40 +0200 Subject: [PATCH 018/163] Update communication.md --- collaboration/communication.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 0c128d83e..47a1198f6 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -4,9 +4,9 @@ ______________________________________________________________________ ## Communication Schedule -| Day | How | The topic of discussion | +| Day | How | The topic of discussion | |-----------|------|-------------------------| -| | | | +| | | | ## Communication Channels @@ -25,8 +25,8 @@ ______________________________________________________________________ | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| -| Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | -| Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | +| Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | +| Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | From 3eb25a17029e1e76c7fcc0879ecfc3daa3bdfa67 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:29:29 +0200 Subject: [PATCH 019/163] Update communication.md --- collaboration/communication.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 47a1198f6..bba93aab6 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -1,7 +1,5 @@ # Communication -______________________________________________________________________ - ## Communication Schedule | Day | How | The topic of discussion | @@ -10,15 +8,13 @@ ______________________________________________________________________ ## Communication Channels -how often will we get in touch on each channel, and what we will discuss there: +How often will we get in touch on each channel, and what we will discuss there: - **Issues**: - **Pull Requests**: - **Slack/Discord**: - **Video Calls**: -______________________________________________________________________ - ## Availability ### Availability for calling/messaging @@ -42,9 +38,9 @@ ______________________________________________________________________ ## 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 +stubbornly getting nowhere. Here is a general guide for when to ask for help based on how long you've been stuck on the same problem: 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 +2. _30 -> 60 min_: Ask your group for help +3. _60+ min_: Tag your coaches in Slack or GitHub From e46082c8bdfaffafba73e6876ad92b5141934c3d Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:32:02 +0200 Subject: [PATCH 020/163] Update README.md --- collaboration/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collaboration/README.md b/collaboration/README.md index 31176de4a..05789a56f 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -5,7 +5,7 @@ Our team thrives on openness, respect, and the shared belief that collaboration makes us stronger. -## group norms list +## Group Norms list - Feedback is given constructively, aiming to improve ideas rather than criticize individuals. From b87f5944064f5b6cc21a3a689aa4f56afda35b01 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 14:44:39 +0200 Subject: [PATCH 021/163] Update communication.md --- collaboration/communication.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 3e5a66017..d6e43499f 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -4,9 +4,9 @@ | Day | How | The topic of discussion | |-----------|------|-------------------------| -| Tuesday 31/12 | Whatsapp Group | Collaboration Documents | -| Thursday 2/1 | Zoom Online Meeting | Problems Delivery | -| Saturday 4/1 | GitHub | Problems Delivery Deadline | +| Thursday 2/1 | Whatsapp Group | Collaboration Documents | +| Thursday 4/1 | Zoom Online Meeting | Problems Delivery | +| Saturday 6/1 | GitHub | Problems Delivery Deadline | | Tuesday 7/1 | GitHub | Solutions Delivery Deadline | | Wednesday 8/1 | Zoom Online Meeting | Retrospective | From 32ac05d835bbd920aa82aef30bccd619397d5cf1 Mon Sep 17 00:00:00 2001 From: Rouaa93 Date: Fri, 3 Jan 2025 16:33:16 +0200 Subject: [PATCH 022/163] Update communication.md --- collaboration/communication.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/collaboration/communication.md b/collaboration/communication.md index d6e43499f..7aa2f3283 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -30,6 +30,7 @@ How often will we get in touch on each channel, and what we will discuss there: | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | +| Rouaa Hamzah | 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | 7-9PM | | | ### How many hours everyone has per day @@ -38,6 +39,7 @@ How often will we get in touch on each channel, and what we will discuss there: - Maher: _~2h_; - Maria: _~2h_; - Muhannad: _~2h_; +- Rouaa: _~2h_; ## Asking for Help From f6ad2ce1e4426473037551655fd7d9489ee9bada Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:59:48 +0200 Subject: [PATCH 023/163] Update constraints.md --- collaboration/constraints.md | 54 +++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index 1240f48e8..05ba94274 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -1,27 +1,43 @@ -# Constraints - -Some boundaries around our project. - -## External - -- Deadline: The project must be finished by **January 10th**. -- Connectivity issues: **Limited internet access** affects some teammates, - especially during peak hours. -- Holiday availability: Team members celebrating **Christmas** may have fewer - hours to contribute this week. - -## Internal: Involuntary - -- **Students** are managing their time between this project - and their **final exams**. -- Some team members are new to **GitHub workflows** and may need extra guidance. -- Limited fluency in English among some members might make it harder to express - complex ideas or provide detailed feedback during discussions. +## External Constraints + +- **Deadline:** The project must be finished by January 10th. +- **Connectivity Issues:** Limited internet access affects some teammates, +especially during peak hours. +- **Holiday Availability:** Team members celebrating Christmas may have fewer +hours to contribute this week. +- **Time Zone Differences:** Team members located in different time zones may +face scheduling conflicts. +- **Resource Limitations:** Access to high-performance computers or specific +software licenses may be restricted for some members. + +## Internal: Involuntary + +- **Students:** Managing their time between this project and their final exams. +- **GitHub Workflows:** Some team members are new to GitHub workflows and may +need extra guidance. +- **English Fluency:** Limited fluency in English among some members might make +it harder to express complex ideas or provide detailed feedback during +discussions. +- **Team Composition:** Uneven distribution of technical skills among team +members may create bottlenecks for specific tasks. +- **Health and Well-being:** Some members may have personal health issues or +responsibilities that could affect their availability. ## Internal: Voluntary +- Agreed Coding Standards and Conventions (Naming conventions, Comment style) +- Code Review Checklist for the Project Repository +- Number of Hours Team Members Want to Spend Working +- Limiting the Scope to specific design choices (e.g., only using black and +white colors) +- **Communication Protocols:** +The team has agreed to use specific tools (e.g., Slack, Zoom) for coordination +and discussions. +- **Review Policies:** +Each pull request must be reviewed by at least two members before merging. + +# Constraints + ## External Constraints - **Deadline:** The project must be finished by January 10th. From de48d81aa51be15c37d23aefbecd572fc625e759 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:07:02 +0200 Subject: [PATCH 025/163] Update README.md --- collaboration/README.md | 101 +++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 12 deletions(-) diff --git a/collaboration/README.md b/collaboration/README.md index 05789a56f..18c730b2b 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -1,21 +1,98 @@ # Collaboration -## Group Norms Summary +## Group Norms -Our team thrives on openness, respect, and the shared belief that collaboration -makes us stronger. +Group norms are the shared values, rules, and expectations that guide our +behavior, communication, and interactions within the team. By establishing clear +norms, we aim to create an inclusive, productive, and collaborative environment +where every member feels valued and empowered. Our team thrives on openness, +respect, and the shared belief that collaboration makes us stronger. -## Group Norms list +## Core Principles -- Feedback is given constructively, aiming to improve ideas rather - than criticize individuals. -- We communicate transparently and ensure all intentions are clear, - fostering trust among the team. +### Respect and Inclusivity +- Acknowledge and appreciate the diverse backgrounds, perspectives, and +contributions of all team members. +- Foster a safe space for open expression, where constructive feedback is +encouraged, and differing opinions are welcomed. + +### Clear Communication +- Use clear, concise, and respectful language in all communications. +- Prioritize active listening during discussions and avoid interrupting others. +- Clarify misunderstandings promptly and assume good intentions when +interpreting messages. + +### Collaboration and Teamwork +- Share responsibilities and ensure equitable participation in tasks. +- Support each other by sharing knowledge, offering help, and celebrating +achievements. +- Commit to resolving conflicts constructively and collaboratively. + +### Accountability and Commitment +- Take responsibility for completing assigned tasks on time and to the best of +your ability. +- Communicate proactively about challenges or delays to ensure transparency. +- Honor commitments made to the group and follow through on agreements. + +### Adaptability and Growth +- Be open to feedback and willing to make improvements based on team discussions +- Embrace flexibility in adapting to new tools, workflows, or ideas introduced +by the group. +- Commit to ongoing learning and professional development as a team. + +## Norms in Practice + +### Communication Norms +- Utilize team communication platforms (e.g., Slack, GitHub, Whatsapp, or email) +for updates, questions, and discussions. +- Maintain professionalism in all written and verbal interactions. +- Schedule regular check-ins or stand-ups to monitor progress and address +concerns. + +### Collaboration Norms +- Use shared tools like GitHub for version control, task assignments, and +documentation. +- Follow branch naming conventions and adhere to agreed processes for code +reviews. +- Complete the collaboration/retrospective.md document at the end of the project +to reflect on successes and areas for improvement. + +### Conflict Resolution +- Approach conflicts with a mindset of dialogue rather than debate. +- Use "I" statements to express concerns constructively (e.g., "I feel X when Y +happens"). +- Seek input from the group or a mediator if conflicts cannot be resolved +independently. + +### Meeting Norms +- Start and end meetings on time, with a clear agenda and objectives. +- Ensure everyone has an opportunity to speak and contribute. +- Summarize key takeaways and action items at the end of each meeting. + +### Inclusion Norms +- Be mindful of language barriers or time zone differences when scheduling +meetings or assigning tasks. +- Rotate leadership roles for tasks or meetings to ensure equitable +participation. +- Celebrate cultural diversity by sharing customs, holidays, or traditions +during appropriate occasions. + +### Group Norms Summary +- Feedback is given constructively, aiming to improve ideas rather than +criticize individuals. +- We communicate transparently and ensure all intentions are clear, fostering +trust among the team. - If someone doesn’t have an answer, they openly admit it, and we work together - to find solutions. +to find solutions. - Concerns or ideas are raised immediately, ensuring smooth progress without - unnecessary delays. +unnecessary delays. - Every contribution is valuable, and all ideas are treated with respect and - consideration. +consideration. - Personal attacks are off-limits; we engage respectfully, focusing on ideas - rather than people. +rather than people. + +## Ongoing Evolution +Group norms are not static. We commit to revisiting and updating these norms +based on feedback, changing circumstances, and lessons learned from our +collaborative experiences. By aligning with these norms, we aim to foster a +supportive team culture that enables high performance and mutual growth. From d3c8195fa4ff9306332de050300c80d595f8d87b Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:11:09 +0200 Subject: [PATCH 026/163] Update README.md --- collaboration/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/collaboration/README.md b/collaboration/README.md index 18c730b2b..dbeba4a57 100644 --- a/collaboration/README.md +++ b/collaboration/README.md @@ -11,30 +11,35 @@ respect, and the shared belief that collaboration makes us stronger. ## Core Principles ### Respect and Inclusivity + - Acknowledge and appreciate the diverse backgrounds, perspectives, and contributions of all team members. - Foster a safe space for open expression, where constructive feedback is encouraged, and differing opinions are welcomed. ### Clear Communication + - Use clear, concise, and respectful language in all communications. - Prioritize active listening during discussions and avoid interrupting others. - Clarify misunderstandings promptly and assume good intentions when interpreting messages. ### Collaboration and Teamwork + - Share responsibilities and ensure equitable participation in tasks. - Support each other by sharing knowledge, offering help, and celebrating achievements. - Commit to resolving conflicts constructively and collaboratively. ### Accountability and Commitment + - Take responsibility for completing assigned tasks on time and to the best of your ability. - Communicate proactively about challenges or delays to ensure transparency. - Honor commitments made to the group and follow through on agreements. ### Adaptability and Growth + - Be open to feedback and willing to make improvements based on team discussions - Embrace flexibility in adapting to new tools, workflows, or ideas introduced by the group. @@ -43,6 +48,7 @@ by the group. ## Norms in Practice ### Communication Norms + - Utilize team communication platforms (e.g., Slack, GitHub, Whatsapp, or email) for updates, questions, and discussions. - Maintain professionalism in all written and verbal interactions. @@ -50,6 +56,7 @@ for updates, questions, and discussions. concerns. ### Collaboration Norms + - Use shared tools like GitHub for version control, task assignments, and documentation. - Follow branch naming conventions and adhere to agreed processes for code @@ -58,6 +65,7 @@ reviews. to reflect on successes and areas for improvement. ### Conflict Resolution + - Approach conflicts with a mindset of dialogue rather than debate. - Use "I" statements to express concerns constructively (e.g., "I feel X when Y happens"). @@ -65,11 +73,13 @@ happens"). independently. ### Meeting Norms + - Start and end meetings on time, with a clear agenda and objectives. - Ensure everyone has an opportunity to speak and contribute. - Summarize key takeaways and action items at the end of each meeting. ### Inclusion Norms + - Be mindful of language barriers or time zone differences when scheduling meetings or assigning tasks. - Rotate leadership roles for tasks or meetings to ensure equitable @@ -78,6 +88,7 @@ participation. during appropriate occasions. ### Group Norms Summary + - Feedback is given constructively, aiming to improve ideas rather than criticize individuals. - We communicate transparently and ensure all intentions are clear, fostering @@ -92,6 +103,7 @@ consideration. rather than people. ## Ongoing Evolution + Group norms are not static. We commit to revisiting and updating these norms based on feedback, changing circumstances, and lessons learned from our collaborative experiences. By aligning with these norms, we aim to foster a From 7d5dd65199c8e40e594eb77500866a01846dd631 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Fri, 3 Jan 2025 20:48:51 +0200 Subject: [PATCH 027/163] Update communication.md Add my free time to communication.md file. --- collaboration/communication.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/collaboration/communication.md b/collaboration/communication.md index 7aa2f3283..a432a475e 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -27,6 +27,7 @@ How often will we get in touch on each channel, and what we will discuss there: |--------------|--------|---------|----------|----------|--------|-------|------| | Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | +| Aseel AbuKmail| 7-10PM| 2-5PM | 5-8PM | 5-7PM | 7-9PM | 6-10PM | 6-8PM | | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | @@ -36,6 +37,7 @@ How often will we get in touch on each channel, and what we will discuss there: - Malak Battatt: _~2h_; - Abdulrahman Alsir: _~2-4h_; +- Abdulrahman Alsir: _~2-4h_; - Maher: _~2h_; - Maria: _~2h_; - Muhannad: _~2h_; From d218ef80ee82413473972154799ffe782b7d15e5 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Fri, 3 Jan 2025 20:49:47 +0200 Subject: [PATCH 028/163] Update communication.md Update my name!! --- collaboration/communication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index a432a475e..a6b5a20b4 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -37,7 +37,7 @@ How often will we get in touch on each channel, and what we will discuss there: - Malak Battatt: _~2h_; - Abdulrahman Alsir: _~2-4h_; -- Abdulrahman Alsir: _~2-4h_; +- Aseel AbuKmail: _~2-4h_; - Maher: _~2h_; - Maria: _~2h_; - Muhannad: _~2h_; From 604b3f7b19a610e69d0c2b1bcc2d76ab5008a613 Mon Sep 17 00:00:00 2001 From: Malak Date: Fri, 3 Jan 2025 23:15:30 +0200 Subject: [PATCH 029/163] Malak's Updated Availability --- collaboration/communication.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index a6b5a20b4..417ed7bed 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -14,10 +14,13 @@ How often will we get in touch on each channel, and what we will discuss there: -- **Issues**: As Needed -- **Pull Requests**: Daily -- **Slack/Discord**: As Needed -- **Video Calls**: Every 3 Days +- **Issues**: Used as needed to track tasks, discuss challenges, or document ideas. + Team members should check for updates daily. +- **Pull Requests**: Created whenever code is ready for review. All team members + should aim to review PRs within 24-48 hours. +- **Slack/WhatsApp**: Active daily for quick updates, questions, or informal discussions. +- **Video Calls**: Scheduled every 3 days to discuss progress, address blockers, + and align on upcoming tasks. ## Availability @@ -25,7 +28,7 @@ How often will we get in touch on each channel, and what we will discuss there: | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| -| Malak Battatt| 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | | | | +| Malak Battatt| 5-8 PM| 5-8 PM | 5-8 PM | 5-8 PM |3-6 PM | 2-4 PM| 2-5 PM | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | | Aseel AbuKmail| 7-10PM| 2-5PM | 5-8PM | 5-7PM | 7-9PM | 6-10PM | 6-8PM | | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | @@ -35,7 +38,7 @@ How often will we get in touch on each channel, and what we will discuss there: ### How many hours everyone has per day -- Malak Battatt: _~2h_; +- Malak Battatt: _~2-4h_; - Abdulrahman Alsir: _~2-4h_; - Aseel AbuKmail: _~2-4h_; - Maher: _~2h_; From 1c356da6dbc81ccd851f615b953be038c7fa14e2 Mon Sep 17 00:00:00 2001 From: Malak Date: Sat, 4 Jan 2025 00:13:50 +0200 Subject: [PATCH 030/163] Updated Communication Schedule with previous meetings --- collaboration/communication.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 417ed7bed..c60b5db36 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -2,13 +2,16 @@ ## Communication Schedule -| Day | How | The topic of discussion | -|-----------|------|-------------------------| -| Thursday 2/1 | Whatsapp Group | Collaboration Documents | -| Thursday 4/1 | Zoom Online Meeting | Problems Delivery | -| Saturday 6/1 | GitHub | Problems Delivery Deadline | -| Tuesday 7/1 | GitHub | Solutions Delivery Deadline | -| Wednesday 8/1 | Zoom Online Meeting | Retrospective | +| Date | Day | How | The topic of discussion | +|-----------|------------|------|--------------| +| Dec. 20th ✅ | Friday | Zoom meeting | Getting to know team members | +| Dec. 23rd ✅ | Monday | Google Meet | Collaboratively exploring the group repo.| +| Dec. 26th ✅ | Thursday | Google Meet |Collaboration folder and its files| +| Jan. 2nd ✅ | Thursday | Google Meet | Final meeting for the collaboration folder| +| Jan. 4th | Saturday | Zoom Online Meeting | Problems Delivery | +| Jan. 6th | Monday | GitHub | Problems Delivery Deadline | +| Jan. 7th | Tuesday | GitHub | Solutions Delivery Deadline | +| Jan. 8th | Wednesday| Zoom Online Meeting | Retrospective | ## Communication Channels From e968ce596f8327b947b8249ed47e723194abe63e Mon Sep 17 00:00:00 2001 From: Malak Date: Sat, 4 Jan 2025 01:10:20 +0200 Subject: [PATCH 031/163] Updated learning goals --- collaboration/learning_goals.md | 64 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/collaboration/learning_goals.md b/collaboration/learning_goals.md index b19c7bc61..83ac5fd2d 100644 --- a/collaboration/learning_goals.md +++ b/collaboration/learning_goals.md @@ -1,33 +1,53 @@ # Learning Goals -## Collective +## Collective Goals ### Technical Skills -- To learn the basics of Python and build a strong and solid foundation by -practicing what we have learned in the sessions. We aim to do this over the -next two weeks. -- To learn how to use GitHub and its features and gain acceptable expertise by -consistently practicing key GitHub workflows, collaborating on projects. -- Develop expertise in setting up, navigating, and customizing VS Code for -coding, predictive stepping, debugging, and collaboration. +- **Learn Python:** Strengthen foundational and advanced Python skills through + practical application. +- **GitHub Expertise:** Gain hands-on experience with GitHub's features for + collaboration, version control, and code review. +- **Visual Studio Code:** Use Visual Studio Code efficiently for coding, + debugging, and managing repositories. ### Human Skills -- Learn how to collaborate and work collectively towards achieving our project -goals. -- To enhance communication skills by consistently practicing clear and effective -verbal and written communication, actively listening, and adapting messages to -diverse audiences and situations. -- Enhance the ability to prioritize tasks, allocate time effectively, -and meet deadlines without compromising quality. -- Develop problem-solving skills and undertand how to approcah problems -systematically while exploring different ways to look at obstacles that we face. -- Get to know each other more and discover new things about different other -cultures. -- Embrace self-directed and problem based learning approaches to gain knowledge -and skills outside traditional formal settings. +- **Collaboration:** Foster teamwork through effective and respectful group interactions. +- **Communication:** Practice clear and constructive communication during code + reviews and discussions. +- **Time Management:** Develop strategies to manage deadlines and balance responsibilities + within the group. +- **Problem-Solving:** Apply structured approaches to troubleshoot and debug code + effectively. +- **Cultural Integration:** Embrace diverse perspectives and learn to work in a + multicultural environment. +- **Non-Formal Education:** Leverage peer learning to improve technical and soft + skills. + +## Individual Goals + +Each team member will focus on one or more personal objectives, which can include: + +- Reviewing and strengthening previously studied data structures and algorithms. +- Learning new data structures, algorithms, or programming paradigms. +- Practicing new programming language features and incorporating them into solutions. +- Writing more complete, readable, and well-documented solutions. +- Increasing efficiency in coding and problem-solving processes. +- Developing expertise in code review by engaging in iterative feedback cycles. +- Gaining confidence in explaining technical decisions and code logic to peers. + +## Project-Specific Objectives + +- **Master the GitHub code review process:** Including creating and reviewing + pull requests, labeling issues, and maintaining a project board. +- **Continuous Integration (CI):** Build familiarity with CI tools and learn to + address CI check failures effectively. +- **Best Practices:** Improve adherence to best practices such as using helpful + branch naming conventions and completing retrospective documentation. --- -## Individual +By documenting and pursuing these goals, we aim to ensure that each group member + maximizes their learning outcomes while contributing to a successful and + collaborative project. From 289863db4a621ba747fbd8ec1dfc04abe06946bd Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 11:23:36 +0200 Subject: [PATCH 032/163] Solution for mirror words challenge Create Solution for mirror words challenge --- solutions/Solution for mirror words challenge | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 solutions/Solution for mirror words challenge diff --git a/solutions/Solution for mirror words challenge b/solutions/Solution for mirror words challenge new file mode 100644 index 000000000..5e6d0de3f --- /dev/null +++ b/solutions/Solution for mirror words challenge @@ -0,0 +1,99 @@ +Create mirror_words_challenge.py on Challenge/1-Mirror_Words-Aseel AbuKmail branch + +--- +name: Mirror Words Challenge +about: A template PR for code review with a checklist +--- + + + + + +## Behavior + +Returns a new string where each word is reversed while preserving the word order. + +The function should: + +- Accept a single string as input. +- Reverse each word in the sentence while keeping the order of the words intact. +- Maintain all spaces and punctuation exactly as they appear. + +### Files + +- [x] The file name describes the function's behavior +- [x] There is a module docstring in the function file +- [x] The test file's name matches the function file name - + `/tests/test_file_name.py` +- [x] There is a module docstring in the tests file + +### Unit Tests + +- [x] The test class has a helpful name in PascalCase +- [x] The test class has a docstring +- Every unit test has + - [x] A helpful name + - [x] A clear docstring + - [x] Only one assertion + - [x] There is no logic in the unit test +- [x] All tests pass +- [x] There are tests for defensive assertions +- [x] There are tests for boundary cases + +### Function Docstring + +- [x] The function's behavior is described +- The function's arguments are described: + - [x] Type + - [x] Purpose + - [x] Other assumptions (eg. if it's a number, what's the expected range?) +- The return value is described + - [x] Type + - [x] Other assumptions are documented +- The defensive assertions are documented using `Raises:` + - [x] Each assumption about an argument is checked with an assertion + - [x] Each assertion checks for _only one_ assumption about the argument +- [x] Include 3 or more (passing!) doctests + +### The Function + +- [x] The function's name describes it's behavior +- [x] The function's name matches the file name +- [x] The function has correct type annotations +- [x] The function is not called in the function file + +## Strategy + +### Do's + +- [x] Variable names help to understand the strategy +- [x] Any comments are clear and describe the strategy +- [x] Lines of code are spaced to help show different stages of the strategy + +### Don'ts + +- [x] The function's strategy _is not_ described in the documentation +- [x] Comments explain the _strategy_, **not** the _implementation_ +- [x] The function _does not_ have more comments than code + - If it does, consider finding a new strategy or a simpler implementation + +## Implementation + +- [x] The code passes the formatting checks +- [x] The code passes all Ruff linting checks +- [x] The code has no (reasonable) Pylint errors + - In code review, you can decide when fixing a Pylint error is helpful and + when it's too restricting. +- [x] Variables are named with snake_case +- [x] Variable names are clear and helpful +- [x] The code follows the strategy as simply as possible +- [x] The implementation is as simple as possible given the strategy +- [x] There are no commented lines of code +- [] There are no `print` statements anywhere +- [x] The code includes defensive assertions +- [x] Defensive assertions include as little logic as possible From 63ceb0eb6baf734e6b493fb01b8faea7143493e6 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 12:21:55 +0200 Subject: [PATCH 033/163] Update Solution for mirror words challenge --- solutions/Solution for mirror words challenge | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/solutions/Solution for mirror words challenge b/solutions/Solution for mirror words challenge index 5e6d0de3f..442de1ac1 100644 --- a/solutions/Solution for mirror words challenge +++ b/solutions/Solution for mirror words challenge @@ -26,74 +26,74 @@ The function should: ### Files -- [x] The file name describes the function's behavior -- [x] There is a module docstring in the function file -- [x] The test file's name matches the function file name - +- [] The file name describes the function's behavior +- [] There is a module docstring in the function file +- [] The test file's name matches the function file name - `/tests/test_file_name.py` -- [x] There is a module docstring in the tests file +- [] There is a module docstring in the tests file ### Unit Tests -- [x] The test class has a helpful name in PascalCase -- [x] The test class has a docstring +- [] The test class has a helpful name in PascalCase +- [] The test class has a docstring - Every unit test has - - [x] A helpful name - - [x] A clear docstring - - [x] Only one assertion - - [x] There is no logic in the unit test -- [x] All tests pass -- [x] There are tests for defensive assertions -- [x] There are tests for boundary cases + - [] A helpful name + - [] A clear docstring + - [] Only one assertion + - [] There is no logic in the unit test +- [] All tests pass +- [] There are tests for defensive assertions +- [] There are tests for boundary cases ### Function Docstring -- [x] The function's behavior is described +- [] The function's behavior is described - The function's arguments are described: - - [x] Type - - [x] Purpose - - [x] Other assumptions (eg. if it's a number, what's the expected range?) + - [] Type + - [] Purpose + - [] Other assumptions (eg. if it's a number, what's the expected range?) - The return value is described - - [x] Type - - [x] Other assumptions are documented + - [] Type + - [] Other assumptions are documented - The defensive assertions are documented using `Raises:` - - [x] Each assumption about an argument is checked with an assertion - - [x] Each assertion checks for _only one_ assumption about the argument -- [x] Include 3 or more (passing!) doctests + - [] Each assumption about an argument is checked with an assertion + - [] Each assertion checks for _only one_ assumption about the argument +- [] Include 3 or more (passing!) doctests ### The Function -- [x] The function's name describes it's behavior -- [x] The function's name matches the file name -- [x] The function has correct type annotations -- [x] The function is not called in the function file +- [] 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 ## Strategy ### Do's -- [x] Variable names help to understand the strategy -- [x] Any comments are clear and describe the strategy -- [x] Lines of code are spaced to help show different stages of the strategy +- [] Variable names help to understand the strategy +- [] Any comments are clear and describe the strategy +- [] Lines of code are spaced to help show different stages of the strategy ### Don'ts -- [x] The function's strategy _is not_ described in the documentation -- [x] Comments explain the _strategy_, **not** the _implementation_ -- [x] The function _does not_ have more comments than code +- [] The function's strategy _is not_ described in the documentation +- [] 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 ## Implementation -- [x] The code passes the formatting checks -- [x] The code passes all Ruff linting checks -- [x] The code has no (reasonable) Pylint errors +- [] The code passes the formatting checks +- [] The code passes all Ruff linting checks +- [] The code has no (reasonable) Pylint errors - In code review, you can decide when fixing a Pylint error is helpful and when it's too restricting. -- [x] Variables are named with snake_case -- [x] Variable names are clear and helpful -- [x] The code follows the strategy as simply as possible -- [x] The implementation is as simple as possible given the strategy -- [x] There are no commented lines of code +- [] Variables are named with snake_case +- [] Variable names are clear and helpful +- [] The code follows the strategy as simply as possible +- [] The implementation is as simple as possible given the strategy +- [] There are no commented lines of code - [] There are no `print` statements anywhere -- [x] The code includes defensive assertions -- [x] Defensive assertions include as little logic as possible +- [] The code includes defensive assertions +- [] Defensive assertions include as little logic as possible From d11d3ceb9a0abc57dbd5bb216473ff8871d4ec39 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 12:35:58 +0200 Subject: [PATCH 034/163] Create mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 solutions/mirror_words_challenge.py diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py new file mode 100644 index 000000000..6d9b2153b --- /dev/null +++ b/solutions/mirror_words_challenge.py @@ -0,0 +1,42 @@ +# mirror_words_challenge +""" +This module provides a function to reverse each word in a sentence +while keeping the order of the words intact. + +Module contents: + - reverse_words: returns a new string where each word is reversed, + but the order of the words and punctuation remains unchanged. + +Created on 2025-01-05 +@author: Aseel A. S. AbuKmail +""" + +def reverse_words(sentence: str) -> str: + """Returns a new string where each word is reversed while preserving the word order. + + The function should: + - Accept a single string as input. + - Reverse each word in the sentence while keeping the order of the words intact. + - Maintain all spaces and punctuation exactly as they appear. + + Parameter: + sentence: a string containing words to be reversed. + + Returns -> str: a new string where each word is reversed, + but the order of the words and spaces is preserved. + + Examples: + >>> reverse_words("Hello world!") + 'olleH dlrow!' + + >>> reverse_words("Python is fun.") + 'nohtyP si .nuf' + + >>> reverse_words("Keep calm and code on.") + 'peeK mlac dna edoc .no' + """ + # Split the sentence into words + words = sentence.split() + # Reverse each word and join them back with spaces + reversed_words = " ".join(word[::-1] for word in words) + return reversed_words From 16dafa25eb3e19c640772d2766cc03cd39a2c3ad Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 12:43:40 +0200 Subject: [PATCH 035/163] Create test_mirror_words_challenge --- solutions/tests/test_mirror_words_challenge | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 solutions/tests/test_mirror_words_challenge diff --git a/solutions/tests/test_mirror_words_challenge b/solutions/tests/test_mirror_words_challenge new file mode 100644 index 000000000..a9ea7d124 --- /dev/null +++ b/solutions/tests/test_mirror_words_challenge @@ -0,0 +1,73 @@ +# test_mirror_words_challenge +""" +Test module for reverse_words function. +Contains correct tests to help identify bugs in the implementation. + +Test categories: + - Standard cases: Sentences with regular words and punctuation. + - Edge cases: Empty strings, single words, or sentences with special characters. + - Defensive cases: Invalid inputs (e.g., non-string types). + +Created on 2025-01-05 +Author: Aseel A. S. AbuKmail +""" + +import unittest + +from solutions.mirror_words_challenge import reverse_words + + +class TestReverseWords(unittest.TestCase): + """Tests for reverse_words function""" + + # Standard test cases + def test_reverse_words_regular_sentence(self): + """It should reverse each word in a regular sentence while maintaining word order""" + actual = reverse_words("Hello world!") + expected = "olleH dlrow!" + self.assertEqual(actual, expected) + + def test_reverse_words_single_word(self): + """It should reverse a single word in a sentence""" + actual = reverse_words("Python") + expected = "nohtyP" + self.assertEqual(actual, expected) + + def test_reverse_words_with_punctuation(self): + """It should handle words with punctuation marks""" + actual = reverse_words("Python is fun.") + expected = "nohtyP si .nuf" + self.assertEqual(actual, expected) + + # Edge Cases + def test_empty_sentence(self): + """It should return an empty string for an empty sentence""" + actual = reverse_words("") + expected = "" + self.assertEqual(actual, expected) + + def test_single_word(self): + """It should return the word reversed for a single word""" + actual = reverse_words("A") + expected = "A" + self.assertEqual(actual, expected) + + def test_sentence_with_special_characters(self): + """It should handle sentences with special characters correctly""" + actual = reverse_words("!@# $%^ &*()") + expected = "!@# $%^ &*()" + self.assertEqual(actual, expected) + + # Defensive test cases + def test_non_string_input(self): + """It should raise AssertionError for non-string input""" + with self.assertRaises(AssertionError): + reverse_words(12345) + + def test_input_with_mixed_data_types(self): + """It should raise AssertionError for input with mixed types""" + with self.assertRaises(AssertionError): + reverse_words(["Hello", "world!"]) + +if __name__ == "__main__": + unittest.main() From feff3c740a611b34a4bafc6d45e9fc2b21dd9a14 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 12:48:58 +0200 Subject: [PATCH 036/163] Rename test_mirror_words_challenge to test_mirror_words_challenge.py --- ...test_mirror_words_challenge => test_mirror_words_challenge.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename solutions/tests/{test_mirror_words_challenge => test_mirror_words_challenge.py} (100%) diff --git a/solutions/tests/test_mirror_words_challenge b/solutions/tests/test_mirror_words_challenge.py similarity index 100% rename from solutions/tests/test_mirror_words_challenge rename to solutions/tests/test_mirror_words_challenge.py From 7e79d9e8a81feab18a2f668c5ddad9590dfeb241 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:03:32 +0200 Subject: [PATCH 037/163] Add files via upload --- solutions/fizz_buzz_challenge.py | 49 ++++++++++++++++++++++++++ solutions/fizz_buzz_game.py | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 solutions/fizz_buzz_challenge.py create mode 100644 solutions/fizz_buzz_game.py diff --git a/solutions/fizz_buzz_challenge.py b/solutions/fizz_buzz_challenge.py new file mode 100644 index 000000000..b0368f6e5 --- /dev/null +++ b/solutions/fizz_buzz_challenge.py @@ -0,0 +1,49 @@ +""" +A module for a game called FizzBuzz. + +Module contents: + - fizz_buzz_challenge: - fizz_buzz_challenge: generates a dictionary where + the key is the numbe and the value is the outcome of that number + "Fizz" when a number is divisible by 3, + "Buzz" if the number is divisible by 5, + and FizzBuzz if the number is divisible by both + +Created on 31-12-24 +@author: Abdulrahman Alsir + Cody +""" + + +def fizz_buzz(start: int, end: int) -> dict: + """ + Generate a dictionary for the FizzBuzz Game + + Parameters: + start: start of the range of numbers (included) + end: end of the range of numbers (excluded) + + Returns: + dict: a dictionary where keys are the numbers and values are the outcome + of that number and the values are "Fizz", "Buzz", "FizzBuzz", or omitted + if none apply + Raises: + AssertionError: if the start or end are not integers. + Examples: + >>> fizz_buzz(2,10) + {3: 'Fizz', 5: 'Buzz', 6: 'Fizz', 9: 'Fizz'} + >>> fizz_buzz(10, 16) + {10: 'Buzz', 12: 'Fizz', 15: 'FizzBuzz'} + + """ + assert isinstance(start, int) and isinstance( + end, int + ), "Start and end must be integers." + + results = {} + for number in range(start, end): + if number % 15 == 0: + results[number] = "FizzBuzz" + elif number % 3 == 0: + results[number] = "Fizz" + elif number % 5 == 0: + results[number] = "Buzz" + return results diff --git a/solutions/fizz_buzz_game.py b/solutions/fizz_buzz_game.py new file mode 100644 index 000000000..346d5aada --- /dev/null +++ b/solutions/fizz_buzz_game.py @@ -0,0 +1,59 @@ +""" +A module for a game called FizzBuzz. +Module contents: + - fizz_buzz: generates a list of strings (Fizz, Buzz, or FizzBuzz) + "Fizz" when a number is divisible by 3, + "Buzz" if the number is divisible by 5, + and FizzBuzz if the number is divisible by both +Created on 31-12-24 +@author: Abdulrahman Ali + Cody +""" + + +def fizz_buzz(start: int, end: int) -> list: + """ + Generate a list for the FizzBuzz Game. + + Strategy: + The function iterates through each number in the range [start, end). + For each number: + - If divisible by 15 (both 3 and 5), append "FizzBuzz". + - Else, if divisible by 3, append "Fizz". + - Else, if divisible by 5, append "Buzz". + - Otherwise, skip the number. + + Parameters: + start (int): Start of the range of numbers (included). + end (int): End of the range of numbers (excluded). + + Returns: + list: A list containing "Fizz", "Buzz", or "FizzBuzz" based on divisibility + conditions. Numbers divisible by neither 3 nor 5 are excluded. + + Raises: + AssertionError: If start or end is not an integer or if start > end. + + Examples: + >>> fizz_buzz(2, 10) + ['Fizz', 'Buzz', 'Fizz', 'Fizz'] + >>> fizz_buzz(10, 16) + ['Buzz', 'Fizz', 'FizzBuzz'] + >>> fizz_buzz(7, 9) + [] + """ + # Checking for invalid input + if not isinstance(start, int) or not isinstance(end, int): + raise AssertionError("start and end must be integers") + if start > end: + raise AssertionError("start must be less than or equal to end") + + # Generating the FizzBuzz list + result = [] + for number in range(start, end): + if number % 15 == 0: + result.append("FizzBuzz") + elif number % 3 == 0: + result.append("Fizz") + elif number % 5 == 0: + result.append("Buzz") + return result From 67f8053e0d30036c58084ff4861058117e45e2b7 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:05:36 +0200 Subject: [PATCH 038/163] Add files via upload --- solutions/tests/test_fizz_buzz.py | 95 +++++++++++++++++ solutions/tests/test_fizz_buzz_challenge.py | 112 ++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 solutions/tests/test_fizz_buzz.py create mode 100644 solutions/tests/test_fizz_buzz_challenge.py diff --git a/solutions/tests/test_fizz_buzz.py b/solutions/tests/test_fizz_buzz.py new file mode 100644 index 000000000..f592450b7 --- /dev/null +++ b/solutions/tests/test_fizz_buzz.py @@ -0,0 +1,95 @@ +""" +Test module for fizz_buzz function in the FizzBuzz game. +Contains tests for standard cases, edge cases, and defensive assertions. +Test categories: + - Standard cases: typical user inputs and expected game results + - Edge cases: empty input, invalid input + - Defensive tests: wrong input types, assertions +Created on 31-12-24 +Author: Cody + Abdulrahman Ali +""" + +import unittest +from ..fizz_buzz_game import fizz_buzz + + +class TestFizzBuzz(unittest.TestCase): + """ + Test cases for the FizzBuzz game function. + """ + + def test_fizz_buzz_2_to_10(self): + """ + Test for the range 2 to 10, expected output should only include Fizz, + Buzz, and FizzBuzz. + """ + result = fizz_buzz(2, 10) + self.assertEqual(result, ["Fizz", "Buzz", "Fizz", "Fizz"]) + + def test_fizz_buzz_10_to_16(self): + """ + Test for the range 10 to 16, expected output should include Fizz, Buzz, + and FizzBuzz. + """ + result = fizz_buzz(10, 16) + self.assertEqual(result, ["Buzz", "Fizz", "FizzBuzz"]) + + def test_empty_range(self): + """ + Test for an empty range where start equals end. + Expected output is an empty list. + """ + result = fizz_buzz(5, 5) + self.assertEqual(result, []) + + def test_no_fizz_or_buzz(self): + """ + Test for a range with no numbers divisible by 3 or 5. + Expected output is an empty list. + """ + result = fizz_buzz(7, 9) + self.assertEqual(result, []) + + def test_invalid_input_start(self): + """ + Test for invalid input where the start value is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz("a", 10) + + def test_invalid_input_end(self): + """ + Test for invalid input where the end value is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz(1, "b") + + def test_start_greater_than_end(self): + """ + Test for invalid input where start is greater than end. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz(10, 5) + + def test_large_range(self): + """ + Test for a large range, verifying the correctness of the output. + """ + result = fizz_buzz(1, 20) + expected = ["Fizz", "Buzz", "Fizz", "Fizz", "Buzz", "Fizz", "FizzBuzz", "Fizz"] + self.assertEqual(result, expected) + + def test_large_range_empty(self): + """ + Test for a large range with no Fizz or Buzz in the range. + Expected output is a single 'Buzz'. + """ + result = fizz_buzz(100, 102) + self.assertEqual(result, ["Buzz"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_fizz_buzz_challenge.py b/solutions/tests/test_fizz_buzz_challenge.py new file mode 100644 index 000000000..5f3a16884 --- /dev/null +++ b/solutions/tests/test_fizz_buzz_challenge.py @@ -0,0 +1,112 @@ +""" +Test module for fizz_buzz function in the FizzBuzz game. +Contains tests for standard cases, edge cases, and defensive assertions. + +Test categories: + - Standard cases: typical user inputs and expected game results + - Edge cases: empty input, invalid input, and quitting the game + - Defensive tests: wrong input types, assertions + +Created on 31-12-24 +Author: Codi + Abdulrahman Alsir +""" + +import unittest +from ..fizz_buzz_challenge import fizz_buzz + + +class TestFizzBuzzChallenge(unittest.TestCase): + """ + Test cases for the FizzBuzz Challenge function. + + These tests verify that the fizz_buzz_challenge function correctly + identifies numbers divisible by 3, 5, or both, and returns the correct + results based on the game rules. + """ + + def test_range_2_to_10(self): + """ + Test for the range 2 to 10, expected output should include only Fizz, + Buzz, and FizzBuzz. + """ + result = fizz_buzz(2, 10) + expected = {3: "Fizz", 5: "Buzz", 6: "Fizz", 9: "Fizz"} + self.assertEqual(result, expected) + + def test_range_10_to_16(self): + """ + Test for the range 10 to 16, expected output should include Fizz, Buzz, + and FizzBuzz. + """ + result = fizz_buzz(10, 16) + expected = {10: "Buzz", 12: "Fizz", 15: "FizzBuzz"} + self.assertEqual(result, expected) + + def test_empty_range(self): + """ + Test for an empty range where start equals end. Expected output is an + empty dictionary. + """ + result = fizz_buzz(5, 5) + self.assertEqual(result, {}) + + def test_no_fizz_or_buzz(self): + """ + Test for a range with no numbers divisible by 3 or 5. Expected output is + an empty dictionary. + """ + result = fizz_buzz(7, 9) + self.assertEqual(result, {}) + + def test_single_fizz(self): + """ + Test for a range that contains only Fizz (divisible by 3 but not 5). + """ + result = fizz_buzz(3, 4) + expected = {3: "Fizz"} + self.assertEqual(result, expected) + + def test_single_buzz(self): + """ + Test for a range that contains only Buzz (divisible by 5 but not 3). + """ + result = fizz_buzz(5, 6) + expected = {5: "Buzz"} + self.assertEqual(result, expected) + + def test_invalid_input_start(self): + """ + Test for invalid input where start is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz("a", 10) + + def test_invalid_input_end(self): + """ + Test for invalid input where end is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz(10, "b") + + def test_large_range(self): + """ + Test a larger range, verifying the correctness of the output. + """ + result = fizz_buzz(1, 20) + expected = { + 3: "Fizz", + 5: "Buzz", + 6: "Fizz", + 9: "Fizz", + 10: "Buzz", + 12: "Fizz", + 15: "FizzBuzz", + 18: "Fizz", + } + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main() From ae161e7da40c9151c3270b773cce52ed641ec2d7 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:08:31 +0200 Subject: [PATCH 039/163] Add files via upload From 627cbd2b2617b5d6001abc5feb31124daaf2409f Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:12:38 +0200 Subject: [PATCH 040/163] Add files via upload --- solutions/rock_paper_scissor.py | 72 +++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 solutions/rock_paper_scissor.py diff --git a/solutions/rock_paper_scissor.py b/solutions/rock_paper_scissor.py new file mode 100644 index 000000000..10455b8c2 --- /dev/null +++ b/solutions/rock_paper_scissor.py @@ -0,0 +1,72 @@ +""" +A module for playing a game of Rock-Paper-Scissors. + +Module contents: + - play_round: plays a round of Rock-Paper-Scissors, the user either wins, + loses, or draws based on the user's choice and the computer's choice. + +Created on 31-12-24 +@author: Abdulrahman Ali (Reviewed by Cody) +""" + +import random + +options = ["rock", "paper", "scissors"] + + +def play_round(user_input: str) -> str: + """ + Allows the user to play a round of Rock-Paper-Scissors with the computer. + + Parameters: + user_input (str): The user's move ('rock', 'paper', or 'scissors'). + + Returns: + str: The result of the round ('win', 'lose', or 'draw'). + + Raises: + AssertionError: If user_input is not a string or is empty. + ValueError: If user_input is not one of the valid options. + + Examples: + >>> play_round('rock') # doctest: +SKIP + 'win' # User plays rock, and the computer picks scissors. + + >>> play_round('paper') # doctest: +SKIP + 'lose' # User plays paper, and the computer picks scissors. + + >>> play_round('scissors') # doctest: +SKIP + 'draw' # User plays scissors, and the computer picks scissors. + + >>> play_round('invalid') + Traceback (most recent call last): + ... + ValueError: Invalid input. Choose one of: rock, paper, scissors. + + Strategy: + 1. Validate the user input to ensure it is a non-empty string and one of the valid options. + 2. Randomly select a move for the computer from the available options. + 3. Compare the user's move with the computer's move to determine the result: + - If both moves are the same, return "draw". + - If the user wins, return "win". + - If the user loses, return "lose". + """ + assert ( + isinstance(user_input, str) and user_input.strip() + ), "Input must be a non-empty string." + + user_input = user_input.strip().lower() + if user_input not in options: + raise ValueError(f"Invalid input. Choose one of: {', '.join(options)}.") + + computer_pick = random.choice(options) + if user_input == computer_pick: + return "draw" + elif ( + (user_input == "rock" and computer_pick == "scissors") + or (user_input == "paper" and computer_pick == "rock") + or (user_input == "scissors" and computer_pick == "paper") + ): + return "win" + else: + return "lose" From e644813fa082ab0885f6643895cb766b3590fd1c Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 5 Jan 2025 13:13:23 +0200 Subject: [PATCH 041/163] Add files via upload --- solutions/tests/test_rock_paper_scissor.py | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 solutions/tests/test_rock_paper_scissor.py diff --git a/solutions/tests/test_rock_paper_scissor.py b/solutions/tests/test_rock_paper_scissor.py new file mode 100644 index 000000000..1113718ad --- /dev/null +++ b/solutions/tests/test_rock_paper_scissor.py @@ -0,0 +1,59 @@ +""" +Test module for play_round function in the Rock-Paper-Scissors game. +Contains tests for standard cases, edge cases, and defensive assertions. + +Created on 31-12-24 +Author: Abdulrahman Alsir + Cody +""" + +import unittest +import random +from ..rock_paper_scissor import play_round + + +class TestPlayRound(unittest.TestCase): + """Test suite for the play_round function in the Rock-Paper-Scissors game.""" + + def test_rock(self): + """Test user input 'rock' against various computer picks.""" + random.choice = lambda options: "scissors" + self.assertEqual(play_round("rock"), "win") + + def test_paper(self): + """Test user input 'paper' against various computer picks.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("paper"), "win") + + def test_scissors(self): + """Test user input 'scissors' against various computer picks.""" + random.choice = lambda options: "paper" + self.assertEqual(play_round("scissors"), "win") + + def test_draw(self): + """Test that the game results in a draw when both play the same.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("rock"), "draw") + + def test_case_insensitivity(self): + """Test that the function handles case insensitivity.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("RoCk"), "draw") + + def test_whitespace_handling(self): + """Test that the function handles leading/trailing whitespace.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round(" rock "), "draw") + + def test_empty_input(self): + """Test that an empty input raises an AssertionError.""" + with self.assertRaises(AssertionError): + play_round("") + + def test_invalid_input(self): + """Test that invalid input raises a ValueError.""" + with self.assertRaises(ValueError): + play_round("invalid_choice") + + +if __name__ == "__main__": + unittest.main() From 99285bb38654210968bc679770f99740338d13c6 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 13:20:21 +0200 Subject: [PATCH 042/163] Delete solutions/Solution for mirror words challenge --- solutions/Solution for mirror words challenge | 99 ------------------- 1 file changed, 99 deletions(-) delete mode 100644 solutions/Solution for mirror words challenge diff --git a/solutions/Solution for mirror words challenge b/solutions/Solution for mirror words challenge deleted file mode 100644 index 442de1ac1..000000000 --- a/solutions/Solution for mirror words challenge +++ /dev/null @@ -1,99 +0,0 @@ -Create mirror_words_challenge.py on Challenge/1-Mirror_Words-Aseel AbuKmail branch - ---- -name: Mirror Words Challenge -about: A template PR for code review with a checklist ---- - - - - - -## Behavior - -Returns a new string where each word is reversed while preserving the word order. - -The function should: - -- Accept a single string as input. -- Reverse each word in the sentence while keeping the order of the words intact. -- Maintain all spaces and punctuation exactly as they appear. - -### Files - -- [] The file name describes the function's behavior -- [] There is a module docstring in the function file -- [] The test file's name matches the function file name - - `/tests/test_file_name.py` -- [] There is a module docstring in the tests file - -### Unit Tests - -- [] The test class has a helpful name in PascalCase -- [] The test class has a docstring -- Every unit test has - - [] A helpful name - - [] A clear docstring - - [] Only one assertion - - [] There is no logic in the unit test -- [] All tests pass -- [] There are tests for defensive assertions -- [] There are tests for boundary cases - -### Function Docstring - -- [] The function's behavior is described -- The function's arguments are described: - - [] Type - - [] Purpose - - [] Other assumptions (eg. if it's a number, what's the expected range?) -- The return value is described - - [] Type - - [] Other assumptions are documented -- The defensive assertions are documented using `Raises:` - - [] Each assumption about an argument is checked with an assertion - - [] Each assertion checks for _only one_ assumption about the argument -- [] Include 3 or more (passing!) doctests - -### The Function - -- [] 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 - -## Strategy - -### Do's - -- [] Variable names help to understand the strategy -- [] Any comments are clear and describe the strategy -- [] Lines of code are spaced to help show different stages of the strategy - -### Don'ts - -- [] The function's strategy _is not_ described in the documentation -- [] 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 - -## Implementation - -- [] The code passes the formatting checks -- [] The code passes all Ruff linting checks -- [] The code has no (reasonable) Pylint errors - - In code review, you can decide when fixing a Pylint error is helpful and - when it's too restricting. -- [] Variables are named with snake_case -- [] Variable names are clear and helpful -- [] The code follows the strategy as simply as possible -- [] The implementation is as simple as possible given the strategy -- [] There are no commented lines of code -- [] There are no `print` statements anywhere -- [] The code includes defensive assertions -- [] Defensive assertions include as little logic as possible From c080bc6f9bfb397c59165c6f45d31409b0fb2a0f Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 16:17:19 +0200 Subject: [PATCH 043/163] Final update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 63 +++++++++++++++-------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 6d9b2153b..035f91268 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,42 +1,43 @@ # mirror_words_challenge -""" -This module provides a function to reverse each word in a sentence -while keeping the order of the words intact. +import re -Module contents: - - reverse_words: returns a new string where each word is reversed, - but the order of the words and punctuation remains unchanged. - -Created on 2025-01-05 -@author: Aseel A. S. AbuKmail -""" - -def reverse_words(sentence: str) -> str: - """Returns a new string where each word is reversed while preserving the word order. - - The function should: - - Accept a single string as input. - - Reverse each word in the sentence while keeping the order of the words intact. - - Maintain all spaces and punctuation exactly as they appear. +def reverse_words(sentence): + """ + Reverses each word in a given sentence while maintaining the order of the words + and correctly handling punctuation marks at the end of words. - Parameter: - sentence: a string containing words to be reversed. + Args: + sentence (str): The sentence to be processed. It can contain words, punctuation, + and spaces. - Returns -> str: a new string where each word is reversed, - but the order of the words and spaces is preserved. + Returns: + str: A new string where each word is reversed, but the word order and punctuation + remain unchanged. - Examples: + Example: >>> reverse_words("Hello world!") 'olleH dlrow!' - >>> reverse_words("Python is fun.") - 'nohtyP si .nuf' + >>> reverse_words("Python is fun") + 'nohtyP si nuf' - >>> reverse_words("Keep calm and code on.") - 'peeK mlac dna edoc .no' + Raises: + TypeError: If the input is not a string. """ - # Split the sentence into words + + if not isinstance(sentence, str): + raise TypeError("Input must be a string") + words = sentence.split() - # Reverse each word and join them back with spaces - reversed_words = " ".join(word[::-1] for word in words) - return reversed_words + reversed_words = [] + for word in words: + # Match the word and any punctuation at the end + match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) + if match: + # Reverse the word and keep punctuation at the end without reversing it + reversed_word = match.group(1)[::-1] + match.group(2) + reversed_words.append(reversed_word) + else: + # If no match is found, just append the word + reversed_words.append(word) + return ' '.join(reversed_words) From 1c8a18e8d794ee207e38c47121d32543711fe7f9 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 16:18:21 +0200 Subject: [PATCH 044/163] Final update test_mirror_words_challenge.py --- .../tests/test_mirror_words_challenge.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index a9ea7d124..e1ca7c3db 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -9,13 +9,16 @@ - Defensive cases: Invalid inputs (e.g., non-string types). Created on 2025-01-05 -Author: Aseel A. S. AbuKmail +Author: Aseel AbuKmail """ +import sys import unittest -from solutions.mirror_words_challenge import reverse_words +# Add the directory containing mirror_words_challenge.py to the module search path +sys.path.append("C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions") +from mirror_words_challenge import reverse_words class TestReverseWords(unittest.TestCase): """Tests for reverse_words function""" @@ -35,19 +38,20 @@ def test_reverse_words_single_word(self): def test_reverse_words_with_punctuation(self): """It should handle words with punctuation marks""" - actual = reverse_words("Python is fun.") - expected = "nohtyP si .nuf" + actual = reverse_words("Python is fun") + expected = "nohtyP si nuf" # Adjusted to match the correct output self.assertEqual(actual, expected) - # Edge Cases + + # Edge cases def test_empty_sentence(self): """It should return an empty string for an empty sentence""" actual = reverse_words("") expected = "" self.assertEqual(actual, expected) - def test_single_word(self): - """It should return the word reversed for a single word""" + def test_single_character(self): + """It should return the single character unchanged""" actual = reverse_words("A") expected = "A" self.assertEqual(actual, expected) @@ -55,18 +59,18 @@ def test_single_word(self): def test_sentence_with_special_characters(self): """It should handle sentences with special characters correctly""" actual = reverse_words("!@# $%^ &*()") - expected = "!@# $%^ &*()" + expected = "!@# $%^ &*()" # Special characters remain intact self.assertEqual(actual, expected) # Defensive test cases def test_non_string_input(self): - """It should raise AssertionError for non-string input""" - with self.assertRaises(AssertionError): + """It should raise TypeError for non-string input""" + with self.assertRaises(TypeError): reverse_words(12345) def test_input_with_mixed_data_types(self): - """It should raise AssertionError for input with mixed types""" - with self.assertRaises(AssertionError): + """It should raise TypeError for input with mixed types""" + with self.assertRaises(TypeError): reverse_words(["Hello", "world!"]) if __name__ == "__main__": From f2e63daa5119273e78747e340d715f72b59db068 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 16:48:55 +0200 Subject: [PATCH 045/163] Last Update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 035f91268..73994a4c7 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,17 +1,18 @@ # mirror_words_challenge import re + def reverse_words(sentence): """ - Reverses each word in a given sentence while maintaining the order of the words + Reverses each word in a given sentence while maintaining the order of the words and correctly handling punctuation marks at the end of words. Args: - sentence (str): The sentence to be processed. It can contain words, punctuation, + sentence (str): The sentence to be processed. It can contain words, punctuation, and spaces. Returns: - str: A new string where each word is reversed, but the word order and punctuation + str: A new string where each word is reversed, but the word order and punctuation remain unchanged. Example: @@ -24,7 +25,7 @@ def reverse_words(sentence): Raises: TypeError: If the input is not a string. """ - + if not isinstance(sentence, str): raise TypeError("Input must be a string") @@ -40,4 +41,4 @@ def reverse_words(sentence): else: # If no match is found, just append the word reversed_words.append(word) - return ' '.join(reversed_words) + return " ".join(reversed_words) From ba48437679098bd24f5818b8a52a539e550dcd94 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 16:49:31 +0200 Subject: [PATCH 046/163] Last Update test_mirror_words_challenge.py --- solutions/tests/test_mirror_words_challenge.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index e1ca7c3db..8a922afdd 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -16,10 +16,13 @@ import unittest # Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append("C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions") +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) from mirror_words_challenge import reverse_words + class TestReverseWords(unittest.TestCase): """Tests for reverse_words function""" @@ -42,7 +45,6 @@ def test_reverse_words_with_punctuation(self): expected = "nohtyP si nuf" # Adjusted to match the correct output self.assertEqual(actual, expected) - # Edge cases def test_empty_sentence(self): """It should return an empty string for an empty sentence""" @@ -73,5 +75,6 @@ def test_input_with_mixed_data_types(self): with self.assertRaises(TypeError): reverse_words(["Hello", "world!"]) + if __name__ == "__main__": unittest.main() From 14cd94923ddbd9d68368c855028937c3eb1bfa3a Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 17:54:41 +0200 Subject: [PATCH 047/163] Update test_mirror_words_challenge.py --- solutions/tests/test_mirror_words_challenge.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 8a922afdd..0a040c7f2 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -15,13 +15,7 @@ import sys import unittest -# Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) - -from mirror_words_challenge import reverse_words - +from solutions.mirror_words_challenge import reverse_words class TestReverseWords(unittest.TestCase): """Tests for reverse_words function""" From cd6f6d951947dde85e135ca45e11358e61c50fb2 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 17:56:48 +0200 Subject: [PATCH 048/163] Update test_mirror_words_challenge.py --- solutions/tests/test_mirror_words_challenge.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 0a040c7f2..e476a2b63 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -11,12 +11,21 @@ Created on 2025-01-05 Author: Aseel AbuKmail """ - import sys +import os import unittest - from solutions.mirror_words_challenge import reverse_words +# Add the directory containing mirror_words_challenge.py to the module search path +sys.path.append("C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions") + +# Add the project root directory to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))) + +# from mirror_words_challenge import reverse_words +# from solutions.mirror_words_challenge import reverse_words + + class TestReverseWords(unittest.TestCase): """Tests for reverse_words function""" From 7056d7eb0b29a44f22dde0926739673cf74dafb6 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 18:00:37 +0200 Subject: [PATCH 049/163] Update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 130 ++++++++++++++++++---------- 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 73994a4c7..825a21695 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,44 +1,86 @@ -# mirror_words_challenge -import re - - -def reverse_words(sentence): - """ - Reverses each word in a given sentence while maintaining the order of the words - and correctly handling punctuation marks at the end of words. - - Args: - sentence (str): The sentence to be processed. It can contain words, punctuation, - and spaces. - - Returns: - str: A new string where each word is reversed, but the word order and punctuation - remain unchanged. - - Example: - >>> reverse_words("Hello world!") - 'olleH dlrow!' - - >>> reverse_words("Python is fun") - 'nohtyP si nuf' - - Raises: - TypeError: If the input is not a string. - """ - - if not isinstance(sentence, str): - raise TypeError("Input must be a string") - - words = sentence.split() - reversed_words = [] - for word in words: - # Match the word and any punctuation at the end - match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) - if match: - # Reverse the word and keep punctuation at the end without reversing it - reversed_word = match.group(1)[::-1] + match.group(2) - reversed_words.append(reversed_word) - else: - # If no match is found, just append the word - reversed_words.append(word) - return " ".join(reversed_words) +# test_mirror_words_challenge +""" +Test module for reverse_words function. +Contains correct tests to help identify bugs in the implementation. + +Test categories: + - Standard cases: Sentences with regular words and punctuation. + - Edge cases: Empty strings, single words, or sentences with special characters. + - Defensive cases: Invalid inputs (e.g., non-string types). + +Created on 2025-01-05 +Author: Aseel AbuKmail +""" + +import sys +import os +import unittest +from solutions.mirror_words_challenge import reverse_words + +# Add the directory containing mirror_words_challenge.py to the module search path +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) + +# Add the project root directory to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) + +# from mirror_words_challenge import reverse_words +# from solutions.mirror_words_challenge import reverse_words + + +class TestReverseWords(unittest.TestCase): + """Tests for reverse_words function""" + + # Standard test cases + def test_reverse_words_regular_sentence(self): + """It should reverse each word in a regular sentence while maintaining word order""" + actual = reverse_words("Hello world!") + expected = "olleH dlrow!" + self.assertEqual(actual, expected) + + def test_reverse_words_single_word(self): + """It should reverse a single word in a sentence""" + actual = reverse_words("Python") + expected = "nohtyP" + self.assertEqual(actual, expected) + + def test_reverse_words_with_punctuation(self): + """It should handle words with punctuation marks""" + actual = reverse_words("Python is fun") + expected = "nohtyP si nuf" # Adjusted to match the correct output + self.assertEqual(actual, expected) + + # Edge cases + def test_empty_sentence(self): + """It should return an empty string for an empty sentence""" + actual = reverse_words("") + expected = "" + self.assertEqual(actual, expected) + + def test_single_character(self): + """It should return the single character unchanged""" + actual = reverse_words("A") + expected = "A" + self.assertEqual(actual, expected) + + def test_sentence_with_special_characters(self): + """It should handle sentences with special characters correctly""" + actual = reverse_words("!@# $%^ &*()") + expected = "!@# $%^ &*()" # Special characters remain intact + self.assertEqual(actual, expected) + + # Defensive test cases + def test_non_string_input(self): + """It should raise TypeError for non-string input""" + with self.assertRaises(TypeError): + reverse_words(12345) + + def test_input_with_mixed_data_types(self): + """It should raise TypeError for input with mixed types""" + with self.assertRaises(TypeError): + reverse_words(["Hello", "world!"]) + + +if __name__ == "__main__": + unittest.main() From d3436fb1587b224f597568a51fcc3e5260081141 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 18:02:35 +0200 Subject: [PATCH 050/163] Update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 130 ++++++++++------------------ 1 file changed, 44 insertions(+), 86 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 825a21695..73994a4c7 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,86 +1,44 @@ -# test_mirror_words_challenge -""" -Test module for reverse_words function. -Contains correct tests to help identify bugs in the implementation. - -Test categories: - - Standard cases: Sentences with regular words and punctuation. - - Edge cases: Empty strings, single words, or sentences with special characters. - - Defensive cases: Invalid inputs (e.g., non-string types). - -Created on 2025-01-05 -Author: Aseel AbuKmail -""" - -import sys -import os -import unittest -from solutions.mirror_words_challenge import reverse_words - -# Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) - -# Add the project root directory to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -# from mirror_words_challenge import reverse_words -# from solutions.mirror_words_challenge import reverse_words - - -class TestReverseWords(unittest.TestCase): - """Tests for reverse_words function""" - - # Standard test cases - def test_reverse_words_regular_sentence(self): - """It should reverse each word in a regular sentence while maintaining word order""" - actual = reverse_words("Hello world!") - expected = "olleH dlrow!" - self.assertEqual(actual, expected) - - def test_reverse_words_single_word(self): - """It should reverse a single word in a sentence""" - actual = reverse_words("Python") - expected = "nohtyP" - self.assertEqual(actual, expected) - - def test_reverse_words_with_punctuation(self): - """It should handle words with punctuation marks""" - actual = reverse_words("Python is fun") - expected = "nohtyP si nuf" # Adjusted to match the correct output - self.assertEqual(actual, expected) - - # Edge cases - def test_empty_sentence(self): - """It should return an empty string for an empty sentence""" - actual = reverse_words("") - expected = "" - self.assertEqual(actual, expected) - - def test_single_character(self): - """It should return the single character unchanged""" - actual = reverse_words("A") - expected = "A" - self.assertEqual(actual, expected) - - def test_sentence_with_special_characters(self): - """It should handle sentences with special characters correctly""" - actual = reverse_words("!@# $%^ &*()") - expected = "!@# $%^ &*()" # Special characters remain intact - self.assertEqual(actual, expected) - - # Defensive test cases - def test_non_string_input(self): - """It should raise TypeError for non-string input""" - with self.assertRaises(TypeError): - reverse_words(12345) - - def test_input_with_mixed_data_types(self): - """It should raise TypeError for input with mixed types""" - with self.assertRaises(TypeError): - reverse_words(["Hello", "world!"]) - - -if __name__ == "__main__": - unittest.main() +# mirror_words_challenge +import re + + +def reverse_words(sentence): + """ + Reverses each word in a given sentence while maintaining the order of the words + and correctly handling punctuation marks at the end of words. + + Args: + sentence (str): The sentence to be processed. It can contain words, punctuation, + and spaces. + + Returns: + str: A new string where each word is reversed, but the word order and punctuation + remain unchanged. + + Example: + >>> reverse_words("Hello world!") + 'olleH dlrow!' + + >>> reverse_words("Python is fun") + 'nohtyP si nuf' + + Raises: + TypeError: If the input is not a string. + """ + + if not isinstance(sentence, str): + raise TypeError("Input must be a string") + + words = sentence.split() + reversed_words = [] + for word in words: + # Match the word and any punctuation at the end + match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) + if match: + # Reverse the word and keep punctuation at the end without reversing it + reversed_word = match.group(1)[::-1] + match.group(2) + reversed_words.append(reversed_word) + else: + # If no match is found, just append the word + reversed_words.append(word) + return " ".join(reversed_words) From 77e12b2fbe8a84efef8dfc6821c78ff24c7bff3e Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 18:03:29 +0200 Subject: [PATCH 051/163] Update test_mirror_words_challenge.py --- solutions/tests/test_mirror_words_challenge.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index e476a2b63..825a21695 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -11,16 +11,19 @@ Created on 2025-01-05 Author: Aseel AbuKmail """ + import sys import os import unittest from solutions.mirror_words_challenge import reverse_words # Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append("C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions") +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) # Add the project root directory to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) # from mirror_words_challenge import reverse_words # from solutions.mirror_words_challenge import reverse_words From 83e66c6ed5186741dac08f1aa5dd718fd4a3020a Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 20:46:57 +0200 Subject: [PATCH 052/163] Create prime_filter_challenge.py --- prime_filter_challenge.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 prime_filter_challenge.py diff --git a/prime_filter_challenge.py b/prime_filter_challenge.py new file mode 100644 index 000000000..fdbc38ced --- /dev/null +++ b/prime_filter_challenge.py @@ -0,0 +1,42 @@ +# prime_filter_challenge + +import math + +def filter_primes(numbers): + """ + Filters out all non-prime numbers from a given list of integers. + + Args: + numbers (list of int): A list of integers to be processed. + + Returns: + list of int: A new list containing only the prime numbers, maintaining the original order. + + Example: + >>> filter_primes([2, 3, 4, 5, 6, 7]) + [2, 3, 5, 7] + + >>> filter_primes([10, 15, 17, 19, 20]) + [17, 19] + + Raises: + TypeError: If the input is not a list of integers. + ValueError: If the list contains a non-integer element. + """ + + if not isinstance(numbers, list): + raise TypeError("Input must be a list of integers.") + + if not all(isinstance(i, int) for i in numbers): + raise ValueError("All elements in the list must be integers.") + + def is_prime(n): + """Helper function to check if a number is prime.""" + if n <= 1: + return False + for i in range(2, int(math.sqrt(n)) + 1): + if n % i == 0: + return False + return True + + return [num for num in numbers if is_prime(num)] From 2ea81f56229b5ed3d8085550eecbd4ee895123b8 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 21:05:56 +0200 Subject: [PATCH 053/163] Create test_prime_filter_challenge.py --- test_prime_filter_challenge.py | 85 ++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 test_prime_filter_challenge.py diff --git a/test_prime_filter_challenge.py b/test_prime_filter_challenge.py new file mode 100644 index 000000000..033d42ab3 --- /dev/null +++ b/test_prime_filter_challenge.py @@ -0,0 +1,85 @@ +# test_prime_filter_challenge +""" +Test module for filter_primes function. +Contains correct tests to help identify bugs in the implementation. + +Test categories: + - Standard cases: Lists with various integers, both prime and non-prime. + - Edge cases: Empty lists, lists with only non-prime numbers. + - Defensive cases: Invalid inputs (e.g., non-list types, mixed data types). + +Created on 2025-01-05 +Author: Aseel AbuKmail +""" + +import sys +import os +import unittest + +# Add the directory containing mirror_words_challenge.py to the module search path +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) + +# Add the project root directory to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) + + +from solutions.prime_filter_challenge import filter_primes + + +class TestPrimeFilter(unittest.TestCase): + """Tests for filter_primes function""" + + # Standard test cases + def test_filter_primes_regular_list(self): + """It should return a list containing only prime numbers""" + actual = filter_primes([2, 3, 4, 5, 6, 7]) + expected = [2, 3, 5, 7] + self.assertEqual(actual, expected) + + def test_filter_primes_with_negative_numbers(self): + """It should return a list of primes, ignoring negative numbers""" + actual = filter_primes([-2, -3, 4, 5, 6, -7]) + expected = [5] + self.assertEqual(actual, expected) + + def test_filter_primes_with_no_primes(self): + """It should return an empty list if there are no primes""" + actual = filter_primes([4, 6, 8, 10]) + expected = [] + self.assertEqual(actual, expected) + + # Edge cases + def test_empty_list(self): + """It should return an empty list for an empty input list""" + actual = filter_primes([]) + expected = [] + self.assertEqual(actual, expected) + + def test_single_number(self): + """It should return a list with the single number if it's prime, or empty if not""" + actual = filter_primes([7]) + expected = [7] + self.assertEqual(actual, expected) + + def test_list_with_only_non_prime_numbers(self): + """It should return an empty list if all numbers are non-prime""" + actual = filter_primes([1, 4, 6, 8]) + expected = [] + self.assertEqual(actual, expected) + + # Defensive test cases + def test_non_list_input(self): + """It should raise TypeError for non-list input""" + with self.assertRaises(TypeError): + filter_primes(12345) + + def test_input_with_mixed_data_types(self): + """It should raise TypeError for input with mixed types""" + with self.assertRaises(ValueError): + filter_primes([2, "3", 5]) + + +if __name__ == "__main__": + unittest.main() From 4a6d2db9c0636ac9a98b10d9e1e24b49b41a51b9 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sun, 5 Jan 2025 21:07:32 +0200 Subject: [PATCH 054/163] Update one for prime_filter_challenge.py --- prime_filter_challenge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/prime_filter_challenge.py b/prime_filter_challenge.py index fdbc38ced..8956c6bfb 100644 --- a/prime_filter_challenge.py +++ b/prime_filter_challenge.py @@ -2,6 +2,7 @@ import math + def filter_primes(numbers): """ Filters out all non-prime numbers from a given list of integers. From 8225190dfc5cc788a3e30d7045f029ea86d5a41c Mon Sep 17 00:00:00 2001 From: Rouaa93 Date: Mon, 6 Jan 2025 14:26:56 +0200 Subject: [PATCH 055/163] Update constraints.md add new constraint --- collaboration/constraints.md | 1 + 1 file changed, 1 insertion(+) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index c1cd04b3b..1c780ccc5 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -13,6 +13,7 @@ hours to contribute this week. face scheduling conflicts. - **Resource Limitations:** Access to high-performance computers or specific software licenses may be restricted for some members. +**Childcare Responsibilities:** Some team members need to take care of their children. ## Internal: Involuntary From 058a5ec31d57082fdfd67cab0bbd4be4683c99a3 Mon Sep 17 00:00:00 2001 From: Rouaa93 Date: Mon, 6 Jan 2025 14:38:21 +0200 Subject: [PATCH 056/163] Update constraints.md --- collaboration/constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index 1c780ccc5..ab8ac882d 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -13,7 +13,7 @@ hours to contribute this week. face scheduling conflicts. - **Resource Limitations:** Access to high-performance computers or specific software licenses may be restricted for some members. -**Childcare Responsibilities:** Some team members need to take care of their children. +- **Childcare Responsibilities:** Some team members need to take care of their children. ## Internal: Involuntary From 64c3c2c38518611cff7afb3dc088371c00b02f06 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:08:43 +0200 Subject: [PATCH 057/163] Delete solutions/fizz_buzz_game.py --- solutions/fizz_buzz_game.py | 59 ------------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 solutions/fizz_buzz_game.py diff --git a/solutions/fizz_buzz_game.py b/solutions/fizz_buzz_game.py deleted file mode 100644 index 346d5aada..000000000 --- a/solutions/fizz_buzz_game.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -A module for a game called FizzBuzz. -Module contents: - - fizz_buzz: generates a list of strings (Fizz, Buzz, or FizzBuzz) - "Fizz" when a number is divisible by 3, - "Buzz" if the number is divisible by 5, - and FizzBuzz if the number is divisible by both -Created on 31-12-24 -@author: Abdulrahman Ali + Cody -""" - - -def fizz_buzz(start: int, end: int) -> list: - """ - Generate a list for the FizzBuzz Game. - - Strategy: - The function iterates through each number in the range [start, end). - For each number: - - If divisible by 15 (both 3 and 5), append "FizzBuzz". - - Else, if divisible by 3, append "Fizz". - - Else, if divisible by 5, append "Buzz". - - Otherwise, skip the number. - - Parameters: - start (int): Start of the range of numbers (included). - end (int): End of the range of numbers (excluded). - - Returns: - list: A list containing "Fizz", "Buzz", or "FizzBuzz" based on divisibility - conditions. Numbers divisible by neither 3 nor 5 are excluded. - - Raises: - AssertionError: If start or end is not an integer or if start > end. - - Examples: - >>> fizz_buzz(2, 10) - ['Fizz', 'Buzz', 'Fizz', 'Fizz'] - >>> fizz_buzz(10, 16) - ['Buzz', 'Fizz', 'FizzBuzz'] - >>> fizz_buzz(7, 9) - [] - """ - # Checking for invalid input - if not isinstance(start, int) or not isinstance(end, int): - raise AssertionError("start and end must be integers") - if start > end: - raise AssertionError("start must be less than or equal to end") - - # Generating the FizzBuzz list - result = [] - for number in range(start, end): - if number % 15 == 0: - result.append("FizzBuzz") - elif number % 3 == 0: - result.append("Fizz") - elif number % 5 == 0: - result.append("Buzz") - return result From a4ba1333d49d3dfee916047d18a84eadfc1f5190 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:09:06 +0200 Subject: [PATCH 058/163] Delete solutions/tests/test_fizz_buzz.py --- solutions/tests/test_fizz_buzz.py | 95 ------------------------------- 1 file changed, 95 deletions(-) delete mode 100644 solutions/tests/test_fizz_buzz.py diff --git a/solutions/tests/test_fizz_buzz.py b/solutions/tests/test_fizz_buzz.py deleted file mode 100644 index f592450b7..000000000 --- a/solutions/tests/test_fizz_buzz.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Test module for fizz_buzz function in the FizzBuzz game. -Contains tests for standard cases, edge cases, and defensive assertions. -Test categories: - - Standard cases: typical user inputs and expected game results - - Edge cases: empty input, invalid input - - Defensive tests: wrong input types, assertions -Created on 31-12-24 -Author: Cody + Abdulrahman Ali -""" - -import unittest -from ..fizz_buzz_game import fizz_buzz - - -class TestFizzBuzz(unittest.TestCase): - """ - Test cases for the FizzBuzz game function. - """ - - def test_fizz_buzz_2_to_10(self): - """ - Test for the range 2 to 10, expected output should only include Fizz, - Buzz, and FizzBuzz. - """ - result = fizz_buzz(2, 10) - self.assertEqual(result, ["Fizz", "Buzz", "Fizz", "Fizz"]) - - def test_fizz_buzz_10_to_16(self): - """ - Test for the range 10 to 16, expected output should include Fizz, Buzz, - and FizzBuzz. - """ - result = fizz_buzz(10, 16) - self.assertEqual(result, ["Buzz", "Fizz", "FizzBuzz"]) - - def test_empty_range(self): - """ - Test for an empty range where start equals end. - Expected output is an empty list. - """ - result = fizz_buzz(5, 5) - self.assertEqual(result, []) - - def test_no_fizz_or_buzz(self): - """ - Test for a range with no numbers divisible by 3 or 5. - Expected output is an empty list. - """ - result = fizz_buzz(7, 9) - self.assertEqual(result, []) - - def test_invalid_input_start(self): - """ - Test for invalid input where the start value is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz("a", 10) - - def test_invalid_input_end(self): - """ - Test for invalid input where the end value is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz(1, "b") - - def test_start_greater_than_end(self): - """ - Test for invalid input where start is greater than end. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz(10, 5) - - def test_large_range(self): - """ - Test for a large range, verifying the correctness of the output. - """ - result = fizz_buzz(1, 20) - expected = ["Fizz", "Buzz", "Fizz", "Fizz", "Buzz", "Fizz", "FizzBuzz", "Fizz"] - self.assertEqual(result, expected) - - def test_large_range_empty(self): - """ - Test for a large range with no Fizz or Buzz in the range. - Expected output is a single 'Buzz'. - """ - result = fizz_buzz(100, 102) - self.assertEqual(result, ["Buzz"]) - - -if __name__ == "__main__": - unittest.main() From e9a18ce13ae70d4a07eb35b046ca197409ffec1b Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:20:18 +0200 Subject: [PATCH 059/163] Add files via upload --- solutions/fizz_buzz_game.py | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 solutions/fizz_buzz_game.py diff --git a/solutions/fizz_buzz_game.py b/solutions/fizz_buzz_game.py new file mode 100644 index 000000000..149efe61c --- /dev/null +++ b/solutions/fizz_buzz_game.py @@ -0,0 +1,55 @@ +""" +A module for a game called FizzBuzz. + +Module contents: + - fizz_buzz: generates a dictionary where the key is the number and the value is the outcome of that number + "Fizz" when a number is divisible by 3, + "Buzz" if the number is divisible by 5, + and "FizzBuzz" if the number is divisible by both. + +Created on 31-12-24 +@author: Abdulrahman Ali + Cody +""" + + +def fizz_buzz(start: int, end: int) -> dict: + """ + Generate a dictionary for the FizzBuzz Game. + + Parameters: + start (int): Start of the range of numbers (inclusive). + end (int): End of the range of numbers (exclusive). + + Returns: + dict: A dictionary where keys are the numbers and values are the outcome + of that number, which can be "Fizz", "Buzz", "FizzBuzz", or omitted + if none apply. + + Raises: + AssertionError: If the start or end are not integers. + + Examples: + >>> fizz_buzz(2, 10) + {3: 'Fizz', 5: 'Buzz', 6: 'Fizz', 9: 'Fizz'} + >>> fizz_buzz(10, 16) + {10: 'Buzz', 12: 'Fizz', 15: 'FizzBuzz'} + >>> fizz_buzz(1, 5) + {3: 'Fizz', 5: 'Buzz'} + """ + assert isinstance(start, int) and isinstance( + end, int + ), "Start and end must be integers." + + results = {} + # Iterate through the range of numbers + for number in range(start, end): + if number % 15 == 0: + results[number] = "FizzBuzz" + elif number % 3 == 0: + results[number] = "Fizz" + elif number % 5 == 0: + results[number] = "Buzz" + return results + + +print(fizz_buzz(2, 10)) From 7d44312edd4fe67e1ad130c9200cfff81e27f94d Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:20:49 +0200 Subject: [PATCH 060/163] Delete solutions/tests/test_fizz_buzz_challenge.py --- solutions/tests/test_fizz_buzz_challenge.py | 112 -------------------- 1 file changed, 112 deletions(-) delete mode 100644 solutions/tests/test_fizz_buzz_challenge.py diff --git a/solutions/tests/test_fizz_buzz_challenge.py b/solutions/tests/test_fizz_buzz_challenge.py deleted file mode 100644 index 5f3a16884..000000000 --- a/solutions/tests/test_fizz_buzz_challenge.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Test module for fizz_buzz function in the FizzBuzz game. -Contains tests for standard cases, edge cases, and defensive assertions. - -Test categories: - - Standard cases: typical user inputs and expected game results - - Edge cases: empty input, invalid input, and quitting the game - - Defensive tests: wrong input types, assertions - -Created on 31-12-24 -Author: Codi + Abdulrahman Alsir -""" - -import unittest -from ..fizz_buzz_challenge import fizz_buzz - - -class TestFizzBuzzChallenge(unittest.TestCase): - """ - Test cases for the FizzBuzz Challenge function. - - These tests verify that the fizz_buzz_challenge function correctly - identifies numbers divisible by 3, 5, or both, and returns the correct - results based on the game rules. - """ - - def test_range_2_to_10(self): - """ - Test for the range 2 to 10, expected output should include only Fizz, - Buzz, and FizzBuzz. - """ - result = fizz_buzz(2, 10) - expected = {3: "Fizz", 5: "Buzz", 6: "Fizz", 9: "Fizz"} - self.assertEqual(result, expected) - - def test_range_10_to_16(self): - """ - Test for the range 10 to 16, expected output should include Fizz, Buzz, - and FizzBuzz. - """ - result = fizz_buzz(10, 16) - expected = {10: "Buzz", 12: "Fizz", 15: "FizzBuzz"} - self.assertEqual(result, expected) - - def test_empty_range(self): - """ - Test for an empty range where start equals end. Expected output is an - empty dictionary. - """ - result = fizz_buzz(5, 5) - self.assertEqual(result, {}) - - def test_no_fizz_or_buzz(self): - """ - Test for a range with no numbers divisible by 3 or 5. Expected output is - an empty dictionary. - """ - result = fizz_buzz(7, 9) - self.assertEqual(result, {}) - - def test_single_fizz(self): - """ - Test for a range that contains only Fizz (divisible by 3 but not 5). - """ - result = fizz_buzz(3, 4) - expected = {3: "Fizz"} - self.assertEqual(result, expected) - - def test_single_buzz(self): - """ - Test for a range that contains only Buzz (divisible by 5 but not 3). - """ - result = fizz_buzz(5, 6) - expected = {5: "Buzz"} - self.assertEqual(result, expected) - - def test_invalid_input_start(self): - """ - Test for invalid input where start is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz("a", 10) - - def test_invalid_input_end(self): - """ - Test for invalid input where end is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz(10, "b") - - def test_large_range(self): - """ - Test a larger range, verifying the correctness of the output. - """ - result = fizz_buzz(1, 20) - expected = { - 3: "Fizz", - 5: "Buzz", - 6: "Fizz", - 9: "Fizz", - 10: "Buzz", - 12: "Fizz", - 15: "FizzBuzz", - 18: "Fizz", - } - self.assertEqual(result, expected) - - -if __name__ == "__main__": - unittest.main() From ee954d24c9388087c29bce540b9efad5ef42d908 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:21:08 +0200 Subject: [PATCH 061/163] Delete solutions/fizz_buzz_challenge.py --- solutions/fizz_buzz_challenge.py | 49 -------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 solutions/fizz_buzz_challenge.py diff --git a/solutions/fizz_buzz_challenge.py b/solutions/fizz_buzz_challenge.py deleted file mode 100644 index b0368f6e5..000000000 --- a/solutions/fizz_buzz_challenge.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -A module for a game called FizzBuzz. - -Module contents: - - fizz_buzz_challenge: - fizz_buzz_challenge: generates a dictionary where - the key is the numbe and the value is the outcome of that number - "Fizz" when a number is divisible by 3, - "Buzz" if the number is divisible by 5, - and FizzBuzz if the number is divisible by both - -Created on 31-12-24 -@author: Abdulrahman Alsir + Cody -""" - - -def fizz_buzz(start: int, end: int) -> dict: - """ - Generate a dictionary for the FizzBuzz Game - - Parameters: - start: start of the range of numbers (included) - end: end of the range of numbers (excluded) - - Returns: - dict: a dictionary where keys are the numbers and values are the outcome - of that number and the values are "Fizz", "Buzz", "FizzBuzz", or omitted - if none apply - Raises: - AssertionError: if the start or end are not integers. - Examples: - >>> fizz_buzz(2,10) - {3: 'Fizz', 5: 'Buzz', 6: 'Fizz', 9: 'Fizz'} - >>> fizz_buzz(10, 16) - {10: 'Buzz', 12: 'Fizz', 15: 'FizzBuzz'} - - """ - assert isinstance(start, int) and isinstance( - end, int - ), "Start and end must be integers." - - results = {} - for number in range(start, end): - if number % 15 == 0: - results[number] = "FizzBuzz" - elif number % 3 == 0: - results[number] = "Fizz" - elif number % 5 == 0: - results[number] = "Buzz" - return results From c48f34a07923d16587c96a75df60fff6cc70eeb6 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:21:24 +0200 Subject: [PATCH 062/163] Add files via upload --- solutions/tests/test_fizz_buzz_game.py | 112 +++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 solutions/tests/test_fizz_buzz_game.py diff --git a/solutions/tests/test_fizz_buzz_game.py b/solutions/tests/test_fizz_buzz_game.py new file mode 100644 index 000000000..c47d1d839 --- /dev/null +++ b/solutions/tests/test_fizz_buzz_game.py @@ -0,0 +1,112 @@ +""" +Test module for fizz_buzz function in the FizzBuzz game. +Contains tests for standard cases, edge cases, and defensive assertions. + +Test categories: + - Standard cases: typical user inputs and expected game results + - Edge cases: empty input, invalid input, and quitting the game + - Defensive tests: wrong input types, assertions + +Created on 31-12-24 +Author: Cody + Abdulrahman Ali +""" + +import unittest +from ..fizz_buzz_game import fizz_buzz + + +class TestFizzBuzzChallenge(unittest.TestCase): + """ + Test cases for the FizzBuzz Challenge function. + + These tests verify that the fizz_buzz_challenge function correctly + identifies numbers divisible by 3, 5, or both, and returns the correct + results based on the game rules. + """ + + def test_range_2_to_10(self): + """ + Test for the range 2 to 10, expected output should include only Fizz, + Buzz, and FizzBuzz. + """ + result = fizz_buzz(2, 10) + expected = {3: "Fizz", 5: "Buzz", 6: "Fizz", 9: "Fizz"} + self.assertEqual(result, expected) + + def test_range_10_to_16(self): + """ + Test for the range 10 to 16, expected output should include Fizz, Buzz, + and FizzBuzz. + """ + result = fizz_buzz(10, 16) + expected = {10: "Buzz", 12: "Fizz", 15: "FizzBuzz"} + self.assertEqual(result, expected) + + def test_empty_range(self): + """ + Test for an empty range where start equals end. Expected output is an + empty dictionary. + """ + result = fizz_buzz(5, 5) + self.assertEqual(result, {}) + + def test_no_fizz_or_buzz(self): + """ + Test for a range with no numbers divisible by 3 or 5. Expected output is + an empty dictionary. + """ + result = fizz_buzz(7, 9) + self.assertEqual(result, {}) + + def test_single_fizz(self): + """ + Test for a range that contains only Fizz (divisible by 3 but not 5). + """ + result = fizz_buzz(3, 4) + expected = {3: "Fizz"} + self.assertEqual(result, expected) + + def test_single_buzz(self): + """ + Test for a range that contains only Buzz (divisible by 5 but not 3). + """ + result = fizz_buzz(5, 6) + expected = {5: "Buzz"} + self.assertEqual(result, expected) + + def test_invalid_input_start(self): + """ + Test for invalid input where start is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz("a", 10) + + def test_invalid_input_end(self): + """ + Test for invalid input where end is not an integer. + Should raise an AssertionError. + """ + with self.assertRaises(AssertionError): + fizz_buzz(10, "b") + + def test_large_range(self): + """ + Test a larger range, verifying the correctness of the output. + """ + result = fizz_buzz(1, 20) + expected = { + 3: "Fizz", + 5: "Buzz", + 6: "Fizz", + 9: "Fizz", + 10: "Buzz", + 12: "Fizz", + 15: "FizzBuzz", + 18: "Fizz", + } + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main() From 9dd25905598d53a851c93a9d000a70cdde6fc089 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:14:33 +0200 Subject: [PATCH 063/163] Add files via upload --- solutions/number_sort.py | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 solutions/number_sort.py diff --git a/solutions/number_sort.py b/solutions/number_sort.py new file mode 100644 index 000000000..387be7559 --- /dev/null +++ b/solutions/number_sort.py @@ -0,0 +1,52 @@ +""" +A module for sorting a list of numbers using a custom implementation of the Bubble Sort algorithm. + +Module contents: + - sort: sorts a list of numbers in ascending order using the Bubble Sort algorithm. + - main loop: collects user input until 9 numbers are provided and sorts them. + +Created on 03-01-25 +@author: Abdulrahman Ali + Cody +""" + + +def sort(list_of_num: list) -> list: + """ + Sorts a list of numbers in ascending order using the Bubble Sort algorithm. + + Parameters: + list_of_num (list): A list of numbers to be sorted. + + Returns: + list: The sorted list of numbers. + + Raises: + TypeError: If the input is not a list. + ValueError: If any element in the list is not a number (int or float). + + Strategy: + The function iterates through the list of numbers and compares each element with the next. + If the current element is greater than the next, the elements are swapped. + This process is repeated until the list is sorted in ascending order. + + Examples: + >>> sort([4, 2, 7, 1]) + [1, 2, 4, 7] + + >>> sort([10, -5, 3, 0]) + [-5, 0, 3, 10] + + >>> sort([1, 2, 3, 4]) + [1, 2, 3, 4] + """ + if not isinstance(list_of_num, list): + raise TypeError("Input must be a list.") + + if not all(isinstance(x, (int, float)) for x in list_of_num): + raise ValueError("All elements in the list must be numbers.") + + for i in range(len(list_of_num) - 1): + for j in range(len(list_of_num) - 1 - i): + if list_of_num[j] > list_of_num[j + 1]: + list_of_num[j], list_of_num[j + 1] = list_of_num[j + 1], list_of_num[j] + return list_of_num From f70d1ba30bedd1a7cb60166c132a08ac0629a92a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Mon, 6 Jan 2025 21:15:06 +0200 Subject: [PATCH 064/163] Add files via upload --- solutions/tests/test_number_sort.py | 89 +++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 solutions/tests/test_number_sort.py diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py new file mode 100644 index 000000000..7fca84ddc --- /dev/null +++ b/solutions/tests/test_number_sort.py @@ -0,0 +1,89 @@ +""" +Test module for the sort function in the custom Bubble Sort implementation. +Contains tests for standard cases, edge cases, and defensive assertions. + +Test categories: + - Standard cases: typical lists of numbers to verify sorting correctness + - Edge cases: empty lists, single-element lists, duplicate values + - Defensive tests: invalid input types, assertions + +Created on 03-01-25 +Author: Cody +""" + +import unittest +from ..number_sort import sort + + +class TestBubbleSort(unittest.TestCase): + """ + Test cases for the custom Bubble Sort function. + """ + + def test_standard_case(self): + """Test for a typical list of unsorted numbers.""" + result = sort([4, 2, 7, 1, 9, 5]) + expected = [1, 2, 4, 5, 7, 9] + self.assertEqual(result, expected) + + def test_reverse_order(self): + """Test for a list in reverse order.""" + result = sort([10, 9, 8, 7, 6, 5]) + expected = [5, 6, 7, 8, 9, 10] + self.assertEqual(result, expected) + + def test_sorted_input(self): + """Test for a list that is already sorted.""" + result = sort([1, 2, 3, 4, 5]) + expected = [1, 2, 3, 4, 5] + self.assertEqual(result, expected) + + def test_single_element(self): + """Test for a list with a single element.""" + result = sort([42]) + expected = [42] + self.assertEqual(result, expected) + + def test_empty_list(self): + """Test for an empty list.""" + result = sort([]) + expected = [] + self.assertEqual(result, expected) + + def test_duplicates(self): + """Test for a list with duplicate numbers.""" + result = sort([4, 2, 7, 2, 5, 4]) + expected = [2, 2, 4, 4, 5, 7] + self.assertEqual(result, expected) + + def test_negative_numbers(self): + """Test for a list with negative numbers.""" + result = sort([-3, -1, -4, -2]) + expected = [-4, -3, -2, -1] + self.assertEqual(result, expected) + + def test_mixed_numbers(self): + """Test for a list with a mix of positive and negative numbers.""" + result = sort([-2, 3, 0, -1, 4]) + expected = [-2, -1, 0, 3, 4] + self.assertEqual(result, expected) + + def test_invalid_input_type(self): + """Test for invalid input where the argument is not a list.""" + with self.assertRaises(TypeError): + sort("not_a_list") + + def test_non_numeric_elements(self): + """Test for a list with non-numeric elements.""" + with self.assertRaises(ValueError): + sort([1, "a", 3]) + + def test_large_numbers(self): + """Test for lists with very large numbers.""" + result = sort([10**6, -(10**6), 0]) + expected = [-(10**6), 0, 10**6] + self.assertEqual(result, expected) + + +if __name__ == "__main__": + unittest.main() From c198b88dfcabc511401bbd1914a0c8fca93774c6 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Tue, 7 Jan 2025 12:41:33 +0200 Subject: [PATCH 065/163] Rename prime_filter_challenge.py to solutions/prime_filter_challenge.py --- prime_filter_challenge.py => solutions/prime_filter_challenge.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename prime_filter_challenge.py => solutions/prime_filter_challenge.py (100%) diff --git a/prime_filter_challenge.py b/solutions/prime_filter_challenge.py similarity index 100% rename from prime_filter_challenge.py rename to solutions/prime_filter_challenge.py From 630c7d62590d164c4d0fad276bd9b9bd17caeb04 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Tue, 7 Jan 2025 12:43:02 +0200 Subject: [PATCH 066/163] Rename test_prime_filter_challenge.py to tests/ttest_prime_filter_challenge.py --- .../ttest_prime_filter_challenge.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test_prime_filter_challenge.py => tests/ttest_prime_filter_challenge.py (100%) diff --git a/test_prime_filter_challenge.py b/tests/ttest_prime_filter_challenge.py similarity index 100% rename from test_prime_filter_challenge.py rename to tests/ttest_prime_filter_challenge.py From 8448292b0f448b65d3d6e34b6030f574a2323770 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Tue, 7 Jan 2025 12:43:17 +0200 Subject: [PATCH 067/163] Rename ttest_prime_filter_challenge.py to test_prime_filter_challenge.py --- ...t_prime_filter_challenge.py => test_prime_filter_challenge.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ttest_prime_filter_challenge.py => test_prime_filter_challenge.py} (100%) diff --git a/tests/ttest_prime_filter_challenge.py b/tests/test_prime_filter_challenge.py similarity index 100% rename from tests/ttest_prime_filter_challenge.py rename to tests/test_prime_filter_challenge.py From f96d1d8fe84b860b0a9fd9c280ed2f62cc3afe10 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:48:32 +0200 Subject: [PATCH 068/163] Update test_number_sort.py --- solutions/tests/test_number_sort.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py index 7fca84ddc..7e918b238 100644 --- a/solutions/tests/test_number_sort.py +++ b/solutions/tests/test_number_sort.py @@ -8,10 +8,12 @@ - Defensive tests: invalid input types, assertions Created on 03-01-25 -Author: Cody +Author: Cody + Abdulrahman Ali """ import unittest +# When cloning the repo and running the test locally, the test file might fail +# Just type from solutions.number_sort import sort and the test will run from ..number_sort import sort From b6752174f1429051f460461cc96c4439fa0e3642 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:52:29 +0200 Subject: [PATCH 069/163] Add files via upload --- solutions/tests/test_number_sort.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py index 7e918b238..314e39730 100644 --- a/solutions/tests/test_number_sort.py +++ b/solutions/tests/test_number_sort.py @@ -8,10 +8,11 @@ - Defensive tests: invalid input types, assertions Created on 03-01-25 -Author: Cody + Abdulrahman Ali +Author: Cody """ import unittest + # When cloning the repo and running the test locally, the test file might fail # Just type from solutions.number_sort import sort and the test will run from ..number_sort import sort From ec75407fc377482286d65884f1220b02c88004f0 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:53:10 +0200 Subject: [PATCH 070/163] Add files via upload --- solutions/tests/test_fizz_buzz_game.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/solutions/tests/test_fizz_buzz_game.py b/solutions/tests/test_fizz_buzz_game.py index c47d1d839..01efeaed9 100644 --- a/solutions/tests/test_fizz_buzz_game.py +++ b/solutions/tests/test_fizz_buzz_game.py @@ -12,6 +12,9 @@ """ import unittest + +# When cloning the repo and running the test locally, the test file might fail +# Just type from solutions.fizz_buzz_game import fizz_buzz and the test will run from ..fizz_buzz_game import fizz_buzz From 731ffe06cf3834ff6f29438e28e1738d5325050a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:55:37 +0200 Subject: [PATCH 071/163] Updated function and test file --- solutions/fizz_buzz_game.py | 3 --- solutions/tests/test_fizz_buzz_game.py | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/solutions/fizz_buzz_game.py b/solutions/fizz_buzz_game.py index 149efe61c..94b686e39 100644 --- a/solutions/fizz_buzz_game.py +++ b/solutions/fizz_buzz_game.py @@ -50,6 +50,3 @@ def fizz_buzz(start: int, end: int) -> dict: elif number % 5 == 0: results[number] = "Buzz" return results - - -print(fizz_buzz(2, 10)) diff --git a/solutions/tests/test_fizz_buzz_game.py b/solutions/tests/test_fizz_buzz_game.py index 01efeaed9..2eae6386f 100644 --- a/solutions/tests/test_fizz_buzz_game.py +++ b/solutions/tests/test_fizz_buzz_game.py @@ -7,14 +7,15 @@ - Edge cases: empty input, invalid input, and quitting the game - Defensive tests: wrong input types, assertions +When cloning the repository locally, the test file might not be able to import the function. +To fix this, remove the 2 dots before fizz_buzz_game and run the following command in the terminal: "python -m tests.test_fizz_buzz_game" +Make sure you are in the correct directory. + Created on 31-12-24 Author: Cody + Abdulrahman Ali """ import unittest - -# When cloning the repo and running the test locally, the test file might fail -# Just type from solutions.fizz_buzz_game import fizz_buzz and the test will run from ..fizz_buzz_game import fizz_buzz @@ -110,6 +111,17 @@ def test_large_range(self): } self.assertEqual(result, expected) + def test_negative_range(self): + """Test FizzBuzz with negative numbers.""" + result = fizz_buzz(-6, -2) + expected = {-6: "Fizz", -5: "Buzz", -3: "Fizz"} + self.assertEqual(result, expected) + + def test_reversed_range(self): + """Test that range with end < start returns empty dict.""" + result = fizz_buzz(10, 5) + self.assertEqual(result, {}) + if __name__ == "__main__": unittest.main() From ebe8df33bf71c4cfe463a7b7e965f36c8ace6d10 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:00:12 +0200 Subject: [PATCH 072/163] Update --- solutions/tests/test_rock_paper_scissor.py | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/solutions/tests/test_rock_paper_scissor.py b/solutions/tests/test_rock_paper_scissor.py index 1113718ad..1541aeccc 100644 --- a/solutions/tests/test_rock_paper_scissor.py +++ b/solutions/tests/test_rock_paper_scissor.py @@ -2,6 +2,15 @@ Test module for play_round function in the Rock-Paper-Scissors game. Contains tests for standard cases, edge cases, and defensive assertions. +Test categories: + - Standard cases: typical user inputs and expected game results + - Edge cases: empty input, invalid input, and quitting the game + - Defensive tests: wrong input types, assertions + +When cloning the repository locally, the test file might not be able to import the function. +To fix this, remove the 2 dots before rock_paper_scissor and run the following command in the terminal: "python -m tests.test_rock_paper_scissor" +Make sure you are in the correct directory. + Created on 31-12-24 Author: Abdulrahman Alsir + Cody """ @@ -54,6 +63,31 @@ def test_invalid_input(self): with self.assertRaises(ValueError): play_round("invalid_choice") + def test_input_with_numbers(self): + """Test that numeric input raises ValueError.""" + with self.assertRaises(ValueError): + play_round("123") + + def test_input_with_special_chars(self): + """Test that special characters raise ValueError.""" + with self.assertRaises(ValueError): + play_round("rock!") + + def test_uppercase_input(self): + """Test that uppercase input is handled correctly.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("SCISSORS"), "lose") + + def test_mixed_case_paper(self): + """Test that mixed case 'paper' input is handled.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("PaPeR"), "win") + + def test_mixed_case_rock(self): + """Test that mixed case 'rock' input is handled.""" + random.choice = lambda options: "scissors" + self.assertEqual(play_round("RoCk"), "win") + if __name__ == "__main__": unittest.main() From 2c4ada7cc1ad7468d97bf0d84c7553f5923dbb7c Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Tue, 7 Jan 2025 21:07:20 +0200 Subject: [PATCH 073/163] Update communication.md --- collaboration/communication.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index c60b5db36..8fea53570 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -8,10 +8,11 @@ | Dec. 23rd ✅ | Monday | Google Meet | Collaboratively exploring the group repo.| | Dec. 26th ✅ | Thursday | Google Meet |Collaboration folder and its files| | Jan. 2nd ✅ | Thursday | Google Meet | Final meeting for the collaboration folder| -| Jan. 4th | Saturday | Zoom Online Meeting | Problems Delivery | -| Jan. 6th | Monday | GitHub | Problems Delivery Deadline | -| Jan. 7th | Tuesday | GitHub | Solutions Delivery Deadline | -| Jan. 8th | Wednesday| Zoom Online Meeting | Retrospective | +| Jan. 4th ✅ | Saturday | Zoom Online Meeting | Problems Delivery | +| Jan. 6th ✅ | Monday | GitHub | Review Session | +| Jan. 8th | Wednesday | GitHub | Challenges Delivery Deadline | +| Jan. 9th | Tuesday | GitHub | Reviews Deadline | +| Jan. 11th | Saturday | Zoom Online Meeting | Retrospective | ## Communication Channels From f6b637b93d33deb99b6ea9193dac4163e93797c4 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 08:43:07 +0200 Subject: [PATCH 074/163] Delete the mirror_words_challenge comment in the first line --- solutions/mirror_words_challenge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 73994a4c7..2ed524372 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,4 +1,3 @@ -# mirror_words_challenge import re From b2d9bc273aab14cd7233a5542f9f1286c419b58f Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 08:53:29 +0200 Subject: [PATCH 075/163] Changing reverse_words to mirror_words --- solutions/mirror_words_challenge.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 2ed524372..24c918001 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,7 +1,7 @@ import re -def reverse_words(sentence): +def mirror_words(sentence: str) -> str """ Reverses each word in a given sentence while maintaining the order of the words and correctly handling punctuation marks at the end of words. @@ -15,10 +15,10 @@ def reverse_words(sentence): remain unchanged. Example: - >>> reverse_words("Hello world!") + >>> mirror_words("Hello world!") 'olleH dlrow!' - >>> reverse_words("Python is fun") + >>> mirror_words("Python is fun") 'nohtyP si nuf' Raises: @@ -29,15 +29,15 @@ def reverse_words(sentence): raise TypeError("Input must be a string") words = sentence.split() - reversed_words = [] + mirror_words = [] for word in words: # Match the word and any punctuation at the end match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) if match: # Reverse the word and keep punctuation at the end without reversing it - reversed_word = match.group(1)[::-1] + match.group(2) - reversed_words.append(reversed_word) + mirror_words = match.group(1)[::-1] + match.group(2) + mirror_words.append(mirror_words) else: # If no match is found, just append the word - reversed_words.append(word) + mirror_words.append(word) return " ".join(reversed_words) From ba23abcaf2d66775388a79a8f38996d35c5b7389 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 09:51:36 +0200 Subject: [PATCH 076/163] Adding a third doctest with special characters to demonstrate --- solutions/mirror_words_challenge.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 24c918001..7ae3b3f1b 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -20,6 +20,9 @@ def mirror_words(sentence: str) -> str >>> mirror_words("Python is fun") 'nohtyP si nuf' + + >>> mirror_words("Keep calm & code on.") + 'peeK mlac & edoc .no' Raises: TypeError: If the input is not a string. From ab25c51dbe2fb07d30c9d7ea776688e9f46802a4 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 09:55:51 +0200 Subject: [PATCH 077/163] update the return value description to include that numbers and special characters --- solutions/mirror_words_challenge.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 7ae3b3f1b..d91b3e51b 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -8,11 +8,10 @@ def mirror_words(sentence: str) -> str Args: sentence (str): The sentence to be processed. It can contain words, punctuation, - and spaces. - + spaces, numbers, and special characters. Returns: - str: A new string where each word is reversed, but the word order and punctuation - remain unchanged. + str: A new string where each word is reversed, but the word order, punctuation, + numbers, and special characters remain unchanged. Example: >>> mirror_words("Hello world!") From 2808c2b38bbe9a10e06acfe8b0bda702f7b4db49 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 09:58:00 +0200 Subject: [PATCH 078/163] Delete test_mirror_words_challenge comment in the first line --- solutions/tests/test_mirror_words_challenge.py | 1 - 1 file changed, 1 deletion(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 825a21695..c3712c04c 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -1,4 +1,3 @@ -# test_mirror_words_challenge """ Test module for reverse_words function. Contains correct tests to help identify bugs in the implementation. From 6ad72b4abf10336b7094ad6be541bb4c68fd3df5 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:03:00 +0200 Subject: [PATCH 079/163] Adjust the spacing and structure --- solutions/mirror_words_challenge.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index d91b3e51b..0c59e8741 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -25,12 +25,15 @@ def mirror_words(sentence: str) -> str Raises: TypeError: If the input is not a string. - """ + """ + # Check if the input is a string if not isinstance(sentence, str): raise TypeError("Input must be a string") + # Split the sentence into words words = sentence.split() + mirror_words = [] for word in words: # Match the word and any punctuation at the end From 9886c595ca2835f5a342fb556fb5fafa9f17c586 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:09:17 +0200 Subject: [PATCH 080/163] Enhance the docstring by including an example and a brief note on the function's behavior --- solutions/mirror_words_challenge.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 0c59e8741..23bd890ba 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -2,9 +2,10 @@ def mirror_words(sentence: str) -> str - """ - Reverses each word in a given sentence while maintaining the order of the words - and correctly handling punctuation marks at the end of words. + """ + Reverses each word in a given sentence while maintaining the order of the words. + Punctuation marks at the end of words are correctly handled, ensuring they remain + in place. Args: sentence (str): The sentence to be processed. It can contain words, punctuation, @@ -26,6 +27,11 @@ def mirror_words(sentence: str) -> str Raises: TypeError: If the input is not a string. + Note: + This function processes each word individually, reversing only the characters + in the word while leaving punctuation, numbers, and special characters + unchanged. + """ # Check if the input is a string if not isinstance(sentence, str): @@ -45,4 +51,4 @@ def mirror_words(sentence: str) -> str else: # If no match is found, just append the word mirror_words.append(word) - return " ".join(reversed_words) + return " ".join(mirror_words) From 5aded703bead62994a2969c02f202651408d4b2b Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:20:42 +0200 Subject: [PATCH 081/163] Replace reverse_words with mirror_words and remove the complex file path line --- .../tests/test_mirror_words_challenge.py | 40 +++++++------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index c3712c04c..0443c2530 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -14,58 +14,46 @@ import sys import os import unittest -from solutions.mirror_words_challenge import reverse_words - -# Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) - -# Add the project root directory to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -# from mirror_words_challenge import reverse_words -# from solutions.mirror_words_challenge import reverse_words - +from solutions.mirror_words_challenge import mirror_words class TestReverseWords(unittest.TestCase): - """Tests for reverse_words function""" + """Tests for mirror_words function""" # Standard test cases - def test_reverse_words_regular_sentence(self): - """It should reverse each word in a regular sentence while maintaining word order""" - actual = reverse_words("Hello world!") + def test_mirror_words_regular_sentence(self): + """It should mirror each word in a regular sentence while maintaining word order""" + actual = mirror_words("Hello world!") expected = "olleH dlrow!" self.assertEqual(actual, expected) - def test_reverse_words_single_word(self): + def test_mirror_words_single_word(self): """It should reverse a single word in a sentence""" - actual = reverse_words("Python") + actual = mirror_words("Python") expected = "nohtyP" self.assertEqual(actual, expected) - def test_reverse_words_with_punctuation(self): + def test_mirror_words_with_punctuation(self): """It should handle words with punctuation marks""" - actual = reverse_words("Python is fun") + actual = mirror_words("Python is fun") expected = "nohtyP si nuf" # Adjusted to match the correct output self.assertEqual(actual, expected) # Edge cases def test_empty_sentence(self): """It should return an empty string for an empty sentence""" - actual = reverse_words("") + actual = mirror_words("") expected = "" self.assertEqual(actual, expected) def test_single_character(self): """It should return the single character unchanged""" - actual = reverse_words("A") + actual = mirror_words("A") expected = "A" self.assertEqual(actual, expected) def test_sentence_with_special_characters(self): """It should handle sentences with special characters correctly""" - actual = reverse_words("!@# $%^ &*()") + actual = mirror_words("!@# $%^ &*()") expected = "!@# $%^ &*()" # Special characters remain intact self.assertEqual(actual, expected) @@ -73,12 +61,12 @@ def test_sentence_with_special_characters(self): def test_non_string_input(self): """It should raise TypeError for non-string input""" with self.assertRaises(TypeError): - reverse_words(12345) + mirror_words(12345) def test_input_with_mixed_data_types(self): """It should raise TypeError for input with mixed types""" with self.assertRaises(TypeError): - reverse_words(["Hello", "world!"]) + mirror_words(["Hello", "world!"]) if __name__ == "__main__": From 4c3c4d9623511636fb656b49daa431340d3527f9 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:39:01 +0200 Subject: [PATCH 082/163] Add the directory containing mirror_words_challenge.py to the module search path --- solutions/tests/test_mirror_words_challenge.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 0443c2530..49cd9cc68 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -16,6 +16,18 @@ import unittest from solutions.mirror_words_challenge import mirror_words +# Add the directory containing mirror_words_challenge.py to the module search path +# This is temporarily needed to ensure that the 'mirror_words_challenge' module can be imported +# for testing purposes while the project structure is being developed. +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) + +# Add the project root directory to sys.path +# Ensures that the root project directory is included in the module search path for importing +# other modules or testing purposes. +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) + class TestReverseWords(unittest.TestCase): """Tests for mirror_words function""" From 744bf9ad25070f24660d323a557dae30b3d8d820 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:41:19 +0200 Subject: [PATCH 083/163] Finish def with : --- solutions/mirror_words_challenge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 23bd890ba..20cd4c6f9 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,7 +1,7 @@ import re -def mirror_words(sentence: str) -> str +def mirror_words(sentence: str) -> str: """ Reverses each word in a given sentence while maintaining the order of the words. Punctuation marks at the end of words are correctly handled, ensuring they remain From 355fd0f5cf8d48c720d9f55e604cd3617273b347 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:49:42 +0200 Subject: [PATCH 084/163] Update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 20cd4c6f9..7f9398de6 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -21,7 +21,7 @@ def mirror_words(sentence: str) -> str: >>> mirror_words("Python is fun") 'nohtyP si nuf' - >>> mirror_words("Keep calm & code on.") + >>> mirror_words("Keep calm & code on.") 'peeK mlac & edoc .no' Raises: @@ -31,7 +31,6 @@ def mirror_words(sentence: str) -> str: This function processes each word individually, reversing only the characters in the word while leaving punctuation, numbers, and special characters unchanged. - """ # Check if the input is a string if not isinstance(sentence, str): @@ -39,15 +38,14 @@ def mirror_words(sentence: str) -> str: # Split the sentence into words words = sentence.split() - + mirror_words = [] for word in words: # Match the word and any punctuation at the end match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) if match: # Reverse the word and keep punctuation at the end without reversing it - mirror_words = match.group(1)[::-1] + match.group(2) - mirror_words.append(mirror_words) + mirror_words.append(match.group(1)[::-1] + match.group(2)) else: # If no match is found, just append the word mirror_words.append(word) From 28bb43b67370df8aecbe7922e42b499c85d86e6e Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 10:50:13 +0200 Subject: [PATCH 085/163] Update test_mirror_words_challenge.py --- solutions/tests/test_mirror_words_challenge.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 49cd9cc68..c50ef0520 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -1,5 +1,5 @@ """ -Test module for reverse_words function. +Test module for mirror_words function. Contains correct tests to help identify bugs in the implementation. Test categories: @@ -28,7 +28,7 @@ # other modules or testing purposes. sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) -class TestReverseWords(unittest.TestCase): +class TestMirrorWords(unittest.TestCase): """Tests for mirror_words function""" # Standard test cases @@ -47,7 +47,7 @@ def test_mirror_words_single_word(self): def test_mirror_words_with_punctuation(self): """It should handle words with punctuation marks""" actual = mirror_words("Python is fun") - expected = "nohtyP si nuf" # Adjusted to match the correct output + expected = "nohtyP si nuf" self.assertEqual(actual, expected) # Edge cases From 47cd2239823f7e317a5cad5f9905ae66816a01c1 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 11:00:26 +0200 Subject: [PATCH 086/163] Update test_mirror_words_challenge.py --- .../tests/test_mirror_words_challenge.py | 30 +++---------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index c50ef0520..869942dc4 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -1,32 +1,11 @@ -""" -Test module for mirror_words function. -Contains correct tests to help identify bugs in the implementation. - -Test categories: - - Standard cases: Sentences with regular words and punctuation. - - Edge cases: Empty strings, single words, or sentences with special characters. - - Defensive cases: Invalid inputs (e.g., non-string types). - -Created on 2025-01-05 -Author: Aseel AbuKmail -""" - import sys import os import unittest -from solutions.mirror_words_challenge import mirror_words -# Add the directory containing mirror_words_challenge.py to the module search path -# This is temporarily needed to ensure that the 'mirror_words_challenge' module can be imported -# for testing purposes while the project structure is being developed. -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) +# Add the parent directory to the sys.path for relative imports +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) -# Add the project root directory to sys.path -# Ensures that the root project directory is included in the module search path for importing -# other modules or testing purposes. -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) +from mirror_words_challenge import mirror_words class TestMirrorWords(unittest.TestCase): """Tests for mirror_words function""" @@ -66,7 +45,7 @@ def test_single_character(self): def test_sentence_with_special_characters(self): """It should handle sentences with special characters correctly""" actual = mirror_words("!@# $%^ &*()") - expected = "!@# $%^ &*()" # Special characters remain intact + expected = "!@# $%^ &*()" self.assertEqual(actual, expected) # Defensive test cases @@ -80,6 +59,5 @@ def test_input_with_mixed_data_types(self): with self.assertRaises(TypeError): mirror_words(["Hello", "world!"]) - if __name__ == "__main__": unittest.main() From ab7ab3e91d06ecfb8af0af9f20ce468cd50343b1 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 11:08:21 +0200 Subject: [PATCH 087/163] Final Update mirror_words_challenge.py --- solutions/mirror_words_challenge.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 7f9398de6..8ac48aaf7 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,8 +1,7 @@ import re - def mirror_words(sentence: str) -> str: - """ + """ Reverses each word in a given sentence while maintaining the order of the words. Punctuation marks at the end of words are correctly handled, ensuring they remain in place. @@ -20,8 +19,8 @@ def mirror_words(sentence: str) -> str: >>> mirror_words("Python is fun") 'nohtyP si nuf' - - >>> mirror_words("Keep calm & code on.") + + >>> mirror_words("Keep calm & code on.") 'peeK mlac & edoc .no' Raises: @@ -30,23 +29,23 @@ def mirror_words(sentence: str) -> str: Note: This function processes each word individually, reversing only the characters in the word while leaving punctuation, numbers, and special characters - unchanged. + unchanged. + """ - # Check if the input is a string if not isinstance(sentence, str): raise TypeError("Input must be a string") # Split the sentence into words words = sentence.split() - mirror_words = [] + mirrored_words_list = [] # List to store the mirrored words for word in words: # Match the word and any punctuation at the end match = re.match(r"([a-zA-Z]+)([^a-zA-Z]*)", word) if match: # Reverse the word and keep punctuation at the end without reversing it - mirror_words.append(match.group(1)[::-1] + match.group(2)) + mirrored_words_list.append(match.group(1)[::-1] + match.group(2)) else: - # If no match is found, just append the word - mirror_words.append(word) - return " ".join(mirror_words) + mirrored_words_list.append(word) + + return " ".join(mirrored_words_list) From e9df0985522460d69e54f13cd39604e58d8490bc Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 11:08:59 +0200 Subject: [PATCH 088/163] Final Update test_mirror_words_challenge.py From d7bb41f3033c44ebf3273f77c3f2f9dfa041e2e6 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 11:22:04 +0200 Subject: [PATCH 089/163] Update the lines path for test code --- solutions/tests/test_mirror_words_challenge.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index 869942dc4..c3c4c1db8 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -2,11 +2,17 @@ import os import unittest -# Add the parent directory to the sys.path for relative imports -sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) +# Add the directory containing mirror_words_challenge.py to the module search path +sys.path.append( + "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" +) + +# Add the project root directory to sys.path +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) from mirror_words_challenge import mirror_words + class TestMirrorWords(unittest.TestCase): """Tests for mirror_words function""" @@ -59,5 +65,6 @@ def test_input_with_mixed_data_types(self): with self.assertRaises(TypeError): mirror_words(["Hello", "world!"]) + if __name__ == "__main__": unittest.main() From 13bba23fc15b39a501843855d3d8a623a2c8c11f Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 13:53:32 +0200 Subject: [PATCH 090/163] Last Update mirror_words_challenge.py according to the group 15!! --- solutions/mirror_words_challenge.py | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index 8ac48aaf7..d27c816cc 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -1,5 +1,6 @@ import re + def mirror_words(sentence: str) -> str: """ Reverses each word in a given sentence while maintaining the order of the words. From a9b826069c9f1858282627727f9bfb0b787eea55 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Wed, 8 Jan 2025 13:54:26 +0200 Subject: [PATCH 091/163] Last Update test_mirror_words_challenge.py according to the group 15 --- solutions/tests/test_mirror_words_challenge.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/solutions/tests/test_mirror_words_challenge.py b/solutions/tests/test_mirror_words_challenge.py index c3c4c1db8..59a0b0348 100644 --- a/solutions/tests/test_mirror_words_challenge.py +++ b/solutions/tests/test_mirror_words_challenge.py @@ -1,16 +1,6 @@ -import sys -import os import unittest -# Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) - -# Add the project root directory to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - -from mirror_words_challenge import mirror_words +from ..mirror_words_challenge import mirror_words class TestMirrorWords(unittest.TestCase): From af169e574012740a9849e00c6a880adcd0692bab Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:32:08 +0200 Subject: [PATCH 092/163] Delete solutions/tests/test_fizz_buzz_game.py --- solutions/tests/test_fizz_buzz_game.py | 127 ------------------------- 1 file changed, 127 deletions(-) delete mode 100644 solutions/tests/test_fizz_buzz_game.py diff --git a/solutions/tests/test_fizz_buzz_game.py b/solutions/tests/test_fizz_buzz_game.py deleted file mode 100644 index 2eae6386f..000000000 --- a/solutions/tests/test_fizz_buzz_game.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Test module for fizz_buzz function in the FizzBuzz game. -Contains tests for standard cases, edge cases, and defensive assertions. - -Test categories: - - Standard cases: typical user inputs and expected game results - - Edge cases: empty input, invalid input, and quitting the game - - Defensive tests: wrong input types, assertions - -When cloning the repository locally, the test file might not be able to import the function. -To fix this, remove the 2 dots before fizz_buzz_game and run the following command in the terminal: "python -m tests.test_fizz_buzz_game" -Make sure you are in the correct directory. - -Created on 31-12-24 -Author: Cody + Abdulrahman Ali -""" - -import unittest -from ..fizz_buzz_game import fizz_buzz - - -class TestFizzBuzzChallenge(unittest.TestCase): - """ - Test cases for the FizzBuzz Challenge function. - - These tests verify that the fizz_buzz_challenge function correctly - identifies numbers divisible by 3, 5, or both, and returns the correct - results based on the game rules. - """ - - def test_range_2_to_10(self): - """ - Test for the range 2 to 10, expected output should include only Fizz, - Buzz, and FizzBuzz. - """ - result = fizz_buzz(2, 10) - expected = {3: "Fizz", 5: "Buzz", 6: "Fizz", 9: "Fizz"} - self.assertEqual(result, expected) - - def test_range_10_to_16(self): - """ - Test for the range 10 to 16, expected output should include Fizz, Buzz, - and FizzBuzz. - """ - result = fizz_buzz(10, 16) - expected = {10: "Buzz", 12: "Fizz", 15: "FizzBuzz"} - self.assertEqual(result, expected) - - def test_empty_range(self): - """ - Test for an empty range where start equals end. Expected output is an - empty dictionary. - """ - result = fizz_buzz(5, 5) - self.assertEqual(result, {}) - - def test_no_fizz_or_buzz(self): - """ - Test for a range with no numbers divisible by 3 or 5. Expected output is - an empty dictionary. - """ - result = fizz_buzz(7, 9) - self.assertEqual(result, {}) - - def test_single_fizz(self): - """ - Test for a range that contains only Fizz (divisible by 3 but not 5). - """ - result = fizz_buzz(3, 4) - expected = {3: "Fizz"} - self.assertEqual(result, expected) - - def test_single_buzz(self): - """ - Test for a range that contains only Buzz (divisible by 5 but not 3). - """ - result = fizz_buzz(5, 6) - expected = {5: "Buzz"} - self.assertEqual(result, expected) - - def test_invalid_input_start(self): - """ - Test for invalid input where start is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz("a", 10) - - def test_invalid_input_end(self): - """ - Test for invalid input where end is not an integer. - Should raise an AssertionError. - """ - with self.assertRaises(AssertionError): - fizz_buzz(10, "b") - - def test_large_range(self): - """ - Test a larger range, verifying the correctness of the output. - """ - result = fizz_buzz(1, 20) - expected = { - 3: "Fizz", - 5: "Buzz", - 6: "Fizz", - 9: "Fizz", - 10: "Buzz", - 12: "Fizz", - 15: "FizzBuzz", - 18: "Fizz", - } - self.assertEqual(result, expected) - - def test_negative_range(self): - """Test FizzBuzz with negative numbers.""" - result = fizz_buzz(-6, -2) - expected = {-6: "Fizz", -5: "Buzz", -3: "Fizz"} - self.assertEqual(result, expected) - - def test_reversed_range(self): - """Test that range with end < start returns empty dict.""" - result = fizz_buzz(10, 5) - self.assertEqual(result, {}) - - -if __name__ == "__main__": - unittest.main() From d44145f731d4ef2b850da3739715540e70717b12 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:32:27 +0200 Subject: [PATCH 093/163] Delete solutions/fizz_buzz_game.py --- solutions/fizz_buzz_game.py | 52 ------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 solutions/fizz_buzz_game.py diff --git a/solutions/fizz_buzz_game.py b/solutions/fizz_buzz_game.py deleted file mode 100644 index 94b686e39..000000000 --- a/solutions/fizz_buzz_game.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -A module for a game called FizzBuzz. - -Module contents: - - fizz_buzz: generates a dictionary where the key is the number and the value is the outcome of that number - "Fizz" when a number is divisible by 3, - "Buzz" if the number is divisible by 5, - and "FizzBuzz" if the number is divisible by both. - -Created on 31-12-24 -@author: Abdulrahman Ali + Cody -""" - - -def fizz_buzz(start: int, end: int) -> dict: - """ - Generate a dictionary for the FizzBuzz Game. - - Parameters: - start (int): Start of the range of numbers (inclusive). - end (int): End of the range of numbers (exclusive). - - Returns: - dict: A dictionary where keys are the numbers and values are the outcome - of that number, which can be "Fizz", "Buzz", "FizzBuzz", or omitted - if none apply. - - Raises: - AssertionError: If the start or end are not integers. - - Examples: - >>> fizz_buzz(2, 10) - {3: 'Fizz', 5: 'Buzz', 6: 'Fizz', 9: 'Fizz'} - >>> fizz_buzz(10, 16) - {10: 'Buzz', 12: 'Fizz', 15: 'FizzBuzz'} - >>> fizz_buzz(1, 5) - {3: 'Fizz', 5: 'Buzz'} - """ - assert isinstance(start, int) and isinstance( - end, int - ), "Start and end must be integers." - - results = {} - # Iterate through the range of numbers - for number in range(start, end): - if number % 15 == 0: - results[number] = "FizzBuzz" - elif number % 3 == 0: - results[number] = "Fizz" - elif number % 5 == 0: - results[number] = "Buzz" - return results From dc1b51be174565dd9d786d20f0d8f853a5401f4f Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:18:02 +0200 Subject: [PATCH 094/163] Modified code and test file upon review --- solutions/rock_paper_scissor.py | 37 +++--- solutions/tests/test_rock_paper_scissor.py | 140 ++++++++++++++------- 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/solutions/rock_paper_scissor.py b/solutions/rock_paper_scissor.py index 10455b8c2..d24421ee4 100644 --- a/solutions/rock_paper_scissor.py +++ b/solutions/rock_paper_scissor.py @@ -6,12 +6,13 @@ loses, or draws based on the user's choice and the computer's choice. Created on 31-12-24 +Updated on 09-01-25 @author: Abdulrahman Ali (Reviewed by Cody) """ import random -options = ["rock", "paper", "scissors"] +OPTIONS: list[str] = ["rock", "paper", "scissors"] def play_round(user_input: str) -> str: @@ -22,51 +23,43 @@ def play_round(user_input: str) -> str: user_input (str): The user's move ('rock', 'paper', or 'scissors'). Returns: - str: The result of the round ('win', 'lose', or 'draw'). + str: A string describing the result of the round and the computer's choice. Raises: AssertionError: If user_input is not a string or is empty. ValueError: If user_input is not one of the valid options. Examples: - >>> play_round('rock') # doctest: +SKIP - 'win' # User plays rock, and the computer picks scissors. + >>> play_round('rock') + 'win! Computer's choice: scissors' - >>> play_round('paper') # doctest: +SKIP - 'lose' # User plays paper, and the computer picks scissors. + >>> play_round('paper') + 'lose! Computer's choice: scissors' - >>> play_round('scissors') # doctest: +SKIP - 'draw' # User plays scissors, and the computer picks scissors. + >>> play_round('scissors') + 'draw! Computer's choice: scissors' >>> play_round('invalid') Traceback (most recent call last): ... ValueError: Invalid input. Choose one of: rock, paper, scissors. - - Strategy: - 1. Validate the user input to ensure it is a non-empty string and one of the valid options. - 2. Randomly select a move for the computer from the available options. - 3. Compare the user's move with the computer's move to determine the result: - - If both moves are the same, return "draw". - - If the user wins, return "win". - - If the user loses, return "lose". """ assert ( isinstance(user_input, str) and user_input.strip() ), "Input must be a non-empty string." user_input = user_input.strip().lower() - if user_input not in options: - raise ValueError(f"Invalid input. Choose one of: {', '.join(options)}.") + if user_input not in OPTIONS: + raise ValueError(f"Invalid input. Choose one of: {', '.join(OPTIONS)}.") - computer_pick = random.choice(options) + computer_pick = random.choice(OPTIONS) if user_input == computer_pick: - return "draw" + return f"draw! Computer's choice: {computer_pick}" elif ( (user_input == "rock" and computer_pick == "scissors") or (user_input == "paper" and computer_pick == "rock") or (user_input == "scissors" and computer_pick == "paper") ): - return "win" + return f"win! Computer's choice: {computer_pick}" else: - return "lose" + return f"lose! Computer's choice: {computer_pick}" diff --git a/solutions/tests/test_rock_paper_scissor.py b/solutions/tests/test_rock_paper_scissor.py index 1541aeccc..89e25504f 100644 --- a/solutions/tests/test_rock_paper_scissor.py +++ b/solutions/tests/test_rock_paper_scissor.py @@ -7,86 +7,132 @@ - Edge cases: empty input, invalid input, and quitting the game - Defensive tests: wrong input types, assertions -When cloning the repository locally, the test file might not be able to import the function. -To fix this, remove the 2 dots before rock_paper_scissor and run the following command in the terminal: "python -m tests.test_rock_paper_scissor" -Make sure you are in the correct directory. - Created on 31-12-24 +Updated on 09-01-25 Author: Abdulrahman Alsir + Cody """ import unittest import random -from ..rock_paper_scissor import play_round +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from rock_paper_scissor import play_round class TestPlayRound(unittest.TestCase): """Test suite for the play_round function in the Rock-Paper-Scissors game.""" - def test_rock(self): - """Test user input 'rock' against various computer picks.""" + def test_rock_beats_scissors(self): + """Test rock wins against scissors.""" random.choice = lambda options: "scissors" - self.assertEqual(play_round("rock"), "win") + self.assertEqual(play_round("rock"), "win! Computer's choice: scissors") - def test_paper(self): - """Test user input 'paper' against various computer picks.""" + def test_paper_beats_rock(self): + """Test paper wins against rock.""" random.choice = lambda options: "rock" - self.assertEqual(play_round("paper"), "win") + self.assertEqual(play_round("paper"), "win! Computer's choice: rock") - def test_scissors(self): - """Test user input 'scissors' against various computer picks.""" + def test_scissors_beats_paper(self): + """Test scissors wins against paper.""" random.choice = lambda options: "paper" - self.assertEqual(play_round("scissors"), "win") + self.assertEqual(play_round("scissors"), "win! Computer's choice: paper") + + def test_rock_loses_to_paper(self): + """Test rock loses to paper.""" + random.choice = lambda options: "paper" + self.assertEqual(play_round("rock"), "lose! Computer's choice: paper") + + def test_paper_loses_to_scissors(self): + """Test paper loses to scissors.""" + random.choice = lambda options: "scissors" + self.assertEqual(play_round("paper"), "lose! Computer's choice: scissors") + + def test_scissors_loses_to_rock(self): + """Test scissors loses to rock.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("scissors"), "lose! Computer's choice: rock") + + def test_rock_draws_with_rock(self): + """Test rock draws with rock.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("rock"), "draw! Computer's choice: rock") + + def test_paper_draws_with_paper(self): + """Test paper draws with paper.""" + random.choice = lambda options: "paper" + self.assertEqual(play_round("paper"), "draw! Computer's choice: paper") + + def test_scissors_draws_with_scissors(self): + """Test scissors draws with scissors.""" + random.choice = lambda options: "scissors" + self.assertEqual(play_round("scissors"), "draw! Computer's choice: scissors") + + def test_uppercase_input(self): + """Test uppercase input handling.""" + random.choice = lambda options: "rock" + self.assertEqual(play_round("ROCK"), "draw! Computer's choice: rock") - def test_draw(self): - """Test that the game results in a draw when both play the same.""" + def test_mixed_case_input(self): + """Test mixed case input handling.""" random.choice = lambda options: "rock" - self.assertEqual(play_round("rock"), "draw") + self.assertEqual(play_round("RoCk"), "draw! Computer's choice: rock") - def test_case_insensitivity(self): - """Test that the function handles case insensitivity.""" + def test_leading_whitespace(self): + """Test leading whitespace handling.""" random.choice = lambda options: "rock" - self.assertEqual(play_round("RoCk"), "draw") + self.assertEqual(play_round(" rock"), "draw! Computer's choice: rock") - def test_whitespace_handling(self): - """Test that the function handles leading/trailing whitespace.""" + def test_trailing_whitespace(self): + """Test trailing whitespace handling.""" random.choice = lambda options: "rock" - self.assertEqual(play_round(" rock "), "draw") + self.assertEqual(play_round("rock "), "draw! Computer's choice: rock") def test_empty_input(self): - """Test that an empty input raises an AssertionError.""" + """Test empty input raises AssertionError.""" with self.assertRaises(AssertionError): play_round("") - def test_invalid_input(self): - """Test that invalid input raises a ValueError.""" + def test_invalid_word_input(self): + """Test invalid word input raises ValueError.""" with self.assertRaises(ValueError): - play_round("invalid_choice") + play_round("invalid") - def test_input_with_numbers(self): - """Test that numeric input raises ValueError.""" - with self.assertRaises(ValueError): - play_round("123") + def test_integer_input(self): + """Test integer input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round(123) - def test_input_with_special_chars(self): - """Test that special characters raise ValueError.""" - with self.assertRaises(ValueError): - play_round("rock!") + def test_float_input(self): + """Test float input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round(1.23) - def test_uppercase_input(self): - """Test that uppercase input is handled correctly.""" - random.choice = lambda options: "rock" - self.assertEqual(play_round("SCISSORS"), "lose") + def test_list_input(self): + """Test list input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round(["rock"]) - def test_mixed_case_paper(self): - """Test that mixed case 'paper' input is handled.""" - random.choice = lambda options: "rock" - self.assertEqual(play_round("PaPeR"), "win") + def test_dict_input(self): + """Test dict input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round({"choice": "rock"}) - def test_mixed_case_rock(self): - """Test that mixed case 'rock' input is handled.""" - random.choice = lambda options: "scissors" - self.assertEqual(play_round("RoCk"), "win") + def test_none_input(self): + """Test None input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round(None) + + def test_boolean_input(self): + """Test boolean input raises TypeError.""" + with self.assertRaises(AssertionError): + play_round(True) + + def test_special_chars_input(self): + """Test input with special characters raises ValueError.""" + with self.assertRaises(ValueError): + play_round("rock!") if __name__ == "__main__": From a51ec35e569723e5ba46a6b63cc8144b328ebce1 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:47:04 +0200 Subject: [PATCH 095/163] Solving formatting issues --- solutions/rock_paper_scissor.py | 14 +++++++------- solutions/tests/test_rock_paper_scissor.py | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/solutions/rock_paper_scissor.py b/solutions/rock_paper_scissor.py index d24421ee4..8af5d4617 100644 --- a/solutions/rock_paper_scissor.py +++ b/solutions/rock_paper_scissor.py @@ -2,8 +2,8 @@ A module for playing a game of Rock-Paper-Scissors. Module contents: - - play_round: plays a round of Rock-Paper-Scissors, the user either wins, - loses, or draws based on the user's choice and the computer's choice. + - play_round: Simulates a round of Rock-Paper-Scissors where the user competes + against the computer. Created on 31-12-24 Updated on 09-01-25 @@ -17,7 +17,7 @@ def play_round(user_input: str) -> str: """ - Allows the user to play a round of Rock-Paper-Scissors with the computer. + Simulates a round of Rock-Paper-Scissors with the computer. Parameters: user_input (str): The user's move ('rock', 'paper', or 'scissors'). @@ -26,7 +26,7 @@ def play_round(user_input: str) -> str: str: A string describing the result of the round and the computer's choice. Raises: - AssertionError: If user_input is not a string or is empty. + TypeError: If user_input is not a string or is empty. ValueError: If user_input is not one of the valid options. Examples: @@ -44,15 +44,15 @@ def play_round(user_input: str) -> str: ... ValueError: Invalid input. Choose one of: rock, paper, scissors. """ - assert ( - isinstance(user_input, str) and user_input.strip() - ), "Input must be a non-empty string." + if not isinstance(user_input, str) or not user_input.strip(): + raise TypeError("Input must be a non-empty string.") user_input = user_input.strip().lower() if user_input not in OPTIONS: raise ValueError(f"Invalid input. Choose one of: {', '.join(OPTIONS)}.") computer_pick = random.choice(OPTIONS) + if user_input == computer_pick: return f"draw! Computer's choice: {computer_pick}" elif ( diff --git a/solutions/tests/test_rock_paper_scissor.py b/solutions/tests/test_rock_paper_scissor.py index 89e25504f..be9d15595 100644 --- a/solutions/tests/test_rock_paper_scissor.py +++ b/solutions/tests/test_rock_paper_scissor.py @@ -9,7 +9,7 @@ Created on 31-12-24 Updated on 09-01-25 -Author: Abdulrahman Alsir + Cody +Author: Abdulrahman Ali + Cody """ import unittest @@ -91,7 +91,7 @@ def test_trailing_whitespace(self): def test_empty_input(self): """Test empty input raises AssertionError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round("") def test_invalid_word_input(self): @@ -101,32 +101,32 @@ def test_invalid_word_input(self): def test_integer_input(self): """Test integer input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round(123) def test_float_input(self): """Test float input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round(1.23) def test_list_input(self): """Test list input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round(["rock"]) def test_dict_input(self): """Test dict input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round({"choice": "rock"}) def test_none_input(self): """Test None input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round(None) def test_boolean_input(self): """Test boolean input raises TypeError.""" - with self.assertRaises(AssertionError): + with self.assertRaises(TypeError): play_round(True) def test_special_chars_input(self): From cacdb12178c59905eb67c8377faf8fb69e0c14ce Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Fri, 10 Jan 2025 02:00:15 +0200 Subject: [PATCH 096/163] Added Maria's solution and test files --- solutions/Maria's soltuion.py | 38 +++++++++++++++++++++++++ solutions/tests/Maria's test.py | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 solutions/Maria's soltuion.py create mode 100644 solutions/tests/Maria's test.py diff --git a/solutions/Maria's soltuion.py b/solutions/Maria's soltuion.py new file mode 100644 index 000000000..2005a7b22 --- /dev/null +++ b/solutions/Maria's soltuion.py @@ -0,0 +1,38 @@ +def minimize_chocolate_difference(chocolates, k): + """ + Function to distribute chocolates to minimize the difference between the maximum + and minimum chocolates received. + + Parameters: + chocolates (list): List of integers representing chocolates in packets. + k (int): Number of students. + + Returns: + int: The minimized difference between the maximum and minimum chocolates received. + """ + if k == 0 or len(chocolates) == 0: + return 0 + + if len(chocolates) < k: + return -1 # Not enough packets for the students + + # Sort the packets + chocolates.sort() + + # Initialize the minimum difference + min_diff = float("inf") + + # Find the smallest difference for a group of k packets + for i in range(len(chocolates) - k + 1): + diff = chocolates[i + k - 1] - chocolates[i] + min_diff = min(min_diff, diff) + + return min_diff + + +# Example usage +if _name_ == "_main_": + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + print(f"Minimum difference is {result}") diff --git a/solutions/tests/Maria's test.py b/solutions/tests/Maria's test.py new file mode 100644 index 000000000..ccae30142 --- /dev/null +++ b/solutions/tests/Maria's test.py @@ -0,0 +1,50 @@ +import unittest +from chocolate_distribution import minimize_chocolate_difference + + +class TestMinimizeChocolateDifference(unittest.TestCase): + def test_valid_case(self): + """Test a valid case with enough packets and students.""" + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 10) + + def test_not_enough_packets(self): + """Test the case where there are fewer packets than students.""" + chocolates = [1, 2, 3] + k = 5 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, -1) + + def test_empty_chocolates_list(self): + """Test the case where the chocolates list is empty.""" + chocolates = [] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_zero_students(self): + """Test the case where the number of students is zero.""" + chocolates = [10, 20, 30] + k = 0 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_single_packet(self): + """Test the case with only one packet and one student.""" + chocolates = [42] + k = 1 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_minimized_difference(self): + """Test a generic case to validate the minimized difference calculation.""" + chocolates = [7, 3, 2, 4, 9, 12, 56] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 2) + + +if _name_ == "_main_": + unittest.main() From a312a439ecc95bb264d83171593cac319491732e Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Fri, 10 Jan 2025 07:50:37 +0200 Subject: [PATCH 097/163] Adding type annotations to my function --- solutions/prime_filter_challenge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/prime_filter_challenge.py b/solutions/prime_filter_challenge.py index 8956c6bfb..d40fa0f00 100644 --- a/solutions/prime_filter_challenge.py +++ b/solutions/prime_filter_challenge.py @@ -3,7 +3,7 @@ import math -def filter_primes(numbers): +def filter_primes(numbers: list[int]) -> list[int]: """ Filters out all non-prime numbers from a given list of integers. From 76e272cf0862e4ffba3713dc49d9cb6cd6592fdf Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Fri, 10 Jan 2025 08:50:08 +0200 Subject: [PATCH 098/163] Update prime_filter_challenge.py --- solutions/prime_filter_challenge.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/solutions/prime_filter_challenge.py b/solutions/prime_filter_challenge.py index d40fa0f00..4b1fa248f 100644 --- a/solutions/prime_filter_challenge.py +++ b/solutions/prime_filter_challenge.py @@ -1,9 +1,8 @@ -# prime_filter_challenge - import math +from typing import List -def filter_primes(numbers: list[int]) -> list[int]: +def prime_filter(numbers: List[int]) -> List[int]: """ Filters out all non-prime numbers from a given list of integers. @@ -14,10 +13,10 @@ def filter_primes(numbers: list[int]) -> list[int]: list of int: A new list containing only the prime numbers, maintaining the original order. Example: - >>> filter_primes([2, 3, 4, 5, 6, 7]) + >>> prime_filter([2, 3, 4, 5, 6, 7]) [2, 3, 5, 7] - >>> filter_primes([10, 15, 17, 19, 20]) + >>> prime_filter([10, 15, 17, 19, 20]) [17, 19] Raises: @@ -31,7 +30,7 @@ def filter_primes(numbers: list[int]) -> list[int]: if not all(isinstance(i, int) for i in numbers): raise ValueError("All elements in the list must be integers.") - def is_prime(n): + def is_prime(n: int) -> bool: """Helper function to check if a number is prime.""" if n <= 1: return False From 2132515e0672da51ce140024d0300b19413bd748 Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Fri, 10 Jan 2025 08:50:37 +0200 Subject: [PATCH 099/163] Update test_prime_filter_challenge.py --- tests/test_prime_filter_challenge.py | 41 ++++++++++------------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/tests/test_prime_filter_challenge.py b/tests/test_prime_filter_challenge.py index 033d42ab3..739e96836 100644 --- a/tests/test_prime_filter_challenge.py +++ b/tests/test_prime_filter_challenge.py @@ -1,4 +1,3 @@ -# test_prime_filter_challenge """ Test module for filter_primes function. Contains correct tests to help identify bugs in the implementation. @@ -12,60 +11,48 @@ Author: Aseel AbuKmail """ -import sys -import os import unittest - -# Add the directory containing mirror_words_challenge.py to the module search path -sys.path.append( - "C:/Users/pc/.vscode/VS code Files/MIT - Project/ET6-foundations-group-17-main/solutions" -) - -# Add the project root directory to sys.path -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) - - -from solutions.prime_filter_challenge import filter_primes +from ..prime_filter_challenge import prime_filter class TestPrimeFilter(unittest.TestCase): - """Tests for filter_primes function""" + """Tests for prime_filter function""" # Standard test cases - def test_filter_primes_regular_list(self): + def test_prime_filter_regular_list(self): """It should return a list containing only prime numbers""" - actual = filter_primes([2, 3, 4, 5, 6, 7]) + actual = prime_filter([2, 3, 4, 5, 6, 7]) expected = [2, 3, 5, 7] self.assertEqual(actual, expected) - def test_filter_primes_with_negative_numbers(self): + def test_prime_filter_with_negative_numbers(self): """It should return a list of primes, ignoring negative numbers""" - actual = filter_primes([-2, -3, 4, 5, 6, -7]) + actual = prime_filter([-2, -3, 4, 5, 6, -7]) expected = [5] self.assertEqual(actual, expected) - def test_filter_primes_with_no_primes(self): + def test_prime_filter_with_no_primes(self): """It should return an empty list if there are no primes""" - actual = filter_primes([4, 6, 8, 10]) + actual = prime_filter([4, 6, 8, 10]) expected = [] self.assertEqual(actual, expected) # Edge cases def test_empty_list(self): """It should return an empty list for an empty input list""" - actual = filter_primes([]) + actual = prime_filter([]) expected = [] self.assertEqual(actual, expected) def test_single_number(self): """It should return a list with the single number if it's prime, or empty if not""" - actual = filter_primes([7]) + actual = prime_filter([7]) expected = [7] self.assertEqual(actual, expected) def test_list_with_only_non_prime_numbers(self): """It should return an empty list if all numbers are non-prime""" - actual = filter_primes([1, 4, 6, 8]) + actual = prime_filter([1, 4, 6, 8]) expected = [] self.assertEqual(actual, expected) @@ -73,12 +60,12 @@ def test_list_with_only_non_prime_numbers(self): def test_non_list_input(self): """It should raise TypeError for non-list input""" with self.assertRaises(TypeError): - filter_primes(12345) + prime_filter(12345) def test_input_with_mixed_data_types(self): - """It should raise TypeError for input with mixed types""" + """It should raise ValueError for input with mixed types""" with self.assertRaises(ValueError): - filter_primes([2, "3", 5]) + prime_filter([2, "3", 5]) if __name__ == "__main__": From 03b3c02165e6ae7055326be578ca29e9b882641b Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Fri, 10 Jan 2025 16:57:58 +0200 Subject: [PATCH 100/163] Delete solutions/tests/Maria's test.py --- solutions/tests/Maria's test.py | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 solutions/tests/Maria's test.py diff --git a/solutions/tests/Maria's test.py b/solutions/tests/Maria's test.py deleted file mode 100644 index ccae30142..000000000 --- a/solutions/tests/Maria's test.py +++ /dev/null @@ -1,50 +0,0 @@ -import unittest -from chocolate_distribution import minimize_chocolate_difference - - -class TestMinimizeChocolateDifference(unittest.TestCase): - def test_valid_case(self): - """Test a valid case with enough packets and students.""" - chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] - k = 7 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 10) - - def test_not_enough_packets(self): - """Test the case where there are fewer packets than students.""" - chocolates = [1, 2, 3] - k = 5 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, -1) - - def test_empty_chocolates_list(self): - """Test the case where the chocolates list is empty.""" - chocolates = [] - k = 3 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_zero_students(self): - """Test the case where the number of students is zero.""" - chocolates = [10, 20, 30] - k = 0 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_single_packet(self): - """Test the case with only one packet and one student.""" - chocolates = [42] - k = 1 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_minimized_difference(self): - """Test a generic case to validate the minimized difference calculation.""" - chocolates = [7, 3, 2, 4, 9, 12, 56] - k = 3 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 2) - - -if _name_ == "_main_": - unittest.main() From 34bbe08045cecb7701c3f14cfe84f0f7865e3b7c Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Fri, 10 Jan 2025 16:58:19 +0200 Subject: [PATCH 101/163] Delete solutions/Maria's soltuion.py --- solutions/Maria's soltuion.py | 38 ----------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 solutions/Maria's soltuion.py diff --git a/solutions/Maria's soltuion.py b/solutions/Maria's soltuion.py deleted file mode 100644 index 2005a7b22..000000000 --- a/solutions/Maria's soltuion.py +++ /dev/null @@ -1,38 +0,0 @@ -def minimize_chocolate_difference(chocolates, k): - """ - Function to distribute chocolates to minimize the difference between the maximum - and minimum chocolates received. - - Parameters: - chocolates (list): List of integers representing chocolates in packets. - k (int): Number of students. - - Returns: - int: The minimized difference between the maximum and minimum chocolates received. - """ - if k == 0 or len(chocolates) == 0: - return 0 - - if len(chocolates) < k: - return -1 # Not enough packets for the students - - # Sort the packets - chocolates.sort() - - # Initialize the minimum difference - min_diff = float("inf") - - # Find the smallest difference for a group of k packets - for i in range(len(chocolates) - k + 1): - diff = chocolates[i + k - 1] - chocolates[i] - min_diff = min(min_diff, diff) - - return min_diff - - -# Example usage -if _name_ == "_main_": - chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] - k = 7 - result = minimize_chocolate_difference(chocolates, k) - print(f"Minimum difference is {result}") From a02c0c756cf35b8e26bac2312eb9d0282aeb2604 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Fri, 10 Jan 2025 21:39:35 +0200 Subject: [PATCH 102/163] Rename tests/test_prime_filter_challenge.py to solutions/tests/test_prime_filter_challenge.py Moving the file to the correct folder. --- {tests => solutions/tests}/test_prime_filter_challenge.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {tests => solutions/tests}/test_prime_filter_challenge.py (100%) diff --git a/tests/test_prime_filter_challenge.py b/solutions/tests/test_prime_filter_challenge.py similarity index 100% rename from tests/test_prime_filter_challenge.py rename to solutions/tests/test_prime_filter_challenge.py From 0da6ec5704bc3a28f0fe661f32545767432f4081 Mon Sep 17 00:00:00 2001 From: Ameen-Agha <115144211+Ameen-Agha@users.noreply.github.com> Date: Sat, 11 Jan 2025 11:45:14 +0200 Subject: [PATCH 103/163] Update communication.md --- collaboration/communication.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 8fea53570..771d28a87 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -18,11 +18,11 @@ How often will we get in touch on each channel, and what we will discuss there: -- **Issues**: Used as needed to track tasks, discuss challenges, or document ideas. - Team members should check for updates daily. +- **Issues**: Used when needed to track tasks, discuss challenges, and document ideas. + Team members should also check for updates on a daily basis. - **Pull Requests**: Created whenever code is ready for review. All team members should aim to review PRs within 24-48 hours. -- **Slack/WhatsApp**: Active daily for quick updates, questions, or informal discussions. +- **Slack/WhatsApp**: Active daily for quick updates, questions, and informal discussions. - **Video Calls**: Scheduled every 3 days to discuss progress, address blockers, and align on upcoming tasks. @@ -32,18 +32,20 @@ How often will we get in touch on each channel, and what we will discuss there: | Name/Day | Sunday | Monday | Tuesday | Wednesday| Thursday | Friday | Saturday| |--------------|--------|---------|----------|----------|--------|-------|------| -| Malak Battatt| 5-8 PM| 5-8 PM | 5-8 PM | 5-8 PM |3-6 PM | 2-4 PM| 2-5 PM | +| Malak Battatt| 5-8PM| 5-8PM | 5-8PM | 5-8PM |3-6PM | 2-4PM| 2-5PM | | Abdulrahman Alsir| 7-9PM| 2-5PM | 4-8PM | 7-9PM | 7-9PM | 6-8PM | 6-8PM | +| Ameen Agha| 2-6PM| 2-6PM | 2-6PM | 2-6PM | 2-6PM | 2-6PM| 2-6PM| | Aseel AbuKmail| 7-10PM| 2-5PM | 5-8PM | 5-7PM | 7-9PM | 6-10PM | 6-8PM | | Maher Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Maria Roufail | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | | Muhannad Assaf | 4-7PM| 4-7PM | 6-7PM | 7-8PM | 7-9PM | 6-8PM | 6-8PM | -| Rouaa Hamzah | 4-7 PM| 4-7 PM | 4-7 PM | 4-7 PM | 7-9PM | | | +| Rouaa Hamzah | 4-7PM| 4-7PM | 4-7PM | 4-7PM | 7-9PM | 7-9PM| 7-9PM| ### How many hours everyone has per day - Malak Battatt: _~2-4h_; - Abdulrahman Alsir: _~2-4h_; +- Ameen Agha: _~2-4h_; - Aseel AbuKmail: _~2-4h_; - Maher: _~2h_; - Maria: _~2h_; From 35f8f972c7cd8654dad5c0aebc23a036c3f5091c Mon Sep 17 00:00:00 2001 From: Ameen-Agha <115144211+Ameen-Agha@users.noreply.github.com> Date: Sat, 11 Jan 2025 11:45:47 +0200 Subject: [PATCH 104/163] Update communication.md --- collaboration/communication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 771d28a87..037682b99 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -10,8 +10,8 @@ | Jan. 2nd ✅ | Thursday | Google Meet | Final meeting for the collaboration folder| | Jan. 4th ✅ | Saturday | Zoom Online Meeting | Problems Delivery | | Jan. 6th ✅ | Monday | GitHub | Review Session | -| Jan. 8th | Wednesday | GitHub | Challenges Delivery Deadline | -| Jan. 9th | Tuesday | GitHub | Reviews Deadline | +| Jan. 8th ✅| Wednesday | GitHub | Challenges Delivery Deadline | +| Jan. 9th ✅| Tuesday | GitHub | Reviews Deadline | | Jan. 11th | Saturday | Zoom Online Meeting | Retrospective | ## Communication Channels From 656c44fd94bd2cf499f60dbd7cb789d028bab057 Mon Sep 17 00:00:00 2001 From: Ameen-Agha <115144211+Ameen-Agha@users.noreply.github.com> Date: Sat, 11 Jan 2025 11:54:32 +0200 Subject: [PATCH 105/163] Update communication.md --- collaboration/communication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index 037682b99..b3f1a20e1 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -19,7 +19,7 @@ How often will we get in touch on each channel, and what we will discuss there: - **Issues**: Used when needed to track tasks, discuss challenges, and document ideas. - Team members should also check for updates on a daily basis. + Team members should also check for updates daily. - **Pull Requests**: Created whenever code is ready for review. All team members should aim to review PRs within 24-48 hours. - **Slack/WhatsApp**: Active daily for quick updates, questions, and informal discussions. From 885ed7c45959eb4098732a1603b7fcf0d540bba4 Mon Sep 17 00:00:00 2001 From: Ameen-Agha <115144211+Ameen-Agha@users.noreply.github.com> Date: Sat, 11 Jan 2025 11:57:52 +0200 Subject: [PATCH 106/163] Update communication.md --- collaboration/communication.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/collaboration/communication.md b/collaboration/communication.md index b3f1a20e1..04088a940 100644 --- a/collaboration/communication.md +++ b/collaboration/communication.md @@ -18,8 +18,8 @@ How often will we get in touch on each channel, and what we will discuss there: -- **Issues**: Used when needed to track tasks, discuss challenges, and document ideas. - Team members should also check for updates daily. +- **Issues**: Used as needed to track tasks, discuss challenges, or document ideas. + Team members should also check for updates on a daily basis. - **Pull Requests**: Created whenever code is ready for review. All team members should aim to review PRs within 24-48 hours. - **Slack/WhatsApp**: Active daily for quick updates, questions, and informal discussions. From e598aeae28a8591e794fea4fbba49cfa46b3a3fe Mon Sep 17 00:00:00 2001 From: Ameen-Agha <115144211+Ameen-Agha@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:00:27 +0200 Subject: [PATCH 107/163] Update constraints.md --- collaboration/constraints.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/collaboration/constraints.md b/collaboration/constraints.md index ab8ac882d..ead4ac343 100644 --- a/collaboration/constraints.md +++ b/collaboration/constraints.md @@ -27,6 +27,8 @@ discussions. members may create bottlenecks for specific tasks. - **Health and Well-being:** Some members may have personal health issues or responsibilities that could affect their availability. +- **Knowledge Gaps:** Some team members may lack experience with certain tools + or frameworks required for the project, leading to delays. ## Internal: Voluntary From 703615de765a8a7ad122d057f4aa8e566b2564c4 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:36:46 +0200 Subject: [PATCH 108/163] Updated Function and Test File --- solutions/tests/test_number_sort.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py index 314e39730..aac71a5db 100644 --- a/solutions/tests/test_number_sort.py +++ b/solutions/tests/test_number_sort.py @@ -8,14 +8,15 @@ - Defensive tests: invalid input types, assertions Created on 03-01-25 +Updated on 10-01-25 Author: Cody """ import unittest - -# When cloning the repo and running the test locally, the test file might fail -# Just type from solutions.number_sort import sort and the test will run -from ..number_sort import sort +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).parent.parent)) +from number_sort import sort class TestBubbleSort(unittest.TestCase): @@ -87,6 +88,24 @@ def test_large_numbers(self): expected = [-(10**6), 0, 10**6] self.assertEqual(result, expected) + def test_very_large_numbers(self): + """Test sorting numbers in the millions range.""" + result = sort([2000000, 1000000, 3000000]) + expected = [1000000, 2000000, 3000000] + self.assertEqual(result, expected) + + def test_very_small_numbers(self): + """Test sorting very small decimal numbers.""" + result = sort([0.0001, 0.0003, 0.0002]) + expected = [0.0001, 0.0002, 0.0003] + self.assertEqual(result, expected) + + def test_wide_range_numbers(self): + """Test sorting numbers with very different magnitudes.""" + result = sort([1000000, 0.001, 1, 10000]) + expected = [0.001, 1, 10000, 1000000] + self.assertEqual(result, expected) + if __name__ == "__main__": unittest.main() From c0c573d2a2bf8a3f9e9228ec09ca16ea8fe78a65 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:42:15 +0200 Subject: [PATCH 109/163] Updated Function and Test File --- solutions/tests/test_number_sort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py index aac71a5db..d0069ebb5 100644 --- a/solutions/tests/test_number_sort.py +++ b/solutions/tests/test_number_sort.py @@ -9,7 +9,7 @@ Created on 03-01-25 Updated on 10-01-25 -Author: Cody +Author: Cody (Reviewed By Abdulrahman) """ import unittest From 760098fb08e168fc2d80f9be82a4833fb34a05d2 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sat, 11 Jan 2025 14:49:58 +0200 Subject: [PATCH 110/163] Resolving formatting issues --- solutions/tests/test_number_sort.py | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/tests/test_number_sort.py b/solutions/tests/test_number_sort.py index d0069ebb5..f1cb6aea8 100644 --- a/solutions/tests/test_number_sort.py +++ b/solutions/tests/test_number_sort.py @@ -15,6 +15,7 @@ import unittest import sys from pathlib import Path + sys.path.append(str(Path(__file__).parent.parent)) from number_sort import sort From 857250a1042dedd1dbb213de36fc22cbf819a43e Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sat, 11 Jan 2025 21:36:42 +0200 Subject: [PATCH 111/163] Update README.md --- README.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5f59c8774..4ae0e136e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,58 @@ -# MIT Avatars 🌟 +# 🐍 MIT Avatars -**Collaborate. Innovate. Illuminate.** +Welcome to the **MIT Avatars** repository! This is where our group collaborates, learns, and grows together by solving Python challenges, sharing knowledge, and developing innovative coding solutions. -Welcome to the repository of **MIT Avatars**, representing -Group 17 in the MIT Emerging Talent Program (Foundations Track for Computer Science -and Data Science program). +--- + +## 👥 Meet the Team: + +We’re a group of passionate and diverse learners united by love for coding and problem-solving. Here's a brief introduction to our team members: + +- **Aseel 🎯** + *Fun Fact:* "I love playing Sudoku—it’s like a workout for my brain!" + +- **Ameen 🏉** + *Fun Fact:* "I am a professional rugby player. I play professional rugby nationally in Lebanon and represent Palestine internationally." + +- **Maria ✈️** + *Fun Fact:* "I love traveling as much as I love food – the only difference is, one fills my passport and the other fills my stomach!" + +- **Muhannad 🧑🏻‍💻** + *Fun Fact:* "When I feel lost in the maze of learning, I just hit the 'code' button because nothing finds the way like programming!" + +- **Maher 🛣️** + *Fun Fact:* "I really like playing rugby." + +- **Malak 🌳** + *Fun Fact:* "The city drains me; nature restores me." + +- **Abdulrahman 🕊** + *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, tech, and leadership in a unique way! I also love cats." + +--- + +## 📌 Purpose of this Repository: + +This repository is our collaborative space for: + +- 📝 **Sharing Python challenges:** Post interesting problems and work together on solutions. +- 💡 **Collaborating on innovative solutions:** Share diverse approaches and refine them. +- 📚 **Documenting our learning journey:** Note lessons learned and best practices. + +--- + +## 🚀 Getting Started: + +Here’s how to get involved and contribute: + +### 1️⃣ Fork the Repository +Click the **“Fork”** button at the top-right of this page to create your copy of the repository. + +### 2️⃣ Create an Issue +Navigate to the **Issues** tab and submit an issue describing the challenge. + +### 3️⃣ Work on the Challenge +Clone the repository to your local machine using: + +```bash +git clone https://github.com/YOUR-USERNAME/ET6-foundations-group-17.git From d8ee5a0dd3772029ad48ffc3e802688036616add Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:21:20 +0200 Subject: [PATCH 112/163] Add files via upload --- solutions/Tic-Tac-Toe (XO) Solution.py | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 solutions/Tic-Tac-Toe (XO) Solution.py diff --git a/solutions/Tic-Tac-Toe (XO) Solution.py b/solutions/Tic-Tac-Toe (XO) Solution.py new file mode 100644 index 000000000..20a8d3bc3 --- /dev/null +++ b/solutions/Tic-Tac-Toe (XO) Solution.py @@ -0,0 +1,69 @@ +def print_board(board): + for row in board: + print(" | ".join(row)) + print("-" * 9) + +def check_winner(board): + # Check rows + for row in board: + if row[0] == row[1] == row[2] and row[0] != " ": + return row[0] + + # Check columns + for col in range(3): + if board[0][col] == board[1][col] == board[2][col] and board[0][col] != " ": + return board[0][col] + + # Check diagonals + if board[0][0] == board[1][1] == board[2][2] and board[0][0] != " ": + return board[0][0] + if board[0][2] == board[1][1] == board[2][0] and board[0][2] != " ": + return board[0][2] + + return None + +def is_full(board): + return all(cell != " " for row in board for cell in row) + +def tic_tac_toe(): + board = [[" " for _ in range(3)] for _ in range(3)] + players = ["X", "O"] + turn = 0 + + print("Welcome to Tic-Tac-Toe!") + print_board(board) + + while True: + current_player = players[turn % 2] + print(f"Player {current_player}'s turn.") + + try: + row, col = map(int, input("Enter row and column (0, 1, or 2 separated by a space): ").split()) + except ValueError: + print("Invalid input. Please enter two numbers separated by a space.") + continue + + if not (0 <= row < 3 and 0 <= col < 3): + print("Invalid position. Please enter numbers between 0 and 2.") + continue + + if board[row][col] != " ": + print("Position already taken. Choose another.") + continue + + board[row][col] = current_player + print_board(board) + + winner = check_winner(board) + if winner: + print(f"Player {winner} wins!") + break + + if is_full(board): + print("It's a tie!") + break + + turn += 1 + +if __name__ == "__main__": + tic_tac_toe() From 3f1bf25ffbce0f0b51a128f6e2117d76acb0dba9 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:21:56 +0200 Subject: [PATCH 113/163] Add files via upload --- solutions/tests/Tic-Tac-Toe (XO) Test.py | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 solutions/tests/Tic-Tac-Toe (XO) Test.py diff --git a/solutions/tests/Tic-Tac-Toe (XO) Test.py b/solutions/tests/Tic-Tac-Toe (XO) Test.py new file mode 100644 index 000000000..5e52c2e1c --- /dev/null +++ b/solutions/tests/Tic-Tac-Toe (XO) Test.py @@ -0,0 +1,64 @@ +import unittest +from unittest.mock import patch +from io import StringIO + +# Assume the main Tic-Tac-Toe functions are imported from the main file +# Example: from tic_tac_toe_game import print_board, check_winner, is_full, tic_tac_toe + +class TestTicTacToe(unittest.TestCase): + def test_check_winner_rows(self): + board = [ + ["X", "X", "X"], + ["O", " ", "O"], + [" ", " ", " "] + ] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_columns(self): + board = [ + ["X", "O", " "], + ["X", "O", " "], + ["X", " ", " "] + ] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_diagonals(self): + board = [ + ["X", "O", "O"], + [" ", "X", " "], + [" ", " ", "X"] + ] + self.assertEqual(check_winner(board), "X") + + def test_is_full_true(self): + board = [ + ["X", "O", "X"], + ["O", "X", "O"], + ["O", "X", "O"] + ] + self.assertTrue(is_full(board)) + + def test_is_full_false(self): + board = [ + ["X", "O", "X"], + ["O", " ", "O"], + ["O", "X", "O"] + ] + self.assertFalse(is_full(board)) + + @patch('builtins.input', side_effect=["0 0", "0 1", "1 1", "0 2", "2 2"]) + @patch('sys.stdout', new_callable=StringIO) + def test_tic_tac_toe_win(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("Player X wins!", output) + + @patch('builtins.input', side_effect=["0 0", "0 1", "1 1", "2 1", "2 0", "2 2", "1 0", "1 2", "0 2"]) + @patch('sys.stdout', new_callable=StringIO) + def test_tic_tac_toe_tie(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("It's a tie!", output) + +if __name__ == "__main__": + unittest.main() From e98948ccc5dc92d48565cf34b928385af6c98358 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:23:49 +0200 Subject: [PATCH 114/163] Add files via upload --- .../Predcation of eletric cars solution.py | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 solutions/Predcation of eletric cars solution.py diff --git a/solutions/Predcation of eletric cars solution.py b/solutions/Predcation of eletric cars solution.py new file mode 100644 index 000000000..9fd62ca30 --- /dev/null +++ b/solutions/Predcation of eletric cars solution.py @@ -0,0 +1,157 @@ +# electric_car_prices_analysis.py + +def load_data(): + """ + Load a predefined electric car dataset. + + Returns: + pd.DataFrame: Loaded dataset as a Pandas DataFrame. + """ + import pandas as pd + from io import StringIO + + try: + # Embedded electric car dataset + data = """Make,Model,Year,BatteryCapacity_kWh,Range_km,Price +Tesla,Model 3,2021,75,450,50000 +Nissan,Leaf,2019,40,240,30000 +Chevrolet,Bolt EV,2020,66,380,37000 +Hyundai,Kona Electric,2021,64,415,40000 +Volkswagen,ID.4,2022,77,520,45000 +BMW,i3,2018,33,200,29000 +""" + return pd.read_csv(StringIO(data)) + except Exception as e: + print(f"Error loading data: {e}") + return pd.DataFrame() + +def preprocess_data(data): + """ + Preprocess the electric car dataset by handling missing values and encoding categorical variables. + """ + import pandas as pd + + try: + data = data.dropna() + # One-hot encode 'Make' and 'Model' + data = pd.get_dummies(data, columns=['Make', 'Model'], drop_first=True) + return data + except Exception as e: + print(f"Error preprocessing data: {e}") + return pd.DataFrame() + +def analyze_data(data): + """ + Perform exploratory data analysis on the dataset. + """ + import matplotlib.pyplot as plt + import seaborn as sns + + try: + print("Dataset Summary:") + print(data.describe()) + + sns.pairplot(data[['Year', 'BatteryCapacity_kWh', 'Range_km', 'Price']]) + plt.show() + except Exception as e: + print(f"Error in data analysis: {e}") + +def train_model(data): + """ + Train a predictive model using the dataset. + + Returns: + tuple: Trained model, test features, test labels + """ + from sklearn.model_selection import train_test_split + from sklearn.linear_model import LinearRegression + from sklearn.preprocessing import StandardScaler + + try: + features = data.drop(columns='Price') + target = data['Price'] + + X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42) + + # Scale the features + scaler = StandardScaler() + X_train = scaler.fit_transform(X_train) + X_test = scaler.transform(X_test) + + model = LinearRegression() + model.fit(X_train, y_train) + + return model, X_test, y_test, scaler + except Exception as e: + print(f"Error in model training: {e}") + return None, None, None, None + +def evaluate_model(model, X_test, y_test): + """ + Evaluate the trained model using Mean Squared Error and R-squared metrics. + """ + from sklearn.metrics import mean_squared_error, r2_score + + try: + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + print(f"Mean Squared Error: {mse}") + print(f"R-squared: {r2}") + except Exception as e: + print(f"Error in model evaluation: {e}") + +def visualize_results(model, X_test, y_test): + """ + Visualize the actual vs predicted prices and feature importance. + """ + import matplotlib.pyplot as plt + import numpy as np + + try: + # Actual vs Predicted Prices + y_pred = model.predict(X_test) + plt.figure(figsize=(10, 6)) + plt.scatter(y_test, y_pred, alpha=0.6, color='blue') + plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], '--r', linewidth=2) + plt.xlabel('Actual Prices') + plt.ylabel('Predicted Prices') + plt.title('Actual vs Predicted Prices') + plt.show() + + # Feature Coefficients + coefficients = model.coef_ + feature_names = ['Year', 'BatteryCapacity_kWh', 'Range_km'] + list(X_test.columns[3:]) + plt.figure(figsize=(8, 4)) + plt.bar(feature_names, coefficients, color='skyblue') + plt.title('Feature Coefficients') + plt.xticks(rotation=45) + plt.ylabel('Coefficient Value') + plt.show() + except Exception as e: + print(f"Error in visualization: {e}") + +def save_model(model, scaler, filename="electric_car_price_model.pkl"): + """ + Save the trained model and scaler to a file. + """ + import joblib + + try: + joblib.dump({'model': model, 'scaler': scaler}, filename) + print(f"Model saved as {filename}") + except Exception as e: + print(f"Error saving model: {e}") + +if _name_ == "_main_": + data = load_data() + if not data.empty: + data = preprocess_data(data) + analyze_data(data) + model, X_test, y_test, scaler = train_model(data) + if model: + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) + save_model(model, scaler) \ No newline at end of file From cd6938c9f21faf528eca6a7cb9143b86033b031c Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:24:23 +0200 Subject: [PATCH 115/163] Add files via upload --- solutions/tests/Muhannad 's Test File.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 solutions/tests/Muhannad 's Test File.py diff --git a/solutions/tests/Muhannad 's Test File.py b/solutions/tests/Muhannad 's Test File.py new file mode 100644 index 000000000..e69de29bb From ab6fde1c0a5c2eac670aa069461b924ec4413348 Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Sat, 11 Jan 2025 23:25:16 +0200 Subject: [PATCH 116/163] Add files via upload --- solutions/Chicken Nugget Fun- solution.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 solutions/Chicken Nugget Fun- solution.py diff --git a/solutions/Chicken Nugget Fun- solution.py b/solutions/Chicken Nugget Fun- solution.py new file mode 100644 index 000000000..9e4e7d66e --- /dev/null +++ b/solutions/Chicken Nugget Fun- solution.py @@ -0,0 +1,22 @@ +import random + +def chicken_nugget_fun(): + print("Welcome to the Chicken Nugget Fun Zone!") + + nugget_facts = [ + "Chicken nuggets were invented in the 1950s by Robert C. Baker!", + "The world record for eating chicken nuggets is 746 grams in 3 minutes!", + "McDonald's nuggets come in four shapes: bell, bow-tie, ball, and boot.", + "Try making homemade nuggets with chicken, breadcrumbs, and spices!", + "Some people dip chicken nuggets in honey—have you tried it?", + "Chicken nuggets are eaten by millions around the world every day!", + "Sweet chili sauce makes chicken nuggets extra tasty!", + "You can even make plant-based chicken nuggets now!" + ] + + random_fact = random.choice(nugget_facts) + print(f"Here's your nugget fun: {random_fact}") + +# Run the function +chicken_nugget_fun() + From a91012d058646f24b9650a21e50697abb0483886 Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Sat, 11 Jan 2025 23:25:48 +0200 Subject: [PATCH 117/163] Add files via upload --- solutions/Maria's soltuion.py | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 solutions/Maria's soltuion.py diff --git a/solutions/Maria's soltuion.py b/solutions/Maria's soltuion.py new file mode 100644 index 000000000..6da320fb3 --- /dev/null +++ b/solutions/Maria's soltuion.py @@ -0,0 +1,38 @@ +def minimize_chocolate_difference(chocolates, k): + """ + Function to distribute chocolates to minimize the difference between the maximum + and minimum chocolates received. + + Parameters: + chocolates (list): List of integers representing chocolates in packets. + k (int): Number of students. + + Returns: + int: The minimized difference between the maximum and minimum chocolates received. + """ + if k == 0 or len(chocolates) == 0: + return 0 + + if len(chocolates) < k: + return -1 # Not enough packets for the students + + # Sort the packets + chocolates.sort() + + # Initialize the minimum difference + min_diff = float("inf") + + # Find the smallest difference for a group of k packets + for i in range(len(chocolates) - k + 1): + diff = chocolates[i + k - 1] - chocolates[i] + min_diff = min(min_diff, diff) + + return min_diff + + +# Example usage +if _name_ == "_main_": + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + print(f"Minimum difference is {result}") From ae862d3b3a2f9290e9874c662112a55e1d162b40 Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Sat, 11 Jan 2025 23:26:13 +0200 Subject: [PATCH 118/163] Add files via upload --- solutions/tests/Maria's test.py | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 solutions/tests/Maria's test.py diff --git a/solutions/tests/Maria's test.py b/solutions/tests/Maria's test.py new file mode 100644 index 000000000..c6ba8d908 --- /dev/null +++ b/solutions/tests/Maria's test.py @@ -0,0 +1,50 @@ +import unittest +from chocolate_distribution import minimize_chocolate_difference + + +class TestMinimizeChocolateDifference(unittest.TestCase): + def test_valid_case(self): + """Test a valid case with enough packets and students.""" + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 10) + + def test_not_enough_packets(self): + """Test the case where there are fewer packets than students.""" + chocolates = [1, 2, 3] + k = 5 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, -1) + + def test_empty_chocolates_list(self): + """Test the case where the chocolates list is empty.""" + chocolates = [] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_zero_students(self): + """Test the case where the number of students is zero.""" + chocolates = [10, 20, 30] + k = 0 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_single_packet(self): + """Test the case with only one packet and one student.""" + chocolates = [42] + k = 1 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_minimized_difference(self): + """Test a generic case to validate the minimized difference calculation.""" + chocolates = [7, 3, 2, 4, 9, 12, 56] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 2) + + +if _name_ == "_main_": + unittest.main() From 9f97ea7be4e4adf08355c9712f8c668264f6c527 Mon Sep 17 00:00:00 2001 From: MaRia19280 Date: Sat, 11 Jan 2025 23:26:37 +0200 Subject: [PATCH 119/163] Add files via upload --- solutions/tests/Chicken Nugget Fun-Test.py | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 solutions/tests/Chicken Nugget Fun-Test.py diff --git a/solutions/tests/Chicken Nugget Fun-Test.py b/solutions/tests/Chicken Nugget Fun-Test.py new file mode 100644 index 000000000..388b75137 --- /dev/null +++ b/solutions/tests/Chicken Nugget Fun-Test.py @@ -0,0 +1,38 @@ +import unittest +from io import StringIO +import sys +import random + +# Assuming the function is in a file named 'chicken_nugget.py' +from chicken_nugget import chicken_nugget_fun + +class TestChickenNuggetFun(unittest.TestCase): + + def test_output(self): + # Backup original stdout + original_stdout = sys.stdout + sys.stdout = StringIO() + + # Run the function + chicken_nugget_fun() + + # Get the output + output = sys.stdout.getvalue() + + # Check if the output contains a nugget fact + self.assertTrue(any(fact in output for fact in [ + "Chicken nuggets were invented", + "The world record for eating chicken nuggets", + "McDonald's nuggets come in four shapes", + "Try making homemade nuggets", + "Some people dip chicken nuggets in honey", + "Chicken nuggets are eaten by millions", + "Sweet chili sauce makes chicken nuggets extra tasty", + "You can even make plant-based chicken nuggets" + ])) + + # Restore original stdout + sys.stdout = original_stdout + +if _name_ == '_main_': + unittest.main() \ No newline at end of file From 45b64a6b5df2c604fc0d3bf64e1672ccff41dc63 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sat, 11 Jan 2025 23:27:13 +0200 Subject: [PATCH 120/163] Add files via upload --- solutions/Travel Planner Fun-Solution.py | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 solutions/Travel Planner Fun-Solution.py diff --git a/solutions/Travel Planner Fun-Solution.py b/solutions/Travel Planner Fun-Solution.py new file mode 100644 index 000000000..712fd5e4a --- /dev/null +++ b/solutions/Travel Planner Fun-Solution.py @@ -0,0 +1,77 @@ +python +import random + +class TravelPlanner: + def __init__(self): + self.destinations = [ + "Paris, France", + "Kyoto, Japan", + "Machu Picchu, Peru", + "Cape Town, South Africa", + "New York City, USA", + "Santorini, Greece", + "Cairo, Egypt", + "Sydney, Australia", + "Reykjavik, Iceland", + "Bali, Indonesia" + ] + self.activities = [ + "exploring ancient ruins", + "dining at a Michelin-star restaurant", + "taking a hot air balloon ride", + "relaxing on a pristine beach", + "hiking through breathtaking trails", + "shopping in local markets", + "attending a cultural festival", + "snorkeling with vibrant marine life", + "enjoying a scenic train ride", + "visiting world-class museums" + ] + self.tips = [ + "Always pack a portable charger!", + "Learn a few phrases in the local language to impress locals.", + "Carry a reusable water bottle to stay hydrated.", + "Don’t forget travel insurance—it’s a lifesaver!", + "Try street food—it’s often the tastiest and cheapest option.", + "Keep a digital and physical copy of your passport.", + "Research local customs to avoid awkward situations.", + "Pack light—you’ll thank yourself later.", + "Wake up early to enjoy tourist spots without crowds.", + "Always have some local currency on hand for small purchases." + ] + + def generate_destination(self): + return random.choice(self.destinations) + + def generate_activity(self): + return random.choice(self.activities) + + def generate_travel_tip(self): + return random.choice(self.tips) + + def plan_trip(self): + destination = self.generate_destination() + activity = self.generate_activity() + tip = self.generate_travel_tip() + + return { + "destination": destination, + "activity": activity, + "tip": tip + } + +def main(): + print("Welcome to the Automated Travel Planner! \U0001F30D") + print("Sit back and relax while we plan your next dream trip.\n") + + planner = TravelPlanner() + trip = planner.plan_trip() + + print(f"\U0001F4CD Destination: {trip['destination']}") + print(f"\U0001F3C3 Activity: {trip['activity']}") + print(f"\U0001F4D6 Travel Tip: {trip['tip']}\n") + + print("Your virtual getaway is ready! Safe travels! \U0001F30F") + +if __name__ == "__main__": + main() From bbcd116b6412ca4513987263ab8fd368a46b16a6 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sat, 11 Jan 2025 23:28:08 +0200 Subject: [PATCH 121/163] Add files via upload --- solutions/Maher's soltuion.py | 108 ++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 solutions/Maher's soltuion.py diff --git a/solutions/Maher's soltuion.py b/solutions/Maher's soltuion.py new file mode 100644 index 000000000..14f64b5db --- /dev/null +++ b/solutions/Maher's soltuion.py @@ -0,0 +1,108 @@ +# housing_prices_analysis_solution.py + +def load_data(): + """ + Load a predefined housing dataset. + + Returns: + pd.DataFrame: Loaded dataset as a Pandas DataFrame. + """ + import pandas as pd + from io import StringIO + + # Embedded dataset + data = """SquareFeet,Bedrooms,Bathrooms,YearBuilt,LocationScore,Price +1500,3,2,2000,85,300000 +2000,4,3,2010,90,450000 +1800,3,2,2005,88,350000 +2400,4,3,2020,92,500000 +1600,3,2,1995,80,280000 +1200,2,1,1980,70,200000 +""" + return pd.read_csv(StringIO(data)) + +def preprocess_data(data): + """ + Preprocess the housing dataset by handling missing values and extracting necessary features. + """ + return data.dropna() + +def analyze_data(data): + """ + Perform exploratory data analysis on the dataset. + """ + import matplotlib.pyplot as plt + import seaborn as sns + + print("Dataset Summary:") + print(data.describe()) + + sns.pairplot(data[['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore', 'Price']]) + plt.show() + +def train_model(data): + """ + Train a predictive model using the dataset. + """ + from sklearn.model_selection import train_test_split + from sklearn.linear_model import LinearRegression + + features = ['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore'] + target = 'Price' + + X = data[features] + y = data[target] + + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) + + model = LinearRegression() + model.fit(X_train, y_train) + + return model, X_test, y_test + +def evaluate_model(model, X_test, y_test): + """ + Evaluate the trained model using Mean Squared Error and R-squared metrics. + """ + from sklearn.metrics import mean_squared_error, r2_score + + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + print(f"Mean Squared Error: {mse}") + print(f"R-squared: {r2}") + +def visualize_results(model, X_test, y_test): + """ + Visualize the actual vs predicted prices and feature importance. + """ + import matplotlib.pyplot as plt + import pandas as pd + + # Actual vs Predicted Prices + y_pred = model.predict(X_test) + plt.figure(figsize=(10, 6)) + plt.scatter(y_test, y_pred, alpha=0.6, color='blue') + plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], '--r', linewidth=2) + plt.xlabel('Actual Prices') + plt.ylabel('Predicted Prices') + plt.title('Actual vs Predicted Prices') + plt.show() + + # Feature Coefficients + coefficients = pd.Series(model.coef_, index=X_test.columns) + plt.figure(figsize=(8, 4)) + coefficients.plot(kind='bar', color='skyblue') + plt.title('Feature Coefficients') + plt.ylabel('Coefficient Value') + plt.show() + +if _name_ == "_main_": + data = load_data() + data = preprocess_data(data) + analyze_data(data) + model, X_test, y_test = train_model(data) + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) \ No newline at end of file From 0019d006bc650c0307397c6b6f9264ae86ea6ca6 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sat, 11 Jan 2025 23:28:35 +0200 Subject: [PATCH 122/163] Add files via upload --- solutions/tests/Travel Planner Fun-Test.py | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 solutions/tests/Travel Planner Fun-Test.py diff --git a/solutions/tests/Travel Planner Fun-Test.py b/solutions/tests/Travel Planner Fun-Test.py new file mode 100644 index 000000000..6d34cf8ae --- /dev/null +++ b/solutions/tests/Travel Planner Fun-Test.py @@ -0,0 +1,27 @@ +import unittest +from travel_planner import TravelPlanner # Replace travel_planner with the name of your script file. + +class TestTravelPlanner(unittest.TestCase): + def setUp(self): + self.planner = TravelPlanner() + + def test_generate_destination(self): + destination = self.planner.generate_destination() + self.assertIn(destination, self.planner.destinations) + + def test_generate_activity(self): + activity = self.planner.generate_activity() + self.assertIn(activity, self.planner.activities) + + def test_generate_travel_tip(self): + tip = self.planner.generate_travel_tip() + self.assertIn(tip, self.planner.tips) + + def test_plan_trip(self): + trip = self.planner.plan_trip() + self.assertIn(trip["destination"], self.planner.destinations) + self.assertIn(trip["activity"], self.planner.activities) + self.assertIn(trip["tip"], self.planner.tips) + +if _name_ == "_main_": + unittest.main() \ No newline at end of file From 05db6971911fea7e952873c0a98ba60c15a666ba Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sat, 11 Jan 2025 23:29:55 +0200 Subject: [PATCH 123/163] Add files via upload --- solutions/tests/Maher's Test.py | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 solutions/tests/Maher's Test.py diff --git a/solutions/tests/Maher's Test.py b/solutions/tests/Maher's Test.py new file mode 100644 index 000000000..9ba8597b0 --- /dev/null +++ b/solutions/tests/Maher's Test.py @@ -0,0 +1,90 @@ +import unittest +import pandas as pd +from io import StringIO +from sklearn.linear_model import LinearRegression +from sklearn.metrics import mean_squared_error, r2_score +import matplotlib.pyplot as plt + +# Assuming the module is named housing_analysis and the functions are imported +from housing_analysis import ( + load_data, preprocess_data, analyze_data, train_model, evaluate_model, visualize_results +) + +class TestHousingAnalysis(unittest.TestCase): + + def setUp(self): + """Set up the dataset for testing.""" + self.data = load_data() + + def test_load_data(self): + """Test if the data loads correctly.""" + self.assertIsInstance(self.data, pd.DataFrame) + self.assertEqual(self.data.shape[0], 6) # Should have 6 rows + self.assertEqual(self.data.shape[1], 6) # Should have 6 columns + + # Verify column names + expected_columns = ['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore', 'Price'] + self.assertListEqual(list(self.data.columns), expected_columns) + + def test_preprocess_data(self): + """Test if preprocessing removes missing values.""" + processed_data = preprocess_data(self.data) + self.assertFalse(processed_data.isnull().values.any()) + self.assertEqual(processed_data.shape, self.data.shape) # No missing values in the initial dataset + + def test_analyze_data(self): + """Test if analysis does not throw errors.""" + try: + analyze_data(self.data) + except Exception as e: + self.fail(f"Analyze data failed with error: {e}") + + def test_train_model(self): + """Test if the model trains correctly.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + + self.assertIsInstance(model, LinearRegression) + self.assertGreater(len(X_test), 0) # Ensure test set is not empty + self.assertGreater(len(y_test), 0) + + # Check that the model coefficients are not all zeros + self.assertTrue(any(model.coef_ != 0), "Model coefficients should not all be zero.") + + def test_evaluate_model(self): + """Test if model evaluation metrics are reasonable.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + self.assertGreaterEqual(mse, 0, "Mean Squared Error should be non-negative.") + self.assertGreaterEqual(r2, -1, "R-squared should be greater than or equal to -1.") + self.assertLessEqual(r2, 1, "R-squared should be less than or equal to 1.") + + def test_visualize_results(self): + """Test if visualization functions run without errors.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + + try: + visualize_results(model, X_test, y_test) + except Exception as e: + self.fail(f"Visualization failed with error: {e}") + + def test_full_pipeline(self): + """Test the full pipeline from data loading to visualization.""" + try: + data = load_data() + processed_data = preprocess_data(data) + analyze_data(processed_data) + model, X_test, y_test = train_model(processed_data) + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) + except Exception as e: + self.fail(f"Full pipeline failed with error: {e}") + +if _name_ == "_main_": + unittest.main() \ No newline at end of file From 1d9b9d51086a9dbce545b06ed1ceb8012547860e Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sat, 11 Jan 2025 23:47:13 +0200 Subject: [PATCH 124/163] Update README.md --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4ae0e136e..a4be76b94 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Welcome to the **MIT Avatars** repository! This is where our group collaborates, --- -## 👥 Meet the Team: +## 👥 Meet the Team -We’re a group of passionate and diverse learners united by love for coding and problem-solving. Here's a brief introduction to our team members: +We’re a group of passionate and diverse learners united by a love for coding and problem-solving. Here's a brief introduction to our team members: - **Aseel 🎯** *Fun Fact:* "I love playing Sudoku—it’s like a workout for my brain!" @@ -31,28 +31,83 @@ We’re a group of passionate and diverse learners united by love for coding and --- -## 📌 Purpose of this Repository: +## 📌 Purpose of this Repository This repository is our collaborative space for: -- 📝 **Sharing Python challenges:** Post interesting problems and work together on solutions. -- 💡 **Collaborating on innovative solutions:** Share diverse approaches and refine them. +- 📝 **Sharing Python challenges:** Post interesting problems and work together on solutions. +- 💡 **Collaborating on innovative solutions:** Share diverse approaches and refine them. - 📚 **Documenting our learning journey:** Note lessons learned and best practices. --- -## 🚀 Getting Started: +## 🚀 Getting Started Here’s how to get involved and contribute: -### 1️⃣ Fork the Repository -Click the **“Fork”** button at the top-right of this page to create your copy of the repository. +### 1️⃣ Fork the Repository + +Click the **Fork** button at the top-right of this page to create your copy of the repository. + +### 2️⃣ Create an Issue -### 2️⃣ Create an Issue Navigate to the **Issues** tab and submit an issue describing the challenge. -### 3️⃣ Work on the Challenge +### 3️⃣ Work on the Challenge + Clone the repository to your local machine using: ```bash git clone https://github.com/YOUR-USERNAME/ET6-foundations-group-17.git +``` + +Create a new branch for your work: + +```bash +git checkout -b feature/your-feature-name +``` + +### 4️⃣ Submit Your Work + +Push your changes to the branch: + +```bash +git add . +git commit -m "Description of changes" +git push origin feature/your-feature-name +``` + +Finally, open a pull request and wait for feedback from your team. + +--- + +## 🌟 Code of Conduct + +To foster a welcoming and collaborative environment: + +- Be respectful and constructive in your feedback. +- Share knowledge and support each other. +- Embrace diversity in ideas and approaches. + +--- + +## 🛠️ Tech Stack + +This repository uses: + +- **Python:** Our primary programming language for solving challenges. +- **Git/GitHub:** For version control and collaboration. + +--- + +## 🎯 Goals + +As the MIT Avatars, we aim to: + +- Strengthen our problem-solving skills. +- Enhance our Python proficiency. +- Build a supportive and creative learning community. +- Make it to the MIT program as a team. + +🔗 **Group Repository:** +Check out our repository: [MIT-Emerging-Talent/ET6-foundations-group-17](https://github.com/MIT-Emerging-Talent/ET6-foundations-group-17) From 55ec78a9209f9bfb12e8489ae52314524c8b2acb Mon Sep 17 00:00:00 2001 From: Aseel AbuKmail Date: Sat, 11 Jan 2025 23:55:09 +0200 Subject: [PATCH 125/163] fix md_formatting on README.md --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a4be76b94..4f24de5dc 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,30 @@ # 🐍 MIT Avatars -Welcome to the **MIT Avatars** repository! This is where our group collaborates, learns, and grows together by solving Python challenges, sharing knowledge, and developing innovative coding solutions. +Welcome to the **MIT Avatars** repository! This is where our group collaborates, +learns, and grows together by solving Python challenges, sharing knowledge, and +developing innovative coding solutions. --- ## 👥 Meet the Team -We’re a group of passionate and diverse learners united by a love for coding and problem-solving. Here's a brief introduction to our team members: +We’re a group of passionate and diverse learners united by a love for coding and +problem-solving. Here's a brief introduction to our team members: - **Aseel 🎯** *Fun Fact:* "I love playing Sudoku—it’s like a workout for my brain!" - **Ameen 🏉** - *Fun Fact:* "I am a professional rugby player. I play professional rugby nationally in Lebanon and represent Palestine internationally." + *Fun Fact:* "I am a professional rugby player. I play professional rugby nationally + in Lebanon and represent Palestine internationally." - **Maria ✈️** - *Fun Fact:* "I love traveling as much as I love food – the only difference is, one fills my passport and the other fills my stomach!" + *Fun Fact:* "I love traveling as much as I love food – the only difference is, one + fills my passport and the other fills my stomach!" - **Muhannad 🧑🏻‍💻** - *Fun Fact:* "When I feel lost in the maze of learning, I just hit the 'code' button because nothing finds the way like programming!" + *Fun Fact:* "When I feel lost in the maze of learning, I just hit the 'code' button + because nothing finds the way like programming!" - **Maher 🛣️** *Fun Fact:* "I really like playing rugby." @@ -27,7 +33,8 @@ We’re a group of passionate and diverse learners united by a love for coding a *Fun Fact:* "The city drains me; nature restores me." - **Abdulrahman 🕊** - *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, tech, and leadership in a unique way! I also love cats." + *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, tech, + and leadership in a unique way! I also love cats." --- @@ -35,8 +42,10 @@ We’re a group of passionate and diverse learners united by a love for coding a This repository is our collaborative space for: -- 📝 **Sharing Python challenges:** Post interesting problems and work together on solutions. -- 💡 **Collaborating on innovative solutions:** Share diverse approaches and refine them. +- 📝 **Sharing Python challenges:** Post interesting problems and work together + on solutions. +- 💡 **Collaborating on innovative solutions:** Share diverse approaches and + refine them. - 📚 **Documenting our learning journey:** Note lessons learned and best practices. --- @@ -47,7 +56,8 @@ Here’s how to get involved and contribute: ### 1️⃣ Fork the Repository -Click the **Fork** button at the top-right of this page to create your copy of the repository. +Click the **Fork** button at the top-right of this page to create your copy of +the repository. ### 2️⃣ Create an Issue @@ -110,4 +120,5 @@ As the MIT Avatars, we aim to: - Make it to the MIT program as a team. 🔗 **Group Repository:** -Check out our repository: [MIT-Emerging-Talent/ET6-foundations-group-17](https://github.com/MIT-Emerging-Talent/ET6-foundations-group-17) +Check out our repository: +[MIT-Emerging-Talent/ET6-foundations-group-17](https://github.com/MIT-Emerging-Talent/ET6-foundations-group-17) From 9565108444a90126c7cf4a1940e0a9126ab7b433 Mon Sep 17 00:00:00 2001 From: Malak Date: Sun, 12 Jan 2025 04:16:12 +0200 Subject: [PATCH 126/163] updated the readme file along with other files --- README.md | 16 ++++++++------ assets/team-work.jpg | Bin 0 -> 63538 bytes solutions/Travel Planner Fun-Solution.py | 26 +++++++++++------------ solutions/mirror_words_challenge.py | 6 +++--- solutions/rock_paper_scissor.py | 2 +- solutions/tests/Muhannad 's Test File.py | 0 6 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 assets/team-work.jpg delete mode 100644 solutions/tests/Muhannad 's Test File.py diff --git a/README.md b/README.md index 4f24de5dc..15f210b05 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# 🐍 MIT Avatars +# MIT Avatars + +![Team Work](../ET6-foundations-group-17/assets/team-work.jpg) Welcome to the **MIT Avatars** repository! This is where our group collaborates, learns, and grows together by solving Python challenges, sharing knowledge, and @@ -19,8 +21,8 @@ problem-solving. Here's a brief introduction to our team members: in Lebanon and represent Palestine internationally." - **Maria ✈️** - *Fun Fact:* "I love traveling as much as I love food – the only difference is, one - fills my passport and the other fills my stomach!" + *Fun Fact:* "I love traveling as much as I love food – the only difference + is, one fills my passport and the other fills my stomach!" - **Muhannad 🧑🏻‍💻** *Fun Fact:* "When I feel lost in the maze of learning, I just hit the 'code' button @@ -29,12 +31,12 @@ problem-solving. Here's a brief introduction to our team members: - **Maher 🛣️** *Fun Fact:* "I really like playing rugby." -- **Malak 🌳** - *Fun Fact:* "The city drains me; nature restores me." +- **Malak ❇️** + *Fun Fact:* "I look for purpose in almost everything I do." - **Abdulrahman 🕊** - *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, tech, - and leadership in a unique way! I also love cats." + *Fun Fact:* "I'm a medical student who codes and leads teams—blending science, + tech, and leadership in a unique way! I also love cats." --- diff --git a/assets/team-work.jpg b/assets/team-work.jpg new file mode 100644 index 0000000000000000000000000000000000000000..83f556c5661598e4e7f3cb24c730ae582ac897dd GIT binary patch literal 63538 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 literal 0 HcmV?d00001 diff --git a/solutions/Travel Planner Fun-Solution.py b/solutions/Travel Planner Fun-Solution.py index 712fd5e4a..6f277b21b 100644 --- a/solutions/Travel Planner Fun-Solution.py +++ b/solutions/Travel Planner Fun-Solution.py @@ -1,6 +1,6 @@ -python import random + class TravelPlanner: def __init__(self): self.destinations = [ @@ -13,7 +13,7 @@ def __init__(self): "Cairo, Egypt", "Sydney, Australia", "Reykjavik, Iceland", - "Bali, Indonesia" + "Bali, Indonesia", ] self.activities = [ "exploring ancient ruins", @@ -25,7 +25,7 @@ def __init__(self): "attending a cultural festival", "snorkeling with vibrant marine life", "enjoying a scenic train ride", - "visiting world-class museums" + "visiting world-class museums", ] self.tips = [ "Always pack a portable charger!", @@ -37,7 +37,7 @@ def __init__(self): "Research local customs to avoid awkward situations.", "Pack light—you’ll thank yourself later.", "Wake up early to enjoy tourist spots without crowds.", - "Always have some local currency on hand for small purchases." + "Always have some local currency on hand for small purchases.", ] def generate_destination(self): @@ -54,24 +54,22 @@ def plan_trip(self): activity = self.generate_activity() tip = self.generate_travel_tip() - return { - "destination": destination, - "activity": activity, - "tip": tip - } + return {"destination": destination, "activity": activity, "tip": tip} + def main(): - print("Welcome to the Automated Travel Planner! \U0001F30D") + print("Welcome to the Automated Travel Planner! \U0001f30d") print("Sit back and relax while we plan your next dream trip.\n") planner = TravelPlanner() trip = planner.plan_trip() - print(f"\U0001F4CD Destination: {trip['destination']}") - print(f"\U0001F3C3 Activity: {trip['activity']}") - print(f"\U0001F4D6 Travel Tip: {trip['tip']}\n") + print(f"\U0001f4cd Destination: {trip['destination']}") + print(f"\U0001f3c3 Activity: {trip['activity']}") + print(f"\U0001f4d6 Travel Tip: {trip['tip']}\n") + + print("Your virtual getaway is ready! Safe travels! \U0001f30f") - print("Your virtual getaway is ready! Safe travels! \U0001F30F") if __name__ == "__main__": main() diff --git a/solutions/mirror_words_challenge.py b/solutions/mirror_words_challenge.py index d27c816cc..bd81759eb 100644 --- a/solutions/mirror_words_challenge.py +++ b/solutions/mirror_words_challenge.py @@ -9,10 +9,10 @@ def mirror_words(sentence: str) -> str: Args: sentence (str): The sentence to be processed. It can contain words, punctuation, - spaces, numbers, and special characters. + spaces, numbers, and special characters. Returns: str: A new string where each word is reversed, but the word order, punctuation, - numbers, and special characters remain unchanged. + numbers, and special characters remain unchanged. Example: >>> mirror_words("Hello world!") @@ -21,7 +21,7 @@ def mirror_words(sentence: str) -> str: >>> mirror_words("Python is fun") 'nohtyP si nuf' - >>> mirror_words("Keep calm & code on.") + >>> mirror_words("Keep calm & code on.") 'peeK mlac & edoc .no' Raises: diff --git a/solutions/rock_paper_scissor.py b/solutions/rock_paper_scissor.py index 8af5d4617..eda534b2b 100644 --- a/solutions/rock_paper_scissor.py +++ b/solutions/rock_paper_scissor.py @@ -3,7 +3,7 @@ Module contents: - play_round: Simulates a round of Rock-Paper-Scissors where the user competes - against the computer. + against the computer. Created on 31-12-24 Updated on 09-01-25 diff --git a/solutions/tests/Muhannad 's Test File.py b/solutions/tests/Muhannad 's Test File.py deleted file mode 100644 index e69de29bb..000000000 From 109e79d48e8b808ab9a5cc4352f051ae5d9fcd5f Mon Sep 17 00:00:00 2001 From: Malak Date: Sun, 12 Jan 2025 04:21:03 +0200 Subject: [PATCH 127/163] fixed team-work pic path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15f210b05..2c2a7c791 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MIT Avatars -![Team Work](../ET6-foundations-group-17/assets/team-work.jpg) +![Team Work](assets/team-work.jpg) Welcome to the **MIT Avatars** repository! This is where our group collaborates, learns, and grows together by solving Python challenges, sharing knowledge, and From 40d1bb21167cbdba9deb13ef333ab14b4d8a72c0 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:48:23 +0200 Subject: [PATCH 128/163] Adding a Note --- notes/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/notes/README.md b/notes/README.md index 17e0f0ded..bc2b24b2d 100644 --- a/notes/README.md +++ b/notes/README.md @@ -1 +1,8 @@ # Notes + +## Abdulrahman + +- For the number sort challenge, I uploaded the file via file upload because I +was facing electricity shortage and I had to use my phone to upload the file. +- All the recent updates were done via CLI since the power is back and I have +access to a laptop right now. \ No newline at end of file From d8f9f88f520553010123cf84fa535f24550255fd Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:53:41 +0200 Subject: [PATCH 129/163] Fix formatting issue in README.md --- notes/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notes/README.md b/notes/README.md index bc2b24b2d..94ee50994 100644 --- a/notes/README.md +++ b/notes/README.md @@ -1,8 +1,8 @@ # Notes -## Abdulrahman +## Abdulrahman Ali - For the number sort challenge, I uploaded the file via file upload because I was facing electricity shortage and I had to use my phone to upload the file. -- All the recent updates were done via CLI since the power is back and I have -access to a laptop right now. \ No newline at end of file +- All the recent updates were done via CLI since the power is partially back and +I have access to a laptop right now. From 56be8bc4c5709be69997da5540e1692249ae472a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:57:46 +0200 Subject: [PATCH 130/163] Fix formatting in README.md --- notes/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/notes/README.md b/notes/README.md index 94ee50994..c8b5e64b1 100644 --- a/notes/README.md +++ b/notes/README.md @@ -6,3 +6,5 @@ was facing electricity shortage and I had to use my phone to upload the file. - All the recent updates were done via CLI since the power is partially back and I have access to a laptop right now. +- The same goes for the other challenges I uploaded via file upload and when I +got my laptop back I updated the files via CLI. From 9c816659e6237ba4911fde077378e32446775215 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:55:55 +0200 Subject: [PATCH 131/163] Update Maher's soltuion.py --- solutions/Maher's soltuion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/Maher's soltuion.py b/solutions/Maher's soltuion.py index 14f64b5db..cda0b739b 100644 --- a/solutions/Maher's soltuion.py +++ b/solutions/Maher's soltuion.py @@ -99,10 +99,10 @@ def visualize_results(model, X_test, y_test): plt.ylabel('Coefficient Value') plt.show() -if _name_ == "_main_": +if __name__ == "__main__": data = load_data() data = preprocess_data(data) analyze_data(data) model, X_test, y_test = train_model(data) evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) \ No newline at end of file + visualize_results(model, X_test, y_test) From b08730943c4635bb1fcd8056b022f28605041cff Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:57:19 +0200 Subject: [PATCH 132/163] Update Maria's soltuion.py --- solutions/Maria's soltuion.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/Maria's soltuion.py b/solutions/Maria's soltuion.py index 6da320fb3..3be8d8549 100644 --- a/solutions/Maria's soltuion.py +++ b/solutions/Maria's soltuion.py @@ -31,8 +31,8 @@ def minimize_chocolate_difference(chocolates, k): # Example usage -if _name_ == "_main_": - chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] +if __name__ == "__main__": +chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] k = 7 result = minimize_chocolate_difference(chocolates, k) print(f"Minimum difference is {result}") From 01bd640d539d93a08fa3907038f2438de1e225da Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:00:15 +0200 Subject: [PATCH 133/163] Update Predcation of eletric cars solution.py --- solutions/Predcation of eletric cars solution.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/solutions/Predcation of eletric cars solution.py b/solutions/Predcation of eletric cars solution.py index 9fd62ca30..7f3bedfb2 100644 --- a/solutions/Predcation of eletric cars solution.py +++ b/solutions/Predcation of eletric cars solution.py @@ -108,7 +108,8 @@ def visualize_results(model, X_test, y_test): Visualize the actual vs predicted prices and feature importance. """ import matplotlib.pyplot as plt - import numpy as np + import numpy + try: # Actual vs Predicted Prices @@ -144,9 +145,8 @@ def save_model(model, scaler, filename="electric_car_price_model.pkl"): print(f"Model saved as {filename}") except Exception as e: print(f"Error saving model: {e}") - -if _name_ == "_main_": - data = load_data() + if __name__ == "__main__": +data = load_data() if not data.empty: data = preprocess_data(data) analyze_data(data) @@ -154,4 +154,4 @@ def save_model(model, scaler, filename="electric_car_price_model.pkl"): if model: evaluate_model(model, X_test, y_test) visualize_results(model, X_test, y_test) - save_model(model, scaler) \ No newline at end of file + save_model(model, scaler) From edd5b18b1eefa57612b5e8bc2a022ade36395c9e Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:03:15 +0200 Subject: [PATCH 134/163] Update Chicken Nugget Fun-Test.py --- solutions/tests/Chicken Nugget Fun-Test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solutions/tests/Chicken Nugget Fun-Test.py b/solutions/tests/Chicken Nugget Fun-Test.py index 388b75137..fde79a111 100644 --- a/solutions/tests/Chicken Nugget Fun-Test.py +++ b/solutions/tests/Chicken Nugget Fun-Test.py @@ -1,7 +1,6 @@ import unittest from io import StringIO import sys -import random # Assuming the function is in a file named 'chicken_nugget.py' from chicken_nugget import chicken_nugget_fun @@ -35,4 +34,4 @@ def test_output(self): sys.stdout = original_stdout if _name_ == '_main_': - unittest.main() \ No newline at end of file + unittest.main() From 40b3d184c0a98b26f0a693d5a529bda5e9d6d049 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:04:11 +0200 Subject: [PATCH 135/163] Update Predcation of eletric cars solution.py --- solutions/Predcation of eletric cars solution.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/solutions/Predcation of eletric cars solution.py b/solutions/Predcation of eletric cars solution.py index 7f3bedfb2..f598c283f 100644 --- a/solutions/Predcation of eletric cars solution.py +++ b/solutions/Predcation of eletric cars solution.py @@ -108,9 +108,6 @@ def visualize_results(model, X_test, y_test): Visualize the actual vs predicted prices and feature importance. """ import matplotlib.pyplot as plt - import numpy - - try: # Actual vs Predicted Prices y_pred = model.predict(X_test) From ad3ee5bf9df7fdcfd6763124ade4ce00966937cd Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:07:53 +0200 Subject: [PATCH 136/163] Update Chicken Nugget Fun-Test.py --- solutions/tests/Chicken Nugget Fun-Test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/tests/Chicken Nugget Fun-Test.py b/solutions/tests/Chicken Nugget Fun-Test.py index fde79a111..61a5eaa3c 100644 --- a/solutions/tests/Chicken Nugget Fun-Test.py +++ b/solutions/tests/Chicken Nugget Fun-Test.py @@ -33,5 +33,5 @@ def test_output(self): # Restore original stdout sys.stdout = original_stdout -if _name_ == '_main_': - unittest.main() +if __name__ == "__main__": +unittest.main() From e9476e165f93befed55694305f3255c5e3a67d82 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:09:02 +0200 Subject: [PATCH 137/163] Update Maher's Test.py --- solutions/tests/Maher's Test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solutions/tests/Maher's Test.py b/solutions/tests/Maher's Test.py index 9ba8597b0..be5d74a1f 100644 --- a/solutions/tests/Maher's Test.py +++ b/solutions/tests/Maher's Test.py @@ -1,6 +1,5 @@ import unittest import pandas as pd -from io import StringIO from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score import matplotlib.pyplot as plt @@ -87,4 +86,4 @@ def test_full_pipeline(self): self.fail(f"Full pipeline failed with error: {e}") if _name_ == "_main_": - unittest.main() \ No newline at end of file + unittest.main() From bcaf9d547be453a5e617ef848385251b4ef9e356 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:09:05 +0200 Subject: [PATCH 138/163] Fix linting and Formatting issues in solution and test files --- solutions/chicken_nugget_fun_solution.py | 19 ++ solutions/chocolate_distribution.py | 38 ++++ solutions/housing_prices_analysis_solution.py | 130 +++++++++++++ .../predcation_of_electric_cars_solution.py | 173 ++++++++++++++++++ solutions/tests/test_chicken_nugget_fun.py | 48 +++++ .../tests/test_chocolate_distribution.py | 54 ++++++ .../tests/test_housing_prices_analysis.py | 112 ++++++++++++ solutions/tests/test_tic_tac_toe_game.py | 53 ++++++ solutions/tests/test_travel_planner_fun.py | 33 ++++ solutions/tic_tac_toe_game.py | 78 ++++++++ solutions/travel_planner_fun_solution.py | 75 ++++++++ 11 files changed, 813 insertions(+) create mode 100644 solutions/chicken_nugget_fun_solution.py create mode 100644 solutions/chocolate_distribution.py create mode 100644 solutions/housing_prices_analysis_solution.py create mode 100644 solutions/predcation_of_electric_cars_solution.py create mode 100644 solutions/tests/test_chicken_nugget_fun.py create mode 100644 solutions/tests/test_chocolate_distribution.py create mode 100644 solutions/tests/test_housing_prices_analysis.py create mode 100644 solutions/tests/test_tic_tac_toe_game.py create mode 100644 solutions/tests/test_travel_planner_fun.py create mode 100644 solutions/tic_tac_toe_game.py create mode 100644 solutions/travel_planner_fun_solution.py diff --git a/solutions/chicken_nugget_fun_solution.py b/solutions/chicken_nugget_fun_solution.py new file mode 100644 index 000000000..cfba29f48 --- /dev/null +++ b/solutions/chicken_nugget_fun_solution.py @@ -0,0 +1,19 @@ +import random + + +def chicken_nugget_fun(): + print("Welcome to the Chicken Nugget Fun Zone!") + + nugget_facts = [ + "Chicken nuggets were invented in the 1950s by Robert C. Baker!", + "The world record for eating chicken nuggets is 746 grams in 3 minutes!", + "McDonald's nuggets come in four shapes: bell, bow-tie, ball, and boot.", + "Try making homemade nuggets with chicken, breadcrumbs, and spices!", + "Some people dip chicken nuggets in honey—have you tried it?", + "Chicken nuggets are eaten by millions around the world every day!", + "Sweet chili sauce makes chicken nuggets extra tasty!", + "You can even make plant-based chicken nuggets now!", + ] + + random_fact = random.choice(nugget_facts) + print(f"Here's your nugget fun: {random_fact}") diff --git a/solutions/chocolate_distribution.py b/solutions/chocolate_distribution.py new file mode 100644 index 000000000..d4cbe0761 --- /dev/null +++ b/solutions/chocolate_distribution.py @@ -0,0 +1,38 @@ +def minimize_chocolate_difference(chocolates, k): + """ + Function to distribute chocolates to minimize the difference between the maximum + and minimum chocolates received. + + Parameters: + chocolates (list): List of integers representing chocolates in packets. + k (int): Number of students. + + Returns: + int: The minimized difference between the maximum and minimum chocolates received. + """ + if k == 0 or len(chocolates) == 0: + return 0 + + if len(chocolates) < k: + return -1 # Not enough packets for the students + + # Sort the packets + chocolates.sort() + + # Initialize the minimum difference + min_diff = float("inf") + + # Find the smallest difference for a group of k packets + for i in range(len(chocolates) - k + 1): + diff = chocolates[i + k - 1] - chocolates[i] + min_diff = min(min_diff, diff) + + return min_diff + + +# Example usage +if __name__ == "__main__": + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + print(f"Minimum difference is {result}") diff --git a/solutions/housing_prices_analysis_solution.py b/solutions/housing_prices_analysis_solution.py new file mode 100644 index 000000000..9d5bd5da5 --- /dev/null +++ b/solutions/housing_prices_analysis_solution.py @@ -0,0 +1,130 @@ +# housing_prices_analysis_solution.py + + +def load_data(): + """ + Load a predefined housing dataset. + + Returns: + pd.DataFrame: Loaded dataset as a Pandas DataFrame. + """ + import pandas as pd + from io import StringIO + + # Embedded dataset + data = """SquareFeet,Bedrooms,Bathrooms,YearBuilt,LocationScore,Price +1500,3,2,2000,85,300000 +2000,4,3,2010,90,450000 +1800,3,2,2005,88,350000 +2400,4,3,2020,92,500000 +1600,3,2,1995,80,280000 +1200,2,1,1980,70,200000 +""" + return pd.read_csv(StringIO(data)) + + +def preprocess_data(data): + """ + Preprocess the housing dataset by handling missing values and extracting necessary features. + """ + return data.dropna() + + +def analyze_data(data): + """ + Perform exploratory data analysis on the dataset. + """ + import matplotlib.pyplot as plt + import seaborn as sns + + print("Dataset Summary:") + print(data.describe()) + + sns.pairplot( + data[ + [ + "SquareFeet", + "Bedrooms", + "Bathrooms", + "YearBuilt", + "LocationScore", + "Price", + ] + ] + ) + plt.show() + + +def train_model(data): + """ + Train a predictive model using the dataset. + """ + from sklearn.model_selection import train_test_split + from sklearn.linear_model import LinearRegression + + features = ["SquareFeet", "Bedrooms", "Bathrooms", "YearBuilt", "LocationScore"] + target = "Price" + + X = data[features] + y = data[target] + + X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=0.2, random_state=42 + ) + + model = LinearRegression() + model.fit(X_train, y_train) + + return model, X_test, y_test + + +def evaluate_model(model, X_test, y_test): + """ + Evaluate the trained model using Mean Squared Error and R-squared metrics. + """ + from sklearn.metrics import mean_squared_error, r2_score + + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + print(f"Mean Squared Error: {mse}") + print(f"R-squared: {r2}") + + +def visualize_results(model, X_test, y_test): + """ + Visualize the actual vs predicted prices and feature importance. + """ + import matplotlib.pyplot as plt + import pandas as pd + + # Actual vs Predicted Prices + y_pred = model.predict(X_test) + plt.figure(figsize=(10, 6)) + plt.scatter(y_test, y_pred, alpha=0.6, color="blue") + plt.plot( + [y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "--r", linewidth=2 + ) + plt.xlabel("Actual Prices") + plt.ylabel("Predicted Prices") + plt.title("Actual vs Predicted Prices") + plt.show() + + # Feature Coefficients + coefficients = pd.Series(model.coef_, index=X_test.columns) + plt.figure(figsize=(8, 4)) + coefficients.plot(kind="bar", color="skyblue") + plt.title("Feature Coefficients") + plt.ylabel("Coefficient Value") + plt.show() + + +if __name__ == "__main__": + data = load_data() + data = preprocess_data(data) + analyze_data(data) + model, X_test, y_test = train_model(data) + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) diff --git a/solutions/predcation_of_electric_cars_solution.py b/solutions/predcation_of_electric_cars_solution.py new file mode 100644 index 000000000..833ab9c84 --- /dev/null +++ b/solutions/predcation_of_electric_cars_solution.py @@ -0,0 +1,173 @@ +# electric_car_prices_analysis.py + + +def load_data(): + """ + Load a predefined electric car dataset. + + Returns: + pd.DataFrame: Loaded dataset as a Pandas DataFrame. + """ + import pandas as pd + from io import StringIO + + try: + # Embedded electric car dataset + data = """Make,Model,Year,BatteryCapacity_kWh,Range_km,Price +Tesla,Model 3,2021,75,450,50000 +Nissan,Leaf,2019,40,240,30000 +Chevrolet,Bolt EV,2020,66,380,37000 +Hyundai,Kona Electric,2021,64,415,40000 +Volkswagen,ID.4,2022,77,520,45000 +BMW,i3,2018,33,200,29000 +""" + return pd.read_csv(StringIO(data)) + except Exception as e: + print(f"Error loading data: {e}") + return pd.DataFrame() + + +def preprocess_data(data): + """ + Preprocess the electric car dataset by handling missing values and encoding categorical variables. + """ + import pandas as pd + + try: + data = data.dropna() + # One-hot encode 'Make' and 'Model' + data = pd.get_dummies(data, columns=["Make", "Model"], drop_first=True) + return data + except Exception as e: + print(f"Error preprocessing data: {e}") + return pd.DataFrame() + + +def analyze_data(data): + """ + Perform exploratory data analysis on the dataset. + """ + import matplotlib.pyplot as plt + import seaborn as sns + + try: + print("Dataset Summary:") + print(data.describe()) + + sns.pairplot(data[["Year", "BatteryCapacity_kWh", "Range_km", "Price"]]) + plt.show() + except Exception as e: + print(f"Error in data analysis: {e}") + + +def train_model(data): + """ + Train a predictive model using the dataset. + + Returns: + tuple: Trained model, test features, test labels + """ + from sklearn.model_selection import train_test_split + from sklearn.linear_model import LinearRegression + from sklearn.preprocessing import StandardScaler + + try: + features = data.drop(columns="Price") + target = data["Price"] + + X_train, X_test, y_train, y_test = train_test_split( + features, target, test_size=0.2, random_state=42 + ) + + # Scale the features + scaler = StandardScaler() + X_train = scaler.fit_transform(X_train) + X_test = scaler.transform(X_test) + + model = LinearRegression() + model.fit(X_train, y_train) + + return model, X_test, y_test, scaler + except Exception as e: + print(f"Error in model training: {e}") + return None, None, None, None + + +def evaluate_model(model, X_test, y_test): + """ + Evaluate the trained model using Mean Squared Error and R-squared metrics. + """ + from sklearn.metrics import mean_squared_error, r2_score + + try: + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + print(f"Mean Squared Error: {mse}") + print(f"R-squared: {r2}") + except Exception as e: + print(f"Error in model evaluation: {e}") + + +def visualize_results(model, X_test, y_test): + """ + Visualize the actual vs predicted prices and feature importance. + """ + import matplotlib.pyplot as plt + + try: + # Actual vs Predicted Prices + y_pred = model.predict(X_test) + plt.figure(figsize=(10, 6)) + plt.scatter(y_test, y_pred, alpha=0.6, color="blue") + plt.plot( + [y_test.min(), y_test.max()], + [y_test.min(), y_test.max()], + "--r", + linewidth=2, + ) + plt.xlabel("Actual Prices") + plt.ylabel("Predicted Prices") + plt.title("Actual vs Predicted Prices") + plt.show() + + # Feature Coefficients + coefficients = model.coef_ + feature_names = ["Year", "BatteryCapacity_kWh", "Range_km"] + list( + X_test.columns[3:] + ) + plt.figure(figsize=(8, 4)) + plt.bar(feature_names, coefficients, color="skyblue") + plt.title("Feature Coefficients") + plt.xticks(rotation=45) + plt.ylabel("Coefficient Value") + plt.show() + except Exception as e: + print(f"Error in visualization: {e}") + + +def save_model(model, scaler, filename="electric_car_price_model.pkl"): + """ + Save the trained model and scaler to a file. + """ + import joblib + + try: + joblib.dump({"model": model, "scaler": scaler}, filename) + print(f"Model saved as {filename}") + except Exception as e: + print(f"Error saving model: {e}") + + +if __name__ == "__main__": + data = load_data() + if not data.empty: + data = preprocess_data(data) + analyze_data(data) + model, X_test, y_test, scaler = train_model(data) + if model: + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) + save_model(model, scaler) diff --git a/solutions/tests/test_chicken_nugget_fun.py b/solutions/tests/test_chicken_nugget_fun.py new file mode 100644 index 000000000..9cc83ec14 --- /dev/null +++ b/solutions/tests/test_chicken_nugget_fun.py @@ -0,0 +1,48 @@ +import unittest +from io import StringIO +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + + +# Assuming the function is in a file named 'chicken_nugget.py' +from chicken_nugget_fun_solution import chicken_nugget_fun + + +class TestChickenNuggetFun(unittest.TestCase): + + def test_output(self): + # Backup original stdout + original_stdout = sys.stdout + sys.stdout = StringIO() + + # Run the function + chicken_nugget_fun() + + # Get the output + output = sys.stdout.getvalue() + + # Check if the output contains a nugget fact + self.assertTrue( + any( + fact in output + for fact in [ + "Chicken nuggets were invented", + "The world record for eating chicken nuggets", + "McDonald's nuggets come in four shapes", + "Try making homemade nuggets", + "Some people dip chicken nuggets in honey", + "Chicken nuggets are eaten by millions", + "Sweet chili sauce makes chicken nuggets extra tasty", + "You can even make plant-based chicken nuggets", + ] + ) + ) + + # Restore original stdout + sys.stdout = original_stdout + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_chocolate_distribution.py b/solutions/tests/test_chocolate_distribution.py new file mode 100644 index 000000000..9e1a0ad4a --- /dev/null +++ b/solutions/tests/test_chocolate_distribution.py @@ -0,0 +1,54 @@ +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from chocolate_distribution import minimize_chocolate_difference + + +class TestMinimizeChocolateDifference(unittest.TestCase): + def test_valid_case(self): + """Test a valid case with enough packets and students.""" + chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] + k = 7 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 10) + + def test_not_enough_packets(self): + """Test the case where there are fewer packets than students.""" + chocolates = [1, 2, 3] + k = 5 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, -1) + + def test_empty_chocolates_list(self): + """Test the case where the chocolates list is empty.""" + chocolates = [] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_zero_students(self): + """Test the case where the number of students is zero.""" + chocolates = [10, 20, 30] + k = 0 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_single_packet(self): + """Test the case with only one packet and one student.""" + chocolates = [42] + k = 1 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 0) + + def test_minimized_difference(self): + """Test a generic case to validate the minimized difference calculation.""" + chocolates = [7, 3, 2, 4, 9, 12, 56] + k = 3 + result = minimize_chocolate_difference(chocolates, k) + self.assertEqual(result, 2) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_housing_prices_analysis.py b/solutions/tests/test_housing_prices_analysis.py new file mode 100644 index 000000000..447551d25 --- /dev/null +++ b/solutions/tests/test_housing_prices_analysis.py @@ -0,0 +1,112 @@ +import unittest +import pandas as pd +from sklearn.linear_model import LinearRegression +from sklearn.metrics import mean_squared_error, r2_score +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + +# Assuming the module is named housing_analysis and the functions are imported +from housing_prices_analysis_solution import ( + load_data, + preprocess_data, + analyze_data, + train_model, + evaluate_model, + visualize_results, +) + + +class TestHousingAnalysis(unittest.TestCase): + + def setUp(self): + """Set up the dataset for testing.""" + self.data = load_data() + + def test_load_data(self): + """Test if the data loads correctly.""" + self.assertIsInstance(self.data, pd.DataFrame) + self.assertEqual(self.data.shape[0], 6) # Should have 6 rows + self.assertEqual(self.data.shape[1], 6) # Should have 6 columns + + # Verify column names + expected_columns = [ + "SquareFeet", + "Bedrooms", + "Bathrooms", + "YearBuilt", + "LocationScore", + "Price", + ] + self.assertListEqual(list(self.data.columns), expected_columns) + + def test_preprocess_data(self): + """Test if preprocessing removes missing values.""" + processed_data = preprocess_data(self.data) + self.assertFalse(processed_data.isnull().values.any()) + self.assertEqual( + processed_data.shape, self.data.shape + ) # No missing values in the initial dataset + + def test_analyze_data(self): + """Test if analysis does not throw errors.""" + try: + analyze_data(self.data) + except Exception as e: + self.fail(f"Analyze data failed with error: {e}") + + def test_train_model(self): + """Test if the model trains correctly.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + + self.assertIsInstance(model, LinearRegression) + self.assertGreater(len(X_test), 0) # Ensure test set is not empty + self.assertGreater(len(y_test), 0) + + # Check that the model coefficients are not all zeros + self.assertTrue( + any(model.coef_ != 0), "Model coefficients should not all be zero." + ) + + def test_evaluate_model(self): + """Test if model evaluation metrics are reasonable.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + y_pred = model.predict(X_test) + + mse = mean_squared_error(y_test, y_pred) + r2 = r2_score(y_test, y_pred) + + self.assertGreaterEqual(mse, 0, "Mean Squared Error should be non-negative.") + self.assertGreaterEqual( + r2, -1, "R-squared should be greater than or equal to -1." + ) + self.assertLessEqual(r2, 1, "R-squared should be less than or equal to 1.") + + def test_visualize_results(self): + """Test if visualization functions run without errors.""" + processed_data = preprocess_data(self.data) + model, X_test, y_test = train_model(processed_data) + + try: + visualize_results(model, X_test, y_test) + except Exception as e: + self.fail(f"Visualization failed with error: {e}") + + def test_full_pipeline(self): + """Test the full pipeline from data loading to visualization.""" + try: + data = load_data() + processed_data = preprocess_data(data) + analyze_data(processed_data) + model, X_test, y_test = train_model(processed_data) + evaluate_model(model, X_test, y_test) + visualize_results(model, X_test, y_test) + except Exception as e: + self.fail(f"Full pipeline failed with error: {e}") + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_tic_tac_toe_game.py b/solutions/tests/test_tic_tac_toe_game.py new file mode 100644 index 000000000..ebd44f198 --- /dev/null +++ b/solutions/tests/test_tic_tac_toe_game.py @@ -0,0 +1,53 @@ +import unittest +from unittest.mock import patch +from io import StringIO +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) + +# Assume the main Tic-Tac-Toe functions are imported from the main file +from tic_tac_toe_game import check_winner, is_full, tic_tac_toe + + +class TestTicTacToe(unittest.TestCase): + def test_check_winner_rows(self): + board = [["X", "X", "X"], ["O", " ", "O"], [" ", " ", " "]] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_columns(self): + board = [["X", "O", " "], ["X", "O", " "], ["X", " ", " "]] + self.assertEqual(check_winner(board), "X") + + def test_check_winner_diagonals(self): + board = [["X", "O", "O"], [" ", "X", " "], [" ", " ", "X"]] + self.assertEqual(check_winner(board), "X") + + def test_is_full_true(self): + board = [["X", "O", "X"], ["O", "X", "O"], ["O", "X", "O"]] + self.assertTrue(is_full(board)) + + def test_is_full_false(self): + board = [["X", "O", "X"], ["O", " ", "O"], ["O", "X", "O"]] + self.assertFalse(is_full(board)) + + @patch("builtins.input", side_effect=["0 0", "0 1", "1 1", "0 2", "2 2"]) + @patch("sys.stdout", new_callable=StringIO) + def test_tic_tac_toe_win(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("Player X wins!", output) + + @patch( + "builtins.input", + side_effect=["0 0", "0 1", "0 2", "1 1", "1 0", "1 2", "2 1", "2 0", "2 2"], + ) + @patch("sys.stdout", new_callable=StringIO) + def test_tic_tac_toe_tie(self, mock_stdout, mock_input): + tic_tac_toe() + output = mock_stdout.getvalue() + self.assertIn("It's a tie!", output) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tests/test_travel_planner_fun.py b/solutions/tests/test_travel_planner_fun.py new file mode 100644 index 000000000..deb0925df --- /dev/null +++ b/solutions/tests/test_travel_planner_fun.py @@ -0,0 +1,33 @@ +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from travel_planner_fun_solution import TravelPlanner + + +class TestTravelPlanner(unittest.TestCase): + def setUp(self): + self.planner = TravelPlanner() + + def test_generate_destination(self): + destination = self.planner.generate_destination() + self.assertIn(destination, self.planner.destinations) + + def test_generate_activity(self): + activity = self.planner.generate_activity() + self.assertIn(activity, self.planner.activities) + + def test_generate_travel_tip(self): + tip = self.planner.generate_travel_tip() + self.assertIn(tip, self.planner.tips) + + def test_plan_trip(self): + trip = self.planner.plan_trip() + self.assertIn(trip["destination"], self.planner.destinations) + self.assertIn(trip["activity"], self.planner.activities) + self.assertIn(trip["tip"], self.planner.tips) + + +if __name__ == "__main__": + unittest.main() diff --git a/solutions/tic_tac_toe_game.py b/solutions/tic_tac_toe_game.py new file mode 100644 index 000000000..a214b119f --- /dev/null +++ b/solutions/tic_tac_toe_game.py @@ -0,0 +1,78 @@ +def print_board(board): + for row in board: + print(" | ".join(row)) + print("-" * 9) + + +def check_winner(board): + # Check rows + for row in board: + if row[0] == row[1] == row[2] and row[0] != " ": + return row[0] + + # Check columns + for col in range(3): + if board[0][col] == board[1][col] == board[2][col] and board[0][col] != " ": + return board[0][col] + + # Check diagonals + if board[0][0] == board[1][1] == board[2][2] and board[0][0] != " ": + return board[0][0] + if board[0][2] == board[1][1] == board[2][0] and board[0][2] != " ": + return board[0][2] + + return None + + +def is_full(board): + return all(cell != " " for row in board for cell in row) + + +def tic_tac_toe(): + board = [[" " for _ in range(3)] for _ in range(3)] + players = ["X", "O"] + turn = 0 + + print("Welcome to Tic-Tac-Toe!") + print_board(board) + + while True: + current_player = players[turn % 2] + print(f"Player {current_player}'s turn.") + + try: + row, col = map( + int, + input( + "Enter row and column (0, 1, or 2 separated by a space): " + ).split(), + ) + except ValueError: + print("Invalid input. Please enter two numbers separated by a space.") + continue + + if not (0 <= row < 3 and 0 <= col < 3): + print("Invalid position. Please enter numbers between 0 and 2.") + continue + + if board[row][col] != " ": + print("Position already taken. Choose another.") + continue + + board[row][col] = current_player + print_board(board) + + winner = check_winner(board) + if winner: + print(f"Player {winner} wins!") + break + + if is_full(board): + print("It's a tie!") + break + + turn += 1 + + +if __name__ == "__main__": + tic_tac_toe() diff --git a/solutions/travel_planner_fun_solution.py b/solutions/travel_planner_fun_solution.py new file mode 100644 index 000000000..38f6c66fb --- /dev/null +++ b/solutions/travel_planner_fun_solution.py @@ -0,0 +1,75 @@ +import random + + +class TravelPlanner: + def __init__(self): + self.destinations = [ + "Paris, France", + "Kyoto, Japan", + "Machu Picchu, Peru", + "Cape Town, South Africa", + "New York City, USA", + "Santorini, Greece", + "Cairo, Egypt", + "Sydney, Australia", + "Reykjavik, Iceland", + "Bali, Indonesia", + ] + self.activities = [ + "exploring ancient ruins", + "dining at a Michelin-star restaurant", + "taking a hot air balloon ride", + "relaxing on a pristine beach", + "hiking through breathtaking trails", + "shopping in local markets", + "attending a cultural festival", + "snorkeling with vibrant marine life", + "enjoying a scenic train ride", + "visiting world-class museums", + ] + self.tips = [ + "Always pack a portable charger!", + "Learn a few phrases in the local language to impress locals.", + "Carry a reusable water bottle to stay hydrated.", + "Don’t forget travel insurance—it’s a lifesaver!", + "Try street food—it’s often the tastiest and cheapest option.", + "Keep a digital and physical copy of your passport.", + "Research local customs to avoid awkward situations.", + "Pack light—you’ll thank yourself later.", + "Wake up early to enjoy tourist spots without crowds.", + "Always have some local currency on hand for small purchases.", + ] + + def generate_destination(self): + return random.choice(self.destinations) + + def generate_activity(self): + return random.choice(self.activities) + + def generate_travel_tip(self): + return random.choice(self.tips) + + def plan_trip(self): + destination = self.generate_destination() + activity = self.generate_activity() + tip = self.generate_travel_tip() + + return {"destination": destination, "activity": activity, "tip": tip} + + +def main(): + print("Welcome to the Automated Travel Planner! \U0001f30d") + print("Sit back and relax while we plan your next dream trip.\n") + + planner = TravelPlanner() + trip = planner.plan_trip() + + print(f"\U0001f4cd Destination: {trip['destination']}") + print(f"\U0001f3c3 Activity: {trip['activity']}") + print(f"\U0001f4d6 Travel Tip: {trip['tip']}\n") + + print("Your virtual getaway is ready! Safe travels! \U0001f30f") + + +if __name__ == "__main__": + main() From 54ba289247ddb3dff5bd45f09bc8a4c6638bc709 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:10:34 +0200 Subject: [PATCH 139/163] Update Maher's Test.py --- solutions/tests/Maher's Test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/tests/Maher's Test.py b/solutions/tests/Maher's Test.py index be5d74a1f..70e3327e6 100644 --- a/solutions/tests/Maher's Test.py +++ b/solutions/tests/Maher's Test.py @@ -2,7 +2,7 @@ import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score -import matplotlib.pyplot as plt + # Assuming the module is named housing_analysis and the functions are imported from housing_analysis import ( From 3c6cc1789b22756677960231f84032b55627bfbe Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:12:05 +0200 Subject: [PATCH 140/163] Update Maher's Test.py --- solutions/tests/Maher's Test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/tests/Maher's Test.py b/solutions/tests/Maher's Test.py index 70e3327e6..002407f00 100644 --- a/solutions/tests/Maher's Test.py +++ b/solutions/tests/Maher's Test.py @@ -85,5 +85,5 @@ def test_full_pipeline(self): except Exception as e: self.fail(f"Full pipeline failed with error: {e}") -if _name_ == "_main_": +if __name__ == "__main__": unittest.main() From b5f817fe75d480673ac71ad4ad72b8614e7619a2 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:13:08 +0200 Subject: [PATCH 141/163] Update Maria's test.py --- solutions/tests/Maria's test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/tests/Maria's test.py b/solutions/tests/Maria's test.py index c6ba8d908..6a4f6e03b 100644 --- a/solutions/tests/Maria's test.py +++ b/solutions/tests/Maria's test.py @@ -46,5 +46,5 @@ def test_minimized_difference(self): self.assertEqual(result, 2) -if _name_ == "_main_": +if __name__ == "__main__": unittest.main() From 55686a172e8e87eaffcfa181c477d1de4f27097a Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:27:29 +0200 Subject: [PATCH 142/163] Update Tic-Tac-Toe (XO) Test.py --- solutions/tests/Tic-Tac-Toe (XO) Test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/solutions/tests/Tic-Tac-Toe (XO) Test.py b/solutions/tests/Tic-Tac-Toe (XO) Test.py index 5e52c2e1c..13c32f439 100644 --- a/solutions/tests/Tic-Tac-Toe (XO) Test.py +++ b/solutions/tests/Tic-Tac-Toe (XO) Test.py @@ -2,8 +2,9 @@ from unittest.mock import patch from io import StringIO -# Assume the main Tic-Tac-Toe functions are imported from the main file -# Example: from tic_tac_toe_game import print_board, check_winner, is_full, tic_tac_toe +# Assuming the main Tic-Tac-Toe functions are imported from the main file +# Example import statement: +# from tic_tac_toe_game import check_winner, is_full, tic_tac_toe class TestTicTacToe(unittest.TestCase): def test_check_winner_rows(self): From b940d935310f06d4d55553e96cb07a8b81fca370 Mon Sep 17 00:00:00 2001 From: Muhannad-Assaf <133141191+MuhannadGTR@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:29:30 +0200 Subject: [PATCH 143/163] Update Travel Planner Fun-Test.py --- solutions/tests/Travel Planner Fun-Test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solutions/tests/Travel Planner Fun-Test.py b/solutions/tests/Travel Planner Fun-Test.py index 6d34cf8ae..bb9fbb1a9 100644 --- a/solutions/tests/Travel Planner Fun-Test.py +++ b/solutions/tests/Travel Planner Fun-Test.py @@ -23,5 +23,5 @@ def test_plan_trip(self): self.assertIn(trip["activity"], self.planner.activities) self.assertIn(trip["tip"], self.planner.tips) -if _name_ == "_main_": - unittest.main() \ No newline at end of file +if __name__ == "__main__": + unittest.main() From 600bb5a09726831a10975cb14e6a77f6a4ebeeb5 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:31:53 +0200 Subject: [PATCH 144/163] Delete solutions/Chicken Nugget Fun- solution.py --- solutions/Chicken Nugget Fun- solution.py | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 solutions/Chicken Nugget Fun- solution.py diff --git a/solutions/Chicken Nugget Fun- solution.py b/solutions/Chicken Nugget Fun- solution.py deleted file mode 100644 index 9e4e7d66e..000000000 --- a/solutions/Chicken Nugget Fun- solution.py +++ /dev/null @@ -1,22 +0,0 @@ -import random - -def chicken_nugget_fun(): - print("Welcome to the Chicken Nugget Fun Zone!") - - nugget_facts = [ - "Chicken nuggets were invented in the 1950s by Robert C. Baker!", - "The world record for eating chicken nuggets is 746 grams in 3 minutes!", - "McDonald's nuggets come in four shapes: bell, bow-tie, ball, and boot.", - "Try making homemade nuggets with chicken, breadcrumbs, and spices!", - "Some people dip chicken nuggets in honey—have you tried it?", - "Chicken nuggets are eaten by millions around the world every day!", - "Sweet chili sauce makes chicken nuggets extra tasty!", - "You can even make plant-based chicken nuggets now!" - ] - - random_fact = random.choice(nugget_facts) - print(f"Here's your nugget fun: {random_fact}") - -# Run the function -chicken_nugget_fun() - From 7f341bcac628eadb27da21357bfba7c13d6525c4 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:32:10 +0200 Subject: [PATCH 145/163] Delete solutions/Maher's soltuion.py --- solutions/Maher's soltuion.py | 108 ---------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 solutions/Maher's soltuion.py diff --git a/solutions/Maher's soltuion.py b/solutions/Maher's soltuion.py deleted file mode 100644 index cda0b739b..000000000 --- a/solutions/Maher's soltuion.py +++ /dev/null @@ -1,108 +0,0 @@ -# housing_prices_analysis_solution.py - -def load_data(): - """ - Load a predefined housing dataset. - - Returns: - pd.DataFrame: Loaded dataset as a Pandas DataFrame. - """ - import pandas as pd - from io import StringIO - - # Embedded dataset - data = """SquareFeet,Bedrooms,Bathrooms,YearBuilt,LocationScore,Price -1500,3,2,2000,85,300000 -2000,4,3,2010,90,450000 -1800,3,2,2005,88,350000 -2400,4,3,2020,92,500000 -1600,3,2,1995,80,280000 -1200,2,1,1980,70,200000 -""" - return pd.read_csv(StringIO(data)) - -def preprocess_data(data): - """ - Preprocess the housing dataset by handling missing values and extracting necessary features. - """ - return data.dropna() - -def analyze_data(data): - """ - Perform exploratory data analysis on the dataset. - """ - import matplotlib.pyplot as plt - import seaborn as sns - - print("Dataset Summary:") - print(data.describe()) - - sns.pairplot(data[['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore', 'Price']]) - plt.show() - -def train_model(data): - """ - Train a predictive model using the dataset. - """ - from sklearn.model_selection import train_test_split - from sklearn.linear_model import LinearRegression - - features = ['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore'] - target = 'Price' - - X = data[features] - y = data[target] - - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) - - model = LinearRegression() - model.fit(X_train, y_train) - - return model, X_test, y_test - -def evaluate_model(model, X_test, y_test): - """ - Evaluate the trained model using Mean Squared Error and R-squared metrics. - """ - from sklearn.metrics import mean_squared_error, r2_score - - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - print(f"Mean Squared Error: {mse}") - print(f"R-squared: {r2}") - -def visualize_results(model, X_test, y_test): - """ - Visualize the actual vs predicted prices and feature importance. - """ - import matplotlib.pyplot as plt - import pandas as pd - - # Actual vs Predicted Prices - y_pred = model.predict(X_test) - plt.figure(figsize=(10, 6)) - plt.scatter(y_test, y_pred, alpha=0.6, color='blue') - plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], '--r', linewidth=2) - plt.xlabel('Actual Prices') - plt.ylabel('Predicted Prices') - plt.title('Actual vs Predicted Prices') - plt.show() - - # Feature Coefficients - coefficients = pd.Series(model.coef_, index=X_test.columns) - plt.figure(figsize=(8, 4)) - coefficients.plot(kind='bar', color='skyblue') - plt.title('Feature Coefficients') - plt.ylabel('Coefficient Value') - plt.show() - -if __name__ == "__main__": - data = load_data() - data = preprocess_data(data) - analyze_data(data) - model, X_test, y_test = train_model(data) - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) From 1e56e57d96c1a47a9ca02c4328848f495346ef20 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:33:15 +0200 Subject: [PATCH 146/163] Delete solutions/Maria's soltuion.py --- solutions/Maria's soltuion.py | 38 ----------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 solutions/Maria's soltuion.py diff --git a/solutions/Maria's soltuion.py b/solutions/Maria's soltuion.py deleted file mode 100644 index 3be8d8549..000000000 --- a/solutions/Maria's soltuion.py +++ /dev/null @@ -1,38 +0,0 @@ -def minimize_chocolate_difference(chocolates, k): - """ - Function to distribute chocolates to minimize the difference between the maximum - and minimum chocolates received. - - Parameters: - chocolates (list): List of integers representing chocolates in packets. - k (int): Number of students. - - Returns: - int: The minimized difference between the maximum and minimum chocolates received. - """ - if k == 0 or len(chocolates) == 0: - return 0 - - if len(chocolates) < k: - return -1 # Not enough packets for the students - - # Sort the packets - chocolates.sort() - - # Initialize the minimum difference - min_diff = float("inf") - - # Find the smallest difference for a group of k packets - for i in range(len(chocolates) - k + 1): - diff = chocolates[i + k - 1] - chocolates[i] - min_diff = min(min_diff, diff) - - return min_diff - - -# Example usage -if __name__ == "__main__": -chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] - k = 7 - result = minimize_chocolate_difference(chocolates, k) - print(f"Minimum difference is {result}") From 28534b220eff35ab7bca8f9f01af6345889d2c82 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:34:16 +0200 Subject: [PATCH 147/163] Delete solutions/Predcation of eletric cars solution.py --- .../Predcation of eletric cars solution.py | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 solutions/Predcation of eletric cars solution.py diff --git a/solutions/Predcation of eletric cars solution.py b/solutions/Predcation of eletric cars solution.py deleted file mode 100644 index f598c283f..000000000 --- a/solutions/Predcation of eletric cars solution.py +++ /dev/null @@ -1,154 +0,0 @@ -# electric_car_prices_analysis.py - -def load_data(): - """ - Load a predefined electric car dataset. - - Returns: - pd.DataFrame: Loaded dataset as a Pandas DataFrame. - """ - import pandas as pd - from io import StringIO - - try: - # Embedded electric car dataset - data = """Make,Model,Year,BatteryCapacity_kWh,Range_km,Price -Tesla,Model 3,2021,75,450,50000 -Nissan,Leaf,2019,40,240,30000 -Chevrolet,Bolt EV,2020,66,380,37000 -Hyundai,Kona Electric,2021,64,415,40000 -Volkswagen,ID.4,2022,77,520,45000 -BMW,i3,2018,33,200,29000 -""" - return pd.read_csv(StringIO(data)) - except Exception as e: - print(f"Error loading data: {e}") - return pd.DataFrame() - -def preprocess_data(data): - """ - Preprocess the electric car dataset by handling missing values and encoding categorical variables. - """ - import pandas as pd - - try: - data = data.dropna() - # One-hot encode 'Make' and 'Model' - data = pd.get_dummies(data, columns=['Make', 'Model'], drop_first=True) - return data - except Exception as e: - print(f"Error preprocessing data: {e}") - return pd.DataFrame() - -def analyze_data(data): - """ - Perform exploratory data analysis on the dataset. - """ - import matplotlib.pyplot as plt - import seaborn as sns - - try: - print("Dataset Summary:") - print(data.describe()) - - sns.pairplot(data[['Year', 'BatteryCapacity_kWh', 'Range_km', 'Price']]) - plt.show() - except Exception as e: - print(f"Error in data analysis: {e}") - -def train_model(data): - """ - Train a predictive model using the dataset. - - Returns: - tuple: Trained model, test features, test labels - """ - from sklearn.model_selection import train_test_split - from sklearn.linear_model import LinearRegression - from sklearn.preprocessing import StandardScaler - - try: - features = data.drop(columns='Price') - target = data['Price'] - - X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42) - - # Scale the features - scaler = StandardScaler() - X_train = scaler.fit_transform(X_train) - X_test = scaler.transform(X_test) - - model = LinearRegression() - model.fit(X_train, y_train) - - return model, X_test, y_test, scaler - except Exception as e: - print(f"Error in model training: {e}") - return None, None, None, None - -def evaluate_model(model, X_test, y_test): - """ - Evaluate the trained model using Mean Squared Error and R-squared metrics. - """ - from sklearn.metrics import mean_squared_error, r2_score - - try: - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - print(f"Mean Squared Error: {mse}") - print(f"R-squared: {r2}") - except Exception as e: - print(f"Error in model evaluation: {e}") - -def visualize_results(model, X_test, y_test): - """ - Visualize the actual vs predicted prices and feature importance. - """ - import matplotlib.pyplot as plt - try: - # Actual vs Predicted Prices - y_pred = model.predict(X_test) - plt.figure(figsize=(10, 6)) - plt.scatter(y_test, y_pred, alpha=0.6, color='blue') - plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], '--r', linewidth=2) - plt.xlabel('Actual Prices') - plt.ylabel('Predicted Prices') - plt.title('Actual vs Predicted Prices') - plt.show() - - # Feature Coefficients - coefficients = model.coef_ - feature_names = ['Year', 'BatteryCapacity_kWh', 'Range_km'] + list(X_test.columns[3:]) - plt.figure(figsize=(8, 4)) - plt.bar(feature_names, coefficients, color='skyblue') - plt.title('Feature Coefficients') - plt.xticks(rotation=45) - plt.ylabel('Coefficient Value') - plt.show() - except Exception as e: - print(f"Error in visualization: {e}") - -def save_model(model, scaler, filename="electric_car_price_model.pkl"): - """ - Save the trained model and scaler to a file. - """ - import joblib - - try: - joblib.dump({'model': model, 'scaler': scaler}, filename) - print(f"Model saved as {filename}") - except Exception as e: - print(f"Error saving model: {e}") - if __name__ == "__main__": -data = load_data() - if not data.empty: - data = preprocess_data(data) - analyze_data(data) - model, X_test, y_test, scaler = train_model(data) - if model: - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) - save_model(model, scaler) From c89cb923f4adbd004b6610b1d4b091372a0198a2 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:34:27 +0200 Subject: [PATCH 148/163] Delete solutions/Tic-Tac-Toe (XO) Solution.py --- solutions/Tic-Tac-Toe (XO) Solution.py | 69 -------------------------- 1 file changed, 69 deletions(-) delete mode 100644 solutions/Tic-Tac-Toe (XO) Solution.py diff --git a/solutions/Tic-Tac-Toe (XO) Solution.py b/solutions/Tic-Tac-Toe (XO) Solution.py deleted file mode 100644 index 20a8d3bc3..000000000 --- a/solutions/Tic-Tac-Toe (XO) Solution.py +++ /dev/null @@ -1,69 +0,0 @@ -def print_board(board): - for row in board: - print(" | ".join(row)) - print("-" * 9) - -def check_winner(board): - # Check rows - for row in board: - if row[0] == row[1] == row[2] and row[0] != " ": - return row[0] - - # Check columns - for col in range(3): - if board[0][col] == board[1][col] == board[2][col] and board[0][col] != " ": - return board[0][col] - - # Check diagonals - if board[0][0] == board[1][1] == board[2][2] and board[0][0] != " ": - return board[0][0] - if board[0][2] == board[1][1] == board[2][0] and board[0][2] != " ": - return board[0][2] - - return None - -def is_full(board): - return all(cell != " " for row in board for cell in row) - -def tic_tac_toe(): - board = [[" " for _ in range(3)] for _ in range(3)] - players = ["X", "O"] - turn = 0 - - print("Welcome to Tic-Tac-Toe!") - print_board(board) - - while True: - current_player = players[turn % 2] - print(f"Player {current_player}'s turn.") - - try: - row, col = map(int, input("Enter row and column (0, 1, or 2 separated by a space): ").split()) - except ValueError: - print("Invalid input. Please enter two numbers separated by a space.") - continue - - if not (0 <= row < 3 and 0 <= col < 3): - print("Invalid position. Please enter numbers between 0 and 2.") - continue - - if board[row][col] != " ": - print("Position already taken. Choose another.") - continue - - board[row][col] = current_player - print_board(board) - - winner = check_winner(board) - if winner: - print(f"Player {winner} wins!") - break - - if is_full(board): - print("It's a tie!") - break - - turn += 1 - -if __name__ == "__main__": - tic_tac_toe() From ccc83d4d43e18a2c03987c6c0eb61c6f3003dc1a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:34:42 +0200 Subject: [PATCH 149/163] Delete solutions/Travel Planner Fun-Solution.py --- solutions/Travel Planner Fun-Solution.py | 75 ------------------------ 1 file changed, 75 deletions(-) delete mode 100644 solutions/Travel Planner Fun-Solution.py diff --git a/solutions/Travel Planner Fun-Solution.py b/solutions/Travel Planner Fun-Solution.py deleted file mode 100644 index 6f277b21b..000000000 --- a/solutions/Travel Planner Fun-Solution.py +++ /dev/null @@ -1,75 +0,0 @@ -import random - - -class TravelPlanner: - def __init__(self): - self.destinations = [ - "Paris, France", - "Kyoto, Japan", - "Machu Picchu, Peru", - "Cape Town, South Africa", - "New York City, USA", - "Santorini, Greece", - "Cairo, Egypt", - "Sydney, Australia", - "Reykjavik, Iceland", - "Bali, Indonesia", - ] - self.activities = [ - "exploring ancient ruins", - "dining at a Michelin-star restaurant", - "taking a hot air balloon ride", - "relaxing on a pristine beach", - "hiking through breathtaking trails", - "shopping in local markets", - "attending a cultural festival", - "snorkeling with vibrant marine life", - "enjoying a scenic train ride", - "visiting world-class museums", - ] - self.tips = [ - "Always pack a portable charger!", - "Learn a few phrases in the local language to impress locals.", - "Carry a reusable water bottle to stay hydrated.", - "Don’t forget travel insurance—it’s a lifesaver!", - "Try street food—it’s often the tastiest and cheapest option.", - "Keep a digital and physical copy of your passport.", - "Research local customs to avoid awkward situations.", - "Pack light—you’ll thank yourself later.", - "Wake up early to enjoy tourist spots without crowds.", - "Always have some local currency on hand for small purchases.", - ] - - def generate_destination(self): - return random.choice(self.destinations) - - def generate_activity(self): - return random.choice(self.activities) - - def generate_travel_tip(self): - return random.choice(self.tips) - - def plan_trip(self): - destination = self.generate_destination() - activity = self.generate_activity() - tip = self.generate_travel_tip() - - return {"destination": destination, "activity": activity, "tip": tip} - - -def main(): - print("Welcome to the Automated Travel Planner! \U0001f30d") - print("Sit back and relax while we plan your next dream trip.\n") - - planner = TravelPlanner() - trip = planner.plan_trip() - - print(f"\U0001f4cd Destination: {trip['destination']}") - print(f"\U0001f3c3 Activity: {trip['activity']}") - print(f"\U0001f4d6 Travel Tip: {trip['tip']}\n") - - print("Your virtual getaway is ready! Safe travels! \U0001f30f") - - -if __name__ == "__main__": - main() From 18e7c25f5d3dd275ed56714088c7a6c5aeb4af1e Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:35:51 +0200 Subject: [PATCH 150/163] Delete solutions/tests/Chicken Nugget Fun-Test.py --- solutions/tests/Chicken Nugget Fun-Test.py | 37 ---------------------- 1 file changed, 37 deletions(-) delete mode 100644 solutions/tests/Chicken Nugget Fun-Test.py diff --git a/solutions/tests/Chicken Nugget Fun-Test.py b/solutions/tests/Chicken Nugget Fun-Test.py deleted file mode 100644 index 61a5eaa3c..000000000 --- a/solutions/tests/Chicken Nugget Fun-Test.py +++ /dev/null @@ -1,37 +0,0 @@ -import unittest -from io import StringIO -import sys - -# Assuming the function is in a file named 'chicken_nugget.py' -from chicken_nugget import chicken_nugget_fun - -class TestChickenNuggetFun(unittest.TestCase): - - def test_output(self): - # Backup original stdout - original_stdout = sys.stdout - sys.stdout = StringIO() - - # Run the function - chicken_nugget_fun() - - # Get the output - output = sys.stdout.getvalue() - - # Check if the output contains a nugget fact - self.assertTrue(any(fact in output for fact in [ - "Chicken nuggets were invented", - "The world record for eating chicken nuggets", - "McDonald's nuggets come in four shapes", - "Try making homemade nuggets", - "Some people dip chicken nuggets in honey", - "Chicken nuggets are eaten by millions", - "Sweet chili sauce makes chicken nuggets extra tasty", - "You can even make plant-based chicken nuggets" - ])) - - # Restore original stdout - sys.stdout = original_stdout - -if __name__ == "__main__": -unittest.main() From 5bc6df6c2e5e6fa4cc70e3e4610c9a5f6a9341fa Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:36:02 +0200 Subject: [PATCH 151/163] Delete solutions/tests/Maher's Test.py --- solutions/tests/Maher's Test.py | 89 --------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 solutions/tests/Maher's Test.py diff --git a/solutions/tests/Maher's Test.py b/solutions/tests/Maher's Test.py deleted file mode 100644 index 002407f00..000000000 --- a/solutions/tests/Maher's Test.py +++ /dev/null @@ -1,89 +0,0 @@ -import unittest -import pandas as pd -from sklearn.linear_model import LinearRegression -from sklearn.metrics import mean_squared_error, r2_score - - -# Assuming the module is named housing_analysis and the functions are imported -from housing_analysis import ( - load_data, preprocess_data, analyze_data, train_model, evaluate_model, visualize_results -) - -class TestHousingAnalysis(unittest.TestCase): - - def setUp(self): - """Set up the dataset for testing.""" - self.data = load_data() - - def test_load_data(self): - """Test if the data loads correctly.""" - self.assertIsInstance(self.data, pd.DataFrame) - self.assertEqual(self.data.shape[0], 6) # Should have 6 rows - self.assertEqual(self.data.shape[1], 6) # Should have 6 columns - - # Verify column names - expected_columns = ['SquareFeet', 'Bedrooms', 'Bathrooms', 'YearBuilt', 'LocationScore', 'Price'] - self.assertListEqual(list(self.data.columns), expected_columns) - - def test_preprocess_data(self): - """Test if preprocessing removes missing values.""" - processed_data = preprocess_data(self.data) - self.assertFalse(processed_data.isnull().values.any()) - self.assertEqual(processed_data.shape, self.data.shape) # No missing values in the initial dataset - - def test_analyze_data(self): - """Test if analysis does not throw errors.""" - try: - analyze_data(self.data) - except Exception as e: - self.fail(f"Analyze data failed with error: {e}") - - def test_train_model(self): - """Test if the model trains correctly.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - - self.assertIsInstance(model, LinearRegression) - self.assertGreater(len(X_test), 0) # Ensure test set is not empty - self.assertGreater(len(y_test), 0) - - # Check that the model coefficients are not all zeros - self.assertTrue(any(model.coef_ != 0), "Model coefficients should not all be zero.") - - def test_evaluate_model(self): - """Test if model evaluation metrics are reasonable.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - self.assertGreaterEqual(mse, 0, "Mean Squared Error should be non-negative.") - self.assertGreaterEqual(r2, -1, "R-squared should be greater than or equal to -1.") - self.assertLessEqual(r2, 1, "R-squared should be less than or equal to 1.") - - def test_visualize_results(self): - """Test if visualization functions run without errors.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - - try: - visualize_results(model, X_test, y_test) - except Exception as e: - self.fail(f"Visualization failed with error: {e}") - - def test_full_pipeline(self): - """Test the full pipeline from data loading to visualization.""" - try: - data = load_data() - processed_data = preprocess_data(data) - analyze_data(processed_data) - model, X_test, y_test = train_model(processed_data) - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) - except Exception as e: - self.fail(f"Full pipeline failed with error: {e}") - -if __name__ == "__main__": - unittest.main() From 47a073a5b93ec567d92c7e63c54116b43001f962 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:36:13 +0200 Subject: [PATCH 152/163] Delete solutions/tests/Maria's test.py --- solutions/tests/Maria's test.py | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 solutions/tests/Maria's test.py diff --git a/solutions/tests/Maria's test.py b/solutions/tests/Maria's test.py deleted file mode 100644 index 6a4f6e03b..000000000 --- a/solutions/tests/Maria's test.py +++ /dev/null @@ -1,50 +0,0 @@ -import unittest -from chocolate_distribution import minimize_chocolate_difference - - -class TestMinimizeChocolateDifference(unittest.TestCase): - def test_valid_case(self): - """Test a valid case with enough packets and students.""" - chocolates = [12, 4, 7, 9, 2, 23, 25, 41, 30, 40, 28, 42, 30, 44, 48, 43, 50] - k = 7 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 10) - - def test_not_enough_packets(self): - """Test the case where there are fewer packets than students.""" - chocolates = [1, 2, 3] - k = 5 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, -1) - - def test_empty_chocolates_list(self): - """Test the case where the chocolates list is empty.""" - chocolates = [] - k = 3 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_zero_students(self): - """Test the case where the number of students is zero.""" - chocolates = [10, 20, 30] - k = 0 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_single_packet(self): - """Test the case with only one packet and one student.""" - chocolates = [42] - k = 1 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 0) - - def test_minimized_difference(self): - """Test a generic case to validate the minimized difference calculation.""" - chocolates = [7, 3, 2, 4, 9, 12, 56] - k = 3 - result = minimize_chocolate_difference(chocolates, k) - self.assertEqual(result, 2) - - -if __name__ == "__main__": - unittest.main() From 3660c0f2fa2ad6b16b78a86da7247245dac27f14 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:36:25 +0200 Subject: [PATCH 153/163] Delete solutions/tests/Tic-Tac-Toe (XO) Test.py --- solutions/tests/Tic-Tac-Toe (XO) Test.py | 65 ------------------------ 1 file changed, 65 deletions(-) delete mode 100644 solutions/tests/Tic-Tac-Toe (XO) Test.py diff --git a/solutions/tests/Tic-Tac-Toe (XO) Test.py b/solutions/tests/Tic-Tac-Toe (XO) Test.py deleted file mode 100644 index 13c32f439..000000000 --- a/solutions/tests/Tic-Tac-Toe (XO) Test.py +++ /dev/null @@ -1,65 +0,0 @@ -import unittest -from unittest.mock import patch -from io import StringIO - -# Assuming the main Tic-Tac-Toe functions are imported from the main file -# Example import statement: -# from tic_tac_toe_game import check_winner, is_full, tic_tac_toe - -class TestTicTacToe(unittest.TestCase): - def test_check_winner_rows(self): - board = [ - ["X", "X", "X"], - ["O", " ", "O"], - [" ", " ", " "] - ] - self.assertEqual(check_winner(board), "X") - - def test_check_winner_columns(self): - board = [ - ["X", "O", " "], - ["X", "O", " "], - ["X", " ", " "] - ] - self.assertEqual(check_winner(board), "X") - - def test_check_winner_diagonals(self): - board = [ - ["X", "O", "O"], - [" ", "X", " "], - [" ", " ", "X"] - ] - self.assertEqual(check_winner(board), "X") - - def test_is_full_true(self): - board = [ - ["X", "O", "X"], - ["O", "X", "O"], - ["O", "X", "O"] - ] - self.assertTrue(is_full(board)) - - def test_is_full_false(self): - board = [ - ["X", "O", "X"], - ["O", " ", "O"], - ["O", "X", "O"] - ] - self.assertFalse(is_full(board)) - - @patch('builtins.input', side_effect=["0 0", "0 1", "1 1", "0 2", "2 2"]) - @patch('sys.stdout', new_callable=StringIO) - def test_tic_tac_toe_win(self, mock_stdout, mock_input): - tic_tac_toe() - output = mock_stdout.getvalue() - self.assertIn("Player X wins!", output) - - @patch('builtins.input', side_effect=["0 0", "0 1", "1 1", "2 1", "2 0", "2 2", "1 0", "1 2", "0 2"]) - @patch('sys.stdout', new_callable=StringIO) - def test_tic_tac_toe_tie(self, mock_stdout, mock_input): - tic_tac_toe() - output = mock_stdout.getvalue() - self.assertIn("It's a tie!", output) - -if __name__ == "__main__": - unittest.main() From 3708588a408a043ae407410eb2d3dc88f2f23331 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:36:38 +0200 Subject: [PATCH 154/163] Delete solutions/tests/Travel Planner Fun-Test.py --- solutions/tests/Travel Planner Fun-Test.py | 27 ---------------------- 1 file changed, 27 deletions(-) delete mode 100644 solutions/tests/Travel Planner Fun-Test.py diff --git a/solutions/tests/Travel Planner Fun-Test.py b/solutions/tests/Travel Planner Fun-Test.py deleted file mode 100644 index bb9fbb1a9..000000000 --- a/solutions/tests/Travel Planner Fun-Test.py +++ /dev/null @@ -1,27 +0,0 @@ -import unittest -from travel_planner import TravelPlanner # Replace travel_planner with the name of your script file. - -class TestTravelPlanner(unittest.TestCase): - def setUp(self): - self.planner = TravelPlanner() - - def test_generate_destination(self): - destination = self.planner.generate_destination() - self.assertIn(destination, self.planner.destinations) - - def test_generate_activity(self): - activity = self.planner.generate_activity() - self.assertIn(activity, self.planner.activities) - - def test_generate_travel_tip(self): - tip = self.planner.generate_travel_tip() - self.assertIn(tip, self.planner.tips) - - def test_plan_trip(self): - trip = self.planner.plan_trip() - self.assertIn(trip["destination"], self.planner.destinations) - self.assertIn(trip["activity"], self.planner.activities) - self.assertIn(trip["tip"], self.planner.tips) - -if __name__ == "__main__": - unittest.main() From b6b0067cc19d8dd904e6c4d67a0bcc3a644ca894 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:44:26 +0200 Subject: [PATCH 155/163] Abdulrahman Notes --- notes/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notes/README.md b/notes/README.md index c8b5e64b1..35b6fa306 100644 --- a/notes/README.md +++ b/notes/README.md @@ -8,3 +8,8 @@ was facing electricity shortage and I had to use my phone to upload the file. I have access to a laptop right now. - The same goes for the other challenges I uploaded via file upload and when I got my laptop back I updated the files via CLI. +- I updated the functions and test files of some of our team members because +they kept failing the CI checks and this affected all other team members and we +needed to complete our project on time but these issues were keeping us from +commiting any new changes to the repository because they will keep failing the +CI checks. From 6ec49e0c5d5435f748823828c194c4d9d2fc6f5d Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sun, 12 Jan 2025 14:59:19 +0200 Subject: [PATCH 156/163] Delete solutions/tests/test_housing_prices_analysis.py --- .../tests/test_housing_prices_analysis.py | 112 ------------------ 1 file changed, 112 deletions(-) delete mode 100644 solutions/tests/test_housing_prices_analysis.py diff --git a/solutions/tests/test_housing_prices_analysis.py b/solutions/tests/test_housing_prices_analysis.py deleted file mode 100644 index 447551d25..000000000 --- a/solutions/tests/test_housing_prices_analysis.py +++ /dev/null @@ -1,112 +0,0 @@ -import unittest -import pandas as pd -from sklearn.linear_model import LinearRegression -from sklearn.metrics import mean_squared_error, r2_score -import sys -from pathlib import Path - -sys.path.append(str(Path(__file__).parent.parent)) - -# Assuming the module is named housing_analysis and the functions are imported -from housing_prices_analysis_solution import ( - load_data, - preprocess_data, - analyze_data, - train_model, - evaluate_model, - visualize_results, -) - - -class TestHousingAnalysis(unittest.TestCase): - - def setUp(self): - """Set up the dataset for testing.""" - self.data = load_data() - - def test_load_data(self): - """Test if the data loads correctly.""" - self.assertIsInstance(self.data, pd.DataFrame) - self.assertEqual(self.data.shape[0], 6) # Should have 6 rows - self.assertEqual(self.data.shape[1], 6) # Should have 6 columns - - # Verify column names - expected_columns = [ - "SquareFeet", - "Bedrooms", - "Bathrooms", - "YearBuilt", - "LocationScore", - "Price", - ] - self.assertListEqual(list(self.data.columns), expected_columns) - - def test_preprocess_data(self): - """Test if preprocessing removes missing values.""" - processed_data = preprocess_data(self.data) - self.assertFalse(processed_data.isnull().values.any()) - self.assertEqual( - processed_data.shape, self.data.shape - ) # No missing values in the initial dataset - - def test_analyze_data(self): - """Test if analysis does not throw errors.""" - try: - analyze_data(self.data) - except Exception as e: - self.fail(f"Analyze data failed with error: {e}") - - def test_train_model(self): - """Test if the model trains correctly.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - - self.assertIsInstance(model, LinearRegression) - self.assertGreater(len(X_test), 0) # Ensure test set is not empty - self.assertGreater(len(y_test), 0) - - # Check that the model coefficients are not all zeros - self.assertTrue( - any(model.coef_ != 0), "Model coefficients should not all be zero." - ) - - def test_evaluate_model(self): - """Test if model evaluation metrics are reasonable.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - self.assertGreaterEqual(mse, 0, "Mean Squared Error should be non-negative.") - self.assertGreaterEqual( - r2, -1, "R-squared should be greater than or equal to -1." - ) - self.assertLessEqual(r2, 1, "R-squared should be less than or equal to 1.") - - def test_visualize_results(self): - """Test if visualization functions run without errors.""" - processed_data = preprocess_data(self.data) - model, X_test, y_test = train_model(processed_data) - - try: - visualize_results(model, X_test, y_test) - except Exception as e: - self.fail(f"Visualization failed with error: {e}") - - def test_full_pipeline(self): - """Test the full pipeline from data loading to visualization.""" - try: - data = load_data() - processed_data = preprocess_data(data) - analyze_data(processed_data) - model, X_test, y_test = train_model(processed_data) - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) - except Exception as e: - self.fail(f"Full pipeline failed with error: {e}") - - -if __name__ == "__main__": - unittest.main() From eb070a49b8550afbea249d0fb4c4786d0190bc0d Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sun, 12 Jan 2025 14:59:34 +0200 Subject: [PATCH 157/163] Delete solutions/housing_prices_analysis_solution.py --- solutions/housing_prices_analysis_solution.py | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 solutions/housing_prices_analysis_solution.py diff --git a/solutions/housing_prices_analysis_solution.py b/solutions/housing_prices_analysis_solution.py deleted file mode 100644 index 9d5bd5da5..000000000 --- a/solutions/housing_prices_analysis_solution.py +++ /dev/null @@ -1,130 +0,0 @@ -# housing_prices_analysis_solution.py - - -def load_data(): - """ - Load a predefined housing dataset. - - Returns: - pd.DataFrame: Loaded dataset as a Pandas DataFrame. - """ - import pandas as pd - from io import StringIO - - # Embedded dataset - data = """SquareFeet,Bedrooms,Bathrooms,YearBuilt,LocationScore,Price -1500,3,2,2000,85,300000 -2000,4,3,2010,90,450000 -1800,3,2,2005,88,350000 -2400,4,3,2020,92,500000 -1600,3,2,1995,80,280000 -1200,2,1,1980,70,200000 -""" - return pd.read_csv(StringIO(data)) - - -def preprocess_data(data): - """ - Preprocess the housing dataset by handling missing values and extracting necessary features. - """ - return data.dropna() - - -def analyze_data(data): - """ - Perform exploratory data analysis on the dataset. - """ - import matplotlib.pyplot as plt - import seaborn as sns - - print("Dataset Summary:") - print(data.describe()) - - sns.pairplot( - data[ - [ - "SquareFeet", - "Bedrooms", - "Bathrooms", - "YearBuilt", - "LocationScore", - "Price", - ] - ] - ) - plt.show() - - -def train_model(data): - """ - Train a predictive model using the dataset. - """ - from sklearn.model_selection import train_test_split - from sklearn.linear_model import LinearRegression - - features = ["SquareFeet", "Bedrooms", "Bathrooms", "YearBuilt", "LocationScore"] - target = "Price" - - X = data[features] - y = data[target] - - X_train, X_test, y_train, y_test = train_test_split( - X, y, test_size=0.2, random_state=42 - ) - - model = LinearRegression() - model.fit(X_train, y_train) - - return model, X_test, y_test - - -def evaluate_model(model, X_test, y_test): - """ - Evaluate the trained model using Mean Squared Error and R-squared metrics. - """ - from sklearn.metrics import mean_squared_error, r2_score - - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - print(f"Mean Squared Error: {mse}") - print(f"R-squared: {r2}") - - -def visualize_results(model, X_test, y_test): - """ - Visualize the actual vs predicted prices and feature importance. - """ - import matplotlib.pyplot as plt - import pandas as pd - - # Actual vs Predicted Prices - y_pred = model.predict(X_test) - plt.figure(figsize=(10, 6)) - plt.scatter(y_test, y_pred, alpha=0.6, color="blue") - plt.plot( - [y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "--r", linewidth=2 - ) - plt.xlabel("Actual Prices") - plt.ylabel("Predicted Prices") - plt.title("Actual vs Predicted Prices") - plt.show() - - # Feature Coefficients - coefficients = pd.Series(model.coef_, index=X_test.columns) - plt.figure(figsize=(8, 4)) - coefficients.plot(kind="bar", color="skyblue") - plt.title("Feature Coefficients") - plt.ylabel("Coefficient Value") - plt.show() - - -if __name__ == "__main__": - data = load_data() - data = preprocess_data(data) - analyze_data(data) - model, X_test, y_test = train_model(data) - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) From 2832faa240c4275d244db38a28d27c36130ff970 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+abdoalsir@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:03:10 +0200 Subject: [PATCH 158/163] Delete solutions/predcation_of_electric_cars_solution.py --- .../predcation_of_electric_cars_solution.py | 173 ------------------ 1 file changed, 173 deletions(-) delete mode 100644 solutions/predcation_of_electric_cars_solution.py diff --git a/solutions/predcation_of_electric_cars_solution.py b/solutions/predcation_of_electric_cars_solution.py deleted file mode 100644 index 833ab9c84..000000000 --- a/solutions/predcation_of_electric_cars_solution.py +++ /dev/null @@ -1,173 +0,0 @@ -# electric_car_prices_analysis.py - - -def load_data(): - """ - Load a predefined electric car dataset. - - Returns: - pd.DataFrame: Loaded dataset as a Pandas DataFrame. - """ - import pandas as pd - from io import StringIO - - try: - # Embedded electric car dataset - data = """Make,Model,Year,BatteryCapacity_kWh,Range_km,Price -Tesla,Model 3,2021,75,450,50000 -Nissan,Leaf,2019,40,240,30000 -Chevrolet,Bolt EV,2020,66,380,37000 -Hyundai,Kona Electric,2021,64,415,40000 -Volkswagen,ID.4,2022,77,520,45000 -BMW,i3,2018,33,200,29000 -""" - return pd.read_csv(StringIO(data)) - except Exception as e: - print(f"Error loading data: {e}") - return pd.DataFrame() - - -def preprocess_data(data): - """ - Preprocess the electric car dataset by handling missing values and encoding categorical variables. - """ - import pandas as pd - - try: - data = data.dropna() - # One-hot encode 'Make' and 'Model' - data = pd.get_dummies(data, columns=["Make", "Model"], drop_first=True) - return data - except Exception as e: - print(f"Error preprocessing data: {e}") - return pd.DataFrame() - - -def analyze_data(data): - """ - Perform exploratory data analysis on the dataset. - """ - import matplotlib.pyplot as plt - import seaborn as sns - - try: - print("Dataset Summary:") - print(data.describe()) - - sns.pairplot(data[["Year", "BatteryCapacity_kWh", "Range_km", "Price"]]) - plt.show() - except Exception as e: - print(f"Error in data analysis: {e}") - - -def train_model(data): - """ - Train a predictive model using the dataset. - - Returns: - tuple: Trained model, test features, test labels - """ - from sklearn.model_selection import train_test_split - from sklearn.linear_model import LinearRegression - from sklearn.preprocessing import StandardScaler - - try: - features = data.drop(columns="Price") - target = data["Price"] - - X_train, X_test, y_train, y_test = train_test_split( - features, target, test_size=0.2, random_state=42 - ) - - # Scale the features - scaler = StandardScaler() - X_train = scaler.fit_transform(X_train) - X_test = scaler.transform(X_test) - - model = LinearRegression() - model.fit(X_train, y_train) - - return model, X_test, y_test, scaler - except Exception as e: - print(f"Error in model training: {e}") - return None, None, None, None - - -def evaluate_model(model, X_test, y_test): - """ - Evaluate the trained model using Mean Squared Error and R-squared metrics. - """ - from sklearn.metrics import mean_squared_error, r2_score - - try: - y_pred = model.predict(X_test) - - mse = mean_squared_error(y_test, y_pred) - r2 = r2_score(y_test, y_pred) - - print(f"Mean Squared Error: {mse}") - print(f"R-squared: {r2}") - except Exception as e: - print(f"Error in model evaluation: {e}") - - -def visualize_results(model, X_test, y_test): - """ - Visualize the actual vs predicted prices and feature importance. - """ - import matplotlib.pyplot as plt - - try: - # Actual vs Predicted Prices - y_pred = model.predict(X_test) - plt.figure(figsize=(10, 6)) - plt.scatter(y_test, y_pred, alpha=0.6, color="blue") - plt.plot( - [y_test.min(), y_test.max()], - [y_test.min(), y_test.max()], - "--r", - linewidth=2, - ) - plt.xlabel("Actual Prices") - plt.ylabel("Predicted Prices") - plt.title("Actual vs Predicted Prices") - plt.show() - - # Feature Coefficients - coefficients = model.coef_ - feature_names = ["Year", "BatteryCapacity_kWh", "Range_km"] + list( - X_test.columns[3:] - ) - plt.figure(figsize=(8, 4)) - plt.bar(feature_names, coefficients, color="skyblue") - plt.title("Feature Coefficients") - plt.xticks(rotation=45) - plt.ylabel("Coefficient Value") - plt.show() - except Exception as e: - print(f"Error in visualization: {e}") - - -def save_model(model, scaler, filename="electric_car_price_model.pkl"): - """ - Save the trained model and scaler to a file. - """ - import joblib - - try: - joblib.dump({"model": model, "scaler": scaler}, filename) - print(f"Model saved as {filename}") - except Exception as e: - print(f"Error saving model: {e}") - - -if __name__ == "__main__": - data = load_data() - if not data.empty: - data = preprocess_data(data) - analyze_data(data) - model, X_test, y_test, scaler = train_model(data) - if model: - evaluate_model(model, X_test, y_test) - visualize_results(model, X_test, y_test) - save_model(model, scaler) From b15e770ea96bfed8fa784cb3c4bb3b508806b534 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:09:45 +0200 Subject: [PATCH 159/163] Fixing Formatting and Testing issues --- solutions/travel_planner_fun_solution.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/solutions/travel_planner_fun_solution.py b/solutions/travel_planner_fun_solution.py index 38f6c66fb..f5cfb0e53 100644 --- a/solutions/travel_planner_fun_solution.py +++ b/solutions/travel_planner_fun_solution.py @@ -1,8 +1,10 @@ import random +from random import choice class TravelPlanner: def __init__(self): + random.seed() self.destinations = [ "Paris, France", "Kyoto, Japan", @@ -31,23 +33,23 @@ def __init__(self): "Always pack a portable charger!", "Learn a few phrases in the local language to impress locals.", "Carry a reusable water bottle to stay hydrated.", - "Don’t forget travel insurance—it’s a lifesaver!", - "Try street food—it’s often the tastiest and cheapest option.", + "Don't forget travel insurance—it's a lifesaver!", + "Try street food—it's often the tastiest and cheapest option.", "Keep a digital and physical copy of your passport.", "Research local customs to avoid awkward situations.", - "Pack light—you’ll thank yourself later.", + "Pack light—you'll thank yourself later.", "Wake up early to enjoy tourist spots without crowds.", "Always have some local currency on hand for small purchases.", ] def generate_destination(self): - return random.choice(self.destinations) + return choice(self.destinations) def generate_activity(self): - return random.choice(self.activities) + return choice(self.activities) def generate_travel_tip(self): - return random.choice(self.tips) + return choice(self.tips) def plan_trip(self): destination = self.generate_destination() From d88ae1e6594248fa6be6fd46604bfdec1f19d9d0 Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:13:09 +0200 Subject: [PATCH 160/163] Fixing formatting and testing issues --- solutions/tests/test_chicken_nugget_fun.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/solutions/tests/test_chicken_nugget_fun.py b/solutions/tests/test_chicken_nugget_fun.py index 9cc83ec14..9078f8543 100644 --- a/solutions/tests/test_chicken_nugget_fun.py +++ b/solutions/tests/test_chicken_nugget_fun.py @@ -5,8 +5,6 @@ sys.path.append(str(Path(__file__).parent.parent)) - -# Assuming the function is in a file named 'chicken_nugget.py' from chicken_nugget_fun_solution import chicken_nugget_fun From 5aa9a004991aca4b8405d765dea05d0b4a9eaa5a Mon Sep 17 00:00:00 2001 From: Abdo <154691903+7mama7@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:17:56 +0200 Subject: [PATCH 161/163] Fixing formatting --- solutions/tests/test_chicken_nugget_fun.py | 54 ++++++++++++---------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/solutions/tests/test_chicken_nugget_fun.py b/solutions/tests/test_chicken_nugget_fun.py index 9078f8543..b49058269 100644 --- a/solutions/tests/test_chicken_nugget_fun.py +++ b/solutions/tests/test_chicken_nugget_fun.py @@ -1,6 +1,6 @@ +import sys import unittest from io import StringIO -import sys from pathlib import Path sys.path.append(str(Path(__file__).parent.parent)) @@ -9,37 +9,41 @@ class TestChickenNuggetFun(unittest.TestCase): + """Test cases for the `chicken_nugget_fun` function.""" def test_output(self): + """Test if the output contains a valid chicken nugget fact.""" # Backup original stdout original_stdout = sys.stdout sys.stdout = StringIO() - # Run the function - chicken_nugget_fun() - - # Get the output - output = sys.stdout.getvalue() - - # Check if the output contains a nugget fact - self.assertTrue( - any( - fact in output - for fact in [ - "Chicken nuggets were invented", - "The world record for eating chicken nuggets", - "McDonald's nuggets come in four shapes", - "Try making homemade nuggets", - "Some people dip chicken nuggets in honey", - "Chicken nuggets are eaten by millions", - "Sweet chili sauce makes chicken nuggets extra tasty", - "You can even make plant-based chicken nuggets", - ] + try: + # Run the function + chicken_nugget_fun() + + # Get the output + output = sys.stdout.getvalue() + + # Check if the output contains a nugget fact + self.assertTrue( + any( + fact in output + for fact in [ + "Chicken nuggets were invented", + "The world record for eating chicken nuggets", + "McDonald's nuggets come in four shapes", + "Try making homemade nuggets", + "Some people dip chicken nuggets in honey", + "Chicken nuggets are eaten by millions", + "Sweet chili sauce makes chicken nuggets extra tasty", + "You can even make plant-based chicken nuggets", + ] + ), + "Output did not contain a valid chicken nugget fact.", ) - ) - - # Restore original stdout - sys.stdout = original_stdout + finally: + # Restore original stdout + sys.stdout = original_stdout if __name__ == "__main__": From 40c7b849800f4e2d39e5766809c3c3ecd8da36d7 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sun, 12 Jan 2025 16:26:27 +0200 Subject: [PATCH 162/163] Add files via upload --- solutions/is_palindrome.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 solutions/is_palindrome.py diff --git a/solutions/is_palindrome.py b/solutions/is_palindrome.py new file mode 100644 index 000000000..b45aae0a8 --- /dev/null +++ b/solutions/is_palindrome.py @@ -0,0 +1,37 @@ +""" +A module to check if a given string is a palindrome. + +Module contents: + - is_palindrome: checks if a string reads the same forwards and backwards. + +Created on 03-01-25 + +""" + + +def is_palindrome(input_string: str) -> bool: + """ + Checks if a string is a palindrome. + + Parameters: + input_string (str): The string to be checked. + + Returns: + bool: True if the string is a palindrome, False otherwise. + + Raises: + TypeError: If the input is not a string. + + Examples: + >>> is_palindrome("madam") + True + + >>> is_palindrome("hello") + False + + >>> is_palindrome("") + True + """ + if not isinstance(input_string, str): + raise TypeError("Input must be a string.") + return input_string == input_string[::-1] From 14a36c37847cdeedcef60e30f1d69af6986a3b86 Mon Sep 17 00:00:00 2001 From: Maher Assaf Date: Sun, 12 Jan 2025 16:26:52 +0200 Subject: [PATCH 163/163] Add files via upload --- solutions/tests/test_is_palindrome.py | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 solutions/tests/test_is_palindrome.py diff --git a/solutions/tests/test_is_palindrome.py b/solutions/tests/test_is_palindrome.py new file mode 100644 index 000000000..19b316da5 --- /dev/null +++ b/solutions/tests/test_is_palindrome.py @@ -0,0 +1,50 @@ +""" +Test module for the is_palindrome function. + +Test categories: + - Standard cases: typical strings to verify palindrome status + - Edge cases: empty string, single-character strings + - Defensive tests: invalid input types + +Created on 03-01-25 +Updated on 10-01-25 + +""" + +import unittest +import sys +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent)) +from is_palindrome import is_palindrome + + +class TestIsPalindrome(unittest.TestCase): + def test_palindrome(self): + """Test for a string that is a palindrome.""" + result = is_palindrome("madam") + self.assertTrue(result) + + def test_non_palindrome(self): + """Test for a string that is not a palindrome.""" + result = is_palindrome("hello") + self.assertFalse(result) + + def test_empty_string(self): + """Test for an empty string.""" + result = is_palindrome("") + self.assertTrue(result) + + def test_single_character(self): + """Test for a single-character string.""" + result = is_palindrome("a") + self.assertTrue(result) + + def test_invalid_input_type(self): + """Test for invalid input types.""" + with self.assertRaises(TypeError): + is_palindrome(12345) + + +if __name__ == "__main__": + unittest.main()