Skip to content

Commit

Permalink
Merge branch 'release/0.1.3' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvw committed Aug 1, 2023
2 parents 0ece110 + 3ed9db8 commit 663545f
Show file tree
Hide file tree
Showing 14 changed files with 384 additions and 28 deletions.
63 changes: 36 additions & 27 deletions edc_next_appointment/form_validators.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,55 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from edc_crf.crf_form_validator import CrfFormValidator
from edc_dx_review.utils import raise_if_clinical_review_does_not_exist
from edc_form_validators import INVALID_ERROR

if TYPE_CHECKING:
from edc_facility.models import HealthFacility


class NextAppointmentFormValidatorMixin(CrfFormValidator):
def __init__(self, **kwargs):
self._clinic_days = None
self._clinic_days_str = None
self._health_facility = None
super().__init__(**kwargs)

def clean(self):
raise_if_clinical_review_does_not_exist(self.cleaned_data.get("subject_visit"))
super().clean()

@property
def clinic_days(self) -> list[int]:
if not self._clinic_days:
def health_facility(self) -> HealthFacility | None:
if not self._health_facility:
if self.cleaned_data.get("health_facility"):
self._clinic_days = self.cleaned_data.get("health_facility").clinic_days
return self._clinic_days
self._health_facility = self.cleaned_data.get("health_facility")
else:
raise self.raise_validation_error(
{"health_facility": "This field is required."}
)
return self._health_facility

def validate_date_is_on_clinic_day(self):
if appt_date := self.cleaned_data.get("appt_date"):
if appt_date.isoweekday() > 5:
day = "Sat" if appt_date.isoweekday() == 6 else "Sun"
raise self.raise_validation_error(
{"appt_date": f"Expected Mon-Fri. Got {day}"},
INVALID_ERROR,
)
if appt_date and self.cleaned_data.get("subject_visit").site:
if (
self.integrated_clinic_days
and appt_date.isoweekday() not in self.integrated_clinic_days
):
dct = dict(zip([1, 2, 3, 4, 5, 6, 7], ["M", "T", "W", "Th", "F", "Sa", "Su"]))
expected_days = [v for k, v in dct.items() if k in self.clinic_days]
raise self.raise_validation_error(
{
"appt_date": (
"Invalid clinic day. "
f"Expected {''.join(expected_days)}. "
f"Got {appt_date.strftime('%A')}"
)
},
INVALID_ERROR,
)
if not self.health_facility.clinic_days:
if appt_date.isoweekday() > 5:
day = "Sat" if appt_date.isoweekday() == 6 else "Sun"
raise self.raise_validation_error(
{"appt_date": f"Expected Mon-Fri. Got {day}"},
INVALID_ERROR,
)
else:
if appt_date.isoweekday() not in self.health_facility.clinic_days:
raise self.raise_validation_error(
{
"appt_date": (
"Invalid clinic day for facility. "
f"Expected {self.health_facility.clinic_days_str}. "
f"Got {appt_date.strftime('%A')}"
)
},
INVALID_ERROR,
)
30 changes: 30 additions & 0 deletions edc_next_appointment/tests/holidays.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
2019-01-01,New Year's Day,tanzania
2019-01-12,Zanzibar Revolution Day,tanzania
2019-04-07,The Sheikh Abeid Amani Karume Day,tanzania
2019-04-17,Easter Monday,tanzania
2019-04-26,Union Day,tanzania
2019-05-01,Labour Day,tanzania
2019-06-26,Eid al-Fitr,tanzania
2019-07-07,Saba Saba Day,tanzania
2019-07-14,Good Friday,tanzania
2019-08-08,Nane Nane Day,tanzania
2019-10-14,Nyerere Day,tanzania
2019-12-01,Mawlid Day,tanzania
2019-12-09,Independence Day,tanzania
2019-12-25,Christmas Day,tanzania
2019-12-26,Boxing Day,tanzania
2019-01-01,New Year's Day,uganda
2019-01-12,Zanzibar Revolution Day,uganda
2019-04-07,The Sheikh Abeid Amani Karume Day,uganda
2019-04-17,Easter Monday,uganda
2019-04-26,Union Day,uganda
2019-05-01,Labour Day,uganda
2019-06-26,Eid al-Fitr,uganda
2019-07-07,Saba Saba Day,uganda
2019-07-14,Good Friday,uganda
2019-08-08,Nane Nane Day,uganda
2019-10-14,Nyerere Day,uganda
2019-12-01,Mawlid Day,uganda
2019-12-09,Independence Day,uganda
2019-12-25,Christmas Day,uganda
2019-12-26,Boxing Day,uganda
51 changes: 51 additions & 0 deletions edc_next_appointment/tests/tests/test_next_appointment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import User
from django.test import TestCase
from edc_appointment.models import Appointment
from edc_facility import import_holidays
from edc_reference import site_reference_configs
from edc_utils import get_utcnow
from edc_visit_schedule import site_visit_schedules
from edc_visit_schedule.apps import populate_visit_schedule
from edc_visit_tracking.utils import get_related_visit_model_cls

from next_appointment_app.models import SubjectConsent
from next_appointment_app.visit_schedules import visit_schedule


class TestNextAppointment(TestCase):
def setUp(self):
import_holidays()
self.user = User.objects.create_superuser("user_login", "u@example.com", "pass")

site_visit_schedules._registry = {}
site_visit_schedules.loaded = False
site_visit_schedules.register(visit_schedule)

populate_visit_schedule()

site_reference_configs.register_from_visit_schedule(
visit_models={"edc_appointment.appointment": "next_appointment_app.subjectvisit"}
)
self.subject_identifier = "101-40990029-4"
identity = "123456789"
subject_consent = SubjectConsent.objects.create(
subject_identifier=self.subject_identifier,
consent_datetime=get_utcnow() - relativedelta(days=10),
identity=identity,
confirm_identity=identity,
dob=get_utcnow() - relativedelta(years=25),
)

# put subject on schedule
_, schedule = site_visit_schedules.get_by_onschedule_model(
"next_appointment_app.onschedule"
)
schedule.put_on_schedule(
subject_identifier=subject_consent.subject_identifier,
onschedule_datetime=subject_consent.consent_datetime,
)

def test_ok(self):
self.assertEqual(5, Appointment.objects.all().count())
get_related_visit_model_cls()
Empty file.
34 changes: 34 additions & 0 deletions next_appointment_app/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from django.contrib import admin
from edc_model_admin.dashboard import (
ModelAdminCrfDashboardMixin,
ModelAdminSubjectDashboardMixin,
)

from .admin_site import next_appointment_app_admin
from .models import CrfOne, OffSchedule, OnSchedule, SubjectConsent, SubjectVisit


@admin.register(OnSchedule, site=next_appointment_app_admin)
class OnScheduleAdmin(ModelAdminSubjectDashboardMixin, admin.ModelAdmin):
pass


@admin.register(OffSchedule, site=next_appointment_app_admin)
class OffScheduleAdmin(ModelAdminSubjectDashboardMixin, admin.ModelAdmin):
pass


@admin.register(SubjectConsent, site=next_appointment_app_admin)
class SubjectConsentAdmin(ModelAdminSubjectDashboardMixin, admin.ModelAdmin):
pass


@admin.register(SubjectVisit, site=next_appointment_app_admin)
class SubjectVisitAdmin(ModelAdminSubjectDashboardMixin, admin.ModelAdmin):
pass


@admin.register(CrfOne, site=next_appointment_app_admin)
class CrfOneAdmin(ModelAdminCrfDashboardMixin, admin.ModelAdmin):
show_save_next = True
show_cancel = True
7 changes: 7 additions & 0 deletions next_appointment_app/admin_site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from edc_model_admin.admin_site import EdcAdminSite

from .apps import AppConfig

next_appointment_app_admin = EdcAdminSite(
name="next_appointment_app_admin", app_label=AppConfig.name
)
6 changes: 6 additions & 0 deletions next_appointment_app/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig as DjangoAppConfig


class AppConfig(DjangoAppConfig):
name = "next_appointment_app"
verbose_name = "next_appointment_app"
17 changes: 17 additions & 0 deletions next_appointment_app/consents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from edc_consent.consent import Consent
from edc_consent.site_consents import site_consents
from edc_constants.constants import FEMALE, MALE
from edc_protocol import Protocol

v1 = Consent(
"next_appointment_app.subjectconsent",
version="1",
start=Protocol().study_open_datetime,
end=Protocol().study_close_datetime,
age_min=18,
age_is_adult=18,
age_max=110,
gender=[MALE, FEMALE],
)

site_consents.register(v1)
69 changes: 69 additions & 0 deletions next_appointment_app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import uuid

from django.db import models
from edc_consent.field_mixins import PersonalFieldsMixin
from edc_consent.field_mixins.identity_fields_mixin import IdentityFieldsMixin
from edc_consent.model_mixins import ConsentModelMixin
from edc_identifier.managers import SubjectIdentifierManager
from edc_identifier.model_mixins import UniqueSubjectIdentifierFieldMixin
from edc_metadata.model_mixins.updates import UpdatesCrfMetadataModelMixin
from edc_model.models import BaseUuidModel
from edc_model.models.historical_records import HistoricalRecords
from edc_offstudy.model_mixins import OffstudyModelMixin
from edc_reference.model_mixins import ReferenceModelMixin
from edc_registration.model_mixins import UpdatesOrCreatesRegistrationModelMixin
from edc_sites.models import SiteModelMixin
from edc_visit_schedule.model_mixins import OffScheduleModelMixin, OnScheduleModelMixin
from edc_visit_tracking.model_mixins import VisitTrackingCrfModelMixin
from edc_visit_tracking.models import SubjectVisit


class OnSchedule(SiteModelMixin, OnScheduleModelMixin, BaseUuidModel):
pass


class OffSchedule(SiteModelMixin, OffScheduleModelMixin, BaseUuidModel):
pass


class SubjectOffstudy(OffstudyModelMixin, BaseUuidModel):
class Meta(OffstudyModelMixin.Meta):
pass


class SubjectConsent(
ConsentModelMixin,
PersonalFieldsMixin,
IdentityFieldsMixin,
UniqueSubjectIdentifierFieldMixin,
UpdatesOrCreatesRegistrationModelMixin,
SiteModelMixin,
BaseUuidModel,
):
objects = SubjectIdentifierManager()

history = HistoricalRecords()

def natural_key(self):
return (self.subject_identifier,) # noqa


class BaseCrfModel(
VisitTrackingCrfModelMixin,
SiteModelMixin,
UpdatesCrfMetadataModelMixin,
ReferenceModelMixin,
models.Model,
):
subject_visit = models.OneToOneField(
SubjectVisit, on_delete=models.PROTECT, related_name="next_appointment_subjectvisit"
)

f1 = models.CharField(max_length=50, default=uuid.uuid4)

class Meta:
abstract = True


class CrfOne(BaseCrfModel, BaseUuidModel):
f1 = models.CharField(max_length=50, null=True)
11 changes: 11 additions & 0 deletions next_appointment_app/navbars.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from edc_navbar.navbar import Navbar
from edc_navbar.site_navbars import site_navbars
from edc_review_dashboard.navbars import navbar as review_navbar

navbar = Navbar(name="next_appointment_app")

for item in review_navbar.items:
navbar.append_item(item)


site_navbars.register(navbar)
19 changes: 19 additions & 0 deletions next_appointment_app/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib import admin
from django.urls import path
from django.views.generic import RedirectView
from edc_utils.paths_for_urlpatterns import paths_for_urlpatterns

from .admin_site import next_appointment_app_admin
from .views import SubjectDashboardView

app_name = "next_appointment_app"

urlpatterns = SubjectDashboardView.urls(app_name, label="subject_dashboard")

urlpatterns += [
*paths_for_urlpatterns("edc_next_appointment"),
path("data_manager_app/admin/", next_appointment_app_admin.urls),
path("/admin", admin.site.urls),
path("", RedirectView.as_view("/admin"), name="home_url"),
path("", RedirectView.as_view("/admin"), name="administration_url"),
]
10 changes: 10 additions & 0 deletions next_appointment_app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from edc_subject_dashboard.views import SubjectDashboardView as BaseSubjectDashboardView


class SubjectDashboardView(BaseSubjectDashboardView):
consent_model = "next_appointment_app.subjectconsent"
navbar_name = "next_appointment_app"
visit_model = "next_appointment_app.subjectvisit"

def get_navbar_context_data(self, context):
return context
Loading

0 comments on commit 663545f

Please sign in to comment.