diff --git a/classes/election_cycle.py b/classes/election_cycle.py index fe845d5..6cd6946 100644 --- a/classes/election_cycle.py +++ b/classes/election_cycle.py @@ -22,12 +22,11 @@ from database_setup.sql_data_classes import ( ) from utils import ( TEMPLATES_DIR, + VoteType, build_highcouncil_votes_str, build_votes_str, edit_clan_council_info, - get_cl_election_name, get_datetime, - get_dm_election_name, get_election_name, ) @@ -294,7 +293,11 @@ class ElectionCycle: session.commit() # add the highcouncil role to everyone with 3 or more votes. set elected to true in database - stmt = select(Votes.VotedForId).where(Votes.ElectionCycle == election_name) + stmt = ( + select(Votes.VotedForId) + .where(Votes.ElectionCycle == election_name) + .where(Votes.Type == VoteType.COUNCIL) + ) vote_count = collections.Counter(session.scalars(stmt)) for user_id, votes in vote_count.items(): @@ -358,11 +361,13 @@ class ElectionCycle: self.config.params.clan_council_role_id ) + election_cycle = get_election_name(self.config) + with Session(engine) as session: # role management stmt = ( select(CouncilMembers.MemberId) - .where(CouncilMembers.ElectionCycle == get_election_name(self.config)) + .where(CouncilMembers.ElectionCycle == election_cycle) .where(CouncilMembers.Elected == true()) ) @@ -379,14 +384,19 @@ class ElectionCycle: # announce high council members # ties are undefined behavior. - dm_election = get_dm_election_name(self.config) - cl_election = get_cl_election_name(self.config) - stmt = select(Votes.VotedForId).where(Votes.ElectionCycle == dm_election) + stmt = ( + select(Votes.VotedForId) + .where(Votes.ElectionCycle == election_cycle) + .where(Votes.Type == VoteType.DISCORDMANAGER) + ) dm_votes = collections.Counter(session.scalars(stmt)) - stmt = select(Votes.VotedForId).where(Votes.ElectionCycle == cl_election) + stmt = ( + select(Votes.VotedForId) + .where(Votes.ElectionCycle == election_cycle) + .where(Votes.Type == VoteType.CLANLEADER) + ) cl_votes = collections.Counter(session.scalars(stmt)) - election_cycle = get_election_name(self.config) try: dm_winner = max(dm_votes, key=dm_votes.get) stmt = ( @@ -425,7 +435,7 @@ class ElectionCycle: await vote_msg.edit(view=None) highcouncil_msg: str = ( - f"Congratulations for our high council members!\n Clan Leader: <@{cl_winner}>\n" + f"Congratulations for our high council members!\nClan Leader: <@{cl_winner}>\n" ) highcouncil_msg += f"Discord Manager: <@{dm_winner}>" await election_thread.send(highcouncil_msg) diff --git a/classes/voting_highcouncil_views.py b/classes/voting_highcouncil_views.py index 3899b57..f52ef5b 100644 --- a/classes/voting_highcouncil_views.py +++ b/classes/voting_highcouncil_views.py @@ -17,12 +17,7 @@ from database_setup.sql_data_classes import ( Votes, engine, ) -from utils import ( - build_highcouncil_votes_str, - get_cl_election_name, - get_dm_election_name, - get_election_name, -) +from utils import VoteType, build_highcouncil_votes_str, get_election_name class VoteSelect(nextcord.ui.StringSelect): @@ -39,20 +34,10 @@ class VoteSelect(nextcord.ui.StringSelect): self.election_type = election_type async def callback(self, interaction: Interaction): - # decide on election type - match self.election_type: - case "cl": - election_name: str = get_cl_election_name(self.config) - case "dm": - election_name: str = get_dm_election_name(self.config) - case _: - # should never happen - raise ValueError('election type was not "cl" or "dm"') - - cycle: str = get_election_name(self.config) + election_name: str = get_election_name(self.config) with Session(engine) as session: stmt = select(ElectionInformation.HighCouncilVoteMsgId).where( - ElectionInformation.ElectionCycle == cycle + ElectionInformation.ElectionCycle == election_name ) vote_msg_id = session.scalar(stmt) @@ -65,6 +50,7 @@ class VoteSelect(nextcord.ui.StringSelect): ElectionCycle=election_name, VoterId=interaction.user.id, VotedForId=voted_for, + Type=self.election_type, ) try: @@ -72,11 +58,13 @@ class VoteSelect(nextcord.ui.StringSelect): session.commit() except exc.IntegrityError: + # user already has a vote in the election and election type session.rollback() stmt = ( update(Votes) .where(Votes.ElectionCycle == election_name) .where(Votes.VoterId == interaction.user.id) + .where(Votes.Type == self.election_type) .values(VotedForId=voted_for) ) session.execute(stmt) @@ -135,20 +123,20 @@ class VoteHighcouncilView(nextcord.ui.View): ) return - cycle: str = get_election_name(self.config) - election: str = get_cl_election_name(self.config) + election_name: str = get_election_name(self.config) with Session(engine) as session: stmt = ( select(Votes.VotedForId) - .where(Votes.ElectionCycle == election) + .where(Votes.ElectionCycle == election_name) .where(Votes.VoterId == interaction.user.id) + .where(Votes.Type == VoteType.CLANLEADER) ) voted_for = session.scalar(stmt) stmt = ( select(CouncilMembers.MemberId) - .where(CouncilMembers.ElectionCycle == cycle) + .where(CouncilMembers.ElectionCycle == election_name) .filter(CouncilMembers.ClanLeaderSpeechId.is_not(None)) ) @@ -175,7 +163,9 @@ class VoteHighcouncilView(nextcord.ui.View): view = nextcord.ui.View() view.add_item( - VoteSelect(options=options, config=self.config, election_type="cl") + VoteSelect( + options=options, config=self.config, election_type=VoteType.CLANLEADER + ) ) await interaction.response.send_message( @@ -213,20 +203,20 @@ class VoteHighcouncilView(nextcord.ui.View): ) return - cycle: str = get_election_name(self.config) - election: str = get_dm_election_name(self.config) + election_name: str = get_election_name(self.config) with Session(engine) as session: stmt = ( select(Votes.VotedForId) - .where(Votes.ElectionCycle == election) + .where(Votes.ElectionCycle == election_name) .where(Votes.VoterId == interaction.user.id) + .where(Votes.Type == VoteType.DISCORDMANAGER) ) voted_for = session.scalar(stmt) stmt = ( select(CouncilMembers.MemberId) - .where(CouncilMembers.ElectionCycle == cycle) + .where(CouncilMembers.ElectionCycle == election_name) .filter(CouncilMembers.DiscordManagerSpeechId.is_not(None)) ) @@ -253,7 +243,11 @@ class VoteHighcouncilView(nextcord.ui.View): view = nextcord.ui.View() view.add_item( - VoteSelect(options=options, config=self.config, election_type="dm") + VoteSelect( + options=options, + config=self.config, + election_type=VoteType.DISCORDMANAGER, + ) ) await interaction.response.send_message( diff --git a/classes/voting_views.py b/classes/voting_views.py index 6d1694d..cdfc8ca 100644 --- a/classes/voting_views.py +++ b/classes/voting_views.py @@ -17,7 +17,7 @@ from database_setup.sql_data_classes import ( Votes, engine, ) -from utils import build_votes_str, get_election_name +from utils import VoteType, build_votes_str, get_election_name class VoteSelect(nextcord.ui.StringSelect): @@ -47,6 +47,7 @@ class VoteSelect(nextcord.ui.StringSelect): ElectionCycle=election_name, VoterId=interaction.user.id, VotedForId=voted_for, + Type=VoteType.COUNCIL, ) try: @@ -59,6 +60,7 @@ class VoteSelect(nextcord.ui.StringSelect): update(Votes) .where(Votes.ElectionCycle == election_name) .where(Votes.VoterId == interaction.user.id) + .where(Votes.Type == VoteType.COUNCIL) .values(VotedForId=voted_for) ) session.execute(stmt) @@ -121,6 +123,7 @@ class VoteView(nextcord.ui.View): select(Votes.VotedForId) .where(Votes.ElectionCycle == election) .where(Votes.VoterId == interaction.user.id) + .where(Votes.Type == VoteType.COUNCIL) ) voted_for = session.scalar(stmt) diff --git a/cogs/commands/view.py b/cogs/commands/view.py index dcf9c6d..b186812 100644 --- a/cogs/commands/view.py +++ b/cogs/commands/view.py @@ -14,6 +14,7 @@ from database_setup.sql_data_classes import ( Votes, engine, ) +from utils import VoteType class ViewCog(commands.Cog, name="View Commands"): @@ -148,6 +149,11 @@ class ViewCog(commands.Cog, name="View Commands"): election_name: str = nextcord.SlashOption( required=True, description="The election you want to view votes for." ), + election_type: str = nextcord.SlashOption( + required=True, + description="The election type. Council, Discord Manager, or Clan Leader.", + choices=VoteType, + ), ): """ Takes in an election name and sends a response message to the user with a list of people who voted in that @@ -159,7 +165,11 @@ class ViewCog(commands.Cog, name="View Commands"): election_name (str): Is SlashOption. The name of the election to view votes for. """ - stmt = select(Votes).where(Votes.ElectionCycle == election_name) + stmt = ( + select(Votes) + .where(Votes.ElectionCycle == election_name) + .where(Votes.Type == election_type) + ) with Session(engine) as session: # Check if election exists. diff --git a/database_setup/database.sql b/database_setup/database.sql index bab1494..03ad9cb 100644 --- a/database_setup/database.sql +++ b/database_setup/database.sql @@ -25,8 +25,9 @@ CREATE TABLE Votes( ElectionCycle varchar(30) not null, VoterId bigint unsigned not null, VotedForId bigint unsigned, + Type varchar(14) not null DEFAULT 'council' check("Type" in ('council', 'discordmanager', 'clanleader')), FOREIGN KEY (ElectionCycle) REFERENCES ElectionInformation(ElectionCycle), - PRIMARY KEY (ElectionCycle, VoterId) + PRIMARY KEY (ElectionCycle, VoterId, Type) ); CREATE TABLE UserBirthday ( diff --git a/utils.py b/utils.py index 75539cc..3caba58 100644 --- a/utils.py +++ b/utils.py @@ -4,6 +4,7 @@ File contains util functions. import os from datetime import datetime +from enum import StrEnum from typing import Iterable import nextcord @@ -24,6 +25,8 @@ REPO_DIR = "./dakiny-messages" MESSAGES_DIR = f"{REPO_DIR}/messages" TEMPLATES_DIR = f"{REPO_DIR}/message_templates" +VoteType = StrEnum("VoteType", ["COUNCIL", "DISCORDMANAGER", "CLANLEADER"]) + def get_datetime(day: str) -> datetime: """ @@ -82,34 +85,6 @@ def get_quarter_name(config: DakinyConfig) -> str: return f"{get_datetime(current_cycle.term_start).year}-{current_cycle.name}" -def get_dm_election_name(config: DakinyConfig) -> str: - """ - creates the name of the discord manager election - - Args: - config (DakinyConfig): Config sent to get_election_cycle_name - - Returns: - str: the name of the election - """ - - return f"{get_election_name(config)}-dm" - - -def get_cl_election_name(config: DakinyConfig) -> str: - """ - creates the name of the clan leader election - - Args: - config (DakinyConfig): Config sent to get_election_cycle_name - - Returns: - str: the name of the election - """ - - return f"{get_election_name(config)}-cl" - - def get_current_election_cycle(config: DakinyConfig) -> int: """ Returns index for active election. An election is considered active between the election start and the term start. @@ -170,9 +145,11 @@ def build_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> str: votes: dict[int, int] = {} + election_name = get_election_name(config) + with Session(engine) as session: stmt = select(CouncilMembers).where( - CouncilMembers.ElectionCycle == get_election_name(config) + CouncilMembers.ElectionCycle == election_name ) for nominee in sorted( @@ -181,7 +158,11 @@ def build_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> str: ): votes[nominee.MemberId] = 0 - stmt = select(Votes).where(Votes.ElectionCycle == get_election_name(config)) + stmt = ( + select(Votes) + .where(Votes.ElectionCycle == election_name) + .where(Votes.Type == VoteType.COUNCIL) + ) for vote in session.scalars(stmt): if vote.VotedForId is not None: @@ -208,10 +189,12 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> cl_votes: dict[int, int] = {} dm_votes: dict[int, int] = {} + election_name = get_election_name(config) + with Session(engine) as session: stmt = ( select(CouncilMembers) - .where(CouncilMembers.ElectionCycle == get_election_name(config)) + .where(CouncilMembers.ElectionCycle == election_name) .filter(CouncilMembers.ClanLeaderSpeechId.is_not(None)) ) @@ -221,7 +204,11 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> ): cl_votes[nominee.MemberId] = 0 - stmt = select(Votes).where(Votes.ElectionCycle == get_cl_election_name(config)) + stmt = ( + select(Votes) + .where(Votes.ElectionCycle == election_name) + .where(Votes.Type == VoteType.CLANLEADER) + ) for vote in session.scalars(stmt): if vote.VotedForId is not None: @@ -229,7 +216,7 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> stmt = ( select(CouncilMembers) - .where(CouncilMembers.ElectionCycle == get_election_name(config)) + .where(CouncilMembers.ElectionCycle == election_name) .filter(CouncilMembers.DiscordManagerSpeechId.is_not(None)) ) @@ -239,7 +226,11 @@ def build_highcouncil_votes_str(config: DakinyConfig, guild: nextcord.Guild) -> ): dm_votes[nominee.MemberId] = 0 - stmt = select(Votes).where(Votes.ElectionCycle == get_dm_election_name(config)) + stmt = ( + select(Votes) + .where(Votes.ElectionCycle == election_name) + .where(Votes.Type == VoteType.DISCORDMANAGER) + ) for vote in session.scalars(stmt): if vote.VotedForId is not None: