make-mp-data 1.4.4 → 1.4.5
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.
- package/.vscode/settings.json +1 -0
- package/core/index.js +24 -19
- package/core/utils.js +134 -77
- package/dungeons/.gitkeep +0 -0
- package/package.json +5 -5
- package/schemas/anon.js +2 -2
- package/schemas/complex.js +22 -22
- package/schemas/foobar.js +2 -2
- package/schemas/funnels.js +10 -10
- package/schemas/simple.js +16 -30
- package/scratch.mjs +4 -7
- package/scripts/new.sh +52 -0
- package/tests/coverage/.gitkeep +0 -0
- package/tests/e2e.test.js +4 -22
- package/tests/jest.config.js +3 -2
- package/tests/unit.test.js +327 -207
- package/types.d.ts +157 -80
- package/schemas/deepNest.js +0 -106
package/types.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
declare namespace main {
|
|
2
|
+
/**
|
|
3
|
+
* most of the time, the value of a property is a primitive
|
|
4
|
+
*/
|
|
2
5
|
type Primitives = string | number | boolean | Date | Record<string, any>;
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
/**
|
|
8
|
+
* a "validValue" can be a primitive, an array of primitives, or a function that returns a primitive
|
|
9
|
+
*/
|
|
5
10
|
export type ValueValid = Primitives | ValueValid[] | (() => ValueValid);
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* main config object for the entire data generation
|
|
14
|
+
*/
|
|
8
15
|
export interface Config {
|
|
9
16
|
token?: string;
|
|
10
17
|
seed?: string;
|
|
@@ -12,18 +19,17 @@ declare namespace main {
|
|
|
12
19
|
epochStart?: number;
|
|
13
20
|
epochEnd?: number;
|
|
14
21
|
numEvents?: number;
|
|
15
|
-
numUsers?: number;
|
|
16
|
-
|
|
17
|
-
//switches
|
|
18
|
-
isAnonymous?: boolean;
|
|
19
|
-
hasLocation?: boolean;
|
|
20
|
-
hasCampaigns?: boolean;
|
|
21
|
-
hasAdSpend?: boolean;
|
|
22
|
-
hasIOSDevices?: boolean;
|
|
23
|
-
hasAndroidDevices?: boolean;
|
|
24
|
-
hasDesktopDevices?: boolean;
|
|
25
|
-
hasBrowser?: boolean;
|
|
22
|
+
numUsers?: number;
|
|
26
23
|
|
|
24
|
+
//switches
|
|
25
|
+
isAnonymous?: boolean;
|
|
26
|
+
hasLocation?: boolean;
|
|
27
|
+
hasCampaigns?: boolean;
|
|
28
|
+
hasAdSpend?: boolean;
|
|
29
|
+
hasIOSDevices?: boolean;
|
|
30
|
+
hasAndroidDevices?: boolean;
|
|
31
|
+
hasDesktopDevices?: boolean;
|
|
32
|
+
hasBrowser?: boolean;
|
|
27
33
|
|
|
28
34
|
format?: "csv" | "json";
|
|
29
35
|
region?: "US" | "EU";
|
|
@@ -35,7 +41,7 @@ declare namespace main {
|
|
|
35
41
|
mirrorProps?: Record<string, MirrorProps>;
|
|
36
42
|
groupKeys?: [string, number][] | [string, number, string[]][]; // [key, numGroups, [events]]
|
|
37
43
|
groupProps?: Record<string, Record<string, ValueValid>>;
|
|
38
|
-
lookupTables?:
|
|
44
|
+
lookupTables?: LookupTableSchema[];
|
|
39
45
|
writeToDisk?: boolean;
|
|
40
46
|
simulationName?: string;
|
|
41
47
|
verbose?: boolean;
|
|
@@ -46,12 +52,18 @@ declare namespace main {
|
|
|
46
52
|
hook?: Hook<any>;
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
/**
|
|
56
|
+
* the soup is a set of parameters that determine the distribution of events over time
|
|
57
|
+
*/
|
|
49
58
|
type soup = {
|
|
50
59
|
deviation?: number;
|
|
51
60
|
peaks?: number;
|
|
52
61
|
mean?: number;
|
|
53
62
|
};
|
|
54
63
|
|
|
64
|
+
/**
|
|
65
|
+
* the types of hooks that can be used
|
|
66
|
+
*/
|
|
55
67
|
type hookTypes =
|
|
56
68
|
| "event"
|
|
57
69
|
| "user"
|
|
@@ -61,91 +73,126 @@ declare namespace main {
|
|
|
61
73
|
| "mirror"
|
|
62
74
|
| "funnel-pre"
|
|
63
75
|
| "funnel-post"
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
| "ad-spend"
|
|
77
|
+
| "churn"
|
|
66
78
|
| "";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* a hook is a function that can be called before each entity is created, and can be used to modify attributes
|
|
82
|
+
*/
|
|
67
83
|
export type Hook<T> = (record: any, type: hookTypes, meta: any) => T;
|
|
68
84
|
|
|
69
|
-
export interface
|
|
85
|
+
export interface hookArrayOptions<T> {
|
|
70
86
|
hook?: Hook<T>;
|
|
71
87
|
type?: hookTypes;
|
|
72
88
|
[key: string]: any;
|
|
73
89
|
}
|
|
74
90
|
|
|
91
|
+
/**
|
|
92
|
+
* an enriched array is an array that has a hookPush method that can be used to transform-then-push items into the array
|
|
93
|
+
*/
|
|
75
94
|
export interface EnrichedArray<T> extends Array<T> {
|
|
76
95
|
hookPush: (item: T) => boolean;
|
|
77
96
|
}
|
|
78
97
|
|
|
98
|
+
/**
|
|
99
|
+
* how we define events and their properties
|
|
100
|
+
*/
|
|
79
101
|
export interface EventConfig {
|
|
80
102
|
event?: string;
|
|
81
103
|
weight?: number;
|
|
82
104
|
properties?: Record<string, ValueValid>;
|
|
83
105
|
isFirstEvent?: boolean;
|
|
106
|
+
isChurnEvent?: boolean;
|
|
84
107
|
relativeTimeMs?: number;
|
|
85
108
|
}
|
|
86
109
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
insert_id: string;
|
|
91
|
-
device_id?: string;
|
|
92
|
-
session_id?: string;
|
|
93
|
-
user_id?: string;
|
|
94
|
-
[key: string]: ValueValid;
|
|
95
|
-
}
|
|
96
|
-
|
|
110
|
+
/**
|
|
111
|
+
* how we define funnels and their properties
|
|
112
|
+
*/
|
|
97
113
|
export interface Funnel {
|
|
114
|
+
/**
|
|
115
|
+
* the sequence of events that define the funnel
|
|
116
|
+
*/
|
|
98
117
|
sequence: string[];
|
|
118
|
+
/**
|
|
119
|
+
* how likely the funnel is to be selected
|
|
120
|
+
*/
|
|
99
121
|
weight?: number;
|
|
122
|
+
/**
|
|
123
|
+
* If true, the funnel will be the first thing the user does
|
|
124
|
+
*/
|
|
100
125
|
isFirstFunnel?: boolean;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
/**
|
|
127
|
+
* If true, the funnel will require the user to repeat the sequence of events in order to convert
|
|
128
|
+
* If false, the user does not need to repeat the sequence of events in order to convert
|
|
129
|
+
* ^ when false, users who repeat the repetitive steps are more likely to convert
|
|
130
|
+
*/
|
|
131
|
+
requireRepeats?: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* how the events in the funnel are ordered for each user
|
|
134
|
+
*/
|
|
107
135
|
order?:
|
|
108
136
|
| "sequential"
|
|
109
137
|
| "first-fixed"
|
|
110
138
|
| "last-fixed"
|
|
111
|
-
| "random"
|
|
139
|
+
| "random" //totally shuffled
|
|
112
140
|
| "first-and-last-fixed"
|
|
113
141
|
| "middle-fixed"
|
|
114
|
-
|
|
142
|
+
| "interrupted"; //todo: explain this
|
|
143
|
+
/**
|
|
144
|
+
* todo: implement this
|
|
145
|
+
* if set, the funnel might be the last thing the user does
|
|
146
|
+
* ^ the numerical value is the likelihood that the user will churn
|
|
147
|
+
* todo: allow for users to be resurrected
|
|
148
|
+
*/
|
|
149
|
+
isChurnFunnel?: void | number;
|
|
150
|
+
/**
|
|
151
|
+
* the likelihood that a user will convert (0-100%)
|
|
152
|
+
*/
|
|
115
153
|
conversionRate?: number;
|
|
154
|
+
/**
|
|
155
|
+
* the time it takes (on average) to convert in hours
|
|
156
|
+
*/
|
|
116
157
|
timeToConvert?: number;
|
|
158
|
+
/**
|
|
159
|
+
* funnel properties go onto each event in the funnel and are held constant
|
|
160
|
+
*/
|
|
117
161
|
props?: Record<string, ValueValid>;
|
|
118
162
|
}
|
|
119
163
|
|
|
164
|
+
/**
|
|
165
|
+
* mirror props are used to show mutations of event data over time
|
|
166
|
+
* there are different strategies for how to mutate the data
|
|
167
|
+
*/
|
|
120
168
|
export interface MirrorProps {
|
|
169
|
+
/**
|
|
170
|
+
* the event that will be mutated in the new version
|
|
171
|
+
*/
|
|
121
172
|
events?: string[] | "*";
|
|
122
|
-
|
|
123
|
-
|
|
173
|
+
/**
|
|
174
|
+
* "create" - create this key in the new version; value are chosen
|
|
175
|
+
* "update" - update this key in the new version; values are chosen
|
|
176
|
+
* "fill" - update this key in the new version, but only if the existing key is null or unset
|
|
177
|
+
* "delete" - delete this key in the new version; values are ignored
|
|
178
|
+
*/
|
|
179
|
+
strategy?: "create" | "update" | "fill" | "delete" | "";
|
|
180
|
+
values?: ValueValid[];
|
|
124
181
|
}
|
|
125
182
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
183
|
+
/**
|
|
184
|
+
* the generated event data
|
|
185
|
+
*/
|
|
186
|
+
export interface EventSchema {
|
|
187
|
+
event: string;
|
|
188
|
+
time: string;
|
|
189
|
+
insert_id: string;
|
|
190
|
+
device_id?: string;
|
|
191
|
+
session_id?: string;
|
|
192
|
+
user_id?: string;
|
|
136
193
|
[key: string]: ValueValid;
|
|
137
194
|
}
|
|
138
195
|
|
|
139
|
-
export type Result = {
|
|
140
|
-
eventData: EventData[];
|
|
141
|
-
userProfilesData: any[];
|
|
142
|
-
scdTableData: any[];
|
|
143
|
-
groupProfilesData: GroupProfilesData[];
|
|
144
|
-
lookupTableData: LookupTableData[];
|
|
145
|
-
importResults?: ImportResults;
|
|
146
|
-
files?: string[];
|
|
147
|
-
};
|
|
148
|
-
|
|
149
196
|
export interface EventData {
|
|
150
197
|
event: string;
|
|
151
198
|
source: string;
|
|
@@ -156,26 +203,15 @@ declare namespace main {
|
|
|
156
203
|
[key: string]: any;
|
|
157
204
|
}
|
|
158
205
|
|
|
159
|
-
export interface
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
key: string;
|
|
166
|
-
data: any[];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export interface ImportResults {
|
|
170
|
-
events: ImportResult;
|
|
171
|
-
users: ImportResult;
|
|
172
|
-
groups: ImportResult[];
|
|
206
|
+
export interface UserProfile {
|
|
207
|
+
name?: string;
|
|
208
|
+
email?: string;
|
|
209
|
+
avatar?: string;
|
|
210
|
+
created: string | undefined;
|
|
211
|
+
distinct_id: string;
|
|
212
|
+
[key: string]: ValueValid;
|
|
173
213
|
}
|
|
174
214
|
|
|
175
|
-
export interface ImportResult {
|
|
176
|
-
success: number;
|
|
177
|
-
bytes: number;
|
|
178
|
-
}
|
|
179
215
|
export interface Person {
|
|
180
216
|
name: string;
|
|
181
217
|
email: string;
|
|
@@ -186,14 +222,55 @@ declare namespace main {
|
|
|
186
222
|
distinct_id?: string;
|
|
187
223
|
}
|
|
188
224
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
225
|
+
/**
|
|
226
|
+
* the generated user data
|
|
227
|
+
*/
|
|
228
|
+
export interface LookupTableSchema {
|
|
229
|
+
key: string;
|
|
230
|
+
entries: number;
|
|
231
|
+
attributes: Record<string, ValueValid>;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface LookupTableData {
|
|
235
|
+
key: string;
|
|
236
|
+
data: any[];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface SCDSchema {
|
|
194
240
|
distinct_id: string;
|
|
241
|
+
insertTime: string;
|
|
242
|
+
startTime: string;
|
|
195
243
|
[key: string]: ValueValid;
|
|
196
244
|
}
|
|
245
|
+
|
|
246
|
+
export interface GroupProfileSchema {
|
|
247
|
+
key: string;
|
|
248
|
+
data: any[];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* the end result of importing data into mixpanel
|
|
253
|
+
*/
|
|
254
|
+
export interface ImportResults {
|
|
255
|
+
events: ImportResult;
|
|
256
|
+
users: ImportResult;
|
|
257
|
+
groups: ImportResult[];
|
|
258
|
+
}
|
|
259
|
+
type ImportResult = import("mixpanel-import").ImportResults;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* the end result of the data generation
|
|
263
|
+
*/
|
|
264
|
+
export type Result = {
|
|
265
|
+
eventData: EventData[];
|
|
266
|
+
userProfilesData: any[];
|
|
267
|
+
scdTableData: any[];
|
|
268
|
+
adSpendData: EventData[];
|
|
269
|
+
groupProfilesData: GroupProfileSchema[];
|
|
270
|
+
lookupTableData: LookupTableData[];
|
|
271
|
+
importResults?: ImportResults;
|
|
272
|
+
files?: string[];
|
|
273
|
+
};
|
|
197
274
|
}
|
|
198
275
|
|
|
199
276
|
/**
|
package/schemas/deepNest.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
const Chance = require('chance');
|
|
2
|
-
const chance = new Chance();
|
|
3
|
-
const u = require('../core/utils');
|
|
4
|
-
|
|
5
|
-
const plans = ['free', 'premium', 'casual', 'influencer'];
|
|
6
|
-
const marketingChannels = ["Organic", "Organic", "Organic", "Organic", "Instagram Ads", "Facebook Ads", "Google Ads", "Youtube Ads", "Instagram Post", "Instagram Post", "Facebook Post"];
|
|
7
|
-
|
|
8
|
-
const config = {
|
|
9
|
-
token: "",
|
|
10
|
-
secret: "",
|
|
11
|
-
seed: "get nesty!",
|
|
12
|
-
events: ['watch video', 'upload video', 'like video', 'dislike video', 'subscribe'],
|
|
13
|
-
eventProperties: {
|
|
14
|
-
videoMeta: generateVideoMeta
|
|
15
|
-
},
|
|
16
|
-
userProperties: {
|
|
17
|
-
userMeta: generateUserMeta
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
groupKeys: [],
|
|
22
|
-
groupProperties: {}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function generateVideoMeta() {
|
|
27
|
-
let formats = ['2160p', '1440p', '1080p', '720p', '480p', '360p', '240p'];
|
|
28
|
-
let ratios = ['4:3', '16:10', '16:9'];
|
|
29
|
-
let containers = ["WEBM", ["MPG", "MP2", "MPEG"], ["MP4", "M4P", "M4V"], ["AVI", "WMV"], ["MOV", "QT"], ["FLV", "SWF"], "AVCHD"];
|
|
30
|
-
let hashtags = ["#AK", "#bitcoin", "#cureForMiley", "#faceValue", "#blm", "#fwiw", "#inappropriateFuneralSongs", "#jurassicPork", "#lolCats", "#wheatForSheep", "#momTexts", "#myWeirdGymStory", "#poppy", "#resist", "#tbt", "#wilson", "#worstGiftEver", "#yolo", "#phish", "#crypto", "#memes", "#wrongMovie", "#careerEndingTwitterTypos", "#twoThingsThatDontMix"];
|
|
31
|
-
let platforms = ["Web", "Mobile Web", "Native (Android)", "Native (iOS)", "Native (Desktop)", "IoT"];
|
|
32
|
-
let plans = ['free', 'premium', 'casual', 'influencer'];
|
|
33
|
-
let categories = ["Product reviews video", "How-to videos", "Vlogs", "Gaming videos", "Comedy/skit videos", "Haul videos", "Memes/tags", "Favorites/best of", "Educational videos", "Unboxing videos", "Q&A videos", "Collection", "Prank videos"];
|
|
34
|
-
let marketingChannels = ["Organic", "Organic", "Organic", "Organic", "Instagram Ads", "Facebook Ads", "Google Ads", "Youtube Ads", "Instagram Post", "Instagram Post", "Facebook Post"];
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
let videoTemplate = {
|
|
38
|
-
videoFormatInfo: {
|
|
39
|
-
availableFormats: chance.pickset(formats, int(1, formats.length)),
|
|
40
|
-
availableAspectRatios: chance.pickset(ratios, int(1, ratios.length)),
|
|
41
|
-
availableContainerFormats: chance.pickset(containers, int(1, containers.length)),
|
|
42
|
-
observedLatencyTimestamps: chance.pickset(u.range(1, 300000), int(1, 40))
|
|
43
|
-
},
|
|
44
|
-
videoStats: {
|
|
45
|
-
numberOfPlays: int(10, 10000000),
|
|
46
|
-
isRecommendedVideo: chance.bool(),
|
|
47
|
-
inPlaylists: chance.pickset(hashtags, int(1, hashtags.length)),
|
|
48
|
-
likers: chance.n(chance.guid, int(3, 100)),
|
|
49
|
-
dislikers: chance.n(chance.guid, int(3, 100)),
|
|
50
|
-
},
|
|
51
|
-
videoContentInfo: {
|
|
52
|
-
categories: {
|
|
53
|
-
hashtags: chance.pickset(hashtags, int(1, 10)),
|
|
54
|
-
category: chance.pickone(categories),
|
|
55
|
-
},
|
|
56
|
-
availability: {
|
|
57
|
-
hasAdvertisements: chance.bool(),
|
|
58
|
-
canBeSeenOnPlans: chance.pickset(plans, int(1, plans.length)),
|
|
59
|
-
releaseInfo: {
|
|
60
|
-
isReleased: chance.bool({ likelihood: 90 }),
|
|
61
|
-
releaseDate: chance.date({ year: 2021 })
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
},
|
|
66
|
-
uploaderInfo: {
|
|
67
|
-
platform: chance.pickone(platforms),
|
|
68
|
-
uuid: chance.guid(),
|
|
69
|
-
plan: chance.pickone(plans)
|
|
70
|
-
},
|
|
71
|
-
viewerInfo: {
|
|
72
|
-
platform: chance.pickone(platforms),
|
|
73
|
-
uuid: chance.guid(),
|
|
74
|
-
plan: chance.pickone(plans)
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
return videoTemplate;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function generateUserMeta() {
|
|
82
|
-
|
|
83
|
-
let userTemplate = {
|
|
84
|
-
favoriteNumber: chance.prime(),
|
|
85
|
-
attributionChain: chance.pickset(marketingChannels, int(1, 10)),
|
|
86
|
-
importantUserDates: {
|
|
87
|
-
firstSeenDate: chance.date({ year: 2010 }),
|
|
88
|
-
firstPurchaseDate: chance.date({ year: 2011 }),
|
|
89
|
-
firstSubscribeDate: chance.date({ year: 2011 }),
|
|
90
|
-
lastPurchaseDate: chance.date({ year: 2012 })
|
|
91
|
-
|
|
92
|
-
},
|
|
93
|
-
plan: chance.pickone(plans),
|
|
94
|
-
followers: chance.n(chance.guid, int(1, 100)),
|
|
95
|
-
follows: chance.n(chance.guid, int(1, 100))
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
return userTemplate;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function int(min, max) {
|
|
103
|
-
return chance.integer({ min, max });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
module.exports = config;
|