mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-06 22:07:58 +03:00
checkpoint
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
import type { DeepPartial } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -44,8 +44,6 @@ const scoreData: ScoreData<"arcaea:Touch"> = {
|
||||
const mockScore = mkMockScore("arcaea", "Touch", TestingArcaeaSheriruthFTR, scoreData);
|
||||
const mockPB = mkMockPB("arcaea", "Touch", TestingArcaeaSheriruthFTR, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("Arcaea Implementation", (t) => {
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (score: number, expected: string) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -45,8 +45,6 @@ function scoreToPercent(score: number) {
|
||||
return (100 * score) / max;
|
||||
}
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
for (const [game, playtype, impl] of [
|
||||
["bms", "7K", BMS_7K_IMPL],
|
||||
["bms", "14K", BMS_14K_IMPL],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -38,8 +38,6 @@ const scoreData: ScoreData<"chunithm:Single"> = {
|
||||
const mockScore = mkMockScore("chunithm", "Single", CHUNITHMBBKKChart, scoreData);
|
||||
const mockPB = mkMockPB("chunithm", "Single", CHUNITHMBBKKChart, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("CHUNITHM Implementation", (t) => {
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (score: number, expected: any) =>
|
||||
|
||||
@@ -22,14 +22,12 @@ import {
|
||||
type SongDocument,
|
||||
} from "../../../../common/src";
|
||||
import db from "../../external/mongo/db";
|
||||
import CreateLogCtx from "../../lib/logger/logger";
|
||||
import { log } from "../../lib/logger/log";
|
||||
import { IsNullish } from "../../utils/misc";
|
||||
import { CreatePBMergeFor } from "../utils/pb-merge";
|
||||
import { SessionAvgBest10For } from "../utils/session-calc";
|
||||
import { GoalFmtScore, GoalOutOfFmtScore, GradeGoalFormatter } from "./_common";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
interface PBScoreDocumentWithSong extends PBScoreDocument<"ddr:DP" | "ddr:SP"> {
|
||||
song: SongDocument<"ddr">;
|
||||
top?: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
} from "../../../../common/src";
|
||||
import { GITADORA_DORA_IMPL, GITADORA_GITA_IMPL } from "./gitadora";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics[GPTStrings["gitadora"]] = {
|
||||
lamp: "CLEAR",
|
||||
percent: 45,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkFakePBIIDXSP, mkFakeScoreIIDXSP } from "#test-utils/misc";
|
||||
@@ -23,8 +23,6 @@ const baseMetrics: ProvidedMetrics["iidx:DP" | "iidx:SP"] = {
|
||||
score: 1000,
|
||||
};
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const max = Testing511SPA.data.notecount * 2;
|
||||
|
||||
function percentToScore(percent: number) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -12,8 +12,6 @@ import type { ProvidedMetrics, ScoreData } from "../../../../common/src";
|
||||
import { JUBEAT_SINGLE_CONF } from "../../../../common/src/config/game-support/jubeat";
|
||||
import { JUBEAT_IMPL } from "./jubeat";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics["jubeat:Single"] = {
|
||||
lamp: "CLEAR",
|
||||
musicRate: 84.21,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -12,8 +12,6 @@ import type { integer, ProvidedMetrics, ScoreData } from "../../../../common/src
|
||||
import { MAIMAI_SINGLE_CONF } from "../../../../common/src/config/game-support/maimai";
|
||||
import { MAIMAI_IMPL } from "./maimai";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics["maimai:Single"] = {
|
||||
lamp: "CLEAR",
|
||||
percent: 97.01,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -11,8 +11,6 @@ import type { ProvidedMetrics, ScoreData } from "../../../../common/src";
|
||||
import { MAIMAI_DX_SINGLE_CONF } from "../../../../common/src/config/game-support/maimai-dx";
|
||||
import { MAIMAIDX_IMPL } from "./maimaidx";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics["maimaidx:Single"] = {
|
||||
lamp: "CLEAR",
|
||||
percent: 97.012,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -34,8 +34,6 @@ const scoreData: ScoreData<"museca:Single"> = {
|
||||
const mockScore = mkMockScore("museca", "Single", TestingMusecaChart, scoreData);
|
||||
const mockPB = mkMockPB("museca", "Single", TestingMusecaChart, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("MUSECA Implementation", (t) => {
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (score: number, expected: any) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc, UpdateChartRanking } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -44,8 +44,6 @@ const scoreData: ScoreData<"ongeki:Single"> = {
|
||||
const mockScore = mkMockScore("ongeki", "Single", TestingOngekiChart, scoreData);
|
||||
const mockPB = mkMockPB("ongeki", "Single", TestingOngekiChart, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("ONGEKI Implementation", (t: any) => {
|
||||
t.test("Grade Deriver", (t: any) => {
|
||||
const f = (score: number, expected: string) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -37,8 +37,6 @@ const scoreData: ScoreData<"popn:9B"> = {
|
||||
const mockScore = mkMockScore("popn", "9B", TestingPopnChart, scoreData);
|
||||
const mockPB = mkMockPB("popn", "9B", TestingPopnChart, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("Pop'n Implementation", (t) => {
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (score: number, expected: any) =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
} from "../../../../common/src";
|
||||
import { SDVX_IMPL } from "./sdvx";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics["sdvx:Single"] = {
|
||||
lamp: "CLEAR",
|
||||
score: 9_003_000,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -16,8 +16,6 @@ import {
|
||||
} from "../../../../common/src";
|
||||
import { USC_CONTROLLER_IMPL, USC_KEYBOARD_IMPL } from "./usc";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const baseMetrics: ProvidedMetrics[GPTStrings["usc"]] = {
|
||||
lamp: "CLEAR",
|
||||
score: 9_003_000,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DeepPartial } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CreatePBDoc } from "#lib/score-import/framework/pb/create-pb-doc";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf, mkMockPB, mkMockScore } from "#test-utils/misc";
|
||||
@@ -38,8 +38,6 @@ const scoreData: ScoreData<"wacca:Single"> = {
|
||||
const mockScore = mkMockScore("wacca", "Single", TestingWaccaPupaExp, scoreData);
|
||||
const mockPB = mkMockPB("wacca", "Single", TestingWaccaPupaExp, scoreData);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("WACCA Implementation", (t) => {
|
||||
t.test("Grade Deriver", (t) => {
|
||||
const f = (score: number, expected: any) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PrudenceError } from "prudence";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { ServerConfig, TachiConfig } from "#lib/setup/config";
|
||||
|
||||
import { type TachiAPIClientDocument, UserAuthLevels } from "../../../../common/src";
|
||||
@@ -11,8 +11,6 @@ import { Random20Hex } from "#utils/misc";
|
||||
import { FormatPrError } from "#utils/prudence";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
type DefaultClients = Array<Omit<TachiAPIClientDocument, "author" | "clientSecret">>;
|
||||
|
||||
// Defines some Tachi API Clients that should come default with a Tachi
|
||||
@@ -315,7 +313,7 @@ async function LoadClients(clients: DefaultClients) {
|
||||
});
|
||||
|
||||
if (!firstAdmin) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`There are no admins on this instance of tachi-server. We cannot create default API Clients!
|
||||
Chances are, you're seeing this message because you just bootstrapped Tachi.
|
||||
You'll need to set a user's authLevel to ${UserAuthLevels.ADMIN}.
|
||||
@@ -351,9 +349,7 @@ If you have no users, go create an account using the frontend, then run pnpm mak
|
||||
try {
|
||||
DatabaseSchemas["api-clients"](realClient);
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`,
|
||||
);
|
||||
log.error(`Invalid API Client ${client.name}: ${FormatPrError(err as PrudenceError)}.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -364,6 +360,6 @@ If you have no users, go create an account using the frontend, then run pnpm mak
|
||||
|
||||
await db["api-clients"].insert(realClient);
|
||||
|
||||
logger.info(`Loaded/Modified new built-in client ${client.name}.`);
|
||||
log.info(`Loaded/Modified new built-in client ${client.name}.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Response } from "express";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
import fs from "fs";
|
||||
import mkdirp from "mkdirp";
|
||||
@@ -13,8 +13,6 @@ const readFilePromise = promisify(fs.readFile);
|
||||
const writeFilePromise = promisify(fs.writeFile);
|
||||
const rmFilePromise = promisify(fs.rm);
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Joins a file location against the location of the CDN static store.
|
||||
*
|
||||
@@ -23,7 +21,7 @@ const logger = CreateLogCtx(__filename);
|
||||
*/
|
||||
export function CDNFileSystemRoot(fileLoc: string) {
|
||||
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "LOCAL_FILESYSTEM") {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`Attempted to run CDNFileSystemRoot, but was not using LOCAL_FILESYSTEM as a CDN.`,
|
||||
{ fileLoc, conf: ServerConfig.CDN_CONFIG },
|
||||
);
|
||||
@@ -43,7 +41,7 @@ export function CDNFileSystemRoot(fileLoc: string) {
|
||||
* As in, this ruins the purpose of a CDN! make sure you have one running.
|
||||
*/
|
||||
export function CDNRetrieve(fileLoc: string) {
|
||||
logger.debug(`Retrieving path ${fileLoc} locally.`);
|
||||
log.debug(`Retrieving path ${fileLoc} locally.`);
|
||||
|
||||
return readFilePromise(CDNFileSystemRoot(fileLoc));
|
||||
}
|
||||
@@ -56,7 +54,7 @@ export function CDNRedirect(res: Response, fileLoc: string) {
|
||||
throw new Error(`Invalid fileLoc - did not start with /.`);
|
||||
}
|
||||
|
||||
logger.debug(`CDN Redirecting to ${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}.`);
|
||||
log.debug(`CDN Redirecting to ${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}.`);
|
||||
|
||||
res.redirect(`${ServerConfig.CDN_CONFIG.WEB_LOCATION}${fileLoc}`);
|
||||
}
|
||||
@@ -65,7 +63,7 @@ export function CDNRedirect(res: Response, fileLoc: string) {
|
||||
* Stores a file at fileLoc. If it already exists, overwrite it.
|
||||
*/
|
||||
export async function CDNStoreOrOverwrite(fileLoc: string, data: string | Buffer): Promise<void> {
|
||||
logger.debug(`Storing or overwriting path ${fileLoc}.`);
|
||||
log.debug(`Storing or overwriting path ${fileLoc}.`);
|
||||
|
||||
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "LOCAL_FILESYSTEM") {
|
||||
const loc = CDNFileSystemRoot(fileLoc);
|
||||
@@ -83,7 +81,7 @@ export async function CDNStoreOrOverwrite(fileLoc: string, data: string | Buffer
|
||||
* Removes a file at this CDN location.
|
||||
*/
|
||||
export async function CDNDelete(fileLoc: string) {
|
||||
logger.verbose(`Deleting path ${fileLoc}.`);
|
||||
log.verbose(`Deleting path ${fileLoc}.`);
|
||||
|
||||
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "LOCAL_FILESYSTEM") {
|
||||
const loc = CDNFileSystemRoot(fileLoc);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
import { DeleteObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
||||
|
||||
const logger = CreateLogCtx("S3 Client");
|
||||
|
||||
let s3: S3Client | null = null;
|
||||
|
||||
if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "S3_BUCKET") {
|
||||
const data = ServerConfig.CDN_CONFIG.SAVE_LOCATION;
|
||||
|
||||
logger.info(`Using S3_BUCKET as CDN location.`, { bootInfo: true });
|
||||
log.info(`Using S3_BUCKET as CDN location.`, { bootInfo: true });
|
||||
s3 = new S3Client({
|
||||
endpoint: data.ENDPOINT,
|
||||
region:
|
||||
@@ -25,10 +23,10 @@ if (ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE === "S3_BUCKET") {
|
||||
* Pushes a file to the configured S3 Bucket. Overwrites if already exists.
|
||||
*/
|
||||
export function PushToS3(path: string, content: string | Buffer) {
|
||||
logger.debug(`Saving content on S3 at ${path}.`);
|
||||
log.debug(`Saving content on S3 at ${path}.`);
|
||||
|
||||
if (!s3 || ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "S3_BUCKET") {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`Attempted to push to S3, but CDN_CONFIG.SAVE_LOCATION.TYPE was not S3_BUCKET?`,
|
||||
ServerConfig.CDN_CONFIG,
|
||||
);
|
||||
@@ -51,7 +49,7 @@ export function PushToS3(path: string, content: string | Buffer) {
|
||||
*/
|
||||
export function DeleteFromS3(path: string) {
|
||||
if (!s3 || ServerConfig.CDN_CONFIG.SAVE_LOCATION.TYPE !== "S3_BUCKET") {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`Attempted to delete from S3, but CDN_CONFIG.SAVE_LOCATION.TYPE was not S3_BUCKET?`,
|
||||
ServerConfig.CDN_CONFIG,
|
||||
);
|
||||
|
||||
@@ -1,51 +1,3 @@
|
||||
import { execSync } from "child_process";
|
||||
import fs from "fs";
|
||||
// Note that Version Info is intentionally identical between all variants of tachi
|
||||
// viz. boku and kamai share versioning. This is because they
|
||||
// essentially share codebases.
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { Env } from "#lib/setup/config";
|
||||
import path from "path";
|
||||
import semver from "semver";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
// we want to read the monorepo root package.json.
|
||||
const packageJSON = JSON.parse(
|
||||
fs.readFileSync(path.join(__dirname, "../../../../package.json"), "utf-8"),
|
||||
) as { version: string };
|
||||
|
||||
const semverInfo = semver.parse(packageJSON.version);
|
||||
|
||||
if (!semverInfo) {
|
||||
logger.warn(
|
||||
`Couldn't parse semverInfo out of package.json? Got a string of '${packageJSON.version}', but couldn't parse. Defaulting to v0.0.0-unknown.`,
|
||||
);
|
||||
}
|
||||
|
||||
let commit: string | null = null;
|
||||
|
||||
try {
|
||||
if (Env.COMMIT_HASH) {
|
||||
commit = Env.COMMIT_HASH;
|
||||
} else {
|
||||
// This fetches the HEAD of our current running branch. This is useful for debugging.
|
||||
// Note that git returns this with a trailing newline, so we have to trim that off.
|
||||
commit = execSync("git rev-parse --short HEAD").toString("utf-8").trim();
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn(`Failed to read what commit we're on -- Using null as our commit version.`, {
|
||||
err,
|
||||
});
|
||||
}
|
||||
|
||||
export const VERSION_INFO = {
|
||||
major: semverInfo?.major ?? 0,
|
||||
minor: semverInfo?.minor ?? 0,
|
||||
patch: semverInfo?.patch ?? 0,
|
||||
commit,
|
||||
};
|
||||
|
||||
export const VERSION_STR = semverInfo?.raw ?? "0.0.0-unknown";
|
||||
|
||||
export const VERSION_PRETTY = `v${VERSION_STR} [${commit}]`;
|
||||
export const VERSION_PRETTY = `v${Env.VERSION} [${Env.COMMIT_HASH ?? "unknown commit"}]`;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { Env, ServerConfig } from "#lib/setup/config";
|
||||
import bunyan from "bunyan";
|
||||
import nodemailer, { type SentMessageInfo, type Transporter } from "nodemailer";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
let transporter: Transporter | undefined;
|
||||
|
||||
if (ServerConfig.EMAIL_CONFIG) {
|
||||
logger.info(`Connecting to email server...`, { bootInfo: true });
|
||||
log.info(`Connecting to email server...`, { bootInfo: true });
|
||||
const conf = ServerConfig.EMAIL_CONFIG;
|
||||
|
||||
try {
|
||||
@@ -23,18 +21,18 @@ if (ServerConfig.EMAIL_CONFIG) {
|
||||
|
||||
transporter.verify((err) => {
|
||||
if (err) {
|
||||
logger.crit(`Could not connect to email server.`, { err });
|
||||
log.fatal(`Could not connect to email server.`, { err });
|
||||
throw err;
|
||||
} else {
|
||||
logger.info(`Successfully connected to email server.`, { bootInfo: true });
|
||||
log.info(`Successfully connected to email server.`, { bootInfo: true });
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logger.crit(`Failed to create email client.`, { err });
|
||||
log.fatal(`Failed to create email client.`, { err });
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
logger.warn(`No EMAIL_CONFIG present in conf, emails will not be sent from the server.`, {
|
||||
log.warn(`No EMAIL_CONFIG present in conf, emails will not be sent from the server.`, {
|
||||
bootInfo: true,
|
||||
});
|
||||
}
|
||||
@@ -46,16 +44,16 @@ export function SendEmail(
|
||||
textContent: string,
|
||||
): Promise<SentMessageInfo> | undefined {
|
||||
if (Env.NODE_ENV === "test") {
|
||||
logger.debug(`Stubbed out SendEmail as env was test.`);
|
||||
log.debug(`Stubbed out SendEmail as env was test.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!transporter || !ServerConfig.EMAIL_CONFIG) {
|
||||
logger.debug(`Stubbed out SendEmail as no EMAIL_CONFIG was set.`);
|
||||
log.debug(`Stubbed out SendEmail as no EMAIL_CONFIG was set.`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.verbose(`Sending email to ${to}.`);
|
||||
log.verbose(`Sending email to ${to}.`);
|
||||
|
||||
return transporter
|
||||
.sendMail({
|
||||
@@ -68,7 +66,7 @@ export function SendEmail(
|
||||
headers: transporter.options.headers,
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
logger.info(`Failed to send email to ${to}.`, {
|
||||
log.info(`Failed to send email to ${to}.`, {
|
||||
err,
|
||||
subject,
|
||||
textContent,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import type { GameGroup, integer, Playtype } from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Create GameSettings for a UGPT (which contains their preferences).
|
||||
*/
|
||||
@@ -16,7 +14,7 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt
|
||||
});
|
||||
|
||||
if (exists) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Cannot create ${userID} ${game} ${playtype} game-settings as one already exists?`,
|
||||
);
|
||||
|
||||
@@ -51,5 +49,5 @@ export async function CreateGameSettings(userID: integer, game: GameGroup, playt
|
||||
rivals: [],
|
||||
});
|
||||
|
||||
logger.info(`Created game settings for ${userID} (${game} ${playtype}).`);
|
||||
log.info(`Created game settings for ${userID} (${game} ${playtype}).`);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { BMSTableHead, RawBMSTableEntry } from "bms-table-loader";
|
||||
import type { Request, Response } from "express-serve-static-core";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { GetRivalUsers } from "#lib/rivals/rivals";
|
||||
import { ServerConfig, TachiConfig } from "#lib/setup/config";
|
||||
import { GetRelevantSongsAndCharts } from "#utils/db";
|
||||
@@ -25,8 +25,6 @@ import {
|
||||
type TableDocument,
|
||||
} from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
// Instead of just supporting existing tables, Tachi should also be able
|
||||
// to emit its own, custom BMS tables. These may be dynamic.
|
||||
|
||||
@@ -48,7 +46,7 @@ function AppendAndConvertChartsToBMSBody(
|
||||
md5: chart.data.hashMD5,
|
||||
});
|
||||
} else {
|
||||
logger.warn(`BMS Chart md5=${chart.data.hashMD5} has no parent song.`);
|
||||
log.warn(`BMS Chart md5=${chart.data.hashMD5} has no parent song.`);
|
||||
body.push({
|
||||
level,
|
||||
md5: chart.data.hashMD5,
|
||||
@@ -80,7 +78,7 @@ export async function TachiTableToBMSTableJSON(
|
||||
const folder = folderMap.get(folderID);
|
||||
|
||||
if (!folder) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Table '${table.title}' refers to folder '${folderID}', yet no such folder exists in the db?`,
|
||||
);
|
||||
continue;
|
||||
@@ -229,7 +227,7 @@ export async function HandleBMSTableHeaderRequest(
|
||||
|
||||
return res.status(200).send(header);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to load header.json for table ${bmsTable.tableName}.`, {
|
||||
log.error(`Failed to load header.json for table ${bmsTable.tableName}.`, {
|
||||
bmsTable,
|
||||
err,
|
||||
});
|
||||
@@ -257,7 +255,7 @@ export async function HandleBMSTableBodyRequest(
|
||||
|
||||
return res.status(200).send(body);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to load body.json for table ${bmsTable.tableName}.`, {
|
||||
log.error(`Failed to load body.json for table ${bmsTable.tableName}.`, {
|
||||
bmsTable,
|
||||
err,
|
||||
});
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { PoyashiBPI } from "rg-stats";
|
||||
|
||||
import type { ChartDocument, integer, Playtypes } from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
interface PlaylistEntry {
|
||||
entry_id: integer;
|
||||
difficulty: "another" | "beginner" | "hyper" | "leggendaria" | "normal";
|
||||
@@ -55,7 +53,7 @@ function ChartsToPlaylistFormat(
|
||||
|
||||
if (el === undefined) {
|
||||
// wut, chart has an empty array of ingameids?
|
||||
logger.warn(`Chart '${chart.chartID}' has an empty array of inGameIDs.`);
|
||||
log.warn(`Chart '${chart.chartID}' has an empty array of inGameIDs.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import type http from "http";
|
||||
import type https from "https";
|
||||
|
||||
import { rootLogger } from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { CloseScoreImportQueue } from "#lib/score-import/worker/queue";
|
||||
import { monkDB } from "#services/mongo/db";
|
||||
import { ClosePgConnection } from "#services/pg/db.js";
|
||||
import { CloseRedisConnection } from "#services/redis/redis";
|
||||
|
||||
const logger = rootLogger;
|
||||
|
||||
export function HandleSIGTERMGracefully(instance?: http.Server | https.Server) {
|
||||
logger.info("SIGTERM Received, closing program.", { shutdownInfo: true });
|
||||
log.info("SIGTERM Received, closing program.", { shutdownInfo: true });
|
||||
|
||||
if (instance) {
|
||||
instance.close(() => CloseEverythingElse());
|
||||
@@ -20,19 +18,19 @@ export function HandleSIGTERMGracefully(instance?: http.Server | https.Server) {
|
||||
}
|
||||
|
||||
async function CloseEverythingElse() {
|
||||
logger.info("Closing Mongo Database.", { shutdownInfo: true });
|
||||
log.info("Closing Mongo Database.", { shutdownInfo: true });
|
||||
await monkDB.close();
|
||||
|
||||
logger.info("Closing database...", { shutdownInfo: true });
|
||||
log.info("Closing database...", { shutdownInfo: true });
|
||||
await ClosePgConnection();
|
||||
|
||||
logger.info("Closing Redis Connection.", { shutdownInfo: true });
|
||||
log.info("Closing Redis Connection.", { shutdownInfo: true });
|
||||
await CloseRedisConnection();
|
||||
|
||||
logger.info("Closing Score Import Queue.", { shutdownInfo: true });
|
||||
log.info("Closing Score Import Queue.", { shutdownInfo: true });
|
||||
await CloseScoreImportQueue();
|
||||
|
||||
logger.info("Everything closed. Waiting for process to exit naturally.", {
|
||||
log.info("Everything closed. Waiting for process to exit naturally.", {
|
||||
shutdownInfo: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import {
|
||||
CheckAndSetOngoingImportLock,
|
||||
UnsetOngoingImportLock,
|
||||
@@ -8,8 +8,6 @@ import db from "#services/mongo/db";
|
||||
|
||||
import type { ImportDocument } from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
interface OngoingImportError {
|
||||
tag: "ONGOING_IMPORT";
|
||||
}
|
||||
@@ -22,16 +20,14 @@ interface OngoingImportError {
|
||||
* If this results in sessions being deleted, it will delete them.
|
||||
*/
|
||||
export async function RevertImport(importDoc: ImportDocument): Promise<OngoingImportError | null> {
|
||||
logger.info(`Received revert-import request for import '${importDoc.importID}'`, { importDoc });
|
||||
log.info(`Received revert-import request for import '${importDoc.importID}'`, { importDoc });
|
||||
|
||||
const scores = await GetImportScores(importDoc);
|
||||
|
||||
const hasNoOngoingImport = await CheckAndSetOngoingImportLock(importDoc.userID);
|
||||
|
||||
if (hasNoOngoingImport) {
|
||||
logger.info(
|
||||
`User ${importDoc.userID} tried to revert an import while they had one ongoing.`,
|
||||
);
|
||||
log.info(`User ${importDoc.userID} tried to revert an import while they had one ongoing.`);
|
||||
|
||||
return {
|
||||
tag: "ONGOING_IMPORT",
|
||||
@@ -41,7 +37,7 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
|
||||
try {
|
||||
await DeleteMultipleScores(scores);
|
||||
|
||||
logger.info(
|
||||
log.info(
|
||||
`Deleted ${scores.length} scores as part of reverting import '${importDoc.importID}'.`,
|
||||
{ importDoc },
|
||||
);
|
||||
@@ -49,9 +45,9 @@ export async function RevertImport(importDoc: ImportDocument): Promise<OngoingIm
|
||||
try {
|
||||
await db.imports.remove({ importID: importDoc.importID });
|
||||
|
||||
logger.info(`Reverted and deleted import '${importDoc.importID}'.`);
|
||||
log.info(`Reverted and deleted import '${importDoc.importID}'.`);
|
||||
} catch (err) {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`Deleted scores that were part of import, but failed to remove the actual import? There is a stale import with ID '${importDoc.importID}', which must be removed manually.`,
|
||||
{ importDoc, err },
|
||||
);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { ONE_MONTH } from "#lib/constants/time";
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
|
||||
import type { UserDocument } from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Users are only allowed to invite so many users, and their invites are
|
||||
* trickled out in bursts of INVITE_BATCH_SIZE.
|
||||
@@ -15,9 +13,7 @@ const logger = CreateLogCtx(__filename);
|
||||
*/
|
||||
export function GetTotalAllowedInvites(user: UserDocument) {
|
||||
if (!ServerConfig.INVITE_CODE_CONFIG) {
|
||||
logger.warn(
|
||||
`No INVITE_CODE_CONFIG set, but tried to get total allowed invites? Returning 0.`,
|
||||
);
|
||||
log.warn(`No INVITE_CODE_CONFIG set, but tried to get total allowed invites? Returning 0.`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { PullDatabaseSeeds } from "#lib/seeds/repo";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* The tachi-server may have its BMS or PMS database update. It needs to sync this
|
||||
* information back with the seeds.
|
||||
@@ -14,12 +12,12 @@ export async function BacksyncBMSPMSSongsAndCharts() {
|
||||
const repo = await PullDatabaseSeeds();
|
||||
|
||||
for (const game of ["bms", "pms"] as const) {
|
||||
logger.info(`Fetching ${game} songs from DB.`);
|
||||
log.info(`Fetching ${game} songs from DB.`);
|
||||
|
||||
// did you know, this code is liable to blow up in my face and OOM one day?
|
||||
let songs = await db.anySongs[game].find({});
|
||||
|
||||
logger.info(`Found ${songs.length} ${game} songs.`);
|
||||
log.info(`Found ${songs.length} ${game} songs.`);
|
||||
|
||||
await repo.WriteCollection(`songs-${game}`, songs);
|
||||
|
||||
@@ -28,10 +26,10 @@ export async function BacksyncBMSPMSSongsAndCharts() {
|
||||
// tries to read even more stuff.
|
||||
songs = null;
|
||||
|
||||
logger.info(`Fetching ${game} charts from DB.`);
|
||||
log.info(`Fetching ${game} charts from DB.`);
|
||||
let charts = await db.anyCharts[game].find({});
|
||||
|
||||
logger.info(`Found ${charts.length} ${game} charts.`);
|
||||
log.info(`Found ${charts.length} ${game} charts.`);
|
||||
|
||||
await repo.WriteCollection(`charts-${game}`, charts);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { PullDatabaseSeeds } from "#lib/seeds/repo";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
import fetch from "node-fetch";
|
||||
@@ -7,8 +7,6 @@ import type { ChartDocument } from "../../../../common/src";
|
||||
|
||||
const AI_URL = "https://bms.hexlataia.xyz/tables/json/ai.json";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
interface AITableEntry {
|
||||
level: string;
|
||||
md5: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import type { FilterQuery } from "mongodb";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { DeorphanIfInQueue } from "#lib/orphan-queue/orphan-queue";
|
||||
import db from "#services/mongo/db";
|
||||
import { InitaliseFolderChartLookup } from "#utils/folder";
|
||||
@@ -15,8 +15,6 @@ import {
|
||||
type Playtypes,
|
||||
} from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Tables might have updates that remove charts from their table.
|
||||
*
|
||||
@@ -30,7 +28,7 @@ async function HandleTableRemovals(
|
||||
prefix: string,
|
||||
) {
|
||||
if (tableEntries.length === 0) {
|
||||
logger.info(
|
||||
log.info(
|
||||
`No entries in table ${prefix}, skipping removals to prevent instantly wiping the table.`,
|
||||
);
|
||||
return;
|
||||
@@ -47,7 +45,7 @@ async function HandleTableRemovals(
|
||||
"data.tableFolders.table": prefix,
|
||||
})) as unknown as Array<ChartDocument<"bms:7K" | "bms:14K">>;
|
||||
|
||||
logger.info(
|
||||
log.info(
|
||||
`Found ${existingCharts.length} existing chart(s) in the database for table ${prefix}.`,
|
||||
);
|
||||
|
||||
@@ -89,7 +87,7 @@ async function HandleTableRemovals(
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`Removing ${toRemove.length} chart(s) from ${prefix}.`);
|
||||
log.info(`Removing ${toRemove.length} chart(s) from ${prefix}.`);
|
||||
|
||||
// remove this table info from all of the charts that no longer
|
||||
// exist in the table.
|
||||
@@ -115,7 +113,7 @@ async function ImportTableLevels(
|
||||
let success = 0;
|
||||
const total = tableEntries.length;
|
||||
|
||||
logger.info(`Handling removals for ${playtype}:${prefix}...`);
|
||||
log.info(`Handling removals for ${playtype}:${prefix}...`);
|
||||
await HandleTableRemovals(tableEntries, playtype, prefix);
|
||||
|
||||
const md5s = tableEntries.filter((e) => e.checksum.type === "md5").map((e) => e.checksum.value);
|
||||
@@ -183,7 +181,7 @@ async function ImportTableLevels(
|
||||
}
|
||||
|
||||
if (!chart) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`No chart exists in table for (${td.checksum.type}=${td.checksum.value} Possible title: ${td.content.title} ${prefix}${td.content.level}`,
|
||||
);
|
||||
failures++;
|
||||
@@ -228,16 +226,16 @@ async function ImportTableLevels(
|
||||
success++;
|
||||
}
|
||||
|
||||
logger.info(`Finished updating table ${prefix}.`);
|
||||
logger.info(`${success} Success | ${failures} Failures | ${total} Total.`);
|
||||
log.info(`Finished updating table ${prefix}.`);
|
||||
log.info(`${success} Success | ${failures} Failures | ${total} Total.`);
|
||||
}
|
||||
|
||||
export async function UpdateTable(tableInfo: BMSTableInfo) {
|
||||
const table = await LoadBMSTable(tableInfo.url);
|
||||
|
||||
logger.info(`Bumping levels...`);
|
||||
log.info(`Bumping levels...`);
|
||||
await ImportTableLevels(table.body, tableInfo.prefix, tableInfo.playtype);
|
||||
logger.info(`Levels bumped.`);
|
||||
log.info(`Levels bumped.`);
|
||||
}
|
||||
|
||||
export async function SyncBMSTables() {
|
||||
@@ -245,13 +243,13 @@ export async function SyncBMSTables() {
|
||||
try {
|
||||
await UpdateTable(table);
|
||||
} catch (err) {
|
||||
logger.error(`Failed to update table ${table.name} (${table.url}).`, { err });
|
||||
log.error(`Failed to update table ${table.name} (${table.url}).`, { err });
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Re-initialising folder-chart-lookup, since changes may have been made.`);
|
||||
log.info(`Re-initialising folder-chart-lookup, since changes may have been made.`);
|
||||
await InitaliseFolderChartLookup();
|
||||
logger.info(`Done.`);
|
||||
log.info(`Done.`);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// Attempt to deoprhan lost scores.
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { DeorphanScores } from "#lib/score-import/framework/orphans/orphans";
|
||||
import { WrapScriptPromise } from "#utils/misc";
|
||||
|
||||
const logger = CreateLogCtx(__dirname);
|
||||
|
||||
export async function DeorphanScoresMain() {
|
||||
const { success, failed, removed } = await DeorphanScores({}, logger);
|
||||
|
||||
logger.info(`Finished attempting deorphaning.`);
|
||||
log.info(`Finished attempting deorphaning.`);
|
||||
|
||||
logger.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`);
|
||||
log.info(`Success: ${success} | Failed ${failed} | Removed ${removed}.`);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { TachiConfig } from "#lib/setup/config";
|
||||
import { DedupeArr } from "#utils/misc";
|
||||
import { Queue, Worker } from "bullmq";
|
||||
@@ -69,19 +69,17 @@ if (TachiConfig.TYPE !== "kamai") {
|
||||
});
|
||||
}
|
||||
|
||||
const logger = CreateLogCtx("JOB_RUNNER");
|
||||
|
||||
/**
|
||||
* Initalises a tachi-server job runner.
|
||||
* This runs the list of jobs defined in jobConfig.jobs.
|
||||
*/
|
||||
export function InitialiseJobRunner() {
|
||||
logger.info(`Booting up Job Runner.`);
|
||||
log.info(`Booting up Job Runner.`);
|
||||
|
||||
const names = jobs.map((e) => e.name);
|
||||
|
||||
if (DedupeArr(names).length !== names.length) {
|
||||
logger.crit(`Jobs has duplicate name fields, refusing to run.`, () => {
|
||||
log.fatal(`Jobs has duplicate name fields, refusing to run.`, () => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -98,12 +96,12 @@ export function InitialiseJobRunner() {
|
||||
const worker = new Worker("Job Runner", async (j) => {
|
||||
const { jobName } = j.data as { jobName: string };
|
||||
|
||||
logger.info(`Running job ${jobName}.`);
|
||||
log.info(`Running job ${jobName}.`);
|
||||
|
||||
const jobInfo = jobNameMap.get(jobName);
|
||||
|
||||
if (!jobInfo) {
|
||||
logger.severe(`Unknown job name ${jobName}, couldn't find a run function?`);
|
||||
log.error(`Unknown job name ${jobName}, couldn't find a run function?`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -112,7 +110,7 @@ export function InitialiseJobRunner() {
|
||||
return true;
|
||||
});
|
||||
|
||||
logger.info(`Initialised ${jobs.length} jobs (${jobs.map((e) => e.name).join(", ")}).`);
|
||||
log.info(`Initialised ${jobs.length} jobs (${jobs.map((e) => e.name).join(", ")}).`);
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { GetMillisecondsSince } from "#utils/misc";
|
||||
import { GetAllRankings } from "#utils/user";
|
||||
|
||||
import type { UserGameStats, UserGameStatsSnapshotDocument } from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
// get the time of this midnight. it's possible this script eclipses itself when weird timezone
|
||||
// nonsense happens. we'll have to see.
|
||||
const currentTime = new Date().setUTCHours(0, 0, 0, 0);
|
||||
@@ -21,7 +19,7 @@ export async function UGSSnapshot() {
|
||||
const alreadyExists = await db["game-stats-snapshots"].findOne({ timestamp: currentTime });
|
||||
|
||||
if (alreadyExists) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`There already exists snapshots at this time. Has this script been ran twice on one day? Ignoring request.`,
|
||||
);
|
||||
|
||||
@@ -30,7 +28,7 @@ export async function UGSSnapshot() {
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Snapshotting UserGameStats.`);
|
||||
log.info(`Snapshotting UserGameStats.`);
|
||||
|
||||
try {
|
||||
await db["game-stats"]
|
||||
@@ -40,7 +38,7 @@ export async function UGSSnapshot() {
|
||||
.each(async (ugs: UserGameStats, { pause, resume }) => {
|
||||
pause();
|
||||
|
||||
logger.debug(`Snapshotting ${ugs.userID} ${ugs.playtype} ${ugs.game}.`);
|
||||
log.debug(`Snapshotting ${ugs.userID} ${ugs.playtype} ${ugs.game}.`);
|
||||
|
||||
const [playcount, rankings] = await Promise.all([
|
||||
db.scores.count({ userID: ugs.userID, playtype: ugs.playtype, game: ugs.game }),
|
||||
@@ -57,7 +55,7 @@ export async function UGSSnapshot() {
|
||||
batchWrite.push(ugsSnapshot);
|
||||
|
||||
if (batchWrite.length >= 500) {
|
||||
logger.verbose(`Flushed batch.`);
|
||||
log.verbose(`Flushed batch.`);
|
||||
await db["game-stats-snapshots"].insert(batchWrite);
|
||||
|
||||
batchWrite = [];
|
||||
@@ -70,22 +68,22 @@ export async function UGSSnapshot() {
|
||||
await db["game-stats-snapshots"].insert(batchWrite);
|
||||
}
|
||||
|
||||
logger.info(
|
||||
log.info(
|
||||
`Successfully snapshotted all data as of ${new Date(
|
||||
currentTime,
|
||||
).toString()}. Took ${GetMillisecondsSince(timeStart)} ms.`,
|
||||
);
|
||||
} catch (err) {
|
||||
// if we panic, we need to revert whatever we did.
|
||||
logger.severe(`FATAL IN UGS-SNAPSHOT - Possibly failed midway through snapshotting.`, {
|
||||
log.error(`FATAL IN UGS-SNAPSHOT - Possibly failed midway through snapshotting.`, {
|
||||
err,
|
||||
});
|
||||
|
||||
logger.info(`Removing all snapshots at this timestamp (${currentTime}).`);
|
||||
log.info(`Removing all snapshots at this timestamp (${currentTime}).`);
|
||||
|
||||
await db["game-stats-snapshots"].remove({ timestamp: currentTime });
|
||||
|
||||
logger.info(`Removed.`);
|
||||
log.info(`Removed.`);
|
||||
|
||||
throw err;
|
||||
}
|
||||
@@ -98,7 +96,7 @@ if (require.main === module) {
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
// This is a severe error, not an error. Running the UGS snapshot every day is necessary.
|
||||
logger.severe(`Failed to snapshot user game stats.`, { err });
|
||||
log.error(`Failed to snapshot user game stats.`, { err });
|
||||
|
||||
setTimeout(() => {
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { BacksyncCollection, PullDatabaseSeeds } from "#lib/seeds/repo";
|
||||
import db from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
@@ -14,8 +14,6 @@ import type {
|
||||
Versions,
|
||||
} from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const difficultyResolve: Record<string, [Playtypes["iidx"], Difficulties["iidx:DP" | "iidx:SP"]]> =
|
||||
{
|
||||
3: ["SP", "HYPER"],
|
||||
@@ -56,12 +54,12 @@ interface PoyashiProxyData {
|
||||
export async function UpdatePoyashiData() {
|
||||
const repo = await PullDatabaseSeeds();
|
||||
|
||||
logger.info("Fetching data from proxy...");
|
||||
log.info("Fetching data from proxy...");
|
||||
const data = (await fetch("https://proxy.poyashi.me/?type=bpi").then((r) =>
|
||||
r.json(),
|
||||
)) as PoyashiProxyData;
|
||||
|
||||
logger.info("Fetched data.");
|
||||
log.info("Fetched data.");
|
||||
|
||||
const iidxSongs: Array<SongDocument<"iidx">> = await repo.ReadCollection("songs-iidx");
|
||||
const iidxCharts: Array<ChartDocument<"iidx:DP" | "iidx:SP">> =
|
||||
@@ -114,7 +112,7 @@ export async function UpdatePoyashiData() {
|
||||
const tachiSong = FindSongOnTitle(d.title);
|
||||
|
||||
if (!tachiSong) {
|
||||
logger.warn(`Cannot find song ${d.title}?`);
|
||||
log.warn(`Cannot find song ${d.title}?`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -122,7 +120,7 @@ export async function UpdatePoyashiData() {
|
||||
const tachiChart = FindChartWithPTDFVersion(tachiSong.id, playtype, diff, "29");
|
||||
|
||||
if (!tachiChart) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Cannot find chart ${tachiSong.title} (${tachiSong.id}) ${playtype}, ${diff}?`,
|
||||
);
|
||||
continue;
|
||||
@@ -131,14 +129,12 @@ export async function UpdatePoyashiData() {
|
||||
const kavg = Number(d.avg);
|
||||
|
||||
if (kavg < 0) {
|
||||
logger.warn(
|
||||
`${tachiSong.title} (${playtype} ${diff}). Invalid kavg ${d.avg}, Skipping.`,
|
||||
);
|
||||
log.warn(`${tachiSong.title} (${playtype} ${diff}). Invalid kavg ${d.avg}, Skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d.removed === true) {
|
||||
logger.info(`Skipping removed chart ${tachiSong.title}.`);
|
||||
log.info(`Skipping removed chart ${tachiSong.title}.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -164,15 +160,15 @@ export async function UpdatePoyashiData() {
|
||||
await repo.WriteCollection("charts-iidx", iidxCharts);
|
||||
|
||||
if (updatedChartIDs.length !== 0) {
|
||||
logger.info(`Finished applying BPI changes. These changes will be backsynced later.`);
|
||||
log.info(`Finished applying BPI changes. These changes will be backsynced later.`);
|
||||
|
||||
logger.info(`Recalcing scores.`);
|
||||
log.info(`Recalcing scores.`);
|
||||
await RecalcAllScores({
|
||||
game: "iidx",
|
||||
chartID: { $in: updatedChartIDs },
|
||||
});
|
||||
|
||||
logger.info(`Finished recalcing scores.`);
|
||||
log.info(`Finished recalcing scores.`);
|
||||
}
|
||||
|
||||
await repo.Destroy();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { BacksyncCollection } from "#lib/seeds/repo";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
@@ -9,8 +9,6 @@ import { FindSongOnTitle } from "#utils/queries/songs";
|
||||
import { decode } from "html-entities";
|
||||
import { parse } from "node-html-parser";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
export async function UpdateDPTiers() {
|
||||
const rawHTML = await fetch("https://zasa.sakura.ne.jp/dp/run.php").then((r) => r.text());
|
||||
|
||||
@@ -40,7 +38,7 @@ export async function UpdateDPTiers() {
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`Got DP tier data. Applying it.`);
|
||||
log.info(`Got DP tier data. Applying it.`);
|
||||
|
||||
const updatedSongIDs = new Set();
|
||||
|
||||
@@ -49,7 +47,7 @@ export async function UpdateDPTiers() {
|
||||
const song = await FindSongOnTitle("iidx", d.songTitle);
|
||||
|
||||
if (!song) {
|
||||
logger.warn(`Couldn't find song with title ${d.songTitle}.`);
|
||||
log.warn(`Couldn't find song with title ${d.songTitle}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,9 +82,7 @@ export async function UpdateDPTiers() {
|
||||
);
|
||||
|
||||
if (updatedSongIDs.size !== 0) {
|
||||
logger.info(
|
||||
`${updatedSongIDs.size} songs were changed. Recalcing the relevant scores now.`,
|
||||
);
|
||||
log.info(`${updatedSongIDs.size} songs were changed. Recalcing the relevant scores now.`);
|
||||
|
||||
await RecalcAllScores({
|
||||
game: "iidx",
|
||||
@@ -94,12 +90,12 @@ export async function UpdateDPTiers() {
|
||||
songID: { $in: [...updatedSongIDs.values()] },
|
||||
});
|
||||
|
||||
logger.info(`Recalced those scores.`);
|
||||
log.info(`Recalced those scores.`);
|
||||
|
||||
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update DP Tierlist");
|
||||
}
|
||||
|
||||
logger.info("Done.");
|
||||
log.info("Done.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ChartDocument, Difficulties, integer } from "../../../../common/sr
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { BacksyncCollection } from "#lib/seeds/repo";
|
||||
import db from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
@@ -12,8 +12,6 @@ import { FindSongOnTitle } from "#utils/queries/songs";
|
||||
import fetch from "node-fetch";
|
||||
import { p } from "prudence";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
interface SP12Data {
|
||||
id: integer;
|
||||
title: string;
|
||||
@@ -49,7 +47,7 @@ async function FetchSP12Data() {
|
||||
});
|
||||
|
||||
if (err) {
|
||||
logger.error(`Got invalid/unexpected content from sp12.`, { unvalidatedRJ });
|
||||
log.error(`Got invalid/unexpected content from sp12.`, { unvalidatedRJ });
|
||||
|
||||
throw new Error(`Got invalid/unexpected content from sp12.`);
|
||||
}
|
||||
@@ -66,7 +64,7 @@ async function FetchSP12Data() {
|
||||
try {
|
||||
chart = await HumanisedTitleLookup(sh.title);
|
||||
} catch (err) {
|
||||
logger.error((err as Error).message);
|
||||
log.error((err as Error).message);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -189,21 +187,21 @@ async function FetchSP12Data() {
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(`Saved ${sh.title} value ${key} = ${val} (${text}).`);
|
||||
log.info(`Saved ${sh.title} value ${key} = ${val} (${text}).`);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedChartIDs.length !== 0) {
|
||||
logger.info(`Finished applying SP12 changes. Recalcing.`);
|
||||
logger.info("These changes will be backsynced by a separate script.");
|
||||
log.info(`Finished applying SP12 changes. Recalcing.`);
|
||||
log.info("These changes will be backsynced by a separate script.");
|
||||
|
||||
logger.info(`Recalcing scores.`);
|
||||
log.info(`Recalcing scores.`);
|
||||
await RecalcAllScores({
|
||||
game: "iidx",
|
||||
chartID: { $in: updatedChartIDs },
|
||||
});
|
||||
|
||||
logger.info(`Finished recalcing scores.`);
|
||||
log.info(`Finished recalcing scores.`);
|
||||
|
||||
await BacksyncCollection("charts-iidx", db.charts.iidx, "Update SP12 Tierlist");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import pino from "pino";
|
||||
|
||||
export type KtLogger = pino.Logger;
|
||||
|
||||
// Use JSON log lines when LOG_JSON=1 (dev/prod); otherwise pretty format for localdev.
|
||||
// Deliberately process.env here instead of importing ENV to avoid a circular dep.
|
||||
const useJson = process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true";
|
||||
|
||||
export const log = pino({
|
||||
level: process.env.LOG_LEVEL ?? "info",
|
||||
...(useJson
|
||||
? {}
|
||||
: {
|
||||
transport: {
|
||||
options: { colorize: true },
|
||||
target: "pino-pretty",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export function AppendLogCtx(context: string, parent: KtLogger = log): KtLogger {
|
||||
return parent.child({ context });
|
||||
}
|
||||
|
||||
export function ChangeRootLogLevel(level: string): void {
|
||||
log.level = level;
|
||||
}
|
||||
|
||||
export function GetLogLevel(): string {
|
||||
return log.level;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { Env, ServerConfig } from "#lib/setup/config";
|
||||
import t from "tap";
|
||||
|
||||
import CreateLogCtx, { ChangeRootLogLevel, GetLogLevel, rootLogger, Transports } from "./logger";
|
||||
|
||||
const LOG_LEVEL = Env.LOG_LEVEL;
|
||||
|
||||
t.test("Logger Tests", (t) => {
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
if (!Transports[0]) {
|
||||
throw new Error(`No transports were defined? Can't perform logger tests.`);
|
||||
}
|
||||
|
||||
// lol
|
||||
Transports[0].level = "debug";
|
||||
|
||||
logger.debug("Debug Message Test");
|
||||
logger.verbose("Verbose Message Test");
|
||||
logger.info("Info Message Test");
|
||||
logger.warn("Warning Message Test");
|
||||
logger.error("Error Message Test");
|
||||
logger.severe("Severe Message Test");
|
||||
logger.crit("Critical Message Test");
|
||||
|
||||
Transports[0].level = process.env.LOG_LEVEL ?? "info";
|
||||
|
||||
logger.debug("This message shouldn't appear.");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("#GetLogLevel", (t) => {
|
||||
t.equal(GetLogLevel(), LOG_LEVEL);
|
||||
|
||||
ChangeRootLogLevel("crit");
|
||||
t.equal(GetLogLevel(), "crit");
|
||||
|
||||
ChangeRootLogLevel(LOG_LEVEL);
|
||||
t.equal(GetLogLevel(), LOG_LEVEL);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("#ChangeRootLogLevel", (t) => {
|
||||
t.test("Should work with the root logger.", (t) => {
|
||||
rootLogger.info("The below message should appear.");
|
||||
|
||||
ChangeRootLogLevel("verbose");
|
||||
rootLogger.verbose("== SHOULD APPEAR ==");
|
||||
|
||||
ChangeRootLogLevel("info");
|
||||
|
||||
rootLogger.info("The below message SHOULD NOT appear.");
|
||||
|
||||
rootLogger.verbose("== SHOULD NOT APPEAR ==");
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -1,197 +0,0 @@
|
||||
import { Env, TachiConfig } from "#lib/setup/config";
|
||||
import { EscapeStringRegexp } from "#utils/misc";
|
||||
import SafeJSONStringify from "safe-json-stringify";
|
||||
import winston, { format, type LeveledLogMethod, type Logger, transports } from "winston";
|
||||
|
||||
export type KtLogger = { severe: LeveledLogMethod } & Logger;
|
||||
|
||||
const level = process.env.LOG_LEVEL;
|
||||
|
||||
const formatExcessProperties = (meta: Record<string, unknown>, limit = false) => {
|
||||
let i = 0;
|
||||
|
||||
for (const [key, val] of Object.entries(meta)) {
|
||||
// this is probably fine
|
||||
if (val instanceof Error) {
|
||||
meta[key] = { message: val.message, stack: val.stack };
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!i) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const content = SafeJSONStringify(meta);
|
||||
|
||||
return ` ${limit ? StrCap(content) : content}`;
|
||||
};
|
||||
|
||||
function StrCap(string: string) {
|
||||
if (string.length > 300) {
|
||||
return `${string.slice(0, 297)}...`;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
const formatExcessPropertiesNoStack = (
|
||||
meta: Record<string, unknown>,
|
||||
omitKeys: Array<string> = [],
|
||||
limit = false,
|
||||
) => {
|
||||
const realMeta: Record<string, unknown> = {};
|
||||
|
||||
for (const [key, val] of Object.entries(meta)) {
|
||||
if (omitKeys.includes(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is probably fine
|
||||
if (val instanceof Error) {
|
||||
realMeta[key] = { message: val.message };
|
||||
} else if (!key.startsWith("__") && !key.startsWith("!")) {
|
||||
realMeta[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(realMeta).length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const content = SafeJSONStringify(realMeta);
|
||||
|
||||
return ` ${limit ? StrCap(content) : content}`;
|
||||
};
|
||||
|
||||
const tachiPrintf = format.printf(
|
||||
({ level, message, context = "tachi-root", timestamp, ...meta }) =>
|
||||
`${timestamp} [${
|
||||
Array.isArray(context) ? context.join(" | ") : context
|
||||
}] ${level}: ${message}${formatExcessProperties(meta)}`,
|
||||
);
|
||||
|
||||
const tachiConsolePrintf = format.printf(
|
||||
({ level, message, context = "tachi-root", timestamp, hideFromConsole, ...meta }) =>
|
||||
`${timestamp} [${
|
||||
Array.isArray(context) ? context.join(" | ") : context
|
||||
}] ${level}: ${message}${formatExcessPropertiesNoStack(
|
||||
meta,
|
||||
hideFromConsole as Array<string>,
|
||||
true,
|
||||
)}`,
|
||||
);
|
||||
|
||||
winston.addColors({
|
||||
crit: ["bgRed", "black"],
|
||||
severe: ["bgBrightRed", "black"],
|
||||
error: ["red"],
|
||||
warn: ["yellow"],
|
||||
info: ["cyan"],
|
||||
verbose: ["white"],
|
||||
debug: ["white"],
|
||||
});
|
||||
|
||||
const baseFormatRoute = format.combine(
|
||||
format.timestamp({
|
||||
format: "YYYY-MM-DD HH:mm:ss",
|
||||
}),
|
||||
);
|
||||
|
||||
const defaultFormatRoute = format.combine(
|
||||
baseFormatRoute,
|
||||
format.errors({ stack: false }),
|
||||
tachiPrintf,
|
||||
);
|
||||
|
||||
const consoleFormatRoute = format.combine(
|
||||
baseFormatRoute,
|
||||
format.errors({ stack: false }),
|
||||
tachiConsolePrintf,
|
||||
format.colorize({
|
||||
all: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const tports: Array<winston.transport> = [
|
||||
new transports.Console({
|
||||
format: consoleFormatRoute,
|
||||
}),
|
||||
];
|
||||
|
||||
export const rootLogger = winston.createLogger({
|
||||
levels: {
|
||||
// entire process termination is necessary
|
||||
crit: 0,
|
||||
|
||||
// something is wrong, and more than one function is affected (such as a failed assertion that is definitely expected to be true).
|
||||
severe: 1,
|
||||
|
||||
// function call (or related process) has failed unexpectedly
|
||||
error: 2,
|
||||
|
||||
// function call has hit something it didn't want, but can recover
|
||||
warn: 3,
|
||||
|
||||
// something has happened that is expected, but worth logging
|
||||
info: 4,
|
||||
|
||||
// something has happened
|
||||
verbose: 5,
|
||||
|
||||
// glorified console.log debugging
|
||||
debug: 6,
|
||||
},
|
||||
level,
|
||||
format: defaultFormatRoute,
|
||||
transports: tports,
|
||||
defaultMeta: {
|
||||
__ServerName: TachiConfig.NAME,
|
||||
__Worker: !!process.env.IS_WORKER,
|
||||
},
|
||||
}) as KtLogger;
|
||||
|
||||
function CreateLogCtx(filename: string, lg = rootLogger): KtLogger {
|
||||
const replacedFilename = filename.replace(
|
||||
new RegExp(`^${EscapeStringRegexp(process.cwd())}/((js|src)/)?`, "u"),
|
||||
"",
|
||||
);
|
||||
|
||||
const logger = lg.child({
|
||||
context: [replacedFilename],
|
||||
}) as KtLogger;
|
||||
|
||||
// @hack, defaultMeta isn't reactive -- won't be updated unless we do this.
|
||||
|
||||
logger.defaultMeta = { ...(logger.defaultMeta ?? {}), context: [replacedFilename] };
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
export function AppendLogCtx(context: string, lg: KtLogger): KtLogger {
|
||||
const newContext = [...lg.defaultMeta.context, context];
|
||||
|
||||
return lg.child({ context: newContext }) as KtLogger;
|
||||
}
|
||||
|
||||
export function ChangeRootLogLevel(
|
||||
level: "crit" | "debug" | "error" | "info" | "severe" | "verbose" | "warn",
|
||||
) {
|
||||
rootLogger.info(`Changing log level to ${level}.`);
|
||||
|
||||
for (const tp of rootLogger.transports) {
|
||||
tp.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
export function GetLogLevel() {
|
||||
return (
|
||||
rootLogger.transports.map((e) => e.level).find((e) => typeof e === "string") ??
|
||||
Env.LOG_LEVEL
|
||||
);
|
||||
}
|
||||
|
||||
export const Transports = rootLogger.transports;
|
||||
|
||||
export default CreateLogCtx;
|
||||
@@ -1,127 +0,0 @@
|
||||
import db from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import t from "tap";
|
||||
|
||||
import { ApplyMigration, FAKE_MIGRATION, FindUnappliedMigrations } from "./migrations";
|
||||
|
||||
t.test("#FindUnappliedMigrations", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should find all unapplied migrations", async (t) => {
|
||||
const res = await FindUnappliedMigrations();
|
||||
|
||||
t.equal(
|
||||
res.includes("fake-migration"),
|
||||
true,
|
||||
"Should contain fake-migration since it isn't applied.",
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Shouldn't return pending migrations", async (t) => {
|
||||
await db.migrations.insert({
|
||||
migrationID: "fake-migration",
|
||||
status: "pending",
|
||||
});
|
||||
|
||||
const res = await FindUnappliedMigrations();
|
||||
|
||||
t.equal(res.includes("fake-migration"), false);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Shouldn't return applied migrations", async (t) => {
|
||||
await db.migrations.insert({
|
||||
migrationID: "fake-migration",
|
||||
status: "applied",
|
||||
appliedOn: 1000,
|
||||
});
|
||||
|
||||
const res = await FindUnappliedMigrations();
|
||||
|
||||
t.equal(res.includes("fake-migration"), false);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("#ApplyMigration", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
t.test("Should apply a migration.", async (t) => {
|
||||
await ApplyMigration(FAKE_MIGRATION);
|
||||
|
||||
const dbRes = await db.users.findOne({
|
||||
id: 1,
|
||||
});
|
||||
|
||||
t.equal(
|
||||
// @ts-expect-error Our migration is a fake one that adds __wasMigrated
|
||||
// to all user documents. There's nothing special about this property name.
|
||||
dbRes?.__wasMigrated,
|
||||
true,
|
||||
"Migration should be applied on the users.",
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Should panic and reset if something goes wrong.", async (t) => {
|
||||
try {
|
||||
await ApplyMigration({
|
||||
id: "bad-migration",
|
||||
up: () => {
|
||||
throw new Error("Failed to migrate.");
|
||||
},
|
||||
down: () =>
|
||||
db.users.update({}, { $set: { __wasUnmigrated: true } }, { multi: true }),
|
||||
});
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
|
||||
t.equal(err.message, "Was going to exit with statusCode 1, but we're in testing.");
|
||||
}
|
||||
|
||||
const dbRes = await db.users.findOne({
|
||||
id: 1,
|
||||
});
|
||||
|
||||
t.equal(
|
||||
// @ts-expect-error Our un-migration adds this property to check if it worked.
|
||||
dbRes?.__wasUnmigrated,
|
||||
true,
|
||||
"Migration should be applied on the users.",
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test(
|
||||
"Should panic and panic again if un-migration failed, with a scarier log message.",
|
||||
async (t) => {
|
||||
try {
|
||||
await ApplyMigration({
|
||||
id: "bad-migration",
|
||||
up: () => {
|
||||
throw new Error("Failed to migrate.");
|
||||
},
|
||||
down: () => {
|
||||
throw new Error("Failed to undo migration.");
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
|
||||
t.equal(err.message, "Was going to exit with statusCode 1, but we're in testing.");
|
||||
}
|
||||
|
||||
t.end();
|
||||
},
|
||||
);
|
||||
|
||||
t.end();
|
||||
});
|
||||
@@ -1,280 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { Env, TachiConfig } from "#lib/setup/config";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import UserFollowersMigration from "./migrations/add-following-to-users";
|
||||
import AddLockedAt from "./migrations/add-lockedat";
|
||||
import UGPTAddPreferredRanking from "./migrations/add-preferredRanking-to-ugpt";
|
||||
import UGPTRivalsMigration from "./migrations/add-rivals-to-ugpt";
|
||||
import ChunithmFixPreferredDefaultEnum from "./migrations/chunithm-fix-preferred-default-enum";
|
||||
import ChunithmLampSplit from "./migrations/chunithm-lamp-split";
|
||||
import FixUndefinedBMSData from "./migrations/fix-undefined-bms-data";
|
||||
import JoinINFCastHourCharts from "./migrations/join-inf-casthour-charts";
|
||||
import JoinINFCastHourSongs from "./migrations/join-inf-casthour-songs";
|
||||
import KtRatingToCuratorSkill from "./migrations/ktRating-to-curator-skill";
|
||||
import MaimaiDXFixLamps from "./migrations/maimaidx-fix-lamps";
|
||||
import MarkFervidexAsWithoutIntent from "./migrations/mark-fervidex-as-without-intent";
|
||||
import MarkKsHookAsWithoutIntent from "./migrations/mark-kshook-as-without-intent";
|
||||
import NullLR2HookFailedBPs from "./migrations/null-lr2hook-failed-bps";
|
||||
import OngekiV2 from "./migrations/ongeki-v2";
|
||||
import RemoveAbove100pc from "./migrations/remove-above-100pc";
|
||||
import RemoveIIDXBeginners from "./migrations/remove-iidx-beginners";
|
||||
import RemoveIIDX2dxtraBeginners from "./migrations/remove-iidx-extra-beginners";
|
||||
import RemoveMultifolderStats from "./migrations/remove-multifolder-stats";
|
||||
import RemoveRandom from "./migrations/remove-random";
|
||||
import RemoveSessionViews from "./migrations/remove-session-views";
|
||||
import RmHot from "./migrations/rm-hot";
|
||||
|
||||
// import SdvxMaxxiveGoals from "./migrations/sdvx-maxxive-goals";
|
||||
// import SdvxMaxxiveLampRearrange from "./migrations/sdvx-maxxive-lamp-rearrange";
|
||||
import SessionsToScoreIDs from "./migrations/sessions-to-scoreIDs";
|
||||
import UpdateJubeatPreferredTables from "./migrations/update-jubeat-preferred-tables";
|
||||
import V3PropsMigration from "./migrations/v3-props";
|
||||
import V3ScoresMigration from "./migrations/v3-scores";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
// NOTE: This is declared here, and NOT in test-utils. If any runtime file attempts to depend
|
||||
// on test-utils, the server will not boot.
|
||||
|
||||
export const FAKE_MIGRATION: Migration = {
|
||||
id: "fake-migration",
|
||||
up: () => db.users.update({}, { $set: { __wasMigrated: true } }, { multi: true }),
|
||||
down: () => db.users.update({}, { $unset: { __wasMigrated: 1 } }, { multi: true }),
|
||||
};
|
||||
|
||||
// Migrations are stored in an array because they have some concept of order
|
||||
// That is, migrations should ideally be applied in a fixed order just to avoid
|
||||
// any potential unsavoury interactions.
|
||||
// If we're testing, we should pull fake migrations instead to ensure the tests
|
||||
// stay consistent
|
||||
const REGISTERED_MIGRATIONS: Array<Migration> =
|
||||
Env.NODE_ENV === "test"
|
||||
? [FAKE_MIGRATION]
|
||||
: [
|
||||
UGPTRivalsMigration,
|
||||
RemoveMultifolderStats,
|
||||
UGPTAddPreferredRanking,
|
||||
UserFollowersMigration,
|
||||
RemoveSessionViews,
|
||||
SessionsToScoreIDs,
|
||||
V3PropsMigration,
|
||||
V3ScoresMigration,
|
||||
AddLockedAt,
|
||||
];
|
||||
|
||||
// only apply type-specific migrations if we're not in testing
|
||||
if (Env.NODE_ENV !== "test") {
|
||||
// kamaitachi specific migrations
|
||||
if (TachiConfig.TYPE !== "boku") {
|
||||
REGISTERED_MIGRATIONS.push(
|
||||
RemoveIIDXBeginners,
|
||||
UpdateJubeatPreferredTables,
|
||||
JoinINFCastHourCharts,
|
||||
JoinINFCastHourSongs,
|
||||
MarkFervidexAsWithoutIntent,
|
||||
MarkKsHookAsWithoutIntent,
|
||||
KtRatingToCuratorSkill,
|
||||
RemoveIIDX2dxtraBeginners,
|
||||
RmHot,
|
||||
ChunithmLampSplit,
|
||||
OngekiV2,
|
||||
ChunithmFixPreferredDefaultEnum,
|
||||
// SdvxMaxxiveLampRearrange,
|
||||
// SdvxMaxxiveGoals,
|
||||
MaimaiDXFixLamps,
|
||||
);
|
||||
}
|
||||
|
||||
// bokutachi specific migrations
|
||||
if (TachiConfig.TYPE !== "kamai") {
|
||||
REGISTERED_MIGRATIONS.push(
|
||||
NullLR2HookFailedBPs,
|
||||
FixUndefinedBMSData,
|
||||
RemoveRandom,
|
||||
RemoveAbove100pc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function CreateMigrationLookupMap(migrations: Array<Migration>) {
|
||||
const map = new Map<string, Migration>();
|
||||
|
||||
for (const mig of migrations) {
|
||||
if (map.get(mig.id)) {
|
||||
logger.crit(
|
||||
`Multiple migrations are registered for ${mig.id}. Cannot safely apply migrations.`,
|
||||
() => {
|
||||
// note, we want to exit in testing here, this is fine.
|
||||
process.exit(1);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
map.set(mig.id, mig);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
// That said, we still make a lookup map so we can efficiently find registered
|
||||
// migrations.
|
||||
const MIGRATION_LOOKUP = CreateMigrationLookupMap(REGISTERED_MIGRATIONS);
|
||||
|
||||
/**
|
||||
* Return the migrationID of all migrations that have not been applied.
|
||||
*/
|
||||
export async function FindUnappliedMigrations() {
|
||||
// An unapplied migration is one that is acknowledged in the codebase
|
||||
// (see REGISTERED_MIGRATIONS), but not stored inside the mongodb instance.
|
||||
const migrations = await db.migrations.find(
|
||||
{},
|
||||
{
|
||||
projection: {
|
||||
migrationID: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const appliedIDs = new Set(migrations.map((e) => e.migrationID));
|
||||
|
||||
const notApplied = [];
|
||||
|
||||
for (const migration of REGISTERED_MIGRATIONS) {
|
||||
if (!appliedIDs.has(migration.id)) {
|
||||
notApplied.push(migration.id);
|
||||
}
|
||||
}
|
||||
|
||||
return notApplied;
|
||||
}
|
||||
|
||||
export async function ApplyUnappliedMigrations() {
|
||||
const unapplied = await FindUnappliedMigrations();
|
||||
|
||||
if (unapplied.length === 0) {
|
||||
logger.verbose(`Found no migrations to apply. Not bothering.`);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Found ${
|
||||
unapplied.length
|
||||
} migration(s) that have not been applied yet. These are '${unapplied.join("', '")}'.`,
|
||||
{ unapplied },
|
||||
);
|
||||
|
||||
for (const migrationID of unapplied) {
|
||||
// These things need to apply in lockstep.
|
||||
// Note that if any migration fails, this will exit at CRIT level
|
||||
// and not continue.
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await ApplyMigrationByID(migrationID);
|
||||
}
|
||||
}
|
||||
|
||||
export function ApplyMigrationByID(migrationID: string) {
|
||||
const migration = MIGRATION_LOOKUP.get(migrationID);
|
||||
|
||||
if (!migration) {
|
||||
logger.error(
|
||||
`Attempted to apply migration ${migrationID}, but that migration doesn't exist?`,
|
||||
);
|
||||
throw new Error(
|
||||
`Attempted to apply migration ${migrationID}, but that migration doesn't exist?`,
|
||||
);
|
||||
}
|
||||
|
||||
return ApplyMigration(migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a migration. Will **EXIT TACHI** if a migration fails to apply, as this implies
|
||||
* database level inconsistencies which could cause severe problems.
|
||||
*/
|
||||
export async function ApplyMigration(migration: Migration) {
|
||||
const migrationID = migration.id;
|
||||
|
||||
logger.info(`Received request to apply migration '${migrationID}'.`);
|
||||
|
||||
// Lock the migration here:
|
||||
// If the migration is already applied (i.e. migrationID is set and pending/applied)
|
||||
// don't bother applying again
|
||||
// If the migration isn't, set it as pending and continue. This is race-condition
|
||||
// safe.
|
||||
const wasApplied = await db.migrations.findOneAndUpdate(
|
||||
{ migrationID },
|
||||
{
|
||||
$setOnInsert: {
|
||||
migrationID,
|
||||
status: "pending",
|
||||
},
|
||||
},
|
||||
{
|
||||
returnOriginal: true,
|
||||
upsert: true,
|
||||
},
|
||||
);
|
||||
|
||||
if (wasApplied) {
|
||||
logger.error(`Tried to apply migration ${migrationID}, but it was already applied.`);
|
||||
throw new Error(`Tried to apply migration ${migrationID}, but it was already applied.`);
|
||||
}
|
||||
|
||||
try {
|
||||
await migration.up();
|
||||
|
||||
logger.info(`Successfully applied migration '${migrationID}'.`);
|
||||
|
||||
await db.migrations.update(
|
||||
{
|
||||
migrationID,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
status: "applied",
|
||||
appliedOn: Date.now(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
logger.info(`Successfully set migration state for '${migrationID}' to applied.`);
|
||||
} catch (err) {
|
||||
logger.severe(
|
||||
`Failed to apply migration '${migrationID}'. Attempting to revert migration. Tachi will exit after attempting to revert this migration.`,
|
||||
{ err },
|
||||
);
|
||||
|
||||
try {
|
||||
await migration.down();
|
||||
|
||||
logger.info(
|
||||
`Successfully reverted migration. Database state is okay, but Tachi will not boot.`,
|
||||
);
|
||||
|
||||
logger.crit(
|
||||
`Failed to apply migration '${migrationID}'. Although successfully reverted, Tachi will not boot if a migration fails.`,
|
||||
);
|
||||
} catch (err) {
|
||||
logger.crit(
|
||||
`FAILED TO REVERT PARTIAL MIGRATION '${migrationID}'. THE DATABASE IS ALMOST CERTAINLY TAINTED. MANUAL INTERVENTION/BACKUP RESTORE NECESSARY.`,
|
||||
{
|
||||
err,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// remove the stale migration so it can be re-ran in the future.
|
||||
await db.migrations.findOneAndDelete({ migrationID });
|
||||
|
||||
if (Env.NODE_ENV === "test") {
|
||||
throw new Error("Was going to exit with statusCode 1, but we're in testing.");
|
||||
}
|
||||
|
||||
logger.crit(`Exiting.`, () => {
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "add-following-to-users",
|
||||
up: async () => {
|
||||
await db["user-settings"].update(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
following: [],
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: async () => {
|
||||
await db["user-settings"].update(
|
||||
{},
|
||||
{
|
||||
$unset: { following: 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "add-lockedat",
|
||||
up: async () => {
|
||||
await db["import-locks"].update(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
lockedAt: null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: async () => {
|
||||
await db["import-locks"].update(
|
||||
{},
|
||||
{
|
||||
$unset: {
|
||||
lockedAt: 1,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "add-preferredRanking-to-ugpt",
|
||||
up: async () => {
|
||||
await db["game-settings"].update(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
"preferences.preferredRanking": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: async () => {
|
||||
await db["game-settings"].update(
|
||||
{},
|
||||
{
|
||||
$unset: { "preferences.preferredRanking": 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "add-rivals-to-ugpt",
|
||||
up: async () => {
|
||||
await db["game-settings"].update(
|
||||
{},
|
||||
{
|
||||
$set: {
|
||||
rivals: [],
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: async () => {
|
||||
await db["game-settings"].update(
|
||||
{},
|
||||
{
|
||||
$unset: { rivals: 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "chunithm-fix-preferred-default-enum",
|
||||
up: async () => {
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
game: "chunithm",
|
||||
"preferences.preferredDefaultEnum": "lamp",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.preferredDefaultEnum": "noteLamp",
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,593 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs";
|
||||
import { CreateFullScoreData } from "#lib/score-import/framework/score-importing/derivers";
|
||||
import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id";
|
||||
import { CreateGoalID } from "#lib/targets/goals";
|
||||
|
||||
import type { GetEnumValue } from "../../../../../common/src/types/metrics";
|
||||
|
||||
import {
|
||||
GetGPTString,
|
||||
type GoalDocument,
|
||||
type GoalSubscriptionDocument,
|
||||
type integer,
|
||||
type ScoreData,
|
||||
type ScoreDocument,
|
||||
} from "../../../../../common/src";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db, { monkDB } from "#services/mongo/db";
|
||||
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const OLD_LAMP_INDEXES = {
|
||||
FAILED: 0,
|
||||
CLEAR: 1,
|
||||
"FULL COMBO": 2,
|
||||
"ALL JUSTICE": 3,
|
||||
"ALL JUSTICE CRITICAL": 4,
|
||||
} as const;
|
||||
|
||||
const NEW_CLEAR_LAMP_INDEXES = {
|
||||
FAILED: 0,
|
||||
CLEAR: 1,
|
||||
HARD: 2,
|
||||
BRAVE: 3,
|
||||
ABSOLUTE: 4,
|
||||
CATASTROPHY: 5,
|
||||
} as const;
|
||||
|
||||
const NEW_CLEAR_LAMPS = ["FAILED", "CLEAR", "HARD", "BRAVE", "ABSOLUTE", "CATASTROPHY"] as const;
|
||||
|
||||
const NEW_NOTE_LAMP_INDEXES = {
|
||||
NONE: 0,
|
||||
"FULL COMBO": 1,
|
||||
"ALL JUSTICE": 2,
|
||||
"ALL JUSTICE CRITICAL": 3,
|
||||
} as const;
|
||||
|
||||
const NEW_NOTE_LAMPS = ["NONE", "FULL COMBO", "ALL JUSTICE", "ALL JUSTICE CRITICAL"] as const;
|
||||
|
||||
async function FastUpdateSessions() {
|
||||
const sessions = await db.sessions.find({ game: "chunithm", playtype: "Single" });
|
||||
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
|
||||
.get("temp-update-map")
|
||||
.find({});
|
||||
const lookup = new Map(newScoreIDRefs.map((r) => [r.old, r.new]));
|
||||
const newScoreIDs = new Set(newScoreIDRefs.map((r) => r.new));
|
||||
|
||||
await Promise.allSettled(
|
||||
sessions.map(async (session) => {
|
||||
const updatedScoreIDs: Array<string> = [];
|
||||
|
||||
for (const oldScoreID of session.scoreIDs) {
|
||||
const newScoreID = lookup.get(oldScoreID);
|
||||
|
||||
// If the migration died here before, we might run into sessions where the scoreIDs
|
||||
// are already the migrated ones. Therefore, we only emit errors for IDs that are
|
||||
// actually non-existent.
|
||||
if (!newScoreID && !newScoreIDs.has(oldScoreID)) {
|
||||
logger.warning(
|
||||
`[session ${session.sessionID}] No such score ${oldScoreID} exists in lookup, nor is it a new scoreID. Ignoring this score.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
updatedScoreIDs.push(newScoreID ?? oldScoreID);
|
||||
}
|
||||
|
||||
await db.sessions.update(
|
||||
{ sessionID: session.sessionID },
|
||||
{
|
||||
$set: { scoreIDs: updatedScoreIDs },
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function FastUpdateImports() {
|
||||
const imports = await db.imports.find({ game: "chunithm", playtypes: "Single" });
|
||||
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
|
||||
.get("temp-update-map")
|
||||
.find({});
|
||||
const lookup = new Map(newScoreIDRefs.map((r) => [r.old, r.new]));
|
||||
const newScoreIDs = new Set(newScoreIDRefs.map((r) => r.new));
|
||||
|
||||
await Promise.allSettled(
|
||||
imports.map(async (importDoc) => {
|
||||
const updatedScoreIDs: Array<string> = [];
|
||||
|
||||
for (const oldScoreID of importDoc.scoreIDs) {
|
||||
const newScoreID = lookup.get(oldScoreID);
|
||||
|
||||
// If the migration died here before, we might run into sessions where the scoreIDs
|
||||
// are already the migrated ones. Therefore, we only emit errors for IDs that are
|
||||
// actually non-existent.
|
||||
if (!newScoreID && !newScoreIDs.has(oldScoreID)) {
|
||||
logger.warning(
|
||||
`[import ${importDoc.importID}] No such score ${oldScoreID} exists in lookup, nor is it a new scoreID. Ignoring this score.`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
updatedScoreIDs.push(newScoreID ?? oldScoreID);
|
||||
}
|
||||
|
||||
await db.imports.update(
|
||||
{ importID: importDoc.importID },
|
||||
{
|
||||
$set: { scoreIDs: updatedScoreIDs },
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function MoveLamps(
|
||||
scoreData: { lamp?: string } & ScoreData<"chunithm:Single">,
|
||||
): ScoreData<"chunithm:Single"> {
|
||||
// This score has already been migrated.
|
||||
|
||||
if (scoreData.clearLamp && scoreData.noteLamp && !scoreData.lamp) {
|
||||
return scoreData;
|
||||
}
|
||||
|
||||
const oldLamp = scoreData.lamp;
|
||||
|
||||
let noteLamp: GetEnumValue<"chunithm:Single", "noteLamp">;
|
||||
let clearLamp: GetEnumValue<"chunithm:Single", "clearLamp">;
|
||||
|
||||
// If the old score had a combo lamp, we assume that the score was at least a clear.
|
||||
// While it is theoretically possible for there to be a failed all justice, the chance
|
||||
// is small.
|
||||
if (oldLamp === "FAILED" || oldLamp === "CLEAR") {
|
||||
noteLamp = "NONE";
|
||||
clearLamp = oldLamp;
|
||||
} else if (
|
||||
oldLamp === "FULL COMBO" ||
|
||||
oldLamp === "ALL JUSTICE" ||
|
||||
oldLamp === "ALL JUSTICE CRITICAL"
|
||||
) {
|
||||
noteLamp = oldLamp;
|
||||
clearLamp = "CLEAR";
|
||||
} else {
|
||||
noteLamp = "NONE";
|
||||
clearLamp = "FAILED";
|
||||
}
|
||||
|
||||
const newScoreData = {
|
||||
...scoreData,
|
||||
noteLamp,
|
||||
clearLamp,
|
||||
};
|
||||
|
||||
delete newScoreData.lamp;
|
||||
|
||||
return newScoreData;
|
||||
}
|
||||
|
||||
const migration: Migration = {
|
||||
id: "chunithm-lamp-split",
|
||||
up: async () => {
|
||||
logger.info("Migrating CHUNITHM scores to new lamps...");
|
||||
const gptString = GetGPTString("chunithm", "Single") as "chunithm:Single";
|
||||
|
||||
await EfficientDBIterate(
|
||||
// @ts-expect-error filter assures we're getting only chuni scores
|
||||
db.scores,
|
||||
(score: { scoreData: { lamp?: string } } & ScoreDocument<"chunithm:Single">) => {
|
||||
// @ts-expect-error it does exist sometimes
|
||||
delete score._id;
|
||||
|
||||
const newScore = {
|
||||
...score,
|
||||
scoreData: MoveLamps(score.scoreData),
|
||||
};
|
||||
const newScoreID = CreateScoreID(
|
||||
gptString,
|
||||
newScore.userID,
|
||||
newScore,
|
||||
newScore.chartID,
|
||||
);
|
||||
|
||||
// To make this faster, we update only the relevant parts of the scores instead
|
||||
// of having it go through the UpdateScore process, which requires querying
|
||||
// a chart from the database every UpdateScore call.
|
||||
//
|
||||
// We skip calculating calculatedData since that is outside the scope
|
||||
// of this lamp move; no CHUNITHM calculated metrics depend on lamp.
|
||||
newScore.scoreID = newScoreID;
|
||||
newScore.scoreData = CreateFullScoreData(
|
||||
gptString,
|
||||
newScore.scoreData,
|
||||
// @ts-expect-error This is intentional, CHUNITHM derivers do not
|
||||
// depend on the chart.
|
||||
null,
|
||||
logger,
|
||||
);
|
||||
|
||||
return {
|
||||
oldScoreID: score.scoreID,
|
||||
newScore,
|
||||
};
|
||||
},
|
||||
async (changes) => {
|
||||
const changesPrime = [];
|
||||
|
||||
for (const change of changes) {
|
||||
const scoreExists = await db.scores.findOne({
|
||||
scoreID: change.newScore.scoreID,
|
||||
});
|
||||
|
||||
if (scoreExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changesPrime.push(change);
|
||||
}
|
||||
|
||||
await db.scores.bulkWrite(
|
||||
changesPrime.map((c) => ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
scoreID: c.oldScoreID,
|
||||
},
|
||||
update: {
|
||||
$set: c.newScore,
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
await monkDB.get("temp-update-map").insert(
|
||||
changes.map((c) => ({
|
||||
old: c.oldScoreID,
|
||||
new: c.newScore.scoreID,
|
||||
})),
|
||||
);
|
||||
},
|
||||
{
|
||||
game: "chunithm",
|
||||
playtype: "Single",
|
||||
"scoreData.noteLamp": { $exists: false },
|
||||
"scoreData.clearLamp": { $exists: false },
|
||||
},
|
||||
);
|
||||
|
||||
logger.info("Reconstructing PBs...");
|
||||
const usersWithChuniScores = await db.scores.distinct("userID", {
|
||||
game: "chunithm",
|
||||
playtype: "Single",
|
||||
});
|
||||
|
||||
// We don't use UpdateAllPBs here because it re-PBs all of a user's
|
||||
// games and playtypes which is fairly wasteful.
|
||||
for (const userID of usersWithChuniScores) {
|
||||
const chartIDs = await db.scores.distinct("chartID", {
|
||||
userID,
|
||||
game: "chunithm",
|
||||
playtype: "Single",
|
||||
});
|
||||
|
||||
if (chartIDs.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`PBing #${userID}'s scores.`);
|
||||
|
||||
await ProcessPBs("chunithm", "Single", userID, new Set(chartIDs), logger);
|
||||
}
|
||||
|
||||
logger.info("Updating references to scores...");
|
||||
await FastUpdateSessions();
|
||||
await FastUpdateImports();
|
||||
|
||||
await monkDB.get("temp-update-map").drop();
|
||||
|
||||
logger.info("Updating blacklisted scores...");
|
||||
await EfficientDBIterate(
|
||||
// @ts-expect-error this works because of the filter restricting game and playtype
|
||||
db["score-blacklist"],
|
||||
(blacklistedScore: {
|
||||
score: ScoreDocument<"chunithm:Single">;
|
||||
scoreID: string;
|
||||
userID: integer;
|
||||
}) => {
|
||||
const newScore = {
|
||||
...blacklistedScore.score,
|
||||
scoreData: MoveLamps(blacklistedScore.score.scoreData),
|
||||
};
|
||||
const newScoreID = CreateScoreID(
|
||||
gptString,
|
||||
blacklistedScore.userID,
|
||||
newScore,
|
||||
newScore.chartID,
|
||||
);
|
||||
|
||||
newScore.scoreID = newScoreID;
|
||||
newScore.scoreData = CreateFullScoreData(
|
||||
gptString,
|
||||
newScore.scoreData,
|
||||
// @ts-expect-error This is intentional. CHUNITHM derivers
|
||||
// do not need a chart.
|
||||
null,
|
||||
logger,
|
||||
);
|
||||
|
||||
return {
|
||||
oldScoreID: blacklistedScore.scoreID,
|
||||
newScoreID,
|
||||
newScore,
|
||||
};
|
||||
},
|
||||
async (changes) => {
|
||||
await db["score-blacklist"].bulkWrite(
|
||||
changes
|
||||
.filter((c) => c.oldScoreID !== c.newScoreID)
|
||||
.map((c) => ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
scoreID: c.oldScoreID,
|
||||
},
|
||||
update: {
|
||||
$set: {
|
||||
scoreID: c.newScoreID,
|
||||
score: c.newScore,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
},
|
||||
{
|
||||
"score.game": "chunithm",
|
||||
"score.playtype": "Single",
|
||||
"score.scoreData.noteLamp": { $exists: false },
|
||||
"score.scoreData.clearLamp": { $exists: false },
|
||||
},
|
||||
);
|
||||
|
||||
logger.info("Updating stat showcases...");
|
||||
await db["game-settings"].bulkWrite([
|
||||
...(["FULL COMBO", "ALL JUSTICE", "ALL JUSTICE CRITICAL"] as const).map((lamp) => ({
|
||||
updateMany: {
|
||||
filter: { game: "chunithm", playtype: "Single" } as const,
|
||||
update: {
|
||||
$set: {
|
||||
"preferences.stats.$[e].metric": "noteLamp",
|
||||
"preferences.stats.$[e].gte": NEW_NOTE_LAMP_INDEXES[lamp],
|
||||
},
|
||||
},
|
||||
arrayFilters: [
|
||||
{
|
||||
"e.mode": "folder",
|
||||
"e.metric": "lamp",
|
||||
"e.gte": OLD_LAMP_INDEXES[lamp],
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
...(["FAILED", "CLEAR"] as const).map((lamp) => ({
|
||||
updateMany: {
|
||||
filter: { game: "chunithm", playtype: "Single" } as const,
|
||||
update: {
|
||||
$set: {
|
||||
"preferences.stats.$[e].metric": "clearLamp",
|
||||
"preferences.stats.$[e].gte": NEW_CLEAR_LAMP_INDEXES[lamp],
|
||||
},
|
||||
},
|
||||
arrayFilters: [
|
||||
{
|
||||
"e.mode": "folder",
|
||||
"e.metric": "lamp",
|
||||
"e.gte": OLD_LAMP_INDEXES[lamp],
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
// Unfortunately we don't have enough data to accurately migrate
|
||||
// over chart lamp showcases, so we'll automatically migrate everyone
|
||||
// over to noteLamp, which is what people usually showcase.
|
||||
{
|
||||
updateMany: {
|
||||
filter: { game: "chunithm", playtype: "Single" } as const,
|
||||
update: {
|
||||
$set: {
|
||||
"preferences.stats.$[e].metric": "noteLamp",
|
||||
},
|
||||
},
|
||||
arrayFilters: [
|
||||
{
|
||||
"e.mode": "chart",
|
||||
"e.metric": "lamp",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
logger.info("Updating goals...");
|
||||
await EfficientDBIterate(
|
||||
db.goals,
|
||||
(goal: GoalDocument) => {
|
||||
const oldGoalID = goal.goalID;
|
||||
|
||||
if (goal.criteria.value <= OLD_LAMP_INDEXES.CLEAR) {
|
||||
goal.criteria.key = "clearLamp";
|
||||
// goal.criteria.value remains unchanged
|
||||
} else {
|
||||
goal.criteria.key = "noteLamp";
|
||||
|
||||
// old lamps have FC = 2, AJ = 3, AJC = 4
|
||||
// new noteLamps have FC = 1, AJ = 2, AJC = 3
|
||||
goal.criteria.value = goal.criteria.value - 1;
|
||||
}
|
||||
|
||||
const newGoalID = CreateGoalID(
|
||||
goal.charts,
|
||||
goal.criteria,
|
||||
goal.game,
|
||||
goal.playtype,
|
||||
);
|
||||
|
||||
// @ts-expect-error it exists
|
||||
delete goal._id;
|
||||
|
||||
return {
|
||||
oldGoalID,
|
||||
newGoalID,
|
||||
goal,
|
||||
};
|
||||
},
|
||||
async (changes) => {
|
||||
await db.goals.bulkWrite(
|
||||
changes.map((c) => ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
goalID: c.oldGoalID,
|
||||
},
|
||||
update: {
|
||||
$set: {
|
||||
goalID: c.newGoalID,
|
||||
criteria: c.goal.criteria,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
await monkDB.get("temp-update-goal-map").insert(
|
||||
changes.map((c) => ({
|
||||
old: c.oldGoalID,
|
||||
new: c.newGoalID,
|
||||
goal: c.goal,
|
||||
})),
|
||||
);
|
||||
},
|
||||
{ game: "chunithm", playtype: "Single", "criteria.key": "lamp" },
|
||||
);
|
||||
|
||||
logger.info("Updating goal subscriptions...");
|
||||
const updatedGoalIDs: Array<{ goal: GoalDocument; new: string; old: string }> = await monkDB
|
||||
.get("temp-update-goal-map")
|
||||
.find({});
|
||||
const updatedGoalIDMap = new Map(updatedGoalIDs.map((u) => [u.old, u]));
|
||||
|
||||
await EfficientDBIterate(
|
||||
db["goal-subs"],
|
||||
(subscription: GoalSubscriptionDocument) => {
|
||||
// We're 100% sure the goal ID exists in updatedGoals due to the query.
|
||||
|
||||
const { goal, new: newGoalID } = updatedGoalIDMap.get(subscription.goalID)!;
|
||||
|
||||
if (goal.criteria.key !== "clearLamp" && goal.criteria.key !== "noteLamp") {
|
||||
throw new Error(`Received invalid criteria key ${goal.criteria.key}`);
|
||||
}
|
||||
|
||||
// We don't update goal progress for goals that are "absolute" | "proportion"
|
||||
// since the progress is the number of items already done, and not the lamp.
|
||||
// Simply migrate them over to the new goal ID.
|
||||
if (goal.criteria.mode !== "single") {
|
||||
return {
|
||||
userID: subscription.userID,
|
||||
goalID: subscription.goalID,
|
||||
newGoalID,
|
||||
progress: subscription.progress,
|
||||
progressHuman: subscription.progressHuman,
|
||||
outOf: subscription.outOf,
|
||||
outOfHuman: subscription.outOfHuman,
|
||||
};
|
||||
}
|
||||
|
||||
let newProgress: integer | null;
|
||||
let newProgressHuman: string | undefined;
|
||||
const newOutOfHuman =
|
||||
goal.criteria.key === "clearLamp"
|
||||
? NEW_CLEAR_LAMPS[goal.criteria.value]
|
||||
: NEW_NOTE_LAMPS[goal.criteria.value];
|
||||
|
||||
if (!newOutOfHuman) {
|
||||
logger.severe(`Received invalid criteria value for a goal of mode "single".`, {
|
||||
mode: goal.criteria.mode,
|
||||
key: goal.criteria.key,
|
||||
value: goal.criteria.value,
|
||||
});
|
||||
throw new Error(
|
||||
`Received invalid progress for a goal of mode "single". criteria_mode=${goal.criteria.mode} criteria_key=${goal.criteria.key} criteria_value=${goal.criteria.value}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (subscription.progress === null) {
|
||||
newProgress = null;
|
||||
newProgressHuman = "NO DATA";
|
||||
} else if (goal.criteria.key === "clearLamp") {
|
||||
// Clamp back to CLEAR if the user has made better progress (e.g. FULL COMBO).
|
||||
newProgress = Math.min(NEW_CLEAR_LAMP_INDEXES.CLEAR, subscription.progress);
|
||||
newProgressHuman = NEW_CLEAR_LAMPS[newProgress];
|
||||
} else {
|
||||
// Clamp back to NONE if the user has made worse progress (e.g. FAILED).
|
||||
// Otherwise, the -1 maps the old progress onto the new progress nicely:
|
||||
// CLEAR: 1 -> NONE: 0
|
||||
// FULL COMBO: 2 -> FULL COMBO: 1
|
||||
// ...
|
||||
newProgress = Math.max(NEW_NOTE_LAMP_INDEXES.NONE, subscription.progress - 1);
|
||||
newProgressHuman = NEW_NOTE_LAMPS[newProgress];
|
||||
}
|
||||
|
||||
if (!newProgressHuman) {
|
||||
logger.severe(`Received invalid progress for a goal of mode "single".`, {
|
||||
criteria: {
|
||||
mode: goal.criteria.mode,
|
||||
key: goal.criteria.key,
|
||||
},
|
||||
progress: subscription.progress,
|
||||
newProgress,
|
||||
});
|
||||
throw new Error(
|
||||
`Received invalid progress for a goal of mode "single". criteria_key=${goal.criteria.key} criteria_mode=${goal.criteria.mode} progress=${subscription.progress} newProgress=${newProgress}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
userID: subscription.userID,
|
||||
goalID: subscription.goalID,
|
||||
newGoalID,
|
||||
progress: newProgress,
|
||||
progressHuman: newProgressHuman,
|
||||
outOf: goal.criteria.value,
|
||||
outOfHuman: newOutOfHuman,
|
||||
};
|
||||
},
|
||||
async (updates) => {
|
||||
await db["goal-subs"].bulkWrite(
|
||||
updates.map((u) => ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
userID: u.userID,
|
||||
goalID: u.goalID,
|
||||
},
|
||||
update: {
|
||||
$set: {
|
||||
goalID: u.newGoalID,
|
||||
progress: u.progress,
|
||||
progressHuman: u.progressHuman,
|
||||
outOf: u.outOf,
|
||||
outOfHuman: u.outOfHuman,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
},
|
||||
{ goalID: { $in: updatedGoalIDs.map((u) => u.old) } },
|
||||
);
|
||||
|
||||
await monkDB.get("temp-update-goal-map").drop();
|
||||
},
|
||||
down: () => {
|
||||
throw new Error("Reverting this change is not possible.");
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "fix-undefined-bms-data",
|
||||
up: async () => {
|
||||
await db.songs.bms.update(
|
||||
{
|
||||
"data.tableString": { $exists: false },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"data.tableString": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db.charts.bms.update(
|
||||
{
|
||||
"data.aiLevel": { $exists: false },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"data.aiLevel": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Cannot revert migration.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,47 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import UpdateScore from "#lib/score-mutation/update-score";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
// old chart -> new chart
|
||||
const mappings = [
|
||||
["4ff8e32a6eefee5e476f6c8261db5c66e2496b51", "1de27f681eb00b8d422658b66507c5f438379e40"],
|
||||
["827b1f8cccce51d3e2e0fd2c0b1117a47b530c70", "96bf5cd64056bca7e870233eb1096e8bb24d24b7"],
|
||||
["43a7b6386da6b0c5d2d577c9f09ab83d516038a2", "764f6c14f4be643fe4d6e7715a77d4f5789a255e"],
|
||||
["ad0a143262671e970c3e148b62d4aaebec17f604", "2981d53beda743071c0174b3235e8b811c227231"],
|
||||
["69093ac77208326f1261b3dcc38f73d054b0400e", "f7fb763afd99981232c92a41a6c3276692551101"],
|
||||
["2f8bac0550e95c87f4ff4c931c49d118622d320d", "1d4025230032b8ee6ce787e80756286a1626dc04"],
|
||||
["2b2dc7bc90eb7715fda937a263878dc16c3df507", "78f3fcd2765233c1d588ea588d1416a45065b272"],
|
||||
["3cf992f0615ab51903d45fda6d4fda5ed5ec5c7c", "9b6eac3a3f152a43e3ef297130677daa42d895fd"],
|
||||
["fb4a6ca054b9697a34841afc52f52d7d100a5779", "6264bb33e8ab53e226cd514696789dd330ab13fc"],
|
||||
["4ee3a65c0ab0968ed3c52bf90753cfa7620fee94", "03efef860d7aa122dd601853d99b73622b560aa6"],
|
||||
["28c1ca0d1ef50f56d05e3143a1cde25e9b25a1ef", "1e112b872267c0b66fb64671ef2c8aedff8bcf7e"],
|
||||
["1cdb02868c5a23ca4c3131f9cf0403b7ef178b83", "bbe64b3d1158db2f1255eb342bafe165ba2871e5"],
|
||||
["29d7c72e8ed01173f7f68fb8102706694ba80619", "9d96ec36c37f7f723d3851618e7daef6d58a5176"],
|
||||
["4d3d3ae0760c520b471a6c4025fd4517e792fdd5", "e42f30c04bcdd07ccf46470c8ce321814c69f62f"],
|
||||
["5d2b11fc4d26b965dd30094c06548a55a670408c", "ee22f91a9be8826ef3b371ef9c3ab518d34753b0"],
|
||||
["46d3a5c245898f8fbc2492e293a8dadcfaff363c", "92efb4e0d1b036e385cb5a73a0ace0e12baaa0cc"],
|
||||
["39ed6d4a5c7a4557ab00266fae2363a6094fc0e1", "02218c7580fffe984d5db15e3ac9703c7c5ff42c"],
|
||||
["6e5231055e517dbe6f67d68f572286b1c647eceb", "ef2359505d0be7492dacc4b416d7528f669b4228"],
|
||||
] as const;
|
||||
|
||||
const migration: Migration = {
|
||||
id: "join-inf-casthour-charts",
|
||||
up: async () => {
|
||||
for (const [oldChartID, newChartID] of mappings) {
|
||||
const scores = await db.scores.find({
|
||||
chartID: oldChartID,
|
||||
});
|
||||
|
||||
for (const score of scores) {
|
||||
await UpdateScore(score, { ...score, chartID: newChartID });
|
||||
}
|
||||
}
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Cannot undo migration.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import UpdateScore from "#lib/score-mutation/update-score";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
// old songID -> new songID
|
||||
const mappings = [
|
||||
[2100, 2042],
|
||||
[2130, 1997],
|
||||
[2142, 2000],
|
||||
] as const;
|
||||
|
||||
// i messed up the last migration, by only updating the chartID and not the song ID.
|
||||
|
||||
const migration: Migration = {
|
||||
id: "join-inf-casthour-songs",
|
||||
up: async () => {
|
||||
for (const [oldSongID, newSongID] of mappings) {
|
||||
const scores = await db.scores.find({
|
||||
songID: oldSongID,
|
||||
});
|
||||
|
||||
for (const score of scores) {
|
||||
await UpdateScore(score, {
|
||||
...score,
|
||||
songID: newSongID,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Cannot undo migration.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "ktRating-to-curator-skill",
|
||||
up: async () => {
|
||||
await RecalcAllScores({ game: "museca" });
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Not possible to revert.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,106 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { ProcessPBs } from "#lib/score-import/framework/pb/process-pbs";
|
||||
import UpdateScore from "#lib/score-mutation/update-score";
|
||||
import db from "#services/mongo/db";
|
||||
import { RecalcAllScores } from "#utils/calculations/recalc-scores";
|
||||
|
||||
import type { integer, ProvidedMetrics, ScoreDocument } from "../../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const migration: Migration = {
|
||||
id: "maimaidx-fix-lamps",
|
||||
up: async () => {
|
||||
const affectedUserCharts: Map<integer, Set<string>> = new Map();
|
||||
const affectedCharts: Set<string> = new Set();
|
||||
|
||||
// @ts-expect-error query assures we're getting maimaidx:Single scores
|
||||
const invalidScores: Array<ScoreDocument<"maimaidx:Single">> = await db.scores.find({
|
||||
game: "maimaidx",
|
||||
playtype: "Single",
|
||||
"scoreData.lamp": "FAILED",
|
||||
"scoreData.percent": { $gte: 80 },
|
||||
});
|
||||
|
||||
invalidScores.push(
|
||||
// @ts-expect-error query assures we're getting maimaidx:Single scores
|
||||
...(await db.scores.find({
|
||||
game: "maimaidx",
|
||||
playtype: "Single",
|
||||
"scoreData.lamp": "CLEAR",
|
||||
"scoreData.percent": { $lt: 80 },
|
||||
})),
|
||||
);
|
||||
|
||||
for (const score of invalidScores) {
|
||||
// @ts-expect-error just in case
|
||||
delete score._id;
|
||||
|
||||
if (affectedUserCharts.has(score.userID)) {
|
||||
affectedUserCharts.get(score.userID)!.add(score.chartID);
|
||||
} else {
|
||||
affectedUserCharts.set(score.userID, new Set([score.chartID]));
|
||||
}
|
||||
|
||||
affectedCharts.add(score.chartID);
|
||||
|
||||
let lamp: ProvidedMetrics["maimaidx:Single"]["lamp"];
|
||||
|
||||
if (score.scoreData.percent === 101) {
|
||||
lamp = "ALL PERFECT+";
|
||||
} else if (
|
||||
score.scoreData.percent >= 100.5 &&
|
||||
score.scoreData.judgements.great === 0 &&
|
||||
score.scoreData.judgements.good === 0 &&
|
||||
score.scoreData.judgements.miss === 0
|
||||
) {
|
||||
lamp = "ALL PERFECT";
|
||||
} else if (
|
||||
score.scoreData.judgements.good === 0 &&
|
||||
score.scoreData.judgements.miss === 0
|
||||
) {
|
||||
lamp = "FULL COMBO+";
|
||||
} else if (score.scoreData.judgements.miss === 0) {
|
||||
lamp = "FULL COMBO";
|
||||
} else if (score.scoreData.percent >= 80) {
|
||||
lamp = "CLEAR";
|
||||
} else {
|
||||
lamp = "FAILED";
|
||||
}
|
||||
|
||||
await UpdateScore(
|
||||
score,
|
||||
{
|
||||
...score,
|
||||
scoreData: {
|
||||
...score.scoreData,
|
||||
lamp,
|
||||
},
|
||||
},
|
||||
/* updateOldChart=*/ false,
|
||||
/* skipUpdatingPBs=*/ true,
|
||||
);
|
||||
}
|
||||
|
||||
for (const [userID, chartIDs] of affectedUserCharts.entries()) {
|
||||
logger.info(`PBing #${userID}'s scores.`);
|
||||
|
||||
await ProcessPBs("maimaidx", "Single", userID, chartIDs, logger);
|
||||
}
|
||||
|
||||
// maimai DX CiRCLE rating recalc
|
||||
await RecalcAllScores({
|
||||
game: "maimaidx",
|
||||
"scoreData.lamp": {
|
||||
$in: ["ALL PERFECT", "ALL PERFECT+"],
|
||||
},
|
||||
});
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Unable to revert transaction.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "mark-fervidex-as-without-intent",
|
||||
up: async () => {
|
||||
await db.imports.update(
|
||||
{
|
||||
importType: "ir/fervidex",
|
||||
},
|
||||
{
|
||||
$set: { userIntent: false },
|
||||
},
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Unable to revert transaction.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "mark-kshook-as-without-intent",
|
||||
up: async () => {
|
||||
await db.imports.update(
|
||||
{
|
||||
importType: { $in: ["ir/kshook-sv6c", "ir/kshook-sv6c-static"] },
|
||||
},
|
||||
{
|
||||
$set: { userIntent: false },
|
||||
},
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Unable to revert transaction.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import { UpdateAllPBs } from "#utils/calculations/recalc-scores";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "null-lr2hook-failed-bps",
|
||||
up: async () => {
|
||||
await db.scores.update(
|
||||
{
|
||||
importType: "ir/lr2hook",
|
||||
"scoreMeta.gauge": "HARD",
|
||||
lamp: { $in: ["FAILED", "NO PLAY"] },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"scoreData.optional.bp": null,
|
||||
},
|
||||
},
|
||||
{
|
||||
multi: true,
|
||||
},
|
||||
);
|
||||
|
||||
await UpdateAllPBs(undefined, { game: "bms" });
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Unable to revert transaction, some scores may have been nulled.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { rootLogger } from "#lib/logger/logger";
|
||||
import UpdateScore from "#lib/score-mutation/update-score";
|
||||
import db from "#services/mongo/db";
|
||||
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
|
||||
|
||||
import { ONGEKI_NOTE_LAMPS, type ScoreDocument } from "../../../../../common/src";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "ongeki-v2",
|
||||
up: async () => {
|
||||
const failedScores = [];
|
||||
|
||||
await EfficientDBIterate(
|
||||
db.scores,
|
||||
(score) => score as ScoreDocument<"ongeki:Single">,
|
||||
async (scores: Array<ScoreDocument<"ongeki:Single">>) => {
|
||||
for (const score of scores) {
|
||||
// @ts-expect-error just incase
|
||||
delete score._id;
|
||||
|
||||
try {
|
||||
const newScore = {
|
||||
...score,
|
||||
};
|
||||
|
||||
if (newScore.scoreData.score === 1010000) {
|
||||
newScore.scoreData.noteLamp = "ALL BREAK+";
|
||||
newScore.scoreData.enumIndexes.noteLamp =
|
||||
ONGEKI_NOTE_LAMPS.ALL_BREAK_PLUS;
|
||||
}
|
||||
|
||||
if (
|
||||
"platScore" in newScore.scoreData.optional &&
|
||||
typeof newScore.scoreData.optional.platScore === "number"
|
||||
) {
|
||||
newScore.scoreData.platinumScore =
|
||||
newScore.scoreData.optional.platScore;
|
||||
delete newScore.scoreData.optional.platScore;
|
||||
} else {
|
||||
newScore.scoreData.platinumScore ??= 0;
|
||||
}
|
||||
|
||||
await UpdateScore(score, newScore);
|
||||
} catch (err) {
|
||||
rootLogger.warn(`Error applying ${score.scoreID}:`);
|
||||
rootLogger.warn(err);
|
||||
rootLogger.warn("Continuing through the error.");
|
||||
|
||||
failedScores.push(score.scoreID);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ game: "ongeki" },
|
||||
);
|
||||
|
||||
if (failedScores.length > 0) {
|
||||
throw new Error(
|
||||
`${failedScores.length} failed to be migrated. Resolve these manually, please.`,
|
||||
);
|
||||
}
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-above-100pc",
|
||||
up: async () => {
|
||||
const toDelete = await db.scores.find({
|
||||
game: "bms",
|
||||
"scoreData.percent": { $gt: 100.0 },
|
||||
});
|
||||
|
||||
await DeleteMultipleScores(toDelete);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,316 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-iidx-beginners",
|
||||
up: async () => {
|
||||
// copied from seeds before they were removed
|
||||
// the charts themselves are not guaranteed to be in the db
|
||||
// so they have to be hardcoded.
|
||||
const beginners = [
|
||||
"71865a2b6d3581decf076ae83c6621302c4bb271",
|
||||
"fc143b488d1af894fdd0c5be145e1d613aaf5214",
|
||||
"5e7128485017b67e85cc75432326c9656a33f310",
|
||||
"5798dd1c2a16e6ef3e4469f7a2bdea8a7bc1cd8f",
|
||||
"dafa3f6511a3150015ffaaac3f4a008aacc0ebe1",
|
||||
"62c5cb9228f39e2d6d222ecf84aa709c5e2271ce",
|
||||
"e852f83964486ef81558b84dd4b554ffc9caf174",
|
||||
"963edf351b3c6f29c09c54e55ec2c3dd041340a9",
|
||||
"17514921a23abf2b76983cf50e79b3ff600763b1",
|
||||
"950c0181ee33d10d3af448c00657702d1a479b8c",
|
||||
"7e70836d0a3001aa7bd5e9488660f31746be60d8",
|
||||
"888259e7cc44e2bf2e032898f6a9e85cd4fcad1b",
|
||||
"02aea8a2135311a7a1f9404e6a12f138ecfae092",
|
||||
"87b65dbf2965949644c72515a202bb4363662b6b",
|
||||
"d1e92965be52b5e887089383e0a687d20098e5b4",
|
||||
"139d5851baf82d75731d9629a25fdee2b75d41c5",
|
||||
"d903739755a1de064e646280260f7dabe16fbc2e",
|
||||
"48ee9417cbc4ebad782d372d63548085f65ed660",
|
||||
"d8ee3c7e42227d9f660598508531e30a990bae39",
|
||||
"2a2406e6c5236448615f177eb5ac830c353bde12",
|
||||
"fa424066e32730fe29faad0b0d25aa6323ea6129",
|
||||
"b38485e92c09f6736ab7e1737c0ed7a191fcdba8",
|
||||
"241a27876b22646ccb85b6f53a61d7a46fc93168",
|
||||
"0f39a16ff18d509f44dcf4e37850269477475e77",
|
||||
"1a44bf2a27033a89af7dad853d846c7bba9f9a48",
|
||||
"c94536933f21c0ceb2f0b68b4b4bc35849f1d04b",
|
||||
"4dd7495c32b560f50ad54bec6753c010feb9381c",
|
||||
"46b18a37648c06bf480487c2b62f5caa8995281a",
|
||||
"2a9b158e5974fb2f8707843493099b7fc728a320",
|
||||
"db81c35d160fc5870f5957361a8b32add1bc318b",
|
||||
"d399af231b377289e1d575337a43609c17998e35",
|
||||
"e9ca3d286a330bb4ce29e89334c465c243af29c2",
|
||||
"794ae8f3b8b1b62d0ff9f32c890bd5ccb887d0a0",
|
||||
"c22c1a520f7dd87be4d1ba1c1ec59a4d572951ff",
|
||||
"55471eca15caaa14b6dd9f4eaa7bcede2b6f8e7b",
|
||||
"9f8deae94e16a9b075a8a5290a14df5bb1d349cc",
|
||||
"5716dddd0316b932fd8b3a9c530cd624c1c50bdf",
|
||||
"4eafaf1009423849c3709936e1a0b361a0c5e530",
|
||||
"3f4cda22aa09aad1959993570447e107b6d3cf4c",
|
||||
"b194cb8b2aa80189567a7bab0949d067792a9086",
|
||||
"498b6d1388ce9f9672ba038a6b4b02aae4c8cc4c",
|
||||
"24b4c560ee3daa9a286cca100246b3dc06e96cf0",
|
||||
"5770f819940914328b74fc7616b69562285eed11",
|
||||
"6b0831dbde8a52ae377be6aaddea37ec0771a49f",
|
||||
"2f92bf3627e07e836a5d68aa538f33b3563ed167",
|
||||
"75373c0c4d1dfef62de50bbb25085f729a20a72a",
|
||||
"0cca3d6b4361195e883c79017e9850bc767b4f22",
|
||||
"dcd047a218e17011c7ab340236af62be64893781",
|
||||
"d821202823731335567f32730bfed70d5bfd1368",
|
||||
"439bb68fc96706a86a98d437e4f03e032597bc7c",
|
||||
"79c7ad40e434a5bc0cb139c9e6e0ad06730e9ea1",
|
||||
"2d549a71666934fcf86d9d827c5f2f6959d49fa7",
|
||||
"ccb99f9d00ae04b7a0fd0754c9b3024ec527f718",
|
||||
"21bcddc1191acaf1911fc173d23eb81c939bbe2e",
|
||||
"10a78c2b7ecf9f5f9c3a11f2385977dc789fbb4e",
|
||||
"ec7d6764c543dccfbe097a014b6b8839000b76de",
|
||||
"be19c987cdc067369151829a9889bcae2799fe95",
|
||||
"b382c1cf9732e132b2fb7800d2d82a93b2497789",
|
||||
"c70cf629f97a2623d5cff716185350f85b07435a",
|
||||
"5a94628384b43f68cfee44df205bc0a3edbd1913",
|
||||
"1c964098d2954f4c8d2e7b723d77a2d1f79f7317",
|
||||
"a31d41fe3bfd21c0c6e71f8d6df4e556be329123",
|
||||
"b0b3e92576de8d9090472d118c424e08d1bdd826",
|
||||
"d667a2e1f226c09e2549393240e00878a8cc4edf",
|
||||
"e1dbe5d59d4a776ecbffeb0b165c715aae2995b1",
|
||||
"fb24f2688473c46c04d666d27a9210c871a8191f",
|
||||
"cb61bd200eea0936d5278b3b28e1033c36713341",
|
||||
"bcd1fc3e53e2c1c001079159a26767a07f8e628e",
|
||||
"871db812f3ee5002266edfe7b2cf48b4b2451302",
|
||||
"01b60f5125ef5123c3840943c047446d167f3e3e",
|
||||
"10f3292e7f1c91bb9c3e7d8c7b1b43fc8d9d929f",
|
||||
"6ab72d59a8967bb6265dc82c28ac054d1f86684d",
|
||||
"2ea729f82867a2c29d34c88ef0cde22358702b00",
|
||||
"7f5b5f3e3640e2848985b85a574dffbf95a37030",
|
||||
"7c78e67927f620c06d30887e29cc26683f3748f6",
|
||||
"b34d86e91d32590d39dcb62216d1a26ea6bdfbef",
|
||||
"be31fc23b03c53aa58a3a7b599a5b8b2e7ee2ede",
|
||||
"3f5b76fcb0e9db6146e2f216771250d89bcb6942",
|
||||
"0568ca6e19a3660b74bbf6fb97453d57b8bd0c83",
|
||||
"ff1d34af1db5a089505f84a9aa2407b95a64f29b",
|
||||
"0484e8f3ab6c6dae1311ff4f2980cf76b438aaf9",
|
||||
"66624f8162c7949744e84f3ef3abb314fcb2da8e",
|
||||
"531d5a70c4b02da03ee3be0d1efcffb78fe4ef43",
|
||||
"5df850385272324559182a07d6a2a6b84bbd32bf",
|
||||
"8ea6931b5382a6460e052410990f8e7c6d2fbcbc",
|
||||
"8c20bfdaa71322bd572f38c95ffe22b8e65a9fc7",
|
||||
"fcda03d3fb068fb58c4ae4234582d313ee22eb9d",
|
||||
"c030db51ec510815e28f0ba181a650de492d2bb5",
|
||||
"f865fcc658bce3ee89e19dac88486546eb4a5648",
|
||||
"4de8a44831d3d730d24a174a3c78fa4d17271b87",
|
||||
"1dfb262a9455e010e635989e480affaf61cd0113",
|
||||
"49ac835cf5e5cd96a095df0f7220f2532a8c3632",
|
||||
"3cb97b11313657f9f59cd1ce859c6c642e7a0afe",
|
||||
"a7286b7086f8e19d78c7cc3fe73249d752fe9fb4",
|
||||
"982d79f972da4e0a98c73d7d6709b931aa9940a6",
|
||||
"1757d3c8c1f2e5392ec8aa6adb31a5a85ce1754f",
|
||||
"c14ac167a2141d14609878cb8b5a0bacaab18468",
|
||||
"876c1d284d63b272725a33d83b061e8d187bd60d",
|
||||
"7d495aad1d8e07a499e30373b44e2e1257c21281",
|
||||
"aa87f48af5a7a658e3f518f07cf5a45ea6750d5e",
|
||||
"8c88175d419658c9f0ca1ac0994bbd40675027be",
|
||||
"f98896e0dcdf7994c2ed38f452b1bf3dadd345f8",
|
||||
"20941a1f2f707ce1a3587be135e42f453230f337",
|
||||
"dbece3e1d537f91b2d0a4bfbb6d1ce98cf08a20c",
|
||||
"25c2c3b947ff45795f1aeacf257a872dad8a65a5",
|
||||
"6e071685bb2aa43878945fad560c68ce8db7c57d",
|
||||
"88a1445681b2f70f0e84c30a66737dc942a42005",
|
||||
"c32334b6f121993235082be73afb8137d0c0da19",
|
||||
"5b49b8de2b70283e2364418ae50af0586a4373c2",
|
||||
"009aa903a4b9fac40138deb8c5162280a747ae66",
|
||||
"5b4bf383e44746468c21bf800eac0ef13e72d2f4",
|
||||
"8c8917854d96a23a40b844beca846f7211c26be6",
|
||||
"e73a369bbc639f8bfca9b00ec167c772300fcae2",
|
||||
"3fb05d8b7e68c340f0257c1c405e8a9c75d03119",
|
||||
"f4af6d0da9cc5f21b8dcb447bc79dcaec0d82965",
|
||||
"06acbead44e406c41e8827186850950096ffe6e5",
|
||||
"e6b480eba3d7aaca43f05fcc243cae558f846b72",
|
||||
"91076b64d7aae8f053a9cc1b6849fbefcde68717",
|
||||
"8536c1167ee235c1c9c07740af55ef45348bc0fb",
|
||||
"275e441a1589572ce76067e7db8a51755d6133b3",
|
||||
"d13ce9d0b458d2d77ee92957a68df649a4ee723a",
|
||||
"29f7e78c0b9062cb8b12b327253a6dd0358612e1",
|
||||
"fda142a18547351ad1085c7a82fc0546a5aeba57",
|
||||
"9b7367e990ab8251597dc85b9a8ffe46284881d0",
|
||||
"53978253c493013d7e5f6cb4161cba6691414fb2",
|
||||
"719b865582fe63cc16e4a376a28296853c19ec95",
|
||||
"f41c638800df3f5ed079ed64ba1cc692e6976c49",
|
||||
"c29c03903449bbe998640c7e9db74a76ec7fe06a",
|
||||
"675ebf4acb10bffce2bf11a5d058ee22a04efc17",
|
||||
"42fe786b7af412634ab8c2d939bad0d9dc29e38e",
|
||||
"e0ea98985e05cf5f806cf8215ad3a1ee15423451",
|
||||
"72c1a989bef60be4a8c1c62a3efe1e92233f6a7c",
|
||||
"772756db2182cfbca706ee62357ebd2539f81c16",
|
||||
"0493b956a14beca895b3f0b5e2d7e3471387ee63",
|
||||
"f7c14287e59c6cc41cd4f6296a51694b6a5c8249",
|
||||
"0e8875810b6b895b37e028a0f9a75ad14d17e28b",
|
||||
"61322a1d7dc669604c9a8621b60eeb887e60dbd6",
|
||||
"b83b48401a4352c533bfefde2f19b87815e88394",
|
||||
"9f99251bf88c0efecb6cbcc62d5cb64be4c82a9d",
|
||||
"507b2342d683a10cac317346fd5c743b129fb786",
|
||||
"f17367f08b15d4af2823534180b290128852fb6c",
|
||||
"738a4332f3aa8ef2c2ca698444621f7657c611cb",
|
||||
"03877a6ee4f639c8449065d6e35709b22e9224af",
|
||||
"e6b3645a8543654224140ac5854ab4584911b822",
|
||||
"99a6807e22830dabd3f3aa5631dda0fd38940220",
|
||||
"1af1a81e2ac30f5e21ac1eb39ebb970894c1db95",
|
||||
"b026831b99bcb0c4b259c3446f969d2e75978474",
|
||||
"204b94938a992ee3539324c70b922dd4b05d4156",
|
||||
"0b8722317353af3bbb28d8578b08c682a3a442aa",
|
||||
"ed8b651f6fbfbcb5bf258147d3bfe276f8e183bf",
|
||||
"daec5d11791b7e87c8a67968ce2cd9a066167f6e",
|
||||
"389e82aeebbe95519bf2957265f4e7126d90d27a",
|
||||
"e1b25bb9af0b24562b51cd857b9fadb697ec6a0b",
|
||||
"007d459a4ee5a2b29fdb5396d291a151b4915c58",
|
||||
"29aa55ecfa77f792166b12a8188a9d1468af66df",
|
||||
"519a1e30b9f563abb78654798be8304e55b1ef2c",
|
||||
"a0b67afb6b3335a81dfe400d2ebf32d7d83f1924",
|
||||
"edaa8463ac326baa34c99976c36f31a957e62190",
|
||||
"7ba5b22204cbcdeaab550a7e37a8744a7760dd9e",
|
||||
"34bdf077fcc2bbcc01cea8a2a7956dc988212815",
|
||||
"b0f7f654a1663d21eca4665f9f98912c655065b0",
|
||||
"81aa485038cec17ac15c3f39c8127cdb027e038c",
|
||||
"be9b86ac9a898efa539ad74ae3106a4dc82d5aa1",
|
||||
"868fa4e239b59a6eaeede9ea17fe15c466f018d5",
|
||||
"27270db6941d575ae94c028df1b21eec448fb9e9",
|
||||
"d7eaf8be7fafdd96f635954521c3e917fb9ad7c5",
|
||||
"7551c2a33656c3065755e66519d245efcc6f7330",
|
||||
"3625c061aaf90c384868b7f3745f7b77edc63b27",
|
||||
"f68fd5dda2bfe7e5c8bcb961918ca87b5c8d4347",
|
||||
"5d0ce8c1973e14aad82dd51c07fca8cdd5ad3dbb",
|
||||
"356c75587a1d45c1bb02aab987ac15350e3dc665",
|
||||
"cd84e01b39898103907e183b79b99d49da36fd50",
|
||||
"8f76cd178ef94b5784b6a8011a71870b17705a77",
|
||||
"9ee1128a70428a4ec7938173d6ba0f577d6b3069",
|
||||
"86639d28029e7c8eb0246db1de83fa9134fb9997",
|
||||
"c0aa7fcbc44e33a937bf21d761f9b5d348dc8cbe",
|
||||
"451714744633ac72d15922ad651deabc611d1db9",
|
||||
"42be14e5a7b8991d4e7be715dc43d3fce0326021",
|
||||
"e653ffb2f2ea14e1bcdedbfaa9b7780e009c3dad",
|
||||
"7962ec3bb553bf9103e7a186a02627a0f2086b8f",
|
||||
"0a89606183c4d5aea122d6406a5c3eb2be895ec8",
|
||||
"8f406d3aad3b5e1ac37486c71af86249f2f577c3",
|
||||
"9c1ae44ea3ec24e3bb3a60a68fa8d1211f6d1bf8",
|
||||
"27536e3ea51722536e236b916eb7d91c9ca98fa0",
|
||||
"281ddcbba655858fdd5a408ff8c4ae1ed24ce0f4",
|
||||
"0fcbce768b9f939b31c04132b00f18a27fc10fd3",
|
||||
"3d483bdced0dced981b810c6a48caa78ffe7253a",
|
||||
"a962cb8891965e5921b1f4910e99a36e688a9ac1",
|
||||
"038db5c9c451e3e36ac5a9e48912fbad87502731",
|
||||
"d376224b9c1ef9af6fee83ef01b2ee04c20319f5",
|
||||
"50b1a2eaf172613919159f92bfb3d457174495fd",
|
||||
"e710068ab927ee86ad0d1151531c2ae34b121321",
|
||||
"9b0da3ed3be75dd75d445b503d3a95e743b2cff8",
|
||||
"074bd51bbc30fa82ebe182fe9acaeedb8c7ef438",
|
||||
"110b33a9b62d44d9792e0b5489e7595414e32464",
|
||||
"83fa7c43089640b59f9ebd14ab76eed341284b26",
|
||||
"a9de15a3d57fb813133aee02a6ae0d356e269c08",
|
||||
"e35b6405fcf935e87fcfa50def7fa0f80efeb726",
|
||||
"0261c38912717678437bfccaf9dc82bc70261e02",
|
||||
"c9800a30504106c706326feea7567160ba67dad6",
|
||||
"797d205f3ba57f5b7a95d9c94c0ade68012a16f6",
|
||||
"6dac6a3ddb3ceec1f6c495d27143a77f6c5c838a",
|
||||
"1af6993dbcb5b1dfa7719273104789f392473e0f",
|
||||
"18e8111348312a2e2767dc7efb60f1f3df510ba5",
|
||||
"1b0610afe3d5bcc4a1974d0ab5e8771ec2e1136c",
|
||||
"1a5406ccf15a24d00a98b9a8032b935f107db3c7",
|
||||
"d82fbd903ee1e18c6a472e18d6216b0f3ae062d1",
|
||||
"3f5db998c2229ebb746b6bea276b30cf35e67209",
|
||||
"fc642d958803578c20d4c23dc4c22a81c3fed837",
|
||||
"5187b5640d7ca637dcc81752afa8c7186ba5b63a",
|
||||
"9ca79b85d01b26e17e01e9d2afa72f902e3cff1c",
|
||||
"6c8eac6e855e2ea8aeab605c070227fee4886f52",
|
||||
"fbd5bb18023140aa47441db95c5c6e425eb36755",
|
||||
"9037b528e4c59fb11f127e91e9dfce6981be93f9",
|
||||
"f824c4a8333300fb48b2a6f3180fdb7079e5df3f",
|
||||
"5ddd3993d2511402aad650bf8151f103d051e8fb",
|
||||
"f6a7743e006826f1bc0e67689a42ce59273903d0",
|
||||
"41510d24645438f3f7e26841985b57395ba4d63b",
|
||||
"ee7620a802a13bbdc0aab6ee9ed5be64af55deb7",
|
||||
"27dab68221b340a8e32117c2614206156641efb9",
|
||||
"915d5cda9e21ae6d6dd50475bbd5beaed9088c8e",
|
||||
"5f07b4e354fd330e2af82442d6e4feb9aa537dd9",
|
||||
"3a3e57b1de5abf4249926cc8f71be71b8008fe2e",
|
||||
"a7f5d9980188a6f146193447655d2b9a9c9a9b0b",
|
||||
"1bf603dc03acca6369e66c91477c6d5493720595",
|
||||
"7f1a82b40bae1b97c97505c2e038607e68d916ee",
|
||||
"97956b06fab7cea820753c1cb538071f99173a02",
|
||||
"b774f50e8fe309ffa9518296e0dca96e2b6aa329",
|
||||
"f148efed4c6dd03e6568167db4b94b5b0b91ff68",
|
||||
"d0275578f535d5644ada372923d749d86e2c91d1",
|
||||
"6004ca07f5dcd4b3539237fe48e11c3f07bc9c83",
|
||||
"d91b28a8d208c21fec5012f60d7052c0504043a4",
|
||||
"10e6520b81372c1f9215b41fd8de26dfc26265c9",
|
||||
"644d0643cac05a8c35a9ae75096684cb810453a1",
|
||||
"24d417effd3daabaf210d1c198c3cdb242eda0a1",
|
||||
"2f1d1a4ba1132a51624070e96699c9113e74e02e",
|
||||
"1b88e59648977946136b592352c92bbbf399acb6",
|
||||
"8a6b62892cfea4101ad0c7eefc465e14d7d46e09",
|
||||
"1eb88833f3619c907715160ba3db08a35f535a70",
|
||||
"4308833a3e042140f6f575f05e76e30e47a16e72",
|
||||
"05d449abf2a7c10c21d25db1fdce1f37547d41e2",
|
||||
"b1b4d7a79c8a616ce2929b073b389f9359bba734",
|
||||
"942bbf29e46a42c8d513504a5fcd3fb311da7557",
|
||||
"75b4172c7cf619d2862f85b5b94b0ebb50d70273",
|
||||
"0280d845615cd24cf32f766e251508f381c6978c",
|
||||
"f9e30a6b8ca6c0e401c88bda7700892ec068f3c4",
|
||||
"abb2b4d61bddcae851fc0b207ee2253d9640e1f2",
|
||||
"f547dc535fa50270b0dde4ae612a6eafa13d612d",
|
||||
"682c0454c7b3dea6dcee516c6262410e8938c849",
|
||||
"05f092dbcc41c1babe03956cd7da15ca6f997d12",
|
||||
"819272234eef7fd5a3c6513a88a4c03636095584",
|
||||
"1efed0d0ad95ff26142c576e63b8c602d39e0bfc",
|
||||
"d06a6fa2fc3ed12b2662a609a8fb6ce054c22353",
|
||||
"0669ea3d4a5faf5fafb5a70003ef2859c815bcb6",
|
||||
"773e810fd95414af969b79682bbd66f82a0f478c",
|
||||
"2480042e8eca6c91c5eda1f44b69134d09acfbaa",
|
||||
"e058be2882fc01a2185212edcee770bc59346f05",
|
||||
"0b7eee74be4866ebc23fc9355ce7d622435352a6",
|
||||
"95f0e6f558090371b657342e3fd9a3faedea4a7f",
|
||||
"dce6e24b4b72502995246ee0044953fcb8e7ea31",
|
||||
"daa8492c68c6a41bb8a44f184bff4166f5cd421d",
|
||||
"495865634d8c25f8e470cdf3864b62d175d3fc52",
|
||||
"73eedaacedcbe3d6fa6dbd61175e571c45dde5db",
|
||||
"65e718fbfa6dab8e8d5476cf134b607206e6d965",
|
||||
"f121729bc1bfc399056f2c75cb733686814720a0",
|
||||
"97459484cb0e0f41a1185c6e99fb994d00994aeb",
|
||||
"bb76acff886184885cd262993aa4881773345ccd",
|
||||
"961a83bbd5bd5f21490fe0c4415c5c43564981fb",
|
||||
"b0df9236b4470ccb92db426fa17f3355514499b0",
|
||||
"006950c9d7150c418c2a9affcb0fac1df9d5837e",
|
||||
"3d98209e094cb09f932183109c68aff8f6789120",
|
||||
"6539c449fb725fc647a01128392f71acd01c892e",
|
||||
"bb0f7986f49ba638c092ad7849964c43095571f0",
|
||||
"3b709254daa125c7f8440227c3fdcadf7583e616",
|
||||
"1bc75b9f978cfcb5f86be6b78de0670a228194ec",
|
||||
"3f9e7bf39c71bd1e781565e97311ec0bc9ca4dd8",
|
||||
"00e731f4917e805c8df7252228197bfd86dc3e85",
|
||||
"8e00ebfa454c91687b899f63cf224957966b32d4",
|
||||
"5a2ddf992d135a647e77ceefcdf8fd50b8f1b503",
|
||||
"7d4518eb1728f4d33a6d4a8b06ed666c11dfdfb7",
|
||||
"ee61b3f43ff8309923ee62386d8058578feb6992",
|
||||
"6f0079dc782cd267ee7f2bc1efac0b4e1b959ac7",
|
||||
"e24a499d0a46c2d5021260a0c858fcd62525f544",
|
||||
"3b82acd434b9bec22e76cd3ba276ce1b8e286318",
|
||||
"7c32caa24c73f52cc39f3af98d451f75bb2e8773",
|
||||
"e72249d9b6675f00c6411fa659d6b877cb94f32c",
|
||||
"29c57eeea632fcb862099db5e24cc45f13e6e0c3",
|
||||
"704301142885cbb14ea45de3b3cf758bfd97a3f4",
|
||||
"3451b5d2b387e7103b7fa855ca8899d4c0e04abc",
|
||||
];
|
||||
|
||||
const toDelete = await db.scores.find({
|
||||
game: "iidx",
|
||||
chartID: { $in: beginners },
|
||||
});
|
||||
|
||||
await DeleteMultipleScores(toDelete);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,989 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-iidx-extra-beginners",
|
||||
up: async () => {
|
||||
// copied from seeds before they were removed
|
||||
// the charts themselves are not guaranteed to be in the db
|
||||
// so they have to be hardcoded.
|
||||
const beginners = [
|
||||
"661ef517b026d69617787d3330b8e9dec4267e9d",
|
||||
"5db25449c7b2ce780c864d3714a652ac763cf9dd",
|
||||
"badefcb014e1a7eb180544d559962a4ddb761c58",
|
||||
"d8bc08dacbecc4a57d05f501ebaa828e2a676df0",
|
||||
"076c5aa10170468a2a771cbb461b30ca8b5d9ccc",
|
||||
"9152feb1f2cdfc49f6972e626e5377cfbe9a3afc",
|
||||
"afecfe333c2c0fc6087f14ab0d99cd479aba763e",
|
||||
"5ba20d81aa60a6341e60685bf5d855c47b2725ef",
|
||||
"bd92be1e0dc5f9cb2d6c6f05322c1f1d1e8d26b3",
|
||||
"603cc46b10b5afe78a80e9a3da195d3ea748288c",
|
||||
"9eb7434051156021b23e219bd5bf7504d415858b",
|
||||
"565ee5e53365ff98196ee5a75985a58962702445",
|
||||
"8384d234c4cf1c4c6ac50c2187384a816088d7ec",
|
||||
"45f4d12f5268c2bd1b8bc13add5b08d0f160cb7c",
|
||||
"78624d9d5d0ce4dc54b31187e448046d53237f1e",
|
||||
"0301762d5593914dfce8478ed0a67abbbf642fc0",
|
||||
"1c98b4e3a051058ec13123c91c0c600c0cc515ee",
|
||||
"4bcb67d0267ad6734aeaacccbd6699ade3eea7c1",
|
||||
"634fcb687087fb6e6d33440a573fc70736e4a2ee",
|
||||
"774cf1e81d2e73a50c9f9b565f1ed1ffa16cac68",
|
||||
"4391de0a3768d01ca40c4d25a304c81c50d874bf",
|
||||
"3d5bf64fc577446099fb7ddd6f91b7bb441a8b41",
|
||||
"a4d99811a54b103af446673221f03531ecbffcf5",
|
||||
"d50e8aaede0de6cc485d9fe9c6fffb21f91c2530",
|
||||
"4b74fc4ef182f589995db1c132fd4897cb9685fa",
|
||||
"36cb496960ff9df38ee2f89996851ca42790c315",
|
||||
"912ac090102b9ad9a05a87261907f3edc3f82691",
|
||||
"2b393fa0857931aac303a26a54a5a955cfcc9299",
|
||||
"b549d6670cc30624b8e3f87f97dd0d1cd1702968",
|
||||
"25795d571d54ddfb27be5b2fc92c65e6ea68bba3",
|
||||
"4b1ccae70b551f3e98e8fdc55c11f6c809f6cdd5",
|
||||
"d26a29b485506f1761bbcfc99fe1963514c01028",
|
||||
"4e03a64a613442ef5c18a58fccaff8889189e9d2",
|
||||
"c435b0e66a6ead55b199d60c39cbf9ffd462a324",
|
||||
"649fb4ee4e0a85ae2d108afb3f6ad36fde48b584",
|
||||
"132021f620225d183e0f012b34ba6ebd66a402c6",
|
||||
"18e9074f7a049c6023729de1e1edb9844a5afea7",
|
||||
"8584a69f97e352ae8098492281542ec64e19589f",
|
||||
"d2c16b237907d891cc16a1621e9788becfef6f21",
|
||||
"3df0d37f3bf27ce2b8755541b407b6a2497820f4",
|
||||
"e633d900cde7f44cb92d8bd976e5230fc10f9561",
|
||||
"41ce5b44f364810496d689e751d939cd6da7646d",
|
||||
"9c423859fa5467376a7a08cb96e6cb242282aed6",
|
||||
"2dd6630dee52bea24bec2b021ff93b0b85d69377",
|
||||
"d68e42c9b0e47c049985333178505be8de03d5f0",
|
||||
"7c5667140ac1849cf14ae3771160bb07c2b54af7",
|
||||
"4fe8b90c77832a5d656d69f046205dc4a6c03844",
|
||||
"aea3e2128fe4ec63da7f4ece53dcf7498ff7aae1",
|
||||
"5a47dc5639816e0fb9ec09ac83d53750d4cd4913",
|
||||
"03a25566d002590ad1e9c3d59c76b8f954973377",
|
||||
"34c2d0fe6a02f36052f17608b74694bdc729f309",
|
||||
"86fac1a7a61da348f632273baaf89400b08000bc",
|
||||
"2a0f07945050fc10c4095dcca54b4d53ef6beb4d",
|
||||
"545c87b32cc0e2c5f93152431b8a011e59cd523c",
|
||||
"90c0d2395690e64bffb295c59aaf867bea1de36d",
|
||||
"842c48ccd6c3acdacc7f25d38e694bd91be90cc4",
|
||||
"bfbb0c4f035698dda3e481ef97d5f4239c6039b8",
|
||||
"739f198a65dc013b85e057bad0ea6cfb485eeeee",
|
||||
"aadef0e3f5d1cf853b549b91b6e96b8d404b4c9e",
|
||||
"1be0fd1abc68d0d89f0a57645255ed6369fc516a",
|
||||
"95a83da1367ae85c138a13b45c936c22a94b6eea",
|
||||
"eef1462f4c8d4f7427d82cd8b5a1f8de4cb46f9c",
|
||||
"951f61227587b464bf95b04c936ed81f1e017213",
|
||||
"c9cc718f69740e43a3f4c175fcdd8ceadff89246",
|
||||
"a051c7c54f30fec226a8cf818acdf177cf6303b0",
|
||||
"f83bafd60b4e4f6fb487bb22891ddd3cffb65ee1",
|
||||
"d4371d5fbce357fb9051e14cb840ff260f9e314d",
|
||||
"6baa57136dd329a61f9696b9ab9646409f126c41",
|
||||
"51250776fef8c940b849c416f85cdde31852efa5",
|
||||
"bdb69fd8088eb0559924c0db6410657aad1b168f",
|
||||
"92b0807fd7b28a018f591e825c5a5a7e08b57c70",
|
||||
"9e14a7c0e548bfef46b4541935110d4ac0f7ad31",
|
||||
"4c2e7fa313c532fd7c9d10e773b6f8b5ebb007d8",
|
||||
"aa8b7fd55db286e1aaaeac6f10e04663b625faf7",
|
||||
"bddb0f0674c4f46ac566f6c16f67fc168c85ab4e",
|
||||
"1a1d9f07d0eb6aa9185e081a51caf3ed0ec810a3",
|
||||
"5590b2fbf8c9b6f64bbded8609891b7c7ef4d58b",
|
||||
"b373944c0f39817bbc95d3473a6780460a6a1925",
|
||||
"ddaf62ed398483f92b16f24c5d9bc3b22e0b9463",
|
||||
"5b611dfd1c7501c53e33925decdf7e82731bd351",
|
||||
"a57bb36f065df9bc877cf49992ea97a02ace31fb",
|
||||
"d6c7f16b0f7858faf3405702c0ed204aca6c50a4",
|
||||
"3a55cec3b6a4e7431f0121473c83f0ee0f660241",
|
||||
"b079edccf0e90c80286587b5b619587922df2b26",
|
||||
"bb896e6d8b5a74988cb2e7999beaafb6fc34b7f8",
|
||||
"f1e9be1f667be2ec00f65450583908d369bca3e7",
|
||||
"c4e32f10002bdd99d18f683dfebd5d774e932ae0",
|
||||
"2f609a11fae78c10cd406fc0ab42848a24c05886",
|
||||
"ee97d7fcd90786be6fa331997d6c36b4d53112d3",
|
||||
"55449c569329c31abfb01f0712b4f9ecacaf8420",
|
||||
"9b4b295b2b953f6a67981957d0a53aab55e8dadc",
|
||||
"d43971c7afbfbb71efa3a6b3071586a67f9358c1",
|
||||
"321486a05d7e28fca044d88b95cbdf4be1ab56f7",
|
||||
"1dd1bfd3f24521a37440e5b5b3a2b1614781e726",
|
||||
"110cc1c334849305240d4571d58c68dc0f49e16a",
|
||||
"f34b22a54026a1e286a79053c6c0cbd8aac82111",
|
||||
"bd05e161f1bd2d7c0815d763a51f25a1328019ee",
|
||||
"b9dd1bd65a79ab68299cbb40cf930f9f9e0f0566",
|
||||
"cb1c16e68b7beb3c955631643fdb6b2a3cf51f25",
|
||||
"eb240a2118e7ba270b574ff913ee9b1948c87f84",
|
||||
"b253b7d18813ef655ec9645ed74d06bea3497600",
|
||||
"de2c9d8835122cdbc83653548240193f157e4c56",
|
||||
"c608109064b0a3d7700fa94cccc365d7da1b065f",
|
||||
"ef50dcce52d277246cbd1eb2ad0f63b086b15613",
|
||||
"bbbede4a4341a5e48d9ce2272cb2a35dcabe8236",
|
||||
"af7adf981622e4fb1e4160f583af5a1e36adc1a8",
|
||||
"35be3c96ad2a423729b158318c49506e52720340",
|
||||
"20542febf4e69e212d8aaba8303f5fc4a1309839",
|
||||
"52f0c154a646232609d3604970c42e021a45bc25",
|
||||
"824640e1b06f8b5a1c0fdf997e21397e1b21234a",
|
||||
"38129eb41a24bd45d6d7e81fffc247751cf6b4f1",
|
||||
"1c8eee750da7f77482c68bc2a5615ec113a2a139",
|
||||
"2b7b3ebe2cb3e276418aa0993c4151be2cae2510",
|
||||
"efa019fdffba654a58861a2c7ef4e0b482cf6740",
|
||||
"4abffcad8ac89a23816831d6de29a04c306fd634",
|
||||
"003e1d384c306515cab3087c8b0fc69ee4b846ed",
|
||||
"d7149e80a0520654115c279e2e568777d0b4c7ba",
|
||||
"987f4d414415647dfad38a0c62754798e64cc622",
|
||||
"9e402abd7dc68d002ac29efe741bae1770c0ac5c",
|
||||
"761983a6a52dc19145a20839f24967fe6dd5e58b",
|
||||
"3b5c6488f14c7bf7ad26dca9bab646c43b0db033",
|
||||
"7e0e8f7df56fde7535e804640f48a91ba24300e2",
|
||||
"97f509f5aa3b4f012eb30ca19c032342058fb5ae",
|
||||
"f317126b5fe11b7983080dfaa3769238fafd4d25",
|
||||
"154495d34250b918ec43b8e5dc1319a994dc4ea8",
|
||||
"5f4b69f18f43d68a05c62fd76e0926fc5ace659f",
|
||||
"b36f6b80497a5aecbbaf587c984b36ed7f5fd37c",
|
||||
"67080be1cb2331dadc3a4ee797b8cd75243ed950",
|
||||
"7d9c61075b1a32dcbe93ff54539fae9ac1b21285",
|
||||
"8bac128c26535144ccf702c28f71d0b1565f6ad6",
|
||||
"14e86070032a42ef1c80c176cc48c31845d7c3ff",
|
||||
"23917e570bbc0949afd97372ae0a5907765f0e37",
|
||||
"d9d92cbae7abd8d50c1ad69eb0462e87ebb02066",
|
||||
"92cbcb207d3c75e30ee25a0c2ce4c4cdbe7dfac1",
|
||||
"f565df60602320e715ce5bb07095446e25e93a26",
|
||||
"acbfd89b1303d235dce0c8f5d3039b90a0763d59",
|
||||
"3ace93e28d251c458506f0acde582742ee5a7e5e",
|
||||
"2d8dc46b4a621aaa954cdf9b1432d15c5f5c4783",
|
||||
"0287936bbb7e60d70922f895c81f42a55a81436b",
|
||||
"db9355e671e5af59ff8712927f0386621a911874",
|
||||
"f47cb021b4ebda91d0404c4f30280e6396cd4be5",
|
||||
"b1a4c2a959c1b1fdbd20579c1b874d40e8355dfa",
|
||||
"8a1932353f141cc44dfdfa297243e38eff6bc88d",
|
||||
"41a61e4be75e329d7e52f034dd03b38cbfc0dc31",
|
||||
"314c5c3647b2b1ea8d5d4b67c9bde434c08ea40f",
|
||||
"2e0261d7fd00c0f4f76e82255658b6e27209ba98",
|
||||
"00c20b66b9c7a286f918e37ad0ba3690307d0288",
|
||||
"73dda04a4c1eb86e5b9316cdaca7fdf8f9dd045e",
|
||||
"4e0e1b44ebd373615fc21de1a8e2afddeab30a0f",
|
||||
"869a59b0728b5fd3d0bee8b35cd72e363267e588",
|
||||
"3ce2b6ad46886b244106c6c6ddbe50e9c3ad47c7",
|
||||
"f1930ae5bdc25bdd6bdc34956389959ec6fc6b9a",
|
||||
"8d71628f1d281f69e2162dfb0c6d6ac55482a0c0",
|
||||
"79959cff3137fe640c3c13975fb4bc20b5f81983",
|
||||
"f6873d250b57e3246ccb019c15f0325207a27199",
|
||||
"b3cda4c1989ae7ea78d1ae2105f828f0b5c5bd30",
|
||||
"bf0381fcaa3ea0635b5b4937ff41f71ee68e527e",
|
||||
"ef0c0c8a265d075183015a8d817aa28dd3d6490e",
|
||||
"97d9f87e888c23587b7690c9a42c1a1b1ff77321",
|
||||
"6005b8ac86e3ee274193b21d148c32ac7e8ce21b",
|
||||
"fe9e0497e0d059b530e2be559e0789b101f82886",
|
||||
"fb148b2fe9b982420f478cb60f335afc46917405",
|
||||
"7e3f2df88518a060f5bdd1d41041b061868e42b5",
|
||||
"8eb325bfbca0a98e9d265c9650f1a73c1b7d3f3d",
|
||||
"1f5dc62094fa8e5f70818a6a6052ad7a3c339867",
|
||||
"5809be7f3ab9678af36a85ad9b1ad152d5ef6b97",
|
||||
"6f6386629a02a3c05c66cf3b76d52eea9dfc1f66",
|
||||
"32fa465f6ddbf7ff85df79bfd702c7b4871b2ce5",
|
||||
"359febfe39a747bdb0d23a4e3946fcae340c3d8a",
|
||||
"f1e607a001b8a82f2eddd1e4135e0686fb3c9c02",
|
||||
"434543abcfe7dc2b8e31eada49227f5d24df9496",
|
||||
"71b138941abef6865e796733767edb216b02e533",
|
||||
"4d5a45e2b16cc6d1d39b5cb4fbbeadae28bd2818",
|
||||
"ea59c3e921437da14e3b3a3fea0b261f5f8911f2",
|
||||
"4133265895f20fb034c7827f635926730c277e6d",
|
||||
"32f07bcae894a62660b3583954c88913758d2fcc",
|
||||
"e53b65318b548eefb4b8b6427d6de008bc4419ef",
|
||||
"5cec40c1ce58d4824ba7157361995897e822aa90",
|
||||
"5d3fd69ce84dcaf7acb58ffb85bc264ca979853a",
|
||||
"6bd02e8f9871463ae8ce18331a38ca6f669fab85",
|
||||
"3a0a45380dfa73a523b0aa505bd95157e3f6ecda",
|
||||
"89c3b3417577d5a212ed74116eccb0eae65e7071",
|
||||
"88bf55fcfff10bede9503e170ad72e378a91dfb9",
|
||||
"143a8bed833222c83ee284ba42159ed420932c0a",
|
||||
"05b99fe6781edd9b2e730074cb4e26ab8e0edb25",
|
||||
"c9e0c22e41a885dfb9edee81b2bd24411599017c",
|
||||
"5e2fe3883337b15f47b279c483e47c57ac63272a",
|
||||
"e351184f283bce39e43ce6a3c7bd26af5689262e",
|
||||
"6ff4668de182a47e0b1a6fb534824971448a0b06",
|
||||
"05565ae23927a7227578f7c9ad837d759aa36e07",
|
||||
"8573e508b23c69304b0d60a8f65ef881397aaada",
|
||||
"1d345afde76156f620f9385594f517a3b66704ef",
|
||||
"274412110cff255b4e83d4b2ab034806a487d391",
|
||||
"c23a69ca5157222526cc8e979dfe4d0c0e030144",
|
||||
"5f0d2b7424016a0d16e783d5392ef9cb0802d1c5",
|
||||
"d670727f4c3edee023ebfc5bc3779b9ee61fec0a",
|
||||
"87037822b3b959a98e4875b2685e6e0284824818",
|
||||
"fc7389fb7453bab1c40abcda82390b5ea52bf7ec",
|
||||
"58147614cca9f43f4d392508da8f5a6eef850a6e",
|
||||
"b98e20a4e21542edc04447f63a7f65f6daa5c944",
|
||||
"f3e64f88953eeb2d1e3a809a05d8ed2d7cd13f25",
|
||||
"61aa9f77e538f87a72b720726685c0452ea1e6ad",
|
||||
"fc32afb41e6a53a7c17dcadae04648ca1698391d",
|
||||
"e3994c1be06d17c4311245ba3554445245ef50d3",
|
||||
"85b881645fc988984bff9d650ad6ce3f15954d32",
|
||||
"e6722a337ef56712401fe2289ba91fc170f95d5e",
|
||||
"48c637ec5619e2b0623e8934ac602527b139db80",
|
||||
"b3a17ff07ec43bf551b2dddfe1e1d3bad0de788c",
|
||||
"d2f7d85edf14d6def97d44d22edac72c52cfa530",
|
||||
"84b8ee4c6c565535568e6707e327f48fcb1c02dc",
|
||||
"931db4091942668efd050a8acf4a4cd3278fc607",
|
||||
"e1ae1c80a1b63c5451757a6097c7d65837339f74",
|
||||
"b413600118f500b3bd8bc4bd87a2e9ce66653e93",
|
||||
"bd2f959f0feb0ff2b38c9e69a982bae0724a6808",
|
||||
"e74e59833d1311b9696cf8418a0a5dead1c5d61a",
|
||||
"e81953d7042d923453f2ee38aff65d7ae06c0920",
|
||||
"3082f75036b6196b4b8dda1d04b87e8cea33cc8a",
|
||||
"18108c442d57ae8688a7be265cd2309e52eb3823",
|
||||
"5d94fb94c26235f084a3a165967f3a2052690399",
|
||||
"63772bd386708944660ffe118fdb911477bf163a",
|
||||
"4b9b2c93e24d64cc827cf307836bf1ea645259d5",
|
||||
"2a364f95e38f094fdffe931d0ef0d95e065e8ad9",
|
||||
"5c9d79af48b880abaa035bcb608a4af7dc11874b",
|
||||
"cc9127f801536f308fd5be2fc3fb529a01de2f3d",
|
||||
"5721c3bc13cd1444d1eca0d75e0fecdb31477c28",
|
||||
"4e4f535c744ab77b64e0c774af9d3c603dccc6b9",
|
||||
"6c5e4ab388a2384dba1c07521a6dd453d2472339",
|
||||
"8d02f8458554f10a1fc2935f9edf1a0fc1bffe41",
|
||||
"647e23e587dc009b97a42f1d695bc2f4940187ba",
|
||||
"d8fbf3e52eba573c04721cbab25409f64f3f863a",
|
||||
"aa3c7075dbe567c49f93c12dbc837ff18032c1f6",
|
||||
"15766bf8720c9cfc38a877679737558fd92b2873",
|
||||
"60da87f2cc15cd560107cebaeb4cea0306b24f40",
|
||||
"acefef2e119d69a1b8c652f54a90dd71f7eb2da4",
|
||||
"25de55c00eb1e2f66a289e7dd9f55f1c18b6e0f0",
|
||||
"dce02a2af13f45e303849f3d01422c79b3c517fa",
|
||||
"9131b7f5299abc4a3898cf011cb60d5b2767a176",
|
||||
"c4abc38f4caf6b8205ef2c06c5262900bd25ace1",
|
||||
"158dfe41824261305e91d8c8856aba160266d9ec",
|
||||
"9b03fcb6e7055450550de1f77278dc2b8e631006",
|
||||
"ada22a32b9c0a4046b852a1054b940625292c24c",
|
||||
"fab9f586312463b0e691603bd1423fe6009a758d",
|
||||
"9a753509e951225ec29323de2f0fcc57996664bd",
|
||||
"89e00a559782f55944a444e197e5b77a9a80933c",
|
||||
"9eb7806961df7931a853cca6b34e0e93b67aed00",
|
||||
"981a6e6028402d8d8b1413b2db687a43f0742265",
|
||||
"856de204915853d5a5621924621b4989eb0d6f4d",
|
||||
"5cf850241f14347e435708c4864a4d5437def899",
|
||||
"912e8d6ca87a83e1f5f2492e281ee4e9320e351e",
|
||||
"abe2585d5a74f7183f067322593902f183d1f5ab",
|
||||
"17ce6c85cff578e8f6332a3ddda4da15a83a951c",
|
||||
"c3cf771dcbb5820063eb92cddd3d1f9ca552b49c",
|
||||
"9217aad3be0c2e9137551675796bcf30bf86385c",
|
||||
"b3242f5366b7569ce510bf2867d71f148659c5ce",
|
||||
"3b4eac036e2ead87527b848c4cb09b103099ed93",
|
||||
"73270bde4f47973b4741beffbbf984ddf4722fd6",
|
||||
"0b7c24db33a65216c7071ecc2c9038066d531cf1",
|
||||
"6c5c8cb009458d586c1f6bba31c3597bf93d7058",
|
||||
"135353d0d80d7f4d53303e9274c683b194ef7fb9",
|
||||
"bbc2f1233f6c257f96b6c52c60b86048521280ec",
|
||||
"3bb3fada8e9ec4bccb3524ce141118edb2ee4ec2",
|
||||
"ba92a9a30086b080879eb2e6659b1bdfb45fe7cc",
|
||||
"c61739fdd80d2694ff1eeb0c4affa1e309a2c7d2",
|
||||
"6c978544bd31102016ba757cc9eca9c2b977e48c",
|
||||
"495167b4b7ed2132c75f29217226d01edf159b9d",
|
||||
"05e4a77c9d5224792fc1d523ca73533b11b99c49",
|
||||
"49a3cdf900f1956dcca818c06402f3c6083cc064",
|
||||
"6e6fe05a55d14096d7dc6666971fb3bafe984981",
|
||||
"6a35a6dc7c7a0ad54feb97d7ffbb68d96c6c1fe2",
|
||||
"445048a42ffd2b99f40159227ad55ab102ed9517",
|
||||
"f943ae18952b4b652ad0d7ac03ee662a2b996cd4",
|
||||
"1d97254882f0c942bcc6ae94f398eabe3529e5c2",
|
||||
"98771b9cb9eeec788cfb96f07b318c5862185691",
|
||||
"469b93b630ae13a522653a6a09dfb152297a1103",
|
||||
"191e94fef9e39ad64ac7053609b316d6bcd88998",
|
||||
"6f2c87359cc182065f00647e9e2c8ea53a47100a",
|
||||
"211a2590e0ec315770fb0dc951811bf63a31be16",
|
||||
"6776022cbc158164cad300a220f4124e8aed9fa3",
|
||||
"1b4dd9046af2f02e4db4f63a062cc7566a723ad2",
|
||||
"c86dd9bf95aee32f3e2769a64a50aacacd0d2cc5",
|
||||
"4fffa6d8809bb6d43a0cc3f9d81cba06f60c35c9",
|
||||
"053309e6273ba4ae18db581240ee274a9624650d",
|
||||
"642a16109de71c902315d1d52ca4f93eb2be146c",
|
||||
"d31b02d2867ff6ca0a6c6f48aef52474a4dc33f6",
|
||||
"fcc468d222de0ab95460739dff3de57eed8d1717",
|
||||
"20fb5e4da4cf16c47773f2bc913d6214658f2f4d",
|
||||
"982b6c05cfb5337d75e646b4ae328ba2bdad91c4",
|
||||
"2bb0b45659c549b4b5562c3ae1c53646fb4e6be5",
|
||||
"1bf8d06211232d3af4dcacb4358503ad1477972a",
|
||||
"ec4fd5b625658ebeec8efd0dabbbfd7257c2f329",
|
||||
"73199f0cac9fb5d3bde33b7ac7b5b37f37e709f9",
|
||||
"dbc23d272c7934a19f1518d946fdba5739db6293",
|
||||
"a7f48ffc54f93fb84ed661ce04efdf53c8cbe98d",
|
||||
"58da1cfdd00b03e0f5a3c3863052840651c67943",
|
||||
"28219e8374b133be408424c19a5e64fe817cad39",
|
||||
"b7436a00af93cd59078bd3d271cfc6e9534a9c5b",
|
||||
"b591163919446bd5cee65afb537e387952092057",
|
||||
"623bdc242e7997932f879d80b112549441fa26f3",
|
||||
"953cfb211fcb78cc7ca316587335588f92ab8dfe",
|
||||
"672fe1d3522588ee4f4876ddbb9a336697514215",
|
||||
"b372fb595269347a4c1423c5a5dec047e8c64219",
|
||||
"d31813d7f861d8cbd0c27b967b85f6dde08626a5",
|
||||
"0dcee6320f0942298ef1109d912a64f26b852f59",
|
||||
"e07df555b0143ffd7f64c4b8fe2a03d0a87653d4",
|
||||
"798df9d8129e134e6fbeb6a2676f8fbd95b87303",
|
||||
"431e01faed05ed856dd7471370ed60f697273735",
|
||||
"2feaec1c81fecff2bf227c09eba8b5d143ada31d",
|
||||
"7bb9c4bc7557d5c086f26fa1d394e2bc4cd61459",
|
||||
"a77ffc29ecc0cfab3880efbbdf7833aa7f12cbac",
|
||||
"4f47f6732cb15a760acc918d4d5ee9923638e6ad",
|
||||
"9298a270beac1b2bdf7098e8c370b81e56a76aee",
|
||||
"930a72a66717026c08111ec41e0d987613321c59",
|
||||
"c55043fdcf0a12f423e70094441f8a246fb5a518",
|
||||
"69b7c73fd53cb7c003cedb6515a03b6484a5ff1a",
|
||||
"473d7db7fe74d5ef48b8cdb6f78c3a0308e7cbe8",
|
||||
"f33029333409e829c0c2425ea2e2c5313f17ad6a",
|
||||
"f07d57305fa377a856e33db230b12a7cb72d830f",
|
||||
"50504cc2b0bc8c61086c664488eebb4b096c5f1c",
|
||||
"cc4512dbb6496610bd80decda249fe94fd5490b4",
|
||||
"d78f91d6ce8de4b8db77a2acbbda9df163d1263c",
|
||||
"8bb316892e5c7df55b5b450f3ed7d0b14e110e76",
|
||||
"09e7035a1cfe261abe911e2b6e885d167b241402",
|
||||
"256377f02c7378720e60dc129435355c06868fda",
|
||||
"29a0dd24b4427ce98be10c4f0085977bfd18253f",
|
||||
"5d040c18fac3ed826ef827ef5f52ae6a29aaf227",
|
||||
"fc491a68d0e3fe56dc43b00e511cd2736df87f6e",
|
||||
"645d83e627654293ae0faee6118cc4de741da862",
|
||||
"e41eef2ecd3865c087413b05018b3ad1c7525291",
|
||||
"92cdeebc7767d89208eab5bb9307d0c27ba42cbc",
|
||||
"9dc2df9bf9d4487856ffc005088a36b81ce3d60a",
|
||||
"9bfcfabf6928e7136dfd4d3ca02f5818a34f24c7",
|
||||
"c4afd4d18fa93aaf6a0919ffeaaca8400724e874",
|
||||
"03398570a0f6f9cc6e801c1a3b7d358ce4e0674d",
|
||||
"16c65e5861ea9037919213a38abc2c9b3c63db3d",
|
||||
"eaacaf57ca8b2eb7ab21a0c9f0d6ddd612e7dfe0",
|
||||
"20d99e144f08bd704961f4a10a6a00356f40841e",
|
||||
"9625ac84fa3f6d9c937f2db84da2c20ada875ff1",
|
||||
"1a5e02d14ee35a4e262ec6df346b589c7d6cd849",
|
||||
"e1ccf35f312e670bcef36f3fa7dde64720b6c2e6",
|
||||
"efb361a3210ca28e880a057a200d8dfe29f8a49a",
|
||||
"0560f93794f79accda85f5d5dc1febfe001950b1",
|
||||
"eee7f0cf957bd6de9661af3160216e6bd7503402",
|
||||
"389ec863fd47bbf8202903a888c9892b5d4e4601",
|
||||
"49212d5592ae021321b1d0b317e63755476d3f93",
|
||||
"27f30b6f8aa4668d569cfdf121f3fb23dbaeeae1",
|
||||
"48c42a48c8493257f9137ec541057f25b9205105",
|
||||
"d552cdb76740ad4655a0af177294e94df9fef11e",
|
||||
"9729d7c0ca63e5ca7ea529beb50ea28331aed3bc",
|
||||
"dd98309f2dc82f2ced97ee22c46190ec2967a22b",
|
||||
"3055361001656483c48c5f57329d3837803357fd",
|
||||
"f56b0e71751e5352f089e5c5cc7946c1b680195b",
|
||||
"41068945d7673f9cc6f4ff0d614f5c4c2e86a72b",
|
||||
"6a9f7cb1585630f4f419a239118085c1332ce97e",
|
||||
"b9e23d53a9ec08f08e39f503d1d91d76c6c4e92b",
|
||||
"411b2a0a91f5406a67e22239c380d54e417c6c14",
|
||||
"cd101799f67fa4bea0836bf1cae7f56483f85961",
|
||||
"c751963865e2d389a8c05dfb33b3fb66588b1e28",
|
||||
"7bd8bda5c695ecd83ad414b105ade4a4d443bfe0",
|
||||
"ac16a90821566a47b7fec44ba207a3dc23e5c998",
|
||||
"0e7bcc798f848bb3f393dec56c27b31a32d7a1e3",
|
||||
"f55b652322417f6cf79f9a45fe5ffe8a7b986001",
|
||||
"cb32c897e85aaad6463a985a059cd8975f756c0a",
|
||||
"41c2db43f7c1ece6ca8760c1c8149e8f7c90014c",
|
||||
"64f1800df8001f9b70fa3fc2d9f0a2a3591fe227",
|
||||
"4ec95aa11b6bdd235e7eae759850aa64047d86a2",
|
||||
"df188c438cb0619c51203ee144cc021ae1f5cc75",
|
||||
"02f7bb9f4eef89d4eb1ec7d966f68a804b082dcf",
|
||||
"1bd753ee8d55f543d0f1b36c0d71968fa6dca97b",
|
||||
"8df5ae837397aa7de54b185de1375f935a07d9ac",
|
||||
"068e54406bd9cfe6fa2c1ba25c4604249005c957",
|
||||
"52e5841906b62148d4b32b613de6d2ca037ab763",
|
||||
"1b375b046c1b8741785b98afcdeb0c317d550df2",
|
||||
"275ee7f8f35ddaf883a90b2280f80863fb8079e1",
|
||||
"ce264cc6c0ffce8f346729f8829893589dadc223",
|
||||
"47a67a3b625528f1e50e5f61730ed3ba2202e1ea",
|
||||
"876820530a58d11d6578b47c7e89011c3bd61b6b",
|
||||
"d2994fa15cd04cf0319ca8d788a3444e514f4ca8",
|
||||
"938f8c47a37f4854f04d5f092dd54017538486fb",
|
||||
"2a2eb914361a101bf47377da5f7255ebc45506aa",
|
||||
"5de52db3efccb482e36110b436e5927a81b5eefc",
|
||||
"52fd672afa633036888512d7429c43d0fdf1260e",
|
||||
"493f083dd62c8ea8136a715deb0b47e81889015f",
|
||||
"f217354944876d478eef34ab6fd9fc792855eeee",
|
||||
"d3485eef775f8967e6dd8cb54403880a910aa30c",
|
||||
"a5f179871a2f87324c9de852357b148d244e462b",
|
||||
"746b7c3f420a41e19e1bda63d71fbc5304e9107c",
|
||||
"a3346c541418b5b171e821b40546a5506985b658",
|
||||
"5214ad784bd487634342ed5accb0b6e50b9a3e52",
|
||||
"1d55635f9d8cd1d0e9db7263050b5d59eabae9a7",
|
||||
"f38e13a3402f4f07646ad24b9b84a914f086e99c",
|
||||
"97af01d4ea8aa264b33abd8b5eee7cc656b7691c",
|
||||
"c78b0a814460e3c27bbe8cdc28cb02fd0776d5b2",
|
||||
"a862e6bc181e1b06de773c053a3e849537c83e29",
|
||||
"fdc0a4e501a197c7e0f77a0a59b168a06231e958",
|
||||
"60f514dd88ae987b9ffbc6fc85f6cd5d86d9add0",
|
||||
"e805ef6643cf4322914df94d44b05c2c33a313a9",
|
||||
"916f52e2757722cde0c6b7122e796cf666f3c29b",
|
||||
"ac6e415f395c3363d4da7c63fa7a4ba5fb18b53c",
|
||||
"3163fa6bc1ccbf0b9dbc5806ad253436911bab13",
|
||||
"4c2599bf6b7a18f7ce95a9ab68f6a9e86d55d511",
|
||||
"03c9f7c81707056b56aace3306bde3cae3e850f2",
|
||||
"d448dbb537b8058cae0c1059876b6698ca35b3be",
|
||||
"7a3fc6bc3c6f46d3dcfbe220f62dca5f855f6d62",
|
||||
"d1e8d96d3568513a240e43b4737b09cb79ed6403",
|
||||
"df7edca301064a23ecc40cd5068a9e2b8b847a5d",
|
||||
"c93270bbaeb82fa7dc9d09f91f97c7a1504bd77a",
|
||||
"c025eef1993f9b7cc49f478b75e7e3b118e2012f",
|
||||
"7b87015f33c5d910b23e0c57077d92b9503e27f6",
|
||||
"84fdd626a18709f6546dd61172feb573e5875716",
|
||||
"a97094b96b0448fdbe01936a6b62ff35825c0eae",
|
||||
"b4641c57260ddd9b0c766d348045b012b4631f17",
|
||||
"c6e5b39ff3bb039f12e337ad66b57d9fffb11b7d",
|
||||
"f1ba2bee42aee0d9ea6ffdc8835e259cb55fe4a7",
|
||||
"91ce3cf27118a44c531308f7aed4f871d1c99e68",
|
||||
"ed0a3685464b70287baa989d6763c359fd2852c4",
|
||||
"abf2d7e317003083b78c92fd1d28a1d54cd89b3c",
|
||||
"60fbfc13555a22ad9d5a17a7ef6c909f3dc798b2",
|
||||
"a765548ce6c964d23b368a40bd86374dec50d1ab",
|
||||
"6fc4ddab97a40617df0a3d23f65f6c6157d88713",
|
||||
"2e31ac1f46fc3d9875110445c2f02852a3406aa5",
|
||||
"fce62cfb8c7bf2a837f43e746657e6df3f9336bc",
|
||||
"db4823ff5b70e5ccc16d3a0e80dda9c2bba8d472",
|
||||
"4c3b715944ae9ab07bd0bf6eb89b7f4f9ad48a85",
|
||||
"fb1f7514e58c52426e822cac7307413cc458e3f0",
|
||||
"07cd9c1e430831b7616aa0874e861b886d97ff6a",
|
||||
"fb352bb389abe213843ab5c53c70cffde93352aa",
|
||||
"a3a04027cdcc6b6fcb4065246f720cf8d707003e",
|
||||
"8091d2c5b9812f6a5827dcd084240ad8b6046469",
|
||||
"a4d373d199174113de05c1cf7c95acba08485aa2",
|
||||
"baa07c18513ad734dfa100170a03e1199f3c882f",
|
||||
"885b7375fe79df4f7afa2a05ba70239be5451c19",
|
||||
"ce5455a72e679a6fd858cf7406e6e39056c85ea2",
|
||||
"161fbcf59a5ca4ddfcdd2d84d543369957599c4b",
|
||||
"2a2067531b2372d2847211086620da44beb6aa02",
|
||||
"d58f0b7902969a83e02ce67a965427135beb3902",
|
||||
"442e0a043467292e3e064021ae24b957c7535ae4",
|
||||
"dc406d4f6487a7a2d8f668a4b53dd5cf95e4fc5e",
|
||||
"5662b4d2b6c7b23c9423ee39415abce3515d8967",
|
||||
"106b2dcaeb426c7f1b72163fef3a414ebe1cef21",
|
||||
"1fbf6b6bc47284ab5aecbc917bc5c69e9930c438",
|
||||
"2fb2bff8de52f5414712a3a8fc06f6245ea38682",
|
||||
"0202b3c5e522907af1b198dc1422dfed65503054",
|
||||
"0faf0ed0f848cebe614b7922b87ecee481157497",
|
||||
"9125c893a2f6031f897bb4041a2b806329fdf5af",
|
||||
"d59639f546370774513085fb1ab54121e2b2a56f",
|
||||
"bebbb9856a927d3a0bdb641658a7aac32ca491bb",
|
||||
"47f46650d837c9d7e54e1fd72fb318a04d8f179a",
|
||||
"0f048ed4b8b914406c1f709d83c179013efce9be",
|
||||
"1c3e6c3274ed59d3549f49a24d2cb1f94be81e85",
|
||||
"d421b2165778b73bd0cc82221240774d6a0a920b",
|
||||
"5d7ac6111f7c4e98f65612aeb6a4bae64511b164",
|
||||
"96a5e71e931a8ca3b2db1850f416951aa11b6220",
|
||||
"a66b8e11b98d78957213794bd9548686178d2891",
|
||||
"8a5b5c3bd7a8dbc79f2b745610602ce2dc081e71",
|
||||
"93667b009651ff896cd41cfb7e885a15a250bf29",
|
||||
"9bbc1956990fab45fe2f678a5435472d7d39bcad",
|
||||
"58220a1b88eb0dc1383fcb09e49433447c0c2b03",
|
||||
"a84b40ef648c7ddfae0ed4e894c2e9352dbd868b",
|
||||
"cd56842cbc2628eb71ddff63bfb7532c01858018",
|
||||
"866fc4cfb176739e10e23f34af5c96b1312c6ea6",
|
||||
"45309953bb82e3fe69426f5ae2ddbc5c4b81a8dd",
|
||||
"67b3cedb4987ac68a8108b2b5a87f838c0de8c41",
|
||||
"93a758c7f11ca6264286e41164bf74c4f7aa470f",
|
||||
"5956b45826849de26ff9b879ac93b7802b9826e8",
|
||||
"00d855c1225cb42604ef03ce6b06484e503737f5",
|
||||
"47be771c26fc8d1af7b97db5e3ea2a4f42718074",
|
||||
"546099ad054195fea9c6cb62c800ff7b5abb27d6",
|
||||
"099d5a2d4591c10c1d67865780e66cb02ae743d8",
|
||||
"9304109ec4b04b4d30567308e22195ae4374acb3",
|
||||
"5b786550ad0343f10d9cc6fb2eb626589ae72933",
|
||||
"a688d1c49dcb65ba5fe31b7b19c12603dc8f5cc2",
|
||||
"80c10985281060da276f06009b7320c66122db40",
|
||||
"96e4fb1b132c3d4084abf7945da6cfccf942ff56",
|
||||
"7e3e4fbc0cceb92148122b5d689f4543ac4cf30b",
|
||||
"7cab88bdb4a73edd53e4e82a3cc3c2e949661e65",
|
||||
"5bd79b641f7653ef38825dd5145087499fa303df",
|
||||
"ad8446426d232f83b1956ee8809196d6c2478f25",
|
||||
"6b2fd95d06558bd242db1987a557fb045947bde6",
|
||||
"a1bb65ceb97b73d98812f89952a8dd3d91c06f29",
|
||||
"6fb049764a8e1c61e68f867a9fd42cf582ac2639",
|
||||
"7e50f8f6d3ea4ff4d699d8a46350ca9b478f4df7",
|
||||
"46b4f4f87bbc76dd6cd035a868500673ddf3d907",
|
||||
"05d0726345dd072d5b95b8136c881910e00efbc0",
|
||||
"4ad8f54b1cdd1fd453a784a7e56c573e02d17091",
|
||||
"418eecd61315cd3b7fad281b9bfb22c873736815",
|
||||
"b555e65b9e1da944ac12aa4c6f14295b49cf866d",
|
||||
"c30601770bfcdfcd3c093137894f0bd7cf57e601",
|
||||
"30c3e4e96b10722e0a3f1a27004ec9481d833d36",
|
||||
"fd00f81c491bc35c63ddb72cb108be6bbe4e0494",
|
||||
"75c82f898ac88fdf55f4c32940ee366e6bea12f3",
|
||||
"0313dd5fa669f78262a823f0b2a1f0ea44787e6e",
|
||||
"eaabfaf4932b7790ea966a65b6f9a9f16c2b96fb",
|
||||
"25dc6d1aa70263bfc0fbade85fbf1cbba951206d",
|
||||
"07418fe7bbddb437a0180b261f55e38ae89fedc2",
|
||||
"db8cdb15305b833c03d7af35e03abdf946136cf4",
|
||||
"4e7c2df5c4c5f8a4d40269ad01d66c09a12feb93",
|
||||
"d324d6a8adc8374e0ae6d550e71092cb6665de73",
|
||||
"e97adea34e4be1b6dcc5169d5d238f818f727810",
|
||||
"6296657c5167e8dbc89dadd5a84fe32c13189c98",
|
||||
"be57e4235074dc1981362ba89537d994f4703371",
|
||||
"ba3501692905f9cbdf9b3e7cc987f4863e48ff6e",
|
||||
"df939fa5faac2a9ec813f7b3d1ef856bac138cee",
|
||||
"0e7c38a84643ffcf94567dede478191da80ecf8e",
|
||||
"20faf996c508db4769ebc33a5d601d2a061d9eba",
|
||||
"5288d0bb27932cdef3c947c0db5c0c3bd6c04f03",
|
||||
"bc37702d1eca33414b1aaef1d1296b50d4dfcb53",
|
||||
"60ca7c8fd036c1563daab42ea17e2682399f594d",
|
||||
"3103a5e27c9fa7c8e6a4716ad7baa099c685ec97",
|
||||
"5c83b690204fbcc631dfb9ce08e3ad8bb2013cc7",
|
||||
"744e26330f76d58b3361d65c7a6bae617c5de3a9",
|
||||
"ca701a0c86244af58cc0011e28881b4476680ad9",
|
||||
"62bedd62745a4438e51f163274093be24a51c85c",
|
||||
"2894400b7d8b2eebd2c58da54fcc11f26a83e2ad",
|
||||
"1691cbd8cbf8607d0f595cb49ff539cf1aa5649a",
|
||||
"2c4ebd5c09687709501ecf3325b5b73567276db5",
|
||||
"2b155dd8d78695230d9ea3f0ff6869abedbce850",
|
||||
"61c5df33f5e90a5cbb42b490b75646045fec5b29",
|
||||
"24220318ce27141e01489a7133d36a5ec89d38d6",
|
||||
"40769bcdd436403cc6cda2b6e4619f5c88ca3062",
|
||||
"c6ee190a242f6cad9ed672c2bb2ddd5f24c513b2",
|
||||
"6396e4b80acbe113bfa305f04459cc926192afd7",
|
||||
"fce0e0b21211906978bc2f33e2aa4dab27aa034c",
|
||||
"c2a5cf13b012a01878b792406e29e7f104a7aeb1",
|
||||
"89c38e25dbaa133562781c5b75700bfa85f7307f",
|
||||
"7318411156bc0e9607cb023986999bc371a9b295",
|
||||
"7159c95a40648b20cbdbb52d0e0e99c4b299aed8",
|
||||
"61308a0b2204bfaf77f2e7841b9c05163e6c4faf",
|
||||
"c65cef23eba65016011d4eaacd9547849105e813",
|
||||
"a274c5325bcaff83388233005d6fcef54c7c711d",
|
||||
"edbda78c45a5540ad3a20ee0403206872f56ee45",
|
||||
"5eee152a701b74de55f941d8d68a4747089349a6",
|
||||
"586a20b2add3c93c9746f44da16eccbe5bb3ac63",
|
||||
"e4b807374e0033f90e57323b6462ad376711c2ec",
|
||||
"e211be66d052e4ed0e51c77407ec6346aa719738",
|
||||
"ac10c29b29f6939ffe241d9ba642f8145226c61a",
|
||||
"3e7df2ef0c608726ea6eb664815ce7e31358ea03",
|
||||
"2a028d2a3a32b779a4243a5eb2322e6088626b1c",
|
||||
"7c97ef5d1e22a60fa800c20abeb2a2c09492e00c",
|
||||
"a1ac9f40c425b6d02627616d04d732e6e883a513",
|
||||
"9defa08a5d66e06bd02a14d816d41524e9026361",
|
||||
"c0bd6c7da6ac49acf8e38ea86f1f38f583df449c",
|
||||
"7824bdab89f911cbc9c4844e122432c7d61b05c0",
|
||||
"c2f62061343cb08f0bc4ba7e90c825a41dab3064",
|
||||
"711898933be17e4f14756c2fa23275fcab81b267",
|
||||
"94205d18851257af1118c50224e1ac4caba8fbd4",
|
||||
"4c3c1350bae96ef643c72a6b1a2827ca9661a49a",
|
||||
"59f170bd88b2ee6714f2de5da63680b5ae4d62b7",
|
||||
"9b6ed450d5b8ad9241573b22fdcb488d1a8fdbfe",
|
||||
"f44234ea03ad99515f943073a98a897e306be782",
|
||||
"22ec0849d2c56385d4c9cf64a33c17c1f7122bec",
|
||||
"a50326b6ef3299b881bd90d613f97d0782a5361a",
|
||||
"72320aa416f668905b37773566754a82dd3c5b9e",
|
||||
"9d4da973037050f21bc3bc594c37d38a2c488aed",
|
||||
"def66308dca8c9a4a148d46548fbcc538a0ddd18",
|
||||
"6899d9b70875150486b4c10fceabb4bbf231a82d",
|
||||
"e84b3d6a2fba29231491789741dd1ebc013a31d9",
|
||||
"ba3b49c09b97b28c5e35ad2ceba88586d3f92e09",
|
||||
"757ba44fa70d1b3258f49d8a4612d52390988632",
|
||||
"a7cbdd470a30c4de7354778539fdb4a50e016f99",
|
||||
"8ae8b6e2ddfba263e0ab388379c40227c164b8ca",
|
||||
"b32b8178ebf63838e46200a741da5569ea5f0697",
|
||||
"9b281f385d5b7c767c2990ececeeae33a2692b4c",
|
||||
"4c39ec49cb5cc0f7ea4ef2153d7a80e871d294ed",
|
||||
"0ce4f6eb81686d1c4bd9060c33fe568a1ee5e140",
|
||||
"749a7513deab3d925d528aa4ee67524e7d5d85a1",
|
||||
"aa1ed59fda540ac6ace91dd17a455f56e05f0201",
|
||||
"6ed3b764e569e7ea0e8345d897481422554f9d82",
|
||||
"7fcf8e13dd639a3246fa85a773da91aa16f03862",
|
||||
"73bc3f5d3cf932a2afe24614e29df75f3adca969",
|
||||
"3b4d1ac660029ff0ab7f67901eeac3d7eb78264c",
|
||||
"a55b643b9e0dedb6da9e2e292bfcddbce73ea856",
|
||||
"f920cff2e0eb5d8eaa1963ab6c0a5f7ead426eec",
|
||||
"2ee672c1923e29cd256ba6b164be9378558bae18",
|
||||
"8f3b0986577fc71c259a90dcb12facebed94a4dd",
|
||||
"28286b8566bcfe223e27de8e8528672ef7686552",
|
||||
"60cc5472c70dedacc9c35329d348b3512f0b7684",
|
||||
"59204498f84611716ae173e699d88152a85a2aba",
|
||||
"b2aa6d13e04c18918b563d55854e7672ac5ea25f",
|
||||
"3ff385bd4659b3806e35752500b2a7d18438a3b9",
|
||||
"df2eee9c3f81384843708244bc5b9282bdfbaf44",
|
||||
"347b6bc26a3de4bb584bca65bbdbc97f9a7c822c",
|
||||
"04825179dfe97f8bda20c25683e7a04b2a5a26ea",
|
||||
"86726851c5813fa12b359902a7d3cb4245f9846d",
|
||||
"f571ccc44a8a2ce2ec4f3492ab70246dd3528230",
|
||||
"1cb0471af86f9ec7c8ebf28d89035d3360e5a760",
|
||||
"de7cd46ecfe8ffc1845f54399798c2bd3435d27a",
|
||||
"abe63bdab5657026379ae3dc3314d1d74441ff3c",
|
||||
"f6ee583dd1a29034340995e692ae57318a7bfdbb",
|
||||
"fc49498fd063fdcd2928f8c151f80ca2cbcc1591",
|
||||
"3494924ac518bb1edf39385728de55c4a478da5b",
|
||||
"b935632b67f7dca5013f77f1ada6ee6800204370",
|
||||
"9347f7d33cf1d6428b7fb79e0b515cef51d55708",
|
||||
"7ae2ea9dcbfae8bc13f2a5d6b71ae82961c687db",
|
||||
"3de907f5c95f33d28232c4a50c893256e9a4624e",
|
||||
"189e164fa8d4691f652c8e809525a2487807c8ac",
|
||||
"60d077985f65f86dd9623dab0c5ebd4026113dfa",
|
||||
"2d0d0b1a815375825ae0b702f966404fe5632ea4",
|
||||
"f960e47109712881dffa6732dcb8103c18748c5a",
|
||||
"82d55eb13fee9545214b3bbf58091c2499e022fb",
|
||||
"a2bf54eb5b3e5acb847c1ac16d06a82689f846cc",
|
||||
"bffead1a879079ed8daba69bf509723be372be18",
|
||||
"99adaf337cab34f56383c1fd8d2c4cdf53bf66a2",
|
||||
"fbcebfd1a6f9f395f2384914e4d6c942fd93dd69",
|
||||
"6535447739d1cbb2d008662a2c543a5538da4261",
|
||||
"6447f4b736947c2d3bb6f3377bdc57c102a30e37",
|
||||
"fa3b2a1ceedc63a3cf804942b0242ef81de62405",
|
||||
"a02661b0b6bd3535eee8afca694c52e033d08730",
|
||||
"05b736f44e3f625bf9c3d4b2a2eea63a8df7d91d",
|
||||
"2056b07b25e55cdc4693cf754f979bd06510ad68",
|
||||
"1a669306c84aa8c3e1b5504beb510d6a4354f0a4",
|
||||
"490af3c6aa59844fb758ba43132309bd8407a802",
|
||||
"4202a38283d46d27a70ea139e557e3ace5afbfe2",
|
||||
"c25d4d2acf1608b6d1fd460e90f781d32dba55d9",
|
||||
"1cee6f3b8d08dac6fcfa0ce30d1bbc67a7fe0c9a",
|
||||
"e7261017e8744ddb8a8b3145111ddfc160942388",
|
||||
"bc09dd500ae7b60067c928ed20ce6967a0c8cba0",
|
||||
"0b9997111802fd4708e4a90cb6fbd0fb968eec7d",
|
||||
"73d903a6e969e4c55c996f3c2e71fd737aab0a9a",
|
||||
"81d310901256725dc3dbe658fe9768239299952d",
|
||||
"7117d019e61351bd711bc04d536ee673519e8866",
|
||||
"a4de4109230439a3f0f9532785cc17386cdc5d5b",
|
||||
"5203d0816976e5539ee2a53f80338140984777ff",
|
||||
"f5f3d43962e494689b6cfc49029f54c49f36cc20",
|
||||
"32b489dfacd741e7945a2bd851f6b6d63f0997a1",
|
||||
"c6c96367123866ae305ad3b7a6d14bccdf278430",
|
||||
"b67ae3d116b48b86b7a2a0556fdf43a567c6fcdb",
|
||||
"ea2dac39522405c5977892564edc489f0859870a",
|
||||
"f5f1a6731486a3241814aa32760ddda22cad09d7",
|
||||
"267d052b683ec1e8e9cb03fff7594ab5e16ebd40",
|
||||
"fd8356698432d629e82c1f0430809f5d4b3978ca",
|
||||
"ba23700b82e09b5574d9f51941266ad5cda7c5c6",
|
||||
"416e5da53fb50464ede3efc00b59b646e7f2bd1e",
|
||||
"5b93aabe50964908a2be04131b74ab52b4580ac6",
|
||||
"55d832cfe5719b4d3312d9db4afac6819ff2350f",
|
||||
"cbd3be2d22f81cca65c63ee55f632075a4a947cc",
|
||||
"519c1bb35a1bef899bf70b03813197b09877ddde",
|
||||
"2765beacf45a8d1eb902ab06d53aeefcaca60d67",
|
||||
"730e38144f7dd8c6df14211965277d615973d815",
|
||||
"e224031d709c56998298b1558b8ab48353087d0a",
|
||||
"4d82f48ca4bab4928292903984b13646577e6fe1",
|
||||
"5c2eba1e9996149317458b0a44fc37f080809eb1",
|
||||
"792754253877a3be70481e94324f2d9860fd6ce6",
|
||||
"f918e7c4fc2d02e9e1ac7e5c7a664b23acdb0ec6",
|
||||
"765d6c16779dd07e05a5a430b460a709141534eb",
|
||||
"f741c54d8e099de1b13c053b3c0bc14a12ab57ce",
|
||||
"2957dba2823f4c1df3a36446aa6770333694978c",
|
||||
"6450b0b2055288605b075775525722161dee9382",
|
||||
"91130087e53328532c6a23f74451b5b6b7b31bda",
|
||||
"52bc45485d0d489c98db495f6f9fd28247f90e9a",
|
||||
"fc099f901f55f79daae2f91226cdd4b722895482",
|
||||
"257c8f051d6110360c2902d12d2d5c8598177864",
|
||||
"46651694e48ced51ad3daf2c842f3f089810c760",
|
||||
"5e0fef152403f7c969538cde36e1afbffe062ef5",
|
||||
"a8ef5a35dfaec15bbd3ad45ce0278dc67c2a1cb0",
|
||||
"1568cba236677c952731fa99d4cb77f7ff295bcc",
|
||||
"5ef980270be1a44d3f174e3a32a9ee320a7207ae",
|
||||
"ae9984829aa0e9bca22c11c19792079188896b33",
|
||||
"4c56d19327f821e40028a3c85b3a57d7ab95e1cd",
|
||||
"087ccd321532fb9e6d0b4dc6b41fb5da9f2a3d07",
|
||||
"5929efe3039f964b08f366a22553acc9b17fd39c",
|
||||
"7ef896f73a531269ab7e0a3929b7d0fae4d77ebc",
|
||||
"a096f3f657c09450891e95becaaffc80e6be8573",
|
||||
"afa9f40f6f847e7e5d01d6b34248ed1375310a38",
|
||||
"d4dc74f00cf43e0f0cc1fb23c9351fc194066a7e",
|
||||
"d3b0ea6fd747a5a7b4197a3ccbd8c58f87d2e622",
|
||||
"a08b48e9461237bcd3f31396d0b5ef95809c0c48",
|
||||
"23439f0f1332f4c28a5470ea70b5639bb6274173",
|
||||
"8b95ced29cf8bcee89b7cb8bf304e2ac19af3229",
|
||||
"9b02d601852578bedcec852becccae7a2f2f3676",
|
||||
"f75b4ca108cbdff0f70c593c00cd4e2ddb6524ec",
|
||||
"19671ca0b42c3de113d239b613fbca3cd9698297",
|
||||
"47aee42e8df5e26964a6afcdad13d9adb264a046",
|
||||
"90d6353d339470c65ad41bfcf908c4211b6114f3",
|
||||
"c424a84d1767d76e0548e7f9449e589daca5402d",
|
||||
"436caccea52b061196e47378566811e1bbd8bfa6",
|
||||
"547d444254e461df5f557ed90a68c623635d9d61",
|
||||
"aa7437cb15a217316ce9f9298933dc3b241dbb9d",
|
||||
"049353a3d3473900d6f1b8787b52dd4006e3ea16",
|
||||
"3dbd3bbb0c5ed182a7fd5d4964e0a1795024397b",
|
||||
"0b77d01cc53ff095342e0ede84b31950db2c1c21",
|
||||
"2a8ecde4d001d6e2370c9e4dfc94fc9cdafda543",
|
||||
"c9365acb116ac4e0f13f786a968404839a9941ab",
|
||||
"8c496d1b4fc14f90ef43d6dc93cea0033353c519",
|
||||
"bf62083c34967e933125a6f8c953790e712935f6",
|
||||
"5a260ffb21cfadae9aa3a80e148b1e838aff66d5",
|
||||
"81e7ede501486351847f116581824d6797bbeced",
|
||||
"69663fae94e0d7abd4e3a659f490d3171678327f",
|
||||
"a76f630d135721108db5e0f0bc7010a03adade0d",
|
||||
"77bc193da7be8218ee8225903e334bc5da140033",
|
||||
"820a23e16c599168d552f4068614e6b84aeed204",
|
||||
"eb6480e87aeecb950d6214170059f9de44e12142",
|
||||
"3643de2fd794fba4d9fc711ebb6f8d7bb853305a",
|
||||
"2ec1ffe2c17c4401bda89f6ade266f7d11de0b2b",
|
||||
"c935b1892cecf78cb8ac0f931c06eb4b8cd6fbed",
|
||||
"8d8c245a046603d55dacf21c675ff8d08f2f6c49",
|
||||
"399e9d5a67d1fefba95590f47e758deee3582702",
|
||||
"88b167dac82c76e39d070fd29c7c01ced2c06394",
|
||||
"e84e6faf60ef18890db8b038e59953fa8ee269a8",
|
||||
"7b2bda1731540468ecfbe63bd3bb2f559781699f",
|
||||
"2cc977a3ae387c509a4744c5d2413fe23eec7434",
|
||||
"b3563a6a6abf98000e8bee4e5f8d278a5669db4f",
|
||||
"7900b7cedf6e2e6ab5ad94139957511249fc98b7",
|
||||
"29e2d91a6bb63e0cb3e95abcdd38fb84311e8325",
|
||||
"3299188a53081b1fbde608eb9b6c5fdc3c65b223",
|
||||
"4f6fb80e9a3d05ce3902c4bcec690f6d31da9db8",
|
||||
"76f808e48c852aa1e3d29d45bc56fb943f91302f",
|
||||
"cfc2fc795bb58d69c560e86d0647f913cca9d575",
|
||||
"aab9f8af4bf136634492f92eb276ad8511691c97",
|
||||
"95cc8f92750ce0594128163f25b94439806ebc9d",
|
||||
"684dd0f19b0c00b04f722193edb78c13f0e0bf6a",
|
||||
"d5de1b4c9dce7fd3fd5a623a19cd7188417e8ca7",
|
||||
"d5e6591aade627f43228d73f9effa3fac1d7a073",
|
||||
"e8fda62a36f6cb18f6e8e7c24c97e741f2cc15ff",
|
||||
"dce3b7610b26c123cc399586971e40f5d7204a32",
|
||||
"5c4a64afbc7fa8816124d33e63912481535801f9",
|
||||
"cc1b1e771754ced2c5052a45a487dbab139d5e2a",
|
||||
"c3a38aa8fa0f1c1b19ba06cc8731d605ebae4037",
|
||||
"0da6321df53c79cd7be4ae52131f70c06b483a90",
|
||||
"e5cb93bc9c9e319fde3c2a0c618fab884e4de1c1",
|
||||
"8493deed55bd9241020d52e49d87102a5a262980",
|
||||
"14f8742e937dba15b5676cfa2b2f7fa23084b508",
|
||||
"745d186a53397b981e4d76cb50ac5c1578d5ef3f",
|
||||
"ed9c4c150fe525c51c902f12797bd1a51c2d30c2",
|
||||
"52842828e9b02efb10ab687917e4fcfea8afef43",
|
||||
"88ebf01384c8a82eaa8e3fb3c4b8be411c8da681",
|
||||
"eec189c881245d66d71842a6699c1becc3322dd4",
|
||||
"c96080731dc25a5b439a76880922fa933fde1e1b",
|
||||
"e1086bdba41cb1dcb3c7fa7bedc3a9913609378b",
|
||||
"3005f5c28ee615d8fafb0410ef74afab21c0a250",
|
||||
"37d6280f625db75e8677a5f7edddb26b1c80c223",
|
||||
"61e62d047ba5037e6959744681709ad631c28d21",
|
||||
"01599eead34323fd3d6172594c3abb009c710236",
|
||||
"bf329f3d4ff25f3753c6ee413ae0c114afca8ec3",
|
||||
"3f8c0c124807e1b554329635d26349c40bb373b8",
|
||||
"99849f2b41b6c5d1bfdccf9bae54880c086c9147",
|
||||
"d722a780c36be715a27644933bd053a33e0ab8ed",
|
||||
"b2e7b1cc84d2a938a44438c1dbb1964fdf826917",
|
||||
"9cb78a414c185c55bc653b8a6640a5da98603e07",
|
||||
"81c2e8569ce844d35ff5076e767e5667823e2c7a",
|
||||
"a33c11a246478d1704b61bf06b40490395a35225",
|
||||
"70ef10d8b4d0b396fcf3d145926be9b3f33c1d09",
|
||||
"1e91949e730d4b370fc7573e78fabd9743530586",
|
||||
"2d8610c7e48b387a90a51fe26123a268da29ab9d",
|
||||
"145b1d913f5cf742c08086845343b5fcf8c988aa",
|
||||
"8a2a73aed2b157c0ab69550c31250c02c886558f",
|
||||
"6b3ec12d6a6bea8c2bc9b375f99995b136104b29",
|
||||
"dcb87acc18da271f4b7d668ff928d175f5e8214b",
|
||||
"acfd42912e25f21786807c6e8b21114d0e08d1f7",
|
||||
"d2dbaab513c1f67b989c51f86ea48585ffa19fe8",
|
||||
"a95ad3047c03d113d1180fadf2711a3749f72e97",
|
||||
"d7cc4a32063b7ad4372eae8bd02d016c03e22cc6",
|
||||
"529783fd8948ab10ee41e5febc1413b0a306fdc2",
|
||||
"728b726e5bda1731a9dc12fff708981eee424b23",
|
||||
"e41b7dbaa3ce306dde66ed59fb535acd96bf9798",
|
||||
"d428b2e059a734a50295517d29ba892b63215e5e",
|
||||
"9eeaa086c83ee78c2ea79d3f0e7693bd10f7e34c",
|
||||
"659f727b4a6734b8e2f523907b7e22917889e578",
|
||||
"aa250c7b435485e150fc797ba1e9b20b6945c19e",
|
||||
"1717219aaedc95c384bb7f13624fc6278d152948",
|
||||
"5420879e60a0a73a15b2095de34ac4ed59983561",
|
||||
"43a557fe4212f2fdea2bc774a6943f70247e6325",
|
||||
"87de1176c7e0316c3aee8b27b59b6886018cd966",
|
||||
"eff8acd9ea8570d7fa641865ae7532a8f69ebb5d",
|
||||
"4effcec62202f5e8dd1618bd6859847d8014a310",
|
||||
"95423f26cee41b6ab01b2466b09c0fb421f6aacb",
|
||||
"66418ba2eec57654dec8c6ce08d94105347f45a4",
|
||||
"36a13cf846b5d88a9823e686089ff812a639265b",
|
||||
"a9c4e592c9b6cc65565d6276e76bf8495aeb20ed",
|
||||
"9798b69aa6111f75cc8f649fc7ca64d7b51850ad",
|
||||
"82786c1dc19bc326df4a8c4904203e2297b2be2a",
|
||||
"6075ab3f0d54667549b0e73d3b1ceee29fa89d31",
|
||||
"691bdcb475375321ff12b6914f73e41e724b1bd5",
|
||||
"a214fb4c8c629cc9e6430316ed036503cdb79664",
|
||||
"222e65439aec018e46817b9301c0a13d9e64fd13",
|
||||
"ab4d72b9fcf370cc87f3c14be57c02b7149caa83",
|
||||
"d90f5722a2624dc55a01cf5a243d25727407fe19",
|
||||
"9c1398288cea88446e6cf0ea5d2634aadd7ec057",
|
||||
"00e0e4864c4fcfb1a303f655e549d8a46d53fcc7",
|
||||
"6a933e4c1e1dbd4072a01ac549deec81cb39be6d",
|
||||
"ec5e0773a3199a94e32892444611c345a38ac8ef",
|
||||
"4355112f572f124e42bbc74aa9e615b9f362d522",
|
||||
"cf55fde290dcdf2cbe91cd7f3421e763d9f6db62",
|
||||
"19dbbbb5d9b076e3e184e18cdbbdc395c4323be8",
|
||||
"4a4d19686af4dc058c7b20e717255b5fe295a398",
|
||||
"c052d869700ff60057fb93dd791836e3886a5398",
|
||||
"f6208aa606ffc6f1fa06383534abc5303087094c",
|
||||
"99e3b71ff8e92a36bdc1fb9a2495e1eab2841414",
|
||||
"f3d6193e684ebfb5ec0df9f96df3264c4685c561",
|
||||
"92f6124c73bae0da8673adeac94c025c25cf291c",
|
||||
"7ad6d768773a77b6d86279327b8d83ad6d04664d",
|
||||
"fce8d87884a1dc2f81b1b8f0778dafbc3a52da6c",
|
||||
"f7abe720edbdd905a325b6c1f17a707f6b25a88a",
|
||||
"f5ee28189c92ecb3ac8e39731937f025c7fcade4",
|
||||
"1b25cef981a7d3e84bd77bc8d75b4e8a9d42da0d",
|
||||
"33cbfd39a6b231a6cf41cfe9ae91550e988abc2e",
|
||||
"4238b24b1df8dd2c01aa9719560e363f6705fe52",
|
||||
"5e3d3593b5fc8aa790b6072bcffa440a8102dbef",
|
||||
"873fc83e5afe2d7a58a7b3bdf1d9fb9b43ad678e",
|
||||
"620f0aa2d9134b94cf8396bb1793a03f1da11cbc",
|
||||
"3f1629ebf3cc02af5cf666b51206b8fbf8c020a1",
|
||||
"cf47fc3f9511af4fbb9041e1b533cf00a8db7c85",
|
||||
"b7bcc338e00e48ba4ec7808fa04e17f72bb0c6ee",
|
||||
"958f4f0ad68ea2bad4a71cbf3d07e6f01f7800fa",
|
||||
"9c1758498dac3963ed1fc474454012146fafbf28",
|
||||
"44ea1a0c03c82feb9e561ecdf6663244e95f5c6a",
|
||||
"d19ee330ad6fed7b7d2fbdf0dd5c96673863a606",
|
||||
"dcba1860a53be99e2b98725681ef971d38f6adcc",
|
||||
"bb73501ef6fe994d0b6d6e92a3fee6d02cc34a86",
|
||||
"a634edc54f31a151e075043918a1ab1ef667d75c",
|
||||
"70d9a7d460bb90d00228e363ae0e3a02a990fa03",
|
||||
"196fe4d6d4ccdc2f6742bbd4ff7d53dd576a31d1",
|
||||
"08a02a21f8b27fadfbae8f80fa08eaa8453b0da8",
|
||||
"46bdfe3bb814d5aee9e4cb8a3df9d4c0c0155676",
|
||||
"bc611d3207c7c83274029ed75e0d7a0842871182",
|
||||
"a2be919ee2e8305377a8a88fb10ef1520dc0792c",
|
||||
"e3285a3654eff5d2d4ac9bd00b445d92027d3c3f",
|
||||
"b0df17f74cfbedc15d3d37718db0631def860638",
|
||||
"3fe8c5ec18180d2d7f7826d9c26d2a36dd1e3a7f",
|
||||
"f19dd315d535b2e5547252d3d781b0da27fa5cf7",
|
||||
"d8e23fa6edc48c20c750563a2271524f5c7edd0d",
|
||||
"43f6f591ac952854f52cefd0f45fa230f589c8b6",
|
||||
"f706cef7fe4e1dfefbf64886307711ac27e88633",
|
||||
"1b15a74007771eb9146c8e06476892c92c1d111e",
|
||||
"01595d5229194fe3dda764e884d4432d71fedbad",
|
||||
"3376cb6f201ad88d908be08bc068e1137d54edab",
|
||||
"77170527d2de9b9d7c3ba8371021b78468745cb8",
|
||||
"f7828300974982844f7903c9acb6f2933966ea8f",
|
||||
"113d3e05e114d71b4e7a294b5e1fdd9ac964996a",
|
||||
"3dd13035cf8ed8fa4be49cedeeab8852150c7132",
|
||||
"933a5e1f1cdedfac2922d1701acc3ecb665521c3",
|
||||
"dfaa9fffe27e0b8898b4a97bbc95778a88a43037",
|
||||
"882451528aa8e48d2b4e3c2fb45b18b69e743bd4",
|
||||
"3998d7313448f2835a07aff2ca30beb6d076b5c6",
|
||||
"fac84d9ab171ec5b3e3345e60eb86f4b84d915c9",
|
||||
"e6323f4d8d15c51f983dd030070caf931d66b80b",
|
||||
"2b51fce7c00c0fea47408441ecfc98823068286b",
|
||||
"5c36d5180cd58a51b7f8ec410f1b0e74f53b565e",
|
||||
"a1ad36ac70b427e676097c87229d13336774b37f",
|
||||
"0224e08d32f8642f39503fef0df6f1237b1c0583",
|
||||
"15a2cfac5cae8f54e33278f0df714a33b3d0ade9",
|
||||
"640f1149b5976278c28ab96a2b6e56259cdde4b0",
|
||||
"fd81804ecc7a9e12fa55976196d7201a07f22fe6",
|
||||
"0653caa316c76415416cb1b939ec0d52294bee76",
|
||||
"aaad037f557a9f0550a1c17425c87e02f1247b18",
|
||||
"b8befc039f6b083e4f15a14757303f36ccde447b",
|
||||
"1a8661a9a2209d92fe047a5c8600d7be12cd126b",
|
||||
"b7ce808b24e6f895aadbd78e202d144839559790",
|
||||
"8092c4698e9b797cdb24b3cd53b230b6e342dcc6",
|
||||
"b872f67e8d25cd1f8b0781976114f5826a2d5914",
|
||||
"9ca4133308e64d363d6a1662b6f8b794285a7d99",
|
||||
"412786052a477ad8f6db40c783b18d26385580c4",
|
||||
"5fc77d1ba542bf5783132458b181ae2977a1f482",
|
||||
"f1118ccf09145691c7f9a5af00ef9c304ebbf899",
|
||||
"a1d74f34ada315db6d91acfbd4cf67e8df2422e2",
|
||||
"7448859d90ec18d5a2f1f3da04343b6cc5f33711",
|
||||
"0bc6003615fa23c5a0edfb8f6a8ffbf0ff27be43",
|
||||
"fd3992b114340a09f01b0345fcd1cc94e6643185",
|
||||
"870d1ed56d764380bd36692571243423cd025688",
|
||||
"affc3e75b3feada02a7ffcf690843b83c1d8deec",
|
||||
"6acdb2a34170e8151993867b7e34896e887600e8",
|
||||
"9c4b5f193b38d0d19234a2472249e7062d58ca13",
|
||||
"6605b6a3589c4df2ec70d81da3c151eb2daec254",
|
||||
"58df496ff6a19a5deeef5d6e3b064f3bfa7ea350",
|
||||
"bc7474c739a0d0daa5fad5ccf171e168aa52a82d",
|
||||
"2b43573acd47d8dfad89c01218e9ec581ce95067",
|
||||
"73028241cc6f6f6004b4448056e2b3692016e93f",
|
||||
"191b283db4c19a1fd596997a4e48f9a64163d6fb",
|
||||
"c1cf36701ec466960cbc46e729ef03978cdb7140",
|
||||
"959f8dead8def7554482ff590f02c6cbfab55a52",
|
||||
"a849ed976b7f464b322acd6997407bbd94744016",
|
||||
"2bfe00fa4a09d6cb4109ec7b825910c2379a711d",
|
||||
"e844c9f7850945c17a9ab01ca3f8809fd627ca93",
|
||||
"15a897a7790aebe8751703953fe76fa631c84e41",
|
||||
"3f040f7a287f0700aac10df1d9e923c1781d067f",
|
||||
"4fa1a2c395aac52f0ee79608dc000832a6239a08",
|
||||
"bb7a799e9ef50a1c5133bb017cc3871f99f2ea80",
|
||||
"b686668366529c0969834c00a8e3ac957ab92b48",
|
||||
"d2a1cb43dfcf5fc4b4eaec33dda1cf2d5ed5dcb7",
|
||||
"4092a7ee4201b434a8d0931a0af6130eefe85222",
|
||||
"a771348eda9592b9bc300b10cc71d9d08e54ac50",
|
||||
"a749b75db4ae419d490b2320b0d50f7163375e84",
|
||||
"b852cbe4e70f83b62015709b4b829f150b25a4d8",
|
||||
"dbd294d1537f586af756d0223c8509946fe19c72",
|
||||
"b59a11c4329a2b86b8261d1d68f6986dbf7a1750",
|
||||
"aa618f9613d155e32e8a9cc234ecaef1942fe26d",
|
||||
"53c39bf13c20a523a3aec7b43374228363af1c23",
|
||||
"873cd59031929efd8b4d80be6ec9dc119cf7ff63",
|
||||
"ca95fee72c33adb37add739f4c2d09e81ac715fb",
|
||||
"3ffdb9590ae3d5372d92748c6e4f71bdd7d9c0b6",
|
||||
"88d32bd5c7d0677744733b0e372614eed4a83771",
|
||||
"2a4fe0216e7d4183ceacf097c889e456ee06bcc3",
|
||||
"d557f3208e0ffe408c8f63efe696f0484aed7cd4",
|
||||
"a39cbef0ed86ab17b5ae980cbdb3a627546562ef",
|
||||
"0321b945180f1a6f1c0a7cc02cd9b9a5f5f9d347",
|
||||
"03c8e5e03c6538e3b9d13d958cda825156cb02f2",
|
||||
"e75bff7bbe762ad020151db8cae641a5a141252c",
|
||||
"75cbe4a9a1d86a8fd12428434112e077f8617d83",
|
||||
"3c9d72392a5f7675a100beb3238dc191ae6adac6",
|
||||
"0b5aa8252ef054ca1a867a009283a56ee14feb03",
|
||||
"8d2e5c9895663805202b8bda9121748ced63b0a1",
|
||||
"6ea012cb3073e1ff2450b7a284f7800fc64acc97",
|
||||
"07b7e823a22e198f9bae145819a21a5e39615774",
|
||||
"c4f6d1764dd71c7c1ffab960087908e0ee1ad562",
|
||||
"dce23af94b0de5c12752b312c98b9a817aa9dcc9",
|
||||
"225279b710df7296cbeed02b596b56115e9d98c5",
|
||||
"d4838241c2f7136d5cbe0bc83076507dc3a39b99",
|
||||
"4c3254437f705b33869810913fbd9ccad29ccff7",
|
||||
"6a61fca4e7d8efabaf07e346b2d4deb27d95e5a7",
|
||||
"18edfff26b39300f03e396bbc604d517387a28d4",
|
||||
"ce4ddc63b688bbee53b8e1cccd12fe2f271af558",
|
||||
"05d0601b66880d011b162230c5a3845bb9f22283",
|
||||
"58fe4103287f33c11e5e08c7a5b5efe715d402a8",
|
||||
"28a67cdfde3d57d974789c01588e434ec4b41245",
|
||||
"5cc1e0b3d3a611d216cab6f12836da0cf72749ea",
|
||||
"460cee911aedeceab401b6e835193ec3770a7b98",
|
||||
"b0cd7fa11857007b96a6f78826e11035870ad99a",
|
||||
"0c9b9f3b58aada909f2c4b53c9bbc677b48a01c7",
|
||||
"8dd6a95bbb3938960713d1432f1ca201fe769d38",
|
||||
"ca65fc832ce0591fba2373066723657af1b35ea5",
|
||||
"658914b3780029f78e7db8c114650272579534c5",
|
||||
"da8ba2bbae6239c366a8c69b8b4192751caae48e",
|
||||
"a4bcafd5dcdf7dc991383618d166492152cf742c",
|
||||
"dd2f5c849a2526fd9f3947be6fba0f798d0ea05d",
|
||||
"b9f40cbc79238063354ebda0ecc5f17f30bbf7d6",
|
||||
"30084963244dd8b1ae7aa35fc698bd813fea8067",
|
||||
"2f2d8797e98f240e009b6ea9ac2dc642d7cfb436",
|
||||
"8fa41bcb155b247b4b98029d26a233bfd915fa5d",
|
||||
"cfa98ee1bef7ba2b4fe531de38ba46a7266f9d3f",
|
||||
"44fc9a4f3981b742e22769e56b317ae76528f8c3",
|
||||
"444b9e41e4e09b539bdba1f2e2a6b935d72c744b",
|
||||
"669a94001b8666c725674dfe634c706dcc4088e3",
|
||||
"d3b0c191dc5e53bd1c25519c66fc3e33dd05d781",
|
||||
"1ad560bd6f9e702cc48035a699418f68e852a7dd",
|
||||
"acb09fb1bb00c3f527c14cc58aeb61ebeff86e24",
|
||||
"ef046f2e39af156ae4e227c4761918531e811998",
|
||||
"524b1abe9e01621788528b89dfc77f98114406d8",
|
||||
"9b7725d7f5070abbd63aecbc43ad3c95ca451cb2",
|
||||
"1d8876c9d1068f2df16d514009191c487a1f5652",
|
||||
"8ba0908a9e885a955ea3159f3f62373f60f6e8ad",
|
||||
"731244cbab264eec570d725903a7af17726eac02",
|
||||
"c584df0ee552d355e6b0f000f6219084c304b475",
|
||||
"2bf817a2113a376f73c7639ebc562357cee4750c",
|
||||
"93a87c51f4d5dc4742e840a3fe38a510d66f7f11",
|
||||
"3ddc3885f58963d2bb8bfcf1ddbb3febf89fa5b1",
|
||||
"0687d129962bd2d906b6d9326c8d0318d2bc98a1",
|
||||
"784d0ff036b5d812003ade8756e825c8382e2006",
|
||||
"eca6008ac22644bfa7784922026516ca2d6a52bd",
|
||||
"1686924230e7bb08c458c9c640588c2bb2d97346",
|
||||
"834dcad3ef464483b316470bead5adbdb6ef1d61",
|
||||
"199e9dc4f1ca75724b52a531959b30d121a5647d",
|
||||
"c36ffa60a42b70d66ad7b7e4c412a5e3512e893f",
|
||||
"d42f2beb114523527a111e2088cf2a76418b4cea",
|
||||
"e08fb8cba473cfbe29a67db6aadfc08155b6fac9",
|
||||
"3605f62e6b0411134d4085b7857fff4041ea26bf",
|
||||
"26c0c877692664b43461c9962b7591d33efad1a0",
|
||||
"d6d744471b895081826d81118f1e5cefad2f05bf",
|
||||
"2d7f5fef141b66e9a9714c120136891b011410f5",
|
||||
"d68077b9380db564151c842be4c8b53f5de1e1d5",
|
||||
"3ba847423badafd9e7a648cd9d9902018d1627d3",
|
||||
"9d562785f7232ae7c7b3498a6e0095820190c968",
|
||||
"e0be45044b759131d7f226bdfc3d17ba7214dbc9",
|
||||
"54e14e1b5d6ee0bd6d4d348a7e32b0975624db2d",
|
||||
];
|
||||
|
||||
const toDelete = await db.scores.find({
|
||||
game: "iidx",
|
||||
chartID: { $in: beginners },
|
||||
});
|
||||
|
||||
await DeleteMultipleScores(toDelete);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-jubeat-night",
|
||||
up: async () => {
|
||||
// copied from seeds before they were removed
|
||||
// the charts themselves are not guaranteed to be in the db
|
||||
// so they have to be hardcoded.
|
||||
const nightSongIDs = [
|
||||
724, 723, 1026, 1028, 1043, 1030, 1014, 1032, 1013, 1046, 1029, 1031, 1006, 1047, 1018,
|
||||
1037, 1008, 1021, 1023, 1035, 1005, 1034, 1036, 1038, 1040, 1025, 1017, 1015, 1011,
|
||||
1049, 1027, 1045, 1019, 1016, 1020, 1042, 1009, 1007, 1012, 1010, 1044, 1022, 1004,
|
||||
1024, 1039, 1041, 1033, 1048,
|
||||
];
|
||||
const toDelete = await db.scores.find({
|
||||
game: "jubeat",
|
||||
songID: { $in: nightSongIDs },
|
||||
});
|
||||
|
||||
await DeleteMultipleScores(toDelete);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-multifolder-stats",
|
||||
up: async () => {
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
"preferences.stats.mode": "folder",
|
||||
"preferences.stats.folderID": { $type: "array" },
|
||||
},
|
||||
{
|
||||
$pull: {
|
||||
"preferences.stats": {
|
||||
folderID: { $type: "array" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,571 +0,0 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
// ^ pulled from my local raja install. some of these are in the db.
|
||||
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const HAS_RANDOM_MD5 = [
|
||||
"b94b380dbf8ebb9e132f9314dfc90501",
|
||||
"68cdc6f205aaf61a6f7d534df9a1ca89",
|
||||
"55b9007054f81b9c90afe4ceb487d1a2",
|
||||
"a808756a7ccadc5fa8bd2811acdc5757",
|
||||
"28fd26d7e8b10ebee2c9fe66f6c2f3e1",
|
||||
"3acc17d29dfa493069a4f190f28201de",
|
||||
"c0633baaf71683c2e958abc05cc227c4",
|
||||
"5001337d252b64edadbc5654e2653a41",
|
||||
"e7cde366b201c84d3e802718fc6642b2",
|
||||
"2d693e4081da35517527806454a563c3",
|
||||
"2b91d8ce1cdbc9a44f71c922f5b4207d",
|
||||
"f7f6b9e9390bb2744290b4673e111022",
|
||||
"472590a92b09bb1250ee5b7b6f209373",
|
||||
"0dc76d5e5fddc6768b1a2090a4afe35f",
|
||||
"5106e14f3f7dec7bfa4177cc6a3fe815",
|
||||
"3c9b0770aeedce6f973a4776a9ba7fd4",
|
||||
"ee665f640d74d3447718baadf490d24f",
|
||||
"13aeb61f696554359f5defd70379d677",
|
||||
"5a9c567289fdacda73b4b28d125e5c13",
|
||||
"e81ac8710f19eef8ee3f055427987e5a",
|
||||
"dee90bfd129388419e72f0df85b53bbc",
|
||||
"97c0cf8cd7c184d76593e12083bad836",
|
||||
"f168af78895b093cc0fdffeb771cebac",
|
||||
"9dfd19c33b08c9f710ca6a43bc0670f2",
|
||||
"6c86021350b7b105995502dd24cc9bf3",
|
||||
"7450fecbdecb5b657ecf0885d64c731f",
|
||||
"7977867add6f1888200ce81f75ee255f",
|
||||
"28bd2e6da722861c9d209e4c28ac152b",
|
||||
"beec0ec093eaf6c9d3f0c55ff573d526",
|
||||
"178b00a163dda5688ed4be49a88465ca",
|
||||
"2c23d407ecdffd405d5c9c95cad7b91e",
|
||||
"09b7fb5be08b8d07594e523a29405867",
|
||||
"03b8718bf088f6951cf4e2e631738a22",
|
||||
"5c160092005c193ab138ff0fe46b9ab9",
|
||||
"8c0d6af3f1f4e2e73f1ef97d4fc2cd40",
|
||||
"90bfa7091b38bc322af3ec08cc455e43",
|
||||
"b3ec50bc5d0c8bf9938aeb39a215139f",
|
||||
"6c771af5d84f3623b1271b31b058959d",
|
||||
"c80816ef8f631d72a2dfbb4634079934",
|
||||
"c00c50a59ace750cd8cc943897b39959",
|
||||
"a8bf592f5ebd7869933ec1b5dd07333e",
|
||||
"088a4dbae11c802dcbd0b313eeb69068",
|
||||
"726cf167d76d14c40ef22c6684614107",
|
||||
"4b445b51fe38e7a3ec5421cb50cdae1d",
|
||||
"e420e30e57ba5d1b6e688864001684af",
|
||||
"570c7e899e1cade06de5ebcbf52d9c02",
|
||||
"9506d011abed48e851b79a7a5691d143",
|
||||
"11c506b28a8492bc8d8f39bd423115e0",
|
||||
"937e6e8c5c22398dca8bbccf6f519ea6",
|
||||
"ef50e27377664ee1e2a29ae3a3c91b53",
|
||||
"aba3b40c23f6d0a8c19aed91a1022d27",
|
||||
"02d3ce07e8862a502a8e9a9353039713",
|
||||
"46781572ce1711fb04c6f065bacf02a9",
|
||||
"3482fe1a9ac768c9f43995507afaf2d7",
|
||||
"72f120200fde9fc056bc65599a7a4906",
|
||||
"f898064566edbbed05e2208c10203a9c",
|
||||
"72e67be42857a55beb4d5490d43365f1",
|
||||
"f60e07a810f5e8fcff965b20226e064d",
|
||||
"2ab44688b54eba07d575e8df7155a206",
|
||||
"b7209e898b880b413674236422891f41",
|
||||
"057180cd0d9edc8d6f3ba80be414779c",
|
||||
"bb279f79c74b788372ac513e8c97cb5e",
|
||||
"830d10ad7eff9f489ca1a3f1226cc19b",
|
||||
"bc3dc318370357a91a8d89eb3bc73def",
|
||||
"011b37018971ee57a0956e50f3e67cf2",
|
||||
"73f88511307ab1c632c4bb01449a501f",
|
||||
"f80bc6c1205cd658d6e3a9262a058793",
|
||||
"5669e90e9a3909ff03208f509a229f47",
|
||||
"06c8de3fa651c6218e3540002567d9a4",
|
||||
"45a8ce75415aa4f204ead4afa041ac75",
|
||||
"9303858450c0668322c5015576e6aac7",
|
||||
"2c9d5684208571436b3cde863c546602",
|
||||
"cdf6cdc493c3cac3d5b9efb3457324bf",
|
||||
"77e8cc866aac02ea056b2c232513f0cb",
|
||||
"8f009e68251be9ad2603b19f8dec849d",
|
||||
"8050d463f7efba554901fa3bc578f10a",
|
||||
"7f8aef4f43a54065fd6c8ac0d1a361d9",
|
||||
"4e269cd830d68e3e1497691be41fbb62",
|
||||
"18dd57edd1dc9b969de7cec2ced0c41c",
|
||||
"db2ce3c79016eb7a05104a26df2eb02c",
|
||||
"bbee066aa3666bf0e812015515d40500",
|
||||
"0624e226b1341db8457aaa3f608370c5",
|
||||
"620c162aec244bc3a42e6daf3786cc18",
|
||||
"138dc09661f886a51b210af6a551487e",
|
||||
"df79c7967a9840964cabf2cabb2765f3",
|
||||
"1fe19d30f345ca2ca95c15be90a7a8a0",
|
||||
"73354263fa3beefec2635627b92551ab",
|
||||
"e9a266efc4ec71fddbc4639ac626f23a",
|
||||
"5dc8a641979b741fd17939c8121626f3",
|
||||
"994fabe27b2d38152d5a1bb6d5e8de39",
|
||||
"b25de77b496ecef8a48ab0b334d01ad2",
|
||||
"619c8357df2399ae18e22c4e36b2be95",
|
||||
"3bbec1005ef59916b0c0e6ca931c1756",
|
||||
"049327db798151b4e25a6f8ef8a3fc3d",
|
||||
"04c57f5a3826c3d3b4d704df486e21a1",
|
||||
"188ac44b91c8f97436dd74b2d6825f2e",
|
||||
"ff2d2ffa4ae22da44b8cc3f20597a899",
|
||||
"778aeea01fdea30a4287e7b2eb19ee11",
|
||||
"2ea031596696506e9d6d58354399e453",
|
||||
"507bc8bb28eaa904942a630b09155b32",
|
||||
"ee6dc9c8d2073f8b37d9f71b1c6a1d0e",
|
||||
"6e98007ab76c890a7dc7a7b9a7f0ee21",
|
||||
"9f6c6c2b7ea20a8189bfc90b25c2c273",
|
||||
"f1b7eb1f64d98474f1fcbd0ba91fe3a3",
|
||||
"c81c0b1698dbfbefe90ce6d2b502b63d",
|
||||
"a5e08dc3211f3a5f5daaf56025050097",
|
||||
"7ea4e79c5c8a71394b00d5ba845cc217",
|
||||
"523862f70a3cec67ebe0a3267252140f",
|
||||
"db3a25e763c3b4d970ec29e195fae473",
|
||||
"4cb86b641a7ffdd5bcd0c3bd1ce00244",
|
||||
"dace2eb74865e7280765ecd7e2398cfc",
|
||||
"45f0574c38325760ed49b5020c6e8ece",
|
||||
"657b5ff7cf6aabbaca438308e57db88e",
|
||||
"ac82aca2b52ce00bdcb1b43091f8b77c",
|
||||
"8adb5d61edb6fcb349cbb37b37c00de5",
|
||||
"eeac12de1dfa182ceb518f431cb268aa",
|
||||
"52bccda6a2a4c1235410dec3d467208f",
|
||||
"a0d0614e9e0f4cdec2175ce89b094fca",
|
||||
"5b0ba8f75ab1a58937a2c9eb7a68a7b6",
|
||||
"fd7c600950424c69f075b84f7e821413",
|
||||
"87323a23d5aee737f6bf8765570160c5",
|
||||
"95543acc75936a31bfea37b88e17a86b",
|
||||
"bcb0386396ed1eb128658dde13057229",
|
||||
"3d5397f8c68912e477492003187a9072",
|
||||
"67a169717caa40eec50ab1355e501965",
|
||||
"6d53512547d425c0dbe574b5f65e59b7",
|
||||
"83a96c2e76540a42859c795dbdee6768",
|
||||
"87667ee3acde34b662b003938545c434",
|
||||
"5945cddb62ebfe1119fd4686fd3742be",
|
||||
"106827d8ef85f9d7bcd7b757e7347d80",
|
||||
"ed5b2787a89d4a43c9f6337fcc110ad9",
|
||||
"0a5a008e261a79a2a66126b67a980c87",
|
||||
"17a2dd033d1161456b62f2c0b11e12c5",
|
||||
"6f1d541e9e92ba6dd06a9836d922a039",
|
||||
"2ba24954dd2e06b318cb043a287a89ca",
|
||||
"307293dc6a70835717084ea2caa2e4fd",
|
||||
"5259db5547f5b153fd82ed7c2da187ce",
|
||||
"a5d55133fd47bf414da89de1f28f8415",
|
||||
"08922898c9a4b7e6240e27202e6a325e",
|
||||
"576045481be4bb3400d45d9dc3eed006",
|
||||
"74c83032660dd26a4389095d7c480e4a",
|
||||
"e6f384ed7a0889e9df5aee06c2a46b9f",
|
||||
"83d685a5239f28db9eab94d846b44016",
|
||||
"7de28e26e5579f11cf1f805907f234fd",
|
||||
"76a43be54dad1bd972ed1c6a357fed86",
|
||||
"74e311b2a2229fdbd1b1268766948d52",
|
||||
"22b7b4da78dbf2eac4575deda4534fe4",
|
||||
"c09e67e807d3be0d14695c3060ad7956",
|
||||
"aaa6a9021bffee4f930ae91f32774113",
|
||||
"bb9072b6b0460a5feb96a6a5dad45875",
|
||||
"ab42a6ca7b0352d960ac1c1a6a9041d9",
|
||||
"938660483afa26f132df20da002a549e",
|
||||
"942a2ef62f2cb219046b2007775c46bc",
|
||||
"75cde6cceabe2b0bb5f4b1731b97dd08",
|
||||
"3b073741055654a2bcf0fb925489c869",
|
||||
"458a168843bb988e840b8bd5312aa6ed",
|
||||
"229e6053b6cc2a97bc2f37ed58b035c2",
|
||||
"37ab461684ef428de94387be689b25a1",
|
||||
"82fbb000f431997c84cd25dd230471c9",
|
||||
"29233a1ae18aeb55104573533a1d8788",
|
||||
"fda9c90f716be783b598609b93642596",
|
||||
"34ebe51b1053a5043061c73e1164ebe9",
|
||||
"a0c354b7dc48e1b4bdbde40721a6a9d6",
|
||||
"c725ff7f7b7a99ad01339cf6d05f6af5",
|
||||
"968d17b52500bc20415d9669f11e69f5",
|
||||
"7ae3d2d7314e5e404e543ef4d9a4add2",
|
||||
"3e1c9c40ac31e2f2f509182e596e232b",
|
||||
"02f573770990a9af045a07c81ffbb59e",
|
||||
"a205c9b3199f0a61c9981eccb4dc34b6",
|
||||
"1c68375cac73c18be626a1b9f59e5eeb",
|
||||
"41d5fd0d8a43eef42665703508b47e99",
|
||||
"b40d9e637e3ce07a3b4054ae7b453e8a",
|
||||
"725276e227259ff5613b7cba51903662",
|
||||
"117d4598613c124b363561118808a13d",
|
||||
"623c71599979807ce585c0b520fb79ca",
|
||||
"715fa80618eeb3c0e2aa8219ef5a5207",
|
||||
"f9060a24e376900064eb89cc124e0d94",
|
||||
"1823e859b25257450139c44c670cf6a9",
|
||||
"96ce2a1b3db373a89f22f53c97291a98",
|
||||
"52201e88ad8175c93632d21eb11ccaca",
|
||||
"bb71bcd0811e1c4ae68649d8157cc84b",
|
||||
"09a397d277e23a5b4756f6422d357a45",
|
||||
"572a212cf25e7ec13d95d36aa2350a24",
|
||||
"61e43cd4eee6b231b063acb89048e27e",
|
||||
"520a4d6fec68a28dbe13accb42a1a8a4",
|
||||
"6c611a667e7a5f0cc757448e17a2fdc7",
|
||||
"5e8182cfe19b53c915aa89afc9e069c0",
|
||||
"7926ffe1f6915ea0b2e3e56de7ff52bf",
|
||||
"686765b33d03faa0b19c19d8e147b391",
|
||||
"0ffa538bbed25d43c3b8d11e66287160",
|
||||
"799e7598d530456cb394c663dd88f590",
|
||||
"ff654863671f01edc77a8b3058fe8758",
|
||||
"04d1578d5550091630d2835870e39d9d",
|
||||
"1820f45f501a633a01b4e991732f50be",
|
||||
"ac035ea64b6502a5a9b36b37ce0b1b33",
|
||||
"f735c0a5ff59d6faa177a4bb418e12f3",
|
||||
"864406f20586a91bc6a28db7218a01ed",
|
||||
"66743c12c927a8c1dd80e9d6b4c74e6a",
|
||||
"6d20b030dd391e2d3d6a39860e16307c",
|
||||
"bb01d921e6816e0cf8be789908d4c44f",
|
||||
"9699fa026cb94b3c120114dd3311d70a",
|
||||
"3ddbbae842e07855879b04b7ced50f06",
|
||||
"73852585ef0a02b5ae2221977f134d12",
|
||||
"dff93f5258e9ff8e34ca245df485bb40",
|
||||
"b9ea3d3671badf0620e49416e5aa21be",
|
||||
"9cea969b996bc6291e3b1781a61a7373",
|
||||
"00351165ad4deb19dd03e059138a4a8d",
|
||||
"726cf7fc681e4b95d48c39ce2d8740cc",
|
||||
"ddafb40446a20a17b804ddf74dc127a2",
|
||||
"886b376a46a7f9c397b62e539ed294bc",
|
||||
"a540c995e5a4cc114ee93db84ecd0d8d",
|
||||
"2a1b6a32b52bc46641ac25ea4b4a6828",
|
||||
"cf0819a3ef3d59dc77e66c7e8eef4e47",
|
||||
"be956ee713848ae7f11c8cdd044bdd84",
|
||||
"a7a92f35284ecec49a0369fc2ba89daa",
|
||||
"4010caafe71464630760391f1652046b",
|
||||
"2d2d3268e83a8caccada4c0cd60a66ad",
|
||||
"8a54a27a27bba973b1dbee516de4a9f2",
|
||||
"3f132456a2ec89cc2e8b3a49fca1e74a",
|
||||
"19342db445a0ba93a98d1c4c351f3e0c",
|
||||
"0d8e9aab1b701c0420af4781f391576a",
|
||||
"bb6db2432a7d440fb326e3c7ba0aa023",
|
||||
"be4a9651fc5992be75c593a50f17e4fa",
|
||||
"15ff6e8c810293548926104e65402c75",
|
||||
"4d926fc787feaaf5ca79a5a8bb7ba76c",
|
||||
"2570d6d58449f7299fc8c4660a6520b3",
|
||||
"06b349050c015c66a24fdfde68c2a7b1",
|
||||
"c47a24a1d5d06995679337ab8d658ba4",
|
||||
"82470c719b1c5c67720775556f135fd9",
|
||||
"c172fe29c37bc226ebbead5b55cd7ef9",
|
||||
"73cf8af05f23baf56f43b85e74c28e4b",
|
||||
"32e4fd1200f6650068c2143931afbba9",
|
||||
"64134628009c0335f1b42222dc6ebf28",
|
||||
"51aa4bb15c69386f0e01dd913487b1b6",
|
||||
"23631ff772d634847c10bcf86f103b99",
|
||||
"af6c77b4f516310f109af58adccb6c41",
|
||||
"ccc86f0e28a4f5539f7cb005c9d43d06",
|
||||
"0a360fe1d0420809fe967af8d51d81f2",
|
||||
"6c2adb8118b1c073d0b70fd730accc9d",
|
||||
"c51449041d7c70ee43145f6183b9c777",
|
||||
"f5c8ffdfcb8fa5a509807d9f6197754e",
|
||||
"7320658d347306fbbb8063b2ae6470d3",
|
||||
"32ead63e9a5eb5b5e78bb91e24145603",
|
||||
"8520d99bcd3ebc73b56890ce1581c25d",
|
||||
"2615ff7950813b9af883b5a9616c444c",
|
||||
"08684508f408e3bb8ab7246a773a8802",
|
||||
"b553fef79ac2299a887c8eab80edbbc6",
|
||||
"cdcf3d6785d6df87cc11f8e3186789fc",
|
||||
"064f3800e0fa782fbe23bb13b88a3fad",
|
||||
"7d9369c46503bdb00fa2d879675bb2a5",
|
||||
"5e3046fc8a3deaf9e805a2cdba7b9286",
|
||||
"d5f9e96ba7e019acde3c9863d25b0e37",
|
||||
"21d5a1fa9996251bf7eb9e589cac6de1",
|
||||
"06cc5247f7d1d95e1cca84d4ffb7d26b",
|
||||
"520e23729ba9420da3d177ccdb7fb1f3",
|
||||
"fbfd4a93aa3e91e758d6a767bfc1e782",
|
||||
"3bd4480e0aaabd1bd0edc5cf6eb124e3",
|
||||
"36d0cbc6f0427a3eca843b4efef7fcc1",
|
||||
"770f14f022857cac0069aa2277c7937a",
|
||||
"016f9de5b8338c522417313caba5746e",
|
||||
"5b0ed334acd5ed0dfbc74e0ecfd46df6",
|
||||
"c5ca232ab116e1c90b9f28665a2a97b7",
|
||||
"d21691ebf9f690a360ccd269819464f2",
|
||||
"d87f1b2a7436a13b4203c8269a5ce82a",
|
||||
"be5d99e035c49b1ad1537e1a3363702b",
|
||||
"47f172131f9afbf9ccc3ae58c2ab1766",
|
||||
"29ac7babc977e0aff5ec52ab38b888f3",
|
||||
"031d5e6b1ba8b705de216ce2ee738a03",
|
||||
"a4fab81cbcb9a5c382afc99f9efa654b",
|
||||
"28846fa5c003b5228dea15d9588b26ef",
|
||||
"a1a8fc59d659db3421fc992e6ed43ebb",
|
||||
"70cbd8da12ab2d623ea64a7ff59d6017",
|
||||
"38c455db117cd47abe7ec84adcb82703",
|
||||
"384e6e5390d91bf026ad92de3e736b67",
|
||||
"20c07f23c94241c0544bedab4a27616c",
|
||||
"f7768c8027f456fd023e0047bab85e50",
|
||||
"6d5000b7960b444e735bcde5bbadcf67",
|
||||
"27819ccc024698ce25f09c0d8fac7fba",
|
||||
"7283f3d39e9f81f882cdd3a88a7c1975",
|
||||
"5bf6405dd62de5d1a62ce5a869b0ff7a",
|
||||
"aa271d610c9fbfda90456e83014a1219",
|
||||
"98e5a0cfe3865a392c456a39d04a0d1e",
|
||||
"e655c37f44efcbc6d9911a2d21de0057",
|
||||
"e99804eaaee76e3ff71e5d0e1206cdc6",
|
||||
"f81c6a8cb8bd3274f00f91f4d2311978",
|
||||
"41355bba3a9a90dde0ca91883c94d7de",
|
||||
"7746715309f78a5da6d1b6f98e11fbaf",
|
||||
"4c57ace7a707683c873b652d4177c12a",
|
||||
"4f7c6afaa207e58111e35be7511353ec",
|
||||
"5af1f5e15d88dd0f947510dc0a27713b",
|
||||
"8fa0805bcff15df07c6a485278bb1e67",
|
||||
"bb71ab72d9008b92825b043932927990",
|
||||
"5c9afbc6178718517a407d7d6349b6a9",
|
||||
"ca16b49b82ecb338ba2e49be83f3e768",
|
||||
"b4530667334ccdec608611afa283d263",
|
||||
"f30aab1f10f73d3fd66dc590eabe072e",
|
||||
"9207526277f1bc81817769eff74dc6b9",
|
||||
"6d255bcaaf17dbd177d45215f584f276",
|
||||
"cc879c77bfa5d15fd31be930e87cbf1e",
|
||||
"cf8288d8f28b79a93819fe227a76ddb2",
|
||||
"ff5303531b1ffe2f921c61a4aa03323f",
|
||||
"4f73f087afc31294ed956c2600b37b1c",
|
||||
"668215e653a481fa0086e45bfa933d3d",
|
||||
"4c78e318b619e825b88ffe160eebeb5e",
|
||||
"7690972aad49eeb701d2d18a745a98b6",
|
||||
"8c7940010a72f0a48a037fe8391cd2fb",
|
||||
"2ea3b62d1813e049596a9b0fda6b3e4e",
|
||||
"7785ee8a88113c583146aa8dc23f946e",
|
||||
"0f4c8e00fc2b26a8a29f4db770183dfa",
|
||||
"1093a140b83ac76ad325c203a531f65e",
|
||||
"c582e68cb6ed95348e8f6fcf074ff2fc",
|
||||
"c6032361913d4785ed481f48b33f731a",
|
||||
"217faeaad9304408b161e701285a3b8e",
|
||||
"f45d0372f347002b98720d9453ddf1a2",
|
||||
"b4d4c66327a1cdab0c07cd0093a974a8",
|
||||
"954d8356dced1f1a3720da4f0046a398",
|
||||
"911df550a51c8a181ef32b9753555dfb",
|
||||
"bda7df95e666eafe33a8f2abe2fea634",
|
||||
"0e0c4d35a061ad013a41b6e7d1c98b98",
|
||||
"bf5b76dae267f0395505cfec63b50646",
|
||||
"76bf70a68ab95a425ff8dcac46131132",
|
||||
"c7e70ebf1632e663996b35118c160c8e",
|
||||
"318c1b8c660d6af6d17fd6273b245af9",
|
||||
"a0dfd7d70a53e4752d39e09b23877cff",
|
||||
"e6f289a63cfe387475d8163aedcc81ad",
|
||||
"1e5e699b986f4c23d09eff0e217085b8",
|
||||
"833a5c48288f4952e9c61e6b0b930fa7",
|
||||
"8ed1cf85741113aaf857e2965d4e4272",
|
||||
"6dd9ab107eb20bbaeea02fa3ec0f33d2",
|
||||
"a7b7eaf135d71e3022ad76ec107b48ff",
|
||||
"073af39e97efb74768bb3183da953566",
|
||||
"c3f863d0bdbafc92c0ca3faaed93ced4",
|
||||
"08af1b56c7fc6425374e9872ff785b09",
|
||||
"c0d0a55c63e6e33eec91128dc058ba23",
|
||||
"91cbd62d57c8933f3c411118a81ab04d",
|
||||
"a8cbfae060e9ce8f94a5c5cbcbee8df9",
|
||||
"8f5b21d65d86a840a009eead6b8884cc",
|
||||
"b1afdd65bac87ffd1c04793a0ab972aa",
|
||||
"e40c4f069de5d95683ccdb5c0628b2ef",
|
||||
"6491aef47c7959bce2e0a884abeb3bb2",
|
||||
"f5e004e2bc642ca31f3abe2710c8a881",
|
||||
"e1579a648e8e94d490c78527672dbffa",
|
||||
"9635c20a64553e6333a134f256e0d8f6",
|
||||
"709925f349a00398e02fdd0bb1ca3a2a",
|
||||
"f79d18e08ad731408737f94fd5ec311e",
|
||||
"fa49527db20263c08efbda57d32fb60f",
|
||||
"abeb2e43db5870bfc7f67c95a5d0715e",
|
||||
"1c7921492252eda86523556ee1b31cf0",
|
||||
"86c17d99c0ba64ecc8dae0d1c374801b",
|
||||
"413ee29a922e595bdc831d55d7bf869c",
|
||||
"c1346e4458a976189e1f766edc58e3c6",
|
||||
"77e3c9a12c3f439d8a688cd7104c6945",
|
||||
"1edba00a2065fea20698be50c53aa469",
|
||||
"108b628730e6b6d95a934dc753ba7800",
|
||||
"765df7084717a95d8b661d94470d9874",
|
||||
"b6f9d18e20f9fbfcc0f58a8249701c39",
|
||||
"6c37a50657fc642ceba428745aa05524",
|
||||
"645d96cd1ab254cb2960882c91e59818",
|
||||
"51a91086bbf25cb202e7d90a90a96a23",
|
||||
"a7cc74f1090aaf4c7f8ba15d47b94607",
|
||||
"22db653c956f93fe86edc58e3d891d33",
|
||||
"082f11ad48e6915edbcf73d1ce5f4d14",
|
||||
"0052b5b88344ebe4964cd74f3468e33e",
|
||||
"3d279760a58611179383a54938fee4c1",
|
||||
"e7703438b9827715d48364f56b2394ae",
|
||||
"3ce79f5b99aca6020af533fe9e3591c4",
|
||||
"97e28d0cf7e7b90e5c8e28a824e39b20",
|
||||
"03e342c00179c3afb0fe2a1959a40f2e",
|
||||
"677c9d5e8606dbabed966fd9998d36a5",
|
||||
"03122db996d2dd586190ecede173e042",
|
||||
"31d0ab82d1381d6c404a4b4c2d312df5",
|
||||
"210fa9270878d6779e3197642122c897",
|
||||
"55af1f915c8305b6009ead4a3a5ca206",
|
||||
"ea8d7e974991f0fd7ce49f1a6958b908",
|
||||
"cb4acc5d6ba761d28d0eaf5998ccfbc3",
|
||||
"2cd1ba926f08e0b8921ee731d4ad45d2",
|
||||
"59cecd5ff719d4dbff1d5db7896073f8",
|
||||
"23fcfe052943f99ae0be66429e2fed13",
|
||||
"8ab828468c65071c469377994d333fd0",
|
||||
"bb60899434542599246a5506f91acc70",
|
||||
"9511650eaa7a03e0395bda03f81c8e46",
|
||||
"25402db80324219bf47190e565826947",
|
||||
"3fa08036424eb4abd45dff9e5a44d053",
|
||||
"d227f25d2fec870921f490264cf10d4d",
|
||||
"b673e3f2b80a49f5d1a302688ad6c507",
|
||||
"a1089fb46ad31d4f2e5bdcb89c4ca450",
|
||||
"a4cab8697eb3d01da02e8f919a6aa623",
|
||||
"b31f456ef56d61c4caf94e7f08caddc0",
|
||||
"9bcc87ca47c213d47c910eb42784a36b",
|
||||
"b10d304e187eb51636281dcca6998bb4",
|
||||
"a5a9774d73b86d4417da071e86d3e3c0",
|
||||
"6ce588f19710e366eebadae627eee1fc",
|
||||
"578dc4c94adf0c34b45663eb4a85a838",
|
||||
"bf84f07538cc95f066691f26be070348",
|
||||
"caf153b8021978ae8f9df99c2af233d9",
|
||||
"89dfa8569d80c9740b4688c0c4817333",
|
||||
"3a90490dd7f7ef066bf28b74f81e8c1f",
|
||||
"ed7212665806b5001731bf221739ed36",
|
||||
"652e1e096bad409f4284acb9e9eabd19",
|
||||
"25356f8d57abcdf0b06b8e5602e0ccf1",
|
||||
"03cc9703cd331fb9e79976c73ba65913",
|
||||
"1824aa65b7e6c8b7723e635ad6639101",
|
||||
"2c0120f93b519fe5182b422962212e15",
|
||||
"8615b9ae85fdbf7e7a6738e8e3e1bd45",
|
||||
"b01d663a2a02c660981056d7f16df586",
|
||||
"906f62aa308315508487282538d96db1",
|
||||
"d749670a542f3d85d257c694e997e316",
|
||||
"df81bc8022bd51e2e17dd9738b452041",
|
||||
"a8caeb99af594f40f7fff48b32066f89",
|
||||
"8f263eb262bd4615303303cebf86782f",
|
||||
"4d1b54f56bde5b2279a44fdfe096413f",
|
||||
"21da00d34b00b6a3abb2dd0c680d1c37",
|
||||
"0c10a05600ea952624c929253bf4ce8a",
|
||||
"6df46e4c7f11a01eb2a362c02f17ed22",
|
||||
"7bf5124eddcd6122b2709f56f045b7cf",
|
||||
"9ff5bdd19778f4fa67984b5b7492e036",
|
||||
"a60ba550973bfcd1415b8ed5b12de272",
|
||||
"828df338ec04cb2f304eea9bf640999c",
|
||||
"10b1f6d46172ca42292dd53533670daa",
|
||||
"97aec799b90fd5bea919ad7eb8ae1e5e",
|
||||
"010c173afbaa3eb6d78005836f687a10",
|
||||
"2c1518c284eef37d9710dca5236d545e",
|
||||
"ab6b23baf829811a17ac367aae043b19",
|
||||
"e8c0d51d4086da5ab5c8f61e69397615",
|
||||
"cff001e1b62d1b725ce16bf5a1065d14",
|
||||
"34477cc17921837cb65f5bff8d53f55c",
|
||||
"82f760de665244c7dc41f988660c0a77",
|
||||
"778abd1a5c5422e7b8d52506fad0c4c3",
|
||||
"6d86d5c5e9545eade5e5c40a0e7d9959",
|
||||
"1b823369559fe3222934b016b661ae12",
|
||||
"3dd185ee23db47cb9357f808a3d5892a",
|
||||
"a1dc473d8b85302571f25b6a93269d26",
|
||||
"f551b1b02371ea764b1a7816a16b9cde",
|
||||
"b7d73f3a3240338cb956b7f75bd11a83",
|
||||
"20f53f1fa841a187328da52185aeafe3",
|
||||
"ad0af27bbe31f4a95f63d38ce3097a5c",
|
||||
"743b0415fb37ed722ebc8fdcd6566b1c",
|
||||
"99049f4e27c1075f5c2a8bee2a6078da",
|
||||
"204d1e686918408ff168d13ffc06a572",
|
||||
"6f6a7ac25ae4614ff7842849a2ee9d94",
|
||||
"85ca525f01d26e432f9595ba148438f6",
|
||||
"6d1522b0beb17d68ad4230b0158c4652",
|
||||
"f7a42d6350eba9acc454dd6c08068c54",
|
||||
"0da0c5262f4dca9033a7a7cd565b9d10",
|
||||
"dbbcacbb8629807a8a022ae0c2051041",
|
||||
"b40fa988b67df4f490895064fec7dac7",
|
||||
"c87da7fa9431c9bf6173dcfc86bae31b",
|
||||
"d5a32f44c958cefcdf3348be4de3ac6a",
|
||||
"a3d87f9c67d70809291a10136fdd304e",
|
||||
"b9e82e3fbdc7bdb4130cc00735503fb2",
|
||||
"ee1b7fefc7a213cd3bc73b84e32f38b2",
|
||||
"c4c038a06d11fd8101a1776764d27518",
|
||||
"db080178c87a02266d23f07cd72d30b2",
|
||||
"d0b25179c2eca995999f1eec6c266a4f",
|
||||
"2b26cd9c0535473865d2242f9399ce8b",
|
||||
"6dc87b9326fd2ff9e6a9e6e0bb330fb3",
|
||||
"c4496fd117d8ce36f7052f03ccad1670",
|
||||
"bba80ab1558408c357ad47aaaaeafe38",
|
||||
"7481c11cb646d723c3b9ee6f0fae9330",
|
||||
"bfc3ce3b1248483331c53d172bd2fbd0",
|
||||
"9e4546091b7bf30d44473ada7a17634b",
|
||||
"6503febe7505e7a128e4496c38ecdf1a",
|
||||
"d230bd356b8efd6a1a0634b85676a07f",
|
||||
"6e4beda3e0d14554270df61ebc78231d",
|
||||
"7440252c5b23d6ec6e354a7cfc02afe5",
|
||||
"7c55fa1d99d2cf5df085dfc292bb580d",
|
||||
"d6f3b316e695d8fb421cb7dd1e236991",
|
||||
"0a2cac0dcb4a896ee57f3abbddb19b8b",
|
||||
"240526792717478c5ce8e89adc8f66f9",
|
||||
"5d5266e37a48f0066c266418621c6e2b",
|
||||
"8e23b3b768cf3df7b0a753607386ce92",
|
||||
"20465d43926878705d88c9d31d5f5128",
|
||||
"448c9f9ca3f7f13842d83a303a83a6e4",
|
||||
"803aa630470ff5fe98c216f6dd307ec1",
|
||||
"d4bb28b4788a28000c24bd20fd224168",
|
||||
"80c30fd234cefa21fd37528b1f28fcca",
|
||||
"9f36eb1cd54566e72ddecb7177c9662a",
|
||||
"f25028ffcfd67f948323c209862fd81a",
|
||||
"ac643a14932b315b1c55df2a79a6b9ed",
|
||||
"2f4dadff83681e3487ee4120c502c8ee",
|
||||
"8e68432df47e80760d47eae1bec44271",
|
||||
"52997076c191e305515bf647e8a7f39c",
|
||||
"6fabd22dbb92abf8f19ef7925b49018e",
|
||||
"d736f396d6b8009ab3da1507298bd252",
|
||||
"44eee6eefef6b5ab50953600e5216e58",
|
||||
"007d43c295692443836fcb1c856a46df",
|
||||
"55fb67ad0d29d3efd8cc4f4961c8cd9d",
|
||||
"058edd56fcaf4f3e5b1f99da9901c8b5",
|
||||
"67b805c0cef73db3706b1329775af537",
|
||||
"834c3067f4149288a2e52a1aa992e908",
|
||||
"37e9d2336b1321846741c784e2edf0b7",
|
||||
"6091e0c0c2d192b489bdd3c852a3e08c",
|
||||
"e1c5046950883c22740491ac7e9593b2",
|
||||
"3cc29dc8878988d88875f7f48cfd06f4",
|
||||
"3f0cbc3c2b5f3fc7d124ea0194594403",
|
||||
"cdb6ba798a0249f78f6564c611606fd5",
|
||||
"18a80a2c808396a7e5c92b1708bb26b8",
|
||||
"dcd7de44b633139e72badc2bddb08b06",
|
||||
"9e0da4fd7049a852eb895d42f5f6b233",
|
||||
"3e76a01e9e73bea957eee0319f87da8b",
|
||||
"64f3177822b9315319f1703606b6b8ae",
|
||||
"a44fa7b7845808821af63c46378ee43a",
|
||||
"9cbea4427d7e8caff0d28f9d7600cdec",
|
||||
"c4d2162d5badfa27d969b03babf9698b",
|
||||
"dc38dee4ece34ef9c253e0002e4aaa8e",
|
||||
"f814e597a8c9a33f65061178cb2d7eb4",
|
||||
"79d5a69c0c2fe77163f16ba16d810730",
|
||||
"1c0adfdbdcda89c4c0472be4a6b36f0b",
|
||||
"e9cd7a3495bce7deef0917766bd4ae9c",
|
||||
"929dd6c71919f79896296cb65eda4e13",
|
||||
"7fea808fa65ee1a8b692d6949e76de62",
|
||||
"a697c5cc8c4dcbc81b5349a8299cfd2f",
|
||||
"9382ec316f372840b9f07ab68adfd4be",
|
||||
"a4923efc2333c32956995ae7a84224c9",
|
||||
"0f3c5b204ec882447dc8aae513321508",
|
||||
"c9d8c8b2eedc262a501f38d17ff8576b",
|
||||
"e39d2cac795946660a0109410650131e",
|
||||
"9a8324ed4219be3da821d7d7d51403de",
|
||||
"c403cdc94083cf5038eecde2158f27b9",
|
||||
"c1af93053920fe633a0071072a279669",
|
||||
"4474fde06c283fbc09c86fd7c634b15f",
|
||||
"7d008c39d752b615d703068aced8118f",
|
||||
"7c305b8a003d4578b58cd26236f29d10",
|
||||
"8729dd951263fa4636e54ff191b864c4",
|
||||
"f8ccac3dd7cb79ae7129a4c8b0e26958",
|
||||
"aceb234bd3ef312f8480f299690c8d85",
|
||||
"2699862b096e1aa6fe6da4ebb4849753",
|
||||
"13203d1e1076b42d3e4bb429870c5ada",
|
||||
"63b8a6743660cb6ba3c8513435a20312",
|
||||
"4cea997415cc8f2be6b61667b01913f3",
|
||||
"64c61e76a8173efa4cf0b9b5554813ed",
|
||||
"78182197c48a3db307ecbc4d9df83954",
|
||||
"aee0711c7175fd5a41bfbb6b55ad3ab1",
|
||||
];
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-random",
|
||||
up: async () => {
|
||||
for (const md5 of HAS_RANDOM_MD5) {
|
||||
const chart = await db.charts.bms.findOne({ "data.hashMD5": md5 });
|
||||
|
||||
if (!chart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const scores = await db.scores.find({
|
||||
chartID: chart.chartID,
|
||||
});
|
||||
|
||||
await DeleteMultipleScores(scores);
|
||||
|
||||
await db.charts.bms.remove({
|
||||
chartID: chart.chartID,
|
||||
});
|
||||
}
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Cannot undo.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db, { monkDB } from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "remove-session-views",
|
||||
up: async () => {
|
||||
await db.sessions.update(
|
||||
{},
|
||||
{
|
||||
$unset: {
|
||||
views: 1,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
// remove session view cache (which no longer exists, so we have to
|
||||
// access it raw with monk).
|
||||
await monkDB.get("session-view-cache").drop();
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "rm-hot",
|
||||
up: async () => {
|
||||
const toRemove = [
|
||||
["gitadora", "skill"],
|
||||
["wacca", "rate"],
|
||||
["maimai", "rate"],
|
||||
["maimaidx", "rate"],
|
||||
] as const;
|
||||
|
||||
for (const [game, met] of toRemove) {
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
game,
|
||||
"preferences.preferredProfileAlg": met,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.preferredProfileAlg": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db["game-stats"].update(
|
||||
{ game },
|
||||
{
|
||||
$unset: { [`ratings.${met}`]: 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
await db["game-stats-snapshots"].update(
|
||||
{ game },
|
||||
{
|
||||
$unset: { [`ratings.${met}`]: 1, [`rankings.${met}`]: 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
}
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "sdvx-maxxive-lamp-rearrange",
|
||||
up: async () => {
|
||||
await db.goals.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"criteria.key": "lamp",
|
||||
"criteria.value": 4,
|
||||
name: /PERFECT ULTIMATE CHAIN/u,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"criteria.value": 5,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await db.goals.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"criteria.key": "lamp",
|
||||
"criteria.value": 3,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"criteria.value": 4,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error("not doing this");
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "sdvx-maxxive-lamp-rearrange",
|
||||
up: async () => {
|
||||
await db.scores.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"scoreData.lamp": "ULTIMATE CHAIN",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"scoreData.enumIndexes.lamp": 4,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await db.scores.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"scoreData.lamp": "PERFECT ULTIMATE CHAIN",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"scoreData.enumIndexes.lamp": 5,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
down: async () => {
|
||||
await db.scores.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"scoreData.lamp": "ULTIMATE CHAIN",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"scoreData.enumIndexes.lamp": 3,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await db.scores.update(
|
||||
{
|
||||
game: "sdvx",
|
||||
"scoreData.lamp": "PERFECT ULTIMATE CHAIN",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"scoreData.enumIndexes.lamp": 4,
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,47 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
|
||||
|
||||
import type { SessionDocument } from "../../../../../common/src";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "sessions-to-scoreIDs",
|
||||
up: async () => {
|
||||
await EfficientDBIterate(
|
||||
db.sessions,
|
||||
(s: any) => {
|
||||
// this is completely unsafe but i don't care
|
||||
|
||||
s.scoreIDs = s.scoreInfo.map((e: any) => e.scoreID);
|
||||
|
||||
return s as SessionDocument;
|
||||
},
|
||||
async (updates: Array<SessionDocument>) => {
|
||||
await db.sessions.bulkWrite(
|
||||
updates.map((e) => ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
sessionID: e.sessionID,
|
||||
},
|
||||
update: {
|
||||
$set: {
|
||||
scoreIDs: e.scoreIDs,
|
||||
},
|
||||
$unset: {
|
||||
scoreInfo: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
})),
|
||||
);
|
||||
},
|
||||
{ scoreIDs: { $exists: false } },
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
const migration: Migration = {
|
||||
id: "update-jubeat-preferred-tables",
|
||||
up: async () => {
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
game: "jubeat",
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.defaultTable": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,202 +0,0 @@
|
||||
import type { Migration } from "#utils/types";
|
||||
import type { FilterQuery } from "mongodb";
|
||||
|
||||
import { EditGoal } from "#lib/targets/goals";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import type { GoalDocument } from "../../../../../common/src";
|
||||
|
||||
async function UpdateGoals(
|
||||
query: FilterQuery<GoalDocument>,
|
||||
newCriteriaKey: string,
|
||||
newCriteriaValue?: (goal: GoalDocument) => number,
|
||||
) {
|
||||
const goals = await db.goals.find(query);
|
||||
|
||||
for (const oldGoal of goals) {
|
||||
// too lazy to parallelise this wrt. the quest changes.
|
||||
// ah well
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await EditGoal(oldGoal, {
|
||||
...oldGoal,
|
||||
criteria: {
|
||||
...oldGoal.criteria,
|
||||
key: newCriteriaKey,
|
||||
value: newCriteriaValue?.(oldGoal) ?? oldGoal.criteria.value,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function perToScore(goal: GoalDocument) {
|
||||
const v = goal.criteria.value / 100;
|
||||
|
||||
switch (goal.game) {
|
||||
case "sdvx":
|
||||
case "usc":
|
||||
return v * 10_000_000;
|
||||
case "wacca":
|
||||
case "chunithm":
|
||||
case "ongeki":
|
||||
case "ddr":
|
||||
case "museca":
|
||||
return v * 1_000_000;
|
||||
case "popn":
|
||||
return v * 100_000;
|
||||
case "iidx":
|
||||
case "bms":
|
||||
case "gitadora":
|
||||
case "jubeat":
|
||||
case "maimai":
|
||||
case "maimaidx":
|
||||
case "pms":
|
||||
case "itg":
|
||||
case "arcaea":
|
||||
throw new Error(`Cannot convert this percent to score for game ${goal.game}`);
|
||||
}
|
||||
}
|
||||
|
||||
const migration: Migration = {
|
||||
id: "v3-props",
|
||||
up: async () => {
|
||||
// no more clans
|
||||
await db.users.update(
|
||||
{},
|
||||
{
|
||||
$unset: { clan: 1 },
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
"preferences.defaultTable": { $exists: false },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.defaultTable": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
await db["user-settings"].update(
|
||||
{
|
||||
"preferences.deletableScores": { $exists: false },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.deletableScores": false,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db["game-settings"].update(
|
||||
{ "preferences.scoreBucket": { $exists: true } },
|
||||
{
|
||||
$rename: {
|
||||
"preferences.scoreBucket": "preferences.preferredDefaultEnum",
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
"preferences.preferredDefaultEnum": { $exists: false },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"preferences.preferredDefaultEnum": null,
|
||||
},
|
||||
},
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
await db["game-settings"].update(
|
||||
{ "preferences.preferredScoreAlg": "ktRating" },
|
||||
{ $set: { "preferences.preferredScoreAlg": null } },
|
||||
{ multi: true },
|
||||
);
|
||||
await db["game-settings"].update(
|
||||
{ "preferences.preferredSessionAlg": "ktRating" },
|
||||
{ $set: { "preferences.preferredSessionAlg": null } },
|
||||
{ multi: true },
|
||||
);
|
||||
await db["game-settings"].update(
|
||||
{ "preferences.preferredProfileAlg": "ktRating" },
|
||||
{ $set: { "preferences.preferredProfileAlg": null } },
|
||||
{ multi: true },
|
||||
);
|
||||
|
||||
const settings = await db["game-settings"].find({});
|
||||
|
||||
for (const set of settings) {
|
||||
const newStats = [];
|
||||
|
||||
for (const stat of set.preferences.stats) {
|
||||
if ("property" in stat) {
|
||||
newStats.push({
|
||||
...stat,
|
||||
metric: stat.property,
|
||||
});
|
||||
} else {
|
||||
newStats.push(stat);
|
||||
}
|
||||
}
|
||||
|
||||
for (const stat of newStats) {
|
||||
// @ts-expect-error we're renaming the prop
|
||||
delete stat.property;
|
||||
}
|
||||
|
||||
await db["game-settings"].update(
|
||||
{
|
||||
userID: set.userID,
|
||||
game: set.game,
|
||||
playtype: set.playtype,
|
||||
},
|
||||
{
|
||||
$set: { "preferences.stats": newStats },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await db.goals.update({}, { $unset: { timeAdded: 1 } }, { multi: true });
|
||||
|
||||
const r = await db.goals.find({ "charts.type": "any" });
|
||||
|
||||
await db["goal-subs"].remove({ goalID: { $in: r.map((e) => e.goalID) } });
|
||||
await db.goals.remove({ "charts.type": "any" });
|
||||
|
||||
// score in these games is now percent
|
||||
await UpdateGoals(
|
||||
{
|
||||
game: { $in: ["gitadora", "maimaidx"] },
|
||||
"criteria.key": "scoreData.score",
|
||||
},
|
||||
"percent",
|
||||
);
|
||||
|
||||
// percent no longer exists for these games
|
||||
await UpdateGoals(
|
||||
{
|
||||
game: { $in: ["sdvx", "usc", "wacca", "museca", "popn", "chunithm"] },
|
||||
"criteria.key": "scoreData.percent",
|
||||
},
|
||||
"score",
|
||||
perToScore,
|
||||
);
|
||||
|
||||
// obvious mappings
|
||||
await UpdateGoals({ "criteria.key": "scoreData.score" }, "score");
|
||||
await UpdateGoals({ "criteria.key": "scoreData.percent" }, "percent");
|
||||
await UpdateGoals({ "criteria.key": "scoreData.lampIndex" }, "lamp");
|
||||
await UpdateGoals({ "criteria.key": "scoreData.gradeIndex" }, "grade");
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
export default migration;
|
||||
@@ -1,487 +0,0 @@
|
||||
import type { DryScoreData } from "#lib/score-import/framework/common/types";
|
||||
import type { Migration } from "#utils/types";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { CreateScoreID } from "#lib/score-import/framework/score-importing/score-id";
|
||||
import { DeleteMultipleScores } from "#lib/score-mutation/delete-scores";
|
||||
import UpdateScore from "#lib/score-mutation/update-score";
|
||||
import { RecalcGameProfiles } from "#scripts/state-sync/recalc-game-profiles";
|
||||
|
||||
import {
|
||||
allSupportedGameGroups,
|
||||
GetGamePTConfig,
|
||||
GetGPTString,
|
||||
type GPTString,
|
||||
type GPTStrings,
|
||||
type integer,
|
||||
} from "../../../../../common/src";
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import db, { monkDB } from "#services/mongo/db";
|
||||
import { UpdateAllPBs } from "#utils/calculations/recalc-scores";
|
||||
import { RecalcSessions } from "#utils/calculations/recalc-sessions";
|
||||
import { EfficientDBIterate } from "#utils/efficient-db-iterate";
|
||||
|
||||
interface OldScoreData {
|
||||
score: number;
|
||||
percent: number;
|
||||
lamp: string;
|
||||
grade: string;
|
||||
lampIndex: integer;
|
||||
gradeIndex: integer;
|
||||
hitMeta: any;
|
||||
judgements: any;
|
||||
}
|
||||
|
||||
type ScoreMover<GPT extends GPTString> = (old: OldScoreData) => DryScoreData<GPT>;
|
||||
type ScoreMovers = {
|
||||
[GPT in GPTString]: ScoreMover<GPT>;
|
||||
};
|
||||
|
||||
const IIDX_MV: ScoreMover<GPTStrings["iidx"]> = (old) => ({
|
||||
score: old.score,
|
||||
lamp: old.lamp as any,
|
||||
judgements: old.judgements,
|
||||
optional: {
|
||||
bp: old.hitMeta.bp,
|
||||
comboBreak: old.hitMeta.comboBreak,
|
||||
fast: old.hitMeta.fast,
|
||||
slow: old.hitMeta.slow,
|
||||
maxCombo: old.hitMeta.maxCombo,
|
||||
gauge: old.hitMeta.gauge,
|
||||
gaugeHistory: old.hitMeta.gaugeHistory,
|
||||
gsmEasy: old.hitMeta.gsm?.EASY,
|
||||
gsmNormal: old.hitMeta.gsm?.NORMAL,
|
||||
gsmHard: old.hitMeta.gsm?.HARD,
|
||||
gsmEXHard: old.hitMeta.gsm?.EX_HARD,
|
||||
},
|
||||
});
|
||||
|
||||
type NeutralGames = GPTStrings["chunithm" | "museca" | "sdvx" | "usc" | "wacca"];
|
||||
|
||||
const _NEUTRAL_MV = (old: OldScoreData): DryScoreData<NeutralGames> => ({
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
score: old.score,
|
||||
});
|
||||
|
||||
// silly hack for typechecker
|
||||
const NEUTRAL_MV = _NEUTRAL_MV as any;
|
||||
|
||||
const GITADORA_MV: ScoreMover<GPTStrings["gitadora"]> = (old) => ({
|
||||
percent: old.percent,
|
||||
optional: old.hitMeta,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
});
|
||||
|
||||
const BMS_MV: ScoreMover<GPTStrings["bms" | "pms"]> = (old) => ({
|
||||
score: old.score,
|
||||
lamp: old.lamp as any,
|
||||
judgements: old.judgements,
|
||||
optional: old.hitMeta,
|
||||
});
|
||||
|
||||
const scoreMovers: ScoreMovers = {
|
||||
"iidx:SP": IIDX_MV,
|
||||
"iidx:DP": IIDX_MV,
|
||||
"bms:14K": BMS_MV,
|
||||
"bms:7K": BMS_MV,
|
||||
"pms:Controller": BMS_MV,
|
||||
"pms:Keyboard": BMS_MV,
|
||||
"chunithm:Single": NEUTRAL_MV,
|
||||
"ongeki:Single": NEUTRAL_MV,
|
||||
"usc:Controller": NEUTRAL_MV,
|
||||
"usc:Keyboard": NEUTRAL_MV,
|
||||
"sdvx:Single": NEUTRAL_MV,
|
||||
"wacca:Single": NEUTRAL_MV,
|
||||
"museca:Single": NEUTRAL_MV,
|
||||
"ddr:SP": NEUTRAL_MV,
|
||||
"ddr:DP": NEUTRAL_MV,
|
||||
"maimai:Single": (old) => ({
|
||||
percent: old.percent,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
}),
|
||||
"maimaidx:Single": (old) => ({
|
||||
percent: old.percent,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
}),
|
||||
"jubeat:Single": (old) => ({
|
||||
musicRate: old.percent,
|
||||
score: old.score,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
}),
|
||||
"itg:Stamina": () => {
|
||||
throw new Error(
|
||||
`Delete ITG scores instead of migrating them, ITG support has fundamentally changed.`,
|
||||
);
|
||||
},
|
||||
"popn:9B": (old) => {
|
||||
if (!old.hitMeta.specificClearType) {
|
||||
throw new Error(
|
||||
`This score has an undefined specificClearType, but this property is now necessary. You should have wiped these scores.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
clearMedal: old.hitMeta.specificClearType,
|
||||
judgements: old.judgements,
|
||||
optional: {
|
||||
fast: old.hitMeta.fast,
|
||||
slow: old.hitMeta.slow,
|
||||
gauge: old.hitMeta.gauge,
|
||||
maxCombo: old.hitMeta.maxCombo,
|
||||
},
|
||||
score: old.score,
|
||||
};
|
||||
},
|
||||
"gitadora:Dora": GITADORA_MV,
|
||||
"gitadora:Gita": GITADORA_MV,
|
||||
"arcaea:Touch": (old) => ({
|
||||
score: old.score,
|
||||
judgements: old.judgements,
|
||||
lamp: old.lamp as any,
|
||||
optional: old.hitMeta,
|
||||
}),
|
||||
};
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const migration: Migration = {
|
||||
id: "v3-scores",
|
||||
up: async () => {
|
||||
// welp. here we go
|
||||
// we need to do a **mass** score migration
|
||||
|
||||
for (const game of allSupportedGameGroups) {
|
||||
logger.info(`Removing dangling scores for ${game}...`);
|
||||
|
||||
const allChartIDs = (
|
||||
await db.anyCharts[game].find(
|
||||
{},
|
||||
{
|
||||
projection: { chartID: 1 },
|
||||
},
|
||||
)
|
||||
).map((e) => e.chartID);
|
||||
|
||||
await DeleteMultipleScores(
|
||||
await db.scores.find({
|
||||
game,
|
||||
chartID: { $nin: allChartIDs },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
await DeleteMultipleScores(await db.scores.find({ game: "itg" }));
|
||||
await DeleteMultipleScores(
|
||||
await db.scores.find({
|
||||
game: "popn",
|
||||
"scoreData.hitMeta": { $exists: true },
|
||||
$or: [
|
||||
{ "scoreData.hitMeta.specificClearType": { $exists: false } },
|
||||
{ "scoreData.hitMeta.specificClearType": null },
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
logger.info(`Starting score migration...`);
|
||||
|
||||
const count = await db.scores.count({
|
||||
"scoreData.enumIndexes": { $exists: false },
|
||||
});
|
||||
|
||||
logger.info(`Migrating ${count} scores.`);
|
||||
|
||||
const failedScores = [];
|
||||
|
||||
await EfficientDBIterate(
|
||||
db.scores,
|
||||
async (score) => {
|
||||
// @ts-expect-error just incase
|
||||
delete score._id;
|
||||
|
||||
try {
|
||||
const gptString = GetGPTString(score.game, score.playtype);
|
||||
|
||||
const newScore = {
|
||||
...score,
|
||||
scoreData: scoreMovers[gptString](
|
||||
score.scoreData as unknown as OldScoreData,
|
||||
),
|
||||
};
|
||||
|
||||
const newScoreID = CreateScoreID(
|
||||
gptString,
|
||||
newScore.userID,
|
||||
newScore,
|
||||
newScore.chartID,
|
||||
);
|
||||
|
||||
await monkDB.get("temp-update-map").insert({
|
||||
old: score.scoreID,
|
||||
new: newScoreID,
|
||||
});
|
||||
|
||||
await UpdateScore(
|
||||
score,
|
||||
newScore,
|
||||
undefined,
|
||||
true, // skipUpdatingPBs because we'll do it after
|
||||
// all scores are guaranteeably correct.
|
||||
true, // dangerously skip updating refs
|
||||
// as we're gonna do it ourselves
|
||||
);
|
||||
} catch (err) {
|
||||
logger.warn(err);
|
||||
logger.warn("Continuing through the error.");
|
||||
|
||||
failedScores.push(score.scoreID);
|
||||
}
|
||||
},
|
||||
// no-op
|
||||
|
||||
async () => void 0,
|
||||
{
|
||||
"scoreData.enumIndexes": { $exists: false },
|
||||
},
|
||||
10000,
|
||||
);
|
||||
|
||||
if (failedScores.length > 0) {
|
||||
throw new Error(
|
||||
`${failedScores.length} failed to be migrated. Resolve these manually, please.`,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Reconstructing PBs...`);
|
||||
|
||||
await UpdateAllPBs();
|
||||
|
||||
logger.info(`Re-calcing Game Profiles...`);
|
||||
await RecalcGameProfiles();
|
||||
|
||||
// turn old integery classes into strings
|
||||
const profiles = await db["game-stats"].find({});
|
||||
|
||||
for (const p of profiles) {
|
||||
const newClasses: Record<string, string | null> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(p.classes)) {
|
||||
if (value === null) {
|
||||
newClasses[key] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
newClasses[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
const cVal = GetGamePTConfig(p.game, p.playtype).classes[key]!.values[value];
|
||||
|
||||
if (!cVal) {
|
||||
logger.error(`No such class ${p.game} ${p.playtype} ${key} ${value}?`);
|
||||
newClasses[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
newClasses[key] = cVal.id;
|
||||
}
|
||||
|
||||
await db["game-stats"].update(
|
||||
{
|
||||
userID: p.userID,
|
||||
game: p.game,
|
||||
playtype: p.playtype,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
classes: newClasses,
|
||||
oldClasses: p.classes,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const classAchievements = await db["class-achievements"].find({}, { projectID: true });
|
||||
|
||||
for (const c of classAchievements) {
|
||||
const gptConfig = GetGamePTConfig(c.game, c.playtype);
|
||||
|
||||
let newPreviousValue: string | null | undefined = c.classOldValue;
|
||||
|
||||
if (typeof newPreviousValue === "number") {
|
||||
newPreviousValue =
|
||||
// @ts-expect-error hack, we know this type isn't what it
|
||||
// claims to be
|
||||
gptConfig.classes[c.classSet].values[c.classOldValue]?.id;
|
||||
}
|
||||
|
||||
let newValue: string | undefined = c.classValue;
|
||||
|
||||
if (typeof newValue === "number") {
|
||||
newValue = // @ts-expect-error hack, we know this type isn't what it
|
||||
// claims to be
|
||||
gptConfig.classes[c.classSet].values[c.classValue]?.id;
|
||||
}
|
||||
|
||||
if (newPreviousValue === undefined) {
|
||||
logger.error(
|
||||
`OLD: No such class ${c.game} ${c.playtype} ${c.classSet} ${c.classOldValue}?`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newValue === undefined) {
|
||||
logger.error(
|
||||
`NEW: No such class ${c.game} ${c.playtype} ${c.classSet} ${c.classValue}?`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
await db["class-achievements"].update(
|
||||
{
|
||||
_id: c._id,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
classOldValue: newPreviousValue,
|
||||
classValue: newValue,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// somehow these sessions got corrupted, my bad.
|
||||
const corruptSessionsSomehow = await db.sessions.find({
|
||||
"scoreIDs.0": { $exists: false },
|
||||
});
|
||||
|
||||
for (const ses of corruptSessionsSomehow) {
|
||||
await db.sessions.update(
|
||||
{ sessionID: ses.sessionID },
|
||||
{ $set: { scoreIDs: [ses.scoreIDs as unknown as string] } },
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Starting Ref Update`);
|
||||
|
||||
await FastUpdateSessions();
|
||||
await FastUpdateImports();
|
||||
|
||||
logger.info(`Finished Ref Update`);
|
||||
|
||||
logger.info(`Re-calcing Sessions...`);
|
||||
await RecalcSessions();
|
||||
|
||||
logger.info("Done!");
|
||||
},
|
||||
down: () => {
|
||||
throw new Error(`Reverting this change is not possible.`);
|
||||
},
|
||||
};
|
||||
|
||||
async function FastUpdateSessions() {
|
||||
const sessions = await db.sessions.find({});
|
||||
|
||||
await Promise.allSettled(
|
||||
sessions.map(async (session) => {
|
||||
if (session.scoreIDs.every((k) => k.startsWith("T"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
|
||||
.get("temp-update-map")
|
||||
.find({ old: { $in: session.scoreIDs } });
|
||||
|
||||
const lookup = new Map<string, string>();
|
||||
|
||||
for (const n of newScoreIDRefs) {
|
||||
lookup.set(n.old, n.new);
|
||||
}
|
||||
|
||||
const newScoreIDs: Array<string> = [];
|
||||
|
||||
for (const oldScoreID of session.scoreIDs) {
|
||||
// definitely new format
|
||||
if (oldScoreID.startsWith("T")) {
|
||||
newScoreIDs.push(oldScoreID);
|
||||
continue;
|
||||
}
|
||||
|
||||
const newScoreID = lookup.get(oldScoreID);
|
||||
|
||||
if (!newScoreID) {
|
||||
logger.warn(`No such scoreID ${oldScoreID} exists in lookup? Skipping score.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
newScoreIDs.push(newScoreID);
|
||||
}
|
||||
|
||||
await db.sessions.update(
|
||||
{ sessionID: session.sessionID },
|
||||
{
|
||||
$set: { scoreIDs: newScoreIDs },
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function FastUpdateImports() {
|
||||
const sessions = await db.imports.find({});
|
||||
|
||||
await Promise.allSettled(
|
||||
sessions.map(async (importDoc) => {
|
||||
if (importDoc.scoreIDs.every((k) => k.startsWith("T"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newScoreIDRefs: Array<{ new: string; old: string }> = await monkDB
|
||||
.get("temp-update-map")
|
||||
.find({ old: { $in: importDoc.scoreIDs } });
|
||||
|
||||
const lookup = new Map<string, string>();
|
||||
|
||||
for (const n of newScoreIDRefs) {
|
||||
lookup.set(n.old, n.new);
|
||||
}
|
||||
|
||||
const newScoreIDs: Array<string> = [];
|
||||
|
||||
for (const oldScoreID of importDoc.scoreIDs) {
|
||||
// definitely new format
|
||||
if (oldScoreID.startsWith("T")) {
|
||||
newScoreIDs.push(oldScoreID);
|
||||
continue;
|
||||
}
|
||||
|
||||
const newScoreID = lookup.get(oldScoreID);
|
||||
|
||||
if (!newScoreID) {
|
||||
logger.warn(`No such scoreID ${oldScoreID} exists in lookup? Skipping score.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
newScoreIDs.push(newScoreID);
|
||||
}
|
||||
|
||||
await db.imports.update(
|
||||
{ importID: importDoc.importID },
|
||||
{
|
||||
$set: { scoreIDs: newScoreIDs },
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default migration;
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FilterQuery } from "mongodb";
|
||||
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { GetNextCounterValue } from "#utils/db";
|
||||
import { DedupeArr } from "#utils/misc";
|
||||
@@ -14,8 +14,6 @@ import type {
|
||||
SongDocument,
|
||||
} from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Handles an orphan queue request.
|
||||
*
|
||||
@@ -38,7 +36,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
userID: integer,
|
||||
chartName: string,
|
||||
) {
|
||||
logger.debug(`Received orphanqueue request for ${chartName}.`);
|
||||
log.debug(`Received orphanqueue request for ${chartName}.`);
|
||||
|
||||
const orphanChart = await db["orphan-chart-queue"].findOne(
|
||||
{ gptString, ...orphanMatchCriteria },
|
||||
@@ -48,7 +46,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
);
|
||||
|
||||
if (!orphanChart) {
|
||||
logger.verbose(`Received unknown chart ${chartName}, orphaning.`);
|
||||
log.verbose(`Received unknown chart ${chartName}, orphaning.`);
|
||||
|
||||
await db["orphan-chart-queue"].insert({
|
||||
gptString,
|
||||
@@ -70,10 +68,10 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
// it.
|
||||
|
||||
if (playcount >= queueSize) {
|
||||
logger.info(`Song ${chartName} was unorphaned by userIDs ${uniqueUsersArr.join(", ")}.`);
|
||||
log.info(`Song ${chartName} was unorphaned by userIDs ${uniqueUsersArr.join(", ")}.`);
|
||||
const songID = await GetNextCounterValue(`${game}-song-id`);
|
||||
|
||||
logger.verbose(`${chartName} has been assigned songID ${songID}.`);
|
||||
log.verbose(`${chartName} has been assigned songID ${songID}.`);
|
||||
|
||||
const { songDoc, chartDoc } = orphanChart;
|
||||
|
||||
@@ -92,7 +90,7 @@ export async function HandleOrphanQueue<GPT extends GPTString>(
|
||||
|
||||
// otherwise, update the state of this orphan.
|
||||
|
||||
logger.verbose(`UserID ${userID} played ${chartName}, which is now at ${playcount} plays.`);
|
||||
log.verbose(`UserID ${userID} played ${chartName}, which is now at ${playcount} plays.`);
|
||||
await db["orphan-chart-queue"].update(
|
||||
{
|
||||
_id: orphanChart._id,
|
||||
@@ -131,10 +129,10 @@ export async function DeorphanIfInQueue<GPT extends GPTString>(
|
||||
|
||||
const { songDoc, chartDoc } = orphanChart;
|
||||
|
||||
logger.info(`Song ${songDoc.title} was unorphaned forcefully.`);
|
||||
log.info(`Song ${songDoc.title} was unorphaned forcefully.`);
|
||||
const songID = await GetNextCounterValue(`${game}-song-id`);
|
||||
|
||||
logger.verbose(`${songDoc.title} has been assigned songID ${songID}.`);
|
||||
log.verbose(`${songDoc.title} has been assigned songID ${songID}.`);
|
||||
|
||||
songDoc.id = songID;
|
||||
chartDoc.songID = songID;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { BulkWriteUpdateOneOperation } from "mongodb";
|
||||
|
||||
import { SetRivalsFailReasons } from "#lib/constants/err-codes";
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { SendSetRivalNotification } from "#lib/notifications/notification-wrappers";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
import db from "#services/mongo/db";
|
||||
@@ -17,8 +17,6 @@ import {
|
||||
type Playtype,
|
||||
} from "../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Retrieve all of a user's set rival IDs.
|
||||
* Throws if the user hasn't played the GPT in question.
|
||||
@@ -125,7 +123,7 @@ export async function SetRivals(
|
||||
});
|
||||
|
||||
if (!currentGameSettings) {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`User ${userID} attempted to set rivals for ${FormatGameGroup(
|
||||
game,
|
||||
playtype,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import type { ClassConfigs, Classes, GPTString, integer } from "../../../../../../common/src";
|
||||
import type {
|
||||
@@ -19,5 +19,5 @@ export type ClassProvider<GPT extends GPTString = GPTString> = (
|
||||
gptString: GPT,
|
||||
userID: integer,
|
||||
ratings: Record<string, number | null>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) => Partial<RecordClassProvider<GPT>> | Promise<Partial<RecordClassProvider<GPT>>> | undefined;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
import type {
|
||||
ImportTypeContextMap,
|
||||
ImportTypeDataMap,
|
||||
@@ -14,7 +14,7 @@ export function GetInputParser<I extends ImportTypes>(jobData: ScoreImportJobDat
|
||||
// Retrieve the set parser function for this import type.
|
||||
const ParserFunction = Parsers[jobData.importType];
|
||||
|
||||
const InputParser = (logger: KtLogger) =>
|
||||
const InputParser = (log: KtLogger) =>
|
||||
// @ts-expect-error TypeScript doesn't like the fact that we
|
||||
// pass this as a rest parameter, since none of the parsers
|
||||
// actually take rest args. However, this is the only way to
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type KtLogger, rootLogger } from "#lib/logger/logger";
|
||||
import { type KtLogger, log } from "#lib/logger/log.js";
|
||||
import { FormatUserDoc } from "#utils/user";
|
||||
|
||||
import type { ImportTypes, UserDocument } from "../../../../../../common/src";
|
||||
@@ -8,15 +8,8 @@ export function CreateScoreLogger(
|
||||
importID: string,
|
||||
importType: ImportTypes,
|
||||
): KtLogger {
|
||||
const meta = {
|
||||
return log.child({
|
||||
context: ["Score Import", importType, FormatUserDoc(user)],
|
||||
importID,
|
||||
};
|
||||
|
||||
// used so appendLogCtx works
|
||||
const childLogger = rootLogger.child(meta);
|
||||
|
||||
childLogger.defaultMeta = meta;
|
||||
|
||||
return childLogger as KtLogger;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { Random20Hex } from "#utils/misc";
|
||||
|
||||
import type {
|
||||
@@ -18,8 +18,6 @@ export interface WrappedAPIResponse {
|
||||
body: SuccessfulAPIResponse<ImportDocument> | UnsuccessfulAPIResponse;
|
||||
}
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* A thin(ish) wrapper for ScoreImportMain which converts thrown
|
||||
* errors and import documents into a WrappedAPIResponse, which can
|
||||
@@ -33,7 +31,7 @@ export async function ExpressWrappedScoreImportMain<I extends ImportTypes>(
|
||||
): Promise<WrappedAPIResponse> {
|
||||
const importID = Random20Hex();
|
||||
|
||||
logger.debug("Received import request.");
|
||||
log.debug("Received import request.");
|
||||
|
||||
try {
|
||||
const res = await MakeScoreImport({
|
||||
@@ -55,7 +53,7 @@ export async function ExpressWrappedScoreImportMain<I extends ImportTypes>(
|
||||
} catch (err) {
|
||||
// this is definitely fine, as the errors are emitted from the same place.
|
||||
if (err instanceof ScoreImportFatalError) {
|
||||
logger.info(err.message);
|
||||
log.info(err.message);
|
||||
return {
|
||||
statusCode: err.statusCode,
|
||||
body: {
|
||||
@@ -65,7 +63,7 @@ export async function ExpressWrappedScoreImportMain<I extends ImportTypes>(
|
||||
};
|
||||
}
|
||||
|
||||
logger.error(err);
|
||||
log.error(err);
|
||||
return {
|
||||
statusCode: 500,
|
||||
body: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { dmf } from "#test-utils/misc.js";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
@@ -15,8 +15,6 @@ import type { GoalDocument, GoalSubscriptionDocument } from "../../../../../../c
|
||||
|
||||
import { ProcessGoal, UpdateGoalsForUser } from "./goals";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("#UpdateGoalsForUser", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import { EvaluateGoalForUser, GetRelevantGoals } from "#lib/targets/goals";
|
||||
import { EmitWebhookEvent } from "#lib/webhooks/webhooks";
|
||||
@@ -18,7 +18,7 @@ export async function GetAndUpdateUsersGoals(
|
||||
game: GameGroup,
|
||||
userID: integer,
|
||||
chartIDs: Set<string>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const { goals, goalSubsMap } = await GetRelevantGoals(game, userID, chartIDs, logger);
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function GetAndUpdateUsersGoals(
|
||||
return [];
|
||||
}
|
||||
|
||||
logger.verbose(`Found ${goals.length} relevant goals.`);
|
||||
log.verbose(`Found ${goals.length} relevant goals.`);
|
||||
|
||||
return UpdateGoalsForUser(goals, goalSubsMap, userID, logger);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export async function UpdateGoalsForUser(
|
||||
goals: Array<GoalDocument>,
|
||||
goalSubsMap: Map<string, GoalSubscriptionDocument>,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
skipMismatch = false,
|
||||
) {
|
||||
const returns = await Promise.all(
|
||||
@@ -48,7 +48,7 @@ export async function UpdateGoalsForUser(
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.error(
|
||||
log.error(
|
||||
`UserGoal:GoalID mismatch ${goal.goalID} - this user has no goalSub for this, yet it is set.`,
|
||||
);
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function UpdateGoalsForUser(
|
||||
}
|
||||
|
||||
return ProcessGoal(goal, goalSub, userID, logger).catch((err: Error) => {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Failed to process goal '${goal.name}' for ${userID}, ${err.message}. Skipping.`,
|
||||
{ goal, err, userID, goalSub },
|
||||
);
|
||||
@@ -111,7 +111,7 @@ export async function ProcessGoal(
|
||||
goal: GoalDocument,
|
||||
goalSub: GoalSubscriptionDocument,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const res = await EvaluateGoalForUser(goal, userID, logger);
|
||||
|
||||
@@ -181,7 +181,7 @@ export async function ProcessGoal(
|
||||
// If this goal was achieved, and is now *not* achieved, we need to unset
|
||||
// some things.
|
||||
if (goalSub.achieved && !res.achieved) {
|
||||
logger.info(`User ${userID} lost their achieved status on ${goal.name}.`, {
|
||||
log.info(`User ${userID} lost their achieved status on ${goal.name}.`, {
|
||||
goal,
|
||||
res,
|
||||
goalSub,
|
||||
@@ -211,19 +211,19 @@ export async function ProcessGoal(
|
||||
};
|
||||
}
|
||||
|
||||
export async function UpdateGoalsInFolder(folderID: string, logger: KtLogger) {
|
||||
export async function UpdateGoalsInFolder(folderID: string, log: KtLogger) {
|
||||
const goals = await db.goals.find({
|
||||
"charts.type": "folder",
|
||||
"charts.data": folderID,
|
||||
});
|
||||
|
||||
logger.info(`Updating ${goals.length} goals for ${folderID}`);
|
||||
log.info(`Updating ${goals.length} goals for ${folderID}`);
|
||||
|
||||
const goalSubs = await db["goal-subs"].find({
|
||||
goalID: { $in: goals.map((e) => e.goalID) },
|
||||
});
|
||||
|
||||
logger.info(`Updating ${goalSubs.length} goal subs for ${folderID}`);
|
||||
log.info(`Updating ${goalSubs.length} goal subs for ${folderID}`);
|
||||
|
||||
// (User -> (goalID -> GoalSub))
|
||||
const ugsMap = new Map<integer, Map<string, GoalSubscriptionDocument>>();
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { ONE_DAY, ONE_HOUR } from "#lib/constants/time";
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import type { integer } from "../../../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* If a user has no ongoing import, enable the import lock and return true.
|
||||
* If a user has an ongoing import, return false.
|
||||
@@ -26,7 +24,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) {
|
||||
lockedAt: null,
|
||||
});
|
||||
} else if (lockExists.locked && lockExists.lockedAt! + ONE_DAY < Date.now()) {
|
||||
logger.warn(`Removed import lock for ${userID} as it is ostensibly stuck.`);
|
||||
log.warn(`Removed import lock for ${userID} as it is ostensibly stuck.`);
|
||||
await db["import-locks"].update(
|
||||
{
|
||||
userID,
|
||||
@@ -56,7 +54,7 @@ export async function CheckAndSetOngoingImportLock(userID: integer) {
|
||||
|
||||
if (lockWasSet.lockedAt !== null) {
|
||||
if (Date.now() - lockWasSet.lockedAt > ONE_HOUR) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`User ${userID} has been locked for an hour. Automatically freeing the lock as they're stuck.`,
|
||||
);
|
||||
await UnsetOngoingImportLock(userID);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import deepmerge from "deepmerge";
|
||||
@@ -11,8 +11,6 @@ import type { OrphanScoreDocument } from "../../import-types/common/types";
|
||||
|
||||
import { OrphanScore, ReprocessOrphan } from "./orphans";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const batchManualScore: BatchManualScore = {
|
||||
score: 500,
|
||||
lamp: "HARD CLEAR",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
import type { FilterQuery } from "mongodb";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
@@ -33,7 +33,7 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
context: ImportTypeContextMap[T],
|
||||
errMsg: string | null,
|
||||
game: GameGroup,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const orphan: Pick<OrphanScoreDocument, "context" | "data" | "importType" | "userID"> = {
|
||||
importType,
|
||||
@@ -42,21 +42,21 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
userID,
|
||||
};
|
||||
|
||||
logger.debug("Orphaning document", orphan);
|
||||
log.debug("Orphaning document", orphan);
|
||||
|
||||
let orphanID;
|
||||
|
||||
try {
|
||||
orphanID = `O${fjsh.hash(orphan, "sha256")}`;
|
||||
} catch (err) {
|
||||
logger.error(`Failed to orphan score -- `, { err, orphan });
|
||||
log.error(`Failed to orphan score -- `, { err, orphan });
|
||||
throw new Error(`Failed to orphan score. ${(err as Error).message}`);
|
||||
}
|
||||
|
||||
const exists = await db["orphan-scores"].findOne({ orphanID });
|
||||
|
||||
if (exists) {
|
||||
logger.debug(`Skipped orphaning score ${orphanID} because it already exists.`);
|
||||
log.debug(`Skipped orphaning score ${orphanID} because it already exists.`);
|
||||
return { success: false, orphanID };
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
timeInserted: Date.now(),
|
||||
};
|
||||
|
||||
logger.debug(`Inserting orphanScoreDoc...`, orphanScoreDoc);
|
||||
log.debug(`Inserting orphanScoreDoc...`, orphanScoreDoc);
|
||||
|
||||
await db["orphan-scores"].insert(orphanScoreDoc);
|
||||
|
||||
@@ -86,7 +86,7 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
export async function ReprocessOrphan(
|
||||
orphan: OrphanScoreDocument,
|
||||
blacklist: Array<string>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const ConverterFunction = Converters[orphan.importType] as ConverterFunction<
|
||||
ImportTypeDataMap[ImportTypes],
|
||||
@@ -103,7 +103,7 @@ export async function ReprocessOrphan(
|
||||
// this is impossible to test, so we're going to ignore it
|
||||
/* istanbul ignore next */
|
||||
if (!("failureType" in err)) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Converter function ${orphan.importType} returned unexpected error. ID=${orphan.orphanID}`,
|
||||
{
|
||||
err,
|
||||
@@ -121,16 +121,16 @@ export async function ReprocessOrphan(
|
||||
if ("failureType" in res) {
|
||||
// If the data still can't be found, we do nothing about it.
|
||||
if (res.failureType === "SongOrChartNotFound") {
|
||||
logger.debug(`Unorphaning ${orphan.orphanID} failed. (${res.message})`);
|
||||
log.debug(`Unorphaning ${orphan.orphanID} failed. (${res.message})`);
|
||||
return false;
|
||||
} else if (res.failureType === "Internal") {
|
||||
logger.error(`Orphan Internal Failure - ${res.message}, OrphanID ${orphan.orphanID}`);
|
||||
log.error(`Orphan Internal Failure - ${res.message}, OrphanID ${orphan.orphanID}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, it's another converterfailure we don't need to specifically handle.
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`received ConverterFailure ${res.message} on orphan ${orphan.orphanID}. Removing orphan.`,
|
||||
);
|
||||
|
||||
@@ -172,7 +172,7 @@ export async function ReprocessOrphan(
|
||||
const user = await GetUserWithID(orphan.userID);
|
||||
|
||||
if (!user) {
|
||||
logger.severe(
|
||||
log.error(
|
||||
`Orphan ${orphan.orphanID} belongs to ${orphan.userID}, but that user no longer exists in the database. Going to skip this and remove the orphan.`,
|
||||
);
|
||||
await db["orphan-scores"].remove({ orphanID: orphan.orphanID });
|
||||
@@ -193,13 +193,13 @@ export async function ReprocessOrphan(
|
||||
return converterReturns;
|
||||
}
|
||||
|
||||
export async function DeorphanScores(query: FilterQuery<OrphanScoreDocument>, logger: KtLogger) {
|
||||
export async function DeorphanScores(query: FilterQuery<OrphanScoreDocument>, log: KtLogger) {
|
||||
const orphans = await db["orphan-scores"].find(query);
|
||||
|
||||
// ScoreIDs are essentially userID dependent, so this is fine.
|
||||
const blacklist = await GetBlacklist();
|
||||
|
||||
logger.info(`Found ${orphans.length} orphans.`, { query });
|
||||
log.info(`Found ${orphans.length} orphans.`, { query });
|
||||
|
||||
let failed = 0;
|
||||
let success = 0;
|
||||
@@ -226,7 +226,7 @@ export async function DeorphanScores(query: FilterQuery<OrphanScoreDocument>, lo
|
||||
success++;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Failed to reprocess orphan.`, { orphanID: or.orphanID, err });
|
||||
log.error(`Failed to reprocess orphan.`, { orphanID: or.orphanID, err });
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx, { type KtLogger } from "#lib/logger/logger";
|
||||
import { log, type KtLogger } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import {
|
||||
@@ -15,8 +15,6 @@ import { CreatePBDoc, type PBScoreDocumentNoRank } from "./create-pb-doc";
|
||||
|
||||
const IIDXScore = TestingIIDXSPScore;
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("#CreatePBDoc", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
import type { BulkWriteUpdateOneOperation, FilterQuery, SortOptionObject } from "mongodb";
|
||||
|
||||
import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations";
|
||||
@@ -33,7 +33,7 @@ export async function CreatePBDoc(
|
||||
gpt: GPTString,
|
||||
userID: integer,
|
||||
chart: ChartDocument,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
asOfTimestamp?: number,
|
||||
) {
|
||||
const chartID = chart.chartID;
|
||||
@@ -62,13 +62,10 @@ export async function CreatePBDoc(
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
`User ${userID} has no scores on chart, but a PB was attempted to be created?`,
|
||||
{
|
||||
chartID,
|
||||
userID,
|
||||
},
|
||||
);
|
||||
log.warn(`User ${userID} has no scores on chart, but a PB was attempted to be created?`, {
|
||||
chartID,
|
||||
userID,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { Testing511SPA, TestingIIDXSPScore } from "#test-utils/test-data";
|
||||
@@ -8,8 +8,6 @@ import t from "tap";
|
||||
|
||||
import { ProcessPBs } from "./process-pbs";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("#ProcessPBs", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import { GetChartForIDGuaranteed } from "#utils/db";
|
||||
@@ -19,7 +19,7 @@ export async function ProcessPBs(
|
||||
playtype: Playtype,
|
||||
userID: integer,
|
||||
chartIDs: Set<string>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): Promise<void> {
|
||||
if (chartIDs.size === 0) {
|
||||
return;
|
||||
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import t from "tap";
|
||||
|
||||
import { GITADORA_COLOURS, type UserGameStats } from "../../../../../../common/src";
|
||||
import { CalculateUGPTClasses, ProcessClassDeltas } from "./classes";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
t.test("#CalculateUGPTClasses", (t) => {
|
||||
t.test("Should produce an empty object by default", async (t) => {
|
||||
const res = await CalculateUGPTClasses("iidx", "SP", 1, {}, null, logger);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import { EmitWebhookEvent } from "#lib/webhooks/webhooks";
|
||||
import db from "#services/mongo/db";
|
||||
@@ -49,7 +49,7 @@ export async function CalculateUGPTClasses(
|
||||
userID: integer,
|
||||
ratings: Record<string, number | null>,
|
||||
ClassProvider: ClassProvider | null,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): Promise<ExtractedClasses[GPTString]> {
|
||||
const gptString = GetGPTString(game, playtype);
|
||||
|
||||
@@ -59,7 +59,7 @@ export async function CalculateUGPTClasses(
|
||||
// If this import method is providing us classes, merge those with the
|
||||
// other classes we have.
|
||||
if (ClassProvider) {
|
||||
logger.debug(`Calling custom class handler.`);
|
||||
log.debug(`Calling custom class handler.`);
|
||||
const customClasses = (await ClassProvider(gptString, userID, ratings, logger)) ?? {};
|
||||
|
||||
classes = deepmerge(customClasses, classes);
|
||||
@@ -86,7 +86,7 @@ export async function ProcessClassDeltas(
|
||||
classes: AnyClasses,
|
||||
userGameStats: UserGameStats | null,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): Promise<Array<ClassDelta>> {
|
||||
const gptString = GetGPTString(game, playtype);
|
||||
|
||||
@@ -101,7 +101,7 @@ export async function ProcessClassDeltas(
|
||||
const classVal = classes[classSet];
|
||||
|
||||
if (classVal === undefined || classVal === null) {
|
||||
logger.debug(`Skipped deltaing-class ${classSet}.`);
|
||||
log.debug(`Skipped deltaing-class ${classSet}.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ export async function ProcessClassDeltas(
|
||||
deltas.push(delta);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
log.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
import type { BulkWriteUpdateOneOperation } from "mongodb";
|
||||
|
||||
import { EvaluateQuestProgress } from "#lib/targets/quests";
|
||||
@@ -20,7 +20,7 @@ export async function UpdateUsersQuests(
|
||||
game: GameGroup,
|
||||
playtypes: Array<Playtype>,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const goalIDs = importGoalInfo.map((e) => e.goalID);
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function UpdateQuestsForUser(
|
||||
|
||||
game: GameGroup,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
// create a map here to avoid linear searching when
|
||||
// co-iterating
|
||||
@@ -56,7 +56,7 @@ export async function UpdateQuestsForUser(
|
||||
const questSub = questSubMap.get(quest.questID);
|
||||
|
||||
if (!questSub) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Invalid state achieved in quest processing - processed quest that user did not have? ${quest.questID}`,
|
||||
);
|
||||
|
||||
@@ -126,7 +126,7 @@ async function GetRelevantQuests(
|
||||
game: GameGroup,
|
||||
playtypes: Array<Playtype>,
|
||||
userID: integer,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const questSubs = await db["quest-subs"].find({
|
||||
game,
|
||||
@@ -134,14 +134,14 @@ async function GetRelevantQuests(
|
||||
userID,
|
||||
});
|
||||
|
||||
logger.debug(`Found ${questSubs.length} quest-subs.`);
|
||||
log.debug(`Found ${questSubs.length} quest-subs.`);
|
||||
|
||||
const quests = await db.quests.find({
|
||||
questID: { $in: questSubs.map((e) => e.questID) },
|
||||
"questData.goals.goalID": { $in: goalIDs },
|
||||
});
|
||||
|
||||
logger.debug(`Found ${quests.length} relevant quests.`);
|
||||
log.debug(`Found ${quests.length} relevant quests.`);
|
||||
|
||||
return { questSubs, quests };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { JOB_RETRY_COUNT } from "#lib/constants/tachi";
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import { ServerConfig } from "#lib/setup/config";
|
||||
import { Sleep } from "#utils/misc";
|
||||
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
StartTrackingImport,
|
||||
} from "./status-tracking/import-status-tracking";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* Makes a score import given ScoreImportJobData.
|
||||
* If USE_EXTERNAL_SCORE_IMPORT_WORKER is set, then this will instead
|
||||
@@ -85,7 +83,7 @@ async function MakeScoreImportInner<I extends ImportTypes>(
|
||||
|
||||
const backoff = ExponentialBackoff(timesAttempted - 1);
|
||||
|
||||
logger.info(
|
||||
log.info(
|
||||
`User ${jobData.userID} already had an import ongoing. (${
|
||||
jobData.importID
|
||||
}) Backing off for ${(backoff / 1_000).toFixed(2)} seconds.`,
|
||||
@@ -100,14 +98,14 @@ async function MakeScoreImportInner<I extends ImportTypes>(
|
||||
timesAttempted++;
|
||||
}
|
||||
|
||||
logger.error(
|
||||
log.error(
|
||||
`User ${jobData.userID} didn't get an import through in around 6 hours. Has their lock gotten stuck?`,
|
||||
jobData,
|
||||
);
|
||||
|
||||
await UnsetOngoingImportLock(jobData.userID);
|
||||
|
||||
logger.error(`Forcing off ${jobData.userID}'s import lock. Sketchy.`);
|
||||
log.error(`Forcing off ${jobData.userID}'s import lock. Sketchy.`);
|
||||
|
||||
throw new ScoreImportFatalError(
|
||||
409,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GPTDerivers } from "#game-implementations/types";
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import { GPT_SERVER_IMPLEMENTATIONS } from "#game-implementations/game-implementations";
|
||||
|
||||
@@ -50,7 +50,7 @@ function DeriveMetrics<GPT extends GPTString>(
|
||||
return derivedMetrics as DerivedMetrics[GPT];
|
||||
}
|
||||
|
||||
export function CreateEnumIndexes<GPT extends GPTString>(gpt: GPT, metrics: any, logger: KtLogger) {
|
||||
export function CreateEnumIndexes<GPT extends GPTString>(gpt: GPT, metrics: any, log: KtLogger) {
|
||||
const gptConfig = GetGPTConfig(gpt);
|
||||
|
||||
const indexes: Record<string, integer> = {};
|
||||
@@ -67,7 +67,7 @@ export function CreateEnumIndexes<GPT extends GPTString>(gpt: GPT, metrics: any,
|
||||
const index = conf.values.indexOf(metrics[key]);
|
||||
|
||||
if (index === -1) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Got an invalid enum value of ${metrics[key]} for ${gpt} ${key} on DryScore. Can't add indexes?`,
|
||||
{ metrics, key, conf },
|
||||
);
|
||||
@@ -96,7 +96,7 @@ export function CreateEnumIndexes<GPT extends GPTString>(gpt: GPT, metrics: any,
|
||||
const index = conf.values.indexOf(metrics.optional[key]);
|
||||
|
||||
if (index === -1) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Got an invalid enum value of ${metrics.optional[key]} for ${gpt} optional.${key} on DryScore. Can't add indexes?`,
|
||||
{ metrics, key, conf },
|
||||
);
|
||||
@@ -122,7 +122,7 @@ export function CreateFullScoreData<GPT extends GPTString>(
|
||||
gpt: GPT,
|
||||
dryScoreData: DryScore<GPT>["scoreData"],
|
||||
chart: ChartDocument<GPT>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const derivedMetrics = DeriveMetrics(gpt, dryScoreData, chart);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import type { DryScore } from "../common/types";
|
||||
|
||||
@@ -23,7 +23,7 @@ export function HydrateScore(
|
||||
chart: ChartDocument,
|
||||
song: SongDocument,
|
||||
scoreID: string,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): ScoreDocument {
|
||||
const gpt = GetGPTString(dryScore.game, chart.playtype);
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import type { integer, ScoreDocument } from "../../../../../../common/src";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
const MAX_PIPELINE_LENGTH = 500;
|
||||
|
||||
interface ScoreQueue {
|
||||
@@ -67,7 +66,7 @@ export async function InsertQueue(userID: integer) {
|
||||
try {
|
||||
await db.scores.insert(queuedScores);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Triggered duplicate key protection. Race condition protected against, but this is not good.`,
|
||||
{ err },
|
||||
);
|
||||
@@ -89,16 +88,16 @@ export function QueueScoreInsert(score: ScoreDocument) {
|
||||
|
||||
if (scoreQueue.scoreIDSet.has(score.scoreID)) {
|
||||
// skip
|
||||
logger.verbose(`Score ID ${score.scoreID} was already queued to be imported.`);
|
||||
log.verbose(`Score ID ${score.scoreID} was already queued to be imported.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
AddToScoreQueue(scoreQueue, score);
|
||||
|
||||
logger.debug(`ScoreQueue for ${score.userID} is now at ${scoreQueue.queue.length}.`);
|
||||
log.debug(`ScoreQueue for ${score.userID} is now at ${scoreQueue.queue.length}.`);
|
||||
|
||||
if (scoreQueue.queue.length >= MAX_PIPELINE_LENGTH) {
|
||||
logger.verbose(`Triggered pipeline flush with len ${scoreQueue.queue.length}.`);
|
||||
log.verbose(`Triggered pipeline flush with len ${scoreQueue.queue.length}.`);
|
||||
return InsertQueue(score.userID);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
import fjsh from "fast-json-stable-hash";
|
||||
|
||||
+32
-34
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/log/log";
|
||||
import type { ScoreImportJob } from "#lib/score-import/worker/types";
|
||||
|
||||
import db from "#services/mongo/db";
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "../../../../../../common/src";
|
||||
import { Converters } from "../../import-types/converters";
|
||||
import { InternalFailure } from "../common/converter-failures";
|
||||
import { CreateScoreLogger } from "../common/import-logger";
|
||||
import { CreateScoreLogger } from "../common/import-log";
|
||||
import { GetAndUpdateUsersGoals } from "../goals/goals";
|
||||
import { CheckAndSetOngoingImportLock, UnsetOngoingImportLock } from "../import-locks/lock";
|
||||
import { ProcessPBs } from "../pb/process-pbs";
|
||||
@@ -54,23 +54,23 @@ export default async function ScoreImportMain<D, C>(
|
||||
);
|
||||
}
|
||||
|
||||
let logger;
|
||||
let log;
|
||||
|
||||
if (!providedLogger) {
|
||||
// If they weren't given to us -
|
||||
// we create an "import logger".
|
||||
// we create an "import log".
|
||||
// this holds a reference to the user's name, ID, and type
|
||||
// of score import for any future debugging.
|
||||
logger = CreateScoreLogger(user, importID, importType);
|
||||
logger.debug("Received import request.");
|
||||
log = CreateScoreLogger(user, importID, importType);
|
||||
log.debug("Received import request.");
|
||||
} else {
|
||||
logger = providedLogger;
|
||||
log = providedLogger;
|
||||
}
|
||||
|
||||
const hasNoOngoingImport = await CheckAndSetOngoingImportLock(user.id);
|
||||
|
||||
if (hasNoOngoingImport) {
|
||||
logger.info(`User ${userID} made an import while they had one ongoing.`);
|
||||
log.info(`User ${userID} made an import while they had one ongoing.`);
|
||||
|
||||
// @danger
|
||||
// Throwing away an import if the user already has one outgoing is *bad*, as in the case
|
||||
@@ -90,11 +90,11 @@ export default async function ScoreImportMain<D, C>(
|
||||
// We get an iterable from the provided parser function, alongside some context and a converter function.
|
||||
// This iterable does not have to be an array - it's anything that's iterable, like a generator.
|
||||
const parseTimeStart = process.hrtime.bigint();
|
||||
const { iterable, context, game, classProvider: classProvider } = await InputParser(logger);
|
||||
const { iterable, context, game, classProvider: classProvider } = await InputParser(log);
|
||||
|
||||
const parseTime = GetMillisecondsSince(parseTimeStart);
|
||||
|
||||
logger.debug(`Parsing took ${parseTime} milliseconds.`);
|
||||
log.debug(`Parsing took ${parseTime} milliseconds.`);
|
||||
|
||||
void SetJobProgress(
|
||||
job,
|
||||
@@ -128,7 +128,7 @@ export default async function ScoreImportMain<D, C>(
|
||||
ConverterFunction,
|
||||
context,
|
||||
game,
|
||||
logger,
|
||||
log,
|
||||
job,
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -139,11 +139,11 @@ export default async function ScoreImportMain<D, C>(
|
||||
});
|
||||
|
||||
if (r.deletedCount !== 0) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`An error was thrown from ImportAllIterableData, which has resulted in a potential partial-score-import. Undoing scores inserted from this import.`,
|
||||
{ err },
|
||||
);
|
||||
logger.error(
|
||||
log.error(
|
||||
`Removed ${r.deletedCount} scores from the database to undo partial-import.`,
|
||||
);
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export default async function ScoreImportMain<D, C>(
|
||||
const importTime = GetMillisecondsSince(importTimeStart);
|
||||
const importTimeRel = importTime / importInfo.length;
|
||||
|
||||
logger.debug(`Importing took ${importTime} milliseconds. (${importTimeRel}ms/doc)`);
|
||||
log.debug(`Importing took ${importTime} milliseconds. (${importTimeRel}ms/doc)`);
|
||||
|
||||
void SetJobProgress(job, `Imported scores, took ${importTime} milliseconds. `);
|
||||
|
||||
@@ -177,7 +177,7 @@ export default async function ScoreImportMain<D, C>(
|
||||
importType,
|
||||
game,
|
||||
classProvider,
|
||||
logger,
|
||||
log,
|
||||
job,
|
||||
);
|
||||
|
||||
@@ -215,11 +215,11 @@ export default async function ScoreImportMain<D, C>(
|
||||
|
||||
// I only really want to log "big" imports. The others are here for debugging purposes.
|
||||
if (scoreIDs.length > 500) {
|
||||
logger.info(logMessage);
|
||||
log.info(logMessage);
|
||||
} else if (scoreIDs.length > 1) {
|
||||
logger.verbose(logMessage);
|
||||
log.verbose(logMessage);
|
||||
} else {
|
||||
logger.debug(logMessage);
|
||||
log.debug(logMessage);
|
||||
}
|
||||
|
||||
await db.imports.insert(ImportDocument);
|
||||
@@ -265,7 +265,7 @@ export async function HandlePostImportSteps(
|
||||
importType: ImportTypes,
|
||||
game: GameGroup,
|
||||
classProvider: ClassProvider | null,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
job: ScoreImportJob | undefined,
|
||||
) {
|
||||
// --- 3. ParseImportInfo ---
|
||||
@@ -277,9 +277,7 @@ export async function HandlePostImportSteps(
|
||||
const importParseTime = GetMillisecondsSince(importParseTimeStart);
|
||||
const importParseTimeRel = importParseTime / importInfo.length;
|
||||
|
||||
logger.debug(
|
||||
`Import Parsing took ${importParseTime} milliseconds. (${importParseTimeRel}ms/doc)`,
|
||||
);
|
||||
log.debug(`Import Parsing took ${importParseTime} milliseconds. (${importParseTimeRel}ms/doc)`);
|
||||
|
||||
void SetJobProgress(job, "Inserting Sessions.");
|
||||
|
||||
@@ -287,12 +285,12 @@ export async function HandlePostImportSteps(
|
||||
// We create (or update existing) sessions here. This uses the aforementioned parsed import info
|
||||
// to determine what goes where.
|
||||
const sessionTimeStart = process.hrtime.bigint();
|
||||
const sessionInfo = await CreateSessions(user.id, game, scorePlaytypeMap, logger);
|
||||
const sessionInfo = await CreateSessions(user.id, game, scorePlaytypeMap, log);
|
||||
|
||||
const sessionTime = GetMillisecondsSince(sessionTimeStart);
|
||||
const sessionTimeRel = sessionTime / sessionInfo.length;
|
||||
|
||||
logger.debug(`Session Processing took ${sessionTime} milliseconds (${sessionTimeRel}ms/doc).`);
|
||||
log.debug(`Session Processing took ${sessionTime} milliseconds (${sessionTimeRel}ms/doc).`);
|
||||
|
||||
void SetJobProgress(job, "Processing scores and updating PBs.");
|
||||
|
||||
@@ -314,47 +312,47 @@ export async function HandlePostImportSteps(
|
||||
|
||||
await Promise.all(
|
||||
Object.entries(chartIDsSeparatedByPlaytype).map(([playtype, chartIDs]) =>
|
||||
ProcessPBs(game, playtype as Playtype, user.id, chartIDs, logger),
|
||||
ProcessPBs(game, playtype as Playtype, user.id, chartIDs, log),
|
||||
),
|
||||
);
|
||||
|
||||
const pbTime = GetMillisecondsSince(pbTimeStart);
|
||||
const pbTimeRel = pbTime / chartIDs.size;
|
||||
|
||||
logger.debug(`PB Processing took ${pbTime} milliseconds (${pbTimeRel}ms/doc)`);
|
||||
log.debug(`PB Processing took ${pbTime} milliseconds (${pbTimeRel}ms/doc)`);
|
||||
|
||||
void SetJobProgress(job, "Updating profile statistics.");
|
||||
|
||||
// --- 6. Game Stats ---
|
||||
// This function updates the users "stats" for this game - such as their profile rating or their classes.
|
||||
const ugsTimeStart = process.hrtime.bigint();
|
||||
const classDeltas = await UpdateUsersGameStats(game, playtypes, user.id, classProvider, logger);
|
||||
const classDeltas = await UpdateUsersGameStats(game, playtypes, user.id, classProvider, log);
|
||||
|
||||
const ugsTime = GetMillisecondsSince(ugsTimeStart);
|
||||
|
||||
logger.debug(`UGS Processing took ${ugsTime} milliseconds.`);
|
||||
log.debug(`UGS Processing took ${ugsTime} milliseconds.`);
|
||||
|
||||
void SetJobProgress(job, "Updating Goals.");
|
||||
|
||||
// --- 7. Goals ---
|
||||
// Evaluate and update the users goals. This returns information about goals that have changed.
|
||||
const goalTimeStart = process.hrtime.bigint();
|
||||
const goalInfo = await GetAndUpdateUsersGoals(game, user.id, chartIDs, logger);
|
||||
const goalInfo = await GetAndUpdateUsersGoals(game, user.id, chartIDs, log);
|
||||
|
||||
const goalTime = GetMillisecondsSince(goalTimeStart);
|
||||
|
||||
logger.debug(`Goal Processing took ${goalTime} milliseconds.`);
|
||||
log.debug(`Goal Processing took ${goalTime} milliseconds.`);
|
||||
|
||||
void SetJobProgress(job, "Updating Quests.");
|
||||
|
||||
// --- 8. Quests ---
|
||||
// Evaluate and update the users quests. This returns...
|
||||
const questTimeStart = process.hrtime.bigint();
|
||||
const questInfo = await UpdateUsersQuests(goalInfo, game, playtypes, user.id, logger);
|
||||
const questInfo = await UpdateUsersQuests(goalInfo, game, playtypes, user.id, log);
|
||||
|
||||
const questTime = GetMillisecondsSince(questTimeStart);
|
||||
|
||||
logger.debug(`Quest Processing took ${questTime} milliseconds.`);
|
||||
log.debug(`Quest Processing took ${questTime} milliseconds.`);
|
||||
|
||||
return {
|
||||
classDeltas,
|
||||
@@ -389,7 +387,7 @@ async function UpdateUsersGameStats(
|
||||
modifiedPlaytypes: Array<Playtype>,
|
||||
userID: integer,
|
||||
classProvider: ClassProvider | null,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const promises = [];
|
||||
|
||||
@@ -401,7 +399,7 @@ async function UpdateUsersGameStats(
|
||||
const playtypes = classProvider ? allPlaytypes : modifiedPlaytypes;
|
||||
|
||||
for (const pt of playtypes) {
|
||||
promises.push(UpdateUsersGamePlaytypeStats(game, pt, userID, classProvider, logger));
|
||||
promises.push(UpdateUsersGamePlaytypeStats(game, pt, userID, classProvider, log));
|
||||
}
|
||||
|
||||
const r = await Promise.all(promises);
|
||||
|
||||
+26
-26
@@ -1,6 +1,6 @@
|
||||
import type { ScoreImportJob } from "#lib/score-import/worker/types";
|
||||
|
||||
import { AppendLogCtx, type KtLogger } from "#lib/logger/logger";
|
||||
import { AppendLogCtx, type KtLogger } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { ClassToObject } from "#utils/misc";
|
||||
|
||||
@@ -44,10 +44,10 @@ export async function ImportAllIterableData<D, C>(
|
||||
ConverterFunction: ConverterFunction<D, C>,
|
||||
context: C,
|
||||
game: GameGroup,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
job: ScoreImportJob | undefined,
|
||||
): Promise<Array<ImportProcessingInfo>> {
|
||||
logger.verbose("Getting Blacklist...");
|
||||
log.verbose("Getting Blacklist...");
|
||||
|
||||
// @optimisable: could filter harder with score.game and score.playtype
|
||||
// stuff.
|
||||
@@ -57,7 +57,7 @@ export async function ImportAllIterableData<D, C>(
|
||||
})
|
||||
).map((e) => e.scoreID);
|
||||
|
||||
logger.verbose(`Starting Data Processing...`);
|
||||
log.verbose(`Starting Data Processing...`);
|
||||
|
||||
const processedResults = [];
|
||||
|
||||
@@ -89,21 +89,21 @@ export async function ImportAllIterableData<D, C>(
|
||||
|
||||
// We need to filter out nulls, which we don't care for (these are neither successes or failures)
|
||||
|
||||
logger.verbose(`Finished Importing Data (${processedResults.length} datapoints).`);
|
||||
logger.debug(`Removing null returns...`);
|
||||
log.verbose(`Finished Importing Data (${processedResults.length} datapoints).`);
|
||||
log.debug(`Removing null returns...`);
|
||||
|
||||
const datapoints = processedResults.filter((e) => e !== null) as Array<ImportProcessingInfo>;
|
||||
|
||||
logger.debug(`Removed null from results.`);
|
||||
log.debug(`Removed null from results.`);
|
||||
|
||||
logger.verbose(`received ${datapoints.length} returns, from ${processedResults.length} data.`);
|
||||
log.verbose(`received ${datapoints.length} returns, from ${processedResults.length} data.`);
|
||||
|
||||
// Flush the score queue out after finishing most of the import. This ensures no scores get left in the
|
||||
// queue.
|
||||
const emptied = await InsertQueue(userID);
|
||||
|
||||
if (emptied !== 0 && emptied !== null) {
|
||||
logger.verbose(`Emptied ${emptied} documents from score queue.`);
|
||||
log.verbose(`Emptied ${emptied} documents from score queue.`);
|
||||
}
|
||||
|
||||
return datapoints;
|
||||
@@ -125,7 +125,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
context: C,
|
||||
game: GameGroup,
|
||||
blacklist: Array<string>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): Promise<ImportProcessingInfo | null> {
|
||||
try {
|
||||
const cfnReturn = await ConverterFunction(data, context, importType, logger);
|
||||
@@ -139,7 +139,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
// if this isn't a converterFailure, it's just a general error.
|
||||
// Some sort of internal issue?
|
||||
if (!IsConverterFailure(err)) {
|
||||
logger.error(`Unknown error thrown from converter, Ignoring.`, {
|
||||
log.error(`Unknown error thrown from converter, Ignoring.`, {
|
||||
err,
|
||||
});
|
||||
return {
|
||||
@@ -159,12 +159,12 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
case "SongOrChartNotFound": {
|
||||
const dnfErr = err as SongOrChartNotFoundFailure<ImportTypes>;
|
||||
|
||||
logger.info(`SongOrChartNotFoundFailure: ${dnfErr.message}`, {
|
||||
log.info(`SongOrChartNotFoundFailure: ${dnfErr.message}`, {
|
||||
err: ClassToObject(dnfErr),
|
||||
hideFromConsole: ["cfnReturn"],
|
||||
});
|
||||
|
||||
logger.debug("Inserting orphan...", { cfnReturn: dnfErr });
|
||||
log.debug("Inserting orphan...", { cfnReturn: dnfErr });
|
||||
|
||||
const insertOrphan = await OrphanScore(
|
||||
dnfErr.importType,
|
||||
@@ -177,7 +177,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
);
|
||||
|
||||
if (insertOrphan.success) {
|
||||
logger.debug("Orphan inserted successfully.", {
|
||||
log.debug("Orphan inserted successfully.", {
|
||||
orphanID: insertOrphan.orphanID,
|
||||
});
|
||||
return {
|
||||
@@ -192,7 +192,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug(`Orphan already exists.`, { orphanID: insertOrphan.orphanID });
|
||||
log.debug(`Orphan already exists.`, { orphanID: insertOrphan.orphanID });
|
||||
|
||||
return {
|
||||
success: false,
|
||||
@@ -205,7 +205,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
}
|
||||
|
||||
case "InvalidScore": {
|
||||
logger.info(`InvalidScoreFailure: ${err.message}`, {
|
||||
log.info(`InvalidScoreFailure: ${err.message}`, {
|
||||
err: ClassToObject(err),
|
||||
hideFromConsole: ["cfnReturn"],
|
||||
});
|
||||
@@ -218,7 +218,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
}
|
||||
|
||||
case "Internal": {
|
||||
logger.error(`Internal error occured.`, { err: ClassToObject(err) });
|
||||
log.error(`Internal error occured.`, { err: ClassToObject(err) });
|
||||
return {
|
||||
success: false,
|
||||
type: "InternalError",
|
||||
@@ -232,7 +232,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
case "AmbiguousTitle": {
|
||||
const atErr = err as AmbiguousTitleFailure;
|
||||
|
||||
logger.info(`AmbiguousTitleFailure: ${err.message}`, { err: ClassToObject(err) });
|
||||
log.info(`AmbiguousTitleFailure: ${err.message}`, { err: ClassToObject(err) });
|
||||
|
||||
return {
|
||||
type: "AmbiguousTitle",
|
||||
@@ -248,7 +248,7 @@ export async function ImportIterableDatapoint<D, C>(
|
||||
return null;
|
||||
|
||||
default: {
|
||||
logger.warn(`Unknown error returned as ConverterFailure, Ignoring.`, {
|
||||
log.warn(`Unknown error returned as ConverterFailure, Ignoring.`, {
|
||||
err: ClassToObject(err),
|
||||
});
|
||||
return {
|
||||
@@ -266,7 +266,7 @@ export async function ProcessSuccessfulConverterReturn(
|
||||
userID: integer,
|
||||
cfnReturn: ConverterFnSuccessReturn,
|
||||
blacklist: Array<string>,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
forceImmediateImport = false,
|
||||
): Promise<ImportProcessingInfo | null> {
|
||||
const result = await HydrateCheckAndInsertScore(
|
||||
@@ -286,7 +286,7 @@ export async function ProcessSuccessfulConverterReturn(
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug(`Successfully imported score: ${result.scoreID}`);
|
||||
log.debug(`Successfully imported score: ${result.scoreID}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -315,7 +315,7 @@ async function HydrateCheckAndInsertScore(
|
||||
chart: ChartDocument,
|
||||
song: SongDocument,
|
||||
blacklist: Array<string>,
|
||||
importLogger: KtLogger,
|
||||
importlog: KtLogger,
|
||||
force = false,
|
||||
): Promise<ScoreDocument | null> {
|
||||
const gptString = GetGPTString(dryScore.game, chart.playtype);
|
||||
@@ -326,7 +326,7 @@ async function HydrateCheckAndInsertScore(
|
||||
const logger = AppendLogCtx(scoreID, importLogger);
|
||||
|
||||
if (blacklist.length && blacklist.includes(scoreID)) {
|
||||
logger.verbose("Skipped score, as it was on the blacklist.");
|
||||
log.verbose("Skipped score, as it was on the blacklist.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -345,13 +345,13 @@ async function HydrateCheckAndInsertScore(
|
||||
);
|
||||
|
||||
if (existingScore) {
|
||||
logger.verbose(`Skipped score.`);
|
||||
log.verbose(`Skipped score.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If this users score queue
|
||||
if (GetScoreQueueMaybe(userID)?.scoreIDSet.has(scoreID) === true) {
|
||||
logger.verbose(`Skipped score.`);
|
||||
log.verbose(`Skipped score.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ async function HydrateCheckAndInsertScore(
|
||||
|
||||
// this is a last resort for avoiding doubled imports
|
||||
if (res === null) {
|
||||
logger.verbose(`Skipped score - Race Condition protection triggered.`);
|
||||
log.verbose(`Skipped score - Race Condition protection triggered.`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ONE_HOUR } from "#lib/constants/time";
|
||||
import CreateLogCtx, { AppendLogCtx, type KtLogger } from "#lib/logger/logger";
|
||||
import { log, AppendLogCtx, type KtLogger } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import { GetChartForIDGuaranteed } from "#utils/db";
|
||||
import { GetScoresFromSession } from "#utils/session";
|
||||
@@ -30,7 +30,7 @@ export async function CreateSessions(
|
||||
userID: integer,
|
||||
game: GameGroup,
|
||||
scorePtMap: ScorePlaytypeMap,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
) {
|
||||
const allSessionInfo = [];
|
||||
|
||||
@@ -99,8 +99,6 @@ function ScoreToSessionScoreInfo(
|
||||
export async function GetSessionScoreInfo(
|
||||
session: SessionDocument,
|
||||
): Promise<Array<SessionScoreInfo>> {
|
||||
const logger = CreateLogCtx(`Session ${session.sessionID} Score Info`);
|
||||
|
||||
const scores = await db.scores.find({
|
||||
scoreID: { $in: session.scoreIDs },
|
||||
});
|
||||
@@ -187,7 +185,7 @@ export async function LoadScoresIntoSessions(
|
||||
importScores: Array<ScoreDocument>,
|
||||
game: GameGroup,
|
||||
playtype: Playtype,
|
||||
baseLogger: KtLogger,
|
||||
baselog: KtLogger,
|
||||
): Promise<Array<SessionInfoReturn>> {
|
||||
const logger = AppendLogCtx("Session Generation", baseLogger);
|
||||
|
||||
@@ -195,7 +193,7 @@ export async function LoadScoresIntoSessions(
|
||||
|
||||
for (const score of importScores) {
|
||||
if (score.timeAchieved === null) {
|
||||
logger.verbose(`Ignored score ${score.scoreID}, as it had no timeAchieved.`);
|
||||
log.verbose(`Ignored score ${score.scoreID}, as it had no timeAchieved.`);
|
||||
|
||||
// ignore scores without timestamps. We can't use these for sessions.
|
||||
continue;
|
||||
@@ -206,7 +204,7 @@ export async function LoadScoresIntoSessions(
|
||||
|
||||
// If we have nothing to work with, why bother?
|
||||
if (timestampedScores.length === 0) {
|
||||
logger.verbose(`Skipped calculating sessions as there were no timestamped scores`);
|
||||
log.verbose(`Skipped calculating sessions as there were no timestamped scores`);
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -235,7 +233,7 @@ export async function LoadScoresIntoSessions(
|
||||
// so push the group (which is guaranteed to have atleast one score)
|
||||
sessionScoreGroups.push(curGroup);
|
||||
|
||||
logger.verbose(`Created ${sessionScoreGroups.length} groups from timestamped scores.`);
|
||||
log.verbose(`Created ${sessionScoreGroups.length} groups from timestamped scores.`);
|
||||
|
||||
const sessionInfoReturns: Array<SessionInfoReturn> = [];
|
||||
|
||||
@@ -268,7 +266,7 @@ export async function LoadScoresIntoSessions(
|
||||
let infoReturn: SessionInfoReturn;
|
||||
|
||||
if (nearbySession) {
|
||||
logger.verbose(
|
||||
log.verbose(
|
||||
`Found nearby session for ${userID} (${game} ${playtype}) around ${startOfGroup} ${endOfGroup}.`,
|
||||
);
|
||||
|
||||
@@ -287,7 +285,7 @@ export async function LoadScoresIntoSessions(
|
||||
},
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
log.debug(
|
||||
`Creating new session for ${userID} (${game} ${playtype}) around ${startOfGroup} ${endOfGroup}.`,
|
||||
);
|
||||
|
||||
|
||||
+2
-4
@@ -2,14 +2,12 @@ import type { ScoreImportJobData } from "#lib/score-import/worker/types";
|
||||
|
||||
import { CDNStoreOrOverwrite } from "#lib/cdn/cdn";
|
||||
import { GetScoreImportInputURL } from "#lib/cdn/url-format";
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
|
||||
import type { ImportTypes } from "../../../../../../common/src";
|
||||
import type ScoreImportFatalError from "../score-importing/score-import-error";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
/**
|
||||
* For us to save the incoming parserArguments,
|
||||
* we want to pretty it up a bit into something
|
||||
@@ -52,7 +50,7 @@ export async function StartTrackingImport(jobData: ScoreImportJobData<ImportType
|
||||
// to store large amounts of write-only data, so lets do that.
|
||||
CDNStoreOrOverwrite(GetScoreImportInputURL(jobData.importID), SerialiseJobData(jobData)).catch(
|
||||
(err) => {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Failed to save score-import-input for import '${
|
||||
jobData.importID
|
||||
}' at path '${GetScoreImportInputURL(jobData.importID)}'.`,
|
||||
|
||||
+1
-3
@@ -1,4 +1,4 @@
|
||||
import CreateLogCtx from "#lib/logger/logger";
|
||||
import { log } from "#lib/logger/log.js";
|
||||
import db from "#services/mongo/db";
|
||||
import ResetDBState from "#test-utils/resets";
|
||||
import { TestingIIDXSPScorePB } from "#test-utils/test-data";
|
||||
@@ -8,8 +8,6 @@ import t from "tap";
|
||||
|
||||
import { UpdateUsersGamePlaytypeStats } from "./update-ugpt-stats";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
// more of an integration test
|
||||
t.test("#UpdateUsersGamePlaytypeStats", (t) => {
|
||||
t.beforeEach(ResetDBState);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import { CreateGameSettings } from "#lib/game-settings/create-game-settings";
|
||||
import db from "#services/mongo/db";
|
||||
@@ -20,9 +20,9 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
playtype: Playtype,
|
||||
userID: integer,
|
||||
classProvider: ClassProvider | null,
|
||||
logger: KtLogger,
|
||||
log: KtLogger,
|
||||
): Promise<Array<ClassDelta>> {
|
||||
logger.debug(`Calculating Ratings...`);
|
||||
log.debug(`Calculating Ratings...`);
|
||||
|
||||
const ratings = await CalculateProfileRatings(game, playtype, userID);
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
userID,
|
||||
});
|
||||
|
||||
logger.debug(`Calculating UGSClasses...`);
|
||||
log.debug(`Calculating UGSClasses...`);
|
||||
|
||||
const classes = await CalculateUGPTClasses(
|
||||
game,
|
||||
@@ -45,16 +45,16 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
logger,
|
||||
);
|
||||
|
||||
logger.debug(`Finished Calculating UGSClasses`);
|
||||
log.debug(`Finished Calculating UGSClasses`);
|
||||
|
||||
logger.debug(`Calculating Class Deltas...`);
|
||||
log.debug(`Calculating Class Deltas...`);
|
||||
|
||||
const deltas = await ProcessClassDeltas(game, playtype, classes, userGameStats, userID, logger);
|
||||
|
||||
logger.debug(`Had ${deltas.length} deltas.`);
|
||||
log.debug(`Had ${deltas.length} deltas.`);
|
||||
|
||||
if (userGameStats) {
|
||||
logger.debug(`Updated player gamestats for ${game} (${playtype})`);
|
||||
log.debug(`Updated player gamestats for ${game} (${playtype})`);
|
||||
|
||||
const updateClasses: Record<string, string> = {};
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
});
|
||||
|
||||
if (!hasAnyScores) {
|
||||
logger.debug("Not creating new game stats for user with no scores.", {
|
||||
log.debug("Not creating new game stats for user with no scores.", {
|
||||
userID,
|
||||
game,
|
||||
playtype,
|
||||
@@ -99,7 +99,7 @@ export async function UpdateUsersGamePlaytypeStats(
|
||||
classes,
|
||||
};
|
||||
|
||||
logger.info(`Created new gamestats for ${game} (${playtype})`);
|
||||
log.info(`Created new gamestats for ${game} (${playtype})`);
|
||||
await db["game-stats"].insert(newStats);
|
||||
await CreateGameSettings(userID, game, playtype);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { KtLogger } from "#lib/logger/logger";
|
||||
import type { KtLogger } from "#lib/logger/log.js";
|
||||
|
||||
import { GetKaiAuthGuaranteed } from "#utils/queries/auth";
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { integer } from "../../../../../../../common/src";
|
||||
|
||||
import { ParseKaiIIDX } from "../../common/api-kai/iidx/parser";
|
||||
|
||||
export async function ParseEagIIDX(userID: integer, logger: KtLogger) {
|
||||
export async function ParseEagIIDX(userID: integer, log: KtLogger) {
|
||||
const authDoc = await GetKaiAuthGuaranteed(userID, "EAG", logger);
|
||||
|
||||
return ParseKaiIIDX("EAG", authDoc, logger);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user