Created by: Joseph Bettenhausen, 2022, DĂĽsseldorf, Germany
This is a Flask application for the final project of CS50X. The title of the project is called Groundhog
. This app allows users to record sightings of groundhogs within the continental United States.
I chose to use Poetry for environment and dependency management. I have experience with Poetry and found it easy to use. It is also becoming very popular among Python developers and I wanted to have more exposure to it.
Even though I have experience creating Django applications, I chose to make a Flask application. This would allow me to use my Python knowledge and expand my exposure to Flask, which was introduced in Week 9 of the CS50X course.
I opted not to use the CS50 library for SQL and instead chose to use Flask SQL Alchemy. It is an extension for Flask which makes it easier to use SQLAlchemy. SQLAlchemy is an Object Relational Mapper (ORM), which are often used in larger projects to simplify querying and abstract away the specific database systems. As I didn't have a lot of experience with an ORM before, I thought it important to pick one for ease of use, setup, with good documentation, and examples. I wanted to implement SQLAlchemy into the project, because it has received a lot of good reviews for usage with Python.
It is said, that code is more often read than it is written. Therefore formatting and linting are important. That is why I picked the popular packages Black and Flake8 to format and lint my code. Both were easy to setup and immediately useful in improving the code quality. I was also able to set them up on pre-commit check, so that they are always checking the code on every commit.
The tutorial from the official Flask documentation uses Pytest. Pytest is a reliable and detailed framework for testing Python projects not just Flask applications. I was really interested in using it to write my tests and to further my experience with it.
The project focuses on tracking your groundhog sightings.
Since the project uses Flask-Migrate, the following command can be used to update your db to the latest version:
flask db upgrade
The project comes with 2 Zoos that stated they had groundhogs. To insert them into the database use the CLI command:
flask create-zoos
To the run the application from the root project directory execute the command:
flask run
The application consists of a few different pages. The following section gives an overview.
This is the start page for the application and is accessible without logging in.
Here the user can create a username, include their email, and create a password.
On this page the user can login with created username and password from the registraion page.
This page shows all of the sightings from the database using markers.
This page shows a list of zoos in the database. As mentioned above a few default zoos can be created with the CLI.
This page provides a form for the user to add the location about a sighting of a groundhog.
This page shows the results for all sightings in the database.
The project structure is shown below. It includes files, which are not committed to GitHub. They are mentioned here to give a complete picture of the final project. Flask is very flexible and doesn't standardize the layout. I have chosen to use the application factory pattern.
groundhog
|-- .github
| |-- codecov.yml
| |-- workflows
| |-- dev_pipeline.yml
| `-- prod_pipeline.yml
|-- .venv
|-- groundhog
| |-- __init__.py
| |-- auth.py
| |-- helpers.py
| |-- models.py
| |-- routes.py
| |-- static
| | |-- css
| | | |-- main.min.css
| | | |-- main.min.css.map
| | |-- images
| | | |-- groundhog_brand.png
| | | |-- groundhog_main.jpeg
| | | |-- groundhog_sighting.jpeg
| | | |-- habitat_leaf.png
| | | `-- seasons.png
| | |-- node_modules
| | |-- sass
| | `-- main.css
| | |-- package-lock.json
| | |-- package.json
| `-- templates
| |-- auth
| | |-- login.html
| | `-- register.html
| |-- base.html
| |-- history.html
| |-- index.html
| |-- map.html
| |-- sighting.html
| `-- zoo.html
|-- migrations
| |-- README
| |-- alembic.ini
| |-- env.py
| |-- script.py.mako
| `-- versions
| `-- fed7b412c063_initial_migrate.py
|-- tests
| |-- conftest.py
| |-- test_sightings
| |-- functional
| | |-- __init__.py
| | |-- test_auth.py
| | `-- test_routes.py
| |-- integration
| | |-- __init__.py
| | |-- test_config.py
| | `-- test_db.py
| `-- unit
| |-- __init__.py
| |-- test_helpers.py
| `-- test_models.py
|-- .env
|-- .env.template
|-- .flaskenv
|-- .gitignore
|-- .pre-commit-config.yaml
|-- config.py
|-- LICENSE
|-- poetry.lock
|-- pyproject.toml
|-- README.md
|-- requirements.txt
|-- setup.cfg
`-- wsgi.py
The following directory and files exist in the project root directory.
- .venv
- .env
- .env.template
- .flaskenv
- .gitignore
- .pre-commit-config.yaml
- config.py
- LICENSE
- poetry.lock
- pyproject.toml
- README.md
- requirements.txt
- setup.cfg
- wsgi.py
The .venv
directory is not tracked nor uploaded to public repositories. However, I listed it here as an example. This is where Poetry stores my environment for development. Using a separate environment for each project allows me to keep my global workspace clean, use different versions of libraries for each project, and supply a valid requirements.txt
or poetry.lock
files for others to use.
The .env
file contains secrets like passwords and API keys and should not be uploaded to public repositories! I have included a sample as .env.template
as a way for people to get started quickly. They just need to add their specific information and remove .template
from the file name. You can select your configuration via the environment by setting the config class in CONFIG_TYPE
in .env and the class from config.py
. The config.py
shouldn't contain secrets either, so we can upload it to public repositories. This file allows you to have default configurations and build upon that via classes.
The .flaskenv
file is used by Flask to setup environment variables that are needed such as FLASK_APP
and FLASK_DEBUG
, before the application is configured with the above mentioned .env
file.
The .gitignore
file is needed for projects tracked with Git. This file allows you to not upload temporary files, secrets, or caches that are in the project folder to the public repository.
The .pre-commit-config.yaml
file is the configuration needed for the pre-commit package. This is the tool that executes the other tools e.g. Flake8, Black, and end of file checks on every commit. It can be manually run with the following command:
pre-commit run --all-files
The config.py
file contains classes for creating configurations for different environments. This allows for configurations of different environments to be stored in a single file. It also gives the ability to inherit from the default class configuration. I chose this method, because it avoids having repetition in the multiple configurations.
The LICENSE
file is created from the GitHub template for MIT licenses and specifies the usage of this project.
The pyproject.toml
and poetry.lock
files are created by Poetry. The pyproject.toml
is a configuration file from Poetry, which contains the build information defined in PEP 518. That file contains the dependencies for the project. The poetry.lock
file is created by Poetry and this 'pins' the versions defined in the pyproject.toml
file. This should also be uploaded to public repositories so others can install the same version of dependencies that I used.
This README.md
file is the current file. These are a common way to give information on the project, ex. installation or usage documentation.
The requirements.txt
file are often still needed for builds such as GitHub Actions Continuous Integration and Continuous Delivery (CI/CD) or Platform As A Service (PaaS), such as Heroku or Render. Therefore Poetry allows for creating a requirements.txt
file from the poetry.lock
. To create this file, you can run this command:
poetry export --with dev --without-hashes --format=requirements.txt > requirements.txt
The setup.cfg
file is the configuration file need for Flake8. For example, it has it excluded the migrations folder from being checked.
The wsgi.py
file is the entry point for the application. This is often named app.py as well.
The directory .github\workflows contains YAML files for running GitHub actions. The current configruation builds the project and executes the unit tests. The codecov.yml
file is used for configuring non-defaults for the coverage report for the project. The dev_pipeline.yml
runs the CI on any commit to any branch -- other than the trunk branch. The prod_pipeline.yml
runs the CI on any commit to the trunk branch. This workflow also creates and uploads a test report to Codecov. This allows for the usage of the Codecov badge at the top of this README.md. In the future this workflow would also include a CD component and deploy the app to a PAAS.
.github
- codecov.yml
.github\workflows
- dev_pipeline.yml
- prod_pipeline.yml
- __init__.py
- auth.py
- helpers.py
- models.py
- routes.py
The __init__.py
file is what creates the instance of the application. This is called by the wsgi.py file and is where plugins and Blueprints are registered.
The auth.py
file contains the routes for registering, login, and logout functionalities.
The helpers.py
file contains the decorator function for checking if the user is logged in.
The models.py
file contains code for creating the tables and to be able to create, edit, delete records from the database.
The routes.py
file contains the routes for the remaining pages of the application, such as Index/Home, Map, Zoo, Sighting, and Tracking History.
- package-lock.json
- package.json
css
Compiled css from the sass main.scss
file.
images
Contains all the images used in the application.
- groundhog_brand.png
- groundhog_main.jpeg
- groundhog_sighting.jpeg
- habitat_leaf.png
- seasons.png
sass
- main.scss
This package-lock.json
and package.json
files are for showing the version of Bootstrap that was installed during development. This was necessary in order to create the main.scss
file. This contains the css for the project as well as the updating bootstrap default colors. This file is compiled and then put into the css folder as main.min.css
and main.min.css.map
for the application to use.
Contains the templates for the application.
- base.html
- history.html
- index.html
- map.html
- sighting.html
- zoo.html
This base.html
file creates the basic structure html for all other templates. All other templates extend this template.
This history.html
file is the template for the Tracking History page. This index.html
file is the template for the main homepage. This map.html
file is the template of the Map page. This sighting.html
file is the template for the form in order to enter a sighting. This zoo.html
file is the template for showing a list of zoos in the database.
auth
Contains the templates for the auth
Blueprint.
- login.html
- register.html
This login.html
file is the template for allowing a user to log into the application. This register.html
file is the template for creating a new user in the application.
- fed7b412c063_initial_migrate.py
The fed7b412c063_initial_migrate.py
was created by using Flask Migrate when I initialized and created the initial database version. The rest of this folder was generated automatically by the Flask Migrate plugin.
The command to run all the tests, create a report as well as show the lines which are not yet covered is:
pytest --setup-show --cov=groundhog --cov-report term-missing
- conftest.py
unit
- __init__.py
- test_helpers.py
- test_models.py
integration
- __init__.py
- test_config.py
- test_db.py
functional
- __init__.py
- test_auth.py
- test_routes.py
The conftest.py
file is used by Pytest and contains the setup and teardown code for the tests. The test.db
isn't included in the repository as it is created only for some functional tests.
The test_sightings
file is SQL that inserts some test sightings to the database.
I grouped the tests into two directories either unit
, integration
, or functional
which was based on the level of the test.
The unit tests test the models with SQLAlchemy (not the database) test_models.py
. The test_helpers.py
tests the functions in the helper.py
file.
The integration tests test configuring the application test_config.py
or a PostgreSQL database in test_db.py
. The functional tests test the user interactions.
The functional tests test the routes in the auth blueprint in test_auth.py
and the test_routes.py
test routes in routes blueprint.
- Flask application factory tutorial
- Testing Flask applications
- The Flask mega tutorial
- Github Actions examples
- Ecology and Management of the Groundhog (Marmota monax)
- The data for the Habitat map is provided by U.S. Geological Survey (USGS) Gap Analysis Project (GAP), 2018, U.S.Geological Survey - Gap Analysis Project Species Range Maps CONUS_2001: U.S. Geological Survey data release, https://doi.org/10.5066/F7Q81B3R.
- The data for the Range map is provided by U.S. Geological Survey (USGS) Gap Analysis Project (GAP), 2018, U.S.Geological Survey - Gap Analysis Project Species Range Maps CONUS_2001: U.S. Geological Survey data release, https://doi.org/10.5066/F7Q81B3R.
- The original picture for the index page
groundhog_main.jpeg
is found here - The original picture for
groundhog_brand.png
is found here - The original picture for
groundhog_sighting.jpeg
is found here