Builds migration with seed

This commit is contained in:
Kalle 2022-08-30 09:49:17 +03:00
parent 1c53c03294
commit 0efff0c907
12 changed files with 243 additions and 17 deletions

2
.gitignore vendored
View File

@ -5,6 +5,8 @@ node_modules
/public/build
.env
notes.md
db*.sqlite3*
dump

View File

@ -3,6 +3,7 @@ import * as plusSuggestions from "./models/plusSuggestions/queries.server";
import * as plusVotes from "./models/plusVotes/queries.server";
import * as badges from "./models/badges/queries.server";
import * as calendarEvents from "./models/calendar/queries.server";
import * as builds from "./models/builds/queries.server";
export const db = {
users,
@ -10,4 +11,5 @@ export const db = {
plusVotes,
badges,
calendarEvents,
builds,
};

View File

@ -0,0 +1,20 @@
insert into
"Build" (
"ownerId",
"title",
"description",
"modes",
"headGearSplId",
"clothesGearSplId",
"shoesGearSplId"
)
values
(
@ownerId,
@title,
@description,
@modes,
@headGearSplId,
@clothesGearSplId,
@shoesGearSplId
) returning *

View File

@ -0,0 +1,4 @@
insert into
"BuildAbility" ("buildId", "gearType", "ability", "slotIndex")
values
(@buildId, @gearType, @ability, @slotIndex)

View File

@ -0,0 +1,4 @@
insert into
"BuildWeapon" ("buildId", "weaponSplId")
values
(@buildId, @weaponSplId)

View File

@ -0,0 +1,53 @@
import { sql } from "~/db/sql";
import type { Build, BuildAbility, BuildWeapon } from "~/db/types";
import type { Ability, ModeShort } from "~/modules/in-game-lists";
import createBuildSql from "./createBuild.sql";
import createBuildWeaponSql from "./createBuildWeapon.sql";
import createBuildAbilitySql from "./createBuildAbility.sql";
const createBuildStm = sql.prepare(createBuildSql);
const createBuildWeaponStm = sql.prepare(createBuildWeaponSql);
const createBuildAbilityStm = sql.prepare(createBuildAbilitySql);
interface CreateArgs {
ownerId: Build["ownerId"];
title: Build["title"];
description: Build["description"];
modes: Array<ModeShort> | null;
headGearSplId: Build["headGearSplId"];
clothesGearSplId: Build["clothesGearSplId"];
shoesGearSplId: Build["shoesGearSplId"];
weaponSplIds: Array<BuildWeapon["weaponSplId"]>;
abilities: Array<{
gearType: BuildAbility["gearType"];
ability: Ability;
slotIndex: BuildAbility["slotIndex"];
}>;
}
export const create = sql.transaction((build: CreateArgs) => {
const createdBuild = createBuildStm.get({
ownerId: build.ownerId,
title: build.title,
description: build.description,
modes: build.modes?.join(",") ?? null,
headGearSplId: build.headGearSplId,
clothesGearSplId: build.clothesGearSplId,
shoesGearSplId: build.shoesGearSplId,
}) as Build;
for (const weaponSplId of build.weaponSplIds) {
createBuildWeaponStm.run({
buildId: createdBuild.id,
weaponSplId,
});
}
for (const { gearType, ability, slotIndex } of build.abilities) {
createBuildAbilityStm.run({
buildId: createdBuild.id,
gearType,
ability,
slotIndex,
});
}
});

View File

@ -1,17 +1,25 @@
import { faker } from "@faker-js/faker";
import capitalize from "just-capitalize";
import shuffle from "just-shuffle";
import invariant from "tiny-invariant";
import { ADMIN_DISCORD_ID } from "~/constants";
import { db } from "~/db";
import { sql } from "~/db/sql";
import {
abilityCodes,
clothesGearIds,
headGearIds,
modesShort,
shoesGearIds,
weaponIds,
} from "~/modules/in-game-lists";
import {
lastCompletedVoting,
nextNonCompletedVoting,
} from "~/modules/plus-server";
import { db } from "~/db";
import { sql } from "~/db/sql";
import type { UpsertManyPlusVotesArgs } from "./models/plusVotes/queries.server";
import { ADMIN_DISCORD_ID } from "~/constants";
import shuffle from "just-shuffle";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import capitalize from "just-capitalize";
import allTags from "~/routes/calendar/tags.json";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import type { UpsertManyPlusVotesArgs } from "./models/plusVotes/queries.server";
const ADMIN_TEST_AVATAR = "e424e1ba50d2019fdc4730d261e56c55";
@ -36,6 +44,7 @@ const basicSeeds = [
calendarEvents,
calendarEventBadges,
calendarEventResults,
adminBuilds,
];
export function seed() {
@ -486,3 +495,63 @@ function calendarEventResults() {
userIds = userIdsInRandomOrder();
}
}
function adminBuilds() {
for (let i = 0; i < 50; i++) {
const randomOrderHeadGear = shuffle(headGearIds.slice());
const randomOrderClothesGear = shuffle(clothesGearIds.slice());
const randomOrderShoesGear = shuffle(shoesGearIds.slice());
const randomOrderWeaponIds = shuffle(weaponIds.slice());
db.builds.create({
title: `${capitalize(faker.word.adjective())} ${capitalize(
faker.word.noun()
)}`,
ownerId: 1,
description: Math.random() < 0.75 ? faker.lorem.paragraph() : null,
clothesGearSplId: randomOrderHeadGear[0]!,
headGearSplId: randomOrderClothesGear[0]!,
shoesGearSplId: randomOrderShoesGear[0]!,
weaponSplIds: new Array(
faker.helpers.arrayElement([1, 1, 1, 2, 2, 3, 4, 5, 6])
)
.fill(null)
.map(() => randomOrderWeaponIds.pop()!),
modes:
Math.random() < 0.75
? modesShort.filter(() => Math.random() < 0.5)
: null,
abilities: new Array(12).fill(null).map((_, i) => {
const gearType = i < 4 ? "HEAD" : i < 8 ? "CLOTHES" : "SHOES";
const randomOrderAbilities = shuffle([...abilityCodes]);
const getAbility = () => {
const legalAbilityForSlot = randomOrderAbilities.find((ability) => {
if (ability.type === "HEAD_ONLY" && gearType !== "HEAD") {
return false;
}
if (ability.type === "CLOTHES_ONLY" && gearType !== "CLOTHES") {
return false;
}
if (ability.type === "SHOES_ONLY" && gearType !== "SHOES") {
return false;
}
return true;
});
invariant(legalAbilityForSlot);
return legalAbilityForSlot.name;
};
return {
ability: getAbility(),
gearType,
slotIndex: (i % 4) as any,
};
}),
});
}
}

View File

@ -1,3 +1,4 @@
import type { Ability } from "~/modules/in-game-lists";
import type allTags from "../routes/calendar/tags.json";
export interface User {
@ -117,3 +118,27 @@ export interface CalendarEventBadge {
eventId: number;
badgeId: number;
}
export interface Build {
id: number;
ownerId: number;
title: string;
description: string | null;
modes: string | null;
headGearSplId: number;
clothesGearSplId: number;
shoesGearSplId: number;
updatedAt: number;
}
export interface BuildWeapon {
buildId: number;
weaponSplId: number;
}
export interface BuildAbility {
buildId: number;
gearType: "HEAD" | "CLOTHES" | "SHOES";
ability: Ability;
slotIndex: 0 | 1 | 2 | 3;
}

View File

@ -1,12 +1,4 @@
export type AbilityType =
| "STACKABLE"
| "HEAD_ONLY"
| "CLOTHES_ONLY"
| "SHOES_ONLY";
export const abilityCodes: Readonly<
Array<{ name: string; type: AbilityType }>
> = [
export const abilityCodes = [
{ name: "AD", type: "CLOTHES_ONLY" },
{ name: "BRU", type: "STACKABLE" },
{ name: "CB", type: "HEAD_ONLY" },
@ -34,3 +26,5 @@ export const abilityCodes: Readonly<
{ name: "T", type: "CLOTHES_ONLY" },
{ name: "TI", type: "CLOTHES_ONLY" },
] as const;
export const abilitiesShort = abilityCodes.map((ability) => ability.name);

View File

@ -2,4 +2,5 @@ export { stages } from "./stages";
export { modes, modesShort } from "./modes";
export { weaponIds } from "./weapon-ids";
export { headGearIds, clothesGearIds, shoesGearIds } from "./gear-ids";
export type { ModeShort, Stage } from "./types";
export { abilitiesShort, abilityCodes } from "./abilities";
export type { Ability, AbilityType, ModeShort, Stage } from "./types";

View File

@ -1,6 +1,10 @@
import type { abilityCodes } from "./abilities";
import type { modes } from "./modes";
import type { stages } from "./stages";
export type ModeShort = typeof modes[number]["short"];
export type Stage = typeof stages[number];
export type Ability = typeof abilityCodes[number]["name"];
export type AbilityType = typeof abilityCodes[number]["type"];

48
migrations/007-builds.js Normal file
View File

@ -0,0 +1,48 @@
module.exports.up = function (db) {
db.prepare(
`
create table "Build" (
"id" integer primary key,
"ownerId" integer not null,
"title" text not null,
"description" text,
"modes" text,
"headGearSplId" integer not null,
"clothesGearSplId" integer not null,
"shoesGearSplId" integer not null,
"updatedAt" integer default (strftime('%s', 'now')) not null,
foreign key ("ownerId") references "User"("id") on delete restrict
) strict
`
).run();
db.prepare(
`
create table "BuildWeapon" (
"buildId" integer not null,
"weaponSplId" integer not null,
foreign key ("buildId") references "Build"("id") on delete cascade,
unique("buildId", "weaponSplId") on conflict rollback
) strict
`
).run();
db.prepare(
`
create table "BuildAbility" (
"buildId" integer not null,
"gearType" text not null,
"ability" text not null,
"slotIndex" integer not null,
foreign key ("buildId") references "Build"("id") on delete cascade,
unique("buildId", "gearType", "slotIndex") on conflict rollback
) strict
`
).run();
};
module.exports.down = function (db) {
for (const table of ["Build", "BuildWeapon", "BuildAbility"]) {
db.prepare(`drop table "${table}"`).run();
}
};