1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-04 22:40:30 +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/ /deploy/traefik/
/migrations/ /dev/
compose.yaml
Containerfile
/poetry.lock /poetry.lock
/pyproject.toml
*.pyc *.pyc
__pycache__/ __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/ /migrations/
# Development # Development
/dev_data/ /dev/
# Python # Python
__pycache__/ __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 EXPOSE 80
ENV ADVLABDB_DATA_DIR=/volumes/data ENV ADVLABDB_DATA_DIR=/volumes/data
WORKDIR /volumes/repo WORKDIR /volumes/repo
COPY requirements.txt . COPY requirements.txt .
RUN pip3 install -r requirements.txt RUN pip3 install -r requirements.txt
COPY . . COPY . .
RUN rm requirements.txt
CMD ["/bin/bash", "init.sh"] CMD ["./init.sh"]

View file

@ -1,9 +1,5 @@
# AdvLabDB # 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 ## About
AdvLabDB is a database with a web interface for labs. AdvLabDB is a database with a web interface for labs.

View file

@ -1,8 +1,9 @@
import subprocess
from datetime import datetime from datetime import datetime
from flask import flash from flask import flash
from flask_login import current_user from flask_login import current_user
from sqlalchemy import select from sqlalchemy import select
import subprocess
from . import data_dir from . import data_dir
from .models import Assistant, Semester, User, db 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") flash("Manually updated all final experiment and part marks.", "success")
def backup(backup_prefix): def backup(backup_prefix):
db_path = data_dir / "db/advlabdb.sqlite" 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") now = datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
dest = db_bk_dir / f"{backup_prefix}_{now}.sqlite" dest = db_bk_dir / f"{backup_prefix}_{now}.sqlite"
status = subprocess.run(["sqlite3", db_path, f".backup {dest}"]).returncode try:
if status == 0: status = subprocess.run(["sqlite3", db_path, f".backup {dest}"], check=False).returncode
flash(f"Created a database backup at the path {dest}", "success") if status == 0:
else: 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") flash("Failed to create a database backup! Make sure that `sqlite3` is installed on your system!", "danger")
def manual_backup(): def manual_backup():
backup("manual") backup("manual")
def deactivate_assistants(): def deactivate_assistants():
user_ids_to_deactivate = db.session.scalars( user_ids_to_deactivate = db.session.scalars(
select(Assistant.user_id) select(Assistant.user_id)
.join(User) .join(User)
.where(User.active == True) .where(User.active is True)
.except_( .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 wtforms.widgets import NumberInput
from . import data_dir, user_datastore 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 ( from .admin_link_formatters import (
admin_formatter, admin_formatter,
appointment_date_formatter, appointment_date_formatter,
@ -108,7 +112,7 @@ def semesterExperimentQueryFactory():
class SemesterRowFilter(FilterEqual): class SemesterRowFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): if not has_request_context():
return tuple() return ()
semesters = Semester.sortedSemestersStartingWithNewest() semesters = Semester.sortedSemestersStartingWithNewest()
return tuple((semester.id, str(semester)) for semester in semesters) return tuple((semester.id, str(semester)) for semester in semesters)
@ -189,7 +193,8 @@ class UserView(SecureAdminModelView):
"active_semester", "active_semester",
"current_login_at", "current_login_at",
] ]
column_details_list = column_list + [ column_details_list = [
*column_list,
"phone_number", "phone_number",
"mobile_phone_number", "mobile_phone_number",
"building", "building",
@ -307,9 +312,8 @@ class UserView(SecureAdminModelView):
f"{model.email} registered with role(s): {', '.join(role.name for role in model.roles)}.", f"{model.email} registered with role(s): {', '.join(role.name for role in model.roles)}.",
category="success", category="success",
) )
else: elif model == current_user:
if model == current_user: flash(f"Active semester is {model.active_semester}.", "warning")
flash(f"Active semester is {model.active_semester}.", "warning")
class SemesterView(SecureAdminModelView): class SemesterView(SecureAdminModelView):
@ -319,16 +323,16 @@ class SemesterView(SecureAdminModelView):
last_semester = Semester.lastSemester() last_semester = Semester.lastSemester()
if last_semester.label == "WS": if last_semester.label == "WS":
return "SS" return "SS"
else:
return "WS" return "WS"
@staticmethod @staticmethod
def defaultFormYear(): def defaultFormYear():
last_semester = Semester.lastSemester() last_semester = Semester.lastSemester()
if last_semester.label == "WS": if last_semester.label == "WS":
return last_semester.year + 1 return last_semester.year + 1
else:
return last_semester.year return last_semester.year
label = RadioField( label = RadioField(
"Semester", "Semester",
@ -414,10 +418,9 @@ class SemesterView(SecureAdminModelView):
else: else:
current_user.active_semester = model current_user.active_semester = model
flash(f"Active semester changed to the new semester {model}!", "warning") flash(f"Active semester changed to the new semester {model}!", "warning")
else: elif model.done:
if model.done: next_semester = db.session.get(Semester, model.id + 1)
next_semester = db.session.get(Semester, model.id + 1) model.set_done(next_semester)
model.set_done(next_semester)
def programQueryFactory(): def programQueryFactory():
@ -480,7 +483,8 @@ class StudentView(SecureAdminModelView):
"first_name", "first_name",
"last_name", "last_name",
] ]
column_searchable_list = column_sortable_list + [ column_searchable_list = [
*column_sortable_list,
"uni_email", "uni_email",
"contact_email", "contact_email",
] ]
@ -538,7 +542,7 @@ def groupQueryFactory():
class PartRowFilter(FilterEqual): class PartRowFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): if not has_request_context():
return tuple() return ()
parts = db.session.scalars(select(Part).where(Part.semester == current_user.active_semester)) 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) return tuple((part.id, part.str_without_semester()) for part in parts)
@ -616,7 +620,7 @@ def partStudentQueryFactory():
class ProgramRowFilter(FilterEqual): class ProgramRowFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): if not has_request_context():
return tuple() return ()
programs = db.session.scalars(select(Program)) programs = db.session.scalars(select(Program))
return tuple((program.id, str(program)) for program in programs) return tuple((program.id, str(program)) for program in programs)
@ -632,14 +636,14 @@ class GroupView(SecureAdminModelView):
if is_created: if is_created:
def query_factory(): def query_factory():
return partStudentQueryFactory().where(PartStudent.group == None) return partStudentQueryFactory().where(PartStudent.group is None)
else: else:
def query_factory(): def query_factory():
return partStudentQueryFactory().where( return partStudentQueryFactory().where(
or_( or_(
and_(PartStudent.group == None, Part.program == group.program), and_(PartStudent.group is None, Part.program == group.program),
PartStudent.group == group, PartStudent.group == group,
) )
) )
@ -714,7 +718,8 @@ class ExperimentView(SecureAdminModelView):
"title", "title",
] ]
form_columns = column_list + [ form_columns = [
*column_list,
"description", "description",
"wiki_link", "wiki_link",
"building", "building",
@ -749,7 +754,7 @@ class ExperimentView(SecureAdminModelView):
def assistantQueryFactory(): 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): def weighting_field(label: str, default: float):
@ -771,7 +776,7 @@ class SemesterExperimentView(SecureAdminModelView):
class CreateForm(FlaskForm): class CreateForm(FlaskForm):
@staticmethod @staticmethod
def experimentQueryFactory(): def experimentQueryFactory():
return Experiment.query.where(Experiment.active == True) return Experiment.query.where(Experiment.active is True)
experiment = QuerySelectField( experiment = QuerySelectField(
"Experiment", "Experiment",
@ -853,8 +858,8 @@ def userHasRoleFilterFactory(column, role_name):
query = query.join(User) query = query.join(User)
if bool(int(value)): # value is string "0" or "1" if bool(int(value)): # value is string "0" or "1"
return query.join(User.roles).where(Role.name == role_name) 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") return UserHasRoleFilter(column, f"Currently has {role_name} role")
@ -930,9 +935,9 @@ class AdminView(SecureAdminModelView):
class ExperimentRowFilter(FilterEqual): class ExperimentRowFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): 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( return tuple(
( (
f"{activeExperiment.number},{activeExperiment.program_id}", f"{activeExperiment.number},{activeExperiment.program_id}",
@ -1080,9 +1085,8 @@ class GroupExperimentView(SecureAdminModelView):
self.session.add(appointment) self.session.add(appointment)
def after_model_change(self, form, model, is_created): def after_model_change(self, form, model, is_created):
if is_created: if is_created and model.appointments:
if model.appointments: flash(f"Appointments {model.appointments} added.", "success")
flash(f"Appointments {model.appointments} added.", "success")
def groupExperimentQueryFactory(): def groupExperimentQueryFactory():
@ -1094,7 +1098,7 @@ def groupExperimentQueryFactory():
class AssistantRowFilter(FilterEqual): class AssistantRowFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): if not has_request_context():
return tuple() return ()
activeAssistants = assistantQueryFactory() activeAssistants = assistantQueryFactory()
return tuple((assistant.id, str(assistant)) for assistant in activeAssistants) return tuple((assistant.id, str(assistant)) for assistant in activeAssistants)
@ -1187,9 +1191,9 @@ class ExperimentMarkView(SecureAdminModelView):
class AdminFilter(FilterEqual): class AdminFilter(FilterEqual):
def get_options(self, view): def get_options(self, view):
if not has_request_context(): 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) return tuple((admin.id, str(admin)) for admin in admins)
def apply(self, query, value, alias=None): def apply(self, query, value, alias=None):
@ -1387,8 +1391,12 @@ class AnalysisView(SecureAdminBaseView):
if form.validate_on_submit(): if form.validate_on_submit():
if form.assistant_marks_submit.data: if form.assistant_marks_submit.data:
return assistant_marks_analysis(self) ret = assistant_marks_analysis(self)
elif form.final_part_marks_submit.data:
if ret is not None:
return ret
if form.final_part_marks_submit.data:
return final_part_marks_analysis(self) return final_part_marks_analysis(self)
return self.render("analysis/analysis.jinja.html", form=form) return self.render("analysis/analysis.jinja.html", form=form)

View file

@ -2,6 +2,7 @@ from base64 import b64encode
from io import BytesIO from io import BytesIO
import numpy as np import numpy as np
from flask import flash
from flask_login import current_user from flask_login import current_user
from matplotlib.figure import Figure from matplotlib.figure import Figure
from matplotlib.ticker import MaxNLocator from matplotlib.ticker import MaxNLocator
@ -76,7 +77,11 @@ def mark_hists(markType, active_assistants):
def assistant_marks_analysis(cls): 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) oral_mark_hists = mark_hists("Oral", active_assistants)
protocol_mark_hists = mark_hists("Protocol", active_assistants) protocol_mark_hists = mark_hists("Protocol", active_assistants)

View file

@ -211,7 +211,7 @@ class AssistantUserView(SecureAssistantModelView):
@staticmethod @staticmethod
def semesterQueryFactory(): def semesterQueryFactory():
# Show only last two semesters to assistants # 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 = QuerySelectField(
"Active Semester", "Active Semester",

View file

@ -6,7 +6,7 @@ from pathlib import Path
def get_data_dir() -> Path: def get_data_dir() -> Path:
data_dir_env_variable = "ADVLABDB_DATA_DIR" 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(): if not data_dir.is_dir():
sys.exit( sys.exit(
@ -34,16 +34,14 @@ def load_config(file_name: str, data_dir: Path):
def get_secrets(data_dir: Path): def get_secrets(data_dir: Path):
config = load_config("secrets.ini", data_dir) config = load_config("secrets.ini", data_dir)
secrets = config["Secrets"]
return secrets return config["Secrets"]
def get_settings(data_dir: Path): def get_settings(data_dir: Path):
config = load_config("settings.ini", data_dir) config = load_config("settings.ini", data_dir)
settings = config["Settings"]
return settings return config["Settings"]
def set_config(app, data_dir: Path): def set_config(app, data_dir: Path):

View file

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

View file

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

View file

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

View file

@ -231,10 +231,7 @@ class Group(db.Model):
.order_by(Group.number.desc()) .order_by(Group.number.desc())
) )
if highestGroupNumber is not None: number = highestGroupNumber + 1 if highestGroupNumber is not None else 1
number = highestGroupNumber + 1
else:
number = 1
return Group( return Group(
program=program, program=program,
@ -460,13 +457,12 @@ class Appointment(db.Model):
if assistant is not None: if assistant is not None:
if assistant not in semesterExperimentAssistants: if assistant not in semesterExperimentAssistants:
raise DatabaseException(f"{assistant} is not responsible for {semesterExperiment}!") raise DatabaseException(f"{assistant} is not responsible for {semesterExperiment}!")
elif len(semesterExperimentAssistants) == 1:
assistant = semesterExperimentAssistants[0]
else: else:
if len(semesterExperimentAssistants) == 1: raise DatabaseException(
assistant = semesterExperimentAssistants[0] f"Experiment {semesterExperiment} has more than one assistant. You have to assign one of these assistants: {semesterExperimentAssistants}"
else: )
raise DatabaseException(
f"Experiment {semesterExperiment} has more than one assistant. You have to assign one of these assistants: {semesterExperimentAssistants}"
)
return assistant return assistant
@ -529,9 +525,8 @@ class Semester(db.Model):
def __init__(self, label, year, **kwargs): def __init__(self, label, year, **kwargs):
last_semester = Semester.lastSemester() last_semester = Semester.lastSemester()
if last_semester is not None: if last_semester is not None and (year < last_semester.year or (year == last_semester.year and label == "SS")):
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}!")
raise DatabaseException(f"You can only create semesters later than the last semester {last_semester}!")
super().__init__(label=label, year=year, **kwargs) super().__init__(label=label, year=year, **kwargs)
@ -542,7 +537,7 @@ class Semester(db.Model):
if transferParts: if transferParts:
semester.transferPartsFrom(oldSemester) 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) newSemesterExperiment = SemesterExperiment(experiment=experiment, semester=semester)
if transferAssistants: if transferAssistants:
@ -580,7 +575,7 @@ class Semester(db.Model):
.join(GroupExperiment) .join(GroupExperiment)
.join(SemesterExperiment) .join(SemesterExperiment)
.where(SemesterExperiment.semester == self) .where(SemesterExperiment.semester == self)
.where(ExperimentMark.final_experiment_mark == None) .where(ExperimentMark.final_experiment_mark is None)
) )
def set_done(self, next_semester=None): def set_done(self, next_semester=None):
@ -657,9 +652,9 @@ class ExperimentMark(db.Model):
def check(part_student, group_experiment): def check(part_student, group_experiment):
if not part_student.group: if not part_student.group:
raise DatabaseException("The part student does not have a group yet!") raise DatabaseException("The part student does not have a group yet!")
else:
if group_experiment not in part_student.group.group_experiments: 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!") raise DatabaseException("The group of the part student does not have the given group experiment!")
def __init__(self, part_student, group_experiment, **kwargs): def __init__(self, part_student, group_experiment, **kwargs):
ExperimentMark.check(part_student, group_experiment) 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>" 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 @security.login_context_processor
def login_context_processor(): def login_context_processor():
return dict(message=settings.get("login_view_message")) return {"message": settings.get("login_view_message")}
@bp.route("/") @bp.route("/")
@ -44,15 +44,9 @@ def post_login():
if current_active_semester.done: if current_active_semester.done:
current_user.set_last_semester_as_active() current_user.set_last_semester_as_active()
if current_user.has_role("admin"): endpoint_base = "admin" if current_user.has_role("admin") else "assistant"
endpoint_base = "admin"
else:
endpoint_base = "assistant"
if current_user.login_count == 1: url = url_for(endpoint_base + "_docs.index") if current_user.login_count == 1 else url_for(endpoint_base + ".index")
url = url_for(endpoint_base + "_docs.index")
else:
url = url_for(endpoint_base + ".index")
return redirect(url) return redirect(url)
@ -60,10 +54,7 @@ def post_login():
@bp.route("/user-settings") @bp.route("/user-settings")
@auth_required() @auth_required()
def user_settings(): def user_settings():
if current_user.has_role("admin"): role = "admin" if current_user.has_role("admin") else "assistant"
role = "admin"
else:
role = "assistant"
url = url_for("main.index") + role + "/user/edit/?id=" + str(current_user.id) 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) app = create_app(create_for_server=False)
with app.app_context(): with app.app_context(), db.session.begin():
with db.session.begin(): admins = db.session.scalars(select(Admin).join(User).where(User.active is True)).all()
admins = db.session.scalars(select(Admin).join(User).where(User.active == True)).all() activate_user = False
activate_user = False
if len(admins) == 0: if len(admins) == 0:
click.echo("There is no admin with an active user. The user of the chosen admin will be activated.") 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() admins = db.session.scalars(select(Admin)).all()
activate_user = True activate_user = True
num_admins = len(admins) num_admins = len(admins)
prompt = "Admins:\n" prompt = "Admins:\n"
for ind, admin in enumerate(admins): for ind, admin in enumerate(admins):
user = admin.user user = admin.user
prompt += f"[{ind}] {user.first_name} {user.last_name}: {user.email}\n" prompt += f"[{ind}] {user.first_name} {user.last_name}: {user.email}\n"
prompt += f"Enter number [0-{num_admins - 1}]" prompt += f"Enter number [0-{num_admins - 1}]"
admin_index = click.prompt( admin_index = click.prompt(
prompt, prompt,
type=click.IntRange(0, num_admins - 1), 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( admin_change_password(
chosen_admin_user, new_password, notify=False chosen_admin_user, new_password, notify=False
) # Password is automatically hashed with this function ) # Password is automatically hashed with this function
if activate_user: if activate_user:
chosen_admin_user.active = True chosen_admin_user.active = True
manage.box(f"New password: {new_password}") manage.box(f"New password: {new_password}")

View file

@ -9,10 +9,10 @@ def _generate_secrets():
file = data_dir / "secrets.ini" file = data_dir / "secrets.ini"
if file.is_file(): 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 return
with open(file, "w") as f: with file.open("w") as f:
f.write("[Secrets]\n") f.write("[Secrets]\n")
key = secrets.SystemRandom().getrandbits(128) 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): class EmailParamType(click.ParamType):
def convert(self, value, param, ctx): def convert(self, value, param, ctx):
try: try:
return validate_email(value).email return validate_email(value).normalized
except Exception: except Exception:
self.fail(f"{value} is not a valid email!", param, ctx) self.fail(f"{value} is not a valid email!", param, ctx)
@ -19,69 +19,66 @@ class EmailParamType(click.ParamType):
def _init_db(manage): def _init_db(manage):
db_file = data_dir / "db/advlabdb.sqlite" db_file = data_dir / "db/advlabdb.sqlite"
if db_file.is_file(): 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 return
app = create_app(create_for_server=False) app = create_app(create_for_server=False)
with app.app_context(): with app.app_context(), db.session.begin():
with db.session.begin(): # Create new database
# Create new database db.create_all()
db.create_all()
semester_label = click.prompt( semester_label = click.prompt(
"Enter the label of the current semester", "Enter the label of the current semester",
type=click.Choice(("SS", "WS")), type=click.Choice(("SS", "WS")),
) )
semester_year = click.prompt( semester_year = click.prompt(
f"Enter the year of the current semester (between {MIN_YEAR} and {MAX_YEAR})", f"Enter the year of the current semester (between {MIN_YEAR} and {MAX_YEAR})",
type=click.IntRange(MIN_YEAR, 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") adminRole = user_datastore.create_role(name="admin")
user_datastore.create_role(name="assistant") 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( admin_email = click.prompt(
"Enter the admin's email address", "Enter the admin's email address",
type=EmailParamType(), type=EmailParamType(),
) )
admin_first_name = click.prompt("Enter the admin's first name") admin_first_name = click.prompt("Enter the admin's first name")
admin_last_name = click.prompt("Enter the admin's last name") admin_last_name = click.prompt("Enter the admin's last name")
admin_phone_number = click.prompt( admin_phone_number = click.prompt("Enter the admin's phone number (optional)", default="", show_default=False)
"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_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_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_password = randomPassword()
admin_hashed_password = hash_password(admin_password) admin_hashed_password = hash_password(admin_password)
admin_user = user_datastore.create_user( admin_user = user_datastore.create_user(
email=admin_email, email=admin_email,
password=admin_hashed_password, password=admin_hashed_password,
roles=[adminRole], roles=[adminRole],
first_name=admin_first_name.strip(), first_name=admin_first_name.strip(),
last_name=admin_last_name.strip(), last_name=admin_last_name.strip(),
phone_number=admin_phone_number.strip() or None, phone_number=admin_phone_number.strip() or None,
mobile_phone_number=admin_mobile_phone_number.strip() or None, mobile_phone_number=admin_mobile_phone_number.strip() or None,
building=admin_building.strip() or None, building=admin_building.strip() or None,
room=admin_room.strip() or None, room=admin_room.strip() or None,
active_semester=semester, 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}") manage.box(f"Admin password: {admin_password}")

View file

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

470
poetry.lock generated
View file

@ -21,13 +21,13 @@ tz = ["python-dateutil"]
[[package]] [[package]]
name = "blinker" name = "blinker"
version = "1.6.3" version = "1.7.0"
description = "Fast, simple object-to-object and broadcast signaling" description = "Fast, simple object-to-object and broadcast signaling"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "blinker-1.6.3-py3-none-any.whl", hash = "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa"}, {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"},
{file = "blinker-1.6.3.tar.gz", hash = "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d"}, {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"},
] ]
[[package]] [[package]]
@ -57,136 +57,66 @@ files = [
[[package]] [[package]]
name = "contourpy" name = "contourpy"
version = "1.1.0" version = "1.2.0"
description = "Python library for calculating contours of 2D quadrilateral grids" description = "Python library for calculating contours of 2D quadrilateral grids"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"},
{file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"},
{file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"},
{file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"},
{file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"}, {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"},
{file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"},
{file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"},
{file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"},
{file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"},
{file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"},
{file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"}, {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"},
{file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"},
{file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"},
{file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"},
{file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"},
{file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"},
{file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"}, {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"},
{file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"},
{file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"},
{file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"},
{file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"},
{file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"},
{file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"}, {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"},
{file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"},
{file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"},
{file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"},
{file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, {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] [package.dependencies]
numpy = ">=1.16" numpy = ">=1.20,<2.0"
[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\""}
[package.extras] [package.extras]
bokeh = ["bokeh", "selenium"] bokeh = ["bokeh", "selenium"]
docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] 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 = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
test-no-images = ["pytest", "pytest-cov", "wurlitzer"] test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"]
[[package]] [[package]]
name = "cycler" name = "cycler"
@ -224,28 +154,28 @@ wmi = ["wmi (>=1.5.1,<2.0.0)"]
[[package]] [[package]]
name = "email-validator" name = "email-validator"
version = "1.3.1" version = "2.1.0.post1"
description = "A robust email address syntax and deliverability validation library." description = "A robust email address syntax and deliverability validation library."
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.8"
files = [ files = [
{file = "email_validator-1.3.1-py2.py3-none-any.whl", hash = "sha256:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda"}, {file = "email_validator-2.1.0.post1-py3-none-any.whl", hash = "sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637"},
{file = "email_validator-1.3.1.tar.gz", hash = "sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2"}, {file = "email_validator-2.1.0.post1.tar.gz", hash = "sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44"},
] ]
[package.dependencies] [package.dependencies]
dnspython = ">=1.15.0" dnspython = ">=2.0.0"
idna = ">=2.0.0" idna = ">=2.0.0"
[[package]] [[package]]
name = "flask" name = "flask"
version = "2.3.3" version = "3.0.0"
description = "A simple framework for building complex web applications." description = "A simple framework for building complex web applications."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, {file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"},
{file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, {file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"},
] ]
[package.dependencies] [package.dependencies]
@ -253,7 +183,7 @@ blinker = ">=1.6.2"
click = ">=8.1.3" click = ">=8.1.3"
itsdangerous = ">=2.1.2" itsdangerous = ">=2.1.2"
Jinja2 = ">=3.1.2" Jinja2 = ">=3.1.2"
Werkzeug = ">=2.3.7" Werkzeug = ">=3.0.0"
[package.extras] [package.extras]
async = ["asgiref (>=3.2)"] async = ["asgiref (>=3.2)"]
@ -325,17 +255,17 @@ Flask = "*"
[[package]] [[package]]
name = "flask-security-too" name = "flask-security-too"
version = "5.3.1" version = "5.3.2"
description = "Quickly add security features to your Flask application." description = "Quickly add security features to your Flask application."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "Flask-Security-Too-5.3.1.tar.gz", hash = "sha256:1dafe00c611ce3811e7fe1686ecd7750938806a1ec3c5a278185f31958895d3c"}, {file = "Flask-Security-Too-5.3.2.tar.gz", hash = "sha256:c0b5075df0d64a9ef35b04c88d31fbf405a59243736f6d6d0a1a4b4845abf3e5"},
{file = "Flask_Security_Too-5.3.1-py3-none-any.whl", hash = "sha256:159ed080dce4a717c2852eac50443221f50b391f5af6f03c82febc3740d572d1"}, {file = "Flask_Security_Too-5.3.2-py3-none-any.whl", hash = "sha256:eb6b56c6e1d108a11a1a86dcb17dd97e596c027b68a73f0acc9fc7f433c441a6"},
] ]
[package.dependencies] [package.dependencies]
email-validator = ">=1.1.1" email-validator = ">=2.0.0"
Flask = ">=2.3.2" Flask = ">=2.3.2"
Flask-Login = ">=0.6.2" Flask-Login = ">=0.6.2"
Flask-Principal = ">=0.4.0" Flask-Principal = ">=0.4.0"
@ -354,18 +284,18 @@ mfa = ["cryptography (>=40.0.2)", "phonenumberslite (>=8.13.11)", "qrcode (>=7.4
[[package]] [[package]]
name = "flask-sqlalchemy" name = "flask-sqlalchemy"
version = "3.0.5" version = "3.1.1"
description = "Add SQLAlchemy support to your Flask application." description = "Add SQLAlchemy support to your Flask application."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "flask_sqlalchemy-3.0.5-py3-none-any.whl", hash = "sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283"}, {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"},
{file = "flask_sqlalchemy-3.0.5.tar.gz", hash = "sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1"}, {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"},
] ]
[package.dependencies] [package.dependencies]
flask = ">=2.2.5" flask = ">=2.2.5"
sqlalchemy = ">=1.4.18" sqlalchemy = ">=2.0.16"
[[package]] [[package]]
name = "flask-wtf" name = "flask-wtf"
@ -388,57 +318,57 @@ email = ["email-validator"]
[[package]] [[package]]
name = "fonttools" name = "fonttools"
version = "4.43.1" version = "4.44.0"
description = "Tools to manipulate font files" description = "Tools to manipulate font files"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273"}, {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c"},
{file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c"}, {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae"},
{file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca"}, {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5"},
{file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6"}, {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf"},
{file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184"}, {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e"},
{file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7"}, {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86"},
{file = "fonttools-4.43.1-cp310-cp310-win32.whl", hash = "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02"}, {file = "fonttools-4.44.0-cp310-cp310-win32.whl", hash = "sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287"},
{file = "fonttools-4.43.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef"}, {file = "fonttools-4.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c"},
{file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b"}, {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3"},
{file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f"}, {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d"},
{file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0"}, {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d"},
{file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8"}, {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4"},
{file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd"}, {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e"},
{file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b"}, {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2"},
{file = "fonttools-4.43.1-cp311-cp311-win32.whl", hash = "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204"}, {file = "fonttools-4.44.0-cp311-cp311-win32.whl", hash = "sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5"},
{file = "fonttools-4.43.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8"}, {file = "fonttools-4.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189"},
{file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5"}, {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267"},
{file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c"}, {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b"},
{file = "fonttools-4.43.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680"}, {file = "fonttools-4.44.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400"},
{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.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.43.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3"}, {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2"},
{file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b"}, {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb"},
{file = "fonttools-4.43.1-cp312-cp312-win32.whl", hash = "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f"}, {file = "fonttools-4.44.0-cp312-cp312-win32.whl", hash = "sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5"},
{file = "fonttools-4.43.1-cp312-cp312-win_amd64.whl", hash = "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72"}, {file = "fonttools-4.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e"},
{file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590"}, {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928"},
{file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7"}, {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d"},
{file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6"}, {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219"},
{file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d"}, {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a"},
{file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0"}, {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2"},
{file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933"}, {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc"},
{file = "fonttools-4.43.1-cp38-cp38-win32.whl", hash = "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b"}, {file = "fonttools-4.44.0-cp38-cp38-win32.whl", hash = "sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97"},
{file = "fonttools-4.43.1-cp38-cp38-win_amd64.whl", hash = "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896"}, {file = "fonttools-4.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040"},
{file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba"}, {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39"},
{file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13"}, {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2"},
{file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477"}, {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d"},
{file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860"}, {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111"},
{file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86"}, {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0"},
{file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3"}, {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20"},
{file = "fonttools-4.43.1-cp39-cp39-win32.whl", hash = "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad"}, {file = "fonttools-4.44.0-cp39-cp39-win32.whl", hash = "sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b"},
{file = "fonttools-4.43.1-cp39-cp39-win_amd64.whl", hash = "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a"}, {file = "fonttools-4.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b"},
{file = "fonttools-4.43.1-py3-none-any.whl", hash = "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9"}, {file = "fonttools-4.44.0-py3-none-any.whl", hash = "sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335"},
{file = "fonttools-4.43.1.tar.gz", hash = "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212"}, {file = "fonttools-4.44.0.tar.gz", hash = "sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252"},
] ]
[package.extras] [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)"] graphite = ["lz4 (>=1.7.4.2)"]
interpolatable = ["munkres", "scipy"] interpolatable = ["munkres", "scipy"]
lxml = ["lxml (>=4.0,<5)"] lxml = ["lxml (>=4.0,<5)"]
@ -448,7 +378,7 @@ repacker = ["uharfbuzz (>=0.23.0)"]
symfont = ["sympy"] symfont = ["sympy"]
type1 = ["xattr"] type1 = ["xattr"]
ufo = ["fs (>=2.2.0,<3)"] 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)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
[[package]] [[package]]
@ -523,17 +453,17 @@ test = ["objgraph", "psutil"]
[[package]] [[package]]
name = "gunicorn" name = "gunicorn"
version = "20.1.0" version = "21.2.0"
description = "WSGI HTTP Server for UNIX" description = "WSGI HTTP Server for UNIX"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, {file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"},
] ]
[package.dependencies] [package.dependencies]
setuptools = ">=3.0" packaging = "*"
[package.extras] [package.extras]
eventlet = ["eventlet (>=0.24.1)"] eventlet = ["eventlet (>=0.24.1)"]
@ -846,36 +776,43 @@ python-dateutil = ">=2.7"
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "1.25.2" version = "1.26.1"
description = "Fundamental package for array computing in Python" description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = "<3.13,>=3.9"
files = [ files = [
{file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, {file = "numpy-1.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af"},
{file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, {file = "numpy-1.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, {file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, {file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67"},
{file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, {file = "numpy-1.26.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2"},
{file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, {file = "numpy-1.26.1-cp310-cp310-win32.whl", hash = "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297"},
{file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, {file = "numpy-1.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab"},
{file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, {file = "numpy-1.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a"},
{file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, {file = "numpy-1.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, {file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, {file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974"},
{file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, {file = "numpy-1.26.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c"},
{file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, {file = "numpy-1.26.1-cp311-cp311-win32.whl", hash = "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b"},
{file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, {file = "numpy-1.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53"},
{file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, {file = "numpy-1.26.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f"},
{file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, {file = "numpy-1.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, {file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, {file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124"},
{file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, {file = "numpy-1.26.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c"},
{file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, {file = "numpy-1.26.1-cp312-cp312-win32.whl", hash = "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66"},
{file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, {file = "numpy-1.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, {file = "numpy-1.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, {file = "numpy-1.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, {file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e"},
{file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, {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]] [[package]]
@ -1030,60 +967,85 @@ files = [
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "1.4.50" version = "2.0.23"
description = "Database Abstraction Library" description = "Database Abstraction Library"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" python-versions = ">=3.7"
files = [ files = [
{file = "SQLAlchemy-1.4.50-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5"}, {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"},
{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-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"},
{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-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"},
{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-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"},
{file = "SQLAlchemy-1.4.50-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7"}, {file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"},
{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-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"},
{file = "SQLAlchemy-1.4.50-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc"}, {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1"},
{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-2.0.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72"},
{file = "SQLAlchemy-1.4.50-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293"}, {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e"},
{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-2.0.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d"},
{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-2.0.23-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d"},
{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-2.0.23-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4"},
{file = "SQLAlchemy-1.4.50-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a"}, {file = "SQLAlchemy-2.0.23-cp311-cp311-win32.whl", hash = "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855"},
{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-2.0.23-cp311-cp311-win_amd64.whl", hash = "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22"},
{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-2.0.23-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45"},
{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-2.0.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4"},
{file = "SQLAlchemy-1.4.50-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436"}, {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac"},
{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-2.0.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577"},
{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-2.0.23-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda"},
{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-2.0.23-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60"},
{file = "SQLAlchemy-1.4.50-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49"}, {file = "SQLAlchemy-2.0.23-cp312-cp312-win32.whl", hash = "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846"},
{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-2.0.23-cp312-cp312-win_amd64.whl", hash = "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca"},
{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-2.0.23-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d"},
{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-2.0.23-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf"},
{file = "SQLAlchemy-1.4.50.tar.gz", hash = "sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf"}, {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] [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] [package.extras]
aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] 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)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"] asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
mssql = ["pyodbc"] mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"] mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"] mssql-pyodbc = ["pyodbc"]
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mypy = ["mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] mysql = ["mysqlclient (>=1.4.0)"]
mysql-connector = ["mysql-connector-python"] 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 = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] 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-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"] postgresql-psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"] postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3-binary"] sqlcipher = ["sqlcipher3-binary"]
[[package]] [[package]]
@ -1133,5 +1095,5 @@ email = ["email-validator"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = ">3.11,<3.13"
content-hash = "e21cdc3b5d6a838558d704b3b01d854b501dfc2a3f36db83536eef29061b5d5b" content-hash = "b38d3a2a1660a6acfce855d57802eef662b3e0718c6835759a3650f4ad097454"

View file

@ -6,31 +6,67 @@ authors = ["Mo Bitar <mo8it@proton.me>"]
readme = "README.md" readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.11" python = ">3.11,<3.13"
click = "^8.1" click = "^8.1"
email-validator = "^1.3" email-validator = "^2.1"
flask = "^2.2" flask = "^3.0"
flask-admin = "^1.6" flask-admin = "^1.6"
flask-login = "^0.6" flask-login = "^0.6"
flask-migrate = "^4.0" flask-migrate = "^4.0"
flask-security-Too = "^5.1" flask-security-Too = "^5.3"
flask-sqlalchemy = "^3.0" flask-sqlalchemy = "^3.1"
flask-wtf = "^1.1" flask-wtf = "^1.2"
gunicorn = "^20.1" gunicorn = "^21.2"
markupsafe = "^2.1" markupsafe = "^2.1"
matplotlib = "^3.6" matplotlib = "^3.8"
numpy = "^1.24" numpy = "^1.26"
sqlalchemy = "<2" setuptools = "^68.2"
[tool.poetry.dev-dependencies] [tool.ruff]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 120 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] [tool.isort]
profile = "black" profile = "black"
skip = ["__init__.py"] 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:47d52e3dfb03666ed945becb723d6482e52190917fdb47071440cfdba05d92cb \
--hash=sha256:bca5877e9678b454706347bc10b97cb7d67f300320fa5c3a94423e8266e2823f --hash=sha256:bca5877e9678b454706347bc10b97cb7d67f300320fa5c3a94423e8266e2823f
blinker==1.6.3 ; 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:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d \ --hash=sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9 \
--hash=sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa --hash=sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182
click==8.1.7 ; python_version >= "3.11" and python_version < "4.0" \ click==8.1.7 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de --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:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
contourpy==1.1.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:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6 \ --hash=sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8 \
--hash=sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33 \ --hash=sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956 \
--hash=sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8 \ --hash=sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5 \
--hash=sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d \ --hash=sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063 \
--hash=sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d \ --hash=sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286 \
--hash=sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c \ --hash=sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a \
--hash=sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf \ --hash=sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686 \
--hash=sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e \ --hash=sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9 \
--hash=sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e \ --hash=sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f \
--hash=sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163 \ --hash=sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4 \
--hash=sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532 \ --hash=sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e \
--hash=sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2 \ --hash=sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0 \
--hash=sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8 \ --hash=sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e \
--hash=sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1 \ --hash=sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488 \
--hash=sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b \ --hash=sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399 \
--hash=sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9 \ --hash=sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431 \
--hash=sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916 \ --hash=sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779 \
--hash=sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23 \ --hash=sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9 \
--hash=sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb \ --hash=sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab \
--hash=sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a \ --hash=sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0 \
--hash=sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e \ --hash=sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd \
--hash=sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442 \ --hash=sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e \
--hash=sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684 \ --hash=sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc \
--hash=sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34 \ --hash=sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6 \
--hash=sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d \ --hash=sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316 \
--hash=sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d \ --hash=sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808 \
--hash=sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9 \ --hash=sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0 \
--hash=sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45 \ --hash=sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f \
--hash=sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718 \ --hash=sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843 \
--hash=sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab \ --hash=sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9 \
--hash=sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3 \ --hash=sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95 \
--hash=sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae \ --hash=sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9 \
--hash=sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb \ --hash=sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de \
--hash=sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5 \ --hash=sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4 \
--hash=sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba \ --hash=sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4 \
--hash=sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0 \ --hash=sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa \
--hash=sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217 \ --hash=sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8 \
--hash=sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887 \ --hash=sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776 \
--hash=sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887 \ --hash=sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41 \
--hash=sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62 \ --hash=sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108 \
--hash=sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431 \ --hash=sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e \
--hash=sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b \ --hash=sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8 \
--hash=sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce \ --hash=sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727 \
--hash=sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b \ --hash=sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a
--hash=sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f \ cycler==0.12.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--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" \
--hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \ --hash=sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 \
--hash=sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c --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:57c6fbaaeaaf39c891292012060beb141791735dbb4004798328fc2c467402d8 \
--hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984 --hash=sha256:8dcfae8c7460a2f84b4072e26f1c9f4101ca20c071649cb7c34e8b6a93d58984
email-validator==1.3.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:49a72f5fa6ed26be1c964f0567d931d10bf3fdeeacdf97bc26ef1cd2a44e0bda \ --hash=sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44 \
--hash=sha256:d178c5c6fa6c6824e9b04f199cf23e79ac15756786573c190d2ad13089411ad2 --hash=sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637
flask-admin==1.6.1 ; python_version >= "3.11" and python_version < "4.0" \ flask-admin==1.6.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369 \ --hash=sha256:24cae2af832b6a611a01d7dc35f42d266c1d6c75a426b869d8cb241b78233369 \
--hash=sha256:fd8190f1ec3355913a22739c46ed3623f1d82b8112cde324c60a6fc9b21c9406 --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:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \
--hash=sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d --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:613a2df703998e78716cace68cd83972960834424457f5b67f56e74fff950aef \
--hash=sha256:d3f437a8b5f3849d1bb1b60e1b818efc564c66e3fefe90b62e5db08db295e1b1 --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 --hash=sha256:f5d6134b5caebfdbb86f32d56d18ee44b080876a27269560a96ea35f75c99453
flask-security-too==5.3.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:159ed080dce4a717c2852eac50443221f50b391f5af6f03c82febc3740d572d1 \ --hash=sha256:c0b5075df0d64a9ef35b04c88d31fbf405a59243736f6d6d0a1a4b4845abf3e5 \
--hash=sha256:1dafe00c611ce3811e7fe1686ecd7750938806a1ec3c5a278185f31958895d3c --hash=sha256:eb6b56c6e1d108a11a1a86dcb17dd97e596c027b68a73f0acc9fc7f433c441a6
flask-sqlalchemy==3.0.5 ; python_version >= "3.11" and python_version < "4.0" \ flask-sqlalchemy==3.1.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:c5765e58ca145401b52106c0f46178569243c5da25556be2c231ecc60867c5b1 \ --hash=sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0 \
--hash=sha256:cabb6600ddd819a9f859f36515bb1bd8e7dbf30206cc679d2b081dff9e383283 --hash=sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312
flask-wtf==1.2.1 ; python_version >= "3.11" and python_version < "4.0" \ flask-wtf==1.2.1 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695 \ --hash=sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695 \
--hash=sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287 --hash=sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287
flask==2.3.3 ; python_version >= "3.11" and python_version < "4.0" \ flask==3.0.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc \ --hash=sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638 \
--hash=sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b --hash=sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58
fonttools==4.43.1 ; python_version >= "3.11" and python_version < "4.0" \ fonttools==4.44.0 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad \ --hash=sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e \
--hash=sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c \ --hash=sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5 \
--hash=sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba \ --hash=sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e \
--hash=sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212 \ --hash=sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2 \
--hash=sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86 \ --hash=sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b \
--hash=sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477 \ --hash=sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4 \
--hash=sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f \ --hash=sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97 \
--hash=sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef \ --hash=sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111 \
--hash=sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02 \ --hash=sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252 \
--hash=sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860 \ --hash=sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf \
--hash=sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72 \ --hash=sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189 \
--hash=sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3 \ --hash=sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b \
--hash=sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b \ --hash=sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e \
--hash=sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184 \ --hash=sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267 \
--hash=sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7 \ --hash=sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20 \
--hash=sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5 \ --hash=sha256:66bc6efd829382f7a7e6cf33c2fb32b13edc8a239eb15f32acbf197dce7a0165 \
--hash=sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9 \ --hash=sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2 \
--hash=sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7 \ --hash=sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc \
--hash=sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b \ --hash=sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400 \
--hash=sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933 \ --hash=sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928 \
--hash=sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0 \ --hash=sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a \
--hash=sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6 \ --hash=sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5 \
--hash=sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8 \ --hash=sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d \
--hash=sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c \ --hash=sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2 \
--hash=sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590 \ --hash=sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb \
--hash=sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215 \ --hash=sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0 \
--hash=sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680 \ --hash=sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2 \
--hash=sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8 \ --hash=sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335 \
--hash=sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0 \ --hash=sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae \
--hash=sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b \ --hash=sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287 \
--hash=sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca \ --hash=sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d \
--hash=sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3 \ --hash=sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b \
--hash=sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6 \ --hash=sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39 \
--hash=sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13 \ --hash=sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040 \
--hash=sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204 \ --hash=sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d \
--hash=sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f \ --hash=sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c \
--hash=sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273 \ --hash=sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86 \
--hash=sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b \ --hash=sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c \
--hash=sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd \ --hash=sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3 \
--hash=sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a \ --hash=sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d \
--hash=sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d \ --hash=sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219 \
--hash=sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896 --hash=sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5
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" \ 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:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174 \
--hash=sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd \ --hash=sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd \
--hash=sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa \ --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:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65 \
--hash=sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064 \ --hash=sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064 \
--hash=sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36 --hash=sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36
gunicorn==20.1.0 ; 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:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \ --hash=sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0 \
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8 --hash=sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033
idna==3.4 ; python_version >= "3.11" and python_version < "4.0" \ idna==3.4 ; python_full_version > "3.11.0" and python_version < "3.13" \
--hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
--hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 --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:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9 \
--hash=sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83 --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:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
--hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a --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:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
--hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 --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:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf \
--hash=sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e \ --hash=sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e \
--hash=sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af \ --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:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797 \
--hash=sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892 \ --hash=sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892 \
--hash=sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f --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:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818 \
--hash=sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34 --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:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
--hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
--hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ --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:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
--hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \ --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
--hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11 --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:044df81c1f6f3a8e52d70c4cfcb44e77ea9632a10929932870dfaa90de94365d \
--hash=sha256:0d24c47a1bb47e392fbcd26fe322e4ff3431653ac1e8718e4e147d450ae97a44 \ --hash=sha256:0d24c47a1bb47e392fbcd26fe322e4ff3431653ac1e8718e4e147d450ae97a44 \
--hash=sha256:1fcb49b6baf0375281979cbf26695ec10bd1cada1e311893e89533b3b70143e7 \ --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:f34b46dbb1db1f09bfa937cd5853e5f2af232caeeff509c3ab6e43fd33780eae \
--hash=sha256:f55fb5ff02d999a100be28bf6ffe826e1867a54c7b465409685332c9dd48ffa5 \ --hash=sha256:f55fb5ff02d999a100be28bf6ffe826e1867a54c7b465409685332c9dd48ffa5 \
--hash=sha256:ff842e27bc6a80de08c40e0bfdce460bd08080e8a94af131162b6a1b8948f2cc --hash=sha256:ff842e27bc6a80de08c40e0bfdce460bd08080e8a94af131162b6a1b8948f2cc
numpy==1.25.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:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2 \ --hash=sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668 \
--hash=sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55 \ --hash=sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9 \
--hash=sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf \ --hash=sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f \
--hash=sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01 \ --hash=sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5 \
--hash=sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca \ --hash=sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53 \
--hash=sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901 \ --hash=sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2 \
--hash=sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d \ --hash=sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974 \
--hash=sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4 \ --hash=sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f \
--hash=sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf \ --hash=sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42 \
--hash=sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380 \ --hash=sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2 \
--hash=sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044 \ --hash=sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af \
--hash=sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545 \ --hash=sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67 \
--hash=sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f \ --hash=sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e \
--hash=sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f \ --hash=sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c \
--hash=sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3 \ --hash=sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7 \
--hash=sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364 \ --hash=sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e \
--hash=sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9 \ --hash=sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908 \
--hash=sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418 \ --hash=sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66 \
--hash=sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f \ --hash=sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24 \
--hash=sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295 \ --hash=sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b \
--hash=sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3 \ --hash=sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e \
--hash=sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187 \ --hash=sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe \
--hash=sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926 \ --hash=sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a \
--hash=sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357 \ --hash=sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575 \
--hash=sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760 --hash=sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297 \
packaging==23.2 ; python_version >= "3.11" and python_version < "4.0" \ --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:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
--hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 --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:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \
--hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 --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:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d \
--hash=sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de \ --hash=sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de \
--hash=sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616 \ --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:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996 \
--hash=sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a \ --hash=sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a \
--hash=sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793 --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:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb \
--hash=sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db --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:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
--hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 --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:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87 \
--hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a --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:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
sqlalchemy==1.4.50 ; 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:0b7dbe6369677a2bea68fe9812c6e4bbca06ebfa4b5cde257b2b0bf208709131 \ --hash=sha256:0a8c6aa506893e25a04233bc721c6b6cf844bafd7250535abb56cb6cc1368884 \
--hash=sha256:128a948bd40780667114b0297e2cc6d657b71effa942e0a368d8cc24293febb3 \ --hash=sha256:0e680527245895aba86afbd5bef6c316831c02aa988d1aad83c47ffe92655e74 \
--hash=sha256:14b0cacdc8a4759a1e1bd47dc3ee3f5db997129eb091330beda1da5a0e9e5bd7 \ --hash=sha256:14aebfe28b99f24f8a4c1346c48bc3d63705b1f919a24c27471136d2f219f02d \
--hash=sha256:1fb9cb60e0f33040e4f4681e6658a7eb03b5cb4643284172f91410d8c493dace \ --hash=sha256:1e018aba8363adb0599e745af245306cb8c46b9ad0a6fc0a86745b6ff7d940fc \
--hash=sha256:273505fcad22e58cc67329cefab2e436006fc68e3c5423056ee0513e6523268a \ --hash=sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca \
--hash=sha256:2e70e0673d7d12fa6cd363453a0d22dac0d9978500aa6b46aa96e22690a55eab \ --hash=sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d \
--hash=sha256:34e1c5d9cd3e6bf3d1ce56971c62a40c06bfc02861728f368dcfec8aeedb2814 \ --hash=sha256:3e983fa42164577d073778d06d2cc5d020322425a509a08119bdcee70ad856bf \
--hash=sha256:3b97ddf509fc21e10b09403b5219b06c5b558b27fc2453150274fa4e70707dbf \ --hash=sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846 \
--hash=sha256:3f6997da81114daef9203d30aabfa6b218a577fc2bd797c795c9c88c9eb78d49 \ --hash=sha256:42ede90148b73fe4ab4a089f3126b2cfae8cfefc955c8174d697bb46210c8306 \
--hash=sha256:82dd4131d88395df7c318eeeef367ec768c2a6fe5bd69423f7720c4edb79473c \ --hash=sha256:4895a63e2c271ffc7a81ea424b94060f7b3b03b4ea0cd58ab5bb676ed02f4221 \
--hash=sha256:85292ff52ddf85a39367057c3d7968a12ee1fb84565331a36a8fead346f08796 \ --hash=sha256:4af79c06825e2836de21439cb2a6ce22b2ca129bad74f359bddd173f39582bf5 \
--hash=sha256:8a7a66297e46f85a04d68981917c75723e377d2e0599d15fbe7a56abed5e2d75 \ --hash=sha256:5f94aeb99f43729960638e7468d4688f6efccb837a858b34574e01143cf11f89 \
--hash=sha256:8b881ac07d15fb3e4f68c5a67aa5cdaf9eb8f09eb5545aaf4b0a5f5f4659be18 \ --hash=sha256:616fe7bcff0a05098f64b4478b78ec2dfa03225c23734d83d6c169eb41a93e55 \
--hash=sha256:a3257a6e09626d32b28a0c5b4f1a97bced585e319cfa90b417f9ab0f6145c33c \ --hash=sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72 \
--hash=sha256:a9bddb60566dc45c57fd0a5e14dd2d9e5f106d2241e0a2dc0c1da144f9444516 \ --hash=sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea \
--hash=sha256:bdb77e1789e7596b77fd48d99ec1d2108c3349abd20227eea0d48d3f8cf398d9 \ --hash=sha256:63bfc3acc970776036f6d1d0e65faa7473be9f3135d37a463c5eba5efcdb24c8 \
--hash=sha256:c1db0221cb26d66294f4ca18c533e427211673ab86c1fbaca8d6d9ff78654293 \ --hash=sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577 \
--hash=sha256:c4cb501d585aa74a0f86d0ea6263b9c5e1d1463f8f9071392477fd401bd3c7cc \ --hash=sha256:64ac935a90bc479fee77f9463f298943b0e60005fe5de2aa654d9cdef46c54df \
--hash=sha256:d00665725063692c42badfd521d0c4392e83c6c826795d38eb88fb108e5660e5 \ --hash=sha256:683ef58ca8eea4747737a1c35c11372ffeb84578d3aab8f3e10b1d13d66f2bc4 \
--hash=sha256:d0fed0f791d78e7767c2db28d34068649dfeea027b83ed18c45a423f741425cb \ --hash=sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d \
--hash=sha256:d69738d582e3a24125f0c246ed8d712b03bd21e148268421e4a4d09c34f521a5 \ --hash=sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4 \
--hash=sha256:db4db3c08ffbb18582f856545f058a7a5e4ab6f17f75795ca90b3c38ee0a8ba4 \ --hash=sha256:7e0dc9031baa46ad0dd5a269cb7a92a73284d1309228be1d5935dac8fb3cae24 \
--hash=sha256:f1fcee5a2c859eecb4ed179edac5ffbc7c84ab09a5420219078ccc6edda45436 \ --hash=sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6 \
--hash=sha256:f2d526aeea1bd6a442abc7c9b4b00386fd70253b80d54a0930c0a216230a35be \ --hash=sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965 \
--hash=sha256:fbaf6643a604aa17e7a7afd74f665f9db882df5c297bdd86c38368f2c471f37d --hash=sha256:964971b52daab357d2c0875825e36584d58f536e920f2968df8d581054eada4b \
typing-extensions==4.8.0 ; python_version >= "3.11" and python_version < "4.0" \ --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:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
--hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef --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:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \
--hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10 --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:5e51df8af9a60f6beead75efa10975e97768825a82146a65c7cbf5b915990620 \
--hash=sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0 --hash=sha256:ae7c54b29806c70f7bce8eb9f24afceb10ca5c32af3d9f04f74d2f66ccc5c7e0