import { getPropertyId, formatPropertyPath, executeReport } from "../../client.js";
import { resolveDate } from "../../utils/date.js";
import { roundToDecimal } from "../../utils/format.js";
import type {
GetDailyTrendInput,
GetDailyTrendOutput,
DailyData,
} from "../../types.js";
/**
* 日別のトレンドを取得
*/
export async function getDailyTrend(
input: GetDailyTrendInput
): Promise<GetDailyTrendOutput> {
const propertyId = getPropertyId(input.propertyId);
const property = formatPropertyPath(propertyId);
const startDate = resolveDate(input.startDate);
const endDate = resolveDate(input.endDate);
// デフォルトのメトリクス
const metrics = input.metrics || ["screenPageViews", "activeUsers"];
const response = await executeReport({
property,
dateRanges: [{ startDate, endDate }],
dimensions: [{ name: "date" }],
metrics: metrics.map((name) => ({ name })),
orderBys: [{ dimension: { dimensionName: "date" }, desc: false }],
limit: 366, // 最大1年分
});
const trend: DailyData[] = [];
const metricSums: Record<string, number> = {};
let maxRecord: DailyData | null = null;
let minRecord: DailyData | null = null;
let maxValue = -Infinity;
let minValue = Infinity;
// メトリクスの合計を初期化
for (const metric of metrics) {
metricSums[metric] = 0;
}
// メトリクスヘッダー
const metricHeaders = response.metricHeaders || [];
for (const row of response.rows || []) {
const dateStr = row.dimensionValues?.[0]?.value || "";
// YYYYMMDD を YYYY-MM-DD に変換
const formattedDate = `${dateStr.slice(0, 4)}-${dateStr.slice(4, 6)}-${dateStr.slice(6, 8)}`;
const dataPoint: DailyData = { date: formattedDate };
let primaryValue = 0;
(row.metricValues || []).forEach((value, index) => {
const metricName = metricHeaders[index]?.name || `metric${index}`;
const numValue = value.value ? parseFloat(value.value) : 0;
dataPoint[metricName] = numValue;
metricSums[metricName] = (metricSums[metricName] || 0) + numValue;
// 最初のメトリクスを使って最大/最小を判定
if (index === 0) {
primaryValue = numValue;
}
});
trend.push(dataPoint);
// 最大値の更新
if (primaryValue > maxValue) {
maxValue = primaryValue;
maxRecord = dataPoint;
}
// 最小値の更新
if (primaryValue < minValue) {
minValue = primaryValue;
minRecord = dataPoint;
}
}
// サマリーの計算
const count = trend.length || 1;
const total: Record<string, number> = {};
const average: Record<string, number> = {};
for (const metric of metrics) {
total[metric] = Math.round(metricSums[metric] || 0);
average[metric] = roundToDecimal((metricSums[metric] || 0) / count);
}
// 最大/最小レコードの整形
const formatSummaryRecord = (
record: DailyData | null
): { date: string } & Record<string, number> => {
if (!record) {
const result: { date: string } & Record<string, number> = { date: "" };
for (const metric of metrics) {
result[metric] = 0;
}
return result;
}
const result: { date: string } & Record<string, number> = {
date: record.date as string,
};
for (const metric of metrics) {
result[metric] = typeof record[metric] === "number" ? record[metric] as number : 0;
}
return result;
};
return {
trend,
summary: {
total,
average,
max: formatSummaryRecord(maxRecord),
min: formatSummaryRecord(minRecord),
},
};
}