Skip to content

Commit da680be

Browse files
authored
Merge pull request #196 from praekeltfoundation/repo-consistency
Repo consistency
2 parents bd9cada + e8861c4 commit da680be

35 files changed

+1921
-359
lines changed

.github/workflows/test.yaml

+21-16
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,33 @@ jobs:
2424
env:
2525
RP_SIDEKICK_DATABASE: postgres://postgres:postgres@localhost/rp_sidekick
2626
steps:
27-
- uses: actions/checkout@v2
27+
- uses: actions/checkout@v4
2828
- name: Install gettext
2929
run: sudo apt-get install gettext
30-
- uses: actions/cache@v2
30+
- uses: actions/setup-python@v5
3131
with:
32-
path: ~/.cache/pip
33-
key: ${{ hashFiles('requirements.txt', 'requirements-dev.txt') }}-pip
34-
- uses: actions/setup-python@v2
35-
with:
36-
python-version: 3.9
37-
- name: Install dependancies
32+
python-version: "3.10"
33+
- uses: astral-sh/setup-uv@v3
34+
- name: Install dependencies
35+
id: install-deps
36+
run: |
37+
uv sync --dev
38+
- name: Check formatting
39+
# Lints/tests should always run, even if other lints/tests have failed.
40+
if: success() || failure() && steps.install-deps.outcome == 'success'
3841
run: |
39-
python -m pip install --upgrade pip
40-
pip install -r requirements.txt -r requirements-dev.txt
42+
uv run ruff format --check
4143
- name: Lint
44+
if: success() || failure() && steps.install-deps.outcome == 'success'
45+
run: |
46+
uv run ruff check
47+
- name: Run migrations
48+
if: success() || failure() && steps.install-deps.outcome == 'success'
4249
run: |
43-
flake8
44-
python manage.py makemigrations rp_dtone rp_transferto\
50+
uv run ./manage.py makemigrations rp_dtone rp_transferto\
4551
sidekick --dry-run | grep 'No changes detected' || (echo 'There are\
4652
changes which require migrations.' && exit 1)
47-
black --check .
48-
isort -c -rc .
49-
- name: Test
53+
- name: Run tests
54+
if: success() || failure() && steps.install-deps.outcome == 'success'
5055
run: |
51-
py.test
56+
uv run pytest -vv

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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
COPY . /app
44

config/celery.py

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from __future__ import absolute_import
2-
31
import os
42

53
from celery import Celery

config/settings/dev.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
SECRET_KEY = env.str("SECRET_KEY", "dev_secret_key")
77

88
ENV_HOSTS = [host for host in env.str("ALLOWED_HOSTS", "").split(",") if host]
9-
ALLOWED_HOSTS = ENV_HOSTS + ["localhost", ".localhost", "127.0.0.1", "0.0.0.0"]
9+
ALLOWED_HOSTS = [*ENV_HOSTS, "localhost", ".localhost", "127.0.0.1", "0.0.0.0"]

config/settings/test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",)
1616

1717
ENV_HOSTS = [host for host in env.str("ALLOWED_HOSTS", "").split(",") if host]
18-
ALLOWED_HOSTS = ENV_HOSTS + ["localhost", ".localhost", "127.0.0.1", "0.0.0.0"]
18+
ALLOWED_HOSTS = [*ENV_HOSTS, "localhost", ".localhost", "127.0.0.1", "0.0.0.0"]
1919

2020
TRANSFERTO_LOGIN = ("fake_transferto_login",)
2121
TRANSFERTO_TOKEN = ("fake_transferto_token",)

msisdn_utils/views.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ def get_middle_tz(zones):
2323

2424
approx_tz = ordered_tzs[floor(len(ordered_tzs) / 2)]["name"]
2525

26-
LOGGER.info(
27-
"Available timezones: {}. Returned timezone: {}".format(ordered_tzs, approx_tz)
28-
)
26+
LOGGER.info(f"Available timezones: {ordered_tzs}. Returned timezone: {approx_tz}")
2927
return approx_tz
3028

3129

@@ -36,21 +34,21 @@ class GetMsisdnTimezones(APIView):
3634
def post(self, request, *args, **kwargs):
3735
try:
3836
msisdn = request.data["whatsapp_id"]
39-
except KeyError:
40-
raise ValidationError({"whatsapp_id": ["This field is required."]})
37+
except KeyError as e:
38+
raise ValidationError({"whatsapp_id": ["This field is required."]}) from e
4139

4240
msisdn = msisdn if msisdn.startswith("+") else "+" + msisdn
4341

4442
try:
4543
msisdn = phonenumbers.parse(msisdn)
46-
except phonenumbers.phonenumberutil.NumberParseException:
44+
except phonenumbers.phonenumberutil.NumberParseException as e:
4745
raise ValidationError(
4846
{
4947
"whatsapp_id": [
5048
"This value must be a phone number with a region prefix."
5149
]
5250
}
53-
)
51+
) from e
5452

5553
if not (
5654
phonenumbers.is_possible_number(msisdn)

pyproject.toml

+109-24
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,109 @@
1-
# Example configuration for Black.
2-
3-
# see https://raw.githubusercontent.com/ambv/black/master/pyproject.toml
4-
5-
[tool.black]
6-
line-length = 88
7-
include = '\.pyi?$'
8-
exclude = '''
9-
/(
10-
\.git
11-
| \.hg
12-
| \.mypy_cache
13-
| \.tox
14-
| \.venv
15-
| \.ve
16-
| _build
17-
| buck-out
18-
| build
19-
| dist
20-
| migrations
21-
| env
22-
| ve
23-
)/
24-
'''
1+
[project]
2+
name = "rp-sidekick"
3+
version = "1.13.2"
4+
description = ""
5+
authors = [
6+
{name = "Praekelt Foundation", email = "[email protected]"},
7+
]
8+
license = {text = "BSD"}
9+
readme = "README.rst"
10+
requires-python = "<4.0,>=3.10"
11+
dependencies = [
12+
"celery<6.0.0,>=5.2.3",
13+
"django<5.0.0,>=4.2.15",
14+
"django-environ<1.0.0,>=0.4.5",
15+
"django-extensions<4.0.0,>=3.1.5",
16+
"django-phonenumber-field<4.0.0,>=3.0.1",
17+
"django-prometheus<3.0.0,>=2.2.0",
18+
"djangorestframework<4.0.0,>=3.15.2",
19+
"json2html<2.0.0,>=1.3.0",
20+
"phonenumbers<9.0.0,>=8.13.45",
21+
"psycopg2-binary<3.0.0,>=2.9.9",
22+
"rapidpro-python==2.6.1",
23+
"redis<5.0.0,>=4.5.4",
24+
"whitenoise<5.0.0,>=4.1.4",
25+
"raven<7.0.0,>=6.10.0",
26+
"hashids<2.0.0,>=1.3.1",
27+
"django-filter<3.0.0,>=2.4.0",
28+
"sentry-sdk<3.0.0,>=2.8.0",
29+
"dj-database-url<1.0.0,>=0.5.0",
30+
"jsonschema<5.0.0,>=4.21.1",
31+
"boto3<2.0.0,>=1.35.35",
32+
]
33+
34+
[project.urls]
35+
repository = "http://github.com/praekeltfoundation/rp-sidekick"
36+
37+
[build-system]
38+
requires = ["hatchling"]
39+
build-backend = "hatchling.build"
40+
41+
[tool.hatch.build.targets.wheel]
42+
packages = [
43+
"config",
44+
"msisdn_utils",
45+
"randomisation",
46+
"rp_dtone",
47+
"rp_interceptors",
48+
"rp_transferto",
49+
"rp_yal",
50+
"sidekick",
51+
]
52+
53+
[dependency-groups]
54+
dev = [
55+
"ruff<1.0.0,>=0.6.9",
56+
"pytest-django<5.0.0,>=4.9.0",
57+
"pytest-cov<6.0.0,>=5.0.0",
58+
"responses<1.0.0,>=0.25.3",
59+
"freezegun<2.0.0,>=1.5.1",
60+
"recommonmark<1.0.0,>=0.7.1",
61+
"sphinx<8.0.0",
62+
"sphinx-rtd-theme<4.0.0,>=3.0.0",
63+
"moto<6.0.0,>=5.0.16",
64+
]
65+
66+
[tool.ruff]
67+
extend-exclude = [
68+
"*/migrations/*.py",
69+
"docs/conf.py",
70+
]
71+
72+
[tool.ruff.lint]
73+
select = [
74+
"E", "F", "W", # pycodestyle + pyflakes == flake8 - mccabe
75+
"I", # isort
76+
"UP", # pyupgrade
77+
"S", # flake8-bandit
78+
"B", # flake8-bugbear
79+
"C4", # flake8-comprehensions
80+
# "DJ", # flake8-django
81+
"PIE", # flake8-pie
82+
# "PT", # flake8-pytest-style
83+
"SIM", # flake8-simplify
84+
"PTH", # flake8-use-pathlib
85+
"RUF", # ruff-specific rules
86+
]
87+
88+
ignore = [
89+
"E501", # TODO: Something about these long lines.
90+
"S113", # TODO: Add request timeouts.
91+
"PTH118", # TODO: Switch to pathlib
92+
"RUF012", # We usually want immutable instance attributes
93+
]
94+
95+
[tool.ruff.lint.per-file-ignores]
96+
"**/tests/**" = [
97+
"S101", # It's okay to use `assert` in tests.
98+
]
99+
100+
# TODO: Move this somewhere sensible?
101+
"**/tests.py" = [
102+
"S101", # It's okay to use `assert` in tests.
103+
]
104+
105+
"**/config/settings/{test,dev}.py" = [
106+
"S104", # It's okay to bind to all interfaces in tests
107+
"F405", # Its okay to import * in settings
108+
"S105", # Its okay to have hardcoded secrets in test config
109+
]

randomisation/tests/test_utils.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ def test_stratification_balancing(self):
144144

145145
totals = defaultdict(int)
146146
stratas = defaultdict(lambda: defaultdict(int))
147-
for i in range(100):
148-
random_age = random.choice(["18-29", "29-39"])
149-
random_province = random.choice(["WC", "GT"])
147+
for _i in range(100):
148+
random_age = random.choice(["18-29", "29-39"]) # noqa: S311 This is not for crypto
149+
random_province = random.choice(["WC", "GT"]) # noqa: S311 This is not for crypto
150150

151151
data = {"age-group": random_age, "province": random_province}
152152

@@ -155,7 +155,7 @@ def test_stratification_balancing(self):
155155
totals[random_arm] += 1
156156

157157
def check_arms_balanced(arms, diff, description):
158-
values = [value for value in arms.values()]
158+
values = list(arms.values())
159159
msg = f"Arms not balanced: {description} - {values}"
160160
assert max(values) - diff < values[0] < min(values) + diff, msg
161161

requirements-dev.txt

-13
This file was deleted.

requirements.txt

-2
This file was deleted.

rp_interceptors/models.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ class Interceptor(models.Model):
88
channel_uuid = models.CharField(
99
max_length=255,
1010
null=False,
11-
help_text="The uuid of the WhatsApp channel in RapidPro that should receive messages",
11+
help_text="The uuid of the WhatsApp channel in RapidPro that should"
12+
" receive messages",
1213
)
1314
hmac_secret = models.CharField(
1415
max_length=255,

0 commit comments

Comments
 (0)