make-mp-data 2.0.18 → 2.0.21

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.
@@ -0,0 +1,158 @@
1
+ /**
2
+ * A "validValue" can be a primitive, an array of primitives,
3
+ * or a function that returns a primitive or an array of primitives.
4
+ * This is the building block for all property values in the dungeon.
5
+ */
6
+ type Primitives = string | number | boolean | Date;
7
+ type ValueValid = Primitives | Primitives[] | (() => Primitives | Primitives[]);
8
+
9
+
10
+ /**
11
+ * The main configuration object for the entire data generation spec, known as a "Dungeon".
12
+ * This is the high-level object you will be constructing.
13
+ */
14
+ export interface Dungeon {
15
+ /** A list of all possible events that can occur in the simulation. */
16
+ events?: EventConfig[];
17
+
18
+ /** A list of event sequences that represent user journeys (e.g., sign-up, purchase). */
19
+ funnels?: Funnel[];
20
+
21
+ /** Properties that are attached to every event for all users. */
22
+ superProps?: Record<string, ValueValid>;
23
+
24
+ /** Properties that define the characteristics of individual users. */
25
+ userProps?: Record<string, ValueValid>;
26
+
27
+ /** Properties that change for users or groups over time (Slowly Changing Dimensions). */
28
+ scdProps?: Record<string, SCDProp>;
29
+
30
+ /** Defines group entities, like companies or teams, and how many of each to create. */
31
+ groupKeys?: [string, number, string[]?][]; // [key, numGroups, optional_events_for_group]
32
+
33
+ /** Properties that define the characteristics of the groups defined in groupKeys. */
34
+ groupProps?: Record<string, Record<string, ValueValid>>;
35
+
36
+ /** Events that are attributed to a group entity rather than an individual user. */
37
+ groupEvents?: GroupEventConfig[];
38
+
39
+ /** Static data tables (e.g., product catalogs) that can be referenced in events. */
40
+ lookupTables?: LookupTableSchema[];
41
+
42
+ }
43
+
44
+
45
+ /**
46
+ * Defines a single event, its properties, and its likelihood of occurring.
47
+ */
48
+ interface EventConfig {
49
+ /** The name of the event (e.g., "Page View", "Add to Cart"). */
50
+ event?: string;
51
+
52
+ /** The relative frequency of this event compared to others. Higher numbers are more frequent. */
53
+ weight?: number;
54
+
55
+ /** A dictionary of properties associated with this event. */
56
+ properties?: Record<string, ValueValid>;
57
+
58
+ /** If true, this event will be the first event for a new user. */
59
+ isFirstEvent?: boolean;
60
+
61
+ /** If true, this event signifies that a user has churned. */
62
+ isChurnEvent?: boolean;
63
+ }
64
+
65
+
66
+ /**
67
+ * Defines a sequence of events that represents a meaningful user journey or workflow.
68
+ */
69
+ interface Funnel {
70
+ /** The name of the funnel (e.g., "Purchase Funnel"). */
71
+ name?: string;
72
+
73
+ /** A list of event names that make up the sequence of this funnel. */
74
+ sequence: string[];
75
+
76
+ /** The likelihood that a user will attempt this funnel. */
77
+ weight?: number;
78
+
79
+ /** If true, this funnel is part of the initial user experience (e.g., onboarding). */
80
+ isFirstFunnel?: boolean;
81
+
82
+ /** The probability (0-100) that a user who starts the funnel will complete it. */
83
+ conversionRate?: number;
84
+
85
+ /** The average time (in hours) it takes a user to complete the funnel. */
86
+ timeToConvert?: number;
87
+
88
+ /** * Defines the ordering of events within the funnel for a user.
89
+ * "sequential": Events must happen in the exact order defined in `sequence`.
90
+ * "random": Events can happen in any order.
91
+ * "first-and-last-fixed": The first and last events are fixed, but the middle ones are random.
92
+ */
93
+ order?: "sequential" | "random" | "first-and-last-fixed" | "first-fixed" | "last-fixed" | "interrupted";
94
+
95
+ /** Properties that will be attached to every event generated within this funnel journey. */
96
+ props?: Record<string, ValueValid>;
97
+ }
98
+
99
+
100
+ /**
101
+ * Defines a "Slowly Changing Dimension" — a property of a user or group
102
+ * that changes periodically over time (e.g., subscription plan, user role).
103
+ */
104
+ interface SCDProp {
105
+ /** The entity this property belongs to ('user' or a group key like 'company_id'). */
106
+ type?: string;
107
+
108
+ /** How often the value of this property can change. */
109
+ frequency: "day" | "week" | "month" | "year";
110
+
111
+ /** A list or function defining the possible values for this property. */
112
+ values: ValueValid;
113
+
114
+ /** * 'fixed': Changes occur exactly on the frequency interval.
115
+ * 'fuzzy': Changes occur randomly around the frequency interval.
116
+ */
117
+ timing: "fixed" | "fuzzy";
118
+
119
+ /** The maximum number of times this property can change for a single entity. */
120
+ max?: number;
121
+ }
122
+
123
+
124
+ /**
125
+ * Defines an event that is attributed to a group and occurs on a regular schedule.
126
+ * (e.g., a monthly subscription charge for a company).
127
+ */
128
+ interface GroupEventConfig extends EventConfig {
129
+ /** How often the event occurs (in days). */
130
+ frequency: number;
131
+
132
+ /** The group key this event is associated with (e.g., "company_id"). */
133
+ group_key: string;
134
+
135
+ /** If true, a random user within the group is also associated with the event. */
136
+ attribute_to_user: boolean;
137
+
138
+ /** The number of entities in this group. */
139
+ group_size: number;
140
+ }
141
+
142
+
143
+ /**
144
+ * Defines the schema for a static lookup table, which can be used to enrich event data.
145
+ * For example, a "products" table could hold details about product IDs.
146
+ */
147
+ interface LookupTableSchema {
148
+ /** The name of the key that will be used to join this table in an event (e.g., "product_id"). */
149
+ key: string;
150
+
151
+ /** The number of unique rows to generate for this table. */
152
+ entries: number;
153
+
154
+ /** A dictionary of attributes (columns) for the table and their possible values. */
155
+ attributes: Record<string, ValueValid>;
156
+ }
157
+ /**
158
+
@@ -4,8 +4,8 @@
4
4
  * @fileoverview Contains default values for campaigns, devices, locations, and domains
5
5
  */
6
6
 
7
- /** @typedef {import('../../types.d.ts').main.Dungeon} Config */
8
- /** @typedef {import('../../types.d.ts').main.ValueValid} ValueValid */
7
+ /** @typedef {import('../../types.js').Dungeon} Config */
8
+ /** @typedef {import('../../types.js').ValueValid} ValueValid */
9
9
 
10
10
  //? https://docs.mixpanel.com/docs/data-structure/property-reference#default-properties
11
11
 
@@ -0,0 +1,110 @@
1
+ import dayjs from "dayjs";
2
+ import utc from "dayjs/plugin/utc.js";
3
+ import "dotenv/config";
4
+ import { weighNumRange, range, date, initChance, exhaust, choose, integer, decimal } from "../../lib/utils/utils.js";
5
+
6
+ dayjs.extend(utc);
7
+
8
+ /**
9
+ * Dungeon template for AI-generated configurations
10
+ * This template provides the structure and standard configuration
11
+ * AI-generated content will be injected into the placeholders
12
+ */
13
+
14
+ export function createDungeonTemplate({
15
+ seed = "ai-generated-seed",
16
+ numUsers = 1000,
17
+ numDays = 100,
18
+ events = [],
19
+ funnels = [],
20
+ superProps = {},
21
+ userProps = {},
22
+ scdProps = {},
23
+ mirrorProps = {},
24
+ groupKeys = [],
25
+ groupProps = {},
26
+ lookupTables = [],
27
+ hookFunction = null
28
+ }) {
29
+ const chance = u.initChance(seed);
30
+ const numEvents = numUsers * 100;
31
+
32
+ /** @typedef {import("../../types.d.ts").Dungeon} Config */
33
+
34
+ /** @type {Config} */
35
+ const config = {
36
+ token: "",
37
+ seed: seed,
38
+ numDays: numDays,
39
+ numEvents: numEvents,
40
+ numUsers: numUsers,
41
+ hasAnonIds: false,
42
+ hasSessionIds: false,
43
+ format: "json",
44
+ alsoInferFunnels: true,
45
+ hasLocation: true,
46
+ hasAndroidDevices: true,
47
+ hasIOSDevices: true,
48
+ hasDesktopDevices: true,
49
+ hasBrowser: true,
50
+ hasCampaigns: true,
51
+ isAnonymous: false,
52
+ hasAdSpend: true,
53
+
54
+ hasAvatar: true,
55
+ makeChart: false,
56
+
57
+ batchSize: 1_500_000,
58
+ concurrency: 1,
59
+ writeToDisk: false,
60
+
61
+ // AI-generated content will be injected here
62
+ funnels: funnels,
63
+ events: events,
64
+ superProps: superProps,
65
+ userProps: userProps,
66
+ scdProps: scdProps,
67
+ mirrorProps: mirrorProps,
68
+ groupKeys: groupKeys,
69
+ groupProps: groupProps,
70
+ lookupTables: lookupTables,
71
+
72
+ hook: hookFunction || function (record, type, meta) {
73
+ // const NOW = dayjs();
74
+
75
+ // CALLED AFTER EACH EVENT IS CREATED
76
+ if (type === "event") {
77
+ // const EVENT_TIME = dayjs(record.time);
78
+ }
79
+
80
+ // CALLED ONCE FOR EACH USER AND DEFINES THEIR PROPS
81
+ if (type === "user") {
82
+
83
+ }
84
+
85
+ // CALLED AFTER FUNNELS EVENTS ARE CHOSEN
86
+ if (type === "funnel-post") {
87
+
88
+ }
89
+
90
+ // CALLED BEFORE FUNNELS EVENTS ARE CHOSEN
91
+ if (type === "funnel-pre") {
92
+
93
+ }
94
+
95
+ // CALLED BEFORE SCD EVENTS ARE CHOSEN
96
+ if (type === "scd-pre") {
97
+
98
+ }
99
+
100
+ // CALLED AT THE END WHEN A USERS ENTIRE HISTORY EXISTS
101
+ if (type === "everything") {
102
+
103
+ }
104
+
105
+ return record;
106
+ }
107
+ };
108
+
109
+ return config;
110
+ }
@@ -0,0 +1,77 @@
1
+ You are an AI assistant that generates a structured JavaScript object ("Dungeon") that defines a synthetic analytics dataset designed to simulate realistic user behavior, based on a prompt describing a business or product use case.
2
+
3
+ the spec you are building is a javascript object... the high level spec looks like this:
4
+
5
+ --------------
6
+
7
+ const my_dungeon = {
8
+ funnels: [],
9
+ events: [],
10
+ superProps: {},
11
+ userProps: {},
12
+ scdProps: {},
13
+ groupKeys: [],
14
+ groupProps: {},
15
+ lookupTables: [],
16
+ };
17
+
18
+ --------------
19
+
20
+
21
+ here's the full typescript definition for each of the properties in the entire spec; we call the specs 'DUNGEONS' and each dungeon is a javascript object:
22
+
23
+ --------------
24
+ //types.d.ts
25
+
26
+ <TYPES>
27
+
28
+ --------------
29
+
30
+ here is now an implementation of the dungeon spec which shows how these above features work together to create a data set that tells a story!
31
+
32
+ Pay close attention to how function calls like weighNumRange(...) and chance.company() are written directly into the object without quotes.
33
+
34
+ Your output must follow this exact format:
35
+
36
+ --------------
37
+
38
+ //my-dungeon.js
39
+ /** @type {import('../../types.js').Dungeon} */
40
+
41
+ <VERBOSE_SCHEMA>
42
+
43
+ --------------
44
+
45
+ Final Instructions & Rules:
46
+
47
+ Your job is to build a dungeon based on the analytics use case described in the prompt. Your only output will be a single, valid JavaScript object literal.
48
+
49
+ Core Requirements:
50
+
51
+ 1. Output Format: Your output must be a valid JavaScript object literal, NOT JSON. This is crucial. Your entire response should start with { and end with } and contain nothing else.
52
+
53
+ 2. Mandatory Fields: You MUST create funnels, events, superProps, and userProps for every dungeon. They should be directly related to the analytics use cases stated in the prompt.
54
+
55
+ 3. Optional Fields: You should include scdProps, groupKeys, groupProps, and lookupTables only if they are clearly relevant based on the use case prompt.
56
+ - Use groups if the prompt involves B2B, teams, companies, or organizations.
57
+ - Use SCD if user or group traits change over time (e.g., subscription tier).
58
+ - Use lookup tables if events reference external entities with their own attributes (e.g., product_id, video_id).
59
+
60
+ 4. Available Functions: You have access to these built-in functions: date, weighNumRange, range, and the chance library.
61
+
62
+ ❌ Critical Rules to Follow:
63
+
64
+ - Do NOT wrap function calls in quotes.
65
+
66
+ Correct: amount: weighNumRange(5, 500)
67
+ Incorrect: amount: "weighNumRange(5, 500)"
68
+
69
+ - Do NOT output comments, explanations, or any text outside of the JavaScript object.
70
+
71
+ - Do NOT generate vague or placeholder values like "value1", "example", or "random_string".
72
+
73
+ - Do NOT leave required arrays empty (funnels, events, etc.).
74
+
75
+ Your only task is to generate a single JavaScript object that follows this structure based on the user's request.
76
+ =================
77
+
@@ -0,0 +1,311 @@
1
+
2
+
3
+
4
+ import Chance from "chance";
5
+ const chance = new Chance();
6
+ import dayjs from "dayjs";
7
+ import utc from "dayjs/plugin/utc.js";
8
+ dayjs.extend(utc);
9
+ import { uid, comma } from "ak-tools";
10
+ import { pickAWinner, weighNumRange, date, integer, weighChoices, range } from "../utils/utils.js";
11
+
12
+
13
+ /** @type {import("../../types.js").Dungeon} */
14
+ //SPLIT HERE
15
+ const DUNGEON = {
16
+
17
+ /**
18
+ * events are the core building blocks of the dungeon
19
+ * each event has a name, a weight, and properties
20
+ * the weight determines how often the event occurs relative to other events
21
+ * properties are the data associated with the event
22
+ * they can be simple values or functions that return a value
23
+ * we have a few built-in functions to help you generate data
24
+ * you MUST create events for every dungeon
25
+ */
26
+ events: [
27
+ {
28
+ event: "checkout",
29
+ weight: 2,
30
+ properties: {
31
+ amount: weighNumRange(5, 500, .25), // this is ok if you need to pick a number from a range; params are min, max, skew (opt), and size (size of pool, also opt)
32
+ currency: ["USD", "CAD", "EUR", "BTC", "ETH", "JPY"],
33
+ coupon: ["none", "none", "none", "none", "10%OFF", "20%OFF", "10%OFF", "20%OFF", "30%OFF", "40%OFF", "50%OFF"],
34
+ numItems: weighNumRange(1, 10),
35
+
36
+ }
37
+ },
38
+ {
39
+ event: "add to cart",
40
+ weight: 4,
41
+ properties: {
42
+ amount: weighNumRange(5, 500, .25),
43
+ rating: weighNumRange(1, 5),
44
+ reviews: weighNumRange(0, 35),
45
+ isFeaturedItem: [true, false, false],
46
+ itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
47
+ dateItemListed: date(30, true, "YYYY-MM-DD") // this is ok if you need to pick a date from a range; params are intheLastDays, isPast, format
48
+ }
49
+ },
50
+ {
51
+ event: "page view",
52
+ weight: 10,
53
+ properties: {
54
+ page: ["/", "/", "/help", "/account", "/watch", "/listen", "/product", "/people", "/peace"],
55
+ utm_source: ["$organic", "$organic", "$organic", "$organic", "google", "google", "google", "facebook", "facebook", "twitter", "linkedin"],
56
+ }
57
+ },
58
+ {
59
+ event: "watch video",
60
+ weight: 8,
61
+ properties: {
62
+ videoCategory: ["funny", "educational", "inspirational", "music", "news", "sports", "cooking", "DIY", "travel", "gaming"],
63
+ isFeaturedItem: [true, false, false],
64
+ watchTimeSec: weighNumRange(10, 600, .25),
65
+ quality: ["2160p", "1440p", "1080p", "720p", "480p", "360p", "240p"],
66
+ format: ["mp4", "avi", "mov", "mpg"],
67
+ uploader_id: chance.guid.bind(chance), // You have access to the chance.js library for generating random data. You can assign a chance function directly to a property, like name: chance.company or uploader_id: chance.guid.
68
+ video_id: range(1, 1000) // SEE LOOKUP TABLES BELOW
69
+
70
+ }
71
+ },
72
+ {
73
+ event: "view item",
74
+ weight: 8,
75
+ properties: {
76
+ isFeaturedItem: [true, false, false],
77
+ itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
78
+ dateItemListed: date(30, true, "YYYY-MM-DD"),
79
+ product_id: range(1, 1000) // SEE LOOKUP TABLES BELOW
80
+ }
81
+ },
82
+ {
83
+ event: "save item",
84
+ weight: 5,
85
+ properties: {
86
+ isFeaturedItem: [true, false, false],
87
+ itemCategory: ["Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden", "Pet", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art", "Food", "Appliances", "Office", "Wedding", "Software"],
88
+ dateItemListed: date(30, true, "YYYY-MM-DD"),
89
+ }
90
+ },
91
+ {
92
+ event: "sign up",
93
+ isFirstEvent: true,
94
+ weight: 0,
95
+ properties: {
96
+ variants: ["A", "B", "C", "Control"],
97
+ flows: ["new", "existing", "loyal", "churned"],
98
+ flags: ["on", "off"],
99
+ experiment_ids: ["1234", "5678", "9012", "3456", "7890"],
100
+ multiVariate: [true, false]
101
+ }
102
+ }
103
+ ],
104
+ /**
105
+ * superProps are properties that are attached to every event
106
+ * they are selected randomly each time an event is generated
107
+ * they can be simple values or functions that return a value
108
+ * you will LIKELY create superProps for every dungeon
109
+ */
110
+ superProps: {
111
+ theme: ["light", "dark", "custom", "light", "dark"],
112
+ },
113
+ /**
114
+ * user properties work the same as event properties during the generation phase
115
+ * except each user has a single set of user properties
116
+ * these are properties that are attached to the user profile
117
+ * they can be simple values or functions that return a value
118
+ * you MUST create userProps for every dungeon
119
+ */
120
+ userProps: {
121
+ title: ["Mr.", "Ms.", "Mrs.", "Dr.", "Prof.", "Sir", "Madam", "Lord", "Lady", "Dame", "Baron", "Baroness", "Count", "Countess", "Viscount", "Viscountess", "Marquis", "Marchioness"],
122
+ luckyNumber: weighNumRange(42, 420, .3),
123
+ 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"]
124
+ },
125
+ /**
126
+ * Funnels represent intentional user journeys (e.g., sign-up, checkout),
127
+ * composed of a sequence of events.
128
+ * You should design at least one first-time funnel (e.g., onboarding)
129
+ * and one recurring funnel (e.g., purchases or content browsing)
130
+ * unless otherwise specified in the prompt.
131
+ * Funnels are the primary mechanism used to generate the example data, and it's critical sequences match events in the events array.
132
+ * there are many different options for the funnels like:
133
+ * isFirstFunnel, conversionRate, isChurnFunnel, order, props, requireRepeats, timeToConvert, weight
134
+ *
135
+ * isFirstFunnel are funnels a user will only go through once (like a sign up)
136
+ * non isFirstFunnel are funnels a user will go through multiple times (like a purchase)
137
+ *
138
+ */
139
+ funnels: [
140
+ {
141
+ name: "sign up funnel",
142
+ description: "journey for users signing up",
143
+ sequence: ["page view", "page view", "sign up"],
144
+ isFirstFunnel: true,
145
+ conversionRate: 50, // 50% of users will convert
146
+ order: "sequential", // events must occur in order
147
+ requireRepeats: false, // users can repeat events in the funnel
148
+ props: {}, // you can add properties to the funnel
149
+ timeToConvert: 1, // time to convert in hours
150
+ },
151
+ {
152
+ name: "purchase funnel",
153
+ description: "how users purchase items",
154
+ sequence: ["page view", "view item", "add to cart", "checkout"],
155
+ isFirstFunnel: false,
156
+ conversionRate: 10,
157
+ timeToConvert: 24,
158
+ requireRepeats: true, // users can repeat events in the funnel
159
+ order: "first-and-last-fixed"
160
+ },
161
+ {
162
+ name: "browsing funnel",
163
+ description: "how users browse items",
164
+ sequence: ["page view", "view item", "watch video", "save item"],
165
+ isFirstFunnel: false,
166
+ conversionRate: 65,
167
+ timeToConvert: 2,
168
+ requireRepeats: true, // users can repeat events in the funnel
169
+ order: "random",
170
+ props: {
171
+ "browsing type": ["casual", "intentional", "exploratory"] // you can add properties to the funnel
172
+ }
173
+ }
174
+ ],
175
+ /**
176
+ * scdProps are used for to represent slowly changing dimensions (at both a user and group level)
177
+ * these are properties that change over time, but not frequently
178
+ * and importantly we care about behaviors (events) during different epochs of SCD state
179
+ * you do NOT need to make SCD properties for every dungeon; only the ones with a use case for them
180
+ */
181
+ scdProps: {
182
+ role: {
183
+ type: "user",
184
+ frequency: "week",
185
+ values: ["admin", "collaborator", "user", "view only", "no access"],
186
+ timing: "fuzzy",
187
+ max: 10
188
+ },
189
+ NPS: {
190
+ type: "user",
191
+ frequency: "day",
192
+ values: weighNumRange(1, 10, 2, 150),
193
+ timing: "fuzzy",
194
+ max: 10
195
+ },
196
+ MRR: {
197
+ type: "company_id",
198
+ frequency: "month",
199
+ values: weighNumRange(0, 10000, .15),
200
+ timing: "fixed",
201
+ max: 10
202
+ },
203
+ AccountHealthScore: {
204
+ type: "company_id",
205
+ frequency: "week",
206
+ values: weighNumRange(1, 10, .15),
207
+ timing: "fixed",
208
+ max: 40
209
+ },
210
+ plan: {
211
+ type: "company_id",
212
+ frequency: "month",
213
+ values: ["free", "basic", "premium", "enterprise"],
214
+ timing: "fixed",
215
+ max: 10
216
+ }
217
+ },
218
+
219
+
220
+ /**
221
+ * groupKeys are used to represent entities for custom uniqueBy analysis
222
+ * they are an array of arrays, where each inner array is a pair of group_key and the number of profiles for that key
223
+ * this is useful for group analysis, where you want to analyze data by groups of users (by company, institution, school, etc...)
224
+ * groups have props too (they can also have events) ... but there can be more than one "group entity"
225
+ * you do NOT need to make groupKeys for every dungeon; only the ones with a use case for them
226
+ */
227
+ groupKeys: [
228
+ ["company_id", 500, []],
229
+ ["room_id", 10000, ["save video", "comment", "watch video"]]
230
+ ],
231
+ groupProps: {
232
+ company_id: {
233
+ name: () => { return chance.company(); }, // YOU CAN USE CHANCE IT"S BUILT IN!
234
+ email: () => { return `CSM: ${chance.pickone(["AK", "Jessica", "Michelle", "Dana", "Brian", "Dave"])}`; },
235
+ "# of employees": weighNumRange(3, 10000),
236
+ "industry": ["tech", "finance", "healthcare", "education", "government", "non-profit"],
237
+ "segment": ["enterprise", "SMB", "mid-market"],
238
+ "products": [["core"], ["core"], ["core", "add-ons"], ["core", "pro-serve"], ["core", "add-ons", "pro-serve"], ["core", "BAA", "enterprise"], ["free"], ["free"], ["free", "addons"]],
239
+ },
240
+ room_id: {
241
+ name: () => { return `#${chance.word({ length: integer(4, 24), capitalize: true })}`; },
242
+ email: ["public", "private"],
243
+ "room provider": ["partner", "core", "core", "core"],
244
+ "room capacity": weighNumRange(3, 1000000),
245
+ "isPublic": [true, false, false, false, false],
246
+ "country": chance.country.bind(chance),
247
+ "isVerified": [true, true, false, false, false],
248
+ }
249
+ },
250
+
251
+ /**
252
+ * groupEvents are used to represent events that are associated with a group that occur
253
+ * regularly (like a card charged, or a subscription renewed)
254
+ * they are generally NOT attributed to a user, but rather to a group entity
255
+ * you do NOT need to make groupEvents for every dungeon; only the ones with a use case for them
256
+ */
257
+ groupEvents: [
258
+ {
259
+ attribute_to_user: false,
260
+ event: "card charged",
261
+ weight: 1,
262
+
263
+ frequency: 30,
264
+ group_key: "company_id",
265
+ group_size: 500,
266
+ properties: {
267
+ amount: weighNumRange(5, 500, .25),
268
+ currency: ["USD", "USD", "USD", "CAD", "EUR", "EUR", "BTC", "BTC", "ETH", "JPY"],
269
+ plan: ["basic", "premium", "enterprise"],
270
+ "payment method": []
271
+ }
272
+ }],
273
+
274
+ /**
275
+ * lookupTables are used to represent static data that can be used in events
276
+ * they are an array of objects, where each object is a key-value pair
277
+ * this is useful for representing static data that can be used in events
278
+ * like product information, user information, etc...
279
+ * you do NOT need to make lookupTables for every dungeon; only the ones with a use case for them
280
+ * if you DO make a lookupTable, you MUST provide a key in some events whose value will be numerical
281
+ */
282
+ lookupTables: [{
283
+ key: "product_id",
284
+ entries: 1000,
285
+ attributes: {
286
+ category: [
287
+ "Books", "Movies", "Music", "Games", "Electronics", "Computers", "Smart Home", "Home", "Garden & Tools", "Pet Supplies", "Food & Grocery", "Beauty", "Health", "Toys", "Kids", "Baby", "Handmade", "Sports", "Outdoors", "Automotive", "Industrial", "Entertainment", "Art"
288
+ ],
289
+ "demand": ["high", "medium", "medium", "low"],
290
+ "supply": ["high", "medium", "medium", "low"],
291
+ "manufacturer": chance.company.bind(chance),
292
+ "price": weighNumRange(5, 500, .25),
293
+ "rating": weighNumRange(1, 5),
294
+ "reviews": weighNumRange(0, 35)
295
+ }
296
+
297
+ },
298
+ {
299
+ key: "video_id",
300
+ entries: 50000,
301
+ attributes: {
302
+ isFlagged: [true, false, false, false, false],
303
+ copyright: ["all rights reserved", "creative commons", "creative commons", "public domain", "fair use"],
304
+ uploader_id: chance.guid.bind(chance),
305
+ "uploader influence": ["low", "low", "low", "medium", "medium", "high"],
306
+ thumbs: weighNumRange(0, 35),
307
+ rating: ["G", "PG", "PG-13", "R", "NC-17", "PG-13", "R", "NC-17", "R", "PG", "PG"]
308
+ }
309
+
310
+ }],
311
+ };