Add USC DB Migration script.

This commit is contained in:
zkldi
2022-02-06 19:04:20 +00:00
parent 410908b3d1
commit d529ad79d7
7 changed files with 7239 additions and 10 deletions
+171
View File
@@ -0,0 +1,171 @@
import db from "external/mongo/db";
import fs from "fs";
import {
USC_DEFAULT_HOLD,
USC_DEFAULT_MISS,
USC_DEFAULT_NEAR,
USC_DEFAULT_PERFECT,
USC_DEFAULT_SLAM,
} from "lib/constants/usc-ir";
import CreateLogCtx from "lib/logger/logger";
import { OrphanScore } from "lib/score-import/framework/orphans/orphans";
import UpdateScore from "lib/score-mutation/update-score";
import { integer, ScoreDocument } from "tachi-common";
import { DeleteScore } from "lib/score-mutation/delete-scores";
import { UpdateAllPBs } from "utils/calculations/recalc-scores";
const logger = CreateLogCtx(__filename);
const oldChartIDtoSHA1 = JSON.parse(fs.readFileSync(`${__dirname}/usc-map.json`, "utf-8"));
if (require.main === module) {
(async () => {
const keep = await db.charts.usc.find(
{ "data.hashSHA1": { $in: Object.values(oldChartIDtoSHA1) } },
{ projection: { chartID: 1, "data.hashSHA1": 1, songID: 1, playtype: 1 } }
);
// @ts-expect-error asdf
const keepHashes = keep.map((e) => e.data.hashSHA1);
const keepChartIDs = Object.entries(oldChartIDtoSHA1)
.filter(([, v]) => keepHashes.includes(v))
.map(([k]) => k);
const rmScores = (await db.scores.find({
game: "usc",
chartID: { $nin: keepChartIDs },
})) as ScoreDocument<"usc:Controller" | "usc:Keyboard">[];
logger.info(`Re-orphaning and unseating ${rmScores.length} scores.`);
let i = 0;
for (const score of rmScores) {
i++;
if (i % 100 === 0) {
logger.info(`${i}/${rmScores.length}...`);
}
const chartSHA1 = oldChartIDtoSHA1[score.chartID];
if (!chartSHA1) {
logger.warn(`Can't find a chartsha1 for ${score.chartID}?`);
continue;
}
await ReOrphanScore(score, chartSHA1);
await DeleteScore(score, false, false);
}
const keepScores = await db.scores.find({
game: "usc",
chartID: { $in: keepChartIDs },
});
logger.info(`Keeping and migrating ${keepScores.length} scores.`);
const sha1ToChartID = Object.fromEntries(
keep.map((e) => [
// @ts-expect-error hashsha1 exists i'm just lazy
`${e.data.hashSHA1}:${e.playtype}`,
{ chartID: e.chartID, songID: e.songID },
])
) as { [key: string]: { chartID: string; songID: integer } };
let j = 0;
for (const score of keepScores) {
j++;
if (j % 100 === 0) {
logger.info(`${j}/${keepScores.length}`);
}
const sha1 = oldChartIDtoSHA1[score.chartID];
const newChartID = sha1ToChartID[`${sha1}:${score.playtype}`].chartID;
const newSongID = sha1ToChartID[`${sha1}:${score.playtype}`].songID;
if (!newChartID) {
logger.error(
`Couldn't resolve old:${score.chartID} -> sha1:${sha1} (playtype: ${score.playtype}) -> new:${newChartID}?`
);
throw new Error("panic");
}
await UpdateScore(
score,
Object.assign({}, score, { songID: newSongID, chartID: newChartID }),
false
);
}
// You'd think the update scripts don't result in pbs that point to
// dead charts, but they do.
logger.info(`Reloading USC PBs.`);
await db["personal-bests"].remove({ game: "usc" });
await UpdateAllPBs(undefined, { game: "usc" });
logger.info(`Done.`);
process.exit(0);
})();
}
function ReOrphanScore(sc: ScoreDocument<"usc:Controller" | "usc:Keyboard">, chartSHA1: string) {
let mirror = false;
let random = false;
if (sc.scoreMeta.noteMod === "MIR-RAN") {
mirror = true;
random = false;
} else if (sc.scoreMeta.noteMod === "RANDOM") {
random = true;
} else if (sc.scoreMeta.noteMod === "MIRROR") {
mirror = true;
}
const gaugeType =
sc.scoreMeta.gaugeMod === "NORMAL"
? 0
: sc.scoreMeta.gaugeMod === "HARD"
? 1
: sc.scoreMeta.gaugeMod === "PERMISSIVE"
? 2
: 0;
return OrphanScore(
"ir/usc",
sc.userID,
{
combo: sc.scoreData.hitMeta.maxCombo ?? null,
crit: sc.scoreData.judgements.critical!,
near: sc.scoreData.judgements.near!,
error: sc.scoreData.judgements.miss!,
early: sc.scoreData.hitMeta.fast ?? null,
late: sc.scoreData.hitMeta.slow ?? null,
gauge: sc.scoreData.hitMeta.gauge!,
options: {
autoFlags: 0,
gaugeOpt: 0,
gaugeType,
mirror,
random,
},
score: sc.scoreData.score,
timestamp: sc.timeAchieved!,
windows: {
perfect: USC_DEFAULT_PERFECT,
good: USC_DEFAULT_NEAR,
hold: USC_DEFAULT_HOLD,
miss: USC_DEFAULT_MISS,
slam: USC_DEFAULT_SLAM,
},
},
{
chartHash: chartSHA1,
playtype: sc.playtype,
timeReceived: sc.timeAchieved!,
},
`Lost parent as a result of USC migration.`,
"usc",
logger
);
}
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -32,8 +32,10 @@ const staticIndexes: Partial<Record<Databases, Index[]>> = {
index({ userID: 1, game: 1, playtype: 1 }),
],
sessions: [
// lol
index({ userID: 1, game: 1, playtype: 1, timeStarted: 1, timeEnded: 1 }),
// Optimises score modification, since sessions need to be repointed.
// also, just generally useful.
index({ "scoreInfo.scoreID": 1 }),
index({ name: "text" }),
],
"game-stats": [index({ userID: 1, game: 1, playtype: 1 }, UNIQUE)],
@@ -43,7 +43,13 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
logger.debug("Orphaning document", orphan);
const orphanID = `O${fjsh.hash(orphan, "sha256")}`;
let orphanID;
try {
orphanID = `O${fjsh.hash(orphan, "sha256")}`;
} catch (err) {
logger.error(`Failed to orphan chart -- `, { err, orphan });
throw new Error(`Failed to orphan chart. ${err.message}`);
}
const exists = await db["orphan-scores"].findOne({ orphanID });
@@ -12,7 +12,11 @@ const logger = CreateLogCtx(__filename);
* Deletes the provided score. This needs a dedicated helper method due to
* needing to unset things like sessions and recalcs.
*/
export async function DeleteScore(score: ScoreDocument, blacklist = false) {
export async function DeleteScore(
score: ScoreDocument,
blacklist = false,
attemptPBReprocess = true
) {
await db.scores.remove({
scoreID: score.scoreID,
});
@@ -79,7 +83,7 @@ export async function DeleteScore(score: ScoreDocument, blacklist = false) {
chartID: score.chartID,
});
if (userHasOtherScores) {
if (userHasOtherScores && attemptPBReprocess) {
await ProcessPBs(score.userID, new Set([score.chartID]), logger);
} else {
await db["personal-bests"].remove({
+12 -5
View File
@@ -14,7 +14,11 @@ import { FormatUserDoc, GetUserWithID } from "utils/user";
*
* @note You don't need to recalc the scoreID for newScore, it's done for you.
*/
export default async function UpdateScore(oldScore: ScoreDocument, newScore: ScoreDocument) {
export default async function UpdateScore(
oldScore: ScoreDocument,
newScore: ScoreDocument,
updateOldChart = true
) {
const userID = oldScore.userID;
const user = await GetUserWithID(userID);
@@ -141,12 +145,15 @@ export default async function UpdateScore(oldScore: ScoreDocument, newScore: Sco
await UpdateAllPBs([userID], {
chartID: newScore.chartID,
});
await UpdateAllPBs([userID], {
chartID: oldScore.chartID,
});
await UpdateChartRanking(newScore.chartID);
await UpdateChartRanking(oldScore.chartID);
if (updateOldChart) {
await UpdateAllPBs([userID], {
chartID: oldScore.chartID,
});
await UpdateChartRanking(oldScore.chartID);
}
const imports = await db.imports.find({
scoreIDs: oldScoreID,
@@ -109,5 +109,5 @@ export async function UpdateAllPBs(userIDs?: integer[], filter = {}) {
await ProcessPBs(user.id, new Set(scores.map((e) => e.chartID)), logger);
}
logger.info(`Done!`);
logger.verbose(`Done!`);
}