diff --git a/didier/cogs/currency.py b/didier/cogs/currency.py index f7204a8..05446f0 100644 --- a/didier/cogs/currency.py +++ b/didier/cogs/currency.py @@ -27,13 +27,10 @@ class Currency(commands.Cog): @commands.command(name="Award") @commands.check(is_owner) - async def award( - self, - ctx: commands.Context, - user: discord.User, - amount: typing.Annotated[int, abbreviated_number], - ): + async def award(self, ctx: commands.Context, user: discord.User, amount: abbreviated_number): # type: ignore """Award a user a given amount of Didier Dinks""" + amount = typing.cast(int, amount) + async with self.client.postgres_session as session: await crud.add_dinks(session, user.id, amount) plural = pluralize("Didier Dink", amount) @@ -48,7 +45,8 @@ class Currency(commands.Cog): async with self.client.postgres_session as session: bank = await crud.get_bank(session, ctx.author.id) - embed = discord.Embed(title=f"{ctx.author.display_name}'s Bank", colour=discord.Colour.blue()) + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name=f"{ctx.author.display_name}'s Bank") embed.set_thumbnail(url=ctx.author.avatar.url) embed.add_field(name="Interest level", value=bank.interest_level) @@ -63,7 +61,8 @@ class Currency(commands.Cog): async with self.client.postgres_session as session: bank = await crud.get_bank(session, ctx.author.id) - embed = discord.Embed(title="Bank upgrades", colour=discord.Colour.blue()) + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name="Bank upgrades") embed.add_field( name=f"Interest ({bank.interest_level})", value=str(interest_upgrade_price(bank.interest_level)) @@ -119,8 +118,10 @@ class Currency(commands.Cog): await ctx.reply(f"**{ctx.author.display_name}** has **{bank.dinks}** {plural}.", mention_author=False) @commands.command(name="Invest", aliases=["Deposit", "Dep"]) - async def invest(self, ctx: commands.Context, amount: typing.Annotated[typing.Union[str, int], abbreviated_number]): + async def invest(self, ctx: commands.Context, amount: abbreviated_number): # type: ignore """Invest a given amount of Didier Dinks""" + amount = typing.cast(typing.Union[str, int], amount) + async with self.client.postgres_session as session: invested = await crud.invest(session, ctx.author.id, amount) plural = pluralize("Didier Dink", invested) diff --git a/didier/cogs/help.py b/didier/cogs/help.py index 73c66e5..77ae0f1 100644 --- a/didier/cogs/help.py +++ b/didier/cogs/help.py @@ -10,12 +10,13 @@ from didier import Didier class CustomHelpCommand(commands.MinimalHelpCommand): """Customised Help command to override the default implementation - The default is ugly as hell, so we do some fiddling with it + The default is ugly as hell so we do some fiddling with it """ def _help_embed_base(self, title: str) -> discord.Embed: """Create the base structure for the embeds that get sent with the Help commands""" - embed = discord.Embed(title=title, colour=discord.Colour.blue()) + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name=title) embed.set_footer(text="Syntax: Didier Help [Categorie] of Didier Help [Commando]") return embed diff --git a/didier/cogs/school.py b/didier/cogs/school.py index f8a0d1b..460f2b2 100644 --- a/didier/cogs/school.py +++ b/didier/cogs/school.py @@ -1,6 +1,3 @@ -from datetime import date -from typing import Optional - import discord from discord import app_commands from discord.ext import commands @@ -8,11 +5,7 @@ from discord.ext import commands from database.crud import ufora_courses from database.crud.deadlines import get_deadlines from didier import Didier -from didier.data.apis.hydra import fetch_menu from didier.data.embeds.deadlines import Deadlines -from didier.data.embeds.hydra import no_menu_found -from didier.exceptions import HTTPException -from didier.utils.discord.converters.time import DateTransformer from didier.utils.discord.flags.school import StudyGuideFlags @@ -33,25 +26,6 @@ class School(commands.Cog): embed = Deadlines(deadlines).to_embed() await ctx.reply(embed=embed, mention_author=False, ephemeral=False) - @commands.hybrid_command( - name="menu", - description="Show the menu in the Ghent University restaurants.", - aliases=["Eten", "Food"], - ) - @app_commands.rename(day_dt="date") - async def menu(self, ctx: commands.Context, day_dt: Optional[app_commands.Transform[date, DateTransformer]] = None): - """Show the menu in the Ghent University restaurants. - - Menus are Dutch, as a lot of dishes have very weird translations - """ - async with ctx.typing(): - try: - menu = await fetch_menu(self.client.http_session, day_dt) - embed = menu.to_embed(day_dt=day_dt) - except HTTPException: - embed = no_menu_found(day_dt) - await ctx.reply(embed=embed, mention_author=False) - @commands.hybrid_command( name="fiche", description="Sends the link to the study guide for [Course]", aliases=["guide", "studiefiche"] ) diff --git a/didier/data/apis/hydra.py b/didier/data/apis/hydra.py deleted file mode 100644 index 620e0df..0000000 --- a/didier/data/apis/hydra.py +++ /dev/null @@ -1,15 +0,0 @@ -from datetime import date - -from aiohttp import ClientSession - -from didier.data.embeds.hydra import Menu -from didier.utils.http.requests import ensure_get - -__all__ = ["fetch_menu"] - - -async def fetch_menu(http_session: ClientSession, day_dt: date) -> Menu: - """Fetch the menu for a given day""" - endpoint = f"https://hydra.ugent.be/api/2.0/resto/menu/nl/{day_dt.year}/{day_dt.month}/{day_dt.day}.json" - async with ensure_get(http_session, endpoint, log_exceptions=False) as response: - return Menu.parse_obj(response) diff --git a/didier/data/embeds/base.py b/didier/data/embeds/base.py index 4b32224..45be7a0 100644 --- a/didier/data/embeds/base.py +++ b/didier/data/embeds/base.py @@ -13,7 +13,7 @@ class EmbedBaseModel(ABC): """Abstract base class for a model that can be turned into a Discord embed""" @abstractmethod - def to_embed(self, **kwargs) -> discord.Embed: + def to_embed(self, **kwargs: dict) -> discord.Embed: """Turn this model into a Discord embed""" raise NotImplementedError diff --git a/didier/data/embeds/deadlines.py b/didier/data/embeds/deadlines.py index 371eee9..6f46bbd 100644 --- a/didier/data/embeds/deadlines.py +++ b/didier/data/embeds/deadlines.py @@ -22,8 +22,9 @@ class Deadlines(EmbedBaseModel): self.deadlines.sort(key=lambda deadline: deadline.deadline) @overrides - def to_embed(self, **kwargs) -> discord.Embed: - embed = discord.Embed(title="Upcoming Deadlines", colour=discord.Colour.dark_gold()) + def to_embed(self, **kwargs: dict) -> discord.Embed: + embed = discord.Embed(colour=discord.Colour.dark_gold()) + embed.set_author(name="Upcoming Deadlines") now = tz_aware_now() has_active_deadlines = False diff --git a/didier/data/embeds/error_embed.py b/didier/data/embeds/error_embed.py index 9118c6d..0dc1b80 100644 --- a/didier/data/embeds/error_embed.py +++ b/didier/data/embeds/error_embed.py @@ -37,7 +37,8 @@ def create_error_embed(ctx: commands.Context, exception: Exception) -> discord.E invocation = f"{ctx.author.display_name} in {origin}" - embed = discord.Embed(title="Error", colour=discord.Colour.red()) + embed = discord.Embed(colour=discord.Colour.red()) + embed.set_author(name="Error") embed.add_field(name="Command", value=f"{ctx.message.content}", inline=True) embed.add_field(name="Context", value=invocation, inline=True) embed.add_field(name="Exception", value=abbreviate(str(exception), Limits.EMBED_FIELD_VALUE_LENGTH), inline=False) diff --git a/didier/data/embeds/google/google_search.py b/didier/data/embeds/google/google_search.py index dd0383b..dcbad9c 100644 --- a/didier/data/embeds/google/google_search.py +++ b/didier/data/embeds/google/google_search.py @@ -19,7 +19,8 @@ class GoogleSearch(EmbedBaseModel): def _error_embed(self) -> discord.Embed: """Custom embed for unsuccessful requests""" - embed = discord.Embed(title="Google Search", colour=discord.Colour.red()) + embed = discord.Embed(colour=discord.Colour.red()) + embed.set_author(name="Google Search") # Empty embed if not self.data.results: @@ -32,11 +33,12 @@ class GoogleSearch(EmbedBaseModel): return embed @overrides - def to_embed(self, **kwargs) -> discord.Embed: + def to_embed(self, **kwargs: dict) -> discord.Embed: if not self.data.results or self.data.status_code != HTTPStatus.OK: return self._error_embed() - embed = discord.Embed(title="Google Search", colour=discord.Colour.blue()) + embed = discord.Embed(colour=discord.Colour.blue()) + embed.set_author(name="Google Search") embed.set_footer(text=self.data.result_stats or None) # Add all results into the description diff --git a/didier/data/embeds/hydra/__init__.py b/didier/data/embeds/hydra/__init__.py deleted file mode 100644 index 798cd46..0000000 --- a/didier/data/embeds/hydra/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .menu import Menu, no_menu_found - -__all__ = ["Menu", "no_menu_found"] diff --git a/didier/data/embeds/hydra/menu.py b/didier/data/embeds/hydra/menu.py deleted file mode 100644 index 8d1e8d4..0000000 --- a/didier/data/embeds/hydra/menu.py +++ /dev/null @@ -1,126 +0,0 @@ -from datetime import date -from enum import Enum -from typing import Optional, cast - -import discord -from overrides import overrides -from pydantic import BaseModel - -from didier.data.embeds.base import EmbedPydantic -from didier.utils.discord.colours import ghent_university_blue, ghent_university_yellow -from didier.utils.types.datetime import int_to_weekday -from didier.utils.types.string import leading - -__all__ = ["Menu", "no_menu_found"] - - -class _MealKind(str, Enum): - FISH = "fish" - MEAT = "meat" - SOUP = "soup" - VEGAN = "vegan" - VEGETARIAN = "vegetarian" - - -class _MealType(str, Enum): - COLD = "cold" - MAIN = "main" - SIDE = "side" - - -class _Meal(BaseModel): - """Model for an item on the menu""" - - kind: _MealKind - name: str - price: str - type: _MealType - - -class Menu(EmbedPydantic): - """Embed that shows the menu in Ghent University restaurants""" - - meals: list[_Meal] = [] - open: bool - vegetables: list[str] = [] - message: Optional[str] = None - - def _get_dutch_meal_prefix(self, meal: _Meal) -> str: - if meal.kind == _MealKind.MEAT: - prefix = "Vlees" - elif meal.kind == _MealKind.FISH: - prefix = "Vis" - elif meal.kind == _MealKind.VEGETARIAN: - prefix = "Vegetarisch" - else: - prefix = "Vegan" - - return prefix - - def _get_soups(self) -> str: - acc = "" - - for meal in self.meals: - if meal.kind == _MealKind.SOUP: - acc += f"{meal.name} ({meal.price})\n" - - return acc.strip() - - def _get_main_courses(self) -> str: - acc = "" - - for meal in self.meals: - if meal.type != _MealType.MAIN: - continue - - prefix = self._get_dutch_meal_prefix(meal) - - acc += f"* {prefix}: {meal.name} ({meal.price})\n" - - return acc.strip() - - def _get_cold_meals(self) -> str: - acc = "" - - for meal in self.meals: - if meal.type == _MealType.COLD: - acc += f"* {self._get_dutch_meal_prefix(meal)}: {meal.name} ({meal.price})\n" - - return acc.strip() - - def _closed_embed(self, embed: discord.Embed) -> discord.Embed: - embed.colour = ghent_university_yellow() - embed.description = "The restaurants are closed today." - return embed - - def _regular_embed(self, embed: discord.Embed) -> discord.Embed: - embed.add_field(name="🥣 Soep", value=self._get_soups(), inline=False) - embed.add_field(name="🍴 Hoofdgerechten", value=self._get_main_courses(), inline=False) - embed.add_field(name="❄️Koud", value=self._get_cold_meals(), inline=False) - - vegetables = "\n".join(list(sorted(self.vegetables))) - embed.add_field(name="🥦 Groenten", value=vegetables, inline=False) - - return embed - - @overrides - def to_embed(self, **kwargs) -> discord.Embed: - day_dt: date = cast(date, kwargs.get("day_dt")) - weekday = int_to_weekday(day_dt.weekday()) - formatted_date = f"{leading('0', str(day_dt.day))}/{leading('0', str(day_dt.month))}/{day_dt.year}" - - embed = discord.Embed(title=f"Menu - {weekday} {formatted_date}", colour=ghent_university_blue()) - - embed = self._regular_embed(embed) if self.open else self._closed_embed(embed) - - if self.message: - embed.add_field(name="📣 Extra Mededeling", value=self.message, inline=False) - - return embed - - -def no_menu_found(day_dt: date) -> discord.Embed: - """Return a different embed if no menu could be found""" - embed = discord.Embed(title="Menu", colour=discord.Colour.red()) - embed.description = f"Unable to retrieve menu for {day_dt.strftime('%d/%m/%Y')}." - return embed diff --git a/didier/data/embeds/ufora/announcements.py b/didier/data/embeds/ufora/announcements.py index 59e8290..a766e79 100644 --- a/didier/data/embeds/ufora/announcements.py +++ b/didier/data/embeds/ufora/announcements.py @@ -15,7 +15,6 @@ import settings from database.crud import ufora_announcements as crud from database.schemas.relational import UforaCourse from didier.data.embeds.base import EmbedBaseModel -from didier.utils.discord.colours import ghent_university_blue from didier.utils.types.datetime import int_to_weekday from didier.utils.types.string import leading @@ -48,9 +47,9 @@ class UforaNotification(EmbedBaseModel): self.published_dt = self._published_datetime() self._published = self._get_published() - def to_embed(self, **kwargs) -> discord.Embed: + def to_embed(self, **kwargs: dict) -> discord.Embed: """Turn the notification into an embed""" - embed = discord.Embed(title=self._title, colour=ghent_university_blue()) + embed = discord.Embed(colour=discord.Colour.from_rgb(30, 100, 200)) embed.set_author(name=self.course.name) embed.title = self._title diff --git a/didier/data/embeds/urban_dictionary.py b/didier/data/embeds/urban_dictionary.py index 3126fed..afaf4c5 100644 --- a/didier/data/embeds/urban_dictionary.py +++ b/didier/data/embeds/urban_dictionary.py @@ -46,8 +46,9 @@ class Definition(EmbedPydantic): return string_utils.abbreviate(field, max_length=Limits.EMBED_FIELD_VALUE_LENGTH) @overrides - def to_embed(self, **kwargs) -> discord.Embed: - embed = discord.Embed(title="Urban Dictionary", colour=colours.urban_dictionary_green()) + def to_embed(self, **kwargs: dict) -> discord.Embed: + embed = discord.Embed(colour=colours.urban_dictionary_green()) + embed.set_author(name="Urban Dictionary") embed.add_field(name="Term", value=self.word, inline=True) embed.add_field(name="Author", value=self.author, inline=True) diff --git a/didier/data/embeds/wordle.py b/didier/data/embeds/wordle.py index d29a29f..44439c0 100644 --- a/didier/data/embeds/wordle.py +++ b/didier/data/embeds/wordle.py @@ -126,7 +126,7 @@ class WordleErrorEmbed(EmbedBaseModel): message: str @overrides - def to_embed(self, **kwargs) -> discord.Embed: + def to_embed(self, **kwargs: dict) -> discord.Embed: embed = discord.Embed(colour=discord.Colour.red(), title="Wordle") embed.description = self.message embed.set_footer(text=footer()) diff --git a/didier/utils/discord/autocompletion/__init__.py b/didier/utils/discord/autocompletion/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/didier/utils/discord/autocompletion/time.py b/didier/utils/discord/autocompletion/time.py deleted file mode 100644 index b5003fd..0000000 --- a/didier/utils/discord/autocompletion/time.py +++ /dev/null @@ -1,31 +0,0 @@ -from discord import app_commands - -__all__ = ["autocomplete_day"] - - -def autocomplete_day(argument: str) -> list[app_commands.Choice]: - """Autocompletion for day-arguments - - This supports relative offsets ("tomorrow") as well as weekdays - """ - argument = argument.lower() - values = [ - "Tomorrow", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Sunday", - "Morgen", - "Overmorgen", - "Maandag", - "Dinsdag", - "Woensdag", - "Donderdag", - "Vrijdag", - "Zaterdag", - "Zondag", - ] - - return [app_commands.Choice(name=value, value=value.lower()) for value in values if argument in value.lower()] diff --git a/didier/utils/discord/colours.py b/didier/utils/discord/colours.py index 7c26769..ffcd8eb 100644 --- a/didier/utils/discord/colours.py +++ b/didier/utils/discord/colours.py @@ -1,14 +1,6 @@ import discord -__all__ = ["ghent_university_blue", "ghent_university_yellow", "urban_dictionary_green"] - - -def ghent_university_blue() -> discord.Colour: - return discord.Colour.from_rgb(30, 100, 200) - - -def ghent_university_yellow() -> discord.Colour: - return discord.Colour.from_rgb(255, 210, 0) +__all__ = ["urban_dictionary_green"] def urban_dictionary_green() -> discord.Colour: diff --git a/didier/utils/discord/converters/time.py b/didier/utils/discord/converters/time.py deleted file mode 100644 index 6fbad9a..0000000 --- a/didier/utils/discord/converters/time.py +++ /dev/null @@ -1,66 +0,0 @@ -import contextlib -import datetime -from datetime import date, timedelta -from typing import Optional, Union - -import discord -from discord import app_commands -from discord.ext.commands import ArgumentParsingError -from overrides import overrides - -from didier.utils.discord.autocompletion.time import autocomplete_day -from didier.utils.types.datetime import ( - forward_to_next_weekday, - parse_dm_string, - str_to_weekday, -) - -__all__ = ["date_converter"] - - -def date_converter(argument: Optional[str]) -> date: - """Converter to turn a string into a date""" - # Store original argument for error message purposes - original_argument = argument - - # Default to today - if not argument: - return date.today() - - argument = argument.lower() - - # Manual offsets - if argument in ( - "tomorrow", - "tmrw", - "morgen", - ): - return date.today() + timedelta(days=1) - - if argument in ("overmorgen",): - return date.today() + timedelta(days=2) - - # Weekdays passed in words - with contextlib.suppress(ValueError): - weekday = str_to_weekday(argument) - return forward_to_next_weekday(date.today(), weekday, allow_today=False) - - # Date strings - with contextlib.suppress(ValueError): - return parse_dm_string(argument) - - # Unparseable - raise ArgumentParsingError(f"Unable to interpret `{original_argument}` as a date.") - - -class DateTransformer(app_commands.Transformer): - """Application commands transformer for dates""" - - async def autocomplete( - self, interaction: discord.Interaction, value: Union[int, float, str] - ) -> list[app_commands.Choice[Union[int, float, str]]]: - return autocomplete_day(str(value)) - - @overrides - async def transform(self, interaction: discord.Interaction, value: str) -> datetime.date: - return date_converter(value) diff --git a/didier/utils/http/requests.py b/didier/utils/http/requests.py index ffcb498..f649701 100644 --- a/didier/utils/http/requests.py +++ b/didier/utils/http/requests.py @@ -2,7 +2,7 @@ import logging from contextlib import asynccontextmanager from typing import AsyncGenerator -from aiohttp import ClientResponse, ClientSession, ContentTypeError +from aiohttp import ClientResponse, ClientSession from didier.exceptions.http_exception import HTTPException @@ -18,19 +18,13 @@ def request_successful(response: ClientResponse) -> bool: @asynccontextmanager -async def ensure_get( - http_session: ClientSession, endpoint: str, *, log_exceptions: bool = True -) -> AsyncGenerator[dict, None]: +async def ensure_get(http_session: ClientSession, endpoint: str) -> AsyncGenerator[dict, None]: """Context manager that automatically raises an exception if a GET-request fails""" async with http_session.get(endpoint) as response: - try: - content = await response.json() - except ContentTypeError: - content = await response.text() - if not request_successful(response): - if log_exceptions: - logger.error("Failed HTTP request to %s (status %s)\nResponse: %s", endpoint, response.status, content) + logger.error( + "Failed HTTP request to %s (status %s)\nResponse: %s", endpoint, response.status, await response.json() + ) raise HTTPException(response.status) @@ -39,29 +33,18 @@ async def ensure_get( @asynccontextmanager async def ensure_post( - http_session: ClientSession, - endpoint: str, - payload: dict, - *, - log_exceptions: bool = True, - expect_return: bool = True + http_session: ClientSession, endpoint: str, payload: dict, *, expect_return: bool = True ) -> AsyncGenerator[dict, None]: """Context manager that automatically raises an exception if a POST-request fails""" async with http_session.post(endpoint, data=payload) as response: if not request_successful(response): - try: - content = await response.json() - except ContentTypeError: - content = await response.text() - - if log_exceptions: - logger.error( - "Failed HTTP request to %s (status %s)\nPayload: %s\nResponse: %s", - endpoint, - response.status, - payload, - content, - ) + logger.error( + "Failed HTTP request to %s (status %s)\nPayload: %s\nResponse: %s", + endpoint, + response.status, + payload, + await response.json(), + ) raise HTTPException(response.status) diff --git a/didier/utils/types/datetime.py b/didier/utils/types/datetime.py index 07ad29a..7b2d5c1 100644 --- a/didier/utils/types/datetime.py +++ b/didier/utils/types/datetime.py @@ -1,91 +1,18 @@ import datetime -import re import zoneinfo -from typing import TypeVar, Union -__all__ = [ - "LOCAL_TIMEZONE", - "forward_to_next_weekday", - "int_to_weekday", - "parse_dm_string", - "str_to_date", - "str_to_month", - "str_to_weekday", - "tz_aware_now", -] +__all__ = ["LOCAL_TIMEZONE", "int_to_weekday", "str_to_date", "tz_aware_now"] -DateType = TypeVar("DateType", datetime.date, datetime.datetime) +from typing import Union LOCAL_TIMEZONE = zoneinfo.ZoneInfo("Europe/Brussels") -def forward_to_next_weekday(day_dt: DateType, target_weekday: int, *, allow_today: bool = False) -> DateType: - """Forward a date to the next occurence of a weekday""" - if not 0 <= target_weekday <= 6: - raise ValueError - - # Skip at least one day - if not allow_today: - day_dt += datetime.timedelta(days=1) - - while day_dt.weekday() != target_weekday: - day_dt += datetime.timedelta(days=1) - - return day_dt - - def int_to_weekday(number: int) -> str: # pragma: no cover # it's useless to write a test for this """Get the Dutch name of a weekday from the number""" return ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"][number] -def parse_dm_string(argument: str) -> datetime.date: - """Parse a string to [day]/[month] - - The year is set to the current year by default, as this can be changed easily. - - This supports: - - DD/MM - - DD (month defaults to current) - - DD [Dutch Month, possibly abbreviated] - - DD [English Month, possibly abbreviated] - - [Dutch Month, possibly abbreviated] DD - - [English Month, possibly abbreviated] DD - """ - argument = argument.lower() - today = datetime.date.today() - - # DD/MM - if "/" in argument: - spl = argument.split("/") - if len(spl) != 2: - raise ValueError - - return datetime.date(day=int(spl[0]), month=int(spl[1]), year=today.year) - - # Try to interpret text - spl = argument.split(" ") - if len(spl) != 2: - raise ValueError - - # Day Month - match = re.search(r"\d+", spl[0]).group() - if match is not None: - day = int(match) - month = str_to_month(spl[1]) - return datetime.date(day=day, month=month, year=today.year) - - # Month Day - match = re.search(r"\d+", spl[0]).group() - if match is not None: - day = int(match) - month = str_to_month(spl[0]) - return datetime.date(day=day, month=month, year=today.year) - - # Unparseable - raise ValueError - - def str_to_date(date_str: str, formats: Union[list[str], str] = "%d/%m/%Y") -> datetime.date: """Turn a string into a DD/MM/YYYY date""" # Allow passing multiple formats in a list @@ -101,76 +28,6 @@ def str_to_date(date_str: str, formats: Union[list[str], str] = "%d/%m/%Y") -> d raise ValueError -def str_to_month(argument: str) -> int: - """Turn a string int oa month, bilingual""" - argument = argument.lower() - - month_dict = { - # English - "january": 1, - "february": 2, - "march": 3, - "april": 4, - "may": 5, - "june": 6, - "july": 7, - # August is a prefix of Augustus so it is skipped - "september": 9, - "october": 10, - "november": 11, - "december": 12, - # Dutch - "januari": 1, - "februari": 2, - "maart": 3, - # April is the same in English so it is skipped - "mei": 5, - "juni": 6, - "juli": 7, - "augustus": 8, - # September is the same in English so it is skipped - "oktober": 10, - # November is the same in English so it is skipped - # December is the same in English so it is skipped - } - - for key, value in month_dict.items(): - if key.startswith(argument): - return value - - raise ValueError - - -def str_to_weekday(argument: str) -> int: - """Turn a string into a weekday, bilingual""" - argument = argument.lower() - - weekday_dict = { - # English - "monday": 0, - "tuesday": 1, - "wednesday": 2, - "thursday": 3, - "friday": 4, - "saturday": 5, - "sunday": 6, - # Dutch - "maandag": 0, - "dinsdag": 1, - "woensdag": 2, - "donderdag": 3, - "vrijdag": 4, - "zaterdag": 5, - "zondag": 6, - } - - for key, value in weekday_dict.items(): - if key.startswith(argument): - return value - - raise ValueError - - def tz_aware_now() -> datetime.datetime: """Get the current date & time, but timezone-aware""" return datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone(LOCAL_TIMEZONE)