clinicedc 2.0.4__py3-none-any.whl → 2.0.6__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.6.dist-info}/METADATA +41 -45
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.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 -10
- 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.6.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -186,7 +186,7 @@ class AddToStorageBinView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, T
|
|
|
186
186
|
},
|
|
187
187
|
)
|
|
188
188
|
return HttpResponseRedirect(url)
|
|
189
|
-
|
|
189
|
+
if items_to_scan and stock_codes:
|
|
190
190
|
try:
|
|
191
191
|
codes_created, codes_not_created = update_bin(
|
|
192
192
|
storage_bin,
|
|
@@ -33,9 +33,7 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
33
33
|
|
|
34
34
|
def get_context_data(self, **kwargs):
|
|
35
35
|
remaining_count, total_count = self.get_counts(self.stock_request)
|
|
36
|
-
show_count = (
|
|
37
|
-
self.items_per_page if remaining_count >= self.items_per_page else remaining_count
|
|
38
|
-
)
|
|
36
|
+
show_count = min(self.items_per_page, remaining_count)
|
|
39
37
|
kwargs.update(
|
|
40
38
|
stock_request=self.stock_request,
|
|
41
39
|
assignment=self.selected_assignment,
|
|
@@ -255,7 +253,6 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
255
253
|
def redirect_on_all_allocated_for_assignment(
|
|
256
254
|
self, stock_request: StockRequest, assignment: Assignment
|
|
257
255
|
) -> str | None:
|
|
258
|
-
|
|
259
256
|
if not stock_request.stockrequestitem_set.filter(
|
|
260
257
|
allocation__isnull=True, assignment=assignment
|
|
261
258
|
).exists():
|
|
@@ -284,7 +281,6 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
284
281
|
stock_request: StockRequest,
|
|
285
282
|
assignment: Assignment,
|
|
286
283
|
) -> str | None:
|
|
287
|
-
|
|
288
284
|
if stock_codes and Stock.objects.filter(
|
|
289
285
|
code__in=stock_codes, lot__assignment=assignment
|
|
290
286
|
).count() != len(stock_codes):
|
|
@@ -347,7 +343,7 @@ class AllocateToSubjectView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin,
|
|
|
347
343
|
return HttpResponseRedirect(url)
|
|
348
344
|
|
|
349
345
|
if stock_codes and subject_identifiers and assignment:
|
|
350
|
-
allocation_data = dict(zip(stock_codes, subject_identifiers))
|
|
346
|
+
allocation_data = dict(zip(stock_codes, subject_identifiers, strict=False))
|
|
351
347
|
try:
|
|
352
348
|
allocated, not_allocated = allocate_stock(
|
|
353
349
|
stock_request,
|
|
@@ -47,7 +47,7 @@ class ConfirmStockFromInstanceView(
|
|
|
47
47
|
)
|
|
48
48
|
.count()
|
|
49
49
|
)
|
|
50
|
-
unconfirmed_count =
|
|
50
|
+
unconfirmed_count = min(unconfirmed_count, 12)
|
|
51
51
|
|
|
52
52
|
kwargs.update(
|
|
53
53
|
source_identifier=dct.get("source_identifier"),
|
|
@@ -83,12 +83,12 @@ class ConfirmStockFromInstanceView(
|
|
|
83
83
|
@property
|
|
84
84
|
def source_changelist_url(self):
|
|
85
85
|
return reverse(
|
|
86
|
-
f"edc_pharmacy_admin:edc_pharmacy_{self.kwargs.get(
|
|
86
|
+
f"edc_pharmacy_admin:edc_pharmacy_{self.kwargs.get('model')}_changelist"
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
@property
|
|
90
90
|
def model_cls(self):
|
|
91
|
-
return django_apps.get_model(f"edc_pharmacy.{self.kwargs.get(
|
|
91
|
+
return django_apps.get_model(f"edc_pharmacy.{self.kwargs.get('model')}")
|
|
92
92
|
|
|
93
93
|
def post(self, request, *args, **kwargs):
|
|
94
94
|
dct = self.get_values_dict(**kwargs)
|
|
@@ -114,5 +114,5 @@ class ConfirmStockFromInstanceView(
|
|
|
114
114
|
):
|
|
115
115
|
url = reverse("edc_pharmacy:confirm_stock_from_instance_url", kwargs=kwargs)
|
|
116
116
|
else:
|
|
117
|
-
url = f"{self.source_changelist_url}?q={dct.get(
|
|
117
|
+
url = f"{self.source_changelist_url}?q={dct.get('obj').pk}"
|
|
118
118
|
return HttpResponseRedirect(url)
|
|
@@ -105,11 +105,7 @@ class ConfirmStockFromQuerySetView(
|
|
|
105
105
|
|
|
106
106
|
@property
|
|
107
107
|
def adjusted_unconfirmed_count(self) -> int:
|
|
108
|
-
return (
|
|
109
|
-
self.codes_per_page
|
|
110
|
-
if self.unconfirmed_count > self.codes_per_page
|
|
111
|
-
else self.unconfirmed_count
|
|
112
|
-
)
|
|
108
|
+
return min(self.unconfirmed_count, self.codes_per_page)
|
|
113
109
|
|
|
114
110
|
@property
|
|
115
111
|
def source_changelist_url(self):
|
|
@@ -94,7 +94,7 @@ class ConfirmationAtSiteView(
|
|
|
94
94
|
|
|
95
95
|
def get_adjusted_unconfirmed_count(self, stock_transfer):
|
|
96
96
|
unconfirmed_count = self.get_unconfirmed_count(stock_transfer)
|
|
97
|
-
return
|
|
97
|
+
return min(unconfirmed_count, 12)
|
|
98
98
|
|
|
99
99
|
def get_stock_codes(self, stock_transfer):
|
|
100
100
|
stock_codes = [
|
|
@@ -199,7 +199,6 @@ class ConfirmationAtSiteView(
|
|
|
199
199
|
return stock_transfer
|
|
200
200
|
|
|
201
201
|
def post(self, request, *args, **kwargs) -> HttpResponseRedirect:
|
|
202
|
-
|
|
203
202
|
# cancel
|
|
204
203
|
if request.POST.get("cancel") and request.POST.get("cancel") == "cancel":
|
|
205
204
|
url = reverse("edc_pharmacy:home_url")
|
|
@@ -230,7 +229,7 @@ class ConfirmationAtSiteView(
|
|
|
230
229
|
)
|
|
231
230
|
return HttpResponseRedirect(url)
|
|
232
231
|
|
|
233
|
-
|
|
232
|
+
if stock_codes:
|
|
234
233
|
# you have scanned codes, process them
|
|
235
234
|
confirmed, already_confirmed, invalid = confirm_stock_at_site(
|
|
236
235
|
stock_transfer, stock_codes, location_id, request=request
|
|
@@ -106,7 +106,7 @@ class DispenseView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Template
|
|
|
106
106
|
)
|
|
107
107
|
return HttpResponseRedirect(url)
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
if stock_codes and location and formulation and rx and container_count:
|
|
110
110
|
dispense_qs = dispense(
|
|
111
111
|
stock_codes,
|
|
112
112
|
location,
|
|
@@ -28,7 +28,7 @@ def move_to_bin(
|
|
|
28
28
|
)
|
|
29
29
|
if new_capacity > 50:
|
|
30
30
|
raise StorageBinError(f"Storage bin {storage_bin.name} capacity may not exceeded 50")
|
|
31
|
-
|
|
31
|
+
if new_capacity > storage_bin.capacity:
|
|
32
32
|
storage_bin.capacity = new_capacity
|
|
33
33
|
storage_bin.save()
|
|
34
34
|
messages.add_message(
|
|
@@ -66,7 +66,6 @@ def move_to_bin(
|
|
|
66
66
|
|
|
67
67
|
@method_decorator(login_required, name="dispatch")
|
|
68
68
|
class MoveToStorageBinView(AddToStorageBinView):
|
|
69
|
-
|
|
70
69
|
action_word = "Move"
|
|
71
70
|
|
|
72
71
|
def redirect_on_stock_not_already_in_a_bin(
|
|
@@ -109,7 +108,7 @@ class MoveToStorageBinView(AddToStorageBinView):
|
|
|
109
108
|
},
|
|
110
109
|
)
|
|
111
110
|
return HttpResponseRedirect(url)
|
|
112
|
-
|
|
111
|
+
if items_to_scan and stock_codes:
|
|
113
112
|
try:
|
|
114
113
|
codes_moved, codes_not_moved = move_to_bin(
|
|
115
114
|
storage_bin, stock_codes, request.user.username, request
|
|
@@ -94,37 +94,36 @@ class PrintLabelsView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Templ
|
|
|
94
94
|
),
|
|
95
95
|
)
|
|
96
96
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER", ""))
|
|
97
|
+
label_data = [obj for obj in queryset]
|
|
98
|
+
drawing_callable = site_label_configs.get(
|
|
99
|
+
label_configuration.name
|
|
100
|
+
).drawing_callable
|
|
101
|
+
specs = Specification(**label_configuration.label_specification.as_dict)
|
|
102
|
+
sheet = Sheet(
|
|
103
|
+
specs,
|
|
104
|
+
drawing_callable,
|
|
105
|
+
border=label_configuration.label_specification.border,
|
|
106
|
+
)
|
|
107
|
+
try:
|
|
108
|
+
sheet.add_labels(label_data)
|
|
109
|
+
except AttributeError:
|
|
110
|
+
messages.add_message(
|
|
111
|
+
request,
|
|
112
|
+
messages.ERROR,
|
|
113
|
+
(
|
|
114
|
+
"Unable to print these items using the "
|
|
115
|
+
f"label format '{label_configuration.name}'. Perhaps try another."
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
return HttpResponseRedirect(request.META.get("HTTP_REFERER", ""))
|
|
97
119
|
else:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
border=label_configuration.label_specification.border,
|
|
120
|
+
buffer = sheet.save_to_buffer()
|
|
121
|
+
now = get_utcnow()
|
|
122
|
+
return FileResponse(
|
|
123
|
+
buffer,
|
|
124
|
+
as_attachment=True,
|
|
125
|
+
filename=(
|
|
126
|
+
f"{label_configuration.name}_{now.strftime('%Y-%m-%d %H:%M')}.pdf"
|
|
127
|
+
),
|
|
107
128
|
)
|
|
108
|
-
try:
|
|
109
|
-
sheet.add_labels(label_data)
|
|
110
|
-
except AttributeError:
|
|
111
|
-
messages.add_message(
|
|
112
|
-
request,
|
|
113
|
-
messages.ERROR,
|
|
114
|
-
(
|
|
115
|
-
"Unable to print these items using the "
|
|
116
|
-
f"label format '{label_configuration.name}'. Perhaps try another."
|
|
117
|
-
),
|
|
118
|
-
)
|
|
119
|
-
return HttpResponseRedirect(request.META.get("HTTP_REFERER", ""))
|
|
120
|
-
else:
|
|
121
|
-
buffer = sheet.save_to_buffer()
|
|
122
|
-
now = get_utcnow()
|
|
123
|
-
return FileResponse(
|
|
124
|
-
buffer,
|
|
125
|
-
as_attachment=True,
|
|
126
|
-
filename=(
|
|
127
|
-
f"{label_configuration.name}_{now.strftime('%Y-%m-%d %H:%M')}.pdf"
|
|
128
|
-
),
|
|
129
|
-
)
|
|
130
129
|
return HttpResponseRedirect(url)
|
|
@@ -35,7 +35,7 @@ class TransferStockView(EdcViewMixin, NavbarViewMixin, EdcProtocolViewMixin, Tem
|
|
|
35
35
|
stock_transfer=stock_transfer
|
|
36
36
|
).count()
|
|
37
37
|
item_count = stock_transfer.item_count - transferred_count
|
|
38
|
-
item_count =
|
|
38
|
+
item_count = min(item_count, 12)
|
|
39
39
|
kwargs.update(
|
|
40
40
|
stock_transfer=stock_transfer,
|
|
41
41
|
source_model_name=self.model_cls._meta.verbose_name_plural,
|
edc_prn/prn.py
CHANGED
|
@@ -58,7 +58,7 @@ class Prn:
|
|
|
58
58
|
try:
|
|
59
59
|
return django_apps.get_model(self.model)
|
|
60
60
|
except LookupError as e:
|
|
61
|
-
raise PrnError(f"{e}. See {
|
|
61
|
+
raise PrnError(f"{e}. See {self!r}")
|
|
62
62
|
|
|
63
63
|
def get_show_on_dashboard(self, subject_identifier=None, **kwargs):
|
|
64
64
|
count = 0
|
edc_prn/site_prn_forms.py
CHANGED
|
@@ -38,8 +38,7 @@ class PrnFormsCollection:
|
|
|
38
38
|
def register(self, prn: Prn = None) -> None:
|
|
39
39
|
if prn.model in self.registry:
|
|
40
40
|
raise AlreadyRegistered(f"PRN form {prn.model} is already registered.")
|
|
41
|
-
|
|
42
|
-
self.registry.update({prn.model: prn})
|
|
41
|
+
self.registry.update({prn.model: prn})
|
|
43
42
|
self.reorder_registry()
|
|
44
43
|
|
|
45
44
|
def unregister(self, prn: Prn = None) -> None:
|
|
@@ -15,7 +15,7 @@ class ProtocolDeviationViolationAction(ActionWithNotification):
|
|
|
15
15
|
name = PROTOCOL_DEVIATION_VIOLATION_ACTION
|
|
16
16
|
display_name = "Submit Protocol Deviation / Violation Report"
|
|
17
17
|
notification_display_name = "Protocol Deviation / Violation Report"
|
|
18
|
-
parent_action_names =
|
|
18
|
+
parent_action_names = ()
|
|
19
19
|
show_link_to_changelist = True
|
|
20
20
|
show_link_to_add = True
|
|
21
21
|
priority = HIGH_PRIORITY
|
|
@@ -31,7 +31,7 @@ class ProtocolIncidentAction(ActionWithNotification):
|
|
|
31
31
|
name = PROTOCOL_INCIDENT_ACTION
|
|
32
32
|
display_name = "Submit Protocol Incident Report"
|
|
33
33
|
notification_display_name = "Protocol Incident Report"
|
|
34
|
-
parent_action_names =
|
|
34
|
+
parent_action_names = ()
|
|
35
35
|
show_link_to_changelist = True
|
|
36
36
|
show_link_to_add = True
|
|
37
37
|
priority = HIGH_PRIORITY
|
|
@@ -32,7 +32,7 @@ class ProtocolDeviationViolationModelMixin(models.Model):
|
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
safety_impact = models.CharField(
|
|
35
|
-
verbose_name="Could this occurrence have an impact on safety of the
|
|
35
|
+
verbose_name="Could this occurrence have an impact on safety of the participant?",
|
|
36
36
|
max_length=25,
|
|
37
37
|
choices=YES_NO_NA,
|
|
38
38
|
default=NOT_APPLICABLE,
|
|
@@ -78,7 +78,7 @@ class ProtocolDeviationViolationModelMixin(models.Model):
|
|
|
78
78
|
null=True,
|
|
79
79
|
blank=True,
|
|
80
80
|
help_text=(
|
|
81
|
-
"Describe in full. Explain how the violation
|
|
81
|
+
"Describe in full. Explain how the violation happened, what occurred, etc."
|
|
82
82
|
),
|
|
83
83
|
)
|
|
84
84
|
|
|
@@ -135,4 +135,4 @@ class ProtocolDeviationViolationModelMixin(models.Model):
|
|
|
135
135
|
abstract = True
|
|
136
136
|
verbose_name = "Protocol Deviation/Violation"
|
|
137
137
|
verbose_name_plural = "Protocol Deviations/Violations"
|
|
138
|
-
indexes =
|
|
138
|
+
indexes = (models.Index(fields=["subject_identifier", "action_identifier", "site"]),)
|
|
@@ -19,10 +19,8 @@ class ProtocolIncidentModelMixin(models.Model):
|
|
|
19
19
|
null=True,
|
|
20
20
|
blank=False,
|
|
21
21
|
help_text=(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"a more detailed description"
|
|
25
|
-
)
|
|
22
|
+
"Max 35 characters. Note: there is additional space below for "
|
|
23
|
+
"a more detailed description"
|
|
26
24
|
),
|
|
27
25
|
)
|
|
28
26
|
|
|
@@ -132,4 +130,4 @@ class ProtocolIncidentModelMixin(models.Model):
|
|
|
132
130
|
abstract = True
|
|
133
131
|
verbose_name = "Protocol Incident"
|
|
134
132
|
verbose_name_plural = "Protocol Incident"
|
|
135
|
-
indexes =
|
|
133
|
+
indexes = (models.Index(fields=["subject_identifier", "action_identifier", "site"]),)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
from django.contrib import admin
|
|
4
2
|
from django.utils.html import format_html
|
|
5
3
|
from django_audit_fields.admin import audit_fieldset_tuple
|
|
@@ -88,7 +86,7 @@ class ProtocolIncidentModelAdminMixin:
|
|
|
88
86
|
|
|
89
87
|
search_fields = ("subject_identifier",)
|
|
90
88
|
|
|
91
|
-
def get_list_display(self, request) ->
|
|
89
|
+
def get_list_display(self, request) -> tuple[str, ...]:
|
|
92
90
|
list_display = super().get_list_display(request)
|
|
93
91
|
custom_fields = (
|
|
94
92
|
"subject_identifier",
|
|
@@ -103,12 +101,12 @@ class ProtocolIncidentModelAdminMixin:
|
|
|
103
101
|
)
|
|
104
102
|
return custom_fields + tuple(f for f in list_display if f not in custom_fields)
|
|
105
103
|
|
|
106
|
-
def get_list_filter(self, request) ->
|
|
104
|
+
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
107
105
|
list_filter = super().get_list_filter(request)
|
|
108
106
|
custom_fields = ("action_required", "report_status", "report_type")
|
|
109
107
|
return custom_fields + tuple(f for f in list_filter if f not in custom_fields)
|
|
110
108
|
|
|
111
|
-
def get_search_fields(self, request) ->
|
|
109
|
+
def get_search_fields(self, request) -> tuple[str, ...]:
|
|
112
110
|
search_fields = super().get_search_fields(request)
|
|
113
111
|
custom_fields = ("short_description",)
|
|
114
112
|
return tuple(set(custom_fields + search_fields))
|
|
@@ -23,12 +23,12 @@ class ProtocolDeviationViolation(
|
|
|
23
23
|
objects = ActionIdentifierModelManager()
|
|
24
24
|
|
|
25
25
|
def natural_key(self):
|
|
26
|
-
return (self.action_identifier,)
|
|
26
|
+
return (self.action_identifier,)
|
|
27
27
|
|
|
28
28
|
class Meta(ProtocolDeviationViolationModelMixin.Meta, BaseUuidModel.Meta):
|
|
29
29
|
indexes = (
|
|
30
|
-
ProtocolDeviationViolationModelMixin.Meta.indexes
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
*ProtocolDeviationViolationModelMixin.Meta.indexes,
|
|
31
|
+
*NonUniqueSubjectIdentifierFieldMixin.Meta.indexes,
|
|
32
|
+
*ActionModelMixin.Meta.indexes,
|
|
33
|
+
*BaseUuidModel.Meta.indexes,
|
|
34
34
|
)
|
|
@@ -23,12 +23,12 @@ class ProtocolIncident(
|
|
|
23
23
|
objects = ActionIdentifierModelManager()
|
|
24
24
|
|
|
25
25
|
def natural_key(self):
|
|
26
|
-
return (self.action_identifier,)
|
|
26
|
+
return (self.action_identifier,)
|
|
27
27
|
|
|
28
28
|
class Meta(ProtocolIncidentModelMixin.Meta, BaseUuidModel.Meta):
|
|
29
29
|
indexes = (
|
|
30
|
-
ProtocolIncidentModelMixin.Meta.indexes
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
*ProtocolIncidentModelMixin.Meta.indexes,
|
|
31
|
+
*NonUniqueSubjectIdentifierFieldMixin.Meta.indexes,
|
|
32
|
+
*ActionModelMixin.Meta.indexes,
|
|
33
|
+
*BaseUuidModel.Meta.indexes,
|
|
34
34
|
)
|
|
@@ -2,8 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import copy
|
|
4
4
|
import sys
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from importlib import import_module
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
7
8
|
|
|
8
9
|
from django.apps import apps as django_apps
|
|
9
10
|
from django.core.management.color import color_style
|
|
@@ -67,10 +68,9 @@ class SiteLabelConfigs:
|
|
|
67
68
|
) -> None:
|
|
68
69
|
if name in self.registry:
|
|
69
70
|
raise AlreadyRegistered(f"Already registered. Got name='{name}' ")
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)
|
|
71
|
+
self.registry.update(
|
|
72
|
+
{name: LabelConfig(name, drawing_callable, label_cls, test_data_func)}
|
|
73
|
+
)
|
|
74
74
|
|
|
75
75
|
def all(self) -> dict[str, LabelConfig]:
|
|
76
76
|
return self.registry
|
|
@@ -9,7 +9,6 @@ from .qa_reports_permissions import qa_reports_permissions
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class QaReportModelMixin(models.Model):
|
|
12
|
-
|
|
13
12
|
report_model = models.CharField(max_length=50)
|
|
14
13
|
|
|
15
14
|
subject_identifier = models.CharField(max_length=25)
|
|
@@ -18,6 +17,9 @@ class QaReportModelMixin(models.Model):
|
|
|
18
17
|
|
|
19
18
|
created = models.DateTimeField(default=get_utcnow)
|
|
20
19
|
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return f"{self.subject_identifier}: {self.report_model}"
|
|
22
|
+
|
|
21
23
|
@classmethod
|
|
22
24
|
def recreate_db_view(cls, drop: bool | None = None, verbose: bool | None = None):
|
|
23
25
|
recreate_db_view(cls, drop=drop, verbose=verbose)
|
|
@@ -25,4 +27,4 @@ class QaReportModelMixin(models.Model):
|
|
|
25
27
|
class Meta:
|
|
26
28
|
abstract = True
|
|
27
29
|
default_permissions = qa_reports_permissions
|
|
28
|
-
indexes =
|
|
30
|
+
indexes = (Index(fields=["subject_identifier", "site"]),)
|
|
@@ -7,7 +7,6 @@ from ..model_mixins import qa_reports_permissions
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class QaReportLog(models.Model):
|
|
10
|
-
|
|
11
10
|
username = models.CharField(max_length=100)
|
|
12
11
|
site = models.ForeignKey(Site, on_delete=models.CASCADE)
|
|
13
12
|
report_model = models.CharField(max_length=100)
|
|
@@ -16,5 +15,5 @@ class QaReportLog(models.Model):
|
|
|
16
15
|
class Meta:
|
|
17
16
|
verbose_name = "QA Report Log"
|
|
18
17
|
verbose_name_plural = "QA Report Logs"
|
|
19
|
-
indexes =
|
|
18
|
+
indexes = (models.Index(fields=["accessed"]),)
|
|
20
19
|
default_permissions = qa_reports_permissions
|
edc_qareports/utils.py
CHANGED
|
@@ -84,7 +84,7 @@ def recreate_db_view(model_cls, drop: bool | None = None, verbose: bool | None =
|
|
|
84
84
|
"""
|
|
85
85
|
drop = True if drop is None else drop
|
|
86
86
|
try:
|
|
87
|
-
sql = model_cls.view_definition.get(settings.DATABASES["default"]["ENGINE"])
|
|
87
|
+
sql = model_cls.view_definition.get(settings.DATABASES["default"]["ENGINE"])
|
|
88
88
|
except AttributeError as e:
|
|
89
89
|
raise AttributeError(
|
|
90
90
|
f"Is this model linked to a view? Declare model with `DBView`. Got {e}"
|
|
@@ -107,7 +107,6 @@ def recreate_db_view(model_cls, drop: bool | None = None, verbose: bool | None =
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
def recreate_dbview_for_all():
|
|
110
|
-
|
|
111
110
|
from .model_mixins import QaReportModelMixin
|
|
112
111
|
|
|
113
112
|
for model_cls in django_apps.get_models():
|
edc_randomization/admin.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
from django.conf import settings
|
|
4
2
|
from django.contrib import admin
|
|
5
3
|
from django.contrib.admin.sites import AlreadyRegistered
|
|
@@ -91,7 +89,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
91
89
|
qs = qs.order_by(*ordering)
|
|
92
90
|
return qs
|
|
93
91
|
|
|
94
|
-
def get_list_display(self, request) ->
|
|
92
|
+
def get_list_display(self, request) -> tuple[str, ...]:
|
|
95
93
|
list_display = [
|
|
96
94
|
"sid",
|
|
97
95
|
"assignment",
|
|
@@ -114,7 +112,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
114
112
|
return tuple(list_display)
|
|
115
113
|
|
|
116
114
|
@staticmethod
|
|
117
|
-
def get_fieldnames(request) ->
|
|
115
|
+
def get_fieldnames(request) -> tuple[str, ...]:
|
|
118
116
|
fields = [
|
|
119
117
|
"subject_identifier",
|
|
120
118
|
"sid",
|
|
@@ -132,7 +130,7 @@ class RandomizationListModelAdmin(TemplatesModelAdminMixin, admin.ModelAdmin):
|
|
|
132
130
|
fields.remove("assignment")
|
|
133
131
|
return tuple(fields)
|
|
134
132
|
|
|
135
|
-
def get_list_filter(self, request) ->
|
|
133
|
+
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
136
134
|
list_filter = [
|
|
137
135
|
"assignment",
|
|
138
136
|
"allocated_datetime",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from .site_randomizers import site_randomizers
|
|
5
4
|
|
|
@@ -36,7 +35,7 @@ export_rando = [
|
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
# post_update_func
|
|
39
|
-
def update_rando_group_permissions(auth_updater, app_label:
|
|
38
|
+
def update_rando_group_permissions(auth_updater, app_label: str | None):
|
|
40
39
|
"""Update group permissions for each registered randomizer class."""
|
|
41
40
|
for randomizer_cls in site_randomizers._registry.values():
|
|
42
41
|
if auth_updater.verbose:
|
|
@@ -56,7 +55,7 @@ def update_rando_group_permissions(auth_updater, app_label: Optional[str]):
|
|
|
56
55
|
)
|
|
57
56
|
|
|
58
57
|
|
|
59
|
-
def make_randomizationlist_view_only(auth_updater, app_label:
|
|
58
|
+
def make_randomizationlist_view_only(auth_updater, app_label: str | None):
|
|
60
59
|
for randomizer_cls in site_randomizers._registry.values():
|
|
61
60
|
randomizer_cls.apps = auth_updater.apps
|
|
62
61
|
app_label, model = randomizer_cls.model_cls()._meta.label_lower.split(".")
|
edc_randomization/blinding.py
CHANGED
edc_randomization/constants.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Dict
|
|
2
|
-
|
|
3
1
|
RANDOMIZED = "RANDOMIZED"
|
|
4
2
|
|
|
5
3
|
ACTIVE = "active"
|
|
@@ -7,8 +5,8 @@ ACTIVE_NAME = "Active: "
|
|
|
7
5
|
PLACEBO = "placebo"
|
|
8
6
|
PLACEBO_NAME = "Placebo: "
|
|
9
7
|
DEFAULT = "default"
|
|
10
|
-
DEFAULT_ASSIGNMENT_MAP:
|
|
11
|
-
DEFAULT_ASSIGNMENT_DESCRIPTION_MAP:
|
|
8
|
+
DEFAULT_ASSIGNMENT_MAP: dict[str, int] = {ACTIVE: 1, PLACEBO: 2}
|
|
9
|
+
DEFAULT_ASSIGNMENT_DESCRIPTION_MAP: dict[str, str] = {
|
|
12
10
|
ACTIVE: "Active",
|
|
13
11
|
PLACEBO: "Placebo",
|
|
14
12
|
}
|
|
@@ -72,7 +72,7 @@ class RandomizationListModelMixin(models.Model):
|
|
|
72
72
|
def save(self, *args, **kwargs):
|
|
73
73
|
self.randomizer_name = self.randomizer_cls.name
|
|
74
74
|
try:
|
|
75
|
-
|
|
75
|
+
self.assignment_description
|
|
76
76
|
except RandomizationError as e:
|
|
77
77
|
raise RandomizationListModelError(e)
|
|
78
78
|
try:
|
|
@@ -80,7 +80,7 @@ class RandomizationListModelMixin(models.Model):
|
|
|
80
80
|
except ObjectDoesNotExist:
|
|
81
81
|
site_names = [obj.name for obj in Site.objects.all()]
|
|
82
82
|
raise RandomizationListModelError(
|
|
83
|
-
f"Invalid site name. Got {self.site_name}.
|
|
83
|
+
f"Invalid site name. Got {self.site_name}. Expected one of {site_names}."
|
|
84
84
|
)
|
|
85
85
|
super().save(*args, **kwargs)
|
|
86
86
|
|
|
@@ -105,7 +105,7 @@ class RandomizationListModelMixin(models.Model):
|
|
|
105
105
|
return self.assignment
|
|
106
106
|
|
|
107
107
|
def natural_key(self):
|
|
108
|
-
return (self.sid,)
|
|
108
|
+
return (self.sid,)
|
|
109
109
|
|
|
110
110
|
class Meta:
|
|
111
111
|
abstract = True
|
|
@@ -153,18 +153,17 @@ class RandomizationListImporter:
|
|
|
153
153
|
f"File is empty. See {self.randomizer_name}. "
|
|
154
154
|
f"Got {self.randomizationlist_path} (1)."
|
|
155
155
|
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
156
|
+
index = 0
|
|
157
|
+
with self.randomizationlist_path.open(mode="r") as csvfile:
|
|
158
|
+
reader = csv.DictReader(csvfile)
|
|
159
|
+
for index, row in enumerate(reader):
|
|
160
|
+
if index == 0:
|
|
161
|
+
continue
|
|
162
|
+
if index == 0:
|
|
163
|
+
raise RandomizationListImportError(
|
|
164
|
+
f"File is empty. See {self.randomizer_name}. "
|
|
165
|
+
f"Got {self.randomizationlist_path} (2)."
|
|
166
|
+
)
|
|
168
167
|
|
|
169
168
|
def _raise_on_invalid_header(self):
|
|
170
169
|
with self.randomizationlist_path.open(mode="r") as csvfile:
|
|
@@ -174,7 +173,7 @@ class RandomizationListImporter:
|
|
|
174
173
|
for fieldname in self.required_csv_fieldnames:
|
|
175
174
|
if fieldname not in row:
|
|
176
175
|
raise RandomizationListImportError(
|
|
177
|
-
"Invalid header. Missing column
|
|
176
|
+
f"Invalid header. Missing column `{fieldname}`. Got {row}"
|
|
178
177
|
)
|
|
179
178
|
elif index == 1:
|
|
180
179
|
if self.dryrun:
|
|
@@ -286,7 +285,7 @@ class RandomizationListImporter:
|
|
|
286
285
|
raise InvalidAssignment(
|
|
287
286
|
f"Invalid assignment. Expected one of {list(assignment_map.keys())}. "
|
|
288
287
|
f"Got `{assignment}`. "
|
|
289
|
-
f"See randomizer `{self.randomizer_name}` {
|
|
288
|
+
f"See randomizer `{self.randomizer_name}` {self!r}. "
|
|
290
289
|
)
|
|
291
290
|
return assignment
|
|
292
291
|
|