334 lines
9.1 KiB
TypeScript
334 lines
9.1 KiB
TypeScript
import { database } from "~/database/context";
|
|
import * as schema from "~/database/schema";
|
|
import { eq, and } from "drizzle-orm";
|
|
|
|
interface ExportData {
|
|
version: string;
|
|
exportedAt: string;
|
|
sports: Array<{
|
|
name: string;
|
|
type: string;
|
|
slug: string;
|
|
description: string | null;
|
|
}>;
|
|
sportsSeasons: Array<{
|
|
sportSlug: string;
|
|
name: string;
|
|
year: number;
|
|
startDate: string | null;
|
|
endDate: string | null;
|
|
status: string;
|
|
scoringType: string;
|
|
}>;
|
|
participants: Array<{
|
|
sportSlug: string;
|
|
seasonName: string;
|
|
seasonYear: number;
|
|
name: string;
|
|
shortName: string | null;
|
|
externalId: string | null;
|
|
}>;
|
|
seasonTemplates: Array<{
|
|
name: string;
|
|
year: number;
|
|
description: string | null;
|
|
isActive: boolean;
|
|
sports: Array<{
|
|
sportSlug: string;
|
|
seasonName: string;
|
|
seasonYear: number;
|
|
isRequired: boolean;
|
|
}>;
|
|
}>;
|
|
}
|
|
|
|
export async function exportSportsDataToJSON(): Promise<ExportData> {
|
|
const db = database();
|
|
|
|
// Export sports
|
|
const sports = await db.query.sports.findMany({
|
|
orderBy: (sports, { asc }) => [asc(sports.name)],
|
|
});
|
|
|
|
// Export sports seasons with sport relation
|
|
const sportsSeasons = await db.query.sportsSeasons.findMany({
|
|
with: {
|
|
sport: true,
|
|
},
|
|
orderBy: (sportsSeasons, { desc, asc }) => [
|
|
desc(sportsSeasons.year),
|
|
asc(sportsSeasons.name),
|
|
],
|
|
});
|
|
|
|
// Export participants with relations
|
|
const participants = await db.query.participants.findMany({
|
|
with: {
|
|
sportsSeason: {
|
|
with: {
|
|
sport: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: (participants, { asc }) => [asc(participants.name)],
|
|
});
|
|
|
|
// Export season templates with sports
|
|
const seasonTemplates = await db.query.seasonTemplates.findMany({
|
|
with: {
|
|
seasonTemplateSports: {
|
|
with: {
|
|
sportsSeason: {
|
|
with: {
|
|
sport: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
orderBy: (templates, { desc }) => [desc(templates.year)],
|
|
});
|
|
|
|
// Build export data structure
|
|
return {
|
|
version: "1.0",
|
|
exportedAt: new Date().toISOString(),
|
|
sports: sports.map((sport) => ({
|
|
name: sport.name,
|
|
type: sport.type,
|
|
slug: sport.slug,
|
|
description: sport.description,
|
|
})),
|
|
sportsSeasons: sportsSeasons.map((season) => ({
|
|
sportSlug: season.sport.slug,
|
|
name: season.name,
|
|
year: season.year,
|
|
startDate: season.startDate,
|
|
endDate: season.endDate,
|
|
status: season.status,
|
|
scoringType: season.scoringType,
|
|
})),
|
|
participants: participants.map((participant) => ({
|
|
sportSlug: participant.sportsSeason.sport.slug,
|
|
seasonName: participant.sportsSeason.name,
|
|
seasonYear: participant.sportsSeason.year,
|
|
name: participant.name,
|
|
shortName: participant.shortName,
|
|
externalId: participant.externalId,
|
|
})),
|
|
seasonTemplates: seasonTemplates.map((template) => ({
|
|
name: template.name,
|
|
year: template.year,
|
|
description: template.description,
|
|
isActive: template.isActive,
|
|
sports: template.seasonTemplateSports.map((ts) => ({
|
|
sportSlug: ts.sportsSeason.sport.slug,
|
|
seasonName: ts.sportsSeason.name,
|
|
seasonYear: ts.sportsSeason.year,
|
|
isRequired: ts.isRequired,
|
|
})),
|
|
})),
|
|
};
|
|
}
|
|
|
|
export async function importSportsDataFromJSON(
|
|
jsonData: string,
|
|
mode: "merge" | "replace" = "merge"
|
|
): Promise<{ created: number; updated: number; skipped: number }> {
|
|
const db = database();
|
|
const importData: ExportData = JSON.parse(jsonData);
|
|
|
|
let created = 0;
|
|
let updated = 0;
|
|
let skipped = 0;
|
|
|
|
if (mode === "replace") {
|
|
// Delete in reverse order of dependencies
|
|
await db.delete(schema.seasonTemplateSports);
|
|
await db.delete(schema.seasonTemplates);
|
|
await db.delete(schema.participantResults);
|
|
await db.delete(schema.participants);
|
|
await db.delete(schema.seasonSports);
|
|
await db.delete(schema.sportsSeasons);
|
|
await db.delete(schema.sports);
|
|
}
|
|
|
|
// Track created IDs for relationships
|
|
const sportIdMap = new Map<string, string>();
|
|
const sportsSeasonIdMap = new Map<string, string>();
|
|
|
|
// Import sports
|
|
for (const sport of importData.sports) {
|
|
const existing = await db.query.sports.findFirst({
|
|
where: eq(schema.sports.slug, sport.slug),
|
|
});
|
|
|
|
if (existing) {
|
|
if (mode === "merge") {
|
|
await db
|
|
.update(schema.sports)
|
|
.set({
|
|
name: sport.name,
|
|
type: sport.type as "team" | "individual",
|
|
description: sport.description,
|
|
})
|
|
.where(eq(schema.sports.slug, sport.slug));
|
|
sportIdMap.set(sport.slug, existing.id);
|
|
updated++;
|
|
}
|
|
} else {
|
|
const [createdSport] = await db
|
|
.insert(schema.sports)
|
|
.values({
|
|
name: sport.name,
|
|
type: sport.type as "team" | "individual",
|
|
slug: sport.slug,
|
|
description: sport.description,
|
|
})
|
|
.returning();
|
|
sportIdMap.set(sport.slug, createdSport.id);
|
|
created++;
|
|
}
|
|
}
|
|
|
|
// Import sports seasons
|
|
for (const season of importData.sportsSeasons) {
|
|
const sportId = sportIdMap.get(season.sportSlug);
|
|
if (!sportId) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
const existing = await db.query.sportsSeasons.findFirst({
|
|
where: and(
|
|
eq(schema.sportsSeasons.sportId, sportId),
|
|
eq(schema.sportsSeasons.name, season.name),
|
|
eq(schema.sportsSeasons.year, season.year)
|
|
),
|
|
});
|
|
|
|
const seasonKey = `${season.sportSlug}:${season.name}:${season.year}`;
|
|
|
|
if (existing) {
|
|
if (mode === "merge") {
|
|
await db
|
|
.update(schema.sportsSeasons)
|
|
.set({
|
|
startDate: season.startDate,
|
|
endDate: season.endDate,
|
|
status: season.status as "upcoming" | "active" | "completed",
|
|
scoringType: season.scoringType as "playoffs" | "regular_season" | "majors",
|
|
})
|
|
.where(eq(schema.sportsSeasons.id, existing.id));
|
|
sportsSeasonIdMap.set(seasonKey, existing.id);
|
|
updated++;
|
|
}
|
|
} else {
|
|
const [createdSeason] = await db
|
|
.insert(schema.sportsSeasons)
|
|
.values({
|
|
sportId,
|
|
name: season.name,
|
|
year: season.year,
|
|
startDate: season.startDate,
|
|
endDate: season.endDate,
|
|
status: season.status as "upcoming" | "active" | "completed",
|
|
scoringType: season.scoringType as "playoffs" | "regular_season" | "majors",
|
|
})
|
|
.returning();
|
|
sportsSeasonIdMap.set(seasonKey, createdSeason.id);
|
|
created++;
|
|
}
|
|
}
|
|
|
|
// Import participants
|
|
for (const participant of importData.participants) {
|
|
const seasonKey = `${participant.sportSlug}:${participant.seasonName}:${participant.seasonYear}`;
|
|
const sportsSeasonId = sportsSeasonIdMap.get(seasonKey);
|
|
|
|
if (!sportsSeasonId) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
const existing = await db.query.participants.findFirst({
|
|
where: and(
|
|
eq(schema.participants.sportsSeasonId, sportsSeasonId),
|
|
eq(schema.participants.name, participant.name)
|
|
),
|
|
});
|
|
|
|
if (!existing) {
|
|
await db.insert(schema.participants).values({
|
|
sportsSeasonId,
|
|
name: participant.name,
|
|
shortName: participant.shortName,
|
|
externalId: participant.externalId,
|
|
});
|
|
created++;
|
|
} else {
|
|
skipped++;
|
|
}
|
|
}
|
|
|
|
// Import season templates
|
|
for (const template of importData.seasonTemplates) {
|
|
const existing = await db.query.seasonTemplates.findFirst({
|
|
where: and(
|
|
eq(schema.seasonTemplates.name, template.name),
|
|
eq(schema.seasonTemplates.year, template.year)
|
|
),
|
|
});
|
|
|
|
let templateId: string;
|
|
|
|
if (existing) {
|
|
if (mode === "merge") {
|
|
await db
|
|
.update(schema.seasonTemplates)
|
|
.set({
|
|
description: template.description,
|
|
isActive: template.isActive,
|
|
})
|
|
.where(eq(schema.seasonTemplates.id, existing.id));
|
|
templateId = existing.id;
|
|
updated++;
|
|
|
|
// Remove existing template sports to re-add them
|
|
await db
|
|
.delete(schema.seasonTemplateSports)
|
|
.where(eq(schema.seasonTemplateSports.templateId, templateId));
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
const [createdTemplate] = await db
|
|
.insert(schema.seasonTemplates)
|
|
.values({
|
|
name: template.name,
|
|
year: template.year,
|
|
description: template.description,
|
|
isActive: template.isActive,
|
|
})
|
|
.returning();
|
|
templateId = createdTemplate.id;
|
|
created++;
|
|
}
|
|
|
|
// Add template sports
|
|
for (const templateSport of template.sports) {
|
|
const seasonKey = `${templateSport.sportSlug}:${templateSport.seasonName}:${templateSport.seasonYear}`;
|
|
const sportsSeasonId = sportsSeasonIdMap.get(seasonKey);
|
|
|
|
if (sportsSeasonId) {
|
|
await db.insert(schema.seasonTemplateSports).values({
|
|
templateId,
|
|
sportsSeasonId,
|
|
isRequired: templateSport.isRequired,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
return { created, updated, skipped };
|
|
}
|