clinicedc 2.0.4__py3-none-any.whl → 2.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clinicedc might be problematic. Click here for more details.
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/METADATA +41 -45
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/RECORD +478 -478
- edc_action_item/action.py +76 -54
- edc_action_item/action_item_notification.py +1 -3
- edc_action_item/action_with_notification.py +4 -4
- edc_action_item/create_action_item.py +2 -2
- edc_action_item/create_or_update_action_type.py +1 -1
- edc_action_item/data_fixers.py +4 -5
- edc_action_item/delete_action_item.py +0 -1
- edc_action_item/modeladmin_mixins.py +2 -4
- edc_action_item/modelform_mixins/modelform_mixins.py +1 -1
- edc_action_item/models/action_item.py +53 -38
- edc_action_item/models/action_model_mixin.py +17 -17
- edc_action_item/models/action_type.py +33 -13
- edc_action_item/models/reference.py +3 -2
- edc_action_item/site_action_items.py +13 -14
- edc_action_item/stubs.py +9 -9
- edc_action_item/templatetags/action_item_extras.py +2 -2
- edc_action_item/view_utils/action_item_button.py +3 -3
- edc_action_item/view_utils/action_item_popover_list_item.py +4 -4
- edc_adherence/form_validator_mixin.py +1 -1
- edc_adverse_event/action_items/ae_followup_action.py +1 -1
- edc_adverse_event/action_items/ae_initial_action.py +1 -1
- edc_adverse_event/action_items/ae_susar_action.py +1 -1
- edc_adverse_event/action_items/ae_tmg_action.py +1 -1
- edc_adverse_event/action_items/death_report_action.py +1 -2
- edc_adverse_event/action_items/death_report_tmg_action.py +1 -1
- edc_adverse_event/action_items/death_report_tmg_second_action.py +1 -1
- edc_adverse_event/action_items/hospitalization_action.py +1 -1
- edc_adverse_event/form_validator_mixins/death_report_form_validator.py +3 -3
- edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +3 -4
- edc_adverse_event/form_validators/death_report_tmg.py +1 -1
- edc_adverse_event/model_mixins/ae_followup/ae_followup_methods_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_followup/ae_followup_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +0 -1
- edc_adverse_event/model_mixins/ae_initial/ae_initial_methods_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_initial/ae_initial_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_special_interest/aesi_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_special_interest/aesi_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_susar/ae_susar_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_susar/ae_susar_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +4 -5
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/death_report/death_report_extra_fields_model_mixin.py +2 -3
- edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +11 -11
- edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +9 -11
- edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +7 -6
- edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +0 -1
- edc_adverse_event/modeladmin_mixins/ae_followup_admin_mixin.py +9 -11
- edc_adverse_event/modeladmin_mixins/ae_initial_admin_mixin.py +4 -6
- edc_adverse_event/modeladmin_mixins/modeladmin_mixins.py +1 -1
- edc_adverse_event/modeladmin_mixins/utils.py +4 -4
- edc_adverse_event/models/signals.py +7 -19
- edc_adverse_event/pdf_reports/death_pdf_report.py +8 -9
- edc_adverse_event/templatetags/edc_adverse_event_extras.py +10 -20
- edc_adverse_event/urls.py +7 -3
- edc_adverse_event/utils.py +2 -2
- edc_adverse_event/view_utils/tmg_button.py +4 -4
- edc_appconfig/system_checks.py +1 -1
- edc_appointment/appointment_status_updater.py +14 -15
- edc_appointment/creators/appointment_creator.py +7 -11
- edc_appointment/creators/appointments_creator.py +1 -1
- edc_appointment/creators/unscheduled_appointment_creator.py +12 -13
- edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +1 -3
- edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -6
- edc_appointment/form_validators/appointment_form_validator.py +2 -4
- edc_appointment/form_validators/utils.py +4 -4
- edc_appointment/managers.py +4 -4
- edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -2
- edc_appointment/model_mixins/appointment_model_mixin.py +5 -5
- edc_appointment/model_mixins/window_period_model_mixin.py +1 -1
- edc_appointment/models/appointment.py +1 -1
- edc_appointment/skip_appointments.py +5 -6
- edc_appointment/stubs.py +12 -12
- edc_appointment/utils.py +41 -47
- edc_appointment/view_utils/appointment_button.py +3 -5
- edc_auth/admin/role_admin.py +5 -7
- edc_auth/auth_objects/default_roles.py +1 -3
- edc_auth/auth_updater/auth_updater.py +5 -7
- edc_auth/auth_updater/group_updater.py +8 -8
- edc_auth/auth_updater/role_updater.py +1 -2
- edc_auth/get_app_codenames.py +1 -3
- edc_auth/import_users.py +19 -19
- edc_auth/models/role.py +4 -3
- edc_auth/password_setter.py +6 -7
- edc_auth/send_new_credentials_to_user.py +2 -3
- edc_auth/site_auths.py +3 -3
- edc_consent/actions.py +4 -5
- edc_consent/consent_definition.py +8 -11
- edc_consent/consent_definition_extension.py +3 -3
- edc_consent/form_validators/__init__.py +1 -1
- edc_consent/model_mixins/__init__.py +2 -2
- edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
- edc_consent/modeladmin_mixins/consent_model_admin_mixin.py +1 -2
- edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +3 -5
- edc_consent/site_consents.py +10 -15
- edc_consent/stubs.py +2 -2
- edc_consent/utils.py +1 -1
- edc_constants/utils.py +2 -4
- edc_crf/crf_form_validator_mixins.py +2 -2
- edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
- edc_crf/model_mixins/crf_status_model_mixin.py +1 -1
- edc_crf/models/crf_status.py +10 -12
- edc_crf/update_crf_status_command.py +1 -3
- edc_dashboard/management/commands/update_search_slugs.py +8 -11
- edc_dashboard/templatetags/edc_dashboard_extras.py +1 -1
- edc_dashboard/url_config.py +3 -3
- edc_dashboard/utils.py +3 -4
- edc_dashboard/views/dashboard_view.py +1 -1
- edc_data_manager/models/data_dictionary.py +1 -2
- edc_data_manager/models/data_query.py +3 -3
- edc_data_manager/models/query_rule.py +2 -2
- edc_data_manager/tasks.py +0 -2
- edc_device/device.py +1 -1
- edc_device/view_mixins.py +1 -1
- edc_document_status/fieldsets.py +1 -3
- edc_document_status/model_mixins.py +2 -2
- edc_document_status/modeladmin_mixins.py +1 -4
- edc_egfr/admin/egfr_drop_notification_admin_mixin.py +5 -7
- edc_egfr/egfr.py +6 -7
- edc_egfr/get_drop_notification_model.py +1 -1
- edc_egfr/model_mixins/egfr_drop_notification_model_mixin.py +1 -1
- edc_export/archive_exporter.py +26 -27
- edc_export/exportables.py +2 -3
- edc_export/management/commands/import_receipts.py +6 -6
- edc_export/model_exporter/file_history_updater.py +1 -1
- edc_export/model_exporter/model_exporter.py +1 -1
- edc_export/model_exporter/value_getter.py +6 -6
- edc_export/model_mixins/notification_model_mixin.py +4 -4
- edc_export/models/data_request.py +3 -3
- edc_export/models/data_request_history.py +2 -2
- edc_export/models/export_receipt.py +1 -1
- edc_export/models/file_history.py +5 -5
- edc_export/models/plan.py +4 -4
- edc_export/models/upload_export_receipt_file.py +2 -2
- edc_export/utils.py +1 -1
- edc_facility/facility.py +6 -6
- edc_facility/holidays.py +2 -5
- edc_facility/import_holidays.py +6 -6
- edc_facility/model_mixins.py +1 -1
- edc_facility/models/holiday.py +1 -1
- edc_facility/utils.py +3 -3
- edc_fieldsets/fieldsets.py +1 -1
- edc_form_describer/forms_reference.py +5 -8
- edc_form_describer/make_forms_reference.py +1 -1
- edc_form_describer/management/commands/make_forms_reference.py +1 -1
- edc_form_label/custom_label_condition.py +1 -1
- edc_form_label/form_label.py +2 -2
- edc_form_runners/exceptions.py +1 -1
- edc_form_runners/models/issue.py +3 -2
- edc_form_runners/site.py +2 -2
- edc_form_runners/templatetags/form_runners_extras.py +1 -1
- edc_form_runners/utils.py +5 -5
- edc_form_validators/applicable_field_validator.py +32 -32
- edc_form_validators/base_form_validator.py +1 -1
- edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
- edc_form_validators/many_to_many_field_validator.py +38 -43
- edc_form_validators/other_specify_field_validator.py +9 -17
- edc_form_validators/required_field_validator.py +34 -39
- edc_form_validators/test_case_mixin.py +5 -5
- edc_identifier/admin.py +1 -3
- edc_identifier/identifier.py +2 -3
- edc_identifier/model_mixins.py +5 -8
- edc_identifier/research_identifier.py +10 -12
- edc_identifier/short_identifier.py +1 -1
- edc_identifier/simple_identifier.py +22 -22
- edc_identifier/utils.py +1 -1
- edc_lab/admin/fieldsets.py +7 -9
- edc_lab/admin/modeladmin_mixins.py +5 -6
- edc_lab/form_validators/requisition_form_validator_mixin.py +2 -3
- edc_lab/forms/box_form.py +5 -11
- edc_lab/identifiers/aliquot_identifier.py +5 -8
- edc_lab/identifiers/prefix.py +2 -5
- edc_lab/lab/aliquot_creator.py +1 -2
- edc_lab/lab/aliquot_type.py +2 -4
- edc_lab/lab/manifest.py +5 -7
- edc_lab/lab/requisition_panel.py +2 -4
- edc_lab/lab/requisition_panel_group.py +5 -7
- edc_lab/model_mixins/requisition/requisition_model_mixin.py +5 -4
- edc_lab/model_mixins/shipping/manifest_model_mixin.py +6 -6
- edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -3
- edc_lab/models/aliquot.py +1 -1
- edc_lab/models/box.py +2 -2
- edc_lab/models/box_item.py +1 -1
- edc_lab/models/box_type.py +1 -1
- edc_lab/models/manifest/manifest_item.py +1 -1
- edc_lab/pdf_reports/manifest_pdf_report.py +3 -7
- edc_lab/site_labs.py +3 -3
- edc_lab_dashboard/dashboard_templates.py +1 -1
- edc_lab_dashboard/view_mixins/box_view_mixin.py +4 -9
- edc_lab_dashboard/views/action_views/action_view.py +1 -2
- edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
- edc_lab_dashboard/views/action_views/manifest_view.py +9 -11
- edc_lab_dashboard/views/action_views/pack_view.py +11 -14
- edc_lab_results/calculate_missing.py +2 -2
- edc_lab_results/fieldsets.py +6 -6
- edc_lab_results/model_mixin_factories/__init__.py +2 -2
- edc_lab_results/model_mixin_factories/reportable_result_model_mixin_factory.py +7 -9
- edc_lab_results/model_mixin_factories/result_model_mixin_factory.py +7 -9
- edc_lab_results/model_mixins/fbg_model_mixin.py +1 -1
- edc_lab_results/model_mixins/glucose_model_mixin.py +1 -1
- edc_label/admin.py +1 -3
- edc_label/label.py +2 -4
- edc_label/label_template.py +1 -1
- edc_label/subject_label.py +1 -3
- edc_list_data/admin.py +3 -5
- edc_list_data/load_list_data.py +1 -1
- edc_list_data/load_model_data.py +1 -3
- edc_list_data/model_mixins.py +3 -5
- edc_list_data/preload_data.py +2 -4
- edc_list_data/site_list_data.py +6 -7
- edc_listboard/filters/listboard_filter.py +2 -2
- edc_listboard/views/listboard_view.py +1 -3
- edc_locator/models.py +2 -2
- edc_locator/view_mixins/subject_locator_view_mixins.py +2 -2
- edc_ltfu/action_items.py +1 -1
- edc_ltfu/model_mixins.py +0 -2
- edc_ltfu/models.py +4 -4
- edc_metadata/admin/modeladmin_mixins.py +1 -1
- edc_metadata/admin/requisition_metadata.py +2 -4
- edc_metadata/metadata/crf_metadata_getter.py +1 -3
- edc_metadata/metadata/requisition_metadata_getter.py +1 -3
- edc_metadata/metadata_handler.py +2 -2
- edc_metadata/metadata_helper/metadata_helper_mixin.py +2 -3
- edc_metadata/metadata_mixins/source_model_metadata_mixin.py +2 -2
- edc_metadata/metadata_refresher.py +1 -1
- edc_metadata/metadata_rules/crf/crf_rule_group.py +3 -5
- edc_metadata/metadata_rules/logic.py +1 -1
- edc_metadata/metadata_rules/requisition/requisition_rule.py +4 -4
- edc_metadata/metadata_rules/requisition/requisition_rule_group.py +2 -2
- edc_metadata/metadata_rules/rule_evaluator.py +5 -6
- edc_metadata/metadata_rules/rule_group_metaclass.py +2 -3
- edc_metadata/metadata_rules/site.py +6 -7
- edc_metadata/metadata_updater.py +2 -2
- edc_metadata/metadata_wrappers/metadata_wrapper.py +4 -4
- edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +6 -7
- edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +2 -2
- edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +2 -2
- edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +2 -2
- edc_metadata/models/crf_metadata.py +27 -29
- edc_metadata/models/crf_metadata_model_mixin.py +1 -1
- edc_metadata/models/requisition_metadata.py +29 -31
- edc_metadata/stubs.py +17 -17
- edc_metadata/utils.py +4 -4
- edc_model/__init__.py +2 -2
- edc_model/models/historical_records.py +2 -2
- edc_model/models/signals.py +1 -1
- edc_model/models/url_model_mixin.py +1 -1
- edc_model/utils.py +0 -2
- edc_model_admin/dashboard/model_admin_dashboard_mixin.py +1 -3
- edc_model_admin/history/model_admin_simple_history.py +7 -9
- edc_model_admin/mixins/base_model_admin_redirect_mixin.py +4 -6
- edc_model_admin/mixins/model_admin_limit_to_selected_foreignkey.py +2 -2
- edc_model_admin/mixins/model_admin_model_redirect_mixin.py +2 -10
- edc_model_admin/mixins/model_admin_next_url_redirect_mixin.py +8 -9
- edc_model_admin/utils.py +6 -9
- edc_model_form/mixins/__init__.py +1 -1
- edc_model_form/mixins/base_model_form_mixin.py +1 -1
- edc_model_to_dataframe/model_to_dataframe.py +2 -4
- edc_navbar/site_navbars.py +2 -2
- edc_navbar/system_checks.py +1 -1
- edc_notification/mailing_list_manager.py +5 -8
- edc_notification/management/commands/list_recipients_by_notification.py +1 -1
- edc_notification/models/__init__.py +4 -2
- edc_notification/notification/graded_event_notification.py +2 -4
- edc_notification/notification/model_notification.py +1 -3
- edc_notification/notification/notification.py +14 -17
- edc_notification/site_notifications.py +7 -7
- edc_notification/stubs.py +6 -6
- edc_notification/update_mailing_lists_in_m2m.py +2 -2
- edc_offstudy/action_items.py +2 -2
- edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
- edc_offstudy/models.py +1 -1
- edc_pdf_reports/crf_pdf_report.py +2 -2
- edc_pdf_reports/model_mixins.py +2 -2
- edc_pdf_reports/report.py +4 -5
- edc_pdf_reports/utils.py +1 -1
- edc_pdutils/dataframes/get_subject_consent.py +1 -3
- edc_pdutils/df_exporters/csv_exporter.py +5 -7
- edc_pdutils/df_exporters/tables_exporter.py +2 -2
- edc_pdutils/df_handlers/crf_df_handler.py +1 -2
- edc_pdutils/dialects/crf_dialect.py +3 -3
- edc_pdutils/management/commands/export_models.py +6 -7
- edc_pdutils/site_values_mappings.py +2 -2
- edc_pdutils/utils/datetime_to_date.py +1 -2
- edc_pdutils/utils/refresh_model_from_dataframe.py +1 -3
- edc_pdutils/utils/table_names.py +2 -4
- edc_pdutils/utils/undash.py +1 -1
- edc_pharmacy/admin/medication/assignment_admin.py +2 -4
- edc_pharmacy/admin/medication/dosage_guideline_admin.py +2 -4
- edc_pharmacy/admin/medication/formulation_admin.py +3 -5
- edc_pharmacy/admin/medication/medication_admin.py +3 -5
- edc_pharmacy/admin/prescription/rx_refill_admin.py +7 -9
- edc_pharmacy/admin/stock/lot_admin.py +5 -7
- edc_pharmacy/admin/stock/order_admin.py +2 -2
- edc_pharmacy/admin/stock/order_item_admin.py +6 -6
- edc_pharmacy/admin/stock/receive_admin.py +1 -1
- edc_pharmacy/admin/stock/receive_item_admin.py +2 -2
- edc_pharmacy/admin/stock/stock_request_admin.py +4 -6
- edc_pharmacy/dosage_calculator.py +4 -4
- edc_pharmacy/form_validators/crf/study_medication_form_validator.py +8 -8
- edc_pharmacy/forms/stock/stock_request_form.py +2 -4
- edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +2 -2
- edc_pharmacy/models/medication/formulation.py +3 -4
- edc_pharmacy/models/medication/medication.py +1 -2
- edc_pharmacy/models/prescription/rx.py +2 -2
- edc_pharmacy/models/prescription/rx_refill.py +7 -9
- edc_pharmacy/models/reports/stock_availability.py +3 -4
- edc_pharmacy/models/stock/confirmation_at_site.py +1 -3
- edc_pharmacy/models/stock/order.py +1 -2
- edc_pharmacy/models/stock/receive.py +3 -4
- edc_pharmacy/models/stock/receive_item.py +2 -3
- edc_pharmacy/models/stock/stock.py +1 -2
- edc_pharmacy/models/stock/stock_adjustment.py +1 -2
- edc_pharmacy/models/stock/stock_request.py +4 -5
- edc_pharmacy/models/storage/box.py +1 -1
- edc_pharmacy/models/storage/items/container_model_mixin.py +1 -1
- edc_pharmacy/models/storage/room.py +1 -1
- edc_pharmacy/models/storage/shelf.py +1 -1
- edc_pharmacy/models/storage/utils.py +15 -16
- edc_pharmacy/prescribe/create_prescription.py +5 -5
- edc_pharmacy/refill/refill_creator.py +1 -1
- edc_pharmacy/sample_usb_printing/usb_printing.py +1 -1
- edc_pharmacy/utils/__init__.py +1 -1
- edc_pharmacy/utils/confirm_stock.py +3 -3
- edc_pharmacy/utils/confirm_stock_at_site.py +9 -8
- edc_pharmacy/utils/dispense.py +5 -8
- edc_pharmacy/utils/format_qty.py +3 -3
- edc_pharmacy/utils/get_codenames.py +1 -1
- edc_pharmacy/utils/miscellaneous.py +1 -1
- edc_pharmacy/utils/process_repack_request.py +18 -19
- edc_pharmacy/utils/process_repack_request_queryset.py +0 -2
- edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
- edc_pharmacy/views/add_to_storage_bin_view.py +1 -1
- edc_pharmacy/views/allocate_to_subject_view.py +2 -6
- edc_pharmacy/views/confirm_stock_from_instance_view.py +4 -4
- edc_pharmacy/views/confirm_stock_from_queryset_view.py +1 -5
- edc_pharmacy/views/confirmation_at_site_view.py +2 -3
- edc_pharmacy/views/dispense_view.py +1 -1
- edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
- edc_pharmacy/views/print_labels_view.py +30 -31
- edc_pharmacy/views/transfer_stock_view.py +1 -1
- edc_prn/prn.py +1 -1
- edc_prn/site_prn_forms.py +1 -2
- edc_protocol_incident/action_items.py +2 -2
- edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +3 -3
- edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +3 -5
- edc_protocol_incident/modeladmin_mixins.py +3 -5
- edc_protocol_incident/models/protocol_deviation_violation.py +5 -5
- edc_protocol_incident/models/protocol_incident.py +5 -5
- edc_pylabels/site_label_configs.py +5 -5
- edc_qareports/model_mixins/qa_report_model_mixin.py +4 -2
- edc_qareports/models/qa_reports_log.py +1 -2
- edc_qareports/utils.py +1 -2
- edc_randomization/admin.py +3 -5
- edc_randomization/auth_objects.py +2 -3
- edc_randomization/blinding.py +1 -1
- edc_randomization/constants.py +2 -4
- edc_randomization/model_mixins.py +3 -3
- edc_randomization/randomization_list_importer.py +13 -14
- edc_randomization/randomization_list_verifier.py +11 -13
- edc_randomization/randomizer.py +2 -2
- edc_randomization/system_checks.py +1 -2
- edc_randomization/utils.py +5 -5
- edc_refusal/admin.py +1 -1
- edc_refusal/model_mixins.py +1 -1
- edc_refusal/utils.py +1 -1
- edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +2 -2
- edc_registration/modeladmin_mixins.py +4 -7
- edc_registration/models/registered_subject.py +7 -9
- edc_registration/utils.py +3 -4
- edc_reportable/data/grading_data/daids_july_2017.py +3 -3
- edc_reportable/evaluator.py +5 -5
- edc_reportable/formula.py +1 -1
- edc_reportable/reference_range_evaluator.py +3 -3
- edc_reportable/utils/convert_units.py +10 -12
- edc_reportable/utils/grading_data_model_cls.py +2 -2
- edc_reportable/utils/grading_exception_model_cls.py +2 -2
- edc_reportable/utils/molecular_weight_model_cls.py +2 -2
- edc_reportable/utils/normal_data_model_cls.py +2 -2
- edc_reportable/utils/reference_range_colllection_model_cls.py +2 -2
- edc_screening/age_evaluator.py +2 -4
- edc_screening/eligibility.py +1 -1
- edc_screening/form_validator_mixins.py +2 -2
- edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
- edc_screening/screening_eligibility.py +1 -1
- edc_screening/utils.py +4 -6
- edc_search/model_mixins.py +1 -1
- edc_search/search_slug.py +1 -3
- edc_search/updater.py +1 -1
- edc_sites/admin/site_model_admin_mixin.py +2 -2
- edc_sites/model_mixins/site_model_mixin.py +1 -2
- edc_sites/modelform_mixins.py +2 -2
- edc_sites/models/__init__.py +1 -1
- edc_sites/models/site_profile.py +4 -4
- edc_sites/site.py +24 -32
- edc_sites/system_checks.py +1 -1
- edc_sites/utils/get_message_text.py +1 -1
- edc_sites/utils/valid_site_for_subject_or_raise.py +5 -6
- edc_subject_dashboard/requisition_report.py +4 -5
- edc_subject_dashboard/requisition_verifier.py +4 -2
- edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +16 -17
- edc_subject_dashboard/view_utils/__init__.py +1 -1
- edc_subject_dashboard/view_utils/crf_button.py +1 -3
- edc_subject_dashboard/view_utils/subject_consent_dashboard_button.py +2 -2
- edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +2 -2
- edc_subject_dashboard/view_utils/timepoint_status_button.py +1 -4
- edc_subject_dashboard/views/requisition_print_actions_view.py +2 -2
- edc_subject_dashboard/views/subject_dashboard_view.py +1 -1
- edc_timepoint/model_mixins.py +2 -2
- edc_transfer/action_items.py +1 -1
- edc_transfer/model_mixins.py +4 -3
- edc_transfer/modeladmin_mixins.py +3 -6
- edc_unblinding/action_items.py +2 -2
- edc_unblinding/models/unblinding_request.py +3 -3
- edc_unblinding/models/unblinding_review.py +3 -3
- edc_utils/__init__.py +6 -5
- edc_utils/age.py +1 -1
- edc_utils/celery.py +3 -8
- edc_utils/get_static_file.py +1 -1
- edc_utils/text.py +5 -6
- edc_view_utils/__init__.py +3 -3
- edc_view_utils/dashboard_model_button.py +4 -4
- edc_view_utils/model_button.py +7 -8
- edc_view_utils/perms.py +3 -3
- edc_visit_schedule/action_items.py +2 -2
- edc_visit_schedule/admin/list_filters.py +11 -9
- edc_visit_schedule/admin/subject_schedule_history_admin.py +13 -15
- edc_visit_schedule/admin/visit_schedule_admin.py +7 -7
- edc_visit_schedule/fieldsets.py +4 -6
- edc_visit_schedule/management/commands/find_invalid_onschedules.py +1 -1
- edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +3 -1
- edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
- edc_visit_schedule/modelform_mixins/__init__.py +1 -1
- edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -2
- edc_visit_schedule/models/subject_schedule_history.py +2 -1
- edc_visit_schedule/models/visit_schedule.py +2 -2
- edc_visit_schedule/schedule/schedule.py +11 -12
- edc_visit_schedule/schedule/visit_collection.py +4 -3
- edc_visit_schedule/schedule/window.py +19 -12
- edc_visit_schedule/site_visit_schedules.py +9 -9
- edc_visit_schedule/subject_schedule.py +6 -7
- edc_visit_schedule/system_checks.py +1 -1
- edc_visit_schedule/view_mixins.py +1 -1
- edc_visit_schedule/visit/crf.py +3 -7
- edc_visit_schedule/visit/requisition.py +2 -2
- edc_visit_schedule/visit/visit.py +6 -7
- edc_visit_schedule/visit/window_period.py +8 -8
- edc_visit_schedule/visit_schedule/schedules_collection.py +2 -5
- edc_visit_tracking/action_items.py +11 -13
- edc_visit_tracking/form_validators/visit_form_validator.py +5 -5
- edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -4
- edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
- edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -3
- edc_visit_tracking/model_mixins/utils.py +1 -1
- edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +2 -2
- edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +3 -3
- edc_visit_tracking/modeladmin_mixins/crf_model_admin_mixin.py +6 -4
- edc_visit_tracking/modeladmin_mixins/visit_model_admin_mixin.py +6 -7
- edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py +4 -5
- edc_visit_tracking/modelform_mixins/visit_tracking_modelform_mixin.py +3 -4
- edc_visit_tracking/models/subject_visit.py +1 -1
- edc_visit_tracking/models/subject_visit_missed.py +1 -1
- edc_visit_tracking/stubs.py +7 -7
- edc_visit_tracking/typing_stubs.py +11 -11
- edc_visit_tracking/utils.py +5 -5
- edc_visit_tracking/view_utils/related_visit_button.py +5 -5
- edc_vitals/calculators/bmi.py +1 -1
- edc_vitals/form_validators/blood_pressure_form_validator_mixin.py +8 -12
- edc_vitals/form_validators/bmi_form_validator_mixin.py +1 -1
- edc_vitals/form_validators/weight_height_with_bmi_form_validator_mixin.py +5 -2
- edc_vitals/model_mixins/blood_pressure_model_mixin.py +3 -4
- edc_vitals/model_mixins/weight_height_bmi_model_mixin.py +4 -4
- edc_vitals/utils.py +1 -1
- edc_vitals/validators.py +1 -1
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from secrets import choice
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
4
|
from django.apps import apps as django_apps
|
|
5
5
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -26,12 +26,12 @@ class SimpleIdentifier:
|
|
|
26
26
|
|
|
27
27
|
def __init__(
|
|
28
28
|
self,
|
|
29
|
-
template:
|
|
30
|
-
random_string_length:
|
|
31
|
-
identifier_prefix:
|
|
32
|
-
device_id:
|
|
29
|
+
template: str | None = None,
|
|
30
|
+
random_string_length: int | None = None,
|
|
31
|
+
identifier_prefix: str | None = None,
|
|
32
|
+
device_id: str | None = None,
|
|
33
33
|
) -> None:
|
|
34
|
-
self._identifier:
|
|
34
|
+
self._identifier: str | None = None
|
|
35
35
|
self.template = template or self.template
|
|
36
36
|
self.random_string_length = random_string_length or self.random_string_length
|
|
37
37
|
self.device_id = device_id or django_apps.get_app_config("edc_device").device_id
|
|
@@ -80,14 +80,14 @@ class SimpleTimestampIdentifier(SimpleIdentifier):
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
class SimpleSequentialIdentifier:
|
|
83
|
-
prefix:
|
|
83
|
+
prefix: str | None = None
|
|
84
84
|
|
|
85
85
|
def __init__(self):
|
|
86
86
|
sequence: int = int(get_utcnow().timestamp())
|
|
87
87
|
random_number: int = choice(range(1000, 9999)) # nosec B311
|
|
88
88
|
sequence: str = f"{sequence}{random_number}"
|
|
89
89
|
chk: int = int(sequence) % 11
|
|
90
|
-
self.identifier: str = f
|
|
90
|
+
self.identifier: str = f"{self.prefix or ''}{sequence}{chk}"
|
|
91
91
|
|
|
92
92
|
def __str__(self) -> str:
|
|
93
93
|
return self.identifier
|
|
@@ -107,26 +107,26 @@ class SimpleUniqueIdentifier:
|
|
|
107
107
|
identifier_attr: str = "identifier"
|
|
108
108
|
model: str = "edc_identifier.identifiermodel"
|
|
109
109
|
template: str = "{device_id}{random_string}"
|
|
110
|
-
identifier_prefix:
|
|
110
|
+
identifier_prefix: str | None = None
|
|
111
111
|
identifier_cls = SimpleIdentifier
|
|
112
|
-
make_human_readable:
|
|
112
|
+
make_human_readable: bool | None = None
|
|
113
113
|
|
|
114
114
|
def __init__(
|
|
115
115
|
self,
|
|
116
116
|
model: str = None,
|
|
117
|
-
identifier_attr:
|
|
118
|
-
identifier_type:
|
|
119
|
-
identifier_prefix:
|
|
120
|
-
make_human_readable:
|
|
121
|
-
linked_identifier:
|
|
122
|
-
protocol_number:
|
|
123
|
-
source_model:
|
|
124
|
-
subject_identifier:
|
|
117
|
+
identifier_attr: str | None = None,
|
|
118
|
+
identifier_type: str | None = None,
|
|
119
|
+
identifier_prefix: str | None = None,
|
|
120
|
+
make_human_readable: bool | None = None,
|
|
121
|
+
linked_identifier: str | None = None,
|
|
122
|
+
protocol_number: str | None = None,
|
|
123
|
+
source_model: str | None = None,
|
|
124
|
+
subject_identifier: str | None = None,
|
|
125
125
|
name: str | None = None,
|
|
126
|
-
site:
|
|
127
|
-
site_id:
|
|
126
|
+
site: Any | None = None,
|
|
127
|
+
site_id: str | None = None,
|
|
128
128
|
):
|
|
129
|
-
self._identifier:
|
|
129
|
+
self._identifier: str | None = None
|
|
130
130
|
self.site_id = site_id
|
|
131
131
|
self.name = name or ""
|
|
132
132
|
self.model = model or self.model
|
|
@@ -194,7 +194,7 @@ class SimpleUniqueIdentifier:
|
|
|
194
194
|
return identifier.identifier
|
|
195
195
|
|
|
196
196
|
@property
|
|
197
|
-
def model_cls(self) ->
|
|
197
|
+
def model_cls(self) -> type[models.Model]:
|
|
198
198
|
return django_apps.get_model(self.model)
|
|
199
199
|
|
|
200
200
|
def update_identifier_model(self, **kwargs) -> bool:
|
edc_identifier/utils.py
CHANGED
|
@@ -21,7 +21,7 @@ def is_subject_identifier_or_raise(subject_identifier, reference_obj=None, raise
|
|
|
21
21
|
):
|
|
22
22
|
reference_msg = ""
|
|
23
23
|
if reference_obj:
|
|
24
|
-
reference_msg = f"See {
|
|
24
|
+
reference_msg = f"See {reference_obj!r}. "
|
|
25
25
|
raise SubjectIdentifierError(
|
|
26
26
|
f"Invalid format for subject identifier. {reference_msg}"
|
|
27
27
|
f"Got `{subject_identifier or ''}`. "
|
edc_lab/admin/fieldsets.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
requisition_fieldset: Tuple[str, Dict[str, Tuple[str, ...]]] = (
|
|
1
|
+
requisition_fieldset: tuple[str, dict[str, tuple[str, ...]]] = (
|
|
4
2
|
"Requisition",
|
|
5
3
|
{
|
|
6
4
|
"fields": (
|
|
@@ -17,7 +15,7 @@ requisition_fieldset: Tuple[str, Dict[str, Tuple[str, ...]]] = (
|
|
|
17
15
|
)
|
|
18
16
|
|
|
19
17
|
|
|
20
|
-
requisition_status_fields:
|
|
18
|
+
requisition_status_fields: tuple[str, ...] = (
|
|
21
19
|
"received",
|
|
22
20
|
"received_datetime",
|
|
23
21
|
"processed",
|
|
@@ -28,29 +26,29 @@ requisition_status_fields: Tuple[str, ...] = (
|
|
|
28
26
|
"shipped_datetime",
|
|
29
27
|
)
|
|
30
28
|
|
|
31
|
-
requisition_verify_fields:
|
|
29
|
+
requisition_verify_fields: tuple[str, ...] = (
|
|
32
30
|
"clinic_verified",
|
|
33
31
|
"clinic_verified_datetime",
|
|
34
32
|
)
|
|
35
33
|
|
|
36
|
-
requisition_status_fieldset:
|
|
34
|
+
requisition_status_fieldset: tuple[str, dict[str, tuple[str, ...]]] = (
|
|
37
35
|
"Status (For laboratory use only)",
|
|
38
36
|
{"classes": ("collapse",), "fields": requisition_status_fields},
|
|
39
37
|
)
|
|
40
38
|
|
|
41
39
|
|
|
42
|
-
requisition_identifier_fields:
|
|
40
|
+
requisition_identifier_fields: tuple[str, ...] = (
|
|
43
41
|
"requisition_identifier",
|
|
44
42
|
"identifier_prefix",
|
|
45
43
|
"primary_aliquot_identifier",
|
|
46
44
|
)
|
|
47
45
|
|
|
48
|
-
requisition_identifier_fieldset:
|
|
46
|
+
requisition_identifier_fieldset: tuple[str, dict[str, tuple[str, ...]]] = (
|
|
49
47
|
"Identifiers",
|
|
50
48
|
{"classes": ("collapse",), "fields": requisition_identifier_fields},
|
|
51
49
|
)
|
|
52
50
|
|
|
53
|
-
requisition_verify_fieldset:
|
|
51
|
+
requisition_verify_fieldset: tuple[str, dict[str, tuple[str, ...]]] = (
|
|
54
52
|
"Verification",
|
|
55
53
|
{"classes": ("collapse",), "fields": requisition_verify_fields},
|
|
56
54
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
1
|
from uuid import UUID
|
|
3
2
|
|
|
4
3
|
from django.contrib import admin
|
|
@@ -29,7 +28,7 @@ class RequisitionAdminMixin:
|
|
|
29
28
|
"item_type": admin.VERTICAL,
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
search_fields:
|
|
31
|
+
search_fields: tuple[str, ...] = (
|
|
33
32
|
"requisition_identifier",
|
|
34
33
|
"subject_identifier",
|
|
35
34
|
"panel__display_name",
|
|
@@ -43,7 +42,7 @@ class RequisitionAdminMixin:
|
|
|
43
42
|
def requisition(obj=None):
|
|
44
43
|
if obj.is_drawn == YES:
|
|
45
44
|
return obj.requisition_identifier
|
|
46
|
-
|
|
45
|
+
if not obj.is_drawn:
|
|
47
46
|
return format_html(
|
|
48
47
|
'<span style="color:red;">{}</span>', obj.requisition_identifier
|
|
49
48
|
)
|
|
@@ -61,13 +60,13 @@ class RequisitionAdminMixin:
|
|
|
61
60
|
)
|
|
62
61
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
|
63
62
|
|
|
64
|
-
def get_list_filter(self, request) ->
|
|
63
|
+
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
65
64
|
list_filter = super().get_list_filter(request)
|
|
66
65
|
custom_fields = ("requisition_datetime", "site", "is_drawn", "panel")
|
|
67
66
|
list_filter = tuple(f for f in list_filter if f not in custom_fields)
|
|
68
67
|
return custom_fields + list_filter
|
|
69
68
|
|
|
70
|
-
def get_list_display(self, request) ->
|
|
69
|
+
def get_list_display(self, request) -> tuple[str, ...]:
|
|
71
70
|
list_display = super().get_list_display(request)
|
|
72
71
|
custom_fields = (
|
|
73
72
|
"requisition",
|
|
@@ -80,7 +79,7 @@ class RequisitionAdminMixin:
|
|
|
80
79
|
list_display = tuple(f for f in list_display if f not in custom_fields)
|
|
81
80
|
return custom_fields + list_display
|
|
82
81
|
|
|
83
|
-
def get_readonly_fields(self, request, obj=None) ->
|
|
82
|
+
def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
|
|
84
83
|
readonly_fields = super().get_readonly_fields(request, obj=obj)
|
|
85
84
|
return tuple(
|
|
86
85
|
set(readonly_fields + requisition_identifier_fields + requisition_verify_fields)
|
|
@@ -24,7 +24,7 @@ class RequisitionFormValidatorMixin:
|
|
|
24
24
|
if self.instance:
|
|
25
25
|
if self.cleaned_data.get("packed") != self.instance.packed:
|
|
26
26
|
raise forms.ValidationError({"packed": "Value may not be changed here."})
|
|
27
|
-
|
|
27
|
+
if self.cleaned_data.get("processed") != self.instance.processed:
|
|
28
28
|
if self.aliqout_model_cls.objects.filter(
|
|
29
29
|
requisition_identifier=self.instance.requisition_identifier
|
|
30
30
|
).exists():
|
|
@@ -42,8 +42,7 @@ class RequisitionFormValidatorMixin:
|
|
|
42
42
|
)
|
|
43
43
|
elif self.instance.received:
|
|
44
44
|
raise forms.ValidationError(
|
|
45
|
-
"Requisition may not be changed. The specimen has "
|
|
46
|
-
"already been received."
|
|
45
|
+
"Requisition may not be changed. The specimen has already been received."
|
|
47
46
|
)
|
|
48
47
|
|
|
49
48
|
self.applicable_if(NO, field="is_drawn", field_applicable="reason_not_drawn")
|
edc_lab/forms/box_form.py
CHANGED
|
@@ -14,23 +14,17 @@ class BoxForm(forms.ModelForm):
|
|
|
14
14
|
if cleaned_data.get("specimen_types"):
|
|
15
15
|
pattern = "([0-9][0-9]*[ ]*,[ ]*)*[0-9][0-9]*"
|
|
16
16
|
match = re.match(pattern, cleaned_data.get("specimen_types"))
|
|
17
|
-
if not match:
|
|
17
|
+
if not match or match.group() != cleaned_data.get("specimen_types"):
|
|
18
18
|
raise forms.ValidationError(
|
|
19
19
|
{"specimen_types": "Invalid list of specimen types."},
|
|
20
20
|
code="invalid format",
|
|
21
21
|
)
|
|
22
|
-
|
|
22
|
+
specimen_types = [code.strip() for code in match.group().split(",")]
|
|
23
|
+
if len(specimen_types) != len(list(set(specimen_types))):
|
|
23
24
|
raise forms.ValidationError(
|
|
24
|
-
{"specimen_types": "
|
|
25
|
-
code="
|
|
25
|
+
{"specimen_types": "List must be unique."},
|
|
26
|
+
code="list not unique",
|
|
26
27
|
)
|
|
27
|
-
else:
|
|
28
|
-
specimen_types = [code.strip() for code in match.group().split(",")]
|
|
29
|
-
if len(specimen_types) != len(list(set(specimen_types))):
|
|
30
|
-
raise forms.ValidationError(
|
|
31
|
-
{"specimen_types": "List must be unique."},
|
|
32
|
-
code="list not unique",
|
|
33
|
-
)
|
|
34
28
|
cleaned_data["specimen_types"] = ",".join(specimen_types)
|
|
35
29
|
form_validator.validate_other_specify("category")
|
|
36
30
|
return cleaned_data
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
class AliquotIdentifierLengthError(Exception):
|
|
5
2
|
pass
|
|
6
3
|
|
|
@@ -17,10 +14,10 @@ class AliquotIdentifier:
|
|
|
17
14
|
|
|
18
15
|
def __init__(
|
|
19
16
|
self,
|
|
20
|
-
identifier_prefix:
|
|
21
|
-
parent_segment:
|
|
22
|
-
numeric_code:
|
|
23
|
-
count:
|
|
17
|
+
identifier_prefix: str | None = None,
|
|
18
|
+
parent_segment: str | None = None,
|
|
19
|
+
numeric_code: str | None = None,
|
|
20
|
+
count: int | None = None,
|
|
24
21
|
):
|
|
25
22
|
"""
|
|
26
23
|
A class to generate aliquot identifiers:
|
|
@@ -30,7 +27,7 @@ class AliquotIdentifier:
|
|
|
30
27
|
* numeric_code: aliquot type numeric code (2 digits segment)
|
|
31
28
|
* count: sequence in aliquoting history relative to primary. (01 for primary)
|
|
32
29
|
"""
|
|
33
|
-
self.count:
|
|
30
|
+
self.count: int | None = count
|
|
34
31
|
self.identifier_prefix: str = identifier_prefix or ""
|
|
35
32
|
self.is_primary: bool = True
|
|
36
33
|
self.numeric_code: str = numeric_code or ""
|
edc_lab/identifiers/prefix.py
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
class PrefixLengthError(Exception):
|
|
5
2
|
pass
|
|
6
3
|
|
|
@@ -17,8 +14,8 @@ class Prefix:
|
|
|
17
14
|
|
|
18
15
|
def __init__(
|
|
19
16
|
self,
|
|
20
|
-
template:
|
|
21
|
-
length:
|
|
17
|
+
template: str | None = None,
|
|
18
|
+
length: int | None = None,
|
|
22
19
|
**template_opts,
|
|
23
20
|
):
|
|
24
21
|
self.template: str = template or self.template
|
edc_lab/lab/aliquot_creator.py
CHANGED
|
@@ -37,8 +37,7 @@ class AliquotCreator:
|
|
|
37
37
|
"Cannot create child aliquot without parent aliquot identifier. "
|
|
38
38
|
f"Got is_primary={is_primary}."
|
|
39
39
|
)
|
|
40
|
-
|
|
41
|
-
self.parent_identifier = parent_identifier
|
|
40
|
+
self.parent_identifier = parent_identifier
|
|
42
41
|
self.identifier_prefix = (
|
|
43
42
|
identifier_prefix
|
|
44
43
|
or f"{ResearchProtocolConfig().protocol_number}{self.requisition_identifier}"
|
edc_lab/lab/aliquot_type.py
CHANGED
|
@@ -22,12 +22,10 @@ class AliquotType:
|
|
|
22
22
|
self.name = name
|
|
23
23
|
if not alpha_code or not re.match(r"^[A-Z]+$", alpha_code, re.ASCII):
|
|
24
24
|
raise AliquotTypeAlphaCodeError(f"Invalid alpha code. Got {alpha_code}.")
|
|
25
|
-
|
|
26
|
-
self.alpha_code = alpha_code
|
|
25
|
+
self.alpha_code = alpha_code
|
|
27
26
|
if not numeric_code or not re.match(r"^\d+$", numeric_code, re.ASCII):
|
|
28
27
|
raise AliquotTypeNumericCodeError(f"Invalid numeric code. Got {numeric_code}.")
|
|
29
|
-
|
|
30
|
-
self.numeric_code = numeric_code
|
|
28
|
+
self.numeric_code = numeric_code
|
|
31
29
|
|
|
32
30
|
def __repr__(self):
|
|
33
31
|
return "{self.__class__.__name__}({self.name}, {self.alpha_code}, {self.numeric_code})"
|
edc_lab/lab/manifest.py
CHANGED
|
@@ -41,7 +41,7 @@ class Manifest:
|
|
|
41
41
|
"""
|
|
42
42
|
added = 0
|
|
43
43
|
if not box:
|
|
44
|
-
message = "Box does not exist. Got {}."
|
|
44
|
+
message = f"Box does not exist. Got {manifest_item_identifier}."
|
|
45
45
|
messages.error(self.request, message)
|
|
46
46
|
elif self.validate_box_category(box) and self.validate_box_verified(box):
|
|
47
47
|
try:
|
|
@@ -77,8 +77,8 @@ class Manifest:
|
|
|
77
77
|
)
|
|
78
78
|
messages.error(self.request, message)
|
|
79
79
|
else:
|
|
80
|
-
message =
|
|
81
|
-
manifest_item.manifest.human_readable_identifier
|
|
80
|
+
message = (
|
|
81
|
+
f"Duplicate item. Got {manifest_item.manifest.human_readable_identifier}."
|
|
82
82
|
)
|
|
83
83
|
messages.error(self.request, message)
|
|
84
84
|
return added
|
|
@@ -86,9 +86,7 @@ class Manifest:
|
|
|
86
86
|
def validate_box_category(self, box=None):
|
|
87
87
|
"""Returns True if box category matches manifest category."""
|
|
88
88
|
if box.category != self.manifest.category:
|
|
89
|
-
message = "Invalid category. Manifest accepts {}. Got {}."
|
|
90
|
-
self.manifest.get_category_display(), box.get_category_display()
|
|
91
|
-
)
|
|
89
|
+
message = f"Invalid category. Manifest accepts {self.manifest.get_category_display()}. Got {box.get_category_display()}."
|
|
92
90
|
messages.error(self.request, message)
|
|
93
91
|
return False
|
|
94
92
|
return True
|
|
@@ -96,7 +94,7 @@ class Manifest:
|
|
|
96
94
|
def validate_box_verified(self, box=None):
|
|
97
95
|
"""Returns True if box status is verified."""
|
|
98
96
|
if box.status != VERIFIED:
|
|
99
|
-
message = "Box is not verified. Got {}."
|
|
97
|
+
message = f"Box is not verified. Got {box.human_readable_identifier}."
|
|
100
98
|
messages.error(self.request, message)
|
|
101
99
|
return False
|
|
102
100
|
return True
|
edc_lab/lab/requisition_panel.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Type
|
|
4
|
-
|
|
5
3
|
from django.apps import apps as django_apps
|
|
6
4
|
from django.db import models
|
|
7
5
|
|
|
@@ -85,7 +83,7 @@ class RequisitionPanel:
|
|
|
85
83
|
return self._panel_model_obj
|
|
86
84
|
|
|
87
85
|
@property
|
|
88
|
-
def requisition_model_cls(self) ->
|
|
86
|
+
def requisition_model_cls(self) -> type[models.Model]:
|
|
89
87
|
"""Returns the requisition model class associated with this
|
|
90
88
|
panel (set by it's lab profile).
|
|
91
89
|
"""
|
|
@@ -95,7 +93,7 @@ class RequisitionPanel:
|
|
|
95
93
|
raise RequisitionPanelLookupError(
|
|
96
94
|
f"Invalid requisition model. requisition model="
|
|
97
95
|
f"'{self.requisition_model}'. "
|
|
98
|
-
f"See {
|
|
96
|
+
f"See {self!r} or the lab profile {self.lab_profile_name}."
|
|
99
97
|
f"Got {e}"
|
|
100
98
|
)
|
|
101
99
|
return requisition_model_cls
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
from .processing_profile import ProcessingProfile
|
|
4
2
|
from .requisition_panel import RequisitionPanel
|
|
5
3
|
|
|
@@ -12,11 +10,11 @@ class RequisitionPanelGroup(RequisitionPanel):
|
|
|
12
10
|
def __init__(
|
|
13
11
|
self,
|
|
14
12
|
*panels,
|
|
15
|
-
name:
|
|
16
|
-
verbose_name:
|
|
17
|
-
abbreviation:
|
|
18
|
-
is_poc:
|
|
19
|
-
reference_range_collection_name:
|
|
13
|
+
name: str | None = None,
|
|
14
|
+
verbose_name: str | None = None,
|
|
15
|
+
abbreviation: str | None = None,
|
|
16
|
+
is_poc: bool | None = None,
|
|
17
|
+
reference_range_collection_name: str | None = None,
|
|
20
18
|
):
|
|
21
19
|
processing_profile = ProcessingProfile(
|
|
22
20
|
name=f"{name} group", aliquot_type=panels[0].aliquot_type
|
|
@@ -110,7 +110,7 @@ class RequisitionModelMixin(
|
|
|
110
110
|
),
|
|
111
111
|
)
|
|
112
112
|
|
|
113
|
-
comments = models.TextField(max_length=25,
|
|
113
|
+
comments = models.TextField(max_length=25, blank=True, default="")
|
|
114
114
|
|
|
115
115
|
objects = RequisitionManager()
|
|
116
116
|
|
|
@@ -130,7 +130,7 @@ class RequisitionModelMixin(
|
|
|
130
130
|
super().save(*args, **kwargs)
|
|
131
131
|
|
|
132
132
|
def natural_key(self):
|
|
133
|
-
return (self.requisition_identifier,)
|
|
133
|
+
return (self.requisition_identifier,)
|
|
134
134
|
|
|
135
135
|
natural_key.dependencies = [settings.SUBJECT_VISIT_MODEL, "sites.Site"]
|
|
136
136
|
|
|
@@ -155,8 +155,9 @@ class RequisitionModelMixin(
|
|
|
155
155
|
)
|
|
156
156
|
]
|
|
157
157
|
|
|
158
|
-
indexes =
|
|
158
|
+
indexes = (
|
|
159
|
+
*NonUniqueSubjectIdentifierFieldMixin.Meta.indexes,
|
|
159
160
|
models.Index(fields=["subject_visit", "site", "panel"]),
|
|
160
161
|
models.Index(fields=["subject_visit", "panel"]),
|
|
161
162
|
models.Index(fields=["subject_visit", "report_datetime"]),
|
|
162
|
-
|
|
163
|
+
)
|
|
@@ -20,13 +20,13 @@ class ManifestModelMixin(SiteModelMixin, models.Model):
|
|
|
20
20
|
|
|
21
21
|
manifest_datetime = models.DateTimeField(default=timezone.now)
|
|
22
22
|
|
|
23
|
-
export_datetime = models.DateTimeField(
|
|
23
|
+
export_datetime = models.DateTimeField(blank=True)
|
|
24
24
|
|
|
25
|
-
export_references = models.TextField(
|
|
25
|
+
export_references = models.TextField(blank=True, default="")
|
|
26
26
|
|
|
27
27
|
description = models.TextField(
|
|
28
28
|
verbose_name="Description of contents",
|
|
29
|
-
|
|
29
|
+
default="",
|
|
30
30
|
help_text="If blank will be automatically generated",
|
|
31
31
|
)
|
|
32
32
|
|
|
@@ -36,7 +36,7 @@ class ManifestModelMixin(SiteModelMixin, models.Model):
|
|
|
36
36
|
|
|
37
37
|
category_other = models.CharField(max_length=25, null=True, blank=True)
|
|
38
38
|
|
|
39
|
-
comment = models.TextField(verbose_name="Comment",
|
|
39
|
+
comment = models.TextField(verbose_name="Comment", blank=True, default="")
|
|
40
40
|
|
|
41
41
|
shipped = models.BooleanField(default=False)
|
|
42
42
|
|
|
@@ -56,14 +56,14 @@ class ManifestModelMixin(SiteModelMixin, models.Model):
|
|
|
56
56
|
super().save(*args, **kwargs)
|
|
57
57
|
|
|
58
58
|
def natural_key(self):
|
|
59
|
-
return (self.manifest_identifier,)
|
|
59
|
+
return (self.manifest_identifier,)
|
|
60
60
|
|
|
61
61
|
natural_key.dependencies = ["sites.Site"]
|
|
62
62
|
|
|
63
63
|
@property
|
|
64
64
|
def human_readable_identifier(self):
|
|
65
65
|
x = self.manifest_identifier
|
|
66
|
-
return "{
|
|
66
|
+
return f"{x[0:4]}-{x[4:8]}-{x[8:12]}"
|
|
67
67
|
|
|
68
68
|
class Meta:
|
|
69
69
|
abstract = True
|
|
@@ -41,9 +41,10 @@ class VerifyBoxModelMixin(VerifyModelMixin, models.Model):
|
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
43
|
def is_verified(self):
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
if (
|
|
45
|
+
self.boxitem_set.all().count() == 0
|
|
46
|
+
or self.boxitem_set.filter(verified=False).exists()
|
|
47
|
+
):
|
|
47
48
|
return False
|
|
48
49
|
return True
|
|
49
50
|
|
edc_lab/models/aliquot.py
CHANGED
|
@@ -42,7 +42,7 @@ class Aliquot(
|
|
|
42
42
|
def human_readable_identifier(self):
|
|
43
43
|
"""Returns a human readable aliquot identifier."""
|
|
44
44
|
x = self.aliquot_identifier
|
|
45
|
-
return "{
|
|
45
|
+
return f"{x[0:3]}-{x[3:6]}-{x[6:10]}-{x[10:14]}-{x[14:18]}"
|
|
46
46
|
|
|
47
47
|
class Meta(BaseUuidModel.Meta):
|
|
48
48
|
verbose_name = "Aliquot"
|
edc_lab/models/box.py
CHANGED
|
@@ -68,7 +68,7 @@ class Box(SearchSlugModelMixin, VerifyBoxModelMixin, SiteModelMixin, BaseUuidMod
|
|
|
68
68
|
help_text="Tick to allow 'primary' specimens to be added to this box",
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
comment = models.TextField(
|
|
71
|
+
comment = models.TextField(blank=True, default="")
|
|
72
72
|
|
|
73
73
|
objects = BoxManager()
|
|
74
74
|
|
|
@@ -104,7 +104,7 @@ class Box(SearchSlugModelMixin, VerifyBoxModelMixin, SiteModelMixin, BaseUuidMod
|
|
|
104
104
|
@property
|
|
105
105
|
def human_readable_identifier(self):
|
|
106
106
|
x = self.box_identifier
|
|
107
|
-
return "{
|
|
107
|
+
return f"{x[0:4]}-{x[4:8]}-{x[8:12]}"
|
|
108
108
|
|
|
109
109
|
@property
|
|
110
110
|
def next_position(self):
|
edc_lab/models/box_item.py
CHANGED
|
@@ -45,7 +45,7 @@ class BoxItem(SearchSlugModelMixin, VerifyModelMixin, BaseUuidModel):
|
|
|
45
45
|
if self.identifier:
|
|
46
46
|
x = self.identifier
|
|
47
47
|
if re.match(aliquot_pattern, self.identifier):
|
|
48
|
-
return "{
|
|
48
|
+
return f"{x[0:3]}-{x[3:6]}-{x[6:10]}-{x[10:14]}-{x[14:18]}"
|
|
49
49
|
return self.identifier
|
|
50
50
|
|
|
51
51
|
def get_slugs(self):
|
edc_lab/models/box_type.py
CHANGED
|
@@ -35,7 +35,7 @@ class ManifestItem(SiteModelMixin, SearchSlugModelMixin, VerifyModelMixin, BaseU
|
|
|
35
35
|
@property
|
|
36
36
|
def human_readable_identifier(self):
|
|
37
37
|
x = self.identifier
|
|
38
|
-
return "{
|
|
38
|
+
return f"{x[0:4]}-{x[4:8]}-{x[8:12]}"
|
|
39
39
|
|
|
40
40
|
class Meta(BaseUuidModel.Meta):
|
|
41
41
|
verbose_name = "Manifest Item"
|
|
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
|
|
|
23
23
|
|
|
24
24
|
from ..model_mixins import ManifestModelMixin
|
|
25
25
|
|
|
26
|
-
class Model(PdfReportModelMixin, ManifestModelMixin, BaseUuidModel): ...
|
|
26
|
+
class Model(PdfReportModelMixin, ManifestModelMixin, BaseUuidModel): ...
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class ManifestPdfReportError(Exception):
|
|
@@ -403,7 +403,7 @@ class ManifestPdfReport(Report):
|
|
|
403
403
|
),
|
|
404
404
|
Paragraph(box.specimen_types, self.styles["line_data_large"]),
|
|
405
405
|
Paragraph(
|
|
406
|
-
f"{
|
|
406
|
+
f"{box.count!s}/{box.box_type.total!s}",
|
|
407
407
|
self.styles["line_data_large"],
|
|
408
408
|
),
|
|
409
409
|
Paragraph(
|
|
@@ -450,11 +450,7 @@ class ManifestPdfReport(Report):
|
|
|
450
450
|
Paragraph(aliquot.human_readable_identifier, self.styles["row_data"]),
|
|
451
451
|
Paragraph(aliquot.subject_identifier, self.styles["row_data"]),
|
|
452
452
|
Paragraph(
|
|
453
|
-
"{} ({}) {}"
|
|
454
|
-
aliquot.aliquot_type,
|
|
455
|
-
aliquot.numeric_code,
|
|
456
|
-
panel_object.abbreviation,
|
|
457
|
-
),
|
|
453
|
+
f"{aliquot.aliquot_type} ({aliquot.numeric_code}) {panel_object.abbreviation}",
|
|
458
454
|
self.styles["row_data"],
|
|
459
455
|
),
|
|
460
456
|
Paragraph(
|
edc_lab/site_labs.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import sys
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from django.apps import apps as django_apps
|
|
8
8
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -53,7 +53,7 @@ class SiteLabs:
|
|
|
53
53
|
def registry(self):
|
|
54
54
|
if not self.loaded:
|
|
55
55
|
raise RegistryNotLoaded(
|
|
56
|
-
"Registry not loaded. Is AppConfig for 'edc_lab'
|
|
56
|
+
"Registry not loaded. Is AppConfig for 'edc_lab' declared in settings?."
|
|
57
57
|
)
|
|
58
58
|
return self._registry
|
|
59
59
|
|
|
@@ -112,7 +112,7 @@ class SiteLabs:
|
|
|
112
112
|
f"before attempting to creating new Panel model instances."
|
|
113
113
|
)
|
|
114
114
|
|
|
115
|
-
def update_panel_model(self, panel_model_cls:
|
|
115
|
+
def update_panel_model(self, panel_model_cls: type[Panel] = None) -> None:
|
|
116
116
|
"""Updates or creates panel mode instances.
|
|
117
117
|
|
|
118
118
|
Initially called in the post_migrate signal.
|
|
@@ -12,7 +12,7 @@ dashboard_templates = dict(
|
|
|
12
12
|
edc_lab_base_listboard_template="edc_lab_dashboard/base_listboard.html",
|
|
13
13
|
edc_lab_listboard_template="edc_lab_dashboard/listboard.html",
|
|
14
14
|
manage_box_listboard_template="edc_lab_dashboard/manage_box_listboard.html",
|
|
15
|
-
manage_manifest_listboard_template="edc_lab_dashboard/manage_manifest_listboard.html",
|
|
15
|
+
manage_manifest_listboard_template="edc_lab_dashboard/manage_manifest_listboard.html",
|
|
16
16
|
manifest_listboard_template="edc_lab_dashboard/manifest_listboard.html",
|
|
17
17
|
pack_listboard_template="edc_lab_dashboard/pack_listboard.html",
|
|
18
18
|
process_listboard_template="edc_lab_dashboard/process_listboard.html",
|