From aabc9cf73385d7181fad1051f6984c61275cfe63 Mon Sep 17 00:00:00 2001 From: spacemeowx2 Date: Sat, 26 Nov 2022 22:45:44 +0800 Subject: [PATCH] refactor: add ExportResult (fix #12) --- src/app.ts | 113 ++++++++++++++++++++++++-------------- src/exporters/file.ts | 11 +++- src/exporters/stat.ink.ts | 10 +++- src/types.ts | 23 ++++++-- 4 files changed, 108 insertions(+), 49 deletions(-) diff --git a/src/app.ts b/src/app.ts index 6c11a84..a123da6 100644 --- a/src/app.ts +++ b/src/app.ts @@ -35,6 +35,29 @@ type Progress = { total: number; }; +class StepProgress { + currentUrl?: string; + total: number; + exported: number; + done: number; + skipped: Record; + + constructor() { + this.total = 1; + this.exported = 0; + this.done = 0; + this.skipped = {}; + } +} + +function progress({ total, currentUrl, done }: StepProgress): Progress { + return { + total, + currentUrl, + current: done, + }; +} + export class App { profile: Profile; env: Env; @@ -128,7 +151,7 @@ export class App { const exporters = await this.getExporters(); const initStats = () => Object.fromEntries( - exporters.map((e) => [e.name, 0]), + exporters.map((e) => [e.name, new StepProgress()]), ); let stats = initStats(); const skipMode = this.getSkipMode(); @@ -158,14 +181,11 @@ export class App { fetcher, exporter: e, gameList, - onStep: (progress) => { - redraw(e.name, progress); - stats[e.name] = progress.current; + stepProgress: stats[e.name], + onStep: () => { + redraw(e.name, progress(stats[e.name])); }, - }) - .then((count) => { - stats[e.name] = count; - }), + }), ) .catch((err) => { errors.push(err); @@ -215,14 +235,11 @@ export class App { fetcher, exporter: e, gameList: coopBattleList, - onStep: (progress) => { - stats[e.name] = progress.current; - redraw(e.name, progress); + stepProgress: stats[e.name], + onStep: () => { + redraw(e.name, progress(stats[e.name])); }, - }) - .then((count) => { - stats[e.name] = count; - }), + }), ) .catch((err) => { errors.push(err); @@ -295,20 +312,17 @@ export class App { fetcher, exporter, gameList, + stepProgress, onStep, }: { type: Game["type"]; exporter: GameExporter; fetcher: GameFetcher; gameList: string[]; - onStep: (progress: Progress) => void; - }) { - let exported = 0; - - onStep?.({ - current: 0, - total: 1, - }); + stepProgress: StepProgress; + onStep: () => void; + }): Promise { + onStep?.(); const workQueue = [ ...await exporter.notExported({ @@ -320,39 +334,56 @@ export class App { const step = async (id: string) => { const detail = await fetcher.fetch(type, id); - const { url } = await exporter.exportGame(detail); - exported += 1; - onStep?.({ - currentUrl: url, - current: exported, - total: workQueue.length, - }); + const result = await exporter.exportGame(detail); + + stepProgress.done += 1; + stepProgress.currentUrl = undefined; + + if (result.status === "success") { + stepProgress.exported += 1; + stepProgress.currentUrl = result.url; + } else if (result.status === "skip") { + const { skipped } = stepProgress; + skipped[result.reason] = (skipped[result.reason] ?? 0) + 1; + } else { + const _never: never = result; + } + + onStep?.(); }; if (workQueue.length > 0) { - onStep?.({ - current: exported, - total: workQueue.length, - }); + stepProgress.total = workQueue.length; + onStep?.(); for (const battle of workQueue) { await step(battle); } } else { - onStep?.({ - current: 1, - total: 1, - }); + stepProgress.done = 1; + onStep?.(); } - return exported; + return stepProgress; } - printStats(stats: Record) { + printStats(stats: Record) { this.env.logger.log( `Exported ${ Object.entries(stats) - .map(([name, count]) => `${name}: ${count}`) + .map(([name, { exported }]) => `${name}: ${exported}`) .join(", ") }`, ); + if (Object.values(stats).some((i) => Object.keys(i.skipped).length > 0)) { + this.env.logger.log( + `Skipped ${ + Object.entries(stats) + .map(([name, { skipped }]) => + Object.entries(skipped).map(([reason, count]) => + `${name}: ${reason} (${count})` + ).join(", ") + ) + }`, + ); + } } } diff --git a/src/exporters/file.ts b/src/exporters/file.ts index ea6f3c7..67250be 100644 --- a/src/exporters/file.ts +++ b/src/exporters/file.ts @@ -1,4 +1,10 @@ -import { CoopInfo, Game, GameExporter, VsInfo } from "../types.ts"; +import { + CoopInfo, + ExportResult, + Game, + GameExporter, + VsInfo, +} from "../types.ts"; import { path } from "../../deps.ts"; import { NSOAPP_VERSION, S3SI_VERSION } from "../constant.ts"; import { parseHistoryDetailId, urlSimplify } from "../utils.ts"; @@ -83,7 +89,7 @@ export class FileExporter implements GameExporter { }, })); } - async exportGame(info: Game) { + async exportGame(info: Game): Promise { await Deno.mkdir(this.exportPath, { recursive: true }); const filename = this.getFilenameById(info.detail.id); @@ -103,6 +109,7 @@ export class FileExporter implements GameExporter { ); return { + status: "success", url: filepath, }; } diff --git a/src/exporters/stat.ink.ts b/src/exporters/stat.ink.ts index 9723fc2..4c2f316 100644 --- a/src/exporters/stat.ink.ts +++ b/src/exporters/stat.ink.ts @@ -8,6 +8,7 @@ import { CoopHistoryDetail, CoopHistoryPlayerResult, CoopInfo, + ExportResult, Game, GameExporter, Image, @@ -231,10 +232,13 @@ export class StatInkExporter implements GameExporter { isTriColor({ vsMode }: VsHistoryDetail): boolean { return vsMode.mode === "FEST" && b64Number(vsMode.id) === 8; } - async exportGame(game: VsInfo | CoopInfo) { + async exportGame(game: Game): Promise { if (game.type === "VsInfo" && this.isTriColor(game.detail)) { // TODO: support tri-color fest - return {}; + return { + status: "skip", + reason: "Tri-color fest is not supported", + }; } if (game.type === "VsInfo") { @@ -242,6 +246,7 @@ export class StatInkExporter implements GameExporter { const { url } = await this.api.postBattle(body); return { + status: "success", url, }; } else { @@ -249,6 +254,7 @@ export class StatInkExporter implements GameExporter { const { url } = await this.api.postCoop(body); return { + status: "success", url, }; } diff --git a/src/types.ts b/src/types.ts index 30d1120..b846513 100644 --- a/src/types.ts +++ b/src/types.ts @@ -296,12 +296,27 @@ export type CoopHistoryDetail = { jobBonus: null | number; }; +export type ExportResult = { + status: "success"; + url?: string; +} | { + status: "skip"; + reason: string; +}; + +export type SummaryFetcher = { + fetchSummary( + type: T, + ): Promise; +}; + export type GameExporter = { name: string; notExported: ( { type, list }: { type: Game["type"]; list: string[] }, ) => Promise; - exportGame: (game: Game) => Promise<{ url?: string }>; + exportGame: (game: Game) => Promise; + exportSummary?: (fetcher: SummaryFetcher) => Promise; }; export type BankaraBattleHistories = { @@ -737,7 +752,7 @@ export type RankParam = { }; export enum SummaryEnum { - ConfigureAnalyticsQuery, - HistoryRecordQuery, - CoopHistoryQuery, + ConfigureAnalyticsQuery = Queries.ConfigureAnalyticsQuery, + HistoryRecordQuery = Queries.HistoryRecordQuery, + CoopHistoryQuery = Queries.CoopHistoryQuery, }