From e96acabcefcc8a14230e59287ae7dff152ee4ed1 Mon Sep 17 00:00:00 2001 From: Hung Pham Date: Sat, 16 Mar 2024 01:59:29 +1100 Subject: [PATCH] feat: small quality of life changes (#21) --- cogs/puzzle.py | 44 +++++++++++++++++++++++++++++----- src/context/puzzle.py | 19 +++++++++++---- src/db/seed.py | 28 +++++++++++----------- src/queries/puzzle.py | 7 +++--- tests/puzzle_test.py | 55 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 122 insertions(+), 31 deletions(-) diff --git a/cogs/puzzle.py b/cogs/puzzle.py index d0d0f4a..b980685 100644 --- a/cogs/puzzle.py +++ b/cogs/puzzle.py @@ -165,7 +165,25 @@ async def submit_answer( ) return - await interaction.followup.send("The submitted answer is ...CORRECT!") + completed_puzzles = await get_completed_puzzles(player.team_name) + # we must have submitted a puzzle correctly at this point + # if so, we can check if the number of puzzles completed is + # UTS - 4, USYD - 11 (4 + 1 + 6), or UNSW - 16 (4 + 1 + 6 + 1 + 4) respectively + num_of_puzzles_completed = len(completed_puzzles) + if num_of_puzzles_completed == 4: + await interaction.followup.send( + "The submitted answer is ...CORRECT! The meta for UTS is now unlocked!" + ) + elif num_of_puzzles_completed == 11: + await interaction.followup.send( + "The submitted answer is ...CORRECT! The meta for USYD is now unlocked!" + ) + elif num_of_puzzles_completed == 16: + await interaction.followup.send( + "The submitted answer is ...CORRECT! The meta for UNSW is now unlocked!" + ) + else: + await interaction.followup.send("The submitted answer is ...CORRECT!") @app_commands.command(name="list", description="List the available puzzles") @in_team_channel @@ -183,6 +201,7 @@ async def list_puzzles(self, interaction: discord.Interaction): puzzle_ids = [] puzzle_name_links = [] + puzzle_answers = [] for puzzle in puzzles: submissions = await find_submissions_by_discord_id_and_puzzle_id( player.discord_id, puzzle.puzzle_id @@ -190,13 +209,16 @@ async def list_puzzles(self, interaction: discord.Interaction): if any([submission.submission_is_correct for submission in submissions]): puzzle_ids.append(f":white_check_mark: {puzzle.puzzle_id}") + puzzle_answers.append(f"{puzzle.puzzle_answer}") else: puzzle_ids.append(puzzle.puzzle_id) + puzzle_answers.append("?") puzzle_name_links.append(f"[{puzzle.puzzle_name}]({puzzle.puzzle_link})") embed.add_field(name="ID", value="\n".join(puzzle_ids), inline=True) embed.add_field(name="Puzzles", value="\n".join(puzzle_name_links), inline=True) + embed.add_field(name="Answers", value="\n".join(puzzle_answers), inline=True) await interaction.followup.send(embed=embed) @app_commands.command( @@ -274,19 +296,29 @@ async def leaderboard(self, interaction: discord.Interaction): ) for _ in range(num_embeds) ] - leaderboard_text = [("", "") for _ in range(num_embeds)] + leaderboard_text = [("", "", "") for _ in range(num_embeds)] for i, val in enumerate(leaderboard_values): - team_name, puzzles_solved = val - - team_str, puzzles_solved_str = leaderboard_text[i // TEAMS_PER_EMBED] + team_name, puzzles_solved, submission_time = val + print(leaderboard_text[i // TEAMS_PER_EMBED]) + team_str, puzzles_solved_str, submission_time_str = leaderboard_text[ + i // TEAMS_PER_EMBED + ] team_str += f"{i+1}. {team_name}\n" puzzles_solved_str += f"{puzzles_solved}\n" + submission_time_str += f"{submission_time.strftime('%d/%m %X') if submission_time else 'N/A'}\n" - leaderboard_text[i // TEAMS_PER_EMBED] = (team_str, puzzles_solved_str) + leaderboard_text[i // TEAMS_PER_EMBED] = ( + team_str, + puzzles_solved_str, + submission_time_str, + ) for page_num, embed in enumerate(leaderboard_embeds): embed.add_field(name="Team", value=leaderboard_text[page_num][0]) embed.add_field(name="Puzzles Solved", value=leaderboard_text[page_num][1]) + embed.add_field( + name="Last Submission Time", value=leaderboard_text[page_num][2] + ) await interaction.followup.send( embed=leaderboard_embeds[0], view=PaginationView(leaderboard_embeds) diff --git a/src/context/puzzle.py b/src/context/puzzle.py index 77da402..6f188e2 100644 --- a/src/context/puzzle.py +++ b/src/context/puzzle.py @@ -66,8 +66,17 @@ async def can_access_puzzle(puzzle: Puzzle, team_name: str) -> bool: async def get_accessible_puzzles(team_name: str) -> List[Puzzle]: puzzles = await get_puzzles() completed_puzzles = await get_completed_puzzles(team_name) - return [ - puzzle - for puzzle in puzzles - if can_access_puzzle_context(puzzle, completed_puzzles) - ] + return sorted( + [ + puzzle + for puzzle in puzzles + if can_access_puzzle_context(puzzle, completed_puzzles) + ], + key=lambda p: ( + p.uni != "UTS", + p.uni != "USYD", + p.uni != "UNSW", + p.uni != "METAMETA", + p.puzzle_name, + ), + ) diff --git a/src/db/seed.py b/src/db/seed.py index 3fc6f9f..454933f 100644 --- a/src/db/seed.py +++ b/src/db/seed.py @@ -10,22 +10,22 @@ async def seed_puzzles(): sample_puzzles = [ - Puzzle("UTS-1", "UTS Puz1", "uts1", "skelly", "tiny.cc/puz", "UTS"), - Puzzle("UTS-2", "UTS Puz2", "uts2", "skelly", "tiny.cc/puz", "UTS"), - Puzzle("UTS-3", "UTS Puz3", "uts3", "skelly", "tiny.cc/puz", "UTS"), - Puzzle("UTS-4", "UTS Puz4", "uts4", "skelly", "tiny.cc/puz", "UTS"), + Puzzle("UTS-01", "UTS Puz1", "uts1", "skelly", "tiny.cc/puz", "UTS"), + Puzzle("UTS-02", "UTS Puz2", "uts2", "skelly", "tiny.cc/puz", "UTS"), + Puzzle("UTS-03", "UTS Puz3", "uts3", "skelly", "tiny.cc/puz", "UTS"), + Puzzle("UTS-04", "UTS Puz4", "uts4", "skelly", "tiny.cc/puz", "UTS"), Puzzle("UTS-M", "UTS PuzM", "utsm", "skelly", "tiny.cc/puz", "UTS"), - Puzzle("USYD-1", "USYD Puz1", "usyd1", "simon", "tiny.cc/puz", "USYD"), - Puzzle("USYD-2", "USYD Puz2", "usyd2", "simon", "tiny.cc/puz", "USYD"), - Puzzle("USYD-3", "USYD Puz3", "usyd3", "simon", "tiny.cc/puz", "USYD"), - Puzzle("USYD-4", "USYD Puz4", "usyd4", "simon", "tiny.cc/puz", "USYD"), - Puzzle("USYD-5", "USYD Puz5", "usyd5", "simon", "tiny.cc/puz", "USYD"), - Puzzle("USYD-6", "USYD Puz6", "usyd6", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-01", "USYD Puz1", "usyd1", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-02", "USYD Puz2", "usyd2", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-03", "USYD Puz3", "usyd3", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-04", "USYD Puz4", "usyd4", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-05", "USYD Puz5", "usyd5", "simon", "tiny.cc/puz", "USYD"), + Puzzle("USYD-06", "USYD Puz6", "usyd6", "simon", "tiny.cc/puz", "USYD"), Puzzle("USYD-M", "USYD PuzM", "usydm", "simon", "tiny.cc/puz", "USYD"), - Puzzle("UNSW-1", "UNSW Puz1", "unsw1", "timothy", "tiny.cc/puz", "UNSW"), - Puzzle("UNSW-2", "UNSW Puz2", "unsw2", "timothy", "tiny.cc/puz", "UNSW"), - Puzzle("UNSW-3", "UNSW Puz3", "unsw3", "timothy", "tiny.cc/puz", "UNSW"), - Puzzle("UNSW-4", "UNSW Puz4", "unsw4", "timothy", "tiny.cc/puz", "UNSW"), + Puzzle("UNSW-01", "UNSW Puz1", "unsw1", "timothy", "tiny.cc/puz", "UNSW"), + Puzzle("UNSW-02", "UNSW Puz2", "unsw2", "timothy", "tiny.cc/puz", "UNSW"), + Puzzle("UNSW-03", "UNSW Puz3", "unsw3", "timothy", "tiny.cc/puz", "UNSW"), + Puzzle("UNSW-04", "UNSW Puz4", "unsw4", "timothy", "tiny.cc/puz", "UNSW"), Puzzle("UNSW-M", "UNSW PuzM", "unswm", "timothy", "tiny.cc/puz", "UNSW"), Puzzle( "METAMETA", "Meta Meta", "youwin", "everyone <3", "tiny.cc/puz", "METAMETA" diff --git a/src/queries/puzzle.py b/src/queries/puzzle.py index 6b2bc1e..15a1653 100644 --- a/src/queries/puzzle.py +++ b/src/queries/puzzle.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import List import psycopg from psycopg.rows import class_row, dict_row @@ -131,13 +132,13 @@ async def delete_puzzle(puzzle_id: str): return True -async def get_leaderboard() -> tuple[str, int]: +async def get_leaderboard() -> List[tuple[str, int, datetime | None]]: aconn = await psycopg.AsyncConnection.connect(DATABASE_URL) acur = aconn.cursor() - + await acur.execute("SET TIMEZONE to 'Australia/Sydney'") await acur.execute( """ - SELECT t.team_name, t.puzzle_solved + SELECT t.team_name, t.puzzle_solved, MAX(s.submission_time) FROM public.teams AS t LEFT JOIN public.submissions AS s ON (t.team_name = s.team_name) AND s.submission_is_correct = TRUE diff --git a/tests/puzzle_test.py b/tests/puzzle_test.py index ec45789..aa5972c 100644 --- a/tests/puzzle_test.py +++ b/tests/puzzle_test.py @@ -1,8 +1,12 @@ +from datetime import datetime +from zoneinfo import ZoneInfo import pytest import pytest_asyncio from tests.utils import truncate -from src.queries.puzzle import create_puzzle, find_puzzle +from src.queries.puzzle import create_puzzle, get_puzzle, get_leaderboard +from src.queries.team import create_team, increase_puzzles_solved +from src.queries.submission import create_submission from src.models.puzzle import Puzzle @@ -15,7 +19,7 @@ async def async_setup(self): @pytest.mark.asyncio async def test_can_create_puzzle(self): expected: Puzzle = Puzzle( - "UTS-1", "The Answer of Life", "42", "Skelly", "tiny.cc/rickroll", "UTS" + "UTS-01", "The Answer of Life", "42", "Skelly", "tiny.cc/rickroll", "UTS" ) await create_puzzle( expected.puzzle_id, @@ -25,5 +29,50 @@ async def test_can_create_puzzle(self): expected.puzzle_link, expected.uni, ) - result = await find_puzzle("UTS-1") + result = await get_puzzle("UTS-01") assert expected == result + + @pytest.mark.asyncio + async def test_empty_leaderboard(self): + await create_puzzle( + "UTS-01", + "The Answer of Life", + "42", + "Skelly", + "tiny.cc/rickroll", + "UTS", + ) + await create_team("test team", 1, 1, 1, 1) + assert await get_leaderboard() == [("test team", 0, None)] + + @pytest.mark.asyncio + async def test_leaderboard(self): + await create_puzzle( + "UTS-01", + "The Answer of Life", + "42", + "Skelly", + "tiny.cc/rickroll", + "UTS", + ) + await create_team("test team 1", 1, 1, 1, 1) + await create_team("test team 2", 1, 1, 1, 1) + + await create_submission( + "UTS-01", + "test team 2", + datetime(2024, 1, 1, tzinfo=ZoneInfo("Australia/Sydney")), + "42", + True, + ) + + await increase_puzzles_solved("test team 2") + + assert await get_leaderboard() == [ + ( + "test team 2", + 1, + datetime(2024, 1, 1, tzinfo=ZoneInfo("Australia/Sydney")), + ), + ("test team 1", 0, None), + ]