Skip to content

Commit a02dc92

Browse files
committed
Updated to uv, fixed ruff issues, updated github actions & docker
1 parent 90acd3d commit a02dc92

File tree

8 files changed

+1355
-82
lines changed

8 files changed

+1355
-82
lines changed

.github/workflows/test.yaml

+7-9
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,27 @@ jobs:
2929
run: sudo apt-get install gettext
3030
- uses: actions/setup-python@v5
3131
with:
32-
python-version: 3.9
33-
- uses: abatilo/actions-poetry@v3
32+
python-version: '3.10'
33+
- uses: astral-sh/setup-uv@v3
3434
- name: Install dependencies
3535
id: install-deps
3636
run: |
37-
poetry install
37+
uv sync --dev
3838
- name: Check formatting
3939
# Lints/tests should always run, even if other lints/tests have failed.
4040
if: success() || failure() && steps.install-deps.outcome == 'success'
4141
run: |
42-
poetry run ruff format --check
42+
uv run ruff format --check
4343
- name: Lint
4444
if: success() || failure() && steps.install-deps.outcome == 'success'
4545
run: |
46-
poetry run ruff check
46+
uv run ruff check
4747
- name: Run migrations
4848
if: success() || failure() && steps.install-deps.outcome == 'success'
4949
run: |
50-
poetry run ./manage.py makemigrations registrations changes\
51-
eventstore --dry-run | grep 'No changes detected' || (echo 'There\
52-
are changes which require migrations.' && exit 1)
50+
uv run ./manage.py makemigrations --dry-run | grep 'No changes detected' || (echo 'There are changes which require migrations.' && exit 1)
5351
- name: Run tests
5452
if: success() || failure() && steps.install-deps.outcome == 'success'
5553
run: |
56-
poetry run pytest -vv
54+
uv run pytest -vv
5755

Dockerfile

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
FROM ghcr.io/praekeltfoundation/docker-django-bootstrap-nw:py3.9-bullseye
1+
FROM ghcr.io/praekeltfoundation/docker-django-bootstrap-nw:py3.10-bullseye
22

33
ENV DJANGO_SETTINGS_MODULE "ndoh_hub.settings"
44

55
COPY . /app
66

7-
RUN pip install poetry
8-
RUN poetry config virtualenvs.create false \
9-
&& poetry install --no-dev --no-interaction --no-ansi --no-cache
7+
RUN pip install -e .
108

119
RUN apt-get-install.sh gettext; \
1210
django-admin compilemessages; \

eventstore/tasks.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def get_inbound_intent(text):
812812
)
813813
def label_whatsapp_message(label, message_id):
814814
headers = {
815-
"Authorization": "Bearer {}".format(settings.TURN_TOKEN),
815+
"Authorization": f"Bearer {settings.TURN_TOKEN}",
816816
"content-type": "application/json",
817817
"Accept": "application/vnd.v1+json",
818818
}

ndoh_hub/testsettings.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from ndoh_hub.settings import * # noqa: F403 # flake8: noqa
1+
from ndoh_hub.settings import * # noqa: F403
22

33
# SECURITY WARNING: keep the secret key used in production secret!
4-
SECRET_KEY = "TESTSEKRET" # noqa - Ok to use hardcoded secrets in tests
4+
SECRET_KEY = "TESTSEKRET"
55

66
# SECURITY WARNING: don't run with debug turned on in production!
77
DEBUG = True

pyproject.toml

+132-58
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,77 @@
1-
[tool.poetry]
1+
[project]
22
name = "ndoh-hub"
33
version = "0.10.25"
44
description = "NDOH Registration and Change service for MomConnect and NurseConnect"
5-
authors = ["Fritz Brand <[email protected]>"]
6-
repository = "https://github.com/praekeltfoundation/ndoh-hub"
7-
license = "BSD"
5+
authors = [
6+
{name = "Reach Digital Health", email = "[email protected]"},
7+
]
8+
license = {text = "BSD"}
89
readme = "README.md"
10+
requires-python = ">=3.10,<4.0"
11+
dependencies = [
12+
"Django>=4.2.15",
13+
"djangorestframework>=3.15.2",
14+
"coreapi>=2.3.3",
15+
"Markdown>=3.1.1",
16+
"dj-database-url>=1.2.0",
17+
"django-environ>=0.10.0",
18+
"psycopg2-binary>=2.8.6",
19+
"raven>=6.9.0",
20+
"django-filter>=2.4.0",
21+
"celery>=5.2.3",
22+
"six>=1.11.0",
23+
"requests>=2.32.0",
24+
"demands>=3.0.0",
25+
"structlog>=18.2.0",
26+
"phonenumberslite>=8.9.15",
27+
"django-simple-history>=3.3.0",
28+
"openpyxl>=2.5.9",
29+
"django-prometheus>=2.2.0",
30+
"rapidpro-python==2.6.1", #V2.7.0 removed the V1 API support, which we use
31+
"pycountry>=19.8.18",
32+
"attrs>=24.2.0",
33+
"iso6709>=0.1.5",
34+
"redis>=4.5.4",
35+
"django-redis>=5.2.0",
36+
"celery_batches>=0.7",
37+
"python-dateutil>=2.8.2",
38+
]
39+
40+
[project.urls]
41+
repository = "http://github.com/praekeltfoundation/ndoh-hub"
942

43+
[build-system]
44+
requires = ["hatchling"]
45+
build-backend = "hatchling.build"
46+
47+
[tool.hatch.build.targets.wheel]
1048
packages = [
11-
{ include = "aaq" },
12-
{ include = "changes" },
13-
{ include = "eventstore" },
14-
{ include = "registrations" },
49+
"aaq",
50+
"changes",
51+
"eventstore",
52+
"registrations",
1553
]
1654

17-
[tool.poetry.dependencies]
18-
python = "^3.9"
19-
Django = "^4.2.15"
20-
djangorestframework = "^3.15.2"
21-
coreapi = "^2.3.3"
22-
Markdown = "^3.1.1"
23-
dj-database-url = "^1.2.0"
24-
django-environ = "^0.10.0"
25-
psycopg2-binary = "^2.8.6"
26-
raven = "^6.9.0"
27-
django-filter = "^2.4.0"
28-
celery = "^5.2.3"
29-
six = "^1.11.0"
30-
requests = "^2.32.0"
31-
demands = "^3.0.0"
32-
structlog = "^18.2.0"
33-
phonenumberslite = "^8.9.15"
34-
django-simple-history = "^3.3.0"
35-
openpyxl = "^2.5.9"
36-
django-prometheus = "^2.2.0"
37-
rapidpro-python = "2.6.1" #V2.7.0 removed the V1 API support, which we use
38-
pycountry = "^19.8.18"
39-
attrs = "^24.2.0"
40-
iso6709 = "^0.1.5"
41-
redis = "^4.5.4"
42-
django-redis = "^5.2.0"
43-
celery_batches = "^0.7"
44-
python-dateutil = "^2.8.2"
45-
46-
[tool.poetry.group.dev.dependencies]
47-
ruff = "^0.6.9"
48-
pytest = "^7.2.1"
49-
pytest-cov = "^4.0.0"
50-
pytest-django = "^4.5.2"
51-
pytest-asyncio = "^0.21.0"
52-
responses = "^0.23.1"
53-
pre-commit = "^3.2.0"
54-
freezegun = "^1.2.2"
55+
[dependency-groups]
56+
dev = [
57+
"ruff>=0.6.9",
58+
"pytest>=7.2.1",
59+
"pytest-cov>=4.0.0",
60+
"pytest-django>=4.5.2",
61+
"pytest-asyncio>=0.21.0",
62+
"responses>=0.23.1",
63+
"pre-commit>=3.2.0",
64+
"freezegun>=1.2.2",
65+
]
5566

56-
[build-system]
57-
requires = ["poetry-core"]
58-
build-backend = "poetry.core.masonry.api"
67+
[tool.pytest.ini_options]
68+
filterwarnings = ['ignore::DeprecationWarning',]
69+
python_files = "test*.py"
70+
addopts = "--verbose --ds=config.settings.test"
5971

6072
[tool.ruff]
6173
extend-exclude = [
6274
"*/migrations/*.py",
63-
"docs/conf.py",
64-
6575
]
6676

6777
[tool.ruff.lint]
@@ -84,32 +94,96 @@ ignore = [
8494
"E501", # TODO: Something about these long lines.
8595
"S113", # TODO: Add request timeouts.
8696
"PTH118", # TODO: Switch to pathlib
87-
"PTH100", # TODO: 'os.path.abspath()' should be replaced by 'Path.resolve()' maybe use pathlib?
88-
"PTH120", # TODO: 'os.path.dirname()' should be replaced by 'Path.parent' maybe use pathlib?
97+
"PTH100", # TODO: Switch to pathlib
98+
"PTH120", # TODO: Switch to pathlib
99+
"RUF012", # We usually want immutable instance attributes
89100
"PTH123", # TODO: 'open()' should be replaced by 'Path.open()' maybe use pathlib?
90101
# Ignores below this line needs to be checked out by someone with more knowledge of the project
91102
"S501", #TODO: Probable use of 'requests' call with 'verify=False' disabling SSL certificate checks
92103
"SIM115", # TODO: Use a context manager for opening files - FWB: Tried fixing these, but ran into errors on tests
93104
"S101", # TODO: Use of 'assert' detected - FWB: Looks like this is only used in migration files
94105
"S608", # TODO: Possible SQL injection vector through string-based query construction
95106
"RUF012", # We usually want immutable instance attributes
96-
97107
]
98108

99-
# Do not add S106 or S105 global ignores. Rather use inline ignores (noqa)
100109
[tool.ruff.lint.per-file-ignores]
101110
"**/tests/**" = [
102-
"S101", # It's okay to use 'assert' in tests.
111+
"S101", # It's okay to use `assert` in tests.
103112
]
104113

105114
# TODO: Move this somewhere sensible?
106115
"**/tests.py" = [
107-
"S101", # It's okay to use 'assert' in tests.
116+
"S101", # It's okay to use `assert` in tests.
108117
]
109118

110119
"**/config/settings/{test,dev}.py" = [
111120
"S104", # It's okay to bind to all interfaces in tests
112121
"F405", # Its okay to import * in settings
113122
"S105", # Its okay to have hardcoded secrets in test config
123+
]
124+
"ndoh_hub/testsettings.py" = [
125+
"S105", # Its okay to have hardcoded secrets in test config
126+
]
127+
128+
129+
130+
# [build-system]
131+
# requires = ["poetry-core"]
132+
# build-backend = "poetry.core.masonry.api"
133+
134+
# [tool.ruff]
135+
# extend-exclude = [
136+
# "*/migrations/*.py",
137+
# "docs/conf.py",
138+
139+
# ]
140+
141+
# [tool.ruff.lint]
142+
# select = [
143+
# "E", "F", "W", # pycodestyle + pyflakes == flake8 - mccabe
144+
# "I", # isort
145+
# "UP", # pyupgrade
146+
# "S", # flake8-bandit
147+
# "B", # flake8-bugbear
148+
# "C4", # flake8-comprehensions
149+
# # "DJ", # flake8-django
150+
# "PIE", # flake8-pie
151+
# # "PT", # flake8-pytest-style
152+
# "SIM", # flake8-simplify
153+
# "PTH", # flake8-use-pathlib
154+
# "RUF", # ruff-specific rules
155+
# ]
156+
157+
# ignore = [
158+
# "E501", # TODO: Something about these long lines.
159+
# "S113", # TODO: Add request timeouts.
160+
# "PTH118", # TODO: Switch to pathlib
161+
# "PTH100", # TODO: 'os.path.abspath()' should be replaced by 'Path.resolve()' maybe use pathlib?
162+
# "PTH120", # TODO: 'os.path.dirname()' should be replaced by 'Path.parent' maybe use pathlib?
163+
# "PTH123", # TODO: 'open()' should be replaced by 'Path.open()' maybe use pathlib?
164+
# # Ignores below this line needs to be checked out by someone with more knowledge of the project
165+
# "S501", #TODO: Probable use of 'requests' call with 'verify=False' disabling SSL certificate checks
166+
# "SIM115", # TODO: Use a context manager for opening files - FWB: Tried fixing these, but ran into errors on tests
167+
# "S101", # TODO: Use of 'assert' detected - FWB: Looks like this is only used in migration files
168+
# "S608", # TODO: Possible SQL injection vector through string-based query construction
169+
# "RUF012", # We usually want immutable instance attributes
170+
171+
# ]
172+
173+
# # Do not add S106 or S105 global ignores. Rather use inline ignores (noqa)
174+
# [tool.ruff.lint.per-file-ignores]
175+
# "**/tests/**" = [
176+
# "S101", # It's okay to use 'assert' in tests.
177+
# ]
178+
179+
# # TODO: Move this somewhere sensible?
180+
# "**/tests.py" = [
181+
# "S101", # It's okay to use 'assert' in tests.
182+
# ]
183+
184+
# "**/config/settings/{test,dev}.py" = [
185+
# "S104", # It's okay to bind to all interfaces in tests
186+
# "F405", # Its okay to import * in settings
187+
# "S105", # Its okay to have hardcoded secrets in test config
114188

115-
]
189+
# ]

scripts/migrate_to_rapidpro/collect_sms_content.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import json
22
from collections import defaultdict
3-
from typing import Tuple
43
from uuid import uuid4
54

65
import psycopg2
76

8-
PREBIRTH: Tuple[Tuple[str, str, Tuple[int, ...], int], ...] = (
7+
PREBIRTH: tuple[tuple[str, str, tuple[int, ...], int], ...] = (
98
("pmtct_prebirth.patient.1", "PMTCT 1 SMS", (0,), 5),
109
("pmtct_prebirth.patient.2", "PMTCT 2 SMS", (0, 3), 30),
1110
("pmtct_prebirth.patient.3", "PMTCT 3 SMS", (0, 2, 4), 35),
@@ -17,24 +16,24 @@
1716
("momconnect_prebirth.hw_full.6", "Prebirth 6 SMS", (0, 1, 2, 3, 4, 5, 6), 39),
1817
)
1918

20-
POSTBIRTH: Tuple[Tuple[str, str, Tuple[int, ...], int], ...] = (
19+
POSTBIRTH: tuple[tuple[str, str, tuple[int, ...], int], ...] = (
2120
("momconnect_postbirth.hw_full.1", "Postbirth SMS", (0, 3), 0),
2221
("momconnect_postbirth.hw_full.2", "Postbirth SMS", (0,), 15),
2322
("pmtct_postbirth.patient.1", "PMTCT Postbirth SMS", (0, 3), 0),
2423
("pmtct_postbirth.patient.2", "PMTCT Postbirth SMS", (0,), 2),
2524
)
2625

27-
LOSS: Tuple[Tuple[str, str, Tuple[int, ...]], ...] = (
26+
LOSS: tuple[tuple[str, str, tuple[int, ...]], ...] = (
2827
("loss_babyloss.patient.1", "Babyloss SMS", (0, 3)),
2928
("loss_stillbirth.patient.1", "Stillbirth SMS", (0,)),
3029
("loss_miscarriage.patient.1", "Miscarriage SMS", (0, 3)),
3130
)
3231

33-
PUBLIC: Tuple[Tuple[str, str, Tuple[int, ...]], ...] = (
32+
PUBLIC: tuple[tuple[str, str, tuple[int, ...]], ...] = (
3433
("momconnect_prebirth.patient.1", "Public SMS", (0, 3)),
3534
)
3635

37-
POPI: Tuple[str, str] = ("popi.hw_full.1", "POPI SMS")
36+
POPI: tuple[str, str] = ("popi.hw_full.1", "POPI SMS")
3837

3938

4039
def create_campaign(name: str, group: str) -> dict:

scripts/migrate_to_turn/compare_contacts.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def get_turn_contact(wa_id):
3535
"Accept": "application/vnd.v1+json",
3636
}
3737
response = requests.get(
38-
urljoin(TURN_URL, "/v1/contacts/{}/profile".format(wa_id)),
38+
urljoin(TURN_URL, f"/v1/contacts/{wa_id}/profile"),
3939
headers=headers,
4040
)
4141
contact = response.json()["fields"]
@@ -53,7 +53,7 @@ def compare_contacts():
5353
turn_data = get_turn_contact(wa_id)
5454

5555
row = {"wa_id": wa_id}
56-
for rapidpro_field in FIELD_MAPPING.keys():
56+
for rapidpro_field in FIELD_MAPPING:
5757
row[f"RP {rapidpro_field}"] = rapidpro_data[rapidpro_field]
5858
row[f"TURN {rapidpro_field}"] = turn_data[rapidpro_field]
5959

0 commit comments

Comments
 (0)