clinicedc 2.0.7__py3-none-any.whl → 2.0.9__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.

Files changed (142) hide show
  1. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/METADATA +4 -3
  2. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/RECORD +136 -137
  3. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/WHEEL +1 -1
  4. edc_action_item/auths.py +37 -32
  5. edc_action_item/models/action_model_mixin.py +1 -2
  6. edc_action_item/models/signals.py +22 -23
  7. edc_action_item/site_action_items.py +5 -9
  8. edc_action_item/utils.py +3 -3
  9. edc_adverse_event/auths.py +55 -51
  10. edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +2 -4
  11. edc_appointment/auths.py +14 -10
  12. edc_appointment/creators/appointment_creator.py +1 -1
  13. edc_appointment/creators/appointments_creator.py +1 -1
  14. edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -3
  15. edc_appointment/model_mixins/appointment_model_mixin.py +31 -28
  16. edc_appointment/models/appointment.py +1 -1
  17. edc_appointment/utils.py +19 -24
  18. edc_auth/auth_objects/__init__.py +2 -20
  19. edc_auth/auth_objects/default_groups.py +13 -11
  20. edc_auth/auth_objects/default_roles.py +26 -24
  21. edc_auth/auth_updater/auth_updater.py +13 -2
  22. edc_auth/auth_updater/group_updater.py +12 -10
  23. edc_auth/auth_updater/role_updater.py +2 -2
  24. edc_auth/constants.py +10 -0
  25. edc_auth/import_users.py +3 -3
  26. edc_auth/migrations/0036_alter_userprofile_alternate_email_and_more.py +88 -0
  27. edc_auth/models/user_profile.py +14 -11
  28. edc_auth/site_auths.py +80 -67
  29. edc_consent/auths.py +18 -12
  30. edc_constants/constants.py +1 -0
  31. edc_crf/auths.py +5 -0
  32. edc_dashboard/auths.py +10 -6
  33. edc_dashboard/url_config.py +92 -83
  34. edc_dashboard/url_names.py +4 -4
  35. edc_dashboard/view_mixins/url_request_context_mixin.py +6 -5
  36. edc_data_manager/admin/data_query_admin.py +12 -11
  37. edc_data_manager/auths.py +37 -34
  38. edc_data_manager/rule/query_rule_wrapper.py +7 -7
  39. edc_export/archive_exporter.py +3 -2
  40. edc_export/auths.py +32 -28
  41. edc_export/model_exporter/model_exporter.py +4 -1
  42. edc_facility/auths.py +8 -3
  43. edc_facility/facility.py +8 -9
  44. edc_form_label/custom_label_condition.py +11 -8
  45. edc_form_label/form_label.py +1 -1
  46. edc_form_runners/auths.py +11 -6
  47. edc_form_validators/applicable_field_validator.py +7 -6
  48. edc_form_validators/base_form_validator.py +8 -9
  49. edc_form_validators/other_specify_field_validator.py +2 -8
  50. edc_form_validators/required_field_validator.py +19 -16
  51. edc_identifier/research_identifier.py +11 -10
  52. edc_identifier/simple_identifier.py +8 -2
  53. edc_lab/auths.py +26 -23
  54. edc_lab/lab/aliquot_creator.py +5 -8
  55. edc_lab/lab/primary_aliquot.py +14 -5
  56. edc_lab/migrations/0038_alter_aliquot_slug_alter_box_slug_alter_boxitem_slug_and_more.py +112 -0
  57. edc_lab/model_mixins/requisition/requisition_model_mixin.py +6 -8
  58. edc_lab/models/aliquot.py +2 -2
  59. edc_lab/models/manifest/manifest.py +2 -2
  60. edc_lab/models/manifest/manifest_item.py +1 -1
  61. edc_lab_dashboard/auths.py +16 -11
  62. edc_lab_results/calculate_missing.py +8 -8
  63. edc_lab_results/form_validator_mixins/blood_results_form_validator_mixin.py +2 -2
  64. edc_lab_results/get_summary.py +26 -25
  65. edc_lab_results/model_mixins/blood_result_model_mixin.py +2 -0
  66. edc_label/auths.py +6 -1
  67. edc_label/label_template.py +8 -8
  68. edc_list_data/load_model_data.py +3 -3
  69. edc_list_data/post_migrate_signals.py +1 -1
  70. edc_list_data/preload_data.py +2 -2
  71. edc_list_data/row.py +1 -1
  72. edc_list_data/site_list_data.py +6 -5
  73. edc_locator/auths.py +18 -13
  74. edc_metadata/auths.py +11 -7
  75. edc_metadata/metadata/metadata.py +1 -1
  76. edc_metadata/metadata_rules/crf/crf_rule.py +1 -1
  77. edc_metadata/metadata_rules/metadata_rule_evaluator.py +5 -3
  78. edc_metadata/metadata_rules/rule.py +2 -3
  79. edc_metadata/metadata_rules/rule_evaluator.py +1 -1
  80. edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +7 -4
  81. edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +5 -2
  82. edc_metadata/models/signals.py +10 -11
  83. edc_navbar/auths.py +18 -13
  84. edc_notification/auths.py +9 -4
  85. edc_notification/notification/graded_event_notification.py +2 -2
  86. edc_notification/notification/model_notification.py +3 -30
  87. edc_notification/notification/new_model_notification.py +1 -1
  88. edc_notification/notification/notification.py +1 -1
  89. edc_notification/notification/updated_model_notification.py +2 -2
  90. edc_offstudy/auths.py +12 -7
  91. edc_pdutils/df_exporters/csv_model_exporter.py +5 -2
  92. edc_pharmacy/auths.py +19 -15
  93. edc_pharmacy/models/medication/formulation.py +5 -7
  94. edc_pharmacy/prescribe/create_prescription.py +3 -3
  95. edc_pharmacy/utils/confirm_stock.py +1 -1
  96. edc_pharmacy/utils/confirm_stock_at_site.py +1 -1
  97. edc_pharmacy/views/confirmation_at_site_view.py +6 -9
  98. edc_prn/admin_site.py +5 -0
  99. edc_prn/prn.py +10 -11
  100. edc_prn/urls.py +11 -0
  101. edc_protocol_incident/action_items.py +4 -4
  102. edc_protocol_incident/auths.py +27 -20
  103. edc_pylabels/auths.py +6 -1
  104. edc_qareports/auths.py +11 -7
  105. edc_randomization/admin.py +30 -24
  106. edc_randomization/auths.py +12 -7
  107. edc_randomization/randomizer.py +22 -20
  108. edc_randomization/utils.py +17 -16
  109. edc_refusal/auths.py +7 -2
  110. edc_refusal/model_mixins.py +1 -1
  111. edc_registration/auths.py +28 -23
  112. edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +13 -4
  113. edc_registration/models/registered_subject.py +1 -1
  114. edc_reportable/utils/get_reference_range_collection.py +2 -3
  115. edc_reportable/utils/load_data.py +1 -1
  116. edc_review_dashboard/auths.py +23 -18
  117. edc_screening/age_evaluator.py +3 -3
  118. edc_screening/auths.py +35 -30
  119. edc_screening/eligibility.py +1 -1
  120. edc_screening/gender_evaluator.py +1 -1
  121. edc_screening/model_mixins/eligibility_model_mixin.py +0 -2
  122. edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
  123. edc_screening/screening_eligibility.py +2 -4
  124. edc_screening/utils.py +9 -9
  125. edc_search/generate_slug.py +26 -0
  126. edc_search/model_mixins.py +10 -21
  127. edc_sites/auths.py +8 -3
  128. edc_subject_dashboard/auths.py +27 -22
  129. edc_timepoint/apps.py +0 -21
  130. edc_unblinding/auths.py +9 -4
  131. edc_utils/__init__.py +3 -1
  132. edc_utils/show_urls.py +29 -2
  133. edc_visit_schedule/auths.py +6 -1
  134. edc_visit_schedule/site_visit_schedules.py +2 -2
  135. edc_visit_tracking/models/signals.py +2 -2
  136. edc_form_label/models.py +0 -0
  137. edc_search/constants.py +0 -1
  138. edc_search/models.py +0 -0
  139. edc_search/search_slug.py +0 -51
  140. edc_search/updater.py +0 -30
  141. edc_search/wsgi.py +0 -7
  142. {clinicedc-2.0.7.dist-info → clinicedc-2.0.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- import os
1
+ from pathlib import Path
2
2
  from string import Template
3
3
 
4
4
  from django.apps import apps as django_apps
@@ -19,27 +19,27 @@ class LabelTemplate:
19
19
  zpl_label_template = django_apps.get_model(
20
20
  "edc_label.zpllabeltemplates"
21
21
  ).objects.get(name=self.template_name)
22
- except ObjectDoesNotExist:
22
+ except ObjectDoesNotExist as e:
23
23
  if static_files_path:
24
- path = finders.find(os.path.join(static_files_path, template_name))
24
+ path = finders.find(str(Path(static_files_path) / template_name))
25
25
  else:
26
26
  app_config = django_apps.get_app_config("edc_label")
27
27
  try:
28
28
  path = app_config.label_templates[template_name]
29
- except KeyError:
29
+ except KeyError as e:
30
30
  raise LabelTemplateError(
31
31
  f"Invalid label template name. "
32
32
  f"Expected one of {list(app_config.label_templates.keys())}. "
33
33
  f"Got '{template_name}'. "
34
34
  f"See edc_label.app_config."
35
- )
36
- if not os.path.exists(path or ""):
35
+ ) from e
36
+ if not path or not Path(path).exists():
37
37
  raise LabelTemplateError(
38
38
  f"Invalid label template path. "
39
39
  f"Looking for template '{template_name}'. "
40
40
  f"Got {path}. See edc_label.app_config."
41
- )
42
- with open(path) as f:
41
+ ) from e
42
+ with Path(path).open("r") as f:
43
43
  self.template = f.read()
44
44
  else:
45
45
  self.template = zpl_label_template.zpl_data.strip()
@@ -1,3 +1,5 @@
1
+ import contextlib
2
+
1
3
  from django.apps import AppConfig
2
4
  from django.apps import apps as django_apps
3
5
  from django.core.exceptions import ObjectDoesNotExist
@@ -34,10 +36,8 @@ def load_model_data(model_data: dict, apps: AppConfig | None = None) -> int:
34
36
  try:
35
37
  obj = model.objects.get(**{unique_field: opts.get(unique_field)})
36
38
  except ObjectDoesNotExist:
37
- try:
39
+ with contextlib.suppress(IntegrityError):
38
40
  model.objects.create(**opts)
39
- except IntegrityError:
40
- pass
41
41
  else:
42
42
  for key, value in opts.items():
43
43
  setattr(obj, key, value)
@@ -6,7 +6,7 @@ style = color_style()
6
6
 
7
7
 
8
8
  def post_migrate_list_data(sender=None, **kwargs):
9
- from .site_list_data import get_autodiscover_enabled, site_list_data
9
+ from .site_list_data import get_autodiscover_enabled, site_list_data # noqa: PLC0415
10
10
 
11
11
  if get_autodiscover_enabled():
12
12
  sys.stdout.write(style.MIGRATE_HEADING("Updating list data:\n"))
@@ -11,7 +11,7 @@ style = color_style()
11
11
  class PreloadData:
12
12
  def __init__(
13
13
  self,
14
- list_data: dict[str, list[tuple[str | int, str]]] | None = None,
14
+ list_data: dict[str, list[tuple[str | int, str]]],
15
15
  model_data: dict | None = None,
16
16
  list_data_model_name: str | None = None,
17
17
  apps: AppConfig = None,
@@ -24,7 +24,7 @@ class PreloadData:
24
24
  if self.model_data:
25
25
  self.item_count += self.load_model_data()
26
26
 
27
- def load_list_data(self, model_name: str = None, apps: AppConfig | None = None) -> int:
27
+ def load_list_data(self, model_name: str, apps: AppConfig | None = None) -> int:
28
28
  return load_list_data(self.list_data, model_name=model_name, apps=apps)
29
29
 
30
30
  def load_model_data(self, apps: AppConfig | None = None) -> int:
edc_list_data/row.py CHANGED
@@ -24,7 +24,7 @@ class Row:
24
24
  @dataclass
25
25
  class AsListData:
26
26
  data: tuple[tuple[str, str, str], ...] | Choices
27
- rows: list[Row, ...] | None = field(default_factory=list, init=False)
27
+ rows: list[Row] | None = field(default_factory=list, init=False)
28
28
 
29
29
  def __post_init__(self):
30
30
  for tpl in self.data:
@@ -1,3 +1,4 @@
1
+ import contextlib
1
2
  import copy
2
3
  import sys
3
4
  from importlib import import_module
@@ -51,7 +52,7 @@ class SiteListData:
51
52
  if app_name and app_name in self.app_names:
52
53
  raise AlreadyLoaded(f"App already loaded. Got {app_name}.")
53
54
  self.app_names.append(app_name)
54
- opts = copy.deepcopy(self._get_options(module))
55
+ opts = {k: v for k, v in self._get_options(module).items()}
55
56
  sys.stdout.write(f" - registered {self.module_name} from '{module.__name__}'\n")
56
57
  if opts.get(self.module_name):
57
58
  self._replace_list_data_or_raise_on_duplicate(module, opts)
@@ -60,6 +61,8 @@ class SiteListData:
60
61
  def load_data(self) -> None:
61
62
  """Calls `load` class with each list_data dictionary module to
62
63
  update database `list` tables.
64
+
65
+ See post_migrate_list_data.
63
66
  """
64
67
  style = color_style()
65
68
  for module_name, opts in self.registry.items():
@@ -142,10 +145,8 @@ class SiteListData:
142
145
  ):
143
146
  sys.stdout.write(f" * checking sites for `{self.module_name}` ...\n")
144
147
  for app_name in django_apps.app_configs:
145
- try:
148
+ with contextlib.suppress(ModuleNotFoundError):
146
149
  self._import_and_register(app_name)
147
- except ModuleNotFoundError:
148
- pass
149
150
 
150
151
  def _import_and_register(self, app_name: str) -> None:
151
152
  mod = import_module(app_name)
@@ -155,7 +156,7 @@ class SiteListData:
155
156
  except ImportError as e:
156
157
  site_list_data.registry = before_import_registry
157
158
  if module_has_submodule(mod, self.module_name):
158
- raise SiteListDataError(f"{e} See {app_name}.{self.module_name}")
159
+ raise SiteListDataError(f"{e} See {app_name}.{self.module_name}") from e
159
160
  else:
160
161
  self.register(module, app_name=app_name)
161
162
 
edc_locator/auths.py CHANGED
@@ -1,17 +1,22 @@
1
1
  from edc_auth.constants import PII, PII_VIEW
2
2
  from edc_auth.site_auths import site_auths
3
3
 
4
- site_auths.update_group(
5
- "edc_locator.add_subjectlocator",
6
- "edc_locator.change_subjectlocator",
7
- "edc_locator.view_historicalsubjectlocator",
8
- "edc_locator.view_subjectlocator",
9
- name=PII,
10
- )
11
4
 
12
- site_auths.update_group(
13
- "edc_locator.view_historicalsubjectlocator",
14
- "edc_locator.view_subjectlocator",
15
- name=PII_VIEW,
16
- )
17
- site_auths.add_pii_model("edc_locator.subjectlocator")
5
+ def update_site_auths():
6
+ site_auths.update_group(
7
+ "edc_locator.add_subjectlocator",
8
+ "edc_locator.change_subjectlocator",
9
+ "edc_locator.view_historicalsubjectlocator",
10
+ "edc_locator.view_subjectlocator",
11
+ name=PII,
12
+ )
13
+
14
+ site_auths.update_group(
15
+ "edc_locator.view_historicalsubjectlocator",
16
+ "edc_locator.view_subjectlocator",
17
+ name=PII_VIEW,
18
+ )
19
+ site_auths.add_pii_model("edc_locator.subjectlocator")
20
+
21
+
22
+ update_site_auths()
edc_metadata/auths.py CHANGED
@@ -1,12 +1,16 @@
1
1
  from django.apps import apps as django_apps
2
2
 
3
3
  from edc_auth.site_auths import site_auths
4
+ from edc_export.constants import EXPORT
4
5
 
5
- if django_apps.is_installed("edc_export"):
6
- from edc_export.constants import EXPORT
7
6
 
8
- site_auths.update_group(
9
- "edc_metadata.export_crfmetadata",
10
- "edc_metadata.export_requisitionmetadata",
11
- name=EXPORT,
12
- )
7
+ def update_site_auths():
8
+ if django_apps.is_installed("edc_export"):
9
+ site_auths.update_group(
10
+ "edc_metadata.export_crfmetadata",
11
+ "edc_metadata.export_requisitionmetadata",
12
+ name=EXPORT,
13
+ )
14
+
15
+
16
+ update_site_auths()
@@ -110,7 +110,7 @@ class CrfCreator(SourceModelMetadataMixin):
110
110
  metadata_obj = self.metadata_model_cls.objects.create(**opts)
111
111
  except IntegrityError as e:
112
112
  msg = f"Integrity error creating. Tried with {opts}. Got {e}."
113
- raise CreatesMetadataError(msg)
113
+ raise CreatesMetadataError(msg) from e
114
114
  else:
115
115
  if not registered:
116
116
  metadata_obj.delete()
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
14
14
  pass
15
15
 
16
16
 
17
- class CrfRuleModelConflict(Exception):
17
+ class CrfRuleModelConflict(Exception): # noqa: N818
18
18
  pass
19
19
 
20
20
 
@@ -29,9 +29,11 @@ class MetadataRuleEvaluator:
29
29
  if not self.app_labels:
30
30
  for rule_groups in site_metadata_rules.registry.values():
31
31
  for rule_group in rule_groups:
32
- if rule_group._meta.related_visit_model == self.related_visit_model:
33
- if rule_group._meta.app_label not in self.app_labels:
34
- self.app_labels.append(rule_group._meta.app_label)
32
+ if (
33
+ rule_group._meta.related_visit_model == self.related_visit_model
34
+ and rule_group._meta.app_label not in self.app_labels
35
+ ):
36
+ self.app_labels.append(rule_group._meta.app_label)
35
37
 
36
38
  def evaluate_rules(self) -> None:
37
39
  for app_label in self.app_labels:
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from typing import TYPE_CHECKING
4
5
 
5
6
  from edc_appointment.constants import MISSED_APPT
@@ -91,8 +92,6 @@ class Rule:
91
92
  try:
92
93
  field_names = [self.predicate.attr]
93
94
  except AttributeError:
94
- try:
95
+ with contextlib.suppress(AttributeError):
95
96
  field_names = self.predicate.attrs
96
- except AttributeError:
97
- pass
98
97
  return field_names
@@ -77,5 +77,5 @@ class RuleEvaluator:
77
77
  f"Registered subject required for rule {self!r}. "
78
78
  f"subject_identifier='{self.related_visit.subject_identifier}'. "
79
79
  f"Got {e}."
80
- )
80
+ ) from e
81
81
  return self._registered_subject
@@ -50,16 +50,19 @@ class UpdatesCrfMetadataModelMixin(UpdatesMetadataModelMixin):
50
50
  return django_apps.get_model(metadata_model)
51
51
 
52
52
  @property
53
- def metadata_default_entry_status(self: CrfModel) -> str:
53
+ def metadata_default_entry_status(self: CrfModel) -> str | None:
54
54
  """Returns a string that represents the default entry status
55
55
  of the CRF in the visit schedule.
56
56
  """
57
57
  crfs_prn = self.metadata_visit_object.crfs_prn
58
58
  if self.related_visit.visit_code_sequence != 0:
59
- crfs = self.metadata_visit_object.crfs_unscheduled.forms + crfs_prn.forms
59
+ crfs = (*self.metadata_visit_object.crfs_unscheduled.forms, *crfs_prn.forms)
60
60
  else:
61
- crfs = self.metadata_visit_object.crfs.forms + crfs_prn.forms
62
- crf = next(c for c in crfs if c.model == self._meta.label_lower)
61
+ crfs = (*self.metadata_visit_object.crfs.forms, *crfs_prn.forms)
62
+ try:
63
+ crf = next(c for c in crfs if c.model == self._meta.label_lower)
64
+ except StopIteration:
65
+ return None
63
66
  return REQUIRED if crf.required else NOT_REQUIRED
64
67
 
65
68
  class Meta:
@@ -50,7 +50,7 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
50
50
  return options
51
51
 
52
52
  @property
53
- def metadata_default_entry_status(self: RequisitionModel) -> str:
53
+ def metadata_default_entry_status(self: RequisitionModel) -> str | None:
54
54
  """Returns a string that represents the configured
55
55
  entry status of the requisition in the visit schedule.
56
56
  """
@@ -64,7 +64,10 @@ class UpdatesRequisitionMetadataModelMixin(UpdatesMetadataModelMixin):
64
64
  requisitions = (
65
65
  self.metadata_visit_object.requisitions.forms + requisitions_prn.forms
66
66
  )
67
- requisition = next(r for r in requisitions if r.panel.name == self.panel.name)
67
+ try:
68
+ requisition = next(r for r in requisitions if r.panel.name == self.panel.name)
69
+ except StopIteration:
70
+ return None
68
71
  return REQUIRED if requisition.required else NOT_REQUIRED
69
72
 
70
73
  @property
@@ -94,14 +94,13 @@ def metadata_reset_on_post_delete(sender, instance, using, **kwargs) -> None:
94
94
  def metadata_update_previous_timepoints_for_singleton_on_post_save(
95
95
  sender, instance, raw, created, using, **kwargs
96
96
  ):
97
- if not raw and not kwargs.get("update_fields"):
98
- if isinstance(instance, (SingletonCrfModelMixin,)):
99
- appointment = (
100
- instance.related_visit.appointment.relative_previous_with_related_visit
101
- )
102
- while appointment:
103
- if appointment.related_visit:
104
- refresh_metadata_for_timepoint(
105
- appointment.related_visit, allow_create=False
106
- )
107
- appointment = appointment.relative_previous_with_related_visit
97
+ if (
98
+ not raw
99
+ and not kwargs.get("update_fields")
100
+ and isinstance(instance, (SingletonCrfModelMixin,))
101
+ ):
102
+ appointment = instance.related_visit.appointment.relative_previous_with_related_visit
103
+ while appointment:
104
+ if appointment.related_visit:
105
+ refresh_metadata_for_timepoint(appointment.related_visit, allow_create=False)
106
+ appointment = appointment.relative_previous_with_related_visit
edc_navbar/auths.py CHANGED
@@ -4,17 +4,22 @@ from edc_auth.utils import remove_default_model_permissions_from_edc_permissions
4
4
 
5
5
  from .auth_objects import custom_codename_tuples
6
6
 
7
- site_auths.add_post_update_func(
8
- "edc_navbar", remove_default_model_permissions_from_edc_permissions
9
- )
10
- site_auths.add_custom_permissions_tuples(
11
- model="edc_navbar.edcpermissions", codename_tuples=custom_codename_tuples
12
- )
13
7
 
14
- site_auths.update_group(
15
- "edc_navbar.nav_administration",
16
- "edc_navbar.nav_home",
17
- "edc_navbar.nav_logout",
18
- "edc_navbar.nav_public",
19
- name=EVERYONE,
20
- )
8
+ def update_site_auths():
9
+ site_auths.add_post_update_func(
10
+ "edc_navbar", remove_default_model_permissions_from_edc_permissions
11
+ )
12
+ site_auths.add_custom_permissions_tuples(
13
+ model="edc_navbar.edcpermissions", codename_tuples=custom_codename_tuples
14
+ )
15
+
16
+ site_auths.update_group(
17
+ "edc_navbar.nav_administration",
18
+ "edc_navbar.nav_home",
19
+ "edc_navbar.nav_logout",
20
+ "edc_navbar.nav_public",
21
+ name=EVERYONE,
22
+ )
23
+
24
+
25
+ update_site_auths()
edc_notification/auths.py CHANGED
@@ -6,7 +6,12 @@ from edc_export.constants import EXPORT
6
6
 
7
7
  from .auth_objects import NOTIFICATION, codenames
8
8
 
9
- site_auths.add_group(*codenames, name=NOTIFICATION)
10
- if django_apps.is_installed("edc_export"):
11
- site_auths.update_group("edc_notification.export_notification", name=EXPORT)
12
- site_auths.update_role(NOTIFICATION, name=ACCOUNT_MANAGER_ROLE)
9
+
10
+ def update_site_auths():
11
+ site_auths.add_group(*codenames, name=NOTIFICATION)
12
+ if django_apps.is_installed("edc_export"):
13
+ site_auths.update_group("edc_notification.export_notification", name=EXPORT)
14
+ site_auths.update_role(NOTIFICATION, name=ACCOUNT_MANAGER_ROLE)
15
+
16
+
17
+ update_site_auths()
@@ -4,8 +4,8 @@ from .model_notification import ModelNotification
4
4
  class GradedEventNotification(ModelNotification):
5
5
  grade: int | None = None
6
6
  model: str | None = None
7
- create_fields = ["ae_grade"]
8
- update_fields = ["ae_grade"]
7
+ create_fields: tuple[str] = ("ae_grade",)
8
+ update_fields: tuple[str] = ("ae_grade",)
9
9
 
10
10
  def field_value_condition_on_create(self, field, current_value):
11
11
  return str(current_value) == str(self.grade)
@@ -17,10 +17,10 @@ class ModelNotification(Notification):
17
17
 
18
18
  model: str | None = None # label_lower format
19
19
 
20
- model_operations = [CREATE, UPDATE, DELETE]
20
+ model_operations: tuple[str] = (CREATE, UPDATE, DELETE)
21
21
 
22
- create_fields = ["created"]
23
- update_fields = ["modified"]
22
+ create_fields: tuple[str] = ("created",)
23
+ update_fields: tuple[str] = ("modified",)
24
24
 
25
25
  email_body_template: str = (
26
26
  "\n\nDo not reply to this email\n\n"
@@ -177,30 +177,3 @@ class ModelNotification(Notification):
177
177
  )
178
178
 
179
179
  return opts
180
-
181
- # @property
182
- # def test_template_options(self) -> dict:
183
- # class Site:
184
- # domain = "gaborone.example.com"
185
- # name = "gaborone"
186
- # id = 99
187
- #
188
- # class Meta:
189
- # label_lower = self.model
190
- #
191
- # class DummyHistory:
192
- # def __init__(self, objects):
193
- # self._objects = objects
194
- #
195
- # def all(self):
196
- # return self._objects
197
- #
198
- # class DummyInstance:
199
- # id = 99
200
- # subject_identifier = "123456910"
201
- # site = Site()
202
- # _meta = Meta()
203
- #
204
- # instance = DummyInstance()
205
- # instance.history = DummyHistory(objects=[instance])
206
- # return dict(instance=instance)
@@ -3,4 +3,4 @@ from .model_notification import ModelNotification
3
3
 
4
4
 
5
5
  class NewModelNotification(ModelNotification):
6
- model_operations = [CREATE]
6
+ model_operations = (CREATE,)
@@ -214,7 +214,7 @@ class Notification:
214
214
 
215
215
  def send_email(
216
216
  self,
217
- email_to: list[str],
217
+ email_to: list[str] | None = None,
218
218
  fail_silently: bool | None = None,
219
219
  email_body_template: str | None = None,
220
220
  **kwargs,
@@ -3,9 +3,9 @@ from .model_notification import ModelNotification
3
3
 
4
4
 
5
5
  class UpdatedModelNotification(ModelNotification):
6
- model_operations = [UPDATE]
6
+ model_operations = (UPDATE,)
7
7
 
8
- update_fields = ["modified"]
8
+ update_fields = ("modified",)
9
9
 
10
10
  email_subject_template = (
11
11
  "*UPDATE* {test_subject_line}{protocol_name}: "
edc_offstudy/auths.py CHANGED
@@ -8,10 +8,15 @@ from edc_auth.site_auths import site_auths
8
8
 
9
9
  from .auth_objects import OFFSTUDY, OFFSTUDY_SUPER, OFFSTUDY_VIEW, codenames
10
10
 
11
- site_auths.add_group(*codenames, name=OFFSTUDY, no_delete=True)
12
- site_auths.add_group(*codenames, name=OFFSTUDY_SUPER)
13
- site_auths.add_group(*codenames, name=OFFSTUDY_VIEW, view_only=True)
14
- site_auths.update_role(OFFSTUDY, name=CLINICIAN_ROLE)
15
- site_auths.update_role(OFFSTUDY, name=NURSE_ROLE)
16
- site_auths.update_role(OFFSTUDY_SUPER, name=CLINICIAN_SUPER_ROLE)
17
- site_auths.update_role(OFFSTUDY_VIEW, name=AUDITOR_ROLE)
11
+
12
+ def update_site_auths():
13
+ site_auths.add_group(*codenames, name=OFFSTUDY, no_delete=True)
14
+ site_auths.add_group(*codenames, name=OFFSTUDY_SUPER)
15
+ site_auths.add_group(*codenames, name=OFFSTUDY_VIEW, view_only=True)
16
+ site_auths.update_role(OFFSTUDY, name=CLINICIAN_ROLE)
17
+ site_auths.update_role(OFFSTUDY, name=NURSE_ROLE)
18
+ site_auths.update_role(OFFSTUDY_SUPER, name=CLINICIAN_SUPER_ROLE)
19
+ site_auths.update_role(OFFSTUDY_VIEW, name=AUDITOR_ROLE)
20
+
21
+
22
+ update_site_auths()
@@ -30,6 +30,7 @@ class CsvModelExporter:
30
30
  **kwargs,
31
31
  ):
32
32
  self.model = model or queryset.model._meta.label_lower
33
+ self.export_folder = export_folder
33
34
  self.df_maker = self.df_maker_cls(
34
35
  model=model,
35
36
  queryset=queryset,
@@ -46,8 +47,10 @@ class CsvModelExporter:
46
47
 
47
48
  def to_csv(self):
48
49
  dataframe = self.df_maker.dataframe
49
- return self.csv_exporter.to_csv(dataframe=dataframe)
50
+ return self.csv_exporter.to_csv(dataframe=dataframe, export_folder=self.export_folder)
50
51
 
51
52
  def to_stata(self):
52
53
  dataframe = self.df_maker.dataframe
53
- return self.csv_exporter.to_stata(dataframe=dataframe)
54
+ return self.csv_exporter.to_stata(
55
+ dataframe=dataframe, export_folder=self.export_folder
56
+ )
edc_pharmacy/auths.py CHANGED
@@ -18,21 +18,25 @@ from .auth_objects import (
18
18
  prescriber_codenames,
19
19
  )
20
20
 
21
- site_auths.add_post_update_func(
22
- "edc_pharmacy", remove_default_model_permissions_from_edc_permissions
23
- )
24
- site_auths.add_custom_permissions_tuples(
25
- model="edc_pharmacy.edcpermissions", codename_tuples=navbar_tuples
26
- )
27
21
 
28
- site_auths.add_group(*pharmacy_codenames, name=PHARMACY_VIEW, view_only=True)
29
- site_auths.add_group(*prescriber_codenames, name=PHARMACY_PRESCRIBER, no_delete=True)
30
- site_auths.add_group(*pharmacy_site_codenames, name=PHARMACY_SITE, no_delete=False)
31
- site_auths.add_group(*pharmacy_codenames, name=PHARMACY, no_delete=False)
22
+ def update_site_auths() -> None:
23
+ site_auths.add_post_update_func(
24
+ "edc_pharmacy", remove_default_model_permissions_from_edc_permissions
25
+ )
26
+ site_auths.add_custom_permissions_tuples(
27
+ model="edc_pharmacy.edcpermissions", codename_tuples=navbar_tuples
28
+ )
29
+
30
+ site_auths.add_group(*pharmacy_codenames, name=PHARMACY_VIEW, view_only=True)
31
+ site_auths.add_group(*prescriber_codenames, name=PHARMACY_PRESCRIBER, no_delete=True)
32
+ site_auths.add_group(*pharmacy_site_codenames, name=PHARMACY_SITE, no_delete=False)
33
+ site_auths.add_group(*pharmacy_codenames, name=PHARMACY, no_delete=False)
34
+
35
+ site_auths.add_role(PHARMACY_VIEW, name=PHARMACY_AUDITOR_ROLE)
36
+ site_auths.add_role(PHARMACY_PRESCRIBER, name=PHARMACY_PRESCRIBER_ROLE)
37
+ site_auths.add_role(PHARMACY_SITE, PYLABELS, name=SITE_PHARMACIST_ROLE)
38
+ site_auths.add_role(PHARMACY, PYLABELS, name=PHARMACIST_ROLE)
39
+ site_auths.add_role(PHARMACY, name=PHARMACY_SUPER_ROLE)
32
40
 
33
41
 
34
- site_auths.add_role(PHARMACY_VIEW, name=PHARMACY_AUDITOR_ROLE)
35
- site_auths.add_role(PHARMACY_PRESCRIBER, name=PHARMACY_PRESCRIBER_ROLE)
36
- site_auths.add_role(PHARMACY_SITE, PYLABELS, name=SITE_PHARMACIST_ROLE)
37
- site_auths.add_role(PHARMACY, PYLABELS, name=PHARMACIST_ROLE)
38
- site_auths.add_role(PHARMACY, name=PHARMACY_SUPER_ROLE)
42
+ update_site_auths()
@@ -61,18 +61,17 @@ class Formulation(BaseUuidModel):
61
61
  def save(self, *args, **kwargs):
62
62
  self.description = self.get_description()
63
63
  if not self.imp:
64
- self.imp_description = None
64
+ self.imp_description = ""
65
65
  else:
66
66
  self.imp_description = (
67
67
  self.imp_description if self.imp_description else self.description
68
68
  )
69
69
  super().save(*args, **kwargs)
70
70
 
71
- def get_description(self):
71
+ def get_description(self) -> str:
72
72
  return (
73
73
  f"{self.medication} {round_half_away_from_zero(self.strength, 0)}"
74
74
  f"{self.get_units_display()} "
75
- # f"{self.get_formulation_type_display()} "
76
75
  f"{self.get_route_display()}"
77
76
  )
78
77
 
@@ -80,7 +79,6 @@ class Formulation(BaseUuidModel):
80
79
  return (
81
80
  f"{self.medication} {round_half_away_from_zero(self.strength, 0)}"
82
81
  f"{self.get_units_display()} "
83
- # f"{self.get_formulation_type_display()} "
84
82
  )
85
83
 
86
84
  def get_description_with_assignment(self, assignment: Assignment) -> str:
@@ -103,9 +101,9 @@ class Formulation(BaseUuidModel):
103
101
  class Meta(BaseUuidModel.Meta):
104
102
  verbose_name = "Formulation"
105
103
  verbose_name_plural = "Formulations"
106
- constraints = [
104
+ constraints = (
107
105
  UniqueConstraint(
108
106
  fields=["medication", "strength", "units", "formulation_type"],
109
107
  name="%(app_label)s_%(class)s_med_stren_uniq",
110
- )
111
- ]
108
+ ),
109
+ )
@@ -30,11 +30,11 @@ def create_prescription(
30
30
  for medication_name in medication_names:
31
31
  try:
32
32
  obj = medication_model_cls.objects.get(name__iexact=medication_name)
33
- except ObjectDoesNotExist:
33
+ except ObjectDoesNotExist as e:
34
34
  raise PrescriptionError(
35
35
  "Unable to create prescription. Medication does not exist. "
36
36
  f"Got {medication_name}"
37
- )
37
+ ) from e
38
38
  else:
39
39
  medications.append(obj)
40
40
  try:
@@ -51,7 +51,7 @@ def create_prescription(
51
51
  try:
52
52
  rx = rx_model_cls.objects.create(**opts)
53
53
  except ObjectDoesNotExist as e:
54
- raise CommandError(f"Site does not exists. site_id={site_id}. Got {e}")
54
+ raise CommandError(f"Site does not exists. site_id={site_id}. Got {e}") from e
55
55
  for obj in medications:
56
56
  rx.medications.add(obj)
57
57
  else:
@@ -15,7 +15,7 @@ def confirm_stock(
15
15
  stock_codes: list[str],
16
16
  fk_attr: str | None = None,
17
17
  confirmed_by: str | None = None,
18
- user_created: str = None,
18
+ user_created: str | None = None,
19
19
  ) -> tuple[list[str], list[str], list[str]]:
20
20
  """Confirm stock instances given a list of stock codes
21
21
  and a request/receive pk.