@mathrunet/masamune_firebase_workflow_marketing 3.8.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/CHANGELOG.md +361 -0
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/assets/fonts/NotoSans-Bold.ttf +1439 -0
- package/assets/fonts/NotoSans-Regular.ttf +1439 -0
- package/assets/fonts/NotoSans.ttf +0 -0
- package/assets/fonts/NotoSansJP-Bold.ttf +0 -0
- package/assets/fonts/NotoSansJP-Regular.ttf +0 -0
- package/assets/fonts/NotoSansKR-Bold.ttf +1439 -0
- package/assets/fonts/NotoSansKR-Regular.ttf +1439 -0
- package/assets/fonts/NotoSansKR.ttf +0 -0
- package/assets/fonts/NotoSansSC-Bold.ttf +1439 -0
- package/assets/fonts/NotoSansSC-Regular.ttf +1439 -0
- package/assets/fonts/NotoSansSC.ttf +0 -0
- package/dist/clients/app_store_client.d.ts +54 -0
- package/dist/clients/app_store_client.js +280 -0
- package/dist/clients/app_store_client.js.map +1 -0
- package/dist/clients/firebase_analytics_client.d.ts +67 -0
- package/dist/clients/firebase_analytics_client.js +411 -0
- package/dist/clients/firebase_analytics_client.js.map +1 -0
- package/dist/clients/github_content_client.d.ts +83 -0
- package/dist/clients/github_content_client.js +443 -0
- package/dist/clients/github_content_client.js.map +1 -0
- package/dist/clients/google_play_client.d.ts +49 -0
- package/dist/clients/google_play_client.js +275 -0
- package/dist/clients/google_play_client.js.map +1 -0
- package/dist/functions/analyze_github_init.d.ts +37 -0
- package/dist/functions/analyze_github_init.js +299 -0
- package/dist/functions/analyze_github_init.js.map +1 -0
- package/dist/functions/analyze_github_process.d.ts +25 -0
- package/dist/functions/analyze_github_process.js +268 -0
- package/dist/functions/analyze_github_process.js.map +1 -0
- package/dist/functions/analyze_github_summary.d.ts +32 -0
- package/dist/functions/analyze_github_summary.js +270 -0
- package/dist/functions/analyze_github_summary.js.map +1 -0
- package/dist/functions/analyze_market_research.d.ts +40 -0
- package/dist/functions/analyze_market_research.js +346 -0
- package/dist/functions/analyze_market_research.js.map +1 -0
- package/dist/functions/analyze_marketing_data.d.ts +96 -0
- package/dist/functions/analyze_marketing_data.js +990 -0
- package/dist/functions/analyze_marketing_data.js.map +1 -0
- package/dist/functions/collect_from_app_store.d.ts +25 -0
- package/dist/functions/collect_from_app_store.js +148 -0
- package/dist/functions/collect_from_app_store.js.map +1 -0
- package/dist/functions/collect_from_firebase_analytics.d.ts +25 -0
- package/dist/functions/collect_from_firebase_analytics.js +142 -0
- package/dist/functions/collect_from_firebase_analytics.js.map +1 -0
- package/dist/functions/collect_from_google_play_console.d.ts +25 -0
- package/dist/functions/collect_from_google_play_console.js +142 -0
- package/dist/functions/collect_from_google_play_console.js.map +1 -0
- package/dist/functions/generate_marketing_markdown.d.ts +89 -0
- package/dist/functions/generate_marketing_markdown.js +653 -0
- package/dist/functions/generate_marketing_markdown.js.map +1 -0
- package/dist/functions/generate_marketing_pdf.d.ts +25 -0
- package/dist/functions/generate_marketing_pdf.js +175 -0
- package/dist/functions/generate_marketing_pdf.js.map +1 -0
- package/dist/functions/research_market.d.ts +56 -0
- package/dist/functions/research_market.js +434 -0
- package/dist/functions/research_market.js.map +1 -0
- package/dist/functions.d.ts +571 -0
- package/dist/functions.js +648 -0
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/locales/de_DE.d.ts +5 -0
- package/dist/locales/de_DE.js +111 -0
- package/dist/locales/de_DE.js.map +1 -0
- package/dist/locales/en.d.ts +5 -0
- package/dist/locales/en.js +111 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es_ES.d.ts +5 -0
- package/dist/locales/es_ES.js +111 -0
- package/dist/locales/es_ES.js.map +1 -0
- package/dist/locales/fr_FR.d.ts +5 -0
- package/dist/locales/fr_FR.js +111 -0
- package/dist/locales/fr_FR.js.map +1 -0
- package/dist/locales/id_ID.d.ts +5 -0
- package/dist/locales/id_ID.js +111 -0
- package/dist/locales/id_ID.js.map +1 -0
- package/dist/locales/index.d.ts +59 -0
- package/dist/locales/index.js +155 -0
- package/dist/locales/index.js.map +1 -0
- package/dist/locales/ja.d.ts +5 -0
- package/dist/locales/ja.js +111 -0
- package/dist/locales/ja.js.map +1 -0
- package/dist/locales/ko_KR.d.ts +5 -0
- package/dist/locales/ko_KR.js +111 -0
- package/dist/locales/ko_KR.js.map +1 -0
- package/dist/locales/pt_PT.d.ts +5 -0
- package/dist/locales/pt_PT.js +111 -0
- package/dist/locales/pt_PT.js.map +1 -0
- package/dist/locales/ru_RU.d.ts +5 -0
- package/dist/locales/ru_RU.js +111 -0
- package/dist/locales/ru_RU.js.map +1 -0
- package/dist/locales/types.d.ts +105 -0
- package/dist/locales/types.js +8 -0
- package/dist/locales/types.js.map +1 -0
- package/dist/locales/zh_CN.d.ts +5 -0
- package/dist/locales/zh_CN.js +111 -0
- package/dist/locales/zh_CN.js.map +1 -0
- package/dist/models/app_store_data.d.ts +87 -0
- package/dist/models/app_store_data.js +8 -0
- package/dist/models/app_store_data.js.map +1 -0
- package/dist/models/chart_data.d.ts +104 -0
- package/dist/models/chart_data.js +8 -0
- package/dist/models/chart_data.js.map +1 -0
- package/dist/models/common.d.ts +42 -0
- package/dist/models/common.js +8 -0
- package/dist/models/common.js.map +1 -0
- package/dist/models/firebase_analytics_data.d.ts +134 -0
- package/dist/models/firebase_analytics_data.js +8 -0
- package/dist/models/firebase_analytics_data.js.map +1 -0
- package/dist/models/github_analysis_data.d.ts +266 -0
- package/dist/models/github_analysis_data.js +101 -0
- package/dist/models/github_analysis_data.js.map +1 -0
- package/dist/models/google_play_data.d.ts +115 -0
- package/dist/models/google_play_data.js +8 -0
- package/dist/models/google_play_data.js.map +1 -0
- package/dist/models/index.d.ts +14 -0
- package/dist/models/index.js +36 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/market_research_data.d.ts +259 -0
- package/dist/models/market_research_data.js +8 -0
- package/dist/models/market_research_data.js.map +1 -0
- package/dist/models/marketing_analysis_data.d.ts +200 -0
- package/dist/models/marketing_analysis_data.js +8 -0
- package/dist/models/marketing_analysis_data.js.map +1 -0
- package/dist/models/pdf_data.d.ts +112 -0
- package/dist/models/pdf_data.js +8 -0
- package/dist/models/pdf_data.js.map +1 -0
- package/dist/services/chart_service.d.ts +54 -0
- package/dist/services/chart_service.js +436 -0
- package/dist/services/chart_service.js.map +1 -0
- package/dist/services/github_analysis_service.d.ts +73 -0
- package/dist/services/github_analysis_service.js +488 -0
- package/dist/services/github_analysis_service.js.map +1 -0
- package/dist/services/pdf_service.d.ts +112 -0
- package/dist/services/pdf_service.js +1397 -0
- package/dist/services/pdf_service.js.map +1 -0
- package/dist/utils/github_analysis_storage.d.ts +88 -0
- package/dist/utils/github_analysis_storage.js +154 -0
- package/dist/utils/github_analysis_storage.js.map +1 -0
- package/package.json +64 -0
- package/scripts/cleanup_test_data.ts +304 -0
- package/scripts/list_test_data.ts +85 -0
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GenerateMarketingMarkdown = void 0;
|
|
4
|
+
const masamune_firebase_workflow_1 = require("@mathrunet/masamune_firebase_workflow");
|
|
5
|
+
require("@mathrunet/masamune");
|
|
6
|
+
const locales_1 = require("../locales");
|
|
7
|
+
/**
|
|
8
|
+
* A function for generating marketing analytics Markdown report.
|
|
9
|
+
*
|
|
10
|
+
* マーケティング分析Markdownレポートを生成するためのFunction。
|
|
11
|
+
*/
|
|
12
|
+
class GenerateMarketingMarkdown extends masamune_firebase_workflow_1.WorkflowProcessFunctionBase {
|
|
13
|
+
/**
|
|
14
|
+
* The ID of the function.
|
|
15
|
+
*
|
|
16
|
+
* 関数のID。
|
|
17
|
+
*/
|
|
18
|
+
id = "generate_marketing_markdown";
|
|
19
|
+
/**
|
|
20
|
+
* Translations for the current locale.
|
|
21
|
+
*/
|
|
22
|
+
translations = (0, locales_1.getTranslations)("en");
|
|
23
|
+
/**
|
|
24
|
+
* The process of the function.
|
|
25
|
+
*
|
|
26
|
+
* @param context
|
|
27
|
+
* The context of the function.
|
|
28
|
+
*
|
|
29
|
+
* @returns
|
|
30
|
+
* The action of the function.
|
|
31
|
+
*/
|
|
32
|
+
async process(context) {
|
|
33
|
+
const action = context.action;
|
|
34
|
+
const task = context.task;
|
|
35
|
+
// 1. task.results から各データを取得
|
|
36
|
+
const googlePlayConsole = task.results?.googlePlayConsole;
|
|
37
|
+
const appStore = task.results?.appStore;
|
|
38
|
+
const firebaseAnalytics = task.results?.firebaseAnalytics;
|
|
39
|
+
const marketingAnalytics = task.results?.marketingAnalytics;
|
|
40
|
+
const githubRepository = task.results?.githubRepository;
|
|
41
|
+
const githubImprovements = task.results?.githubImprovements;
|
|
42
|
+
// 2. いずれのデータも無ければ空データを返却
|
|
43
|
+
if (!googlePlayConsole && !appStore && !firebaseAnalytics && !marketingAnalytics) {
|
|
44
|
+
console.log("GenerateMarketingMarkdown: No marketing data found in task.results");
|
|
45
|
+
return {
|
|
46
|
+
...action,
|
|
47
|
+
results: {
|
|
48
|
+
marketingAnalyticsMarkdown: "",
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
// 3. Markdownレポートを生成
|
|
54
|
+
console.log("GenerateMarketingMarkdown: Generating Markdown report...");
|
|
55
|
+
const inputData = {
|
|
56
|
+
googlePlayConsole,
|
|
57
|
+
appStore,
|
|
58
|
+
firebaseAnalytics,
|
|
59
|
+
marketingAnalytics,
|
|
60
|
+
githubRepository,
|
|
61
|
+
githubImprovements,
|
|
62
|
+
};
|
|
63
|
+
// Determine app name from available data
|
|
64
|
+
const appName = appStore?.appName ||
|
|
65
|
+
googlePlayConsole?.packageName?.split(".")?.pop() ||
|
|
66
|
+
"Marketing Report";
|
|
67
|
+
// Determine date range from action command or use defaults
|
|
68
|
+
const command = action.command;
|
|
69
|
+
const commandData = command?.data;
|
|
70
|
+
const dateRange = commandData?.startDate && commandData?.endDate
|
|
71
|
+
? { startDate: commandData.startDate, endDate: commandData.endDate }
|
|
72
|
+
: undefined;
|
|
73
|
+
const markdownContent = this.generateMarkdownReport({
|
|
74
|
+
data: inputData,
|
|
75
|
+
appName,
|
|
76
|
+
reportType: commandData?.reportType || "weekly",
|
|
77
|
+
dateRange,
|
|
78
|
+
locale: action.locale,
|
|
79
|
+
});
|
|
80
|
+
console.log("GenerateMarketingMarkdown: Markdown report generated, length:", markdownContent.length, "characters");
|
|
81
|
+
// 4. results.marketingAnalyticsMarkdown に文字列として格納
|
|
82
|
+
return {
|
|
83
|
+
...action,
|
|
84
|
+
results: {
|
|
85
|
+
marketingAnalyticsMarkdown: markdownContent,
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
console.error("GenerateMarketingMarkdown: Failed to generate Markdown report", error);
|
|
91
|
+
return {
|
|
92
|
+
...action,
|
|
93
|
+
results: {
|
|
94
|
+
marketingAnalyticsMarkdown: "",
|
|
95
|
+
markdownError: error.message,
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Generate the complete Markdown report.
|
|
102
|
+
*/
|
|
103
|
+
generateMarkdownReport(options) {
|
|
104
|
+
// Initialize translations based on locale
|
|
105
|
+
const locale = typeof options.locale === "object"
|
|
106
|
+
? options.locale["@language"]
|
|
107
|
+
: options.locale;
|
|
108
|
+
this.translations = (0, locales_1.getTranslations)(locale);
|
|
109
|
+
const sections = [];
|
|
110
|
+
// Header section
|
|
111
|
+
sections.push(this.generateHeader(options));
|
|
112
|
+
// Executive Summary
|
|
113
|
+
if (options.data.marketingAnalytics?.overallAnalysis) {
|
|
114
|
+
sections.push(this.generateExecutiveSummary(options));
|
|
115
|
+
}
|
|
116
|
+
// Highlights & Concerns
|
|
117
|
+
const analysis = options.data.marketingAnalytics?.overallAnalysis;
|
|
118
|
+
if ((analysis?.highlights?.length || 0) > 0 || (analysis?.concerns?.length || 0) > 0) {
|
|
119
|
+
sections.push(this.generateHighlightsConcerns(options));
|
|
120
|
+
}
|
|
121
|
+
// User Analytics
|
|
122
|
+
if (options.data.firebaseAnalytics) {
|
|
123
|
+
sections.push(this.generateUserAnalytics(options));
|
|
124
|
+
}
|
|
125
|
+
// Ratings & Reviews
|
|
126
|
+
if (options.data.googlePlayConsole || options.data.appStore || options.data.marketingAnalytics?.reviewAnalysis) {
|
|
127
|
+
sections.push(this.generateRatingsReviews(options));
|
|
128
|
+
}
|
|
129
|
+
// Competitive Positioning (market research data)
|
|
130
|
+
if (options.data.marketingAnalytics?.competitivePositioning) {
|
|
131
|
+
sections.push(this.generateCompetitivePositioning(options));
|
|
132
|
+
}
|
|
133
|
+
// Market Opportunity Priority (market research data)
|
|
134
|
+
if (options.data.marketingAnalytics?.marketOpportunityPriority) {
|
|
135
|
+
sections.push(this.generateMarketOpportunityPriority(options));
|
|
136
|
+
}
|
|
137
|
+
// Trend Analysis
|
|
138
|
+
if (options.data.marketingAnalytics?.trendAnalysis) {
|
|
139
|
+
sections.push(this.generateTrendAnalysis(options));
|
|
140
|
+
}
|
|
141
|
+
// Improvement Suggestions
|
|
142
|
+
if (options.data.marketingAnalytics?.improvementSuggestions?.length) {
|
|
143
|
+
sections.push(this.generateImprovements(options));
|
|
144
|
+
}
|
|
145
|
+
// GitHub Code Improvements
|
|
146
|
+
if (options.data.githubImprovements?.improvements?.length) {
|
|
147
|
+
sections.push(this.generateGitHubImprovements(options));
|
|
148
|
+
}
|
|
149
|
+
// Footer
|
|
150
|
+
sections.push(this.generateFooter());
|
|
151
|
+
return sections.join("\n\n---\n\n");
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Generate header section.
|
|
155
|
+
*/
|
|
156
|
+
generateHeader(options) {
|
|
157
|
+
const t = this.translations;
|
|
158
|
+
const appName = options.appName || "Marketing Report";
|
|
159
|
+
const reportTypeLabel = options.reportType === "daily"
|
|
160
|
+
? t.dailyReport
|
|
161
|
+
: options.reportType === "weekly"
|
|
162
|
+
? t.weeklyReport
|
|
163
|
+
: t.monthlyReport;
|
|
164
|
+
const lines = [
|
|
165
|
+
`# ${appName}`,
|
|
166
|
+
"",
|
|
167
|
+
`**${t.reportType}:** ${reportTypeLabel}`,
|
|
168
|
+
];
|
|
169
|
+
if (options.dateRange) {
|
|
170
|
+
lines.push(`**${t.period}:** ${options.dateRange.startDate} - ${options.dateRange.endDate}`);
|
|
171
|
+
}
|
|
172
|
+
lines.push(`**${t.generated}:** ${new Date().toISOString().split("T")[0]}`);
|
|
173
|
+
// Data sources
|
|
174
|
+
const sources = [];
|
|
175
|
+
if (options.data.googlePlayConsole)
|
|
176
|
+
sources.push("Google Play");
|
|
177
|
+
if (options.data.appStore)
|
|
178
|
+
sources.push("App Store");
|
|
179
|
+
if (options.data.firebaseAnalytics)
|
|
180
|
+
sources.push("Firebase Analytics");
|
|
181
|
+
if (options.data.marketingAnalytics)
|
|
182
|
+
sources.push(t.aiAnalysis);
|
|
183
|
+
if (sources.length > 0) {
|
|
184
|
+
lines.push(`**${t.dataSources}:** ${sources.join(" | ")}`);
|
|
185
|
+
}
|
|
186
|
+
return lines.join("\n");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Generate executive summary section.
|
|
190
|
+
*/
|
|
191
|
+
generateExecutiveSummary(options) {
|
|
192
|
+
const t = this.translations;
|
|
193
|
+
const analysis = options.data.marketingAnalytics?.overallAnalysis;
|
|
194
|
+
const lines = [`## ${t.executiveSummary}`];
|
|
195
|
+
// Summary text
|
|
196
|
+
if (analysis?.summary) {
|
|
197
|
+
lines.push("", analysis.summary);
|
|
198
|
+
}
|
|
199
|
+
// Key Metrics table
|
|
200
|
+
if (analysis?.keyMetrics?.length) {
|
|
201
|
+
lines.push("", `### ${t.metric}`, "");
|
|
202
|
+
lines.push(`| ${t.metric} | ${t.value} | ${t.trend} |`);
|
|
203
|
+
lines.push("|--------|-------|-------|");
|
|
204
|
+
for (const metric of analysis.keyMetrics) {
|
|
205
|
+
const trendIcon = metric.trend === "up" ? "↑" : metric.trend === "down" ? "↓" : "→";
|
|
206
|
+
lines.push(`| ${metric.metric} | ${metric.value} | ${trendIcon} |`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return lines.join("\n");
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Generate highlights and concerns section.
|
|
213
|
+
*/
|
|
214
|
+
generateHighlightsConcerns(options) {
|
|
215
|
+
const t = this.translations;
|
|
216
|
+
const analysis = options.data.marketingAnalytics?.overallAnalysis;
|
|
217
|
+
const lines = [`## ${t.highlightsAndConcerns}`];
|
|
218
|
+
// Highlights
|
|
219
|
+
if (analysis?.highlights?.length) {
|
|
220
|
+
lines.push("", `### ${t.highlights}`);
|
|
221
|
+
for (const highlight of analysis.highlights) {
|
|
222
|
+
lines.push(`- ${highlight}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Concerns
|
|
226
|
+
if (analysis?.concerns?.length) {
|
|
227
|
+
lines.push("", `### ${t.concerns}`);
|
|
228
|
+
for (const concern of analysis.concerns) {
|
|
229
|
+
lines.push(`- ${concern}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return lines.join("\n");
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Generate user analytics section.
|
|
236
|
+
*/
|
|
237
|
+
generateUserAnalytics(options) {
|
|
238
|
+
const t = this.translations;
|
|
239
|
+
const firebase = options.data.firebaseAnalytics;
|
|
240
|
+
const lines = [`## ${t.userAnalytics}`];
|
|
241
|
+
if (!firebase) {
|
|
242
|
+
return lines.join("\n");
|
|
243
|
+
}
|
|
244
|
+
// Main metrics table
|
|
245
|
+
lines.push("", `### ${t.activeUsers}`, "");
|
|
246
|
+
lines.push(`| ${t.metric} | ${t.value} |`);
|
|
247
|
+
lines.push("|--------|-------|");
|
|
248
|
+
if (firebase.dau !== undefined) {
|
|
249
|
+
lines.push(`| DAU | ${firebase.dau.toLocaleString()} |`);
|
|
250
|
+
}
|
|
251
|
+
if (firebase.wau !== undefined) {
|
|
252
|
+
lines.push(`| WAU | ${firebase.wau.toLocaleString()} |`);
|
|
253
|
+
}
|
|
254
|
+
if (firebase.mau !== undefined) {
|
|
255
|
+
lines.push(`| MAU | ${firebase.mau.toLocaleString()} |`);
|
|
256
|
+
}
|
|
257
|
+
if (firebase.newUsers !== undefined) {
|
|
258
|
+
lines.push(`| ${t.newUsers} | ${firebase.newUsers.toLocaleString()} |`);
|
|
259
|
+
}
|
|
260
|
+
// Retention ratio
|
|
261
|
+
if (firebase.dau && firebase.mau && firebase.mau > 0) {
|
|
262
|
+
const retentionRatio = ((firebase.dau / firebase.mau) * 100).toFixed(1);
|
|
263
|
+
lines.push(`| ${t.retention} (DAU/MAU) | ${retentionRatio}% |`);
|
|
264
|
+
}
|
|
265
|
+
// Session stats
|
|
266
|
+
if (firebase.averageSessionDuration !== undefined || firebase.sessionsPerUser !== undefined) {
|
|
267
|
+
lines.push("", `### ${t.sessionStatistics}`, "");
|
|
268
|
+
lines.push(`| ${t.metric} | ${t.value} |`);
|
|
269
|
+
lines.push("|--------|-------|");
|
|
270
|
+
if (firebase.averageSessionDuration !== undefined) {
|
|
271
|
+
const minutes = Math.floor(firebase.averageSessionDuration / 60);
|
|
272
|
+
const seconds = Math.floor(firebase.averageSessionDuration % 60);
|
|
273
|
+
lines.push(`| ${t.avgSessionDuration} | ${minutes}m ${seconds}s |`);
|
|
274
|
+
}
|
|
275
|
+
if (firebase.sessionsPerUser !== undefined) {
|
|
276
|
+
lines.push(`| ${t.sessionsPerUser} | ${firebase.sessionsPerUser.toFixed(1)} |`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Demographics
|
|
280
|
+
if (firebase.demographics?.ageGroups && Object.keys(firebase.demographics.ageGroups).length > 0) {
|
|
281
|
+
lines.push("", `### ${t.ageDemographics}`, "");
|
|
282
|
+
lines.push(`| Age Group | ${t.percentage} |`);
|
|
283
|
+
lines.push("|-----------|------------|");
|
|
284
|
+
const sortedAgeGroups = Object.entries(firebase.demographics.ageGroups)
|
|
285
|
+
.sort((a, b) => b[1] - a[1]);
|
|
286
|
+
for (const [ageGroup, percentage] of sortedAgeGroups) {
|
|
287
|
+
const bar = this.generateProgressBar(percentage);
|
|
288
|
+
lines.push(`| ${ageGroup} | ${bar} ${percentage}% |`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (firebase.demographics?.countryDistribution && Object.keys(firebase.demographics.countryDistribution).length > 0) {
|
|
292
|
+
lines.push("", `### ${t.countryDistribution}`, "");
|
|
293
|
+
lines.push(`| Country | ${t.percentage} |`);
|
|
294
|
+
lines.push("|---------|------------|");
|
|
295
|
+
const sortedCountries = Object.entries(firebase.demographics.countryDistribution)
|
|
296
|
+
.sort((a, b) => b[1] - a[1])
|
|
297
|
+
.slice(0, 10); // Top 10 countries
|
|
298
|
+
for (const [country, percentage] of sortedCountries) {
|
|
299
|
+
const bar = this.generateProgressBar(percentage);
|
|
300
|
+
lines.push(`| ${country} | ${bar} ${percentage}% |`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return lines.join("\n");
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Generate ratings and reviews section.
|
|
307
|
+
*/
|
|
308
|
+
generateRatingsReviews(options) {
|
|
309
|
+
const t = this.translations;
|
|
310
|
+
const googlePlay = options.data.googlePlayConsole;
|
|
311
|
+
const appStore = options.data.appStore;
|
|
312
|
+
const reviewAnalysis = options.data.marketingAnalytics?.reviewAnalysis;
|
|
313
|
+
const lines = [`## ${t.ratingsAndReviews}`];
|
|
314
|
+
// Rating summary
|
|
315
|
+
if (googlePlay?.averageRating || appStore?.averageRating) {
|
|
316
|
+
lines.push("", `### ${t.overallRatings}`, "");
|
|
317
|
+
lines.push(`| ${t.platform} | ${t.rating} | ${t.totalRatings} |`);
|
|
318
|
+
lines.push("|----------|--------|---------------|");
|
|
319
|
+
if (googlePlay?.averageRating) {
|
|
320
|
+
const stars = this.generateStars(googlePlay.averageRating);
|
|
321
|
+
lines.push(`| Google Play | ${stars} ${googlePlay.averageRating.toFixed(1)} | ${(googlePlay.totalRatings || 0).toLocaleString()} |`);
|
|
322
|
+
}
|
|
323
|
+
if (appStore?.averageRating) {
|
|
324
|
+
const stars = this.generateStars(appStore.averageRating);
|
|
325
|
+
lines.push(`| App Store | ${stars} ${appStore.averageRating.toFixed(1)} | ${(appStore.totalRatings || 0).toLocaleString()} |`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// Rating distribution
|
|
329
|
+
if (googlePlay?.ratingDistribution || appStore?.ratingDistribution) {
|
|
330
|
+
lines.push("", `### ${t.ratingDistribution}`, "");
|
|
331
|
+
const hasGooglePlay = googlePlay?.ratingDistribution;
|
|
332
|
+
const hasAppStore = appStore?.ratingDistribution;
|
|
333
|
+
if (hasGooglePlay && hasAppStore) {
|
|
334
|
+
lines.push(`| ${t.rating} | Google Play | App Store |`);
|
|
335
|
+
lines.push("|--------|-------------|-----------|");
|
|
336
|
+
}
|
|
337
|
+
else if (hasGooglePlay) {
|
|
338
|
+
lines.push(`| ${t.rating} | Google Play |`);
|
|
339
|
+
lines.push("|--------|-------------|");
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
lines.push(`| ${t.rating} | App Store |`);
|
|
343
|
+
lines.push("|--------|-----------|");
|
|
344
|
+
}
|
|
345
|
+
for (let i = 5; i >= 1; i--) {
|
|
346
|
+
const starLabel = "★".repeat(i) + "☆".repeat(5 - i);
|
|
347
|
+
const gpValue = googlePlay?.ratingDistribution?.[i.toString()] || googlePlay?.ratingDistribution?.[`star${i}`] || 0;
|
|
348
|
+
const asValue = appStore?.ratingDistribution?.[i.toString()] || appStore?.ratingDistribution?.[`star${i}`] || 0;
|
|
349
|
+
if (hasGooglePlay && hasAppStore) {
|
|
350
|
+
const gpBar = this.generateProgressBar(gpValue);
|
|
351
|
+
const asBar = this.generateProgressBar(asValue);
|
|
352
|
+
lines.push(`| ${starLabel} | ${gpBar} ${gpValue}% | ${asBar} ${asValue}% |`);
|
|
353
|
+
}
|
|
354
|
+
else if (hasGooglePlay) {
|
|
355
|
+
const gpBar = this.generateProgressBar(gpValue);
|
|
356
|
+
lines.push(`| ${starLabel} | ${gpBar} ${gpValue}% |`);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
const asBar = this.generateProgressBar(asValue);
|
|
360
|
+
lines.push(`| ${starLabel} | ${asBar} ${asValue}% |`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Review Analysis
|
|
365
|
+
if (reviewAnalysis) {
|
|
366
|
+
// Sentiment
|
|
367
|
+
if (reviewAnalysis.sentiment) {
|
|
368
|
+
lines.push("", `### ${t.sentimentAnalysis}`, "");
|
|
369
|
+
lines.push(`| Sentiment | ${t.percentage} |`);
|
|
370
|
+
lines.push("|-----------|------------|");
|
|
371
|
+
const posBar = this.generateProgressBar(reviewAnalysis.sentiment.positive);
|
|
372
|
+
const neuBar = this.generateProgressBar(reviewAnalysis.sentiment.neutral);
|
|
373
|
+
const negBar = this.generateProgressBar(reviewAnalysis.sentiment.negative);
|
|
374
|
+
lines.push(`| ${t.positive} | ${posBar} ${reviewAnalysis.sentiment.positive}% |`);
|
|
375
|
+
lines.push(`| ${t.neutral} | ${neuBar} ${reviewAnalysis.sentiment.neutral}% |`);
|
|
376
|
+
lines.push(`| ${t.negative} | ${negBar} ${reviewAnalysis.sentiment.negative}% |`);
|
|
377
|
+
}
|
|
378
|
+
// Common themes
|
|
379
|
+
if (reviewAnalysis.commonThemes?.length) {
|
|
380
|
+
lines.push("", `### ${t.commonThemes}`);
|
|
381
|
+
for (const theme of reviewAnalysis.commonThemes) {
|
|
382
|
+
lines.push(`- ${theme}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Actionable insights
|
|
386
|
+
if (reviewAnalysis.actionableInsights?.length) {
|
|
387
|
+
lines.push("", `### ${t.actionableInsights}`);
|
|
388
|
+
for (const insight of reviewAnalysis.actionableInsights) {
|
|
389
|
+
lines.push(`- ${insight}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return lines.join("\n");
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Generate improvement suggestions section.
|
|
397
|
+
*/
|
|
398
|
+
generateImprovements(options) {
|
|
399
|
+
const t = this.translations;
|
|
400
|
+
const suggestions = options.data.marketingAnalytics?.improvementSuggestions || [];
|
|
401
|
+
const lines = [`## ${t.improvementSuggestions}`];
|
|
402
|
+
for (const suggestion of suggestions) {
|
|
403
|
+
const priorityEmoji = suggestion.priority === "high" ? "🔴" : suggestion.priority === "medium" ? "🟡" : "🟢";
|
|
404
|
+
lines.push("");
|
|
405
|
+
lines.push(`### ${priorityEmoji} ${suggestion.priority.toUpperCase()}: [${suggestion.category}] ${suggestion.title}`);
|
|
406
|
+
lines.push("");
|
|
407
|
+
lines.push(suggestion.description);
|
|
408
|
+
if (suggestion.expectedImpact) {
|
|
409
|
+
lines.push("");
|
|
410
|
+
lines.push(`**${t.expectedImpact}:** ${suggestion.expectedImpact}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return lines.join("\n");
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Generate competitive positioning section.
|
|
417
|
+
*
|
|
418
|
+
* 競合ポジショニング分析セクションを生成。
|
|
419
|
+
*/
|
|
420
|
+
generateCompetitivePositioning(options) {
|
|
421
|
+
const t = this.translations;
|
|
422
|
+
const positioning = options.data.marketingAnalytics?.competitivePositioning;
|
|
423
|
+
const lines = [`## ${t.competitivePositioning}`];
|
|
424
|
+
// Market Position
|
|
425
|
+
if (positioning?.marketPosition) {
|
|
426
|
+
lines.push("", `### ${t.marketPosition}`);
|
|
427
|
+
lines.push("", positioning.marketPosition);
|
|
428
|
+
}
|
|
429
|
+
// Competitor Comparison
|
|
430
|
+
if (positioning?.competitorComparison?.length) {
|
|
431
|
+
lines.push("", `### ${t.competitorComparison}`);
|
|
432
|
+
for (const comp of positioning.competitorComparison) {
|
|
433
|
+
lines.push("");
|
|
434
|
+
lines.push(`#### vs ${comp.competitor}`);
|
|
435
|
+
lines.push("");
|
|
436
|
+
if (comp.ourStrengths?.length) {
|
|
437
|
+
lines.push(`**${t.ourStrengths}:**`);
|
|
438
|
+
for (const strength of comp.ourStrengths) {
|
|
439
|
+
lines.push(`- ✅ ${strength}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (comp.ourWeaknesses?.length) {
|
|
443
|
+
lines.push("");
|
|
444
|
+
lines.push(`**${t.ourWeaknesses}:**`);
|
|
445
|
+
for (const weakness of comp.ourWeaknesses) {
|
|
446
|
+
lines.push(`- ⚠️ ${weakness}`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (comp.battleStrategy) {
|
|
450
|
+
lines.push("");
|
|
451
|
+
lines.push(`**${t.battleStrategy}:** ${comp.battleStrategy}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Differentiation Strategy
|
|
456
|
+
if (positioning?.differentiationStrategy) {
|
|
457
|
+
lines.push("", `### ${t.differentiationStrategy}`);
|
|
458
|
+
lines.push("", positioning.differentiationStrategy);
|
|
459
|
+
}
|
|
460
|
+
// Quick Wins
|
|
461
|
+
if (positioning?.quickWins?.length) {
|
|
462
|
+
lines.push("", `### ${t.quickWins}`);
|
|
463
|
+
for (const quickWin of positioning.quickWins) {
|
|
464
|
+
lines.push(`- 🚀 ${quickWin}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return lines.join("\n");
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Generate market opportunity priority section.
|
|
471
|
+
*
|
|
472
|
+
* 市場機会優先度分析セクションを生成。
|
|
473
|
+
*/
|
|
474
|
+
generateMarketOpportunityPriority(options) {
|
|
475
|
+
const t = this.translations;
|
|
476
|
+
const priority = options.data.marketingAnalytics?.marketOpportunityPriority;
|
|
477
|
+
const lines = [`## ${t.marketOpportunityPriority}`];
|
|
478
|
+
// Prioritized Opportunities
|
|
479
|
+
if (priority?.prioritizedOpportunities?.length) {
|
|
480
|
+
lines.push("", `### ${t.prioritizedOpportunities}`, "");
|
|
481
|
+
// Summary table
|
|
482
|
+
lines.push(`| ${t.opportunity} | ${t.fitScore} | ${t.effort} |`);
|
|
483
|
+
lines.push("|------|--------|------|");
|
|
484
|
+
const fitScoreEmoji = {
|
|
485
|
+
excellent: "🟢",
|
|
486
|
+
good: "🔵",
|
|
487
|
+
moderate: "🟡",
|
|
488
|
+
poor: "🔴",
|
|
489
|
+
};
|
|
490
|
+
const effortLabel = {
|
|
491
|
+
low: t.low,
|
|
492
|
+
medium: t.medium,
|
|
493
|
+
high: t.high,
|
|
494
|
+
};
|
|
495
|
+
for (const opp of priority.prioritizedOpportunities) {
|
|
496
|
+
const emoji = fitScoreEmoji[opp.fitScore] || "⚪";
|
|
497
|
+
const effort = effortLabel[opp.estimatedEffort] || opp.estimatedEffort;
|
|
498
|
+
lines.push(`| ${opp.opportunity} | ${emoji} ${opp.fitScore} | ${effort} |`);
|
|
499
|
+
}
|
|
500
|
+
// Detailed view for each opportunity
|
|
501
|
+
lines.push("");
|
|
502
|
+
for (const opp of priority.prioritizedOpportunities) {
|
|
503
|
+
const emoji = fitScoreEmoji[opp.fitScore] || "⚪";
|
|
504
|
+
lines.push(`#### ${emoji} ${opp.opportunity}`);
|
|
505
|
+
lines.push("");
|
|
506
|
+
lines.push(`**${t.fitScore}:** ${opp.fitScore}`);
|
|
507
|
+
if (opp.fitReason) {
|
|
508
|
+
lines.push(`**${t.reason}:** ${opp.fitReason}`);
|
|
509
|
+
}
|
|
510
|
+
if (opp.requiredChanges?.length) {
|
|
511
|
+
lines.push("");
|
|
512
|
+
lines.push(`**${t.requiredChanges}:**`);
|
|
513
|
+
for (const change of opp.requiredChanges) {
|
|
514
|
+
lines.push(`- ${change}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (opp.recommendedAction) {
|
|
518
|
+
lines.push("");
|
|
519
|
+
lines.push(`**${t.recommendedAction}:** ${opp.recommendedAction}`);
|
|
520
|
+
}
|
|
521
|
+
lines.push("");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Strategic Recommendation
|
|
525
|
+
if (priority?.strategicRecommendation) {
|
|
526
|
+
lines.push(`### ${t.strategicRecommendations}`);
|
|
527
|
+
lines.push("", priority.strategicRecommendation);
|
|
528
|
+
}
|
|
529
|
+
return lines.join("\n");
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Generate GitHub code improvements section.
|
|
533
|
+
*/
|
|
534
|
+
generateGitHubImprovements(options) {
|
|
535
|
+
const t = this.translations;
|
|
536
|
+
const githubImprovements = options.data.githubImprovements;
|
|
537
|
+
const improvements = githubImprovements?.improvements || [];
|
|
538
|
+
const lines = [`## ${t.codebaseImprovements}`];
|
|
539
|
+
// Repository info
|
|
540
|
+
const repo = githubImprovements?.repository || "";
|
|
541
|
+
const framework = githubImprovements?.framework || "";
|
|
542
|
+
if (repo || framework) {
|
|
543
|
+
lines.push("");
|
|
544
|
+
lines.push(`**${t.repository}:** ${repo} | **${t.framework}:** ${framework}`);
|
|
545
|
+
}
|
|
546
|
+
// Summary
|
|
547
|
+
if (githubImprovements?.improvementSummary) {
|
|
548
|
+
lines.push("");
|
|
549
|
+
lines.push(githubImprovements.improvementSummary);
|
|
550
|
+
}
|
|
551
|
+
// Each improvement
|
|
552
|
+
for (const improvement of improvements) {
|
|
553
|
+
const priorityEmoji = improvement.priority === "high" ? "🔴" : improvement.priority === "medium" ? "🟡" : "🟢";
|
|
554
|
+
lines.push("");
|
|
555
|
+
lines.push(`### ${priorityEmoji} ${(improvement.priority || "medium").toUpperCase()} [${improvement.category || ""}]: ${improvement.title || ""}`);
|
|
556
|
+
lines.push("");
|
|
557
|
+
if (improvement.description) {
|
|
558
|
+
lines.push(improvement.description);
|
|
559
|
+
}
|
|
560
|
+
if (improvement.relatedFeature) {
|
|
561
|
+
lines.push("");
|
|
562
|
+
lines.push(`**${t.relatedFeature}:** ${improvement.relatedFeature}`);
|
|
563
|
+
}
|
|
564
|
+
// Code References
|
|
565
|
+
const codeRefs = improvement.codeReferences || [];
|
|
566
|
+
if (codeRefs.length > 0) {
|
|
567
|
+
lines.push("");
|
|
568
|
+
lines.push(`**${t.fileModifications}:**`);
|
|
569
|
+
lines.push("");
|
|
570
|
+
lines.push(`| ${t.type} | ${t.file} | ${t.current} | ${t.proposed} |`);
|
|
571
|
+
lines.push("|------|------|---------|----------|");
|
|
572
|
+
const modIcon = {
|
|
573
|
+
add: "+",
|
|
574
|
+
modify: "~",
|
|
575
|
+
refactor: "R",
|
|
576
|
+
optimize: "O",
|
|
577
|
+
};
|
|
578
|
+
for (const ref of codeRefs) {
|
|
579
|
+
const icon = modIcon[ref.modificationType || ""] || "?";
|
|
580
|
+
const filePath = ref.filePath || "";
|
|
581
|
+
const current = (ref.currentFunctionality || "").replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
582
|
+
const proposed = (ref.proposedChange || "").replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
583
|
+
lines.push(`| ${icon} | \`${filePath}\` | ${current} | ${proposed} |`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (improvement.expectedImpact) {
|
|
587
|
+
lines.push("");
|
|
588
|
+
lines.push(`**${t.expectedImpact}:** ${improvement.expectedImpact}`);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return lines.join("\n");
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Generate trend analysis section.
|
|
595
|
+
*/
|
|
596
|
+
generateTrendAnalysis(options) {
|
|
597
|
+
const t = this.translations;
|
|
598
|
+
const trendAnalysis = options.data.marketingAnalytics?.trendAnalysis;
|
|
599
|
+
const lines = [`## ${t.trendAnalysisAndPredictions}`];
|
|
600
|
+
if (trendAnalysis?.userGrowthTrend) {
|
|
601
|
+
lines.push("", `### ${t.userGrowth}`);
|
|
602
|
+
lines.push(trendAnalysis.userGrowthTrend);
|
|
603
|
+
}
|
|
604
|
+
if (trendAnalysis?.engagementTrend) {
|
|
605
|
+
lines.push("", `### ${t.engagement}`);
|
|
606
|
+
lines.push(trendAnalysis.engagementTrend);
|
|
607
|
+
}
|
|
608
|
+
if (trendAnalysis?.ratingTrend) {
|
|
609
|
+
lines.push("", `### ${t.ratings}`);
|
|
610
|
+
lines.push(trendAnalysis.ratingTrend);
|
|
611
|
+
}
|
|
612
|
+
if (trendAnalysis?.predictions?.length) {
|
|
613
|
+
lines.push("", `### ${t.predictions}`);
|
|
614
|
+
for (const prediction of trendAnalysis.predictions) {
|
|
615
|
+
lines.push(`- ${prediction}`);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return lines.join("\n");
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Generate footer section.
|
|
622
|
+
*/
|
|
623
|
+
generateFooter() {
|
|
624
|
+
const t = this.translations;
|
|
625
|
+
return `*${t.generatedBy} - ${new Date().toISOString().split("T")[0]}*`;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Generate a progress bar using block characters.
|
|
629
|
+
*/
|
|
630
|
+
generateProgressBar(percentage, maxBlocks = 10) {
|
|
631
|
+
// Clamp percentage to 0-100 range
|
|
632
|
+
const clampedPercentage = Math.max(0, Math.min(100, percentage));
|
|
633
|
+
const filledBlocks = Math.round((clampedPercentage / 100) * maxBlocks);
|
|
634
|
+
const emptyBlocks = maxBlocks - filledBlocks;
|
|
635
|
+
return "█".repeat(filledBlocks) + "░".repeat(emptyBlocks);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Generate star rating display.
|
|
639
|
+
*/
|
|
640
|
+
generateStars(rating) {
|
|
641
|
+
// Clamp rating to 0-5 range
|
|
642
|
+
const clampedRating = Math.max(0, Math.min(5, rating));
|
|
643
|
+
const fullStars = Math.floor(clampedRating);
|
|
644
|
+
const hasHalfStar = clampedRating - fullStars >= 0.5;
|
|
645
|
+
const emptyStars = Math.max(0, 5 - fullStars - (hasHalfStar ? 1 : 0));
|
|
646
|
+
return "★".repeat(fullStars) + (hasHalfStar ? "☆" : "") + "☆".repeat(emptyStars);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
exports.GenerateMarketingMarkdown = GenerateMarketingMarkdown;
|
|
650
|
+
module.exports = (regions, options, data) => new GenerateMarketingMarkdown(options).build(regions);
|
|
651
|
+
// Export class for testing
|
|
652
|
+
module.exports.GenerateMarketingMarkdown = GenerateMarketingMarkdown;
|
|
653
|
+
//# sourceMappingURL=generate_marketing_markdown.js.map
|