59 lines
3.6 KiB
JavaScript
59 lines
3.6 KiB
JavaScript
// Firestore 시드 스크립트 — matches / ai_predictions / crowd_stats 초기화
|
|
// 실행: GOOGLE_APPLICATION_CREDENTIALS=./serviceAccount.json node scripts/seed.mjs
|
|
// ⚠️ AI 예측은 플레이스홀더. 발행 전 실제 GPT/Claude/Gemini 출력으로 교체.
|
|
// (경기 데이터는 lib/schedule.ts 와 동일하게 유지할 것 — 여기서는 JS로 인라인)
|
|
import { initializeApp, cert, applicationDefault } from "firebase-admin/app";
|
|
import { getFirestore, FieldValue } from "firebase-admin/firestore";
|
|
|
|
initializeApp({ credential: applicationDefault() });
|
|
const db = getFirestore();
|
|
|
|
const lock = (iso) => new Date(new Date(iso).getTime() - 10 * 60000).toISOString();
|
|
const T = {
|
|
KOR: { name: "Korea Republic", shortName: "한국", code: "KOR", flag: "🇰🇷" },
|
|
CZE: { name: "Czechia", shortName: "체코", code: "CZE", flag: "🇨🇿" },
|
|
MEX: { name: "Mexico", shortName: "멕시코", code: "MEX", flag: "🇲🇽" },
|
|
RSA: { name: "South Africa", shortName: "남아공", code: "RSA", flag: "🇿🇦" },
|
|
};
|
|
|
|
const MATCHES = [
|
|
["A_MEX_RSA_20260612", "Match 01 · 개막전", T.MEX, T.RSA, "2026-06-12T04:00:00+09:00", "Estadio Azteca · Mexico City", "월드컵 개막전, AI는 개최국을 믿을까"],
|
|
["A_KOR_CZE_20260612", "Match 01", T.KOR, T.CZE, "2026-06-12T11:00:00+09:00", "Estadio Guadalajara (Akron)", "한국 첫 경기, AI의 선택은 갈렸다"],
|
|
["A_CZE_RSA_20260619", "Match 02", T.CZE, T.RSA, "2026-06-19T01:00:00+09:00", "USA (TBD)", "체코 vs 남아공, AI의 예측은"],
|
|
["A_MEX_KOR_20260619", "Match 02", T.MEX, T.KOR, "2026-06-19T10:00:00+09:00", "Estadio Guadalajara (Akron)", "개최국 멕시코 vs 한국, AI는 누구 편"],
|
|
["A_CZE_MEX_20260625", "Match 03", T.CZE, T.MEX, "2026-06-25T10:00:00+09:00", "Estadio Azteca · Mexico City", "체코 vs 멕시코, 운명의 최종전"],
|
|
["A_RSA_KOR_20260625", "Match 03", T.RSA, T.KOR, "2026-06-25T10:00:00+09:00", "Estadio BBVA · Monterrey", "한국 16강의 갈림길, AI의 마지막 예측"],
|
|
];
|
|
|
|
// 한국 vs 체코 AI 예측 (플레이스홀더)
|
|
const AI = {
|
|
A_KOR_CZE_20260612: [
|
|
{ model: "GPT", outcome: "TEAM_A_WIN", scoreA: 2, scoreB: 1, confidencePct: 62, reasonShort: "한국의 측면 공격 우위와 세트피스 강점", generatedAt: "2026-06-08" },
|
|
{ model: "Claude", outcome: "DRAW", scoreA: 1, scoreB: 1, confidencePct: 41, reasonShort: "중원 밸런스가 팽팽한 접전 예상", generatedAt: "2026-06-08" },
|
|
{ model: "Gemini", outcome: "TEAM_B_WIN", scoreA: 0, scoreB: 2, confidencePct: 28, reasonShort: "체코의 전환 속도와 결정력 우세", generatedAt: "2026-06-08" },
|
|
],
|
|
};
|
|
|
|
async function run() {
|
|
const batch = db.batch();
|
|
for (const [matchId, round, a, b, kickoff, venue, hook] of MATCHES) {
|
|
batch.set(db.collection("matches").doc(matchId), {
|
|
matchId, roundLabel: round, group: "A", teamA: a, teamB: b,
|
|
kickoffKst: kickoff, venue, lockAt: lock(kickoff), status: "upcoming",
|
|
hookText: hook, result: null, updatedAt: FieldValue.serverTimestamp(),
|
|
});
|
|
// crowd_stats 초기화 (0)
|
|
batch.set(db.collection("crowd_stats").doc(matchId), {
|
|
matchId, total: 0, teamAWin: 0, draw: 0, teamBWin: 0, updatedAt: FieldValue.serverTimestamp(),
|
|
}, { merge: true });
|
|
}
|
|
for (const [matchId, preds] of Object.entries(AI)) {
|
|
for (const p of preds) {
|
|
batch.set(db.collection("ai_predictions").doc(`${matchId}_${p.model}`), { matchId, ...p });
|
|
}
|
|
}
|
|
await batch.commit();
|
|
console.log(`seeded: ${MATCHES.length} matches, ${Object.values(AI).flat().length} ai_predictions, crowd_stats init`);
|
|
}
|
|
run().catch((e) => { console.error(e); process.exit(1); });
|