Revised the documentation of Period in the Mia core application.

This commit is contained in:
依瑪貓 2020-08-02 02:20:08 +08:00
parent 1b89fef01c
commit 783ecea5cb

View File

@ -18,9 +18,8 @@
"""The period chooser utilities of the Mia core application. """The period chooser utilities of the Mia core application.
""" """
import datetime
import re import re
from datetime import date, timedelta
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.template import defaultfilters from django.template import defaultfilters
@ -35,47 +34,8 @@ class Period:
Args: Args:
spec (str): The current period specification spec (str): The current period specification
data_start (date): The available first day of the data. data_start (datetime.date): The available first day of the data.
data_end (date): The available last day of the data. data_end (datetime.date): The available last day of the data.
Attributes:
spec (date): The currently-working period specification.
start (date): The start day of the currently-specified period.
end (date): The end day of the currently-specified period.
description (str): The description of the currently-specified
period.
this_month (str): The specification of this month.
last_month (str): The specification of last month.
since_last_month (str): The specification since last month.
has_months_to_choose (bool): Whether there are months to
choose besides this month and
last month.
chosen_month (bool): The specification of the chosen month,
or None if the current period is not a
month or is out of available data range.
this_year (str): The specification of this year.
last_year (str): The specification of last year.
has_years_to_choose (bool): Whether there are years to
choose besides this year and
last year.
years_to_choose (list[str]): This specification of the
available years to choose,
besides this year and last year.
today (str): The specification of today.
yesterday (str): The specification of yesterday.
chosen_start (str): The specification of the first day of the
specified period, as the default date for
the single-day chooser.
has_days_to_choose (bool): Whether there is a day range to
choose.
data_start (str): The specification of the available first day.
data_end (str): The specification of the available last day.
chosen_start (str): The specification of the first day of the
specified period
chosen_end (str): The specification of the last day of the
specified period
month_picker_params (str): The month-picker parameters, as a
JSON text string
""" """
def __init__(self, spec=None, data_start=None, data_end=None): def __init__(self, spec=None, data_start=None, data_end=None):
self._period = self.Parser(spec) self._period = self.Parser(spec)
@ -84,22 +44,48 @@ class Period:
@property @property
def spec(self): def spec(self):
"""Returns the period specification.
Returns:
str: The period specification.
"""
return self._period.spec return self._period.spec
@property @property
def start(self): def start(self):
"""Returns the start day of the currently-specified period.
Returns:
datetime.date: The start day of the currently-specified period.
"""
return self._period.start return self._period.start
@property @property
def end(self): def end(self):
"""Returns the end day of the currently-specified period.
Returns:
datetime.date: The end day of the currently-specified period.
"""
return self._period.end return self._period.end
@property @property
def description(self): def description(self):
"""Returns the text description of the currently-specified period.
Returns:
str: The text description of the currently-specified period
"""
return self._period.description return self._period.description
@property @property
def error(self): def error(self):
"""Returns the error of the period specification format.
Returns:
str|None: The error of the period specification format, or None on
success.
"""
return self._period.error return self._period.error
@staticmethod @staticmethod
@ -107,7 +93,7 @@ class Period:
"""Returns the first day of the last month. """Returns the first day of the last month.
Returns: Returns:
date: The first day of the last month. datetime.date: The first day of the last month.
""" """
today = timezone.localdate() today = timezone.localdate()
month = today.month - 1 month = today.month - 1
@ -115,14 +101,14 @@ class Period:
if month < 1: if month < 1:
month = 12 month = 12
year = year - 1 year = year - 1
return date(year, month, 1) return datetime.date(year, month, 1)
@staticmethod @staticmethod
def _get_next_month_start(): def _get_next_month_start():
"""Returns the first day of the next month. """Returns the first day of the next month.
Returns: Returns:
date: The first day of the next month. datetime.date: The first day of the next month.
""" """
today = timezone.localdate() today = timezone.localdate()
month = today.month + 1 month = today.month + 1
@ -130,14 +116,20 @@ class Period:
if month > 12: if month > 12:
month = 1 month = 1
year = year + 1 year = year + 1
return date(year, month, 1) return datetime.date(year, month, 1)
@property @property
def this_month(self): def this_month(self):
"""Returns the specification of this month.
Returns:
str|None: The specification of this month, or None if there is no
data in or before this month.
"""
if self._data_start is None: if self._data_start is None:
return None return None
today = timezone.localdate() today = timezone.localdate()
first_month_start = date( first_month_start = datetime.date(
self._data_start.year, self._data_start.month, 1) self._data_start.year, self._data_start.month, 1)
if today < first_month_start: if today < first_month_start:
return None return None
@ -145,10 +137,16 @@ class Period:
@property @property
def last_month(self): def last_month(self):
"""Returns the specification of last month.
Returns:
str|None: The specification of last month, or None if there is no
data in or before last month.
"""
if self._data_start is None: if self._data_start is None:
return None return None
last_month_start = self._get_last_month_start() last_month_start = self._get_last_month_start()
first_month_start = date( first_month_start = datetime.date(
self._data_start.year, self._data_start.month, 1) self._data_start.year, self._data_start.month, 1)
if last_month_start < first_month_start: if last_month_start < first_month_start:
return None return None
@ -156,6 +154,12 @@ class Period:
@property @property
def since_last_month(self): def since_last_month(self):
"""Returns the specification since last month.
Returns:
str|None: The specification since last month, or None if there is
no data in or before last month.
"""
last_month = self.last_month last_month = self.last_month
if last_month is None: if last_month is None:
return None return None
@ -163,8 +167,15 @@ class Period:
@property @property
def has_months_to_choose(self): def has_months_to_choose(self):
"""Returns whether there are months to choose besides this month and
last month.
Returns:
bool: True if there are months to choose besides this month and
last month, or False otherwise.
"""
if self._data_start is None: if self._data_start is None:
return None return False
if self._data_start < self._get_last_month_start(): if self._data_start < self._get_last_month_start():
return True return True
if self._data_end >= self._get_next_month_start(): if self._data_end >= self._get_next_month_start():
@ -173,6 +184,14 @@ class Period:
@property @property
def chosen_month(self): def chosen_month(self):
"""Returns the specification of the chosen month, or None if the
current period is not a month or is out of available data range.
Returns:
str|None: The specification of the chosen month, or None if the
current period is not a month or is out of available data
range.
"""
if self._data_start is None: if self._data_start is None:
return None return None
m = re.match("^[0-9]{4}-[0-2]{2}", self._period.spec) m = re.match("^[0-9]{4}-[0-2]{2}", self._period.spec)
@ -186,6 +205,12 @@ class Period:
@property @property
def this_year(self): def this_year(self):
"""Returns the specification of this year.
Returns:
str|None: The specification of this year, or None if there is no
data in or before this year.
"""
if self._data_start is None: if self._data_start is None:
return None return None
this_year = timezone.localdate().year this_year = timezone.localdate().year
@ -195,6 +220,12 @@ class Period:
@property @property
def last_year(self): def last_year(self):
"""Returns the specification of last year.
Returns:
str|None: The specification of last year, or None if there is no
data in or before last year.
"""
if self._data_start is None: if self._data_start is None:
return None return None
last_year = timezone.localdate().year - 1 last_year = timezone.localdate().year - 1
@ -204,8 +235,15 @@ class Period:
@property @property
def has_years_to_choose(self): def has_years_to_choose(self):
"""Returns whether there are years to choose besides this year and
last year.
Returns:
bool: True if there are years to choose besides this year and
last year, or False otherwise.
"""
if self._data_start is None: if self._data_start is None:
return None return False
this_year = timezone.localdate().year this_year = timezone.localdate().year
if self._data_start.year < this_year - 1: if self._data_start.year < this_year - 1:
return True return True
@ -215,6 +253,11 @@ class Period:
@property @property
def years_to_choose(self): def years_to_choose(self):
"""Returns the years to choose besides this year and last year.
Returns:
list[str]: The years to choose besides this year and last year.
"""
if self._data_start is None: if self._data_start is None:
return None return None
this_year = timezone.localdate().year this_year = timezone.localdate().year
@ -225,8 +268,7 @@ class Period:
return after + before[::-1] return after + before[::-1]
def is_chosen_year(self, year): def is_chosen_year(self, year):
"""Returne whether the specified year is the currently-chosen """Returns whether the specified year is the currently-chosen year.
year.
Args: Args:
year (str): the year. year (str): the year.
@ -240,6 +282,12 @@ class Period:
@property @property
def today(self): def today(self):
"""Returns the specification of today.
Returns:
(str): The specification of today, or None if there is no data
in or before today.
"""
if self._data_start is None: if self._data_start is None:
return None return None
today = timezone.localdate() today = timezone.localdate()
@ -249,19 +297,37 @@ class Period:
@property @property
def yesterday(self): def yesterday(self):
"""Returns the specification of yesterday.
Returns:
(str): The specification of yesterday, or None if there is no data
in or before yesterday.
"""
if self._data_start is None: if self._data_start is None:
return None return None
yesterday = timezone.localdate() - timedelta(days=1) yesterday = timezone.localdate() - datetime.timedelta(days=1)
if yesterday < self._data_start or yesterday > self._data_end: if yesterday < self._data_start or yesterday > self._data_end:
return None return None
return dateformat.format(yesterday, "Y-m-d") return dateformat.format(yesterday, "Y-m-d")
@property @property
def chosen_day(self): def chosen_day(self):
"""Returns the specification of the chosen day.
Returns:
(str): The specification of the chosen day, or the start day
of the period if the current period is not a day.
"""
return dateformat.format(self._period.start, "Y-m-d") return dateformat.format(self._period.start, "Y-m-d")
@property @property
def has_days_to_choose(self): def has_days_to_choose(self):
"""Returns whether there are more than one day to choose from.
Returns:
bool: True if there are more than one day to choose from,
or False otherwise.
"""
if self._data_start is None: if self._data_start is None:
return False return False
if self._data_start == self._data_end: if self._data_start == self._data_end:
@ -270,18 +336,35 @@ class Period:
@property @property
def first_day(self): def first_day(self):
"""Returns the specification of the available first day.
Returns:
str: The specification of the available first day.
"""
if self._data_start is None: if self._data_start is None:
return None return None
return dateformat.format(self._data_start, "Y-m-d") return dateformat.format(self._data_start, "Y-m-d")
@property @property
def last_day(self): def last_day(self):
"""Returns the specification of the available last day.
Returns:
str: The specification of the available last day.
"""
if self._data_end is None: if self._data_end is None:
return None return None
return dateformat.format(self._data_end, "Y-m-d") return dateformat.format(self._data_end, "Y-m-d")
@property @property
def chosen_start(self): def chosen_start(self):
"""Returns the specification of of the first day of the
specified period.
Returns:
str: The specification of of the first day of the
specified period.
"""
if self._data_start is None: if self._data_start is None:
return None return None
day = self._period.start \ day = self._period.start \
@ -291,6 +374,13 @@ class Period:
@property @property
def chosen_end(self): def chosen_end(self):
"""Returns the specification of of the last day of the
specified period.
Returns:
str: The specification of of the last day of the
specified period.
"""
if self._data_end is None: if self._data_end is None:
return None return None
day = self._period.end \ day = self._period.end \
@ -300,9 +390,14 @@ class Period:
@property @property
def month_picker_params(self): def month_picker_params(self):
"""Returns the parameters for the month-picker, as a JSON text string.
Returns:
str: The parameters for the month-picker, as a JSON text string.
"""
if self._data_start is None: if self._data_start is None:
return None return None
start = date(self._data_start.year, self._data_start.month, 1) start = datetime.date(self._data_start.year, self._data_start.month, 1)
return DjangoJSONEncoder().encode({ return DjangoJSONEncoder().encode({
"locale": Language.current().locale, "locale": Language.current().locale,
"minDate": start, "minDate": start,
@ -350,7 +445,7 @@ class Period:
year = int(m.group(1)) year = int(m.group(1))
month = int(m.group(2)) month = int(m.group(2))
try: try:
self.start = date(year, month, 1) self.start = datetime.date(year, month, 1)
except ValueError: except ValueError:
self.invalid_period() self.invalid_period()
return return
@ -363,7 +458,7 @@ class Period:
year = int(m.group(1)) year = int(m.group(1))
month = int(m.group(2)) month = int(m.group(2))
try: try:
self.start = date(year, month, 1) self.start = datetime.date(year, month, 1)
except ValueError: except ValueError:
self.invalid_period() self.invalid_period()
return return
@ -377,11 +472,11 @@ class Period:
if m is not None: if m is not None:
year = int(m.group(1)) year = int(m.group(1))
try: try:
self.start = date(year, 1, 1) self.start = datetime.date(year, 1, 1)
except ValueError: except ValueError:
self.invalid_period() self.invalid_period()
return return
self.end = date(year, 12, 31) self.end = datetime.date(year, 12, 31)
today = timezone.localdate() today = timezone.localdate()
if year == today.year: if year == today.year:
self.description = gettext("This Year") self.description = gettext("This Year")
@ -392,7 +487,7 @@ class Period:
return return
# All time # All time
if spec == "-": if spec == "-":
self.start = date(2000, 1, 1) self.start = datetime.date(2000, 1, 1)
self.end = self.get_month_last_day( self.end = self.get_month_last_day(
timezone.localdate()) timezone.localdate())
self.description = gettext("All") self.description = gettext("All")
@ -402,7 +497,7 @@ class Period:
spec) spec)
if m is not None: if m is not None:
try: try:
self.start = date( self.start = datetime.date(
int(m.group(1)), int(m.group(1)),
int(m.group(2)), int(m.group(2)),
int(m.group(3))) int(m.group(3)))
@ -418,11 +513,11 @@ class Period:
spec) spec)
if m is not None: if m is not None:
try: try:
self.start = date( self.start = datetime.date(
int(m.group(1)), int(m.group(1)),
int(m.group(2)), int(m.group(2)),
int(m.group(3))) int(m.group(3)))
self.end = date( self.end = datetime.date(
int(m.group(4)), int(m.group(4)),
int(m.group(5)), int(m.group(5)),
int(m.group(6))) int(m.group(6)))
@ -467,7 +562,7 @@ class Period:
"""Sets the period to this month.""" """Sets the period to this month."""
today = timezone.localdate() today = timezone.localdate()
self.spec = dateformat.format(today, "Y-m") self.spec = dateformat.format(today, "Y-m")
self.start = date(today.year, today.month, 1) self.start = datetime.date(today.year, today.month, 1)
self.end = self.get_month_last_day(self.start) self.end = self.get_month_last_day(self.start)
self.description = gettext("This Month") self.description = gettext("This Month")
@ -493,7 +588,8 @@ class Period:
if next_month > 12: if next_month > 12:
next_month = 1 next_month = 1
next_year = next_year + 1 next_year = next_year + 1
return date(next_year, next_month, 1) - timedelta(days=1) return datetime.date(
next_year, next_month, 1) - datetime.timedelta(days=1)
@staticmethod @staticmethod
def get_month_text(year, month): def get_month_text(year, month):
@ -514,7 +610,7 @@ class Period:
if prev_month < 1: if prev_month < 1:
prev_month = 12 prev_month = 12
prev_year = prev_year - 1 prev_year = prev_year - 1
prev = date(prev_year, prev_month, 1) prev = datetime.date(prev_year, prev_month, 1)
if year == prev.year and month == prev.month: if year == prev.year and month == prev.month:
return gettext("Last Month") return gettext("Last Month")
return "%d/%d" % (year, month) return "%d/%d" % (year, month)
@ -532,7 +628,7 @@ class Period:
today = timezone.localdate() today = timezone.localdate()
if day == today: if day == today:
return gettext("Today") return gettext("Today")
elif day == today - timedelta(days=1): elif day == today - datetime.timedelta(days=1):
return gettext("Yesterday") return gettext("Yesterday")
elif day.year != today.year: elif day.year != today.year:
return defaultfilters.date(day, "Y/n/j") return defaultfilters.date(day, "Y/n/j")