meta-edc 1.0.7__py3-none-any.whl → 1.1.1__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 (63) hide show
  1. meta_ae/action_items.py +10 -2
  2. meta_ae/baker_recipes.py +1 -2
  3. meta_ae/tests/tests/test_actions.py +1 -2
  4. meta_analytics/README.rst +1 -2
  5. meta_analytics/notebooks/anu.ipynb +95 -0
  6. meta_analytics/notebooks/appointment_planning.ipynb +329 -0
  7. meta_analytics/notebooks/arvs.ipynb +103 -0
  8. meta_analytics/notebooks/cleaning/consent_v1_ext.ipynb +227 -0
  9. meta_analytics/notebooks/cleaning/offschedule_eos.ipynb +353 -0
  10. meta_analytics/notebooks/dsmc/renal_dysfunction.ipynb +435 -0
  11. meta_analytics/notebooks/endpoints/meta_endpoints_by_date.ipynb +664 -0
  12. meta_analytics/notebooks/followup_examination.ipynb +141 -0
  13. meta_analytics/notebooks/hba1c.ipynb +136 -0
  14. meta_analytics/notebooks/hiv_regimens.ipynb +122 -118
  15. meta_analytics/notebooks/incidence.ipynb +232 -0
  16. meta_analytics/notebooks/liver.ipynb +389 -0
  17. meta_analytics/notebooks/magreth.ipynb +645 -0
  18. meta_analytics/notebooks/monitoring_report.ipynb +721 -448
  19. meta_analytics/notebooks/pharmacy.ipynb +405 -306
  20. meta_analytics/notebooks/pharmacy_stock_202410.ipynb +306 -0
  21. meta_analytics/notebooks/steering.ipynb +61 -0
  22. meta_analytics/notebooks/undiagnosed/meta3_screening_consort_chart.ipynb +1176 -0
  23. meta_analytics/notebooks/undiagnosed/meta3_screening_undiagnosed.ipynb +519 -0
  24. meta_analytics/notebooks/undiagnosed/meta_screening_table2.ipynb +964 -0
  25. meta_analytics/notebooks/undiagnosed/screen_undiagnosed_or.ipynb +296 -0
  26. meta_analytics/notebooks/undiagnosed/screening.ipynb +273 -0
  27. meta_analytics/notebooks/undiagnosed/screening2.ipynb +958 -0
  28. meta_analytics/notebooks/undiagnosed/screening_undiagnosed_20241002.ipynb +958 -0
  29. meta_analytics/notebooks/ven.ipynb +191 -0
  30. meta_analytics/notebooks/vitals.ipynb +263 -0
  31. meta_edc/settings/debug.py +3 -2
  32. meta_edc/urls.py +1 -0
  33. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/METADATA +3 -3
  34. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/RECORD +62 -35
  35. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/WHEEL +1 -1
  36. meta_labs/reportables.py +14 -11
  37. meta_labs/tests/test_reportables.py +33 -12
  38. meta_pharmacy/notebooks/pharmacy.ipynb +41 -0
  39. meta_prn/admin/offschedule_pregnancy_admin.py +3 -3
  40. meta_prn/admin/onschedule_dm_referral_admin.py +5 -5
  41. meta_prn/form_validators/end_of_study.py +2 -2
  42. meta_prn/migrations/0063_historicaloffstudymedication_singleton_field_and_more.py +37 -0
  43. meta_prn/migrations/0064_auto_20250602_2143.py +18 -0
  44. meta_prn/models/end_of_study.py +2 -0
  45. meta_prn/models/off_study_medication.py +2 -0
  46. meta_reports/admin/last_imp_refill_admin.py +3 -2
  47. meta_screening/eligibility/eligibility_part_three/base_eligibility_part_three.py +59 -47
  48. meta_screening/form_validators/screening_part_three.py +6 -1
  49. meta_screening/tests/meta_test_case_mixin.py +3 -0
  50. meta_screening/tests/tests/test_forms.py +9 -2
  51. meta_screening/tests/tests/test_screening_part_three.py +11 -14
  52. meta_subject/action_items.py +2 -3
  53. meta_subject/choices.py +2 -1
  54. meta_subject/form_validators/delivery_form_validator.py +1 -0
  55. meta_subject/forms/blood_results/blood_results_rft_form.py +60 -3
  56. meta_subject/forms/delivery_form.py +2 -0
  57. meta_subject/migrations/0223_bloodresultsfbc_errors_bloodresultsgludummy_errors_and_more.py +83 -0
  58. meta_subject/migrations/0224_bloodresultsfbc_abnormal_summary_and_more.py +153 -0
  59. meta_subject/tests/tests/test_egfr.py +5 -5
  60. meta_analytics/dataframes/enrolled/__init__.py +0 -0
  61. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/licenses/AUTHORS.rst +0 -0
  62. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/licenses/LICENSE +0 -0
  63. {meta_edc-1.0.7.dist-info → meta_edc-1.1.1.dist-info}/top_level.txt +0 -0
meta_ae/action_items.py CHANGED
@@ -12,7 +12,16 @@ from edc_adverse_event.constants import (
12
12
  DEATH_REPORT_ACTION,
13
13
  DEATH_REPORT_TMG_ACTION,
14
14
  )
15
- from edc_constants.constants import CLOSED, DEAD, HIGH_PRIORITY, NO, YES
15
+ from edc_constants.constants import (
16
+ CLOSED,
17
+ DEAD,
18
+ GRADE3,
19
+ GRADE4,
20
+ GRADE5,
21
+ HIGH_PRIORITY,
22
+ NO,
23
+ YES,
24
+ )
16
25
  from edc_lab_results.constants import (
17
26
  BLOOD_RESULTS_FBC_ACTION,
18
27
  BLOOD_RESULTS_GLU_ACTION,
@@ -22,7 +31,6 @@ from edc_lab_results.constants import (
22
31
  )
23
32
  from edc_ltfu.constants import LOST_TO_FOLLOWUP
24
33
  from edc_notification.utils import get_email_contacts
25
- from edc_reportable import GRADE3, GRADE4, GRADE5
26
34
  from edc_visit_schedule.utils import get_offschedule_models
27
35
 
28
36
  from meta_prn.constants import OFFSTUDY_MEDICATION_ACTION
meta_ae/baker_recipes.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from edc_adverse_event.constants import NOT_RELATED
2
- from edc_constants.constants import NO, NOT_APPLICABLE, YES
3
- from edc_reportable.constants import GRADE4
2
+ from edc_constants.constants import GRADE4, NO, NOT_APPLICABLE, YES
4
3
  from edc_utils.date import get_utcnow
5
4
  from model_bakery.recipe import Recipe
6
5
 
@@ -7,8 +7,7 @@ from edc_adverse_event.constants import (
7
7
  DEATH_REPORT_ACTION,
8
8
  DEATH_REPORT_TMG_ACTION,
9
9
  )
10
- from edc_constants.constants import CLOSED, NEW
11
- from edc_reportable.constants import GRADE4, GRADE5
10
+ from edc_constants.constants import CLOSED, GRADE4, GRADE5, NEW
12
11
  from model_bakery import baker
13
12
 
14
13
  from meta_screening.tests.meta_test_case_mixin import MetaTestCaseMixin
meta_analytics/README.rst CHANGED
@@ -1,5 +1,4 @@
1
-
2
-
1
+ pip install -U pdfkit great_tables scipy django_pandas dj_notebook
3
2
 
4
3
  import pandas as pd
5
4
  import numpy as np
@@ -0,0 +1,95 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "0",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "%%capture\n",
11
+ "import pandas as pd\n",
12
+ "from django_pandas.io import read_frame\n",
13
+ "from pathlib import Path\n",
14
+ "from dj_notebook import activate\n",
15
+ "\n",
16
+ "plus = activate(dotenv_file=\"/Users/erikvw/source/edc_source/meta-edc/.env\")\n",
17
+ "report_folder = Path(\"/Users/erikvw/Documents/ucl/protocols/meta3/reports/\")\n",
18
+ "# output is suppressed -- normally would spew out all the edc loading messages\n"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "id": "1",
25
+ "metadata": {},
26
+ "outputs": [],
27
+ "source": [
28
+ "from edc_pdutils.dataframes import get_crf\n",
29
+ "\n",
30
+ "df_patient_history = get_crf(model=\"meta_subject.patienthistory\", subject_visit_model=\"meta_subject.subjectvisit\")"
31
+ ]
32
+ },
33
+ {
34
+ "cell_type": "code",
35
+ "execution_count": null,
36
+ "id": "2",
37
+ "metadata": {},
38
+ "outputs": [],
39
+ "source": [
40
+ "df_blood_results_rft = get_crf(model=\"meta_subject.bloodresultsrft\", subject_visit_model=\"meta_subject.subjectvisit\")\n"
41
+ ]
42
+ },
43
+ {
44
+ "cell_type": "code",
45
+ "execution_count": null,
46
+ "id": "3",
47
+ "metadata": {},
48
+ "outputs": [],
49
+ "source": [
50
+ "df_patient_history.to_csv(Path(\"/Users/erikvw/Documents/ucl/protocols/meta3/analysis/\") / \"patient_history.csv\")"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "code",
55
+ "execution_count": null,
56
+ "id": "4",
57
+ "metadata": {},
58
+ "outputs": [],
59
+ "source": [
60
+ "df_blood_results_rft.to_csv(Path(\"/Users/erikvw/Documents/ucl/protocols/meta3/analysis/\") / \"bllod_results_rft.csv\")"
61
+ ]
62
+ },
63
+ {
64
+ "cell_type": "code",
65
+ "execution_count": null,
66
+ "id": "5",
67
+ "metadata": {},
68
+ "outputs": [],
69
+ "source": [
70
+ "df_visit ="
71
+ ]
72
+ }
73
+ ],
74
+ "metadata": {
75
+ "kernelspec": {
76
+ "display_name": "Python 3",
77
+ "language": "python",
78
+ "name": "python3"
79
+ },
80
+ "language_info": {
81
+ "codemirror_mode": {
82
+ "name": "ipython",
83
+ "version": 2
84
+ },
85
+ "file_extension": ".py",
86
+ "mimetype": "text/x-python",
87
+ "name": "python",
88
+ "nbconvert_exporter": "python",
89
+ "pygments_lexer": "ipython2",
90
+ "version": "2.7.6"
91
+ }
92
+ },
93
+ "nbformat": 4,
94
+ "nbformat_minor": 5
95
+ }
@@ -0,0 +1,329 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "0",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": [
10
+ "%%capture\n",
11
+ "import os\n",
12
+ "from pathlib import Path\n",
13
+ "import pandas as pd\n",
14
+ "from dj_notebook import activate\n",
15
+ "import numpy as np\n",
16
+ "from django_pandas.io import read_frame\n",
17
+ "\n",
18
+ "env_file = os.environ[\"META_ENV\"]\n",
19
+ "reports_folder = Path(os.environ[\"META_REPORTS_FOLDER\"])\n",
20
+ "analysis_folder = Path(os.environ[\"META_ANALYSIS_FOLDER\"])\n",
21
+ "pharmacy_folder = Path(os.environ[\"META_PHARMACY_FOLDER\"])\n",
22
+ "plus = activate(dotenv_file=env_file)\n",
23
+ "pd.set_option('future.no_silent_downcasting', True)"
24
+ ]
25
+ },
26
+ {
27
+ "cell_type": "code",
28
+ "execution_count": null,
29
+ "id": "1",
30
+ "metadata": {},
31
+ "outputs": [],
32
+ "source": [
33
+ "import pdfkit\n",
34
+ "from datetime import date\n",
35
+ "from edc_pdutils.dataframes import get_subject_visit\n",
36
+ "from meta_analytics.dataframes import get_glucose_fbg_ogtt_df, get_glucose_fbg_df\n",
37
+ "from meta_visit_schedule.constants import MONTH15, MONTH18, MONTH21, MONTH27, MONTH30, MONTH33, MONTH39\n",
38
+ "from meta_analytics.dataframes import GlucoseEndpointsByDate\n",
39
+ "from scipy.stats import chi2\n",
40
+ "from great_tables import loc, style, md\n",
41
+ "from meta_analytics.dataframes import get_eos_df\n",
42
+ "from meta_analytics.utils import df_as_great_table, df_as_great_table2\n",
43
+ "from meta_prn.models import LossToFollowup\n",
44
+ "from edc_visit_schedule.models import SubjectScheduleHistory\n",
45
+ "from edc_appointment.analytics import get_appointment_df\n",
46
+ "from edc_appointment.constants import NEW_APPT, CANCELLED_APPT, ONTIME_APPT, MISSED_APPT, SCHEDULED_APPT, COMPLETE_APPT, INCOMPLETE_APPT, IN_PROGRESS_APPT, UNSCHEDULED_APPT\n",
47
+ "from edc_constants.constants import YES\n",
48
+ "from meta_consent.models import SubjectConsentV1Ext"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": null,
54
+ "id": "2",
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "html_data = []\n",
59
+ "cutoff_date = date(2025,3, 31)\n",
60
+ "end_of_trial_date= date(2026,3, 1)\n",
61
+ "document_title = f\"<h2>Monitoring Report: {cutoff_date.strftime('%B %Y')}</h2><h5>Data Download: {cutoff_date.strftime('%d %B %Y')}</h5>\"\n",
62
+ "study_title = 'META3 - Metformin treatment for diabetes prevention in Africa'\n",
63
+ "pdf_filename = f\"monitoring_report_{cutoff_date.strftime('%Y%m%d')}.pdf\"\n",
64
+ "\n",
65
+ "column_headers = {\"appt_datetime\": \"Appointment\", \"year\": \"Year\", \"month\": \"Month\", \"10\": \"Hindu Mandal\", \"20\": \"Amana\", \"30\": \"Temeke\", \"40\": \"Mwananyamala\", \"60\": \"Mnazi Moja\", \"total\": \"Total\"}\n",
66
+ "\n"
67
+ ]
68
+ },
69
+ {
70
+ "cell_type": "code",
71
+ "execution_count": null,
72
+ "id": "3",
73
+ "metadata": {},
74
+ "outputs": [],
75
+ "source": [
76
+ "df_visit = get_subject_visit(\"meta_subject.subjectvisit\")\n",
77
+ "df_visit = df_visit[df_visit.appt_datetime.dt.date<=cutoff_date]\n",
78
+ "df_appointments = get_appointment_df()\n",
79
+ "df_appointments[\"site_id\"] = df_appointments.site_id.astype(str)\n",
80
+ "cls = GlucoseEndpointsByDate()\n",
81
+ "cls.run()\n",
82
+ "df_endpoint = cls.endpoint_only_df.copy()\n",
83
+ "df_glucose = get_glucose_fbg_ogtt_df()\n",
84
+ "df_glucose_fbg = get_glucose_fbg_df()\n",
85
+ "df_glucose = pd.concat([df_glucose, df_glucose_fbg])\n",
86
+ "\n",
87
+ "enrolled = df_visit.copy()\n",
88
+ "enrolled[\"site_id\"] = enrolled[\"site_id\"].astype(str)\n",
89
+ "enrolled_pivot = (\n",
90
+ " enrolled\n",
91
+ " .query(\"visit_code==1000.0\").groupby([\"site_id\"])\n",
92
+ " .size()\n",
93
+ " .reset_index()\n",
94
+ " .pivot_table(columns=\"site_id\", values=0, observed=True)\n",
95
+ ")\n",
96
+ "enrolled_pivot.columns.name=\"\"\n",
97
+ "enrolled_pivot[\"total\"] = enrolled_pivot[[\"10\", \"20\",\"30\",\"40\",\"60\"]].sum(axis=1)\n",
98
+ "\n"
99
+ ]
100
+ },
101
+ {
102
+ "cell_type": "code",
103
+ "execution_count": null,
104
+ "id": "4",
105
+ "metadata": {},
106
+ "outputs": [],
107
+ "source": [
108
+ "# Table 1f Future scheduled appointments per month\n",
109
+ "df_appt_pivot = (\n",
110
+ " # df_appointments.query(\"appt_datetime<=@cutoff_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and ~appt_status.isin([@NEW_APPT])\")\n",
111
+ " df_appointments.query(\"@cutoff_date<=appt_datetime<=@end_of_trial_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status.isin([@NEW_APPT])\")\n",
112
+ " .set_index(\"appt_datetime\")\n",
113
+ " .groupby(by=[\"site_id\", pd.Grouper(freq=\"ME\")])\n",
114
+ " .size()\n",
115
+ " .to_frame()\n",
116
+ " .reset_index()\n",
117
+ " .rename(columns={0:\"patients\"})\n",
118
+ " .pivot(index=\"appt_datetime\", columns=\"site_id\", values=\"patients\")\n",
119
+ " .reset_index()\n",
120
+ " .fillna(0)\n",
121
+ ")\n",
122
+ "\n",
123
+ "df_appt_pivot.columns.name = None\n",
124
+ "df_appt_pivot[\"total\"] = df_appt_pivot.iloc[:,1:].sum(axis=1)\n",
125
+ "df_appt_pivot[\"appt_datetime\"] = df_appt_pivot.appt_datetime.dt.strftime(\"%Y-%m\")\n",
126
+ "sum_row = df_appt_pivot.select_dtypes(include='float64').sum()\n",
127
+ "sum_row['appt_datetime'] = 'Total-'\n",
128
+ "sum_row_df = pd.DataFrame(sum_row).T\n",
129
+ "df_appt_pivot = pd.concat([df_appt_pivot, sum_row_df], axis=0)\n",
130
+ "df_appt_pivot[[\"year\", \"month\"]] = df_appt_pivot[\"appt_datetime\"].str.split(\"-\", expand=True)\n",
131
+ "\n",
132
+ "df_appt_pivot2 = (\n",
133
+ " # df_appointments.query(\"appt_datetime<=@cutoff_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and ~appt_status.isin([@NEW_APPT])\")\n",
134
+ " df_appointments.query(\"@cutoff_date<=appt_datetime<=@end_of_trial_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status.isin([@NEW_APPT])\")\n",
135
+ " .set_index(\"visit_code\")\n",
136
+ " .groupby(by=[\"site_id\", \"visit_code\"])\n",
137
+ " .agg([\"last\"])\n",
138
+ " .size()\n",
139
+ " .to_frame()\n",
140
+ " .reset_index()\n",
141
+ " .rename(columns={0:\"patients\"})\n",
142
+ " .pivot(index=\"visit_code\", columns=\"site_id\", values=\"patients\")\n",
143
+ " .reset_index()\n",
144
+ " .fillna(0)\n",
145
+ ")\n",
146
+ "\n",
147
+ "df_appt_pivot2.columns.name = None\n",
148
+ "df_appt_pivot2[\"total\"] = df_appt_pivot2.iloc[:,1:].sum(axis=1)\n",
149
+ "df_appt_pivot2[\"visit_code\"] = df_appt_pivot2.visit_code.astype(str)\n",
150
+ "sum_row = df_appt_pivot2.select_dtypes(include='float64').sum()\n",
151
+ "sum_row['visit_code'] = 'Total-'\n",
152
+ "sum_row_df = pd.DataFrame(sum_row).T\n",
153
+ "df_appt_pivot2 = pd.concat([df_appt_pivot2, sum_row_df], axis=0)\n",
154
+ "\n",
155
+ "# df_appt_pivot2[[\"year\", \"month\"]] = df_appt_pivot2[\"appt_datetime\"].str.split(\"-\", expand=True)\n",
156
+ "\n",
157
+ "\n",
158
+ "df_appt_pivot2"
159
+ ]
160
+ },
161
+ {
162
+ "cell_type": "code",
163
+ "execution_count": null,
164
+ "id": "5",
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": [
168
+ "def get_df_appt(criteria:str):\n",
169
+ " df_appt = (\n",
170
+ " df_appointments.query(\"@cutoff_date<=appt_datetime<=@end_of_trial_date and appt_reason==@SCHEDULED_APPT and appt_timing==@ONTIME_APPT and appt_status.isin([@NEW_APPT]) and visit_code<2000.0\")\n",
171
+ " .groupby([\"site_id\", \"appt_datetime\"])\n",
172
+ " .agg(\"last\")\n",
173
+ " .reset_index()\n",
174
+ " .query(criteria)\n",
175
+ " .set_index(\"appt_datetime\")\n",
176
+ " .groupby(by=[\"site_id\", pd.Grouper(freq=\"ME\")])\n",
177
+ " .size()\n",
178
+ " .to_frame()\n",
179
+ " .reset_index()\n",
180
+ " .rename(columns={0:\"patients\"})\n",
181
+ " .pivot(index=\"appt_datetime\", columns=\"site_id\", values=\"patients\")\n",
182
+ " .reset_index()\n",
183
+ " .fillna(0)\n",
184
+ " )\n",
185
+ " df_appt.columns.name = None\n",
186
+ " df_appt[\"total\"] = df_appt.iloc[:,1:].sum(axis=1)\n",
187
+ " sum_row = df_appt.select_dtypes(include='float64').sum()\n",
188
+ " sum_row_df = pd.DataFrame(sum_row).T\n",
189
+ " df_appt = pd.concat([df_appt, sum_row_df], axis=0)\n",
190
+ " df_appt[\"appt_datetime\"] = df_appt.appt_datetime.dt.strftime(\"%Y-%m\")\n",
191
+ " df_appt[[\"year\", \"month\"]] = df_appt[\"appt_datetime\"].str.split(\"-\", expand=True)\n",
192
+ " df_appt[\"year\"] = df_appt[\"year\"].fillna(\"Total\")\n",
193
+ " return df_appt\n",
194
+ "\n",
195
+ "\n",
196
+ "gt = df_as_great_table2(\n",
197
+ " get_df_appt(criteria=\"visit_code.isin([1360.0, 1480.0])\"),\n",
198
+ " title=\"Table 1f: Participants who will complete followup on 1360 or 1480 before 2026-03-01\",\n",
199
+ " # subtitle=\"Visit codes 1360 or 1480 only\",\n",
200
+ " rowname_col=\"month\",\n",
201
+ " groupname_col=\"year\",\n",
202
+ ")\n",
203
+ "gt = (\n",
204
+ " gt\n",
205
+ " .cols_label({k:v for k, v in column_headers.items() if k!=\"label\"})\n",
206
+ " .cols_align(align=\"center\", columns=[\"appt_datetime\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
207
+ " .cols_align(align=\"left\", columns=[\"month\", \"year\"])\n",
208
+ " .fmt_number(columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"], decimals=0)\n",
209
+ " .tab_source_note(source_note=f\"Scheduled appointment date is on or after {cutoff_date.strftime('%d %B %Y')} and before {end_of_trial_date.strftime('%d %B %Y')}.\")\n",
210
+ " .tab_style(\n",
211
+ " style=[\n",
212
+ " style.text(color=\"black\", weight=\"bold\"),\n",
213
+ " style.fill(color=\"lightgray\")\n",
214
+ " ],\n",
215
+ " locations=loc.row_groups()\n",
216
+ " )\n",
217
+ ")\n",
218
+ "html_data.append(gt.as_raw_html())\n",
219
+ "gt.show()"
220
+ ]
221
+ },
222
+ {
223
+ "cell_type": "code",
224
+ "execution_count": null,
225
+ "id": "6",
226
+ "metadata": {},
227
+ "outputs": [],
228
+ "source": [
229
+ "\n",
230
+ "gt = df_as_great_table2(\n",
231
+ " get_df_appt(criteria=\"~visit_code.isin([1360.0, 1480.0])\"),\n",
232
+ " title=\"Table 1f: Participants who will NOT complete followup on 1360 or 1480 before 2026-03-01\",\n",
233
+ " rowname_col=\"month\",\n",
234
+ " groupname_col=\"year\",\n",
235
+ ")\n",
236
+ "gt = (\n",
237
+ " gt\n",
238
+ " .cols_label({k:v for k, v in column_headers.items() if k!=\"label\"})\n",
239
+ " .cols_align(align=\"center\", columns=[\"appt_datetime\", \"10\", \"20\", \"30\", \"40\", \"60\", \"total\"])\n",
240
+ " .cols_align(align=\"left\", columns=[\"month\", \"year\"])\n",
241
+ " .fmt_number(columns=[\"10\", \"20\", \"30\", \"40\", \"60\", \"total\"], decimals=0)\n",
242
+ " .tab_source_note(source_note=f\"Scheduled appointment date is on or after {cutoff_date.strftime('%d %B %Y')} and before {end_of_trial_date.strftime('%d %B %Y')}.\")\n",
243
+ " .tab_style(\n",
244
+ " style=[\n",
245
+ " style.text(color=\"black\", weight=\"bold\"),\n",
246
+ " style.fill(color=\"lightgray\")\n",
247
+ " ],\n",
248
+ " locations=loc.row_groups()\n",
249
+ " )\n",
250
+ ")\n",
251
+ "html_data.append(gt.as_raw_html())\n",
252
+ "gt.show()"
253
+ ]
254
+ },
255
+ {
256
+ "cell_type": "code",
257
+ "execution_count": null,
258
+ "id": "7",
259
+ "metadata": {},
260
+ "outputs": [],
261
+ "source": [
262
+ "# gather raw html\n",
263
+ "raw_html = [f'<div class=\"page-break\">{s}</div>' for s in html_data]\n",
264
+ "style_css = \"\"\"\n",
265
+ "<style>\n",
266
+ " .page-break {\n",
267
+ " page-break-inside: avoid; /* Always add page break before this element */\n",
268
+ " }\n",
269
+ " .table-header {\n",
270
+ " font-weight: bold;\n",
271
+ " font-size: 18px;\n",
272
+ " text-align: center;\n",
273
+ " border-bottom: None;\n",
274
+ " }\n",
275
+ "</style>\n",
276
+ "\"\"\"\n",
277
+ "raw_html = ''.join(raw_html)\n",
278
+ "raw_html = f'<!DOCTYPE html>\\n<html lang=\"en\">\\n{style_css}\\n<head>\\n<meta charset=\"utf-8\"/>\\n</head>\\n<body>\\n' + document_title + raw_html + '\\n</body>\\n</html>\\n'"
279
+ ]
280
+ },
281
+ {
282
+ "cell_type": "code",
283
+ "execution_count": null,
284
+ "id": "8",
285
+ "metadata": {},
286
+ "outputs": [],
287
+ "source": [
288
+ "pdfkit.from_string(raw_html, str(analysis_folder / pdf_filename),\n",
289
+ "options={\n",
290
+ " 'footer-center': 'Page [page] of [topage]',\n",
291
+ " 'footer-font-size': '8',\n",
292
+ " 'footer-spacing': '5',\n",
293
+ " 'encoding': \"UTF-8\",\n",
294
+ " 'margin-top':'10mm',\n",
295
+ " 'margin-right':'15mm',\n",
296
+ " 'margin-bottom':'15mm',\n",
297
+ " 'margin-left':'15mm',\n",
298
+ " 'header-center': study_title,\n",
299
+ " 'header-font-size': '6',\n",
300
+ " 'header-spacing': '0',\n",
301
+ " 'disable-javascript': None,\n",
302
+ " 'no-outline': None,\n",
303
+ "},\n",
304
+ "verbose=True)"
305
+ ]
306
+ }
307
+ ],
308
+ "metadata": {
309
+ "kernelspec": {
310
+ "display_name": "Python 3",
311
+ "language": "python",
312
+ "name": "python3"
313
+ },
314
+ "language_info": {
315
+ "codemirror_mode": {
316
+ "name": "ipython",
317
+ "version": 2
318
+ },
319
+ "file_extension": ".py",
320
+ "mimetype": "text/x-python",
321
+ "name": "python",
322
+ "nbconvert_exporter": "python",
323
+ "pygments_lexer": "ipython2",
324
+ "version": "2.7.6"
325
+ }
326
+ },
327
+ "nbformat": 4,
328
+ "nbformat_minor": 5
329
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": null,
6
+ "id": "0",
7
+ "metadata": {},
8
+ "outputs": [],
9
+ "source": []
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": null,
14
+ "id": "1",
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "%%capture\n",
19
+ "import os\n",
20
+ "import pandas as pd\n",
21
+ "import numpy as np\n",
22
+ "from dj_notebook import activate\n",
23
+ "from pathlib import Path\n",
24
+ "\n",
25
+ "env_file = os.environ[\"META_ENV\"]\n",
26
+ "analysis_folder = Path(os.environ[\"META_ANALYSIS_FOLDER\"])\n",
27
+ "reports_folder = Path(os.environ[\"META_ANALYSIS_FOLDER\"])\n",
28
+ "plus = activate(dotenv_file=env_file)"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": null,
34
+ "id": "2",
35
+ "metadata": {},
36
+ "outputs": [],
37
+ "source": [
38
+ "from edc_pdutils.dataframes import get_crf"
39
+ ]
40
+ },
41
+ {
42
+ "cell_type": "code",
43
+ "execution_count": null,
44
+ "id": "3",
45
+ "metadata": {},
46
+ "outputs": [],
47
+ "source": [
48
+ "df_patient_history = get_crf(\"meta_subject.patienthistory\", subject_visit_model=\"meta_subject.subjectvisit\")"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": null,
54
+ "id": "4",
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "df_patient_history[['subject_identifier', \"current_arv_regimen\"]].groupby(by=[\"current_arv_regimen\"])[\"current_arv_regimen\"].value_counts()"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": null,
64
+ "id": "5",
65
+ "metadata": {},
66
+ "outputs": [],
67
+ "source": [
68
+ "df_patient_history[['subject_identifier', \"current_arv_regimen\", \"other_current_arv_regimen\"]][[ \"other_current_arv_regimen\"]].value_counts()"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": null,
74
+ "id": "6",
75
+ "metadata": {},
76
+ "outputs": [],
77
+ "source": [
78
+ "df_patient_history[ \"other_current_arv_regimen\"] = df_patient_history[ \"other_current_arv_regimen\"].apply(lambda x : x.split(\"-\")[0])"
79
+ ]
80
+ }
81
+ ],
82
+ "metadata": {
83
+ "kernelspec": {
84
+ "display_name": "Python 3",
85
+ "language": "python",
86
+ "name": "python3"
87
+ },
88
+ "language_info": {
89
+ "codemirror_mode": {
90
+ "name": "ipython",
91
+ "version": 2
92
+ },
93
+ "file_extension": ".py",
94
+ "mimetype": "text/x-python",
95
+ "name": "python",
96
+ "nbconvert_exporter": "python",
97
+ "pygments_lexer": "ipython2",
98
+ "version": "2.7.6"
99
+ }
100
+ },
101
+ "nbformat": 4,
102
+ "nbformat_minor": 5
103
+ }