{"id":25841936,"url":"https://github.com/courseworks/aut_ap_2024_spring_hw2","last_synced_at":"2025-06-12T17:32:20.745Z","repository":{"id":229375057,"uuid":"776533682","full_name":"courseworks/AUT_AP_2024_Spring_HW2","owner":"courseworks","description":"The second assignment for AUT's Advanced Programming course (Spring 2024), featuring a banking system simulation project. It includes Person, Account, and Bank classes to manage transactions and operations, enhancing secure programming and data handling skills.","archived":false,"fork":false,"pushed_at":"2024-05-03T12:13:05.000Z","size":96,"stargazers_count":3,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-01T05:32:49.825Z","etag":null,"topics":["banking-applications","cpp","homework","homework-assignments"],"latest_commit_sha":null,"homepage":"","language":"C++","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-03-23T19:14:48.000Z","updated_at":"2024-12-24T14:26:23.000Z","dependencies_parsed_at":"2024-05-03T13:44:36.149Z","dependency_job_id":"7859407a-0499-4890-b18d-4ad23b6fc9c5","html_url":"https://github.com/courseworks/AUT_AP_2024_Spring_HW2","commit_stats":null,"previous_names":["courseworks/aut_ap_2024_spring_hw2"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/courseworks/AUT_AP_2024_Spring_HW2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/courseworks","download_url":"https://codeload.github.com/courseworks/AUT_AP_2024_Spring_HW2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/courseworks%2FAUT_AP_2024_Spring_HW2/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259512282,"owners_count":22869378,"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":["banking-applications","cpp","homework","homework-assignments"],"created_at":"2025-03-01T05:32:50.848Z","updated_at":"2025-06-12T17:32:20.721Z","avatar_url":"https://github.com/courseworks.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n\u003cstrong\u003eAUT_AP_2024_Spring Homework 2\u003c/strong\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cstrong\u003e Deadline: 20th of Farvardin - 23:59 o'clock\u003c/strong\u003e\n\u003c/p\u003e\n\n# **Banking System Development**\n\nThis project is designed to simulate a simplified banking system, focusing on the core functionalities of managing customer accounts, bank operations, and financial transactions. By implementing the `Person`, `Account`, and `Bank` classes.\n\n## Project Overview\n\n- **Person Class**: Represents an individual with attributes like name, age, gender, and financial details. You'll practice data encapsulation and learn how to manage personal information securely.\n- **Account Class**: Simulates a bank account, linking customers to their financial transactions. This class will introduce you to managing complex relationships within software, focusing on secure access and modification of sensitive data.\n- **Bank Class**: Acts as the central node that oversees accounts, loans, and customer interactions. Here, you'll apply advanced programming concepts to implement banking operations such as deposits, withdrawals, and loan management.\n\n## Learning Outcomes\n\n- **Secure Programming**: Learn how to handle sensitive information securely, using hashing for fingerprints and ensuring data integrity.\n- **Advanced Object-Oriented Concepts**: Deepen your understanding of object-oriented principles, including the use of classes, objects, and the implementation of the spaceship operator for custom sorting and comparison logic.\n- **Error Handling and Validation**: Enhance your skills in robust programming by implementing error checking, validations, and exception handling to manage user input and system operations safely.\n- **Practical Application of Data Structures**: Apply data structures like `std::map` and `std::vector` in a practical context, managing relationships between complex entities.\n\n---\n\n## **Person Class Implementation Guide**\n\nThe `Person` class is a fundamental component of our project, designed to represent individuals within a system that simulates or manages various aspects of real-life entities. This class encapsulates personal information such as name, age, gender, and unique identifiers, ensuring data integrity and providing a standardized way to interact with individual person data.\n\n## Implementation Considerations\n\nWhen implementing the `Person` class in the corresponding `.cpp` file, several key considerations must be taken into account to ensure the class functions correctly and securely:\n\n### 1. Gender Validation\n\nThe `Person` class restricts the `gender` attribute to either \"Female\" or \"Male\". During the instantiation of a `Person` object or when setting the gender, any input other than these two specified values should result in an error. This can be implemented by throwing a standard exception (e.g., `std::invalid_argument`) if the gender does not match the expected values.\n\n### 2. Fingerprint Hashing\n\nFor security reasons, the actual fingerprint string is not stored within the `Person` class. Instead, a hashed version of the fingerprint is kept. Use the `std::hash\u003cstd::string\u003e` function from the `\u003cfunctional\u003e` header to convert the fingerprint string to its hashed representation. This hash is subsequently used for authentication purposes.\n\n### 3. Socioeconomic Rank Validation\n\nThe socioeconomic rank is an integer value ranging from 1 to 10, representing the person's social and economic status. This rank influences future functionalities, such as loan acquisition from the bank. Ensure that the socioeconomic rank is within the specified range when setting this attribute. If the input rank is outside this range, throw an exception to indicate the error.\n\n### 4. Implementation of the Spaceship Operator\n\nThe spaceship operator (`\u003c=\u003e`) is required for enabling comparisons between `Person` objects, particularly when using `Person` instances as keys in a `std::map`. Implement this operator to compare `Person` objects based on their `hashed_fingerprint`, as it is unique and provides a reliable method for comparison.\n\n```cpp\n#ifndef PERSON_H // Prevents double inclusion of this header\n#define PERSON_H\n\n#include \u003ccompare\u003e  // For std::strong_ordering\n#include \u003coptional\u003e // For std::optional\n#include \u003cstring\u003e   // For std::string\n\n// Models an individual with personal attributes\nclass Person {\npublic:\n    // Constructor with personal attributes\n    Person(std::string \u0026name, size_t age, std::string \u0026gender,\n           std::string \u0026fingerprint, size_t socioeconomic_rank, bool is_alive);\n\n    // Getters\n    std::string get_name() const;\n    size_t get_age() const;\n    std::string get_gender() const;\n    size_t get_hashed_fingerprint() const;\n    size_t get_socioeconomic_rank() const;\n    bool get_is_alive() const;\n\n    // Setters\n    bool set_age(size_t age);\n    bool set_socioeconomic_rank(size_t rank);\n    bool set_is_alive(bool is_alive);\n\n    // Spaceship operator for Person comparison\n    std::strong_ordering operator\u003c=\u003e(const Person \u0026other) const;\n\n    // Outputs person information, supports writing to file, if passed\n    void get_info(std::optional\u003cstd::string\u003e file_name = std::nullopt) const;\n\nprivate:\n    const std::string name;\n    size_t age;\n    const std::string gender;\n    const size_t hashed_fingerprint;\n    size_t socioeconomic_rank;\n    bool is_alive;\n};\n\n#endif // PERSON_H\n```\n\n---\n\n## **Account Class Implementation Guide**\n\nThe `Account` class plays a pivotal role in our banking system, representing bank accounts that hold crucial information such as account balance, ownership, and status. This class ensures secure access and modification of account details, encapsulating the complexity of managing financial transactions and account credentials.\n\n## Implementation Considerations\n\nTo ensure the `Account` class functions correctly and securely within our system, the following considerations must be meticulously implemented:\n\n### 1. Initialization of Member Variables\n\nAll member variables not directly initialized via the constructor must be zero-initialized to ensure a clean and predictable starting state. This is particularly important for numeric and boolean data types such as `balance` and `account_status`.\n\n### 2. Generation of Account Number\n\nThe account number is a unique 16-digit identifier stored as a string. During the construction of an `Account` object, this number must be randomly generated to ensure uniqueness. Consider using functions from `\u003crandom\u003e` or another suitable method for generating this number.\n\n### 3. Secure Access to Credentials\n\nAccess to sensitive credentials (CVV2, password, expiration date) requires the owner's fingerprint authentication. Implement a verification mechanism that compares the hashed version of the provided fingerprint against the owner's hashed fingerprint. If authentication fails, throw an appropriate error.\n\n### 4. Implementation of the Spaceship Operator\n\nImplement the spaceship operator for account comparison, primarily to facilitate the use of `Account` objects as keys in a `std::map`. Compare accounts based on their unique account numbers.\n\n### 5. Information Display with `get_info`\n\nImplement the `get_info` method to display or save account information. Exclude credentials from the output. If a file name is provided, write the information to a file; otherwise, print it to the terminal. Ensure the output is well-formatted and informative.\n\n```cpp\n#ifndef ACCOUNT_H // Prevents double inclusion of this header\n#define ACCOUNT_H\n\n#include \u003ccompare\u003e  // For std::strong_ordering\n#include \u003coptional\u003e // For std::optional\n#include \u003cstring\u003e   // For std::string\n\nclass Bank; // Forward declaration of Bank\nclass Person; // Forward declaration of Person\n\n// Represents a bank account with owner, bank, balance, status, and credentials\nclass Account {\n    friend class Bank; // Make Bank a friend of Account for full access\n\npublic:\n    // Constructor with owner, bank, and password\n    Account(const Person* const owner, const Bank* const bank, std::string\u0026 password);\n\n    // Getters\n    const Person* get_owner() const;\n    double get_balance() const;\n    std::string get_account_number() const;\n    bool get_status() const;\n\n    // Getters requiring owner's fingerprint for authentication\n    std::string get_CVV2(std::string\u0026 owner_fingerprint) const;\n    std::string get_password(std::string\u0026 owner_fingerprint) const;\n    std::string get_exp_date(std::string\u0026 owner_fingerprint) const;\n\n    // Setters requiring owner's fingerprint for authentication\n    bool set_password(std::string\u0026 password, std::string\u0026 owner_fingerprint);\n\n    // Spaceship operator for Account comparison\n    std::strong_ordering operator\u003c=\u003e(const Account\u0026 other) const;\n\n    // Outputs account information, supports writing to file\n    void get_info(std::optional\u003cstd::string\u003e file_name = std::nullopt) const;\n\nprivate:\n    // Member variables\n    Person* owner;\n    const Bank* bank;\n    const std::string account_number;\n    double balance;\n    bool account_status;\n\n    // Credential variables\n    const std::string CVV2;\n    std::string password;\n    std::string exp_date;\n};\n\n#endif // ACCOUNT_H\n```\n\n---\n\n## **Bank Class Implementation Guide**\n\nThe `Bank` class is a cornerstone of our banking system simulation, responsible for managing all interactions between accounts and their owners, handling financial operations like deposits, withdrawals, and loans, and ensuring secure transactions. This class encapsulates the complexity of a banking system and serves as an excellent case study in object-oriented programming and security best practices.\n\n## Implementation Considerations\n\nImplementing the `Bank` class requires attention to detail, particularly regarding security, data integrity, and the simulation of real-world banking operations. Below are key considerations for a robust implementation:\n\n### 1. Secure Fingerprint Storage\n\nEach bank has a unique fingerprint. However, storing this fingerprint directly within the class is insecure. Instead, store a hashed version of the fingerprint using `std::hash\u003cstd::string\u003e` from the `\u003cfunctional\u003e` header. This hashed fingerprint is used for authenticating bank operations.\n\n### 2. Account to Customer Mapping\n\nThe `account_2_customer` variable maps accounts to their respective owners. It's crucial that both the key (Account) and the value (Person) are pointers, not copies, to ensure data consistency and avoid unnecessary duplication.\n\n### 3. Multiple Accounts Per Person\n\nIn the real world, individuals may have multiple bank accounts. Reflect this in your simulation by using a `std::map` with `Person*` as keys and `std::vector\u003cAccount*\u003e` as values for the `customer_2_accounts` variable.\n\n### 4. Loan Management\n\nManaging loans involves keeping track of paid and unpaid amounts (`customer_2_paid_loan` and `customer_2_unpaid_loan`). Ensure accurate accounting for each customer across all their accounts. Implement logic to calculate loan eligibility based on socioeconomic rank and existing balances.\n\n### 5. Interest and Profit Tracking\n\nWhen a customer pays back a loan, they also pay interest, which constitutes the bank's profit. Accumulate this profit in the `bank_total_balance` variable. Ensure all loan payments are correctly processed to reflect on both the customer's loan balance and the bank's total profit.\n\n### 6. Loan and Unpaid Loan Tracking\n\nUse `bank_total_loan` to track the total amount of outstanding loans issued by the bank. This helps in financial forecasting and risk management.\n\n### 7. Zero Initialization\n\nEnsure that all member variables not explicitly initialized in the constructor are zero-initialized to prevent undefined behavior.\n\n### 8. Creating and Deleting Accounts and Customers\n\n- **Creating Accounts**: Authenticate the owner's identity via their fingerprint before creating an account. Update all related member variables accordingly.\n- **Deleting Accounts**: Authenticate the owner before deletion. Update all related member variables to reflect the change.\n- **Deleting Customers**: Remove all information related to a person from the bank's records without \"deleting the person.\", **Remember, deleting a customer's account doesn't mean we're deleting the person!**\n\n### 9. Financial Operations\n\n- **Deposits and Withdrawals**: Verify the owner's fingerprint before processing. For withdrawals, also ensure sufficient account balance.\n- **Transfers**: Authenticate using the owner's fingerprint and verify all provided credentials. Check for sufficient funds before transferring.\n- **Loans**: Calculate loan eligibility based on socioeconomic rank and existing balances. Ensure the requested amount is within the permissible limit and update loan-related variables upon approval.\n\n### 10. Socioeconomic Rank Upgrades\n\nImplement logic to upgrade a person's socioeconomic rank based on their loan repayment history. For example, paying off a total loan amount equal to `10^rank` triggers a rank upgrade.\n\n### 11. Displaying Bank Information\n\nImplement the `get_info` function to output detailed bank information in an informative and well-formatted manner. Avoid disclosing any credentials.\n\n\n### 12. Taking a Loan\n\nWhen a customer requests a loan, the bank evaluates their eligibility based on the following criteria:\n\n- **Owner Authentication**: Verify the owner's identity by comparing the hash of the provided fingerprint against the owner's stored hashed fingerprint.\n- **Loan Amount Calculation**: The maximum loan amount a customer can request is determined by their socioeconomic rank. Specifically, a customer can borrow up to `(10 * rank)%` of the total balance across all their accounts.\n  - For example, if a customer has a socioeconomic rank of 8 and a total balance of $10,000 across all accounts, they can request a loan of up to 80% of $10,000, which equals $8,000.\n- **Total Loan Limit**: Ensure the requested loan does not exceed the customer's loan limit, considering their current unpaid loans.\n- **Loan Distribution**: If the loan request is approved, distribute the loan amount to the specified account and update the relevant loan tracking variables (`bank_total_loan`, `customer_2_unpaid_loan`).\n\n### 13. Paying a Loan\n\nWhen a customer pays back part or all of a loan, the bank processes the payment as follows:\n\n- **Interest**: Incorporate an additional `10/rank` percent interest charge on the loan payment. The interest serves as the bank's profit and is added to `bank_total_balance`. this amount is calculated when you take a loan, not when you pay it.\n- **Loan Update**: Deduct the payment from the customer's total unpaid loan and update `customer_2_paid_loan` and `customer_2_unpaid_loan` accordingly.\n- **Socioeconomic Rank Upgrade**: After the payment, check if the customer's total paid loan reaches a threshold for upgrading their socioeconomic rank. The threshold is `10^rank`. If the threshold is met, increase the customer's rank by one.\n  - For instance, if a customer with a rank of 4 pays off a total loan amount reaching $10,000, their rank is upgraded to 5.\n\n### 15. Upgrading Socioeconomic Rank\n\nThe socioeconomic rank of a customer affects their loan eligibility and represents their reliability and financial status within the bank. The upgrade mechanism encourages customers to maintain good financial habits, such as timely loan repayment.\n\n- **Rank Upgrade Criteria**: Once a customer's total paid loan exceeds `10^rank`, their socioeconomic rank is incremented by one. This progression allows customers to access greater loan amounts and benefits within the bank.\n\n```cpp\n#ifndef BANK_H // Prevents double inclusion of this header\n#define BANK_H\n\n#include \u003ccompare\u003e  // For std::strong_ordering\n#include \u003cmap\u003e      // For std::map\n#include \u003coptional\u003e // For std::optional\n#include \u003cstring\u003e   // For std::string\n#include \u003cvector\u003e   // For std::vector\n\nclass Account; // Forward declaration of Account\nclass Person; // Forward declaration of Person\n\n// Represents a banking institution\nclass Bank {\npublic:\n    // Constructor with bank name and security fingerprint\n    Bank(const std::string\u0026 bank_name, const std::string\u0026 bank_fingerprint);\n\n    ~Bank(); // Destructor\n\n    // Bank operations\n    Account* create_account(Person\u0026 owner, const std::string\u0026 owner_fingerprint, std::string password);\n    bool delete_account(Account\u0026 account, const std::string\u0026 owner_fingerprint);\n    bool delete_customer(Person\u0026 owner, const std::string\u0026 owner_fingerprint);\n    bool deposit(Account\u0026 account, const std::string\u0026 owner_fingerprint, double amount);\n    bool withdraw(Account\u0026 account, const std::string\u0026 owner_fingerprint, double amount);\n    bool transfer(Account\u0026 source, Account\u0026 destination, const std::string\u0026 owner_fingerprint,\n                  const std::string\u0026 CVV2, const std::string\u0026 password, const std::string\u0026 exp_date, double amount);\n    bool take_loan(Account\u0026 account, const std::string\u0026 owner_fingerprint, double amount);\n    bool pay_loan(Account\u0026 account, double amount);\n\n    // Getters\n    const std::string\u0026 get_bank_name() const;\n    size_t get_hashed_bank_fingerprint() const;\n\n    // Getters requiring bank authentication\n    const std::vector\u003cPerson*\u003e\u0026 get_bank_customers(std::string\u0026 bank_fingerprint) const;\n    const std::vector\u003cAccount*\u003e\u0026 get_bank_accounts(std::string\u0026 bank_fingerprint) const;\n    const std::map\u003cAccount*, Person*\u003e\u0026 get_account_2_customer_map(std::string\u0026 bank_fingerprint) const;\n    const std::map\u003cPerson*, std::vector\u003cAccount*\u003e\u003e\u0026 get_customer_2_accounts_map(std::string\u0026 bank_fingerprint) const;\n    const std::map\u003cPerson*, double\u003e\u0026 get_customer_2_paid_loan_map(std::string\u0026 bank_fingerprint) const;\n    const std::map\u003cPerson*, double\u003e\u0026 get_customer_2_unpaid_loan_map(std::string\u0026 bank_fingerprint) const;\n    double get_bank_total_balance(std::string\u0026 bank_fingerprint) const;\n    double get_bank_total_loan(std::string\u0026 bank_fingerprint) const;\n\n    // Account Setters requiring owner and bank authentication\n    bool set_owner(Account\u0026 account, const Person* new_owner, std::string\u0026 owner_fingerprint, std::string\u0026 bank_fingerprint);\n\n    // Account Setters requiring bank authentication\n    bool set_account_status(Account\u0026 account, bool status, std::string\u0026 bank_fingerprint);\n    bool set_exp_date(Account\u0026 account, std::string\u0026 exp_date, std::string\u0026 bank_fingerprint);\n\n    // Outputs bank information, supports writing to file\n    void get_info(std::optional\u003cstd::string\u003e file_name = std::nullopt) const;\n\nprivate:\n    // Private member variables\n    const std::string bank_name;\n    const size_t hashed_bank_fingerprint;\n    std::vector\u003cPerson*\u003e bank_customers;\n    std::vector\u003cAccount*\u003e bank_accounts;\n    std::map\u003cAccount*, Person*\u003e account_2_customer;\n    std::map\u003cPerson*, std::vector\u003cAccount*\u003e\u003e customer_2_accounts;\n    std::map\u003cPerson*, double\u003e customer_2_paid_loan;\n    std::map\u003cPerson*, double\u003e customer_2_unpaid_loan;\n    double bank_total_balance; // Total bank profit\n    double bank_total_loan; // Total loans issued\n};\n\n#endif // BANK_H\n```\n\n---\n\n## Utilizing `utils.cpp` for Common Functions\n\nAs you work through the implementation of the banking system, you'll likely find certain operations and functions recur across different classes. To streamline your code and enhance maintainability, you're encouraged to consolidate these frequently used functionalities into a single file: `utils.cpp`.\n\n### What Goes into `utils.cpp`?\n\nCommon tasks such as hashing fingerprints, generating random numbers, or any utility function that supports your classes across `Person`, `Account`, and `Bank` implementations are perfect candidates for `utils.cpp`.\n\n### Benefits\n\n- **Code Reusability**: Write once, use everywhere. Avoid duplicating code across multiple files.\n- **Maintainability**: Centralizing utility functions makes your codebase easier to manage and update.\n- **Readability**: Keeps your class implementations clean and focused on their primary responsibilities.\n\n**_Remember to keep your code DRY (Don't Repeat Yourself)!_**\n\n---\n\n## **Final Step: How To Test Your Program**\n\nIf you want to debug your code, set the `if` statement to `true`. This will allow you to place your debugging code in the designated section. Once you're done with the debugging process, remember to set the `if` statement back to `false` to test your program using the provided `unit-test.cpp`.\n\nFurthermore, whatever code you write should be implemented in the `Bank.cpp`, `Account.cpp`, `Person.cpp` and `utils.cpp` files. Please refrain from making any changes to other files in the project.\n\n```cpp\n#include \u003ciostream\u003e\n#include \u003cgtest/gtest.h\u003e\n#include \"Bank.h\"\n\nint main(int argc, char **argv)\n{\n    if (true) // Set to false to run unit-tests\n    {\n        // Debug section: Place your debugging code here\n    }\n    else\n    {\n        ::testing::InitGoogleTest(\u0026argc, argv);\n        std::cout \u003c\u003c \"RUNNING TESTS ...\" \u003c\u003c std::endl;\n        int ret{RUN_ALL_TESTS()};\n        if (!ret)\n            std::cout \u003c\u003c \"\u003c\u003c\u003cSUCCESS\u003e\u003e\u003e\" \u003c\u003c std::endl;\n        else\n            std::cout \u003c\u003c \"FAILED\" \u003c\u003c std::endl;\n    }\n    return 0;\n}\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./Resource/Thank_us_later.jpg\" alt=\"Say Hello To Other Errors\" 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_hw2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcourseworks%2Faut_ap_2024_spring_hw2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcourseworks%2Faut_ap_2024_spring_hw2/lists"}