#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# This file is part of Mentat system (https://mentat.cesnet.cz/).
#
# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/)
# Use of this source is governed by the MIT license, see LICENSE file.
# -------------------------------------------------------------------------------
"""
This module contains global application-wide constants for Hawat user interface.
"""
__author__ = "Jan Mach <jan.mach@cesnet.cz>"
__credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
import re
import datetime
[docs]def tr_(val):
"""
Method for marking translatable strings according to the documentation
recipe at https://docs.python.org/3/library/gettext.html#deferred-translations.
"""
return val
CRE_LOGIN = re.compile('^[-_@.a-zA-Z0-9]+$')
"""Compiled regular expression for login validation."""
CRE_EMAIL = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
"""Compiled regular expression for email address format validation."""
CRE_COUNTRY_CODE = re.compile('^[a-zA-Z]{2,3}$')
"""Compiled regular expression for validating language/country codes."""
CRE_LANG_CODE = re.compile('^[a-zA-Z]{2}(_[a-zA-Z]{2})?$')
"""Compiled regular expression for validating language codes."""
DEFAULT_LOCALE = 'en'
"""Default application locale."""
DEFAULT_TIMEZONE = 'UTC'
"""Default application timezone."""
FLASH_INFO = 'info'
"""Class for *info* flash messages."""
FLASH_SUCCESS = 'success'
"""Class for *success* flash messages."""
FLASH_WARNING = 'warning'
"""Class for *warning* flash messages."""
FLASH_FAILURE = 'danger'
"""Class for *failure* flash messages."""
FORM_ACTION_SUBMIT = 'submit'
"""Name of the item form *submit* button."""
FORM_ACTION_CANCEL = 'cancel'
"""Name of the item form *cancel* button."""
ACTION_ITEM_CREATE = 'create'
"""Name of the item *create* action."""
ACTION_ITEM_CREATEFOR = 'createfor'
"""Name of the item *createfor* action."""
ACTION_ITEM_UPDATE = 'update'
"""Name of the item *update* action."""
ACTION_ITEM_ENABLE = 'enable'
"""Name of the item *enable* action."""
ACTION_ITEM_DISABLE = 'disable'
"""Name of the item *disable* action."""
ACTION_ITEM_DELETE = 'delete'
"""Name of the item *delete* action."""
ACTION_USER_LOGIN = 'login'
"""Name of the user *login* action."""
ACTION_USER_LOGOUT = 'logout'
"""Name of the user *logout* action."""
ACTION_USER_REGISTER = 'register'
"""Name of the user *register* action."""
MODEL_USER = 'user'
"""Name of the 'user' model."""
MODEL_GROUP = 'group'
"""Name of the 'user' model."""
MODEL_ITEM_CHANGELOG = 'item_changelog'
"""Name of the 'user' model."""
ROLE_USER = 'user'
"""Name of the 'user' role."""
ROLE_DEVELOPER = 'developer'
"""Name of the 'developer' role."""
ROLE_MAINTAINER = 'maintainer'
"""Name of the 'maintainer' role."""
ROLE_ADMIN = 'admin'
"""Name of the 'admin' role."""
ROLES = [
ROLE_USER,
ROLE_DEVELOPER,
ROLE_MAINTAINER,
ROLE_ADMIN
]
"""List of valid user roles."""
NO_ROLE = '__NO_ROLE__'
"""Special constant for selecting users with no roles."""
CFGKEY_MENU_MAIN_SKELETON = 'MENU_MAIN_SKELETON'
"""Configuration key name: Default application main menu skeleton."""
CFGKEY_ENABLED_BLUEPRINTS = 'ENABLED_BLUEPRINTS'
"""Configuration key name: List of all requested blueprints."""
CFGKEY_MODELS = 'MODELS'
"""Configuration key name: List of all requested blueprints."""
DEFAULT_PAGER_LIMIT = 100
"""Default page limit for pager/paginator."""
PAGER_LIMIT_CHOICES = [
(5, 5),
(10, 10),
(20, 20),
(25, 25),
(50, 50),
(100, 100),
(200, 200),
(250, 250),
(500, 500),
(1000, 1000),
(2500, 2500),
(5000, 5000),
(10000, 10000),
(25000, 25000),
(50000, 50000),
(100000, 100000)
]
"""List of available valid pager limit choices."""
DEFAULT_RESULT_TIMEDELTA = 7
"""Default result time delta for searching various objects."""
RESOURCE_BABEL = 'babel'
"""Name for the ``flask_babel.Babel`` object within the application resources."""
RESOURCE_LOGIN_MANAGER = 'login_manager'
"""Name for the ``flask_login.LoginManager`` object within the application resources."""
RESOURCE_MIGRATE = 'migrate'
"""Name for the ``flask_migrate.Migrate`` object within the application resources."""
RESOURCE_PRINCIPAL = 'principal'
"""Name for the ``flask_principal.Principal`` object within the application resources."""
CFGKEY_MENTAT_CORE = 'MENTAT_CORE'
"""Configuration key name: Core Mentat configurations."""
CFGKEY_MENTAT_CACHE_DIR = 'MENTAT_CACHE_DIR'
"""Configuration key name: Path to Mentat cache dir."""
#
# List of all existing Context Search Action Group names (CSAG).
#
CSAG_ABUSE = 'abuses'
CSAG_ADDRESS = 'ips'
CSAG_CATEGORY = 'categories'
CSAG_CLASS = 'classes'
CSAG_DETECTOR = 'detectors'
CSAG_DETTYPE = 'detector_types'
CSAG_HOSTTYPE = 'host_types'
CSAG_PORT = 'ports'
CSAG_PROTOCOL = 'protocols'
CSAG_SEVERITY = 'severities'
#
# List of all existing Additonal Object Data Service names (AODS).
#
AODS_IP4 = 'ip4'
AODS_IP6 = 'ip6'
ICON_NAME_MISSING_ICON = 'missing-icon'
"""Name of the icon to display instead of missing icons."""
ICONS = {
#
# General icons.
#
'login': '<i class="fas fa-fw fa-sign-in-alt"></i>',
'logout': '<i class="fas fa-fw fa-sign-out-alt"></i>',
'register': '<i class="fas fa-fw fa-user-plus"></i>',
'help': '<i class="fas fa-fw fa-question-circle"></i>',
'language': '<i class="fas fa-fw fa-globe"></i>',
'role-anonymous': '<i class="fas fa-fw fa-user-secret"></i>',
'role-user': '<i class="fas fa-fw fa-user"></i>',
'role-developer': '<i class="fas fa-fw fa-user-md"></i>',
'role-maintainer': '<i class="fas fa-fw fa-user-tie"></i>',
'role-admin': '<i class="fas fa-fw fa-user-ninja"></i>',
#
# Main site section icons.
#
'section-home': '<i class="fas fa-fw fa-home"></i>',
'section-dashboards': '<i class="fas fa-fw fa-tachometer-alt"></i>',
'section-more': '<i class="fas fa-fw fa-puzzle-piece"></i>',
'section-administration': '<i class="fas fa-fw fa-cogs"></i>',
'section-development': '<i class="fas fa-fw fa-bug"></i>',
#
# Built-in module icons.
#
'module-home': '<i class="fas fa-fw fa-home"></i>',
'module-auth-api': '<i class="fas fa-fw fa-id-card-alt"></i>',
'module-auth-dev': '<i class="fas fa-fw fa-id-card-alt"></i>',
'module-auth-env': '<i class="fas fa-fw fa-id-card-alt"></i>',
'module-changelogs': '<i class="fas fa-fw fa-clipboard-list"></i>',
'module-dashboards': '<i class="fas fa-fw fa-tachometer-alt"></i>',
'module-dbstatus': '<i class="fas fa-fw fa-database"></i>',
'module-design': '<i class="fas fa-fw fa-palette"></i>',
'module-devtools': '<i class="fas fa-fw fa-bug"></i>',
'module-dnsr': '<i class="fas fa-fw fa-directions"></i>',
'module-events': '<i class="fas fa-fw fa-bell"></i>',
'module-filters': '<i class="fas fa-fw fa-filter"></i>',
'module-geoip': '<i class="fas fa-fw fa-map-marked-alt"></i>',
'module-groups': '<i class="fas fa-fw fa-users"></i>',
'module-help': '<i class="fas fa-fw fa-question-circle"></i>',
'module-hosts': '<i class="fas fa-fw fa-server"></i>',
'module-networks': '<i class="fas fa-fw fa-sitemap"></i>',
'module-nerd': '<i class="fas fa-fw fa-certificate"></i>',
'module-pdnsr': '<i class="fas fa-fw fa-compass"></i>',
'module-performance': '<i class="fas fa-fw fa-chart-bar"></i>',
'module-reports': '<i class="fas fa-fw fa-newspaper"></i>',
'module-settings-reporting': '<i class="fas fa-fw fa-sliders-h"></i>',
'module-skeleton': '<i class="fas fa-fw fa-skull"></i>',
'module-status': '<i class="fas fa-fw fa-heartbeat"></i>',
'module-timeline': '<i class="fas fa-fw fa-chart-line"></i>',
'module-users': '<i class="fas fa-fw fa-user"></i>',
'module-whois': '<i class="fas fa-fw fa-map-signs"></i>',
'profile': '<i class="fas fa-fw fa-id-card"></i>',
'modal-question': '<i class="fas fa-fw fa-question-circle"></i>',
'missing-icon': '<i class="fas fa-fw fa-question" title="Missing icon"></i>',
#
# Action icons.
#
'action-sort': '<i class="fas fa-fw fa-sort"></i>',
'action-sort-asc': '<i class="fas fa-fw fa-sort-up"></i>',
'action-sort-desc': '<i class="fas fa-fw fa-sort-down"></i>',
'action-more': '<i class="fas fa-fw fa-cubes"></i>',
'action-search': '<i class="fas fa-fw fa-search"></i>',
'action-show': '<i class="fas fa-fw fa-eye"></i>',
'action-show-user': '<i class="fas fa-fw fa-user-circle"></i>',
'action-create': '<i class="fas fa-fw fa-plus-circle"></i>',
'action-create-user': '<i class="fas fa-fw fa-user-plus"></i>',
'action-update': '<i class="fas fa-fw fa-edit"></i>',
'action-update-user': '<i class="fas fa-fw fa-user-edit"></i>',
'action-enable': '<i class="fas fa-fw fa-unlock"></i>',
'action-enable-user': '<i class="fas fa-fw fa-user-check"></i>',
'action-disable': '<i class="fas fa-fw fa-lock"></i>',
'action-disable-user': '<i class="fas fa-fw fa-user-lock"></i>',
'action-delete': '<i class="fas fa-fw fa-trash"></i>',
'action-delete-user': '<i class="fas fa-fw fa-user-slash"></i>',
'action-add-member': '<i class="fas fa-fw fa-user-plus"></i>',
'action-rej-member': '<i class="fas fa-fw fa-user-minus"></i>',
'action-rem-member': '<i class="fas fa-fw fa-user-times"></i>',
'action-save': '<i class="fas fa-fw fa-save"></i>',
'action-download': '<i class="fas fa-fw fa-file-download"></i>',
'action-download-zip': '<i class="fas fa-fw fa-file-archive"></i>',
'action-download-csv': '<i class="fas fa-fw fa-file-csv"></i>',
'action-download-svg': '<i class="fas fa-fw fa-file-image"></i>',
'action-download-js': '<i class="fab fa-fw fa-js"></i>',
'action-mail': '<i class="fas fa-fw fa-envelope"></i>',
'action-reload': '<i class="fas fa-fw fa-sync-alt"></i>',
'action-genkey': '<i class="fas fa-fw fa-key"></i>',
'action-stop': '<i class="fas fa-fw fa-stop-circle"></i>',
'alert-success': '<i class="fas fa-fw fa-check-circle"></i>',
'alert-info': '<i class="fas fa-fw fa-info-circle"></i>',
'alert-warning': '<i class="fas fa-fw fa-exclamation-circle"></i>',
'alert-danger': '<i class="fas fa-fw fa-exclamation-triangle"></i>',
'item-enabled': '<i class="fas fa-fw fa-toggle-on"></i>',
'item-disabled': '<i class="fas fa-fw fa-toggle-off"></i>',
'r-t-summary': '<i class="fas fa-fw fa-archive"></i>',
'r-t-extra': '<i class="fas fa-fw fa-file-alt"></i>',
'r-s-unknown': '<i class="fas fa-fw fa-thermometer-empty"></i>',
'r-s-low': '<i class="fas fa-fw fa-thermometer-quarter"></i>',
'r-s-medium': '<i class="fas fa-fw fa-thermometer-half"></i>',
'r-s-high': '<i class="fas fa-fw fa-thermometer-three-quarters"></i>',
'r-s-critical': '<i class="fas fa-fw fa-thermometer-full"></i>',
'report-data-relapsed': '<i class="fas fa-fw fa-sync-alt"></i>',
'report-data-filtered': '<i class="fas fa-fw fa-filter"></i>',
'report-data-test': '<i class="fas fa-fw fa-bug"></i>',
'report-data-mailed': '<i class="fas fa-fw fa-envelope"></i>',
'ajax-loader': '<i class="fas fa-fw fa-spinner fa-spin fa-4x"></i>',
'caret-down': '<i class="fas fa-fw fa-caret-square-down"></i>',
'unassigned': '<i class="fas fa-fw fa-minus"></i>',
'undisclosed': '<i class="fas fa-fw fa-minus"></i>',
'calendar': '<i class="fas fa-fw fa-calendar-alt"></i>',
'stopwatch': '<i class="fas fa-fw fa-stopwatch"></i>',
'clock': '<i class="fas fa-fw fa-clock"></i>',
'domain': '<i class="fas fa-fw fa-tag"></i>',
'time-from': '<i class="fas fa-fw fa-hourglass-start"></i>',
'time-to': '<i class="fas fa-fw fa-hourglass-end"></i>',
'debug': '<i class="fas fa-fw fa-bug"></i>',
'eventclss': '<i class="fas fa-fw fa-book"></i>',
'reference': '<i class="fas fa-fw fa-external-link-alt"></i>',
'anchor': '<i class="fas fa-fw fa-anchor"></i>',
'search': '<i class="fas fa-fw fa-search"></i>',
'weight': '<i class="fas fa-fw fa-weight"></i>',
'list': '<i class="fas fa-fw fa-list-ul"></i>',
'mail': '<i class="fas fa-fw fa-envelope"></i>',
'redirect': '<i class="fas fa-fw fa-share"></i>',
'unredirect': '<span class="fa-layers fa-fw"><i class="fas fa-fw fa-share"></i><i class="fas fa-fw fa-ban"></i></span>',
'mute': '<i class="fas fa-fw fa-volume-off"></i>',
'unmute': '<i class="fas fa-fw fa-volume-up"></i>',
'compress': '<i class="fas fa-fw fa-gift"></i>',
'uncompress': '<span class="fa-layers fa-fw"><i class="fas fa-fw fa-gift"></i><i class="fas fa-fw fa-ban"></i></span>',
'import': '<i class="fas fa-fw fa-cloud-upload"></i>',
'export': '<i class="fas fa-fw fa-cloud-download"></i>',
'validate': '<i class="fas fa-fw fa-check-circle"></i>',
'min': '<i class="fas fa-fw fa-angle-double-down"></i>',
'max': '<i class="fas fa-fw fa-angle-double-up"></i>',
'sum': '<i class="fas fa-fw fa-plus"></i>',
'cnt': '<i class="fas fa-fw fa-hashtag"></i>',
'avg': '<i class="fas fa-fw fa-dot-circle"></i>',
'med': '<i class="fas fa-fw fa-bullseye"></i>',
'na': '<i class="fas fa-fw fa-times"></i>',
'stats': '<i class="fas fa-fw fa-bar-chart"></i>',
'structure': '<i class="fas fa-fw fa-tree"></i>',
'actions': '<i class="fas fa-fw fa-wrench"></i>',
'cog': '<i class="fas fa-fw fa-cog"></i>',
'check': '<i class="fas fa-fw fa-check-square"></i>',
'check_blank': '<i class="far fa-fw fa-square"></i>',
'ok': '<i class="fas fa-fw fa-check"></i>',
'ko': '<i class="fas fa-fw fa-times"></i>',
'sortasc': '<i class="fas fa-fw fa-sort-asc"></i>',
'sortdesc': '<i class="fas fa-fw fa-sort-desc"></i>',
'backtotop': '<i class="fas fa-fw fa-level-up-alt"></i>',
'first': '<i class="fas fa-fw fa-angle-double-left"></i>',
'previous': '<i class="fas fa-fw fa-angle-left"></i>',
'next': '<i class="fas fa-fw fa-angle-right"></i>',
'last': '<i class="fas fa-fw fa-angle-double-right" aria-hidden="true"></i>',
'liitem': '<i class="fas fa-li fa-asterisk" aria-hidden="true"></i>',
'expand': '<i class="fas fa-fw fa-angle-left" aria-hidden="true"></i>',
'collapse': '<i class="fas fa-fw fa-angle-down" aria-hidden="true"></i>',
'form-error': '<i class="fas fa-fw fa-exclamation-triangle" aria-hidden="true"></i>',
'table': '<i class="fas fa-fw fa-table"></i>',
'quicksearch': '<i class="fab fa-fw fa-searchengin"></i>',
'playground': '<i class="fas fa-fw fa-gamepad"></i>'
}
"""
Predefined list of selected `font-awesome <https://fontawesome.io/icons/>`__ icons
that are used in this application.
"""
TIME_WINDOWS = {
'1h': {
'current': lambda x: (x - datetime.timedelta(hours=1)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=1)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=1)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'2h': {
'current': lambda x: (x - datetime.timedelta(hours=2)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=2)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=2)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'3h': {
'current': lambda x: (x - datetime.timedelta(hours=3)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=3)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=3)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'4h': {
'current': lambda x: (x - datetime.timedelta(hours=4)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=4)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=4)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'6h': {
'current': lambda x: (x - datetime.timedelta(hours=6)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=6)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=6)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'12h': {
'current': lambda x: (x - datetime.timedelta(hours=12)).replace(minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(hours=12)).replace(minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(hours=12)).replace(minute=0, second=0, microsecond=0, tzinfo=None)
},
'1d': {
'current': lambda x: (x - datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'2d': {
'current': lambda x: (x - datetime.timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'3d': {
'current': lambda x: (x - datetime.timedelta(days=3)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=3)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=3)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'1w': {
'current': lambda x: (x - datetime.timedelta(weeks=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(weeks=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(weeks=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'2w': {
'current': lambda x: (x - datetime.timedelta(weeks=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(weeks=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(weeks=2)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'4w': {
'current': lambda x: (x - datetime.timedelta(weeks=4)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(weeks=4)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(weeks=4)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'12w': {
'current': lambda x: (x - datetime.timedelta(weeks=12)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(weeks=12)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(weeks=12)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'td': {
'current': lambda x: x.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'tw': {
'current': lambda x: (x - datetime.timedelta(days=x.weekday())).replace(hour=0, minute=0, second=0,
microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'tm': {
'current': lambda x: x.replace(day=1, hour=0, minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: (x - datetime.timedelta(days=1)).replace(day=1, hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: (x + datetime.timedelta(days=32)).replace(day=1, hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
},
'ty': {
'current': lambda x: x.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0, tzinfo=None),
'previous': lambda x: x.replace(year=x.year - 1, month=1, day=1, hour=0, minute=0, second=0, microsecond=0,
tzinfo=None),
'next': lambda x: x.replace(year=x.year + 1, month=1, day=1, hour=0, minute=0, second=0, microsecond=0,
tzinfo=None)
}
}
"""Default list of time windows for 'by time' quicksearch lists."""