To develop locally:
Linux:
$ python3 -m venv .venv
$ . .venv/bin/activate
(.venv) $ pip install -e . -r requirements.txt
(.venv) $ cp config.json.example config.json
(.venv) $ FLASK_DEBUG=1 python3 -m intermittent_tracker.flask_server
Windows (make sure you are in the correct directory):
python -m venv .venv
.venv\Scripts\activate
(.venv) pip install -e . -r requirements.txt
(.venv) copy config.json.example config.json
(.venv) set FLASK_DEBUG=1 python -m intermittent_tracker.flask_server
To run tests:
Linux:
(.venv) $ python3 -m intermittent_tracker.tests
Windows:
(.venv) python -m intermittent_tracker.tests
To generate a dashboard_secret
for config.json:
Linux:
$ python3 -c 'import secrets; print(secrets.token_urlsafe())'
Windows:
python -c "import secrets; print(secrets.token_urlsafe())"
To update the pinned dependencies in the lockfile:
Linux:
(.venv) $ pip freeze --exclude-editable > requirements.txt
Windows:
(.venv) pip freeze --exclude-editable > requirements.txt
- Python 3.7+
- SQLite 3.22.0+
- Ubuntu 18.04 LTS has libsqlite3-0 = 3.22.0
This means your pip is too old. Upgrade your system pip…
Linux:
(.venv) $ deactivate
$ rm -R .venv
$ pip3 install -U pip
Windows:
(.venv) deactivate
rmdir /s .venv
pip install -U pip
…or try virtualenv instead of venv.
Linux:
(.venv) $ pip --version
pip 9.0.1 from /path/to/intermittent-tracker/.venv/lib/python3.7/site-packages (python 3.7)
(.venv) $ deactivate
$ rm -R .venv
$ virtualenv -p python3.7 .venv
$ . .venv/bin/activate
(.venv) $ pip --version
pip 23.0 from /path/to/intermittent-tracker/.venv/lib/python3.7/site-packages/pip (python 3.7)
Windows:
(.venv) pip --version
(.venv) deactivate
rmdir /s .venv
virtualenv -p python3.7 .venv
.venv\Scripts\activate
(.venv) pip --version
Below are some recommendations to ensure compatibility:
- don’t use UPSERT aka INSERT ON CONFLICT (unavailable < SQLite 3.24.0)
- don’t use generated columns (unavailable < SQLite 3.31.0)
- don’t use JSON functions (optional < SQLite 3.38.0)
- give each table foo a
"foo_id" INTEGER PRIMARY KEY
with exactly that syntax (rowid alias)- having a rowid alias means you can use Cursor.lastrowid, and ensures rowid is stable under VACUUM
"foo"."foo_id"
is a bit redundant, but"id"
can get accidentally shadowed in row dicts- non-rowid primary keys like (path,subtest) are cute, but they waste space and misbehave on NULL
- NULL in sqlite behaves like NaN sometimes, so if treating NULL as an ordinary column value:
- compare those columns with IS, not = (the latter yields NULL if either side is NULL)
- ok to create non-UNIQUE indexes involving those columns (NULL can be indexed)
- ok to GROUP BY those columns (NULL values are equal in this context)
- ok to SELECT DISTINCT those columns (NULL values are equal in this context)
- don’t create UNIQUE indexes involving those columns (they won’t do what you want)
- don’t create UNIQUE constraints involving those columns (they won’t do what you want)
- don’t create PRIMARY KEY constraints involving those columns (they won’t do what you want)
- don’t create FOREIGN KEY constraints involving those columns (they won’t do what you want)
- when writing schema migrations:
- check the SQLite guide to schema changes
- if new columns can’t be filled in SQLite alone (e.g. crc32), make them NULL and update in application code
- don’t modify old migrations that have been deployed to production