didier/didier/data/embeds/ufora/announcements.py

101 lines
3.5 KiB
Python

from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional
from zoneinfo import ZoneInfo
import discord
from markdownify import markdownify as md
from database.schemas import UforaCourse
from didier.data.embeds.base import EmbedBaseModel
from didier.utils.discord.colours import ghent_university_blue
from didier.utils.types.datetime import LOCAL_TIMEZONE, int_to_weekday
from didier.utils.types.string import leading
__all__ = [
"UforaNotification",
]
@dataclass
class UforaNotification(EmbedBaseModel):
"""A single notification from Ufora"""
content: dict
course: UforaCourse
notification_id: Optional[int] = None
course_id: Optional[int] = None
_view_url: str = field(init=False)
_title: str = field(init=False)
_description: str = field(init=False)
published_dt: datetime = field(init=False)
_published: str = field(init=False)
def __post_init__(self):
self._view_url = self._create_url()
self._title = self._clean_content(self.content["title"])
self._description = self._get_description()
self.published_dt = self._published_datetime()
self._published = self._get_published()
def to_embed(self, **kwargs) -> discord.Embed:
"""Turn the notification into an embed"""
embed = discord.Embed(title=self._title, colour=ghent_university_blue())
embed.set_author(name=self.course.name)
embed.title = self._title
embed.url = self._view_url
embed.description = self._description
embed.set_footer(text=self._published)
return embed
def get_id(self) -> int:
"""Parse the id out of the notification"""
return int(self.notification_id) if self.notification_id is not None else self.content["id"]
def _create_url(self):
if self.notification_id is None or self.course_id is None:
return self.content["link"]
return f"https://ufora.ugent.be/d2l/le/news/{self.course_id}/{self.notification_id}/view?ou={self.course_id}"
def _get_description(self):
desc = self._clean_content(self.content["summary"])
if len(desc) > 4096:
return desc[:4093] + "..."
return desc
def _clean_content(self, text: str):
# Escape *-characters because they mess up the layout
text = text.replace("*", "\\*")
return md(text)
def _published_datetime(self) -> datetime:
"""Get a datetime instance of the publication date"""
# Datetime is unable to parse the timezone because it's useless
# We will hereby cut it out and pray the timezone will always be UTC+0
published = self.content["published"].rsplit(" ", 1)[0]
time_string = "%a, %d %b %Y %H:%M:%S"
dt = datetime.strptime(published, time_string).replace(tzinfo=ZoneInfo("GMT")).astimezone(LOCAL_TIMEZONE)
return dt
def _get_published(self) -> str:
"""Get a formatted string that represents when this announcement was published"""
return (
f"{int_to_weekday(self.published_dt.weekday())} "
f"{leading('0', str(self.published_dt.day))}"
"/"
f"{leading('0', str(self.published_dt.month))}"
"/"
f"{self.published_dt.year} "
f"{leading('0', str(self.published_dt.hour))}"
":"
f"{leading('0', str(self.published_dt.minute))}"
":"
f"{leading('0', str(self.published_dt.second))}"
)