This commit is contained in:
George Powell
2025-12-16 20:44:52 -05:00
parent 427d1dc918
commit 32a078dd98
10 changed files with 634 additions and 277 deletions

View File

@@ -0,0 +1,120 @@
import type { RequestHandler } from './$types';
import { db } from '$lib/server/db';
import { dailyCompletions } from '$lib/server/db/schema';
import { and, eq, asc } from 'drizzle-orm';
import { json } from '@sveltejs/kit';
import crypto from 'node:crypto';
export const POST: RequestHandler = async ({ request }) => {
try {
const { anonymousId, date, guessCount } = await request.json();
// Validation
if (!anonymousId || !date || typeof guessCount !== 'number' || guessCount < 1) {
return json({ error: 'Invalid data' }, { status: 400 });
}
const completedAt = new Date();
try {
// Insert with duplicate prevention
await db.insert(dailyCompletions).values({
id: crypto.randomUUID(),
anonymousId,
date,
guessCount,
completedAt,
});
} catch (err: any) {
if (err?.code === 'SQLITE_CONSTRAINT_UNIQUE' || err?.message?.includes('UNIQUE')) {
return json({ error: 'Already submitted' }, { status: 409 });
}
throw err;
}
// Calculate statistics
const allCompletions = await db
.select()
.from(dailyCompletions)
.where(eq(dailyCompletions.date, date))
.orderBy(asc(dailyCompletions.completedAt));
const totalSolves = allCompletions.length;
// Solve rank: position in time-ordered list
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
// Guess rank: count how many had FEWER guesses (ties get same rank)
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
const guessRank = betterGuesses + 1;
// Average guesses
const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0);
const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10;
return json({
success: true,
stats: { solveRank, guessRank, totalSolves, averageGuesses }
});
} catch (err) {
console.error('Error submitting completion:', err);
return json({ error: 'Failed to submit completion' }, { status: 500 });
}
};
export const GET: RequestHandler = async ({ url }) => {
try {
const anonymousId = url.searchParams.get('anonymousId');
const date = url.searchParams.get('date');
if (!anonymousId || !date) {
return json({ error: 'Invalid data' }, { status: 400 });
}
const userCompletions = await db
.select()
.from(dailyCompletions)
.where(and(
eq(dailyCompletions.anonymousId, anonymousId),
eq(dailyCompletions.date, date)
))
.limit(1);
if (userCompletions.length === 0) {
return json({ error: 'No completion found' }, { status: 404 });
}
const userCompletion = userCompletions[0];
const guessCount = userCompletion.guessCount;
// Calculate statistics
const allCompletions = await db
.select()
.from(dailyCompletions)
.where(eq(dailyCompletions.date, date))
.orderBy(asc(dailyCompletions.completedAt));
const totalSolves = allCompletions.length;
// Solve rank: position in time-ordered list
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
// Guess rank: count how many had FEWER guesses (ties get same rank)
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
const guessRank = betterGuesses + 1;
// Average guesses
const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0);
const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10;
return json({
success: true,
stats: { solveRank, guessRank, totalSolves, averageGuesses }
});
} catch (err) {
console.error('Error fetching stats:', err);
return json({ error: 'Failed to fetch stats' }, { status: 500 });
}
};