import datetime from typing import List, Union import ceo_common.utils as utils class Term: """A representation of a term in the CSC LDAP, e.g. 's2021'.""" seasons = ['w', 's', 'f'] def __init__(self, s_term: str): assert len(s_term) == 5 and s_term[0] in self.seasons and \ s_term[1:].isdigit() self.s_term = s_term def __repr__(self): return self.s_term @staticmethod def from_datetime(dt: datetime.datetime): """Get a Term object for the given date.""" idx = (dt.month - 1) // 4 c = Term.seasons[idx] s_term = c + str(dt.year) return Term(s_term) @staticmethod def current(): """Get a Term object for the current date.""" dt = utils.get_current_datetime() return Term.from_datetime(dt) def start_month(self): return self.seasons.index(self.s_term[0]) * 4 + 1 def __add__(self, other): assert type(other) is int c = self.s_term[0] season_idx = self.seasons.index(c) year = int(self.s_term[1:]) season_idx += other year += season_idx // 3 season_idx %= 3 s_term = self.seasons[season_idx] + str(year) return Term(s_term) def __sub__(self, other): assert type(other) is int return self.__add__(-other) def __eq__(self, other): return isinstance(other, Term) and self.s_term == other.s_term def __lt__(self, other): if not isinstance(other, Term): return NotImplemented c1, c2 = self.s_term[0], other.s_term[0] year1, year2 = int(self.s_term[1:]), int(other.s_term[1:]) return year1 < year2 or ( year1 == year2 and self.seasons.index(c1) < self.seasons.index(c2) ) def __gt__(self, other): if not isinstance(other, Term): return NotImplemented c1, c2 = self.s_term[0], other.s_term[0] year1, year2 = int(self.s_term[1:]), int(other.s_term[1:]) return year1 > year2 or ( year1 == year2 and self.seasons.index(c1) > self.seasons.index(c2) ) def __ge__(self, other): return self > other or self == other def __le__(self, other): return self < other or self == other def to_datetime(self) -> datetime.datetime: c = self.s_term[0] year = int(self.s_term[1:]) month = self.seasons.index(c) * 4 + 1 day = 1 return datetime.datetime(year, month, day) # Utility functions def get_terms_for_new_user(num_terms: int) -> List[str]: current_term = Term.current() terms = [current_term + i for i in range(num_terms)] return list(map(str, terms)) def get_terms_for_renewal( existing_terms: Union[List[str], None], num_terms: int, ) -> List[str]: """Calculates the terms for which a member or club rep should be renewed. :param terms: The existing terms for the user being renewed. If the user is being renewed as a regular member, these should be the member terms. If they are being renewed as a club rep, these should be the non-member terms. This may be None if the user does not have any terms of the appropriate type (an empty list is also acceptable). :param num_terms: The number of terms for which the user is being renewed. """ max_term = None current_term = Term.current() if existing_terms: max_term = max(map(Term, existing_terms)) if max_term is not None and max_term >= current_term: next_term = max_term + 1 else: next_term = current_term terms = [next_term + i for i in range(num_terms)] return list(map(str, terms))