diff --git a/scripts/scrape-onepiece.ts b/scripts/scrape-onepiece.ts index 60d90a6..278f879 100644 --- a/scripts/scrape-onepiece.ts +++ b/scripts/scrape-onepiece.ts @@ -659,9 +659,9 @@ function extractOrigin($: cheerio.CheerioAPI): string | null { /** * Extract status from infobox */ -function extractStatus($: cheerio.CheerioAPI): string { +function extractStatus($: cheerio.CheerioAPI): string | null { const div = $('[data-source="statut"] .pi-data-value'); - if (div.length === 0) return 'Alive'; + if (div.length === 0) return null; const statusText = div.text().trim().toLowerCase(); @@ -669,6 +669,8 @@ function extractStatus($: cheerio.CheerioAPI): string { return 'Alive'; } else if (statusText.includes('décédé')) { return 'Dead'; + } else if (statusText.includes('inconnu')) { + return 'Unknown'; } return 'Alive'; diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index ffb34f8..49b755f 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -4,6 +4,8 @@ import { user } from './auth.schema'; // Define devil fruit types export type DevilFruitType = 'Paramecia' | 'Zoan' | 'Logia' | 'Smile' | 'Unknown'; +export type Status = 'Alive' | 'Dead' | 'Unknown'; + // Define the site config table schema export const config = sqliteTable('config', { key: text('key').primaryKey(), @@ -44,7 +46,7 @@ export const character = sqliteTable('character', { firstAppearance: integer('firstAppearance').notNull(), pictureUrl: text('pictureUrl'), epithets: text('epithets', { mode: 'json' }).$type(), - status: text('status'), + status: text('status').$type(), arcId: text('arcId').references(() => arc.id), url: text('url'), isInDailyMode: integer('isInDailyMode', { mode: 'boolean' }).default(true) @@ -67,7 +69,7 @@ export const characterOverride = sqliteTable('characterOverride', { firstAppearance: integer('firstAppearance'), pictureUrl: text('pictureUrl'), epithets: text('epithets', { mode: 'json' }).$type(), - status: text('status'), + status: text('status').$type(), arcId: text('arcId').references(() => arc.id), url: text('url'), notes: text('notes') @@ -90,7 +92,7 @@ export const characterScrapeValidation = sqliteTable('characterScrapeValidation' firstAppearance: integer('firstAppearance').notNull(), pictureUrl: text('pictureUrl'), epithets: text('epithets', { mode: 'json' }).$type(), - status: text('status'), + status: text('status').$type(), arcId: text('arcId').references(() => arc.id), url: text('url') }); diff --git a/src/routes/(admin)/admin/character-changes/+page.server.ts b/src/routes/(admin)/admin/character-changes/+page.server.ts index 7d12078..f3e246b 100644 --- a/src/routes/(admin)/admin/character-changes/+page.server.ts +++ b/src/routes/(admin)/admin/character-changes/+page.server.ts @@ -1,5 +1,64 @@ import { db } from '$lib/server/db'; import { character, characterScrapeValidation } from '$lib/server/db/schema'; +import { eq } from 'drizzle-orm'; + +async function upsertCharacterFromScrapeValidation(characterId: string): Promise { + const [scraped] = await db + .select() + .from(characterScrapeValidation) + .where(eq(characterScrapeValidation.id, characterId)); + + if (!scraped) { + return false; + } + + await db + .insert(character) + .values({ + id: scraped.id, + name: scraped.name, + gender: scraped.gender, + age: scraped.age, + affiliations: scraped.affiliations, + devilFruitId: scraped.devilFruitId, + hakiObservation: scraped.hakiObservation, + hakiArmament: scraped.hakiArmament, + hakiConqueror: scraped.hakiConqueror, + bounty: scraped.bounty, + height: scraped.height, + origin: scraped.origin, + firstAppearance: scraped.firstAppearance, + pictureUrl: scraped.pictureUrl, + epithets: scraped.epithets, + status: scraped.status, + arcId: scraped.arcId, + url: scraped.url + }) + .onConflictDoUpdate({ + target: character.id, + set: { + name: scraped.name, + gender: scraped.gender, + age: scraped.age, + affiliations: scraped.affiliations, + devilFruitId: scraped.devilFruitId, + hakiObservation: scraped.hakiObservation, + hakiArmament: scraped.hakiArmament, + hakiConqueror: scraped.hakiConqueror, + bounty: scraped.bounty, + height: scraped.height, + origin: scraped.origin, + firstAppearance: scraped.firstAppearance, + pictureUrl: scraped.pictureUrl, + epithets: scraped.epithets, + status: scraped.status, + arcId: scraped.arcId, + url: scraped.url + } + }); + + return true; +} export async function load() { // Get all characters from both tables @@ -86,3 +145,37 @@ export async function load() { }) }; } + +export const actions = { + acceptOne: async ({ request }) => { + const formData = await request.formData(); + const characterId = formData.get('characterId'); + + if (!characterId || typeof characterId !== 'string') { + return { success: false, message: 'characterId is required' }; + } + + const applied = await upsertCharacterFromScrapeValidation(characterId); + return { + success: applied, + message: applied ? 'Character applied successfully' : 'Character not found in scrape validation table' + }; + }, + + acceptAll: async () => { + const scrapedCharacters = await db.select().from(characterScrapeValidation); + let appliedCount = 0; + + for (const scraped of scrapedCharacters) { + const applied = await upsertCharacterFromScrapeValidation(scraped.id); + if (applied) { + appliedCount++; + } + } + + return { + success: true, + appliedCount + }; + } +}; diff --git a/src/routes/(admin)/admin/character-changes/+page.svelte b/src/routes/(admin)/admin/character-changes/+page.svelte index a70e4dc..e877322 100644 --- a/src/routes/(admin)/admin/character-changes/+page.svelte +++ b/src/routes/(admin)/admin/character-changes/+page.svelte @@ -6,6 +6,11 @@ const newCharacters = $derived(data.changes.filter((c: any) => c.type === 'new')); const modifiedCharacters = $derived(data.changes.filter((c: any) => c.type === 'modified')); + function fandomUrl(path: string | null | undefined): string { + if (!path) return 'https://onepiece.fandom.com/fr/wiki'; + return `https://onepiece.fandom.com/fr/wiki/${path}`; + } + function formatValue(value: any): string { if (value === null || value === undefined) { return '—'; @@ -35,6 +40,16 @@

Character Changes

Total changes: {newCharacters.length} new, {modifiedCharacters.length} modified

+ {#if newCharacters.length + modifiedCharacters.length > 0} +
+ +
+ {/if}
@@ -46,18 +61,31 @@
{#each newCharacters as change (change.id)}
-
- {#if change.scraped.pictureUrl} - {change.scraped.name} - {/if} -
-

{change.scraped.name}

-

{change.id}

+
+
+ {#if change.scraped.pictureUrl} + + {change.scraped.name} + + {/if} +
+

{change.scraped.name}

+

{change.id}

+
+
+ + +
@@ -92,18 +120,31 @@
{#each modifiedCharacters as change (change.id)}
-
- {#if change.current?.pictureUrl} - {change.current.name} - {/if} -
-

{change.current?.name ?? change.scraped.name}

-

{change.id}

+
+
+ {#if change.current?.pictureUrl} + + {change.current.name} + + {/if} +
+

{change.current?.name ?? change.scraped.name}

+

{change.id}

+
+
+ + +
{#if change.differences}