make-mp-data 2.0.22 → 2.1.0
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/dungeons/ai-chat-analytics-ed.js +274 -0
- package/dungeons/business.js +0 -1
- package/dungeons/complex.js +0 -1
- package/dungeons/experiments.js +0 -1
- package/dungeons/gaming.js +47 -14
- package/dungeons/media.js +5 -6
- package/dungeons/mil.js +296 -0
- package/dungeons/money2020-ed-also.js +277 -0
- package/dungeons/money2020-ed.js +579 -0
- package/dungeons/sanity.js +0 -1
- package/dungeons/scd.js +0 -1
- package/dungeons/simple.js +57 -18
- package/dungeons/student-teacher.js +0 -1
- package/dungeons/text-generation.js +706 -0
- package/dungeons/userAgent.js +1 -2
- package/entry.js +4 -0
- package/index.js +63 -38
- package/lib/cli/cli.js +7 -8
- package/lib/core/config-validator.js +11 -13
- package/lib/core/context.js +13 -1
- package/lib/core/storage.js +45 -13
- package/lib/generators/adspend.js +1 -1
- package/lib/generators/events.js +18 -17
- package/lib/generators/funnels.js +293 -240
- package/lib/generators/text-bak-old.js +1121 -0
- package/lib/generators/text.js +1173 -0
- package/lib/orchestrators/mixpanel-sender.js +1 -1
- package/lib/templates/abbreviated.d.ts +13 -3
- package/lib/templates/defaults.js +311 -169
- package/lib/templates/hooks-instructions.txt +434 -0
- package/lib/templates/phrases-bak.js +925 -0
- package/lib/templates/phrases.js +2066 -0
- package/lib/templates/{instructions.txt → schema-instructions.txt} +78 -1
- package/lib/templates/scratch-dungeon-template.js +1 -1
- package/lib/templates/textQuickTest.js +172 -0
- package/lib/utils/ai.js +51 -2
- package/lib/utils/utils.js +145 -7
- package/package.json +8 -5
- package/types.d.ts +322 -7
- package/lib/utils/chart.js +0 -206
package/types.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface Dungeon {
|
|
|
20
20
|
epochEnd?: number;
|
|
21
21
|
numEvents?: number;
|
|
22
22
|
numUsers?: number;
|
|
23
|
-
format?: "csv" | "json" | string;
|
|
23
|
+
format?: "csv" | "json" | "parquet" | string;
|
|
24
24
|
region?: "US" | "EU";
|
|
25
25
|
concurrency?: number;
|
|
26
26
|
batchSize?: number;
|
|
@@ -30,7 +30,6 @@ export interface Dungeon {
|
|
|
30
30
|
projectId?: string;
|
|
31
31
|
|
|
32
32
|
// ids
|
|
33
|
-
simulationName?: string;
|
|
34
33
|
name?: string;
|
|
35
34
|
|
|
36
35
|
//switches
|
|
@@ -44,11 +43,11 @@ export interface Dungeon {
|
|
|
44
43
|
hasDesktopDevices?: boolean;
|
|
45
44
|
hasBrowser?: boolean;
|
|
46
45
|
writeToDisk?: boolean | string;
|
|
46
|
+
gzip?: boolean;
|
|
47
47
|
verbose?: boolean;
|
|
48
48
|
hasAnonIds?: boolean;
|
|
49
49
|
hasSessionIds?: boolean;
|
|
50
50
|
alsoInferFunnels?: boolean;
|
|
51
|
-
makeChart?: boolean | string;
|
|
52
51
|
singleCountry?: string;
|
|
53
52
|
|
|
54
53
|
//models
|
|
@@ -117,7 +116,7 @@ export interface hookArrayOptions<T> {
|
|
|
117
116
|
hook?: Hook<T>;
|
|
118
117
|
type?: hookTypes;
|
|
119
118
|
filename?: string;
|
|
120
|
-
format?: "csv" | "json" | string;
|
|
119
|
+
format?: "csv" | "json" | "parquet" | string;
|
|
121
120
|
concurrency?: number;
|
|
122
121
|
context?: Context;
|
|
123
122
|
[key: string]: any;
|
|
@@ -179,6 +178,8 @@ export interface Defaults {
|
|
|
179
178
|
desktopDevices: () => any[];
|
|
180
179
|
browsers: () => any[];
|
|
181
180
|
campaigns: () => any[];
|
|
181
|
+
devicePools: { android: any[]; ios: any[]; desktop: any[] };
|
|
182
|
+
allDevices:any[];
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
/**
|
|
@@ -311,11 +312,16 @@ export interface Funnel {
|
|
|
311
312
|
* funnel properties go onto each event in the funnel and are held constant
|
|
312
313
|
*/
|
|
313
314
|
props?: Record<string, ValueValid>;
|
|
315
|
+
/**
|
|
316
|
+
* funnel conditions (user properties) are used to filter users who are eligible for the funnel
|
|
317
|
+
* these conditions must match the current user's profile for the user to be eligible for the funnel
|
|
318
|
+
*/
|
|
319
|
+
conditions?: Record<string, ValueValid>;
|
|
314
320
|
/**
|
|
315
|
-
* funnel
|
|
316
|
-
*
|
|
321
|
+
* If true, the funnel will be part of an experiment where we generate 3 variants of the funnel with different conversion rates
|
|
322
|
+
*
|
|
317
323
|
*/
|
|
318
|
-
|
|
324
|
+
experiment?: boolean;
|
|
319
325
|
}
|
|
320
326
|
|
|
321
327
|
/**
|
|
@@ -430,3 +436,312 @@ export type Result = {
|
|
|
430
436
|
declare function main(config: Dungeon): Promise<Result>;
|
|
431
437
|
|
|
432
438
|
export default main;
|
|
439
|
+
|
|
440
|
+
// ============= Text Generator Types =============
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Sentiment tone of generated text
|
|
444
|
+
*/
|
|
445
|
+
export type TextTone = "pos" | "neg" | "neu";
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Style of text generation
|
|
449
|
+
*
|
|
450
|
+
* Supported styles:
|
|
451
|
+
* - "support": Customer support tickets and requests
|
|
452
|
+
* - "review": Product reviews and ratings
|
|
453
|
+
* - "search": Search queries and keywords
|
|
454
|
+
* - "feedback": User feedback and suggestions
|
|
455
|
+
* - "chat": Casual chat messages and conversations
|
|
456
|
+
* - "email": Formal email communications
|
|
457
|
+
* - "forum": Forum posts and discussions
|
|
458
|
+
* - "comments": Social media comments and reactions
|
|
459
|
+
* - "tweet": Twitter-style social media posts
|
|
460
|
+
*/
|
|
461
|
+
export type TextStyle = "support" | "review" | "search" | "feedback" | "chat" | "email" | "forum" | "comments" | "tweet";
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Emotional intensity level
|
|
465
|
+
*/
|
|
466
|
+
export type TextIntensity = "low" | "medium" | "high";
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Language formality level
|
|
470
|
+
*/
|
|
471
|
+
export type TextFormality = "casual" | "business" | "technical";
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Output format for batch generation
|
|
475
|
+
*/
|
|
476
|
+
export type TextReturnType = "strings" | "objects";
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Domain-specific keywords to inject into generated text
|
|
480
|
+
*
|
|
481
|
+
* Common predefined categories include:
|
|
482
|
+
* - features: Product features to mention
|
|
483
|
+
* - products: Product/company names
|
|
484
|
+
* - competitors: Competitor names for comparisons
|
|
485
|
+
* - technical: Technical terms and jargon
|
|
486
|
+
* - versions: Version numbers and releases
|
|
487
|
+
* - errors: Specific error messages or codes
|
|
488
|
+
* - metrics: Business metrics or KPIs
|
|
489
|
+
* - events: Event types (e.g., 'wedding', 'celebration', 'conference')
|
|
490
|
+
* - emotions: Emotional descriptors (e.g., 'inspiring', 'heartwarming')
|
|
491
|
+
* - issues: Common problems or issues
|
|
492
|
+
* - team: Team or role references
|
|
493
|
+
* - business_impact: Business impact phrases
|
|
494
|
+
* - comparisons: Comparison phrases
|
|
495
|
+
* - credibility: Credibility markers
|
|
496
|
+
* - user_actions: User action descriptions
|
|
497
|
+
* - specific_praise: Specific positive details
|
|
498
|
+
* - specific_issues: Specific negative details
|
|
499
|
+
* - error_messages: Error message text
|
|
500
|
+
* - categories: General categories
|
|
501
|
+
* - brands: Brand names
|
|
502
|
+
* - vendors: Vendor references
|
|
503
|
+
* - services: Service types
|
|
504
|
+
* - locations: Location references
|
|
505
|
+
*
|
|
506
|
+
* Custom categories can be added as needed.
|
|
507
|
+
*/
|
|
508
|
+
export interface TextKeywordSet {
|
|
509
|
+
/** Product features to mention */
|
|
510
|
+
features?: string[];
|
|
511
|
+
/** Product/company names */
|
|
512
|
+
products?: string[];
|
|
513
|
+
/** Competitor names for comparisons */
|
|
514
|
+
competitors?: string[];
|
|
515
|
+
/** Technical terms and jargon */
|
|
516
|
+
technical?: string[];
|
|
517
|
+
/** Version numbers and releases */
|
|
518
|
+
versions?: string[];
|
|
519
|
+
/** Specific error messages or codes */
|
|
520
|
+
errors?: string[];
|
|
521
|
+
/** Business metrics or KPIs */
|
|
522
|
+
metrics?: string[];
|
|
523
|
+
/** Event types (e.g., 'wedding', 'celebration', 'conference') */
|
|
524
|
+
events?: string[];
|
|
525
|
+
/** Emotional descriptors (e.g., 'inspiring', 'heartwarming') */
|
|
526
|
+
emotions?: string[];
|
|
527
|
+
/** Common problems or issues */
|
|
528
|
+
issues?: string[];
|
|
529
|
+
/** Team or role references */
|
|
530
|
+
team?: string[];
|
|
531
|
+
/** Business impact phrases */
|
|
532
|
+
business_impact?: string[];
|
|
533
|
+
/** Comparison phrases */
|
|
534
|
+
comparisons?: string[];
|
|
535
|
+
/** Credibility markers */
|
|
536
|
+
credibility?: string[];
|
|
537
|
+
/** User action descriptions */
|
|
538
|
+
user_actions?: string[];
|
|
539
|
+
/** Specific positive details */
|
|
540
|
+
specific_praise?: string[];
|
|
541
|
+
/** Specific negative details */
|
|
542
|
+
specific_issues?: string[];
|
|
543
|
+
/** Error message text */
|
|
544
|
+
error_messages?: string[];
|
|
545
|
+
/** General categories */
|
|
546
|
+
categories?: string[];
|
|
547
|
+
/** Brand names */
|
|
548
|
+
brands?: string[];
|
|
549
|
+
/** Vendor references */
|
|
550
|
+
vendors?: string[];
|
|
551
|
+
/** Service types */
|
|
552
|
+
services?: string[];
|
|
553
|
+
/** Location references */
|
|
554
|
+
locations?: string[];
|
|
555
|
+
/** Allow any custom keyword category */
|
|
556
|
+
[key: string]: string[] | undefined;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Configuration for text generator instance
|
|
561
|
+
*/
|
|
562
|
+
export interface TextGeneratorConfig {
|
|
563
|
+
/** Default sentiment tone */
|
|
564
|
+
tone?: TextTone;
|
|
565
|
+
/** Type of text to generate */
|
|
566
|
+
style?: TextStyle;
|
|
567
|
+
/** Emotional intensity */
|
|
568
|
+
intensity?: TextIntensity;
|
|
569
|
+
/** Language formality */
|
|
570
|
+
formality?: TextFormality;
|
|
571
|
+
/** Minimum text length in characters */
|
|
572
|
+
min?: number;
|
|
573
|
+
/** Maximum text length in characters */
|
|
574
|
+
max?: number;
|
|
575
|
+
/** RNG seed for reproducibility */
|
|
576
|
+
seed?: string;
|
|
577
|
+
/** Domain-specific keywords to inject */
|
|
578
|
+
keywords?: TextKeywordSet;
|
|
579
|
+
/** Probability of keyword injection (0-1) */
|
|
580
|
+
keywordDensity?: number;
|
|
581
|
+
/** Enable realistic typos */
|
|
582
|
+
typos?: boolean;
|
|
583
|
+
/** Base typo probability per word */
|
|
584
|
+
typoRate?: number;
|
|
585
|
+
/** Allow sentiment mixing for realism */
|
|
586
|
+
mixedSentiment?: boolean;
|
|
587
|
+
/** Amount of authentic markers (0-1) */
|
|
588
|
+
authenticityLevel?: number;
|
|
589
|
+
/** Add timestamps to some messages */
|
|
590
|
+
timestamps?: boolean;
|
|
591
|
+
/** Include user role/experience markers */
|
|
592
|
+
userPersona?: boolean;
|
|
593
|
+
/** Allow sentiment to drift during generation (0-1) */
|
|
594
|
+
sentimentDrift?: number;
|
|
595
|
+
/** Add metadata to generated text */
|
|
596
|
+
includeMetadata?: boolean;
|
|
597
|
+
/** How specific/detailed to make claims (0-1) */
|
|
598
|
+
specificityLevel?: number;
|
|
599
|
+
/** Filter near-duplicates */
|
|
600
|
+
enableDeduplication?: boolean;
|
|
601
|
+
/** Max generation attempts per item */
|
|
602
|
+
maxAttempts?: number;
|
|
603
|
+
// performanceMode removed - system is always optimized for speed + uniqueness
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Metadata for generated text
|
|
608
|
+
*/
|
|
609
|
+
export interface TextMetadata {
|
|
610
|
+
/** Timestamp if enabled */
|
|
611
|
+
timestamp?: string;
|
|
612
|
+
/** Sentiment analysis score */
|
|
613
|
+
sentimentScore?: number;
|
|
614
|
+
/** Keywords that were injected */
|
|
615
|
+
injectedKeywords?: string[];
|
|
616
|
+
/** User persona information */
|
|
617
|
+
persona?: Record<string, any>;
|
|
618
|
+
/** Flesch reading ease score */
|
|
619
|
+
readabilityScore?: number;
|
|
620
|
+
/** Text style used */
|
|
621
|
+
style?: TextStyle | string;
|
|
622
|
+
/** Intensity level used */
|
|
623
|
+
intensity?: TextIntensity | string;
|
|
624
|
+
/** Formality level used */
|
|
625
|
+
formality?: TextFormality | string;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Simple generated text object (without metadata)
|
|
630
|
+
*/
|
|
631
|
+
export interface SimpleGeneratedText {
|
|
632
|
+
/** The generated text */
|
|
633
|
+
text: string;
|
|
634
|
+
/** Actual tone of generated text */
|
|
635
|
+
tone: TextTone | string;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Generated text with metadata
|
|
640
|
+
*/
|
|
641
|
+
export interface GeneratedText {
|
|
642
|
+
/** The generated text */
|
|
643
|
+
text: string;
|
|
644
|
+
/** Actual tone of generated text */
|
|
645
|
+
tone: TextTone | string;
|
|
646
|
+
/** Additional metadata */
|
|
647
|
+
metadata?: TextMetadata;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Options for batch text generation
|
|
652
|
+
*/
|
|
653
|
+
export interface TextBatchOptions {
|
|
654
|
+
/** Number of items to generate */
|
|
655
|
+
n: number;
|
|
656
|
+
/** Output format */
|
|
657
|
+
returnType?: TextReturnType;
|
|
658
|
+
/** Override tone for this batch */
|
|
659
|
+
tone?: TextTone;
|
|
660
|
+
/** Generate related/coherent items */
|
|
661
|
+
related?: boolean;
|
|
662
|
+
/** Shared context/topic for related items */
|
|
663
|
+
sharedContext?: string;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Statistics for text generator performance
|
|
668
|
+
*/
|
|
669
|
+
export interface TextGeneratorStats {
|
|
670
|
+
/** Configuration used */
|
|
671
|
+
config: TextGeneratorConfig;
|
|
672
|
+
/** Total items generated */
|
|
673
|
+
generatedCount: number;
|
|
674
|
+
/** Items that were duplicates */
|
|
675
|
+
duplicateCount: number;
|
|
676
|
+
/** Items that failed generation */
|
|
677
|
+
failedCount: number;
|
|
678
|
+
/** Average generation time per item */
|
|
679
|
+
avgGenerationTime: number;
|
|
680
|
+
/** Total generation time */
|
|
681
|
+
totalGenerationTime: number;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Text generator instance interface
|
|
686
|
+
*/
|
|
687
|
+
export interface TextGenerator {
|
|
688
|
+
/** Generate a single text item */
|
|
689
|
+
generateOne(): string | GeneratedText | null;
|
|
690
|
+
/** Generate multiple text items in batch */
|
|
691
|
+
generateBatch(options: TextBatchOptions): (string | GeneratedText | SimpleGeneratedText)[];
|
|
692
|
+
/** Get generation statistics */
|
|
693
|
+
getStats(): TextGeneratorStats;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Creates a new text generator instance
|
|
698
|
+
* @param config - Configuration options for the generator
|
|
699
|
+
* @returns Text generator instance
|
|
700
|
+
*/
|
|
701
|
+
export declare function createGenerator(config?: TextGeneratorConfig): TextGenerator;
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Generate a batch of text items directly (standalone function)
|
|
705
|
+
* @param options - Combined generator config and batch options
|
|
706
|
+
* @returns Array of generated text items
|
|
707
|
+
*/
|
|
708
|
+
export declare function generateBatch(options: TextGeneratorConfig & TextBatchOptions): (string | GeneratedText | SimpleGeneratedText)[];
|
|
709
|
+
|
|
710
|
+
// ============= Additional Utility Types =============
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* File path configuration for data generation output
|
|
714
|
+
*/
|
|
715
|
+
export interface WritePaths {
|
|
716
|
+
eventFiles: string[];
|
|
717
|
+
userFiles: string[];
|
|
718
|
+
adSpendFiles: string[];
|
|
719
|
+
scdFiles: string[];
|
|
720
|
+
mirrorFiles: string[];
|
|
721
|
+
groupFiles: string[];
|
|
722
|
+
lookupFiles: string[];
|
|
723
|
+
folder: string;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Configuration for TimeSoup time distribution function
|
|
728
|
+
*/
|
|
729
|
+
export interface TimeSoupOptions {
|
|
730
|
+
earliestTime?: number;
|
|
731
|
+
latestTime?: number;
|
|
732
|
+
peaks?: number;
|
|
733
|
+
deviation?: number;
|
|
734
|
+
mean?: number;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Test context configuration for unit/integration tests
|
|
739
|
+
*/
|
|
740
|
+
export interface TestContext {
|
|
741
|
+
config: Dungeon;
|
|
742
|
+
storage: Storage | null;
|
|
743
|
+
defaults: Defaults;
|
|
744
|
+
campaigns: any[];
|
|
745
|
+
runtime: RuntimeState;
|
|
746
|
+
[key: string]: any;
|
|
747
|
+
}
|
package/lib/utils/chart.js
DELETED
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/** @typedef {import('../../types.js').EventSchema} EventSchema */
|
|
2
|
-
/** @typedef {import('../../types.js').Result} Result */
|
|
3
|
-
/** @typedef {import('../../types.js').Context} Context */
|
|
4
|
-
|
|
5
|
-
import { ChartJSNodeCanvas } from 'chartjs-node-canvas';
|
|
6
|
-
import fs from 'fs';
|
|
7
|
-
import * as u from 'ak-tools';
|
|
8
|
-
import dayjs from 'dayjs';
|
|
9
|
-
import { openFinder } from './utils.js';
|
|
10
|
-
const { existsSync } = fs;
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import 'dotenv/config';
|
|
13
|
-
const { NODE_ENV = "unknown" } = process.env;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
let tempDir;
|
|
17
|
-
const dataFolder = path.resolve("./data");
|
|
18
|
-
if (existsSync(dataFolder)) tempDir = dataFolder;
|
|
19
|
-
else tempDir = path.resolve("./");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Function to count events per day
|
|
23
|
-
function countDailyEvents(eventData) {
|
|
24
|
-
const dailyCounts = {};
|
|
25
|
-
|
|
26
|
-
eventData.forEach(event => {
|
|
27
|
-
const date = dayjs(event.time).format('YYYY-MM-DD');
|
|
28
|
-
if (!dailyCounts[date]) {
|
|
29
|
-
dailyCounts[date] = 0;
|
|
30
|
-
}
|
|
31
|
-
dailyCounts[date]++;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return dailyCounts;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Function to count daily users
|
|
38
|
-
function countDailyUsers(eventData) {
|
|
39
|
-
const dailyUsers = {};
|
|
40
|
-
|
|
41
|
-
eventData.forEach(event => {
|
|
42
|
-
const date = dayjs(event.time).format('YYYY-MM-DD');
|
|
43
|
-
if (!dailyUsers[date]) {
|
|
44
|
-
dailyUsers[date] = new Set();
|
|
45
|
-
}
|
|
46
|
-
dailyUsers[date].add(event.user_id);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return dailyUsers;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Function to count daily new users based on signup events
|
|
53
|
-
function countDailyNewUsers(eventData, signupEvents) {
|
|
54
|
-
const dailyNewUsers = {};
|
|
55
|
-
const seenUsers = new Set();
|
|
56
|
-
|
|
57
|
-
eventData.forEach(event => {
|
|
58
|
-
const date = dayjs(event.time).format('YYYY-MM-DD');
|
|
59
|
-
if (!dailyNewUsers[date]) {
|
|
60
|
-
dailyNewUsers[date] = new Set();
|
|
61
|
-
}
|
|
62
|
-
if (signupEvents.includes(event.event) && !seenUsers.has(event.user_id)) {
|
|
63
|
-
dailyNewUsers[date].add(event.user_id);
|
|
64
|
-
seenUsers.add(event.user_id);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
return dailyNewUsers;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Function to generate line chart
|
|
72
|
-
async function generateLineChart(rawData, signupEvents = ["sign up"], fileName) {
|
|
73
|
-
const width = 1600;
|
|
74
|
-
const height = 1200;
|
|
75
|
-
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height, backgroundColour: 'black' });
|
|
76
|
-
const eventData = countDailyEvents(rawData);
|
|
77
|
-
const userData = countDailyUsers(rawData);
|
|
78
|
-
const newUserData = countDailyNewUsers(rawData, signupEvents);
|
|
79
|
-
|
|
80
|
-
// @ts-ignore
|
|
81
|
-
const sortedEventEntries = Object.entries(eventData).sort((a, b) => new Date(a[0]) - new Date(b[0]));
|
|
82
|
-
const eventLabels = sortedEventEntries.map(entry => entry[0]);
|
|
83
|
-
const eventValues = sortedEventEntries.map(entry => entry[1]);
|
|
84
|
-
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
const sortedUserEntries = Object.entries(userData).sort((a, b) => new Date(a[0]) - new Date(b[0]));
|
|
87
|
-
const userLabels = sortedUserEntries.map(entry => entry[0]);
|
|
88
|
-
const userValues = sortedUserEntries.map(entry => entry[1].size);
|
|
89
|
-
|
|
90
|
-
// @ts-ignore
|
|
91
|
-
const sortedNewUserEntries = Object.entries(newUserData).sort((a, b) => new Date(a[0]) - new Date(b[0]));
|
|
92
|
-
const newUserLabels = sortedNewUserEntries.map(entry => entry[0]);
|
|
93
|
-
const newUserValues = sortedNewUserEntries.map(entry => entry[1].size);
|
|
94
|
-
|
|
95
|
-
const configuration = {
|
|
96
|
-
type: 'line',
|
|
97
|
-
data: {
|
|
98
|
-
labels: eventLabels,
|
|
99
|
-
datasets: [
|
|
100
|
-
{
|
|
101
|
-
label: '# EVENTS',
|
|
102
|
-
data: eventValues,
|
|
103
|
-
yAxisID: 'y1',
|
|
104
|
-
fill: true,
|
|
105
|
-
borderColor: '#4F44E0',
|
|
106
|
-
tension: 0.1
|
|
107
|
-
},
|
|
108
|
-
// {
|
|
109
|
-
// label: '# USERS',
|
|
110
|
-
// data: userValues,
|
|
111
|
-
// yAxisID: 'y2',
|
|
112
|
-
// fill: true,
|
|
113
|
-
// borderColor: '#E34F2F',
|
|
114
|
-
// tension: 0.1
|
|
115
|
-
// },
|
|
116
|
-
{
|
|
117
|
-
label: '# NEW',
|
|
118
|
-
data: newUserValues,
|
|
119
|
-
yAxisID: 'y3',
|
|
120
|
-
fill: true,
|
|
121
|
-
borderColor: '#219464',
|
|
122
|
-
tension: 0.1
|
|
123
|
-
}
|
|
124
|
-
]
|
|
125
|
-
},
|
|
126
|
-
options: {
|
|
127
|
-
scales: {
|
|
128
|
-
x: {
|
|
129
|
-
title: {
|
|
130
|
-
display: true,
|
|
131
|
-
text: 'Date',
|
|
132
|
-
color: 'white'
|
|
133
|
-
},
|
|
134
|
-
ticks: {
|
|
135
|
-
color: 'white'
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
y1: {
|
|
139
|
-
type: 'linear',
|
|
140
|
-
display: true,
|
|
141
|
-
position: 'left',
|
|
142
|
-
title: {
|
|
143
|
-
display: true,
|
|
144
|
-
text: 'Count of Events',
|
|
145
|
-
color: '#4F44E0'
|
|
146
|
-
},
|
|
147
|
-
ticks: {
|
|
148
|
-
color: '#4F44E0'
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
y3: {
|
|
152
|
-
type: 'linear',
|
|
153
|
-
display: true,
|
|
154
|
-
position: 'right',
|
|
155
|
-
offset: true,
|
|
156
|
-
title: {
|
|
157
|
-
display: true,
|
|
158
|
-
text: 'Count of New Users',
|
|
159
|
-
color: '#219464'
|
|
160
|
-
},
|
|
161
|
-
ticks: {
|
|
162
|
-
color: '#219464'
|
|
163
|
-
},
|
|
164
|
-
grid: {
|
|
165
|
-
drawOnChartArea: false
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
plugins: {
|
|
170
|
-
legend: {
|
|
171
|
-
labels: {
|
|
172
|
-
color: 'white'
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// @ts-ignore
|
|
180
|
-
if (typeof fileName === undefined) fileName = 'chart';
|
|
181
|
-
if (typeof fileName !== 'string') fileName = 'chart';
|
|
182
|
-
// @ts-ignore
|
|
183
|
-
const imageBuffer = await chartJSNodeCanvas.renderToBuffer(configuration);
|
|
184
|
-
const filePath = path.join(tempDir, `${fileName}.png`);
|
|
185
|
-
const removed = await u.rm(filePath);
|
|
186
|
-
// @ts-ignore - imageBuffer is a Buffer but touch accepts it
|
|
187
|
-
const file = await u.touch(filePath, imageBuffer);
|
|
188
|
-
|
|
189
|
-
console.log(`Chart saved as ${fileName}.png`);
|
|
190
|
-
openFinder(path)
|
|
191
|
-
return file;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export { generateLineChart };
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
199
|
-
generateLineChart()
|
|
200
|
-
.then((result)=>{
|
|
201
|
-
if (NODE_ENV === "dev") debugger;
|
|
202
|
-
})
|
|
203
|
-
.catch((error)=>{
|
|
204
|
-
if (NODE_ENV === "dev") debugger;
|
|
205
|
-
})
|
|
206
|
-
}
|