1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-02 22:33:05 +00:00

Compare commits

...

38 commits

Author SHA1 Message Date
26a8785d7a Wrap everything in a try-except… If you read this, consider rewriting in Rust! 2023-11-03 22:10:33 +01:00
91f0effd3a Fix crash on empty active_assistants 2023-11-03 22:04:21 +01:00
190c8bdba4 Use dev/data as data dir during development 2023-11-03 21:57:47 +01:00
7029e6915a Update Containerfile 2023-11-03 21:44:39 +01:00
060422a751 Allow copying requirements.txt 2023-11-03 21:44:07 +01:00
4f414b000f Make init.sh executable 2023-11-03 21:42:36 +01:00
6109a3d696 Update deps 2023-11-03 21:42:19 +01:00
2bc15ec7f2 Better wording 2023-11-03 21:41:28 +01:00
ef037a41e2 Remove some newlines 2023-11-03 21:23:36 +01:00
98bd273bcb Remove requirements.txt after installation 2023-11-03 21:23:29 +01:00
4e25ea51d0 Install sqlite3 2023-11-03 21:23:08 +01:00
d076a19c78 Update to Python 3.12 2023-11-03 21:22:58 +01:00
32f5141ac7 Update .gitignore 2023-11-03 21:22:13 +01:00
82684cf579 Update .containerignore 2023-11-03 21:22:06 +01:00
fcfd282e79 Remove nosec comments 2023-11-03 21:21:50 +01:00
570e4e121e Remove flake8 2023-11-03 21:20:56 +01:00
794c41d677 Update lock file 2023-11-02 20:48:41 +01:00
8d64b18fd9 Explicitely add setuptools to fix "No module named 'pkg_resources'" 2023-11-02 20:48:19 +01:00
f2a1aed87b Move single option up 2023-11-02 20:42:27 +01:00
837c6529d5 Update versions in pyproject.toml 2023-11-02 20:32:47 +01:00
f91762edcb Update numpy 2023-11-02 20:30:56 +01:00
bc81e50917 Update gunicorn 2023-11-02 20:27:14 +01:00
7efa58d044 Update flask-sqlalchemy 2023-11-02 20:25:34 +01:00
353477c8bc Update flask 2023-11-02 20:22:36 +01:00
7f1a498b04 Update flask-security-Too 2023-11-02 20:20:59 +01:00
3723a50b94 Update email-validator 2023-11-02 20:20:49 +01:00
68289c6a2c Apply RUF 2023-11-02 20:10:06 +01:00
75dff0bde0 Apply PERF 2023-11-02 20:06:16 +01:00
e2d5a920ec Apply PL 2023-11-02 20:04:09 +01:00
a33a7ec060 Apply PTH 2023-11-02 19:44:24 +01:00
b1d3c26b07 Apply SIM 2023-11-02 19:38:09 +01:00
029cc974e3 Apply RET 2023-11-02 19:24:14 +01:00
d408e8fc08 Apply C4 2023-11-02 19:01:07 +01:00
45258fa4c6 Unused iteration variables 2023-11-02 18:55:05 +01:00
907951a49f Sort imports 2023-11-02 18:47:52 +01:00
897f3c57f8 Use ruff 2023-11-02 18:47:46 +01:00
e4e22b5dd3 Remove unneeded dev-deps section 2023-11-02 18:09:19 +01:00
0371cbf688 Apply ruff lints 2023-11-02 18:09:10 +01:00
24 changed files with 833 additions and 837 deletions

View file

@ -1,10 +1,13 @@
.*
/dev_data/
/docs/
/migrations/
/deploy/traefik/
/dev/
compose.yaml
Containerfile
/poetry.lock
/pyproject.toml
*.pyc
__pycache__/
/pyproject.toml
.containerignore
Containerfile

10
.flake8
View file

@ -1,10 +0,0 @@
[flake8]
ignore =
E402,
E501,
E711,
E712,
W503,
exclude =
advlabdb/__init__.py,
migrations/,

2
.gitignore vendored
View file

@ -9,7 +9,7 @@ settings.ini
/migrations/
# Development
/dev_data/
/dev/
# Python
__pycache__/

View file

@ -1,15 +1,14 @@
FROM docker.io/library/python:3.11-slim
FROM docker.io/library/python:3.12-slim
RUN apt update && apt install -y sqlite3
EXPOSE 80
ENV ADVLABDB_DATA_DIR=/volumes/data
WORKDIR /volumes/repo
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY . .
RUN rm requirements.txt
CMD ["/bin/bash", "init.sh"]
CMD ["./init.sh"]

View file

@ -1,9 +1,5 @@
# AdvLabDB
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
[![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
## About
AdvLabDB is a database with a web interface for labs.

View file

@ -1,8 +1,9 @@
import subprocess
from datetime import datetime
from flask import flash
from flask_login import current_user
from sqlalchemy import select
import subprocess
from . import data_dir
from .models import Assistant, Semester, User, db
@ -14,6 +15,7 @@ def update_final_experiment_and_part_marks():
flash("Manually updated all final experiment and part marks.", "success")
def backup(backup_prefix):
db_path = data_dir / "db/advlabdb.sqlite"
@ -23,22 +25,27 @@ def backup(backup_prefix):
now = datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
dest = db_bk_dir / f"{backup_prefix}_{now}.sqlite"
status = subprocess.run(["sqlite3", db_path, f".backup {dest}"]).returncode
if status == 0:
flash(f"Created a database backup at the path {dest}", "success")
else:
try:
status = subprocess.run(["sqlite3", db_path, f".backup {dest}"], check=False).returncode
if status == 0:
flash(f"Created a database backup at the path {dest}", "success")
else:
flash("Failed to create a database backup!", "danger")
except:
flash("Failed to create a database backup! Make sure that `sqlite3` is installed on your system!", "danger")
def manual_backup():
backup("manual")
def deactivate_assistants():
user_ids_to_deactivate = db.session.scalars(
select(Assistant.user_id)
.join(User)
.where(User.active == True)
.where(User.active is True)
.except_(
select(Assistant.user_id).join(Assistant.semester_experiments).join(Semester).where(Semester.done == False)
select(Assistant.user_id).join(Assistant.semester_experiments).join(Semester).where(Semester.done is False)
)
)

View file

@ -28,7 +28,11 @@ from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional
from wtforms.widgets import NumberInput
from . import data_dir, user_datastore
from .actions import deactivate_assistants, update_final_experiment_and_part_marks, manual_backup
from .actions import (
deactivate_assistants,
manual_backup,
update_final_experiment_and_part_marks,
)
from .admin_link_formatters import (
admin_formatter,
appointment_date_formatter,
@ -108,7 +112,7 @@ def semesterExperimentQueryFactory():
class SemesterRowFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
semesters = Semester.sortedSemestersStartingWithNewest()
return tuple((semester.id, str(semester)) for semester in semesters)
@ -189,7 +193,8 @@ class UserView(SecureAdminModelView):
"active_semester",
"current_login_at",
]
column_details_list = column_list + [
column_details_list = [
*column_list,
"phone_number",
"mobile_phone_number",
"building",
@ -307,9 +312,8 @@ class UserView(SecureAdminModelView):
f"{model.email} registered with role(s): {', '.join(role.name for role in model.roles)}.",
category="success",
)
else:
if model == current_user:
flash(f"Active semester is {model.active_semester}.", "warning")
elif model == current_user:
flash(f"Active semester is {model.active_semester}.", "warning")
class SemesterView(SecureAdminModelView):
@ -319,16 +323,16 @@ class SemesterView(SecureAdminModelView):
last_semester = Semester.lastSemester()
if last_semester.label == "WS":
return "SS"
else:
return "WS"
return "WS"
@staticmethod
def defaultFormYear():
last_semester = Semester.lastSemester()
if last_semester.label == "WS":
return last_semester.year + 1
else:
return last_semester.year
return last_semester.year
label = RadioField(
"Semester",
@ -414,10 +418,9 @@ class SemesterView(SecureAdminModelView):
else:
current_user.active_semester = model
flash(f"Active semester changed to the new semester {model}!", "warning")
else:
if model.done:
next_semester = db.session.get(Semester, model.id + 1)
model.set_done(next_semester)
elif model.done:
next_semester = db.session.get(Semester, model.id + 1)
model.set_done(next_semester)
def programQueryFactory():
@ -480,7 +483,8 @@ class StudentView(SecureAdminModelView):
"first_name",
"last_name",
]
column_searchable_list = column_sortable_list + [
column_searchable_list = [
*column_sortable_list,
"uni_email",
"contact_email",
]
@ -538,7 +542,7 @@ def groupQueryFactory():
class PartRowFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
parts = db.session.scalars(select(Part).where(Part.semester == current_user.active_semester))
return tuple((part.id, part.str_without_semester()) for part in parts)
@ -616,7 +620,7 @@ def partStudentQueryFactory():
class ProgramRowFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
programs = db.session.scalars(select(Program))
return tuple((program.id, str(program)) for program in programs)
@ -632,14 +636,14 @@ class GroupView(SecureAdminModelView):
if is_created:
def query_factory():
return partStudentQueryFactory().where(PartStudent.group == None)
return partStudentQueryFactory().where(PartStudent.group is None)
else:
def query_factory():
return partStudentQueryFactory().where(
or_(
and_(PartStudent.group == None, Part.program == group.program),
and_(PartStudent.group is None, Part.program == group.program),
PartStudent.group == group,
)
)
@ -714,7 +718,8 @@ class ExperimentView(SecureAdminModelView):
"title",
]
form_columns = column_list + [
form_columns = [
*column_list,
"description",
"wiki_link",
"building",
@ -749,7 +754,7 @@ class ExperimentView(SecureAdminModelView):
def assistantQueryFactory():
return Assistant.query.join(User).where(User.active == True)
return Assistant.query.join(User).where(User.active is True)
def weighting_field(label: str, default: float):
@ -771,7 +776,7 @@ class SemesterExperimentView(SecureAdminModelView):
class CreateForm(FlaskForm):
@staticmethod
def experimentQueryFactory():
return Experiment.query.where(Experiment.active == True)
return Experiment.query.where(Experiment.active is True)
experiment = QuerySelectField(
"Experiment",
@ -853,8 +858,8 @@ def userHasRoleFilterFactory(column, role_name):
query = query.join(User)
if bool(int(value)): # value is string "0" or "1"
return query.join(User.roles).where(Role.name == role_name)
else:
return query.where(not_(User.roles.any(Role.name == role_name)))
return query.where(not_(User.roles.any(Role.name == role_name)))
return UserHasRoleFilter(column, f"Currently has {role_name} role")
@ -930,9 +935,9 @@ class AdminView(SecureAdminModelView):
class ExperimentRowFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
activeExperiments = db.session.scalars(select(Experiment).where(Experiment.active == True))
activeExperiments = db.session.scalars(select(Experiment).where(Experiment.active is True))
return tuple(
(
f"{activeExperiment.number},{activeExperiment.program_id}",
@ -1080,9 +1085,8 @@ class GroupExperimentView(SecureAdminModelView):
self.session.add(appointment)
def after_model_change(self, form, model, is_created):
if is_created:
if model.appointments:
flash(f"Appointments {model.appointments} added.", "success")
if is_created and model.appointments:
flash(f"Appointments {model.appointments} added.", "success")
def groupExperimentQueryFactory():
@ -1094,7 +1098,7 @@ def groupExperimentQueryFactory():
class AssistantRowFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
activeAssistants = assistantQueryFactory()
return tuple((assistant.id, str(assistant)) for assistant in activeAssistants)
@ -1187,9 +1191,9 @@ class ExperimentMarkView(SecureAdminModelView):
class AdminFilter(FilterEqual):
def get_options(self, view):
if not has_request_context():
return tuple()
return ()
admins = db.session.scalars(select(Admin).join(User).where(User.active == True))
admins = db.session.scalars(select(Admin).join(User).where(User.active is True))
return tuple((admin.id, str(admin)) for admin in admins)
def apply(self, query, value, alias=None):
@ -1387,8 +1391,12 @@ class AnalysisView(SecureAdminBaseView):
if form.validate_on_submit():
if form.assistant_marks_submit.data:
return assistant_marks_analysis(self)
elif form.final_part_marks_submit.data:
ret = assistant_marks_analysis(self)
if ret is not None:
return ret
if form.final_part_marks_submit.data:
return final_part_marks_analysis(self)
return self.render("analysis/analysis.jinja.html", form=form)

View file

@ -2,6 +2,7 @@ from base64 import b64encode
from io import BytesIO
import numpy as np
from flask import flash
from flask_login import current_user
from matplotlib.figure import Figure
from matplotlib.ticker import MaxNLocator
@ -76,7 +77,11 @@ def mark_hists(markType, active_assistants):
def assistant_marks_analysis(cls):
active_assistants = db.session.scalars(select(Assistant).join(User).where(User.active == True)).all()
active_assistants = db.session.scalars(select(Assistant).join(User).where(User.active is True)).all()
if not active_assistants:
flash("No active assistants!", "warning")
return None
oral_mark_hists = mark_hists("Oral", active_assistants)
protocol_mark_hists = mark_hists("Protocol", active_assistants)

View file

@ -211,7 +211,7 @@ class AssistantUserView(SecureAssistantModelView):
@staticmethod
def semesterQueryFactory():
# Show only last two semesters to assistants
return Semester.query.order_by(Semester.id.desc()).where(Semester.done == False).limit(2)
return Semester.query.order_by(Semester.id.desc()).where(Semester.done is False).limit(2)
active_semester = QuerySelectField(
"Active Semester",

View file

@ -6,7 +6,7 @@ from pathlib import Path
def get_data_dir() -> Path:
data_dir_env_variable = "ADVLABDB_DATA_DIR"
data_dir = Path(environ.get(data_dir_env_variable, "dev_data")).resolve()
data_dir = Path(environ.get(data_dir_env_variable, "dev/data")).resolve()
if not data_dir.is_dir():
sys.exit(
@ -34,16 +34,14 @@ def load_config(file_name: str, data_dir: Path):
def get_secrets(data_dir: Path):
config = load_config("secrets.ini", data_dir)
secrets = config["Secrets"]
return secrets
return config["Secrets"]
def get_settings(data_dir: Path):
config = load_config("settings.ini", data_dir)
settings = config["Settings"]
return settings
return config["Settings"]
def set_config(app, data_dir: Path):

View file

@ -44,9 +44,9 @@ class SecureAdminIndexView(CustomIndexView):
.join(Assistant.semester_experiments)
.where(SemesterExperiment.semester == current_user.active_semester)
.join(SemesterExperiment.group_experiments)
.where(GroupExperiment.experiment_marks_missing == True)
.where(GroupExperiment.experiment_marks_missing is True)
.join(GroupExperiment.experiment_marks)
.where(ExperimentMark.final_experiment_mark == None)
.where(ExperimentMark.final_experiment_mark is None)
.group_by(Assistant.id)
.order_by(func.count().desc())
)
@ -71,7 +71,7 @@ class SecureAssistantIndexView(CustomIndexView):
.where(SemesterExperiment.semester == current_user.active_semester)
.join(SemesterExperiment.assistants)
.where(Assistant.user == current_user)
.where(ExperimentMark.final_experiment_mark == None)
.where(ExperimentMark.final_experiment_mark is None)
)
return self.render(
@ -280,6 +280,8 @@ class SecureAssistantModelView(CustomModelView):
self.handle_view_exception(ModelViewException("Unauthorized action!"))
return redirect(self.url)
return None
def on_model_delete(self, model):
reportBadAttempt("An assistant tried to delete a model!")
raise ModelViewException("Assistants can not delete models!")

View file

@ -4,9 +4,8 @@ from pathlib import Path
from flask import flash
from sqlalchemy import select
from . import data_dir
from .exceptions import DatabaseImportException
from .actions import backup
from .exceptions import DatabaseImportException
from .models import (
Appointment,
Assistant,
@ -26,7 +25,7 @@ from .models import (
def is_null(entry):
return entry == "NULL" or entry == ""
return entry in {"NULL", ""}
def nullable(entry):
@ -58,7 +57,7 @@ def importFromFile(filePath: Path):
groupExperiments = {}
appointments = {}
with open(filePath) as f:
with filePath.open() as f:
flash("Reading file...")
expectingTable = True

View file

@ -20,11 +20,7 @@ def assistant_group_experiment_form_factory(current_user, group_experiment):
appointments = group_experiment.appointments
appointment_num = 1
for appointment in appointments:
description = None
if appointment.special:
description = "Yes"
else:
description = "No"
description = "Yes" if appointment.special else "No"
setattr(
AssistantGroupExperimentForm,

View file

@ -231,10 +231,7 @@ class Group(db.Model):
.order_by(Group.number.desc())
)
if highestGroupNumber is not None:
number = highestGroupNumber + 1
else:
number = 1
number = highestGroupNumber + 1 if highestGroupNumber is not None else 1
return Group(
program=program,
@ -460,13 +457,12 @@ class Appointment(db.Model):
if assistant is not None:
if assistant not in semesterExperimentAssistants:
raise DatabaseException(f"{assistant} is not responsible for {semesterExperiment}!")
elif len(semesterExperimentAssistants) == 1:
assistant = semesterExperimentAssistants[0]
else:
if len(semesterExperimentAssistants) == 1:
assistant = semesterExperimentAssistants[0]
else:
raise DatabaseException(
f"Experiment {semesterExperiment} has more than one assistant. You have to assign one of these assistants: {semesterExperimentAssistants}"
)
raise DatabaseException(
f"Experiment {semesterExperiment} has more than one assistant. You have to assign one of these assistants: {semesterExperimentAssistants}"
)
return assistant
@ -529,9 +525,8 @@ class Semester(db.Model):
def __init__(self, label, year, **kwargs):
last_semester = Semester.lastSemester()
if last_semester is not None:
if year < last_semester.year or (year == last_semester.year and label == "SS"):
raise DatabaseException(f"You can only create semesters later than the last semester {last_semester}!")
if last_semester is not None and (year < last_semester.year or (year == last_semester.year and label == "SS")):
raise DatabaseException(f"You can only create semesters later than the last semester {last_semester}!")
super().__init__(label=label, year=year, **kwargs)
@ -542,7 +537,7 @@ class Semester(db.Model):
if transferParts:
semester.transferPartsFrom(oldSemester)
for experiment in db.session.scalars(select(Experiment).where(Experiment.active == True)):
for experiment in db.session.scalars(select(Experiment).where(Experiment.active is True)):
newSemesterExperiment = SemesterExperiment(experiment=experiment, semester=semester)
if transferAssistants:
@ -580,7 +575,7 @@ class Semester(db.Model):
.join(GroupExperiment)
.join(SemesterExperiment)
.where(SemesterExperiment.semester == self)
.where(ExperimentMark.final_experiment_mark == None)
.where(ExperimentMark.final_experiment_mark is None)
)
def set_done(self, next_semester=None):
@ -657,9 +652,9 @@ class ExperimentMark(db.Model):
def check(part_student, group_experiment):
if not part_student.group:
raise DatabaseException("The part student does not have a group yet!")
else:
if group_experiment not in part_student.group.group_experiments:
raise DatabaseException("The group of the part student does not have the given group experiment!")
if group_experiment not in part_student.group.group_experiments:
raise DatabaseException("The group of the part student does not have the given group experiment!")
def __init__(self, part_student, group_experiment, **kwargs):
ExperimentMark.check(part_student, group_experiment)

View file

@ -14,12 +14,12 @@ def util_processor():
footer = f"AdvLabDB - <a href={repo_url} target='_blank' rel='noopener noreferrer'>Source code</a>"
return dict(active_semester_str=active_semester_str, current_user=current_user, footer=footer)
return {"active_semester_str": active_semester_str, "current_user": current_user, "footer": footer}
@security.login_context_processor
def login_context_processor():
return dict(message=settings.get("login_view_message"))
return {"message": settings.get("login_view_message")}
@bp.route("/")
@ -44,15 +44,9 @@ def post_login():
if current_active_semester.done:
current_user.set_last_semester_as_active()
if current_user.has_role("admin"):
endpoint_base = "admin"
else:
endpoint_base = "assistant"
endpoint_base = "admin" if current_user.has_role("admin") else "assistant"
if current_user.login_count == 1:
url = url_for(endpoint_base + "_docs.index")
else:
url = url_for(endpoint_base + ".index")
url = url_for(endpoint_base + "_docs.index") if current_user.login_count == 1 else url_for(endpoint_base + ".index")
return redirect(url)
@ -60,10 +54,7 @@ def post_login():
@bp.route("/user-settings")
@auth_required()
def user_settings():
if current_user.has_role("admin"):
role = "admin"
else:
role = "assistant"
role = "admin" if current_user.has_role("admin") else "assistant"
url = url_for("main.index") + role + "/user/edit/?id=" + str(current_user.id)

View file

@ -12,39 +12,38 @@ def _reset_admin_password(manage):
app = create_app(create_for_server=False)
with app.app_context():
with db.session.begin():
admins = db.session.scalars(select(Admin).join(User).where(User.active == True)).all()
activate_user = False
with app.app_context(), db.session.begin():
admins = db.session.scalars(select(Admin).join(User).where(User.active is True)).all()
activate_user = False
if len(admins) == 0:
click.echo("There is no admin with an active user. The user of the chosen admin will be activated.")
admins = db.session.scalars(select(Admin)).all()
activate_user = True
if len(admins) == 0:
click.echo("There is no admin with an active user. The user of the chosen admin will be activated.")
admins = db.session.scalars(select(Admin)).all()
activate_user = True
num_admins = len(admins)
num_admins = len(admins)
prompt = "Admins:\n"
for ind, admin in enumerate(admins):
user = admin.user
prompt += f"[{ind}] {user.first_name} {user.last_name}: {user.email}\n"
prompt += f"Enter number [0-{num_admins - 1}]"
prompt = "Admins:\n"
for ind, admin in enumerate(admins):
user = admin.user
prompt += f"[{ind}] {user.first_name} {user.last_name}: {user.email}\n"
prompt += f"Enter number [0-{num_admins - 1}]"
admin_index = click.prompt(
prompt,
type=click.IntRange(0, num_admins - 1),
)
admin_index = click.prompt(
prompt,
type=click.IntRange(0, num_admins - 1),
)
chosen_admin_user = admins[admin_index].user
chosen_admin_user = admins[admin_index].user
new_password = randomPassword()
new_password = randomPassword()
admin_change_password(
chosen_admin_user, new_password, notify=False
) # Password is automatically hashed with this function
admin_change_password(
chosen_admin_user, new_password, notify=False
) # Password is automatically hashed with this function
if activate_user:
chosen_admin_user.active = True
if activate_user:
chosen_admin_user.active = True
manage.box(f"New password: {new_password}")

View file

@ -9,10 +9,10 @@ def _generate_secrets():
file = data_dir / "secrets.ini"
if file.is_file():
click.echo(f"Skipping secrets generation because the secrets file does already exist at {file}.")
click.echo(f"Skipping secrets generation because the secrets file already exists at {file}.")
return
with open(file, "w") as f:
with file.open("w") as f:
f.write("[Secrets]\n")
key = secrets.SystemRandom().getrandbits(128)

View file

@ -11,7 +11,7 @@ from advlabdb.models import MAX_YEAR, MIN_YEAR, Admin, Semester, db
class EmailParamType(click.ParamType):
def convert(self, value, param, ctx):
try:
return validate_email(value).email
return validate_email(value).normalized
except Exception:
self.fail(f"{value} is not a valid email!", param, ctx)
@ -19,69 +19,66 @@ class EmailParamType(click.ParamType):
def _init_db(manage):
db_file = data_dir / "db/advlabdb.sqlite"
if db_file.is_file():
click.echo(f"Skipping database initialization because the database does already exist at {db_file}.")
click.echo(f"Skipping database initialization because the database already exists at {db_file}.")
return
app = create_app(create_for_server=False)
with app.app_context():
with db.session.begin():
# Create new database
db.create_all()
with app.app_context(), db.session.begin():
# Create new database
db.create_all()
semester_label = click.prompt(
"Enter the label of the current semester",
type=click.Choice(("SS", "WS")),
)
semester_year = click.prompt(
f"Enter the year of the current semester (between {MIN_YEAR} and {MAX_YEAR})",
type=click.IntRange(MIN_YEAR, MAX_YEAR),
)
semester_label = click.prompt(
"Enter the label of the current semester",
type=click.Choice(("SS", "WS")),
)
semester_year = click.prompt(
f"Enter the year of the current semester (between {MIN_YEAR} and {MAX_YEAR})",
type=click.IntRange(MIN_YEAR, MAX_YEAR),
)
semester = Semester(label=semester_label, year=semester_year)
semester = Semester(label=semester_label, year=semester_year)
db.session.add(semester)
db.session.add(semester)
adminRole = user_datastore.create_role(name="admin")
user_datastore.create_role(name="assistant")
adminRole = user_datastore.create_role(name="admin")
user_datastore.create_role(name="assistant")
manage.box("The first admin account will be created now.")
manage.box("The first admin account will be created now.")
admin_email = click.prompt(
"Enter the admin's email address",
type=EmailParamType(),
)
admin_email = click.prompt(
"Enter the admin's email address",
type=EmailParamType(),
)
admin_first_name = click.prompt("Enter the admin's first name")
admin_last_name = click.prompt("Enter the admin's last name")
admin_phone_number = click.prompt(
"Enter the admin's phone number (optional)", default="", show_default=False
)
admin_mobile_phone_number = click.prompt(
"Enter the admin's mobile phone number (optional)", default="", show_default=False
)
admin_building = click.prompt("Enter the admin's building (optional)", default="", show_default=False)
admin_room = click.prompt("Enter the admin's room (optional)", default="", show_default=False)
admin_first_name = click.prompt("Enter the admin's first name")
admin_last_name = click.prompt("Enter the admin's last name")
admin_phone_number = click.prompt("Enter the admin's phone number (optional)", default="", show_default=False)
admin_mobile_phone_number = click.prompt(
"Enter the admin's mobile phone number (optional)", default="", show_default=False
)
admin_building = click.prompt("Enter the admin's building (optional)", default="", show_default=False)
admin_room = click.prompt("Enter the admin's room (optional)", default="", show_default=False)
admin_password = randomPassword()
admin_hashed_password = hash_password(admin_password)
admin_password = randomPassword()
admin_hashed_password = hash_password(admin_password)
admin_user = user_datastore.create_user(
email=admin_email,
password=admin_hashed_password,
roles=[adminRole],
first_name=admin_first_name.strip(),
last_name=admin_last_name.strip(),
phone_number=admin_phone_number.strip() or None,
mobile_phone_number=admin_mobile_phone_number.strip() or None,
building=admin_building.strip() or None,
room=admin_room.strip() or None,
active_semester=semester,
)
admin_user = user_datastore.create_user(
email=admin_email,
password=admin_hashed_password,
roles=[adminRole],
first_name=admin_first_name.strip(),
last_name=admin_last_name.strip(),
phone_number=admin_phone_number.strip() or None,
mobile_phone_number=admin_mobile_phone_number.strip() or None,
building=admin_building.strip() or None,
room=admin_room.strip() or None,
active_semester=semester,
)
admin = Admin(user=admin_user)
admin = Admin(user=admin_user)
db.session.add(admin)
db.session.add(admin)
manage.box(f"Admin password: {admin_password}")

View file

@ -1,6 +1,6 @@
from datetime import date
from math import ceil
from random import randint, random
from random import randint
import click
from flask_security.utils import hash_password
@ -37,7 +37,7 @@ def _generate_test_db():
if db_file.is_file():
click.echo(
click.style(
f"Generating a test database is not allowed because a database does already exist at {db_file}.",
f"Generating a test database is not allowed because a database already exists at {db_file}.",
fg="red",
)
)
@ -45,202 +45,196 @@ def _generate_test_db():
app = create_app(create_for_server=False)
with app.app_context():
with db.session.begin():
db.create_all()
with app.app_context(), db.session.begin():
db.create_all()
bs_prog = db_add(Program(label="BS"))
ms_prog = db_add(Program(label="MS"))
be_prog = db_add(Program(label="BE"))
programs = (bs_prog, ms_prog, be_prog)
bs_prog = db_add(Program(label="BS"))
ms_prog = db_add(Program(label="MS"))
be_prog = db_add(Program(label="BE"))
programs = (bs_prog, ms_prog, be_prog)
ss = db_add(Semester(label="SS", year=23))
ws = db_add(Semester(label="WS", year=23))
semesters = (ss, ws)
ss = db_add(Semester(label="SS", year=23))
ws = db_add(Semester(label="WS", year=23))
semesters = (ss, ws)
bs_1_ss_part = db_add(Part(semester=ss, program=bs_prog, number=1))
bs_2_ss_part = db_add(Part(semester=ss, program=bs_prog, number=2))
ms_1_ss_part = db_add(Part(semester=ss, program=ms_prog, number=1))
ms_2_ss_part = db_add(Part(semester=ss, program=ms_prog, number=2))
be_1_ss_part = db_add(Part(semester=ss, program=be_prog, number=1))
be_2_ss_part = db_add(Part(semester=ss, program=be_prog, number=2))
ss_parts = (bs_1_ss_part, bs_2_ss_part, ms_1_ss_part, ms_2_ss_part, be_1_ss_part, be_2_ss_part)
bs_1_ss_part = db_add(Part(semester=ss, program=bs_prog, number=1))
bs_2_ss_part = db_add(Part(semester=ss, program=bs_prog, number=2))
ms_1_ss_part = db_add(Part(semester=ss, program=ms_prog, number=1))
ms_2_ss_part = db_add(Part(semester=ss, program=ms_prog, number=2))
be_1_ss_part = db_add(Part(semester=ss, program=be_prog, number=1))
be_2_ss_part = db_add(Part(semester=ss, program=be_prog, number=2))
ss_parts = (bs_1_ss_part, bs_2_ss_part, ms_1_ss_part, ms_2_ss_part, be_1_ss_part, be_2_ss_part)
db_add(Part(semester=ws, program=bs_prog, number=1))
bs_2_ws_part = db_add(Part(semester=ws, program=bs_prog, number=2))
ms_1_ws_part = db_add(Part(semester=ws, program=ms_prog, number=1))
ms_2_ws_part = db_add(Part(semester=ws, program=ms_prog, number=2))
db_add(Part(semester=ws, program=be_prog, number=1))
be_2_ws_part = db_add(Part(semester=ws, program=be_prog, number=2))
db_add(Part(semester=ws, program=bs_prog, number=1))
bs_2_ws_part = db_add(Part(semester=ws, program=bs_prog, number=2))
ms_1_ws_part = db_add(Part(semester=ws, program=ms_prog, number=1))
ms_2_ws_part = db_add(Part(semester=ws, program=ms_prog, number=2))
db_add(Part(semester=ws, program=be_prog, number=1))
be_2_ws_part = db_add(Part(semester=ws, program=be_prog, number=2))
semester_program_part_students = {
ss: {program: [] for program in programs},
ws: {program: [] for program in programs},
}
semester_program_part_students = {
ss: {program: [] for program in programs},
ws: {program: [] for program in programs},
}
def add_part_student(student, part):
part_student = db_add(PartStudent(student=student, part=part))
semester_program_part_students[part.semester][part.program].append(part_student)
def add_part_student(student, part):
part_student = db_add(PartStudent(student=student, part=part))
semester_program_part_students[part.semester][part.program].append(part_student)
for ind, name in enumerate(student_names):
if randint(0, 1) == 0: # nosec: B311
contact_email = f"{name[0]}-{name[1]}@private.de".lower()
else:
contact_email = None
for ind, name in enumerate(student_names):
contact_email = f"{name[0]}-{name[1]}@private.de".lower() if ind % 2 == 0 else None
student = db_add(
Student(
student_number=ind,
first_name=name[0],
last_name=name[1],
uni_email=f"{name[0]}-{name[1]}@uni.de".lower(),
contact_email=contact_email,
)
student = db_add(
Student(
student_number=ind,
first_name=name[0],
last_name=name[1],
uni_email=f"{name[0]}-{name[1]}@uni.de".lower(),
contact_email=contact_email,
)
)
part = ss_parts[ind % len(ss_parts)]
add_part_student(student, part)
part = ss_parts[ind % len(ss_parts)]
add_part_student(student, part)
if random() < 0.9: # nosec: B311
# Transfer to the next part in the second semester
if part == bs_1_ss_part:
add_part_student(student, bs_2_ws_part)
elif part == bs_2_ss_part:
add_part_student(student, ms_1_ws_part)
elif part == ms_1_ss_part:
add_part_student(student, ms_2_ws_part)
elif part == be_1_ss_part:
add_part_student(student, be_2_ws_part)
if ind % 10 < 9:
# Transfer to the next part in the second semester
if part == bs_1_ss_part:
add_part_student(student, bs_2_ws_part)
elif part == bs_2_ss_part:
add_part_student(student, ms_1_ws_part)
elif part == ms_1_ss_part:
add_part_student(student, ms_2_ws_part)
elif part == be_1_ss_part:
add_part_student(student, be_2_ws_part)
program_groups = {program: [] for program in programs}
for semester, program_part_students in semester_program_part_students.items():
for program, part_students in program_part_students.items():
if len(part_students) % 2 == 1:
# Add the first 3 students into a special group for an uneven number of students
group_part_students = []
for i in range(3):
if len(part_students) == 0:
break
group_part_students.append(part_students.pop(0))
group = db_add(Group.customInit(group_part_students))
program_groups[program].append(group)
while len(part_students) >= 2:
# Add the rest of the students into groups of 2
group = db_add(Group.customInit([part_students.pop(0) for i in range(2)]))
program_groups[program].append(group)
program_semester_experiments = {program: [] for program in programs}
all_semester_experiments = []
for ind, title in enumerate(experiment_titles):
program = programs[ind % len(programs)]
experiment = db_add(
Experiment(
number=ind + 1,
program=program,
title=title,
building="Not assigned",
room="Not assigned",
duration_in_days=2,
)
)
for semester in semesters:
semester_experiment = db_add(SemesterExperiment(experiment=experiment, semester=semester))
program_semester_experiments[program].append(semester_experiment)
all_semester_experiments.append(semester_experiment)
all_group_experiments = []
for program in programs:
semester_experiments = program_semester_experiments[program]
num_semester_experiments = len(semester_experiments)
for ind, group in enumerate(program_groups[program]):
num_tries = 0
while True:
try:
semester_experiment = semester_experiments[ind % num_semester_experiments]
group_experiment = db_add(
GroupExperiment(semester_experiment=semester_experiment, group=group)
)
except DatabaseException as ex:
# Catch an error when a student becomes the same experiment for the second time.
# Try the next experiment!
ind += 1
num_tries += 1
if num_tries == num_semester_experiments:
raise ex
else:
program_groups = {program: [] for program in programs}
for program_part_students in semester_program_part_students.values():
for program, part_students in program_part_students.items():
if len(part_students) % 2 == 1:
# Add the first 3 students into a special group for an uneven number of students
group_part_students = []
for _ in range(3):
if len(part_students) == 0:
break
all_group_experiments.append(group_experiment)
group_part_students.append(part_students.pop(0))
adminRole = user_datastore.create_role(name="admin")
assistantRole = user_datastore.create_role(name="assistant")
group = db_add(Group.customInit(group_part_students))
program_groups[program].append(group)
admin_names = (
("Aileen", "Grant"),
("Ben", "Huber"),
while len(part_students) >= 2:
# Add the rest of the students into groups of 2
group = db_add(Group.customInit([part_students.pop(0) for i in range(2)]))
program_groups[program].append(group)
program_semester_experiments = {program: [] for program in programs}
all_semester_experiments = []
for ind, title in enumerate(experiment_titles):
program = programs[ind % len(programs)]
experiment = db_add(
Experiment(
number=ind + 1,
program=program,
title=title,
building="Not assigned",
room="Not assigned",
duration_in_days=2,
)
)
for name in admin_names:
user = user_datastore.create_user(
email=f"{name[0]}-{name[1]}@uni.de".lower(),
password=hash_password("admin"),
roles=[adminRole],
first_name=name[0],
last_name=name[1],
active_semester=ws,
)
db_add(Admin(user=user))
for semester in semesters:
semester_experiment = db_add(SemesterExperiment(experiment=experiment, semester=semester))
program_semester_experiments[program].append(semester_experiment)
all_semester_experiments.append(semester_experiment)
num_assistants = len(assistant_names)
num_assistant_experiments = ceil(len(all_semester_experiments) / num_assistants)
all_group_experiments = []
for program in programs:
semester_experiments = program_semester_experiments[program]
num_semester_experiments = len(semester_experiments)
for name in assistant_names:
user = user_datastore.create_user(
email=f"{name[0]}-{name[1]}@uni.de".lower(),
password=hash_password("assistant"),
roles=[assistantRole],
first_name=name[0],
last_name=name[1],
mobile_phone_number="01" + "".join(str(randint(0, 9)) for i in range(10)), # nosec: B311
active_semester=ws,
)
semester_experiments = []
for i in range(num_assistant_experiments):
if len(all_semester_experiments) == 0:
for ind, group in enumerate(program_groups[program]):
num_tries = 0
while True:
try:
semester_experiment = semester_experiments[ind % num_semester_experiments]
group_experiment = db_add(GroupExperiment(semester_experiment=semester_experiment, group=group))
except DatabaseException as ex:
# Catch an error when a student becomes the same experiment for the second time.
# Try the next experiment!
ind += 1
num_tries += 1
if num_tries == num_semester_experiments:
raise ex
else:
break
semester_experiments.append(all_semester_experiments.pop(0))
all_group_experiments.append(group_experiment)
db_add(Assistant(user=user, semester_experiments=semester_experiments))
adminRole = user_datastore.create_role(name="admin")
assistantRole = user_datastore.create_role(name="assistant")
for group_experiment in all_group_experiments:
semester_experiment = group_experiment.semester_experiment
special = False
semester = semester_experiment.semester
if semester.label == "SS":
month = randint(3, 8) # nosec: B311
if month <= 4:
special = True
else:
month = randint(9, 12) # nosec: B311
if month <= 10:
special = True
admin_names = (
("Aileen", "Grant"),
("Ben", "Huber"),
)
for name in admin_names:
user = user_datastore.create_user(
email=f"{name[0]}-{name[1]}@uni.de".lower(),
password=hash_password("admin"),
roles=[adminRole],
first_name=name[0],
last_name=name[1],
active_semester=ws,
)
year = 2000 + semester.year
day = randint(1, 28) # nosec: B311
for appointment_date in (date(year, month, day), date(year, month, day + 1)):
db_add(
Appointment(
date=appointment_date,
special=special,
group_experiment=group_experiment,
assistant=semester_experiment.assistants[0],
)
db_add(Admin(user=user))
num_assistants = len(assistant_names)
num_assistant_experiments = ceil(len(all_semester_experiments) / num_assistants)
for name in assistant_names:
user = user_datastore.create_user(
email=f"{name[0]}-{name[1]}@uni.de".lower(),
password=hash_password("assistant"),
roles=[assistantRole],
first_name=name[0],
last_name=name[1],
mobile_phone_number="01" + "".join(str(randint(0, 9)) for i in range(10)),
active_semester=ws,
)
semester_experiments = []
for _ in range(num_assistant_experiments):
if len(all_semester_experiments) == 0:
break
semester_experiments.append(all_semester_experiments.pop(0))
db_add(Assistant(user=user, semester_experiments=semester_experiments))
for group_experiment in all_group_experiments:
semester_experiment = group_experiment.semester_experiment
special = False
semester = semester_experiment.semester
if semester.label == "SS":
month = randint(3, 8)
if month <= 4:
special = True
else:
month = randint(9, 12)
if month <= 10:
special = True
year = 2000 + semester.year
day = randint(1, 28)
for appointment_date in (date(year, month, day), date(year, month, day + 1)):
db_add(
Appointment(
date=appointment_date,
special=special,
group_experiment=group_experiment,
assistant=semester_experiment.assistants[0],
)
)

0
init.sh Normal file → Executable file
View file

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3
import subprocess # nosec 404
import subprocess
import click
@ -13,7 +13,7 @@ from cli.test.generate_test_db.main import _generate_test_db
class Manage:
@staticmethod
def run(command: str, **kwargs):
return subprocess.run(command, shell=True, **kwargs) # nosec B602
return subprocess.run(command, shell=True, check=False, **kwargs)
@staticmethod
def box(message: str):

470
poetry.lock generated
View file

@ -21,13 +21,13 @@ tz = ["python-dateutil"]
[[package]]
name = "blinker"
version = "1.6.3"
version = "1.7.0"
description = "Fast, simple object-to-object and broadcast signaling"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "blinker-1.6.3-py3-none-any.whl", hash = "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa"},
{file = "blinker-1.6.3.tar.gz", hash = "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d"},
{file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"},
{file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"},
]
[[package]]
@ -57,136 +57,66 @@ files = [
[[package]]
name = "contourpy"
version = "1.1.0"
version = "1.2.0"
description = "Python library for calculating contours of 2D quadrilateral grids"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"},
{file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"},
{file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"},
{file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"},
{file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"},
{file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"},
{file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"},
{file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"},
{file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"},
{file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"},
{file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"},
{file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"},
{file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"},
{file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"},
{file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"},
{file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"},
{file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"},
{file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"},
{file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"},
{file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"},
{file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"},
{file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"},
{file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"},
{file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"},
{file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"},
{file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"},
{file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"},
{file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"},
{file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"},
{file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"},
{file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"},
{file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"},
{file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"},
{file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"},
{file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"},
{file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"},
{file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"},
{file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"},
{file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"},
{file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"},
{file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"},
{file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"},
{file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"},
{file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"},
{file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"},
{file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"},
{file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"},
{file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"},
{file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"},
{file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"},
{file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"},
{file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"},
{file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"},
{file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"},
{file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"},
{file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"},
{file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"},
]
[package.dependencies]
numpy = ">=1.16"
[package.extras]
bokeh = ["bokeh", "selenium"]
docs = ["furo", "sphinx-copybutton"]
mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"]
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "wurlitzer"]
[[package]]
name = "contourpy"
version = "1.1.1"
description = "Python library for calculating contours of 2D quadrilateral grids"
optional = false
python-versions = ">=3.8"
files = [
{file = "contourpy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b"},
{file = "contourpy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d"},
{file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae"},
{file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916"},
{file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0"},
{file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1"},
{file = "contourpy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d"},
{file = "contourpy-1.1.1-cp310-cp310-win32.whl", hash = "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431"},
{file = "contourpy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb"},
{file = "contourpy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2"},
{file = "contourpy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b"},
{file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b"},
{file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532"},
{file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e"},
{file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5"},
{file = "contourpy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62"},
{file = "contourpy-1.1.1-cp311-cp311-win32.whl", hash = "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33"},
{file = "contourpy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45"},
{file = "contourpy-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a"},
{file = "contourpy-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e"},
{file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442"},
{file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8"},
{file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7"},
{file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf"},
{file = "contourpy-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d"},
{file = "contourpy-1.1.1-cp312-cp312-win32.whl", hash = "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6"},
{file = "contourpy-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970"},
{file = "contourpy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d"},
{file = "contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9"},
{file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217"},
{file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684"},
{file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce"},
{file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8"},
{file = "contourpy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251"},
{file = "contourpy-1.1.1-cp38-cp38-win32.whl", hash = "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7"},
{file = "contourpy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9"},
{file = "contourpy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba"},
{file = "contourpy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34"},
{file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887"},
{file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718"},
{file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f"},
{file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85"},
{file = "contourpy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e"},
{file = "contourpy-1.1.1-cp39-cp39-win32.whl", hash = "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0"},
{file = "contourpy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887"},
{file = "contourpy-1.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e"},
{file = "contourpy-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3"},
{file = "contourpy-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23"},
{file = "contourpy-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb"},
{file = "contourpy-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163"},
{file = "contourpy-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c"},
{file = "contourpy-1.1.1.tar.gz", hash = "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab"},
]
[package.dependencies]
numpy = {version = ">=1.16,<2.0", markers = "python_version <= \"3.11\""}
numpy = ">=1.20,<2.0"
[package.extras]
bokeh = ["bokeh", "selenium"]
docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.4.1)", "types-Pillow"]
mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"]
test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "wurlitzer"]
test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"]
[[package]]
name = "cycler"
@ -224,28 +154,28 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"]
[[package]]
name = "email-validator"
version = "1.3.1"
version = "2.1.0.post1"
description = "A robust email address syntax and deliverability validation library."
optional = false
python-versions = ">=3.5"
python-versions = ">=3.8"
files = [
{file = "email_validator-1.3.1-py2.py3-none-any.whl", hash = "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda"},
{file = "email_validator-1.3.1.tar.gz", hash = "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2"},
{file = "email_validator-2.1.0.post1-py3-none-any.whl", hash = "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637"},
{file = "email_validator-2.1.0.post1.tar.gz", hash = "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44"},
]
[package.dependencies]
dnspython = ">=1.15.0"
dnspython = ">=2.0.0"
idna = ">=2.0.0"
[[package]]
name = "flask"
version = "2.3.3"
version = "3.0.0"
description = "A simple framework for building complex web applications."
optional = false
python-versions = ">=3.8"
files = [
{file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"},
{file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"},
{file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"},
{file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"},
]
[package.dependencies]
@ -253,7 +183,7 @@ blinker = ">=1.6.2"
click = ">=8.1.3"
itsdangerous = ">=2.1.2"
Jinja2 = ">=3.1.2"
Werkzeug = ">=2.3.7"
Werkzeug = ">=3.0.0"
[package.extras]
async = ["asgiref (>=3.2)"]
@ -325,17 +255,17 @@ Flask = "*"
[[package]]
name = "flask-security-too"
version = "5.3.1"
version = "5.3.2"
description = "Quickly add security features to your Flask application."
optional = false
python-versions = ">=3.8"
files = [
{file = "Flask-Security-Too-5.3.1.tar.gz", hash = "sha256:1dafe00c611ce3811e7fe1686ecd7750938806a1ec3c5a278185f31958895d3c"},
{file = "Flask_Security_Too-5.3.1-py3-none-any.whl", hash = "sha256:159ed080dce4a717c2852eac50443221f50b391f5af6f03c82febc3740d572d1"},
{file = "Flask-Security-Too-5.3.2.tar.gz", hash = "sha256:c0b5075df0d64a9ef35b04c88d31fbf405a59243736f6d6d0a1a4b4845abf3e5"},
{file = "Flask_Security_Too-5.3.2-py3-none-any.whl", hash = "sha256:eb6b56c6e1d108a11a1a86dcb17dd97e596c027b68a73f0acc9fc7f433c441a6"},
]
[package.dependencies]
email-validator = ">=1.1.1"
email-validator = ">=2.0.0"
Flask = ">=2.3.2"
Flask-Login = ">=0.6.2"
Flask-Principal = ">=0.4.0"
@ -354,18 +284,18 @@ mfa = ["cryptography (>=40.0.2)", "phonenumberslite (>=8.13.11)", "qrcode (>=7.4
[[package]]
name = "flask-sqlalchemy"
version = "3.0.5"
version = "3.1.1"
description = "Add SQLAlchemy support to your Flask application."
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "flask_sqlalchemy-3.0.5-py3-none-any.whl", hash = "sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283"},
{file = "flask_sqlalchemy-3.0.5.tar.gz", hash = "sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1"},
{file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"},
{file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"},
]
[package.dependencies]
flask = ">=2.2.5"
sqlalchemy = ">=1.4.18"
sqlalchemy = ">=2.0.16"
[[package]]
name = "flask-wtf"
@ -388,57 +318,57 @@ email = ["email-validator"]
[[package]]
name = "fonttools"
version = "4.43.1"
version = "4.44.0"
description = "Tools to manipulate font files"
optional = false
python-versions = ">=3.8"
files = [
{file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273"},
{file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c"},
{file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca"},
{file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6"},
{file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184"},
{file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7"},
{file = "fonttools-4.43.1-cp310-cp310-win32.whl", hash = "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02"},
{file = "fonttools-4.43.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef"},
{file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b"},
{file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f"},
{file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0"},
{file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8"},
{file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd"},
{file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b"},
{file = "fonttools-4.43.1-cp311-cp311-win32.whl", hash = "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204"},
{file = "fonttools-4.43.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8"},
{file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5"},
{file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c"},
{file = "fonttools-4.43.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680"},
{file = "fonttools-4.43.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215"},
{file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3"},
{file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b"},
{file = "fonttools-4.43.1-cp312-cp312-win32.whl", hash = "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f"},
{file = "fonttools-4.43.1-cp312-cp312-win_amd64.whl", hash = "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72"},
{file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590"},
{file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7"},
{file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6"},
{file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d"},
{file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0"},
{file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933"},
{file = "fonttools-4.43.1-cp38-cp38-win32.whl", hash = "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b"},
{file = "fonttools-4.43.1-cp38-cp38-win_amd64.whl", hash = "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896"},
{file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba"},
{file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13"},
{file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477"},
{file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860"},
{file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86"},
{file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3"},
{file = "fonttools-4.43.1-cp39-cp39-win32.whl", hash = "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad"},
{file = "fonttools-4.43.1-cp39-cp39-win_amd64.whl", hash = "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a"},
{file = "fonttools-4.43.1-py3-none-any.whl", hash = "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9"},
{file = "fonttools-4.43.1.tar.gz", hash = "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212"},
{file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c"},
{file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae"},
{file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5"},
{file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf"},
{file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e"},
{file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86"},
{file = "fonttools-4.44.0-cp310-cp310-win32.whl", hash = "sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287"},
{file = "fonttools-4.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c"},
{file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3"},
{file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d"},
{file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d"},
{file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4"},
{file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e"},
{file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2"},
{file = "fonttools-4.44.0-cp311-cp311-win32.whl", hash = "sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5"},
{file = "fonttools-4.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189"},
{file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267"},
{file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b"},
{file = "fonttools-4.44.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400"},
{file = "fonttools-4.44.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66bc6efd829382f7a7e6cf33c2fb32b13edc8a239eb15f32acbf197dce7a0165"},
{file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2"},
{file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb"},
{file = "fonttools-4.44.0-cp312-cp312-win32.whl", hash = "sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5"},
{file = "fonttools-4.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e"},
{file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928"},
{file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d"},
{file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219"},
{file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a"},
{file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2"},
{file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc"},
{file = "fonttools-4.44.0-cp38-cp38-win32.whl", hash = "sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97"},
{file = "fonttools-4.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040"},
{file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39"},
{file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2"},
{file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d"},
{file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111"},
{file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0"},
{file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20"},
{file = "fonttools-4.44.0-cp39-cp39-win32.whl", hash = "sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b"},
{file = "fonttools-4.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b"},
{file = "fonttools-4.44.0-py3-none-any.whl", hash = "sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335"},
{file = "fonttools-4.44.0.tar.gz", hash = "sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252"},
]
[package.extras]
all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"]
all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres", "scipy"]
lxml = ["lxml (>=4.0,<5)"]
@ -448,7 +378,7 @@ repacker = ["uharfbuzz (>=0.23.0)"]
symfont = ["sympy"]
type1 = ["xattr"]
ufo = ["fs (>=2.2.0,<3)"]
unicode = ["unicodedata2 (>=15.0.0)"]
unicode = ["unicodedata2 (>=15.1.0)"]
woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
[[package]]
@ -523,17 +453,17 @@ test = ["objgraph", "psutil"]
[[package]]
name = "gunicorn"
version = "20.1.0"
version = "21.2.0"
description = "WSGI HTTP Server for UNIX"
optional = false
python-versions = ">=3.5"
files = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
{file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"},
{file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"},
]
[package.dependencies]
setuptools = ">=3.0"
packaging = "*"
[package.extras]
eventlet = ["eventlet (>=0.24.1)"]
@ -846,36 +776,43 @@ python-dateutil = ">=2.7"
[[package]]
name = "numpy"
version = "1.25.2"
version = "1.26.1"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
python-versions = "<3.13,>=3.9"
files = [
{file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"},
{file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"},
{file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"},
{file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"},
{file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"},
{file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"},
{file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"},
{file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"},
{file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"},
{file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"},
{file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"},
{file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"},
{file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"},
{file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"},
{file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"},
{file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"},
{file = "numpy-1.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af"},
{file = "numpy-1.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575"},
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244"},
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67"},
{file = "numpy-1.26.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2"},
{file = "numpy-1.26.1-cp310-cp310-win32.whl", hash = "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297"},
{file = "numpy-1.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab"},
{file = "numpy-1.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a"},
{file = "numpy-1.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9"},
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3"},
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974"},
{file = "numpy-1.26.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c"},
{file = "numpy-1.26.1-cp311-cp311-win32.whl", hash = "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b"},
{file = "numpy-1.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53"},
{file = "numpy-1.26.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f"},
{file = "numpy-1.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24"},
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e"},
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124"},
{file = "numpy-1.26.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c"},
{file = "numpy-1.26.1-cp312-cp312-win32.whl", hash = "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66"},
{file = "numpy-1.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7"},
{file = "numpy-1.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e"},
{file = "numpy-1.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617"},
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e"},
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908"},
{file = "numpy-1.26.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5"},
{file = "numpy-1.26.1-cp39-cp39-win32.whl", hash = "sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104"},
{file = "numpy-1.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f"},
{file = "numpy-1.26.1.tar.gz", hash = "sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe"},
]
[[package]]
@ -1030,60 +967,85 @@ files = [
[[package]]
name = "sqlalchemy"
version = "1.4.50"
version = "2.0.23"
description = "Database Abstraction Library"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb"},
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be"},
{file = "SQLAlchemy-1.4.50.tar.gz", hash = "sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"},
{file = "SQLAlchemy-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-win32.whl", hash = "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855"},
{file = "SQLAlchemy-2.0.23-cp311-cp311-win_amd64.whl", hash = "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-win32.whl", hash = "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846"},
{file = "SQLAlchemy-2.0.23-cp312-cp312-win_amd64.whl", hash = "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-win32.whl", hash = "sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18"},
{file = "SQLAlchemy-2.0.23-cp37-cp37m-win_amd64.whl", hash = "sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-win32.whl", hash = "sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306"},
{file = "SQLAlchemy-2.0.23-cp38-cp38-win_amd64.whl", hash = "sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-win32.whl", hash = "sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884"},
{file = "SQLAlchemy-2.0.23-cp39-cp39-win_amd64.whl", hash = "sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b"},
{file = "SQLAlchemy-2.0.23-py3-none-any.whl", hash = "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d"},
{file = "SQLAlchemy-2.0.23.tar.gz", hash = "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69"},
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
typing-extensions = ">=4.2.0"
[package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
aioodbc = ["aioodbc", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"]
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
mypy = ["mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0)"]
mysql-connector = ["mysql-connector-python"]
oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"]
oracle = ["cx-oracle (>=8)"]
oracle-oracledb = ["oracledb (>=1.0.1)"]
postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
postgresql-psycopg = ["psycopg (>=3.0.7)"]
postgresql-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"]
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3-binary"]
[[package]]
@ -1133,5 +1095,5 @@ email = ["email-validator"]
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "e21cdc3b5d6a838558d704b3b01d854b501dfc2a3f36db83536eef29061b5d5b"
python-versions = ">3.11,<3.13"
content-hash = "b38d3a2a1660a6acfce855d57802eef662b3e0718c6835759a3650f4ad097454"

View file

@ -6,31 +6,67 @@ authors = ["Mo Bitar <mo8it@proton.me>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
python = ">3.11,<3.13"
click = "^8.1"
email-validator = "^1.3"
flask = "^2.2"
email-validator = "^2.1"
flask = "^3.0"
flask-admin = "^1.6"
flask-login = "^0.6"
flask-migrate = "^4.0"
flask-security-Too = "^5.1"
flask-sqlalchemy = "^3.0"
flask-wtf = "^1.1"
gunicorn = "^20.1"
flask-security-Too = "^5.3"
flask-sqlalchemy = "^3.1"
flask-wtf = "^1.2"
gunicorn = "^21.2"
markupsafe = "^2.1"
matplotlib = "^3.6"
numpy = "^1.24"
sqlalchemy = "<2"
matplotlib = "^3.8"
numpy = "^1.26"
setuptools = "^68.2"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
[tool.ruff]
line-length = 120
select = [
"E",
"W",
"F",
"I",
"UP",
"YTT",
"S",
"B",
"C4",
"ICN",
"PIE",
"Q",
"RET",
"SIM",
"TID",
"PTH",
"ERA",
"PL",
"NPY",
"PERF",
"RUF",
]
ignore = [
"E5",
"S3",
"S6",
"RET504",
"PLR0912",
"PLR0915",
"PLW2901",
"PLR2004",
"PERF203",
"RUF012",
]
[tool.ruff.per-file-ignores]
"__init__.py" = ["I"]
[tool.isort]
profile = "black"
skip = ["__init__.py"]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

View file

@ -1,144 +1,136 @@
alembic==1.12.1 ; python_version >= "3.11" and python_version < "4.0" \
alembic==1.12.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:47d52e3dfb03666ed945becb723d6482e52190917fdb47071440cfdba05d92cb \
--hash=sha256:bca5877e9678b454706347bc10b97cb7d67f300320fa5c3a94423e8266e2823f
blinker==1.6.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d \
--hash=sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa
click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \
blinker==1.7.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \
--hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182
click==8.1.7 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
colorama==0.4.6 ; python_version >= "3.11" and python_version < "4.0" and platform_system == "Windows" \
colorama==0.4.6 ; python_full_version > "3.11.0" and python_version < "3.13" and platform_system == "Windows" \
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
contourpy==1.1.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6 \
--hash=sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33 \
--hash=sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8 \
--hash=sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d \
--hash=sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d \
--hash=sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c \
--hash=sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf \
--hash=sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e \
--hash=sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e \
--hash=sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163 \
--hash=sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532 \
--hash=sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2 \
--hash=sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8 \
--hash=sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1 \
--hash=sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b \
--hash=sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9 \
--hash=sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916 \
--hash=sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23 \
--hash=sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb \
--hash=sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a \
--hash=sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e \
--hash=sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442 \
--hash=sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684 \
--hash=sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34 \
--hash=sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d \
--hash=sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d \
--hash=sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9 \
--hash=sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45 \
--hash=sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718 \
--hash=sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab \
--hash=sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3 \
--hash=sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae \
--hash=sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb \
--hash=sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5 \
--hash=sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba \
--hash=sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0 \
--hash=sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217 \
--hash=sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887 \
--hash=sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887 \
--hash=sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62 \
--hash=sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431 \
--hash=sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b \
--hash=sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce \
--hash=sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b \
--hash=sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f \
--hash=sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85 \
--hash=sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e \
--hash=sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7 \
--hash=sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251 \
--hash=sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970 \
--hash=sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0 \
--hash=sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7
cycler==0.12.1 ; python_version >= "3.11" and python_version < "4.0" \
contourpy==1.2.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8 \
--hash=sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956 \
--hash=sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5 \
--hash=sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063 \
--hash=sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286 \
--hash=sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a \
--hash=sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686 \
--hash=sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9 \
--hash=sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f \
--hash=sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4 \
--hash=sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e \
--hash=sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0 \
--hash=sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e \
--hash=sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488 \
--hash=sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399 \
--hash=sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431 \
--hash=sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779 \
--hash=sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9 \
--hash=sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab \
--hash=sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0 \
--hash=sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd \
--hash=sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e \
--hash=sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc \
--hash=sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6 \
--hash=sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316 \
--hash=sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808 \
--hash=sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0 \
--hash=sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f \
--hash=sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843 \
--hash=sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9 \
--hash=sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95 \
--hash=sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9 \
--hash=sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de \
--hash=sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4 \
--hash=sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4 \
--hash=sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa \
--hash=sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8 \
--hash=sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776 \
--hash=sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41 \
--hash=sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108 \
--hash=sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e \
--hash=sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8 \
--hash=sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727 \
--hash=sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a
cycler==0.12.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \
--hash=sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c
dnspython==2.4.2 ; python_version >= "3.11" and python_version < "4.0" \
dnspython==2.4.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8 \
--hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984
email-validator==1.3.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda \
--hash=sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2
flask-admin==1.6.1 ; python_version >= "3.11" and python_version < "4.0" \
email-validator==2.1.0.post1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44 \
--hash=sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637
flask-admin==1.6.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369 \
--hash=sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406
flask-login==0.6.3 ; python_version >= "3.11" and python_version < "4.0" \
flask-login==0.6.3 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \
--hash=sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d
flask-migrate==4.0.5 ; python_version >= "3.11" and python_version < "4.0" \
flask-migrate==4.0.5 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef \
--hash=sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1
flask-principal==0.4.0 ; python_version >= "3.11" and python_version < "4.0" \
flask-principal==0.4.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:f5d6134b5caebfdbb86f32d56d18ee44b080876a27269560a96ea35f75c99453
flask-security-too==5.3.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:159ed080dce4a717c2852eac50443221f50b391f5af6f03c82febc3740d572d1 \
--hash=sha256:1dafe00c611ce3811e7fe1686ecd7750938806a1ec3c5a278185f31958895d3c
flask-sqlalchemy==3.0.5 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1 \
--hash=sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283
flask-wtf==1.2.1 ; python_version >= "3.11" and python_version < "4.0" \
flask-security-too==5.3.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:c0b5075df0d64a9ef35b04c88d31fbf405a59243736f6d6d0a1a4b4845abf3e5 \
--hash=sha256:eb6b56c6e1d108a11a1a86dcb17dd97e596c027b68a73f0acc9fc7f433c441a6
flask-sqlalchemy==3.1.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0 \
--hash=sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312
flask-wtf==1.2.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695 \
--hash=sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287
flask==2.3.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc \
--hash=sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b
fonttools==4.43.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad \
--hash=sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c \
--hash=sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba \
--hash=sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212 \
--hash=sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86 \
--hash=sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477 \
--hash=sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f \
--hash=sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef \
--hash=sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02 \
--hash=sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860 \
--hash=sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72 \
--hash=sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3 \
--hash=sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b \
--hash=sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184 \
--hash=sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7 \
--hash=sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5 \
--hash=sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9 \
--hash=sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7 \
--hash=sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b \
--hash=sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933 \
--hash=sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0 \
--hash=sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6 \
--hash=sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8 \
--hash=sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c \
--hash=sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590 \
--hash=sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215 \
--hash=sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680 \
--hash=sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8 \
--hash=sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0 \
--hash=sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b \
--hash=sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca \
--hash=sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3 \
--hash=sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6 \
--hash=sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13 \
--hash=sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204 \
--hash=sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f \
--hash=sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273 \
--hash=sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b \
--hash=sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd \
--hash=sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a \
--hash=sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d \
--hash=sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896
greenlet==3.0.1 ; python_version >= "3.11" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version < "4.0" \
flask==3.0.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638 \
--hash=sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58
fonttools==4.44.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e \
--hash=sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5 \
--hash=sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e \
--hash=sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2 \
--hash=sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b \
--hash=sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4 \
--hash=sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97 \
--hash=sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111 \
--hash=sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252 \
--hash=sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf \
--hash=sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189 \
--hash=sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b \
--hash=sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e \
--hash=sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267 \
--hash=sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20 \
--hash=sha256:66bc6efd829382f7a7e6cf33c2fb32b13edc8a239eb15f32acbf197dce7a0165 \
--hash=sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2 \
--hash=sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc \
--hash=sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400 \
--hash=sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928 \
--hash=sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a \
--hash=sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5 \
--hash=sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d \
--hash=sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2 \
--hash=sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb \
--hash=sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0 \
--hash=sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2 \
--hash=sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335 \
--hash=sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae \
--hash=sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287 \
--hash=sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d \
--hash=sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b \
--hash=sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39 \
--hash=sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040 \
--hash=sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d \
--hash=sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c \
--hash=sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86 \
--hash=sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c \
--hash=sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3 \
--hash=sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d \
--hash=sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219 \
--hash=sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5
greenlet==3.0.1 ; python_full_version > "3.11.0" and python_version < "3.13" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") \
--hash=sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174 \
--hash=sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd \
--hash=sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa \
@ -196,22 +188,22 @@ greenlet==3.0.1 ; python_version >= "3.11" and (platform_machine == "aarch64" or
--hash=sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65 \
--hash=sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064 \
--hash=sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36
gunicorn==20.1.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
idna==3.4 ; python_version >= "3.11" and python_version < "4.0" \
gunicorn==21.2.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0 \
--hash=sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033
idna==3.4 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
importlib-resources==6.1.0 ; python_version >= "3.11" and python_version < "4.0" \
importlib-resources==6.1.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9 \
--hash=sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83
itsdangerous==2.1.2 ; python_version >= "3.11" and python_version < "4.0" \
itsdangerous==2.1.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
--hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a
jinja2==3.1.2 ; python_version >= "3.11" and python_version < "4.0" \
jinja2==3.1.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
kiwisolver==1.4.5 ; python_version >= "3.11" and python_version < "4.0" \
kiwisolver==1.4.5 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf \
--hash=sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e \
--hash=sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af \
@ -316,10 +308,10 @@ kiwisolver==1.4.5 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797 \
--hash=sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892 \
--hash=sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f
mako==1.2.4 ; python_version >= "3.11" and python_version < "4.0" \
mako==1.2.4 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818 \
--hash=sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34
markupsafe==2.1.3 ; python_version >= "3.11" and python_version < "4.0" \
markupsafe==2.1.3 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
@ -380,7 +372,7 @@ markupsafe==2.1.3 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
--hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
--hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
matplotlib==3.8.1 ; python_version >= "3.11" and python_version < "4.0" \
matplotlib==3.8.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:044df81c1f6f3a8e52d70c4cfcb44e77ea9632a10929932870dfaa90de94365d \
--hash=sha256:0d24c47a1bb47e392fbcd26fe322e4ff3431653ac1e8718e4e147d450ae97a44 \
--hash=sha256:1fcb49b6baf0375281979cbf26695ec10bd1cada1e311893e89533b3b70143e7 \
@ -409,39 +401,46 @@ matplotlib==3.8.1 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:f34b46dbb1db1f09bfa937cd5853e5f2af232caeeff509c3ab6e43fd33780eae \
--hash=sha256:f55fb5ff02d999a100be28bf6ffe826e1867a54c7b465409685332c9dd48ffa5 \
--hash=sha256:ff842e27bc6a80de08c40e0bfdce460bd08080e8a94af131162b6a1b8948f2cc
numpy==1.25.2 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2 \
--hash=sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55 \
--hash=sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf \
--hash=sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01 \
--hash=sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca \
--hash=sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901 \
--hash=sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d \
--hash=sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4 \
--hash=sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf \
--hash=sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380 \
--hash=sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044 \
--hash=sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545 \
--hash=sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f \
--hash=sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f \
--hash=sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3 \
--hash=sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364 \
--hash=sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9 \
--hash=sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418 \
--hash=sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f \
--hash=sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295 \
--hash=sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3 \
--hash=sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187 \
--hash=sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926 \
--hash=sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357 \
--hash=sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760
packaging==23.2 ; python_version >= "3.11" and python_version < "4.0" \
numpy==1.26.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668 \
--hash=sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9 \
--hash=sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f \
--hash=sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5 \
--hash=sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53 \
--hash=sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2 \
--hash=sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974 \
--hash=sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f \
--hash=sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42 \
--hash=sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2 \
--hash=sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af \
--hash=sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67 \
--hash=sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e \
--hash=sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c \
--hash=sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7 \
--hash=sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e \
--hash=sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908 \
--hash=sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66 \
--hash=sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24 \
--hash=sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b \
--hash=sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e \
--hash=sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe \
--hash=sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a \
--hash=sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575 \
--hash=sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297 \
--hash=sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104 \
--hash=sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab \
--hash=sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3 \
--hash=sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244 \
--hash=sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124 \
--hash=sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617 \
--hash=sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c
packaging==23.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
passlib==1.7.4 ; python_version >= "3.11" and python_version < "4.0" \
passlib==1.7.4 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \
--hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04
pillow==10.1.0 ; python_version >= "3.11" and python_version < "4.0" \
pillow==10.1.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d \
--hash=sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de \
--hash=sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616 \
@ -496,50 +495,70 @@ pillow==10.1.0 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996 \
--hash=sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a \
--hash=sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793
pyparsing==3.1.1 ; python_version >= "3.11" and python_version < "4.0" \
pyparsing==3.1.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb \
--hash=sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db
python-dateutil==2.8.2 ; python_version >= "3.11" and python_version < "4.0" \
python-dateutil==2.8.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
setuptools==68.2.2 ; python_version >= "3.11" and python_version < "4.0" \
setuptools==68.2.2 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87 \
--hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a
six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" \
six==1.16.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
sqlalchemy==1.4.50 ; python_version >= "3.11" and python_version < "4.0" \
--hash=sha256:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131 \
--hash=sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3 \
--hash=sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7 \
--hash=sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace \
--hash=sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a \
--hash=sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab \
--hash=sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814 \
--hash=sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf \
--hash=sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49 \
--hash=sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c \
--hash=sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796 \
--hash=sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75 \
--hash=sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18 \
--hash=sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c \
--hash=sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516 \
--hash=sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9 \
--hash=sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293 \
--hash=sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc \
--hash=sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5 \
--hash=sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb \
--hash=sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5 \
--hash=sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4 \
--hash=sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436 \
--hash=sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be \
--hash=sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d
typing-extensions==4.8.0 ; python_version >= "3.11" and python_version < "4.0" \
sqlalchemy==2.0.23 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884 \
--hash=sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74 \
--hash=sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d \
--hash=sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc \
--hash=sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca \
--hash=sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d \
--hash=sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf \
--hash=sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846 \
--hash=sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306 \
--hash=sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221 \
--hash=sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5 \
--hash=sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89 \
--hash=sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55 \
--hash=sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72 \
--hash=sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea \
--hash=sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8 \
--hash=sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577 \
--hash=sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df \
--hash=sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4 \
--hash=sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d \
--hash=sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4 \
--hash=sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24 \
--hash=sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6 \
--hash=sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965 \
--hash=sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b \
--hash=sha256:967c0b71156f793e6662dd839da54f884631755275ed71f1539c95bbada9aaab \
--hash=sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22 \
--hash=sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4 \
--hash=sha256:aeb397de65a0a62f14c257f36a726945a7f7bb60253462e8602d9b97b5cbe204 \
--hash=sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855 \
--hash=sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d \
--hash=sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab \
--hash=sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69 \
--hash=sha256:c4722f3bc3c1c2fcc3702dbe0016ba31148dd6efcd2a2fd33c1b4897c6a19693 \
--hash=sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e \
--hash=sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8 \
--hash=sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45 \
--hash=sha256:d4041ad05b35f1f4da481f6b811b4af2f29e83af253bf37c3c4582b2c68934ab \
--hash=sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1 \
--hash=sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d \
--hash=sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda \
--hash=sha256:f3420d00d2cb42432c1d0e44540ae83185ccbbc67a6054dcc8ab5387add6620b \
--hash=sha256:f48ed89dd11c3c586f45e9eec1e437b355b3b6f6884ea4a4c3111a3358fd0c18 \
--hash=sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac \
--hash=sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60
typing-extensions==4.8.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
--hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef
werkzeug==3.0.1 ; python_version >= "3.11" and python_version < "4.0" \
werkzeug==3.0.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \
--hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10
wtforms==3.1.1 ; python_version >= "3.11" and python_version < "4.0" \
wtforms==3.1.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620 \
--hash=sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0