Skip to content

Commit

Permalink
add list methods to formscollection, pep8, build
Browse files Browse the repository at this point in the history
  • Loading branch information
erikvw committed Apr 27, 2022
1 parent 56735b9 commit d3775fd
Show file tree
Hide file tree
Showing 26 changed files with 231 additions and 116 deletions.
16 changes: 2 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,12 @@ jobs:
python-version: ['3.9']
django-version: ['3.2']

services:

mysql:
image: mysql:latest
env:
MYSQL_DATABASE: mysql
MYSQL_ROOT_PASSWORD: mysql
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

steps:
- name: Install pycups and words dependency
run: |
sudo apt-get install libcups2-dev wamerican
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
Expand Down Expand Up @@ -62,7 +50,7 @@ jobs:
env:
DJANGO: ${{ matrix.django-version }}

- name: Upload coverage
uses: codecov/codecov-action@v1
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
name: Python ${{ matrix.python-version }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
_version.py
django_crypto_fields
.env

Expand Down
Empty file added AUTHORS
Empty file.
File renamed without changes.
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ include MANIFEST.in
include *.rst
include *.txt
recursive-include edc_visit_schedule/templates *
recursive-include edc_visit_schedule/static *
5 changes: 4 additions & 1 deletion edc_visit_schedule/admin/subject_schedule_history_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ def dashboard(self, obj=None, label=None):

def review(self, obj=None):
try:
url = f"{reverse('edc_review_dashboard:subject_review_listboard_url')}?q={obj.subject_identifier}"
url = (
f"{reverse('edc_review_dashboard:subject_review_listboard_url')}?"
f"q={obj.subject_identifier}"
)
except NoReverseMatch:
context = {}
else:
Expand Down
2 changes: 0 additions & 2 deletions edc_visit_schedule/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@

class SubjectScheduleHistoryForm(forms.ModelForm):
def clean(self):
cleaned_data = super().clean()
raise forms.ValidationError(
"This is not a user form. This form may only be edited by the system."
)
return cleaned_data

class Meta:
model = SubjectScheduleHistory
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/ordered_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _iter_keys(self, key=None, reverse=None):
seq = reversed(self.keys())
else:
seq = iter(self.keys())
keys = itertools.dropwhile(lambda k: k != key, seq)
keys = itertools.dropwhile(lambda x: x != key, seq)
try:
k = next(keys)
except StopIteration:
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/schedule/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def raise_for_scheduled_not_in_window(self):

def raise_for_unscheduled_not_in_window(self):
"""Returns the datetime if it falls within the
window period for a unscheduled `visit` otherwise
window period for an unscheduled `visit` otherwise
raises an exception.
Window period for an unscheduled date is anytime
Expand Down
19 changes: 18 additions & 1 deletion edc_visit_schedule/tests/tests/test_forms_collection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.test import TestCase, tag
from django.test import TestCase
from django.test.utils import override_settings

from edc_visit_schedule.visit import Crf, FormsCollection, FormsCollectionError
Expand Down Expand Up @@ -59,3 +59,20 @@ def test_forms_collection_excludes_by_site_id3(self):
crfs.append(Crf(show_order=i, model=f"x.{i}"))
forms = FormsCollection(*crfs)
self.assertEqual(len(forms.forms), 10)

@override_settings(SITE_ID=40)
def test_forms_collection_list_like_behaviour(self):
forms = FormsCollection()
for i in range(0, 5):
forms.append(Crf(show_order=i, model=f"x.{i}"))
for i in range(6, 11):
forms.append(Crf(show_order=i, model=f"x.{i}"))
self.assertEqual(len(forms), 10)
forms.remove(Crf(show_order=1, model="x.1"))
self.assertEqual(len(forms), 9)
max_show_order = max([item.show_order for item in forms])
forms.insert_last(Crf(show_order=1, model="x.1"))
self.assertGreater(max([item.show_order for item in forms]), max_show_order)
self.assertRaises(
FormsCollectionError, forms.insert_last, Crf(show_order=1, model="x.1")
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.test import TestCase, tag
from django.test import TestCase

from edc_visit_schedule.visit_schedule import (
SchedulesCollection,
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_site_visit_schedule.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.test import TestCase, tag
from django.test import TestCase

from edc_visit_schedule.schedule import Schedule
from edc_visit_schedule.site_visit_schedules import (
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_subject_schedule.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase, tag
from django.test import TestCase
from edc_consent import site_consents
from edc_consent.consent import Consent
from edc_constants.constants import FEMALE, MALE
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_system_checks.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dateutil.relativedelta import relativedelta
from django.apps import apps as django_apps
from django.test import TestCase, tag
from django.test import TestCase

from edc_visit_schedule.schedule import Schedule
from edc_visit_schedule.site_visit_schedules import site_visit_schedules
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import date

from dateutil.relativedelta import relativedelta
from django.test import TestCase, override_settings, tag
from django.test import TestCase, override_settings
from edc_appointment.models import Appointment
from edc_consent import site_consents
from edc_consent.consent import Consent
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_visit.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def test_good_codes(self):
rupper=relativedelta(days=6),
timepoint=1,
)
except (VisitCodeError) as e:
except VisitCodeError as e:
self.fail(f"VisitError unexpectedly raised. Got {e}")
try:
Visit(
Expand Down
2 changes: 1 addition & 1 deletion edc_visit_schedule/tests/tests/test_visit_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dateutil.relativedelta import relativedelta
from dateutil.tz import tzutc
from django.test import TestCase, override_settings, tag
from django.test import TestCase, override_settings
from edc_appointment.models import Appointment
from edc_consent import site_consents
from edc_consent.consent import Consent
Expand Down
18 changes: 0 additions & 18 deletions edc_visit_schedule/views/home_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,9 @@
from edc_dashboard.view_mixins import EdcViewMixin
from edc_navbar.view_mixin import NavbarViewMixin

from ..site_visit_schedules import SiteVisitScheduleError, site_visit_schedules


class HomeView(EdcViewMixin, NavbarViewMixin, TemplateView):

template_name = f"edc_visit_schedule/bootstrap{settings.EDC_BOOTSTRAP}/home.html"
navbar_name = "edc_visit_schedule"
navbar_selected_item = "visit_schedule"

# def get_context_data(self, **kwargs):
# context_data = super().get_context_data(**kwargs)
# try:
# selected_visit_schedule = site_visit_schedules.get_visit_schedule(
# visit_schedule_name=self.kwargs.get("visit_schedule")
# )
# except SiteVisitScheduleError:
# selected_visit_schedule = None
# context_data.update(
# {
# "visit_schedules": site_visit_schedules.registry,
# "selected_visit_schedule": selected_visit_schedule,
# }
# )
# return context_data
4 changes: 4 additions & 0 deletions edc_visit_schedule/visit/crf.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ def __str__(self):
required = "Required" if self.required else ""
return f"{self.model} {required}"

@property
def name(self) -> str:
return f"{self.model}.{'required' if self.required else 'not_required'}"

def validate(self):
"""Raises an exception if the model class lookup fails."""
try:
Expand Down
68 changes: 65 additions & 3 deletions edc_visit_schedule/visit/forms_collection.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
from typing import Optional, Tuple, Union
from uuid import uuid4

from django.conf import settings

from .crf import Crf
from .requisition import Requisition


class FormsCollectionError(Exception):
pass


class FormsCollection:
def __init__(self, *forms, name=None, **kwargs):
self._forms = None
self.name = name
def __init__(self, *forms: Union[Crf, Requisition], name: Optional[str] = None, **kwargs):
self._forms: Optional[Tuple[Union[Crf, Requisition]]] = None
self.name = name or uuid4().hex
forms = [] if not forms or forms == (None,) else list(forms)

# exclude any flagged for a site that is not the current
Expand Down Expand Up @@ -37,6 +43,62 @@ def __repr__(self):
def __iter__(self):
return iter(self._forms)

def __len__(self):
return len(self._forms)

def append(self, value):
if value:
forms = list(self._forms)
for item in forms:
if item.name == value.name:
raise FormsCollectionError(
f"Append failed. Item is not unique. Got {value.name}"
)
forms.append(value)
forms.sort(key=lambda x: x.show_order)
self._forms = forms

def extend(self, value: Union[tuple, list]):
if value:
for v in value:
self.append(v)
forms = list(self._forms)
forms.sort(key=lambda x: x.show_order)
self._forms = tuple(forms)

def insert(self, index, value):
if value:
forms = list(self._forms)
for index, item in forms:
if item.name == value.name:
raise FormsCollectionError(
f"Insert failed. Item is not unique. Got {value.name}"
)
forms.insert(index, value)
forms.sort(key=lambda x: x.show_order)
self._forms = tuple(forms)

def remove(self, value):
if value:
forms = list(self._forms)
for index, item in enumerate(forms):
if item.name == value.name:
forms.pop(index)
self._forms = tuple(forms)
break
else:
raise FormsCollectionError("Remove failed. Item not found")

def pop(self, index):
forms = list(self._forms)
forms.pop(index)
self._forms = tuple(forms)

def insert_last(self, value):
forms = list(self._forms)
value.show_order = 100 + max([item.show_order for item in forms or []])
self.append(value)

@property
def forms(self):
return self._forms
43 changes: 27 additions & 16 deletions edc_visit_schedule/visit/visit.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import re
from decimal import Decimal
from typing import Optional

import arrow
from dateutil.relativedelta import relativedelta
from django.apps import apps as django_apps
from django.conf import settings

from .forms_collection import FormsCollection
from .window_period import WindowPeriod


Expand Down Expand Up @@ -49,34 +52,41 @@ class Visit:

def __init__(
self,
code=None,
timepoint=None,
rbase=None,
rlower=None,
rupper=None,
crfs=None,
requisitions=None,
crfs_unscheduled=None,
crfs_missed=None,
requisitions_unscheduled=None,
crfs_prn=None,
requisitions_prn=None,
title=None,
code: Optional[str] = None,
timepoint: Optional[int] = None,
rbase: Optional[relativedelta] = None,
rlower: Optional[relativedelta] = None,
rupper: Optional[relativedelta] = None,
crfs: Optional[FormsCollection] = None,
requisitions: Optional[FormsCollection] = None,
crfs_unscheduled: Optional[FormsCollection] = None,
crfs_missed: Optional[FormsCollection] = None,
requisitions_unscheduled: Optional[FormsCollection] = None,
crfs_prn: Optional[FormsCollection] = None,
requisitions_prn: Optional[FormsCollection] = None,
title: Optional[str] = None,
allow_unscheduled: Optional[bool] = None,
facility_name: Optional[str] = None,
instructions=None,
grouping=None,
allow_unscheduled=None,
facility_name=None,
):

self.crfs = crfs.forms if crfs else ()
self.crfs = crfs.forms if crfs else tuple()
self.crfs_unscheduled = crfs_unscheduled.forms if crfs_unscheduled else ()
self.crfs_missed = crfs_missed.forms if crfs_missed else ()
self.crfs_prn = crfs_prn.forms if crfs_prn else ()
for prn in self.crfs_prn:
prn.required = False

self.requisitions = requisitions.forms if requisitions else ()
self.requisitions_unscheduled = (
requisitions_unscheduled.forms if requisitions_unscheduled else ()
)
self.requisitions_prn = requisitions_prn.forms if requisitions_prn else ()
for prn in self.requisitions_prn:
prn.required = False
# self.requisitions = self.requisitions + self.requisitions_prn

self.instructions = instructions
self.timepoint = Decimal(str(timepoint))
self.rbase = rbase
Expand Down Expand Up @@ -186,6 +196,7 @@ def timepoint_datetime(self, dt=None):

def check(self):
warnings = []
crf = None
try:
for crf in self.crfs:
django_apps.get_model(crf.model)
Expand Down
10 changes: 7 additions & 3 deletions edc_visit_schedule/visit_schedule/visit_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,14 @@ def check(self):

@property
def all_post_consent_models(self):
"""Returns a dictionary of models and the needed consent model.
"""Returns a dictionary of models and the needed consent
model.
These models may only be complete after the consent model.
{model_name1: consent_model_name, model_name2: consent_model_name, ...}
{model_name1:
consent_model_name, model_name2:
consent_model_name, ...}
"""
if not self._all_post_consent_models:
models = {}
Expand All @@ -131,7 +135,7 @@ def all_post_consent_models(self):
models.update({crf.model: schedule.consent_model})
if None in (list(models.keys())):
raise VisitScheduleError(
f"One or more required models has not been defined. "
"One or more required models has not been defined. "
f"Check the declaration for visit schedule '{self}'. "
f"Got {models}."
)
Expand Down
Loading

0 comments on commit d3775fd

Please sign in to comment.