Update pre-commit (#1138)

* Update pre-commit

* Upgrade djlint, remove flake8 and add pylint

* Reformat with new djlint version

* Run pre-commit on CI

* Use only python3.10 on CI

* Reformat files with pre-commit

* Run pre-commit against all files

* Reformat

* Added global excludes

* Added pre-commit to the contributing file

* Set python 3.9 as default

* Set language version to python3

Co-authored-by: Adrià Casajús <adria.casajus@proton.ch>
Co-authored-by: Carlos Quintana <carlos.quintana@proton.ch>
This commit is contained in:
Adrià Casajús 2022-07-04 16:01:04 +02:00 committed by GitHub
parent e2f9ea4ae1
commit 046748c443
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1188 additions and 808 deletions

View File

@ -9,7 +9,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.7, "3.10"]
python-version: ["3.9", "3.10"]
# service containers to run with `postgres-job`
services:
@ -75,9 +75,7 @@ jobs:
- name: Check formatting & linting
run: |
poetry run black --check .
poetry run flake8
poetry run djlint --check templates
poetry run pre-commit run --all-files
- name: Run db migration
run: |

View File

@ -1,10 +1,27 @@
exclude: "(migrations|static/node_modules|static/assets|static/vendor)"
default_language_version:
python: python3
repos:
- repo: https://github.com/psf/black
rev: 22.1.0
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
hooks:
- id: check-yaml
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
language_version: python3.7
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.3.0
hooks:
- id: djlint-jinja
files: '.*\.html'
entry: djlint --reformat
- repo: https://github.com/PyCQA/pylint
rev: v2.14.4
hooks:
- id: pylint

227
.pylintrc Normal file
View File

@ -0,0 +1,227 @@
[MASTER]
extension-pkg-allow-list=re2
fail-under=7.0
ignore=CVS
ignore-paths=migrations
ignore-patterns=^\.#
jobs=0
[MESSAGES CONTROL]
disable=missing-function-docstring,
missing-module-docstring,
duplicate-code,
#import-error,
missing-class-docstring,
useless-object-inheritance,
use-dict-literal,
logging-format-interpolation,
consider-using-f-string,
unnecessary-comprehension,
inconsistent-return-statements,
wrong-import-order,
line-too-long,
invalid-name,
global-statement,
no-else-return,
unspecified-encoding,
logging-fstring-interpolation,
too-few-public-methods,
bare-except,
fixme,
unnecessary-pass,
f-string-without-interpolation,
super-init-not-called,
unused-argument,
ungrouped-imports,
too-many-locals,
consider-using-with,
too-many-statements,
consider-using-set-comprehension,
unidiomatic-typecheck,
useless-else-on-loop,
too-many-return-statements,
broad-except,
protected-access,
consider-using-enumerate,
too-many-nested-blocks,
too-many-branches,
simplifiable-if-expression,
possibly-unused-variable,
pointless-string-statement,
wrong-import-position,
redefined-outer-name,
raise-missing-from,
logging-too-few-args,
redefined-builtin,
too-many-arguments,
import-outside-toplevel,
redefined-argument-from-local,
logging-too-many-args,
too-many-instance-attributes,
unreachable,
no-name-in-module,
no-member,
consider-using-ternary,
too-many-lines,
arguments-differ,
too-many-public-methods,
unused-variable,
consider-using-dict-items,
consider-using-in,
reimported,
too-many-boolean-expressions,
cyclic-import,
not-callable, # (paddle_utils.py) verifier.verify cannot be called (although it can)
abstract-method, # (models.py)
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style. If left empty, argument names will be checked with the set
# naming style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style. If left empty, attribute names will be checked with the set naming
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style. If left empty, class attribute names will be checked
# with the set naming style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style. If left empty, class constant names will be checked with
# the set naming style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style. If left empty, class names will be checked with the set naming style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style. If left empty, constant names will be checked with the set naming
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style. If left empty, function names will be checked with the set
# naming style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style. If left empty, inline iteration names will be checked
# with the set naming style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style. If left empty, method names will be checked with the set naming style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style. If left empty, module names will be checked with the set naming style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Regular expression matching correct type variable names. If left empty, type
# variable names will be checked with the set naming style.
#typevar-rgx=
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style. If left empty, variable names will be checked with the set
# naming style.
#variable-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[FORMAT]
max-line-length=88
single-line-if-stmt=yes

View File

@ -50,6 +50,16 @@ More info on https://github.com/andreasvc/pyre2
brew install -s re2 pybind11
```
## Linting and static analysis
We use pre-commit to run all our linting and static analysis checks. Please run
```bash
poetry run pre-commit install
```
To install it in your development environment.
## Run tests
```bash

309
poetry.lock generated
View File

@ -66,14 +66,6 @@ python-dateutil = "*"
python-editor = ">=0.3"
SQLAlchemy = ">=1.1.0"
[[package]]
name = "appdirs"
version = "1.4.4"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "appnope"
version = "0.1.0"
@ -93,6 +85,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
python-dateutil = ">=2.7.0"
[[package]]
name = "astroid"
version = "2.11.6"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
lazy-object-proxy = ">=1.4.0"
typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
wrapt = ">=1.11,<2"
[[package]]
name = "async-timeout"
version = "3.0.1"
@ -142,6 +148,21 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "backports.entry-points-selectable"
version = "1.1.1"
description = "Compatibility shim providing selectable entry points for older implementations"
category = "dev"
optional = false
python-versions = ">=2.7"
[package.dependencies]
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"]
[[package]]
name = "bcrypt"
version = "3.2.0"
@ -378,6 +399,17 @@ wrapt = ">=1.10,<2"
[package.extras]
dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"]
[[package]]
name = "dill"
version = "0.3.5.1"
description = "serialize all of python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
[package.extras]
graph = ["objgraph (>=1.7.2)"]
[[package]]
name = "distlib"
version = "0.3.1"
@ -388,23 +420,26 @@ python-versions = "*"
[[package]]
name = "djlint"
version = "0.7.3"
version = "1.3.0"
description = "HTML Template Linter and Formatter"
category = "dev"
optional = false
python-versions = ">=3.6.2,<4.0.0"
python-versions = ">=3.7,<4.0"
[package.dependencies]
click = ">=8.0.1,<9.0.0"
colorama = ">=0.4.4,<0.5.0"
html-tag-names = ">=0.1.2,<0.2.0"
html-void-elements = ">=0.1.0,<0.2.0"
importlib-metadata = ">=4.11.0,<5.0.0"
pathspec = ">=0.9.0,<0.10.0"
PyYAML = ">=6.0,<7.0"
regex = ">=2022.1.18,<2023.0.0"
tomlkit = ">=0.8.0,<0.9.0"
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
tqdm = ">=4.62.2,<5.0.0"
[package.extras]
test = ["coverage (>=6.2,<7.0)", "pytest (>=6.2.5,<7.0.0)", "pytest-cov (>=3.0.0,<4.0.0)"]
test = ["coverage (>=6.3.1,<7.0.0)", "pytest (>=7.0.1,<8.0.0)", "pytest-cov (>=3.0.0,<4.0.0)"]
[[package]]
name = "dkimpy"
@ -469,35 +504,6 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""}
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "flake8-bugbear"
version = "22.1.11"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
attrs = ">=19.2.0"
flake8 = ">=3.0.0"
[package.extras]
dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"]
[[package]]
name = "flanker"
version = "0.9.11"
@ -816,6 +822,22 @@ gevent = ["gevent (>=0.13)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "html-tag-names"
version = "0.1.2"
description = "List of known HTML tag names"
category = "dev"
optional = false
python-versions = ">=3.7,<4.0"
[[package]]
name = "html-void-elements"
version = "0.1.0"
description = "List of HTML void tag names."
category = "dev"
optional = false
python-versions = ">=3.7,<4.0"
[[package]]
name = "httplib2"
version = "0.18.1"
@ -856,18 +878,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "importlib-metadata"
version = "1.7.0"
version = "4.12.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["sphinx", "rst.linker"]
testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
perf = ["ipython"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
[[package]]
name = "iniconfig"
@ -917,6 +941,20 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "itsdangerous"
version = "1.1.0"
@ -973,6 +1011,14 @@ python-versions = "*"
[package.dependencies]
cryptography = ">=2.3"
[[package]]
name = "lazy-object-proxy"
version = "1.7.1"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "limits"
version = "1.5.1"
@ -1020,11 +1066,11 @@ traitlets = "*"
[[package]]
name = "mccabe"
version = "0.6.1"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
python-versions = ">=3.6"
[[package]]
name = "memory-profiler"
@ -1283,14 +1329,6 @@ python-versions = "*"
[package.dependencies]
pyasn1 = ">=0.4.6,<0.5.0"
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pycparser"
version = "2.20"
@ -1307,14 +1345,6 @@ category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pygments"
version = "2.7.4"
@ -1337,6 +1367,29 @@ dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)",
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
[[package]]
name = "pylint"
version = "2.14.4"
description = "python code static checker"
category = "dev"
optional = false
python-versions = ">=3.7.2"
[package.dependencies]
astroid = ">=2.11.6,<=2.12.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = ">=0.2"
isort = ">=4.2.5,<6"
mccabe = ">=0.6,<0.8"
platformdirs = ">=2.2.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
tomlkit = ">=0.10.1"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras]
spelling = ["pyenchant (>=3.2,<4.0)"]
testutils = ["gitpython (>3)"]
[[package]]
name = "pyopenssl"
version = "19.1.0"
@ -1731,7 +1784,7 @@ python-versions = "*"
[[package]]
name = "tomli"
version = "2.0.0"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
@ -1739,7 +1792,7 @@ python-versions = ">=3.7"
[[package]]
name = "tomlkit"
version = "0.8.0"
version = "0.11.0"
description = "Style preserving TOML library"
category = "dev"
optional = false
@ -1836,22 +1889,23 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "virtualenv"
version = "20.0.31"
version = "20.8.1"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[package.dependencies]
appdirs = ">=1.4.3,<2"
"backports.entry-points-selectable" = ">=1.0.4"
distlib = ">=0.3.1,<1"
filelock = ">=3.0.0,<4"
importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
platformdirs = ">=2,<3"
six = ">=1.9.0,<2"
[package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
[[package]]
name = "watchtower"
@ -2004,8 +2058,8 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "29d95a850f0a87a38aabb8f5eddba072316860dae26943fd690898ea9bac2b02"
python-versions = "^3.7.2"
content-hash = "04190874ee0655ddf7d67e024a61009c5f9c92439ff0af8dfce3341580269edb"
[metadata.files]
aiohttp = [
@ -2038,10 +2092,6 @@ alembic = [
{file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"},
{file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"},
]
appdirs = [
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
]
appnope = [
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"},
@ -2050,6 +2100,10 @@ arrow = [
{file = "arrow-0.16.0-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"},
{file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"},
]
astroid = [
{file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"},
{file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"},
]
async-timeout = [
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"},
@ -2069,6 +2123,10 @@ backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
"backports.entry-points-selectable" = [
{file = "backports.entry_points_selectable-1.1.1-py2.py3-none-any.whl", hash = "sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b"},
{file = "backports.entry_points_selectable-1.1.1.tar.gz", hash = "sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"},
]
bcrypt = [
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd"},
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"},
@ -2269,13 +2327,17 @@ deprecated = [
{file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
]
dill = [
{file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"},
{file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"},
]
distlib = [
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
]
djlint = [
{file = "djlint-0.7.3-py3-none-any.whl", hash = "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f"},
{file = "djlint-0.7.3.tar.gz", hash = "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05"},
{file = "djlint-1.3.0-py3-none-any.whl", hash = "sha256:0c986bf542cdac3025d431a5b15e6c3977f652f2e76e408dbb5e7aaab6b73d99"},
{file = "djlint-1.3.0.tar.gz", hash = "sha256:b2d8e6c0a14f88da165296f0da05795d15299b7ab0a9093d670ce9ffd867bc79"},
]
dkimpy = [
{file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"},
@ -2296,14 +2358,6 @@ filelock = [
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
]
flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
flake8-bugbear = [
{file = "flake8-bugbear-22.1.11.tar.gz", hash = "sha256:4c2a4136bd4ecb8bf02d5159af302ffc067642784c9d0488b33ce4610da825ee"},
{file = "flake8_bugbear-22.1.11-py3-none-any.whl", hash = "sha256:ce7ae44aaaf67ef192b8a6de94a5ac617144e1675ad0654fdea556f48dc18d9b"},
]
flanker = [
{file = "flanker-0.9.11.tar.gz", hash = "sha256:974418e5b498fd3bcb3859c22e22d26495257f9cf98b744c17f2335aca86e001"},
]
@ -2474,6 +2528,14 @@ gunicorn = [
{file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"},
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"},
]
html-tag-names = [
{file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"},
{file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"},
]
html-void-elements = [
{file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"},
{file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"},
]
httplib2 = [
{file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"},
{file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"},
@ -2491,8 +2553,8 @@ idna = [
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
importlib-metadata = [
{file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
{file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
{file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
{file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
]
iniconfig = [
{file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"},
@ -2506,6 +2568,10 @@ ipython-genutils = [
{file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
{file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
]
isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
@ -2526,6 +2592,45 @@ jwcrypto = [
{file = "jwcrypto-0.8-py2.py3-none-any.whl", hash = "sha256:16e17faa4dce36551ade3a3ccb06236a61e5924ea1db163c9be9827acf935a82"},
{file = "jwcrypto-0.8.tar.gz", hash = "sha256:b7fee2635bbefdf145399392f5be26ad54161c8271c66b5fe107b4b452f06c24"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
{file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
]
limits = [
{file = "limits-1.5.1-py2-none-any.whl", hash = "sha256:0e5f8b10f18dd809eb2342f5046eb9aa5e4e69a0258567b5f4aa270647d438b3"},
{file = "limits-1.5.1.tar.gz", hash = "sha256:f0c3319f032c4bfad68438ed1325c0fac86dac64582c7c25cddc87a0b658fa20"},
@ -2593,8 +2698,8 @@ matplotlib-inline = [
{file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
memory-profiler = [
{file = "memory_profiler-0.57.0.tar.gz", hash = "sha256:23b196f91ea9ac9996e30bfab1e82fecc30a4a1d24870e81d1e81625f786a2c3"},
@ -2825,10 +2930,6 @@ pyasn1-modules = [
{file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
{file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"},
]
pycodestyle = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
@ -2870,10 +2971,6 @@ pycryptodome = [
{file = "pycryptodome-3.9.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd"},
{file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"},
]
pyflakes = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
pygments = [
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
@ -2882,6 +2979,10 @@ pyjwt = [
{file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"},
{file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"},
]
pylint = [
{file = "pylint-2.14.4-py3-none-any.whl", hash = "sha256:89b61867db16eefb7b3c5b84afc94081edaf11544189e2b238154677529ad69f"},
{file = "pylint-2.14.4.tar.gz", hash = "sha256:47705453aa9dce520e123a7d51843d5f0032cbfa06870f89f00927aa1f735a4a"},
]
pyopenssl = [
{file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"},
{file = "pyOpenSSL-19.1.0.tar.gz", hash = "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"},
@ -3240,12 +3341,12 @@ toml = [
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
]
tomli = [
{file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"},
{file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"},
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
tomlkit = [
{file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"},
{file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"},
{file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"},
{file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"},
]
tqdm = [
{file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"},
@ -3302,8 +3403,8 @@ urllib3 = [
{file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},
]
virtualenv = [
{file = "virtualenv-20.0.31-py2.py3-none-any.whl", hash = "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"},
{file = "virtualenv-20.0.31.tar.gz", hash = "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc"},
{file = "virtualenv-20.8.1-py2.py3-none-any.whl", hash = "sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300"},
{file = "virtualenv-20.8.1.tar.gz", hash = "sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8"},
]
watchtower = [
{file = "watchtower-0.8.0-py2.py3-none-any.whl", hash = "sha256:d6704b258494bddc3e9ddda286ef5067e4b09ab4287aea4289afdd035cc4742b"},

View File

@ -53,7 +53,7 @@ packages = [
include = ["templates/*", "templates/**/*"]
[tool.poetry.dependencies]
python = "^3.7"
python = "^3.7.2"
flask = "^1.1.2"
flask_login = "^0.5.0"
wtforms = "^2.3.3"
@ -116,9 +116,8 @@ pytest = "^7.0.0"
pytest-cov = "^3.0.0"
pre-commit = "^2.17.0"
black = "^22.1.0"
flake8 = "^4.0.1"
flake8-bugbear = "^22.1.11"
djlint = "0.7.3"
djlint = "^1.3.0"
pylint = "^2.14.4"
[build-system]
requires = ["poetry>=0.12"]

View File

@ -6,9 +6,7 @@
<div class="card">
<div class="card-body p-6 text-center">
<h1 class="h4">An email to validate your email is on its way.</h1>
<p>
Please check your inbox/spam folder.
</p>
<p>Please check your inbox/spam folder.</p>
</div>
</div>
{% endblock %}

View File

@ -29,9 +29,7 @@
style="border: 1px solid"
class="my-2 img-fluid"/>
</p>
<p>
This might seem like "magic" but trust us, only the first time is a bit awkward.
</p>
<p>This might seem like "magic" but trust us, only the first time is a bit awkward.</p>
<p>
{% if alias.mailbox_id %}
{% if alias.mailboxes | length == 1 %}

View File

@ -10,9 +10,7 @@
<p>
You are invited to become the owner of the alias <b>{{ alias.email }}</b>
</p>
<p>
Please choose the mailbox(es) that owns this alias 👇
</p>
<p>Please choose the mailbox(es) that owns this alias 👇</p>
<form method="post" class="mt-2">
<select data-width="100%" class="mailbox-select" multiple name="mailbox_ids">
{% for mailbox in mailboxes %}

View File

@ -51,9 +51,7 @@
</form>
{% endif %}
{% endif %}
<p class="mt-5">
This person can then confirm the reception and become the owner of the alias.
</p>
<p class="mt-5">This person can then confirm the reception and become the owner of the alias.</p>
<div class="alert alert-danger">After the confirmation, you can no longer use this alias.</div>
</div>
</div>

View File

@ -131,5 +131,5 @@
that.closest("form").submit();
})
</script>
</script>
{% endblock %}

View File

@ -109,9 +109,7 @@
<div id="debug-zone">
<hr />
<h3>Debug Zone</h3>
<p>
You can test whether an alias will be automatically created given the rules above
</p>
<p>You can test whether an alias will be automatically created given the rules above</p>
<div class="alert alert-info">No worries, no alias will be created during the test :)</div>
<form method="post" action="#debug-zone">
<input type="hidden" name="form-name" value="test-auto-create-rule">

View File

@ -7,12 +7,8 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Entering Sudo Mode</h1>
<p>
The next page contains security related setting.
</p>
<p>
Please enter your account password so that we can ensure it's you.
</p>
<p>The next page contains security related setting.</p>
<p>Please enter your account password so that we can ensure it's you.</p>
<form method="post">
{{ password_check_form.csrf_token }}
<div class="font-weight-bold mt-5">Password</div>

View File

@ -7,9 +7,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Extend Subscription</h1>
<p>
Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}
</p>
<p>Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</p>
<div>
<a class="buy-with-crypto"
data-custom="{{ current_user.id }}"

View File

@ -11,9 +11,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Manage Your Security Key</h1>
<p>
Unlink all keys will also disable WebAuthn 2FA.
</p>
<p>Unlink all keys will also disable WebAuthn 2FA.</p>
<form id="formManageKey" method="post">
{{ fido_manage_form.csrf_token }}
{{ fido_manage_form.credential_id(class="form-control", placeholder="") }}

View File

@ -13,9 +13,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2 text-center">Register Your Security Key</h1>
<p class="text-center">
Follow your browser's steps to register your security key with SimpleLogin
</p>
<p class="text-center">Follow your browser's steps to register your security key with SimpleLogin</p>
<form id="formRegisterKey" method="post">
{{ fido_token_form.csrf_token }}
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}

View File

@ -26,283 +26,333 @@
display: none;
}
</style>
</style>
{% endblock %}
{% block title %}Alias{% endblock %}
{% block default_content %}
<!-- Global Stats -->
<div class="row">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Aliases</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">All time</div>
</div>
<div class="h1 m-0">{{ stats.nb_alias }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Forwards</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_forward }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Replies/Sent</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_reply }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Blocks</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_block }}</div>
</div>
</div>
</div>
</div>
<!-- END Global Stats -->
<!-- Controls: buttons & search -->
<div id="filter-app">
<div class="row mb-3">
<div class="col d-flex">
<div>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-custom-email">
<button data-toggle="tooltip" title="Create a custom alias" class="btn btn-primary mr-2">
<i class="fa fa-plus"></i> New Custom Alias
</button>
</form>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<button data-toggle="tooltip" title="Create a totally random alias" class="btn btn-success">
<i class="fa fa-random"></i> Random Alias
</button>
</form>
<button id="btnGroupDrop1" type="button" class="btn btn-success dropdown-toggle btn-group-border-left" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
</button>
<div class="dropdown-menu dropdown-menu-right border-left" aria-labelledby="btnGroupDrop1">
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden" name="generator_scheme" value="{{ AliasGeneratorEnum.word.value }}">
<button class="dropdown-item">By Random Words</button>
</form>
</div>
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden" name="generator_scheme" value="{{ AliasGeneratorEnum.uuid.value }}">
<button class="dropdown-item">By UUID</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div style="margin-left: auto">
<div class="btn-group">
<a v-if="!showFilter" @click="toggleFilter()" class="btn btn-outline-secondary">
<i class="fe fe-chevrons-down"></i> Filters
</a>
</div>
</div>
</div>
</div>
<div class="row mb-2" v-if="showFilter" id="filter-control">
<!-- Filter Control -->
<div class="col d-flex">
<form method="get" class="form-inline">
<select name="sort" onchange="this.form.submit()" class="form-control mr-3 shadow">
<option value="" {% if sort == "" %} selected{% endif %}>
Sort by most recent activity
</option>
<option value="old2new" {% if sort == "old2new" %} selected{% endif %}>
Alias Old-Recent
</option>
<option value="new2old" {% if sort == "new2old" %} selected{% endif %}>
Alias Recent-Old
</option>
<option value="a2z" {% if sort == "a2z" %} selected{% endif %}>
Alias A-Z
</option>
<option value="z2a" {% if sort == "z2a" %} selected{% endif %}>
Alias Z-A
</option>
</select>
<select name="filter" onchange="this.form.submit()" class="form-control mr-3 shadow" style="max-width: 200px">
<option value="" {% if filter == "" %} selected{% endif %}>
All Aliases
</option>
<option value="pinned" {% if filter == "pinned" %} selected{% endif %}>
Pinned Aliases
</option>
<option value="enabled" {% if filter == "enabled" %} selected{% endif %}>
Only Enabled Aliases
</option>
<option value="disabled" {% if filter == "disabled" %} selected{% endif %}>
Only Disabled Aliases
</option>
<option value="hibp" {% if filter == "hibp" %} selected{% endif %}>
Only Aliases Found In Data Breaches
</option>
{% for mailbox in current_user.mailboxes() %}
<!-- Global Stats -->
<div class="row">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Aliases</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">All time</div>
</div>
<div class="h1 m-0">{{ stats.nb_alias }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Forwards</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_forward }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Replies/Sent</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_reply }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Blocks</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_block }}</div>
</div>
</div>
</div>
</div>
<!-- END Global Stats -->
<!-- Controls: buttons & search -->
<div id="filter-app">
<div class="row mb-3">
<div class="col d-flex">
<div>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-custom-email">
<button data-toggle="tooltip"
title="Create a custom alias"
class="btn btn-primary mr-2">
<i class="fa fa-plus"></i> New Custom Alias
</button>
</form>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<button data-toggle="tooltip"
title="Create a totally random alias"
class="btn btn-success">
<i class="fa fa-random"></i> Random Alias
</button>
</form>
<button id="btnGroupDrop1"
type="button"
class="btn btn-success dropdown-toggle btn-group-border-left"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
</button>
<div class="dropdown-menu dropdown-menu-right border-left"
aria-labelledby="btnGroupDrop1">
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden"
name="generator_scheme"
value="{{ AliasGeneratorEnum.word.value }}">
<button class="dropdown-item">By Random Words</button>
</form>
</div>
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden"
name="generator_scheme"
value="{{ AliasGeneratorEnum.uuid.value }}">
<button class="dropdown-item">By UUID</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div style="margin-left: auto">
<div class="btn-group">
<a v-if="!showFilter"
@click="toggleFilter()"
class="btn btn-outline-secondary">
<i class="fe fe-chevrons-down"></i> Filters
</a>
</div>
</div>
</div>
</div>
<div class="row mb-2" v-if="showFilter" id="filter-control">
<!-- Filter Control -->
<div class="col d-flex">
<form method="get" class="form-inline">
<select name="sort"
onchange="this.form.submit()"
class="form-control mr-3 shadow">
<option value="" {% if sort == "" %} selected{% endif %}>
Sort by most recent activity
</option>
<option value="old2new" {% if sort == "old2new" %} selected{% endif %}>
Alias Old-Recent
</option>
<option value="new2old" {% if sort == "new2old" %} selected{% endif %}>
Alias Recent-Old
</option>
<option value="a2z" {% if sort == "a2z" %} selected{% endif %}>
Alias A-Z
</option>
<option value="z2a" {% if sort == "z2a" %} selected{% endif %}>
Alias Z-A
</option>
</select>
<select name="filter"
onchange="this.form.submit()"
class="form-control mr-3 shadow"
style="max-width: 200px">
<option value="" {% if filter == "" %} selected{% endif %}>
All Aliases
</option>
<option value="pinned" {% if filter == "pinned" %} selected{% endif %}>
Pinned Aliases
</option>
<option value="enabled" {% if filter == "enabled" %} selected{% endif %}>
Only Enabled Aliases
</option>
<option value="disabled" {% if filter == "disabled" %} selected{% endif %}>
Only Disabled Aliases
</option>
<option value="hibp" {% if filter == "hibp" %} selected{% endif %}>
Only Aliases Found In Data Breaches
</option>
{% for mailbox in current_user.mailboxes() %}
<option value="mailbox:{{ mailbox.id }}" {% if filter == "mailbox:" ~ mailbox.id %}
<option value="mailbox:{{ mailbox.id }}" {% if filter == "mailbox:" ~ mailbox.id %}
selected {% endif %}>
{{ mailbox.email }}'s aliases
</option>
{% endfor %}
{% for directory in current_user.directories %}
{{ mailbox.email }}'s aliases
</option>
{% endfor %}
{% for directory in current_user.directories %}
<option value="directory:{{ directory.id }}" {% if filter == "directory:" ~ directory.id %}
<option value="directory:{{ directory.id }}" {% if filter == "directory:" ~ directory.id %}
selected {% endif %}>
Directory <b>{{ directory.name }}</b> aliases
</option>
{% endfor %}
</select>
<input type="search" name="query" placeholder="Enter to search for alias" class="form-control shadow mr-2" style="max-width: 15em" value="{{ query }}">
</form>
<div style="margin-left: auto">
{% if query or sort or filter %}
Directory <b>{{ directory.name }}</b> aliases
</option>
{% endfor %}
</select>
<input type="search"
name="query"
placeholder="Enter to search for alias"
class="form-control shadow mr-2"
style="max-width: 15em"
value="{{ query }}">
</form>
<div style="margin-left: auto">
{% if query or sort or filter %}
<a href="{{ url_for('dashboard.index') }}" class="btn btn-outline-secondary">Reset</a>
{% endif %}
<a v-if="showFilter" @click="toggleFilter()" class="btn btn-outline-secondary">
<i class="fe fe-chevrons-up"></i>
</a>
</div>
</div>
</div>
</div>
<!-- END Controls: buttons & search -->
<!-- Alias list -->
<div class="row">
{% for alias_info in alias_infos %}
<a href="{{ url_for('dashboard.index') }}"
class="btn btn-outline-secondary">Reset</a>
{% endif %}
<a v-if="showFilter"
@click="toggleFilter()"
class="btn btn-outline-secondary">
<i class="fe fe-chevrons-up"></i>
</a>
</div>
</div>
</div>
</div>
<!-- END Controls: buttons & search -->
<!-- Alias list -->
<div class="row">
{% for alias_info in alias_infos %}
{% set alias = alias_info.alias %}
<div class="col-12 col-lg-6" id="alias-container-{{ alias.id }}">
<div class="card p-4 shadow-sm {% if alias.id == highlight_alias_id %} highlight-row{% endif %} " {% if highlight_alias_id and alias.id != highlight_alias_id %} style="opacity: 0.6"{% endif %}>
<div class="row">
<div class="col-8">
<span class="{% if alias.id == highlight_alias_id %} highlighted{% endif %} clipboard cursor mb-0" {% if loop.index ==1 %}
{% set alias = alias_info.alias %}
<div class="col-12 col-lg-6" id="alias-container-{{ alias.id }}">
<div class="card p-4 shadow-sm {% if alias.id == highlight_alias_id %} highlight-row{% endif %} "
{% if highlight_alias_id and alias.id != highlight_alias_id %} style="opacity: 0.6"{% endif %}>
<div class="row">
<div class="col-8">
<span class="{% if alias.id == highlight_alias_id %} highlighted{% endif %} clipboard cursor mb-0" {% if loop.index ==1 %}
data-intro="This is your first <em>alias</em>.
<br />
<br />
Emails sent to an alias are <em>forwarded</em> to your <em>real</em> email address.
<br />
<br />
" data-step="2"
{% endif %}
{% if alias.enabled %}data-toggle="tooltip" title="Click to copy" data-clipboard-text="{{ alias.email }}"{% endif %}
>
<span class="font-weight-bold">{{ alias.email }}</span>
</span>
{% if alias.automatic_creation %}
<br />
<br />
Emails sent to an alias are <em>forwarded</em> to your <em>real</em> email address.
<br />
<br />
" data-step="2"
{% endif %}
{% if alias.enabled %}data-toggle="tooltip" title="Click to copy" data-clipboard-text="{{ alias.email }}"{% endif %}
>
<span class="font-weight-bold">{{ alias.email }}</span>
</span>
{% if alias.automatic_creation %}
<span class="fa fa-inbox" data-toggle="tooltip" title="This alias was automatically generated because of an incoming email"></span>
{% endif %}
{% if alias.pinned %}
<span class="fa fa-inbox"
data-toggle="tooltip"
title="This alias was automatically generated because of an incoming email"></span>
{% endif %}
{% if alias.pinned %}
<span class="fa fa-thumb-tack" data-toggle="tooltip" title="This alias is pinned"></span>
{% endif %}
{% if alias.hibp_breaches | length > 0 %}
<span class="fa fa-thumb-tack"
data-toggle="tooltip"
title="This alias is pinned"></span>
{% endif %}
{% if alias.hibp_breaches | length > 0 %}
<a href="https://haveibeenpwned.com/account/{{ alias.email }}">
<span class="fa fa-warning text-danger" data-toggle="tooltip" title="This alias was found in {{ alias.hibp_breaches | length }} data breaches. Check haveibeenpwned.com for more information."></span>
</a>
{% endif %}
{% if alias.custom_domain and not alias.custom_domain.verified %}
<a href="https://haveibeenpwned.com/account/{{ alias.email }}">
<span class="fa fa-warning text-danger"
data-toggle="tooltip"
title="This alias was found in {{ alias.hibp_breaches | length }} data breaches. Check haveibeenpwned.com for more information."></span>
</a>
{% endif %}
{% if alias.custom_domain and not alias.custom_domain.verified %}
<span class="fa fa-warning text-warning" data-toggle="tooltip" title="Alias can't receive emails as its domain doesn't have MX records set up."></span>
{% endif %}
</div>
<div class="col text-right">
<label class="custom-switch cursor" data-toggle="tooltip" {% if alias.enabled %}
<span class="fa fa-warning text-warning"
data-toggle="tooltip"
title="Alias can't receive emails as its domain doesn't have MX records set up."></span>
{% endif %}
</div>
<div class="col text-right">
<label class="custom-switch cursor" data-toggle="tooltip" {% if alias.enabled %}
title="Disable alias - stop receiving emails sent to this alias" {% else %} title="Enable alias - start receiving emails sent to this alias" {% endif %} {% if loop.index ==1 %}
data-intro="By <em>disabling</em> an alias, emails sent to this alias will <em>not</em> be forwarded to your inbox.
<br />
<br />
" data-step="3"
{% endif %}
style="padding-left: 0px">
<input type="checkbox" class="enable-disable-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.enabled else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
</div>
<!-- Email Activity -->
<div class="row mb-2">
<div class="col">
<div style="font-size: 12px">
{% if alias_info.latest_email_log != None %}
<br />
<br />
" data-step="3"
{% endif %}
style="padding-left: 0px">
<input type="checkbox" class="enable-disable-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.enabled else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
</div>
<!-- Email Activity -->
<div class="row mb-2">
<div class="col">
<div style="font-size: 12px">
{% if alias_info.latest_email_log != None %}
{% set email_log = alias_info.latest_email_log %}
{% set contact = alias_info.latest_contact %}
{% if email_log.is_reply %}
{% set email_log = alias_info.latest_email_log %}
{% set contact = alias_info.latest_contact %}
{% if email_log.is_reply %}
{{ contact.website_email }}
<i class="fa fa-reply mr-2" data-toggle="tooltip" title="Email reply/sent from alias"></i>
{{ email_log.created_at | dt }}
{% elif email_log.bounced %}
<span class="text-danger">
{{ contact.website_email }}
<i class="fa fa-warning mr-2" data-toggle="tooltip" title="Email bounced and cannot be forwarded to your mailbox"></i>
{{ email_log.created_at | dt }}
</span>
{% elif email_log.blocked %}
{{ contact.website_email }}
<i class="fa fa-ban mr-2 text-danger" data-toggle="tooltip" title="Email blocked"></i>
{{ email_log.created_at | dt }}
{% else %}
{{ contact.website_email }}
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email sent to alias"></i>
{{ email_log.created_at | dt }}
{% include 'partials/toggle_contact.html' %}
{{ contact.website_email }}
<i class="fa fa-reply mr-2"
data-toggle="tooltip"
title="Email reply/sent from alias"></i>
{{ email_log.created_at | dt }}
{% elif email_log.bounced %}
<span class="text-danger">
{{ contact.website_email }}
<i class="fa fa-warning mr-2"
data-toggle="tooltip"
title="Email bounced and cannot be forwarded to your mailbox"></i>
{{ email_log.created_at | dt }}
</span>
{% elif email_log.blocked %}
{{ contact.website_email }}
<i class="fa fa-ban mr-2 text-danger"
data-toggle="tooltip"
title="Email blocked"></i>
{{ email_log.created_at | dt }}
{% else %}
{{ contact.website_email }}
<i class="fa fa-paper-plane mr-2"
data-toggle="tooltip"
title="Email sent to alias"></i>
{{ email_log.created_at | dt }}
{% include 'partials/toggle_contact.html' %}
{% endif %}
{% else %}
No emails received/sent in the last 14 days. Created {{ alias.created_at | dt }}.
{% endif %}
</div>
</div>
</div>
<!-- END Email Activity -->
<div class="small-text mt-1">
Alias description
</div>
<div class="d-flex mb-2">
<div class="flex-grow-1 mr-2">
<textarea id="note-{{ alias.id }}" name="note" class="form-control" style="font-size: 12px" rows="2" placeholder="e.g. where the alias is used or why is it created">{{ alias.note or "" }}</textarea>
</div>
<div>
{% endif %}
{% else %}
No emails received/sent in the last 14 days. Created {{ alias.created_at | dt }}.
{% endif %}
</div>
</div>
</div>
<!-- END Email Activity -->
<div class="small-text mt-1">
Alias description
</div>
<div class="d-flex mb-2">
<div class="flex-grow-1 mr-2">
<textarea id="note-{{ alias.id }}" name="note" class="form-control" style="font-size: 12px" rows="2" placeholder="e.g. where the alias is used or why is it created">{{ alias.note or "" }}</textarea>
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-note btn btn-sm btn-outline-success w-100">
Save
</a>
</div>
</div>
<!-- Send Email && More button -->
<div class="row">
</div>
</div>
<!-- Send Email && More button -->
<div class="row">
<div class="col">
<a href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id) }}" id="send-email-{{ alias.id }}" {% if loop.index ==1 %}
data-intro="Not only alias can receive emails, it can <em>send</em> emails too!
@ -318,8 +368,8 @@ Alias description
" data-toggle="tooltip" title="Add new contact, manage your existing contacts">
Contacts&nbsp; &nbsp;<i class="fe fe-send"></i>
</a>
</div>
{% if not current_user.expand_alias_info %}
</div>
{% if not current_user.expand_alias_info %}
<div class="col text-right">
<a class="btn btn-sm collapsed"
@ -335,11 +385,11 @@ Alias description
</span>
</a>
</div>
{% endif %}
</div>
<!-- END Send Email && More button -->
<!-- Collapse section -->
<div class="{% if not current_user.expand_alias_info %} collapse{% endif %} mt-2"
{% endif %}
</div>
<!-- END Send Email && More button -->
<!-- Collapse section -->
<div class="{% if not current_user.expand_alias_info %} collapse{% endif %} mt-2"
id="alias-{{ alias.id }}">
{% if alias_info.latest_email_log != None %}
@ -457,9 +507,9 @@ Alias description
</form>
</div>
</div>
</div>
<!-- END Collapse section -->
</div>
</div>
<!-- END Collapse section -->
</div>
</div>
{% endfor %}
</div>

View File

@ -229,5 +229,5 @@
$(this).closest("form").submit();
});
enableDragDropForPGPKeys('#pgp-public-key');
</script>
{% endblock %}
</script>
{% endblock %}

View File

@ -113,9 +113,7 @@ Upgrade your SimpleLogin account
<i class="fe fe-chevron-up"></i>
</span>
</a>
<p class="mt-2 small">
Starts at $2.5/month (billed yearly)
</p>
<p class="mt-2 small">Starts at $2.5/month (billed yearly)</p>
</div>
</div>
{% endif %}

View File

@ -100,5 +100,5 @@
}
$(this).next('.custom-file-label').html(files.join(', '));
});
</script>
</script>
{% endblock %}

View File

@ -11,9 +11,7 @@
You are about to block the alias
<a href="mailto:{{ alias }}">{{ alias }}</a>
</p>
<p>
After this, you will stop receiving all emails sent to this alias, please confirm.
</p>
<p>After this, you will stop receiving all emails sent to this alias, please confirm.</p>
<form method="post">
<button class="btn btn-warning">Confirm</button>
</form>

View File

@ -2,18 +2,14 @@
<p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ text }}
</p>
margin: .4em 0 1.1875em;">{{ text }}</p>
{% endmacro %}
<!-- To be used instead of render_text, much better! -->
{% macro text() %}
<p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ caller() }}
</p>
margin: .4em 0 1.1875em;">{{ caller() }}</p>
{% endmacro %}
{% macro render_button(button_text, link) %}
<!-- Action -->

View File

@ -13,9 +13,7 @@
alt="SimpleLogin logo">
</a>
<!-- End Logo -->
<p class="small text-white">
SimpleLogin is an open-source email alias solution to protect your email address.
</p>
<p class="small text-white">SimpleLogin is an open-source email alias solution to protect your email address.</p>
<p class="small text-white">
SimpleLogin is the product of SimpleLogin SAS, registered in France under the SIREN number 884302134.
</p>

View File

@ -26,159 +26,172 @@
background-color: #0ff;
border: 3px solid #88d748;
}
</style>
</style>
{% endblock %}
{% block single_content %}
<form class="card" method="post" data-parsley-validate style="max-width: 40rem; margin: auto; border-radius: 2%">
{% if not client.approved %}
<form class="card"
method="post"
data-parsley-validate
style="max-width: 40rem; margin: auto; border-radius: 2%">
{% if not client.approved %}
<div class="alert alert-warning" style="border-bottom: 3px solid;">
<b>{{ client.name }}</b> is in Dev Mode and isn't approved (yet) by SimpleLogin. <b>Please make sure you trust {{ client.name }} before proceeding.</b>
</div>
{% endif %}
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="alert alert-warning" style="border-bottom: 3px solid;">
<b>{{ client.name }}</b> is in Dev Mode and isn't approved (yet) by SimpleLogin. <b>Please make sure you trust {{ client.name }} before proceeding.</b>
</div>
{% endif %}
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<hr />
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following info:
</div>
<div>
{% for scope in client.get_scopes() %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<hr />
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following info:
</div>
<div>
{% for scope in client.get_scopes() %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
avatar:
<img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">{{ user_info[scope.value] }}</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if client.icon_id %}
avatar:
<img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">{{ user_info[scope.value] }}</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if client.icon_id %}
<div class="text-center">
<img src="{{ client.get_icon_url() }}" class="small-client-icon">
</div>
{% endif %}
<div class="text-center" style="font-size: larger">
<b>{{ client.name }}</b> will receive the following info
</div>
<hr />
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Email</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-email">
<option selected value="{{ suggested_email }}">
{{ suggested_email }}
</option>
<option value="{{ current_user.email }}">
{{ current_user.email }} (Personal Email)
</option>
{% for email in other_emails %}
<div class="text-center">
<img src="{{ client.get_icon_url() }}" class="small-client-icon">
</div>
{% endif %}
<div class="text-center" style="font-size: larger">
<b>{{ client.name }}</b> will receive the following info
</div>
<hr />
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Email</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-email">
<option selected value="{{ suggested_email }}">
{{ suggested_email }}
</option>
<option value="{{ current_user.email }}">
{{ current_user.email }} (Personal Email)
</option>
{% for email in other_emails %}
<option value="{{ email }}">
{{ email }}
</option>
{% endfor %}
</select>
{% if current_user.can_create_new_alias() %}
<option value="{{ email }}">
{{ email }}
</option>
{% endfor %}
</select>
{% if current_user.can_create_new_alias() %}
<div class="mt-2 mb-2">OR</div>
<div class="row mb-2">
<div class="col-sm-6 pr-1 mb-1" style="min-width: 5em">
<input name="prefix" class="form-control" type="text" maxlength="40" data-parsley-pattern="[0-9a-z-_.]{1,}" data-parsley-trigger="change" data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported." placeholder="Alias prefix, for example newsletter.com-123_xyz" autofocus>
</div>
<div class="col-sm-6" style="padding-left: 5px">
<select class="form-control" name="suffix">
{% for suffix in suffixes %}
<div class="mt-2 mb-2">OR</div>
<div class="row mb-2">
<div class="col-sm-6 pr-1 mb-1" style="min-width: 5em">
<input name="prefix"
class="form-control"
type="text"
maxlength="40"
data-parsley-pattern="[0-9a-z-_.]{1,}"
data-parsley-trigger="change"
data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported."
placeholder="Alias prefix, for example newsletter.com-123_xyz"
autofocus>
</div>
<div class="col-sm-6" style="padding-left: 5px">
<select class="form-control" name="suffix">
{% for suffix in suffixes %}
<option value="{{ suffix.signed_suffix }}">
{% if suffix.is_custom %}
<option value="{{ suffix.signed_suffix }}">
{% if suffix.is_custom %}
{{ suffix.suffix }} (your domain)
{% else %}
{{ suffix.suffix }}
{% endif %}
</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
</div>
</div>
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Name</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-name">
<option selected value="{{ suggested_name }}">
{{ suggested_name }}
</option>
{% for name in other_names %}
{{ suffix.suffix }} (your domain)
{% else %}
{{ suffix.suffix }}
{% endif %}
</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
</div>
</div>
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Name</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-name">
<option selected value="{{ suggested_name }}">
{{ suggested_name }}
</option>
{% for name in other_names %}
<option value="{{ name }}">
{{ name }}
</option>
{% endfor %}
</select>
<div class="mt-2">OR</div>
<div class="mt-2">
<input class="form-control" name="custom-name">
</div>
</div>
</div>
{% if current_user.profile_picture_id %}
<option value="{{ name }}">
{{ name }}
</option>
{% endfor %}
</select>
<div class="mt-2">OR</div>
<div class="mt-2">
<input class="form-control" name="custom-name">
</div>
</div>
</div>
{% if current_user.profile_picture_id %}
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: 2rem">Avatar</label>
</div>
<div class="col-md-9" style="display: flex; align-items: center">
<label>
<input type="radio" name="avatar-choice" value="real" checked>
<img src="{{ current_user.profile_picture_url() }}" class="small-profile-picture">
</label>
<div style="margin: 0 1rem">OR</div>
<label>
<input type="radio" name="avatar-choice" value="default">
<img src="{{ url_for('static', filename='default-avatar.png') }}" class="small-profile-picture">
</label>
</div>
</div>
{% endif %}
{% endif %}
{% if client_user %}
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: 2rem">Avatar</label>
</div>
<div class="col-md-9" style="display: flex; align-items: center">
<label>
<input type="radio" name="avatar-choice" value="real" checked>
<img src="{{ current_user.profile_picture_url() }}"
class="small-profile-picture">
</label>
<div style="margin: 0 1rem">OR</div>
<label>
<input type="radio" name="avatar-choice" value="default">
<img src="{{ url_for('static', filename='default-avatar.png') }}"
class="small-profile-picture">
</label>
</div>
</div>
{% endif %}
{% endif %}
{% if client_user %}
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Cancel</button>
</div>
</div>
{% else %}
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Deny</button>
</div>
</div>
{% endif %}
</div>
</form>
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Cancel</button>
</div>
</div>
{% else %}
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Deny</button>
</div>
</div>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -18,9 +18,7 @@
</div>
<div class="px-8" style="width:675px;margin:auto;">
<div class="my-6">
<p style="font-size: 1.2em;">
Quickly create aliases everywhere using our {{ browser_name }} extension
</p>
<p style="font-size: 1.2em;">Quickly create aliases everywhere using our {{ browser_name }} extension</p>
<a class="px-4 py-3 mt-4 text-decoration-none text-center"
style="background:black;
color:white;
@ -32,9 +30,7 @@
</div>
<hr style="margin-top: 75px">
<div style="margin-top: 75px">
<p style="font-size: 1.2em;">
Or install the extension later and start discovering our features on our web app
</p>
<p style="font-size: 1.2em;">Or install the extension later and start discovering our features on our web app</p>
<a class="px-4 py-3 mt-4 text-decoration-none text-center"
style="background:white;
color:black;

View File

@ -53,8 +53,7 @@
</p>
<p id="extension-version-text"
class="font-weight-light"
style="display:none;">
</p>
style="display:none;"></p>
</div>
</div>
<script type="text/javascript">