mirror of
https://github.com/umami-software/umami.git
synced 2026-02-13 09:05:36 +01:00
feat(dev): add sample data generator script
Adds a CLI tool to generate realistic analytics data for local development and testing. Creates two demo websites with varying traffic patterns and realistic user behavior distributions.
This commit is contained in:
parent
a19b92a5cb
commit
b7807ed466
13 changed files with 1645 additions and 2 deletions
163
scripts/seed/distributions/referrers.ts
Normal file
163
scripts/seed/distributions/referrers.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import { weightedRandom, pickRandom, randomInt, type WeightedOption } from '../utils.js';
|
||||
|
||||
export type ReferrerType = 'direct' | 'organic' | 'social' | 'paid' | 'referral';
|
||||
|
||||
export interface ReferrerInfo {
|
||||
type: ReferrerType;
|
||||
domain: string | null;
|
||||
path: string | null;
|
||||
utmSource: string | null;
|
||||
utmMedium: string | null;
|
||||
utmCampaign: string | null;
|
||||
utmContent: string | null;
|
||||
utmTerm: string | null;
|
||||
gclid: string | null;
|
||||
fbclid: string | null;
|
||||
}
|
||||
|
||||
const referrerTypeWeights: WeightedOption<ReferrerType>[] = [
|
||||
{ value: 'direct', weight: 0.4 },
|
||||
{ value: 'organic', weight: 0.25 },
|
||||
{ value: 'social', weight: 0.15 },
|
||||
{ value: 'paid', weight: 0.1 },
|
||||
{ value: 'referral', weight: 0.1 },
|
||||
];
|
||||
|
||||
const searchEngines = [
|
||||
{ domain: 'google.com', path: '/search' },
|
||||
{ domain: 'bing.com', path: '/search' },
|
||||
{ domain: 'duckduckgo.com', path: '/' },
|
||||
{ domain: 'yahoo.com', path: '/search' },
|
||||
{ domain: 'baidu.com', path: '/s' },
|
||||
];
|
||||
|
||||
const socialPlatforms = [
|
||||
{ domain: 'twitter.com', path: null },
|
||||
{ domain: 'x.com', path: null },
|
||||
{ domain: 'linkedin.com', path: '/feed' },
|
||||
{ domain: 'facebook.com', path: null },
|
||||
{ domain: 'reddit.com', path: '/r/programming' },
|
||||
{ domain: 'news.ycombinator.com', path: '/item' },
|
||||
{ domain: 'threads.net', path: null },
|
||||
{ domain: 'bsky.app', path: null },
|
||||
];
|
||||
|
||||
const referralSites = [
|
||||
{ domain: 'medium.com', path: '/@author/article' },
|
||||
{ domain: 'dev.to', path: '/post' },
|
||||
{ domain: 'hashnode.com', path: '/blog' },
|
||||
{ domain: 'techcrunch.com', path: '/article' },
|
||||
{ domain: 'producthunt.com', path: '/posts' },
|
||||
{ domain: 'indiehackers.com', path: '/post' },
|
||||
];
|
||||
|
||||
interface PaidCampaign {
|
||||
source: string;
|
||||
medium: string;
|
||||
campaign: string;
|
||||
useGclid?: boolean;
|
||||
useFbclid?: boolean;
|
||||
}
|
||||
|
||||
const paidCampaigns: PaidCampaign[] = [
|
||||
{ source: 'google', medium: 'cpc', campaign: 'brand_search', useGclid: true },
|
||||
{ source: 'google', medium: 'cpc', campaign: 'product_awareness', useGclid: true },
|
||||
{ source: 'facebook', medium: 'paid_social', campaign: 'retargeting', useFbclid: true },
|
||||
{ source: 'facebook', medium: 'paid_social', campaign: 'lookalike', useFbclid: true },
|
||||
{ source: 'linkedin', medium: 'cpc', campaign: 'b2b_targeting' },
|
||||
{ source: 'twitter', medium: 'paid_social', campaign: 'launch_promo' },
|
||||
];
|
||||
|
||||
const organicCampaigns = [
|
||||
{ source: 'newsletter', medium: 'email', campaign: 'weekly_digest' },
|
||||
{ source: 'newsletter', medium: 'email', campaign: 'product_update' },
|
||||
{ source: 'partner', medium: 'referral', campaign: 'integration_launch' },
|
||||
];
|
||||
|
||||
function generateClickId(): string {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getRandomReferrer(): ReferrerInfo {
|
||||
const type = weightedRandom(referrerTypeWeights);
|
||||
|
||||
const result: ReferrerInfo = {
|
||||
type,
|
||||
domain: null,
|
||||
path: null,
|
||||
utmSource: null,
|
||||
utmMedium: null,
|
||||
utmCampaign: null,
|
||||
utmContent: null,
|
||||
utmTerm: null,
|
||||
gclid: null,
|
||||
fbclid: null,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'direct':
|
||||
// No referrer data
|
||||
break;
|
||||
|
||||
case 'organic': {
|
||||
const engine = pickRandom(searchEngines);
|
||||
result.domain = engine.domain;
|
||||
result.path = engine.path;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'social': {
|
||||
const platform = pickRandom(socialPlatforms);
|
||||
result.domain = platform.domain;
|
||||
result.path = platform.path;
|
||||
|
||||
// Some social traffic has UTM params
|
||||
if (Math.random() < 0.3) {
|
||||
result.utmSource = platform.domain.replace('.com', '').replace('.net', '');
|
||||
result.utmMedium = 'social';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'paid': {
|
||||
const campaign = pickRandom(paidCampaigns);
|
||||
result.utmSource = campaign.source;
|
||||
result.utmMedium = campaign.medium;
|
||||
result.utmCampaign = campaign.campaign;
|
||||
result.utmContent = `ad_${randomInt(1, 5)}`;
|
||||
|
||||
if (campaign.useGclid) {
|
||||
result.gclid = generateClickId();
|
||||
result.domain = 'google.com';
|
||||
result.path = '/search';
|
||||
} else if (campaign.useFbclid) {
|
||||
result.fbclid = generateClickId();
|
||||
result.domain = 'facebook.com';
|
||||
result.path = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'referral': {
|
||||
// Mix of pure referrals and organic campaigns
|
||||
if (Math.random() < 0.6) {
|
||||
const site = pickRandom(referralSites);
|
||||
result.domain = site.domain;
|
||||
result.path = site.path;
|
||||
} else {
|
||||
const campaign = pickRandom(organicCampaigns);
|
||||
result.utmSource = campaign.source;
|
||||
result.utmMedium = campaign.medium;
|
||||
result.utmCampaign = campaign.campaign;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue