clinicedc 2.0.11__py3-none-any.whl → 2.0.13__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 (137) hide show
  1. {clinicedc-2.0.11.dist-info → clinicedc-2.0.13.dist-info}/METADATA +2 -1
  2. {clinicedc-2.0.11.dist-info → clinicedc-2.0.13.dist-info}/RECORD +137 -24
  3. edc_action_item/migrations/0017_auto_20190305_0123.py +1 -1
  4. edc_action_item/migrations/0030_edcpermissions.py +1 -1
  5. edc_action_item/migrations/0041_alter_actionitem_revision_alter_actiontype_revision_and_more.py +86 -0
  6. edc_adverse_event/migrations/0001_initial.py +1 -1
  7. edc_adverse_event/migrations/0002_auto_20190802_0059.py +1 -1
  8. edc_adverse_event/migrations/0008_auto_20220825_0451.py +1 -1
  9. edc_adverse_event/migrations/0009_auto_20220907_0157.py +1 -1
  10. edc_adverse_event/migrations/0017_alter_aeactionclassification_revision_and_more.py +77 -0
  11. edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +1 -3
  12. edc_analytics/__init__.py +3 -0
  13. edc_analytics/apps.py +8 -0
  14. edc_analytics/constants.py +26 -0
  15. edc_analytics/custom_tables/__init__.py +11 -0
  16. edc_analytics/custom_tables/age.py +72 -0
  17. edc_analytics/custom_tables/art.py +88 -0
  18. edc_analytics/custom_tables/bmi.py +125 -0
  19. edc_analytics/custom_tables/bp.py +103 -0
  20. edc_analytics/custom_tables/fasting.py +126 -0
  21. edc_analytics/custom_tables/fbg.py +98 -0
  22. edc_analytics/custom_tables/fbg_ogtt.py +384 -0
  23. edc_analytics/custom_tables/gender.py +12 -0
  24. edc_analytics/custom_tables/hba1c.py +87 -0
  25. edc_analytics/custom_tables/ogtt.py +95 -0
  26. edc_analytics/custom_tables/waist.py +105 -0
  27. edc_analytics/data.py +36 -0
  28. edc_analytics/row/__init__.py +4 -0
  29. edc_analytics/row/row_definition.py +43 -0
  30. edc_analytics/row/row_definitions.py +32 -0
  31. edc_analytics/row/row_statistics.py +88 -0
  32. edc_analytics/row/row_statistics_with_gender.py +115 -0
  33. edc_analytics/stata/__init__.py +1 -0
  34. edc_analytics/stata/get_stata_labels_from_model.py +44 -0
  35. edc_analytics/styler.py +93 -0
  36. edc_analytics/table.py +108 -0
  37. edc_analytics/urls.py +6 -0
  38. edc_appointment/migrations/0018_auto_20190305_0123.py +1 -1
  39. edc_appointment/migrations/0051_alter_appointment_revision_and_more.py +38 -0
  40. edc_auth/migrations/0001_squashed_0033_alter_userprofile_is_multisite_viewer.py +1 -1
  41. edc_auth/migrations/0012_auto_20191026_0034.py +1 -1
  42. edc_auth/migrations/0013_auto_20191026_0055.py +1 -1
  43. edc_auth/migrations/0025_permissions.py +1 -1
  44. edc_auth/migrations/0037_alter_edcpermissions_revision_alter_role_revision.py +38 -0
  45. edc_consent/migrations/0001_initial.py +1 -1
  46. edc_consent/migrations/0007_alter_edcpermissions_revision.py +26 -0
  47. edc_crf/migrations/0010_alter_crfstatus_revision.py +26 -0
  48. edc_dashboard/migrations/0001_initial.py +1 -1
  49. edc_dashboard/migrations/0007_alter_edcpermissions_revision.py +26 -0
  50. edc_data_manager/migrations/0001_initial.py +1 -1
  51. edc_data_manager/migrations/0025_edcpermissions.py +1 -1
  52. edc_data_manager/migrations/0042_alter_datadictionary_revision_and_more.py +98 -0
  53. edc_dx/__init__.py +6 -0
  54. edc_dx/apps.py +5 -0
  55. edc_dx/diagnoses.py +250 -0
  56. edc_dx/form_validators/__init__.py +2 -0
  57. edc_dx/form_validators/diagnosis_form_validator_mixin.py +54 -0
  58. edc_dx/form_validators/result_form_validator_mixin.py +65 -0
  59. edc_dx/utils.py +42 -0
  60. edc_dx_review/__init__.py +0 -0
  61. edc_dx_review/apps.py +5 -0
  62. edc_dx_review/auth_objects.py +13 -0
  63. edc_dx_review/auths.py +12 -0
  64. edc_dx_review/choices.py +24 -0
  65. edc_dx_review/constants.py +7 -0
  66. edc_dx_review/fieldsets.py +47 -0
  67. edc_dx_review/form_mixins/__init__.py +3 -0
  68. edc_dx_review/form_mixins/clinical_review_baseline_required_form_mixin.py +25 -0
  69. edc_dx_review/form_validator_mixins/__init__.py +6 -0
  70. edc_dx_review/form_validator_mixins/clinical_review_baseline_form_validator_mixin.py +7 -0
  71. edc_dx_review/form_validator_mixins/clinical_review_followup_form_validator_mixin.py +25 -0
  72. edc_dx_review/list_data.py +19 -0
  73. edc_dx_review/medical_date.py +195 -0
  74. edc_dx_review/migrations/0001_initial.py +307 -0
  75. edc_dx_review/migrations/0002_diagnosislocations_extra_value_and_more.py +32 -0
  76. edc_dx_review/migrations/0003_alter_diagnosislocations_options_and_more.py +148 -0
  77. edc_dx_review/migrations/0004_remove_diagnosislocations_edc_dx_revi_name_a39b40_idx_and_more.py +20 -0
  78. edc_dx_review/migrations/__init__.py +0 -0
  79. edc_dx_review/model_mixins/__init__.py +20 -0
  80. edc_dx_review/model_mixins/clinical_review_baseline_model_mixin.py +25 -0
  81. edc_dx_review/model_mixins/clinical_review_followup/__init__.py +5 -0
  82. edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_chol_model_mixin.py +54 -0
  83. edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_dm_model_mixin.py +54 -0
  84. edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_hiv_model_mixin.py +54 -0
  85. edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_htn_model_mixin.py +56 -0
  86. edc_dx_review/model_mixins/clinical_review_followup/clinical_review_followup_model_mixin.py +25 -0
  87. edc_dx_review/model_mixins/dx_location_model_mixin.py +17 -0
  88. edc_dx_review/model_mixins/factory/__init__.py +4 -0
  89. edc_dx_review/model_mixins/factory/baseline_review_model_mixin_factory.py +55 -0
  90. edc_dx_review/model_mixins/factory/calculate_date.py +43 -0
  91. edc_dx_review/model_mixins/factory/dx_initial_review_model_mixin_factory.py +97 -0
  92. edc_dx_review/model_mixins/factory/followup_review_model_mixin_factory.py +39 -0
  93. edc_dx_review/model_mixins/factory/rx_initial_review_model_mixin_factory.py +69 -0
  94. edc_dx_review/model_mixins/followup_review/__init__.py +2 -0
  95. edc_dx_review/model_mixins/followup_review/followup_review_model_mixin.py +22 -0
  96. edc_dx_review/model_mixins/followup_review/hiv_followup_review_model_mixin.py +32 -0
  97. edc_dx_review/model_mixins/initial_review/__init__.py +6 -0
  98. edc_dx_review/model_mixins/initial_review/chol_initial_review_model_mixin.py +34 -0
  99. edc_dx_review/model_mixins/initial_review/hiv_initial_model_mixins.py +119 -0
  100. edc_dx_review/model_mixins/initial_review/ncd_initial_review_model_mixin.py +42 -0
  101. edc_dx_review/models.py +20 -0
  102. edc_dx_review/radio_fields.py +30 -0
  103. edc_dx_review/utils.py +220 -0
  104. edc_export/migrations/0004_auto_20190305_0123.py +1 -1
  105. edc_export/migrations/0013_edcpermissions.py +1 -1
  106. edc_export/migrations/0024_alter_datarequest_revision_and_more.py +170 -0
  107. edc_facility/migrations/0005_healthfacility_healthfacilitytypes_and_more.py +1 -1
  108. edc_facility/migrations/0018_alter_healthfacility_revision_and_more.py +38 -0
  109. edc_form_runners/migrations/0006_alter_issue_revision.py +26 -0
  110. edc_identifier/migrations/0012_alter_identifiermodel_revision.py +26 -0
  111. edc_lab/migrations/0039_alter_aliquot_revision_alter_box_revision_and_more.py +269 -0
  112. edc_lab_dashboard/migrations/0006_alter_edcpermissions_revision.py +26 -0
  113. edc_label/migrations/0008_alter_zpllabeltemplates_revision.py +26 -0
  114. edc_listboard/migrations/0008_alter_listboard_revision.py +26 -0
  115. edc_locator/migrations/0042_alter_historicalsubjectlocator_revision_and_more.py +38 -0
  116. edc_metadata/migrations/0032_alter_crfmetadata_revision_and_more.py +38 -0
  117. edc_navbar/migrations/0010_alter_edcpermissions_revision.py +26 -0
  118. edc_notification/migrations/0012_alter_notification_revision.py +26 -0
  119. edc_offstudy/migrations/0025_alter_historicalsubjectoffstudy_revision_and_more.py +41 -0
  120. edc_pharmacy/migrations/0091_alter_allocation_revision_alter_assignment_revision_and_more.py +794 -0
  121. edc_protocol_incident/migrations/0026_alter_historicalprotocoldeviationviolation_revision_and_more.py +65 -0
  122. edc_pylabels/migrations/0014_alter_labelconfiguration_revision.py +26 -0
  123. edc_qareports/migrations/0021_alter_edcpermissions_revision_alter_note_revision.py +38 -0
  124. edc_randomization/migrations/0015_alter_edcpermissions_revision_and_more.py +50 -0
  125. edc_refusal/migrations/0014_alter_historicalsubjectrefusal_revision_and_more.py +38 -0
  126. edc_registration/migrations/0034_alter_historicalregisteredsubject_revision_and_more.py +41 -0
  127. edc_reportable/migrations/0008_alter_gradingdata_revision_and_more.py +110 -0
  128. edc_review_dashboard/migrations/0007_alter_edcpermissions_revision.py +26 -0
  129. edc_screening/migrations/0006_alter_edcpermissions_revision.py +26 -0
  130. edc_sites/migrations/0011_alter_edcpermissions_revision.py +26 -0
  131. edc_subject_dashboard/migrations/0006_alter_edcpermissions_revision.py +26 -0
  132. edc_unblinding/migrations/0016_alter_historicalunblindingrequest_revision_and_more.py +65 -0
  133. edc_visit_schedule/migrations/0021_alter_historicalonschedule_revision_and_more.py +89 -0
  134. edc_visit_tracking/migrations/0011_alter_historicalsubjectvisit_revision_and_more.py +65 -0
  135. edc_vitals/model_mixins/blood_pressure_model_mixin.py +1 -0
  136. {clinicedc-2.0.11.dist-info → clinicedc-2.0.13.dist-info}/WHEEL +0 -0
  137. {clinicedc-2.0.11.dist-info → clinicedc-2.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,86 @@
1
+ # Generated by Django 5.2.6 on 2025-09-24 04:56
2
+
3
+ import django_revision.revision_field
4
+ from django.db import migrations
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("edc_action_item", "0040_alter_actionitem_report_datetime_and_more"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="actionitem",
16
+ name="revision",
17
+ field=django_revision.revision_field.RevisionField(
18
+ blank=True,
19
+ default="",
20
+ editable=False,
21
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
22
+ max_length=75,
23
+ verbose_name="Revision",
24
+ ),
25
+ ),
26
+ migrations.AlterField(
27
+ model_name="actiontype",
28
+ name="revision",
29
+ field=django_revision.revision_field.RevisionField(
30
+ blank=True,
31
+ default="",
32
+ editable=False,
33
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
34
+ max_length=75,
35
+ verbose_name="Revision",
36
+ ),
37
+ ),
38
+ migrations.AlterField(
39
+ model_name="edcpermissions",
40
+ name="revision",
41
+ field=django_revision.revision_field.RevisionField(
42
+ blank=True,
43
+ default="",
44
+ editable=False,
45
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
46
+ max_length=75,
47
+ verbose_name="Revision",
48
+ ),
49
+ ),
50
+ migrations.AlterField(
51
+ model_name="historicalactionitem",
52
+ name="revision",
53
+ field=django_revision.revision_field.RevisionField(
54
+ blank=True,
55
+ default="",
56
+ editable=False,
57
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
58
+ max_length=75,
59
+ verbose_name="Revision",
60
+ ),
61
+ ),
62
+ migrations.AlterField(
63
+ model_name="historicalreference",
64
+ name="revision",
65
+ field=django_revision.revision_field.RevisionField(
66
+ blank=True,
67
+ default="",
68
+ editable=False,
69
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
70
+ max_length=75,
71
+ verbose_name="Revision",
72
+ ),
73
+ ),
74
+ migrations.AlterField(
75
+ model_name="reference",
76
+ name="revision",
77
+ field=django_revision.revision_field.RevisionField(
78
+ blank=True,
79
+ default="",
80
+ editable=False,
81
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
82
+ max_length=75,
83
+ verbose_name="Revision",
84
+ ),
85
+ ),
86
+ ]
@@ -4,7 +4,7 @@ import _socket
4
4
  import django_audit_fields.fields.hostname_modification_field
5
5
  import django_audit_fields.fields.userfield
6
6
  import django_audit_fields.fields.uuid_auto_field
7
- import django_audit_fields.models.audit_model_mixin
7
+ import django.utils.timezone
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
10
  import django.utils.timezone
@@ -4,7 +4,7 @@ import _socket
4
4
  import django_audit_fields.fields.hostname_modification_field
5
5
  import django_audit_fields.fields.userfield
6
6
  import django_audit_fields.fields.uuid_auto_field
7
- import django_audit_fields.models.audit_model_mixin
7
+ import django.utils.timezone
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
10
  import django.utils.timezone
@@ -4,7 +4,7 @@ import _socket
4
4
  import django_audit_fields.fields.hostname_modification_field
5
5
  import django_audit_fields.fields.userfield
6
6
  import django_audit_fields.fields.uuid_auto_field
7
- import django_audit_fields.models.audit_model_mixin
7
+ import django.utils.timezone
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
10
  import django.utils.timezone
@@ -4,7 +4,7 @@ import _socket
4
4
  import django_audit_fields.fields.hostname_modification_field
5
5
  import django_audit_fields.fields.userfield
6
6
  import django_audit_fields.fields.uuid_auto_field
7
- import django_audit_fields.models.audit_model_mixin
7
+ import django.utils.timezone
8
8
  import django_revision.revision_field
9
9
  from django.db import migrations, models
10
10
  import django.utils.timezone
@@ -0,0 +1,77 @@
1
+ # Generated by Django 5.2.6 on 2025-09-24 04:56
2
+
3
+ import django_revision.revision_field
4
+ from django.db import migrations
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ (
11
+ "edc_adverse_event",
12
+ "0016_alter_aeactionclassification_device_created_and_more",
13
+ ),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AlterField(
18
+ model_name="aeactionclassification",
19
+ name="revision",
20
+ field=django_revision.revision_field.RevisionField(
21
+ blank=True,
22
+ default="",
23
+ editable=False,
24
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
25
+ max_length=75,
26
+ verbose_name="Revision",
27
+ ),
28
+ ),
29
+ migrations.AlterField(
30
+ model_name="aeclassification",
31
+ name="revision",
32
+ field=django_revision.revision_field.RevisionField(
33
+ blank=True,
34
+ default="",
35
+ editable=False,
36
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
37
+ max_length=75,
38
+ verbose_name="Revision",
39
+ ),
40
+ ),
41
+ migrations.AlterField(
42
+ model_name="causeofdeath",
43
+ name="revision",
44
+ field=django_revision.revision_field.RevisionField(
45
+ blank=True,
46
+ default="",
47
+ editable=False,
48
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
49
+ max_length=75,
50
+ verbose_name="Revision",
51
+ ),
52
+ ),
53
+ migrations.AlterField(
54
+ model_name="edcpermissions",
55
+ name="revision",
56
+ field=django_revision.revision_field.RevisionField(
57
+ blank=True,
58
+ default="",
59
+ editable=False,
60
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
61
+ max_length=75,
62
+ verbose_name="Revision",
63
+ ),
64
+ ),
65
+ migrations.AlterField(
66
+ model_name="saereason",
67
+ name="revision",
68
+ field=django_revision.revision_field.RevisionField(
69
+ blank=True,
70
+ default="",
71
+ editable=False,
72
+ help_text="System field. From git repository (tag:branch:commit), project metadata, project toml, project VERSION, or settings.",
73
+ max_length=75,
74
+ verbose_name="Revision",
75
+ ),
76
+ ),
77
+ ]
@@ -54,9 +54,7 @@ class HospitalizationModelMixin(NonUniqueSubjectIdentifierFieldMixin, models.Mod
54
54
  )
55
55
 
56
56
  narrative = models.TextField(
57
- verbose_name="Narrative",
58
- max_length=500,
59
- blank=True,
57
+ verbose_name="Narrative", max_length=500, blank=True, default=""
60
58
  )
61
59
 
62
60
  class Meta(NonUniqueSubjectIdentifierFieldMixin.Meta):
@@ -0,0 +1,3 @@
1
+ from .row import RowDefinition, RowStatistics, RowStatisticsWithGender
2
+ from .styler import Styler
3
+ from .table import Table
edc_analytics/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig as DjangoAppConfig
2
+
3
+
4
+ class AppConfig(DjangoAppConfig):
5
+ name = "edc_analytics"
6
+ verbose_name = "Edc Analytics"
7
+ has_exportable_data = False
8
+ include_in_administration_section = False
@@ -0,0 +1,26 @@
1
+ MEAN_95CI = "mean_95ci"
2
+ MEAN_RANGE = "mean_range"
3
+ MEAN_SD = "mean_sd"
4
+ MEDIAN_IQR = "median_iqr"
5
+ MEDIAN_RANGE = "median_range"
6
+ N_MEAN = "n_mean"
7
+ N_ONLY = "n_only"
8
+ N_RANGE = "n_range"
9
+ N_WITH_COL_PROP = "n_with_col_prop"
10
+ N_WITH_ROW_PROP: str = "n_with_row_prop"
11
+
12
+ COUNT_COLUMN = "count"
13
+ TITLE_COLUMN = "title"
14
+
15
+ STATISTICS = [
16
+ MEAN_95CI,
17
+ MEAN_RANGE,
18
+ MEAN_SD,
19
+ MEDIAN_IQR,
20
+ MEDIAN_RANGE,
21
+ N_MEAN,
22
+ N_ONLY,
23
+ N_RANGE,
24
+ N_WITH_COL_PROP,
25
+ N_WITH_ROW_PROP,
26
+ ]
@@ -0,0 +1,11 @@
1
+ from .age import AgeTable
2
+ from .art import ArtTable
3
+ from .bmi import BmiTable
4
+ from .bp import BpTable
5
+ from .fasting import FastingTable
6
+ from .fbg import FbgTable
7
+ from .fbg_ogtt import FbgOgttTable
8
+ from .gender import GenderTable
9
+ from .hba1c import HbA1cTable
10
+ from .ogtt import OgttTable
11
+ from .waist import WaistCircumferenceTable
@@ -0,0 +1,72 @@
1
+ import pandas as pd
2
+ from edc_constants.constants import FEMALE, MALE
3
+
4
+ from ..constants import MEDIAN_RANGE, N_ONLY, N_WITH_COL_PROP, N_WITH_ROW_PROP
5
+ from ..row import RowDefinition, RowDefinitions
6
+ from ..table import Table
7
+
8
+
9
+ class AgeTable(Table):
10
+
11
+ col_a = FEMALE
12
+ col_b = MALE
13
+ column_name = "gender"
14
+
15
+ def __init__(self, main_df: pd.DataFrame = None):
16
+ super().__init__(colname="age_in_years", main_df=main_df, title="Age (years)")
17
+
18
+ @property
19
+ def row_definitions(self) -> RowDefinitions:
20
+ df_tmp = self.main_df.copy()
21
+ row_defs = RowDefinitions(reverse_rows=False)
22
+ row0 = RowDefinition(
23
+ title=self.title,
24
+ label=self.default_sublabel,
25
+ condition=(df_tmp[self.column_name].notna()),
26
+ columns={
27
+ self.col_a: (N_ONLY, 2),
28
+ self.col_b: (N_ONLY, 2),
29
+ "All": (N_ONLY, 2),
30
+ },
31
+ drop=False,
32
+ )
33
+ row_defs.add(row0)
34
+ columns = {
35
+ self.col_a: (N_WITH_COL_PROP, 2),
36
+ self.col_b: (N_WITH_COL_PROP, 2),
37
+ "All": (N_WITH_ROW_PROP, 2),
38
+ }
39
+ bin1 = (df_tmp[self.colname] >= 18) & (df_tmp[self.colname] < 35)
40
+ bin2 = (df_tmp[self.colname] >= 35) & (df_tmp[self.colname] < 50)
41
+ bin3 = (df_tmp[self.colname] >= 50) & (df_tmp[self.colname] < 65)
42
+ bin4 = df_tmp[self.colname] >= 65
43
+ row_defs.add(RowDefinition(label="18-34", condition=bin1, columns=columns, drop=False))
44
+ row_defs.add(RowDefinition(label="35-49", condition=bin2, columns=columns, drop=False))
45
+ row_defs.add(RowDefinition(label="50-64", condition=bin3, columns=columns, drop=False))
46
+ row_defs.add(
47
+ RowDefinition(label="65 and older", condition=bin4, columns=columns, drop=False)
48
+ )
49
+ if len(df_tmp[df_tmp[self.colname].isna()]) > 0:
50
+ row_defs.add(
51
+ RowDefinition(
52
+ colname="age_in_years",
53
+ label="not recorded",
54
+ condition=(df_tmp[self.colname].isna()),
55
+ columns=columns,
56
+ drop=False,
57
+ )
58
+ )
59
+ columns = {
60
+ self.col_a: (MEDIAN_RANGE, 2),
61
+ self.col_b: (MEDIAN_RANGE, 2),
62
+ "All": (MEDIAN_RANGE, 2),
63
+ }
64
+ row_defs.add(
65
+ RowDefinition(
66
+ colname="age_in_years",
67
+ label="Median (range)",
68
+ condition=(self.main_df[self.colname].notna()),
69
+ columns=columns,
70
+ )
71
+ )
72
+ return row_defs
@@ -0,0 +1,88 @@
1
+ import pandas as pd
2
+ from edc_constants.constants import FEMALE, MALE
3
+
4
+ from ..constants import N_ONLY, N_WITH_COL_PROP, N_WITH_ROW_PROP
5
+ from ..row import RowDefinition, RowDefinitions
6
+ from ..table import Table
7
+
8
+
9
+ class ArtTable(Table):
10
+ def __init__(self, main_df: pd.DataFrame = None):
11
+ super().__init__(colname=None, main_df=main_df, title="HIV Care")
12
+
13
+ @property
14
+ def row_definitions(self) -> RowDefinitions:
15
+ df_tmp = self.main_df.copy()
16
+ row_defs = RowDefinitions(reverse_rows=False)
17
+ row0 = RowDefinition(
18
+ title=self.title,
19
+ label=self.default_sublabel,
20
+ condition=(df_tmp["gender"].notna()),
21
+ columns={FEMALE: (N_ONLY, 2), MALE: (N_ONLY, 2), "All": (N_ONLY, 2)},
22
+ drop=False,
23
+ )
24
+ row_defs.add(row0)
25
+ columns = {
26
+ FEMALE: (N_WITH_COL_PROP, 2),
27
+ MALE: (N_WITH_COL_PROP, 2),
28
+ "All": (N_WITH_ROW_PROP, 2),
29
+ }
30
+
31
+ cond_art_stable = (
32
+ (df_tmp["on_rx_stable"] == "Yes")
33
+ & (df_tmp["vl_undetectable"] == "Yes")
34
+ & (df_tmp["art_six_months"] == "Yes")
35
+ )
36
+ row_defs.add(
37
+ RowDefinition(
38
+ label="Stable on ART (6m)",
39
+ condition=cond_art_stable,
40
+ columns=columns,
41
+ drop=True,
42
+ )
43
+ )
44
+
45
+ # look for anyone that is not stable by these three values
46
+ condition_partially_stable = (
47
+ (df_tmp["on_rx_stable"] == "Yes")
48
+ | (df_tmp["vl_undetectable"] == "Yes")
49
+ | (df_tmp["art_six_months"] == "Yes")
50
+ )
51
+ row_defs.add(
52
+ RowDefinition(
53
+ label="Other stable (`VL` or `on ART` or `stable 6m`)",
54
+ condition=condition_partially_stable,
55
+ columns=columns,
56
+ drop=True,
57
+ )
58
+ )
59
+
60
+ condition_other = (
61
+ (df_tmp["on_rx_stable"] == "No")
62
+ | (df_tmp["vl_undetectable"] == "No")
63
+ | (df_tmp["art_six_months"] == "No")
64
+ )
65
+
66
+ row_defs.add(
67
+ RowDefinition(
68
+ label="Other",
69
+ condition=condition_other,
70
+ columns=columns,
71
+ drop=True,
72
+ )
73
+ )
74
+
75
+ condition_not_recorded = (
76
+ (df_tmp["on_rx_stable"].isna())
77
+ & (df_tmp["vl_undetectable"].isna())
78
+ & (df_tmp["art_six_months"].isna())
79
+ )
80
+ row_defs.add(
81
+ RowDefinition(
82
+ label="Not recorded",
83
+ condition=condition_not_recorded,
84
+ columns=columns,
85
+ drop=True,
86
+ )
87
+ )
88
+ return row_defs
@@ -0,0 +1,125 @@
1
+ import pandas as pd
2
+ from edc_constants.constants import FEMALE, MALE
3
+
4
+ from ..constants import (
5
+ MEDIAN_IQR,
6
+ MEDIAN_RANGE,
7
+ N_ONLY,
8
+ N_WITH_COL_PROP,
9
+ N_WITH_ROW_PROP,
10
+ )
11
+ from ..row import RowDefinition, RowDefinitions
12
+ from ..table import Table
13
+
14
+
15
+ class BmiTable(Table):
16
+
17
+ colname = "calculated_bmi_value"
18
+
19
+ def __init__(self, main_df: pd.DataFrame = None):
20
+ super().__init__(
21
+ colname="calculated_bmi_value",
22
+ main_df=main_df,
23
+ title="BMI categories (kg/m2)",
24
+ )
25
+
26
+ @property
27
+ def row_definitions(self) -> RowDefinitions:
28
+ df_tmp = self.main_df.copy()
29
+ row_defs = RowDefinitions(reverse_rows=False)
30
+ row0 = RowDefinition(
31
+ title=self.title,
32
+ label=self.default_sublabel,
33
+ condition=(df_tmp["gender"].notna()),
34
+ columns={FEMALE: (N_ONLY, 2), MALE: (N_ONLY, 2), "All": (N_ONLY, 2)},
35
+ drop=False,
36
+ )
37
+ row_defs.add(row0)
38
+ columns = {
39
+ FEMALE: (N_WITH_COL_PROP, 2),
40
+ MALE: (N_WITH_COL_PROP, 2),
41
+ "All": (N_WITH_ROW_PROP, 2),
42
+ }
43
+ row_defs.add(
44
+ RowDefinition(
45
+ colname="calculated_bmi_value",
46
+ label="Less than 18.5",
47
+ condition=(df_tmp[self.colname] < 18.5),
48
+ columns=columns,
49
+ drop=False,
50
+ )
51
+ )
52
+ row_defs.add(
53
+ RowDefinition(
54
+ colname="calculated_bmi_value",
55
+ label="18.5-24.9",
56
+ condition=(df_tmp[self.colname] >= 18.5) & (df_tmp[self.colname] < 25.0),
57
+ columns=columns,
58
+ drop=False,
59
+ )
60
+ )
61
+ row_defs.add(
62
+ RowDefinition(
63
+ colname="calculated_bmi_value",
64
+ label="25.0-29.9",
65
+ condition=(df_tmp[self.colname] >= 25.0) & (df_tmp[self.colname] < 30.0),
66
+ columns=columns,
67
+ drop=False,
68
+ )
69
+ )
70
+ row_defs.add(
71
+ RowDefinition(
72
+ colname="calculated_bmi_value",
73
+ label="30.0-39.9",
74
+ condition=(df_tmp[self.colname] >= 30.0) & (df_tmp[self.colname] < 40.0),
75
+ columns=columns,
76
+ drop=False,
77
+ )
78
+ )
79
+ row_defs.add(
80
+ RowDefinition(
81
+ colname="calculated_bmi_value",
82
+ label="40 or above",
83
+ condition=(df_tmp[self.colname] >= 40.0),
84
+ columns=columns,
85
+ drop=False,
86
+ )
87
+ )
88
+ cond = df_tmp[self.colname].isna()
89
+ if len(df_tmp[cond]) > 0:
90
+ row_defs.add(
91
+ RowDefinition(
92
+ colname="calculated_bmi_value",
93
+ label="not measured",
94
+ condition=cond,
95
+ columns=columns,
96
+ drop=False,
97
+ )
98
+ )
99
+ row_defs.add(
100
+ RowDefinition(
101
+ colname="calculated_bmi_value",
102
+ label="Median (IQR)",
103
+ condition=(df_tmp["gender"].notna()),
104
+ columns={
105
+ FEMALE: (MEDIAN_IQR, 2),
106
+ MALE: (MEDIAN_IQR, 2),
107
+ "All": (MEDIAN_IQR, 2),
108
+ },
109
+ drop=False,
110
+ )
111
+ )
112
+ row_defs.add(
113
+ RowDefinition(
114
+ colname="calculated_bmi_value",
115
+ label="Median (range)",
116
+ condition=(df_tmp["gender"].notna()),
117
+ columns={
118
+ FEMALE: (MEDIAN_RANGE, 2),
119
+ MALE: (MEDIAN_RANGE, 2),
120
+ "All": (MEDIAN_RANGE, 2),
121
+ },
122
+ drop=False,
123
+ )
124
+ )
125
+ return row_defs
@@ -0,0 +1,103 @@
1
+ import pandas as pd
2
+ from edc_constants.constants import FEMALE, MALE
3
+
4
+ from ..constants import N_ONLY, N_WITH_COL_PROP, N_WITH_ROW_PROP
5
+ from ..row import RowDefinition, RowDefinitions
6
+ from ..table import Table
7
+
8
+
9
+ class BpTable(Table):
10
+
11
+ sys_col_name = "sys_blood_pressure_avg"
12
+ dia_col_name = "dia_blood_pressure_avg"
13
+
14
+ def __init__(self, main_df: pd.DataFrame = None):
15
+ super().__init__(
16
+ colname="",
17
+ main_df=main_df,
18
+ title="Blood pressure at baseline (mmHg)",
19
+ )
20
+ self.table_df = self.table_df.reindex(index=self.table_df.index[::-1])
21
+
22
+ @property
23
+ def row_definitions(self) -> RowDefinitions:
24
+ df_tmp = self.main_df.copy()
25
+ row_defs = RowDefinitions(reverse_rows=True)
26
+ row0 = RowDefinition(
27
+ title=self.title,
28
+ label=self.default_sublabel,
29
+ condition=(df_tmp["gender"].notna()),
30
+ columns={
31
+ FEMALE: (N_ONLY, 2),
32
+ MALE: (N_ONLY, 2),
33
+ "All": (N_ONLY, 2),
34
+ },
35
+ drop=False,
36
+ )
37
+ row_defs.add(row0)
38
+ columns = {
39
+ FEMALE: (N_WITH_COL_PROP, 2),
40
+ MALE: (N_WITH_COL_PROP, 2),
41
+ "All": (N_WITH_ROW_PROP, 2),
42
+ }
43
+ row_defs.add(
44
+ RowDefinition(
45
+ label="Severe hypertension (>=180/110)",
46
+ condition=(
47
+ (df_tmp[self.sys_col_name] >= 180) | (df_tmp[self.dia_col_name] >= 110)
48
+ ),
49
+ columns=columns,
50
+ drop=True,
51
+ )
52
+ )
53
+ row_defs.add(
54
+ RowDefinition(
55
+ label="Hypertension (>=140/90)",
56
+ condition=(
57
+ (df_tmp[self.sys_col_name] >= 140) | (df_tmp[self.dia_col_name] >= 90)
58
+ ),
59
+ columns=columns,
60
+ drop=True,
61
+ )
62
+ )
63
+ row_defs.add(
64
+ RowDefinition(
65
+ label="Pre-hypertension (<140/90)",
66
+ condition=(
67
+ (df_tmp[self.sys_col_name] >= 120) | (df_tmp[self.dia_col_name] >= 80)
68
+ ),
69
+ columns=columns,
70
+ drop=True,
71
+ )
72
+ )
73
+ row_defs.add(
74
+ RowDefinition(
75
+ label="Normal (<120/80)",
76
+ condition=(
77
+ (df_tmp[self.sys_col_name] >= 90) | (df_tmp[self.dia_col_name] >= 60)
78
+ ),
79
+ columns=columns,
80
+ drop=True,
81
+ )
82
+ )
83
+ row_defs.add(
84
+ RowDefinition(
85
+ label="Low (<90/60)",
86
+ condition=(
87
+ (df_tmp[self.sys_col_name] >= 0) | (df_tmp[self.dia_col_name] >= 0)
88
+ ),
89
+ columns=columns,
90
+ drop=True,
91
+ )
92
+ )
93
+ row_defs.add(
94
+ RowDefinition(
95
+ label="not measured",
96
+ condition=(
97
+ (df_tmp[self.sys_col_name].isna()) & (df_tmp[self.dia_col_name].isna())
98
+ ),
99
+ columns=columns,
100
+ drop=True,
101
+ )
102
+ )
103
+ return row_defs