def onschedule_or_raise(self, subject_identifier=None, report_datetime=None, compare_as_datetimes=None): try: history_obj = self.history_model_cls.objects.get( subject_identifier=subject_identifier, visit_schedule_name=self.visit_schedule_name, schedule_name=self.schedule_name) except ObjectDoesNotExist: raise NotOnScheduleError( f'Subject is not been put on schedule {self.schedule_name}. ' f'Got {subject_identifier}.') offschedule_datetime = history_obj.offschedule_datetime or get_utcnow() if compare_as_datetimes: in_date_range = (history_obj.onschedule_datetime <= report_datetime <= offschedule_datetime) else: in_date_range = (history_obj.onschedule_datetime.date() <= report_datetime.date() <= offschedule_datetime.date()) if not in_date_range: formatted_date = report_datetime.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT)) raise NotOnScheduleForDateError( f'Subject not on schedule {self.schedule_name} on ' f'{formatted_date}. Got {subject_identifier}.')
def validate_assay_datetime(self, assay_datetime, requisition, field): if assay_datetime: assay_datetime = Arrow.fromdatetime( assay_datetime, assay_datetime.tzinfo).to('utc').datetime if assay_datetime < requisition.requisition_datetime: formatted = timezone.localtime(requisition.requisition_datetime).strftime( convert_php_dateformat(settings.SHORT_DATETIME_FORMAT)) raise forms.ValidationError({ field: (f'Invalid. Cannot be before date of ' f'requisition {formatted}.')})
def validate_requisition_datetime(self): requisition_datetime = self.cleaned_data.get('requisition_datetime') subject_visit = self.cleaned_data.get('subject_visit') if requisition_datetime: requisition_datetime = Arrow.fromdatetime( requisition_datetime, requisition_datetime.tzinfo).to('utc').datetime if requisition_datetime < subject_visit.report_datetime: formatted = timezone.localtime(subject_visit.report_datetime).strftime( convert_php_dateformat(settings.SHORT_DATETIME_FORMAT)) raise forms.ValidationError({ 'requisition_datetime': f'Invalid. Cannot be before date of visit {formatted}.'})
def validate_requisition_datetime(self): requisition_datetime = self.cleaned_data.get('requisition_datetime') maternal_visit = self.cleaned_data.get('maternal_visit') if requisition_datetime: requisition_datetime = Arrow.fromdatetime( requisition_datetime, requisition_datetime.tzinfo).to('utc').datetime if requisition_datetime < maternal_visit.report_datetime: formatted = timezone.localtime(maternal_visit.report_datetime).strftime( convert_php_dateformat(settings.SHORT_DATETIME_FORMAT)) raise forms.ValidationError({ 'requisition_datetime': f'Invalid. Cannot be before date of visit {formatted}.'})
def available_rdate(self, suggested_datetime=None, forward_delta=None, reverse_delta=None, taken_datetimes=None, include_holidays=None): """Returns an arrow object for a datetime equal to or close to the suggested datetime. To exclude datetimes other than holidays, pass a list of datetimes in UTC to `taken_datetimes`. """ available_rdate = None forward_delta = forward_delta or relativedelta(months=1) reverse_delta = reverse_delta or relativedelta(months=0) if suggested_datetime: suggested_rdate = arrow.Arrow.fromdatetime(suggested_datetime) else: suggested_rdate = arrow.Arrow.fromdatetime(get_utcnow()) minimum = self.to_arrow_utc(suggested_rdate.datetime - reverse_delta) maximum = self.to_arrow_utc(suggested_rdate.datetime + forward_delta) rtaken = [self.to_arrow_utc(dt) for dt in taken_datetimes or []] for r in arrow.Arrow.span_range('day', minimum.datetime, maximum.datetime): # add back time to arrow object, r r = arrow.Arrow.fromdatetime( datetime.combine(r[0].date(), suggested_rdate.time())) if (r.datetime.weekday() in self.weekdays and (minimum.date() <= r.date() < maximum.date())): if include_holidays: is_holiday = False else: is_holiday = self.is_holiday(r) if (not is_holiday and r.date() not in [r.date() for r in rtaken] and self.open_slot_on(r)): available_rdate = r break if not available_rdate: if self.best_effort_available_datetime: available_rdate = r else: formatted_date = suggested_datetime.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT)) raise FacilityError( f'No available appointment dates at facility for period. ' f'Got no available dates within {reverse_delta.days}-' f'{forward_delta.days} days of {formatted_date}. ' f'Facility is {repr(self)}.') return available_rdate
def get_consent(self, model=None, report_datetime=None, version=None, consent_group=None, **kwargs): """Return consent object valid for the datetime. """ app_config = django_apps.get_app_config('edc_consent') consent_group = consent_group or app_config.default_consent_group registered_consents = self.registry.values() if consent_group: registered_consents = [ c for c in registered_consents if c.group == consent_group] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}.') if version: registered_consents = [ c for c in registered_consents if c.version == version] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}.') if model: registered_consents = [ c for c in registered_consents if c.model == model] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}, ' f'model={model}.') registered_consents = [ c for c in registered_consents if c.start <= report_datetime <= c.end] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}, ' f'model={model}, report_datetime={report_datetime}.') elif len(registered_consents) > 1: consents = list(set([c.name for c in registered_consents])) formatted_report_datetime = report_datetime.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT)) raise ConsentError( f'Multiple consents found, using consent model={model}, ' f'date={formatted_report_datetime}, ' f'consent_group={consent_group}, version={version}. ' f'Got {consents}') return registered_consents[0]
def get_consent(self, model=None, report_datetime=None, version=None, consent_group=None, **kwargs): """Return consent object valid for the datetime. """ app_config = django_apps.get_app_config('edc_consent') consent_group = consent_group or app_config.default_consent_group registered_consents = self.registry.values() if consent_group: registered_consents = [ c for c in registered_consents if c.group == consent_group] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. Got consent_group={consent_group}.') if version: registered_consents = [ c for c in registered_consents if c.version == version] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}.') if model: registered_consents = [ c for c in registered_consents if c.model == model] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}, ' f'model={model}.') registered_consents = [ c for c in registered_consents if c.start <= report_datetime <= c.end] if not registered_consents: raise ConsentObjectDoesNotExist( f'No matching consent in site consents. ' f'Got consent_group={consent_group}, version={version}, ' f'model={model}, report_datetime={report_datetime}.') elif len(registered_consents) > 1: consents = list(set([c.name for c in registered_consents])) formatted_report_datetime = report_datetime.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT)) raise ConsentError( f'Multiple consents found, using consent model={model}, ' f'date={formatted_report_datetime}, ' f'consent_group={consent_group}, version={version}. ' f'Got {consents}') return registered_consents[0]
def validate_study_day_with_datetime(self, subject_identifier=None, study_day=None, compare_date=None, study_day_field=None): """Raises an exception if study day does not match calculation against import pytz. Note: study-day is 1-based. """ if study_day is not None and compare_date is not None: try: compare_date = compare_date.date() except AttributeError: pass subject_identifier = subject_identifier or self.cleaned_data.get( 'subject_identifier') or self.instance.subject_identifier if not subject_identifier: raise ValueError( f'Subject identifier cannot be None. See {repr(self)}') registered_subject_model_cls = django_apps.get_model( 'edc_registration.registeredsubject') randomization_datetime = registered_subject_model_cls.objects.get( subject_identifier=subject_identifier).randomization_datetime days_on_study = (compare_date - randomization_datetime.date()).days if study_day - 1 != days_on_study: tz = pytz.timezone(settings.TIME_ZONE) formatted_date = Arrow.fromdatetime(randomization_datetime).to( tz).strftime( convert_php_dateformat(settings.DATETIME_FORMAT)) message = { study_day_field: (f'Invalid. Expected {days_on_study + 1}. ' f'Subject was registered on {formatted_date}') } self._errors.update(message) self._error_codes.append(INVALID_ERROR) raise forms.ValidationError(message, code=INVALID_ERROR)
def action_item_with_popover(action_item_model_wrapper, tabindex): strike_thru = None action_item = action_item_model_wrapper.object href = action_item_model_wrapper.href date_format = convert_php_dateformat(settings.SHORT_DATE_FORMAT) if action_item.last_updated: last_updated = action_item.last_updated.strftime(date_format) user_last_updated = action_item.user_last_updated last_updated_text = ( f'Last updated on {last_updated} by {user_last_updated}.') else: last_updated_text = 'This action item has not been updated.' # this reference model and url reference_model_cls = django_apps.get_model(action_item.action_type.model) query_dict = dict(parse_qsl(urlparse(href).query)) model_fk_dict = model_fk(action_item_obj=action_item) if model_fk_dict: query_dict.update(model_fk_dict) parent_reference_model_url = None parent_reference_model_name = None action_item_reason = None parent_action_identifier = None # reference_model and url action_cls = site_action_items.get(reference_model_cls.action_name) try: reference_model_obj = reference_model_cls.objects.get( action_identifier=action_item.action_identifier) except ObjectDoesNotExist: reference_model_obj = None try: subject_visit = reference_model_obj.visit except (AttributeError, ObjectDoesNotExist): pass else: # reference model is a CRF, add visit to querystring query_dict.update({ reference_model_obj.visit_model_attr(): str(subject_visit.pk), 'appointment': str(subject_visit.appointment.pk) }) try: reference_model_url = action_cls.reference_model_url( action_item=action_item, action_identifier=action_item.action_identifier, reference_model_obj=reference_model_obj, **query_dict) except ObjectDoesNotExist: reference_model_url = None # object wont exist if an action item was deleted # that was created by another action item. strike_thru = True else: if action_item.parent_action_item: # parent action item parent_reference_model_cls = django_apps.get_model( action_item.parent_action_item.action_type.model) # parent reference model and url try: parent_reference_model_obj = parent_reference_model_cls.objects.get( tracking_identifier=action_item.parent_action_item. reference_identifier) except ObjectDoesNotExist: pass else: try: subject_visit = parent_reference_model_obj.visit except (AttributeError, ObjectDoesNotExist): pass else: # parent reference model is a CRF, add visit to querystring query_dict.update({ parent_reference_model_obj.visit_model_attr(): str(subject_visit.pk), 'appointment': str(subject_visit.appointment.pk) }) parent_reference_model_url = (action_cls.reference_model_url( reference_model_obj=parent_reference_model_obj, action_item=action_item, action_identifier=action_item.action_identifier, **query_dict)) parent_reference_model_name = ( f'{parent_reference_model_cls._meta.verbose_name} ' f'{parent_reference_model_obj.tracking_identifier}') action_item_reason = parent_reference_model_obj.action_item_reason parent_action_identifier = action_item.parent_action_item.action_identifier open_display = [c[1] for c in ACTION_STATUS if c[0] == OPEN][0] return dict(HIGH_PRIORITY=HIGH_PRIORITY, OPEN=open_display, action_instructions=action_item.instructions, action_item_reason=action_item_reason, report_datetime=action_item.report_datetime, display_name=action_item.action_type.display_name, action_identifier=action_item.action_identifier, parent_action_identifier=parent_action_identifier, parent_action_item=action_item.parent_action_item, href=href, last_updated_text=last_updated_text, name=action_item.action_type.name, reference_model_name=reference_model_cls._meta.verbose_name, reference_model_url=reference_model_url, reference_model_obj=reference_model_obj, action_item_color=action_cls.color_style, parent_model_name=parent_reference_model_name, parent_model_url=parent_reference_model_url, priority=action_item.priority or '', status=action_item.get_status_display(), tabindex=tabindex, strike_thru=strike_thru)
def formatted_date(self): return self.local_date.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT))
def clean(self): try: death_report = self.death_report_model_cls.objects.get( subject_identifier=self.cleaned_data.get('subject_identifier')) except ObjectDoesNotExist: if self.cleaned_data.get('termination_reason') == DEAD: raise forms.ValidationError({ 'termination_reason': 'Patient is deceased, please complete death report form first.' }) else: if self.cleaned_data.get('death_date'): try: self.death_report_model_cls.objects.get( subject_identifier=self.cleaned_data.get( 'subject_identifier'), death_datetime__date=self.cleaned_data.get( 'death_date')) except ObjectDoesNotExist: formatted_date = death_report.death_datetime.strftime( convert_php_dateformat(settings.SHORT_DATE_FORMAT)) raise forms.ValidationError({ 'death_date': 'Date does not match Death Report. ' f'Expected {formatted_date}.' }) self.required_if(YES, field='discharged_after_initial_admission', field_required='initial_discharge_date') self.applicable_if( YES, field='discharged_after_initial_admission', field_applicable='readmission_after_initial_discharge') self.required_if(YES, field='readmission_after_initial_discharge', field_required='readmission_date') self.required_if(DEAD, field='termination_reason', field_required='death_date') self.required_if(CONSENT_WITHDRAWAL, field='termination_reason', field_required='consent_withdrawal_reason') self.applicable_if(CONSENT_WITHDRAWAL, field='termination_reason', field_applicable='willing_to_complete_10w') self.applicable_if('care_transferred_to_another_institution', field='termination_reason', field_applicable='willing_to_complete_centre') self.required_if_true( condition=(self.cleaned_data.get('willing_to_complete_10w') == YES or self.cleaned_data.get('willing_to_complete_centre') == YES), field_required='willing_to_complete_date') self.applicable_if('late_exclusion_criteria_met', field='termination_reason', field_applicable='protocol_exclusion_criterion') self.required_if('included_in_error', field='termination_reason', field_required='included_in_error') self.required_if('included_in_error', field='termination_reason', field_required='included_in_error_date') self.validate_other_specify(field='first_line_regimen') self.validate_other_specify(field='second_line_regimen') self.not_applicable_if(NOT_APPLICABLE, field='first_line_regimen', field_applicable='first_line_choice')