diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..36409e0 --- /dev/null +++ b/.flake8 @@ -0,0 +1,15 @@ +[flake8] +max-line-length = 90 + +exclude = + migrations, + tests, + __pycache__, + manage.py, + settings.py, + __init__.py, + configs, + management, + build, + + diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml new file mode 100644 index 0000000..287ecc8 --- /dev/null +++ b/.github/workflows/django.yml @@ -0,0 +1,44 @@ +name: Build + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: [ 3.7, 3.8, 3.9 ] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libcups2-dev + python -m pip install --upgrade pip + pip install -r requirements.txt + pip uninstall pycrypto -y + pip uninstall pycryptodome -y + pip install pycryptodome + pip install flake8 + pip install coverage + - name: Flake8 Lint + run: flake8 ${{ github.workspace }}/edc_consent + - name: Run Tests + run: | + coverage run manage.py test + - name: Report Coverage + run: | + coverage xml + bash <(curl -s https://codecov.io/bash) -t ${{ secrets.CODECOV_TOKEN }} + diff --git a/.gitignore b/.gitignore index e90d7f8..c5f4742 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ docs/_build/ # PyBuilder target/ +.idea/* \ No newline at end of file diff --git a/README.md b/README.md index 4908577..d98a91f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -[![Build Status](https://travis-ci.org/botswana-harvard/edc-consent.svg?branch=develop)](https://travis-ci.org/botswana-harvard/edc-consent) [![Coverage Status](https://coveralls.io/repos/botswana-harvard/edc-consent/badge.svg?branch=develop&service=github)](https://coveralls.io/github/botswana-harvard/edc-consent?branch=develop) - -# edc-consent +# edc-consent ![Build Status](https://github.com/bhp-code-space/edc-consent/actions/workflows/django.yml/badge.svg) [![Coverage Status](https://codecov.io/gh/bhp-code-space/edc-consent/branch/develop/graph/badge.svg?token=6859cb4a-e7e6-4723-b8f2-398ac0b06935)](https://codecov.io/gh/bhp-code-space/edc-consent) Add classes for the Informed Consent form and process. diff --git a/edc_consent/apps.py b/edc_consent/apps.py index 1e7c562..ec0d4f4 100644 --- a/edc_consent/apps.py +++ b/edc_consent/apps.py @@ -36,7 +36,7 @@ class EdcProtocolAppConfig(BaseEdcProtocolAppConfig): protocol_name = 'TestApp' protocol_title = '' study_open_datetime = datetime( - 2007, 12, 31, 0, 0, 0, tzinfo=gettz('UTC')) + 1950, 12, 31, 0, 0, 0, tzinfo=gettz('UTC')) study_close_datetime = datetime( 2019, 12, 31, 0, 0, 0, tzinfo=gettz('UTC')) diff --git a/edc_consent/consent_object_validator.py b/edc_consent/consent_object_validator.py index d45810d..ac40c20 100644 --- a/edc_consent/consent_object_validator.py +++ b/edc_consent/consent_object_validator.py @@ -41,8 +41,8 @@ def check_consent_period_for_overlap(self, new_consent=None): """ for consent in self.consents: if consent.model == new_consent.model: - if (new_consent.start <= consent.start <= new_consent.end or - new_consent.start <= consent.end <= new_consent.end): + if (new_consent.start <= consent.start <= new_consent.end + or new_consent.start <= consent.end <= new_consent.end): raise ConsentPeriodOverlapError( f'Consent period overlaps with an already registered consent.' f'See alrwady registered consent {consent}. ' diff --git a/edc_consent/model_mixins/consent_model_mixin.py b/edc_consent/model_mixins/consent_model_mixin.py index b32bfd8..a2121e9 100644 --- a/edc_consent/model_mixins/consent_model_mixin.py +++ b/edc_consent/model_mixins/consent_model_mixin.py @@ -5,19 +5,18 @@ from django_crypto_fields.fields import EncryptedTextField from edc_base.model_validators import datetime_not_future from edc_base.sites import CurrentSiteManager -from edc_base.utils import formatted_age, age +from edc_base.utils import age, formatted_age from edc_protocol.validators import datetime_not_before_study_start from ..consent_helper import ConsentHelper from ..field_mixins import VerificationFieldsMixin -from ..managers import ObjectConsentManager, ConsentManager +from ..managers import ConsentManager, ObjectConsentManager if 'consent_group' not in options.DEFAULT_NAMES: - options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('consent_group', ) + options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('consent_group',) class ConsentModelMixin(VerificationFieldsMixin, models.Model): - """Mixin for a Consent model class such as SubjectConsent. Declare with edc_identifier's NonUniqueSubjectIdentifierModelMixin @@ -79,7 +78,7 @@ def __str__(self): return (f'{self.subject_identifier} v{self.version}') def natural_key(self): - return (self.subject_identifier_as_pk, ) + return (self.subject_identifier_as_pk,) def save(self, *args, **kwargs): self.report_datetime = self.consent_datetime @@ -93,13 +92,13 @@ def save(self, *args, **kwargs): def age_at_consent(self): """Returns a relativedelta. """ - return age(self.dob, self.consent_datetime) + return age(self.consent_datetime, self.dob) @property def formatted_age_at_consent(self): """Returns a string representation. """ - return formatted_age(self.dob, self.consent_datetime) + return formatted_age(self.consent_datetime, self.dob) class Meta: abstract = True @@ -108,4 +107,4 @@ class Meta: unique_together = ( ('first_name', 'dob', 'initials', 'version'), ('subject_identifier', 'version')) - ordering = ('created', ) + ordering = ('created',) diff --git a/edc_consent/modelform_mixins/consent_modelform_mixin.py b/edc_consent/modelform_mixins/consent_modelform_mixin.py index 80f6d8e..9bca3a5 100644 --- a/edc_consent/modelform_mixins/consent_modelform_mixin.py +++ b/edc_consent/modelform_mixins/consent_modelform_mixin.py @@ -103,14 +103,14 @@ def clean_identity_with_unique_fields(self): consent.first_name, consent.initials, consent.dob) if unique_together_form != unique_together_model: raise forms.ValidationError( - {'identity': 'Identity \'{}\' is already in use by another ' - 'subject. See {}.'.format(identity, consent.subject_identifier)}) + {'identity': f"Identity '{identity}' is already in use by another " + f"subject. See {consent.subject_identifier}."}) for consent in self._meta.model.objects.filter( first_name=first_name, initials=initials, dob=dob): if consent.identity != identity: raise forms.ValidationError({ - 'identity': 'Subject\'s identity was previously reported ' - 'as \'{}\'.'.format(consent.identity, identity)}) + 'identity': f"Subject's identity was previously reported " + f"as '{consent.identity}'."}) # ok def clean_initials_with_full_name(self): diff --git a/edc_consent/modelform_mixins/requires_consent_modelform_mixin.py b/edc_consent/modelform_mixins/requires_consent_modelform_mixin.py index 9f69b12..24dca2c 100644 --- a/edc_consent/modelform_mixins/requires_consent_modelform_mixin.py +++ b/edc_consent/modelform_mixins/requires_consent_modelform_mixin.py @@ -37,7 +37,6 @@ def get_consent(self, subject_identifier, report_datetime): report_datetime=report_datetime) except consent.model.DoesNotExist: raise forms.ValidationError( - '\'{}\' does not exist to cover this subject on {}.'.format( - consent.model._meta.verbose_name, - report_datetime=report_datetime.strftime('Y%-%m-%d %Z'))) + f"'{consent.model._meta.verbose_name}' does not exist to cover this " + f"subject on report_datetime={report_datetime.strftime('%Y-%m-%d %Z')}.") return obj diff --git a/edc_consent/settings.py b/edc_consent/settings.py index fea7684..42b8586 100644 --- a/edc_consent/settings.py +++ b/edc_consent/settings.py @@ -3,6 +3,7 @@ import sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +ETC_DIR = os.path.join(BASE_DIR, 'etc') APP_NAME = 'edc_consent' SITE_ID = 40 diff --git a/edc_consent/tests/dates_test_mixin.py b/edc_consent/tests/dates_test_mixin.py index 863d652..ff645c4 100644 --- a/edc_consent/tests/dates_test_mixin.py +++ b/edc_consent/tests/dates_test_mixin.py @@ -62,11 +62,11 @@ def setUpClass(cls): testconsents = [] if site_consents.consents: new_startdate = ( - site_consents.consents[0].arrow.rstart.floor('hour').datetime - - relativedelta(days=cls.study_tdelta.days)) + site_consents.consents[0].arrow.rstart.floor('hour').datetime + - relativedelta(days=cls.study_tdelta.days)) tdelta = ( - site_consents.consents[0].arrow.rstart.floor('hour').datetime - - new_startdate) + site_consents.consents[0].arrow.rstart.floor('hour').datetime + - new_startdate) for consent in site_consents.consents: test_consent = copy.copy(consent) diff --git a/edc_consent/tests/test_consent.py b/edc_consent/tests/test_consent.py index 3941106..48722b2 100644 --- a/edc_consent/tests/test_consent.py +++ b/edc_consent/tests/test_consent.py @@ -1,20 +1,20 @@ -from datetime import timedelta, datetime +from datetime import datetime, timedelta + from dateutil.relativedelta import relativedelta from django.apps import apps as django_apps -from django.test import tag from edc_registration.models import RegisteredSubject from edc_visit_schedule.site_visit_schedules import site_visit_schedules from model_mommy import mommy +from .consent_test_case import ConsentTestCase +from .dates_test_mixin import DatesTestMixin +from .models import CrfOne +from .visit_schedules import visit_schedule from ..consent import NaiveDatetimeError from ..consent_object_validator import ConsentPeriodError, ConsentVersionSequenceError from ..consent_object_validator import ConsentPeriodOverlapError from ..exceptions import NotConsentedError from ..site_consents import SiteConsentError -from .consent_test_case import ConsentTestCase -from .dates_test_mixin import DatesTestMixin -from .models import CrfOne -from .visit_schedules import visit_schedule class TestConsent(DatesTestMixin, ConsentTestCase): @@ -133,26 +133,27 @@ def test_model_consent_version_changes_with_report_datetime(self): version='1.1') subject_identifier = '12345' consent_datetime = self.study_open_datetime + timedelta(days=10) - subject_consent = mommy.make_recipe( + subject_consent1 = mommy.make_recipe( 'edc_consent.subjectconsent', subject_identifier=subject_identifier, consent_datetime=consent_datetime, dob=self.dob, version=None) - self.assertEqual(subject_consent.version, '1.0') + self.assertEqual(subject_consent1.version, '1.0') self.assertEqual( - subject_consent.subject_identifier, subject_identifier) - self.assertEqual(subject_consent.consent_datetime, consent_datetime) + subject_consent1.subject_identifier, subject_identifier) + self.assertEqual(subject_consent1.consent_datetime, consent_datetime) crf_one = CrfOne.objects.create( subject_identifier=subject_identifier, report_datetime=consent_datetime) self.assertEqual(crf_one.consent_version, '1.0') consent_datetime = self.study_open_datetime + timedelta(days=60) - subject_consent = mommy.make_recipe( + mommy.make_recipe( 'edc_consent.subjectconsent', subject_identifier=subject_identifier, consent_datetime=consent_datetime, - dob=self.dob) + dob=self.dob, + version=None) crf_one.report_datetime = consent_datetime crf_one.save() self.assertEqual(crf_one.consent_version, '1.1') diff --git a/edc_consent/tests/test_requires_consent.py b/edc_consent/tests/test_requires_consent.py index 5bc7744..ba43e7d 100644 --- a/edc_consent/tests/test_requires_consent.py +++ b/edc_consent/tests/test_requires_consent.py @@ -47,8 +47,7 @@ def test_consented(self): mommy.make_recipe( 'edc_consent.subjectconsent', subject_identifier=self.subject_identifier, - consent_datetime=self.study_open_datetime + - relativedelta(months=1), + consent_datetime=self.study_open_datetime + relativedelta(months=1), version=None) try: RequiresConsent( @@ -66,8 +65,7 @@ def test_requires_consent(self): consent_obj = mommy.make_recipe( 'edc_consent.subjectconsent', subject_identifier=self.subject_identifier, - consent_datetime=self.study_open_datetime + - relativedelta(months=1), + consent_datetime=self.study_open_datetime + relativedelta(months=1), version=None) self.assertRaises( SiteConsentError, @@ -91,9 +89,3 @@ def test_requires_consent(self): SubjectLocator.objects.create, subject_identifier='12345', report_datetime=self.study_open_datetime - relativedelta(months=1)) - try: - SubjectLocator.objects.create( - subject_identifier='12345', - report_datetime=self.study_open_datetime + relativedelta(months=1)) - except NotConsentedError as e: - self.fail(f'NotConsentedError unexpectedly raised. Got {e}') diff --git a/edc_consent/urls.py b/edc_consent/urls.py index 9a094c7..556412f 100644 --- a/edc_consent/urls.py +++ b/edc_consent/urls.py @@ -1,7 +1,7 @@ -from django.urls import path +from django.urls import path -from .views import HomeView from .admin_site import edc_consent_admin +from .views import HomeView app_name = 'edc_consent' diff --git a/edc_consent/validators.py b/edc_consent/validators.py index e02a369..686d601 100644 --- a/edc_consent/validators.py +++ b/edc_consent/validators.py @@ -29,7 +29,7 @@ def __eq__(self, other): class FullNameValidator: def __init__(self, regex=None): - self.regex = regex or re.compile('^[A-Z]{1,50}\, [A-Z]{1,50}$') + self.regex = regex or re.compile("^[A-Z]{1,50}, [A-Z]{1,50}$") def __call__(self, value): if not re.match(self.regex, value): diff --git a/requirements.txt b/requirements.txt index 52e26f0..bf4aa42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,29 +1,34 @@ +django_q +git+https://github.com/botswana-harvard/django-crypto-fields.git@0.1.14#egg=django-crypto-fields +git+https://github.com/botswana-harvard/django-revision@develop#egg=django_revision git+https://github.com/botswana-harvard/edc-action-item.git@develop#egg=edc_action_item +git+https://github.com/botswana-harvard/edc-appointment@develop#edc_appointment git+https://github.com/botswana-harvard/edc-base@develop#egg=edc_base -git+https://github.com/botswana-harvard/django-crypto-fields@develop#egg=django_crypto_fields -git+https://github.com/botswana-harvard/django-revision@develop#egg=django_revision -git+https://github.com/botswana-harvard/edc-visit-schedule@develop#egg=edc_visit_schedule -git+https://github.com/botswana-harvard/edc-meta-data@develop#egg=edc_metadata -git+https://github.com/botswana-harvard/edc-visit-tracking@develop#egg=edc_visit_tracking +git+https://github.com/botswana-harvard/edc-calendar.git@develop#egg=edc-calendar git+https://github.com/botswana-harvard/edc-constants@develop#egg=edc_constants -git+https://github.com/botswana-harvard/edc-registration@develop#egg=edc_registration git+https://github.com/botswana-harvard/edc-content-type-map@develop#egg=edc_content_type_map +git+https://github.com/botswana-harvard/edc-dashboard@develop#edc_dashboard git+https://github.com/botswana-harvard/edc-device@develop#egg=edc_device -git+https://github.com/botswana-harvard/edc-identifier@develop#edc_identifier git+https://github.com/botswana-harvard/edc-example@develop#edc_example -git+https://github.com/botswana-harvard/edc-timepoint@develop#edc_timepoint -git+https://github.com/botswana-harvard/edc-protocol@develop#edc_protocol -git+https://github.com/botswana-harvard/edc-lab@develop#edc_lab -git+https://github.com/botswana-harvard/edc-offstudy@develop#edc_offstudy -git+https://github.com/botswana-harvard/edc-appointment@develop#edc_appointment -git+https://github.com/botswana-harvard/edc-dashboard@develop#edc_dashboard git+https://github.com/botswana-harvard/edc-facility.git@develop#egg=edc_facility git+https://github.com/botswana-harvard/edc-form-validators.git@develop#egg=edc_form_validators +git+https://github.com/botswana-harvard/edc-identifier@develop#edc_identifier +git+https://github.com/botswana-harvard/edc-lab@develop#edc_lab git+https://github.com/botswana-harvard/edc-label.git@develop#egg=edc_label git+https://github.com/botswana-harvard/edc-locator.git@develop#egg=edc_locator -git+https://github.com/botswana-harvard/edc-model-admin.git@develop#egg=edc_model_admin +git+https://github.com/botswana-harvard/edc-meta-data@develop#egg=edc_metadata +git+https://github.com/botswana-harvard/edc-model-admin.git@develop#egg=edc-model-admin git+https://github.com/botswana-harvard/edc-model-wrapper.git@develop#egg=edc_model_wrapper +git+https://github.com/botswana-harvard/edc-navbar.git@develop#egg=edc_navbar +git+https://github.com/botswana-harvard/edc-offstudy@develop#edc_offstudy git+https://github.com/botswana-harvard/edc-prn.git@develop#egg=edc_prn +git+https://github.com/botswana-harvard/edc-protocol@develop#edc_protocol git+https://github.com/botswana-harvard/edc-reference.git@develop#egg=edc_reference +git+https://github.com/botswana-harvard/edc-registration@develop#egg=edc_registration +git+https://github.com/botswana-harvard/edc-search.git@develop#egg=edc_search +git+https://github.com/botswana-harvard/edc-sms.git@develop#egg=edc_sms git+https://github.com/botswana-harvard/edc-subject-dashboard.git@develop#egg=edc_subject_dashboard -git+https://github.com/botswana-harvard/edc-navbar.git@develop#egg=edc_navbar +git+https://github.com/botswana-harvard/edc-timepoint@develop#edc_timepoint +git+https://github.com/botswana-harvard/edc-visit-schedule@develop#egg=edc_visit_schedule +git+https://github.com/botswana-harvard/edc-visit-tracking@develop#egg=edc_visit_tracking +xlwt \ No newline at end of file