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
@@ -2,10 +2,10 @@
2
2
  "cells": [
3
3
  {
4
4
  "cell_type": "code",
5
- "id": "initial_id",
6
- "metadata": {
7
- "collapsed": true
8
- },
5
+ "execution_count": null,
6
+ "id": "0",
7
+ "metadata": {},
8
+ "outputs": [],
9
9
  "source": [
10
10
  "%%capture\n",
11
11
  "import os\n",
@@ -13,20 +13,20 @@
13
13
  "import pandas as pd\n",
14
14
  "from dj_notebook import activate\n",
15
15
  "import numpy as np\n",
16
- "from django_pandas.io import read_frame\n",
17
16
  "\n",
18
17
  "env_file = os.environ[\"META_ENV\"]\n",
19
18
  "reports_folder = Path(os.environ[\"META_REPORTS_FOLDER\"])\n",
20
19
  "analysis_folder = Path(os.environ[\"META_ANALYSIS_FOLDER\"])\n",
21
20
  "pharmacy_folder = Path(os.environ[\"META_PHARMACY_FOLDER\"])\n",
22
21
  "plus = activate(dotenv_file=env_file)"
23
- ],
24
- "outputs": [],
25
- "execution_count": null
22
+ ]
26
23
  },
27
24
  {
28
- "metadata": {},
29
25
  "cell_type": "code",
26
+ "execution_count": null,
27
+ "id": "1",
28
+ "metadata": {},
29
+ "outputs": [],
30
30
  "source": [
31
31
  "from edc_pharmacy.analytics.dataframes import no_stock_for_subjects_df\n",
32
32
  "from datetime import datetime\n",
@@ -46,15 +46,85 @@
46
46
  "from edc_visit_schedule.site_visit_schedules import site_visit_schedules\n",
47
47
  "from edc_pharmacy.models import Container\n",
48
48
  "from great_tables import GT, html, loc, style\n",
49
- "from PIL import Image\n"
50
- ],
51
- "id": "a4c997d7adda770",
49
+ "from PIL import Image\n",
50
+ "from edc_pdutils.dataframes.get_subject_visit import convert_visit_code_to_float\n"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "code",
55
+ "execution_count": null,
56
+ "id": "2",
57
+ "metadata": {},
52
58
  "outputs": [],
53
- "execution_count": null
59
+ "source": [
60
+ "from edc_model_to_dataframe import read_frame_edc\n",
61
+ "from meta_subject.models import FollowupExamination\n",
62
+ "\n",
63
+ "df = read_frame_edc(FollowupExamination.objects.all(), drop_sys_columns=True, drop_action_item_columns=True)\n",
64
+ "df = df.replace(\"none\", pd.NA)\n",
65
+ "df = df.replace(\"none\", pd.NA)\n",
66
+ "df = df.fillna(pd.NA)\n",
67
+ "convert_visit_code_to_float(df)"
68
+ ]
54
69
  },
55
70
  {
71
+ "cell_type": "code",
72
+ "execution_count": null,
73
+ "id": "3",
56
74
  "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "from edc_analytics.stata import get_stata_labels_from_model\n",
78
+ "\n",
79
+ "df = df[[\"subject_identifier\", \"subject_visit_id\", \"report_datetime\", \"visit_code\", \"site_id\", \"site_name\", \"visit_reason\", \"symptoms\",\"symptoms_detail\", \"symptoms_sought_care\", \"symptoms_g3\", \"symptoms_g4\", \"comment\"]].copy().reset_index(drop=True)\n",
80
+ "\n",
81
+ "df = df.astype(\n",
82
+ " {col: \"Float64\" for col in df.select_dtypes(include=[\"float\", \"float64\"]).columns}\n",
83
+ ")\n",
84
+ "df_meds = df.astype(\n",
85
+ " {col: \"Int64\" for col in df.select_dtypes(include=[\"int\", \"int64\"]).columns}\n",
86
+ ")\n",
87
+ "df = df.astype(\n",
88
+ " {\n",
89
+ " col: \"datetime64[ns]\"\n",
90
+ " for col in df.select_dtypes(include=[\"datetime\", \"datetime64\"]).columns\n",
91
+ " }\n",
92
+ ")\n",
93
+ "df = df.astype(\n",
94
+ " {\n",
95
+ " col: str\n",
96
+ " for col in df.select_dtypes(include=[\"object\"]).columns\n",
97
+ " }\n",
98
+ ")\n",
99
+ "df = df.fillna(pd.NA)\n",
100
+ "\n",
101
+ "variable_labels = {}\n",
102
+ "variable_labels.update(**get_stata_labels_from_model(df, model=\"meta_subject.followupexamination\", suffix=None))\n",
103
+ "\n",
104
+ "df.to_stata(\n",
105
+ " path=analysis_folder / \"followupexamination.dta\",\n",
106
+ " variable_labels=variable_labels,\n",
107
+ " version=118,\n",
108
+ " write_index=False,\n",
109
+ ")"
110
+ ]
111
+ },
112
+ {
57
113
  "cell_type": "code",
114
+ "execution_count": null,
115
+ "id": "4",
116
+ "metadata": {},
117
+ "outputs": [],
118
+ "source": [
119
+ "df"
120
+ ]
121
+ },
122
+ {
123
+ "cell_type": "code",
124
+ "execution_count": null,
125
+ "id": "5",
126
+ "metadata": {},
127
+ "outputs": [],
58
128
  "source": [
59
129
  "\n",
60
130
  "def get_great_table(df:pd.DataFrame, title:str, footnote:str|None=None):\n",
@@ -90,51 +160,61 @@
90
160
  "\n",
91
161
  "\n",
92
162
  " )\n"
93
- ],
94
- "id": "6d4f230107499268",
95
- "outputs": [],
96
- "execution_count": null
163
+ ]
97
164
  },
98
165
  {
166
+ "cell_type": "code",
167
+ "execution_count": null,
168
+ "id": "6",
99
169
  "metadata": {},
170
+ "outputs": [],
171
+ "source": [
172
+ "start_from_appt_date = datetime(2025,5,15)"
173
+ ]
174
+ },
175
+ {
100
176
  "cell_type": "code",
177
+ "execution_count": null,
178
+ "id": "7",
179
+ "metadata": {},
180
+ "outputs": [],
101
181
  "source": [
102
182
  "# get rando\n",
103
183
  "df_rando = read_frame(RandomizationList.objects.values(\"subject_identifier\", \"assignment\").filter(subject_identifier__isnull=False))"
104
- ],
105
- "id": "1a5c145dc73ae1ca",
106
- "outputs": [],
107
- "execution_count": null
184
+ ]
108
185
  },
109
186
  {
110
- "metadata": {},
111
187
  "cell_type": "code",
188
+ "execution_count": null,
189
+ "id": "8",
190
+ "metadata": {},
191
+ "outputs": [],
112
192
  "source": [
113
193
  "# get appointments\n",
114
194
  "df_appt = get_appointment_df()\n",
115
- "print(f\"{len(df_appt[(df_appt.appt_status==NEW_APPT) & (df_appt.appt_datetime >= datetime(2025,4,4)) & (df_appt.appt_datetime < datetime(2026,3,1)) & (df_appt.visit_code!=1480.0)])} appointments after filtering\")"
116
- ],
117
- "id": "c0f532dc5f20aef6",
118
- "outputs": [],
119
- "execution_count": null
195
+ "print(f\"{len(df_appt[(df_appt.appt_status==NEW_APPT) & (df_appt.appt_datetime >= start_from_appt_date) & (df_appt.appt_datetime < datetime(2026,3,1)) & (df_appt.visit_code!=1480.0)])} appointments after filtering\")"
196
+ ]
120
197
  },
121
198
  {
122
- "metadata": {},
123
199
  "cell_type": "code",
200
+ "execution_count": null,
201
+ "id": "9",
202
+ "metadata": {},
203
+ "outputs": [],
124
204
  "source": [
125
205
  "# create a dataframe of subjects still on the 'schedule' schedule\n",
126
206
  "# use SubjectScheduleHistory where offschedule_datetime is null\n",
127
207
  "df_subject_schedule = read_frame(SubjectScheduleHistory.objects.values(\"subject_identifier\", \"visit_schedule_name\", \"schedule_name\", \"onschedule_datetime\", \"offschedule_datetime\").filter(offschedule_datetime__isnull=True, schedule_name=\"schedule\"))\n",
128
208
  "\n",
129
209
  "print(f\"{len(df_subject_schedule)} subjects currently onstudy\")"
130
- ],
131
- "id": "dd5fd238c0160518",
132
- "outputs": [],
133
- "execution_count": null
210
+ ]
134
211
  },
135
212
  {
136
- "metadata": {},
137
213
  "cell_type": "code",
214
+ "execution_count": null,
215
+ "id": "10",
216
+ "metadata": {},
217
+ "outputs": [],
138
218
  "source": [
139
219
  "# for now merge with the unfiltered df_appt\n",
140
220
  "df_main = df_subject_schedule.merge(\n",
@@ -150,39 +230,41 @@
150
230
  " (df_main.appt_status==NEW_APPT)\n",
151
231
  "].copy()\n",
152
232
  "print(f\"{len(df_main)} new appointments for subjects on study\")\n"
153
- ],
154
- "id": "13739396761c72c",
155
- "outputs": [],
156
- "execution_count": null
233
+ ]
157
234
  },
158
235
  {
159
- "metadata": {},
160
236
  "cell_type": "code",
237
+ "execution_count": null,
238
+ "id": "11",
239
+ "metadata": {},
240
+ "outputs": [],
161
241
  "source": [
162
242
  "# number of appointments before extended all subjects out to 48m\n",
163
243
  "df_grouped = df_main[\n",
164
- " (df_main.appt_datetime >= datetime(2025,4,4)) &\n",
244
+ " (df_main.appt_datetime >= start_from_appt_date) &\n",
165
245
  " (df_main.appt_datetime < datetime(2026,3,1)) &\n",
166
246
  " (df_main.visit_code!=1480.0)\n",
167
247
  "].visit_code.value_counts().reset_index(name=\"appointments\").sort_values(by=\"visit_code\", ascending=True).reset_index(drop=True)\n",
168
248
  "df_grouped[\"cumsum\"] = df_grouped.appointments.cumsum()\n",
169
249
  "df_grouped[\"cumsum\"].max()\n"
170
- ],
171
- "id": "fdf65539dce9fc30",
172
- "outputs": [],
173
- "execution_count": null
250
+ ]
174
251
  },
175
252
  {
176
- "metadata": {},
177
253
  "cell_type": "code",
178
- "source": "df_main",
179
- "id": "81d462e710e0ae61",
254
+ "execution_count": null,
255
+ "id": "12",
256
+ "metadata": {},
180
257
  "outputs": [],
181
- "execution_count": null
258
+ "source": [
259
+ "df_main"
260
+ ]
182
261
  },
183
262
  {
184
- "metadata": {},
185
263
  "cell_type": "code",
264
+ "execution_count": null,
265
+ "id": "13",
266
+ "metadata": {},
267
+ "outputs": [],
186
268
  "source": [
187
269
  "# now extend everyone to 48 months.\n",
188
270
  "# Subjects are in the process of consenting for extended\n",
@@ -212,34 +294,36 @@
212
294
  "# merge df_main back in\n",
213
295
  "df_pivot = df_pivot.merge(df_main[[\"subject_identifier\", \"visit_code\", \"appt_datetime\", \"appt_status\"]], on=[\"subject_identifier\",\"visit_code\"], how=\"left\")\n",
214
296
  "df_pivot"
215
- ],
216
- "id": "9bd1bf18017c18f2",
217
- "outputs": [],
218
- "execution_count": null
297
+ ]
219
298
  },
220
299
  {
221
- "metadata": {},
222
300
  "cell_type": "code",
223
- "source": "# len(df_pivot[(df_pivot.appt_datetime>=datetime(2025,1,1)) & (df_pivot.visit_code==MONTH48)])/3",
224
- "id": "80ab2992c6ccb274",
301
+ "execution_count": null,
302
+ "id": "14",
303
+ "metadata": {},
225
304
  "outputs": [],
226
- "execution_count": null
305
+ "source": [
306
+ "# len(df_pivot[(df_pivot.appt_datetime>=datetime(2025,1,1)) & (df_pivot.visit_code==MONTH48)])/3"
307
+ ]
227
308
  },
228
309
  {
229
- "metadata": {},
230
310
  "cell_type": "code",
311
+ "execution_count": null,
312
+ "id": "15",
313
+ "metadata": {},
314
+ "outputs": [],
231
315
  "source": [
232
316
  "# extend no one!\n",
233
317
  "# df_pivot = df_pivot[df_pivot.exists==1].copy()\n",
234
318
  "# df_pivot.reset_index(drop=True, inplace=True)\n"
235
- ],
236
- "id": "1e3098273f43e6b2",
237
- "outputs": [],
238
- "execution_count": null
319
+ ]
239
320
  },
240
321
  {
241
- "metadata": {},
242
322
  "cell_type": "code",
323
+ "execution_count": null,
324
+ "id": "16",
325
+ "metadata": {},
326
+ "outputs": [],
243
327
  "source": [
244
328
  "# add appointments do not have an appt_datetime, so calculate\n",
245
329
  "# using the visit schedule relative to baseline_datetime\n",
@@ -264,14 +348,14 @@
264
348
  "df_pivot.loc[df_pivot.exists==0.0, \"appt_status\"] = NEW_APPT\n",
265
349
  "\n",
266
350
  "print(f\"{len(df_pivot)} appointments\")"
267
- ],
268
- "id": "7483e4c8e47becc9",
269
- "outputs": [],
270
- "execution_count": null
351
+ ]
271
352
  },
272
353
  {
273
- "metadata": {},
274
354
  "cell_type": "code",
355
+ "execution_count": null,
356
+ "id": "17",
357
+ "metadata": {},
358
+ "outputs": [],
275
359
  "source": [
276
360
  "# df_subject_appointments is a dataframe of appointments\n",
277
361
  "# - only include NEW appointments\n",
@@ -280,51 +364,53 @@
280
364
  "cutoff_date = datetime(2026,3,1)\n",
281
365
  "df_subject_appointments = df_pivot[\n",
282
366
  " (df_pivot.appt_status==NEW_APPT) &\n",
283
- " (df_pivot.appt_datetime >= datetime(2025,4,4)) &\n",
367
+ " (df_pivot.appt_datetime >= start_from_appt_date) &\n",
284
368
  " (df_pivot.appt_datetime < cutoff_date) &\n",
285
369
  " (df_pivot.visit_code!=1480.0)\n",
286
370
  "].copy()\n",
287
371
  "print(f\"{len(df_subject_appointments)} appointments\")"
288
- ],
289
- "id": "12f04c24f098e88f",
290
- "outputs": [],
291
- "execution_count": null
372
+ ]
292
373
  },
293
374
  {
294
- "metadata": {},
295
375
  "cell_type": "code",
376
+ "execution_count": null,
377
+ "id": "18",
378
+ "metadata": {},
379
+ "outputs": [],
296
380
  "source": [
297
381
  "n = df_subject_appointments.subject_identifier.nunique()\n",
298
382
  "print(f\"{n} subjects\")\n"
299
- ],
300
- "id": "c32531751a339390",
301
- "outputs": [],
302
- "execution_count": null
383
+ ]
303
384
  },
304
385
  {
305
- "metadata": {},
306
386
  "cell_type": "code",
307
- "source": "(len(df_subject_appointments[df_subject_appointments.appt_datetime>=datetime(2026,1,1)])/36)/5",
308
- "id": "e7f520f32116ab1c",
387
+ "execution_count": null,
388
+ "id": "19",
389
+ "metadata": {},
309
390
  "outputs": [],
310
- "execution_count": null
391
+ "source": [
392
+ "(len(df_subject_appointments[df_subject_appointments.appt_datetime>=datetime(2026,1,1)])/36)/5"
393
+ ]
311
394
  },
312
395
  {
313
- "metadata": {},
314
396
  "cell_type": "code",
397
+ "execution_count": null,
398
+ "id": "20",
399
+ "metadata": {},
400
+ "outputs": [],
315
401
  "source": [
316
402
  "# summarize the appointments\n",
317
403
  "df_summary = df_subject_appointments.visit_code.value_counts().reset_index(name=\"appointments\").sort_values(by=[\"visit_code\"], ascending=True)\n",
318
404
  "df_summary[\"cumsum\"] = df_summary.appointments.cumsum()\n",
319
405
  "df_summary"
320
- ],
321
- "id": "f38ead26da10a1ef",
322
- "outputs": [],
323
- "execution_count": null
406
+ ]
324
407
  },
325
408
  {
326
- "metadata": {},
327
409
  "cell_type": "code",
410
+ "execution_count": null,
411
+ "id": "21",
412
+ "metadata": {},
413
+ "outputs": [],
328
414
  "source": [
329
415
  "df = df_subject_appointments.assignment.value_counts(dropna=False).reset_index()\n",
330
416
  "df.rename(columns={\"count\":\"appointments\"}, inplace=True)\n",
@@ -335,18 +421,18 @@
335
421
  "# filter\n",
336
422
  "df.loc[len(df)] = {\"appointments\": df.appointments.sum(), \"bottles\": df.bottles.sum(), \"tablets\": df.tablets.sum()}\n",
337
423
  "df"
338
- ],
339
- "id": "b6a56657d02b04ac",
340
- "outputs": [],
341
- "execution_count": null
424
+ ]
342
425
  },
343
426
  {
344
- "metadata": {},
345
427
  "cell_type": "code",
428
+ "execution_count": null,
429
+ "id": "22",
430
+ "metadata": {},
431
+ "outputs": [],
346
432
  "source": [
347
433
  "gt = get_great_table(\n",
348
434
  " df,\n",
349
- " \"Table 1: IMP Bottles of 128 needed<BR><small>as of 2025-04-04</small>\",\n",
435
+ " \"Table 1: IMP Bottles of 128 needed<BR><small>as of 2025-05-15</small>\",\n",
350
436
  " footnote=(\n",
351
437
  " \"<ol>\"\n",
352
438
  " \"<li>assume all participants consent for extended followup.\"\n",
@@ -356,14 +442,14 @@
356
442
  " \"</ol>\"\n",
357
443
  " ))\n",
358
444
  "gt.show()"
359
- ],
360
- "id": "f9bffc9c05c7fd41",
361
- "outputs": [],
362
- "execution_count": null
445
+ ]
363
446
  },
364
447
  {
365
- "metadata": {},
366
448
  "cell_type": "code",
449
+ "execution_count": null,
450
+ "id": "23",
451
+ "metadata": {},
452
+ "outputs": [],
367
453
  "source": [
368
454
  "\n",
369
455
  "# save as png\n",
@@ -372,14 +458,14 @@
372
458
  "image = Image.open(analysis_folder / \"pharmacy_tbl1.png\")\n",
373
459
  "image = image.resize((image.width * 6, image.height * 6), Image.LANCZOS)\n",
374
460
  "image.save(analysis_folder / \"pharmacy_tbl1.pdf\", \"PDF\", resolution=800, optimize=True, quality=95)"
375
- ],
376
- "id": "53247d65d251bebb",
377
- "outputs": [],
378
- "execution_count": null
461
+ ]
379
462
  },
380
463
  {
381
- "metadata": {},
382
464
  "cell_type": "code",
465
+ "execution_count": null,
466
+ "id": "24",
467
+ "metadata": {},
468
+ "outputs": [],
383
469
  "source": [
384
470
  "# now lets look at the stock\n",
385
471
  "df_stock = read_frame(Stock.objects.values(\"code\", \"lot_id\", \"container__name\", \"confirmed\", \"allocated\", \"dispensed\", \"qty_in\", \"qty_out\", \"unit_qty_in\", \"unit_qty_out\").all(), verbose=False)\n",
@@ -390,14 +476,14 @@
390
476
  "df_stock = df_stock.merge(df_lot[[\"lot_id\", \"assignment\"]], on=\"lot_id\", how=\"left\")\n",
391
477
  "df_stock.rename(columns={\"container__name\":\"container\"}, inplace=True)\n",
392
478
  "df_stock.reset_index(drop=True, inplace=True)"
393
- ],
394
- "id": "6d280a4eeac931a0",
395
- "outputs": [],
396
- "execution_count": null
479
+ ]
397
480
  },
398
481
  {
399
- "metadata": {},
400
482
  "cell_type": "code",
483
+ "execution_count": null,
484
+ "id": "25",
485
+ "metadata": {},
486
+ "outputs": [],
401
487
  "source": [
402
488
  "# merge in container columns\n",
403
489
  "df_container = read_frame(Container.objects.all())\n",
@@ -407,60 +493,64 @@
407
493
  "\n",
408
494
  "# calculate bal\n",
409
495
  "df_stock[\"bal\"] = df_stock[\"unit_qty_in\"] - df_stock[\"unit_qty_out\"]\n"
410
- ],
411
- "id": "a11c4b67752b965d",
412
- "outputs": [],
413
- "execution_count": null
496
+ ]
414
497
  },
415
498
  {
416
- "metadata": {},
417
499
  "cell_type": "code",
500
+ "execution_count": null,
501
+ "id": "26",
502
+ "metadata": {},
503
+ "outputs": [],
418
504
  "source": [
419
505
  "# show the balance of tablets decanted to bottles by assignment (on the EDC)\n",
420
506
  "df2 = df_stock[df_stock.container_display_name==\"Bottle 128\"].groupby(by=[\"assignment\"]).bal.agg(\"sum\").reset_index()\n",
421
507
  "df2.loc[len(df2)] = {\"bal\": df2.bal.sum()}\n",
422
508
  "df2"
423
- ],
424
- "id": "7e275d7fb827535b",
425
- "outputs": [],
426
- "execution_count": null
509
+ ]
427
510
  },
428
511
  {
429
- "metadata": {},
430
512
  "cell_type": "code",
513
+ "execution_count": null,
514
+ "id": "27",
515
+ "metadata": {},
516
+ "outputs": [],
431
517
  "source": [
432
518
  "# some bottles, as of today, have not been captured in the system\n",
433
519
  "# here is an estimate of what has been decanted into bottles but not labelled.\n",
434
520
  "# in the system, these tablets would appear on the EDC as still in buckets\n",
435
521
  "df3 = df2.copy()\n",
436
522
  "df3 = df3.drop(len(df3) - 1)\n",
437
- "placebo_unlabelled = 21*128*128\n",
438
- "active_unlabelled = 25*191*128\n",
523
+ "placebo_unlabelled = 0 # 21*128*128\n",
524
+ "active_unlabelled = 0 # 25*191*128\n",
439
525
  "\n",
440
526
  "# adding in the estimates, this is about what we have bottled\n",
441
527
  "df3.loc[df3.assignment==\"placebo\", \"bal\"] += placebo_unlabelled\n",
442
528
  "df3.loc[df3.assignment==\"active\", \"bal\"] += active_unlabelled\n",
443
529
  "df3.loc[len(df3)] = {\"bal\": df3.bal.sum()}\n",
444
530
  "df3"
445
- ],
446
- "id": "1d7134fe05417475",
447
- "outputs": [],
448
- "execution_count": null
531
+ ]
449
532
  },
450
533
  {
451
- "metadata": {},
452
534
  "cell_type": "code",
535
+ "execution_count": null,
536
+ "id": "28",
537
+ "metadata": {},
538
+ "outputs": [],
453
539
  "source": [
454
- "gt = get_great_table(df3, \"Table 2: IMP tablets in stock<BR><small>as of 2025-04-04</small>\", footnote=\"Includes recently decanted but unlabelled bottles\")\n",
540
+ "gt = get_great_table(\n",
541
+ " df3,\n",
542
+ " \"Table 2: IMP tablets in stock<BR><small>as of 2025-04-04</small>\",\n",
543
+ " # footnote=\"Includes recently decanted but unlabelled bottles\"\n",
544
+ " )\n",
455
545
  "gt.show()"
456
- ],
457
- "id": "7ab587c2d36d2e10",
458
- "outputs": [],
459
- "execution_count": null
546
+ ]
460
547
  },
461
548
  {
462
- "metadata": {},
463
549
  "cell_type": "code",
550
+ "execution_count": null,
551
+ "id": "29",
552
+ "metadata": {},
553
+ "outputs": [],
464
554
  "source": [
465
555
  "# save as png\n",
466
556
  "gt.save(analysis_folder / \"pharmacy_tbl2.png\")\n",
@@ -468,71 +558,71 @@
468
558
  "image = Image.open(analysis_folder / \"pharmacy_tbl2.png\")\n",
469
559
  "image = image.resize((image.width * 6, image.height * 6), Image.LANCZOS)\n",
470
560
  "image.save(analysis_folder / \"pharmacy_tbl2.pdf\", \"PDF\", resolution=800, optimize=True, quality=95)"
471
- ],
472
- "id": "68374d9f02546d6f",
473
- "outputs": [],
474
- "execution_count": null
561
+ ]
475
562
  },
476
563
  {
477
- "metadata": {},
478
564
  "cell_type": "code",
565
+ "execution_count": null,
566
+ "id": "30",
567
+ "metadata": {},
568
+ "outputs": [],
479
569
  "source": [
480
570
  "# tablets: ordered\n",
481
571
  "df_orderitems = read_frame(OrderItem.objects.all())\n",
482
572
  "df_orderitems.qty.sum()"
483
- ],
484
- "id": "9b67d6799735e116",
485
- "outputs": [],
486
- "execution_count": null
573
+ ]
487
574
  },
488
575
  {
489
- "metadata": {},
490
576
  "cell_type": "code",
577
+ "execution_count": null,
578
+ "id": "31",
579
+ "metadata": {},
580
+ "outputs": [],
491
581
  "source": [
492
582
  "# tablets: received\n",
493
583
  "df_received_items = read_frame(ReceiveItem.objects.all())\n",
494
584
  "df_received_items.unit_qty.sum()"
495
- ],
496
- "id": "af0868a54913329",
497
- "outputs": [],
498
- "execution_count": null
585
+ ]
499
586
  },
500
587
  {
501
- "metadata": {},
502
588
  "cell_type": "code",
589
+ "execution_count": null,
590
+ "id": "32",
591
+ "metadata": {},
592
+ "outputs": [],
503
593
  "source": [
504
594
  "# tablets: received into stock\n",
505
595
  "df_stock[df_stock.container_type==\"bucket\"].unit_qty_in.sum()"
506
- ],
507
- "id": "82b0df34b49377f0",
508
- "outputs": [],
509
- "execution_count": null
596
+ ]
510
597
  },
511
598
  {
512
- "metadata": {},
513
599
  "cell_type": "code",
600
+ "execution_count": null,
601
+ "id": "33",
602
+ "metadata": {},
603
+ "outputs": [],
514
604
  "source": [
515
605
  "# tablets: decanted from buckets into bottles\n",
516
606
  "df_stock[df_stock.container_type==\"bucket\"].unit_qty_out.sum()"
517
- ],
518
- "id": "1d1383219f8fd0a4",
519
- "outputs": [],
520
- "execution_count": null
607
+ ]
521
608
  },
522
609
  {
523
- "metadata": {},
524
610
  "cell_type": "code",
611
+ "execution_count": null,
612
+ "id": "34",
613
+ "metadata": {},
614
+ "outputs": [],
525
615
  "source": [
526
616
  "# tablets: total in bottles\n",
527
617
  "df_stock[df_stock.container_type==\"Bottle\"].unit_qty_in.sum()"
528
- ],
529
- "id": "d928f610d5427f39",
530
- "outputs": [],
531
- "execution_count": null
618
+ ]
532
619
  },
533
620
  {
534
- "metadata": {},
535
621
  "cell_type": "code",
622
+ "execution_count": null,
623
+ "id": "35",
624
+ "metadata": {},
625
+ "outputs": [],
536
626
  "source": [
537
627
  "# tablets: total bottles available / not yet dispensed BY ASSIGNMENT\n",
538
628
  "# the total matches the total above for column \"bal\"\n",
@@ -554,50 +644,50 @@
554
644
  "df6.loc[len(df6)] = {\"total\": df6.subtotal.sum()}\n",
555
645
  "df6 = df6[[\"dispensed\", \"assignment\", \"unit_qty_in\", \"subtotal\", \"total\"]]\n",
556
646
  "df6"
557
- ],
558
- "id": "6600690d6e88a1b2",
559
- "outputs": [],
560
- "execution_count": null
647
+ ]
561
648
  },
562
649
  {
563
- "metadata": {},
564
650
  "cell_type": "code",
565
- "source": "",
566
- "id": "3ad00099e744f41b",
651
+ "execution_count": null,
652
+ "id": "36",
653
+ "metadata": {},
567
654
  "outputs": [],
568
- "execution_count": null
655
+ "source": []
569
656
  },
570
657
  {
571
- "metadata": {},
572
658
  "cell_type": "code",
573
- "source": "",
574
- "id": "c460fa84e9fc004e",
659
+ "execution_count": null,
660
+ "id": "37",
661
+ "metadata": {},
575
662
  "outputs": [],
576
- "execution_count": null
663
+ "source": []
577
664
  },
578
665
  {
579
- "metadata": {},
580
666
  "cell_type": "code",
581
- "source": "",
582
- "id": "428d31f52ed17823",
667
+ "execution_count": null,
668
+ "id": "38",
669
+ "metadata": {},
583
670
  "outputs": [],
584
- "execution_count": null
671
+ "source": []
585
672
  },
586
673
  {
587
- "metadata": {},
588
674
  "cell_type": "code",
675
+ "execution_count": null,
676
+ "id": "39",
677
+ "metadata": {},
678
+ "outputs": [],
589
679
  "source": [
590
680
  "from meta_visit_schedule.constants import MONTH36\n",
591
681
  "\n",
592
682
  "df_appt[(df_appt.visit_code_str==MONTH36) & (df_appt.appt_datetime >= datetime(2024,12,15)) & (df_appt.appt_status==NEW_APPT) & (df_appt.appt_datetime <= datetime(2026,2,28))]"
593
- ],
594
- "id": "49a7b405b7973ae3",
595
- "outputs": [],
596
- "execution_count": null
683
+ ]
597
684
  },
598
685
  {
599
- "metadata": {},
600
686
  "cell_type": "code",
687
+ "execution_count": null,
688
+ "id": "40",
689
+ "metadata": {},
690
+ "outputs": [],
601
691
  "source": [
602
692
  "def remove_subjects_where_stock_on_site(stock_request: StockRequest, df: pd.DataFrame):\n",
603
693
  " stock_model_cls = django_apps.get_model(\"edc_pharmacy.Stock\")\n",
@@ -629,14 +719,14 @@
629
719
  " df[\"stock_qty\"] = 0.0\n",
630
720
  " df = df.reset_index(drop=True)\n",
631
721
  " return df\n"
632
- ],
633
- "id": "491b43957fe1c756",
634
- "outputs": [],
635
- "execution_count": null
722
+ ]
636
723
  },
637
724
  {
638
- "metadata": {},
639
725
  "cell_type": "code",
726
+ "execution_count": null,
727
+ "id": "41",
728
+ "metadata": {},
729
+ "outputs": [],
640
730
  "source": [
641
731
  "def pad_with_null_rows(df, qty_needed):\n",
642
732
  " padded_data = []\n",
@@ -649,44 +739,44 @@
649
739
  " for product in products:\n",
650
740
  " padded_data.append({'customer': customer, 'product_code': product})\n",
651
741
  " return pd.DataFrame(padded_data)"
652
- ],
653
- "id": "14fd36a1e0e158b9",
654
- "outputs": [],
655
- "execution_count": null
742
+ ]
656
743
  },
657
744
  {
658
- "metadata": {},
659
745
  "cell_type": "code",
746
+ "execution_count": null,
747
+ "id": "42",
748
+ "metadata": {},
749
+ "outputs": [],
660
750
  "source": [
661
751
  "pk = \"5455cf66-b8e5-449c-a1e8-24d3325026d7\"\n",
662
752
  "stock_request = StockRequest.objects.get(pk=pk)\n"
663
- ],
664
- "id": "ee9423599bad2c6f",
665
- "outputs": [],
666
- "execution_count": null
753
+ ]
667
754
  },
668
755
  {
669
- "metadata": {},
670
756
  "cell_type": "code",
757
+ "execution_count": null,
758
+ "id": "43",
759
+ "metadata": {},
760
+ "outputs": [],
671
761
  "source": [
672
762
  "df_subjects = get_next_scheduled_visit_for_subjects_df(stock_request)\n",
673
763
  "df_subjects"
674
- ],
675
- "id": "f5b22e507f36170d",
676
- "outputs": [],
677
- "execution_count": null
764
+ ]
678
765
  },
679
766
  {
680
- "metadata": {},
681
767
  "cell_type": "code",
682
- "source": "",
683
- "id": "8918d8cd6b2ae777",
768
+ "execution_count": null,
769
+ "id": "44",
770
+ "metadata": {},
684
771
  "outputs": [],
685
- "execution_count": null
772
+ "source": []
686
773
  },
687
774
  {
688
- "metadata": {},
689
775
  "cell_type": "code",
776
+ "execution_count": null,
777
+ "id": "45",
778
+ "metadata": {},
779
+ "outputs": [],
690
780
  "source": [
691
781
  "df = df_subjects.copy()\n",
692
782
  "stock_model_cls = django_apps.get_model(\"edc_pharmacy.Stock\")\n",
@@ -705,22 +795,24 @@
705
795
  " }\n",
706
796
  ")\n",
707
797
  "df_stock"
708
- ],
709
- "id": "164302f67cec31be",
710
- "outputs": [],
711
- "execution_count": null
798
+ ]
712
799
  },
713
800
  {
714
- "metadata": {},
715
801
  "cell_type": "code",
716
- "source": "df.merge(df_stock, on=\"subject_identifier\", how=\"left\")",
717
- "id": "8c1c3f0b594bdf3",
802
+ "execution_count": null,
803
+ "id": "46",
804
+ "metadata": {},
718
805
  "outputs": [],
719
- "execution_count": null
806
+ "source": [
807
+ "df.merge(df_stock, on=\"subject_identifier\", how=\"left\")"
808
+ ]
720
809
  },
721
810
  {
722
- "metadata": {},
723
811
  "cell_type": "code",
812
+ "execution_count": null,
813
+ "id": "47",
814
+ "metadata": {},
815
+ "outputs": [],
724
816
  "source": [
725
817
  "if not df.empty and not df_stock.empty:\n",
726
818
  " df_subject = df.copy()\n",
@@ -736,22 +828,24 @@
736
828
  "df[\"stock_qty\"] = 0.0\n",
737
829
  "df = df.reset_index(drop=True)\n",
738
830
  "df"
739
- ],
740
- "id": "ba3541a4e2cdfde6",
741
- "outputs": [],
742
- "execution_count": null
831
+ ]
743
832
  },
744
833
  {
745
- "metadata": {},
746
834
  "cell_type": "code",
747
- "source": "df.loc[df.index.repeat(3)]",
748
- "id": "5ba4eff2efc5f3eb",
835
+ "execution_count": null,
836
+ "id": "48",
837
+ "metadata": {},
749
838
  "outputs": [],
750
- "execution_count": null
839
+ "source": [
840
+ "df.loc[df.index.repeat(3)]"
841
+ ]
751
842
  },
752
843
  {
753
- "metadata": {},
754
844
  "cell_type": "code",
845
+ "execution_count": null,
846
+ "id": "49",
847
+ "metadata": {},
848
+ "outputs": [],
755
849
  "source": [
756
850
  "if not df.empty and not df_stock.empty:\n",
757
851
  " df = df.merge(df_stock, on=\"subject_identifier\", how=\"left\")\n",
@@ -760,25 +854,25 @@
760
854
  "df[\"stock_qty\"] = 0.0\n",
761
855
  "df = df.reset_index(drop=True)\n",
762
856
  "df"
763
- ],
764
- "id": "79507f3726dd23ac",
765
- "outputs": [],
766
- "execution_count": null
857
+ ]
767
858
  },
768
859
  {
769
- "metadata": {},
770
860
  "cell_type": "code",
861
+ "execution_count": null,
862
+ "id": "50",
863
+ "metadata": {},
864
+ "outputs": [],
771
865
  "source": [
772
866
  "df = remove_subjects_where_stock_on_site(stock_request, df_subjects)\n",
773
867
  "df"
774
- ],
775
- "id": "a23aafeb660d5a9c",
776
- "outputs": [],
777
- "execution_count": null
868
+ ]
778
869
  },
779
870
  {
780
- "metadata": {},
781
871
  "cell_type": "code",
872
+ "execution_count": null,
873
+ "id": "51",
874
+ "metadata": {},
875
+ "outputs": [],
782
876
  "source": [
783
877
  "df_instock = df[~df.code.isna()]\n",
784
878
  "df_instock = df_instock.reset_index(drop=True)\n",
@@ -791,160 +885,165 @@
791
885
  "].reset_index(drop=True)\n",
792
886
  "df_nostock = df_nostock.sort_values(by=[\"subject_identifier\"])\n",
793
887
  "df_nostock[\"code\"] = df_nostock[\"code\"].fillna(\"---\")\n"
794
- ],
795
- "id": "f8e386caf276b53e",
796
- "outputs": [],
797
- "execution_count": null
888
+ ]
798
889
  },
799
890
  {
800
- "metadata": {},
801
891
  "cell_type": "code",
802
- "source": "",
803
- "id": "85715500af7721de",
892
+ "execution_count": null,
893
+ "id": "52",
894
+ "metadata": {},
804
895
  "outputs": [],
805
- "execution_count": null
896
+ "source": []
806
897
  },
807
898
  {
808
- "metadata": {},
809
899
  "cell_type": "code",
810
- "source": "",
811
- "id": "acf8453d693c288e",
900
+ "execution_count": null,
901
+ "id": "53",
902
+ "metadata": {},
812
903
  "outputs": [],
813
- "execution_count": null
904
+ "source": []
814
905
  },
815
906
  {
816
- "metadata": {},
817
907
  "cell_type": "code",
818
- "source": "no_stock_for_subjects_df()",
819
- "id": "73171a433d5b5882",
908
+ "execution_count": null,
909
+ "id": "54",
910
+ "metadata": {},
820
911
  "outputs": [],
821
- "execution_count": null
912
+ "source": [
913
+ "no_stock_for_subjects_df()"
914
+ ]
822
915
  },
823
916
  {
824
- "metadata": {},
825
917
  "cell_type": "code",
826
- "source": "df_schedule = read_frame(SubjectScheduleHistory.objects.values(\"subject_identifier\", \"visit_schedule_name\",\"schedule_name\", \"offschedule_datetime\").all())\n",
827
- "id": "5721c4cf5f08d681",
918
+ "execution_count": null,
919
+ "id": "55",
920
+ "metadata": {},
828
921
  "outputs": [],
829
- "execution_count": null
922
+ "source": [
923
+ "df_schedule = read_frame(SubjectScheduleHistory.objects.values(\"subject_identifier\", \"visit_schedule_name\",\"schedule_name\", \"offschedule_datetime\").all())\n"
924
+ ]
830
925
  },
831
926
  {
832
- "metadata": {},
833
927
  "cell_type": "code",
928
+ "execution_count": null,
929
+ "id": "56",
930
+ "metadata": {},
931
+ "outputs": [],
834
932
  "source": [
835
933
  "df_schedule = df_schedule[(df_schedule.visit_schedule_name==\"visit_schedule\") & (df_schedule.schedule_name==\"schedule\") & df_schedule.offschedule_datetime.isna()]\n",
836
934
  "df_schedule.reset_index(drop=True, inplace=True)"
837
- ],
838
- "id": "621196fa35e00e9c",
839
- "outputs": [],
840
- "execution_count": null
935
+ ]
841
936
  },
842
937
  {
843
- "metadata": {},
844
938
  "cell_type": "code",
939
+ "execution_count": null,
940
+ "id": "57",
941
+ "metadata": {},
942
+ "outputs": [],
845
943
  "source": [
846
944
  "df_stock = read_frame(Stock.objects.all(), verbose=False)\n",
847
945
  "df_stock_on_site = df_stock[(df_stock.confirmed_at_site==True) & (df_stock.dispensed==False)].copy()\n",
848
946
  "df_stock_on_site.reset_index(drop=True, inplace=True)\n",
849
947
  "df_stock_on_site = df_stock_on_site.drop(columns=[\"subject_identifier\"])\n"
850
- ],
851
- "id": "8abada4ffb0db4f0",
852
- "outputs": [],
853
- "execution_count": null
948
+ ]
854
949
  },
855
950
  {
856
- "metadata": {},
857
951
  "cell_type": "code",
952
+ "execution_count": null,
953
+ "id": "58",
954
+ "metadata": {},
955
+ "outputs": [],
858
956
  "source": [
859
957
  "df_allocation = read_frame(Allocation.objects.values(\"id\", \"registered_subject\").all(), verbose=False)\n",
860
958
  "df_rs = read_frame(RegisteredSubject.objects.values(\"id\", \"subject_identifier\").all(), verbose=False)\n",
861
959
  "df_allocation = df_allocation.merge(df_rs[[\"id\", \"subject_identifier\"]], how=\"left\", left_on=\"registered_subject\", right_on=\"id\", suffixes=[\"_allocation\", \"_rs\"])"
862
- ],
863
- "id": "624ebc7e51e679bd",
864
- "outputs": [],
865
- "execution_count": null
960
+ ]
866
961
  },
867
962
  {
868
- "metadata": {},
869
963
  "cell_type": "code",
870
- "source": "df_stock_on_site = df_stock_on_site.merge(df_allocation[[\"id_allocation\", \"subject_identifier\"]], how=\"left\", left_on=\"allocation\", right_on=\"id_allocation\")",
871
- "id": "394e569ac1649719",
964
+ "execution_count": null,
965
+ "id": "59",
966
+ "metadata": {},
872
967
  "outputs": [],
873
- "execution_count": null
968
+ "source": [
969
+ "df_stock_on_site = df_stock_on_site.merge(df_allocation[[\"id_allocation\", \"subject_identifier\"]], how=\"left\", left_on=\"allocation\", right_on=\"id_allocation\")"
970
+ ]
874
971
  },
875
972
  {
876
- "metadata": {},
877
973
  "cell_type": "code",
974
+ "execution_count": null,
975
+ "id": "60",
976
+ "metadata": {},
977
+ "outputs": [],
878
978
  "source": [
879
979
  "df = pd.merge(df_schedule[[\"subject_identifier\", 'offschedule_datetime']], df_stock_on_site, on=\"subject_identifier\", how=\"left\")\n",
880
980
  "df= df[df.code.isna()][[\"subject_identifier\", ]].sort_values(by=[\"subject_identifier\"]).reset_index(drop=True)"
881
- ],
882
- "id": "1ab7350cfb29eab4",
883
- "outputs": [],
884
- "execution_count": null
981
+ ]
885
982
  },
886
983
  {
887
- "metadata": {},
888
984
  "cell_type": "code",
985
+ "execution_count": null,
986
+ "id": "61",
987
+ "metadata": {},
988
+ "outputs": [],
889
989
  "source": [
890
990
  "df_appt = get_next_scheduled_visit_for_subjects_df()\n",
891
991
  "df_appt = df_appt[[\"subject_identifier\", \"site_id\", \"visit_code\", \"appt_datetime\", \"baseline_datetime\"]].copy()\n",
892
992
  "df_appt.reset_index(drop=True, inplace=True)"
893
- ],
894
- "id": "f29cc9c6c1234c34",
895
- "outputs": [],
896
- "execution_count": null
993
+ ]
897
994
  },
898
995
  {
899
- "metadata": {},
900
996
  "cell_type": "code",
997
+ "execution_count": null,
998
+ "id": "62",
999
+ "metadata": {},
1000
+ "outputs": [],
901
1001
  "source": [
902
1002
  "\n",
903
1003
  "df = df.merge(df_appt, how=\"left\", on=\"subject_identifier\")\n",
904
1004
  "df = df[(df.appt_datetime.notna())]\n",
905
1005
  "df.reset_index(drop=True, inplace=True)"
906
- ],
907
- "id": "9e3e3f429a1995ec",
908
- "outputs": [],
909
- "execution_count": null
1006
+ ]
910
1007
  },
911
1008
  {
912
- "metadata": {},
913
1009
  "cell_type": "code",
1010
+ "execution_count": null,
1011
+ "id": "63",
1012
+ "metadata": {},
1013
+ "outputs": [],
914
1014
  "source": [
915
1015
  "utc_now = pd.Timestamp.utcnow().tz_localize(None)\n",
916
1016
  "df[\"relative_days\"] = (df.appt_datetime - utc_now).dt.days\n",
917
1017
  "df_final = df[(df.relative_days >= -105)].copy()\n",
918
1018
  "df_final.reset_index(drop=True, inplace=True)\n",
919
1019
  "df_final"
920
- ],
921
- "id": "d6570f1e94ac23bb",
922
- "outputs": [],
923
- "execution_count": null
1020
+ ]
924
1021
  },
925
1022
  {
926
- "metadata": {},
927
1023
  "cell_type": "code",
928
- "source": "RegisteredSubject.objects.filter(site_id=10)",
929
- "id": "6aef5bb0e7caf4e8",
1024
+ "execution_count": null,
1025
+ "id": "64",
1026
+ "metadata": {},
930
1027
  "outputs": [],
931
- "execution_count": null
1028
+ "source": [
1029
+ "RegisteredSubject.objects.filter(site_id=10)"
1030
+ ]
932
1031
  },
933
1032
  {
934
- "metadata": {},
935
1033
  "cell_type": "code",
936
- "source": "",
937
- "id": "1e6126bbe8d04c70",
1034
+ "execution_count": null,
1035
+ "id": "65",
1036
+ "metadata": {},
938
1037
  "outputs": [],
939
- "execution_count": null
1038
+ "source": []
940
1039
  },
941
1040
  {
942
- "metadata": {},
943
1041
  "cell_type": "code",
944
- "source": "",
945
- "id": "1ee6ef823e6a543d",
1042
+ "execution_count": null,
1043
+ "id": "66",
1044
+ "metadata": {},
946
1045
  "outputs": [],
947
- "execution_count": null
1046
+ "source": []
948
1047
  }
949
1048
  ],
950
1049
  "metadata": {