From bec64946971ff85090e6b483c307e25626f5bf97 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 19 May 2024 11:52:25 -0400 Subject: [PATCH 1/5] Rename ParticipationPoints table to EventPoints --- cogs/commands/view.py | 15 +++++---------- database_setup/database.sql | 5 +++-- database_setup/sql_data_classes.py | 2 +- utils.py | 18 +++++++++--------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/cogs/commands/view.py b/cogs/commands/view.py index b186812..c1d895a 100644 --- a/cogs/commands/view.py +++ b/cogs/commands/view.py @@ -8,12 +8,7 @@ from nextcord.ext import commands from sqlalchemy import select from sqlalchemy.orm import Session -from database_setup.sql_data_classes import ( - ParticipationPoints, - Presences, - Votes, - engine, -) +from database_setup.sql_data_classes import EventPoints, Presences, Votes, engine from utils import VoteType @@ -48,14 +43,14 @@ class ViewCog(commands.Cog, name="View Commands"): with Session(engine) as session: if quarter_name: df = pd.read_sql_query( - sql=session.query(ParticipationPoints) - .where(ParticipationPoints.Cycle.startswith(quarter_name)) + sql=session.query(EventPoints) + .where(EventPoints.Cycle.startswith(quarter_name)) .statement, con=engine, ) else: df = pd.read_sql_query( - sql=session.query(ParticipationPoints).statement, + sql=session.query(EventPoints).statement, con=engine, ) @@ -231,7 +226,7 @@ class ViewCog(commands.Cog, name="View Commands"): """ with Session(engine) as session: - stmt = select(ParticipationPoints.Cycle).distinct() + stmt = select(EventPoints.Cycle).distinct() quarters = session.scalars(stmt) # if empty compare string diff --git a/database_setup/database.sql b/database_setup/database.sql index 03ad9cb..5735865 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -41,7 +41,7 @@ CREATE TABLE Presences ( Presence varchar(100) not null ); -CREATE TABLE ParticipationPoints ( +CREATE TABLE EventPoints ( Cycle varchar(30) not null, UserId varchar(32) not null, DiscordMember boolean not null, @@ -52,4 +52,5 @@ CREATE TABLE ParticipationPoints ( CREATE TABLE Messages ( Path varchar(100) PRIMARY KEY, Id bigint unsigned not null -); \ No newline at end of file +); + diff --git a/database_setup/sql_data_classes.py b/database_setup/sql_data_classes.py index 007d432..48ebdec 100644 --- a/database_setup/sql_data_classes.py +++ b/database_setup/sql_data_classes.py @@ -15,7 +15,7 @@ Base.prepare(autoload_with=engine) CouncilMembers = Base.classes.CouncilMembers ElectionInformation = Base.classes.ElectionInformation Messages = Base.classes.Messages -ParticipationPoints = Base.classes.ParticipationPoints +EventPoints = Base.classes.EventPoints Presences = Base.classes.Presences UserBirthday = Base.classes.UserBirthday Votes = Base.classes.Votes diff --git a/utils.py b/utils.py index 3caba58..375f934 100644 --- a/utils.py +++ b/utils.py @@ -14,8 +14,8 @@ from sqlalchemy.orm import Session from config import DakinyConfig from database_setup.sql_data_classes import ( CouncilMembers, + EventPoints, Messages, - ParticipationPoints, Votes, engine, ) @@ -333,14 +333,14 @@ def record_event_participation(config: DakinyConfig, users: list[str], is_member with Session(engine) as session: for user in users: stmt = ( - select(ParticipationPoints) - .where(ParticipationPoints.UserId == user) - .where(ParticipationPoints.Cycle == cycle) + select(EventPoints) + .where(EventPoints.UserId == user) + .where(EventPoints.Cycle == cycle) ) points = session.scalar(stmt) if points is None: - new_points = ParticipationPoints( + new_points = EventPoints( Cycle=cycle, UserId=user, DiscordMember=is_member, @@ -366,14 +366,14 @@ def record_event_host(config: DakinyConfig, user: int): host_point_value = config.params.event_host_points with Session(engine) as session: stmt = ( - select(ParticipationPoints) - .where(ParticipationPoints.UserId == str(user)) - .where(ParticipationPoints.Cycle == cycle) + select(EventPoints) + .where(EventPoints.UserId == str(user)) + .where(EventPoints.Cycle == cycle) ) points = session.scalar(stmt) if points is None: - new_points = ParticipationPoints( + new_points = EventPoints( Cycle=cycle, UserId=str(user), DiscordMember=True, From c2d823991177f905d676917d8bf6ceaf0a9fa1e5 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 19 May 2024 11:59:56 -0400 Subject: [PATCH 2/5] Revert "Rename ParticipationPoints table to EventPoints" This reverts commit bec64946971ff85090e6b483c307e25626f5bf97. --- cogs/commands/view.py | 15 ++++++++++----- database_setup/database.sql | 5 ++--- database_setup/sql_data_classes.py | 2 +- utils.py | 18 +++++++++--------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/cogs/commands/view.py b/cogs/commands/view.py index c1d895a..b186812 100644 --- a/cogs/commands/view.py +++ b/cogs/commands/view.py @@ -8,7 +8,12 @@ from nextcord.ext import commands from sqlalchemy import select from sqlalchemy.orm import Session -from database_setup.sql_data_classes import EventPoints, Presences, Votes, engine +from database_setup.sql_data_classes import ( + ParticipationPoints, + Presences, + Votes, + engine, +) from utils import VoteType @@ -43,14 +48,14 @@ class ViewCog(commands.Cog, name="View Commands"): with Session(engine) as session: if quarter_name: df = pd.read_sql_query( - sql=session.query(EventPoints) - .where(EventPoints.Cycle.startswith(quarter_name)) + sql=session.query(ParticipationPoints) + .where(ParticipationPoints.Cycle.startswith(quarter_name)) .statement, con=engine, ) else: df = pd.read_sql_query( - sql=session.query(EventPoints).statement, + sql=session.query(ParticipationPoints).statement, con=engine, ) @@ -226,7 +231,7 @@ class ViewCog(commands.Cog, name="View Commands"): """ with Session(engine) as session: - stmt = select(EventPoints.Cycle).distinct() + stmt = select(ParticipationPoints.Cycle).distinct() quarters = session.scalars(stmt) # if empty compare string diff --git a/database_setup/database.sql b/database_setup/database.sql index 5735865..03ad9cb 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -41,7 +41,7 @@ CREATE TABLE Presences ( Presence varchar(100) not null ); -CREATE TABLE EventPoints ( +CREATE TABLE ParticipationPoints ( Cycle varchar(30) not null, UserId varchar(32) not null, DiscordMember boolean not null, @@ -52,5 +52,4 @@ CREATE TABLE EventPoints ( CREATE TABLE Messages ( Path varchar(100) PRIMARY KEY, Id bigint unsigned not null -); - +); \ No newline at end of file diff --git a/database_setup/sql_data_classes.py b/database_setup/sql_data_classes.py index 48ebdec..007d432 100644 --- a/database_setup/sql_data_classes.py +++ b/database_setup/sql_data_classes.py @@ -15,7 +15,7 @@ Base.prepare(autoload_with=engine) CouncilMembers = Base.classes.CouncilMembers ElectionInformation = Base.classes.ElectionInformation Messages = Base.classes.Messages -EventPoints = Base.classes.EventPoints +ParticipationPoints = Base.classes.ParticipationPoints Presences = Base.classes.Presences UserBirthday = Base.classes.UserBirthday Votes = Base.classes.Votes diff --git a/utils.py b/utils.py index 375f934..3caba58 100644 --- a/utils.py +++ b/utils.py @@ -14,8 +14,8 @@ from sqlalchemy.orm import Session from config import DakinyConfig from database_setup.sql_data_classes import ( CouncilMembers, - EventPoints, Messages, + ParticipationPoints, Votes, engine, ) @@ -333,14 +333,14 @@ def record_event_participation(config: DakinyConfig, users: list[str], is_member with Session(engine) as session: for user in users: stmt = ( - select(EventPoints) - .where(EventPoints.UserId == user) - .where(EventPoints.Cycle == cycle) + select(ParticipationPoints) + .where(ParticipationPoints.UserId == user) + .where(ParticipationPoints.Cycle == cycle) ) points = session.scalar(stmt) if points is None: - new_points = EventPoints( + new_points = ParticipationPoints( Cycle=cycle, UserId=user, DiscordMember=is_member, @@ -366,14 +366,14 @@ def record_event_host(config: DakinyConfig, user: int): host_point_value = config.params.event_host_points with Session(engine) as session: stmt = ( - select(EventPoints) - .where(EventPoints.UserId == str(user)) - .where(EventPoints.Cycle == cycle) + select(ParticipationPoints) + .where(ParticipationPoints.UserId == str(user)) + .where(ParticipationPoints.Cycle == cycle) ) points = session.scalar(stmt) if points is None: - new_points = EventPoints( + new_points = ParticipationPoints( Cycle=cycle, UserId=str(user), DiscordMember=True, From c6c44afb76f28bcb325f8fce25db6f559fc33eae Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 19 May 2024 12:05:43 -0400 Subject: [PATCH 3/5] Add table for limiting event points. --- database_setup/database.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/database_setup/database.sql b/database_setup/database.sql index 03ad9cb..652d7ca 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -52,4 +52,11 @@ CREATE TABLE ParticipationPoints ( CREATE TABLE Messages ( Path varchar(100) PRIMARY KEY, Id bigint unsigned not null +); + +CREATE TABLE EventPointsLimit ( + 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 34da664af02e8f29a165c92ee9998ad69e7b66a1 Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 19 May 2024 22:09:10 -0400 Subject: [PATCH 4/5] Add a limit to the amount of event points one can earn weekly. --- cogs/commands/record.py | 20 ++++++-- config.py | 1 + database_setup/sql_data_classes.py | 1 + utils.py | 77 ++++++++++++++---------------- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/cogs/commands/record.py b/cogs/commands/record.py index 66afef0..2e53b23 100644 --- a/cogs/commands/record.py +++ b/cogs/commands/record.py @@ -7,7 +7,7 @@ from discord import TextInputStyle from nextcord.ext import commands from config import DakinyConfig -from utils import record_event_host, record_event_participation +from utils import record_event_points class RecordCog(commands.Cog, name="Record Commands"): @@ -129,14 +129,26 @@ class EventView(nextcord.ui.View): discord_users_list: list[str] = [ str(user.id) for user in discord_users if user.id is not self.host_id ] - record_event_participation(self.config, discord_users_list, True) + record_event_points( + self.config, + discord_users_list, + True, + self.config.params.event_participate_points, + ) non_discord_users = interaction.message.embeds[0].fields[1].value if non_discord_users: non_discord_users_list: list[str] = non_discord_users.split("\n") - record_event_participation(self.config, non_discord_users_list, False) + record_event_points( + self.config, + non_discord_users_list, + False, + self.config.params.event_participate_points, + ) - record_event_host(self.config, self.host_id) + record_event_points( + self.config, [self.host_id], True, self.config.params.event_host_points + ) event_channel: nextcord.TextChannel = interaction.guild.get_channel( self.config.params.event_post_channel_id diff --git a/config.py b/config.py index 6510907..84b2144 100644 --- a/config.py +++ b/config.py @@ -21,6 +21,7 @@ class Params: event_host_points: int event_host_role_id: int event_participate_points: int + event_points_weekly_limit: int event_post_channel_id: int general_channel_id: int git_branch: str diff --git a/database_setup/sql_data_classes.py b/database_setup/sql_data_classes.py index 007d432..67411f3 100644 --- a/database_setup/sql_data_classes.py +++ b/database_setup/sql_data_classes.py @@ -14,6 +14,7 @@ Base.prepare(autoload_with=engine) # define the classes CouncilMembers = Base.classes.CouncilMembers ElectionInformation = Base.classes.ElectionInformation +EventPointsLimit = Base.classes.EventPointsLimit Messages = Base.classes.Messages ParticipationPoints = Base.classes.ParticipationPoints Presences = Base.classes.Presences diff --git a/utils.py b/utils.py index 3caba58..237c4db 100644 --- a/utils.py +++ b/utils.py @@ -5,7 +5,7 @@ File contains util functions. import os from datetime import datetime from enum import StrEnum -from typing import Iterable +from typing import Sequence import nextcord from sqlalchemy import select, true, update @@ -14,6 +14,7 @@ from sqlalchemy.orm import Session from config import DakinyConfig from database_setup.sql_data_classes import ( CouncilMembers, + EventPointsLimit, Messages, ParticipationPoints, Votes, @@ -317,19 +318,21 @@ def decorate_dm_nomination(user_id: int, speech: str) -> str: return msg -def record_event_participation(config: DakinyConfig, users: list[str], is_member: bool): +def record_event_points( + config: DakinyConfig, users: Sequence[str], is_member: bool, points: int +): """ - Adds participation 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. + Adds event 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. Args: config (DakinyConfig): Dakiny config for getting points - users (list[int]): users that participated and need points. + users (Sequence[int]): users that participated and need points. is_member (bool): whether the users are a discord member or not + points (int): number of points to add """ cycle = get_quarter_name(config) - participation_point_value = config.params.event_participate_points with Session(engine) as session: for user in users: stmt = ( @@ -344,49 +347,38 @@ def record_event_participation(config: DakinyConfig, users: list[str], is_member Cycle=cycle, UserId=user, DiscordMember=is_member, - Points=participation_point_value, + Points=points, ) session.add(new_points) else: - points.Points += participation_point_value + # check for limit + this_week = f"{datetime.today().isocalendar().year}-{datetime.today().isocalendar().week}" + stmt = ( + select(EventPointsLimit) + .where(EventPointsLimit.UserId == user) + .where(EventPointsLimit.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) + add_points = ( + points + if limit.Points + points <= config.params.event_points_weekly_limit + else ( + config.params.event_points_weekly_limit - limit.Points + if config.params.event_points_weekly_limit - limit.Points > 0 + else 0 + ) + ) + + limit.Points += add_points + points.Points += add_points session.commit() -def record_event_host(config: DakinyConfig, user: int): - """ - Adds host points to the given user. Checks if they don't exist in order to make a new entry, otherwise adds points - to the existing value. - - Args: - config (DakinyConfig): Dakiny config for getting points - user (int): Id of user that hosted. - """ - cycle = get_quarter_name(config) - host_point_value = config.params.event_host_points - with Session(engine) as session: - stmt = ( - select(ParticipationPoints) - .where(ParticipationPoints.UserId == str(user)) - .where(ParticipationPoints.Cycle == cycle) - ) - - points = session.scalar(stmt) - if points is None: - new_points = ParticipationPoints( - Cycle=cycle, - UserId=str(user), - DiscordMember=True, - Points=host_point_value, - ) - session.add(new_points) - else: - points.Points += host_point_value - - session.commit() - - -async def update_clan_messages(guild: nextcord.Guild, channel_updates: Iterable[str]): +async def update_clan_messages(guild: nextcord.Guild, channel_updates: Sequence[str]): """ Updates all clan messages to contents in MESSAGES_DIR. Does not to any git work. Is expected to be called after git is pulled. @@ -398,6 +390,7 @@ async def update_clan_messages(guild: nextcord.Guild, channel_updates: Iterable[ Args: guild (nextcord.Guild): Guild object that all channels and messages belong to. + channel_updates: (Sequence[str]): a sequence of the channels to update. """ dirs = [ From cbfd6e20bc3b46dea142fdd1be097a965f7cdf2d Mon Sep 17 00:00:00 2001 From: imcompiling Date: Sun, 19 May 2024 22:27:47 -0400 Subject: [PATCH 5/5] Move limit object management into separate function. Need to also get allowed points when creating a new quarter's event point tracker. --- utils.py | 83 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/utils.py b/utils.py index 237c4db..18d9973 100644 --- a/utils.py +++ b/utils.py @@ -319,7 +319,7 @@ def decorate_dm_nomination(user_id: int, speech: str) -> str: def record_event_points( - config: DakinyConfig, users: Sequence[str], is_member: bool, points: int + config: DakinyConfig, users: Sequence[str], is_member: bool, add_points: int ): """ Adds event points to all users listed. Checks if they don't exist in order to make a new entry, otherwise @@ -332,52 +332,83 @@ def record_event_points( points (int): number of points to add """ - cycle = get_quarter_name(config) + quarter_name = get_quarter_name(config) with Session(engine) as session: for user in users: stmt = ( select(ParticipationPoints) .where(ParticipationPoints.UserId == user) - .where(ParticipationPoints.Cycle == cycle) + .where(ParticipationPoints.Cycle == quarter_name) ) points = session.scalar(stmt) if points is None: + add_points = get_allowed_points( + session, config.params.event_points_weekly_limit, user, add_points + ) new_points = ParticipationPoints( - Cycle=cycle, + Cycle=quarter_name, UserId=user, DiscordMember=is_member, - Points=points, + Points=add_points, ) session.add(new_points) else: - # check for limit - this_week = f"{datetime.today().isocalendar().year}-{datetime.today().isocalendar().week}" - stmt = ( - select(EventPointsLimit) - .where(EventPointsLimit.UserId == user) - .where(EventPointsLimit.Week == this_week) + add_points = get_allowed_points( + session, config.params.event_points_weekly_limit, user, add_points ) - - # if under limit, can add up to however many points would make the current limit equal to the - # allowed limit. - limit = session.scalar(stmt) - add_points = ( - points - if limit.Points + points <= config.params.event_points_weekly_limit - else ( - config.params.event_points_weekly_limit - limit.Points - if config.params.event_points_weekly_limit - limit.Points > 0 - else 0 - ) - ) - - limit.Points += add_points points.Points += add_points session.commit() +def get_allowed_points( + session: Session, points_limit: int, user: str, points: int +) -> int: + """ + Gets the maximum number of points a user can be given. Searches how many points they have been given + this week and subtracts that from the weekly limit. If under, returns up to "points" number of points (will not + go over points_limit) otherwise returns 0. Updates the limit object or creates a new one if one isn't found. + + Args: + session (Session): Open connection to database. + points_limit (int): Maximum number of points a user can be given in a week. + user (str): user to be given points. Used to check a user's limit. + points (int): maximum number of points to give to user. + + Returns: + int: how many points are allowed to be given to the user. + """ + # check for limit + this_week = ( + f"{datetime.today().isocalendar().year}-{datetime.today().isocalendar().week}" + ) + stmt = ( + select(EventPointsLimit) + .where(EventPointsLimit.UserId == user) + .where(EventPointsLimit.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) + add_points = points + session.add(new_limit) + else: + add_points = ( + points + if limit.Points + points <= points_limit + else (points_limit - limit.Points if points_limit - limit.Points > 0 else 0) + ) + limit.Points += add_points + + session.commit() + + return add_points + + async def update_clan_messages(guild: nextcord.Guild, channel_updates: Sequence[str]): """ Updates all clan messages to contents in MESSAGES_DIR.