Moved the source files to the "src" subdirectory.
This commit is contained in:
664
src/mia_core/utils.py
Normal file
664
src/mia_core/utils.py
Normal file
@ -0,0 +1,664 @@
|
||||
# The core application of the Mia project.
|
||||
# by imacat <imacat@mail.imacat.idv.tw>, 2020/7/1
|
||||
|
||||
# Copyright (c) 2020 imacat.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""The utilities of the Mia core application.
|
||||
|
||||
"""
|
||||
import datetime
|
||||
import random
|
||||
import urllib.parse
|
||||
from typing import Dict, List, Any, Type, Optional
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Model
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import pgettext, get_language
|
||||
|
||||
|
||||
def new_pk(cls: Type[Model]) -> int:
|
||||
"""Finds a random ID that does not conflict with the existing data records.
|
||||
|
||||
Args:
|
||||
cls: The Django model class.
|
||||
|
||||
Returns:
|
||||
The new random ID.
|
||||
"""
|
||||
while True:
|
||||
pk = random.randint(100000000, 999999999)
|
||||
try:
|
||||
cls.objects.get(pk=pk)
|
||||
except cls.DoesNotExist:
|
||||
return pk
|
||||
|
||||
|
||||
def strip_post(post: Dict[str, str]) -> None:
|
||||
"""Strips the values of the POSTed data. Empty strings are removed.
|
||||
|
||||
Args:
|
||||
post (dict[str]): The POSTed data.
|
||||
"""
|
||||
for key in list(post.keys()):
|
||||
post[key] = post[key].strip()
|
||||
if post[key] == "":
|
||||
del post[key]
|
||||
|
||||
|
||||
STORAGE_KEY: str = "stored_post"
|
||||
|
||||
|
||||
def store_post(request: HttpRequest, post: Dict[str, str]):
|
||||
"""Stores the POST data into the session.
|
||||
|
||||
Args:
|
||||
request: The request.
|
||||
post: The POST data.
|
||||
"""
|
||||
request.session[STORAGE_KEY] = post
|
||||
|
||||
|
||||
def retrieve_store(request: HttpRequest) -> Optional[Dict[str, str]]:
|
||||
"""Retrieves the POST data from the storage.
|
||||
|
||||
Args:
|
||||
request: The request.
|
||||
|
||||
Returns:
|
||||
The POST data, or None if the previously-stored data does not exist.
|
||||
"""
|
||||
if STORAGE_KEY not in request.session:
|
||||
return None
|
||||
post = request.session[STORAGE_KEY]
|
||||
del request.session[STORAGE_KEY]
|
||||
return post
|
||||
|
||||
|
||||
def parse_date(s: str):
|
||||
"""Parses a string for a date. The date can be either YYYY-MM-DD,
|
||||
Y/M/D, or M/D/Y.
|
||||
|
||||
Args:
|
||||
s: The string.
|
||||
|
||||
Returns:
|
||||
The date.
|
||||
|
||||
Raises:
|
||||
ValueError: When the string is not in a valid format.
|
||||
"""
|
||||
for f in ["%Y-%m-%d", "%m/%d/%Y", "%Y/%m/%d"]:
|
||||
try:
|
||||
return datetime.datetime.strptime(s, f)
|
||||
except ValueError:
|
||||
pass
|
||||
raise ValueError(F"not a recognized date {s}")
|
||||
|
||||
|
||||
class Language:
|
||||
"""A language.
|
||||
|
||||
Args:
|
||||
language: The Django language code.
|
||||
|
||||
Attributes:
|
||||
id (str): The language ID
|
||||
db (str): The database column suffix of this language.
|
||||
locale (str); The locale name of this language.
|
||||
is_default (bool): Whether this is the default language.
|
||||
"""
|
||||
def __init__(self, language: str):
|
||||
self.id = language
|
||||
self.db = "_" + language.lower().replace("-", "_")
|
||||
if language == "zh-hant":
|
||||
self.locale = "zh-TW"
|
||||
elif language == "zh-hans":
|
||||
self.locale = "zh-CN"
|
||||
else:
|
||||
self.locale = language
|
||||
self.is_default = (language == settings.LANGUAGE_CODE)
|
||||
|
||||
@staticmethod
|
||||
def default():
|
||||
return Language(settings.LANGUAGE_CODE)
|
||||
|
||||
@staticmethod
|
||||
def current():
|
||||
return Language(get_language())
|
||||
|
||||
|
||||
class UrlBuilder:
|
||||
"""The URL builder.
|
||||
|
||||
Attributes:
|
||||
path (str): the base path
|
||||
params (list[Param]): The query parameters
|
||||
"""
|
||||
def __init__(self, start_url: str):
|
||||
"""Constructs a new URL builder.
|
||||
|
||||
Args:
|
||||
start_url (str): The URL to start with
|
||||
"""
|
||||
pos = start_url.find("?")
|
||||
if pos == -1:
|
||||
self.path = start_url
|
||||
self.params = []
|
||||
return
|
||||
self.path = start_url[:pos]
|
||||
self.params = []
|
||||
for piece in start_url[pos + 1:].split("&"):
|
||||
pos = piece.find("=")
|
||||
name = urllib.parse.unquote_plus(piece[:pos])
|
||||
value = urllib.parse.unquote_plus(piece[pos + 1:])
|
||||
self.params.append(self.Param(name, value))
|
||||
|
||||
def add(self, name, value):
|
||||
"""Adds a query parameter.
|
||||
|
||||
Args:
|
||||
name (str): The parameter name
|
||||
value (str): The parameter value
|
||||
|
||||
Returns:
|
||||
UrlBuilder: The URL builder itself, with the parameter
|
||||
modified.
|
||||
"""
|
||||
if value is not None:
|
||||
self.params.append(self.Param(name, value))
|
||||
return self
|
||||
|
||||
def remove(self, name):
|
||||
"""Removes a query parameter.
|
||||
|
||||
Args:
|
||||
name (str): The parameter name
|
||||
|
||||
Returns:
|
||||
UrlBuilder: The URL builder itself, with the parameter
|
||||
modified.
|
||||
"""
|
||||
self.params = [x for x in self.params if x.name != name]
|
||||
return self
|
||||
|
||||
def query(self, **kwargs):
|
||||
"""A keyword-styled query parameter setter. The existing values are
|
||||
always replaced. Multiple-values are added when the value is a list or
|
||||
tuple. The existing values are dropped when the value is None.
|
||||
"""
|
||||
for key in kwargs:
|
||||
self.remove(key)
|
||||
if isinstance(kwargs[key], list) or isinstance(kwargs[key], tuple):
|
||||
for value in kwargs[key]:
|
||||
self.add(key, value)
|
||||
elif kwargs[key] is None:
|
||||
pass
|
||||
else:
|
||||
self.add(key, kwargs[key])
|
||||
return self
|
||||
|
||||
def clone(self):
|
||||
"""Returns a copy of this URL builder.
|
||||
|
||||
Returns:
|
||||
UrlBuilder: A copy of this URL builder.
|
||||
"""
|
||||
another = UrlBuilder(self.path)
|
||||
another.params = [
|
||||
self.Param(x.name, x.value) for x in self.params]
|
||||
return another
|
||||
|
||||
def __str__(self) -> str:
|
||||
if len(self.params) == 0:
|
||||
return self.path
|
||||
return self.path + "?" + "&".join([
|
||||
str(x) for x in self.params])
|
||||
|
||||
class Param:
|
||||
"""A query parameter.
|
||||
|
||||
Attributes:
|
||||
name: The parameter name
|
||||
value: The parameter value
|
||||
"""
|
||||
def __init__(self, name: str, value: str):
|
||||
"""Constructs a new query parameter
|
||||
|
||||
Args:
|
||||
name (str): The parameter name
|
||||
value (str): The parameter value
|
||||
"""
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Returns the string representation of this query
|
||||
parameter.
|
||||
|
||||
Returns:
|
||||
str: The string representation of this query
|
||||
parameter
|
||||
"""
|
||||
return "%s=%s" % (
|
||||
urllib.parse.quote(self.name),
|
||||
urllib.parse.quote(self.value))
|
||||
|
||||
|
||||
class Pagination:
|
||||
"""The pagination.
|
||||
|
||||
Args:
|
||||
request: The request.
|
||||
items: All the items.
|
||||
is_reversed: Whether we should display the last page first.
|
||||
|
||||
Raises:
|
||||
PaginationException: With invalid pagination parameters
|
||||
|
||||
Attributes:
|
||||
current_url (UrlBuilder): The current request URL.
|
||||
is_reversed (bool): Whether we should display the last page first.
|
||||
page_size (int): The page size.
|
||||
total_pages (int): The total number of pages available.
|
||||
is_paged (bool): Whether there are more than one page.
|
||||
page_no (int): The current page number.
|
||||
items (list[Model]): The items in the current page.
|
||||
"""
|
||||
DEFAULT_PAGE_SIZE = 10
|
||||
|
||||
def __init__(self, request: HttpRequest, items: List[Any],
|
||||
is_reversed: bool = False):
|
||||
self.current_url = UrlBuilder(request.get_full_path())
|
||||
self.is_reversed = is_reversed
|
||||
self.page_size = self.DEFAULT_PAGE_SIZE
|
||||
self.total_pages = None
|
||||
self.is_paged = None
|
||||
self.page_no = 1
|
||||
self.items = []
|
||||
|
||||
# The page size
|
||||
try:
|
||||
self.page_size = int(request.GET["page-size"])
|
||||
if self.page_size == self.DEFAULT_PAGE_SIZE:
|
||||
raise PaginationException(self.current_url.remove("page-size"))
|
||||
if self.page_size < 1:
|
||||
raise PaginationException(self.current_url.remove("page-size"))
|
||||
except KeyError:
|
||||
self.page_size = self.DEFAULT_PAGE_SIZE
|
||||
except ValueError:
|
||||
raise PaginationException(self.current_url.remove("page-size"))
|
||||
self.total_pages = int(
|
||||
(len(items) - 1) / self.page_size) + 1
|
||||
default_page_no = 1 if not is_reversed else self.total_pages
|
||||
self.is_paged = self.total_pages > 1
|
||||
|
||||
# The page number
|
||||
try:
|
||||
self.page_no = int(request.GET["page"])
|
||||
if not self.is_paged:
|
||||
raise PaginationException(self.current_url.remove("page"))
|
||||
if self.page_no == default_page_no:
|
||||
raise PaginationException(self.current_url.remove("page"))
|
||||
if self.page_no < 1:
|
||||
raise PaginationException(self.current_url.remove("page"))
|
||||
if self.page_no > self.total_pages:
|
||||
raise PaginationException(self.current_url.remove("page"))
|
||||
except KeyError:
|
||||
self.page_no = default_page_no
|
||||
except ValueError:
|
||||
raise PaginationException(self.current_url.remove("page"))
|
||||
|
||||
if not self.is_paged:
|
||||
self.page_no = 1
|
||||
self.items = items
|
||||
return
|
||||
start_no = self.page_size * (self.page_no - 1)
|
||||
self.items = items[start_no:start_no + self.page_size]
|
||||
|
||||
def links(self):
|
||||
"""Returns the navigation links of the pagination bar.
|
||||
|
||||
Returns:
|
||||
List[Link]: The navigation links of the pagination bar.
|
||||
"""
|
||||
base_url = self.current_url.clone().remove("page").remove("s")
|
||||
links = []
|
||||
# The previous page
|
||||
link = self.Link()
|
||||
link.title = pgettext("Pagination|", "Previous")
|
||||
if self.page_no > 1:
|
||||
if self.page_no - 1 == 1:
|
||||
if not self.is_reversed:
|
||||
link.url = str(base_url)
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", "1"))
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(self.page_no - 1)))
|
||||
link.is_small_screen = True
|
||||
links.append(link)
|
||||
# The first page
|
||||
link = self.Link()
|
||||
link.title = "1"
|
||||
if not self.is_reversed:
|
||||
link.url = str(base_url)
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", "1"))
|
||||
if self.page_no == 1:
|
||||
link.is_active = True
|
||||
links.append(link)
|
||||
# The previous ellipsis
|
||||
if self.page_no > 4:
|
||||
link = self.Link()
|
||||
if self.page_no > 5:
|
||||
link.title = pgettext("Pagination|", "...")
|
||||
else:
|
||||
link.title = "2"
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", "2"))
|
||||
links.append(link)
|
||||
# The nearby pages
|
||||
for no in range(self.page_no - 2, self.page_no + 3):
|
||||
if no <= 1 or no >= self.total_pages:
|
||||
continue
|
||||
link = self.Link()
|
||||
link.title = str(no)
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(no)))
|
||||
if no == self.page_no:
|
||||
link.is_active = True
|
||||
links.append(link)
|
||||
# The next ellipsis
|
||||
if self.page_no + 3 < self.total_pages:
|
||||
link = self.Link()
|
||||
if self.page_no + 4 < self.total_pages:
|
||||
link.title = pgettext("Pagination|", "...")
|
||||
else:
|
||||
link.title = str(self.total_pages - 1)
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(self.total_pages - 1)))
|
||||
links.append(link)
|
||||
# The last page
|
||||
link = self.Link()
|
||||
link.title = str(self.total_pages)
|
||||
if self.is_reversed:
|
||||
link.url = str(base_url)
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(self.total_pages)))
|
||||
if self.page_no == self.total_pages:
|
||||
link.is_active = True
|
||||
links.append(link)
|
||||
# The next page
|
||||
link = self.Link()
|
||||
link.title = pgettext("Pagination|", "Next")
|
||||
if self.page_no < self.total_pages:
|
||||
if self.page_no + 1 == self.total_pages:
|
||||
if self.is_reversed:
|
||||
link.url = str(base_url)
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(self.total_pages)))
|
||||
else:
|
||||
link.url = str(base_url.clone().add(
|
||||
"page", str(self.page_no + 1)))
|
||||
link.is_small_screen = True
|
||||
links.append(link)
|
||||
return links
|
||||
|
||||
class Link:
|
||||
"""A navigation link in the pagination bar.
|
||||
|
||||
Attributes:
|
||||
url (str): The link URL, or for a non-link slot.
|
||||
title (str): The title of the link.
|
||||
is_active (bool): Whether this link is currently active.
|
||||
is_small_screen (bool): Whether this link is for small
|
||||
screens
|
||||
"""
|
||||
def __int__(self):
|
||||
self.url = None
|
||||
self.title = None
|
||||
self.is_active = False
|
||||
self.is_small_screen = False
|
||||
|
||||
def page_size_options(self):
|
||||
"""Returns the page size options.
|
||||
|
||||
Returns:
|
||||
List[PageSizeOption]: The page size options.
|
||||
"""
|
||||
base_url = self.current_url.remove("page").remove("page-size")
|
||||
return [self.PageSizeOption(x, self._page_size_url(base_url, x))
|
||||
for x in [10, 100, 200]]
|
||||
|
||||
@staticmethod
|
||||
def _page_size_url(base_url: UrlBuilder, size: int) -> str:
|
||||
"""Returns the URL for a new page size.
|
||||
|
||||
Args:
|
||||
base_url (UrlBuilder): The base URL builder.
|
||||
size (int): The new page size.
|
||||
|
||||
Returns:
|
||||
str: The URL for the new page size.
|
||||
"""
|
||||
if size == Pagination.DEFAULT_PAGE_SIZE:
|
||||
return str(base_url)
|
||||
return str(base_url.clone().add("page-size", str(size)))
|
||||
|
||||
class PageSizeOption:
|
||||
"""A page size option.
|
||||
|
||||
Args:
|
||||
size: The page size.
|
||||
url: The URL of this page size.
|
||||
|
||||
Attributes:
|
||||
size (int): The page size.
|
||||
url (str): The URL for this page size.
|
||||
"""
|
||||
def __init__(self, size: int, url: str):
|
||||
self.size = size
|
||||
self.url = url
|
||||
|
||||
|
||||
class PaginationException(Exception):
|
||||
"""The exception thrown with invalid pagination parameters.
|
||||
|
||||
Args:
|
||||
url_builder: The canonical URL to redirect to.
|
||||
|
||||
Attributes:
|
||||
url (str): The canonical URL to redirect to.
|
||||
"""
|
||||
def __init__(self, url_builder: UrlBuilder):
|
||||
self.url = str(url_builder)
|
||||
|
||||
|
||||
CDN_LIBRARIES = {
|
||||
"jquery": {"css": [],
|
||||
"js": ["https://code.jquery.com/jquery-3.5.1.min.js"]},
|
||||
"bootstrap4": {
|
||||
"css": [("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/"
|
||||
"bootstrap.min.css")],
|
||||
"js": [("https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/"
|
||||
"popper.min.js"),
|
||||
("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/"
|
||||
"bootstrap.min.js")]},
|
||||
"font-awesome-5": {
|
||||
"css": ["https://use.fontawesome.com/releases/v5.14.0/css/all.css"],
|
||||
"js": []},
|
||||
"bootstrap4-datatables": {
|
||||
"css": [("https://cdn.datatables.net/1.10.21/css/"
|
||||
"jquery.dataTables.min.css"),
|
||||
("https://cdn.datatables.net/1.10.21/css/"
|
||||
"dataTables.bootstrap4.min.css")],
|
||||
"js": [("https://cdn.datatables.net/1.10.21/js/"
|
||||
"jquery.dataTables.min.js"),
|
||||
("https://cdn.datatables.net/1.10.21/js/"
|
||||
"dataTables.bootstrap4.min.js")]},
|
||||
"jquery-ui": {"css": [("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/"
|
||||
"1.12.1/jquery-ui.min.css")],
|
||||
"js": [("https://cdnjs.cloudflare.com/ajax/libs/jqueryui/"
|
||||
"1.12.1/jquery-ui.min.js")]},
|
||||
"bootstrap4-tempusdominus": {
|
||||
"css": [("https://cdnjs.cloudflare.com/ajax/libs/"
|
||||
"tempusdominus-bootstrap-4/5.1.2/css/"
|
||||
"tempusdominus-bootstrap-4.min.css")],
|
||||
"js": [("https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/"
|
||||
"moment-with-locales.min.js"),
|
||||
("https://cdnjs.cloudflare.com/ajax/libs/"
|
||||
"tempusdominus-bootstrap-4/5.1.2/js/"
|
||||
"tempusdominus-bootstrap-4.js")]},
|
||||
"decimal-js": {"css": [],
|
||||
"js": [("https://cdnjs.cloudflare.com/ajax/libs/decimal.js/"
|
||||
"10.2.0/decimal.min.js")]},
|
||||
"period-chooser": {"css": ["mia_core/css/period-chooser.css"],
|
||||
"js": ["mia_core/js/period-chooser.js"]}
|
||||
}
|
||||
DEFAULT_LIBS = []
|
||||
|
||||
|
||||
class CssAndJavaScriptLibraries:
|
||||
"""The CSS and JavaScript library resolver."""
|
||||
AVAILABLE_LIBS: List[str] = ["jquery", "bootstrap4", "font-awesome-5",
|
||||
"bootstrap4-datatables", "jquery-ui",
|
||||
"bootstrap4-tempusdominus", "decimal-js",
|
||||
"i18n", "period-chooser"]
|
||||
|
||||
def __init__(self, *args):
|
||||
self._use: Dict[str, bool] = {x: False for x in self.AVAILABLE_LIBS}
|
||||
self._add_default_libs()
|
||||
# The specified libraries
|
||||
if len(args) > 0:
|
||||
libs = args[0]
|
||||
invalid = [x for x in libs if x not in self.AVAILABLE_LIBS]
|
||||
if len(invalid) > 0:
|
||||
raise NameError("library %s invalid" % ", ".join(invalid))
|
||||
for lib in libs:
|
||||
self._use[lib] = True
|
||||
self._css = []
|
||||
try:
|
||||
self._css = self._css + settings.DEFAULT_CSS
|
||||
except AttributeError:
|
||||
pass
|
||||
self._js = []
|
||||
try:
|
||||
self._css = self._css + settings.DEFAULT_JS
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _add_default_libs(self):
|
||||
"""Adds the default libraries."""
|
||||
invalid = [x for x in DEFAULT_LIBS if x not in self.AVAILABLE_LIBS]
|
||||
if len(invalid) > 0:
|
||||
raise NameError("library %s invalid" % ", ".join(invalid))
|
||||
for lib in DEFAULT_LIBS:
|
||||
self._use[lib] = True
|
||||
|
||||
def use(self, *args) -> None:
|
||||
"""Use the specific libraries.
|
||||
|
||||
Args:
|
||||
args: The libraries.
|
||||
"""
|
||||
if len(args) == 0:
|
||||
return
|
||||
libs = args[0]
|
||||
invalid = [x for x in libs if x not in self.AVAILABLE_LIBS]
|
||||
if len(invalid) > 0:
|
||||
raise NameError("library %s invalid" % ", ".join(invalid))
|
||||
for lib in libs:
|
||||
self._use[lib] = True
|
||||
|
||||
def add_css(self, css) -> None:
|
||||
"""Adds a custom CSS file."""
|
||||
self._css.append(css)
|
||||
|
||||
def add_js(self, js) -> None:
|
||||
"""Adds a custom JavaScript file."""
|
||||
self._js.append(js)
|
||||
|
||||
def css(self) -> List[str]:
|
||||
"""Returns the stylesheet files to use."""
|
||||
use: Dict[str, bool] = self._solve_use_dependencies()
|
||||
css = []
|
||||
for lib in [x for x in self.AVAILABLE_LIBS if use[x]]:
|
||||
if lib == "i18n":
|
||||
continue
|
||||
try:
|
||||
css = css + settings.STATIC_LIBS[lib]["css"]
|
||||
except AttributeError:
|
||||
css = css + CDN_LIBRARIES[lib]["css"]
|
||||
except TypeError:
|
||||
css = css + CDN_LIBRARIES[lib]["css"]
|
||||
except KeyError:
|
||||
css = css + CDN_LIBRARIES[lib]["css"]
|
||||
return css + self._css
|
||||
|
||||
def js(self) -> List[str]:
|
||||
"""Returns the JavaScript files to use."""
|
||||
use: Dict[str, bool] = self._solve_use_dependencies()
|
||||
js = []
|
||||
for lib in [x for x in self.AVAILABLE_LIBS if use[x]]:
|
||||
if lib == "i18n":
|
||||
js.append(reverse("javascript-catalog"))
|
||||
continue
|
||||
try:
|
||||
js = js + settings.STATIC_LIBS[lib]["js"]
|
||||
except AttributeError:
|
||||
js = js + CDN_LIBRARIES[lib]["js"]
|
||||
except TypeError:
|
||||
js = js + CDN_LIBRARIES[lib]["js"]
|
||||
except KeyError:
|
||||
js = js + CDN_LIBRARIES[lib]["js"]
|
||||
return js + self._js
|
||||
|
||||
def _solve_use_dependencies(self) -> Dict[str, bool]:
|
||||
"""Solves and returns the library dependencies."""
|
||||
use: Dict[str, bool] = {x: self._use[x] for x in self._use}
|
||||
if use["period-chooser"]:
|
||||
use["bootstrap4-tempusdominus"] = True
|
||||
if use["bootstrap4-tempusdominus"]:
|
||||
use["bootstrap4"] = True
|
||||
if use["bootstrap4-datatables"]:
|
||||
use["bootstrap4"] = True
|
||||
if use["jquery-ui"]:
|
||||
use["jquery"] = True
|
||||
if use["bootstrap4"]:
|
||||
use["jquery"] = True
|
||||
return use
|
||||
|
||||
|
||||
def add_default_libs(*args) -> None:
|
||||
"""Adds the specified libraries to the default CSS and JavaScript
|
||||
libraries.
|
||||
|
||||
Args:
|
||||
args: The libraries to be added to the default libraries
|
||||
"""
|
||||
libs = args
|
||||
invalid = [x for x in libs
|
||||
if x not in CssAndJavaScriptLibraries.AVAILABLE_LIBS]
|
||||
if len(invalid) > 0:
|
||||
raise NameError("library %s invalid" % ", ".join(invalid))
|
||||
for lib in libs:
|
||||
if lib not in DEFAULT_LIBS:
|
||||
DEFAULT_LIBS.append(lib)
|
Reference in New Issue
Block a user