Sakuten/backend

View on GitHub
api/time_management.py

Summary

Maintainability
A
0 mins
Test Coverage
from flask import current_app
import datetime


def mod_time(t, dt):
    """
        Modify the supplied time with timedelta
        Args:
            t(datetime.time|datetime.datetime): The Time to modify
            dt(datetime.timedelta): Difference
        Returns:
            time(datetime.time|datetime.datetime): The modified time
    """
    if isinstance(t, datetime.time):
        t = datetime.datetime.combine(datetime.date(2000, 1, 1), t)
        return (t + dt).time()
    else:
        return t + dt


class OutOfHoursError(Exception):
    """
        The Exception that indicates the time was out of festival
    """
    pass


class OutOfAcceptingHoursError(Exception):
    """
        The Exception that indicates the time is in festival,
        but not in accepting time
    """
    pass


def get_current_datetime():
    """
        Get the current datetime.
        Note: This function is intended to be mocked in testing
        Return:
          time(datetime.datetime): current datetime
    """
    return datetime.datetime.now(current_app.config['TIMEZONE'])


def _validate_and_get_time(time):
    if time is None:
        time = get_current_datetime()

    if isinstance(time, datetime.datetime):
        start = current_app.config['START_DATETIME']
        end = current_app.config['END_DATETIME']
        if not (start <= time <= end):
            raise OutOfHoursError()
        # extract datetime.time instance from datetime.datetime
        return time.time()
    return time


def get_draw_time_index(time=None):
    """
        get the lottery index from the drawing time
        args:
          time(datetime.time|datetime.datetime): the time
        return:
          i(int): the lottery index
        raises:
          OutOfHoursError, OutOfAcceptingHoursError
    """
    time = _validate_and_get_time(time)
    ext = current_app.config['DRAWING_TIME_EXTENSION']
    en_margin = current_app.config['TIMEPOINT_END_MARGIN']

    for i, (_, en) in enumerate(current_app.config['TIMEPOINTS']):
        if mod_time(en, en_margin) <= time <= mod_time(en, ext):
            return i

    raise OutOfAcceptingHoursError()


def get_time_index(time=None):
    """
        get the lottery index from the time
        args:
          time(datetime.time|datetime.datetime): the time
        return:
          i(int): the lottery index
        raises:
          OutOfHoursError, OutOfAcceptingHoursError
    """
    time = _validate_and_get_time(time)
    en_margin = current_app.config['TIMEPOINT_END_MARGIN']

    for i, (st, en) in enumerate(current_app.config['TIMEPOINTS']):
        if st <= time <= mod_time(en, en_margin):
            return i

    raise OutOfAcceptingHoursError()


def get_prev_time_index(time=None):
    """
        get the previous lottery index from the time
        args:
          time(datetime.time|datetime.datetime): the time
        return:
          i(int): the lottery index
        raises:
          OutOfHoursError, OutOfAcceptingHoursError

        the graphical description for this method is
        in the commit note of commit 3fb5c1a .
        See it if you confusing how to imagine this work.
        And also, it lived in the commit message,
        but it was wrong. so DON'T REFER TO COMMIT MESSAGE
    """
    time = _validate_and_get_time(time)
    en_margin = current_app.config['TIMEPOINT_END_MARGIN']
    ends = [mod_time(time_point[1], en_margin) for time_point
            in current_app.config['TIMEPOINTS']]

    for i in range(len(ends)-1):
        if ends[i] <= time < ends[i+1]:
            return i

    if ends[-1] <= time:
        return len(current_app.config['TIMEPOINTS']) - 1

    raise OutOfAcceptingHoursError()