Change session usage to on use basis.

This commit is contained in:
alwayscompiling
2022-12-06 22:28:53 -05:00
parent 296e14f0f6
commit 3446ce9ca2
11 changed files with 357 additions and 306 deletions
+11 -9
View File
@@ -6,9 +6,10 @@ from enum import Enum
from nextcord import Embed from nextcord import Embed
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import Category, CategoryGroup, session from utilities.sql_data_classes import Category, CategoryGroup, engine
class SubmissionState(Enum): class SubmissionState(Enum):
@@ -199,14 +200,15 @@ class Submission:
.where(CategoryGroup.GuildId == self.guild_id) .where(CategoryGroup.GuildId == self.guild_id)
.where(CategoryGroup.GroupId == self.group_id) .where(CategoryGroup.GroupId == self.group_id)
) )
embed.add_field(name="Group", value=session.scalar(stmt), inline=False) with Session(engine) as session:
if self.category_id: embed.add_field(name="Group", value=session.scalar(stmt), inline=False)
stmt = ( if self.category_id:
select(Category.CategoryName) stmt = (
.where(Category.GroupId == self.group_id) select(Category.CategoryName)
.where(Category.CategoryId == self.category_id) .where(Category.GroupId == self.group_id)
) .where(Category.CategoryId == self.category_id)
embed.add_field(name="Category", value=session.scalar(stmt), inline=False) )
embed.add_field(name="Category", value=session.scalar(stmt), inline=False)
if self.score: if self.score:
embed.add_field(name="Score", value=self.score, inline=False) embed.add_field(name="Score", value=self.score, inline=False)
+42 -34
View File
@@ -7,9 +7,10 @@ from typing import Optional
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy import distinct, select from sqlalchemy import distinct, select
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import Category, CategoryGroup, Guild, Score, session from utilities.sql_data_classes import Category, CategoryGroup, Guild, Score, engine
from utilities.utils import ( from utilities.utils import (
autofill_categories, autofill_categories,
autofill_groups, autofill_groups,
@@ -70,20 +71,21 @@ class DataManagementCog(commands.Cog, name="Table Management"):
user_name (str): User to remove from highscores user_name (str): User to remove from highscores
""" """
stmt = select(distinct(Score.CategoryId)).where(Score.UserName == user_name) with Session(engine) as session:
category_ids: "list[int]" = list(session.scalars(stmt)) stmt = select(distinct(Score.CategoryId)).where(Score.UserName == user_name)
category_ids: "list[int]" = list(session.scalars(stmt))
stmt = select(Score).where(Score.UserName == user_name) stmt = select(Score).where(Score.UserName == user_name)
for score in session.scalars(stmt): for score in session.scalars(stmt):
session.delete(score) session.delete(score)
session.commit() session.commit()
group_ids = select(Category.GroupId).where(Category.CategoryId.in_(category_ids)) group_ids = select(Category.GroupId).where(Category.CategoryId.in_(category_ids))
stmt = select(CategoryGroup).where(CategoryGroup.GroupId.in_(group_ids)) stmt = select(CategoryGroup).where(CategoryGroup.GroupId.in_(group_ids))
for group in session.scalars(stmt): for group in session.scalars(stmt):
await send_highscore_message(interaction.guild.get_channel(group.ChannelId), group) await send_highscore_message(session, interaction.guild.get_channel(group.ChannelId), group)
await interaction.send(f'User "{user_name}" was removed from all tables.', ephemeral=True) await interaction.send(f'User "{user_name}" was removed from all tables.', ephemeral=True)
@@ -107,20 +109,23 @@ class DataManagementCog(commands.Cog, name="Table Management"):
user_name (str): The user to remove. Is SlashOption. user_name (str): The user to remove. Is SlashOption.
""" """
stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id)) with Session(engine) as session:
group = session.scalar(stmt) stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt)
stmt = select(Category.CategoryName).where(Category.CategoryId == int(category_id)) stmt = select(Category.CategoryName).where(Category.CategoryId == int(category_id))
category_name = session.scalar(stmt) category_name = session.scalar(stmt)
stmt = select(Score).where(Score.CategoryId == int(category_id)).where(Score.UserName == user_name) stmt = select(Score).where(Score.CategoryId == int(category_id)).where(Score.UserName == user_name)
# there should only be one because category id and username are a composite primary key in score # there should only be one because category id and username are a composite primary key in score
session.delete(session.scalar(stmt)) session.delete(session.scalar(stmt))
session.commit() session.commit()
await send_highscore_message(interaction.guild.get_channel(group.ChannelId), group) await send_highscore_message(session, interaction.guild.get_channel(group.ChannelId), group)
await interaction.response.send_message(f'User "{user_name}" removed from "{category_name}".', ephemeral=True) await interaction.response.send_message(
f'User "{user_name}" removed from "{category_name}".', ephemeral=True
)
@user.subcommand(description="Deletes a user's scores from entire group.") @user.subcommand(description="Deletes a user's scores from entire group.")
async def from_group( async def from_group(
@@ -138,19 +143,20 @@ class DataManagementCog(commands.Cog, name="Table Management"):
user_name (str): The user to remove. Is SlashOption. user_name (str): The user to remove. Is SlashOption.
""" """
stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id)) with Session(engine) as session:
group = session.scalar(stmt) stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id))
category_ids = select(Category.CategoryId).where(Category.GroupId == int(group_id)) group = session.scalar(stmt)
stmt = select(Score).where(Score.CategoryId.in_(category_ids)).where(Score.UserName == user_name) category_ids = select(Category.CategoryId).where(Category.GroupId == int(group_id))
stmt = select(Score).where(Score.CategoryId.in_(category_ids)).where(Score.UserName == user_name)
for score in session.scalars(stmt): for score in session.scalars(stmt):
session.delete(score) session.delete(score)
session.commit() session.commit()
await send_highscore_message(interaction.guild.get_channel(group.ChannelId), group) await send_highscore_message(session, interaction.guild.get_channel(group.ChannelId), group)
await interaction.response.send_message( await interaction.response.send_message(
f'User "{user_name}" removed from all categories in {group.GroupName}.', ephemeral=True f'User "{user_name}" removed from all categories in {group.GroupName}.', ephemeral=True
) )
@from_category.on_autocomplete("group_id") @from_category.on_autocomplete("group_id")
@from_group.on_autocomplete("group_id") @from_group.on_autocomplete("group_id")
@@ -215,8 +221,10 @@ class DataManagementCog(commands.Cog, name="Table Management"):
old_username (str): Username to replace. old_username (str): Username to replace.
new_username (str): Username to replace old with. new_username (str): Username to replace old with.
""" """
stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id)
verify_channel = interaction.guild.get_channel(session.scalar(stmt)) with Session(engine) as session:
stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id)
verify_channel = interaction.guild.get_channel(session.scalar(stmt))
embed: nextcord.Embed = nextcord.Embed(title="Username change request.") embed: nextcord.Embed = nextcord.Embed(title="Username change request.")
embed.add_field(name="Old Username", value=old_username) embed.add_field(name="Old Username", value=old_username)
+42 -37
View File
@@ -7,10 +7,11 @@ from typing import Optional
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from cogs.submission.submission_views import SubmissionCreateButton from cogs.submission.submission_views import SubmissionCreateButton
from utilities.sql_alchemy_connection import CategoryGroup, session from utilities.sql_data_classes import CategoryGroup, engine
from utilities.utils import autofill_groups, send_highscore_message from utilities.utils import autofill_groups, send_highscore_message
@@ -55,23 +56,24 @@ class MessageManagementCog(commands.Cog, name="Message Management"):
if not channel: if not channel:
channel = interaction.channel channel = interaction.channel
stmt = ( with Session(engine) as session:
select(CategoryGroup) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup)
.where(CategoryGroup.ChannelId == channel.id) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.ChannelId == channel.id)
# TODO test when nothing in channel )
groups = session.scalars(stmt) # TODO test when nothing in channel
groups = session.scalars(stmt)
await interaction.response.defer(ephemeral=True) await interaction.response.defer(ephemeral=True)
for group in groups: for group in groups:
try: try:
table_message: nextcord.Message = await channel.fetch_message(group.MessageId) table_message: nextcord.Message = await channel.fetch_message(group.MessageId)
await table_message.delete() await table_message.delete()
except nextcord.NotFound: except nextcord.NotFound:
# silently ignore that messages are not found. They got deleted already. # silently ignore that messages are not found. They got deleted already.
pass pass
await interaction.send("Table messages deleted.", ephemeral=True) await interaction.send("Table messages deleted.", ephemeral=True)
@@ -90,21 +92,23 @@ class MessageManagementCog(commands.Cog, name="Message Management"):
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
""" """
stmt = select(CategoryGroup).where(CategoryGroup.GuildId == interaction.guild_id)
# awaiting response because posting scores takes a while with Session(engine) as session:
await interaction.response.defer(ephemeral=True) stmt = select(CategoryGroup).where(CategoryGroup.GuildId == interaction.guild_id)
for group in session.scalars(stmt): # awaiting response because posting scores takes a while
channel = interaction.guild.get_channel(group.ChannelId) await interaction.response.defer(ephemeral=True)
# Error checking for registered highscore channel for group in session.scalars(stmt):
error_response = "Registered Highscores channel does not exist or was never registered. " channel = interaction.guild.get_channel(group.ChannelId)
error_response += 'Use "/register" to register channels.'
assert channel is not None, await interaction.response(error_response, ephemeral=True)
message: nextcord.Message = await send_highscore_message(channel, group) # Error checking for registered highscore channel
await message.edit(view=SubmissionCreateButton(self.bot)) error_response = "Registered Highscores channel does not exist or was never registered. "
error_response += 'Use "/register" to register channels.'
assert channel is not None, await interaction.response(error_response, ephemeral=True)
message: nextcord.Message = await send_highscore_message(session, channel, group)
await message.edit(view=SubmissionCreateButton(self.bot))
await interaction.send(f"Posted all groups in {interaction.guild.name}'s highscores.", ephemeral=True) await interaction.send(f"Posted all groups in {interaction.guild.name}'s highscores.", ephemeral=True)
@@ -125,19 +129,20 @@ class MessageManagementCog(commands.Cog, name="Message Management"):
group_id (str): Str representation of Group id to refresh. Is SlashOption. group_id (str): Str representation of Group id to refresh. Is SlashOption.
""" """
stmt = ( with Session(engine) as session:
select(CategoryGroup) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup)
.where(CategoryGroup.GroupId == int(group_id)) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt) )
group = session.scalar(stmt)
channel = interaction.guild.get_channel(group.ChannelId) channel = interaction.guild.get_channel(group.ChannelId)
message: nextcord.Message = await send_highscore_message(channel, group) message: nextcord.Message = await send_highscore_message(session, channel, group)
await message.edit(view=SubmissionCreateButton(self.bot)) await message.edit(view=SubmissionCreateButton(self.bot))
await interaction.response.send_message(f"Updated table {group.GroupName}!", ephemeral=True) await interaction.response.send_message(f"Updated table {group.GroupName}!", ephemeral=True)
@refresh_group.on_autocomplete("group_id") @refresh_group.on_autocomplete("group_id")
async def refresh_table_autofill(self, interaction: nextcord.Interaction, group_id: str): async def refresh_table_autofill(self, interaction: nextcord.Interaction, group_id: str):
+47 -37
View File
@@ -7,9 +7,10 @@ from typing import Optional
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import CategoryGroup, Guild, session from utilities.sql_data_classes import CategoryGroup, Guild, engine
from utilities.utils import autofill_groups from utilities.utils import autofill_groups
@@ -52,17 +53,19 @@ class SettingsManagementCog(commands.Cog, name="Settings Management"):
channel (nextcord.abc.GuildChannel, optional): Channel to register channel (nextcord.abc.GuildChannel, optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None. Is a SlashOption. Limited to text channels, Defaults to None.
""" """
if not channel: if not channel:
channel = interaction.channel channel = interaction.channel
channel_id = channel.id channel_id = channel.id
stmt = ( with Session(engine) as session:
select(CategoryGroup) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup)
.where(CategoryGroup.GroupId == int(group_id)) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt) )
group.ChannelId = channel_id group = session.scalar(stmt)
session.commit() group.ChannelId = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@register.subcommand(description="Registers the channel for submission messages.") @register.subcommand(description="Registers the channel for submission messages.")
@@ -84,13 +87,16 @@ class SettingsManagementCog(commands.Cog, name="Settings Management"):
channel Optional[nextcord.abc.GuildChannel], optional): Channel to register channel Optional[nextcord.abc.GuildChannel], optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None. Is a SlashOption. Limited to text channels, Defaults to None.
""" """
if not channel: if not channel:
channel = interaction.channel channel = interaction.channel
channel_id = channel.id
stmt = select(Guild).where(Guild.GuildId == interaction.guild_id) with Session(engine) as session:
guild = session.scalar(stmt) channel_id = channel.id
guild.SubmissionChannel = channel_id stmt = select(Guild).where(Guild.GuildId == interaction.guild_id)
session.commit() guild = session.scalar(stmt)
guild.SubmissionChannel = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@register.subcommand(description="Registers the channel for verification messages.") @register.subcommand(description="Registers the channel for verification messages.")
@@ -112,13 +118,15 @@ class SettingsManagementCog(commands.Cog, name="Settings Management"):
channel (Optional[nextcord.abc.GuildChannel]): Channel to register channel (Optional[nextcord.abc.GuildChannel]): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None. Is a SlashOption. Limited to text channels, Defaults to None.
""" """
if not channel: if not channel:
channel = interaction.channel channel = interaction.channel
channel_id = channel.id channel_id = channel.id
stmt = select(Guild).where(Guild.GuildId == interaction.guild_id) with Session(engine) as session:
guild = session.scalar(stmt) stmt = select(Guild).where(Guild.GuildId == interaction.guild_id)
guild.VerificationChannel = channel_id guild = session.scalar(stmt)
session.commit() guild.VerificationChannel = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True)) @nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
@@ -147,17 +155,18 @@ class SettingsManagementCog(commands.Cog, name="Settings Management"):
length (int): Number of characters. Is SlashOption. Defaults to 26. length (int): Number of characters. Is SlashOption. Defaults to 26.
""" """
stmt = ( with Session(engine) as session:
select(CategoryGroup) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup)
.where(CategoryGroup.GroupId == int(group_id)) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt) )
group.PadLength = length group = session.scalar(stmt)
session.commit() group.PadLength = length
await interaction.response.send_message( session.commit()
f"Category pad length set to {length} for {group.GroupName}", ephemeral=True await interaction.response.send_message(
) f"Category pad length set to {length} for {group.GroupName}", ephemeral=True
)
@set.subcommand(description="Set username length for a Group of Categories.") @set.subcommand(description="Set username length for a Group of Categories.")
async def username_length( async def username_length(
@@ -176,14 +185,15 @@ class SettingsManagementCog(commands.Cog, name="Settings Management"):
length (int): Number of characters. Is SlashOption. Defaults to 12. length (int): Number of characters. Is SlashOption. Defaults to 12.
""" """
stmt = ( with Session(engine) as session:
select(CategoryGroup) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup)
.where(CategoryGroup.GroupId == int(group_id)) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.GroupId == int(group_id))
group = session.scalar(stmt) )
group.UsernameLength = length group = session.scalar(stmt)
session.commit() group.UsernameLength = length
session.commit()
await interaction.response.send_message(f"Username length set to {length}.", ephemeral=True) await interaction.response.send_message(f"Username length set to {length}.", ephemeral=True)
@group_output_channel.on_autocomplete("group_id") @group_output_channel.on_autocomplete("group_id")
+26 -24
View File
@@ -2,9 +2,10 @@
import nextcord import nextcord
from sqlalchemy import distinct, exc, select from sqlalchemy import distinct, exc, select
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import Category, CategoryGroup, Score, session from utilities.sql_data_classes import Category, CategoryGroup, Score, engine
from utilities.utils import send_highscore_message from utilities.utils import send_highscore_message
@@ -43,32 +44,33 @@ class UpdateUsernameVerifyView(nextcord.ui.View):
interaction (nextcord.Interaction): Interaction with the button interaction (nextcord.Interaction): Interaction with the button
""" """
try: with Session(engine) as session:
embed_content = interaction.message.embeds[0].to_dict() try:
old_username: str = embed_content["fields"][0]["value"] embed_content = interaction.message.embeds[0].to_dict()
new_username: str = embed_content["fields"][1]["value"] old_username: str = embed_content["fields"][0]["value"]
new_username: str = embed_content["fields"][1]["value"]
stmt = select(distinct(Score.CategoryId)).where(Score.UserName == old_username) stmt = select(distinct(Score.CategoryId)).where(Score.UserName == old_username)
category_ids: "list[int]" = list(session.scalars(stmt)) category_ids: "list[int]" = list(session.scalars(stmt))
stmt = select(Score).where(Score.UserName == old_username) stmt = select(Score).where(Score.UserName == old_username)
for score in session.scalars(stmt): for score in session.scalars(stmt):
score.UserName = new_username score.UserName = new_username
session.commit() session.commit()
group_ids = select(Category.GroupId).where(Category.CategoryId.in_(category_ids)) group_ids = select(Category.GroupId).where(Category.CategoryId.in_(category_ids))
stmt = select(CategoryGroup).where(CategoryGroup.GroupId.in_(group_ids)) stmt = select(CategoryGroup).where(CategoryGroup.GroupId.in_(group_ids))
for group in session.scalars(stmt): for group in session.scalars(stmt):
await send_highscore_message(interaction.guild.get_channel(group.ChannelId), group) await send_highscore_message(session, interaction.guild.get_channel(group.ChannelId), group)
content = interaction.message.content content = interaction.message.content
content += f"\nApproved by <@{interaction.user.id}>." content += f"\nApproved by <@{interaction.user.id}>."
await interaction.message.edit(content=content, embeds=interaction.message.embeds, view=None) await interaction.message.edit(content=content, embeds=interaction.message.embeds, view=None)
except exc.DataError: except exc.DataError:
session.rollback() session.rollback()
content = interaction.message.content content = interaction.message.content
content += "\nError, UserName is too long." content += "\nError, UserName is too long."
await interaction.message.edit(content=content, embeds=interaction.message.embeds, view=None) await interaction.message.edit(content=content, embeds=interaction.message.embeds, view=None)
await interaction.response.send_message(content="User must resubmit with shorter name.", ephemeral=True) await interaction.response.send_message(content="User must resubmit with shorter name.", ephemeral=True)
+7 -3
View File
@@ -4,9 +4,10 @@ Defines cog for guild listeners. Contains functions for various guild events.
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import Guild, session from utilities.sql_data_classes import Guild, engine
from utilities.utils import add_preset_configuration from utilities.utils import add_preset_configuration
@@ -19,9 +20,12 @@ class GuildListener(commands.Cog, name="Guild Listener"):
@commands.Cog.listener() @commands.Cog.listener()
async def on_guild_join(self, guild: nextcord.Guild): async def on_guild_join(self, guild: nextcord.Guild):
"""Listener for Guild Join. Creates skeleton for new guild joined and saves it.""" """Listener for Guild Join. Creates skeleton for new guild joined and saves it."""
new_guild = Guild(GuildId=guild.id) new_guild = Guild(GuildId=guild.id)
session.add(new_guild)
session.commit() with Session(engine) as session:
session.add(new_guild)
session.commit()
# This add config will be removed eventually. Config adding will have to be controlled by commands. # This add config will be removed eventually. Config adding will have to be controlled by commands.
add_preset_configuration(guild.id, "runescape") add_preset_configuration(guild.id, "runescape")
+57 -55
View File
@@ -7,12 +7,12 @@ import re
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import aliased from sqlalchemy.orm import Session
# pylint: disable=import-error # pylint: disable=import-error
from classes.submission import SubmissionState from classes.submission import SubmissionState
from highscores import submission_objects from highscores import submission_objects
from utilities.sql_alchemy_connection import Category, CategoryGroup, Guild, session from utilities.sql_data_classes import Category, Guild, engine
class SubmissionListener(commands.Cog, name="Message Listener"): class SubmissionListener(commands.Cog, name="Message Listener"):
@@ -33,66 +33,68 @@ class SubmissionListener(commands.Cog, name="Message Listener"):
if submission_objects.has_user(message.author.id): if submission_objects.has_user(message.author.id):
submission = submission_objects.get_user_submission(message.author.id) submission = submission_objects.get_user_submission(message.author.id)
message_id = submission.message_id message_id = submission.message_id
stmt = select(Guild.SubmissionChannel).where(Guild.GuildId == guild_id)
submission_channel_id = session.scalar(stmt)
submission_message: nextcord.Message = (
await self.bot.get_guild(guild_id).get_channel(submission_channel_id).fetch_message(message_id)
)
# need check if correct user and correct channel with Session(engine) as session:
if message.channel.id == submission_channel_id: stmt = select(Guild.SubmissionChannel).where(Guild.GuildId == guild_id)
state = submission.submission_state submission_channel_id = session.scalar(stmt)
if state == SubmissionState.SCORE and message.content: submission_message: nextcord.Message = (
await self.bot.get_guild(guild_id).get_channel(submission_channel_id).fetch_message(message_id)
)
stmt = select(Category.IsTimeRecord).where(Category.CategoryId == submission.category_id) # need check if correct user and correct channel
if message.channel.id == submission_channel_id:
state = submission.submission_state
if state == SubmissionState.SCORE and message.content:
# check with regex if score matches correctly stmt = select(Category.IsTimeRecord).where(Category.CategoryId == submission.category_id)
is_time_record = session.scalar(stmt)
time_regex = re.search("^([0-9]{1,2}:[0-9]|[0-9])?[0-9](\\.[0-9])?$", message.content) # check with regex if score matches correctly
int_regex = re.search("^[0-9]+$", message.content) is_time_record = session.scalar(stmt)
if time_regex is None and is_time_record:
embeds = submission_message.embeds[0] time_regex = re.search("^([0-9]{1,2}:[0-9]|[0-9])?[0-9](\\.[0-9])?$", message.content)
embeds.add_field( int_regex = re.search("^[0-9]+$", message.content)
name="Error", if time_regex is None and is_time_record:
value="Time record must follow format MM:SS.ms, SS.ms, or SS.", embeds = submission_message.embeds[0]
embeds.add_field(
name="Error",
value="Time record must follow format MM:SS.ms, SS.ms, or SS.",
)
elif int_regex is None and not is_time_record:
embeds = submission_message.embeds[0]
embeds.add_field(name="Error", value="Number record must only be number.")
else:
score = message.content
if is_time_record:
# standardize time score format.
if re.search("^[1-9]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
# score is already fine.
pass
elif re.search("^[0-9]{2}(\\.[0-9])?$", score):
# need to add leading colon and 0
score = "0:" + score
elif re.search("^[0-9](\\.[0-9])?$", score):
# need to add 0 in seconds and leading colon and 0
score = "0:0" + score
elif re.search("^[0]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
# if there is a minute 10ths place 0, remove
score = score.lstrip("0")
submission.score = score
embeds = submission.get_embeds()
await submission_message.edit(
embeds=embeds,
) )
elif int_regex is None and not is_time_record: elif state == SubmissionState.PROOF and len(message.attachments) > 0:
embeds = submission_message.embeds[0] await submission_message.edit(
embeds.add_field(name="Error", value="Number record must only be number.") files=[await attch.to_file() for attch in message.attachments],
else: )
score = message.content elif state == SubmissionState.NAME and message.content:
if is_time_record: submission.username = message.content
# standardize time score format.
if re.search("^[1-9]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
# score is already fine.
pass
elif re.search("^[0-9]{2}(\\.[0-9])?$", score):
# need to add leading colon and 0
score = "0:" + score
elif re.search("^[0-9](\\.[0-9])?$", score):
# need to add 0 in seconds and leading colon and 0
score = "0:0" + score
elif re.search("^[0]?[0-9]:[0-9]{2}(\\.[0-9])?$", score):
# if there is a minute 10ths place 0, remove
score = score.lstrip("0")
submission.score = score
embeds = submission.get_embeds() embeds = submission.get_embeds()
await submission_message.edit( await submission_message.edit(
embeds=embeds, embeds=embeds,
) )
elif state == SubmissionState.PROOF and len(message.attachments) > 0:
await submission_message.edit(
files=[await attch.to_file() for attch in message.attachments],
)
elif state == SubmissionState.NAME and message.content:
submission.username = message.content
embeds = submission.get_embeds()
await submission_message.edit(
embeds=embeds,
)
await message.delete() await message.delete()
# This function will be called when this extension is loaded. # This function will be called when this extension is loaded.
+23 -18
View File
@@ -5,12 +5,12 @@ from typing import Optional
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import aliased from sqlalchemy.orm import Session, aliased
# pylint: disable=import-error # pylint: disable=import-error
from classes.submission import Submission, SubmissionState from classes.submission import Submission, SubmissionState
from utilities.sql_alchemy_connection import Category, Guild, CategoryGroup, session
from highscores import submission_objects from highscores import submission_objects
from utilities.sql_data_classes import Category, CategoryGroup, Guild, engine
from .verification_view import VerificationView from .verification_view import VerificationView
@@ -18,12 +18,14 @@ from .verification_view import VerificationView
def category_select_options(group_id: int) -> "list[nextcord.SelectOption]": def category_select_options(group_id: int) -> "list[nextcord.SelectOption]":
"""Returns list of categories for a given boss.""" """Returns list of categories for a given boss."""
options: "list[nextcord.SelectOption]" = [] options: "list[nextcord.SelectOption]" = []
subq = select(CategoryGroup).where(CategoryGroup.GroupId == group_id).subquery()
cat_group_alias = aliased(CategoryGroup, subq) with Session(engine) as session:
stmt = select(Category).where(Category.GroupId == cat_group_alias.GroupId) subq = select(CategoryGroup).where(CategoryGroup.GroupId == group_id).subquery()
categories_list = session.scalars(stmt) cat_group_alias = aliased(CategoryGroup, subq)
for category in categories_list: stmt = select(Category).where(Category.GroupId == cat_group_alias.GroupId)
options.append(nextcord.SelectOption(label=category.CategoryName, value=str(category.CategoryId))) categories_list = session.scalars(stmt)
for category in categories_list:
options.append(nextcord.SelectOption(label=category.CategoryName, value=str(category.CategoryId)))
return options return options
@@ -110,8 +112,9 @@ class SubmitButton(nextcord.ui.Button):
self._bot = bot self._bot = bot
async def callback(self, interaction: nextcord.Interaction): async def callback(self, interaction: nextcord.Interaction):
stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id) with Session(engine) as session:
verification_channel_id = session.scalar(stmt) stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id)
verification_channel_id = session.scalar(stmt)
channel = interaction.guild.get_channel(verification_channel_id) channel = interaction.guild.get_channel(verification_channel_id)
submission = submission_objects.get_user_submission(interaction.user.id) submission = submission_objects.get_user_submission(interaction.user.id)
@@ -148,20 +151,22 @@ class SubmissionCreateButton(nextcord.ui.View):
self, button: nextcord.ui.Button, interaction: nextcord.Interaction self, button: nextcord.ui.Button, interaction: nextcord.Interaction
): # pylint: disable=unused-argument ): # pylint: disable=unused-argument
"""Button for score submission.""" """Button for score submission."""
# Don't allow a user to start a new submission if one already exists # Don't allow a user to start a new submission if one already exists
if submission_objects.has_user(interaction.user.id): if submission_objects.has_user(interaction.user.id):
await interaction.send("Please finish in progress submission first.", ephemeral=True) await interaction.send("Please finish in progress submission first.", ephemeral=True)
return return
stmt = ( with Session(engine) as session:
select(CategoryGroup.GroupId) stmt = (
.where(CategoryGroup.GuildId == interaction.guild_id) select(CategoryGroup.GroupId)
.where(CategoryGroup.MessageId == interaction.message.id) .where(CategoryGroup.GuildId == interaction.guild_id)
) .where(CategoryGroup.MessageId == interaction.message.id)
group_id = session.scalar(stmt) )
group_id = session.scalar(stmt)
stmt = select(Guild.SubmissionChannel).where(Guild.GuildId == interaction.guild_id) stmt = select(Guild.SubmissionChannel).where(Guild.GuildId == interaction.guild_id)
channel = interaction.guild.get_channel(session.scalar(stmt)) channel = interaction.guild.get_channel(session.scalar(stmt))
# a user should only have 1 submission active at once. # a user should only have 1 submission active at once.
# split username on pipe. Decided to store whole username... # split username on pipe. Decided to store whole username...
+2 -1
View File
@@ -9,7 +9,8 @@ import sys
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
import utilities.config as config # pylint: disable=import-error
from utilities import config
from classes.submission import SubmissionMap from classes.submission import SubmissionMap
logger = logging.getLogger("nextcord") logger = logging.getLogger("nextcord")
@@ -3,17 +3,15 @@ Simple file to set up the sqlalchemy connection to database.
""" """
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.ext.automap import automap_base from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
import utilities.config as config # pylint: disable=import-error
from utilities import config
# connect with sql alchemy and autoload schema # connect with sql alchemy and autoload schema
url = f"mysql://{config.SQL_USER}:{config.SQL_PASS}@{config.SQL_HOST}/{config.SQL_DB}" url = f"mysql://{config.SQL_USER}:{config.SQL_PASS}@{config.SQL_HOST}/{config.SQL_DB}"
engine = create_engine(url) engine = create_engine(url)
connection = engine.connect()
Base = automap_base() Base = automap_base()
Base.prepare(autoload_with=engine) Base.prepare(autoload_with=engine)
session = Session(engine)
# define the classes # define the classes
Guild = Base.classes.Guilds Guild = Base.classes.Guilds
+98 -84
View File
@@ -8,10 +8,10 @@ import time
import nextcord import nextcord
import pandas as pd import pandas as pd
from sqlalchemy import distinct, select from sqlalchemy import distinct, select
from sqlalchemy.orm import aliased from sqlalchemy.orm import Session, aliased
# pylint: disable=import-error # pylint: disable=import-error
from utilities.sql_alchemy_connection import Category, CategoryGroup, Guild, Score, engine, session from utilities.sql_data_classes import Category, CategoryGroup, Guild, Score, engine
MAX_LINE_LENGTH = 142 MAX_LINE_LENGTH = 142
@@ -30,16 +30,17 @@ def autofill_categories(guild_id: int, group_id: int, category_begin: str) -> "d
dict[str, str]: List of all category names that start with category_begin. dict[str, str]: List of all category names that start with category_begin.
""" """
# search database for all category names with Session(engine) as session:
subq = ( # search database for all category names
select(CategoryGroup) subq = (
.where(CategoryGroup.GuildId == guild_id) select(CategoryGroup)
.where(CategoryGroup.GroupId == group_id) .where(CategoryGroup.GuildId == guild_id)
.subquery() .where(CategoryGroup.GroupId == group_id)
) .subquery()
cat_group_alias = aliased(CategoryGroup, subq) )
stmt = select(Category).where(Category.GroupId == cat_group_alias.GroupId) cat_group_alias = aliased(CategoryGroup, subq)
categories_dict: dict = {cat.CategoryName: str(cat.CategoryId) for cat in session.scalars(stmt)} stmt = select(Category).where(Category.GroupId == cat_group_alias.GroupId)
categories_dict: dict = {cat.CategoryName: str(cat.CategoryId) for cat in session.scalars(stmt)}
# if string empty, return all category names # if string empty, return all category names
if not category_begin: if not category_begin:
@@ -65,11 +66,12 @@ def autofill_groups(guild_id: int, group_begin: str) -> "dict[str, str]":
dict[str, str]: Dict of group names and ids where group name start with group_begin. Key is name, value is id. dict[str, str]: Dict of group names and ids where group name start with group_begin. Key is name, value is id.
""" """
# Select all groups in guild. with Session(engine) as session:
stmt = select(CategoryGroup).where(CategoryGroup.GuildId == guild_id) # Select all groups in guild.
stmt = select(CategoryGroup).where(CategoryGroup.GuildId == guild_id)
# dict has to have key values of same type for discord. # dict has to have key values of same type for discord.
group_dict: dict = {group.GroupName: str(group.GroupId) for group in session.scalars(stmt)} group_dict: dict = {group.GroupName: str(group.GroupId) for group in session.scalars(stmt)}
# if string empty, return all group names # if string empty, return all group names
if not group_begin: if not group_begin:
@@ -98,32 +100,33 @@ def autofill_users_in_group_category(guild_id: int, group_id: int, category_id:
list[str]: List of users that start with user_begin. list[str]: List of users that start with user_begin.
""" """
# get all categories in the group with Session(engine) as session:
subq = ( # get all categories in the group
select(CategoryGroup)
.where(CategoryGroup.GuildId == guild_id)
.where(CategoryGroup.GroupId == group_id)
.subquery()
)
category_group = aliased(CategoryGroup, subq)
# If no category id, get all users in group.
if category_id == 0:
subq = select(Category.CategoryId).where(Category.GroupId == category_group.GroupId)
stmt = select(distinct(Score.UserName)).where(Score.CategoryId.in_(subq))
else:
# get all users in category_id
subq = ( subq = (
select(Category) select(CategoryGroup)
.where(Category.GroupId == category_group.GroupId) .where(CategoryGroup.GuildId == guild_id)
.where(Category.CategoryId == category_id) .where(CategoryGroup.GroupId == group_id)
.subquery() .subquery()
) )
category = aliased(Category, subq) category_group = aliased(CategoryGroup, subq)
stmt = select(distinct(Score.UserName)).where(Score.CategoryId == category.CategoryId)
users_list: "list[str]" = list(session.scalars(stmt)) # If no category id, get all users in group.
if category_id == 0:
subq = select(Category.CategoryId).where(Category.GroupId == category_group.GroupId)
stmt = select(distinct(Score.UserName)).where(Score.CategoryId.in_(subq))
else:
# get all users in category_id
subq = (
select(Category)
.where(Category.GroupId == category_group.GroupId)
.where(Category.CategoryId == category_id)
.subquery()
)
category = aliased(Category, subq)
stmt = select(distinct(Score.UserName)).where(Score.CategoryId == category.CategoryId)
users_list: "list[str]" = list(session.scalars(stmt))
# if string empty, return all users # if string empty, return all users
if not user_begin: if not user_begin:
@@ -146,10 +149,12 @@ def autofill_users_in_guild(guild_id: int, user_begin: str) -> "list[str]":
Returns: Returns:
list[str]: All users with submissions in the guild. list[str]: All users with submissions in the guild.
""" """
group_ids = select(CategoryGroup.GroupId).where(CategoryGroup.GuildId == guild_id)
category_ids = select(Category.CategoryId).where(Category.GroupId.in_(group_ids)) with Session(engine) as session:
stmt = select(distinct(Score.UserName)).where(Score.CategoryId.in_(category_ids)) group_ids = select(CategoryGroup.GroupId).where(CategoryGroup.GuildId == guild_id)
users_list: "list[str]" = list(session.scalars(stmt)) category_ids = select(Category.CategoryId).where(Category.GroupId.in_(group_ids))
stmt = select(distinct(Score.UserName)).where(Score.CategoryId.in_(category_ids))
users_list: "list[str]" = list(session.scalars(stmt))
# if string empty, return all users # if string empty, return all users
if not user_begin: if not user_begin:
@@ -161,18 +166,20 @@ def autofill_users_in_guild(guild_id: int, user_begin: str) -> "list[str]":
return users return users
def format_highscore_message(group: CategoryGroup) -> str: def format_highscore_message(session: Session, group: CategoryGroup) -> str:
""" """
Creates a formatted string for a boss's highscore discord message. Creates a formatted string for a boss's highscore discord message.
This formatted string is in discord's code block to ensure monospace text. This formatted string is in discord's code block to ensure monospace text.
Loops through each category to build the message one place at a time. Loops through each category to build the message one place at a time.
Args: Args:
group (group): group object that contains the contents of the message. session (Session): the sqlalchemy session used for this function call.
group (CategoryGroup): group object that contains the contents of the message.
Returns: Returns:
str: Formatted string for discord message str: Formatted string for discord message
""" """
ret_string = f"**{group.GroupName}**\n```" ret_string = f"**{group.GroupName}**\n```"
stmt = select(Guild).where(Guild.GuildId == group.GuildId) stmt = select(Guild).where(Guild.GuildId == group.GuildId)
@@ -291,19 +298,23 @@ def get_time_str(time_in: float) -> str:
return time.strftime(f"%-M:%S.{milliseconds}", time.gmtime(time_in)) return time.strftime(f"%-M:%S.{milliseconds}", time.gmtime(time_in))
async def send_highscore_message(channel: nextcord.TextChannel, group: CategoryGroup) -> nextcord.Message: async def send_highscore_message(
session: Session, channel: nextcord.TextChannel, group: CategoryGroup
) -> nextcord.Message:
""" """
Function takes care of checking if a message exists for a group. Function takes care of checking if a message exists for a group.
Creates and sends/edits the message. Creates and sends/edits the message.
Args: Args:
session (Session): The sqlalchemy session being used for this function call.
channel (nextcord.TextChannel): Discord channel object where message is sent channel (nextcord.TextChannel): Discord channel object where message is sent
group (CategoryGroup): group to post to the message group (CategoryGroup): group to post to the message
Returns: Returns:
nextcord.Message: Message object that was posted with group contents nextcord.Message: Message object that was posted with group contents
""" """
highscore_string = format_highscore_message(group)
highscore_string = format_highscore_message(session, group)
try: try:
message: nextcord.Message = await channel.fetch_message(group.MessageId) message: nextcord.Message = await channel.fetch_message(group.MessageId)
@@ -332,37 +343,38 @@ async def submit_score(
score (str): Score to submit. Need to convert to correct type. score (str): Score to submit. Need to convert to correct type.
""" """
stmt = select(Category.IsTimeRecord).where(Category.CategoryId == category_id) with Session(engine) as session:
is_time_record = session.scalar(stmt) stmt = select(Category.IsTimeRecord).where(Category.CategoryId == category_id)
is_time_record = session.scalar(stmt)
if is_time_record: if is_time_record:
minutes = float(score.split(":", maxsplit=1)[0]) minutes = float(score.split(":", maxsplit=1)[0])
seconds = float(score.split(":", maxsplit=1)[1]) seconds = float(score.split(":", maxsplit=1)[1])
score = (minutes * 60) + seconds score = (minutes * 60) + seconds
else: else:
score = float(score) score = float(score)
stmt = select(Score).where(Score.UserName == username).where(Score.CategoryId == category_id) stmt = select(Score).where(Score.UserName == username).where(Score.CategoryId == category_id)
existing_score = session.scalar(stmt) existing_score = session.scalar(stmt)
if existing_score: if existing_score:
existing_score.Score = score existing_score.Score = score
else: else:
score_obj = Score(UserName=username, Score=score, CategoryId=category_id) score_obj = Score(UserName=username, Score=score, CategoryId=category_id)
session.add(score_obj) session.add(score_obj)
session.commit() session.commit()
stmt = select(CategoryGroup).where(CategoryGroup.GroupId == group_id) stmt = select(CategoryGroup).where(CategoryGroup.GroupId == group_id)
group = session.scalar(stmt) group = session.scalar(stmt)
# edit message # edit message
channel = interaction.guild.get_channel(group.ChannelId) channel = interaction.guild.get_channel(group.ChannelId)
error_response = "Registered Highscores channel does not exist or was never registered." error_response = "Registered Highscores channel does not exist or was never registered."
error_response += 'Register with "/register" command.' error_response += 'Register with "/register" command.'
assert channel is not None, await interaction.send(error_response, ephemeral=True) assert channel is not None, await interaction.send(error_response, ephemeral=True)
await send_highscore_message(channel, group) await send_highscore_message(session, channel, group)
def add_preset_configuration(guild_id: int, config_str: str) -> None: def add_preset_configuration(guild_id: int, config_str: str) -> None:
@@ -374,19 +386,21 @@ def add_preset_configuration(guild_id: int, config_str: str) -> None:
guild_id (int): Guild id to add config to guild_id (int): Guild id to add config to
config_str (str): Config name to add config_str (str): Config name to add
""" """
with open(f"example_config/{config_str}.json", encoding="utf-8", mode="r") as file:
config = json.load(file)
for group_name, categorys in config["tables"].items(): with Session(engine) as session:
group_obj = CategoryGroup(GroupName=group_name, GuildId=guild_id) with open(f"example_config/{config_str}.json", encoding="utf-8", mode="r") as file:
session.add(group_obj) config = json.load(file)
for category_name in categorys:
cat_obj = Category(
CategoryName=category_name,
Size=config["table_size"],
IsTimeRecord=config["categories"][category_name]["is_time_record"],
IsAscending=config["categories"][category_name]["ascending"],
)
session.add(cat_obj)
session.commit() for group_name, categorys in config["tables"].items():
group_obj = CategoryGroup(GroupName=group_name, GuildId=guild_id)
session.add(group_obj)
for category_name in categorys:
cat_obj = Category(
CategoryName=category_name,
Size=config["table_size"],
IsTimeRecord=config["categories"][category_name]["is_time_record"],
IsAscending=config["categories"][category_name]["ascending"],
)
session.add(cat_obj)
session.commit()