{"id":27737076,"url":"https://github.com/erikw/cs50p-project","last_synced_at":"2025-04-28T14:36:13.542Z","repository":{"id":272915711,"uuid":"917050205","full_name":"erikw/cs50p-project","owner":"erikw","description":"Final project for the CS50P Python course.","archived":false,"fork":false,"pushed_at":"2025-04-21T17:16:10.000Z","size":683,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-21T18:28:12.831Z","etag":null,"topics":["cs50","cs50p","finalproject","python","visa"],"latest_commit_sha":null,"homepage":"https://cs50.harvard.edu/python/2022/project/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erikw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2025-01-15T09:01:55.000Z","updated_at":"2025-04-21T17:16:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"0ab834ab-17f2-419d-9859-61d74837ec48","html_url":"https://github.com/erikw/cs50p-project","commit_stats":null,"previous_names":["erikw/cs50p-project"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikw%2Fcs50p-project","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikw%2Fcs50p-project/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikw%2Fcs50p-project/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikw%2Fcs50p-project/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikw","download_url":"https://codeload.github.com/erikw/cs50p-project/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251331206,"owners_count":21572437,"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":["cs50","cs50p","finalproject","python","visa"],"created_at":"2025-04-28T14:36:12.867Z","updated_at":"2025-04-28T14:36:13.520Z","avatar_url":"https://github.com/erikw.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CS50P Project - Visa Tool 🛂\n[![SLOC](https://sloc.xyz/github/erikw/cs50p-project?lower=true)](#)\n[![Number of programming languages used](https://img.shields.io/github/languages/count/erikw/cs50p-project)](#)\n[![Top programming languages used](https://img.shields.io/github/languages/top/erikw/cs50p-project)](#)\n[![Number of files in repo](https://img.shields.io/github/directory-file-count/erikw/cs50p-project)](#)\n[![Repo file size](https://img.shields.io/github/repo-size/erikw/cs50p-project\n)](#)\n[![Latest tag](https://img.shields.io/github/v/tag/erikw/cs50p-project)](https://github.com/erikw/cs50p-project/tags)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html)\n\n[![Tests](https://github.com/erikw/cs50p-project/actions/workflows/tests.yml/badge.svg)](https://github.com/erikw/cs50p-project/actions/workflows/tests.yml)\n[![Lint](https://github.com/erikw/cs50p-project/actions/workflows/linter.yml/badge.svg)](https://github.com/erikw/cs50p-project/actions/workflows/linter.yml)\n[![CodeQL](https://github.com/erikw/cs50p-project/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/erikw/cs50p-project/actions/workflows/codeql-analysis.yml)\n[![Dependabot Updates](https://github.com/erikw/cs50p-project/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/erikw/cs50p-project/actions/workflows/dependabot/dependabot-updates)\n\n\u003cp align=\"center\"\u003e\n    \u003c!-- Ref: https://dev.to/azure/adding-a-github-codespace-button-to-your-readme-5f6l --\u003e\n    \u003ca href=\"https://github.com/codespaces/new?hide_repo_select=true\u0026ref=main\u0026repo=917050205\" title=\"Open in GitHub Codespaces\" \u003e\u003cimg alt=\"Open in GitHub Codespaces\" src=\"https://github.com/codespaces/badge.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nFinal [project](https://cs50.harvard.edu/python/2022/project/) in the [CS50P](https://cs50.harvard.edu/python/2022/) Python course.\n\n## Demo\n\u003ca href=\"https://youtu.be/cyB0gfufMFk\" title=\"View demo video\"\u003e\u003cimg src=\"img/demo_video_preview.png\" width=\"580\" alt=\"Demo Video Preview\"\u003e\u003c/a\u003e\n\n*Demo URL: https://youtu.be/cyB0gfufMFk*\n\n\n## Achievements\nOfficial certificate awared as of completion of this course:\n\n\u003ca href=\"https://cs50.harvard.edu/certificates/19eba3e9-7198-4909-a320-eebd70f26b70\" title=\"Official certificate\"\u003e\u003cimg src=\"img/cert_thumbnail.png\" width=\"300\" alt=\"Certificate Preview\"\u003e\u003c/a\u003e\n\n*Verification URL: https://cs50.harvard.edu/certificates/19eba3e9-7198-4909-a320-eebd70f26b70*\n\n\n## Problem Description\nFor this project I chose to write a tool that solves a problem I know well, a problem I often have in my life. This makes it all more motivating as well to develop! As a frequent traveler, I'm often faced with two problems:\n\n* I need to research Visa and entry information for a new country that I am interested in or are going to visit, to arrange Visa applications etc. in time. Going out on Google each time and find a different site for each country has some problems with assessing the trustworthiness of the found sound, and maybe even translate the language to English.\n\n* Once I've actually arrived in a country with an entry stamp in my passport, the visit is typically valid for X number of days since the day of entry e.g. 60 or 90 days. It's important to mark in my calendar the exact last day I can stay in the country, to not get in trouble with overstaying. The problem is to calculate that. Given different month lengths and leap years, and having to remember that the day of entry counts as day 1, it's easy to calculate the wrong date +/-1.\n\nThese two problems I've solved with the **Visa Tool** developed in this project.\n\nI took this course not to learn Python (though I did learn some new things that I've not seen or noticed before in the language!), but to be able to help other students learn Python by taking this course.\n\n## Solution\nTo solve these two related but distinct problems, the Visa Tool operates in two modes each targeting the respective problems.\n\nTo get Visa information, the best source I could find on the Internet is [projectvisa.com](https://www.projectvisa.com/) that has information for all countries in the world. While not authoritative, it gives a good enough presentation for the initial Visa research. Additionally it has links to the official websites for each country to check. This satisfies my needs. Unfortunately there's no public API, so I had to resort to web scraping, just showing the text part of the rendered HTML. Considering that this is a tool used to query the website maybe 1-2 times a month or so, this should not cause any denial of service.\n\nTo calculate the last allowed day to stay in a country, the two parameters are the date of entry in the county and the length of allowed stay. Now this is not rocket science directly, it's a matter of one addition and one subtraction operation. The key is to actually add and subtract the right things (every time), which is why this is a helpful tool compared to manual calculating or finger counting in the calendar.\n\n## Usage\nIf you don't specify any CLI arguments, the program will launch in interactive mode. Run the program with `-h` or `\u003ccommand\u003e -h` for the most up to date help descriptions.\n\n\n```command\n$ pip install -r requirements.txt\n$ ./project.py -h\n _   _  _               _____                _\n| | | |(_)             |_   _|              | |\n| | | | _  ___   __ _    | |    ___    ___  | |\n| | | || |/ __| / _` |   | |   / _ \\  / _ \\ | |\n\\ \\_/ /| |\\__ \\| (_| |   | |  | (_) || (_) || |\n \\___/ |_||___/ \\__,_|   \\_/   \\___/  \\___/ |_|\n\n\nusage: Visa Tool [-h] [-v] {visa_info,exit_calc} ...\n\nUtility for Visa related queries. See subcommands.\n\npositional arguments:\n  {visa_info,exit_calc}\n                        Optional Commands. If none is given, the program will\n                        run in interactive mode. Run $(python project.py\n                        \u003ccommand\u003e -h) for more info about a command.\n    visa_info           Get Visa information for a country.\n    exit_calc           Calculate the last day you can stay in a country given\n                        the entry date and entry stamp validity length.\n\noptions:\n  -h, --help            show this help message and exit\n  -v, --version         show program's version number and exit\n\nFind support and source code at https://github.com/erikw/cs50p-project\n```\n\n## Implementation\nHere's a brief implementation description in the format of looking at each module and file's responsibility and actions.\n\nFor this project, I opted to add [type hints](https://docs.python.org/3/library/typing.html) and check with the [`mypy`](https://github.com/python/mypy) tool for compliancy.\n\n#### :page_facing_up: [`.gitignore`](.gitignore)\nSelf-explanatory. What to ignore from being tracked in the VCS.\n\n#### :page_facing_up: [`.mypy.ini`](.mypy.ini)\nConfiguration file for the static type checker.\n\n#### :page_facing_up: [`.pytest.ini`](.pytest.ini)\nConfiguration file for `pytest`. Needed to work around a [bug](https://github.com/Exahilosys/survey/issues/38) that I discovered in the library I used for user input querying.\n\n#### :page_facing_up: [`constants.py`](constants.py)\nContains all constants. I broke these out to its own module to avoid circular dependencies between modules that use the same constants.\n\n#### :page_facing_up: [`countries.csv`](countries.csv)\nThis is a static list of all allowed countries that projectvisa.com knows about. It's in the format that is used in their URL scheme as there could be different ways of abbreviating or styling a country's name. A future extension of this program would introduce a second column being the display name, and use this in the interactive ui selector.\n\n#### :page_facing_up: [`mode_cli.py`](mode_cli.py)\nWhen the CLI mode is activated the functions in this module are used to parse CLI arguments and call the requested functionality with the right parameters.\n\n#### :page_facing_up: [`mode_interactive.py`](mode_interactive.py)\nWhen the program is launched in interactive mode (meaning no CLI args), then the functions in here will take care of querying the user in the right order for the feature to activate and the corresponding parameters needed for that feature.\n\n#### :page_facing_up: [`project.py`](project.py)\nThis is the main module, as per specified mandatory naming convention according to the problem description. The main module will set up a signal handler for the SIGINT (ctr+c) signal and handle a clean exit, print a welcome screen with ASCII art, and then chose to start the program in CLI or interactive mode as determined by whether or not any CLI arguments was passed by the invoker.\n\n#### :page_facing_up: [`requirements.txt`](requirements.txt)\nPip Python package manager listing needed runtime dependencies and development tools used.\n\n#### :page_facing_up: [`test_project.py`](test_project.py)\nTesting functions in `project.py` according to the naming conventions layed out in the problem description.\n\n#### :page_facing_up: [`ui.py`](ui.py)\nThis module takes responsibility for producing much of the output of the user and the formatting of this.\n\n#### :page_facing_up: [`visa.py`](visa.py)\nThe Visa module contains the main business logic of this program including date calculation and Visa information fetching and parsing.\n\n\n## Development\nRemember to run:\n```command\n$ pip install -r requirements.txt\n$ pytest\n$ mypy .\n$ isort .\n$ black .\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikw%2Fcs50p-project","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikw%2Fcs50p-project","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikw%2Fcs50p-project/lists"}