mirror of https://github.com/stijndcl/didier
Merge pull request #108 from stijndcl/compbio_commands
Add commands to track compbio leaderboardspull/136/head
commit
635560dcd0
|
@ -5,6 +5,7 @@ files/stats.json
|
||||||
files/lost.json
|
files/lost.json
|
||||||
files/locked.json
|
files/locked.json
|
||||||
files/ufora_notifications.json
|
files/ufora_notifications.json
|
||||||
|
files/compbio_benchmarks_2.json
|
||||||
.idea/
|
.idea/
|
||||||
__pycache__
|
__pycache__
|
||||||
.env
|
.env
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from discord import SlashCommandGroup
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
|
from discord.commands import slash_command, ApplicationContext, Option, AutocompleteContext
|
||||||
|
|
||||||
|
@ -5,6 +8,7 @@ from data import schedule
|
||||||
from data.courses import load_courses, find_course_from_name
|
from data.courses import load_courses, find_course_from_name
|
||||||
from data.embeds.food import Menu
|
from data.embeds.food import Menu
|
||||||
from data.embeds.deadlines import Deadlines
|
from data.embeds.deadlines import Deadlines
|
||||||
|
from data.menus import leaderboards
|
||||||
from functions import les, config
|
from functions import les, config
|
||||||
from functions.stringFormatters import capitalize
|
from functions.stringFormatters import capitalize
|
||||||
from functions.timeFormatters import skip_weekends
|
from functions.timeFormatters import skip_weekends
|
||||||
|
@ -83,6 +87,30 @@ class SchoolSlash(commands.Cog):
|
||||||
year = 2018 + int(config.get("year"))
|
year = 2018 + int(config.get("year"))
|
||||||
return await ctx.respond(f"https://studiekiezer.ugent.be/studiefiche/nl/{course.code}/{year}")
|
return await ctx.respond(f"https://studiekiezer.ugent.be/studiefiche/nl/{course.code}/{year}")
|
||||||
|
|
||||||
|
_compbio_group = SlashCommandGroup("compbio", "Commands voor compbio opdrachten")
|
||||||
|
|
||||||
|
@_compbio_group.command(name="leaderboard", description="Gesorteerd en ingevuld leaderboard")
|
||||||
|
async def _compbio_lb_slash(self, ctx: ApplicationContext):
|
||||||
|
lb = leaderboards.CompbioLeaderboard(ctx)
|
||||||
|
await lb.respond()
|
||||||
|
|
||||||
|
@_compbio_group.command(name="submit", description="Link een Dodona-submission aan jouw username")
|
||||||
|
async def _compbio_submit_slash(self, ctx: ApplicationContext,
|
||||||
|
submission: Option(int, description="Id van je Dodona indiening.", required=True)):
|
||||||
|
with open("files/compbio_benchmarks_2.json", "r") as fp:
|
||||||
|
file = json.load(fp)
|
||||||
|
|
||||||
|
submission = str(submission)
|
||||||
|
|
||||||
|
if submission in file:
|
||||||
|
return await ctx.respond("❌ Deze submission is al aan iemand gelinkt.", ephemeral=True)
|
||||||
|
|
||||||
|
with open("files/compbio_benchmarks_2.json", "w") as fp:
|
||||||
|
file[submission] = ctx.user.id
|
||||||
|
json.dump(file, fp)
|
||||||
|
|
||||||
|
return await ctx.respond(f"✅ Submission **{submission}** is aan jouw naam gelinkt.", ephemeral=True)
|
||||||
|
|
||||||
|
|
||||||
def setup(client: Didier):
|
def setup(client: Didier):
|
||||||
client.add_cog(SchoolSlash(client))
|
client.add_cog(SchoolSlash(client))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import json
|
||||||
import math
|
import math
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
@ -8,11 +9,12 @@ import requests
|
||||||
from discord import ApplicationContext
|
from discord import ApplicationContext
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
|
import settings
|
||||||
from data.menus.paginated import Paginated
|
from data.menus.paginated import Paginated
|
||||||
from enums.numbers import Numbers
|
from enums.numbers import Numbers
|
||||||
from functions import xp
|
from functions import xp
|
||||||
from functions.database import currency, stats, poke, muttn
|
from functions.database import currency, stats, poke, muttn
|
||||||
from functions.utils import get_display_name
|
from functions.utils import get_display_name, get_mention
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -21,6 +23,7 @@ class Leaderboard(Paginated, ABC):
|
||||||
colour: discord.Colour = discord.Colour.blue()
|
colour: discord.Colour = discord.Colour.blue()
|
||||||
fetch_names: bool = True
|
fetch_names: bool = True
|
||||||
ignore_non_pos: bool = True
|
ignore_non_pos: bool = True
|
||||||
|
reverse: bool = True
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.data = self.process_data(self.get_data())
|
self.data = self.process_data(self.get_data())
|
||||||
|
@ -31,7 +34,7 @@ class Leaderboard(Paginated, ABC):
|
||||||
|
|
||||||
def process_data(self, entries: list[tuple]) -> Optional[list[tuple]]:
|
def process_data(self, entries: list[tuple]) -> Optional[list[tuple]]:
|
||||||
data = []
|
data = []
|
||||||
for i, v in enumerate(sorted(entries, key=self.get_value, reverse=True)):
|
for i, v in enumerate(sorted(entries, key=self.get_value, reverse=self.reverse)):
|
||||||
entry_data = self.get_value(v)
|
entry_data = self.get_value(v)
|
||||||
|
|
||||||
# Leaderboard is empty
|
# Leaderboard is empty
|
||||||
|
@ -90,13 +93,13 @@ class Leaderboard(Paginated, ABC):
|
||||||
return await ctx.reply(embed=embed, **kwargs)
|
return await ctx.reply(embed=embed, **kwargs)
|
||||||
|
|
||||||
async def respond(self, **kwargs) -> discord.Message:
|
async def respond(self, **kwargs) -> discord.Message:
|
||||||
if self.data is None:
|
if self.data is None or not self.data:
|
||||||
return await self.empty_leaderboard(self.ctx, **kwargs)
|
return await self.empty_leaderboard(self.ctx, **kwargs)
|
||||||
|
|
||||||
return await super().respond(**kwargs)
|
return await super().respond(**kwargs)
|
||||||
|
|
||||||
async def send(self, **kwargs) -> discord.Message:
|
async def send(self, **kwargs) -> discord.Message:
|
||||||
if self.data is None:
|
if self.data is None or not self.data:
|
||||||
return await self.empty_leaderboard(self.ctx, **kwargs)
|
return await self.empty_leaderboard(self.ctx, **kwargs)
|
||||||
|
|
||||||
return await super().send(**kwargs)
|
return await super().send(**kwargs)
|
||||||
|
@ -117,6 +120,56 @@ class BitcoinLeaderboard(Leaderboard):
|
||||||
return "Er zijn nog geen personen met Bitcoins."
|
return "Er zijn nog geen personen met Bitcoins."
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CompbioLeaderboard(Leaderboard):
|
||||||
|
colour: discord.Colour = field(default=discord.Colour.green())
|
||||||
|
title: str = field(default="Leaderboard Computationele Biologie #2")
|
||||||
|
reverse: bool = False
|
||||||
|
|
||||||
|
def get_submission_user(self, submission_id: str) -> str:
|
||||||
|
with open("files/compbio_benchmarks_2.json", "r") as fp:
|
||||||
|
file = json.load(fp)
|
||||||
|
|
||||||
|
if submission_id in file:
|
||||||
|
user_id = file[submission_id]
|
||||||
|
return get_mention(self.ctx, user_id)
|
||||||
|
|
||||||
|
return f"[# {submission_id}]"
|
||||||
|
|
||||||
|
def get_data(self) -> list[tuple]:
|
||||||
|
headers = {"Authorization": f"token {settings.UGENT_GH_TOKEN}"}
|
||||||
|
result = requests.get(f"https://github.ugent.be/raw/computationele-biologie/benchmarks-2022/main/reconstruction/J02459.1.50mers.md", headers=headers).text
|
||||||
|
# Remove table headers
|
||||||
|
result = result.split("\n")[2:]
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for line in result:
|
||||||
|
try:
|
||||||
|
cells = line.split("|")
|
||||||
|
submission_id = cells[1].strip()
|
||||||
|
mean = float(cells[2].strip().split(" ")[0])
|
||||||
|
except IndexError:
|
||||||
|
# Other lines because of markdown formatting
|
||||||
|
continue
|
||||||
|
|
||||||
|
data.append((submission_id, mean, ))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def _should_highlight(self, data) -> bool:
|
||||||
|
# TODO maybe find a fix for this?
|
||||||
|
return False
|
||||||
|
|
||||||
|
def format_entry(self, index: int, data: tuple) -> str:
|
||||||
|
return f"{index + 1}: {self.get_submission_user(data[0])} ({self.format_entry_data(data)})"
|
||||||
|
|
||||||
|
def format_entry_data(self, data: tuple) -> str:
|
||||||
|
return f"{str(data[1])} ms"
|
||||||
|
|
||||||
|
def get_value(self, data: tuple):
|
||||||
|
return data[1]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CoronaLeaderboard(Leaderboard):
|
class CoronaLeaderboard(Leaderboard):
|
||||||
colour: discord.Colour = field(default=discord.Colour.red())
|
colour: discord.Colour = field(default=discord.Colour.red())
|
||||||
|
|
|
@ -45,7 +45,7 @@ def format_command_usage(ctx: Context) -> str:
|
||||||
def format_slash_command_usage(interaction: Interaction) -> str:
|
def format_slash_command_usage(interaction: Interaction) -> str:
|
||||||
# Create a string with the options used
|
# Create a string with the options used
|
||||||
options = " ".join(list(map(
|
options = " ".join(list(map(
|
||||||
lambda o: f"{o['name']}: \"{o['value']}\"",
|
lambda o: f"{o['name']}: \"{o['value']}\"" if "value" in o else o["name"],
|
||||||
interaction.data.get("options", [])
|
interaction.data.get("options", [])
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|
|
@ -4,34 +4,55 @@ import discord
|
||||||
from discord import ApplicationContext
|
from discord import ApplicationContext
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
|
|
||||||
from data import constants
|
import settings
|
||||||
|
|
||||||
|
|
||||||
def get_display_name(ctx: Union[ApplicationContext, Context], user_id: int) -> str:
|
def get_member_or_user(ctx: Union[ApplicationContext, Context], user_id: int) -> Optional[Union[discord.Member, discord.User]]:
|
||||||
|
"""Get a COC Member instance of a user if they are in the server,
|
||||||
|
otherwise the regular User instance
|
||||||
|
"""
|
||||||
author = ctx.author if isinstance(ctx, Context) else ctx.user
|
author = ctx.author if isinstance(ctx, Context) else ctx.user
|
||||||
|
|
||||||
# Check if this is a DM, or the user is not in the guild
|
# Check if this is a DM, or the user is not in the guild
|
||||||
if ctx.guild is None or ctx.guild.get_member(user_id) is None:
|
if ctx.guild is None or ctx.guild.get_member(user_id) is None:
|
||||||
# User is the author, no need to fetch their name
|
# User is the author, no need to fetch their name
|
||||||
if user_id == author.id:
|
if user_id == author.id:
|
||||||
return author.display_name
|
return author
|
||||||
|
|
||||||
# Get member instance from CoC
|
# Get member instance from CoC
|
||||||
COC = ctx.bot.get_guild(int(constants.DeZandbak))
|
COC = ctx.bot.get_guild(settings.COC_ID)
|
||||||
member = COC.get_member(user_id)
|
member = COC.get_member(user_id)
|
||||||
if member is not None:
|
if member is not None:
|
||||||
return member.display_name
|
return member
|
||||||
|
|
||||||
# Try to fetch the user
|
# Try to fetch the user
|
||||||
user = ctx.bot.get_user(user_id)
|
return ctx.bot.get_user(user_id)
|
||||||
if user is not None:
|
|
||||||
return user.name
|
|
||||||
|
|
||||||
# User couldn't be found
|
# Guild exists, use that instead
|
||||||
|
return ctx.guild.get_member(user_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_display_name(ctx: Union[ApplicationContext, Context], user_id: int) -> str:
|
||||||
|
member = get_member_or_user(ctx, user_id)
|
||||||
|
|
||||||
|
# Nothing found
|
||||||
|
if member is None:
|
||||||
return f"[? | {user_id}]"
|
return f"[? | {user_id}]"
|
||||||
|
|
||||||
mem = ctx.guild.get_member(user_id)
|
if isinstance(member, discord.Member):
|
||||||
return mem.display_name
|
return member.display_name
|
||||||
|
|
||||||
|
return member.name
|
||||||
|
|
||||||
|
|
||||||
|
def get_mention(ctx: Union[ApplicationContext, Context], user_id: int) -> str:
|
||||||
|
member = get_member_or_user(ctx, user_id)
|
||||||
|
|
||||||
|
# Nothing found
|
||||||
|
if member is None:
|
||||||
|
return f"[? | {user_id}]"
|
||||||
|
|
||||||
|
return member.mention
|
||||||
|
|
||||||
|
|
||||||
async def reply_to_reference(ctx: Context, content: Optional[str] = None, embed: Optional[discord.Embed] = None, always_mention=False):
|
async def reply_to_reference(ctx: Context, content: Optional[str] = None, embed: Optional[discord.Embed] = None, always_mention=False):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Beta version of Discord.py fork
|
# Beta version of Discord.py fork
|
||||||
py-cord==2.0.0b1
|
py-cord==2.0.0b5
|
||||||
|
|
||||||
python-dotenv==0.14.0
|
python-dotenv==0.14.0
|
||||||
beautifulsoup4==4.9.1
|
beautifulsoup4==4.9.1
|
||||||
|
|
|
@ -41,3 +41,6 @@ COC_ID: int = int(os.getenv("COC_ID", "626699611192688641"))
|
||||||
# Ex: 123,456,789
|
# Ex: 123,456,789
|
||||||
_guilds = os.getenv("SLASHTESTGUILDS", "").replace(" ", "")
|
_guilds = os.getenv("SLASHTESTGUILDS", "").replace(" ", "")
|
||||||
SLASH_TEST_GUILDS: List[int] = list(map(lambda x: int(x), _guilds.split(","))) if _guilds else None
|
SLASH_TEST_GUILDS: List[int] = list(map(lambda x: int(x), _guilds.split(","))) if _guilds else None
|
||||||
|
|
||||||
|
# GitHub token (UGent)
|
||||||
|
UGENT_GH_TOKEN = os.getenv("UGENTGHTOKEN", "")
|
||||||
|
|
Loading…
Reference in New Issue