gaard-api 0.2.4__tar.gz → 0.2.5__tar.gz

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 (73) hide show
  1. {gaard_api-0.2.4/src/gaard_api.egg-info → gaard_api-0.2.5}/PKG-INFO +1 -1
  2. {gaard_api-0.2.4 → gaard_api-0.2.5}/pyproject.toml +1 -1
  3. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/example_data/medical_poc/seed.sql +206 -50
  4. gaard_api-0.2.5/src/gaard_api/example_database.py +474 -0
  5. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/main.py +1 -1
  6. {gaard_api-0.2.4 → gaard_api-0.2.5/src/gaard_api.egg-info}/PKG-INFO +1 -1
  7. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_admin_api.py +10 -3
  8. gaard_api-0.2.5/tests/test_server_cli.py +215 -0
  9. gaard_api-0.2.4/src/gaard_api/example_database.py +0 -135
  10. gaard_api-0.2.4/tests/test_server_cli.py +0 -111
  11. {gaard_api-0.2.4 → gaard_api-0.2.5}/MANIFEST.in +0 -0
  12. {gaard_api-0.2.4 → gaard_api-0.2.5}/README.md +0 -0
  13. {gaard_api-0.2.4 → gaard_api-0.2.5}/setup.cfg +0 -0
  14. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/__init__.py +0 -0
  15. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/__init__.py +0 -0
  16. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/database.py +0 -0
  17. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/defaults.py +0 -0
  18. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/models.py +0 -0
  19. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/prompt_runtime.py +0 -0
  20. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/security.py +0 -0
  21. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin/services.py +0 -0
  22. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/assets/getgaard.svg +0 -0
  23. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/assets/main.js +0 -0
  24. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/assets/styles.css +0 -0
  25. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack-all.js +0 -0
  26. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/assets/vendor/gridstack/gridstack.min.css +0 -0
  27. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/index.html +0 -0
  28. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/admin-web/package.json +0 -0
  29. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/__init__.py +0 -0
  30. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/__init__.py +0 -0
  31. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/admin.py +0 -0
  32. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/analysis.py +0 -0
  33. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/dashboards.py +0 -0
  34. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/prompts.py +0 -0
  35. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/query.py +0 -0
  36. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api/v1/schema.py +0 -0
  37. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/api_registry.py +0 -0
  38. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/auth_dependencies.py +0 -0
  39. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/auth_hooks.py +0 -0
  40. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/cli.py +0 -0
  41. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/cli_commands.py +0 -0
  42. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/conversations.py +0 -0
  43. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/core/__init__.py +0 -0
  44. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/core/error_handlers.py +0 -0
  45. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/core/schema_cache.py +0 -0
  46. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/core/settings.py +0 -0
  47. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/example_data/__init__.py +0 -0
  48. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/example_data/medical_poc/__init__.py +0 -0
  49. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/example_data/medical_poc/schema.sql +0 -0
  50. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/extension_services.py +0 -0
  51. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/extensions.py +0 -0
  52. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/license.py +0 -0
  53. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/package_updates.py +0 -0
  54. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/py.typed +0 -0
  55. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/query_hooks.py +0 -0
  56. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/server_cli.py +0 -0
  57. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/siem.py +0 -0
  58. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api/tls_http.py +0 -0
  59. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api.egg-info/SOURCES.txt +0 -0
  60. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api.egg-info/dependency_links.txt +0 -0
  61. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api.egg-info/entry_points.txt +0 -0
  62. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api.egg-info/requires.txt +0 -0
  63. {gaard_api-0.2.4 → gaard_api-0.2.5}/src/gaard_api.egg-info/top_level.txt +0 -0
  64. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_analysis_api.py +0 -0
  65. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_api_registry.py +0 -0
  66. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_conversation_api.py +0 -0
  67. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_error_handlers.py +0 -0
  68. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_license_api.py +0 -0
  69. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_license_service.py +0 -0
  70. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_prompt_runtime.py +0 -0
  71. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_query_error_messages.py +0 -0
  72. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_settings.py +0 -0
  73. {gaard_api-0.2.4 → gaard_api-0.2.5}/tests/test_tls_http.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gaard-api
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: GAARD backend web services providing admin interface
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gaard-api"
7
- version = "0.2.4"
7
+ version = "0.2.5"
8
8
  description = "GAARD backend web services providing admin interface"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -208,36 +208,132 @@ WITH RECURSIVE
208
208
  seq(n) AS (
209
209
  VALUES (1)
210
210
  UNION ALL
211
- SELECT n + 1 FROM seq WHERE n < 420
211
+ SELECT n + 1 FROM seq WHERE n < 560
212
212
  ),
213
- current_bounds(year_start, today, elapsed_days) AS (
213
+ current_bounds(year_start, today, elapsed_days, year_days) AS (
214
214
  SELECT
215
215
  date('now', 'localtime', 'start of year'),
216
216
  date('now', 'localtime'),
217
- CAST(julianday(date('now', 'localtime')) - julianday(date('now', 'localtime', 'start of year')) AS INTEGER)
217
+ CAST(julianday(date('now', 'localtime')) - julianday(date('now', 'localtime', 'start of year')) AS INTEGER),
218
+ CAST(
219
+ julianday(date('now', 'localtime', 'start of year', '+1 year', '-1 day'))
220
+ - julianday(date('now', 'localtime', 'start of year'))
221
+ AS INTEGER
222
+ )
218
223
  ),
219
- appointment_offsets(n, patient_id, doctor_id, day_offset) AS (
224
+ specialty_mix(n, specialization, doctor_start, doctor_count) AS (
220
225
  SELECT
221
226
  seq.n,
222
227
  CASE
223
- WHEN seq.n <= 140 THEN seq.n
224
- WHEN seq.n % 18 = 0 THEN 7
225
- WHEN seq.n % 22 = 0 THEN 31
226
- WHEN seq.n % 27 = 0 THEN 86
227
- ELSE 1 + ((seq.n * 29) % 140)
228
+ WHEN seq.n % 100 < 34 THEN 'cardiology'
229
+ WHEN seq.n % 100 < 61 THEN 'orthopedics'
230
+ WHEN seq.n % 100 < 80 THEN 'dermatology'
231
+ ELSE 'neurology'
228
232
  END,
229
233
  CASE
230
- WHEN seq.n <= 24 THEN seq.n
231
- ELSE 1 + ((seq.n * 7) % 24)
234
+ WHEN seq.n % 100 < 34 THEN 1
235
+ WHEN seq.n % 100 < 61 THEN 13
236
+ WHEN seq.n % 100 < 80 THEN 19
237
+ ELSE 7
232
238
  END,
233
- CASE
234
- WHEN seq.n = 420 THEN current_bounds.elapsed_days
235
- WHEN seq.n <= 112 THEN CAST((seq.n - 1) / 8 AS INTEGER) % (current_bounds.elapsed_days + 1)
236
- ELSE ((seq.n * 37 + CAST(seq.n / 9 AS INTEGER)) % (current_bounds.elapsed_days + 1))
237
- END
239
+ 6
238
240
  FROM seq
241
+ ),
242
+ specialty_ranked AS (
243
+ SELECT
244
+ specialty_mix.n,
245
+ specialty_mix.specialization,
246
+ specialty_mix.doctor_start,
247
+ specialty_mix.doctor_count,
248
+ ROW_NUMBER() OVER (
249
+ PARTITION BY specialty_mix.specialization
250
+ ORDER BY specialty_mix.n
251
+ ) AS specialty_visit_number,
252
+ COUNT(*) OVER (
253
+ PARTITION BY specialty_mix.specialization
254
+ ) AS specialty_visit_total
255
+ FROM specialty_mix
256
+ ),
257
+ seasonal_offsets AS (
258
+ SELECT
259
+ specialty_ranked.n,
260
+ specialty_ranked.specialization,
261
+ specialty_ranked.doctor_start
262
+ + ((specialty_ranked.specialty_visit_number * 5 + specialty_ranked.n) % specialty_ranked.doctor_count)
263
+ AS doctor_id,
264
+ CASE specialty_ranked.specialization
265
+ WHEN 'cardiology' THEN
266
+ current_bounds.year_days - CAST(
267
+ (
268
+ current_bounds.year_days
269
+ * (specialty_ranked.specialty_visit_total - specialty_ranked.specialty_visit_number + 1)
270
+ * (specialty_ranked.specialty_visit_total - specialty_ranked.specialty_visit_number + 1)
271
+ ) / (
272
+ (specialty_ranked.specialty_visit_total + 1)
273
+ * (specialty_ranked.specialty_visit_total + 1)
274
+ )
275
+ AS INTEGER
276
+ ) + ((specialty_ranked.n * 7) % 10)
277
+ WHEN 'orthopedics' THEN
278
+ CASE
279
+ WHEN specialty_ranked.specialty_visit_number % 6 IN (0, 1, 2) THEN
280
+ 150 + ((specialty_ranked.specialty_visit_number * 11 + specialty_ranked.n) % 90)
281
+ WHEN specialty_ranked.specialty_visit_number % 6 IN (3, 4) THEN
282
+ 240 + ((specialty_ranked.specialty_visit_number * 7 + specialty_ranked.n) % 60)
283
+ ELSE
284
+ 5 + ((specialty_ranked.specialty_visit_number * 13 + specialty_ranked.n) % 55)
285
+ END
286
+ WHEN 'dermatology' THEN
287
+ CASE
288
+ WHEN specialty_ranked.specialty_visit_number % 5 IN (0, 1, 2, 3) THEN
289
+ 95 + ((specialty_ranked.specialty_visit_number * 13 + specialty_ranked.n) % 120)
290
+ ELSE
291
+ 220 + ((specialty_ranked.specialty_visit_number * 17 + specialty_ranked.n) % 70)
292
+ END
293
+ ELSE
294
+ CASE
295
+ WHEN specialty_ranked.specialty_visit_number % 4 IN (0, 1) THEN
296
+ 55 + ((specialty_ranked.specialty_visit_number * 13 + specialty_ranked.n) % 70)
297
+ WHEN specialty_ranked.specialty_visit_number % 4 = 2 THEN
298
+ 245 + ((specialty_ranked.specialty_visit_number * 11 + specialty_ranked.n) % 75)
299
+ ELSE
300
+ 130 + ((specialty_ranked.specialty_visit_number * 17 + specialty_ranked.n) % 80)
301
+ END
302
+ END AS raw_day_offset
303
+ FROM specialty_ranked
239
304
  CROSS JOIN current_bounds
240
305
  ),
306
+ bounded_offsets AS (
307
+ SELECT
308
+ seasonal_offsets.n,
309
+ seasonal_offsets.specialization,
310
+ seasonal_offsets.doctor_id,
311
+ CASE
312
+ WHEN seasonal_offsets.raw_day_offset < 0 THEN 0
313
+ WHEN seasonal_offsets.raw_day_offset > current_bounds.year_days THEN current_bounds.year_days
314
+ ELSE seasonal_offsets.raw_day_offset
315
+ END AS day_offset
316
+ FROM seasonal_offsets
317
+ CROSS JOIN current_bounds
318
+ ),
319
+ appointment_offsets(n, patient_id, doctor_id, day_offset, specialization) AS (
320
+ SELECT
321
+ bounded_offsets.n,
322
+ CASE
323
+ WHEN bounded_offsets.specialization = 'cardiology' AND bounded_offsets.n % 6 = 0 THEN 7
324
+ WHEN bounded_offsets.specialization = 'cardiology' AND bounded_offsets.n % 10 = 0 THEN 31
325
+ WHEN bounded_offsets.specialization = 'cardiology' THEN 1 + ((bounded_offsets.n * 17 + bounded_offsets.day_offset) % 90)
326
+ WHEN bounded_offsets.specialization = 'neurology' AND bounded_offsets.n % 8 = 0 THEN 86
327
+ WHEN bounded_offsets.specialization = 'neurology' THEN 25 + ((bounded_offsets.n * 11 + bounded_offsets.day_offset) % 90)
328
+ WHEN bounded_offsets.specialization = 'orthopedics' THEN 1 + ((bounded_offsets.n * 29 + bounded_offsets.day_offset) % 140)
329
+ ELSE 1 + ((bounded_offsets.n * 13 + bounded_offsets.day_offset * 2) % 140)
330
+ END
331
+ AS patient_id,
332
+ bounded_offsets.doctor_id,
333
+ bounded_offsets.day_offset,
334
+ bounded_offsets.specialization
335
+ FROM bounded_offsets
336
+ ),
241
337
  appointment_data AS (
242
338
  SELECT
243
339
  appointment_offsets.n,
@@ -245,46 +341,103 @@ WITH RECURSIVE
245
341
  appointment_offsets.doctor_id,
246
342
  date(current_bounds.year_start, '+' || appointment_offsets.day_offset || ' days') AS appointment_date,
247
343
  printf('%02d:%02d', 8 + ((appointment_offsets.n * 7) % 10), (appointment_offsets.n * 15) % 60) AS appointment_time,
248
- CASE
249
- WHEN appointment_offsets.n % 10 = 0 THEN 60
250
- WHEN appointment_offsets.n % 4 = 0 THEN 45
251
- WHEN appointment_offsets.n % 3 = 0 THEN 15
252
- ELSE 30
344
+ CASE appointment_offsets.specialization
345
+ WHEN 'cardiology' THEN
346
+ CASE
347
+ WHEN appointment_offsets.n % 6 = 0 THEN 60
348
+ WHEN appointment_offsets.n % 2 = 0 THEN 45
349
+ ELSE 30
350
+ END
351
+ WHEN 'neurology' THEN
352
+ CASE
353
+ WHEN appointment_offsets.n % 5 = 0 THEN 60
354
+ WHEN appointment_offsets.n % 3 = 0 THEN 30
355
+ ELSE 45
356
+ END
357
+ WHEN 'orthopedics' THEN
358
+ CASE
359
+ WHEN appointment_offsets.n % 7 = 0 THEN 60
360
+ WHEN appointment_offsets.n % 2 = 0 THEN 30
361
+ ELSE 45
362
+ END
363
+ ELSE
364
+ CASE
365
+ WHEN appointment_offsets.n % 5 = 0 THEN 45
366
+ WHEN appointment_offsets.n % 2 = 0 THEN 15
367
+ ELSE 30
368
+ END
253
369
  END AS duration_minutes,
254
370
  CASE
371
+ WHEN appointment_offsets.day_offset > current_bounds.elapsed_days THEN
372
+ CASE
373
+ WHEN appointment_offsets.n % 31 = 0 THEN 'cancelled'
374
+ ELSE 'scheduled'
375
+ END
255
376
  WHEN appointment_offsets.day_offset = current_bounds.elapsed_days THEN
256
377
  CASE
257
378
  WHEN appointment_offsets.n % 5 = 0 THEN 'cancelled'
258
379
  WHEN appointment_offsets.n % 3 = 0 THEN 'checked_in'
259
380
  ELSE 'scheduled'
260
381
  END
261
- WHEN appointment_offsets.n % 23 = 0 THEN 'no_show'
262
- WHEN appointment_offsets.n % 17 = 0 THEN 'cancelled'
382
+ WHEN appointment_offsets.specialization = 'dermatology' AND appointment_offsets.n % 22 = 0 THEN 'no_show'
383
+ WHEN appointment_offsets.specialization = 'orthopedics' AND appointment_offsets.n % 18 = 0 THEN 'cancelled'
384
+ WHEN appointment_offsets.specialization = 'cardiology' AND appointment_offsets.n % 27 = 0 THEN 'cancelled'
385
+ WHEN appointment_offsets.n % 29 = 0 THEN 'no_show'
386
+ WHEN appointment_offsets.n % 21 = 0 THEN 'cancelled'
263
387
  ELSE 'completed'
264
388
  END AS status,
265
- CASE appointment_offsets.n % 6
266
- WHEN 0 THEN 'telemedicine'
267
- WHEN 1 THEN 'first_visit'
268
- WHEN 2 THEN 'follow_up'
269
- WHEN 3 THEN 'control'
270
- WHEN 4 THEN 'urgent'
271
- ELSE 'preventive'
389
+ CASE appointment_offsets.specialization
390
+ WHEN 'cardiology' THEN
391
+ CASE appointment_offsets.n % 5
392
+ WHEN 0 THEN 'telemedicine'
393
+ WHEN 1 THEN 'follow_up'
394
+ WHEN 2 THEN 'control'
395
+ WHEN 3 THEN 'first_visit'
396
+ ELSE 'preventive'
397
+ END
398
+ WHEN 'neurology' THEN
399
+ CASE appointment_offsets.n % 5
400
+ WHEN 0 THEN 'first_visit'
401
+ WHEN 1 THEN 'follow_up'
402
+ WHEN 2 THEN 'control'
403
+ WHEN 3 THEN 'telemedicine'
404
+ ELSE 'urgent'
405
+ END
406
+ WHEN 'orthopedics' THEN
407
+ CASE appointment_offsets.n % 5
408
+ WHEN 0 THEN 'urgent'
409
+ WHEN 1 THEN 'first_visit'
410
+ WHEN 2 THEN 'follow_up'
411
+ WHEN 3 THEN 'control'
412
+ ELSE 'urgent'
413
+ END
414
+ ELSE
415
+ CASE appointment_offsets.n % 5
416
+ WHEN 0 THEN 'first_visit'
417
+ WHEN 1 THEN 'control'
418
+ WHEN 2 THEN 'preventive'
419
+ WHEN 3 THEN 'follow_up'
420
+ ELSE 'telemedicine'
421
+ END
272
422
  END AS visit_type,
273
423
  CASE
274
- WHEN appointment_offsets.n % 29 = 0 THEN 'high'
424
+ WHEN appointment_offsets.specialization = 'orthopedics' AND appointment_offsets.n % 7 = 0 THEN 'high'
425
+ WHEN appointment_offsets.specialization = 'cardiology' AND appointment_offsets.n % 9 = 0 THEN 'high'
426
+ WHEN appointment_offsets.specialization = 'neurology' AND appointment_offsets.n % 13 = 0 THEN 'high'
275
427
  WHEN appointment_offsets.n % 11 = 0 THEN 'elevated'
276
428
  ELSE 'routine'
277
429
  END AS priority,
278
- CASE
279
- WHEN appointment_offsets.doctor_id BETWEEN 1 AND 6 THEN
280
- CASE appointment_offsets.n % 5
281
- WHEN 0 THEN 'blood pressure follow-up'
282
- WHEN 1 THEN 'chest pain evaluation'
283
- WHEN 2 THEN 'palpitations'
284
- WHEN 3 THEN 'shortness of breath'
430
+ CASE appointment_offsets.specialization
431
+ WHEN 'cardiology' THEN
432
+ CASE
433
+ WHEN appointment_offsets.day_offset > 260 THEN 'winter cardiovascular risk review'
434
+ WHEN appointment_offsets.n % 5 = 0 THEN 'blood pressure follow-up'
435
+ WHEN appointment_offsets.n % 5 = 1 THEN 'chest pain evaluation'
436
+ WHEN appointment_offsets.n % 5 = 2 THEN 'palpitations'
437
+ WHEN appointment_offsets.n % 5 = 3 THEN 'shortness of breath'
285
438
  ELSE 'post-treatment control'
286
439
  END
287
- WHEN appointment_offsets.doctor_id BETWEEN 7 AND 12 THEN
440
+ WHEN 'neurology' THEN
288
441
  CASE appointment_offsets.n % 5
289
442
  WHEN 0 THEN 'migraine follow-up'
290
443
  WHEN 1 THEN 'dizziness'
@@ -292,20 +445,23 @@ WITH RECURSIVE
292
445
  WHEN 3 THEN 'limb numbness'
293
446
  ELSE 'seizure control'
294
447
  END
295
- WHEN appointment_offsets.doctor_id BETWEEN 13 AND 18 THEN
296
- CASE appointment_offsets.n % 5
297
- WHEN 0 THEN 'knee pain'
298
- WHEN 1 THEN 'back pain'
299
- WHEN 2 THEN 'ankle injury'
300
- WHEN 3 THEN 'shoulder mobility issue'
448
+ WHEN 'orthopedics' THEN
449
+ CASE
450
+ WHEN appointment_offsets.day_offset BETWEEN 150 AND 240 THEN 'sports injury'
451
+ WHEN appointment_offsets.day_offset < 70 THEN 'winter fall injury'
452
+ WHEN appointment_offsets.n % 5 = 0 THEN 'knee pain'
453
+ WHEN appointment_offsets.n % 5 = 1 THEN 'back pain'
454
+ WHEN appointment_offsets.n % 5 = 2 THEN 'ankle injury'
455
+ WHEN appointment_offsets.n % 5 = 3 THEN 'shoulder mobility issue'
301
456
  ELSE 'post-injury control'
302
457
  END
303
458
  ELSE
304
- CASE appointment_offsets.n % 5
305
- WHEN 0 THEN 'rash assessment'
306
- WHEN 1 THEN 'mole check'
307
- WHEN 2 THEN 'acne treatment'
308
- WHEN 3 THEN 'psoriasis follow-up'
459
+ CASE
460
+ WHEN appointment_offsets.day_offset BETWEEN 95 AND 220 THEN 'sun exposure skin check'
461
+ WHEN appointment_offsets.n % 5 = 0 THEN 'rash assessment'
462
+ WHEN appointment_offsets.n % 5 = 1 THEN 'mole check'
463
+ WHEN appointment_offsets.n % 5 = 2 THEN 'acne treatment'
464
+ WHEN appointment_offsets.n % 5 = 3 THEN 'psoriasis follow-up'
309
465
  ELSE 'allergy symptoms'
310
466
  END
311
467
  END AS reason