mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-02-04 10:54:44 -05:00
updated ranking formula
This commit is contained in:
21
.env.example
Normal file
21
.env.example
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
DATABASE_URL=example.db
|
||||||
|
|
||||||
|
# nodemailer
|
||||||
|
SMTP_USERNAME=email@example.com
|
||||||
|
SMTP_TOKEN=TOKEN
|
||||||
|
SMTP_SERVER=smtp.example.com
|
||||||
|
SMTP_PORT=port
|
||||||
|
# note from mail provider: Enable TLS or SSL on the external service if it is supported.
|
||||||
|
|
||||||
|
# sign in with Discord
|
||||||
|
|
||||||
|
# sign in with google
|
||||||
|
|
||||||
|
# sign in with apple
|
||||||
|
AUTH_SECRET=your-random-secret-here
|
||||||
|
APPLE_ID=com.yourcompany.yourapp.client
|
||||||
|
APPLE_TEAM_ID=your-team-id
|
||||||
|
APPLE_KEY_ID=your-key-id
|
||||||
|
APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----
|
||||||
|
your-private-key-here
|
||||||
|
-----END PRIVATE KEY-----"
|
||||||
31
analyze_top_users.sh
Executable file
31
analyze_top_users.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# analyze_top_users.sh
|
||||||
|
# Analyzes the daily_completions table to find the top 10 anonymous IDs by completion count
|
||||||
|
|
||||||
|
# Set database path from argument or default to dev.db
|
||||||
|
DB_PATH="${1:-dev.db}"
|
||||||
|
|
||||||
|
# Check if database file exists
|
||||||
|
if [ ! -f "$DB_PATH" ]; then
|
||||||
|
echo "Error: Database file not found: $DB_PATH"
|
||||||
|
echo "Usage: $0 [database_path]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the analysis query
|
||||||
|
sqlite3 "$DB_PATH" <<EOF
|
||||||
|
.mode column
|
||||||
|
.headers on
|
||||||
|
.width 36 16 16 17
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
anonymous_id,
|
||||||
|
COUNT(*) as completion_count,
|
||||||
|
MIN(date) as first_completion,
|
||||||
|
MAX(date) as latest_completion
|
||||||
|
FROM daily_completions
|
||||||
|
GROUP BY anonymous_id
|
||||||
|
ORDER BY completion_count DESC
|
||||||
|
LIMIT 10;
|
||||||
|
EOF
|
||||||
34
daily_completions_report.sh
Executable file
34
daily_completions_report.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
DB_PATH="./local.db"
|
||||||
|
|
||||||
|
# Check if database exists
|
||||||
|
if [ ! -f "$DB_PATH" ]; then
|
||||||
|
echo "Error: Database not found at $DB_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Query for daily completions on 2026-02-01 with ranking
|
||||||
|
echo "Daily Completions for 2026-02-01"
|
||||||
|
echo "================================="
|
||||||
|
echo ""
|
||||||
|
printf "%-12s %-10s %-6s\n" "Anonymous ID" "Guesses" "Rank"
|
||||||
|
printf "%-12s %-10s %-6s\n" "------------" "-------" "----"
|
||||||
|
|
||||||
|
# Execute query with custom column mode
|
||||||
|
sqlite3 "$DB_PATH" <<SQL
|
||||||
|
.mode column
|
||||||
|
.headers off
|
||||||
|
.width 12 10 6
|
||||||
|
SELECT
|
||||||
|
SUBSTR(anonymous_id, 1, 10) as anon_id,
|
||||||
|
guess_count,
|
||||||
|
RANK() OVER (ORDER BY guess_count ASC) as rank
|
||||||
|
FROM daily_completions
|
||||||
|
WHERE date = '2026-02-01'
|
||||||
|
ORDER BY rank, guess_count;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Total entries:"
|
||||||
|
sqlite3 "$DB_PATH" "SELECT COUNT(*) FROM daily_completions WHERE date = '2026-02-01';"
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { integer, sqliteTable, text, index, unique } from 'drizzle-orm/sqlite-core';
|
import { integer, sqliteTable, text, index, unique } from 'drizzle-orm/sqlite-core';
|
||||||
|
|
||||||
import { sql } from 'drizzle-orm';
|
|
||||||
|
|
||||||
export const user = sqliteTable('user', { id: text('id').primaryKey(), age: integer('age') });
|
export const user = sqliteTable('user', { id: text('id').primaryKey(), age: integer('age') });
|
||||||
|
|
||||||
export const session = sqliteTable('session', {
|
export const session = sqliteTable('session', {
|
||||||
|
|||||||
@@ -91,13 +91,20 @@ export const actions: Actions = {
|
|||||||
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
|
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
|
||||||
const guessRank = betterGuesses + 1;
|
const guessRank = betterGuesses + 1;
|
||||||
|
|
||||||
|
// Count ties: how many have the SAME guessCount (excluding self)
|
||||||
|
const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length;
|
||||||
|
|
||||||
// Average guesses
|
// Average guesses
|
||||||
const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0);
|
const totalGuesses = allCompletions.reduce((sum, c) => sum + c.guessCount, 0);
|
||||||
const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10;
|
const averageGuesses = Math.round((totalGuesses / totalSolves) * 10) / 10;
|
||||||
|
|
||||||
|
// Percentile: what percentage of people you beat (100 - your rank percentage)
|
||||||
|
const betterOrEqualCount = allCompletions.filter(c => c.guessCount <= guessCount).length;
|
||||||
|
const percentile = Math.round((betterOrEqualCount / totalSolves) * 100);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
stats: { solveRank, guessRank, totalSolves, averageGuesses }
|
stats: { solveRank, guessRank, totalSolves, averageGuesses, tiedCount, percentile }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,11 +44,9 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
// Solve rank: position in time-ordered list
|
// Solve rank: position in time-ordered list
|
||||||
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
|
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
|
||||||
|
|
||||||
// Guess rank: count how many DISTINCT guess counts are better (grouped ranking)
|
// Guess rank: count how many had FEWER guesses (ties get same rank)
|
||||||
const uniqueBetterGuessCounts = new Set(
|
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
|
||||||
allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount)
|
const guessRank = betterGuesses + 1;
|
||||||
);
|
|
||||||
const guessRank = uniqueBetterGuessCounts.size + 1;
|
|
||||||
|
|
||||||
// Count ties: how many have the SAME guessCount (excluding self)
|
// Count ties: how many have the SAME guessCount (excluding self)
|
||||||
const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length;
|
const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length;
|
||||||
@@ -110,11 +108,9 @@ export const GET: RequestHandler = async ({ url }) => {
|
|||||||
// Solve rank: position in time-ordered list
|
// Solve rank: position in time-ordered list
|
||||||
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
|
const solveRank = allCompletions.findIndex(c => c.anonymousId === anonymousId) + 1;
|
||||||
|
|
||||||
// Guess rank: count how many DISTINCT guess counts are better (grouped ranking)
|
// Guess rank: count how many had FEWER guesses (ties get same rank)
|
||||||
const uniqueBetterGuessCounts = new Set(
|
const betterGuesses = allCompletions.filter(c => c.guessCount < guessCount).length;
|
||||||
allCompletions.filter(c => c.guessCount < guessCount).map(c => c.guessCount)
|
const guessRank = betterGuesses + 1;
|
||||||
);
|
|
||||||
const guessRank = uniqueBetterGuessCounts.size + 1;
|
|
||||||
|
|
||||||
// Count ties: how many have the SAME guessCount (excluding self)
|
// Count ties: how many have the SAME guessCount (excluding self)
|
||||||
const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length;
|
const tiedCount = allCompletions.filter(c => c.guessCount === guessCount && c.anonymousId !== anonymousId).length;
|
||||||
|
|||||||
Reference in New Issue
Block a user