{"id":25841937,"url":"https://github.com/courseworks/aut_ap_2024_spring_hw5","last_synced_at":"2025-10-11T00:34:59.675Z","repository":{"id":243490509,"uuid":"812316131","full_name":"courseworks/AUT_AP_2024_Spring_HW5","owner":"courseworks","description":"The fifth assignment for AUT's Advanced Programming course (Spring 2024), focusing on the principles of Object-Oriented Programming (OOP), Python Decorators, and Asynchronous Programming. Enhance your skills in OOP with practical applications, master decorators for dynamic functions, and learn async programming for concurrent tasks.","archived":false,"fork":false,"pushed_at":"2024-06-18T07:47:36.000Z","size":77,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-18T09:12:14.452Z","etag":null,"topics":["advanced-programming","assignment","asynchronous-programming","decorators","homework","python","python-async","python-decorators","python-oop","python-programming"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/courseworks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-08T14:45:39.000Z","updated_at":"2024-06-18T07:47:39.000Z","dependencies_parsed_at":"2024-06-09T10:57:06.408Z","dependency_job_id":"5c61185c-f0c2-4cc0-bf0a-4615e332db89","html_url":"https://github.com/courseworks/AUT_AP_2024_Spring_HW5","commit_stats":null,"previous_names":["courseworks/aut_ap_2024_spring_hw5"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW5","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW5/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW5/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW5/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/courseworks","download_url":"https://codeload.github.com/courseworks/AUT_AP_2024_Spring_HW5/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241322577,"owners_count":19944073,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["advanced-programming","assignment","asynchronous-programming","decorators","homework","python","python-async","python-decorators","python-oop","python-programming"],"created_at":"2025-03-01T05:32:51.064Z","updated_at":"2025-10-11T00:34:54.640Z","avatar_url":"https://github.com/courseworks.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n\u003cstrong\u003eAUT_AP_2024_Spring Homework 5\u003c/strong\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cstrong\u003e Deadline: 1th of Tir - 23:59 o'clock\u003c/strong\u003e\n\u003c/p\u003e\n\nWelcome to your fifth homework assignment for the Advanced Programming Course! This homework is divided into three sections: `Object-Oriented Programming (OOP)`, `Python Decorators`, and `Asynchronous Programming`. Please read the instructions carefully and make sure to complete all the tasks.\n\n## Section 1: Object-Oriented Programming (OOP)\n\nIn this section, you will implement several classes to practice your OOP skills in Python. Follow the detailed instructions for each class and method.\n\n### 1. Person Class\n\nThis class represents each person. The constructor takes four arguments: `first_name`, `last_name`, `birthday`, and `gender`.\n\n- The `birthday` is a private variable.\n- This class cannot be instantiated directly.\n\n### 2. Student Class\n\nThe constructor of this class takes the following arguments: `first_name`, `last_name`, `birthday`, `gender`, `faculty`, `academic_degree`, `university`, and `gpa`.\n\n- The default value of `gpa` is 15.\n- The `academic_degree` must be one of the following: Bachelor, Master, Doctoral. Otherwise, raise a `ValueError`.\n- `faculty` and `university` are instances of the `Faculty` and `University` classes, respectively.\n- Each student has a unique, immutable 6-digit student number. Attempting to modify it should raise a `ConstError`.\n- Implement the `ConstError` exception in `exceptions.py`.\n- The `get_age()` method should return the age of the student. if today is 2/2/2024 and birthday is 1/2/2000 you should return 24 and if birhday is 3/2/2000 you should return 23.\n- This class should be printable and output the format: `I am (first name) (last name) and studying at (university)`.\n\n### 3. Professor Class\n\nThe constructor takes the following arguments: `first_name`, `last_name`, `birthday`, `gender`, `faculty`, `academic_rank`, `university`, and `salary`.\n\n- The `salary` is a private variable.\n- This class should be printable and output the format: `I am (academic rank) (first name) (last name) and studying at (university)`.\n- The `academic_rank` must be one of the following: Professor, Associate Professor, Assistant Professor, Lecturer. Otherwise, raise a `ValueError`.\n- The `get_age()` method should return the age of the professor.\n\n**Person, Student and Professor classes must be implemented in person.py file.**\n\n### 4. University Class\n\nThis class has member variables: `name`, `established`, `chancellor`, and `faculties`.\n\n- The constructor can either take a filename (with `name`, `established`, and `chancellor` stored in it) or these attributes directly.\n- This class should be printable and output the format: `Here is (name)`.\n- The `which_university_is_this` method returns the university of a student given their student number.\n- The `add_faculties` method adds faculties to the `faculties` list.\n- You can only create three instances of the `University` class. Creating more should raise a `RuntimeError`.\n\n### 5. Faculty Class\n\nThe constructor takes the following arguments: `name` and `university`, with optional `students` (tuple) and `professors` (list).\n\n- The `add_students()` and `add_professors()` methods add objects to the respective lists.\n- The `get_top_remain()` method returns the student with the highest GPA for a specified academic degree.\n- Summing two faculties together should merge their professors into the first faculty.\n- Using the `%` operator should filter and return a list of students with a GPA greater than or equal to a specified value.\n- Ensure that membership checks (`in` operator) work for both professors and students in a faculty.\n\n**Faculty and University classes must be implemented in university.py file.**\n\n## Section 2: Python Decorators\n\nIn this section, you will practice writing Python decorators to modify the behavior of functions.\n\n### A. Reverse String Decorator\n\nModify the `reverse_string` decorator to reverse the output string of a function. If the output is not a string, return `None`.\n\n```python\n# MODIFY THIS DECORATOR\ndef reverse_string(func):\n    \"\"\"If output is a string, reverse it. Otherwise, return None.\"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrapper\n\n# TARGET FUNCTIONS\n@reverse_string\ndef get_university_name() -\u003e str:\n    return \"Western Institute of Technology and Higher Education\"\n\n@reverse_string\ndef get_university_founding_year() -\u003e int:\n    return 1957\n\n# TEST OUPUT\nprint(\n    get_university_name(),\n    get_university_founding_year(),\n    sep=\"\\n\"\n)\n```\n\n### B. Prime Filter Decorator\n\nModify the `prime_filter` decorator to filter and return only prime integers from a list of integers returned by a function.\n\n```python\n# MODIFY THIS DECORATOR\ndef prime_filter(func):\n    \"\"\"Given a list of integers, return only the prime integers.\"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrapper\n\n# TARGET FUNCTIONS\n@prime_filter\ndef numbers(from_num: int, to_num: int) -\u003e List[int]:\n    return [num for num in range(from_num, to_num)]\n\n# TEST OUPUT\nprint(numbers(from_num=2, to_num=20))\n```\n\n### C. Choose One Decorator\n\nModify the `choose_one` decorator to select and return a random element from a list of elements returned by a function.\n\n```python\n# MODIFY THIS DECORATOR\ndef choose_one(func):\n    \"\"\"Given a list of elements, select a random one.\"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrapper\n\n# TARGET FUNCTIONS\n@choose_one\ndef available_options() -\u003e List[str]:\n    return [\"A\", \"B\", \"C\"]\n\n# TEST OUPUT\nprint(available_options())\n```\n\n### D. Power Meta-Decorator\n\nModify the `power` meta-decorator to return a tuple where the first element is the original number and the second is the nth power of the number.\n\n```python\n# MODIFY THIS META DECORATOR\ndef power(n: int):\n    \"\"\"Given a number, return a tuple where the first element is the\n    original number and the second is the nth power.\"\"\"\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            return func(*args, **kwargs)\n        return wrapper\n    return decorator\n\n# TARGET FUNCTIONS\n@power(n=5)\ndef get_random_number(from_num: int, to_num: int):\n    return random.randint(a=from_num, b=to_num)\n\n# TEST OUPUT\nprint(get_random_number(50, 100))\n```\n\n### E. Mask Data Decorator\n\nModify the `mask_data` decorator to replace the value of a specified dictionary key with a masked version.\n\n```python\n# MODIFY THIS DECORATOR\ndef mask_data(target_key: str, replace_with: str = \"*\"):\n    \"\"\"Replace the value of a dictionary with a 'masked' version.\"\"\"\n    def decorator(func):\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            return func(*args, **kwargs)\n        return wrapper\n    return decorator\n\n# TARGET FUNCTIONS\n@mask_data(target_key=\"name\")\ndef get_user(name: str, age: int):\n    return {\n        \"name\": name,\n        \"age\": age\n    }\n\n# TEST OUPUT\nprint(\n    get_user(name=\"Alice\", age=30),\n    get_user(name=\"Bob\", age=25),\n    sep=\"\\n\"\n)\n```\n\nPlease ensure you test your decorators with the provided target functions and verify the outputs.\n\n## Section 3: Asynchronous Programming in Python\n\nIn this section, you will work on tasks that involve asynchronous programming using `asyncio`. These tasks are designed to help you understand how to write and manage asynchronous code in Python.\n\n### Task 1: Fetch Data from Multiple APIs Concurrently\n\nIn this task, you will write a program that fetches data from multiple APIs concurrently. This will help you understand how to use `asyncio` to run multiple I/O-bound tasks in parallel.\n\n1. Create a file named `async_fetch.py`.\n2. Write an asynchronous function `fetch_data(url: str) -\u003e dict` that takes a URL and returns the JSON response as a dictionary.\n3. Write another asynchronous function `main(urls: List[str]) -\u003e List[dict]` that takes a list of URLs, fetches data from all the URLs concurrently, and returns a list of the JSON responses.\n4. Use the `aiohttp` library to perform the HTTP requests. Make sure to handle exceptions and retries in case of network errors.\n5. Test your program with at least three different APIs that return JSON data.\n\nExample Test:\n\n```python\nurls = [\n    'https://jsonplaceholder.typicode.com/posts/1',\n    'https://jsonplaceholder.typicode.com/posts/2',\n    'https://jsonplaceholder.typicode.com/posts/3'\n]\n\nasyncio.run(main(urls))\n```\n\n### Task 2: Implement a Simple Asynchronous Web Crawler\n\nIn this task, you will implement a simple asynchronous web crawler that visits a list of URLs, extracts links from the pages, and continues to visit the new links up to a certain depth. This task will help you understand how to manage concurrency and synchronization in an asynchronous context.\n\n1. Create a file named `async_crawler.py`.\n2. Write an asynchronous function `fetch_page(url: str) -\u003e str` that fetches the HTML content of a given URL.\n3. Write a function `extract_links(html: str) -\u003e List[str]` that extracts and returns all the hyperlinks from the HTML content. You can use the `BeautifulSoup` library for this purpose.\n4. Write an asynchronous function `crawl(start_url: str, max_depth: int) -\u003e None` that performs the following:\n   - Fetch the HTML content of the `start_url`.\n   - Extract links from the fetched HTML.\n   - Recursively visit the extracted links up to `max_depth`.\n   - Ensure that each URL is visited only once.\n5. Use `asyncio` and `aiohttp` for asynchronous network operations, and handle exceptions appropriately.\n\nExample Test:\n\n```python\nstart_url = 'https://ganjoor.net/'\nmax_depth = 2\n\nasyncio.run(crawl(start_url, max_depth))\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./Resource/One-Way-Or-Another.jpeg\" alt=\"It Takes Time, One Way or Another\" style=\"width: 60%;\"\u003e\n\u003c/p\u003e\n\n**Best Regards, [Hamidi](https://github.com/smhamidi)**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcourseworks%2Faut_ap_2024_spring_hw5","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcourseworks%2Faut_ap_2024_spring_hw5","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcourseworks%2Faut_ap_2024_spring_hw5/lists"}