make-mp-data 3.0.1 → 3.0.2

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 (50) hide show
  1. package/dungeons/adspend.js +35 -1
  2. package/dungeons/anon.js +25 -1
  3. package/dungeons/{array-of-object-loopup.js → array-of-object-lookup.js} +28 -8
  4. package/dungeons/benchmark-heavy.js +2 -2
  5. package/dungeons/benchmark-light.js +2 -2
  6. package/dungeons/big.js +2 -2
  7. package/dungeons/business.js +59 -12
  8. package/dungeons/complex.js +34 -1
  9. package/dungeons/copilot.js +1 -1
  10. package/dungeons/{harness/harness-education.js → education.js} +29 -12
  11. package/dungeons/experiments.js +15 -2
  12. package/dungeons/{harness/harness-fintech.js → fintech.js} +8 -8
  13. package/dungeons/foobar.js +33 -1
  14. package/dungeons/{harness/harness-food.js → food.js} +7 -4
  15. package/dungeons/funnels.js +38 -1
  16. package/dungeons/gaming.js +25 -5
  17. package/dungeons/media.js +861 -271
  18. package/dungeons/mil.js +29 -2
  19. package/dungeons/mirror.js +33 -1
  20. package/dungeons/{kurby.js → retention-cadence.js} +1 -1
  21. package/dungeons/{harness/harness-gaming.js → rpg.js} +5 -5
  22. package/dungeons/sanity.js +31 -2
  23. package/dungeons/{harness/harness-sass.js → sass.js} +2 -2
  24. package/dungeons/scd.js +46 -1
  25. package/dungeons/simple.js +1 -1
  26. package/dungeons/{harness/harness-social.js → social.js} +2 -2
  27. package/dungeons/streaming.js +373 -0
  28. package/dungeons/strict-event-test.js +1 -1
  29. package/dungeons/student-teacher.js +18 -5
  30. package/dungeons/text-generation.js +38 -1
  31. package/dungeons/too-big-events.js +38 -1
  32. package/dungeons/{userAgent.js → user-agent.js} +21 -1
  33. package/entry.js +5 -4
  34. package/lib/utils/logger.js +0 -4
  35. package/package.json +1 -4
  36. package/dungeons/ai-chat-analytics-ed.js +0 -275
  37. package/dungeons/clinch-agi.js +0 -632
  38. package/dungeons/ecommerce-store.js +0 -0
  39. package/dungeons/harness/harness-media.js +0 -961
  40. package/dungeons/money2020-ed-also.js +0 -277
  41. package/dungeons/money2020-ed.js +0 -580
  42. package/dungeons/uday-schema.json +0 -220
  43. package/lib/templates/funnels-instructions.txt +0 -272
  44. package/lib/templates/hook-examples.json +0 -187
  45. package/lib/templates/hooks-instructions.txt +0 -721
  46. package/lib/templates/refine-instructions.txt +0 -485
  47. package/lib/templates/schema-instructions.txt +0 -285
  48. package/lib/utils/ai.js +0 -896
  49. package/lib/utils/mixpanel.js +0 -101
  50. package/lib/utils/project.js +0 -167
package/dungeons/mil.js CHANGED
@@ -25,7 +25,7 @@ const channelIds = genIds(100);
25
25
 
26
26
  /** @type {Config} */
27
27
  const config = {
28
- token: process.env.MASTER_PROJECT_TOKEN || "",
28
+ token: "",
29
29
  seed: `LFG!`, //,
30
30
  numDays: days,
31
31
  numEvents: num_users * 62,
@@ -48,7 +48,7 @@ const config = {
48
48
 
49
49
  batchSize: 2_500_000,
50
50
  concurrency: 10,
51
- writeToDisk: true,
51
+ writeToDisk: false,
52
52
 
53
53
  funnels: [],
54
54
 
@@ -290,6 +290,33 @@ const config = {
290
290
  groupProps: {},
291
291
  lookupTables: [],
292
292
  hook: function (record, type, meta) {
293
+ // event hook: tag events by frequency tier and enrich booleans
294
+ if (type === "event") {
295
+ const highFreq = ["foo", "bar", "baz"];
296
+ const lowFreq = ["crumn", "yak"];
297
+ if (highFreq.includes(record.event)) {
298
+ record.frequency_tier = "high";
299
+ } else if (lowFreq.includes(record.event)) {
300
+ record.frequency_tier = "low";
301
+ } else {
302
+ record.frequency_tier = "medium";
303
+ }
304
+ // boolean super prop drives a derived field
305
+ if (record.boolean === true) {
306
+ record.flag_status = "active";
307
+ }
308
+ }
309
+
310
+ // everything hook: users with very few events are marked as churned
311
+ if (type === "everything") {
312
+ if (record.length <= 3) {
313
+ for (const e of record) {
314
+ e.is_churned_user = true;
315
+ }
316
+ }
317
+ return record;
318
+ }
319
+
293
320
  return record;
294
321
  }
295
322
  };
@@ -19,7 +19,7 @@ import { pickAWinner, weighNumRange, date, integer } from "../lib/utils/utils.js
19
19
 
20
20
  /** @type {import('../types').Dungeon} */
21
21
  const config = {
22
- token: process.env.MASTER_PROJECT_TOKEN || "",
22
+ token: "",
23
23
  seed: "mirror me",
24
24
  numDays: 30, //how many days worth of data
25
25
  numEvents: 10000, //how many events
@@ -120,6 +120,38 @@ const config = {
120
120
  groupProps: {},
121
121
  lookupTables: [],
122
122
  hook: function (record, type, meta) {
123
+ // --- user hook: segment users by spirit animal ---
124
+ if (type === "user") {
125
+ const aquatic = ["whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "seahorse", "crab", "lobster", "shrimp"];
126
+ record.segment = aquatic.includes(record.spiritAnimal) ? "aquatic" : "terrestrial";
127
+ return record;
128
+ }
129
+
130
+ // --- event hook: color-based property boost ---
131
+ if (type === "event") {
132
+ // warm-colored events get a higher updateMe value
133
+ const warmColors = ["red", "orange", "yellow"];
134
+ if (warmColors.includes(record.color)) {
135
+ record.updateMe = (record.updateMe || 5) + 10;
136
+ }
137
+ return record;
138
+ }
139
+
140
+ // --- everything hook: inject a "milestone" event for users with 8+ events ---
141
+ if (type === "everything") {
142
+ if (record.length >= 8) {
143
+ const lastEvent = record[record.length - 1];
144
+ record.push({
145
+ event: "milestone_reached",
146
+ time: lastEvent.time,
147
+ user_id: lastEvent.user_id,
148
+ color: lastEvent.color,
149
+ milestone: record.length
150
+ });
151
+ }
152
+ return record;
153
+ }
154
+
123
155
  return record;
124
156
  }
125
157
  };
@@ -67,7 +67,7 @@ function generateCadencedEvents(behaviorName, cadence, startTime, endTime, churn
67
67
 
68
68
  /** @type {Config} */
69
69
  const config = {
70
- token: "ed63e7c6227b1990d2b1b847d7d8e0d9",
70
+ token: "",
71
71
  seed: "lets go",
72
72
  numDays: days,
73
73
  numEvents: num_users * 50,
@@ -1,7 +1,7 @@
1
1
  import dayjs from "dayjs";
2
2
  import utc from "dayjs/plugin/utc.js";
3
3
  import "dotenv/config";
4
- import * as u from "../../lib/utils/utils.js";
4
+ import * as u from "../lib/utils/utils.js";
5
5
  import * as v from "ak-tools";
6
6
 
7
7
  const SEED = "harness-gaming";
@@ -110,7 +110,7 @@ const itemIds = v.range(1, 301).map(n => `item_${v.uid(7)}`);
110
110
 
111
111
  /** @type {Config} */
112
112
  const config = {
113
- token: "7958c5407b34dbbeef4db2c9d1f032d4",
113
+ token: "",
114
114
  seed: SEED,
115
115
  numDays: days,
116
116
  numEvents: num_users * 120,
@@ -585,8 +585,8 @@ const config = {
585
585
  const eventTime = dayjs(event.time);
586
586
  const daysSinceStart = firstEventTime ? eventTime.diff(firstEventTime, 'days', true) : 0;
587
587
 
588
- // Hook #8: Capture subscription tier from first event
589
- if (idx === 0 && event.subscription_tier) {
588
+ // Hook #8: Capture subscription tier from any event that has it
589
+ if (event.subscription_tier) {
590
590
  subscriptionTier = event.subscription_tier;
591
591
  }
592
592
 
@@ -1133,7 +1133,7 @@ export default config;
1133
1133
  * import config from './dungeons/harness-gaming.js';
1134
1134
  * const results = await generate(config);
1135
1135
  *
1136
- * OUTPUT FILES (with writeToDisk: true, format: "parquet", gzip: true):
1136
+ * OUTPUT FILES (with writeToDisk: false, format: "parquet", gzip: true):
1137
1137
  *
1138
1138
  * - needle-in-haystack__events.parquet.gz - All event data
1139
1139
  * - needle-in-haystack__user_profiles.parquet.gz - User profiles
@@ -18,7 +18,7 @@ import { weighNumRange, integer } from "../lib/utils/utils.js";
18
18
 
19
19
  /** @type {import('../types.js').Dungeon} */
20
20
  const config = {
21
- token: process.env.MASTER_PROJECT_TOKEN || "",
21
+ token: "",
22
22
  seed: "foo bar",
23
23
  numDays: 90, //how many days worth of data
24
24
  numEvents: 50_000, //how many events
@@ -29,7 +29,7 @@ const config = {
29
29
  hasSessionIds: false, //if true, hasSessionIds are created for each user
30
30
  alsoInferFunnels: true, //if true, infer funnels from events
31
31
  makeChart: true,
32
- writeToDisk: true,
32
+ writeToDisk: false,
33
33
  concurrency: 25,
34
34
  funnels: [
35
35
  {
@@ -104,6 +104,35 @@ const config = {
104
104
  spiritAnimal: ["duck", "dog", "otter", "penguin", "cat", "elephant", "lion", "cheetah", "giraffe", "zebra", "rhino", "hippo", "whale", "dolphin", "shark", "octopus", "squid", "jellyfish", "starfish", "seahorse", "crab", "lobster", "shrimp", "clam", "snail", "slug", "butterfly", "moth", "bee", "wasp", "ant", "beetle", "ladybug", "caterpillar", "centipede", "millipede", "scorpion", "spider", "tarantula", "tick", "mite", "mosquito", "fly", "dragonfly", "damselfly", "grasshopper", "cricket", "locust", "mantis", "cockroach", "termite", "praying mantis", "walking stick", "stick bug", "leaf insect", "lacewing", "aphid", "cicada", "thrips", "psyllid", "scale insect", "whitefly", "mealybug", "planthopper", "leafhopper", "treehopper", "flea", "louse", "bedbug", "flea beetle", "weevil", "longhorn beetle", "leaf beetle", "tiger beetle", "ground beetle", "lady beetle", "firefly", "click beetle", "rove beetle", "scarab beetle", "dung beetle", "stag beetle", "rhinoceros beetle", "hercules beetle", "goliath beetle", "jewel beetle", "tortoise beetle"]
105
105
  },
106
106
  hook: function (record, type, meta) {
107
+ // --- user hook: tag power users based on luckyNumber ---
108
+ if (type === "user") {
109
+ record.userTier = record.luckyNumber > 300 ? "power" : "regular";
110
+ return record;
111
+ }
112
+
113
+ // --- event hook: add an engagement score based on event type ---
114
+ if (type === "event") {
115
+ const highEngagement = ["fonk", "crumn", "yak"];
116
+ const midEngagement = ["garply", "durtle", "linny"];
117
+ if (highEngagement.includes(record.event)) {
118
+ record.engagement_score = chance.integer({ min: 70, max: 100 });
119
+ } else if (midEngagement.includes(record.event)) {
120
+ record.engagement_score = chance.integer({ min: 30, max: 69 });
121
+ } else {
122
+ record.engagement_score = chance.integer({ min: 1, max: 29 });
123
+ }
124
+ return record;
125
+ }
126
+
127
+ // --- everything hook: low-activity users lose their last few events (churn simulation) ---
128
+ if (type === "everything") {
129
+ if (record.length > 3 && record.length < 8) {
130
+ // users with few events lose the tail end — simulates churn
131
+ return record.slice(0, Math.ceil(record.length * 0.6));
132
+ }
133
+ return record;
134
+ }
135
+
107
136
  return record;
108
137
  }
109
138
  };
@@ -1,7 +1,7 @@
1
1
  import dayjs from "dayjs";
2
2
  import utc from "dayjs/plugin/utc.js";
3
3
  import "dotenv/config";
4
- import * as u from "../../lib/utils/utils.js";
4
+ import * as u from "../lib/utils/utils.js";
5
5
  import * as v from "ak-tools";
6
6
 
7
7
  const SEED = "harness-sass";
@@ -78,7 +78,7 @@ const failedDeployUsers = new Map();
78
78
 
79
79
  /** @type {Config} */
80
80
  const config = {
81
- token: "495b9a9bbfc06c0e65b98827e07a4b87",
81
+ token: "",
82
82
  seed: SEED,
83
83
  numDays: days,
84
84
  numEvents: num_users * 120,
package/dungeons/scd.js CHANGED
@@ -22,7 +22,7 @@ const videoCategories = ["funny", "educational", "inspirational", "music", "news
22
22
 
23
23
  /** @type {import('../types').Dungeon} */
24
24
  const config = {
25
- token: process.env.MASTER_PROJECT_TOKEN || "",
25
+ token: "",
26
26
  seed: "simple is best",
27
27
  numDays: 30, //how many days worth1 of data
28
28
  numEvents: 50000, //how many events
@@ -196,6 +196,51 @@ const config = {
196
196
  }
197
197
  },
198
198
  hook: function (record, type, meta) {
199
+ // --- user hook: classify users into spending tiers ---
200
+ if (type === "user") {
201
+ record.spendTier = record.luckyNumber > 250 ? "high_spender" : "budget";
202
+ return record;
203
+ }
204
+
205
+ // --- event hook: coupon users get discounted checkout amounts ---
206
+ if (type === "event") {
207
+ if (record.event === "checkout" && record.coupon && record.coupon !== "none") {
208
+ const discountPct = parseInt(record.coupon) || 10;
209
+ record.amount = Math.round(record.amount * (1 - discountPct / 100));
210
+ record.discount_applied = true;
211
+ }
212
+ // weekend watchers get longer watch times
213
+ if (record.event === "watch video" && record.time) {
214
+ const day = dayjs(record.time).day();
215
+ if (day === 0 || day === 6) {
216
+ record.watchTimeSec = Math.round((record.watchTimeSec || 60) * 1.5);
217
+ record.is_weekend = true;
218
+ }
219
+ }
220
+ return record;
221
+ }
222
+
223
+ // --- everything hook: simulate cart abandonment ---
224
+ if (type === "everything") {
225
+ const hasAddToCart = record.some(e => e.event === "add to cart");
226
+ const hasCheckout = record.some(e => e.event === "checkout");
227
+ // users who added to cart but never checked out: remove checkout events (if any slipped through)
228
+ // and mark them as abandoned
229
+ if (hasAddToCart && !hasCheckout && record.length > 2) {
230
+ const lastAdd = record.filter(e => e.event === "add to cart").pop();
231
+ if (lastAdd) {
232
+ record.push({
233
+ event: "cart_abandoned",
234
+ time: dayjs(lastAdd.time).add(30, "minute").toISOString(),
235
+ user_id: lastAdd.user_id,
236
+ platform: lastAdd.platform,
237
+ amount: lastAdd.amount
238
+ });
239
+ }
240
+ }
241
+ return record;
242
+ }
243
+
199
244
  return record;
200
245
  }
201
246
  };
@@ -22,7 +22,7 @@ const videoCategories = ["funny", "educational", "inspirational", "music", "news
22
22
 
23
23
  /** @type {import('../types').Dungeon} */
24
24
  const config = {
25
- token: process.env.MASTER_PROJECT_TOKEN || "",
25
+ token: "",
26
26
  seed: "simple is best",
27
27
  numDays: 100, //how many days worth1 of data
28
28
  numEvents: 1_000_000, //how many events
@@ -1,7 +1,7 @@
1
1
  import dayjs from "dayjs";
2
2
  import utc from "dayjs/plugin/utc.js";
3
3
  import "dotenv/config";
4
- import * as u from "../../lib/utils/utils.js";
4
+ import * as u from "../lib/utils/utils.js";
5
5
  import * as v from "ak-tools";
6
6
 
7
7
  const SEED = "harness-social";
@@ -98,7 +98,7 @@ const postIds = v.range(1, 1001).map(n => `post_${v.uid(8)}`);
98
98
 
99
99
  /** @type {Config} */
100
100
  const config = {
101
- token: "5ad61e3f75003dfe4faad0e9e5f41030",
101
+ token: "",
102
102
  seed: SEED,
103
103
  numDays: days,
104
104
  numEvents: num_users * 120,
@@ -0,0 +1,373 @@
1
+
2
+ const SEED = "my-seed";
3
+ import dayjs from 'dayjs';
4
+ import utc from 'dayjs/plugin/utc.js';
5
+ dayjs.extend(utc);
6
+ import 'dotenv/config';
7
+ import * as u from "../lib/utils/utils.js";
8
+ import * as v from 'ak-tools';
9
+ const chance = u.initChance(SEED);
10
+ const num_users = 10_000;
11
+ const days = 125;
12
+
13
+ /** @typedef {import("../types.js").Dungeon} Config */
14
+
15
+ function genIds(numIds = 1000) {
16
+ const ids = [];
17
+ for (let i = 0; i < numIds; i++) {
18
+ ids.push(v.uid());
19
+ }
20
+ return ids;
21
+ }
22
+
23
+ const videoIds = genIds();
24
+ const channelIds = genIds(100);
25
+
26
+ /** @type {Config} */
27
+ const config = {
28
+ // token: "",
29
+ seed: `LFG!`, //,
30
+ numDays: days,
31
+ numEvents: num_users * 63,
32
+ numUsers: num_users,
33
+ hasAnonIds: false,
34
+ hasSessionIds: false,
35
+ format: "csv",
36
+ alsoInferFunnels: false,
37
+ hasLocation: false,
38
+ hasAndroidDevices: true,
39
+ hasIOSDevices: true,
40
+ hasDesktopDevices: true,
41
+ hasBrowser: false,
42
+ hasCampaigns: false,
43
+ isAnonymous: false,
44
+ hasAdSpend: false,
45
+
46
+ hasAvatar: false,
47
+ makeChart: false,
48
+
49
+ batchSize: 2_500_000,
50
+ concurrency: 10,
51
+ writeToDisk: false,
52
+
53
+ funnels: [],
54
+
55
+ events: [
56
+ {
57
+ event: "watch video",
58
+ weight: 55,
59
+ properties: {
60
+ video_id: u.pickAWinner(videoIds),
61
+ "watch percent": u.pickAWinner([
62
+ 25,
63
+ 50,
64
+ 75,
65
+ 100,
66
+ ]),
67
+ "watch time": u.weighNumRange(1, 65, .89, 100),
68
+
69
+ "category": u.pickAWinner([
70
+ "comedy",
71
+ "educational",
72
+ "music",
73
+ "sports",
74
+ "news",
75
+ "gaming",
76
+ "travel",
77
+ ]),
78
+ quality: u.pickAWinner([
79
+ "240p",
80
+ "360p",
81
+ "480p",
82
+ "720p",
83
+ "1080p",
84
+ "4k",
85
+ ], 4),
86
+ autoplay: [
87
+ true,
88
+ false,
89
+ ],
90
+ fullscreen: [
91
+ true,
92
+ false,
93
+ ],
94
+ "ads?": [
95
+ true, true,
96
+ false,
97
+ ],
98
+ },
99
+ },
100
+ {
101
+ event: "like",
102
+ weight: 10,
103
+ properties: {
104
+ video_id: u.pickAWinner(videoIds),
105
+ },
106
+ },
107
+ {
108
+ event: "comment",
109
+ weight: 5,
110
+ properties: {
111
+ video_id: u.pickAWinner(videoIds),
112
+ comment_length: [
113
+ "short",
114
+ "medium",
115
+ "long",
116
+ ],
117
+ },
118
+ },
119
+ {
120
+ event: "share",
121
+ weight: 3,
122
+ properties: {
123
+ video_id: u.pickAWinner(videoIds),
124
+ "share network": u.pickAWinner([
125
+ "facebook",
126
+ "twitter",
127
+ "reddit",
128
+ "email",
129
+ "whatsapp",
130
+ ]),
131
+ },
132
+ },
133
+ {
134
+ event: "search",
135
+ weight: 25,
136
+ properties: {
137
+ search_term: [
138
+ "cats",
139
+ "dogs",
140
+ "tutorial",
141
+ "news",
142
+ "music",
143
+ ],
144
+ "results count": u.pickAWinner([
145
+ 0,
146
+ 1,
147
+ 2,
148
+ 3,
149
+ 4,
150
+ 5,
151
+ 6, 7, 8, 9, 10
152
+ ], 5),
153
+ "search category": [
154
+ "all",
155
+ "channels",
156
+ "playlists",
157
+ ],
158
+ },
159
+ },
160
+ {
161
+ event: "subscribe",
162
+ weight: 7,
163
+ properties: {
164
+ channel_id: u.pickAWinner(genIds()),
165
+ },
166
+ },
167
+ {
168
+ event: "unsubscribe",
169
+ weight: 2,
170
+ properties: {
171
+ channel_id: u.pickAWinner(genIds()),
172
+ },
173
+ },
174
+ {
175
+ event: "create playlist",
176
+ weight: 4,
177
+ properties: {
178
+ "play list name": u.pickAWinner([
179
+ "favorites",
180
+ "watch later",
181
+ "my music",
182
+ "funny videos",
183
+ "educational",
184
+ ]),
185
+ privacy: u.pickAWinner([
186
+ "public",
187
+ "private",
188
+ "unlisted",
189
+ ]),
190
+ },
191
+ },
192
+ {
193
+ event: "account signup",
194
+ weight: 1,
195
+ isFirstEvent: true,
196
+ properties: {
197
+ "sign up method": [
198
+ "email",
199
+ "google",
200
+ "facebook",
201
+ ],
202
+ },
203
+ },
204
+ {
205
+ event: "account login",
206
+ weight: 9,
207
+ properties: {
208
+ "log in method": u.pickAWinner([
209
+ "email",
210
+ "google",
211
+ "facebook",
212
+ ]),
213
+ success: [
214
+ true,
215
+ false,
216
+ ],
217
+ error_message: [
218
+ "incorrect password",
219
+ "user not found",
220
+ "account locked",
221
+ ],
222
+ },
223
+ },
224
+ {
225
+ event: "$experiment_started",
226
+ weight: 5,
227
+ isSessionStartEvent: true,
228
+ properties: {
229
+ "$experiment_type": "ak_ad_hoc",
230
+ "Experiment name": "show results on empty search",
231
+ "Variant name": ["feature enabled", "feature disabled"],
232
+ }
233
+ }
234
+ ],
235
+ superProps: {
236
+ platform: u.pickAWinner([
237
+ "web",
238
+ "ios",
239
+ "android",
240
+ ]),
241
+ network_type: [
242
+ "wifi",
243
+ "cellular",
244
+ ],
245
+ },
246
+ userProps: {
247
+ subscription_status: [
248
+ "free",
249
+ "free",
250
+ "premium",
251
+ ],
252
+ age_range: [
253
+ "13-17",
254
+ "18-24",
255
+ "25-34",
256
+ "35-44",
257
+ "45-54",
258
+ "55+",
259
+ ],
260
+ preferred_genre: u.pickAWinner([
261
+ "comedy",
262
+ "action",
263
+ "drama",
264
+ "sci-fi",
265
+ "horror",
266
+ ]),
267
+ upload_count: [
268
+ 0,
269
+ 1,
270
+ 5,
271
+ 10,
272
+ 20,
273
+ ],
274
+ following_count: [
275
+ 0,
276
+ 10,
277
+ 50,
278
+ 100,
279
+ 500,
280
+ ],
281
+ dark_mode_enabled: [
282
+ true,
283
+ false,
284
+ ],
285
+ },
286
+
287
+ scdProps: {},
288
+ mirrorProps: {},
289
+ groupKeys: [],
290
+ groupProps: {},
291
+ lookupTables: [],
292
+ hook: function (record, type, meta) {
293
+ const NOW = dayjs();
294
+ const TIME_WHEN_SEARCH_GOT_BAD = NOW.subtract(21, 'days');
295
+ const TIME_WE_EXPERIMENTED = NOW.subtract(14, 'days');
296
+
297
+ if (type === "event") {
298
+ const EVENT_TIME = dayjs(record.time);
299
+ //when search got bad, people started searching less
300
+ //and got fewer results
301
+ if (EVENT_TIME.isAfter(TIME_WHEN_SEARCH_GOT_BAD)) {
302
+ if (chance.bool({ likelihood: 50 })) {
303
+ if (record.event === "search") {
304
+ record["results count"] = 0;
305
+ }
306
+ }
307
+
308
+ if (chance.bool({ likelihood: 18 })) {
309
+ record._drop = true;
310
+ }
311
+ }
312
+
313
+ if (EVENT_TIME.isBefore(TIME_WE_EXPERIMENTED)) {
314
+ if (record.event === "$experiment_started") {
315
+ record._drop = true;
316
+ }
317
+ }
318
+ }
319
+
320
+
321
+
322
+ if (type === "everything") {
323
+ // Filter out events tagged for dropping by the event hook
324
+ record = record.filter(e => !e._drop);
325
+
326
+ const hadFeatureEnabled = record.some(event =>
327
+ event.event === "$experiment_started" &&
328
+ event["Variant name"] === "feature enabled"
329
+ );
330
+
331
+ const hadFeatureDisabled = record.some(event =>
332
+ event.event === "$experiment_started" &&
333
+ event["Variant name"] === "feature disabled"
334
+ );
335
+
336
+ record.forEach((event, idx) => {
337
+ const EVENT_TIME = dayjs(event.time);
338
+
339
+ if (EVENT_TIME.isAfter(TIME_WE_EXPERIMENTED)) {
340
+ if (hadFeatureEnabled) {
341
+ // Users with feature enabled variant have a higher likelihood of subscribing.
342
+ // Add an extra subscribe event 50% of the time immediately after watching a video.
343
+ if (event.event === "watch video" && chance.bool({ likelihood: 75 })) {
344
+ // watch time goes up
345
+ event["watch time"] = v.round(event["watch time"] * 1.7);
346
+ const subscribeEvent = {
347
+ event: "subscribe",
348
+ time: dayjs(event.time).add(1, 'minute').toISOString(),
349
+ user_id: event.user_id,
350
+ };
351
+ record.splice(idx + 1, 0, subscribeEvent);
352
+ }
353
+ } else if (hadFeatureDisabled) {
354
+ // Users with feature disabled variant have lower likelihood of subscribing.
355
+ // Drop subscribe events 50% of the time.
356
+ if (event.event === "subscribe" && chance.bool({ likelihood: 75 })) {
357
+ record.splice(idx, 1);
358
+ }
359
+
360
+ // watch time goes down
361
+ if (event.event === "watch video") {
362
+ event["watch time"] = v.round(event["watch time"] * 0.5);
363
+ }
364
+ }
365
+ }
366
+ });
367
+ }
368
+
369
+ return record;
370
+ }
371
+ };
372
+
373
+ export default config;
@@ -14,7 +14,7 @@ export default {
14
14
  strictEventCount: true,
15
15
 
16
16
  verbose: true,
17
- writeToDisk: true,
17
+ writeToDisk: false,
18
18
  format: 'json',
19
19
 
20
20
  events: [