Compare commits

..
9 changed files with 181 additions and 20 deletions
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash -x
# this file is intended to build the docker image and push to the correct repository. Intended to be run by my buildbot instance.
# build, tag, and push develop
docker build -t viker.cheezers.net:5000/agon:develop .
docker push viker.cheezers.net:5000/agon:develop
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash -x
# this file is intended to build the docker image and push to the correct repository. Intended to be run by my buildbot instance.
# check if there is a git tag. if so, tag with that tag and release
# expecting tag to be 6 characters long. otherwise isn't not the right tag
GITTAG=$(git describe --tags)
length=${#GITTAG}
if [ $length -eq 6 ]; then
docker build -t viker.cheezers.net:5000/agon:release -t viker.cheezers.net:5000/agon:$GITTAG .
docker push viker.cheezers.net:5000/agon:release iker.cheezers.net:5000/agon:$GITTAG
else
docker build -t viker.cheezers.net:5000/agon:release .
docker push viker.cheezers.net:5000/agon:release
fi
+2
View File
@@ -10,6 +10,7 @@ from .help import HelpCog
from .refresh import RefreshCog
from .set import SetCog
from .update import UpdateCog
from .view import ViewCog
# This function will be called when this extension is loaded.
@@ -24,3 +25,4 @@ def setup(bot: commands.Bot):
bot.add_cog(RefreshCog(bot))
bot.add_cog(SetCog())
bot.add_cog(UpdateCog(bot))
bot.add_cog(ViewCog())
+112
View File
@@ -0,0 +1,112 @@
"""
View command heirarchy.
"""
import nextcord
import pandas as pd
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
# pylint: disable=import-error
from utilities.sql_data_classes import Category, CategoryGroup, Score, engine
from utilities.utils import MAX_LINE_LENGTH, autofill_categories, autofill_groups, format_score_string
class ViewCog(commands.Cog, name="View Command"):
"""
Class holds commands for viewing data.
"""
def __init__(self) -> None:
"""
Default constructor for ViewCog class. Does nothing.
"""
@nextcord.slash_command()
async def view(self, interaction: nextcord.Interaction):
"""
Parent slash command for view. Never called.
"""
@view.subcommand(description="Lists all scores for a given category.")
async def leaderboard(
self,
interaction: nextcord.Interaction,
group_id: str = nextcord.SlashOption(
name="group", description="Group to show leaderboard from.", required=True
),
category_id: str = nextcord.SlashOption(
name="category", description="Category to show leaderboard.", required=True
),
):
"""
Gathers all users that have a submission in the group/category combo and returns a message with them
in ranked order.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command.
group_id (str): String representation of Group id to search from. Is SlashOption.
category_id (str): String representation of Category id to show leaderboard. Is SlashOption.
"""
str_list: "list[str]" = []
# get group, category, and all scores sorted correctly.
with Session(engine) as session:
stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt)
stmt = select(Category).where(Category.CategoryId == int(category_id))
category = session.scalar(stmt)
d_f = pd.read_sql_query(
sql=session.query(Score).where(Score.CategoryId == category_id).statement, con=engine
)
scores = d_f.sort_values("Score", ascending=category.IsAscending).reset_index()
# get the longest string
longest_str: int = scores.UserName.map(len).max()
for i, row in scores.iterrows():
str_list.append(
format_score_string(
row["UserName"], row["Score"], longest_str, category.IsTimeRecord, i + 1, MAX_LINE_LENGTH
)
)
message_str: str = f"**{group.GroupName} {category.CategoryName} Leaderboard!**\n```"
if len(str_list) == 0:
message_str += "No Submissions!"
else:
for line in str_list:
message_str += f"{line}\n"
message_str += "```"
await interaction.response.send_message(content=message_str)
@leaderboard.on_autocomplete("group_id")
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str):
"""
Autocomplete function for table names.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command.
group_id (str): String to be autocompleted with Group name.
"""
await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id))
@leaderboard.on_autocomplete("category_id")
async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str):
"""
Autocomplete function for category names.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command.
category_id (str): String to be autocompleted with Category name.
group_id (str): Group to find Category from.
"""
await interaction.response.send_autocomplete(
autofill_categories(interaction.guild_id, int(group_id), category_id)
)
+2 -1
View File
@@ -11,7 +11,7 @@ from sqlalchemy.orm import Session
# pylint: disable=import-error
from classes.submission import SubmissionState
from agon import submission_objects
from highscores import submission_objects
from utilities.sql_data_classes import Category, Guild, engine
@@ -45,6 +45,7 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
if message.channel.id == submission_channel_id:
state = submission.submission_state
if state == SubmissionState.SCORE and message.content:
stmt = select(Category.IsTimeRecord).where(Category.CategoryId == submission.category_id)
# check with regex if score matches correctly
+1 -1
View File
@@ -9,7 +9,7 @@ from sqlalchemy.orm import Session, aliased
# pylint: disable=import-error
from classes.submission import Submission, SubmissionState
from agon import submission_objects
from highscores import submission_objects
from utilities.sql_data_classes import Category, CategoryGroup, Guild, engine
from .verification_view import VerificationView
+1 -1
View File
@@ -1,5 +1,5 @@
"""
main file for agon
main file for highscores bot
"""
import logging
+3 -3
View File
@@ -17,7 +17,7 @@ CREATE TABLE CategoryGroups(
PadLength smallint not null DEFAULT 26,
UsernameLength smallint DEFAULT 12,
CONSTRAINT UniqueGuildCategoryGroup UNIQUE (GuildId, GroupName),
FOREIGN KEY (GuildId) REFERENCES Guilds(GuildId),
FOREIGN KEY (GuildId) REFERENCES Guilds(GuildId) ON DELETE CASCADE,
PRIMARY KEY (GroupId)
);
@@ -30,7 +30,7 @@ CREATE TABLE Categories(
IsTimeRecord boolean not null DEFAULT false,
IsValidated boolean not null DEFAULT true,
CONSTRAINT UniqueCategoryInGroup UNIQUE (GroupId, CategoryName),
FOREIGN KEY (GroupId) REFERENCES CategoryGroups(GroupId),
FOREIGN KEY (GroupId) REFERENCES CategoryGroups(GroupId) ON DELETE CASCADE,
PRIMARY Key (CategoryId)
);
@@ -38,6 +38,6 @@ CREATE TABLE Scores(
UserName varchar(30) not null,
Score float not null, -- Maybe should have time and int scores?? timestamp type?
CategoryId int unsigned not null,
FOREIGN KEY (CategoryId) REFERENCES Categories(CategoryId),
FOREIGN KEY (CategoryId) REFERENCES Categories(CategoryId) ON DELETE CASCADE,
PRIMARY KEY (CategoryId, UserName)
);
+37 -14
View File
@@ -231,20 +231,12 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str:
# add each score to list
for ind in range(max_cat_size): # need to subtract to deal with category name string
if ind in processed_group.index:
user_length = group.UsernameLength
username = processed_group["UserName"][ind][:user_length].ljust(user_length, " ")
username = processed_group["UserName"][ind]
score = processed_group["Score"][ind]
# get score, pad to correct length
if category.IsTimeRecord:
# MM:SS.ms is 7 characters long
# if there are single digit minutes, add a space for formatting.
score = get_time_str(processed_group["Score"][ind])
if score.find(":") and len(score.split(":", maxsplit=1)[0]) < 2:
score = " " + score
else:
score = str(int(processed_group["Score"][ind])).rjust(7, " ")
str_list[ind + 1] += f"{ind + 1}: {username} - {score}".ljust(pad_length, " ")
str_list[ind + 1] += format_score_string(
username, score, group.UsernameLength, category.IsTimeRecord, ind + 1, pad_length
)
else:
# if not enough entries, pad out
if ind + 1 <= category.Size:
@@ -262,7 +254,6 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str:
# need to use index to allow for modifying the list
for ind, line in enumerate(str_list):
if len(line) > MAX_LINE_LENGTH:
split_loc = int(MAX_LINE_LENGTH / pad_length) * pad_length
new_line = line[split_loc:]
str_list[ind] = line[:split_loc]
@@ -283,6 +274,38 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str:
return ret_string + "```"
def format_score_string(
username: str, score: float, username_length: int, is_time_record: bool, rank: int, pad_length: int
) -> str:
"""
Builds the string to show a user's score.
Args:
username (str): username.
score (int): score in int form.
username_length (float): limit to how long username is allowed to be.
is_time_record (bool): if the score represents an number or a time record.
rank (int): the user's rank.
pad_length (int): limit to how long the string can be.
Returns:
str: string to show user's score.
"""
username = username[:username_length].ljust(username_length, " ")
# get score, pad to correct length
if is_time_record:
# MM:SS.ms is 7 characters long
# if there are single digit minutes, add a space for formatting.
score = get_time_str(score)
if score.find(":") and len(score.split(":", maxsplit=1)[0]) < 2:
score = " " + score
else:
score = str(int(score)).rjust(7, " ")
return f"{rank}: {username} - {score}".ljust(pad_length, " ")
def get_time_str(time_in: float) -> str:
"""
Formats a float value into the desired string format for a timestamp.