diff --git a/agenda/accommodation.py b/agenda/accommodation.py index 59eb111..59e9d64 100644 --- a/agenda/accommodation.py +++ b/agenda/accommodation.py @@ -2,7 +2,7 @@ import yaml -from .types import Event +from .event import Event def get_events(filepath: str) -> list[Event]: diff --git a/agenda/birthday.py b/agenda/birthday.py index e5f28a8..6fdfd21 100644 --- a/agenda/birthday.py +++ b/agenda/birthday.py @@ -4,7 +4,7 @@ from datetime import date import yaml -from .types import Event +from .event import Event YEAR_NOT_KNOWN = 1900 diff --git a/agenda/bristol_waste.py b/agenda/bristol_waste.py index 0738d32..cab155f 100644 --- a/agenda/bristol_waste.py +++ b/agenda/bristol_waste.py @@ -8,7 +8,7 @@ from datetime import date, datetime, timedelta import httpx -from .types import Event +from .event import Event from .utils import make_waste_dir ttl_hours = 12 diff --git a/agenda/busy.py b/agenda/busy.py index a8868f0..d6978d4 100644 --- a/agenda/busy.py +++ b/agenda/busy.py @@ -7,7 +7,8 @@ from datetime import date, datetime, timedelta import flask from . import events_yaml -from .types import Event, StrDict, Trip +from .event import Event +from .types import StrDict, Trip def busy_event(e: Event) -> bool: diff --git a/agenda/calendar.py b/agenda/calendar.py index 039c546..d1fb985 100644 --- a/agenda/calendar.py +++ b/agenda/calendar.py @@ -3,7 +3,7 @@ import typing from datetime import timedelta -from .types import Event +from .event import Event event_type_color_map = { "bank_holiday": "success-subtle", diff --git a/agenda/carnival.py b/agenda/carnival.py index c6f429d..7f6f2f4 100644 --- a/agenda/carnival.py +++ b/agenda/carnival.py @@ -4,7 +4,7 @@ from datetime import date, timedelta from dateutil.easter import easter -from .types import Event +from .event import Event def rio_carnival_events(start_date: date, end_date: date) -> list[Event]: diff --git a/agenda/conference.py b/agenda/conference.py index 3d96dbb..9960f80 100644 --- a/agenda/conference.py +++ b/agenda/conference.py @@ -6,7 +6,7 @@ from datetime import date, datetime import yaml -from .types import Event +from .event import Event @dataclasses.dataclass diff --git a/agenda/data.py b/agenda/data.py index 5760f56..4bbd76f 100644 --- a/agenda/data.py +++ b/agenda/data.py @@ -35,7 +35,8 @@ from . import ( travel, uk_holiday, ) -from .types import Event, StrDict +from .event import Event +from .types import StrDict from .utils import time_function here = dateutil.tz.tzlocal() diff --git a/agenda/domains.py b/agenda/domains.py index 89aae32..5b03bff 100644 --- a/agenda/domains.py +++ b/agenda/domains.py @@ -4,7 +4,7 @@ import csv import os from datetime import datetime -from .types import Event +from .event import Event url = "https://admin.gandi.net/domain/01578ef0-a84b-11e7-bdf3-00163e6dc886/" diff --git a/agenda/economist.py b/agenda/economist.py index 4438b98..21a8199 100644 --- a/agenda/economist.py +++ b/agenda/economist.py @@ -5,7 +5,7 @@ from datetime import date, time, timedelta from dateutil.relativedelta import TH, relativedelta from . import uk_time -from .types import Event +from .event import Event def publication_dates(start_date: date, end_date: date) -> list[Event]: diff --git a/agenda/event.py b/agenda/event.py new file mode 100644 index 0000000..b975625 --- /dev/null +++ b/agenda/event.py @@ -0,0 +1,149 @@ +"""Types.""" + +import datetime +from dataclasses import dataclass + +from . import utils +from .types import DateOrDateTime, StrDict + +emojis = { + "market": "🧺", + "us_presidential_election": "πŸ—³οΈπŸ‡ΊπŸ‡Έ", + "bus_route_closure": "🚌❌", + "meetup": "πŸ‘₯", + "dinner": "🍷", + "party": "🍷", + "ba_voucher": "✈️", + "accommodation": "🏨", # alternative: 🧳 + "flight": "✈️", + "conference": "🎀", + "rocket": "πŸš€", + "birthday": "🎈", + "waste_schedule": "πŸ—‘οΈ", + "economist": "πŸ“°", + "running": "πŸƒ", + "critical_mass": "🚴", + "trip": "🧳", + "hackathon": "πŸ’»", +} + + +@dataclass +class Event: + """Event.""" + + name: str + date: DateOrDateTime + end_date: DateOrDateTime | None = None + title: str | None = None + url: str | None = None + going: bool | None = None + + @property + def as_datetime(self) -> datetime.datetime: + """Date/time of event.""" + return utils.as_datetime(self.date) + + @property + def has_time(self) -> bool: + """Event has a time associated with it.""" + return isinstance(self.date, datetime.datetime) + + @property + def as_date(self) -> datetime.date: + """Date of event.""" + return ( + self.date.date() if isinstance(self.date, datetime.datetime) else self.date + ) + + @property + def end_as_date(self) -> datetime.date: + """Date of event.""" + return ( + ( + self.end_date.date() + if isinstance(self.end_date, datetime.datetime) + else self.end_date + ) + if self.end_date + else self.as_date + ) + + @property + def display_time(self) -> str | None: + """Time for display on web page.""" + return ( + self.date.strftime("%H:%M") + if isinstance(self.date, datetime.datetime) + else None + ) + + @property + def display_timezone(self) -> str | None: + """Timezone for display on web page.""" + return ( + self.date.strftime("%z") + if isinstance(self.date, datetime.datetime) + else None + ) + + def display_duration(self) -> str | None: + """Duration for display.""" + if self.end_as_date != self.as_date or not self.has_time: + return None + + assert isinstance(self.date, datetime.datetime) + assert isinstance(self.end_date, datetime.datetime) + + secs: int = int((self.end_date - self.date).total_seconds()) + + hours: int = secs // 3600 + mins: int = (secs % 3600) // 60 + + if mins == 0: + return f"{hours:d}h" + if hours == 0: + return f"{mins:d} mins" + + return f"{hours:d}h {mins:02d} mins" + + def delta_days(self, today: datetime.date) -> str: + """Return number of days from today as a string.""" + delta = (self.as_date - today).days + + match delta: + case 0: + return "today" + case 1: + return "1 day" + case _: + return f"{delta:,d} days" + + @property + def display_date(self) -> str: + """Date for display on web page.""" + if isinstance(self.date, datetime.datetime): + return self.date.strftime("%a, %d, %b %Y %H:%M %z") + else: + return self.date.strftime("%a, %d, %b %Y") + + @property + def display_title(self) -> str: + """Name for display.""" + return self.title or self.name + + @property + def emoji(self) -> str | None: + """Emoji.""" + if self.title == "LHG Run Club": + return "πŸƒπŸ»" + return emojis.get(self.name) + + @property + def title_with_emoji(self) -> str | None: + """Title with optional emoji at the start.""" + title = self.title or self.name + if title is None: + return None + emoji = self.emoji + return f"{emoji} {title}" if emoji else title diff --git a/agenda/events_yaml.py b/agenda/events_yaml.py index ca55eea..8678d64 100644 --- a/agenda/events_yaml.py +++ b/agenda/events_yaml.py @@ -9,7 +9,7 @@ import isodate # type: ignore import yaml from . import uk_tz -from .types import Event +from .event import Event def midnight(d: date) -> datetime: diff --git a/agenda/gandi.py b/agenda/gandi.py index d833472..b5f3278 100644 --- a/agenda/gandi.py +++ b/agenda/gandi.py @@ -1,7 +1,7 @@ """Gandi domain renewal dates.""" import os -from .types import Event +from .event import Event from datetime import datetime import json diff --git a/agenda/hn.py b/agenda/hn.py index 9f11e91..52bdb0a 100644 --- a/agenda/hn.py +++ b/agenda/hn.py @@ -5,7 +5,7 @@ from datetime import date, datetime, time, timedelta import pytz from dateutil.relativedelta import relativedelta -from .types import Event +from .event import Event eastern_time = pytz.timezone("America/New_York") diff --git a/agenda/holidays.py b/agenda/holidays.py index 6cda113..620b9ab 100644 --- a/agenda/holidays.py +++ b/agenda/holidays.py @@ -6,7 +6,8 @@ from datetime import date, timedelta import agenda.uk_holiday import holidays -from .types import Event, Holiday +from .event import Event +from .types import Holiday def us_holidays(start_date: date, end_date: date) -> list[Holiday]: @@ -98,9 +99,9 @@ def combine_holidays(holidays: list[Holiday]) -> list[Event]: (12, 26): "Boxing Day", } - combined: collections.defaultdict[ - tuple[date, str], set[str] - ] = collections.defaultdict(set) + combined: collections.defaultdict[tuple[date, str], set[str]] = ( + collections.defaultdict(set) + ) for h in holidays: assert isinstance(h.name, str) and isinstance(h.date, date) diff --git a/agenda/meetup.py b/agenda/meetup.py index 3330e52..df52a23 100644 --- a/agenda/meetup.py +++ b/agenda/meetup.py @@ -4,7 +4,7 @@ import json import os.path from datetime import datetime -from .types import Event +from .event import Event def get_events(data_dir: str) -> list[Event]: diff --git a/agenda/n_somerset_waste.py b/agenda/n_somerset_waste.py index 4621b2e..16b168a 100644 --- a/agenda/n_somerset_waste.py +++ b/agenda/n_somerset_waste.py @@ -9,7 +9,7 @@ import httpx import lxml.html from . import uk_time -from .types import Event +from .event import Event from .utils import make_waste_dir ttl_hours = 12 diff --git a/agenda/subscription.py b/agenda/subscription.py index 9c2036c..a9f8ccc 100644 --- a/agenda/subscription.py +++ b/agenda/subscription.py @@ -2,7 +2,7 @@ import yaml -from .types import Event +from .event import Event def get_events(filepath: str) -> list[Event]: diff --git a/agenda/travel.py b/agenda/travel.py index e7231fc..51a7810 100644 --- a/agenda/travel.py +++ b/agenda/travel.py @@ -9,7 +9,8 @@ import flask import yaml from geopy.distance import geodesic # type: ignore -from .types import Event, StrDict +from .event import Event +from .types import StrDict Leg = dict[str, str] diff --git a/agenda/types.py b/agenda/types.py index a8f3a8f..3fc6b08 100644 --- a/agenda/types.py +++ b/agenda/types.py @@ -351,146 +351,3 @@ class Holiday: if self.local_name and self.local_name != self.name else self.name ) - - -emojis = { - "market": "🧺", - "us_presidential_election": "πŸ—³οΈπŸ‡ΊπŸ‡Έ", - "bus_route_closure": "🚌❌", - "meetup": "πŸ‘₯", - "dinner": "🍷", - "party": "🍷", - "ba_voucher": "✈️", - "accommodation": "🏨", # alternative: 🧳 - "flight": "✈️", - "conference": "🎀", - "rocket": "πŸš€", - "birthday": "🎈", - "waste_schedule": "πŸ—‘οΈ", - "economist": "πŸ“°", - "running": "πŸƒ", - "critical_mass": "🚴", - "trip": "🧳", - "hackathon": "πŸ’»", -} - - -@dataclass -class Event: - """Event.""" - - name: str - date: DateOrDateTime - end_date: DateOrDateTime | None = None - title: str | None = None - url: str | None = None - going: bool | None = None - - @property - def as_datetime(self) -> datetime.datetime: - """Date/time of event.""" - return utils.as_datetime(self.date) - - @property - def has_time(self) -> bool: - """Event has a time associated with it.""" - return isinstance(self.date, datetime.datetime) - - @property - def as_date(self) -> datetime.date: - """Date of event.""" - return ( - self.date.date() if isinstance(self.date, datetime.datetime) else self.date - ) - - @property - def end_as_date(self) -> datetime.date: - """Date of event.""" - return ( - ( - self.end_date.date() - if isinstance(self.end_date, datetime.datetime) - else self.end_date - ) - if self.end_date - else self.as_date - ) - - @property - def display_time(self) -> str | None: - """Time for display on web page.""" - return ( - self.date.strftime("%H:%M") - if isinstance(self.date, datetime.datetime) - else None - ) - - @property - def display_timezone(self) -> str | None: - """Timezone for display on web page.""" - return ( - self.date.strftime("%z") - if isinstance(self.date, datetime.datetime) - else None - ) - - def display_duration(self) -> str | None: - """Duration for display.""" - if self.end_as_date != self.as_date or not self.has_time: - return None - - assert isinstance(self.date, datetime.datetime) - assert isinstance(self.end_date, datetime.datetime) - - secs: int = int((self.end_date - self.date).total_seconds()) - - hours: int = secs // 3600 - mins: int = (secs % 3600) // 60 - - if mins == 0: - return f"{hours:d}h" - if hours == 0: - return f"{mins:d} mins" - - return f"{hours:d}h {mins:02d} mins" - - def delta_days(self, today: datetime.date) -> str: - """Return number of days from today as a string.""" - delta = (self.as_date - today).days - - match delta: - case 0: - return "today" - case 1: - return "1 day" - case _: - return f"{delta:,d} days" - - @property - def display_date(self) -> str: - """Date for display on web page.""" - if isinstance(self.date, datetime.datetime): - return self.date.strftime("%a, %d, %b %Y %H:%M %z") - else: - return self.date.strftime("%a, %d, %b %Y") - - @property - def display_title(self) -> str: - """Name for display.""" - return self.title or self.name - - @property - def emoji(self) -> str | None: - """Emoji.""" - if self.title == "LHG Run Club": - return "πŸƒπŸ»" - return emojis.get(self.name) - - @property - def title_with_emoji(self) -> str | None: - """Title with optional emoji at the start.""" - title = self.title or self.name - if title is None: - return None - emoji = self.emoji - return f"{emoji} {title}" if emoji else title