JustinTX's picture
Add files using upload-large-folder tool
2facf1f verified
Raw
History Blame Contribute Delete
40.3 kB
import dataclasses
import datetime
import json
from typing import Dict, List, Optional
import psycopg2
from libkernelbot.db_types import (
IdentityType,
LeaderboardItem,
LeaderboardRankedEntry,
RunItem,
SubmissionItem,
)
from libkernelbot.run_eval import CompileResult, RunResult, SystemInfo
from libkernelbot.task import LeaderboardDefinition, LeaderboardTask
from libkernelbot.utils import (
KernelBotError,
LRUCache,
setup_logging,
)
logger = setup_logging(__name__)
class LeaderboardDB:
def __init__(self, url: str, ssl_mode: str):
"""Initialize database connection parameters"""
self.url = url
self.ssl_mode = ssl_mode
self.connection: Optional[psycopg2.extensions.connection] = None
self.refcount: int = 0
self.cursor: Optional[psycopg2.extensions.cursor] = None
self.name_cache = LRUCache(max_size=512)
def connect(self) -> bool:
"""Establish connection to the database"""
try:
self.connection = psycopg2.connect(self.url, sslmode=self.ssl_mode)
self.cursor = self.connection.cursor()
return True
except psycopg2.Error as e:
logger.exception("Error connecting to PostgreSQL", exc_info=e)
return False
def disconnect(self):
"""Close database connection and cursor"""
if self.cursor:
self.cursor.close()
if self.connection:
self.connection.close()
self.cursor = None
self.connection = None
def __enter__(self) -> "LeaderboardDB":
"""Context manager entry"""
if self.connection is not None:
self.refcount += 1
return self
if self.connect():
self.refcount = 1
return self
raise KernelBotError("Could not connect to database", code=500)
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit"""
self.refcount -= 1
if self.refcount == 0:
self.disconnect()
def create_leaderboard(
self,
*,
name: str,
deadline: datetime.datetime,
definition: LeaderboardDefinition,
creator_id: int,
forum_id: int,
gpu_types: list | str,
) -> int:
# to prevent surprises, ensure we have specified a timezone
try:
task = definition.task
self.cursor.execute(
"""
INSERT INTO leaderboard.leaderboard (name, deadline, task, creator_id,
forum_id, description)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id
""",
(name, deadline, task.to_str(), creator_id, forum_id, definition.description),
)
leaderboard_id = self.cursor.fetchone()[0]
if isinstance(gpu_types, str):
gpu_types = [gpu_types]
for gpu_type in gpu_types:
self.cursor.execute(
"""
INSERT INTO leaderboard.gpu_type (leaderboard_id, gpu_type)
VALUES (%s, %s)
""",
(leaderboard_id, gpu_type),
)
# insert templates
for lang, code in definition.templates.items():
self.cursor.execute(
"""
INSERT INTO leaderboard.templates (leaderboard_id, lang, code)
VALUES (%s, %s, %s)
""",
(leaderboard_id, lang, code),
)
self.connection.commit()
self.name_cache.invalidate() # Invalidate autocomplete cache
return leaderboard_id
except psycopg2.Error as e:
logger.exception("Error in leaderboard creation.", exc_info=e)
if isinstance(e, psycopg2.errors.UniqueViolation):
raise KernelBotError(
f"Error: Tried to create a leaderboard '{name}' that already exists."
) from e
self.connection.rollback() # Ensure rollback if error occurs
raise KernelBotError("Error in leaderboard creation.") from e
def update_leaderboard(
self, name, deadline: datetime.datetime, definition: LeaderboardDefinition
):
task = definition.task
try:
lb_id = self.get_leaderboard_id(name)
self.cursor.execute(
"""
UPDATE leaderboard.leaderboard
SET deadline = %s, task = %s, description = %s
WHERE id = %s;
""",
(
deadline.astimezone(datetime.timezone.utc),
task.to_str(),
definition.description,
lb_id,
),
)
# replace templates
self.cursor.execute(
"""
DELETE FROM leaderboard.templates
WHERE leaderboard_id = %s
""",
(lb_id,),
)
for lang, code in definition.templates.items():
self.cursor.execute(
"""
INSERT INTO leaderboard.templates (leaderboard_id, lang, code)
VALUES (%s, %s, %s)
""",
(lb_id, lang, code),
)
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Error during leaderboard update", exc_info=e)
raise KernelBotError("Error during leaderboard update") from e
def delete_leaderboard(self, leaderboard_name: str, force: bool = False):
try:
if force:
self.cursor.execute(
"""
DELETE FROM leaderboard.runs
WHERE submission_id IN (
SELECT leaderboard.submission.id
FROM leaderboard.submission
WHERE leaderboard.submission.leaderboard_id IN (
SELECT leaderboard.leaderboard.id FROM leaderboard.leaderboard
WHERE leaderboard.leaderboard.name = %s
)
);
""",
(leaderboard_name,),
)
self.cursor.execute(
"""
DELETE FROM leaderboard.submission
USING leaderboard.leaderboard
WHERE leaderboard.submission.leaderboard_id = leaderboard.leaderboard.id
AND leaderboard.leaderboard.name = %s;
""",
(leaderboard_name,),
)
self.cursor.execute(
"""
DELETE FROM leaderboard.templates
USING leaderboard.leaderboard
WHERE leaderboard.templates.leaderboard_id = leaderboard.leaderboard.id
AND leaderboard.leaderboard.name = %s;
""",
(leaderboard_name,),
)
self.cursor.execute(
"""
DELETE FROM leaderboard.leaderboard WHERE name = %s
""",
(leaderboard_name,),
)
self.connection.commit()
self.name_cache.invalidate() # Invalidate autocomplete cache
except psycopg2.Error as e:
self.connection.rollback()
if isinstance(e, psycopg2.errors.ForeignKeyViolation):
raise KernelBotError(
f"Could not delete leaderboard `{leaderboard_name}` with existing submissions."
) from e
logger.exception("Could not delete leaderboard %s.", leaderboard_name, exc_info=e)
raise KernelBotError(f"Could not delete leaderboard `{leaderboard_name}`.") from e
def validate_identity(
self,
identifier: str,
id_type: IdentityType,
) -> Optional[dict[str, str]]:
"""
Validate an identity (CLI or Web) and return {user_id, user_name} if found.
Args:
identifier: The identifier value (CLI ID or Web Auth ID).
id_type: IdentityType enum (IdentityType.CLI or IdentityType.WEB).
Returns:
Optional[dict[str, str]]: {"user_id": ..., "user_name": ...} if valid; else None.
"""
where_by_type = {
IdentityType.CLI: ("cli_id = %s AND cli_valid = TRUE", "CLI ID"),
IdentityType.WEB: ("web_auth_id = %s", "WEB AUTH ID"),
}
where_clause, human_label = where_by_type[id_type]
try:
self.cursor.execute(
f"""
SELECT id, user_name
FROM leaderboard.user_info
WHERE {where_clause}
""",
(identifier,),
)
row = self.cursor.fetchone()
return (
{"user_id": row[0], "user_name": row[1], "id_type": id_type.value} if row else None
)
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Error validating %s %s", human_label, identifier, exc_info=e)
raise KernelBotError(f"Error validating {human_label}") from e
def create_submission(
self,
leaderboard: str,
file_name: str,
user_id: int,
code: str,
time: datetime.datetime,
user_name: str = None,
) -> Optional[int]:
try:
# check if we already have the code
self.cursor.execute(
"""
SELECT id, code
FROM leaderboard.code_files
WHERE hash = encode(sha256(%s), 'hex')
""",
(code.encode("utf-8"),),
)
code_id = None
for candidate in self.cursor.fetchall():
if bytes(candidate[1]).decode("utf-8") == code:
code_id = candidate[0]
break
if code_id is None:
# a genuinely new submission
self.cursor.execute(
"""
INSERT INTO leaderboard.code_files (CODE)
VALUES (%s)
RETURNING id
""",
(code.encode("utf-8"),),
)
code_id = self.cursor.fetchone()
# Check if user exists in user_info, if not add them
self.cursor.execute(
"""
SELECT 1 FROM leaderboard.user_info WHERE id = %s
""",
(str(user_id),),
)
if not self.cursor.fetchone():
self.cursor.execute(
"""
INSERT INTO leaderboard.user_info (id, user_name)
VALUES (%s, %s)
""",
(str(user_id), user_name),
)
self.cursor.execute(
"""
INSERT INTO leaderboard.submission (leaderboard_id, file_name,
user_id, code_id, submission_time)
VALUES (
(SELECT id FROM leaderboard.leaderboard WHERE name = %s),
%s, %s, %s, %s)
RETURNING id
""",
(
leaderboard,
file_name,
user_id,
code_id,
time,
),
)
submission_id = self.cursor.fetchone()[0]
assert submission_id is not None
self.connection.commit()
return submission_id
except psycopg2.Error as e:
logger.error(
"Error during creation of submission for leaderboard '%s' by user '%s'",
leaderboard,
user_id,
exc_info=e,
)
self.connection.rollback() # Ensure rollback if error occurs
raise KernelBotError("Error during creation of submission") from e
def mark_submission_done(
self,
submission: int,
) -> Optional[int]:
try:
self.cursor.execute(
"""
UPDATE leaderboard.submission
SET done = TRUE
WHERE id = %s
""",
(submission,),
)
self.connection.commit()
except psycopg2.Error as e:
logger.error("Could not mark submission '%s' as done.", submission, exc_info=e)
self.connection.rollback() # Ensure rollback if error occurs
raise KernelBotError("Error while finalizing submission") from e
def update_heartbeat_if_active(self, sub_id: int, ts: datetime.datetime) -> None:
try:
self.cursor.execute(
"""
UPDATE leaderboard.submission_job_status
SET last_heartbeat = %s
WHERE submission_id = %s
AND status IN ('pending','running')
""",
(ts, sub_id),
)
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.error("Failed to upsert submission job status. sub_id: '%s'", sub_id, exc_info=e)
raise KernelBotError("Error updating job status") from e
def upsert_submission_job_status(
self,
sub_id: int,
status: str | None = None,
error: str | None = None,
last_heartbeat: datetime.datetime | None = None,
) -> int:
try:
self.cursor.execute(
"""
INSERT INTO leaderboard.submission_job_status AS s
(submission_id, status, error, last_heartbeat)
VALUES
(%s, %s, %s, %s)
ON CONFLICT (submission_id) DO UPDATE
SET
status = COALESCE(EXCLUDED.status, s.status),
error = COALESCE(EXCLUDED.error, s.error),
last_heartbeat = COALESCE(EXCLUDED.last_heartbeat, s.last_heartbeat)
RETURNING id;
""",
(sub_id, status, error, last_heartbeat),
)
job_id = self.cursor.fetchone()[0]
self.connection.commit()
return int(job_id)
except psycopg2.Error as e:
self.connection.rollback()
logger.error("Failed to upsert submission job status. sub_id: '%s'", sub_id, exc_info=e)
raise KernelBotError("Error updating job status") from e
def create_submission_run(
self,
submission: int,
start: datetime.datetime,
end: datetime.datetime,
mode: str,
secret: bool,
runner: str,
score: Optional[float],
compilation: Optional[CompileResult],
result: RunResult,
system: SystemInfo,
):
try:
if compilation is not None:
compilation = json.dumps(dataclasses.asdict(compilation))
# check validity
self.cursor.execute(
"""
SELECT done FROM leaderboard.submission WHERE id = %s
""",
(submission,),
)
if self.cursor.fetchone()[0]:
logger.error(
"Submission '%s' is already marked as done when trying to add %s run.",
submission,
mode,
)
raise KernelBotError(
"Internal error: Attempted to add run, "
"but submission was already marked as done."
)
meta = {
k: result.__dict__[k]
for k in ["stdout", "stderr", "success", "exit_code", "command", "duration"]
}
self.cursor.execute(
"""
INSERT INTO leaderboard.runs (submission_id, start_time, end_time, mode,
secret, runner, score, passed, compilation, meta, result, system_info
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""",
(
submission,
start,
end,
mode,
secret,
runner,
score,
result.passed,
compilation,
json.dumps(meta),
json.dumps(result.result),
json.dumps(dataclasses.asdict(system)),
),
)
self.connection.commit()
except psycopg2.Error as e:
logger.exception(
"Error during adding %s run on %s for submission '%s'",
mode,
runner,
submission,
exc_info=e,
)
self.connection.rollback() # Ensure rollback if error occurs
raise KernelBotError("Could not create leaderboard submission entry in database") from e
def get_leaderboard_names(self, active_only: bool = False) -> list[str]:
if active_only:
self.cursor.execute(
"SELECT name FROM leaderboard.leaderboard WHERE leaderboard.deadline > %s",
(datetime.datetime.now().astimezone(datetime.timezone.utc),),
)
else:
self.cursor.execute("SELECT name FROM leaderboard.leaderboard")
return [x[0] for x in self.cursor.fetchall()]
def get_leaderboards(self) -> list["LeaderboardItem"]:
self.cursor.execute(
"""
SELECT id, name, deadline, task, creator_id, forum_id, description, secret_seed
FROM leaderboard.leaderboard
"""
)
lbs = self.cursor.fetchall()
leaderboards = []
for lb in lbs:
self.cursor.execute(
"SELECT * from leaderboard.gpu_type WHERE leaderboard_id = %s", [lb[0]]
)
gpu_types = [x[1] for x in self.cursor.fetchall()]
leaderboards.append(
LeaderboardItem(
id=lb[0],
name=lb[1],
deadline=lb[2],
task=LeaderboardTask.from_dict(lb[3]),
gpu_types=gpu_types,
creator_id=lb[4],
forum_id=lb[5],
description=lb[6],
secret_seed=lb[7],
)
)
return leaderboards
def get_leaderboard_gpu_types(self, leaderboard_name: str) -> List[str]:
self.cursor.execute(
"""
SELECT id
FROM leaderboard.leaderboard
WHERE name = %s
""",
(leaderboard_name,),
)
lb_id = self.cursor.fetchone()
if lb_id is None:
raise LeaderboardDoesNotExist(leaderboard_name)
self.cursor.execute(
"""
SELECT gpu_type
FROM leaderboard.gpu_type
WHERE leaderboard_id = %s
""",
(lb_id[0],),
)
return [x[0] for x in self.cursor.fetchall()]
def get_leaderboard_id(self, leaderboard_name: str) -> int:
self.cursor.execute(
"""
SELECT id
FROM leaderboard.leaderboard
WHERE name = %s
""",
(leaderboard_name,),
)
lb_id = self.cursor.fetchone()
if lb_id is None:
raise LeaderboardDoesNotExist(leaderboard_name)
return lb_id[0]
def get_leaderboard_templates(self, leaderboard_name: str) -> Dict[str, str]:
lb_id = self.get_leaderboard_id(leaderboard_name)
self.cursor.execute(
"""
SELECT lang, code
FROM leaderboard.templates
WHERE leaderboard_id = %s
""",
(lb_id,),
)
return {x[0]: x[1] for x in self.cursor.fetchall()}
def get_leaderboard(self, leaderboard_name: str) -> "LeaderboardItem":
self.cursor.execute(
"""
SELECT id, name, deadline, task, creator_id, forum_id, secret_seed, description
FROM leaderboard.leaderboard
WHERE name = %s
""",
(leaderboard_name,),
)
res = self.cursor.fetchone()
if res:
task = LeaderboardTask.from_dict(res[3])
return LeaderboardItem(
id=res[0],
name=res[1],
deadline=res[2],
task=task,
creator_id=res[4],
forum_id=res[5],
secret_seed=res[6],
gpu_types=self.get_leaderboard_gpu_types(res[1]),
description=res[7],
)
else:
raise LeaderboardDoesNotExist(leaderboard_name)
def get_leaderboard_submissions(
self,
leaderboard_name: str,
gpu_name: str,
user_id: Optional[str] = None,
limit: int = None,
offset: int = 0,
) -> list["LeaderboardRankedEntry"]:
# separate cases, for personal we want all submissions, for general we want best per user
if user_id:
# Query all if user_id (means called from show-personal)
query = """
SELECT
s.file_name,
s.id,
s.user_id,
s.submission_time,
r.score,
r.runner,
ui.user_name,
RANK() OVER (ORDER BY r.score ASC) as rank
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
JOIN leaderboard.leaderboard l ON s.leaderboard_id = l.id
JOIN leaderboard.user_info ui ON s.user_id = ui.id
WHERE l.name = %s
AND r.runner = %s
AND NOT r.secret
AND r.score IS NOT NULL
AND r.passed
AND s.user_id = %s
ORDER BY r.score ASC
LIMIT %s OFFSET %s
"""
args = (leaderboard_name, gpu_name, user_id, limit, offset)
else:
# Query best submission per user if no user_id (means called from show)
query = """
WITH best_submissions AS (
SELECT DISTINCT ON (s.user_id)
s.id as submission_id,
s.file_name,
s.user_id,
s.submission_time,
r.score,
r.runner
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
JOIN leaderboard.leaderboard l ON s.leaderboard_id = l.id
JOIN leaderboard.user_info ui ON s.user_id = ui.id
WHERE l.name = %s AND r.runner = %s AND NOT r.secret
AND r.score IS NOT NULL AND r.passed
ORDER BY s.user_id, r.score ASC
)
SELECT
bs.file_name,
bs.submission_id,
bs.user_id,
bs.submission_time,
bs.score,
bs.runner,
ui.user_name,
RANK() OVER (ORDER BY bs.score ASC) as rank
FROM best_submissions bs
JOIN leaderboard.user_info ui ON bs.user_id = ui.id
ORDER BY bs.score ASC
LIMIT %s OFFSET %s
"""
args = (leaderboard_name, gpu_name, limit, offset)
self.cursor.execute(query, args)
result = [
LeaderboardRankedEntry(
submission_name=submission[0],
submission_id=submission[1],
user_id=submission[2],
submission_time=submission[3],
submission_score=submission[4],
user_name=submission[6],
rank=submission[7],
leaderboard_name=leaderboard_name,
gpu_type=gpu_name,
)
for submission in self.cursor.fetchall()
]
if len(result) == 0:
# try to diagnose why we didn't get anything
# this will raise if the LB does not exist at all.
self.get_leaderboard_id(leaderboard_name)
# did we specify a valid GPU?
gpus = self.get_leaderboard_gpu_types(leaderboard_name)
if gpu_name not in gpus:
raise KernelBotError(
f"Invalid GPU type '{gpu_name}' for leaderboard '{leaderboard_name}'"
)
return result
def generate_stats(self, last_day: bool):
try:
return self._generate_stats(last_day)
except Exception as e:
logger.exception("error generating stats", exc_info=e)
raise
def _generate_runner_stats(self, last_day: bool = False):
select_expr = "WHERE NOW() - s.submission_time <= interval '24 hours'" if last_day else ""
# per-runner stats
self.cursor.execute(
f"""
SELECT
runner,
COUNT(*),
COUNT(*) FILTER (WHERE passed),
COUNT(score),
COUNT(*) FILTER (WHERE secret),
MAX(runs.start_time - s.submission_time),
AVG(runs.start_time - s.submission_time),
SUM(runs.end_time - runs.start_time)
FROM leaderboard.runs JOIN leaderboard.submission s ON submission_id = s.id
{select_expr}
GROUP BY runner;
"""
)
result = {}
for row in self.cursor.fetchall():
result[f"num_run.{row[0]}"] = row[1]
result[f"runs_passed.{row[0]}"] = row[2]
result[f"runs_scored.{row[0]}"] = row[3]
result[f"runs_secret.{row[0]}"] = row[4]
result[f"max_delay.{row[0]}"] = row[5]
result[f"avg_delay.{row[0]}"] = row[6]
result[f"total_runtime.{row[0]}"] = row[7]
return result
def _generate_submission_stats(self, last_day: bool = False):
select_expr = "WHERE NOW() - submission_time <= interval '24 hours'" if last_day else ""
self.cursor.execute(
f"""
SELECT
COUNT(*),
COUNT(*) FILTER (WHERE NOT done),
COUNT(DISTINCT user_id)
FROM leaderboard.submission
{select_expr}
;
"""
)
num_sub, num_sub_wait, num_users = self.cursor.fetchone()
return {
"num_submissions": num_sub,
"sub_waiting": num_sub_wait,
"num_users": num_users,
}
def _generate_stats(self, last_day: bool = False):
result = self._generate_submission_stats(last_day)
result.update(self._generate_runner_stats(last_day))
# code-level stats
if not last_day:
self.cursor.execute(
"""
SELECT COUNT(*) FROM leaderboard.code_files;
"""
)
result["num_unique_codes"] = self.cursor.fetchone()[0]
else:
# calculate heavy hitters
self.cursor.execute(
"""
WITH run_durations AS (
SELECT
s.user_id AS user_id,
r.end_time - r.start_time AS duration
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
WHERE NOW() - s.submission_time <= interval '24 hours'
)
SELECT
user_id,
SUM(duration) AS total
FROM run_durations
GROUP BY user_id
ORDER BY total DESC
LIMIT 10;
"""
)
for row in self.cursor.fetchall():
result[f"total.{row[0]}"] = row[1]
return result
def get_user_from_id(self, id: str) -> Optional[str]:
try:
self.cursor.execute(
"""
SELECT user_name FROM leaderboard.user_info WHERE id = %s
""",
(id,),
)
return self.cursor.fetchone()[0]
except Exception:
return None
def delete_submission(self, submission_id: int):
try:
# first, the runs
query = """
DELETE FROM leaderboard.runs
WHERE submission_id = %s
"""
self.cursor.execute(query, (submission_id,))
# next, the submission itself
query = """
DELETE FROM leaderboard.submission
WHERE id = %s
"""
self.cursor.execute(query, (submission_id,))
# TODO delete code file? Could be one-to-many mapping, so we'd need
# to figure out if it is used elsewhere first.
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Could not delete submission %s.", submission_id, exc_info=e)
raise KernelBotError(f"Could not delete submission {submission_id}!") from e
def get_submission_by_id(self, submission_id: int) -> Optional["SubmissionItem"]:
query = """
SELECT s.leaderboard_id, lb.name, s.file_name, s.user_id,
s.submission_time, s.done, c.code
FROM leaderboard.submission s
JOIN leaderboard.code_files c ON s.code_id = c.id
JOIN leaderboard.leaderboard lb ON s.leaderboard_id = lb.id
WHERE s.id = %s
"""
self.cursor.execute(query, (submission_id,))
submission = self.cursor.fetchone()
if submission is None:
return None
# OK, now get the runs
query = """
SELECT start_time, end_time, mode, secret, runner, score,
passed, compilation, meta, result, system_info
FROM leaderboard.runs
WHERE submission_id = %s
"""
self.cursor.execute(query, (submission_id,))
runs = self.cursor.fetchall()
runs = [
RunItem(
start_time=r[0],
end_time=r[1],
mode=r[2],
secret=r[3],
runner=r[4],
score=r[5],
passed=r[6],
compilation=r[7],
meta=r[8],
result=r[9],
system=r[10],
)
for r in runs
]
return SubmissionItem(
submission_id=submission_id,
leaderboard_id=submission[0],
leaderboard_name=submission[1],
file_name=submission[2],
user_id=submission[3],
submission_time=submission[4],
done=submission[5],
code=bytes(submission[6]).decode("utf-8"),
runs=runs,
)
def get_leaderboard_submission_count(
self,
leaderboard_name: str,
gpu_name: str,
user_id: Optional[str] = None,
) -> int:
"""Get the total count of submissions for a leaderboard"""
if user_id:
query = """
SELECT COUNT(*)
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
JOIN leaderboard.leaderboard l ON s.leaderboard_id = l.id
WHERE l.name = %s
AND r.runner = %s
AND NOT r.secret
AND r.score IS NOT NULL
AND r.passed
AND s.user_id = %s
"""
args = (leaderboard_name, gpu_name, user_id)
else:
query = """
SELECT COUNT(DISTINCT s.user_id)
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
JOIN leaderboard.leaderboard l ON s.leaderboard_id = l.id
WHERE l.name = %s
AND r.runner = %s
AND NOT r.secret
AND r.score IS NOT NULL
AND r.passed
"""
args = (leaderboard_name, gpu_name)
self.cursor.execute(query, args)
count = self.cursor.fetchone()[0]
if count == 0:
# try to diagnose why we didn't get anything
# this will raise if the LB does not exist at all.
self.get_leaderboard_id(leaderboard_name)
# did we specify a valid GPU?
gpus = self.get_leaderboard_gpu_types(leaderboard_name)
if gpu_name not in gpus:
raise KernelBotError(
f"Invalid GPU type '{gpu_name}' for leaderboard '{leaderboard_name}'"
)
return count
def init_user_from_cli(self, cli_id: str, auth_provider: str):
"""
Initialize a user from CLI authentication flow.
Checks if cli_id already exists, and if so returns an error.
Creates a temporary user entry with the auth provider and cli_id.
Args:
cli_id (str): The unique identifier from the CLI
auth_provider (str): The authentication provider ('discord' or 'github')
Raises:
KernelBotError: If auth provider is invalid or cli_id already exists
"""
if auth_provider not in ["discord", "github"]:
raise Exception("Invalid auth provider")
try:
# Check if cli_id already exists
self.cursor.execute(
"""
SELECT 1 FROM leaderboard.user_info WHERE cli_id = %s
""",
(cli_id,),
)
if self.cursor.fetchone():
raise Exception("CLI ID already exists")
self.cursor.execute(
"""
INSERT INTO leaderboard.user_info (id, user_name,
cli_id, cli_auth_provider, cli_valid)
VALUES (%s, %s, %s, %s, %s)
""",
(f"temp_{cli_id}", f"temp_user_{cli_id}", cli_id, auth_provider, False),
)
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Error initializing user from CLI with ID %s", cli_id, exc_info=e)
raise KernelBotError("Error initializing user from CLI") from e
def create_user_from_cli(self, user_id: str, user_name: str, cli_id: str, auth_provider: str):
"""
Method to create a user from the CLI. Shouldn't be used for Discord.
Validates that the user doesn't already have a valid row and that the user_id/user_name
are temporary values that need to be updated.
"""
try:
self.cursor.execute(
"""
SELECT 1 FROM leaderboard.user_info WHERE id = %s
""",
(user_id,),
)
if self.cursor.fetchone():
raise Exception(
"User already has a valid account with this User ID."
"Please use the re-register command to re-authenticate."
)
self.cursor.execute(
"""
SELECT cli_valid FROM leaderboard.user_info
WHERE cli_id = %s AND cli_valid = TRUE AND cli_auth_provider = %s
""",
(cli_id, auth_provider),
)
if self.cursor.fetchone():
raise Exception(
"User already has a valid account with this CLI ID."
"Please use the re-register command to re-authenticate."
)
self.cursor.execute(
"""
UPDATE leaderboard.user_info
SET id = %s, user_name = %s, cli_valid = TRUE, cli_auth_provider = %s
WHERE cli_id = %s AND cli_valid = FALSE
""",
(user_id, user_name, auth_provider, cli_id),
)
if self.cursor.rowcount == 0:
raise Exception("No temporary user found with this CLI ID. No effect.")
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Could not create/update user %s from CLI.", user_id, exc_info=e)
raise KernelBotError("Database error while creating/updating user from CLI") from e
def reset_user_from_cli(self, user_id: str, cli_id: str, auth_provider: str):
try:
self.cursor.execute(
"""
SELECT 1 FROM leaderboard.user_info WHERE id = %s
""",
(user_id,),
)
if not self.cursor.fetchone():
raise Exception(
"User not found. Please use the register command to create an account."
)
self.cursor.execute(
"""
UPDATE leaderboard.user_info
SET cli_id = %s, cli_auth_provider = %s, cli_valid = TRUE
WHERE id = %s
""",
(cli_id, auth_provider, user_id),
)
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Could not reset user %s from CLI.", user_id, exc_info=e)
raise KernelBotError("Database error while resetting user from CLI") from e
def cleanup_temp_users(self):
try:
self.cursor.execute(
"""
DELETE FROM leaderboard.user_info WHERE cli_valid = FALSE and created_at <
NOW() - INTERVAL '10 minutes' AND id LIKE 'temp_%' AND user_name LIKE 'temp_%'
"""
)
self.connection.commit()
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Could not cleanup temp users", exc_info=e)
raise KernelBotError("Database error while cleaning up temp users") from e
def validate_cli_id(self, cli_id: str) -> Optional[dict[str, str]]:
"""
Validates a CLI ID and returns the associated user ID if valid.
Args:
cli_id (str): The CLI ID to validate.
Returns:
Optional[str]: The user ID if the CLI ID is valid, otherwise None.
"""
try:
self.cursor.execute(
"""
SELECT id, user_name FROM leaderboard.user_info
WHERE cli_id = %s AND cli_valid = TRUE
""",
(cli_id,),
)
result = self.cursor.fetchone()
return {"user_id": result[0], "user_name": result[1]} if result else None
except psycopg2.Error as e:
self.connection.rollback()
logger.exception("Error validating CLI ID %s", cli_id, exc_info=e)
raise KernelBotError("Error validating CLI ID") from e
class LeaderboardDoesNotExist(KernelBotError):
def __init__(self, name: str):
super().__init__(message=f"Leaderboard `{name}` does not exist.", code=404)