diff --git a/.coveragerc b/.coveragerc
index e9267ea..19109ab 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,4 +1,4 @@
[run]
include = edc_locator/*
omit = edc_locator/tests/*,edc_locator/migrations/*
-branch = 1
\ No newline at end of file
+branch = 1
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 59f4d55..950e9aa 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,20 +5,17 @@ on: [push, pull_request]
jobs:
build:
- name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }})
+ name: |
+ build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}, ${{ matrix.database-engine }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- python-version: ['3.11', '3.12']
- django-version: ['4.2', '5.0', 'dev']
+ python-version: ['3.12', '3.13']
+ django-version: ['5.1', 'dev']
+ database-engine: ["mysql", "postgres"]
- exclude:
- - python-version: '3.12'
- django-version: '4.2'
- - python-version: '3.11'
- django-version: 'dev'
services:
mysql:
image: mysql:latest
@@ -28,6 +25,17 @@ jobs:
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+ postgres:
+ image: postgres:latest
+ env:
+ POSTGRES_PASSWORD: postgres
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ ports:
+ - 5432:5432
steps:
- name: Install pycups and words dependency
@@ -61,7 +69,6 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/tox.txt
-
- name: Tox tests
run: |
tox -v
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3df1e6b..5825210 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,20 +3,20 @@ exclude: tests/etc/user-*
repos:
- repo: https://github.com/PyCQA/bandit
- rev: 1.7.7
+ rev: 1.8.2
hooks:
- id: bandit
args:
- "-x *test*.py"
- repo: https://github.com/psf/black
- rev: 24.2.0
+ rev: 24.10.0
hooks:
- id: black
- language_version: python3.11
+ language_version: python3.12
- repo: https://github.com/pycqa/flake8
- rev: 7.0.0
+ rev: 7.1.1
hooks:
- id: flake8
args:
@@ -28,22 +28,24 @@ repos:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.5.0
+ rev: v5.0.0
hooks:
- - id: requirements-txt-fixer
- files: requirements/.*\.txt$
- - id: trailing-whitespace
- id: check-added-large-files
- - id: fix-byte-order-marker
- id: check-docstring-first
- id: check-executables-have-shebangs
- id: check-merge-conflict
+ - id: check-toml
+ - id: check-yaml
- id: debug-statements
- id: detect-private-key
- - id: check-toml
+ - id: end-of-file-fixer
+ - id: fix-byte-order-marker
+ - id: requirements-txt-fixer
+ files: requirements/.*\.txt$
+ - id: trailing-whitespace
- repo: https://github.com/adrienverge/yamllint
- rev: v1.34.0
+ rev: v1.35.1
hooks:
- id: yamllint
args:
diff --git a/LICENSE b/LICENSE
index 8cdb845..23cb790 100644
--- a/LICENSE
+++ b/LICENSE
@@ -337,4 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
-
diff --git a/edc_locator/model_mixins/subject_contact_fields_mixin.py b/edc_locator/model_mixins/subject_contact_fields_mixin.py
index 43cb294..c4f76e8 100644
--- a/edc_locator/model_mixins/subject_contact_fields_mixin.py
+++ b/edc_locator/model_mixins/subject_contact_fields_mixin.py
@@ -10,8 +10,9 @@ class SubjectContactFieldsMixin(models.Model):
max_length=25,
choices=YES_NO,
verbose_name=format_html(
- "Has the participant given permission to contacted by telephone "
- "or cell by study staff for follow-up purposes during the study?"
+ "Has the participant given permission {} "
+ "by study staff for follow-up purposes during the study?",
+ "to contacted by telephone or cell",
),
)
@@ -20,7 +21,8 @@ class SubjectContactFieldsMixin(models.Model):
choices=YES_NO,
verbose_name=format_html(
"Has the participant given permission for study "
- "staff to make home visits for follow-up purposes?"
+ "staff {} for follow-up purposes?",
+ "to make home visits",
),
)
@@ -30,8 +32,9 @@ class SubjectContactFieldsMixin(models.Model):
null=True,
blank=False,
verbose_name=format_html(
- "Has the participant given permission to be contacted by SMS "
- "by study staff for follow-up purposes during the study?"
+ "Has the participant given permission {} "
+ "by study staff for follow-up purposes during the study?",
+ "to be contacted by SMS",
),
)
diff --git a/edc_locator/model_mixins/subject_indirect_contact_fields_mixin.py b/edc_locator/model_mixins/subject_indirect_contact_fields_mixin.py
index 1ebd38c..ab261d7 100644
--- a/edc_locator/model_mixins/subject_indirect_contact_fields_mixin.py
+++ b/edc_locator/model_mixins/subject_indirect_contact_fields_mixin.py
@@ -10,8 +10,10 @@ class SubjectIndirectContactFieldsMixin(models.Model):
max_length=25,
choices=YES_NO,
verbose_name=format_html(
- "Has the participant given permission for study staff "
- "to contact anyone else for follow-up purposes during the study?"
+ "{} {} {}?",
+ "Has the participant given permission for study staff",
+ "to contact anyone else",
+ "for follow-up purposes during the study",
),
help_text="For example a partner, spouse, family member, neighbour ...",
)
diff --git a/edc_locator/model_mixins/subject_work_fields_mixin.py b/edc_locator/model_mixins/subject_work_fields_mixin.py
index 9609836..f73aa46 100644
--- a/edc_locator/model_mixins/subject_work_fields_mixin.py
+++ b/edc_locator/model_mixins/subject_work_fields_mixin.py
@@ -10,9 +10,10 @@ class SubjectWorkFieldsMixin(models.Model):
max_length=25,
choices=YES_NO,
verbose_name=format_html(
- "Has the participant given permission to be contacted "
- "at work, by telephone or cell, by study staff for follow-up "
- "purposes during the study?"
+ "{} {}, {} ",
+ "Has the participant given permission to be contacted",
+ "at work",
+ "by telephone or cell, by study staff for follow-up purposes during the study?",
),
)
diff --git a/edc_locator/modeladmin_mixins.py b/edc_locator/modeladmin_mixins.py
index d770ebf..a806688 100644
--- a/edc_locator/modeladmin_mixins.py
+++ b/edc_locator/modeladmin_mixins.py
@@ -76,7 +76,7 @@ def subject(self, obj):
age_in_years=age(born=consent.dob, reference_dt=get_utcnow()).years,
initials=consent.initials,
)
- return render_to_string("changelist_locator_subject.html", context=context)
+ return render_to_string("edc_locator/changelist_locator_subject.html", context=context)
@admin.display(description="Contact Rules", ordering="may_call")
def contact_rules(self, obj):
@@ -89,7 +89,9 @@ def contact_rules(self, obj):
YES=YES,
NO=NO,
)
- return render_to_string("changelist_locator_contact_rules.html", context=context)
+ return render_to_string(
+ "edc_locator/changelist_locator_contact_rules.html", context=context
+ )
@admin.display(description="Contacts")
def contacts(self, obj):
@@ -99,4 +101,6 @@ def contacts(self, obj):
subject_phone=obj.subject_phone,
subject_phone_alt=obj.subject_phone_alt,
)
- return render_to_string("changelist_locator_contacts.html", context=context)
+ return render_to_string(
+ "edc_locator/changelist_locator_contacts.html", context=context
+ )
diff --git a/edc_locator/templates/changelist_locator_contact_rules.html b/edc_locator/templates/edc_locator/changelist_locator_contact_rules.html
similarity index 100%
rename from edc_locator/templates/changelist_locator_contact_rules.html
rename to edc_locator/templates/edc_locator/changelist_locator_contact_rules.html
diff --git a/edc_locator/templates/changelist_locator_contacts.html b/edc_locator/templates/edc_locator/changelist_locator_contacts.html
similarity index 100%
rename from edc_locator/templates/changelist_locator_contacts.html
rename to edc_locator/templates/edc_locator/changelist_locator_contacts.html
diff --git a/edc_locator/templates/changelist_locator_subject.html b/edc_locator/templates/edc_locator/changelist_locator_subject.html
similarity index 100%
rename from edc_locator/templates/changelist_locator_subject.html
rename to edc_locator/templates/edc_locator/changelist_locator_subject.html
diff --git a/edc_locator/templates/bootstrap3/locator_include.html b/edc_locator/templates/edc_locator/locator_include.html
similarity index 100%
rename from edc_locator/templates/bootstrap3/locator_include.html
rename to edc_locator/templates/edc_locator/locator_include.html
diff --git a/edc_locator/tests/holidays.csv b/edc_locator/tests/holidays.csv
index 8114d09..6cad2f1 100644
--- a/edc_locator/tests/holidays.csv
+++ b/edc_locator/tests/holidays.csv
@@ -12,4 +12,4 @@ local_date,label,country
2017-10-02,Public Holiday,botswana
2017-12-25,Christmas Day,botswana
2017-12-26,Boxing Day,botswana
-2017-01-01,New Year,botswana
\ No newline at end of file
+2017-01-01,New Year,botswana
diff --git a/edc_locator/tests/test_settings.py b/edc_locator/tests/test_settings.py
new file mode 100644
index 0000000..3d45e7d
--- /dev/null
+++ b/edc_locator/tests/test_settings.py
@@ -0,0 +1,56 @@
+import sys
+from pathlib import Path
+
+from edc_test_settings.default_test_settings import DefaultTestSettings
+
+app_name = "edc_locator"
+base_dir = Path(__file__).parent.parent.parent
+print(base_dir)
+
+project_settings = DefaultTestSettings(
+ calling_file=__file__,
+ template_dirs=[str(base_dir / app_name / "tests" / "templates")],
+ BASE_DIR=base_dir,
+ APP_NAME=app_name,
+ ETC_DIR=str(base_dir / app_name / "tests" / "etc"),
+ SILENCED_SYSTEM_CHECKS=[
+ "sites.E101",
+ "edc_navbar.E002",
+ "edc_navbar.E003",
+ "edc_consent.E001",
+ ],
+ SUBJECT_VISIT_MODEL="edc_visit_tracking.subjectvisit",
+ EDC_SITES_REGISTER_DEFAULT=True,
+ INSTALLED_APPS=[
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "django.contrib.sites",
+ "simple_history",
+ "django_crypto_fields.apps.AppConfig",
+ "edc_action_item.apps.AppConfig",
+ "edc_appointment.apps.AppConfig",
+ "edc_auth.apps.AppConfig",
+ "edc_data_manager.apps.AppConfig",
+ "edc_device.apps.AppConfig",
+ "edc_form_runners.apps.AppConfig",
+ "edc_identifier.apps.AppConfig",
+ "edc_lab.apps.AppConfig",
+ "edc_label.apps.AppConfig",
+ "edc_locator.apps.AppConfig",
+ "edc_metadata.apps.AppConfig",
+ "edc_notification.apps.AppConfig",
+ "edc_registration.apps.AppConfig",
+ "edc_sites.apps.AppConfig",
+ "edc_visit_schedule.apps.AppConfig",
+ "edc_visit_tracking.apps.AppConfig",
+ "edc_appconfig.apps.AppConfig",
+ ],
+ add_dashboard_middleware=True,
+).settings
+
+for k, v in project_settings.items():
+ setattr(sys.modules[__name__], k, v)
diff --git a/pyproject.toml b/pyproject.toml
index d2bf665..319e212 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,17 +1,18 @@
[build-system]
requires = ["setuptools>=60", "setuptools-scm>=8.0"]
+build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
-version_file="_version.py"
+version_file = "_version.py"
[tool.black]
line-length = 95
-target-version = ["py311"]
+target-version = ["py312"]
extend-exclude = '''^(.*\/)*\b(migrations)\b($|\/.*$)'''
[tool.isort]
profile = "black"
-py_version = "311"
+py_version = "312"
skip = [".tox", ".eggs", "migrations"]
[tool.coverage.run]
@@ -35,35 +36,33 @@ exclude_lines = [
legacy_tox_ini = """
[tox]
envlist =
- py{311}-dj{42,50},
- py{312}-dj{50,dev},
+ py{312,313}-dj{51,dev},
lint
+ pre-commit
isolated_build = true
[gh-actions]
python =
- 3.11: py311
- 3.12: py312, lint
+ 3.12: py312, lint, pre-commit
+ 3.13: py313
[gh-actions:env]
DJANGO =
- 4.2: dj42
- 5.0: dj50
- dev: djdev, lint
+ 5.1: dj51
+ dev: djdev, lint, pre-commit
[testenv]
deps =
-r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/tox.txt
-r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/test_utils.txt
-r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/edc.txt
- -r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/third_party_dev.txt
- dj42: Django>=4.2,<5.0
- dj50: Django>=5.0
+ dj51: Django>=5.1,<5.2
djdev: https://github.com/django/django/tarball/main
commands =
- pip install -U pip coverage[toml]
+ pip install -U pip
+ python --version
pip --version
pip freeze
coverage run -a runtests.py
@@ -72,7 +71,19 @@ commands =
[testenv:lint]
deps = -r https://raw.githubusercontent.com/clinicedc/edc/develop/requirements.tests/lint.txt
commands =
+ python --version
+ pip --version
+ pip freeze
isort --profile=black --check --diff .
black --check --diff .
flake8 .
+
+[testenv:pre-commit]
+deps = pre-commit
+commands =
+ python --version
+ pip --version
+ pip freeze
+ pre-commit autoupdate
+ pre-commit run --all-files
"""
diff --git a/runtests.py b/runtests.py
index bc822ba..1a2d24d 100644
--- a/runtests.py
+++ b/runtests.py
@@ -1,62 +1,5 @@
#!/usr/bin/env python
-import logging
-from pathlib import Path
-
-from edc_test_utils import DefaultTestSettings, func_main
-
-app_name = "edc_locator"
-base_dir = Path(__file__).absolute().parent
-
-project_settings = DefaultTestSettings(
- calling_file=__file__,
- template_dirs=[str(base_dir / app_name / "tests" / "templates")],
- BASE_DIR=base_dir,
- APP_NAME=app_name,
- ETC_DIR=str(base_dir / app_name / "tests" / "etc"),
- SILENCED_SYSTEM_CHECKS=[
- "sites.E101",
- "edc_navbar.E002",
- "edc_navbar.E003",
- "edc_consent.E001",
- ],
- SUBJECT_VISIT_MODEL="edc_visit_tracking.subjectvisit",
- EDC_SITES_REGISTER_DEFAULT=True,
- INSTALLED_APPS=[
- "django.contrib.admin",
- "django.contrib.auth",
- "django.contrib.contenttypes",
- "django.contrib.sessions",
- "django.contrib.messages",
- "django.contrib.staticfiles",
- "django.contrib.sites",
- "simple_history",
- "django_crypto_fields.apps.AppConfig",
- "edc_action_item.apps.AppConfig",
- "edc_appointment.apps.AppConfig",
- "edc_auth.apps.AppConfig",
- "edc_data_manager.apps.AppConfig",
- "edc_device.apps.AppConfig",
- "edc_form_runners.apps.AppConfig",
- "edc_identifier.apps.AppConfig",
- "edc_lab.apps.AppConfig",
- "edc_label.apps.AppConfig",
- "edc_locator.apps.AppConfig",
- "edc_metadata.apps.AppConfig",
- "edc_notification.apps.AppConfig",
- "edc_registration.apps.AppConfig",
- "edc_sites.apps.AppConfig",
- "edc_visit_schedule.apps.AppConfig",
- "edc_visit_tracking.apps.AppConfig",
- "edc_appconfig.apps.AppConfig",
- ],
- add_dashboard_middleware=True,
-).settings
-
-
-def main():
- func_main(project_settings, *[f"{app_name}.tests"])
-
+from edc_test_settings.func_main import func_main2
if __name__ == "__main__":
- logging.basicConfig()
- main()
+ func_main2("edc_locator.tests.test_settings", "edc_locator.tests")
diff --git a/setup.cfg b/setup.cfg
index 8efccb1..e8f58e7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -12,16 +12,15 @@ keywords = django Edc participant locator, CRF, clinicedc, clinical trials
classifiers=
Environment :: Web Environment
Framework :: Django
- Framework :: Django :: 4.2
+ Framework :: Django :: 5.1
Intended Audience :: Developers
Intended Audience :: Science/Research
Operating System :: OS Independent
- Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
[options]
-python_requires = >=3.11
+python_requires = >=3.12
zip_safe = False
include_package_data = True
packages = find: