From e36b2425aa85066c2a2a1d349ed65b13dc52ddbf Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:15:09 -0400 Subject: [PATCH 01/13] Add new tables for clan points. --- database_setup/database.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/database_setup/database.sql b/database_setup/database.sql index 26e1fb9..c176a98 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -59,4 +59,19 @@ CREATE TABLE EventPointsLimit ( Points int unsigned DEFAULT 0, Week varchar(7) not null, PRIMARY KEY (UserId, Week) +); + +CREATE TABLE ClanPoints ( + QuarterName varchar(30) not null, + UserId varchar(32) not null, + DiscordMember boolean not null, + Points int unsigned DEFAULT 0, + PRIMARY KEY (QuarterName, UserId) +); + +CREATE TABLE ClanPointsLimit ( + UserId varchar(32) not null, + Points int unsigned DEFAULT 0, + Week varchar(7) not null, + PRIMARY KEY (UserId, Week) ); \ No newline at end of file From c3f25b6b549361a9f3ab2d1fe6607fa535d12433 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:18:14 -0400 Subject: [PATCH 02/13] Add settings to DakinySettings. --- DakinySettings.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DakinySettings.py b/DakinySettings.py index 5939729..e1ac9a6 100644 --- a/DakinySettings.py +++ b/DakinySettings.py @@ -45,6 +45,15 @@ class EventSettings: weekly_limit: int +@dataclass +class ClanPointSettings: + """Class defines settings for Clan Points editable by Clan Council.""" + + points: int + verify: bool + weekly_limit: int + + @dataclass class Params: """Class defines start parameters that shouldn't be changed.""" From 4c40a4c0e50ea88a8cec35166f544eff5981aa65 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:19:23 -0400 Subject: [PATCH 03/13] Reordered to alphabetical like rest of classes. --- DakinySettings.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/DakinySettings.py b/DakinySettings.py index e1ac9a6..32b879d 100644 --- a/DakinySettings.py +++ b/DakinySettings.py @@ -19,6 +19,15 @@ class ChannelIds: voting: int +@dataclass +class ClanPointSettings: + """Class defines settings for Clan Points editable by Clan Council.""" + + points: int + verify: bool + weekly_limit: int + + @dataclass class Election: """Class defines relevant election dates.""" @@ -45,15 +54,6 @@ class EventSettings: weekly_limit: int -@dataclass -class ClanPointSettings: - """Class defines settings for Clan Points editable by Clan Council.""" - - points: int - verify: bool - weekly_limit: int - - @dataclass class Params: """Class defines start parameters that shouldn't be changed.""" From a01503c2c31183e2c2c6516801abddebb530cb6e Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:21:07 -0400 Subject: [PATCH 04/13] Add new sql tables to sql data classes --- database_setup/sql_data_classes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/database_setup/sql_data_classes.py b/database_setup/sql_data_classes.py index abbdd8c..91501e2 100644 --- a/database_setup/sql_data_classes.py +++ b/database_setup/sql_data_classes.py @@ -10,11 +10,13 @@ Base = automap_base() Base.prepare(autoload_with=engine) # define the classes +ClanPoints = Base.classes.ClanPoints +ClanPointsLimit = Base.classes.ClanPointsLimit CouncilMembers = Base.classes.CouncilMembers ElectionInformation = Base.classes.ElectionInformation +EventPoints = Base.classes.EventPoints EventPointsLimit = Base.classes.EventPointsLimit Messages = Base.classes.Messages -EventPoints = Base.classes.EventPoints Presences = Base.classes.Presences UserBirthday = Base.classes.UserBirthday Votes = Base.classes.Votes From 50d10f51689cba45dba4fb44c19749de1051c815 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:42:31 -0400 Subject: [PATCH 05/13] Rework point settings to combine clan and event points into same class for simplicity. --- DakinySettings.py | 34 ++++++++++++++-------------------- cogs/commands/record.py | 12 ++++++------ utils.py | 4 ++-- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/DakinySettings.py b/DakinySettings.py index 32b879d..e0655bb 100644 --- a/DakinySettings.py +++ b/DakinySettings.py @@ -19,15 +19,6 @@ class ChannelIds: voting: int -@dataclass -class ClanPointSettings: - """Class defines settings for Clan Points editable by Clan Council.""" - - points: int - verify: bool - weekly_limit: int - - @dataclass class Election: """Class defines relevant election dates.""" @@ -44,16 +35,6 @@ class Election: voting_start: str -@dataclass -class EventSettings: - """Class defines settings editable by Clan Council.""" - - host_points: int - participate_points: int - verify: bool - weekly_limit: int - - @dataclass class Params: """Class defines start parameters that shouldn't be changed.""" @@ -63,6 +44,19 @@ class Params: guild_id: int +@dataclass +class PointSettings: + """Class defines point settings editable by Clan Council.""" + + clan_points: int + clan_points_weekly_limit: int + events_weekly_limit: int + host_points: int + participate_points: int + verify_clan_points: bool + verify_events: bool + + @dataclass class RoleIds: """Class holds all role ids needed by bot.""" @@ -80,8 +74,8 @@ class DakinySettings(YAMLWizard): channel_ids: ChannelIds election_cycles: list[Election] - event_settings: EventSettings params: Params + point_settings: PointSettings role_ids: RoleIds def save(self) -> None: diff --git a/cogs/commands/record.py b/cogs/commands/record.py index 487d9f0..0b13003 100644 --- a/cogs/commands/record.py +++ b/cogs/commands/record.py @@ -119,14 +119,14 @@ class RecordCog(commands.Cog, name="Record Commands"): elif recipient is not None: # noqa: RET505 record_event_points( [str(recipient.id)], - settings.event_settings.participate_points, + settings.point_settings.participate_points, is_member=True, ) recipient_str = f"<@{recipient.id}>" elif non_discord_recipient is not None: record_event_points( [non_discord_recipient], - settings.event_settings.participate_points, + settings.point_settings.participate_points, is_member=False, ) recipient_str = f'"{non_discord_recipient}"' @@ -180,7 +180,7 @@ class EventView(nextcord.ui.View): Removes view and embed from recording message. Sends point information to database. """ - if settings.event_settings.verify: + if settings.point_settings.verify_events: verification_channel: nextcord.TextChannel = interaction.guild.get_channel(settings.channel_ids.admin) verification_view = EventVerification( self.event_participants.values.members, interaction.message.embeds[0].fields[1].value, self.host_id @@ -339,7 +339,7 @@ def process_event(discord_users: list[nextcord.Member], non_discord_users: str, discord_users_list: list[str] = [str(user.id) for user in discord_users if user.id is not host_id] record_event_points( discord_users_list, - settings.event_settings.participate_points, + settings.point_settings.participate_points, is_member=True, ) @@ -347,8 +347,8 @@ def process_event(discord_users: list[nextcord.Member], non_discord_users: str, non_discord_users_list: list[str] = non_discord_users.split("\n") record_event_points( non_discord_users_list, - settings.event_settings.participate_points, + settings.point_settings.participate_points, is_member=False, ) - record_event_points([host_id], settings.event_settings.host_points, is_member=True) + record_event_points([host_id], settings.point_settings.host_points, is_member=True) diff --git a/utils.py b/utils.py index e63846b..5bdf3af 100644 --- a/utils.py +++ b/utils.py @@ -309,7 +309,7 @@ def record_event_points(users: Sequence[str], add_points: int, *, is_member: boo points = session.scalar(stmt) if points is None: - add_points = get_allowed_points(session, settings.event_settings.weekly_limit, user, add_points) + add_points = get_allowed_points(session, settings.point_settings.events_weekly_limit, user, add_points) new_points = EventPoints( QuarterName=quarter_name, UserId=user, @@ -318,7 +318,7 @@ def record_event_points(users: Sequence[str], add_points: int, *, is_member: boo ) session.add(new_points) else: - add_points = get_allowed_points(session, settings.event_settings.weekly_limit, user, add_points) + add_points = get_allowed_points(session, settings.point_settings.events_weekly_limit, user, add_points) points.Points += add_points session.commit() From e34d847364b56ea28ed8dfb180ea2bb995892159 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:49:13 -0400 Subject: [PATCH 06/13] Add all settings commands for clan points. --- cogs/commands/settings.py | 90 ++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 16 deletions(-) diff --git a/cogs/commands/settings.py b/cogs/commands/settings.py index 73aafe3..7a90b8e 100644 --- a/cogs/commands/settings.py +++ b/cogs/commands/settings.py @@ -144,10 +144,67 @@ class SettingsCog(commands.Cog, name="Settings Commands"): await interaction.response.send_message(f"Voting channel set to <#{channel_id}>.", ephemeral=True) @settings.subcommand() - async def event_settings(self, interaction: nextcord.Interaction) -> None: + async def point_settings(self, interaction: nextcord.Interaction) -> None: """Subcommand parent. Never called.""" - @event_settings.subcommand(description="Sets Event Host Points value.") + @point_settings.subcommand(description="Sets Points value.") + async def clan_points( + self, + interaction: nextcord.Interaction, + points: int = nextcord.SlashOption( + required=True, description="Number of Clan Points the recipient earns.", min_value=0 + ), + ) -> None: + """ + Sets PointSettings.points. + + Args: + interaction (nextcord.Interaction): Discord interaction of command. + points (int): Number of points to assign to PointSettings.points. + """ + settings.point_settings.clan_points = points + settings.save() + await interaction.response.send_message(f"Points set to {points}.", ephemeral=True) + + @point_settings.subcommand(description="Sets Event Weekly Limit value for Clan Points.") + async def clan_points_weekly_limit( + self, + interaction: nextcord.Interaction, + points: int = nextcord.SlashOption( + required=True, description="Number of Clan Points a clan member can earn in a week.", min_value=0 + ), + ) -> None: + """ + Sets DakinySettings event_settings.weekly_limit to the given number of points. + + Args: + interaction (nextcord.Interaction): Discord interaction of command. + points (int): Number of points to set. + """ + settings.point_settings.clan_points_weekly_limit = points + settings.save() + await interaction.response.send_message(f"Clan Points Weekly Limit set to {points}.", ephemeral=True) + + @point_settings.subcommand(description="Sets Event Weekly Limit value.") + async def events_weekly_limit( + self, + interaction: nextcord.Interaction, + points: int = nextcord.SlashOption( + required=True, description="Number of Event Points a clan member can earn in a week.", min_value=0 + ), + ) -> None: + """ + Sets DakinySettings event_settings.weekly_limit to the given number of points. + + Args: + interaction (nextcord.Interaction): Discord interaction of command. + points (int): Number of points to set. + """ + settings.point_settings.events_weekly_limit = points + settings.save() + await interaction.response.send_message(f"Event Weekly Limit set to {points}.", ephemeral=True) + + @point_settings.subcommand(description="Sets Event Host Points value.") async def host_points( self, interaction: nextcord.Interaction, @@ -162,11 +219,11 @@ class SettingsCog(commands.Cog, name="Settings Commands"): interaction (nextcord.Interaction): Discord interaction of command. points (int): Number of points to set. """ - settings.event_settings.host_points = points + settings.point_settings.host_points = points settings.save() await interaction.response.send_message(f"Event Host Points set to {points}.", ephemeral=True) - @event_settings.subcommand(description="Sets Event Participation Points value.") + @point_settings.subcommand(description="Sets Event Participation Points value.") async def participate_points( self, interaction: nextcord.Interaction, @@ -183,12 +240,12 @@ class SettingsCog(commands.Cog, name="Settings Commands"): interaction (nextcord.Interaction): Discord interaction of command. points (int): Number of points to set. """ - settings.event_settings.participate_points = points + settings.point_settings.participate_points = points settings.save() await interaction.response.send_message(f"Event Participation Points set to {points}.", ephemeral=True) - @event_settings.subcommand(description="Sets Event Participation Points value.") - async def verify( + @point_settings.subcommand(description="Sets whether to verify a clan point entry.") + async def verify_clan_points( self, interaction: nextcord.Interaction, do_verification: bool = nextcord.SlashOption( # noqa: FBT001 @@ -203,28 +260,29 @@ class SettingsCog(commands.Cog, name="Settings Commands"): interaction (nextcord.Interaction): Discord interaction of command. do_verification (bool): If the bot should verify events. """ - settings.event_settings.verify = do_verification + settings.point_settings.verify_clan_points = do_verification settings.save() await interaction.response.send_message(f"Event verification set to {do_verification}.", ephemeral=True) - @event_settings.subcommand(description="Sets Event Weekly Limit value.") - async def weekly_limit( + @point_settings.subcommand(description="Sets whether an event.") + async def verify_events( self, interaction: nextcord.Interaction, - points: int = nextcord.SlashOption( - required=True, description="Number of Event Points a clan member can earn in a week.", min_value=0 + do_verification: bool = nextcord.SlashOption( # noqa: FBT001 + required=True, + description="Whether an event should be verified or not.", ), ) -> None: """ - Sets DakinySettings event_settings.weekly_limit to the given number of points. + Sets DakinySettings event_settings.verify to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. - points (int): Number of points to set. + do_verification (bool): If the bot should verify events. """ - settings.event_settings.weekly_limit = points + settings.point_settings.verify_events = do_verification settings.save() - await interaction.response.send_message(f"Event Weekly Limit set to {points}.", ephemeral=True) + await interaction.response.send_message(f"Event verification set to {do_verification}.", ephemeral=True) @settings.subcommand() async def role_ids(self, interaction: nextcord.Interaction) -> None: From be46edd4eab4522290e1cd187a6d877486c93c93 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Fri, 20 Sep 2024 21:52:00 -0400 Subject: [PATCH 07/13] Updated docustrings. --- cogs/commands/settings.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cogs/commands/settings.py b/cogs/commands/settings.py index 7a90b8e..de3914c 100644 --- a/cogs/commands/settings.py +++ b/cogs/commands/settings.py @@ -156,7 +156,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets PointSettings.points. + Sets DakinySettings point_settings.clan_points to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -175,7 +175,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.weekly_limit to the given number of points. + Sets DakinySettings point_settings.clan_points_weekly_limit to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -194,7 +194,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.weekly_limit to the given number of points. + Sets DakinySettings point_settings.events_weekly_limit to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -213,7 +213,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.host_points to the given number of points. + Sets DakinySettings point_settings.host_points to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -234,7 +234,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.participate_points to the given number of points. + Sets DakinySettings point_settings.participate_points to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -254,7 +254,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.verify to the given number of points. + Sets DakinySettings point_settings.verify_clan_points to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. @@ -274,7 +274,7 @@ class SettingsCog(commands.Cog, name="Settings Commands"): ), ) -> None: """ - Sets DakinySettings event_settings.verify to the given number of points. + Sets DakinySettings point_settings.verify_events to the given number of points. Args: interaction (nextcord.Interaction): Discord interaction of command. From 8498f42ba01f50535f60786e6c99fd7b2ebd4fbc Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sat, 5 Oct 2024 14:16:01 -0400 Subject: [PATCH 08/13] update ruff version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 94b1c0b..15859a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dataclass-wizard = "^0.22.3" [tool.poetry.group.dev.dependencies] -ruff = "^0.6.3" +ruff = "^0.6.9" [build-system] requires = ["poetry-core"] From a081718f8dfaa0ead5573e8456a46380795af28b Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 22 Dec 2024 14:41:50 -0500 Subject: [PATCH 09/13] Combine event and clan points into one table. --- database_setup/database.sql | 25 ++++++------------------- database_setup/sql_data_classes.py | 6 ++---- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/database_setup/database.sql b/database_setup/database.sql index c176a98..f8da5b9 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -41,11 +41,12 @@ CREATE TABLE Presences ( Presence varchar(100) not null ); -CREATE TABLE EventPoints ( +CREATE TABLE Points ( QuarterName varchar(30) not null, UserId varchar(32) not null, DiscordMember boolean not null, - Points int unsigned DEFAULT 0, + EventPoints int unsigned DEFAULT 0, + ClanPoints in unsigned DEFAULT 0, PRIMARY KEY (QuarterName, UserId) ); @@ -54,24 +55,10 @@ CREATE TABLE Messages ( Id bigint unsigned not null ); -CREATE TABLE EventPointsLimit ( +CREATE TABLE PointsLimit ( UserId varchar(32) not null, - Points int unsigned DEFAULT 0, - Week varchar(7) not null, - PRIMARY KEY (UserId, Week) -); - -CREATE TABLE ClanPoints ( - QuarterName varchar(30) not null, - UserId varchar(32) not null, - DiscordMember boolean not null, - Points int unsigned DEFAULT 0, - PRIMARY KEY (QuarterName, UserId) -); - -CREATE TABLE ClanPointsLimit ( - UserId varchar(32) not null, - Points int unsigned DEFAULT 0, + EventPoints int unsigned DEFAULT 0, + ClanPoints in unsigned DEFAULT 0, Week varchar(7) not null, PRIMARY KEY (UserId, Week) ); \ No newline at end of file diff --git a/database_setup/sql_data_classes.py b/database_setup/sql_data_classes.py index 91501e2..aaf4d59 100644 --- a/database_setup/sql_data_classes.py +++ b/database_setup/sql_data_classes.py @@ -10,12 +10,10 @@ Base = automap_base() Base.prepare(autoload_with=engine) # define the classes -ClanPoints = Base.classes.ClanPoints -ClanPointsLimit = Base.classes.ClanPointsLimit +Points = Base.classes.Points +PointsLimit = Base.classes.PointsLimit CouncilMembers = Base.classes.CouncilMembers ElectionInformation = Base.classes.ElectionInformation -EventPoints = Base.classes.EventPoints -EventPointsLimit = Base.classes.EventPointsLimit Messages = Base.classes.Messages Presences = Base.classes.Presences UserBirthday = Base.classes.UserBirthday From 1bae3204460283b8c8f3026f132d278e99a89fae Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 22 Dec 2024 15:10:47 -0500 Subject: [PATCH 10/13] Fix in typos. --- database_setup/database.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database_setup/database.sql b/database_setup/database.sql index f8da5b9..7a7ff25 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -46,7 +46,7 @@ CREATE TABLE Points ( UserId varchar(32) not null, DiscordMember boolean not null, EventPoints int unsigned DEFAULT 0, - ClanPoints in unsigned DEFAULT 0, + ClanPoints int unsigned DEFAULT 0, PRIMARY KEY (QuarterName, UserId) ); @@ -58,7 +58,7 @@ CREATE TABLE Messages ( CREATE TABLE PointsLimit ( UserId varchar(32) not null, EventPoints int unsigned DEFAULT 0, - ClanPoints in unsigned DEFAULT 0, + ClanPoints int unsigned DEFAULT 0, Week varchar(7) not null, PRIMARY KEY (UserId, Week) ); \ No newline at end of file From 906b63d57f210d50f373f61d8a8ca30d0c5355bf Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 22 Dec 2024 15:13:35 -0500 Subject: [PATCH 11/13] Update view event points for new table name. --- cogs/commands/view.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cogs/commands/view.py b/cogs/commands/view.py index 65ff0c2..0620e4a 100644 --- a/cogs/commands/view.py +++ b/cogs/commands/view.py @@ -7,7 +7,7 @@ from sqlalchemy import select from sqlalchemy.orm import Session from database_setup.sql_data_classes import ( - EventPoints, + Points, Presences, Votes, engine, @@ -49,12 +49,12 @@ class ViewCog(commands.Cog, name="View Commands"): with Session(engine) as session: if quarter_name: ppdf = pd.read_sql_query( - sql=session.query(EventPoints).where(EventPoints.QuarterName.startswith(quarter_name)).statement, + sql=session.query(Points).where(Points.QuarterName.startswith(quarter_name)).statement, con=engine, ) else: ppdf = pd.read_sql_query( - sql=session.query(EventPoints).statement, + sql=session.query(Points).statement, con=engine, ) @@ -83,7 +83,7 @@ class ViewCog(commands.Cog, name="View Commands"): sorted_all = pd.concat([sorted_members, sorted_non_members], ignore_index=True, sort=False) for row in sorted_all.itertuples(): userid = row.UserId - points = row.Points + points = row.EventPoints discord_member = row.DiscordMember content += f"<@{userid}>: {points}\n" if discord_member else f"{userid}: {points}\n" content += "\n" @@ -201,7 +201,7 @@ class ViewCog(commands.Cog, name="View Commands"): quarter_name (str): String to run autocomplete on. """ with Session(engine) as session: - stmt = select(EventPoints.QuarterName).distinct() + stmt = select(Points.QuarterName).distinct() quarters = session.scalars(stmt) # if empty compare string From a56b47a5fa1eb408d975ef181ce4019d06f606e8 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 22 Dec 2024 15:34:14 -0500 Subject: [PATCH 12/13] Revamped utils to handle clan and event points in one. Updated record as needed. --- cogs/commands/record.py | 20 ++++++++------ utils.py | 59 ++++++++++++++++++++++++++--------------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/cogs/commands/record.py b/cogs/commands/record.py index 0b13003..069fb13 100644 --- a/cogs/commands/record.py +++ b/cogs/commands/record.py @@ -5,7 +5,7 @@ from discord import TextInputStyle from nextcord.ext import commands from DakinySettings import settings -from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, record_event_points +from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, record_points class RecordCog(commands.Cog, name="Record Commands"): @@ -117,16 +117,18 @@ class RecordCog(commands.Cog, name="Record Commands"): await interaction.response.send_message("Please only provide 1 recipient.", ephemeral=True) return elif recipient is not None: # noqa: RET505 - record_event_points( + record_points( [str(recipient.id)], - settings.point_settings.participate_points, + settings.point_settings.clan_points, + PointType.CLAN, is_member=True, ) recipient_str = f"<@{recipient.id}>" elif non_discord_recipient is not None: - record_event_points( + record_points( [non_discord_recipient], - settings.point_settings.participate_points, + settings.point_settings.clan_points, + PointType.CLAN, is_member=False, ) recipient_str = f'"{non_discord_recipient}"' @@ -337,18 +339,20 @@ def process_event(discord_users: list[nextcord.Member], non_discord_users: str, """ if discord_users: discord_users_list: list[str] = [str(user.id) for user in discord_users if user.id is not host_id] - record_event_points( + record_points( discord_users_list, settings.point_settings.participate_points, + PointType.EVENT, is_member=True, ) if non_discord_users: non_discord_users_list: list[str] = non_discord_users.split("\n") - record_event_points( + record_points( non_discord_users_list, settings.point_settings.participate_points, + PointType.EVENT, is_member=False, ) - record_event_points([host_id], settings.point_settings.host_points, is_member=True) + record_points([host_id], settings.point_settings.host_points, PointType.EVENT, is_member=True) diff --git a/utils.py b/utils.py index 66fda0d..fc21c61 100644 --- a/utils.py +++ b/utils.py @@ -15,9 +15,9 @@ from sqlalchemy.orm import Session from DakinySettings import settings from database_setup.sql_data_classes import ( CouncilMembers, - EventPoints, - EventPointsLimit, Messages, + Points, + PointsLimit, Votes, engine, ) @@ -30,6 +30,7 @@ MAX_DISCORD_MESSAGE_LENGTH = 2000 GUILDS = [1000247361639030804, 239758371358638080] VoteType = StrEnum("VoteType", ["COUNCIL", "DISCORDMANAGER", "CLANLEADER"]) +PointType = StrEnum("PointType", ["CLAN", "EVENT"]) def get_datetime(day: str) -> datetime: @@ -290,41 +291,46 @@ def decorate_dm_nomination(user_id: int, speech: str) -> str: return msg -def record_event_points(users: Sequence[str], add_points: int, *, is_member: bool) -> None: +def record_points(users: Sequence[str], add_points: int, point_type: PointType, *, is_member: bool) -> None: """ - Adds event points to all users listed. + Adds points to all users listed. Checks if they don't exist in order to make a new entry, otherwise - adds points to the existing value. Can be used for hosting or participating. + adds points to the existing value. Can be used for hosting or participating in events or for the clan + points. Args: users (Sequence[int]): users that participated and need points. add_points (int): number of points to add + point_type (PointType): which point type to add to is_member (bool): whether the users are a discord member or not """ quarter_name = get_quarter_name() with Session(engine) as session: for user in users: - stmt = select(EventPoints).where(EventPoints.UserId == user).where(EventPoints.QuarterName == quarter_name) + stmt = select(Points).where(Points.UserId == user).where(Points.QuarterName == quarter_name) - points = session.scalar(stmt) - if points is None: - add_points = get_allowed_points(session, settings.point_settings.events_weekly_limit, user, add_points) - new_points = EventPoints( + points_obj = session.scalar(stmt) + add_points = get_allowed_points(session, point_type, user, add_points) + add_clan_points = add_points if point_type == PointType.CLAN else 0 + add_event_points = add_points if point_type == PointType.EVENT else 0 + if points_obj is None: + new_points = Points( QuarterName=quarter_name, UserId=user, DiscordMember=is_member, - Points=add_points, + EventPoints=add_event_points, + ClanPoints=add_clan_points, ) session.add(new_points) else: - add_points = get_allowed_points(session, settings.point_settings.events_weekly_limit, user, add_points) - points.Points += add_points + points_obj.EventPoints += add_event_points + points_obj.ClanPoints += add_clan_points session.commit() -def get_allowed_points(session: Session, points_limit: int, user: str, points: int) -> int: +def get_allowed_points(session: Session, point_type: PointType, user: str, points: int) -> int: """ Gets the maximum number of points a user can be given. @@ -334,7 +340,7 @@ def get_allowed_points(session: Session, points_limit: int, user: str, points: i Args: session (Session): Open connection to database. - points_limit (int): Maximum number of points a user can be given in a week. + point_type (PointType): Which type of points to check the limit for. user (str): user to be given points. Used to check a user's limit. points (int): maximum number of points to give to user. @@ -344,18 +350,29 @@ def get_allowed_points(session: Session, points_limit: int, user: str, points: i # check for limit today = datetime.now(tz=UTC).date() this_week = f"{today.isocalendar().year}-{today.isocalendar().week}" - stmt = select(EventPointsLimit).where(EventPointsLimit.UserId == user).where(EventPointsLimit.Week == this_week) + points_limit = ( + settings.point_settings.events_weekly_limit + if point_type == PointType.EVENT + else settings.point_settings.clan_points_weekly_limit + ) + stmt = select(PointsLimit).where(PointsLimit.UserId == user).where(PointsLimit.Week == this_week) # if under limit, can add up to however many points would make the current limit equal to the # allowed limit. - limit = session.scalar(stmt) - if limit is None: - new_limit = EventPointsLimit(UserId=user, Points=points, Week=this_week) + limit_obj = session.scalar(stmt) + if limit_obj is None: + clan_points = points if point_type == PointType.CLAN else 0 + event_points = points if point_type == PointType.EVENT else 0 + new_limit = PointsLimit(UserId=user, Week=this_week, EventPoints=event_points, ClanPoints=clan_points) add_points = points session.add(new_limit) else: - add_points = points if limit.Points + points <= points_limit else (max(0, points_limit - limit.Points)) - limit.Points += add_points + curr_points = limit_obj.EventPoints if point_type == PointType.EVENT else limit_obj.ClanPoints + add_points = points if curr_points + points <= points_limit else (max(0, points_limit - curr_points)) + if point_type == PointType.EVENT: + limit_obj.EventPoints += add_points + else: + limit_obj.ClanPoints += add_points session.commit() From c8f5c5bffb60c93725b87f9ea9241e1d138d9cc7 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 22 Dec 2024 15:48:10 -0500 Subject: [PATCH 13/13] Add view clan points command. Break out function for creating the string so that code isn't duplicated. --- cogs/commands/view.py | 111 +++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/cogs/commands/view.py b/cogs/commands/view.py index 0620e4a..3852d6e 100644 --- a/cogs/commands/view.py +++ b/cogs/commands/view.py @@ -12,7 +12,7 @@ from database_setup.sql_data_classes import ( Votes, engine, ) -from utils import MAX_DISCORD_MESSAGE_LENGTH, VoteType +from utils import GUILDS, MAX_DISCORD_MESSAGE_LENGTH, PointType, VoteType class ViewCog(commands.Cog, name="View Commands"): @@ -21,7 +21,7 @@ class ViewCog(commands.Cog, name="View Commands"): def __init__(self) -> None: """Default constructor for ViewCog class. Does nothing.""" - @nextcord.slash_command() + @nextcord.slash_command(guild_ids=GUILDS) async def view(self, interaction: nextcord.Interaction) -> None: """Parent slash command for set. Never called.""" @@ -63,30 +63,53 @@ class ViewCog(commands.Cog, name="View Commands"): return quarters = ppdf.groupby("QuarterName") - content = "" - for quarter in quarters.groups: - content += f"**{quarter}**\n" - unsorted_all = quarters.get_group(quarter).groupby("DiscordMember") - members = unsorted_all.get_group(name=True) + content = create_points_string(interaction, quarters, PointType.EVENT) - # there can be no non-discord members sometimes. - try: - sorted_non_members = unsorted_all.get_group(name=False).sort_values(by="UserId") - except KeyError: - sorted_non_members = None - sorted_members = members.sort_values( - by="UserId", - key=lambda user_ids: ( - [interaction.guild.get_member(int(user_id)).display_name.lower() for user_id in user_ids] - ), + if len(content) > MAX_DISCORD_MESSAGE_LENGTH: + await interaction.followup.send("Message length is too long, please select search to limit results.") + return + + await interaction.followup.send(content=content, ephemeral=True) + + @view.subcommand(description="Show event points.") + async def clan_points( + self, + interaction: nextcord.Interaction, + quarter_name: str = nextcord.SlashOption( + description="The quarter to view", + required=False, + ), + ) -> None: + """ + View the clan points. + + Builds a message with the clan points for the quarter that matches with quarter_name. + + Args: + interaction (nextcord.Interaction): Interaction with the message. + quarter_name (str, optional): Same of quarter to autofill with. Is SlashOption. + """ + # just in case + await interaction.response.defer(ephemeral=True) + + with Session(engine) as session: + if quarter_name: + ppdf = pd.read_sql_query( + sql=session.query(Points).where(Points.QuarterName.startswith(quarter_name)).statement, + con=engine, ) - sorted_all = pd.concat([sorted_members, sorted_non_members], ignore_index=True, sort=False) - for row in sorted_all.itertuples(): - userid = row.UserId - points = row.EventPoints - discord_member = row.DiscordMember - content += f"<@{userid}>: {points}\n" if discord_member else f"{userid}: {points}\n" - content += "\n" + else: + ppdf = pd.read_sql_query( + sql=session.query(Points).statement, + con=engine, + ) + + if ppdf.empty: + await interaction.followup.send(f'Quarter "{quarter_name}" is invalid.', ephemeral=True) + return + + quarters = ppdf.groupby("QuarterName") + content = create_points_string(interaction, quarters, PointType.CLAN) if len(content) > MAX_DISCORD_MESSAGE_LENGTH: await interaction.followup.send("Message length is too long, please select search to limit results.") @@ -213,3 +236,43 @@ class ViewCog(commands.Cog, name="View Commands"): quarter for quarter in quarters if quarter.lower().startswith(quarter_name.lower()) ] await interaction.response.send_autocomplete(autofilled_quarters[:25]) + + +def create_points_string(interaction: nextcord.Interaction, quarters: any, point_type: PointType) -> str: + """ + Creates the string to show how many clan/event points users have. + + Args: + interaction (nextcord.Interaction): Discord interaction of the command. + quarters (any): Pandas objects grouped by quarters. + point_type (PointType): The type of points to create the message for. + + Returns: + str: The string with the message content. + """ + content: str = "" + for quarter in quarters.groups: + content += f"**{quarter}**\n" + unsorted_all = quarters.get_group(quarter).groupby("DiscordMember") + members = unsorted_all.get_group(name=True) + + # there can be no non-discord members sometimes. + try: + sorted_non_members = unsorted_all.get_group(name=False).sort_values(by="UserId") + except KeyError: + sorted_non_members = None + sorted_members = members.sort_values( + by="UserId", + key=lambda user_ids: ( + [interaction.guild.get_member(int(user_id)).display_name.lower() for user_id in user_ids] + ), + ) + sorted_all = pd.concat([sorted_members, sorted_non_members], ignore_index=True, sort=False) + for row in sorted_all.itertuples(): + userid = row.UserId + points = row.EventPoints if point_type == PointType.EVENT else row.ClanPoints + discord_member = row.DiscordMember + if points > 0: + content += f"<@{userid}>: {points}\n" if discord_member else f"{userid}: {points}\n" + content += "\n" + return content