@ak--47/dungeon-master 1.1.0 → 1.2.1

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 (72) hide show
  1. package/dungeons/technical/ad-spend-schema.json +2 -2
  2. package/dungeons/technical/ad-spend.js +16 -5
  3. package/dungeons/technical/anonymous-users-schema.json +4 -3
  4. package/dungeons/technical/anonymous-users.js +16 -6
  5. package/dungeons/technical/array-of-object-lookup-schema.json +1 -1
  6. package/dungeons/technical/array-of-object-lookup.js +22 -6
  7. package/dungeons/technical/experiments-schema.json +25 -19
  8. package/dungeons/technical/experiments.js +24 -6
  9. package/dungeons/technical/foobar.js +29 -13
  10. package/dungeons/technical/group-analytics-schema.json +92 -0
  11. package/dungeons/technical/group-analytics.js +16 -6
  12. package/dungeons/technical/mirror-strategies-schema.json +4 -4
  13. package/dungeons/technical/mirror-strategies.js +16 -6
  14. package/dungeons/technical/nested-objects.js +16 -5
  15. package/dungeons/technical/retention-cadence.js +14 -9
  16. package/dungeons/technical/sanity-schema.json +22 -22
  17. package/dungeons/technical/sanity.js +26 -6
  18. package/dungeons/technical/scale-test.js +16 -5
  19. package/dungeons/technical/scd-schema.json +230 -33
  20. package/dungeons/technical/scd.js +25 -5
  21. package/dungeons/technical/simple-schema.json +132 -48
  22. package/dungeons/technical/simple.js +21 -6
  23. package/dungeons/technical/simplest-schema.json +168 -79
  24. package/dungeons/technical/simplest.js +24 -7
  25. package/dungeons/technical/text-generation-schema.json +7 -4
  26. package/dungeons/technical/text-generation.js +23 -8
  27. package/dungeons/user/.gitkeep +0 -0
  28. package/dungeons/user/shalini.js +847 -0
  29. package/dungeons/vertical/community-schema.json +71 -49
  30. package/dungeons/vertical/community.js +57 -30
  31. package/dungeons/vertical/devtools-schema.json +69 -48
  32. package/dungeons/vertical/devtools.js +58 -31
  33. package/dungeons/vertical/ecommerce-schema.json +126 -108
  34. package/dungeons/vertical/ecommerce.js +32 -8
  35. package/dungeons/vertical/education-schema.json +5261 -272
  36. package/dungeons/vertical/education.js +45 -22
  37. package/dungeons/vertical/fintech-schema.json +165 -98
  38. package/dungeons/vertical/fintech.js +43 -11
  39. package/dungeons/vertical/fitness-schema.json +69 -47
  40. package/dungeons/vertical/fitness.js +56 -24
  41. package/dungeons/vertical/food-delivery-schema.json +36237 -187
  42. package/dungeons/vertical/food-delivery.js +67 -15
  43. package/dungeons/vertical/gaming-schema.json +104 -60
  44. package/dungeons/vertical/gaming.js +61 -16
  45. package/dungeons/vertical/healthcare-schema.json +70 -48
  46. package/dungeons/vertical/healthcare.js +77 -42
  47. package/dungeons/vertical/insurance-application-schema.json +66 -47
  48. package/dungeons/vertical/insurance-application.js +34 -10
  49. package/dungeons/vertical/logistics-schema.json +60 -38
  50. package/dungeons/vertical/logistics.js +77 -43
  51. package/dungeons/vertical/marketplace-schema.json +65 -43
  52. package/dungeons/vertical/marketplace.js +52 -21
  53. package/dungeons/vertical/media-schema.json +4085 -123
  54. package/dungeons/vertical/media.js +65 -18
  55. package/dungeons/vertical/rpg-schema.json +1984 -147
  56. package/dungeons/vertical/rpg.js +47 -11
  57. package/dungeons/vertical/sass-schema.json +2622 -147
  58. package/dungeons/vertical/sass.js +40 -10
  59. package/dungeons/vertical/social-schema.json +146 -78
  60. package/dungeons/vertical/social.js +39 -10
  61. package/dungeons/vertical/travel-schema.json +57 -35
  62. package/dungeons/vertical/travel.js +58 -21
  63. package/index.js +4 -1
  64. package/lib/core/config-validator.js +13 -2
  65. package/lib/core/context.js +9 -0
  66. package/lib/core/storage.js +2 -0
  67. package/lib/generators/events.js +3 -5
  68. package/lib/orchestrators/user-loop.js +24 -1
  69. package/lib/utils/utils.js +129 -9
  70. package/package.json +1 -1
  71. package/scripts/extract-dungeon-schema.mjs +64 -18
  72. package/types.d.ts +8 -3
@@ -1,16 +1,23 @@
1
+ // ── TWEAK THESE ──
2
+ const SEED = "harness-food";
3
+ const num_days = 100;
4
+ const num_users = 5_000;
5
+ const avg_events_per_user = 120;
6
+ let token = "your-mixpanel-token";
7
+
8
+ // ── env overrides ──
9
+ if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
10
+
1
11
  import dayjs from "dayjs";
2
12
  import utc from "dayjs/plugin/utc.js";
3
13
  import "dotenv/config";
4
14
  import * as u from "../../lib/utils/utils.js";
5
15
  import * as v from "ak-tools";
6
16
 
7
- const SEED = "harness-food";
8
17
  dayjs.extend(utc);
9
18
  const chance = u.initChance(SEED);
10
- const num_users = 5_000;
11
- const days = 100;
12
19
 
13
- /** @typedef {import("../types.d.ts").Dungeon} Config */
20
+ /** @typedef {import("../../types").Dungeon} Config */
14
21
 
15
22
  /*
16
23
  * ═══════════════════════════════════════════════════════════════════════════════
@@ -156,10 +163,10 @@ const couponCodes = v.range(1, 51).map(n => `QUICK${v.uid(5).toUpperCase()}`);
156
163
 
157
164
  /** @type {Config} */
158
165
  const config = {
159
- token: "",
166
+ token,
160
167
  seed: SEED,
161
- numDays: days,
162
- numEvents: num_users * 120,
168
+ numDays: num_days,
169
+ numEvents: num_users * avg_events_per_user,
163
170
  numUsers: num_users,
164
171
  hasAnonIds: false,
165
172
  hasSessionIds: true,
@@ -176,10 +183,23 @@ const config = {
176
183
  hasAdSpend: false,
177
184
  percentUsersBornInDataset: 50,
178
185
  hasAvatar: true,
179
- batchSize: 2_500_000,
180
186
  concurrency: 1,
181
187
  writeToDisk: false,
182
- scdProps: {},
188
+ scdProps: {
189
+ subscription_tier: {
190
+ values: ["free", "trial", "monthly", "annual"],
191
+ frequency: "month",
192
+ timing: "fuzzy",
193
+ max: 6
194
+ },
195
+ restaurant_tier: {
196
+ values: ["new", "verified", "featured", "premium"],
197
+ frequency: "month",
198
+ timing: "fixed",
199
+ max: 6,
200
+ type: "restaurant_id"
201
+ }
202
+ },
183
203
 
184
204
  funnels: [
185
205
  {
@@ -366,6 +386,9 @@ const config = {
366
386
  "order_id": orderIds,
367
387
  "actual_delivery_mins": u.weighNumRange(12, 90, 1.0, 40),
368
388
  "on_time": [false, false, false, true, true, true, true, true, true, true],
389
+ "lunch_rush": [false],
390
+ "dinner_rush": [false],
391
+ "first_order_bonus": [false],
369
392
  "trial_retained": [false],
370
393
  }
371
394
  },
@@ -446,7 +469,7 @@ const config = {
446
469
  ],
447
470
 
448
471
  superProps: {
449
- platform: ["iOS", "Android", "Web"],
472
+ Platform: ["iOS", "Android", "Web"],
450
473
  subscription_tier: ["Free", "Free", "Free", "Free", "QuickBite+"],
451
474
  city: ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "San Francisco"],
452
475
  },
@@ -467,6 +490,9 @@ const config = {
467
490
  "favorite_restaurant_count": u.weighNumRange(1, 10),
468
491
  "is_high_risk": [false],
469
492
  "churn_risk_score": [0],
493
+ "Platform": ["iOS", "Android", "Web"],
494
+ "subscription_tier": ["Free", "Free", "Free", "Free", "QuickBite+"],
495
+ "city": ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "San Francisco"],
470
496
  },
471
497
 
472
498
  groupKeys: [
@@ -509,7 +535,7 @@ const config = {
509
535
  */
510
536
  hook: function (record, type, meta) {
511
537
  const NOW = dayjs();
512
- const DATASET_START = NOW.subtract(days, 'days');
538
+ const DATASET_START = NOW.subtract(num_days, 'days');
513
539
  const RAINY_WEEK_START = DATASET_START.add(20, 'days');
514
540
  const RAINY_WEEK_END = DATASET_START.add(27, 'days');
515
541
 
@@ -526,13 +552,11 @@ const config = {
526
552
 
527
553
  // Lunch rush: 11AM - 1PM
528
554
  if (hour >= 11 && hour <= 13) {
529
- record.conversionRate = record.conversionRate * 1.4;
530
555
  record.props.lunch_rush = true;
531
556
  record.props.dinner_rush = false;
532
557
  }
533
558
  // Dinner rush: 5PM - 8PM
534
559
  else if (hour >= 17 && hour <= 20) {
535
- record.conversionRate = record.conversionRate * 1.2;
536
560
  record.props.lunch_rush = false;
537
561
  record.props.dinner_rush = true;
538
562
  } else {
@@ -555,7 +579,6 @@ const config = {
555
579
  const userId = meta && meta.user && (meta.user.distinct_id || String(meta.user));
556
580
  const isNewUser = userId && userId.charCodeAt(0) % 2 === 0;
557
581
  if (isNewUser) {
558
- record.conversionRate = Math.min(98, record.conversionRate * 1.4);
559
582
  record.props.first_order_bonus = true;
560
583
  } else {
561
584
  record.props.first_order_bonus = false;
@@ -592,7 +615,7 @@ const config = {
592
615
  time: midTime,
593
616
  user_id: firstEvent.user_id,
594
617
  subscription_tier: firstEvent.subscription_tier,
595
- platform: firstEvent.platform,
618
+ Platform: firstEvent.Platform,
596
619
  city: firstEvent.city,
597
620
  coupon_code: chance.pickone(couponCodes),
598
621
  discount_type: chance.pickone(["percent", "flat", "free_delivery"]),
@@ -660,6 +683,35 @@ const config = {
660
683
  // ═══════════════════════════════════════════════════════════════════
661
684
  if (type === "everything") {
662
685
  const userEvents = record;
686
+ const profile = meta.profile;
687
+
688
+ // ── Stamp superProps from profile so they stay consistent per user ──
689
+ if (profile) {
690
+ userEvents.forEach((event) => {
691
+ if (profile.Platform !== undefined) event.Platform = profile.Platform;
692
+ if (profile.subscription_tier !== undefined) event.subscription_tier = profile.subscription_tier;
693
+ if (profile.city !== undefined) event.city = profile.city;
694
+ });
695
+ }
696
+
697
+ // ── Bug 2 fix: Conversion filtering for lunch rush + first order ──
698
+ // Hook 1: Non-lunch/dinner-hour events → drop ~30% of final funnel step
699
+ // Hook 8: Returning users (no first_order_bonus) → drop ~30% of final funnel step
700
+ for (let i = userEvents.length - 1; i >= 0; i--) {
701
+ const event = userEvents[i];
702
+ if (event.event === "order delivered") {
703
+ // Hook 1: if not during lunch or dinner rush, drop 30%
704
+ if (!event.lunch_rush && !event.dinner_rush && chance.bool({ likelihood: 30 })) {
705
+ userEvents.splice(i, 1);
706
+ continue;
707
+ }
708
+ // Hook 8: returning users (no first_order_bonus) drop 30%
709
+ if (event.first_order_bonus === false && chance.bool({ likelihood: 30 })) {
710
+ userEvents.splice(i, 1);
711
+ continue;
712
+ }
713
+ }
714
+ }
663
715
 
664
716
  // First pass: identify user patterns
665
717
  let isReferralUser = false;
@@ -69,12 +69,14 @@
69
69
  5
70
70
  ]
71
71
  },
72
- "used hint": {
73
- "$type": "array"
74
- },
75
- "used boost": {
76
- "$type": "array"
77
- },
72
+ "used hint": [
73
+ "no",
74
+ "yes"
75
+ ],
76
+ "used boost": [
77
+ "no",
78
+ "yes"
79
+ ],
78
80
  "level at start": {
79
81
  "$range": [
80
82
  2,
@@ -99,15 +101,18 @@
99
101
  451
100
102
  ]
101
103
  },
102
- "quest success": {
103
- "$type": "array"
104
- },
105
- "used hint": {
106
- "$type": "array"
107
- },
108
- "used boost": {
109
- "$type": "array"
110
- },
104
+ "quest success": [
105
+ "yes",
106
+ "no"
107
+ ],
108
+ "used hint": [
109
+ "no",
110
+ "yes"
111
+ ],
112
+ "used boost": [
113
+ "no",
114
+ "yes"
115
+ ],
111
116
  "number of deaths": [
112
117
  1,
113
118
  3
@@ -247,8 +252,51 @@
247
252
  }
248
253
  }
249
254
  ],
255
+ "funnels": [
256
+ {
257
+ "sequence": [
258
+ "app install",
259
+ "character creation",
260
+ "join party"
261
+ ],
262
+ "isFirstFunnel": true,
263
+ "conversionRate": 0.8,
264
+ "timeToConvert": 0.25
265
+ },
266
+ {
267
+ "sequence": [
268
+ "start quest",
269
+ "gameplay summary",
270
+ "complete quest"
271
+ ],
272
+ "conversionRate": 0.99,
273
+ "timeToConvert": 1,
274
+ "props": {
275
+ "quest type": [
276
+ "Rescue",
277
+ "Retrieve",
278
+ "Explore",
279
+ "Destroy",
280
+ "Investigate"
281
+ ],
282
+ "quest difficulty": [
283
+ "Easy",
284
+ "Medium",
285
+ "Hard",
286
+ "Legendary"
287
+ ],
288
+ "quest location": [
289
+ "Forest",
290
+ "Dungeon",
291
+ "Mountain",
292
+ "City",
293
+ "Desert"
294
+ ]
295
+ }
296
+ }
297
+ ],
250
298
  "superProps": {
251
- "platform": [
299
+ "Platform": [
252
300
  "Mobile",
253
301
  "Xbox",
254
302
  "Playstation",
@@ -319,8 +367,8 @@
319
367
  ],
320
368
  "level": {
321
369
  "$range": [
322
- 2,
323
- 17
370
+ 3,
371
+ 15
324
372
  ]
325
373
  },
326
374
  "background": [
@@ -340,55 +388,51 @@
340
388
  ],
341
389
  "subscription MRR": {
342
390
  "$range": [
343
- 1,
344
- 221
391
+ 0,
392
+ 244
345
393
  ]
346
394
  },
347
395
  "archetype": [
348
396
  "neutral"
349
397
  ]
350
398
  },
351
- "funnels": [
352
- {
353
- "sequence": [
354
- "app install",
355
- "character creation",
356
- "join party"
357
- ],
358
- "isFirstFunnel": true,
359
- "conversionRate": 0.8,
360
- "timeToConvert": 0.25
361
- },
362
- {
363
- "sequence": [
364
- "start quest",
365
- "gameplay summary",
366
- "complete quest"
367
- ],
368
- "conversionRate": 0.99,
369
- "timeToConvert": 1,
370
- "props": {
371
- "quest type": [
372
- "Rescue",
373
- "Retrieve",
374
- "Explore",
375
- "Destroy",
376
- "Investigate"
377
- ],
378
- "quest difficulty": [
379
- "Easy",
380
- "Medium",
381
- "Hard",
382
- "Legendary"
383
- ],
384
- "quest location": [
385
- "Forest",
386
- "Dungeon",
387
- "Mountain",
388
- "City",
389
- "Desert"
399
+ "scdProps": {
400
+ "subscription MRR": {
401
+ "type": "user",
402
+ "frequency": "week",
403
+ "timing": "fixed",
404
+ "max": 250,
405
+ "values": {
406
+ "$range": [
407
+ 1,
408
+ 221
390
409
  ]
391
410
  }
411
+ },
412
+ "player_rank": {
413
+ "type": "user",
414
+ "frequency": "week",
415
+ "timing": "fuzzy",
416
+ "max": 250,
417
+ "values": [
418
+ "bronze",
419
+ "silver",
420
+ "gold",
421
+ "diamond",
422
+ "legendary"
423
+ ]
424
+ },
425
+ "guild_rank": {
426
+ "type": "guild_id",
427
+ "frequency": "month",
428
+ "timing": "fixed",
429
+ "max": 6,
430
+ "values": [
431
+ "bronze",
432
+ "silver",
433
+ "gold",
434
+ "legendary"
435
+ ]
392
436
  }
393
- ]
437
+ }
394
438
  }
@@ -1,3 +1,12 @@
1
+ // ── TWEAK THESE ──
2
+ const SEED = "my-seed";
3
+ const num_days = 180;
4
+ const num_users = 12_000;
5
+ const avg_events_per_user = 90;
6
+ let token = "your-mixpanel-token";
7
+
8
+ // ── env overrides ──
9
+ if (process.env.MP_TOKEN) token = process.env.MP_TOKEN;
1
10
 
2
11
  import dayjs from "dayjs";
3
12
  import utc from "dayjs/plugin/utc.js";
@@ -5,13 +14,10 @@ import "dotenv/config";
5
14
  import * as u from "../../lib/utils/utils.js";
6
15
  import * as v from "ak-tools";
7
16
 
8
- const SEED = "my-seed";
9
17
  dayjs.extend(utc);
10
18
  const chance = u.initChance(SEED);
11
- const num_users = 12_000;
12
- const days = 180;
13
19
 
14
- /** @typedef {import("../types.d.ts").Dungeon} Config */
20
+ /** @typedef {import("../../types").Dungeon} Config */
15
21
 
16
22
  /*
17
23
  * ═══════════════════════════════════════════════════════════════════════════════
@@ -154,10 +160,10 @@ const days = 180;
154
160
 
155
161
  /** @type {Config} */
156
162
  const config = {
157
- token: "",
163
+ token,
158
164
  seed: "i am gamer face",
159
- numDays: days,
160
- numEvents: num_users * 90,
165
+ numDays: num_days,
166
+ numEvents: num_users * avg_events_per_user,
161
167
  numUsers: num_users,
162
168
  hasAnonIds: false,
163
169
  hasSessionIds: false,
@@ -175,7 +181,6 @@ const config = {
175
181
 
176
182
  hasAvatar: true,
177
183
 
178
- batchSize: 2_500_000,
179
184
  concurrency: 1,
180
185
  writeToDisk: false,
181
186
  funnels: [
@@ -361,7 +366,7 @@ const config = {
361
366
  },
362
367
  ],
363
368
  superProps: {
364
- platform: [
369
+ Platform: [
365
370
  "Mobile",
366
371
  "Xbox",
367
372
  "Playstation",
@@ -384,15 +389,47 @@ const config = {
384
389
 
385
390
  },
386
391
  scdProps: {
387
- // "subscription MRR" : {
388
- // "frequency": "week",
389
- // "type": "number",
390
- // values: u.weighNumRange(0, 250, 1, 200),
391
- // timing: "fixed",
392
- // max: 250,
393
- // }
392
+ "subscription MRR" : {
393
+ frequency: "week",
394
+ values: u.weighNumRange(0, 250, 1, 200),
395
+ timing: "fixed",
396
+ max: 250,
397
+ },
398
+ player_rank: {
399
+ values: ["bronze", "silver", "gold", "diamond", "legendary"],
400
+ frequency: "week",
401
+ timing: "fuzzy",
402
+ max: 250
403
+ },
404
+ guild_rank: {
405
+ values: ["bronze", "silver", "gold", "legendary"],
406
+ frequency: "month",
407
+ timing: "fixed",
408
+ max: 6,
409
+ type: "guild_id"
410
+ }
394
411
  },
395
412
  userProps: {
413
+ Platform: [
414
+ "Mobile",
415
+ "Xbox",
416
+ "Playstation",
417
+ "Switch",
418
+ "Web"
419
+ ],
420
+ "game mode": u.pickAWinner([
421
+ "Single Player",
422
+ "Multiplayer",
423
+ ], 1),
424
+ language: [
425
+ "English",
426
+ "Spanish",
427
+ "French",
428
+ "German",
429
+ "Japanese",
430
+ "Korean",
431
+ "Chinese",
432
+ ],
396
433
  experiment: [
397
434
  "fast leveling",
398
435
  "tension economy",
@@ -500,7 +537,15 @@ const config = {
500
537
  }
501
538
 
502
539
  if (type === "everything") {
540
+ // Stamp superProps from profile for consistency
541
+ const profile = meta.profile;
542
+ record.forEach(e => {
543
+ e.Platform = profile.Platform;
544
+ e["game mode"] = profile["game mode"];
545
+ e.language = profile.language;
546
+ });
503
547
 
548
+ return record;
504
549
  }
505
550
 
506
551
  return record;
@@ -411,6 +411,9 @@
411
411
  },
412
412
  {
413
413
  "event": "account deactivated",
414
+ "isChurnEvent": true,
415
+ "returnLikelihood": 0.15,
416
+ "isStrictEvent": true,
414
417
  "properties": {
415
418
  "reason": [
416
419
  "switched_provider",
@@ -422,53 +425,6 @@
422
425
  }
423
426
  }
424
427
  ],
425
- "superProps": {
426
- "subscription_tier": [
427
- "free",
428
- "basic",
429
- "premium"
430
- ],
431
- "platform": [
432
- "ios",
433
- "android",
434
- "web"
435
- ]
436
- },
437
- "userProps": {
438
- "role": [
439
- "patient",
440
- "nurse",
441
- "doctor"
442
- ],
443
- "specialty": [
444
- "none"
445
- ],
446
- "years_experience": {
447
- "$range": [
448
- 0,
449
- 5
450
- ]
451
- },
452
- "preferred_language": [
453
- "en",
454
- "es",
455
- "pt",
456
- "de",
457
- "fr"
458
- ],
459
- "has_chronic_condition": [
460
- false,
461
- true
462
- ],
463
- "age_range": [
464
- "18-25",
465
- "26-35",
466
- "36-45",
467
- "46-55",
468
- "56-65",
469
- "65+"
470
- ]
471
- },
472
428
  "funnels": [
473
429
  {
474
430
  "sequence": [
@@ -477,6 +433,7 @@
477
433
  "symptom search",
478
434
  "appointment booked"
479
435
  ],
436
+ "name": "Onboarding Flow",
480
437
  "isFirstFunnel": true,
481
438
  "conversionRate": 45,
482
439
  "timeToConvert": 72,
@@ -488,6 +445,7 @@
488
445
  "appointment booked",
489
446
  "consultation completed"
490
447
  ],
448
+ "name": "Booking to Consultation",
491
449
  "conversionRate": 40,
492
450
  "timeToConvert": 48,
493
451
  "weight": 5
@@ -499,6 +457,7 @@
499
457
  "prescription issued",
500
458
  "follow up scheduled"
501
459
  ],
460
+ "name": "Full Care Journey",
502
461
  "conversionRate": 30,
503
462
  "timeToConvert": 168,
504
463
  "weight": 3
@@ -509,6 +468,7 @@
509
468
  "prescription refill",
510
469
  "payment processed"
511
470
  ],
471
+ "name": "Prescription Lifecycle",
512
472
  "conversionRate": 55,
513
473
  "timeToConvert": 720,
514
474
  "weight": 2
@@ -519,9 +479,71 @@
519
479
  "provider rated",
520
480
  "follow up scheduled"
521
481
  ],
482
+ "name": "Patient Satisfaction",
522
483
  "conversionRate": 25,
523
484
  "timeToConvert": 72,
524
485
  "weight": 2
525
486
  }
526
- ]
487
+ ],
488
+ "superProps": {
489
+ "subscription_tier": [
490
+ "free",
491
+ "basic",
492
+ "premium"
493
+ ],
494
+ "Platform": [
495
+ "ios",
496
+ "android",
497
+ "web"
498
+ ]
499
+ },
500
+ "userProps": {
501
+ "role": [
502
+ "patient",
503
+ "nurse",
504
+ "doctor"
505
+ ],
506
+ "specialty": [
507
+ "none"
508
+ ],
509
+ "years_experience": {
510
+ "$range": [
511
+ 0,
512
+ 5
513
+ ]
514
+ },
515
+ "preferred_language": [
516
+ "en",
517
+ "es",
518
+ "pt",
519
+ "de",
520
+ "fr"
521
+ ],
522
+ "has_chronic_condition": [
523
+ false,
524
+ true
525
+ ],
526
+ "age_range": [
527
+ "18-25",
528
+ "26-35",
529
+ "36-45",
530
+ "46-55",
531
+ "56-65",
532
+ "65+"
533
+ ]
534
+ },
535
+ "scdProps": {
536
+ "care_plan": {
537
+ "type": "user",
538
+ "frequency": "month",
539
+ "timing": "fuzzy",
540
+ "max": 8,
541
+ "values": [
542
+ "preventive",
543
+ "routine",
544
+ "chronic",
545
+ "acute"
546
+ ]
547
+ }
548
+ }
527
549
  }