meta-edc 0.3.26__py3-none-any.whl → 0.3.28__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.
Files changed (25) hide show
  1. meta_dashboard/views/subject/dashboard/dashboard_view.py +1 -0
  2. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/METADATA +3 -2
  3. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/RECORD +25 -15
  4. meta_reports/admin/__init__.py +1 -0
  5. meta_reports/admin/unmanaged/__init__.py +4 -1
  6. meta_reports/admin/unmanaged/missing_screening_ogtt_admin/__init__.py +2 -0
  7. meta_reports/admin/unmanaged/missing_screening_ogtt_admin/note_model_admin.py +53 -0
  8. meta_reports/admin/unmanaged/{missing_screening_ogtt_admin.py → missing_screening_ogtt_admin/unmanaged_model_admin.py} +32 -7
  9. meta_reports/forms/__init__.py +1 -0
  10. meta_reports/forms/missing_ogtt_note_form.py +33 -0
  11. meta_reports/migrations/0034_auto_20240823_1642.py +54 -0
  12. meta_reports/migrations/0035_historicalmissingogttnote_missingogttnote.py +457 -0
  13. meta_reports/migrations/0036_historicalmissingogttnote_fasting_and_more.py +86 -0
  14. meta_reports/migrations/0037_historicalmissingogttnote_result_status_and_more.py +51 -0
  15. meta_reports/migrations/0038_alter_historicalmissingogttnote_fasting_and_more.py +33 -0
  16. meta_reports/models/__init__.py +2 -0
  17. meta_reports/models/dbviews/__init__.py +1 -1
  18. meta_reports/models/dbviews/missing_screening_ogtt/__init__.py +1 -0
  19. meta_reports/models/dbviews/missing_screening_ogtt/note_model.py +57 -0
  20. meta_reports/models/dbviews/missing_screening_ogtt/unmanaged_model.py +0 -2
  21. meta_reports/models/dbviews/missing_screening_ogtt/view_definition.py +6 -6
  22. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/AUTHORS +0 -0
  23. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/LICENSE +0 -0
  24. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/WHEEL +0 -0
  25. {meta_edc-0.3.26.dist-info → meta_edc-0.3.28.dist-info}/top_level.txt +0 -0
@@ -17,6 +17,7 @@ class DashboardView(SubjectDashboardView):
17
17
  history_button_label = _("Audit")
18
18
 
19
19
  def get_context_data(self, **kwargs) -> dict[str, Any]:
20
+ """Add message if subject reaches DM Endpoint."""
20
21
  context = super().get_context_data(**kwargs)
21
22
  try:
22
23
  Endpoints.objects.get(subject_identifier=self.subject_identifier)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meta-edc
3
- Version: 0.3.26
3
+ Version: 0.3.28
4
4
  Summary: META Trial EDC (http://www.isrctn.com/ISRCTN76157257)
5
5
  Home-page: https://github.com/meta-trial/meta-edc
6
6
  Author: Erik van Widenfelt
@@ -10,6 +10,7 @@ Keywords: django edc META EDC,clinicedc,clinical trials
10
10
  Classifier: Environment :: Web Environment
11
11
  Classifier: Framework :: Django
12
12
  Classifier: Framework :: Django :: 4.2
13
+ Classifier: Framework :: Django :: 5.1
13
14
  Classifier: Intended Audience :: Developers
14
15
  Classifier: Intended Audience :: Science/Research
15
16
  Classifier: Operating System :: OS Independent
@@ -19,7 +20,7 @@ Requires-Python: >=3.12
19
20
  Description-Content-Type: text/x-rst
20
21
  License-File: LICENSE
21
22
  License-File: AUTHORS
22
- Requires-Dist: edc ==0.6.3
23
+ Requires-Dist: edc ==0.6.5
23
24
  Requires-Dist: edc-microscopy
24
25
  Requires-Dist: beautifulsoup4
25
26
  Requires-Dist: edc-analytics
@@ -204,7 +204,7 @@ meta_dashboard/views/screening/__init__.py,sha256=bjnvZdqdonOW_DPFs1MR2GQv2x4oqA
204
204
  meta_dashboard/views/screening/listboard_view.py,sha256=DF42XR3ftCHO4vd3cuxtq5ir2NqOmQHGQV6cGObr7is,578
205
205
  meta_dashboard/views/subject/__init__.py,sha256=s7l0halOqlqscWdMD_r98V4VKeEsgchhY3eAgs0EI3k,81
206
206
  meta_dashboard/views/subject/dashboard/__init__.py,sha256=1961drFBW7OVhglUAL4OGFxLOU0tN3jOGMvO7Oone8U,42
207
- meta_dashboard/views/subject/dashboard/dashboard_view.py,sha256=FBgOQpZAkTOSeZtUnKRc_CayACOTMeeH6lmwNvEG9vQ,1330
207
+ meta_dashboard/views/subject/dashboard/dashboard_view.py,sha256=inFSVxZDnNghGJlEYIVK0Xy3mtSmPUEoJWj4D6yYU3o,1388
208
208
  meta_dashboard/views/subject/listboard/__init__.py,sha256=WHtaM6dXPN7vtnROBOutMO9zEBVIkx3T0RPJdbq4sq4,49
209
209
  meta_dashboard/views/subject/listboard/listboard_view.py,sha256=bQ5fPqbbkVvPNrmNvIGNZ_GoRIkqUFHk6T7p1dkjr_Y,601
210
210
  meta_data_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -433,18 +433,22 @@ meta_reports/death_report.py,sha256=mJw08jMxOWIAtsKQN8zCP64HIrxW1fb48D9OXqEMpM0,
433
433
  meta_reports/pdf_report.py,sha256=9lWnRc_BNieDFXC4VgA0QHK6g_ct58u8tupPRsbRvV0,2395
434
434
  meta_reports/tasks.py,sha256=wPzfm99Hox1AIvt1KxYg1r6g9v8ta2AHOHesoN6AxEk,370
435
435
  meta_reports/urls.py,sha256=_c6AMrK4U5MsyFSU2JSPNfiZlbhuw24C7CyPNYpqGd0,206
436
- meta_reports/admin/__init__.py,sha256=dpLUWcQsAUP-Mfx-eehY9Zegia9qp0uF82iwIdJCego,313
436
+ meta_reports/admin/__init__.py,sha256=OOVq30qqC5s7SVkWGO-AVHVOeyIrCXcfBGcpahAOrIo,344
437
437
  meta_reports/admin/endpoints_admin.py,sha256=L4mDLNIkR6203RI3ImBYel2kevaH_Nwa6KMYuoDwyNU,518
438
438
  meta_reports/admin/endpoints_all_admin.py,sha256=_czdezZf9fSjwZJmO-3HbmdXrXP6nk8-HdiweqUzPRE,476
439
439
  meta_reports/admin/list_filters.py,sha256=pByBzz9-qGwqBfdZFlByxJUZDTJQXOTWfNLjmK8KF00,870
440
440
  meta_reports/admin/modeladmin_mixins.py,sha256=YgzUjGlzG5S3i8daVaexBpZ8WAEPBjlhktsRIvOkyo4,3752
441
- meta_reports/admin/unmanaged/__init__.py,sha256=df6FzJNWtCS7KxPqxZ5cYWImRS3wUuXujJ9Tch57hr4,431
441
+ meta_reports/admin/unmanaged/__init__.py,sha256=56wDkwrBBt-50mQNHK9UiIRZAOBpsKk4Ui5PwWvb-Nk,471
442
442
  meta_reports/admin/unmanaged/glucose_summary_admin.py,sha256=NqnDKx5l0Q-2xxRGdnf50DaRX1Sn8-DC0jHSk96LkRo,4005
443
- meta_reports/admin/unmanaged/missing_screening_ogtt_admin.py,sha256=rpT5j4DsIB_475B2joeDwCd3kOtgiMR0eJBroVwIFGA,1836
444
443
  meta_reports/admin/unmanaged/patient_history_missing_baseline_cd4_admin.py,sha256=XyroEqxlX816xrdi0IVSelNPxPBWi96niKa-MQVGkck,2058
445
444
  meta_reports/admin/unmanaged/unattended_three_in_row2_admin.py,sha256=0r13KRr0gsH2osmSlffRV11SpSBpWM612AYl-GOoYm0,1347
446
445
  meta_reports/admin/unmanaged/unattended_three_in_row_admin.py,sha256=i_-H3pMXRjssf8jFw8gJ8_b0FaM4tX2AI4xfHQDyssU,1121
447
446
  meta_reports/admin/unmanaged/unattended_two_in_row_admin.py,sha256=tbnga05uTMgdK8ULsKYV7IuyhyPe0e-JpbMm6etU6Pk,1062
447
+ meta_reports/admin/unmanaged/missing_screening_ogtt_admin/__init__.py,sha256=UZMypicuUuZ2jD2Rf0hcDUi-oBUf7_3fgyV5KacIJtE,117
448
+ meta_reports/admin/unmanaged/missing_screening_ogtt_admin/note_model_admin.py,sha256=RcksbJ9HinK71iN9Pr_xMp-voj17pBqjLZpfMTJ7300,1357
449
+ meta_reports/admin/unmanaged/missing_screening_ogtt_admin/unmanaged_model_admin.py,sha256=1pWmmqk6LADkPWqyxcS8nwC-KwMjuN8O7fmG_AbcA3g,2644
450
+ meta_reports/forms/__init__.py,sha256=F5AIupgum2dWL1_HqQCjLkrpMzjQ2dbo065hlkXBTNY,56
451
+ meta_reports/forms/missing_ogtt_note_form.py,sha256=nUG5uNqE-U6KtY9m-S48bricyERKHd95GkWn364oT1Q,1445
448
452
  meta_reports/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
449
453
  meta_reports/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
450
454
  meta_reports/management/commands/generate_endpoints.py,sha256=LcQyLb5cinYUq9kmUTZOin08-sP_hzM7ve8chEq9M1I,348
@@ -481,18 +485,24 @@ meta_reports/migrations/0030_auto_20240822_1637.py,sha256=StGg7ifQj7fF18BC0j_lGl
481
485
  meta_reports/migrations/0031_endpointsproxy.py,sha256=8yeRLRF6j0nWpNZxlsDc-V7qlUubOz7z9fSqQm6XBQM,629
482
486
  meta_reports/migrations/0032_alter_endpointsproxy_options.py,sha256=07lWYABDDwKtd3rmPmNoRhu3y7Q-J74HUTq9kLx-jV4,539
483
487
  meta_reports/migrations/0033_auto_20240823_0012.py,sha256=4N1fvwzjt2EWaXrqjt3vL47OvfuHxC7G9pPzR2OKiJo,8777
488
+ meta_reports/migrations/0034_auto_20240823_1642.py,sha256=XcuAbFJ8tokZSE6o_iXBxvqAncFehCQE7s_afJjl3Qo,6601
489
+ meta_reports/migrations/0035_historicalmissingogttnote_missingogttnote.py,sha256=Pv2J7K9Auw1Y_gFKvkdL05i-hO_8xMT3xa-6K5aA6do,18235
490
+ meta_reports/migrations/0036_historicalmissingogttnote_fasting_and_more.py,sha256=sr0xr1MslT-jXD_mNr59IY3R1QPcq5Bpa6vc67VHBY4,3167
491
+ meta_reports/migrations/0037_historicalmissingogttnote_result_status_and_more.py,sha256=FdGYLdu4zTxoDO9PCI2wlDS43fxfNGtynCKYABs5cAg,1600
492
+ meta_reports/migrations/0038_alter_historicalmissingogttnote_fasting_and_more.py,sha256=-HljbT033-M0XZanZfcEA8jXmW3vhLoigdEC3hE5jOs,1010
484
493
  meta_reports/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
485
- meta_reports/models/__init__.py,sha256=8GgaeJKDmpAmG2oaEdKgVk6afynblRFT3zzpw9_9nTM,263
494
+ meta_reports/models/__init__.py,sha256=mdSLwykOHh_Y9QSS9HeTes8SDie_Y4aRU8yYXehTjpA,303
486
495
  meta_reports/models/endpoints.py,sha256=HfxQuKkzu883xbTeVfIEktN-ARoGe1qC6KgEAzRtV5E,925
487
496
  meta_reports/models/endpoints_proxy.py,sha256=3h3JGaPmnJedFPVWGrk4idV4YtdihqiCI5W7QomwG4E,318
488
497
  meta_reports/models/dbviews/README,sha256=GdeboB7Xqb-Qo_rE1ondcxP8Ps93MIjLzCePQCePig0,686
489
- meta_reports/models/dbviews/__init__.py,sha256=ZbOD_j45lHNKJG7HZ7G3BL4hPLNbKVOsc8PhFQoxT9g,356
498
+ meta_reports/models/dbviews/__init__.py,sha256=tE_j0PeMl-r97-KNATY9tdQYiw0sNH39s8E2PAyk2Q4,388
490
499
  meta_reports/models/dbviews/glucose_summary/__init__.py,sha256=Tfgjso4TpxMCidAdRJp8g1Ykqqa43yWlQSotzAaORAo,93
491
500
  meta_reports/models/dbviews/glucose_summary/unmanaged_model.py,sha256=uqhD5JLXtyQ94zpG5Puxodjzli3quE4GgiFi7en6EeM,1055
492
501
  meta_reports/models/dbviews/glucose_summary/view_definition.py,sha256=f_lUpaKRMa1Z0y8Y7DPB2qbHEPeEXBDXr2FnM5yD55s,4029
493
- meta_reports/models/dbviews/missing_screening_ogtt/__init__.py,sha256=_-Sn6ArPFRyzYLA6WfGGzvq5UpYWExgTo2ELPVA_OGc,50
494
- meta_reports/models/dbviews/missing_screening_ogtt/unmanaged_model.py,sha256=68erN-uo085xBYekgPncNO-ZffGJ7gefdsh15Mk_yCM,1406
495
- meta_reports/models/dbviews/missing_screening_ogtt/view_definition.py,sha256=YBadEjekt58qKdLAQbDxPS9d6QPAIpcj6bAvKo-ERFA,2584
502
+ meta_reports/models/dbviews/missing_screening_ogtt/__init__.py,sha256=7uX_2HfVU-3G1O_xecfGnE5_pcX5eceiRaapeDQtzIw,105
503
+ meta_reports/models/dbviews/missing_screening_ogtt/note_model.py,sha256=JHItVS11WDFg3lTe7ayH9T7Epd-VvXXD_79FssrmAwc,1813
504
+ meta_reports/models/dbviews/missing_screening_ogtt/unmanaged_model.py,sha256=_XFzpOiMoxr-F0ebKNEnyXqS6dnb3P5tBRnZlbrQ2x8,1346
505
+ meta_reports/models/dbviews/missing_screening_ogtt/view_definition.py,sha256=2f7A0VLcb5ZGee8Us3SPLeExayCTXrGlCHt3j7072HY,2638
496
506
  meta_reports/models/dbviews/patient_history_missing_baseline_cd4/__init__.py,sha256=eIyKY9mdIE3-J2vtQtY7MOg_RQkwKj7aserwO5PBNQQ,62
497
507
  meta_reports/models/dbviews/patient_history_missing_baseline_cd4/unmanaged_model.py,sha256=7TbSZJLdprIO8FvVq5a_MUXgiEvqAZ5NrirQVfH8Fnk,957
498
508
  meta_reports/models/dbviews/patient_history_missing_baseline_cd4/view_definition.py,sha256=AlraPbMyJ_bdfb2sBPbNT-CP_dUK8nSFh9bH1j3DTmw,2066
@@ -1089,9 +1099,9 @@ tests/etc/user-rsa-restricted-private.pem,sha256=CUcHW9bznWdmmASN00hCzvxFPAFl4N2
1089
1099
  tests/etc/user-rsa-restricted-public.pem,sha256=mt84djoL-uHw6Wc5SJh0zml6VzXulnf8eQSFg7-fheg,450
1090
1100
  tests/etc/user-salt-local.key,sha256=x5anBw9fvbHurczouT3CjrkWb_xs7Ypm1htIJsgiuiw,256
1091
1101
  tests/etc/user-salt-restricted.key,sha256=pxmpcfBRNB-4C6wTvHXz-9fOfJgKIFOjaAF8ZFfa4q4,256
1092
- meta_edc-0.3.26.dist-info/AUTHORS,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1093
- meta_edc-0.3.26.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1094
- meta_edc-0.3.26.dist-info/METADATA,sha256=G4yeTPysQ4wOftmA6bRMObIYXwqjSanz2r6NCLcOAs0,2771
1095
- meta_edc-0.3.26.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
1096
- meta_edc-0.3.26.dist-info/top_level.txt,sha256=RkzjNXwRq2kg_uZ_1bDwPUntijSXoY2YBqtByDwvvrc,244
1097
- meta_edc-0.3.26.dist-info/RECORD,,
1102
+ meta_edc-0.3.28.dist-info/AUTHORS,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1103
+ meta_edc-0.3.28.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1104
+ meta_edc-0.3.28.dist-info/METADATA,sha256=01QtxWazwkpcrqvp_W9kznFwV9OEdJQyfrztvCcVGUI,2810
1105
+ meta_edc-0.3.28.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
1106
+ meta_edc-0.3.28.dist-info/top_level.txt,sha256=RkzjNXwRq2kg_uZ_1bDwPUntijSXoY2YBqtByDwvvrc,244
1107
+ meta_edc-0.3.28.dist-info/RECORD,,
@@ -2,6 +2,7 @@ from .endpoints_admin import EndpointsAdmin
2
2
  from .endpoints_all_admin import EndpointsAllAdmin
3
3
  from .unmanaged import (
4
4
  GlucoseSummaryAdmin,
5
+ MissingOgttNoteModelAdmin,
5
6
  MissingScreeningOgttAdmin,
6
7
  PatientHistoryMissingBaselineCd4Admin,
7
8
  UnattendedThreeInRow2Admin,
@@ -1,5 +1,8 @@
1
1
  from .glucose_summary_admin import GlucoseSummaryAdmin
2
- from .missing_screening_ogtt_admin import MissingScreeningOgttAdmin
2
+ from .missing_screening_ogtt_admin import (
3
+ MissingOgttNoteModelAdmin,
4
+ MissingScreeningOgttAdmin,
5
+ )
3
6
  from .patient_history_missing_baseline_cd4_admin import (
4
7
  PatientHistoryMissingBaselineCd4Admin,
5
8
  )
@@ -0,0 +1,2 @@
1
+ from .note_model_admin import MissingOgttNoteModelAdmin
2
+ from .unmanaged_model_admin import MissingScreeningOgttAdmin
@@ -0,0 +1,53 @@
1
+ from django.contrib import admin
2
+ from django_audit_fields import audit_fieldset_tuple
3
+ from edc_qareports.modeladmin_mixins import NoteModelAdminMixin
4
+
5
+ from ....admin_site import meta_reports_admin
6
+ from ....forms import MissingOgttNoteForm
7
+ from ....models import MissingOgttNote
8
+
9
+
10
+ @admin.register(MissingOgttNote, site=meta_reports_admin)
11
+ class MissingOgttNoteModelAdmin(
12
+ NoteModelAdminMixin,
13
+ admin.ModelAdmin,
14
+ ):
15
+ """A modeladmin class for the Note model."""
16
+
17
+ form = MissingOgttNoteForm
18
+ note_template_name = "edc_qareports/qa_report_note.html"
19
+
20
+ fieldsets = (
21
+ (
22
+ None,
23
+ {"fields": ("subject_identifier", "report_datetime", "result_status")},
24
+ ),
25
+ (
26
+ "OGTT",
27
+ {
28
+ "fields": (
29
+ "fasting",
30
+ "ogtt_base_datetime",
31
+ "ogtt_datetime",
32
+ "ogtt_value",
33
+ "ogtt_units",
34
+ )
35
+ },
36
+ ),
37
+ (
38
+ "Note",
39
+ {
40
+ "fields": (
41
+ "note",
42
+ "report_model",
43
+ )
44
+ },
45
+ ),
46
+ audit_fieldset_tuple,
47
+ )
48
+
49
+ radio_fields = {
50
+ "ogtt_units": admin.VERTICAL,
51
+ "result_status": admin.VERTICAL,
52
+ "fasting": admin.VERTICAL,
53
+ }
@@ -4,12 +4,19 @@ from django.urls import reverse
4
4
  from django.utils.translation import gettext as _
5
5
  from edc_model_admin.dashboard import ModelAdminDashboardMixin
6
6
  from edc_model_admin.mixins import TemplatesModelAdminMixin
7
+ from edc_qareports.modeladmin_mixins import (
8
+ NoteStatusListFilter as BaseNoteStatusListFilter,
9
+ )
7
10
  from edc_qareports.modeladmin_mixins import QaReportModelAdminMixin
8
11
  from edc_sites.admin import SiteModelAdminMixin
9
12
  from edc_visit_schedule.admin import ScheduleStatusListFilter
10
13
 
11
- from ...admin_site import meta_reports_admin
12
- from ...models import MissingScreeningOgtt
14
+ from ....admin_site import meta_reports_admin
15
+ from ....models import NOTE_STATUSES, MissingScreeningOgtt
16
+
17
+
18
+ class NoteStatusListFilter(BaseNoteStatusListFilter):
19
+ note_model_status_choices = NOTE_STATUSES
13
20
 
14
21
 
15
22
  @admin.register(MissingScreeningOgtt, site=meta_reports_admin)
@@ -20,13 +27,19 @@ class MissingScreeningOgttAdmin(
20
27
  TemplatesModelAdminMixin,
21
28
  admin.ModelAdmin,
22
29
  ):
23
- ordering = ["site", "screening_identifier"]
30
+ note_model = "meta_reports.missingogttnote"
31
+ note_status_list_filter = NoteStatusListFilter
32
+ note_template = "edc_qareports/columns/notes_column.html"
33
+ include_note_column = True
34
+
35
+ ordering = ["site", "subject_identifier"]
36
+
24
37
  list_display = [
25
38
  "dashboard",
26
- "screening_identifier",
39
+ "subject_identifier",
27
40
  "site",
28
- "screening_datetime",
29
- "fbg_datetime",
41
+ "screening_date",
42
+ "fbg_date",
30
43
  "fbg_value",
31
44
  "ogtt_value",
32
45
  "repeated",
@@ -46,7 +59,7 @@ class MissingScreeningOgttAdmin(
46
59
  "p3_ltfu",
47
60
  ]
48
61
 
49
- search_fields = ["screening_identifier"]
62
+ search_fields = ["subject_identifier"]
50
63
 
51
64
  def dashboard(self, obj=None, label=None) -> str:
52
65
  url = self.get_subject_dashboard_url(obj=obj)
@@ -57,3 +70,15 @@ class MissingScreeningOgttAdmin(
57
70
  )
58
71
  context = dict(title=_("Go to subject screening"), url=url, label=label)
59
72
  return render_to_string("dashboard_button.html", context=context)
73
+
74
+ @admin.display(description="Screen date", ordering="screening_datetime")
75
+ def screening_date(self, obj):
76
+ if obj.screening_datetime:
77
+ return obj.screening_datetime.date()
78
+ return None
79
+
80
+ @admin.display(description="Fbg date", ordering="fbg_datetime")
81
+ def fbg_date(self, obj):
82
+ if obj.fbg_datetime:
83
+ return obj.fbg_datetime.date()
84
+ return None
@@ -0,0 +1 @@
1
+ from .missing_ogtt_note_form import MissingOgttNoteForm
@@ -0,0 +1,33 @@
1
+ from django import forms
2
+ from edc_constants.constants import NO, YES
3
+ from edc_form_validators import FormValidator, FormValidatorMixin
4
+ from edc_glucose.form_validators import OgttFormValidatorMixin
5
+ from edc_glucose.utils import validate_glucose_as_millimoles_per_liter
6
+
7
+ from ..models import MissingOgttNote
8
+
9
+
10
+ class MissingOgttNoteFormValidator(OgttFormValidatorMixin, FormValidator):
11
+
12
+ def clean(self):
13
+ self.applicable_if(YES, field="result_status", field_applicable="fasting")
14
+ self.required_if(YES, NO, field="fasting", field_required="ogtt_base_datetime")
15
+ self.required_if(YES, NO, field="fasting", field_required="ogtt_datetime")
16
+ self.required_if(YES, NO, field="fasting", field_required="ogtt_value")
17
+ self.applicable_if(YES, NO, field="fasting", field_applicable="ogtt_units")
18
+ validate_glucose_as_millimoles_per_liter("ogtt", self.cleaned_data)
19
+ self.validate_ogtt_dates()
20
+ self.validate_ogtt_time_interval()
21
+
22
+
23
+ class MissingOgttNoteForm(FormValidatorMixin, forms.ModelForm):
24
+ form_validator_cls = MissingOgttNoteFormValidator
25
+
26
+ class Meta:
27
+ model = MissingOgttNote
28
+ fields = "__all__"
29
+ help_text = {"subject_identifier": "(read-only)", "name": "(read-only)"}
30
+ widgets = {
31
+ "report_model": forms.TextInput(attrs={"readonly": "readonly"}),
32
+ "subject_identifier": forms.TextInput(attrs={"readonly": "readonly"}),
33
+ }
@@ -0,0 +1,54 @@
1
+ # Generated by Django 5.0.8 on 2024-08-23 13:42
2
+
3
+ import django_db_views.migration_functions
4
+ import django_db_views.operations
5
+ from django.db import migrations
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ("meta_reports", "0033_auto_20240823_0012"),
12
+ ]
13
+
14
+ operations = [
15
+ django_db_views.operations.ViewRunPython(
16
+ code=django_db_views.migration_functions.ForwardViewMigration(
17
+ "# noqa\n select *, uuid() as id, now() as created,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,\n p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented,\n screening_identifier as subject_identifier, id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
18
+ "missing_screening_ogtt_view",
19
+ engine="django.db.backends.mysql",
20
+ ),
21
+ reverse_code=django_db_views.migration_functions.BackwardViewMigration(
22
+ "# noqa\n select *, uuid() as id, now() as created,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,\n p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented, \"\" as subject_identifier,\n id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
23
+ "missing_screening_ogtt_view",
24
+ engine="django.db.backends.mysql",
25
+ ),
26
+ atomic=False,
27
+ ),
28
+ django_db_views.operations.ViewRunPython(
29
+ code=django_db_views.migration_functions.ForwardViewMigration(
30
+ "# noqa\n select *, get_random_uuid() as id, now() as created,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,\n p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented,\n screening_identifier as subject_identifier, id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
31
+ "missing_screening_ogtt_view",
32
+ engine="django.db.backends.postgresql",
33
+ ),
34
+ reverse_code=django_db_views.migration_functions.BackwardViewMigration(
35
+ "# noqa\n select *, get_random_uuid() as id, now() as created,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,\n p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented, \"\" as subject_identifier,\n id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
36
+ "missing_screening_ogtt_view",
37
+ engine="django.db.backends.postgresql",
38
+ ),
39
+ atomic=False,
40
+ ),
41
+ django_db_views.operations.ViewRunPython(
42
+ code=django_db_views.migration_functions.ForwardViewMigration(
43
+ "# noqa\nSELECT *, lower(\n hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-' || '4' ||\n substr(hex( randomblob(2)), 2) || '-' ||\n substr('AB89', 1 + (abs(random()) % 4) , 1) ||\n substr(hex(randomblob(2)), 2) || '-' ||\n hex(randomblob(6))\n ) as id, datetime() as `created`,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, fbg2_value, ogtt2_value,\n repeat_glucose_performed as repeated, p3_ltfu, fbg2_datetime, ogtt2_datetime, consented,\n screening_identifier as subject_identifier, id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
44
+ "missing_screening_ogtt_view",
45
+ engine="django.db.backends.sqlite3",
46
+ ),
47
+ reverse_code=django_db_views.migration_functions.BackwardViewMigration(
48
+ "# noqa\nSELECT *, lower(\n hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-' || '4' ||\n substr(hex( randomblob(2)), 2) || '-' ||\n substr('AB89', 1 + (abs(random()) % 4) , 1) ||\n substr(hex(randomblob(2)), 2) || '-' ||\n hex(randomblob(6))\n ) as id, datetime() as `created`,\n 'meta_reports.missing_screening_ogtt_view' as report_model\n from (\n select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,\n converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, fbg2_value, ogtt2_value,\n repeat_glucose_performed as repeated, p3_ltfu, fbg2_datetime, ogtt2_datetime, consented, \"\" as subject_identifier,\n id as original_id\n from meta_screening_subjectscreening\n where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != \"Yes\"\n ) as A\n order by screening_identifier",
49
+ "missing_screening_ogtt_view",
50
+ engine="django.db.backends.sqlite3",
51
+ ),
52
+ atomic=False,
53
+ ),
54
+ ]
@@ -0,0 +1,457 @@
1
+ # Generated by Django 5.0.8 on 2024-08-23 13:59
2
+
3
+ import _socket
4
+ import django.db.models.deletion
5
+ import django.db.models.manager
6
+ import django_audit_fields.fields.hostname_modification_field
7
+ import django_audit_fields.fields.userfield
8
+ import django_audit_fields.fields.uuid_auto_field
9
+ import django_audit_fields.models.audit_model_mixin
10
+ import django_revision.revision_field
11
+ import edc_model.validators.date
12
+ import edc_sites.managers
13
+ import edc_utils.date
14
+ import simple_history.models
15
+ import uuid
16
+ from django.conf import settings
17
+ from django.db import migrations, models
18
+
19
+
20
+ class Migration(migrations.Migration):
21
+
22
+ dependencies = [
23
+ ("meta_reports", "0034_auto_20240823_1642"),
24
+ ("sites", "0002_alter_domain_unique"),
25
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
26
+ ]
27
+
28
+ operations = [
29
+ migrations.CreateModel(
30
+ name="HistoricalMissingOgttNote",
31
+ fields=[
32
+ (
33
+ "revision",
34
+ django_revision.revision_field.RevisionField(
35
+ blank=True,
36
+ editable=False,
37
+ help_text="System field. Git repository tag:branch:commit.",
38
+ max_length=75,
39
+ null=True,
40
+ verbose_name="Revision",
41
+ ),
42
+ ),
43
+ (
44
+ "created",
45
+ models.DateTimeField(
46
+ blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
47
+ ),
48
+ ),
49
+ (
50
+ "modified",
51
+ models.DateTimeField(
52
+ blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
53
+ ),
54
+ ),
55
+ (
56
+ "user_created",
57
+ django_audit_fields.fields.userfield.UserField(
58
+ blank=True,
59
+ help_text="Updated by admin.save_model",
60
+ max_length=50,
61
+ verbose_name="user created",
62
+ ),
63
+ ),
64
+ (
65
+ "user_modified",
66
+ django_audit_fields.fields.userfield.UserField(
67
+ blank=True,
68
+ help_text="Updated by admin.save_model",
69
+ max_length=50,
70
+ verbose_name="user modified",
71
+ ),
72
+ ),
73
+ (
74
+ "hostname_created",
75
+ models.CharField(
76
+ blank=True,
77
+ default=_socket.gethostname,
78
+ help_text="System field. (modified on create only)",
79
+ max_length=60,
80
+ verbose_name="Hostname created",
81
+ ),
82
+ ),
83
+ (
84
+ "hostname_modified",
85
+ django_audit_fields.fields.hostname_modification_field.HostnameModificationField(
86
+ blank=True,
87
+ help_text="System field. (modified on every save)",
88
+ max_length=50,
89
+ verbose_name="Hostname modified",
90
+ ),
91
+ ),
92
+ (
93
+ "device_created",
94
+ models.CharField(blank=True, max_length=10, verbose_name="Device created"),
95
+ ),
96
+ (
97
+ "device_modified",
98
+ models.CharField(
99
+ blank=True, max_length=10, verbose_name="Device modified"
100
+ ),
101
+ ),
102
+ (
103
+ "locale_created",
104
+ models.CharField(
105
+ blank=True,
106
+ help_text="Auto-updated by Modeladmin",
107
+ max_length=10,
108
+ null=True,
109
+ verbose_name="Locale created",
110
+ ),
111
+ ),
112
+ (
113
+ "locale_modified",
114
+ models.CharField(
115
+ blank=True,
116
+ help_text="Auto-updated by Modeladmin",
117
+ max_length=10,
118
+ null=True,
119
+ verbose_name="Locale modified",
120
+ ),
121
+ ),
122
+ (
123
+ "id",
124
+ django_audit_fields.fields.uuid_auto_field.UUIDAutoField(
125
+ blank=True,
126
+ db_index=True,
127
+ editable=False,
128
+ help_text="System auto field. UUID primary key.",
129
+ ),
130
+ ),
131
+ ("subject_identifier", models.CharField(db_index=True, max_length=50)),
132
+ ("report_model", models.CharField(max_length=150)),
133
+ ("report_datetime", models.DateTimeField(default=edc_utils.date.get_utcnow)),
134
+ ("note", models.TextField(blank=True, null=True)),
135
+ (
136
+ "ogtt_base_datetime",
137
+ models.DateTimeField(
138
+ blank=True,
139
+ help_text="(glucose solution given)",
140
+ null=True,
141
+ validators=[edc_model.validators.date.datetime_not_future],
142
+ verbose_name="Date/time oral glucose solution given",
143
+ ),
144
+ ),
145
+ (
146
+ "ogtt_value",
147
+ models.DecimalField(
148
+ blank=True,
149
+ decimal_places=2,
150
+ help_text="A `HIGH` reading may be entered as 9999.99",
151
+ max_digits=8,
152
+ null=True,
153
+ verbose_name="Blood glucose measure 2hrs <u>after</u> oral glucose solution given",
154
+ ),
155
+ ),
156
+ (
157
+ "ogtt_quantifier",
158
+ models.CharField(
159
+ choices=[
160
+ ("=", "="),
161
+ (">", ">"),
162
+ (">=", ">="),
163
+ ("<", "<"),
164
+ ("<=", "<="),
165
+ ],
166
+ default="=",
167
+ max_length=10,
168
+ ),
169
+ ),
170
+ (
171
+ "ogtt_units",
172
+ models.CharField(
173
+ choices=[
174
+ ("mg/dL", "mg/dL"),
175
+ ("mmol/L", "mmol/L (millimoles/L)"),
176
+ ("N/A", "Not applicable"),
177
+ ],
178
+ default="N/A",
179
+ max_length=15,
180
+ verbose_name="Units (Blood glucose 2hrs after...)",
181
+ ),
182
+ ),
183
+ (
184
+ "ogtt_datetime",
185
+ models.DateTimeField(
186
+ blank=True,
187
+ help_text="(2 hours after glucose solution given)",
188
+ null=True,
189
+ validators=[edc_model.validators.date.datetime_not_future],
190
+ verbose_name="Date/time blood glucose measured 2hrs <u>after</u> oral glucose solution given",
191
+ ),
192
+ ),
193
+ (
194
+ "status",
195
+ models.CharField(
196
+ choices=[
197
+ ("done", "Done"),
198
+ ("PENDING", "Data is pending"),
199
+ ("not_available", "Data not available"),
200
+ ],
201
+ default="done",
202
+ max_length=25,
203
+ ),
204
+ ),
205
+ (
206
+ "history_id",
207
+ models.UUIDField(
208
+ default=uuid.uuid4, editable=False, primary_key=True, serialize=False
209
+ ),
210
+ ),
211
+ ("history_date", models.DateTimeField(db_index=True)),
212
+ ("history_change_reason", models.CharField(max_length=100, null=True)),
213
+ (
214
+ "history_type",
215
+ models.CharField(
216
+ choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
217
+ max_length=1,
218
+ ),
219
+ ),
220
+ (
221
+ "history_user",
222
+ models.ForeignKey(
223
+ null=True,
224
+ on_delete=django.db.models.deletion.SET_NULL,
225
+ related_name="+",
226
+ to=settings.AUTH_USER_MODEL,
227
+ ),
228
+ ),
229
+ (
230
+ "site",
231
+ models.ForeignKey(
232
+ blank=True,
233
+ db_constraint=False,
234
+ null=True,
235
+ on_delete=django.db.models.deletion.DO_NOTHING,
236
+ related_name="+",
237
+ to="sites.site",
238
+ ),
239
+ ),
240
+ ],
241
+ options={
242
+ "verbose_name": "historical Screening: Missing OGTT Note",
243
+ "verbose_name_plural": "historical Screening: Missing OGTT Notes",
244
+ "ordering": ("-history_date", "-history_id"),
245
+ "get_latest_by": ("history_date", "history_id"),
246
+ },
247
+ bases=(simple_history.models.HistoricalChanges, models.Model),
248
+ ),
249
+ migrations.CreateModel(
250
+ name="MissingOgttNote",
251
+ fields=[
252
+ (
253
+ "revision",
254
+ django_revision.revision_field.RevisionField(
255
+ blank=True,
256
+ editable=False,
257
+ help_text="System field. Git repository tag:branch:commit.",
258
+ max_length=75,
259
+ null=True,
260
+ verbose_name="Revision",
261
+ ),
262
+ ),
263
+ (
264
+ "created",
265
+ models.DateTimeField(
266
+ blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
267
+ ),
268
+ ),
269
+ (
270
+ "modified",
271
+ models.DateTimeField(
272
+ blank=True, default=django_audit_fields.models.audit_model_mixin.utcnow
273
+ ),
274
+ ),
275
+ (
276
+ "user_created",
277
+ django_audit_fields.fields.userfield.UserField(
278
+ blank=True,
279
+ help_text="Updated by admin.save_model",
280
+ max_length=50,
281
+ verbose_name="user created",
282
+ ),
283
+ ),
284
+ (
285
+ "user_modified",
286
+ django_audit_fields.fields.userfield.UserField(
287
+ blank=True,
288
+ help_text="Updated by admin.save_model",
289
+ max_length=50,
290
+ verbose_name="user modified",
291
+ ),
292
+ ),
293
+ (
294
+ "hostname_created",
295
+ models.CharField(
296
+ blank=True,
297
+ default=_socket.gethostname,
298
+ help_text="System field. (modified on create only)",
299
+ max_length=60,
300
+ verbose_name="Hostname created",
301
+ ),
302
+ ),
303
+ (
304
+ "hostname_modified",
305
+ django_audit_fields.fields.hostname_modification_field.HostnameModificationField(
306
+ blank=True,
307
+ help_text="System field. (modified on every save)",
308
+ max_length=50,
309
+ verbose_name="Hostname modified",
310
+ ),
311
+ ),
312
+ (
313
+ "device_created",
314
+ models.CharField(blank=True, max_length=10, verbose_name="Device created"),
315
+ ),
316
+ (
317
+ "device_modified",
318
+ models.CharField(
319
+ blank=True, max_length=10, verbose_name="Device modified"
320
+ ),
321
+ ),
322
+ (
323
+ "locale_created",
324
+ models.CharField(
325
+ blank=True,
326
+ help_text="Auto-updated by Modeladmin",
327
+ max_length=10,
328
+ null=True,
329
+ verbose_name="Locale created",
330
+ ),
331
+ ),
332
+ (
333
+ "locale_modified",
334
+ models.CharField(
335
+ blank=True,
336
+ help_text="Auto-updated by Modeladmin",
337
+ max_length=10,
338
+ null=True,
339
+ verbose_name="Locale modified",
340
+ ),
341
+ ),
342
+ (
343
+ "id",
344
+ django_audit_fields.fields.uuid_auto_field.UUIDAutoField(
345
+ blank=True,
346
+ editable=False,
347
+ help_text="System auto field. UUID primary key.",
348
+ primary_key=True,
349
+ serialize=False,
350
+ ),
351
+ ),
352
+ ("subject_identifier", models.CharField(max_length=50, unique=True)),
353
+ ("report_model", models.CharField(max_length=150)),
354
+ ("report_datetime", models.DateTimeField(default=edc_utils.date.get_utcnow)),
355
+ ("note", models.TextField(blank=True, null=True)),
356
+ (
357
+ "ogtt_base_datetime",
358
+ models.DateTimeField(
359
+ blank=True,
360
+ help_text="(glucose solution given)",
361
+ null=True,
362
+ validators=[edc_model.validators.date.datetime_not_future],
363
+ verbose_name="Date/time oral glucose solution given",
364
+ ),
365
+ ),
366
+ (
367
+ "ogtt_value",
368
+ models.DecimalField(
369
+ blank=True,
370
+ decimal_places=2,
371
+ help_text="A `HIGH` reading may be entered as 9999.99",
372
+ max_digits=8,
373
+ null=True,
374
+ verbose_name="Blood glucose measure 2hrs <u>after</u> oral glucose solution given",
375
+ ),
376
+ ),
377
+ (
378
+ "ogtt_quantifier",
379
+ models.CharField(
380
+ choices=[
381
+ ("=", "="),
382
+ (">", ">"),
383
+ (">=", ">="),
384
+ ("<", "<"),
385
+ ("<=", "<="),
386
+ ],
387
+ default="=",
388
+ max_length=10,
389
+ ),
390
+ ),
391
+ (
392
+ "ogtt_units",
393
+ models.CharField(
394
+ choices=[
395
+ ("mg/dL", "mg/dL"),
396
+ ("mmol/L", "mmol/L (millimoles/L)"),
397
+ ("N/A", "Not applicable"),
398
+ ],
399
+ default="N/A",
400
+ max_length=15,
401
+ verbose_name="Units (Blood glucose 2hrs after...)",
402
+ ),
403
+ ),
404
+ (
405
+ "ogtt_datetime",
406
+ models.DateTimeField(
407
+ blank=True,
408
+ help_text="(2 hours after glucose solution given)",
409
+ null=True,
410
+ validators=[edc_model.validators.date.datetime_not_future],
411
+ verbose_name="Date/time blood glucose measured 2hrs <u>after</u> oral glucose solution given",
412
+ ),
413
+ ),
414
+ (
415
+ "status",
416
+ models.CharField(
417
+ choices=[
418
+ ("done", "Done"),
419
+ ("PENDING", "Data is pending"),
420
+ ("not_available", "Data not available"),
421
+ ],
422
+ default="done",
423
+ max_length=25,
424
+ ),
425
+ ),
426
+ (
427
+ "site",
428
+ models.ForeignKey(
429
+ null=True,
430
+ on_delete=django.db.models.deletion.PROTECT,
431
+ related_name="+",
432
+ to="sites.site",
433
+ ),
434
+ ),
435
+ ],
436
+ options={
437
+ "verbose_name": "Screening: Missing OGTT Note",
438
+ "verbose_name_plural": "Screening: Missing OGTT Notes",
439
+ "abstract": False,
440
+ "default_permissions": ("add", "change", "delete", "view", "export", "import"),
441
+ "default_manager_name": "objects",
442
+ "indexes": [
443
+ models.Index(
444
+ fields=["modified", "created"], name="meta_report_modifie_4a4894_idx"
445
+ ),
446
+ models.Index(
447
+ fields=["user_modified", "user_created"],
448
+ name="meta_report_user_mo_5b8129_idx",
449
+ ),
450
+ ],
451
+ },
452
+ managers=[
453
+ ("on_site", edc_sites.managers.CurrentSiteManager()),
454
+ ("objects", django.db.models.manager.Manager()),
455
+ ],
456
+ ),
457
+ ]
@@ -0,0 +1,86 @@
1
+ # Generated by Django 5.0.8 on 2024-08-23 14:26
2
+
3
+ import django.core.validators
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("meta_reports", "0035_historicalmissingogttnote_missingogttnote"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="historicalmissingogttnote",
16
+ name="fasting",
17
+ field=models.CharField(
18
+ choices=[("Yes", "Yes"), ("No", "No")],
19
+ max_length=15,
20
+ null=True,
21
+ verbose_name="Did the participant fast?",
22
+ ),
23
+ ),
24
+ migrations.AddField(
25
+ model_name="historicalmissingogttnote",
26
+ name="fasting_duration_delta",
27
+ field=models.DurationField(
28
+ blank=True,
29
+ help_text="system calculated to microseconds. (hours=microseconds/3.6e+9)",
30
+ null=True,
31
+ ),
32
+ ),
33
+ migrations.AddField(
34
+ model_name="historicalmissingogttnote",
35
+ name="fasting_duration_str",
36
+ field=models.CharField(
37
+ blank=True,
38
+ help_text="As reported by patient. Duration of fast. Format is `HHhMMm`. For example 1h23m, 12h7m, etc",
39
+ max_length=8,
40
+ null=True,
41
+ validators=[
42
+ django.core.validators.RegexValidator(
43
+ "^([0-9]{1,3}h([0-5]?[0-9]m)?)$",
44
+ message="Invalid format. Expected something like 1h20m, 11h5m, etc. No spaces allowed.",
45
+ )
46
+ ],
47
+ verbose_name="How long have they fasted in hours and/or minutes?",
48
+ ),
49
+ ),
50
+ migrations.AddField(
51
+ model_name="missingogttnote",
52
+ name="fasting",
53
+ field=models.CharField(
54
+ choices=[("Yes", "Yes"), ("No", "No")],
55
+ max_length=15,
56
+ null=True,
57
+ verbose_name="Did the participant fast?",
58
+ ),
59
+ ),
60
+ migrations.AddField(
61
+ model_name="missingogttnote",
62
+ name="fasting_duration_delta",
63
+ field=models.DurationField(
64
+ blank=True,
65
+ help_text="system calculated to microseconds. (hours=microseconds/3.6e+9)",
66
+ null=True,
67
+ ),
68
+ ),
69
+ migrations.AddField(
70
+ model_name="missingogttnote",
71
+ name="fasting_duration_str",
72
+ field=models.CharField(
73
+ blank=True,
74
+ help_text="As reported by patient. Duration of fast. Format is `HHhMMm`. For example 1h23m, 12h7m, etc",
75
+ max_length=8,
76
+ null=True,
77
+ validators=[
78
+ django.core.validators.RegexValidator(
79
+ "^([0-9]{1,3}h([0-5]?[0-9]m)?)$",
80
+ message="Invalid format. Expected something like 1h20m, 11h5m, etc. No spaces allowed.",
81
+ )
82
+ ],
83
+ verbose_name="How long have they fasted in hours and/or minutes?",
84
+ ),
85
+ ),
86
+ ]
@@ -0,0 +1,51 @@
1
+ # Generated by Django 5.0.8 on 2024-08-23 14:40
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("meta_reports", "0036_historicalmissingogttnote_fasting_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="historicalmissingogttnote",
15
+ name="result_status",
16
+ field=models.CharField(
17
+ choices=[("Yes", "Yes"), ("No", "No")],
18
+ default="Yes",
19
+ max_length=25,
20
+ verbose_name="Is the OGTT result available",
21
+ ),
22
+ ),
23
+ migrations.AddField(
24
+ model_name="missingogttnote",
25
+ name="result_status",
26
+ field=models.CharField(
27
+ choices=[("Yes", "Yes"), ("No", "No")],
28
+ default="Yes",
29
+ max_length=25,
30
+ verbose_name="Is the OGTT result available",
31
+ ),
32
+ ),
33
+ migrations.AlterField(
34
+ model_name="historicalmissingogttnote",
35
+ name="status",
36
+ field=models.CharField(
37
+ choices=[("COMPLETE", "Complete"), ("not_available", "Not available")],
38
+ default="done",
39
+ max_length=25,
40
+ ),
41
+ ),
42
+ migrations.AlterField(
43
+ model_name="missingogttnote",
44
+ name="status",
45
+ field=models.CharField(
46
+ choices=[("COMPLETE", "Complete"), ("not_available", "Not available")],
47
+ default="done",
48
+ max_length=25,
49
+ ),
50
+ ),
51
+ ]
@@ -0,0 +1,33 @@
1
+ # Generated by Django 5.0.8 on 2024-08-23 15:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("meta_reports", "0037_historicalmissingogttnote_result_status_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="historicalmissingogttnote",
15
+ name="fasting",
16
+ field=models.CharField(
17
+ choices=[("Yes", "Yes"), ("No", "No"), ("N/A", "Not applicable")],
18
+ default="N/A",
19
+ max_length=15,
20
+ verbose_name="Did the participant fast?",
21
+ ),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name="missingogttnote",
25
+ name="fasting",
26
+ field=models.CharField(
27
+ choices=[("Yes", "Yes"), ("No", "No"), ("N/A", "Not applicable")],
28
+ default="N/A",
29
+ max_length=15,
30
+ verbose_name="Did the participant fast?",
31
+ ),
32
+ ),
33
+ ]
@@ -1,5 +1,7 @@
1
1
  from .dbviews import (
2
+ NOTE_STATUSES,
2
3
  GlucoseSummary,
4
+ MissingOgttNote,
3
5
  MissingScreeningOgtt,
4
6
  PatientHistoryMissingBaselineCd4,
5
7
  UnattendedThreeInRow,
@@ -1,5 +1,5 @@
1
1
  from .glucose_summary import GlucoseSummary
2
- from .missing_screening_ogtt import MissingScreeningOgtt
2
+ from .missing_screening_ogtt import NOTE_STATUSES, MissingOgttNote, MissingScreeningOgtt
3
3
  from .patient_history_missing_baseline_cd4 import PatientHistoryMissingBaselineCd4
4
4
  from .unattended_three_in_row import UnattendedThreeInRow
5
5
  from .unattended_three_in_row2 import UnattendedThreeInRow2
@@ -1 +1,2 @@
1
+ from .note_model import NOTE_STATUSES, MissingOgttNote
1
2
  from .unmanaged_model import MissingScreeningOgtt
@@ -0,0 +1,57 @@
1
+ from django.db import models
2
+ from edc_constants.choices import YES_NO, YES_NO_NA
3
+ from edc_constants.constants import COMPLETE, DONE, NOT_APPLICABLE, NOT_AVAILABLE, YES
4
+ from edc_glucose.model_mixins import (
5
+ fasting_model_mixin_factory,
6
+ ogtt_model_mixin_factory,
7
+ )
8
+ from edc_identifier.model_mixins import UniqueSubjectIdentifierFieldMixin
9
+ from edc_model.models import BaseUuidModel, HistoricalRecords
10
+ from edc_qareports.model_mixins import NoteModelMixin
11
+
12
+ NOTE_STATUSES = (
13
+ (COMPLETE, "Complete"),
14
+ (NOT_AVAILABLE, "Not available"),
15
+ )
16
+
17
+
18
+ class MissingOgttNote(
19
+ fasting_model_mixin_factory(
20
+ None,
21
+ fasting=models.CharField(
22
+ verbose_name="Did the participant fast?",
23
+ max_length=15,
24
+ choices=YES_NO_NA,
25
+ default=NOT_APPLICABLE,
26
+ blank=False,
27
+ ),
28
+ ),
29
+ ogtt_model_mixin_factory("ogtt"),
30
+ UniqueSubjectIdentifierFieldMixin,
31
+ NoteModelMixin,
32
+ ):
33
+ """Model class to replace default `Note` model used with
34
+ QA Report 'Screening: Missing OGTT'.
35
+ """
36
+
37
+ result_status = models.CharField(
38
+ verbose_name="Is the OGTT result available", max_length=25, default=YES, choices=YES_NO
39
+ )
40
+
41
+ status = models.CharField(max_length=25, default=DONE, choices=NOTE_STATUSES)
42
+
43
+ history = HistoricalRecords()
44
+
45
+ def __str__(self) -> str:
46
+ return f"{self._meta.verbose_name}: {self.subject_identifier}"
47
+
48
+ def save(self, *args, **kwargs):
49
+ if self.result_status == YES:
50
+ self.status = COMPLETE
51
+ else:
52
+ self.status = NOT_AVAILABLE
53
+ super().save(*args, **kwargs)
54
+
55
+ class Meta(UniqueSubjectIdentifierFieldMixin.Meta, BaseUuidModel.Meta):
56
+ verbose_name = "Screening: Missing OGTT Note"
57
+ verbose_name_plural = "Screening: Missing OGTT Notes"
@@ -7,8 +7,6 @@ from .view_definition import get_view_definition
7
7
 
8
8
  class MissingScreeningOgtt(QaReportModelMixin, DBView):
9
9
 
10
- screening_identifier = models.CharField(max_length=25)
11
-
12
10
  screening_datetime = models.DateTimeField(null=True)
13
11
 
14
12
  fbg_datetime = models.DateTimeField(null=True)
@@ -4,8 +4,8 @@ mysql_view: str = """ # noqa
4
4
  from (
5
5
  select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,
6
6
  converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,
7
- p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented, "" as subject_identifier,
8
- id as original_id
7
+ p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented,
8
+ screening_identifier as subject_identifier, id as original_id
9
9
  from meta_screening_subjectscreening
10
10
  where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != "Yes"
11
11
  ) as A
@@ -18,8 +18,8 @@ pg_view: str = """ # noqa
18
18
  from (
19
19
  select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,
20
20
  converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, repeat_glucose_performed as repeated,
21
- p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented, "" as subject_identifier,
22
- id as original_id
21
+ p3_ltfu, fbg2_value, ogtt2_value, fbg2_datetime, ogtt2_datetime, consented,
22
+ screening_identifier as subject_identifier, id as original_id
23
23
  from meta_screening_subjectscreening
24
24
  where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != "Yes"
25
25
  ) as A
@@ -38,8 +38,8 @@ SELECT *, lower(
38
38
  from (
39
39
  select screening_identifier, site_id, report_datetime as 'screening_datetime', fbg_datetime,
40
40
  converted_fbg_value as fbg_value, converted_ogtt_value as ogtt_value, fbg2_value, ogtt2_value,
41
- repeat_glucose_performed as repeated, p3_ltfu, fbg2_datetime, ogtt2_datetime, consented, "" as subject_identifier,
42
- id as original_id
41
+ repeat_glucose_performed as repeated, p3_ltfu, fbg2_datetime, ogtt2_datetime, consented,
42
+ screening_identifier as subject_identifier, id as original_id
43
43
  from meta_screening_subjectscreening
44
44
  where converted_fbg_value is not null and converted_ogtt_value is null and unsuitable_agreed != "Yes"
45
45
  ) as A