diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 89a6de26..6b002fda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.9" cache: "pip" - name: Setup, build, install run: | @@ -34,9 +34,14 @@ jobs: name: Test runs-on: ubuntu-22.04 timeout-minutes: 15 + strategy: + matrix: + postgis_versions: + - 11-3.3 + - 15-3.4 steps: - uses: actions/checkout@v4 - name: Start docker stack - run: docker compose up db -d + run: POSTGIS_VERSION=${{ matrix.postgis_versions }} docker compose up db -d - name: Running tests run: docker compose run qgis_tester diff --git a/.gitignore b/.gitignore index ef6172a3..70fccebd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ .vscode/settings.json *.nix *.log -**/__pycache__/ \ No newline at end of file +**/__pycache__/ +test_outputs/ \ No newline at end of file diff --git a/comptages/__init__.py b/comptages/__init__.py index ca5dcd5e..3d2f7801 100644 --- a/comptages/__init__.py +++ b/comptages/__init__.py @@ -46,7 +46,7 @@ def prepare_django(default_db=None, **additional_settings): USE_TZ=True, TIME_ZONE="Europe/Zurich", SECRET_KEY="09n+dhzh+02+_#$!1+8h-&(s-wbda#0*2mrv@lx*y#&fzlv&l)", - **additional_settings + **additional_settings, ) django.setup() diff --git a/comptages/test/test_import.py b/comptages/test/test_import.py index 6f438c09..6630c7e8 100644 --- a/comptages/test/test_import.py +++ b/comptages/test/test_import.py @@ -1,5 +1,5 @@ import pytz -from datetime import datetime +from datetime import datetime, timezone, tzinfo from django.test import TransactionTestCase from django.core.management import call_command @@ -42,14 +42,17 @@ def test_import_vbv1(self): importer.import_file(utils.test_data_path("00056520.V01"), count) self.assertEqual(models.CountDetail.objects.count(), 18114) + first = models.CountDetail.objects.first() + last = models.CountDetail.objects.last() + assert first + assert last - tz = pytz.timezone("Europe/Zurich") + zurich_timezone = pytz.timezone("Europe/Zurich") + first = first.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) + last = last.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) - first = tz.normalize(models.CountDetail.objects.first().timestamp) - last = tz.normalize(models.CountDetail.objects.last().timestamp) - - self.assertEqual(first, tz.localize(datetime(2021, 10, 15, 9, 46, 43, 500000))) - self.assertEqual(last, tz.localize(datetime(2021, 10, 15, 23, 59, 54, 600000))) + self.assertEqual(first, datetime(2021, 10, 15, 9, 46, 43, 500000)) + self.assertEqual(last, datetime(2021, 10, 15, 23, 59, 54, 600000)) def test_import_mc(self): model = models.Model.objects.all()[0] @@ -76,13 +79,18 @@ def test_import_mc(self): self.assertEqual(models.CountDetail.objects.count(), 25867) - tz = pytz.timezone("Europe/Zurich") + first = models.CountDetail.objects.first() + last = models.CountDetail.objects.last() + zurich_timezone = pytz.timezone("Europe/Zurich") + + assert first + assert last - first = tz.normalize(models.CountDetail.objects.first().timestamp) - last = tz.normalize(models.CountDetail.objects.last().timestamp) + first = first.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) + last = last.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) - self.assertEqual(first, tz.localize(datetime(2021, 9, 10, 4, 16, 16))) - self.assertEqual(last, tz.localize(datetime(2021, 9, 21, 8, 2, 15))) + self.assertEqual(first, datetime(2021, 9, 10, 4, 16, 16)) + self.assertEqual(last, datetime(2021, 9, 21, 8, 2, 15)) def test_import_int2(self): model = models.Model.objects.all()[0] @@ -107,13 +115,17 @@ def test_import_int2(self): importer.import_file(utils.test_data_path("10020260.A01"), count) - tz = pytz.timezone("Europe/Zurich") + first = models.CountDetail.objects.first() + last = models.CountDetail.objects.last() + assert first + assert last - first = tz.normalize(models.CountDetail.objects.first().timestamp) - last = tz.normalize(models.CountDetail.objects.last().timestamp) + zurich_timezone = pytz.timezone("Europe/Zurich") + first = first.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) + last = last.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) - self.assertEqual(first, tz.localize(datetime(2018, 4, 13, 12, 0))) - self.assertEqual(last, tz.localize(datetime(2018, 5, 1, 13, 0))) + self.assertEqual(first, datetime(2018, 4, 13, 12, 0)) + self.assertEqual(last, datetime(2018, 5, 1, 13, 0)) def test_import_simple_int2(self): model = models.Model.objects.all()[0] @@ -142,13 +154,17 @@ def test_import_simple_int2(self): self.assertEqual(models.CountDetail.objects.count(), 52) - tz = pytz.timezone("Europe/Zurich") + first = models.CountDetail.objects.first() + last = models.CountDetail.objects.last() + assert first + assert last - first = tz.normalize(models.CountDetail.objects.first().timestamp) - last = tz.normalize(models.CountDetail.objects.last().timestamp) + zurich_timezone = pytz.timezone("Europe/Zurich") + first = first.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) + last = last.timestamp.astimezone(zurich_timezone).replace(tzinfo=None) - self.assertEqual(first, tz.localize(datetime(2018, 9, 24, 0, 0))) - self.assertEqual(last, tz.localize(datetime(2018, 9, 24, 1, 0))) + self.assertEqual(first, datetime(2018, 9, 24, 0, 0)) + self.assertEqual(last, datetime(2018, 9, 24, 1, 0)) speed20 = models.CountDetail.objects.filter(speed=20) self.assertEqual(speed20[0].times, 3) diff --git a/docker-compose.yml b/docker-compose.yml index 5532b82e..6a65edb1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: - ${PWD}/testoutputs:/OpenComptage/testoutputs db: - image: postgis/postgis:12-2.5 + image: postgis/postgis:${POSTGIS_VERSION} ports: - 5432:5432 environment: diff --git a/docs/development.md b/docs/development.md index 715a1dd6..5bb574fa 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,4 +1,19 @@ # Development + +## Upgrade dependencies + +__Disclaimer__: This section presupposes that you are using a version of `pip-tools` matching your Python interpreter. In other words, if you want to upgrade or pin dependencies for Python 3.9, `pip-tools` need to be downloaded for Python 3.9, which in turn means that if you use `pip`, you need to use `pip` for Pyhton 3.9. + +Make sure you have `pip-tools` installed -- ideally in a virtual environment at the root of the project. Then run + + pip-compile --upgrade + +In case something goes south, you can re-generate `requirements.txt`, which amounts to pinning the dependencies described in `requirements.in` and constrained by `pyproject.toml` to a version matching the project's configuration: + + pip-compile -o requirements.txt pyproject.toml + +Now `pip-compile --upgrade` should work. + ## Data model The data model has been created to easily allow to add functionality to the product e.g. adding new vehicle classes and to be as simple as possible and easily @@ -44,3 +59,4 @@ The code of the plugin (directory =comptages=) is structured in the following wa | ui/ | contains QT's files with the definition of the user interface dialogs | | comptages.py | plugin main module | | metadata.txt | plugin metadata | + diff --git a/pyproject.toml b/pyproject.toml index 6c3f5c65..782829dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,6 +3,7 @@ requires = ["setuptools"] build-backend = "setuptools.build_meta" [project] +requires-python = ">=3.9.0,<=3.9.18" name = "comptages" version = "0.1" dynamic = ["dependencies"] @@ -11,7 +12,7 @@ dynamic = ["dependencies"] check = ["pyright>=1.1.36", "black>=23.11.0", "qgis-plugin-ci>=2.8.1"] [tool.setuptools.dynamic] -dependencies = { file = ["requirements.txt"] } +dependencies = { file = ["requirements.in"] } [tool.qgis-plugin-ci] plugin_path = "comptages" diff --git a/requirements.in b/requirements.in new file mode 100644 index 00000000..e08ce71c --- /dev/null +++ b/requirements.in @@ -0,0 +1,8 @@ +django +icalendar +nose2 +numpy +openpyxl +pandas +plotly==4.14.3 +psycopg2-binary \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 48608fd0..9f0c1a0c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,49 @@ -nose2==0.8.0 -psycopg2-binary==2.8.6 # on Debian derivates this requires `libpq-dev` -icalendar==4.0.3 +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --output-file=requirements.txt pyproject.toml +# +asgiref==3.7.2 + # via django +django==4.2.9 + # via comptages (pyproject.toml) +et-xmlfile==1.1.0 + # via openpyxl +icalendar==5.0.11 + # via comptages (pyproject.toml) +nose2==0.14.0 + # via comptages (pyproject.toml) +numpy==1.26.3 + # via + # comptages (pyproject.toml) + # pandas openpyxl==3.1.2 -django==3.2.15 -plotly==5.3.1 -pandas==1.3.4 + # via comptages (pyproject.toml) +pandas==2.1.4 + # via comptages (pyproject.toml) +plotly==4.14.3 + # via comptages (pyproject.toml) +psycopg2-binary==2.9.9 + # via comptages (pyproject.toml) +python-dateutil==2.8.2 + # via + # icalendar + # pandas +pytz==2023.3.post1 + # via + # icalendar + # pandas +retrying==1.3.4 + # via plotly +six==1.16.0 + # via + # plotly + # python-dateutil + # retrying +sqlparse==0.4.4 + # via django +typing-extensions==4.9.0 + # via asgiref +tzdata==2023.4 + # via pandas