Developer Resources¶
This page contains additional information that may be useful to anyone hoping to work on the dashboard’s code.
The app uses a postgreSQL database and the front end is programmed in python using the Flask framework. If you’re trying to get familiar with Flask we recommend this tutorial. We also use SQLAlchemy to manage the database models.
Our production web server uses uWSGI behind an NGINX server. Authentication is handled using OAuth.
Dashboard Structure¶
dashboard/models/models.py
defines the Object Relational Mapping (ORM)
used by SQLAlchemy to map from the relational database to the python objects
used in the code.
All of the ‘views.py’ scripts define the entry points (i.e. valid URLs for
the app). These have been organized into blueprints (dashboard/blueprints
) that group related
functionality together.
If a blueprint requires any HTML, it is stored inside a ‘templates’ folder
nested within the blueprint. All other HTML templates are in
dashboard/templates/
. Templates with _snip.html or in a ‘snips’ subfolder
are used as reusable pieces embedded in other pages.
Changing the Database Schema¶
To keep the database migrations accurate you should:
Make changes to models.py and not in the database directly.
Generate a migrate script with
flask db migrate
so others can apply the changes with a simpleflask db upgrade
Verify that the script generated by
flask db migrate
is accurate. Flask-migrate uses Alembic, which is not capable of reliably detecting all changes to the schema so manual intervention is sometimes needed. See here for detailed info on what to look out for.
Jinja Template Tips¶
Flask’s HTML templates use Jinja to render pages. The most important thing to be aware of is that while you can break up your pages into smaller, more readable, chunks by saving HTML in another file and then importing it with something like this:
{% include 'my_other_file.html' %}
performance-wise, this is not always a good idea. Each and every time the ‘include’ statement is read while the page is constructed, the included file has to be read from the filesystem. File reads are (relatively) slow and if the include is inside of a loop with a large number of iterations, you can easily add extra seconds of load time for a minimal boost in HTML readability.
- Some tips to get the most out of Jinja without adding too much overhead:
If you have a loop and want to ‘include’ the body of the loop from another file, it’s better to keep the loop inside the included file (so the file is opened and read once, rather than once per iteration)
If you have an ‘if’ statement and the body of it is included from another snippet, it’s better to keep the ‘if’ in your original file, so you don’t need to open the snippet just to discover the ‘if’ statement failed
Also note that if you’re organizing your HTML snippets in a nested folder, you always need to give the full path from the root of the template directory to the file you want to include. If you get an error about a missing template, make sure you quoted the name of the file to be included.
# Good {% include 'my_snippet.html' %} # for templates/my_snippet.html {% include 'modals/incidental_findings.html' %} # For templates/modals/incidental_findings.html # Bad {% include my_snippet.html %} # Missing quotes on file name {% include 'incidental_findings.html' %} # This file won't be found without the full path
SQLAlchemy Tips¶
SQLAlchemy is awesome and very powerful BUT sometimes it makes really naive
queries. If you try to work with objects from dashboard.models
like they’re
normal python objects, you can very easily end up generating thousands of queries
without realizing it. For instance, if you tried doing something like this:
from dashboard.models import Site, db cmh = db.session.get(Site, 'CMH') for timepoint in cmh.timepoints: # Do some stuff with the timepoint record here
This loop would generate one query to the database for each timepoint that has the site ‘CMH’. If code like that were embedded in a function called in a Jinja template, then those extra queries would hit every time a user loaded the page. In these sorts of cases it is often better to craft your own queries using SQLAlchemy’s Query API.
Debugging Tips¶
To help with debugging, consider using the Flask Debug Toolbar. It can assist in identifying bottlenecks in load time caused by excess querying by giving you a breakdown of your SQLAlchemy queries.