Merge pull request #710 from TNG-dev/zkldi/iidx-28-omni-test

This commit is contained in:
zkldi
2022-10-07 01:57:55 +01:00
committed by GitHub
9 changed files with 302 additions and 81 deletions
+1 -2
View File
@@ -59,7 +59,7 @@ function syncDatabaseWithSeeds {
cd server
pnpm run sync-database
pnpm run sync-database-local
echo "Synced."
}
@@ -69,7 +69,6 @@ selfSignHTTPS
pnpmInstall
syncDatabaseWithSeeds
tput setaf 1
cat << EOF
+144 -64
View File
@@ -14801,7 +14801,7 @@
"hashSHA256": null,
"inGameID": 1208,
"kaidenAverage": null,
"notecount": 209,
"notecount": 184,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -80124,7 +80124,9 @@
"rgcID": null,
"songID": 143,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "96879b78bc80745249933dbb9a8ebe6c8f3ec8f7",
@@ -80196,7 +80198,9 @@
"rgcID": null,
"songID": 143,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "9292d34c2132c1da50b1d748e9c25fead37a2c98",
@@ -80268,7 +80272,9 @@
"rgcID": null,
"songID": 143,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "e5db54b7385da1557af89512b658cb18a22ee5f3",
@@ -80354,7 +80360,7 @@
"hashSHA256": null,
"inGameID": 4216,
"kaidenAverage": null,
"notecount": 442,
"notecount": 443,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -80365,7 +80371,9 @@
"rgcID": null,
"songID": 143,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "84dc89843e9859fef23c5a2c3b1913c43f81116f",
@@ -124603,8 +124611,8 @@
},
"difficulty": "ANOTHER",
"isPrimary": true,
"level": "0",
"levelNum": 0,
"level": "7",
"levelNum": 7,
"playtype": "SP",
"rgcID": null,
"songID": 211,
@@ -175700,7 +175708,7 @@
]
},
{
"chartID": "cd89731d004cdcc5292aa10d11c181d8d0545aa5",
"chartID": "e17adc8e3575d2c0cb873f913e3411e6527e3d23",
"data": {
"2dxtraSet": null,
"arcChartID": null,
@@ -175711,6 +175719,30 @@
"notecount": 682,
"worldRecord": null
},
"difficulty": "ANOTHER",
"isPrimary": true,
"level": "8",
"levelNum": 8,
"playtype": "DP",
"rgcID": null,
"songID": 290,
"tierlistInfo": {},
"versions": [
"28-omni"
]
},
{
"chartID": "cd89731d004cdcc5292aa10d11c181d8d0545aa5",
"data": {
"2dxtraSet": null,
"arcChartID": null,
"bpiCoefficient": null,
"hashSHA256": null,
"inGameID": 8022,
"kaidenAverage": null,
"notecount": 503,
"worldRecord": null
},
"difficulty": "HYPER",
"isPrimary": true,
"level": "9",
@@ -175722,12 +175754,10 @@
"versions": [
"27-omni",
"26-omni",
"8-cs",
"bmus",
"6-cs",
"27-2dxtra",
"28-omni",
"28-2dxtra"
"28-2dxtra",
"16-cs"
]
},
{
@@ -175835,7 +175865,7 @@
"hashSHA256": null,
"inGameID": 8022,
"kaidenAverage": null,
"notecount": 503,
"notecount": 267,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -175849,12 +175879,10 @@
"versions": [
"27-omni",
"26-omni",
"8-cs",
"bmus",
"6-cs",
"27-2dxtra",
"28-omni",
"28-2dxtra"
"28-2dxtra",
"16-cs"
]
},
{
@@ -176001,6 +176029,30 @@
"28-2dxtra"
]
},
{
"chartID": "0b4ae8fb5ae20b5277d52953c7cd26ff84d67f18",
"data": {
"2dxtraSet": null,
"arcChartID": null,
"bpiCoefficient": null,
"hashSHA256": null,
"inGameID": 8022,
"kaidenAverage": null,
"notecount": 656,
"worldRecord": null
},
"difficulty": "ANOTHER",
"isPrimary": true,
"level": "8",
"levelNum": 8,
"playtype": "SP",
"rgcID": null,
"songID": 290,
"tierlistInfo": {},
"versions": [
"28-omni"
]
},
{
"chartID": "06a866286936157f613ec50e26b4d774bfc70b2d",
"data": {
@@ -181969,7 +182021,8 @@
"songID": 298,
"tierlistInfo": {},
"versions": [
"inf"
"inf",
"28-omni"
]
},
{
@@ -182141,7 +182194,8 @@
"songID": 298,
"tierlistInfo": {},
"versions": [
"inf"
"inf",
"28-omni"
]
},
{
@@ -182239,7 +182293,8 @@
"songID": 298,
"tierlistInfo": {},
"versions": [
"inf"
"inf",
"28-omni"
]
},
{
@@ -182411,7 +182466,8 @@
"songID": 298,
"tierlistInfo": {},
"versions": [
"inf"
"inf",
"28-omni"
]
},
{
@@ -219920,8 +219976,8 @@
},
"difficulty": "NORMAL",
"isPrimary": true,
"level": "4",
"levelNum": 4,
"level": "3",
"levelNum": 3,
"playtype": "SP",
"rgcID": null,
"songID": 356,
@@ -402332,7 +402388,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "afb62aab0829002d5cc7280749c6580aa1f378cd",
@@ -402354,7 +402412,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "9ec852789f818e7497275cd533458c9d5e068da5",
@@ -402664,7 +402724,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "c5650359963ba876e6ec18f2eb0332ebaeddc9e4",
@@ -402830,7 +402892,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "c49713794172b674d059270ff96e9e9d5f1e1c45",
@@ -402852,7 +402916,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "9e597d306bc3b54117947e914e6189d9a849ff06",
@@ -403162,7 +403228,9 @@
"rgcID": null,
"songID": 629,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "6f7e9548837a038af0fb1aebdcdd41368a46a4c1",
@@ -407027,7 +407095,7 @@
"hashSHA256": null,
"inGameID": 13209,
"kaidenAverage": null,
"notecount": 529,
"notecount": 531,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -408634,7 +408702,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "3cb55a166faa0a3f537bd2fb68e6718dfc92be34",
@@ -408656,7 +408726,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "1e0735f550dc50c6ff6e66f7e6d402b41561be05",
@@ -408966,7 +409038,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "33c475f343e10212508dd71c3f7aa7b4bafb9493",
@@ -409132,7 +409206,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "7a612f4f90a1349a2942412fa81bf7082817ec22",
@@ -409154,7 +409230,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "0c7fb1fff2e323e3ccb0dce1da1ad848952efb96",
@@ -409464,7 +409542,9 @@
"rgcID": null,
"songID": 636,
"tierlistInfo": {},
"versions": []
"versions": [
"28-omni"
]
},
{
"chartID": "0b5216ff09349bf48c3e9013ccf35a3238039333",
@@ -588714,7 +588794,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 897,
"notecount": 901,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -588748,7 +588828,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 703,
"notecount": 707,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -588932,7 +589012,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 409,
"notecount": 414,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -589041,7 +589121,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 892,
"notecount": 894,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -589075,7 +589155,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 673,
"notecount": 676,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -589259,7 +589339,7 @@
"hashSHA256": null,
"inGameID": 17030,
"kaidenAverage": null,
"notecount": 409,
"notecount": 412,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -606168,8 +606248,8 @@
},
"difficulty": "LEGGENDARIA",
"isPrimary": true,
"level": "0",
"levelNum": 0,
"level": "12",
"levelNum": 12,
"playtype": "DP",
"rgcID": null,
"songID": 908,
@@ -619861,7 +619941,7 @@
"hashSHA256": null,
"inGameID": 18007,
"kaidenAverage": null,
"notecount": 582,
"notecount": 583,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -666522,7 +666602,7 @@
"hashSHA256": null,
"inGameID": 18102,
"kaidenAverage": null,
"notecount": 427,
"notecount": 429,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -679696,7 +679776,7 @@
"hashSHA256": null,
"inGameID": 19017,
"kaidenAverage": null,
"notecount": 1067,
"notecount": 1068,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -679910,7 +679990,7 @@
"hashSHA256": null,
"inGameID": 19017,
"kaidenAverage": null,
"notecount": 315,
"notecount": 316,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -680231,7 +680311,7 @@
"hashSHA256": null,
"inGameID": 19017,
"kaidenAverage": null,
"notecount": 295,
"notecount": 297,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -933000,7 +933080,7 @@
"hashSHA256": null,
"inGameID": 23023,
"kaidenAverage": null,
"notecount": 1182,
"notecount": 1183,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -933032,7 +933112,7 @@
"hashSHA256": null,
"inGameID": 23023,
"kaidenAverage": null,
"notecount": 541,
"notecount": 543,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -933321,7 +933401,7 @@
"hashSHA256": null,
"inGameID": 23023,
"kaidenAverage": null,
"notecount": 1185,
"notecount": 1186,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -933353,7 +933433,7 @@
"hashSHA256": null,
"inGameID": 23023,
"kaidenAverage": null,
"notecount": 561,
"notecount": 562,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -944901,7 +944981,7 @@
"hashSHA256": null,
"inGameID": 23040,
"kaidenAverage": null,
"notecount": 939,
"notecount": 940,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -945083,7 +945163,7 @@
"hashSHA256": null,
"inGameID": 23040,
"kaidenAverage": null,
"notecount": 491,
"notecount": 492,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -1025917,7 +1025997,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 1223,
"notecount": 1225,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -1025950,7 +1026030,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 936,
"notecount": 940,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -1026133,7 +1026213,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 388,
"notecount": 393,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -1026241,7 +1026321,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 1098,
"notecount": 1100,
"worldRecord": null
},
"difficulty": "ANOTHER",
@@ -1026274,7 +1026354,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 694,
"notecount": 696,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -1026457,7 +1026537,7 @@
"hashSHA256": null,
"inGameID": 24064,
"kaidenAverage": null,
"notecount": 387,
"notecount": 389,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -1191109,7 +1191189,7 @@
"hashSHA256": null,
"inGameID": 20042,
"kaidenAverage": null,
"notecount": 857,
"notecount": 859,
"worldRecord": null
},
"difficulty": "HYPER",
@@ -1191293,7 +1191373,7 @@
"hashSHA256": null,
"inGameID": 20042,
"kaidenAverage": null,
"notecount": 447,
"notecount": 448,
"worldRecord": null
},
"difficulty": "NORMAL",
@@ -1,2 +1,5 @@
# this gets flooded with extracted .ifses
ifs-output/*
ifs-output/*
# parser.ts spits here if invoked by hand
debug.json
@@ -14,3 +14,9 @@ S16072$
S16080$
S16081$
S16082$
# there are two songs called Changes in omnimix. We support neither.
# this is because it means we can't use songTitle as a matching thing
# even though we really want to.
S10029$
S6211$
@@ -18,6 +18,7 @@ import {
} from "./types";
import fs from "fs/promises";
import logger from "../../../logger";
import path from "path";
interface EventParserContext {
isDP?: boolean;
@@ -228,7 +229,11 @@ function ParseEvent(
obj.col = 1;
} else {
logger.warn(
`Unknown column of scratchObj sent to EventParser: ${data.param}, expected 7 or 14. Skipping.`
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Unknown column of scratchObj sent to EventParser: ${
data.param
}, expected 7 or 14. Skipping.`
);
return null;
}
@@ -251,7 +256,13 @@ function ParseEvent(
};
} else if (type === "BPM") {
if (data.param === 0) {
logger.warn("Invalid value of 0 for denominator in BPM event. Skipping.");
logger.warn(
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Invalid value of 0 for denominator in BPM event (numerator was ${
data.val
}). Skipping.`
);
return null;
}
@@ -291,7 +302,9 @@ function ParseEvent(
if (!window) {
logger.warn(
`Unknown type of timingWindow: ${window}, from .1 param ${data.param}. Skipping.`
`(${
parseContext.songTitle ?? "UNKNOWN"
}) Unknown type of timingWindow: ${window}, from .1 param ${data.param}. Skipping.`
);
return null;
}
@@ -328,7 +341,9 @@ function ParseEvent(
if (parseContext.isDP) {
if (data.param !== 0 && data.param !== 1) {
logger.warn(
`Invalid parameter for measurebar of ${data.param}. Defaulting to 0 (left hand side).`
`(${parseContext.songTitle ?? "UNKNOWN"}) Invalid parameter for measurebar of ${
data.param
}. Defaulting to 0 (left hand side).`
);
obj.side = 0;
} else {
@@ -353,15 +368,15 @@ function ParseEvent(
// this chart is using HCNs.
options.isHellCharge = true;
return null;
} else if (data.type === 11) {
// it's some sort of unknown BGA-related thing. Only seems to happen in older
// charts. Maybe it was some sort of diverging bga instruction at one point?
return null;
} else {
logger.warn(
`(${parseContext.songTitle ?? "UNKNOWN"}) Unknown event type in .1: ${
data.type
} at ${ms.toFixed(2)}ms. Ignoring.${
data.type === 11
? " (It's BGA Related. Not sure what it does, but don't worry.)"
: ""
}`
} at ${ms.toFixed(2)}ms. Ignoring.`
);
return null;
}
@@ -457,7 +472,9 @@ export function ParseDotOne(
if (!difficulty) {
logger.warn(
`Data present inside directory ${i}, but this corresponds to an unknown difficulty. Ignoring.`
`(${
context.songTitle ?? "UNKNOWN"
}) Data present inside directory ${i}, but this corresponds to an unknown difficulty. Ignoring.`
);
continue;
}
@@ -506,11 +523,20 @@ function getNotecount(events: IIDXChartEvents) {
}
export async function ParseDotOneFile(
fileath: string,
filepath: string,
context: IIDXParserContext = {},
options: IIDXParserOptions = {}
) {
const data = await fs.readFile(fileath);
const data = await fs.readFile(filepath);
return ParseDotOne(data, context, options);
}
// if invoked as a script, parse the dotone and spit it out to debug.json.
if (require.main === module) {
(async () => {
const res = await ParseDotOneFile(process.argv.slice(2).join(" "));
await fs.writeFile(path.join(__dirname, "debug.json"), JSON.stringify(res, null, "\t"));
})();
}
+1 -1
View File
@@ -15,7 +15,7 @@
"start-no-build": "node -r ts-node/register -r tsconfig-paths/register js/server/src/main.js",
"runscoreworker": "node -r ts-node/register -r tsconfig-paths/register js/server/src/lib/score-import/worker/worker.js",
"sync-database": "ts-node src/scripts/sync-database",
"sync-database-local": "ts-node src/scripts/sync-database --localPath '../database-seeds'",
"sync-database-local": "ts-node src/scripts/sync-database --localPath '../database-seeds/collections'",
"recalc-everything": "ts-node src/scripts/state-sync/sync-state.ts"
},
"author": "zkldi",
+2
View File
@@ -1,4 +1,5 @@
import UGPTRivalsMigration from "./migrations/add-rivals-to-ugpt";
import RecalcBrokenIIDXNotecounts from "./migrations/recalc-broken-iidx-notecounts";
import RemoveIIDXBeginners from "./migrations/remove-iidx-beginners";
import RemoveMultifolderStats from "./migrations/remove-multifolder-stats";
import UpdateJubeatPreferredTables from "./migrations/update-jubeat-preferred-tables";
@@ -31,6 +32,7 @@ const REGISTERED_MIGRATIONS: Array<Migration> =
RemoveMultifolderStats,
RemoveIIDXBeginners,
UpdateJubeatPreferredTables,
RecalcBrokenIIDXNotecounts,
];
function CreateMigrationLookupMap(migrations: Array<Migration>) {
@@ -0,0 +1,105 @@
// Charts that had their notecounts changed as the result of a recent discovered
import db from "external/mongo/db";
import { GenericGetGradeAndPercent } from "lib/score-import/framework/common/score-utils";
import { DeleteMultipleScores } from "lib/score-mutation/delete-scores";
import UpdateScore from "lib/score-mutation/update-score";
import { GetGamePTConfig } from "tachi-common";
import type { ScoreDocument, ChartDocument } from "tachi-common";
import type { Migration } from "utils/types";
// bug in the seeds.
const recalcChartIDs = [
"38ebf85a6b9079bed6acb418cc3866f3a6b6adb7",
"88a5f281abf7ce08fde62769edb50b2ed8a1eeb9",
"fcb31b1536d97bfb213cc574462d68d40f9fb15e",
"bd67cde14867804902f526c61df59982cb4ffa3e",
"241d190af5648d98a91f3b37585d648020d272ff",
"7275b7c88df34b6401fb99da7dece2af785cfc4f",
"ce71f9c794c429b71ca37ca0615b7b983b3d16ef",
"13d6019c21f8fd53b141000c742d4d2a37616aea",
"e00c3264172f717675e9c4c2d4ee82f2540a6772",
"76b502d2d39861095dc10f691d214bd9f482a815",
"4ad68e155dae973d737d7d8ac57453abcc4ac035",
"8e60d1ad926add5e84fb6de5a0924c89c365f80f",
"a73460d18cf2c6d13a09a46e83073fbd989d2222",
"21a475985581c40b616fdfb8ee60d16f64c9bbfa",
"a0687bc6951293e803bc121526948dc49aa1394f",
"2a80757be4c72f3674c96ff4bdff75deea120892",
"37817b02053862c7887f8e30cda23cd5f2c3be4d",
"df5543a6822c307dfdbdf162fecf6da013043fff",
"828dc62f7cabaf33db72158fdfc5c9ffb89adb4e",
"7e7e6866fc5c587a8029aa1bf0bec56441c8c519",
"8a51375d59bbe382f9e951d69a8f814b359b570b",
"8ce008ee846349caba96edd4538e7a9c148f8af7",
"c8805a4e481bb124b6687c44d3d359bdca4dd50e",
"adc8458064d179fef6302c12d691f9ff58d92163",
"f654276295ca8c228da91c230cc8ac86b23d16e9",
"1a856ae72278eedc5ac19050bc1cb441885ac46d",
"d87e7aba6edf70bd4b0087165afaa82fad801db3",
"85a4e8cebef9a12dddb407ff581f499a078f6010",
];
// jelly kiss DPN and DPH are now unreliable
// their charts were changed at some point, but the versions are wrong,
// we have to wipe these charts, sadly.
const toWipe = [
"cd89731d004cdcc5292aa10d11c181d8d0545aa5",
"8520f95da6f51548adde23ce84bf800e4a19334b",
];
const migration: Migration = {
id: "recalc-broken-iidx-notecounts",
up: async () => {
const toDelete = await db.scores.find({
game: "iidx",
chartID: { $in: toWipe },
});
await DeleteMultipleScores(toDelete);
await Promise.all(recalcChartIDs.map((e) => HandleChangedIIDXNotecount(e)));
},
down: () => {
throw new Error(`Reverting this change is not possible.`);
},
};
async function HandleChangedIIDXNotecount(chartID: string) {
const scores = await db.scores.find({ chartID });
const chart = await db.charts.iidx.findOne({ chartID });
if (!chart) {
throw new Error(`No such chart with the ID ${chartID} exists.`);
}
return Promise.all(scores.map((e) => RecalcPercentForIIDXScore(e, chart)));
}
function RecalcPercentForIIDXScore(score: ScoreDocument, chart: ChartDocument) {
const { percent, grade } = GenericGetGradeAndPercent("iidx", score.scoreData.score, chart);
const gptConfig = GetGamePTConfig("iidx", score.playtype);
const gradeIndex = gptConfig.grades.indexOf(grade);
// shouldn't happen, but lets check anyway.
if (gradeIndex === -1) {
throw new Error(
`Failed to calculate new grade? ${percent} ${grade} on chartID:${chart.chartID}.`
);
}
const newScore: ScoreDocument = {
...score,
scoreData: {
...score.scoreData,
percent,
grade,
gradeIndex,
},
};
return UpdateScore(score, newScore);
}
export default migration;
+1 -1
View File
@@ -340,7 +340,7 @@ const logger = CreateLogCtx("Database Sync");
const program = new Command();
program.option("-l, --localPath <path/to/database-seeds>");
program.option("-l, --localPath <path/to/database-seeds/collections>");
program.parse(process.argv);
const options: {