Added type hints to the Mia core application.
This commit is contained in:
parent
c070b11ea2
commit
1be05c2252
@ -25,11 +25,30 @@ class UserConverter:
|
|||||||
"""The path converter for the user accounts."""
|
"""The path converter for the user accounts."""
|
||||||
regex = ".*"
|
regex = ".*"
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: str) -> User:
|
||||||
|
"""Returns the user by her log in ID.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The log in ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The user.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: When the value is invalid
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
return User.objects.get(login_id=value)
|
return User.objects.get(login_id=value)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value: User) -> str:
|
||||||
|
"""Returns the log in ID of a user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The log in ID.
|
||||||
|
"""
|
||||||
return value.login_id
|
return value.login_id
|
||||||
|
@ -22,6 +22,7 @@ application.
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import socket
|
import socket
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
@ -35,23 +36,23 @@ from .models import User, Country
|
|||||||
class AccountBackend:
|
class AccountBackend:
|
||||||
"""The account backend for the django-digest module."""
|
"""The account backend for the django-digest module."""
|
||||||
|
|
||||||
def get_partial_digest(self, username):
|
def get_partial_digest(self, username: str) -> Optional[str]:
|
||||||
"""Returns the HTTP digest authentication password digest hash
|
"""Returns the HTTP digest authentication password digest hash
|
||||||
of a user.
|
of a user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
username (str): The log in user name.
|
username: The log in user name.
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
str: The HTTP digest authentication password hash of
|
The HTTP digest authentication password hash of the user, or None
|
||||||
the user, or None if the user does not exist.
|
if the user does not exist.
|
||||||
"""
|
"""
|
||||||
user = User.objects.filter(login_id=username).first()
|
user = User.objects.filter(login_id=username).first()
|
||||||
if user is None:
|
if user is None:
|
||||||
return None
|
return None
|
||||||
return user.password
|
return user.password
|
||||||
|
|
||||||
def get_user(self, username):
|
def get_user(self, username: str) -> Optional[User]:
|
||||||
"""Returns the user by her log in user name.
|
"""Returns the user by her log in user name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -86,7 +87,7 @@ def login_required(function=None):
|
|||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def _log_visit(request):
|
def _log_visit(request: HttpRequest) -> None:
|
||||||
"""Logs the visit information for the logged-in user.
|
"""Logs the visit information for the logged-in user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -106,21 +107,29 @@ def _log_visit(request):
|
|||||||
request.session["visit_logged"] = True
|
request.session["visit_logged"] = True
|
||||||
|
|
||||||
|
|
||||||
def _get_remote_ip(request):
|
def _get_remote_ip(request: HttpRequest) -> str:
|
||||||
|
"""Returns the IP of the remote client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: The request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The IP of the remote client.
|
||||||
|
"""
|
||||||
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
|
||||||
if x_forwarded_for:
|
if x_forwarded_for:
|
||||||
return x_forwarded_for.split(",")[0]
|
return x_forwarded_for.split(",")[0]
|
||||||
return request.META.get('REMOTE_ADDR')
|
return request.META.get('REMOTE_ADDR')
|
||||||
|
|
||||||
|
|
||||||
def _get_host(ip):
|
def _get_host(ip: str) -> Optional[str]:
|
||||||
"""Look-up the host name by its IP.
|
"""Look-up the host name by its IP.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ip (str): The IP
|
ip: The IP
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The host name, or None if the look-up fails.
|
The host name, or None if the look-up fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return socket.gethostbyaddr(ip)[0]
|
return socket.gethostbyaddr(ip)[0]
|
||||||
@ -128,31 +137,30 @@ def _get_host(ip):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _get_country(ip):
|
def _get_country(ip: str) -> Optional[Country]:
|
||||||
"""Look-up the country by its IP.
|
"""Look-up the country by its IP.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ip (str): The IP
|
ip: The IP
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Country: The country.
|
The country.
|
||||||
"""
|
"""
|
||||||
code = _get_country_code(ip)
|
code = _get_country_code(ip)
|
||||||
try:
|
try:
|
||||||
return Country.objects.get(code=code)
|
return Country.objects.get(code=code)
|
||||||
except Country.DoesNotExist:
|
except Country.DoesNotExist:
|
||||||
pass
|
return None
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def _get_country_code(ip):
|
def _get_country_code(ip: str) -> Optional[str]:
|
||||||
"""Look-up the country code by its IP.
|
"""Look-up the country code by its IP.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
ip (str): The IP
|
ip: The IP
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The country code, or None if the look-up fails.
|
The country code, or None if the look-up fails.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return geolite2.lookup(ip).country
|
return geolite2.lookup(ip).country
|
||||||
|
@ -75,7 +75,7 @@ class UserForm(forms.Form):
|
|||||||
if errors:
|
if errors:
|
||||||
raise forms.ValidationError(errors)
|
raise forms.ValidationError(errors)
|
||||||
|
|
||||||
def _validate_login_id_unique(self):
|
def _validate_login_id_unique(self) -> None:
|
||||||
"""Validates whether the log in ID is unique.
|
"""Validates whether the log in ID is unique.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -93,7 +93,7 @@ class UserForm(forms.Form):
|
|||||||
self.add_error("login_id", error)
|
self.add_error("login_id", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_password_new_required(self):
|
def _validate_password_new_required(self) -> None:
|
||||||
"""Validates whether the password is entered for newly-created users.
|
"""Validates whether the password is entered for newly-created users.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -108,7 +108,7 @@ class UserForm(forms.Form):
|
|||||||
self.add_error("password", error)
|
self.add_error("password", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_password_login_id_changed_required(self):
|
def _validate_password_login_id_changed_required(self) -> None:
|
||||||
"""Validates whether the password is entered for users whose login ID
|
"""Validates whether the password is entered for users whose login ID
|
||||||
changed.
|
changed.
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ class UserForm(forms.Form):
|
|||||||
self.add_error("password", error)
|
self.add_error("password", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_password2_required(self):
|
def _validate_password2_required(self) -> None:
|
||||||
"""Validates whether the second password is entered.
|
"""Validates whether the second password is entered.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -145,7 +145,7 @@ class UserForm(forms.Form):
|
|||||||
self.add_error("password2", error)
|
self.add_error("password2", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_passwords_equal(self):
|
def _validate_passwords_equal(self) -> None:
|
||||||
"""Validates whether the two passwords are equa.
|
"""Validates whether the two passwords are equa.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@ -162,7 +162,7 @@ class UserForm(forms.Form):
|
|||||||
self.add_error("password2", error)
|
self.add_error("password2", error)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def _validate_is_disabled_not_oneself(self):
|
def _validate_is_disabled_not_oneself(self) -> None:
|
||||||
"""Validates whether the user tries to disable herself
|
"""Validates whether the user tries to disable herself
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
"""The data models of the Mia core application.
|
"""The data models of the Mia core application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import datetime
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from dirtyfields import DirtyFieldsMixin
|
from dirtyfields import DirtyFieldsMixin
|
||||||
@ -57,11 +56,12 @@ class Country(DirtyFieldsMixin, models.Model):
|
|||||||
return self.code.__str__() + " " + self.name.__str__()
|
return self.code.__str__() + " " + self.name.__str__()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self) -> str:
|
||||||
|
"""The country name in the current language."""
|
||||||
return get_multi_lingual_attr(self, "name", "en")
|
return get_multi_lingual_attr(self, "name", "en")
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, value):
|
def name(self, value: str) -> None:
|
||||||
set_multi_lingual_attr(self, "name", value)
|
set_multi_lingual_attr(self, "name", value)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -101,11 +101,11 @@ class User(DirtyFieldsMixin, models.Model):
|
|||||||
USERNAME_FIELD = "login_id"
|
USERNAME_FIELD = "login_id"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_anonymous(self):
|
def is_anonymous(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_authenticated(self):
|
def is_authenticated(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_password(self):
|
def set_password(self):
|
||||||
@ -142,16 +142,16 @@ class User(DirtyFieldsMixin, models.Model):
|
|||||||
F"{login_id}:{settings.DIGEST_REALM}:{password}")
|
F"{login_id}:{settings.DIGEST_REALM}:{password}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def md5(value):
|
def md5(value: str) -> str:
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
m.update(value.encode("utf-8"))
|
m.update(value.encode("utf-8"))
|
||||||
return m.hexdigest()
|
return m.hexdigest()
|
||||||
|
|
||||||
def is_in_use(self):
|
def is_in_use(self) -> bool:
|
||||||
"""Returns whether this user is in use.
|
"""Returns whether this user is in use.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if this user is in use, or False otherwise.
|
True if this user is in use, or False otherwise.
|
||||||
"""
|
"""
|
||||||
for table in connection.introspection.table_names():
|
for table in connection.introspection.table_names():
|
||||||
if self._is_in_use_with(F"SELECT * FROM {table}"
|
if self._is_in_use_with(F"SELECT * FROM {table}"
|
||||||
@ -163,14 +163,14 @@ class User(DirtyFieldsMixin, models.Model):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _is_in_use_with(self, sql):
|
def _is_in_use_with(self, sql: str) -> bool:
|
||||||
"""Returns whether this user is in use with a specific SQL statement.
|
"""Returns whether this user is in use with a specific SQL statement.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sql (str): The SQL query statement
|
sql: The SQL query statement
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if this user is in use, or False otherwise.
|
True if this user is in use, or False otherwise.
|
||||||
"""
|
"""
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
try:
|
try:
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
from datetime import date
|
||||||
|
from typing import Optional, List, Any, Union
|
||||||
|
|
||||||
from django.core.serializers.json import DjangoJSONEncoder
|
from django.core.serializers.json import DjangoJSONEncoder
|
||||||
from django.template import defaultfilters
|
from django.template import defaultfilters
|
||||||
@ -33,72 +35,73 @@ class Period:
|
|||||||
"""The template helper for the period chooser.
|
"""The template helper for the period chooser.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
spec (str): The current period specification
|
spec: The current period specification
|
||||||
data_start (datetime.date): The available first day of the data.
|
data_start: The available first day of the data.
|
||||||
data_end (datetime.date): The available last day of the data.
|
data_end: The available last day of the data.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: When the period specification is invalid.
|
ValueError: When the period specification is invalid.
|
||||||
"""
|
"""
|
||||||
def __init__(self, spec=None, data_start=None, data_end=None):
|
def __init__(self, spec: str = None, data_start: datetime.date = None,
|
||||||
|
data_end: datetime.date = None):
|
||||||
# Raises ValueError
|
# Raises ValueError
|
||||||
self._period = self.Parser(spec)
|
self._period = self.Parser(spec)
|
||||||
self._data_start = data_start
|
self._data_start = data_start
|
||||||
self._data_end = data_end
|
self._data_end = data_end
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def spec(self):
|
def spec(self) -> str:
|
||||||
"""Returns the period specification.
|
"""Returns the period specification.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The period specification.
|
The period specification.
|
||||||
"""
|
"""
|
||||||
return self._period.spec
|
return self._period.spec
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def start(self):
|
def start(self) -> datetime.date:
|
||||||
"""Returns the start day of the currently-specified period.
|
"""Returns the start day of the currently-specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
datetime.date: The start day of the currently-specified period.
|
The start day of the currently-specified period.
|
||||||
"""
|
"""
|
||||||
return self._period.start
|
return self._period.start
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end(self):
|
def end(self) -> datetime.date:
|
||||||
"""Returns the end day of the currently-specified period.
|
"""Returns the end day of the currently-specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
datetime.date: The end day of the currently-specified period.
|
The end day of the currently-specified period.
|
||||||
"""
|
"""
|
||||||
return self._period.end
|
return self._period.end
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self) -> str:
|
||||||
"""Returns the text description of the currently-specified period.
|
"""Returns the text description of the currently-specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The text description of the currently-specified period
|
The text description of the currently-specified period
|
||||||
"""
|
"""
|
||||||
return self._period.description
|
return self._period.description
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prep_desc(self):
|
def prep_desc(self) -> str:
|
||||||
"""Returns the text description with preposition of the
|
"""Returns the text description with preposition of the
|
||||||
currently-specified period.
|
currently-specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The text description with preposition of the
|
The text description with preposition of the currently-specified
|
||||||
currently-specified period
|
period.
|
||||||
"""
|
"""
|
||||||
return self._period.prep_desc
|
return self._period.prep_desc
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_last_month_start():
|
def _get_last_month_start() -> datetime.date:
|
||||||
"""Returns the first day of the last month.
|
"""Returns the first day of the last month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
datetime.date: The first day of the last month.
|
The first day of the last month.
|
||||||
"""
|
"""
|
||||||
today = timezone.localdate()
|
today = timezone.localdate()
|
||||||
month = today.month - 1
|
month = today.month - 1
|
||||||
@ -109,11 +112,11 @@ class Period:
|
|||||||
return datetime.date(year, month, 1)
|
return datetime.date(year, month, 1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_next_month_start():
|
def _get_next_month_start() -> datetime.date:
|
||||||
"""Returns the first day of the next month.
|
"""Returns the first day of the next month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
datetime.date: The first day of the next month.
|
The first day of the next month.
|
||||||
"""
|
"""
|
||||||
today = timezone.localdate()
|
today = timezone.localdate()
|
||||||
month = today.month + 1
|
month = today.month + 1
|
||||||
@ -123,12 +126,12 @@ class Period:
|
|||||||
year = year + 1
|
year = year + 1
|
||||||
return datetime.date(year, month, 1)
|
return datetime.date(year, month, 1)
|
||||||
|
|
||||||
def this_month(self):
|
def this_month(self) -> Optional[str]:
|
||||||
"""Returns the specification of this month.
|
"""Returns the specification of this month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of this month, or None if there is no
|
The specification of this month, or None if there is no data in or
|
||||||
data in or before this month.
|
before this month.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -139,12 +142,12 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return dateformat.format(today, "Y-m")
|
return dateformat.format(today, "Y-m")
|
||||||
|
|
||||||
def last_month(self):
|
def last_month(self) -> Optional[str]:
|
||||||
"""Returns the specification of last month.
|
"""Returns the specification of last month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of last month, or None if there is no
|
The specification of last month, or None if there is no data in or
|
||||||
data in or before last month.
|
before last month.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -155,25 +158,25 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return dateformat.format(last_month_start, "Y-m")
|
return dateformat.format(last_month_start, "Y-m")
|
||||||
|
|
||||||
def since_last_month(self):
|
def since_last_month(self) -> Optional[str]:
|
||||||
"""Returns the specification since last month.
|
"""Returns the specification since last month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification since last month, or None if there is
|
The specification since last month, or None if there is no data in
|
||||||
no data in or before last month.
|
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
|
||||||
return last_month + "-"
|
return last_month + "-"
|
||||||
|
|
||||||
def has_months_to_choose(self):
|
def has_months_to_choose(self) -> bool:
|
||||||
"""Returns whether there are months to choose besides this month and
|
"""Returns whether there are months to choose besides this month and
|
||||||
last month.
|
last month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if there are months to choose besides this month and
|
True if there are months to choose besides this month and last
|
||||||
last month, or False otherwise.
|
month, or False otherwise.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return False
|
return False
|
||||||
@ -183,14 +186,13 @@ class Period:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def chosen_month(self):
|
def chosen_month(self) -> Optional[str]:
|
||||||
"""Returns the specification of the chosen month, or None if the
|
"""Returns the specification of the chosen month, or None if the
|
||||||
current period is not a month or is out of available data range.
|
current period is not a month or is out of available data range.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of the chosen month, or None if the
|
The specification of the chosen month, or None if the current
|
||||||
current period is not a month or is out of available data
|
period is not a month or is out of available data range.
|
||||||
range.
|
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -203,12 +205,12 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return self._period.spec
|
return self._period.spec
|
||||||
|
|
||||||
def this_year(self):
|
def this_year(self) -> Optional[str]:
|
||||||
"""Returns the specification of this year.
|
"""Returns the specification of this year.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of this year, or None if there is no
|
The specification of this year, or None if there is no data in or
|
||||||
data in or before this year.
|
before this year.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -217,12 +219,12 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return str(this_year)
|
return str(this_year)
|
||||||
|
|
||||||
def last_year(self):
|
def last_year(self) -> Optional[str]:
|
||||||
"""Returns the specification of last year.
|
"""Returns the specification of last year.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of last year, or None if there is no
|
The specification of last year, or None if there is no data in or
|
||||||
data in or before last year.
|
before last year.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -231,13 +233,13 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return str(last_year)
|
return str(last_year)
|
||||||
|
|
||||||
def has_years_to_choose(self):
|
def has_years_to_choose(self) -> bool:
|
||||||
"""Returns whether there are years to choose besides this year and
|
"""Returns whether there are years to choose besides this year and
|
||||||
last year.
|
last year.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if there are years to choose besides this year and
|
True if there are years to choose besides this year and last year,
|
||||||
last year, or False otherwise.
|
or False otherwise.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return False
|
return False
|
||||||
@ -248,11 +250,12 @@ class Period:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def years_to_choose(self):
|
def years_to_choose(self) -> Optional[List[str]]:
|
||||||
"""Returns the years to choose besides this year and last year.
|
"""Returns the years to choose besides this year and last year.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[str]: The years to choose besides this year and last year.
|
The years to choose besides this year and last year, or None if
|
||||||
|
there is no data.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -263,12 +266,12 @@ class Period:
|
|||||||
self._data_end.year, this_year, -1)]
|
self._data_end.year, this_year, -1)]
|
||||||
return after + before[::-1]
|
return after + before[::-1]
|
||||||
|
|
||||||
def today(self):
|
def today(self) -> Optional[None]:
|
||||||
"""Returns the specification of today.
|
"""Returns the specification of today.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(str): The specification of today, or None if there is no data
|
The specification of today, or None if there is no data in or
|
||||||
in or before today.
|
before today.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -277,12 +280,12 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return dateformat.format(today, "Y-m-d")
|
return dateformat.format(today, "Y-m-d")
|
||||||
|
|
||||||
def yesterday(self):
|
def yesterday(self) -> Optional[str]:
|
||||||
"""Returns the specification of yesterday.
|
"""Returns the specification of yesterday.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(str): The specification of yesterday, or None if there is no data
|
The specification of yesterday, or None if there is no data in or
|
||||||
in or before yesterday.
|
before yesterday.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -291,21 +294,21 @@ class Period:
|
|||||||
return None
|
return None
|
||||||
return dateformat.format(yesterday, "Y-m-d")
|
return dateformat.format(yesterday, "Y-m-d")
|
||||||
|
|
||||||
def chosen_day(self):
|
def chosen_day(self) -> str:
|
||||||
"""Returns the specification of the chosen day.
|
"""Returns the specification of the chosen day.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(str): The specification of the chosen day, or the start day
|
The specification of the chosen day, or the start day of the period
|
||||||
of the period if the current period is not a day.
|
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")
|
||||||
|
|
||||||
def has_days_to_choose(self):
|
def has_days_to_choose(self) -> bool:
|
||||||
"""Returns whether there are more than one day to choose from.
|
"""Returns whether there are more than one day to choose from.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if there are more than one day to choose from,
|
True if there are more than one day to choose from, or False
|
||||||
or False otherwise.
|
otherwise.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return False
|
return False
|
||||||
@ -313,33 +316,35 @@ class Period:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def first_day(self):
|
def first_day(self) -> Optional[str]:
|
||||||
"""Returns the specification of the available first day.
|
"""Returns the specification of the available first day.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The specification of the available first day.
|
The specification of the available first day, or None if there is
|
||||||
|
no data.
|
||||||
"""
|
"""
|
||||||
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")
|
||||||
|
|
||||||
def last_day(self):
|
def last_day(self) -> Optional[str]:
|
||||||
"""Returns the specification of the available last day.
|
"""Returns the specification of the available last day.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The specification of the available last day.
|
The specification of the available last day, or None if there is no
|
||||||
|
data.
|
||||||
"""
|
"""
|
||||||
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")
|
||||||
|
|
||||||
def chosen_start(self):
|
def chosen_start(self) -> Optional[str]:
|
||||||
"""Returns the specification of of the first day of the
|
"""Returns the specification of of the first day of the
|
||||||
specified period.
|
specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The specification of of the first day of the
|
The specification of of the first day of the specified period, or
|
||||||
specified period.
|
None if there is no data.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -348,13 +353,13 @@ class Period:
|
|||||||
else self._data_start
|
else self._data_start
|
||||||
return dateformat.format(day, "Y-m-d")
|
return dateformat.format(day, "Y-m-d")
|
||||||
|
|
||||||
def chosen_end(self):
|
def chosen_end(self) -> Optional[str]:
|
||||||
"""Returns the specification of of the last day of the
|
"""Returns the specification of of the last day of the
|
||||||
specified period.
|
specified period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The specification of of the last day of the
|
The specification of of the last day of the specified period, or
|
||||||
specified period.
|
None if there is data.
|
||||||
"""
|
"""
|
||||||
if self._data_end is None:
|
if self._data_end is None:
|
||||||
return None
|
return None
|
||||||
@ -363,12 +368,12 @@ class Period:
|
|||||||
else self._data_end
|
else self._data_end
|
||||||
return dateformat.format(day, "Y-m-d")
|
return dateformat.format(day, "Y-m-d")
|
||||||
|
|
||||||
def period_before(self):
|
def period_before(self) -> Optional[str]:
|
||||||
"""Returns the specification of the period before the current period.
|
"""Returns the specification of the period before the current period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str|None: The specification of the period before the current
|
The specification of the period before the current period, or None
|
||||||
period, or None if there is no data before the current period.
|
if there is no data before the current period.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -381,11 +386,12 @@ class Period:
|
|||||||
return dateformat.format(previous_day, "-Y-m")
|
return dateformat.format(previous_day, "-Y-m")
|
||||||
return dateformat.format(previous_day, "-Y-m-d")
|
return dateformat.format(previous_day, "-Y-m-d")
|
||||||
|
|
||||||
def month_picker_params(self):
|
def month_picker_params(self) -> Optional[str]:
|
||||||
"""Returns the parameters for the month-picker, as a JSON text string.
|
"""Returns the parameters for the month-picker, as a JSON text string.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The parameters for the month-picker, as a JSON text string.
|
The parameters for the month-picker, as a JSON text string, or None
|
||||||
|
if there is no data.
|
||||||
"""
|
"""
|
||||||
if self._data_start is None:
|
if self._data_start is None:
|
||||||
return None
|
return None
|
||||||
@ -398,7 +404,7 @@ class Period:
|
|||||||
})
|
})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default_spec():
|
def default_spec() -> str:
|
||||||
"""Returns the specification for the default period.
|
"""Returns the specification for the default period.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -422,9 +428,9 @@ class Period:
|
|||||||
description (str): The text description of the period.
|
description (str): The text description of the period.
|
||||||
prep_desc (str): The text description with preposition.
|
prep_desc (str): The text description with preposition.
|
||||||
"""
|
"""
|
||||||
VERY_START = datetime.date(1990, 1, 1)
|
VERY_START: datetime.date = datetime.date(1990, 1, 1)
|
||||||
|
|
||||||
def __init__(self, spec):
|
def __init__(self, spec: str):
|
||||||
self.spec = None
|
self.spec = None
|
||||||
self.start = None
|
self.start = None
|
||||||
self.end = None
|
self.end = None
|
||||||
@ -574,7 +580,7 @@ class Period:
|
|||||||
# Wrong period format
|
# Wrong period format
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
def _set_this_month(self):
|
def _set_this_month(self) -> None:
|
||||||
"""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")
|
||||||
@ -583,14 +589,14 @@ class Period:
|
|||||||
self.description = gettext("This Month")
|
self.description = gettext("This Month")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _month_last_day(day):
|
def _month_last_day(day: datetime.date) -> datetime.date:
|
||||||
"""Calculates and returns the last day of a month.
|
"""Calculates and returns the last day of a month.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
day (datetime.date): A day in the month.
|
day: A day in the month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
date: The last day in the month
|
The last day in the month
|
||||||
"""
|
"""
|
||||||
next_month = day.month + 1
|
next_month = day.month + 1
|
||||||
next_year = day.year
|
next_year = day.year
|
||||||
@ -601,15 +607,15 @@ class Period:
|
|||||||
next_year, next_month, 1) - datetime.timedelta(days=1)
|
next_year, next_month, 1) - datetime.timedelta(days=1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _month_text(year, month):
|
def _month_text(year: int, month: int) -> str:
|
||||||
"""Returns the text description of a month.
|
"""Returns the text description of a month.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
year (int): The year.
|
year: The year.
|
||||||
month (int): The month.
|
month: The month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The description of the month.
|
The description of the month.
|
||||||
"""
|
"""
|
||||||
today = timezone.localdate()
|
today = timezone.localdate()
|
||||||
if year == today.year and month == today.month:
|
if year == today.year and month == today.month:
|
||||||
@ -625,14 +631,14 @@ class Period:
|
|||||||
return "%d/%d" % (year, month)
|
return "%d/%d" % (year, month)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _year_text(year):
|
def _year_text(year: int) -> str:
|
||||||
"""Returns the text description of a year.
|
"""Returns the text description of a year.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
year (int): The year.
|
year: The year.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The description of the year.
|
The description of the year.
|
||||||
"""
|
"""
|
||||||
this_year = timezone.localdate().year
|
this_year = timezone.localdate().year
|
||||||
if year == this_year:
|
if year == this_year:
|
||||||
@ -642,14 +648,14 @@ class Period:
|
|||||||
return str(year)
|
return str(year)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _date_text(day):
|
def _date_text(day: datetime.date) -> str:
|
||||||
"""Returns the text description of a day.
|
"""Returns the text description of a day.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
day (datetime.date): The date.
|
day: The date.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The description of the day.
|
The description of the day.
|
||||||
"""
|
"""
|
||||||
today = timezone.localdate()
|
today = timezone.localdate()
|
||||||
if day == today:
|
if day == today:
|
||||||
|
@ -19,16 +19,18 @@
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import random
|
import random
|
||||||
|
from typing import Dict, Mapping, Any, Optional
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect, HttpRequest
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
from .utils import UrlBuilder
|
from .utils import UrlBuilder
|
||||||
|
|
||||||
STORAGE_KEY = "stored_post"
|
STORAGE_KEY: str = "stored_post"
|
||||||
|
|
||||||
|
|
||||||
def error_redirect(request, url, post):
|
def error_redirect(request: HttpRequest, url: str,
|
||||||
|
post: Dict[str, str]) -> HttpResponseRedirect:
|
||||||
"""Redirects to a specific URL on error, with the POST data ID appended
|
"""Redirects to a specific URL on error, with the POST data ID appended
|
||||||
as the query parameter "s". The POST data can be loaded with the
|
as the query parameter "s". The POST data can be loaded with the
|
||||||
get_previous_post() utility.
|
get_previous_post() utility.
|
||||||
@ -45,7 +47,7 @@ def error_redirect(request, url, post):
|
|||||||
return redirect(str(UrlBuilder(url).query(s=post_id)))
|
return redirect(str(UrlBuilder(url).query(s=post_id)))
|
||||||
|
|
||||||
|
|
||||||
def get_previous_post(request):
|
def get_previous_post(request: HttpRequest) -> Optional[Dict[str, str]]:
|
||||||
"""Retrieves the previously-stored POST data.
|
"""Retrieves the previously-stored POST data.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -59,16 +61,16 @@ def get_previous_post(request):
|
|||||||
return _retrieve(request, request.GET["s"])
|
return _retrieve(request, request.GET["s"])
|
||||||
|
|
||||||
|
|
||||||
def _store(request, post):
|
def _store(request: HttpRequest, post: Dict[str, str]) -> str:
|
||||||
"""Stores the POST data into the session, and returns the POST data ID that
|
"""Stores the POST data into the session, and returns the POST data ID that
|
||||||
can be used to retrieve it later with _retrieve().
|
can be used to retrieve it later with _retrieve().
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
post (dict): The POST data.
|
post: The POST data.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The POST data ID
|
The POST data ID
|
||||||
"""
|
"""
|
||||||
if STORAGE_KEY not in request.session:
|
if STORAGE_KEY not in request.session:
|
||||||
request.session[STORAGE_KEY] = {}
|
request.session[STORAGE_KEY] = {}
|
||||||
@ -77,15 +79,15 @@ def _store(request, post):
|
|||||||
return post_id
|
return post_id
|
||||||
|
|
||||||
|
|
||||||
def _retrieve(request, post_id):
|
def _retrieve(request: HttpRequest, post_id: str) -> Optional[Dict[str, str]]:
|
||||||
"""Retrieves the POST data from the storage.
|
"""Retrieves the POST data from the storage.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
post_id (str): The POST data ID.
|
post_id: The POST data ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict: The POST data, or None if the corresponding data does not exist.
|
The POST data, or None if the corresponding data does not exist.
|
||||||
"""
|
"""
|
||||||
if STORAGE_KEY not in request.session:
|
if STORAGE_KEY not in request.session:
|
||||||
return None
|
return None
|
||||||
@ -94,7 +96,7 @@ def _retrieve(request, post_id):
|
|||||||
return request.session[STORAGE_KEY][post_id]
|
return request.session[STORAGE_KEY][post_id]
|
||||||
|
|
||||||
|
|
||||||
def _new_post_id(post_store):
|
def _new_post_id(post_store: Mapping[int, Any]) -> str:
|
||||||
"""Generates and returns a new POST ID that does not exist yet.
|
"""Generates and returns a new POST ID that does not exist yet.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -18,12 +18,14 @@
|
|||||||
"""The template tags and filters of the Mia core application.
|
"""The template tags and filters of the Mia core application.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import datetime
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import titlecase
|
import titlecase
|
||||||
from django import template
|
from django import template
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.template import defaultfilters
|
from django.template import defaultfilters, RequestContext
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.safestring import SafeString
|
from django.utils.safestring import SafeString
|
||||||
@ -35,31 +37,31 @@ register = template.Library()
|
|||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def setvar(context, key, value):
|
def setvar(context: RequestContext, key: str, value: Any) -> str:
|
||||||
"""Sets a variable in the template.
|
"""Sets a variable in the template.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (Context): the context
|
context: the context
|
||||||
key (str): The variable name
|
key: The variable name
|
||||||
value (str): The variable value
|
value: The variable value
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: An empty string.
|
An empty string.
|
||||||
"""
|
"""
|
||||||
context.dicts[0][key] = value
|
context.dicts[0][key] = value
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def url_period(context, period_spec):
|
def url_period(context: RequestContext, period_spec: str) -> str:
|
||||||
"""Returns the current URL with a new period.
|
"""Returns the current URL with a new period.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (RequestContext): The request context.
|
context: The request context.
|
||||||
period_spec (str): The period specification.
|
period_spec: The period specification.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The current URL with the new period.
|
The current URL with the new period.
|
||||||
"""
|
"""
|
||||||
view_name = "%s:%s" % (
|
view_name = "%s:%s" % (
|
||||||
context.request.resolver_match.app_name,
|
context.request.resolver_match.app_name,
|
||||||
@ -70,47 +72,47 @@ def url_period(context, period_spec):
|
|||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def url_with_return(context, url):
|
def url_with_return(context: RequestContext, url: str) -> str:
|
||||||
"""Returns the URL with the current page added as the "r" query parameter,
|
"""Returns the URL with the current page added as the "r" query parameter,
|
||||||
so that returning to this page is possible.
|
so that returning to this page is possible.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (RequestContext): The request context.
|
context: The request context.
|
||||||
url (str): The URL.
|
url: The URL.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The URL with the current page added as the "r" query parameter.
|
The URL with the current page added as the "r" query parameter.
|
||||||
"""
|
"""
|
||||||
return str(UrlBuilder(url).query(
|
return str(UrlBuilder(url).query(
|
||||||
r=str(UrlBuilder(context.request.get_full_path()).remove("s"))))
|
r=str(UrlBuilder(context.request.get_full_path()).remove("s"))))
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def url_keep_return(context, url):
|
def url_keep_return(context: RequestContext, url: str) -> str:
|
||||||
"""Returns the URL with the current "r" query parameter set, so that the
|
"""Returns the URL with the current "r" query parameter set, so that the
|
||||||
next processor can still return to the same page.
|
next processor can still return to the same page.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (RequestContext): The request context.
|
context: The request context.
|
||||||
url (str): The URL.
|
url: The URL.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The URL with the current "r" query parameter set.
|
The URL with the current "r" query parameter set.
|
||||||
"""
|
"""
|
||||||
return str(UrlBuilder(url).query(r=context.request.GET.get("r")))
|
return str(UrlBuilder(url).query(r=context.request.GET.get("r")))
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def add_css(context, url):
|
def add_css(context: RequestContext, url: str) -> str:
|
||||||
"""Adds a local CSS file. The file is added to the "css" template
|
"""Adds a local CSS file. The file is added to the "css" template
|
||||||
list variable.
|
list variable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (RequestContext): The request context.
|
context: The request context.
|
||||||
url (str): The URL or path of the CSS file.
|
url: The URL or path of the CSS file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: An empty string
|
An empty string
|
||||||
"""
|
"""
|
||||||
if "css" not in context.dicts[0]:
|
if "css" not in context.dicts[0]:
|
||||||
context.dicts[0]["css"] = []
|
context.dicts[0]["css"] = []
|
||||||
@ -119,16 +121,16 @@ def add_css(context, url):
|
|||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def add_js(context, url):
|
def add_js(context: RequestContext, url: str) -> str:
|
||||||
"""Adds a local JavaScript file. The file is added to the "js" template
|
"""Adds a local JavaScript file. The file is added to the "js" template
|
||||||
list variable.
|
list variable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
context (RequestContext): The request context.
|
context: The request context.
|
||||||
url (str): The URL or path of the JavaScript file.
|
url: The URL or path of the JavaScript file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: An empty string
|
An empty string
|
||||||
"""
|
"""
|
||||||
if "js" not in context.dicts[0]:
|
if "js" not in context.dicts[0]:
|
||||||
context.dicts[0]["js"] = []
|
context.dicts[0]["js"] = []
|
||||||
@ -137,14 +139,14 @@ def add_js(context, url):
|
|||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def smart_date(value):
|
def smart_date(value: datetime.date) -> str:
|
||||||
"""Formats the date for human friendliness.
|
"""Formats the date for human friendliness.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (datetime.date): The date.
|
value: The date.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The human-friendly format of the date.
|
The human-friendly format of the date.
|
||||||
"""
|
"""
|
||||||
if value == date.today():
|
if value == date.today():
|
||||||
return gettext("Today")
|
return gettext("Today")
|
||||||
@ -156,14 +158,14 @@ def smart_date(value):
|
|||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def smart_month(value):
|
def smart_month(value: datetime.date) -> str:
|
||||||
"""Formats the month for human friendliness.
|
"""Formats the month for human friendliness.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (datetime.date): The month.
|
value: The month.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The human-friendly format of the month.
|
The human-friendly format of the month.
|
||||||
"""
|
"""
|
||||||
today = timezone.localdate()
|
today = timezone.localdate()
|
||||||
if value.year == today.year and value.month == today.month:
|
if value.year == today.year and value.month == today.month:
|
||||||
@ -179,14 +181,14 @@ def smart_month(value):
|
|||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def title_case(value):
|
def title_case(value: str) -> str:
|
||||||
"""Formats the title in a proper American-English case.
|
"""Formats the title in a proper American-English case.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (str): The title.
|
value: The title.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: The title in a proper American-English case.
|
The title in a proper American-English case.
|
||||||
"""
|
"""
|
||||||
value = str(value)
|
value = str(value)
|
||||||
if isinstance(value, SafeString):
|
if isinstance(value, SafeString):
|
||||||
@ -195,16 +197,15 @@ def title_case(value):
|
|||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def is_in_section(request, section_name):
|
def is_in_section(request: HttpRequest, section_name: str) -> bool:
|
||||||
"""Returns whether the request is currently in a section.
|
"""Returns whether the request is currently in a section.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
section_name (str): The view name of this section.
|
section_name: The view name of this section.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if the request is currently in this section, or False
|
True if the request is currently in this section, or False otherwise.
|
||||||
otherwise
|
|
||||||
"""
|
"""
|
||||||
if request is None:
|
if request is None:
|
||||||
return False
|
return False
|
||||||
|
@ -20,20 +20,22 @@
|
|||||||
"""
|
"""
|
||||||
import random
|
import random
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
from typing import Dict, List, Any, Type
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Model, Q
|
from django.db.models import Model, Q
|
||||||
|
from django.http import HttpRequest
|
||||||
from django.utils.translation import pgettext, get_language
|
from django.utils.translation import pgettext, get_language
|
||||||
|
|
||||||
|
|
||||||
def new_pk(cls):
|
def new_pk(cls: Type[Model]) -> int:
|
||||||
"""Finds a random ID that does not conflict with the existing data records.
|
"""Finds a random ID that does not conflict with the existing data records.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cls (class): The Django model class.
|
cls: The Django model class.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: The new random ID.
|
The new random ID.
|
||||||
"""
|
"""
|
||||||
while True:
|
while True:
|
||||||
pk = random.randint(100000000, 999999999)
|
pk = random.randint(100000000, 999999999)
|
||||||
@ -43,7 +45,7 @@ def new_pk(cls):
|
|||||||
return pk
|
return pk
|
||||||
|
|
||||||
|
|
||||||
def strip_post(post):
|
def strip_post(post: Dict[str, str]) -> None:
|
||||||
"""Strips the values of the POSTed data. Empty strings are removed.
|
"""Strips the values of the POSTed data. Empty strings are removed.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -59,7 +61,7 @@ class Language:
|
|||||||
"""A language.
|
"""A language.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
language (str): The Django language code.
|
language: The Django language code.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
id (str): The language ID
|
id (str): The language ID
|
||||||
@ -67,7 +69,7 @@ class Language:
|
|||||||
locale (str); The locale name of this language.
|
locale (str); The locale name of this language.
|
||||||
is_default (bool): Whether this is the default language.
|
is_default (bool): Whether this is the default language.
|
||||||
"""
|
"""
|
||||||
def __init__(self, language):
|
def __init__(self, language: str):
|
||||||
self.id = language
|
self.id = language
|
||||||
self.db = "_" + language.lower().replace("-", "_")
|
self.db = "_" + language.lower().replace("-", "_")
|
||||||
if language == "zh-hant":
|
if language == "zh-hant":
|
||||||
@ -87,17 +89,18 @@ class Language:
|
|||||||
return Language(get_language())
|
return Language(get_language())
|
||||||
|
|
||||||
|
|
||||||
def get_multi_lingual_attr(model, name, default=None):
|
def get_multi_lingual_attr(model: Model, name: str,
|
||||||
|
default: str = None) -> str:
|
||||||
"""Returns a multi-lingual attribute of a data model.
|
"""Returns a multi-lingual attribute of a data model.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model (object): The data model.
|
model: The data model.
|
||||||
name (str): The attribute name.
|
name: The attribute name.
|
||||||
default (str): The default language.
|
default: The default language.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(any): The attribute in this language, or in the default
|
The attribute in this language, or in the default language if there is
|
||||||
language if there is no content in the current language.
|
no content in the current language.
|
||||||
"""
|
"""
|
||||||
language = Language.current()
|
language = Language.current()
|
||||||
title = getattr(model, name + language.db)
|
title = getattr(model, name + language.db)
|
||||||
@ -110,27 +113,27 @@ def get_multi_lingual_attr(model, name, default=None):
|
|||||||
return getattr(model, name + Language.default().db)
|
return getattr(model, name + Language.default().db)
|
||||||
|
|
||||||
|
|
||||||
def set_multi_lingual_attr(model, name, value):
|
def set_multi_lingual_attr(model: Model, name: str, value: str) -> None:
|
||||||
"""Sets a multi-lingual attribute of a data model.
|
"""Sets a multi-lingual attribute of a data model.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
model (object): The data model.
|
model: The data model.
|
||||||
name (str): The attribute name.
|
name: The attribute name.
|
||||||
value (any): The new value
|
value: The new value
|
||||||
"""
|
"""
|
||||||
language = Language.current()
|
language = Language.current()
|
||||||
setattr(model, name + language.db, value)
|
setattr(model, name + language.db, value)
|
||||||
|
|
||||||
|
|
||||||
def get_multi_lingual_search(attr, query):
|
def get_multi_lingual_search(attr: str, query: str) -> Q:
|
||||||
"""Returns the query condition on a multi-lingual attribute.
|
"""Returns the query condition on a multi-lingual attribute.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
attr (str): The base name of the multi-lingual attribute.
|
attr: The base name of the multi-lingual attribute.
|
||||||
query (str): The query.
|
query: The query.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Q: The query condition
|
The query condition
|
||||||
"""
|
"""
|
||||||
language = Language.current()
|
language = Language.current()
|
||||||
if language.is_default:
|
if language.is_default:
|
||||||
@ -147,10 +150,10 @@ class UrlBuilder:
|
|||||||
"""The URL builder.
|
"""The URL builder.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
base_path (str): the base path
|
path (str): the base path
|
||||||
params (list[Param]): The query parameters
|
params (list[Param]): The query parameters
|
||||||
"""
|
"""
|
||||||
def __init__(self, start_url):
|
def __init__(self, start_url: str):
|
||||||
"""Constructs a new URL builder.
|
"""Constructs a new URL builder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -158,10 +161,10 @@ class UrlBuilder:
|
|||||||
"""
|
"""
|
||||||
pos = start_url.find("?")
|
pos = start_url.find("?")
|
||||||
if pos == -1:
|
if pos == -1:
|
||||||
self.base_path = start_url
|
self.path = start_url
|
||||||
self.params = []
|
self.params = []
|
||||||
return
|
return
|
||||||
self.base_path = start_url[:pos]
|
self.path = start_url[:pos]
|
||||||
self.params = []
|
self.params = []
|
||||||
for piece in start_url[pos + 1:].split("&"):
|
for piece in start_url[pos + 1:].split("&"):
|
||||||
pos = piece.find("=")
|
pos = piece.find("=")
|
||||||
@ -219,25 +222,25 @@ class UrlBuilder:
|
|||||||
Returns:
|
Returns:
|
||||||
UrlBuilder: A copy of this URL builder.
|
UrlBuilder: A copy of this URL builder.
|
||||||
"""
|
"""
|
||||||
another = UrlBuilder(self.base_path)
|
another = UrlBuilder(self.path)
|
||||||
another.params = [
|
another.params = [
|
||||||
self.Param(x.name, x.value) for x in self.params]
|
self.Param(x.name, x.value) for x in self.params]
|
||||||
return another
|
return another
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
if len(self.params) == 0:
|
if len(self.params) == 0:
|
||||||
return self.base_path
|
return self.path
|
||||||
return self.base_path + "?" + "&".join([
|
return self.path + "?" + "&".join([
|
||||||
str(x) for x in self.params])
|
str(x) for x in self.params])
|
||||||
|
|
||||||
class Param:
|
class Param:
|
||||||
"""A query parameter.
|
"""A query parameter.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
name (str): The parameter name
|
name: The parameter name
|
||||||
value (str): The parameter value
|
value: The parameter value
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, value):
|
def __init__(self, name: str, value: str):
|
||||||
"""Constructs a new query parameter
|
"""Constructs a new query parameter
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -247,7 +250,7 @@ class UrlBuilder:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
"""Returns the string representation of this query
|
"""Returns the string representation of this query
|
||||||
parameter.
|
parameter.
|
||||||
|
|
||||||
@ -264,9 +267,9 @@ class Pagination:
|
|||||||
"""The pagination.
|
"""The pagination.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
items (list): All the items.
|
items: All the items.
|
||||||
is_reversed (bool): Whether we should display the last page first.
|
is_reversed: Whether we should display the last page first.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
PaginationException: With invalid pagination parameters
|
PaginationException: With invalid pagination parameters
|
||||||
@ -282,7 +285,8 @@ class Pagination:
|
|||||||
"""
|
"""
|
||||||
DEFAULT_PAGE_SIZE = 10
|
DEFAULT_PAGE_SIZE = 10
|
||||||
|
|
||||||
def __init__(self, request, items, is_reversed=False):
|
def __init__(self, request: HttpRequest, items: List[Any],
|
||||||
|
is_reversed: bool = False):
|
||||||
self.current_url = UrlBuilder(request.get_full_path())
|
self.current_url = UrlBuilder(request.get_full_path())
|
||||||
self.is_reversed = is_reversed
|
self.is_reversed = is_reversed
|
||||||
self.page_size = self.DEFAULT_PAGE_SIZE
|
self.page_size = self.DEFAULT_PAGE_SIZE
|
||||||
@ -334,7 +338,7 @@ class Pagination:
|
|||||||
"""Returns the navigation links of the pagination bar.
|
"""Returns the navigation links of the pagination bar.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[Link]: The navigation links of the pagination bar.
|
List[Link]: The navigation links of the pagination bar.
|
||||||
"""
|
"""
|
||||||
base_url = self.current_url.clone().remove("page").remove("s")
|
base_url = self.current_url.clone().remove("page").remove("s")
|
||||||
links = []
|
links = []
|
||||||
@ -443,14 +447,14 @@ class Pagination:
|
|||||||
"""Returns the page size options.
|
"""Returns the page size options.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[PageSizeOption]: The page size options.
|
List[PageSizeOption]: The page size options.
|
||||||
"""
|
"""
|
||||||
base_url = self.current_url.remove("page").remove("page-size")
|
base_url = self.current_url.remove("page").remove("page-size")
|
||||||
return [self.PageSizeOption(x, self._page_size_url(base_url, x))
|
return [self.PageSizeOption(x, self._page_size_url(base_url, x))
|
||||||
for x in [10, 100, 200]]
|
for x in [10, 100, 200]]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _page_size_url(base_url, size):
|
def _page_size_url(base_url: UrlBuilder, size: int) -> str:
|
||||||
"""Returns the URL for a new page size.
|
"""Returns the URL for a new page size.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -468,14 +472,14 @@ class Pagination:
|
|||||||
"""A page size option.
|
"""A page size option.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
size (int): The page size.
|
size: The page size.
|
||||||
url (str): The URL of this page size.
|
url: The URL of this page size.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
size (int): The page size.
|
size (int): The page size.
|
||||||
url (str): The URL for this page size.
|
url (str): The URL for this page size.
|
||||||
"""
|
"""
|
||||||
def __init__(self, size, url):
|
def __init__(self, size: int, url: str):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
@ -484,10 +488,10 @@ class PaginationException(Exception):
|
|||||||
"""The exception thrown with invalid pagination parameters.
|
"""The exception thrown with invalid pagination parameters.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url_builder (UrlBuilder): The canonical URL to redirect to.
|
url_builder: The canonical URL to redirect to.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
url (str): The canonical URL to redirect to.
|
url (str): The canonical URL to redirect to.
|
||||||
"""
|
"""
|
||||||
def __init__(self, url_builder):
|
def __init__(self, url_builder: UrlBuilder):
|
||||||
self.url = str(url_builder)
|
self.url = str(url_builder)
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import logout as logout_user
|
from django.contrib.auth import logout as logout_user
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse, HttpRequest, \
|
||||||
|
HttpResponseRedirect
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@ -47,14 +48,14 @@ class DeleteView(SuccessMessageMixin, CoreDeleteView):
|
|||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def logout(request):
|
def logout(request: HttpRequest) -> HttpResponseRedirect:
|
||||||
"""The view to log out a user.
|
"""The view to log out a user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpRedirectResponse: The redirect response.
|
The redirect response.
|
||||||
"""
|
"""
|
||||||
logout_user(request)
|
logout_user(request)
|
||||||
if "next" in request.POST:
|
if "next" in request.POST:
|
||||||
@ -80,15 +81,15 @@ class UserView(DetailView):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def user_form(request, user=None):
|
def user_form(request: HttpRequest, user: User = None) -> HttpResponse:
|
||||||
"""The view to edit an accounting transaction.
|
"""The view to edit an accounting transaction.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
user (User): The account.
|
user: The account.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
previous_post = stored_post.get_previous_post(request)
|
previous_post = stored_post.get_previous_post(request)
|
||||||
if previous_post is not None:
|
if previous_post is not None:
|
||||||
@ -108,15 +109,16 @@ def user_form(request, user=None):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def user_store(request, user=None):
|
def user_store(request: HttpRequest,
|
||||||
|
user: User = None) -> HttpResponseRedirect:
|
||||||
"""The view to store a user.
|
"""The view to store a user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
user (Account): The user.
|
user: The user.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponseRedirect: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
post = request.POST.dict()
|
post = request.POST.dict()
|
||||||
strip_post(post)
|
strip_post(post)
|
||||||
@ -148,15 +150,15 @@ def user_store(request, user=None):
|
|||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def user_delete(request, user):
|
def user_delete(request: HttpRequest, user: User) -> HttpResponseRedirect:
|
||||||
"""The view to delete an user.
|
"""The view to delete an user.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
user (User): The user.
|
user: The user.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponseRedirect: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
message = None
|
message = None
|
||||||
if user.pk == request.user.pk:
|
if user.pk == request.user.pk:
|
||||||
@ -177,14 +179,14 @@ def user_delete(request, user):
|
|||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@login_required
|
@login_required
|
||||||
def my_account_form(request):
|
def my_account_form(request: HttpRequest) -> HttpResponse:
|
||||||
"""The view to edit my account.
|
"""The view to edit my account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
previous_post = stored_post.get_previous_post(request)
|
previous_post = stored_post.get_previous_post(request)
|
||||||
if previous_post is not None:
|
if previous_post is not None:
|
||||||
@ -201,14 +203,14 @@ def my_account_form(request):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def my_account_store(request):
|
def my_account_store(request: HttpRequest) -> HttpResponseRedirect:
|
||||||
"""The view to store my account.
|
"""The view to store my account.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
HttpResponseRedirect: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
post = request.POST.dict()
|
post = request.POST.dict()
|
||||||
strip_post(post)
|
strip_post(post)
|
||||||
@ -232,15 +234,15 @@ def my_account_store(request):
|
|||||||
return redirect("mia_core:my-account")
|
return redirect("mia_core:my-account")
|
||||||
|
|
||||||
|
|
||||||
def api_users_exists(request, login_id):
|
def api_users_exists(request: HttpRequest, login_id: str) -> JsonResponse:
|
||||||
"""The view to check whether a user with a log in ID exists.
|
"""The view to check whether a user with a log in ID exists.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (HttpRequest): The request.
|
request: The request.
|
||||||
login_id (str): The log in ID.
|
login_id: The log in ID.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
JsonResponse: The response.
|
The response.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
User.objects.get(login_id=login_id)
|
User.objects.get(login_id=login_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user