Source code for csbot.plugins.termdates

from csbot.plugin import Plugin
import datetime
import math
import typing as _t

from ..util import ordinal


[docs]class Term: def __init__(self, key: str, start_date: datetime.datetime): self.key = key self.start_date = start_date @property def first_monday(self) -> datetime.datetime: return self.start_date - datetime.timedelta(days=self.start_date.weekday()) @property def last_friday(self) -> datetime.datetime: return self.first_monday + datetime.timedelta(days=4, weeks=9)
[docs] def get_week_number(self, date: datetime.date) -> int: """Get the "term week number" of a date relative to this term. The first week of term is week 1, not week 0. Week 1 starts at the Monday of the term's start date, even if the term's start date is not Monday. Any date before the start of the term gives a negative week number. """ delta = date - self.first_monday.date() week_number = math.floor(delta.days / 7.0) if week_number >= 0: return week_number + 1 else: return week_number
[docs] def get_week_start(self, week_number: int) -> datetime.datetime: """Get the start date of a specific week number relative to this term. The first week of term is week 1, not week 0, although this method allows both. When referring to the first week of term, the start date is the term start date (which may not be a Monday). All other weeks start on their Monday. """ if week_number in (0, 1): return self.start_date elif week_number > 1: return self.first_monday + datetime.timedelta(weeks=week_number - 1) else: return self.first_monday + datetime.timedelta(weeks=week_number)
[docs]class TermDates(Plugin): """ A wonderful plugin allowing old people (graduates) to keep track of the ever-changing calendar. """ DATE_FORMAT = '%Y-%m-%d' TERM_KEYS = ('aut', 'spr', 'sum') db_terms = Plugin.use('mongodb', collection='terms') terms = None _doc_id = None
[docs] def setup(self): super(TermDates, self).setup() self._load()
def _load(self): doc = self.db_terms.find_one() if not doc: return False self.terms = {key: Term(key, doc[key][0]) for key in self.TERM_KEYS} self._doc_id = doc['_id'] return True def _save(self): if not self.terms: return False doc = {key: (self.terms[key].start_date, self.terms[key].last_friday) for key in self.TERM_KEYS} if self._doc_id: self.db_terms.replace_one({'_id': self._doc_id}, doc, upsert=True) else: res = self.db_terms.insert_one(doc) self._doc_id = res.inserted_id return True @property def initialised(self) -> bool: """If no term dates have been set, the calendar is uninitialised and can't be asked about term thing.""" return self._doc_id is not None
[docs] @Plugin.command('termdates', help='termdates: show the current term dates') def termdates(self, e): if not self.initialised: e.reply('error: no term dates (see termdates.set)') else: e.reply('Aut {} -- {}, Spr {} -- {}, Sum {} -- {}'.format( self._term_start('aut'), self._term_end('aut'), self._term_start('spr'), self._term_end('spr'), self._term_start('sum'), self._term_end('sum')))
def _term_start(self, term): """ Get the start date (first Monday) of a term as a string. """ term = term.lower() return self.terms[term].start_date.strftime(self.DATE_FORMAT) def _term_end(self, term): """ Get the end date (last Friday) of a term as a string. """ term = term.lower() return self.terms[term].last_friday.strftime(self.DATE_FORMAT)
[docs] @Plugin.command('week', help='week [term] [num]: info about a week, ' 'relative to the UoY term schedule') def week(self, e): if not self.initialised: e.reply('error: no term dates (see termdates.set)') return # We can handle weeks in the following formats: # !week - get information about the current week # !week n - get the date of week n in the current (or next, if in # holiday) term # !week term n - get the date of week n in the given term # !week n term - as above week = e['data'].lower().split() if len(week) == 0: term, week_number = self._current_week() elif len(week) == 1: try: term = self._current_or_next_term() week_number = int(week[0]) if week_number < 1: e.reply('error: bad week format') return except ValueError: term_key = week[0][:3] term, week_number = self._current_week(term_key) elif len(week) >= 2: try: term_key = week[0][:3] week_number = int(week[1]) except ValueError: try: term_key = week[1][:3] week_number = int(week[0]) except ValueError: e.reply('error: bad week format') return try: term = self.terms[term_key] except KeyError: e.reply('error: bad week format') return else: e.reply('error: bad week format') return if term is None: e.reply('error: no term dates (see termdates.set)') elif week_number > 0: e.reply('{} {}: {}'.format(term.key.capitalize(), week_number, term.get_week_start(week_number).strftime(self.DATE_FORMAT))) else: e.reply('{} week before {} (starts {})' .format(ordinal(-week_number), term.key.capitalize(), term.start_date.strftime(self.DATE_FORMAT)))
def _current_or_next_term(self) -> _t.Optional[Term]: """ Get the name of the current term """ now = datetime.datetime.now().date() for key in self.TERM_KEYS: term = self.terms[key] if now < term.first_monday.date(): return term elif now <= term.last_friday.date(): return term return None def _current_week(self, key: _t.Optional[str] = None) -> (_t.Optional[Term], _t.Optional[int]): if key: term = self.terms.get(key.lower()) else: term = self._current_or_next_term() if term: return term, term.get_week_number(datetime.date.today()) else: return None, None
[docs] @Plugin.command('termdates.set', help='termdates.set <aut> <spr> <sum>: set the term dates') def termdates_set(self, e): dates = e['data'].split() if len(dates) < 3: e.reply('error: all three dates must be provided') return terms = {} for key, date in zip(self.TERM_KEYS, dates): try: term_start = datetime.datetime.strptime(date, self.DATE_FORMAT) except ValueError: e.reply('error: dates must be in %Y-%M-%d format.') return terms[key] = Term(key, term_start) self.terms = terms self._save()