aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore181
-rw-r--r--.gitlab-ci.yml12
-rw-r--r--.python-version1
-rw-r--r--Procfile1
-rw-r--r--app/__init__.py141
-rw-r--r--app/config.py15
-rw-r--r--app/controller.py194
-rw-r--r--app/model.py110
-rw-r--r--app/static/custom.css34
-rw-r--r--app/static/favicon.icobin15406 -> 0 bytes
-rw-r--r--app/static/file.js11
-rw-r--r--app/static/timer.js91
-rw-r--r--app/templates/controls.j248
-rw-r--r--app/templates/games.j221
-rw-r--r--app/templates/index.j216
-rw-r--r--app/templates/login.html23
-rw-r--r--app/templates/macros.j245
-rw-r--r--app/templates/players.j240
-rw-r--r--app/utilities.py172
-rw-r--r--docs/game_screen.png3
-rw-r--r--docs/player_screen.png3
-rw-r--r--index.css10
-rw-r--r--index.html31
-rw-r--r--index.js11
-rw-r--r--migrations/README1
-rw-r--r--migrations/alembic.ini50
-rw-r--r--migrations/env.py108
-rw-r--r--migrations/script.py.mako24
-rw-r--r--migrations/versions/c08245a9c93f_.py122
-rw-r--r--migrations/versions/d158cf8f4950_.py41
-rw-r--r--player.mjs37
-rw-r--r--pyproject.toml17
-rw-r--r--uv.lock487
34 files changed, 89 insertions, 2013 deletions
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 24a8e87..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-*.png filter=lfs diff=lfs merge=lfs -text
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 7e5d5dd..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,181 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# poetry
-# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-
-# pdm
-# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
-#pdm.lock
-# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
-# in version control.
-# https://pdm.fming.dev/#use-with-ide
-.pdm.toml
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# PyCharm
-# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
-# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-# and can be added to the global gitignore or merged into this file. For a more nuclear
-# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
-
-# VS Code
-.vscode
-
-# Data
-data
-*.csv
-
-# Scripts
-*.ps1
-*.cmd
-*.sh
-
-# Mac specfic
-.DS_Store
-
-# Databases
-*.db
-
-# Environmental variables
-*.flaskenv
-
-# Caches
-.ruff_cache \ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index e0d0142..0000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-image: ruby:latest
-
-deploy:
- stage: deploy
- environment:
- name: production
- url: https://match-up-demo-73950c31ba18.herokuapp.com
- script:
- - gem install dpl
- - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY
- rules:
- - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
diff --git a/.python-version b/.python-version
deleted file mode 100644
index e4fba21..0000000
--- a/.python-version
+++ /dev/null
@@ -1 +0,0 @@
-3.12
diff --git a/Procfile b/Procfile
deleted file mode 100644
index 8001d1a..0000000
--- a/Procfile
+++ /dev/null
@@ -1 +0,0 @@
-web: gunicorn app:app \ No newline at end of file
diff --git a/app/__init__.py b/app/__init__.py
deleted file mode 100644
index 0e0ea09..0000000
--- a/app/__init__.py
+++ /dev/null
@@ -1,141 +0,0 @@
-from pandas import read_csv
-from functools import wraps
-from app.config import Config
-from flask_migrate import Migrate
-from flask_sqlalchemy import SQLAlchemy
-from flask_simplelogin import SimpleLogin
-from werkzeug.utils import secure_filename
-from flask_simplelogin import is_logged_in
-from flask import (
- Flask,
- render_template_string,
- render_template,
- request,
- redirect,
- url_for,
- Response,
- get_template_attribute,
-)
-
-app = Flask(__name__)
-app.config.from_object(Config)
-db = SQLAlchemy(app)
-migrate = Migrate(app, db)
-SimpleLogin(app)
-
-
-from app.controller import Session # noqa: E402
-
-g = Session()
-
-
-@app.route("/", methods=["GET"])
-def home():
- return render_template("index.j2", url_for=url_for, login_status=is_logged_in())
-
-
-@app.route("/control", methods=["GET"])
-def get_controls():
- return render_template("controls.j2", courts=g.courts.all())
-
-
-@app.route("/players", methods=["GET"])
-def get_list_of_players():
- return render_template(
- "players.j2",
- regulars=g.players.regulars(),
- guests=g.players.guests(),
- url_for=url_for,
- login_status=is_logged_in(),
- )
-
-
-@app.route("/courts", methods=["GET"])
-def get_list_of_courts():
- return render_template("courts.j2", courts=g.courts.all())
-
-
-def login_required(f):
- @wraps(f)
- def decorated_function(*args, **kwargs):
- if not is_logged_in():
- response = Response("User isn't logged in!")
- response.headers["HX-Redirect"] = url_for("simplelogin.login")
- return response
- return f(*args, **kwargs)
-
- return decorated_function
-
-
-@app.route("/propose", methods=["POST"])
-@login_required
-def get_random_games():
- g.levels = int(request.form["levels"].split()[0])
- g.teams = request.form["team"] == "Teams"
- return render_template("games.j2", games=g.propose(), title="Proposal")
-
-
-@app.route("/confirm", methods=["POST"])
-@login_required
-def confirm_games():
- return render_template("games.j2", games=g.confirm(), title=f"Round: {g.round()}")
-
-
-@app.route("/clear", methods=["POST"])
-def clear_games():
- return render_template(
- "games.j2", games=g.current_round(), title=f"Round: {g.round()}"
- )
-
-
-@app.route("/reset", methods=["POST"])
-@login_required
-def reset_games():
- return render_template("games.j2", games=g.reset(), title=f"Round: {g.round()}")
-
-
-@app.route("/player-toggle/<player_request>", methods=["POST"])
-@login_required
-def toggle_player(player_request):
- id = int(player_request)
- player = g.players.toggle(id)
- button = get_template_attribute("macros.j2", "player_button")
- return render_template_string("{{button(player)}}", player=player, button=button)
-
-
-@app.route("/reset_players", methods=["POST"])
-@login_required
-def reset_all_players():
- g.players.reset()
- return redirect(url_for("players"))
-
-
-@app.route("/update_players", methods=["POST"])
-@login_required
-def update_all_players():
- file = request.files.get("players")
- response = Response("File name or data has issues!", 400)
- response.headers["HX-Refresh"] = "true"
- if file is None:
- return response
- elif file.filename is None:
- return response
- file_name = secure_filename(file.filename)
- try:
- data = read_csv(file_name)
- required_columns = ["id", "first_name", "last_name", "level", "team"]
- if not all([item in data.columns for item in required_columns]):
- raise ImportError("All columns aren't available in the data.")
- except: # noqa: E722
- return response
- g.players.update(data)
- return redirect(url_for("players"))
-
-
-@app.route("/court-toggle/<court_number>", methods=["POST"])
-@login_required
-def toggle_court(court_number):
- id = int(court_number)
- court = g.courts.toggle(id)
- button = get_template_attribute("macros.j2", "court_button")
- return render_template_string("{{button(court)}}", court=court, button=button)
diff --git a/app/config.py b/app/config.py
deleted file mode 100644
index 5bc65b3..0000000
--- a/app/config.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from os import environ
-
-
-class Config:
- SQLALCHEMY_DATABASE_URI = environ.get("SQLALCHEMY_DATABASE_URI")
- SECRET_KEY = environ.get("SECRET_KEY")
- GUEST_COUNT = 4
- COURT_COUNT = 12
- MIN_PLAYER_LEVEL = 1
- MAX_PLAYER_LEVEL = 10
- PLAYERS_PER_COURT = 4
- # Default players as dictionary of first names and primary keys
- DEFAULTS = {"---": 1, "RESERVED": 2}
- # Guest players as dictionary of first names and play level
- GUESTS = {"Beginner": 1, "Novice": 3, "Intermediate": 5}
diff --git a/app/controller.py b/app/controller.py
deleted file mode 100644
index d5bdbcb..0000000
--- a/app/controller.py
+++ /dev/null
@@ -1,194 +0,0 @@
-from math import floor
-from app import app, db
-from datetime import date
-from typing import Sequence
-from pandas import DataFrame
-from operator import attrgetter
-from sqlalchemy import func, select, not_
-from app.model import (
- Player,
- Court,
- Game,
- DisplayGame,
-)
-from app.utilities import (
- get_placeholder_games,
- get_team_games,
- get_shuffle_games,
- separate_teams_and_players,
- initialize_random_database,
- initialize_default_players,
-)
-
-
-class Courts:
- def __init__(self):
- with app.app_context():
- db.session.query(Court).delete()
- for _ in range(app.config["COURT_COUNT"]):
- db.session.add(Court())
- db.session.commit()
-
- def get(self, id: int) -> Court:
- court = db.session.get(Court, id)
- if court is None:
- raise ValueError(f"Court not found: {id}")
- return court
-
- def toggle(self, id: int) -> Court:
- court = self.get(id)
- court.status = not court.status
- db.session.commit()
- return court
-
- def active(self):
- return db.session.query(Court).where(Court.status).all()
-
- def inactive(self):
- return db.session.query(Court).where(not_(Court.status)).all()
-
- def all(self) -> list[Court]:
- return db.session.query(Court).order_by(Court.id).all()
-
-
-class Players:
- def __init__(self) -> None:
- with app.app_context():
- if len(self.regulars()) == 0:
- initialize_random_database()
-
- def update(self, data: DataFrame):
- initialize_default_players()
- for entry in data.to_dict("records"):
- args = entry | {"status": False}
- db.session.add(Player(**args)) # type: ignore
- db.session.commit()
-
- def get(self, id: int) -> Player:
- player = db.session.get(Player, id)
- if player is None:
- raise ValueError(f"Player not found: {id}")
- return player
-
- def toggle(self, id: int) -> Player:
- player = self.get(id)
- player.status = not player.status
- db.session.commit()
- return player
-
- def regulars(self) -> list[Player]:
- filter_list = list(app.config["GUESTS"].keys()) + list(
- app.config["DEFAULTS"].keys()
- )
- return (
- db.session.query(Player)
- .where(Player.id > 1)
- .where(Player.first_name.not_in(filter_list))
- .order_by(Player.first_name, Player.last_name)
- .all()
- )
-
- def guests(self) -> list[Player]:
- return (
- db.session.query(Player)
- .where(Player.id > 1)
- .where(Player.first_name.in_(app.config["GUESTS"].keys()))
- .order_by(Player.first_name, Player.last_name)
- .all()
- )
-
- def active(self) -> Sequence[Player]:
- query = (
- select(Player)
- .where(Player.status)
- .order_by(Player.first_name, Player.last_name)
- )
- return db.session.scalars(query).all()
-
- def reset(self):
- for player in db.session.scalars(select(Player)):
- player.status = False
- db.session.commit()
-
-
-class Session:
- levels: int = 10
- teams: bool = False
- proposal: list[DisplayGame] = []
-
- def _today(self):
- games = (
- db.session.query(Game).filter(func.date(Game.date) == date.today()).all()
- )
- return games
-
- def __init__(self):
- with app.app_context():
- db.create_all()
- self.courts = Courts()
- self.players = Players()
- if len(self._today()) == 0:
- query = select(Player).where(Player.first_name == "---")
- player = db.session.scalars(query).first()
- if player is None:
- raise ValueError("Database isn't initialized.")
- player_data = {f"player_{i + 1}": player.id for i in range(4)}
- for court in self.courts.all():
- args = {"round_id": 0, "court_id": court.id} | player_data
- db.session.add(Game(**args))
- db.session.commit()
-
- def round(self) -> int:
- games = self._today()
- round_id = max([game.round_id for game in games])
- if round_id is None:
- raise ValueError("Database isn't initialized")
- return round_id
-
- def current_round(self) -> list[DisplayGame]:
- games = (
- db.session.query(Game)
- .filter(func.date(Game.date) == date.today())
- .filter(Game.round_id == self.round())
- .order_by(Game.court_id)
- .all()
- )
- games = [DisplayGame(game) for game in games]
- return games
-
- def propose(self) -> list[DisplayGame]:
- reserved_courts = self.courts.inactive()
- reserved_games = get_placeholder_games(reserved_courts, "RESERVED")
- active_courts = self.courts.active()
- active_players = self.players.active()
- game_count = floor(len(active_players) / app.config["PLAYERS_PER_COURT"])
- game_count = min(len(active_courts), game_count)
- used_courts = active_courts[:game_count]
- unused_courts = active_courts[game_count:]
- blank_games = get_placeholder_games(unused_courts, "---")
- active_teams = {}
- if self.teams:
- active_teams, active_players = separate_teams_and_players(active_players)
- team_courts = used_courts[: len(active_teams)]
- shuffle_courts = used_courts[len(active_teams) :]
- team_games = get_team_games(team_courts, active_teams)
- shuffle_games = get_shuffle_games(shuffle_courts, active_players, self.levels)
- self.proposal = shuffle_games + team_games + blank_games + reserved_games
- self.proposal.sort(key=attrgetter("court.id"))
- return self.proposal
-
- def confirm(self) -> list[DisplayGame]:
- if len(self.proposal) == 0:
- return self.current_round()
- round_id = self.round() + 1
- for game in self.proposal:
- db.session.add(game.to_game(round_id))
- db.session.commit()
- self.proposal = []
- return self.current_round()
-
- def reset(self) -> list[DisplayGame]:
- db.session.query(Game).filter(func.date(Game.date) == date.today()).delete()
- db.session.commit()
- self.__init__()
- return self.current_round()
diff --git a/app/model.py b/app/model.py
deleted file mode 100644
index 22424d4..0000000
--- a/app/model.py
+++ /dev/null
@@ -1,110 +0,0 @@
-from app import app, db
-from sqlalchemy.orm import Mapped
-from datetime import date, datetime
-from typing import Optional, Annotated
-from sqlalchemy.orm import mapped_column as mc
-from sqlalchemy import String, Boolean, Integer, ForeignKey
-
-
-class User(db.Model):
- id: Mapped[int] = mc(primary_key=True)
- username: Mapped[str] = mc(String(64), index=True, unique=True)
- password_hash: Mapped[Optional[str]] = mc(String(256))
-
- def __repr__(self):
- return f"User {self.username}"
-
-
-class Court(db.Model):
- id: Mapped[int] = mc(primary_key=True)
- status: Mapped[bool] = mc(Boolean, default=True)
-
- def __repr__(self):
- return f"Court {self.id} is {'active' if self.status else 'inactive'}"
-
-
-class Player(db.Model):
- id: Mapped[int] = mc(primary_key=True)
- first_name: Mapped[str] = mc(String(64), index=True)
- last_name: Mapped[str] = mc(String(64), index=True)
- team: Mapped[int] = mc(Integer, index=True)
- status: Mapped[bool] = mc(Boolean)
- level: Mapped[int] = mc(Integer)
-
- def __init__(
- self,
- id,
- first_name,
- last_name,
- team=0,
- status=False,
- level=int(app.config["MIN_PLAYER_LEVEL"]),
- ):
- self.id = id
- self.first_name = first_name
- self.last_name = last_name
- self.team = team
- self.status = status
- self.level = level
-
- def __repr__(self):
- return f"{self.first_name} {self.last_name} is {'active' if self.status else 'inactive'}"
-
-
-class Game(db.Model):
- id: Mapped[int] = mc(primary_key=True)
- round_id: Mapped[int] = mc(Integer, index=True)
- court_id: Mapped[int] = mc(ForeignKey(Court.id), index=True)
- player_1: Mapped[int] = mc(ForeignKey(Player.id), index=True)
- player_2: Mapped[int] = mc(ForeignKey(Player.id), index=True)
- player_3: Mapped[int] = mc(ForeignKey(Player.id), index=True)
- player_4: Mapped[int] = mc(ForeignKey(Player.id), index=True)
- date: Mapped[datetime] = mc(index=True, default=lambda: date.today())
-
- def __init__(
- self,
- round_id: Optional[int],
- court_id: int,
- player_1: int,
- player_2: int,
- player_3: int,
- player_4: int,
- ):
- if round_id is not None:
- self.round_id = round_id
- self.court_id = court_id
- self.player_1 = player_1
- self.player_2 = player_2
- self.player_3 = player_3
- self.player_4 = player_4
-
-
-class DisplayGame:
- court: Court
- team_1: Annotated[list[Player], 2]
- team_2: Annotated[list[Player], 2]
-
- def __init__(self, game: Game):
- court = db.session.get(Court, game.court_id)
- if court is None:
- raise ValueError("Unknown court")
- self.court = court
- players = []
- for i in range(4):
- player_id = getattr(game, f"player_{i + 1}")
- p = db.session.get(Player, player_id)
- if p is None:
- raise ValueError(f"Player ID: {player_id} mismatch.")
- players.append(p)
- self.team_1 = players[:2]
- self.team_2 = players[2:]
-
- def to_game(self, round_id) -> Game:
- return Game(
- round_id,
- self.court.id,
- self.team_1[0].id,
- self.team_1[1].id,
- self.team_2[0].id,
- self.team_2[1].id,
- )
diff --git a/app/static/custom.css b/app/static/custom.css
deleted file mode 100644
index a5ca36f..0000000
--- a/app/static/custom.css
+++ /dev/null
@@ -1,34 +0,0 @@
-button, input, select {
- width: 100%;
- height: 100%;
-}
-td {
- width: 20%;
-}
-.grid {
- margin-top: 20px;
- margin-bottom: 20px;
-}
-footer {
- text-align: center;
-}
-.parent, .timer {
- display: grid;
- grid-column-gap: 20px;
- grid-row-gap: 20px;
- margin-top: 20px;
- margin-bottom: 20px;
- grid-auto-columns: 1fr;
- grid-auto-rows: 1fr;
-}
-@media (min-width: 900px) {
- table {
- font-size: xx-large;
- }
- .parent {
- grid-template-columns: repeat(4, 1fr);
- }
- .timer {
- grid-template-columns: 4fr repeat(2, 1fr);
- }
-}
diff --git a/app/static/favicon.ico b/app/static/favicon.ico
deleted file mode 100644
index 64eab0a..0000000
--- a/app/static/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/app/static/file.js b/app/static/file.js
deleted file mode 100644
index 403ddf6..0000000
--- a/app/static/file.js
+++ /dev/null
@@ -1,11 +0,0 @@
-window.onload = function() {
- const fileInput = document.querySelector("#file-js input[type=file]");
- if (fileInput !== null) {
- fileInput.onchange = () => {
- if (fileInput.files.length > 0) {
- const fileName = document.querySelector("#file-js .file-name");
- fileName.textContent = fileInput.files[0].name;
- }
- }
- };
-} \ No newline at end of file
diff --git a/app/static/timer.js b/app/static/timer.js
deleted file mode 100644
index e8e7854..0000000
--- a/app/static/timer.js
+++ /dev/null
@@ -1,91 +0,0 @@
-let minsPerRound = Number(sessionStorage.getItem("minsPerRound")) || 12;
-let timerRunningFlag = Number(sessionStorage.getItem("timerRunningFlag")) || 0;
-let deadline = Number(sessionStorage.getItem("deadline")) || new Date().getTime();
-let clock = 0;
-
-function setTimer(m, s) {
- let timer = document.getElementById("timer-display");
- m = m < 10 ? ("0" + m) : m;
- s = s < 10 ? ("0" + s) : s;
- timer.value = `${m}:${s}`;
-}
-
-function setSessionData() {
- sessionStorage.setItem("deadline", deadline);
- sessionStorage.setItem("minsPerRound", minsPerRound);
- sessionStorage.setItem("timerRunningFlag", timerRunningFlag);
-}
-
-function resetTimer() {
- clearInterval(clock);
- setTimer(minsPerRound, 0);
- timerRunningFlag = 0;
- setSessionData();
-}
-
-function updateTimer(milliseconds) {
- const m = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
- const s = Math.floor((milliseconds % (1000 * 60)) / 1000);
- setTimer(m, s);
-}
-
-function refreshTimer() {
- const now = new Date().getTime();
- const millisecondsToDeadline = deadline - now;
- if (millisecondsToDeadline > 0) {
- updateTimer(millisecondsToDeadline);
- return;
- }
- resetTimer();
- const notification = new Notyf({
- duration: 30 * 1000,
- position: {x: 'center', y: 'top'},
- types: [{
- type: 'success',
- background: 'indianred',
- duration: 1000 * 1000,
- dismissible: true
- }]
- });
- notification.success("Round completed");
-}
-
-function startTimer(fresh) {
- if (fresh) {
- now = new Date().getTime();
- const millisecondsToDeadline = minsPerRound * 60 * 1000;
- deadline = now + millisecondsToDeadline;
- }
- timerRunningFlag = 1;
- setSessionData();
- clock = setInterval(refreshTimer, 1000);
-}
-
-window.onload = function () {
- if (timerRunningFlag)
- startTimer(0);
- else resetTimer();
-
- document.getElementById("timer-start").addEventListener('click', ()=> {
- if (timerRunningFlag != 0) return;
- startTimer(1);
- });
-
- document.getElementById("timer-stop").addEventListener('click', ()=>{
- resetTimer();
- });
-
- document.getElementById("timer-inc").addEventListener('click', ()=> {
- if (timerRunningFlag == 0) {
- minsPerRound += 1;
- resetTimer();
- }
- });
-
- document.getElementById("timer-dec").addEventListener('click', ()=> {
- if (timerRunningFlag == 0 && minsPerRound != 0) {
- minsPerRound -= 1;
- resetTimer();
- }
- });
-} \ No newline at end of file
diff --git a/app/templates/controls.j2 b/app/templates/controls.j2
deleted file mode 100644
index 97351bf..0000000
--- a/app/templates/controls.j2
+++ /dev/null
@@ -1,48 +0,0 @@
-{% from "macros.j2" import court_button %}
-<h3>Timer</h3>
-<div class="timer">
- <div role="group" style="height: 100%">
- <button id="timer-dec"><i class="fas fa-light fa-minus"></i></button>
- <input id="timer-display" type="text" style="text-align:center; height: 100%" readonly></input>
- <button id="timer-inc"><i class="fas fa-light fa-plus"></i></button>
- </div>
- <button id="timer-start"><i class="fas fa-light fa-play"></i></button>
- <button id="timer-stop"><i class="fas fa-light fa-stop"></i></button>
-</div>
-<h3>Generation</h3>
-<div class="grid">
- <button class="button is-dark is-responsive"
- hx-post="/propose"
- hx-target="#games"
- hx-include="[name='levels'], [name='team']">Generate</button>
- <button class="button is-dark is-responsive"
- hx-post="/confirm"
- hx-target="#games">Confirm</button>
- <button class="button is-dark is-responsive"
- hx-post="/reset"
- hx-target="#games">RESET!</button>
-</div>
-<div class="grid">
- <div class="select is-responsive">
- <select name="levels">
- <option>10 Levels</option>
- <option>8 Levels</option>
- <option>6 Levels</option>
- <option>4 Levels</option>
- <option>2 Levels</option>
- <option>0 Levels</option>
- </select>
- </div>
- <div class="select is-responsive">
- <select name="team">
- <option>Random</option>
- <option>Teams</option>
- </select>
- </div>
-</div>
-<h3>Courts</h3>
-<div class="parent">
-{% for court in courts %}
- {{ court_button(court) }}
-{% endfor %}
-</div> \ No newline at end of file
diff --git a/app/templates/games.j2 b/app/templates/games.j2
deleted file mode 100644
index 00ffb71..0000000
--- a/app/templates/games.j2
+++ /dev/null
@@ -1,21 +0,0 @@
-<div id="games" class="overflow-auto">
- <h3> {{ title }} </h3>
- <table>
- <thead>
- <th align="center"> Court </th>
- <th colspan="2" align="center"> Team 1 </th>
- <th colspan="2" align="center"> Team 2 </th>
- </thead>
- <tbody>
- {% for game in games %}
- <tr {% if game.team_1[0].first_name == 'RESERVED' %} data-theme="dark" {% endif %}>
- <td align="center" style="width: 8%;">{{ game.court.id }}</th>
- <td style="width: 23%;"><strong>{{ game.team_1[0].first_name }}</strong>{{ " " + game.team_1[0].last_name }}</th>
- <td style="width: 23%;"><strong>{{ game.team_1[1].first_name }}</strong>{{ " " + game.team_1[1].last_name }}</th>
- <td style="width: 23%;"><strong>{{ game.team_2[0].first_name }}</strong>{{ " " + game.team_2[0].last_name }}</th>
- <td style="width: 23%;"><strong>{{ game.team_2[1].first_name }}</strong>{{ " " + game.team_2[1].last_name }}</th>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-<div> \ No newline at end of file
diff --git a/app/templates/index.j2 b/app/templates/index.j2
deleted file mode 100644
index af6ca4c..0000000
--- a/app/templates/index.j2
+++ /dev/null
@@ -1,16 +0,0 @@
-{% from "macros.j2" import header, footer, navbar %}
-<!doctype html>
-<html lang="en">
- {{ header(url_for) }}
- <body>
- <header class="container">{{ navbar(url_for, login_status) }}</header>
- <main class="container">
- <h2>Games</h2>
- <div hx-post="/clear" hx-swap="outerHTML" hx-trigger="load"></div>
- <h2>Controls</h2>
- <div hx-get="/control" hx-swap="outerHTML" hx-trigger="load"></div>
- </main>
- <footer class="container">{{ footer() }}</footer>
- <script src="{{ url_for('static', filename='timer.js') }}"></script>
- </body>
-</html>
diff --git a/app/templates/login.html b/app/templates/login.html
deleted file mode 100644
index e3109b6..0000000
--- a/app/templates/login.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% from "macros.j2" import header, footer, navbar %}
-<!doctype html>
-<html lang="en">
-{{ header(url_for) }}
-<body>
- <header class="container">{{ navbar(url_for) }}</header>
- <main class="container">
- <form action="{{ url_for('simplelogin.login') }}" method="post">
- <div class="form-group">
- {{ form.csrf_token }}
- {{form.username.label(class_="label")}}<div class="control form-control">{{ form.username(class_="input") }}
- </div><br>
- {{form.password.label(class_="label")}}<div class="control form-control"> {{ form.password(class_="input") }}
- </div><br>
- {% if next %}<input type="hidden" name="next" value="{{next}}">{% endif %}
- </form>
- <input class="button is-dark" type="submit" value="Login">
- </form>
- </main>
- <footer class="container">{{ footer() }}</footer>
-</body>
-
-</html> \ No newline at end of file
diff --git a/app/templates/macros.j2 b/app/templates/macros.j2
deleted file mode 100644
index e82b910..0000000
--- a/app/templates/macros.j2
+++ /dev/null
@@ -1,45 +0,0 @@
-{% macro court_button(court) -%}
-<button class= "{% if court.status %}{% else %}contrast{% endif %}"
- hx-post="{{ "/court-toggle/" ~ court.id}}"
- hx-swap="outerHTML"> Court {{ court.id }}</button>
-{%- endmacro %}
-
-{% macro player_button(player) -%}
-<button class="{% if player.status %}{% else %}contrast{% endif %}"
- hx-post="{{ "/player-toggle/" ~ player.id}}"
- hx-swap="outerHTML"> {{ player.first_name + " " + player.last_name}} </button>
-{%- endmacro %}
-
-{% macro header(url_for) -%}
-<head>
-<meta charset="UTF-8">
-<meta name="viewport" content="width=device-width, initial-scale=1">
-<title>Match Up!</title>
-<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css">
-<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
-<link rel="stylesheet" href="{{ url_for('static', filename='custom.css') }}">
-<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js"></script>
-<script src="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js"></script>
-<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/js/fontawesome.min.js"></script>
-</head>
-{%- endmacro %}
-
-{% macro footer() -%}
-<div class="container">Made by <a href="https://karanjayachandra.com/">Karan</a> with <i class="fas fa-heart"></i></div>
-{%- endmacro %}
-
-
-{% macro navbar(url_for, login_status) -%}
-<nav>
- <ul>
- <li><strong>Match Up!</strong></li>
- </ul>
- <ul>
- <li><a href="/">Games</a></li>
- <li><a href="/players">Players</a></li>
- {% if login_status %}<li><a href="/logout/">Logout</a></li>{% else %}<li><a href="/login/">Login</a></li>{% endif %}
- </ul>
-</nav>
-{%- endmacro %} \ No newline at end of file
diff --git a/app/templates/players.j2 b/app/templates/players.j2
deleted file mode 100644
index bacb215..0000000
--- a/app/templates/players.j2
+++ /dev/null
@@ -1,40 +0,0 @@
-{% from "macros.j2" import header, navbar, footer, player_button, guest_control %}
-<!doctype html>
-<html lang="en">
- {{ header(url_for) }}
- <body>
- <header class="container">{{ navbar(url_for, login_status) }}</header>
- <main class="container">
- <h3 >Members</h3>
- <div class="parent">
- {% for regular in regulars %}
- <div class="cell"> {{ player_button(regular) }} </div>
- {% endfor %}
- </div>
- <h3>Guests</h3>
- <div class="parent">
- {% for guest in guests %}
- <div class="cell"> {{ player_button(guest) }} </div>
- {% endfor %}
- </div>
- <h3>Controls</h3>
- <div class="grid">
- <button class="button is-dark is-responsive"
- hx-post="/reset_players">RESET!</button>
- </div>
- <h3>Player Update</h3>
- <div class="grid">
- <form method="POST" enctype="multipart/form-data" hx-post="/update_players">
- <div id="file-js" class="file has-name is-fullwidth">
- <label class="file-label">
- <input class="file-input" type="file" name="players" />
- </label>
- <button class="button is-dark is-responsive">UPDATE!</button>
- </div>
- </form>
- </div>
- <footer class="container">{{ footer() }}</footer>
- <script src="{{ url_for('static', filename='file.js') }}"></script>
- </main>
- </body>
-</html> \ No newline at end of file
diff --git a/app/utilities.py b/app/utilities.py
deleted file mode 100644
index d8bd3da..0000000
--- a/app/utilities.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from math import floor
-from app import app, db
-from datetime import date
-from sqlalchemy import func
-from typing import Tuple, Sequence
-from random import shuffle, sample
-from random import randint, getrandbits
-from names import get_first_name, get_last_name
-from app.model import Player, Court, Game, DisplayGame
-
-
-def _shuffle_two_lists(a: list, b: list) -> Tuple[list, list]:
- indices = list(range(len(a)))
- shuffle(indices)
- a = [a[index] for index in indices]
- b = [b[index] for index in indices]
- return a, b
-
-
-def _pick_from_list_after_sorting_other(a: list, b: list[int]) -> list:
- indices = [i[0] for i in sorted(enumerate(b), key=lambda x: x[1])]
- return [a[index] for index in indices]
-
-
-def sample_list(indices: list, cost: list[int], count=None) -> list:
- if count is None:
- count = len(indices)
- indices, cost = _shuffle_two_lists(indices, cost)
- indices = _pick_from_list_after_sorting_other(indices, cost)[:count]
- return indices
-
-
-def _normalize_skill(level: int, new_max: int) -> int:
- scaling = (new_max - 1) / int(app.config["MAX_PLAYER_LEVEL"])
- new_level = round(scaling * level) + 1
- return new_level
-
-
-def select_players(players: Sequence[Player], count: int) -> list[Player]:
- games = db.session.query(Game).filter(func.date(Game.date) == date.today()).all()
- player_identifiers = []
- for game in games:
- for i in range(4):
- player_identifiers.append(getattr(game, f"player_{i + 1}"))
- identifiers = [player.id for player in players]
- play_count = [player_identifiers.count(player.id) for player in players]
- ids = sample_list(identifiers, play_count, count)
- selected_players = [player for player in players if player.id in ids]
- return selected_players
-
-
-def group_by_four(players: list[Player], new_max: int) -> list[list[Player]]:
- identifiers = [player.id for player in players]
- player_levels = [_normalize_skill(int(player.level), new_max) for player in players]
- identifiers = sample_list(identifiers, player_levels)
- court_players = []
- for i in range(0, len(players), app.config["PLAYERS_PER_COURT"]):
- court_ids = identifiers[i : i + app.config["PLAYERS_PER_COURT"]]
- selected_players = [player for player in players if player.id in court_ids]
- court_players.append(selected_players)
- return court_players
-
-
-def separate_teams_and_players(
- players: Sequence[Player],
-) -> tuple[dict[int, list[Player]], list[Player]]:
- unique_team_id = {player.team for player in players}
- grouped_players_by_team = {team_id: [] for team_id in unique_team_id}
- for player in players:
- team = player.team
- grouped_players_by_team[team].append(player)
- active_teams = {}
- players_without_team = []
- for team_id in grouped_players_by_team:
- if team_id == 0:
- players_without_team += [
- player for player in grouped_players_by_team[team_id]
- ]
- else:
- if len(grouped_players_by_team[team_id]) >= 4:
- active_teams[team_id] = grouped_players_by_team[team_id]
- else:
- players_without_team += [
- player for player in grouped_players_by_team[team_id]
- ]
- return active_teams, players_without_team
-
-
-def get_placeholder_games(courts: list[Court], name: str) -> list[DisplayGame]:
- p = db.session.query(Player).where(Player.first_name == name).first()
- if p is None:
- raise ValueError("Database isn't initialized.")
- player_data = {"round_id": None} | {f"player_{i + 1}": p.id for i in range(4)}
- games = [
- DisplayGame(Game(**({"court_id": court.id} | player_data))) # type: ignore
- for court in courts
- ]
- return games
-
-
-def get_team_games(
- courts: list[Court], teams: dict[int, list[Player]]
-) -> list[DisplayGame]:
- games = []
- for court, (_, players) in zip(courts, teams.items()):
- players = sample(players, app.config["PLAYERS_PER_COURT"])
- players.sort(key=lambda x: x.level, reverse=True)
- game = Game(
- None, court.id, players[0].id, players[3].id, players[1].id, players[2].id
- )
- games.append(DisplayGame(game))
- return games
-
-
-def get_shuffle_games(
- courts: list[Court], players: Sequence[Player], total_levels: int
-) -> list[DisplayGame]:
- required_player_count = len(courts) * app.config["PLAYERS_PER_COURT"]
- selected_players = select_players(players, required_player_count)
- court_players = group_by_four(selected_players, total_levels)
- games = []
- for court, players in zip(courts, court_players):
- game = Game(
- None, court.id, players[0].id, players[3].id, players[1].id, players[2].id
- )
- games.append(DisplayGame(game))
- return games
-
-
-def initialize_default_players():
- db.session.query(Player).delete()
- for k, v in app.config["DEFAULTS"].items():
- db.session.add(Player(**{"id": v, "first_name": k, "last_name": ""}))
- guest_offset = 10000
- for i, (k, v) in enumerate(app.config["GUESTS"].items()):
- level_offset = app.config["GUEST_COUNT"] * i
- for j in range(app.config["GUEST_COUNT"]):
- db.session.add(
- Player(
- **{
- "id": guest_offset + level_offset + j,
- "first_name": k,
- "last_name": f"{j + 1}",
- "level": v,
- }
- )
- )
- db.session.commit()
-
-
-def initialize_random_database():
- initialize_default_players()
- player_count = 80
- total_teams = 5
- players_per_team = 5
- for i in range(player_count):
- if i < total_teams * players_per_team:
- team = floor(i / players_per_team) + 1
- else:
- team = 0
- args = {
- "id": 3 + i,
- "first_name": get_first_name(),
- "last_name": get_last_name(),
- "team": team,
- "status": bool(getrandbits(1)),
- "level": randint(
- app.config["MIN_PLAYER_LEVEL"], app.config["MAX_PLAYER_LEVEL"]
- ),
- }
- db.session.add(Player(**args))
- db.session.commit()
diff --git a/docs/game_screen.png b/docs/game_screen.png
deleted file mode 100644
index e523376..0000000
--- a/docs/game_screen.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8676d7db6fd00871968394395e9329414fd138b7cd16031d3277a94f0d5f06d5
-size 173225
diff --git a/docs/player_screen.png b/docs/player_screen.png
deleted file mode 100644
index a63f4dc..0000000
--- a/docs/player_screen.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:7d52f2e4794814d29097d728f8869e70ca185c0add406811aec550ab8f8d3197
-size 166498
diff --git a/index.css b/index.css
new file mode 100644
index 0000000..50a97c3
--- /dev/null
+++ b/index.css
@@ -0,0 +1,10 @@
+.button-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ grid-column-gap: 30px;
+ grid-row-gap: 30px;
+}
+.button-grid button {
+ padding: 10px;
+ text-align: center;
+} \ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..5ac86c8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
+ <title>Match Up!</title>
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
+ <link rel="stylesheet" href="index.css">
+ </head>
+ <body>
+ <main class="container">
+ <nav>
+ <ul>
+ <li><strong>Match Up</strong></li>
+ </ul>
+ <ul>
+ <li><a href="/players">Players</a></li>
+ <li><a href="#">Guests</a></li>
+ <li><a href="#">Admin</a></li>
+ </ul>
+ </nav>
+ <div>
+ <h2>Regulars</h2>
+ <div id="player_list" class="button-grid"></div>
+ </div>
+ </main>
+ <script src="player.mjs" type="module"></script>
+ <script src="index.js" type="module"></script>
+ </body>
+</html> \ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..97c65d6
--- /dev/null
+++ b/index.js
@@ -0,0 +1,11 @@
+import { generatePlayers } from "./player.mjs";
+
+// Player handling
+let players = generatePlayers();
+let renderedHTML = players.map((p) => {return p.render();}).join('');
+document.getElementById('player_list').innerHTML = renderedHTML;
+function togglePlayer(id) {
+ console.log(players);
+ let player = players.find((p) => p.id === id);
+ document.getElementById('player_' + id).outerHTML = player.toggle();
+} \ No newline at end of file
diff --git a/migrations/README b/migrations/README
deleted file mode 100644
index 0e04844..0000000
--- a/migrations/README
+++ /dev/null
@@ -1 +0,0 @@
-Single-database configuration for Flask.
diff --git a/migrations/alembic.ini b/migrations/alembic.ini
deleted file mode 100644
index ec9d45c..0000000
--- a/migrations/alembic.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-# A generic, single database configuration.
-
-[alembic]
-# template used to generate migration files
-# file_template = %%(rev)s_%%(slug)s
-
-# set to 'true' to run the environment during
-# the 'revision' command, regardless of autogenerate
-# revision_environment = false
-
-
-# Logging configuration
-[loggers]
-keys = root,sqlalchemy,alembic,flask_migrate
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-qualname =
-
-[logger_sqlalchemy]
-level = WARN
-handlers =
-qualname = sqlalchemy.engine
-
-[logger_alembic]
-level = INFO
-handlers =
-qualname = alembic
-
-[logger_flask_migrate]
-level = INFO
-handlers =
-qualname = flask_migrate
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = NOTSET
-formatter = generic
-
-[formatter_generic]
-format = %(levelname)-5.5s [%(name)s] %(message)s
-datefmt = %H:%M:%S
diff --git a/migrations/env.py b/migrations/env.py
deleted file mode 100644
index d004741..0000000
--- a/migrations/env.py
+++ /dev/null
@@ -1,108 +0,0 @@
-import logging
-from logging.config import fileConfig
-
-from flask import current_app
-
-from alembic import context
-
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-fileConfig(config.config_file_name)
-logger = logging.getLogger("alembic.env")
-
-
-def get_engine():
- try:
- # this works with Flask-SQLAlchemy<3 and Alchemical
- return current_app.extensions["migrate"].db.get_engine()
- except (TypeError, AttributeError):
- # this works with Flask-SQLAlchemy>=3
- return current_app.extensions["migrate"].db.engine
-
-
-def get_engine_url():
- try:
- return get_engine().url.render_as_string(hide_password=False).replace("%", "%%")
- except AttributeError:
- return str(get_engine().url).replace("%", "%%")
-
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-config.set_main_option("sqlalchemy.url", get_engine_url())
-target_db = current_app.extensions["migrate"].db
-
-# other values from the config, defined by the needs of env.py,
-# can be acquired:
-# my_important_option = config.get_main_option("my_important_option")
-# ... etc.
-
-
-def get_metadata():
- if hasattr(target_db, "metadatas"):
- return target_db.metadatas[None]
- return target_db.metadata
-
-
-def run_migrations_offline():
- """Run migrations in 'offline' mode.
-
- This configures the context with just a URL
- and not an Engine, though an Engine is acceptable
- here as well. By skipping the Engine creation
- we don't even need a DBAPI to be available.
-
- Calls to context.execute() here emit the given string to the
- script output.
-
- """
- url = config.get_main_option("sqlalchemy.url")
- context.configure(url=url, target_metadata=get_metadata(), literal_binds=True)
-
- with context.begin_transaction():
- context.run_migrations()
-
-
-def run_migrations_online():
- """Run migrations in 'online' mode.
-
- In this scenario we need to create an Engine
- and associate a connection with the context.
-
- """
-
- # this callback is used to prevent an auto-migration from being generated
- # when there are no changes to the schema
- # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
- def process_revision_directives(context, revision, directives):
- if getattr(config.cmd_opts, "autogenerate", False):
- script = directives[0]
- if script.upgrade_ops.is_empty():
- directives[:] = []
- logger.info("No changes in schema detected.")
-
- conf_args = current_app.extensions["migrate"].configure_args
- if conf_args.get("process_revision_directives") is None:
- conf_args["process_revision_directives"] = process_revision_directives
-
- connectable = get_engine()
-
- with connectable.connect() as connection:
- context.configure(
- connection=connection, target_metadata=get_metadata(), **conf_args
- )
-
- with context.begin_transaction():
- context.run_migrations()
-
-
-if context.is_offline_mode():
- run_migrations_offline()
-else:
- run_migrations_online()
diff --git a/migrations/script.py.mako b/migrations/script.py.mako
deleted file mode 100644
index 2c01563..0000000
--- a/migrations/script.py.mako
+++ /dev/null
@@ -1,24 +0,0 @@
-"""${message}
-
-Revision ID: ${up_revision}
-Revises: ${down_revision | comma,n}
-Create Date: ${create_date}
-
-"""
-from alembic import op
-import sqlalchemy as sa
-${imports if imports else ""}
-
-# revision identifiers, used by Alembic.
-revision = ${repr(up_revision)}
-down_revision = ${repr(down_revision)}
-branch_labels = ${repr(branch_labels)}
-depends_on = ${repr(depends_on)}
-
-
-def upgrade():
- ${upgrades if upgrades else "pass"}
-
-
-def downgrade():
- ${downgrades if downgrades else "pass"}
diff --git a/migrations/versions/c08245a9c93f_.py b/migrations/versions/c08245a9c93f_.py
deleted file mode 100644
index 48b115b..0000000
--- a/migrations/versions/c08245a9c93f_.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""empty message
-
-Revision ID: c08245a9c93f
-Revises: d158cf8f4950
-Create Date: 2025-07-12 21:27:46.932139
-
-"""
-
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = "c08245a9c93f"
-down_revision = "d158cf8f4950"
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.create_table(
- "court",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("status", sa.Boolean(), nullable=False),
- sa.PrimaryKeyConstraint("id"),
- )
- op.create_table(
- "player",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("first_name", sa.String(length=64), nullable=False),
- sa.Column("last_name", sa.String(length=64), nullable=False),
- sa.Column("team", sa.Integer(), nullable=False),
- sa.Column("status", sa.Boolean(), nullable=False),
- sa.Column("level", sa.Integer(), nullable=False),
- sa.PrimaryKeyConstraint("id"),
- )
- with op.batch_alter_table("player", schema=None) as batch_op:
- batch_op.create_index(
- batch_op.f("ix_player_first_name"), ["first_name"], unique=False
- )
- batch_op.create_index(
- batch_op.f("ix_player_last_name"), ["last_name"], unique=False
- )
- batch_op.create_index(batch_op.f("ix_player_team"), ["team"], unique=False)
-
- op.create_table(
- "game",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("round_id", sa.Integer(), nullable=False),
- sa.Column("court_id", sa.Integer(), nullable=False),
- sa.Column("player_1", sa.Integer(), nullable=False),
- sa.Column("player_2", sa.Integer(), nullable=False),
- sa.Column("player_3", sa.Integer(), nullable=False),
- sa.Column("player_4", sa.Integer(), nullable=False),
- sa.Column("date", sa.DateTime(), nullable=False),
- sa.ForeignKeyConstraint(
- ["court_id"],
- ["court.id"],
- ),
- sa.ForeignKeyConstraint(
- ["player_1"],
- ["player.id"],
- ),
- sa.ForeignKeyConstraint(
- ["player_2"],
- ["player.id"],
- ),
- sa.ForeignKeyConstraint(
- ["player_3"],
- ["player.id"],
- ),
- sa.ForeignKeyConstraint(
- ["player_4"],
- ["player.id"],
- ),
- sa.PrimaryKeyConstraint("id"),
- )
- with op.batch_alter_table("game", schema=None) as batch_op:
- batch_op.create_index(
- batch_op.f("ix_game_court_id"), ["court_id"], unique=False
- )
- batch_op.create_index(batch_op.f("ix_game_date"), ["date"], unique=False)
- batch_op.create_index(
- batch_op.f("ix_game_player_1"), ["player_1"], unique=False
- )
- batch_op.create_index(
- batch_op.f("ix_game_player_2"), ["player_2"], unique=False
- )
- batch_op.create_index(
- batch_op.f("ix_game_player_3"), ["player_3"], unique=False
- )
- batch_op.create_index(
- batch_op.f("ix_game_player_4"), ["player_4"], unique=False
- )
- batch_op.create_index(
- batch_op.f("ix_game_round_id"), ["round_id"], unique=False
- )
-
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table("game", schema=None) as batch_op:
- batch_op.drop_index(batch_op.f("ix_game_round_id"))
- batch_op.drop_index(batch_op.f("ix_game_player_4"))
- batch_op.drop_index(batch_op.f("ix_game_player_3"))
- batch_op.drop_index(batch_op.f("ix_game_player_2"))
- batch_op.drop_index(batch_op.f("ix_game_player_1"))
- batch_op.drop_index(batch_op.f("ix_game_date"))
- batch_op.drop_index(batch_op.f("ix_game_court_id"))
-
- op.drop_table("game")
- with op.batch_alter_table("player", schema=None) as batch_op:
- batch_op.drop_index(batch_op.f("ix_player_team"))
- batch_op.drop_index(batch_op.f("ix_player_last_name"))
- batch_op.drop_index(batch_op.f("ix_player_first_name"))
-
- op.drop_table("player")
- op.drop_table("court")
- # ### end Alembic commands ###
diff --git a/migrations/versions/d158cf8f4950_.py b/migrations/versions/d158cf8f4950_.py
deleted file mode 100644
index ba0ef5b..0000000
--- a/migrations/versions/d158cf8f4950_.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""empty message
-
-Revision ID: d158cf8f4950
-Revises:
-Create Date: 2025-07-06 15:31:24.124657
-
-"""
-
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = "d158cf8f4950"
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.create_table(
- "user",
- sa.Column("id", sa.Integer(), nullable=False),
- sa.Column("username", sa.String(length=64), nullable=False),
- sa.Column("password_hash", sa.String(length=256), nullable=True),
- sa.PrimaryKeyConstraint("id"),
- )
- with op.batch_alter_table("user", schema=None) as batch_op:
- batch_op.create_index(batch_op.f("ix_user_username"), ["username"], unique=True)
-
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table("user", schema=None) as batch_op:
- batch_op.drop_index(batch_op.f("ix_user_username"))
-
- op.drop_table("user")
- # ### end Alembic commands ###
diff --git a/player.mjs b/player.mjs
new file mode 100644
index 0000000..a0e2535
--- /dev/null
+++ b/player.mjs
@@ -0,0 +1,37 @@
+export class Player {
+ constructor(id, name, level, status) {
+ this.id = id;
+ this.name = name;
+ this.level = typeof level !== 'undefined' ? level : 1;
+ this.status = typeof status !== 'undefined' ? status : false;
+ }
+ toggle() {
+ this.status = !this.status;
+ return this.render();
+ }
+ print() {
+ let status = this.status ? "active" : "inactive";
+ console.log(this.name + " has a level of " + this.level + " and is currently " + status);
+ }
+ render() {
+ let type = this.status ? "" : 'class="outline secondary"';
+ return `<button id=${"player_" + this.id} ${type} onclick='togglePlayer(${this.id})'>${this.name}</button>`;
+ }
+}
+
+function randomLevel() {
+ const max_level = 10;
+ return Math.floor(Math.random() * max_level + 1);
+}
+
+function randomStatus() {
+ return Math.random() < 0.5;
+}
+
+export function generatePlayers() {
+ let players = [];
+ for (let i = 0; i < 71; i++) {
+ players.push(new Player(i, "Player " + i, randomLevel(), randomStatus()));
+ }
+ return players;
+} \ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 9e334a7..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[project]
-name = "match-up"
-version = "3.1"
-description = "Match up application for badminton pairings"
-readme = "README.md"
-requires-python = ">=3.12"
-dependencies = [
- "flask>=3.1.1",
- "flask-migrate>=4.1.0",
- "flask-simplelogin>=0.2.0",
- "flask-sqlalchemy>=3.1.1",
- "flask-wtf>=1.2.2",
- "gunicorn>=23.0.0",
- "names>=0.3.0",
- "pandas>=2.3.1",
- "psycopg2-binary>=2.9.10",
-]
diff --git a/uv.lock b/uv.lock
deleted file mode 100644
index 9bfbf23..0000000
--- a/uv.lock
+++ /dev/null
@@ -1,487 +0,0 @@
-version = 1
-revision = 2
-requires-python = ">=3.12"
-
-[[package]]
-name = "alembic"
-version = "1.16.4"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "mako" },
- { name = "sqlalchemy" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/83/52/72e791b75c6b1efa803e491f7cbab78e963695e76d4ada05385252927e76/alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2", size = 1968161, upload-time = "2025-07-10T16:17:20.192Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/62/96b5217b742805236614f05904541000f55422a6060a90d7fd4ce26c172d/alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d", size = 247026, upload-time = "2025-07-10T16:17:21.845Z" },
-]
-
-[[package]]
-name = "blinker"
-version = "1.9.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" },
-]
-
-[[package]]
-name = "click"
-version = "8.2.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "colorama", marker = "sys_platform == 'win32'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" },
-]
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
-]
-
-[[package]]
-name = "flask"
-version = "3.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "blinker" },
- { name = "click" },
- { name = "itsdangerous" },
- { name = "jinja2" },
- { name = "markupsafe" },
- { name = "werkzeug" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c0/de/e47735752347f4128bcf354e0da07ef311a78244eba9e3dc1d4a5ab21a98/flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e", size = 753440, upload-time = "2025-05-13T15:01:17.447Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3d/68/9d4508e893976286d2ead7f8f571314af6c2037af34853a30fd769c02e9d/flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c", size = 103305, upload-time = "2025-05-13T15:01:15.591Z" },
-]
-
-[[package]]
-name = "flask-migrate"
-version = "4.1.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "alembic" },
- { name = "flask" },
- { name = "flask-sqlalchemy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/47c7b3c93855ceffc2eabfa271782332942443321a07de193e4198f920cf/flask_migrate-4.1.0.tar.gz", hash = "sha256:1a336b06eb2c3ace005f5f2ded8641d534c18798d64061f6ff11f79e1434126d", size = 21965, upload-time = "2025-01-10T18:51:11.848Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/c4/3f329b23d769fe7628a5fc57ad36956f1fb7132cf8837be6da762b197327/Flask_Migrate-4.1.0-py3-none-any.whl", hash = "sha256:24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d", size = 21237, upload-time = "2025-01-10T18:51:09.527Z" },
-]
-
-[[package]]
-name = "flask-simplelogin"
-version = "0.2.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "flask" },
- { name = "flask-wtf" },
- { name = "wtforms" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b4/f8/31d85485099f6b5ad214ec3f89c232d3605a77fefce7796e2a2e7b14e0e3/flask_simplelogin-0.2.0.tar.gz", hash = "sha256:bd8493dcb9e1a3657eff919cf95a8887bde4a861a579a55dd5ea9dd717ef983a", size = 7040, upload-time = "2024-10-30T18:21:03.015Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/96/dc/645e9fd39cd24a3d4c2279cd99faaa0bb50001184f458cf8a19eccec524f/flask_simplelogin-0.2.0-py3-none-any.whl", hash = "sha256:084f96bdd41a886b5bf07f23a1fa0479d8b224247967187a23190ee860909cb0", size = 7319, upload-time = "2024-10-30T18:21:01.544Z" },
-]
-
-[[package]]
-name = "flask-sqlalchemy"
-version = "3.1.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "flask" },
- { name = "sqlalchemy" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/91/53/b0a9fcc1b1297f51e68b69ed3b7c3c40d8c45be1391d77ae198712914392/flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312", size = 81899, upload-time = "2023-09-11T21:42:36.147Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1d/6a/89963a5c6ecf166e8be29e0d1bf6806051ee8fe6c82e232842e3aeac9204/flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", size = 25125, upload-time = "2023-09-11T21:42:34.514Z" },
-]
-
-[[package]]
-name = "flask-wtf"
-version = "1.2.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "flask" },
- { name = "itsdangerous" },
- { name = "wtforms" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/80/9b/f1cd6e41bbf874f3436368f2c7ee3216c1e82d666ff90d1d800e20eb1317/flask_wtf-1.2.2.tar.gz", hash = "sha256:79d2ee1e436cf570bccb7d916533fa18757a2f18c290accffab1b9a0b684666b", size = 42641, upload-time = "2024-10-24T07:18:58.555Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/dc/19/354449145fbebb65e7c621235b6ad69bebcfaec2142481f044d0ddc5b5c5/flask_wtf-1.2.2-py3-none-any.whl", hash = "sha256:e93160c5c5b6b571cf99300b6e01b72f9a101027cab1579901f8b10c5daf0b70", size = 12779, upload-time = "2024-10-24T07:18:56.976Z" },
-]
-
-[[package]]
-name = "greenlet"
-version = "3.2.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365", size = 185752, upload-time = "2025-06-05T16:16:09.955Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d", size = 271992, upload-time = "2025-06-05T16:11:23.467Z" },
- { url = "https://files.pythonhosted.org/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b", size = 638820, upload-time = "2025-06-05T16:38:52.882Z" },
- { url = "https://files.pythonhosted.org/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d", size = 653046, upload-time = "2025-06-05T16:41:36.343Z" },
- { url = "https://files.pythonhosted.org/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264", size = 647701, upload-time = "2025-06-05T16:48:19.604Z" },
- { url = "https://files.pythonhosted.org/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688", size = 649747, upload-time = "2025-06-05T16:13:04.628Z" },
- { url = "https://files.pythonhosted.org/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb", size = 605461, upload-time = "2025-06-05T16:12:50.792Z" },
- { url = "https://files.pythonhosted.org/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c", size = 1121190, upload-time = "2025-06-05T16:36:48.59Z" },
- { url = "https://files.pythonhosted.org/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163", size = 1149055, upload-time = "2025-06-05T16:12:40.457Z" },
- { url = "https://files.pythonhosted.org/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849", size = 297817, upload-time = "2025-06-05T16:29:49.244Z" },
- { url = "https://files.pythonhosted.org/packages/b1/cf/f5c0b23309070ae93de75c90d29300751a5aacefc0a3ed1b1d8edb28f08b/greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad", size = 270732, upload-time = "2025-06-05T16:10:08.26Z" },
- { url = "https://files.pythonhosted.org/packages/48/ae/91a957ba60482d3fecf9be49bc3948f341d706b52ddb9d83a70d42abd498/greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef", size = 639033, upload-time = "2025-06-05T16:38:53.983Z" },
- { url = "https://files.pythonhosted.org/packages/6f/df/20ffa66dd5a7a7beffa6451bdb7400d66251374ab40b99981478c69a67a8/greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3", size = 652999, upload-time = "2025-06-05T16:41:37.89Z" },
- { url = "https://files.pythonhosted.org/packages/51/b4/ebb2c8cb41e521f1d72bf0465f2f9a2fd803f674a88db228887e6847077e/greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95", size = 647368, upload-time = "2025-06-05T16:48:21.467Z" },
- { url = "https://files.pythonhosted.org/packages/8e/6a/1e1b5aa10dced4ae876a322155705257748108b7fd2e4fae3f2a091fe81a/greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb", size = 650037, upload-time = "2025-06-05T16:13:06.402Z" },
- { url = "https://files.pythonhosted.org/packages/26/f2/ad51331a157c7015c675702e2d5230c243695c788f8f75feba1af32b3617/greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b", size = 608402, upload-time = "2025-06-05T16:12:51.91Z" },
- { url = "https://files.pythonhosted.org/packages/26/bc/862bd2083e6b3aff23300900a956f4ea9a4059de337f5c8734346b9b34fc/greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0", size = 1119577, upload-time = "2025-06-05T16:36:49.787Z" },
- { url = "https://files.pythonhosted.org/packages/86/94/1fc0cc068cfde885170e01de40a619b00eaa8f2916bf3541744730ffb4c3/greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36", size = 1147121, upload-time = "2025-06-05T16:12:42.527Z" },
- { url = "https://files.pythonhosted.org/packages/27/1a/199f9587e8cb08a0658f9c30f3799244307614148ffe8b1e3aa22f324dea/greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3", size = 297603, upload-time = "2025-06-05T16:20:12.651Z" },
- { url = "https://files.pythonhosted.org/packages/d8/ca/accd7aa5280eb92b70ed9e8f7fd79dc50a2c21d8c73b9a0856f5b564e222/greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86", size = 271479, upload-time = "2025-06-05T16:10:47.525Z" },
- { url = "https://files.pythonhosted.org/packages/55/71/01ed9895d9eb49223280ecc98a557585edfa56b3d0e965b9fa9f7f06b6d9/greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97", size = 683952, upload-time = "2025-06-05T16:38:55.125Z" },
- { url = "https://files.pythonhosted.org/packages/ea/61/638c4bdf460c3c678a0a1ef4c200f347dff80719597e53b5edb2fb27ab54/greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728", size = 696917, upload-time = "2025-06-05T16:41:38.959Z" },
- { url = "https://files.pythonhosted.org/packages/22/cc/0bd1a7eb759d1f3e3cc2d1bc0f0b487ad3cc9f34d74da4b80f226fde4ec3/greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a", size = 692443, upload-time = "2025-06-05T16:48:23.113Z" },
- { url = "https://files.pythonhosted.org/packages/67/10/b2a4b63d3f08362662e89c103f7fe28894a51ae0bc890fabf37d1d780e52/greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892", size = 692995, upload-time = "2025-06-05T16:13:07.972Z" },
- { url = "https://files.pythonhosted.org/packages/5a/c6/ad82f148a4e3ce9564056453a71529732baf5448ad53fc323e37efe34f66/greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141", size = 655320, upload-time = "2025-06-05T16:12:53.453Z" },
- { url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236, upload-time = "2025-06-05T16:15:20.111Z" },
-]
-
-[[package]]
-name = "gunicorn"
-version = "23.0.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "packaging" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" },
-]
-
-[[package]]
-name = "itsdangerous"
-version = "2.2.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" },
-]
-
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
-]
-
-[[package]]
-name = "mako"
-version = "1.3.10"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9e/38/bd5b78a920a64d708fe6bc8e0a2c075e1389d53bef8413725c63ba041535/mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28", size = 392474, upload-time = "2025-04-10T12:44:31.16Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/87/fb/99f81ac72ae23375f22b7afdb7642aba97c00a713c217124420147681a2f/mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59", size = 78509, upload-time = "2025-04-10T12:50:53.297Z" },
-]
-
-[[package]]
-name = "markupsafe"
-version = "3.0.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537, upload-time = "2024-10-18T15:21:54.129Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274, upload-time = "2024-10-18T15:21:13.777Z" },
- { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348, upload-time = "2024-10-18T15:21:14.822Z" },
- { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149, upload-time = "2024-10-18T15:21:15.642Z" },
- { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118, upload-time = "2024-10-18T15:21:17.133Z" },
- { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993, upload-time = "2024-10-18T15:21:18.064Z" },
- { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178, upload-time = "2024-10-18T15:21:18.859Z" },
- { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319, upload-time = "2024-10-18T15:21:19.671Z" },
- { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352, upload-time = "2024-10-18T15:21:20.971Z" },
- { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097, upload-time = "2024-10-18T15:21:22.646Z" },
- { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601, upload-time = "2024-10-18T15:21:23.499Z" },
- { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274, upload-time = "2024-10-18T15:21:24.577Z" },
- { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352, upload-time = "2024-10-18T15:21:25.382Z" },
- { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122, upload-time = "2024-10-18T15:21:26.199Z" },
- { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085, upload-time = "2024-10-18T15:21:27.029Z" },
- { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978, upload-time = "2024-10-18T15:21:27.846Z" },
- { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208, upload-time = "2024-10-18T15:21:28.744Z" },
- { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357, upload-time = "2024-10-18T15:21:29.545Z" },
- { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344, upload-time = "2024-10-18T15:21:30.366Z" },
- { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101, upload-time = "2024-10-18T15:21:31.207Z" },
- { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603, upload-time = "2024-10-18T15:21:32.032Z" },
- { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510, upload-time = "2024-10-18T15:21:33.625Z" },
- { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486, upload-time = "2024-10-18T15:21:34.611Z" },
- { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480, upload-time = "2024-10-18T15:21:35.398Z" },
- { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914, upload-time = "2024-10-18T15:21:36.231Z" },
- { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796, upload-time = "2024-10-18T15:21:37.073Z" },
- { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473, upload-time = "2024-10-18T15:21:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114, upload-time = "2024-10-18T15:21:39.799Z" },
- { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098, upload-time = "2024-10-18T15:21:40.813Z" },
- { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208, upload-time = "2024-10-18T15:21:41.814Z" },
- { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" },
-]
-
-[[package]]
-name = "match-up"
-version = "3.1"
-source = { virtual = "." }
-dependencies = [
- { name = "flask" },
- { name = "flask-migrate" },
- { name = "flask-simplelogin" },
- { name = "flask-sqlalchemy" },
- { name = "flask-wtf" },
- { name = "gunicorn" },
- { name = "names" },
- { name = "pandas" },
- { name = "psycopg2-binary" },
-]
-
-[package.metadata]
-requires-dist = [
- { name = "flask", specifier = ">=3.1.1" },
- { name = "flask-migrate", specifier = ">=4.1.0" },
- { name = "flask-simplelogin", specifier = ">=0.2.0" },
- { name = "flask-sqlalchemy", specifier = ">=3.1.1" },
- { name = "flask-wtf", specifier = ">=1.2.2" },
- { name = "gunicorn", specifier = ">=23.0.0" },
- { name = "names", specifier = ">=0.3.0" },
- { name = "pandas", specifier = ">=2.3.1" },
- { name = "psycopg2-binary", specifier = ">=2.9.10" },
-]
-
-[[package]]
-name = "names"
-version = "0.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/44/4e/f9cb7ef2df0250f4ba3334fbdabaa94f9c88097089763d8e85ada8092f84/names-0.3.0.tar.gz", hash = "sha256:726e46254f2ed03f1ffb5d941dae3bc67c35123941c29becd02d48d0caa2a671", size = 789099, upload-time = "2013-05-14T14:55:49.073Z" }
-
-[[package]]
-name = "numpy"
-version = "2.3.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664, upload-time = "2025-06-21T12:15:30.845Z" },
- { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078, upload-time = "2025-06-21T12:15:52.23Z" },
- { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554, upload-time = "2025-06-21T12:16:01.434Z" },
- { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560, upload-time = "2025-06-21T12:16:11.895Z" },
- { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638, upload-time = "2025-06-21T12:16:32.611Z" },
- { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729, upload-time = "2025-06-21T12:16:57.439Z" },
- { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330, upload-time = "2025-06-21T12:17:20.638Z" },
- { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734, upload-time = "2025-06-21T12:17:47.938Z" },
- { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411, upload-time = "2025-06-21T12:17:58.475Z" },
- { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973, upload-time = "2025-06-21T12:18:17.601Z" },
- { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491, upload-time = "2025-06-21T12:18:33.585Z" },
- { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" },
- { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" },
- { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" },
- { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" },
- { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" },
- { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" },
- { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" },
- { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" },
- { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" },
- { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" },
- { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" },
- { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" },
- { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" },
- { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" },
- { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" },
- { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" },
- { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" },
- { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" },
- { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" },
- { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" },
- { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" },
- { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" },
-]
-
-[[package]]
-name = "packaging"
-version = "25.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
-]
-
-[[package]]
-name = "pandas"
-version = "2.3.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "numpy" },
- { name = "python-dateutil" },
- { name = "pytz" },
- { name = "tzdata" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/d1/6f/75aa71f8a14267117adeeed5d21b204770189c0a0025acbdc03c337b28fc/pandas-2.3.1.tar.gz", hash = "sha256:0a95b9ac964fe83ce317827f80304d37388ea77616b1425f0ae41c9d2d0d7bb2", size = 4487493, upload-time = "2025-07-07T19:20:04.079Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/46/de/b8445e0f5d217a99fe0eeb2f4988070908979bec3587c0633e5428ab596c/pandas-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:689968e841136f9e542020698ee1c4fbe9caa2ed2213ae2388dc7b81721510d3", size = 11588172, upload-time = "2025-07-07T19:18:52.054Z" },
- { url = "https://files.pythonhosted.org/packages/1e/e0/801cdb3564e65a5ac041ab99ea6f1d802a6c325bb6e58c79c06a3f1cd010/pandas-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:025e92411c16cbe5bb2a4abc99732a6b132f439b8aab23a59fa593eb00704232", size = 10717365, upload-time = "2025-07-07T19:18:54.785Z" },
- { url = "https://files.pythonhosted.org/packages/51/a5/c76a8311833c24ae61a376dbf360eb1b1c9247a5d9c1e8b356563b31b80c/pandas-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7ff55f31c4fcb3e316e8f7fa194566b286d6ac430afec0d461163312c5841e", size = 11280411, upload-time = "2025-07-07T19:18:57.045Z" },
- { url = "https://files.pythonhosted.org/packages/da/01/e383018feba0a1ead6cf5fe8728e5d767fee02f06a3d800e82c489e5daaf/pandas-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dcb79bf373a47d2a40cf7232928eb7540155abbc460925c2c96d2d30b006eb4", size = 11988013, upload-time = "2025-07-07T19:18:59.771Z" },
- { url = "https://files.pythonhosted.org/packages/5b/14/cec7760d7c9507f11c97d64f29022e12a6cc4fc03ac694535e89f88ad2ec/pandas-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56a342b231e8862c96bdb6ab97170e203ce511f4d0429589c8ede1ee8ece48b8", size = 12767210, upload-time = "2025-07-07T19:19:02.944Z" },
- { url = "https://files.pythonhosted.org/packages/50/b9/6e2d2c6728ed29fb3d4d4d302504fb66f1a543e37eb2e43f352a86365cdf/pandas-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ca7ed14832bce68baef331f4d7f294411bed8efd032f8109d690df45e00c4679", size = 13440571, upload-time = "2025-07-07T19:19:06.82Z" },
- { url = "https://files.pythonhosted.org/packages/80/a5/3a92893e7399a691bad7664d977cb5e7c81cf666c81f89ea76ba2bff483d/pandas-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ac942bfd0aca577bef61f2bc8da8147c4ef6879965ef883d8e8d5d2dc3e744b8", size = 10987601, upload-time = "2025-07-07T19:19:09.589Z" },
- { url = "https://files.pythonhosted.org/packages/32/ed/ff0a67a2c5505e1854e6715586ac6693dd860fbf52ef9f81edee200266e7/pandas-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9026bd4a80108fac2239294a15ef9003c4ee191a0f64b90f170b40cfb7cf2d22", size = 11531393, upload-time = "2025-07-07T19:19:12.245Z" },
- { url = "https://files.pythonhosted.org/packages/c7/db/d8f24a7cc9fb0972adab0cc80b6817e8bef888cfd0024eeb5a21c0bb5c4a/pandas-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6de8547d4fdb12421e2d047a2c446c623ff4c11f47fddb6b9169eb98ffba485a", size = 10668750, upload-time = "2025-07-07T19:19:14.612Z" },
- { url = "https://files.pythonhosted.org/packages/0f/b0/80f6ec783313f1e2356b28b4fd8d2148c378370045da918c73145e6aab50/pandas-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782647ddc63c83133b2506912cc6b108140a38a37292102aaa19c81c83db2928", size = 11342004, upload-time = "2025-07-07T19:19:16.857Z" },
- { url = "https://files.pythonhosted.org/packages/e9/e2/20a317688435470872885e7fc8f95109ae9683dec7c50be29b56911515a5/pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9", size = 12050869, upload-time = "2025-07-07T19:19:19.265Z" },
- { url = "https://files.pythonhosted.org/packages/55/79/20d746b0a96c67203a5bee5fb4e00ac49c3e8009a39e1f78de264ecc5729/pandas-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e5635178b387bd2ba4ac040f82bc2ef6e6b500483975c4ebacd34bec945fda12", size = 12750218, upload-time = "2025-07-07T19:19:21.547Z" },
- { url = "https://files.pythonhosted.org/packages/7c/0f/145c8b41e48dbf03dd18fdd7f24f8ba95b8254a97a3379048378f33e7838/pandas-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f3bf5ec947526106399a9e1d26d40ee2b259c66422efdf4de63c848492d91bb", size = 13416763, upload-time = "2025-07-07T19:19:23.939Z" },
- { url = "https://files.pythonhosted.org/packages/b2/c0/54415af59db5cdd86a3d3bf79863e8cc3fa9ed265f0745254061ac09d5f2/pandas-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c78cf43c8fde236342a1cb2c34bcff89564a7bfed7e474ed2fffa6aed03a956", size = 10987482, upload-time = "2025-07-07T19:19:42.699Z" },
- { url = "https://files.pythonhosted.org/packages/48/64/2fd2e400073a1230e13b8cd604c9bc95d9e3b962e5d44088ead2e8f0cfec/pandas-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8dfc17328e8da77be3cf9f47509e5637ba8f137148ed0e9b5241e1baf526e20a", size = 12029159, upload-time = "2025-07-07T19:19:26.362Z" },
- { url = "https://files.pythonhosted.org/packages/d8/0a/d84fd79b0293b7ef88c760d7dca69828d867c89b6d9bc52d6a27e4d87316/pandas-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ec6c851509364c59a5344458ab935e6451b31b818be467eb24b0fe89bd05b6b9", size = 11393287, upload-time = "2025-07-07T19:19:29.157Z" },
- { url = "https://files.pythonhosted.org/packages/50/ae/ff885d2b6e88f3c7520bb74ba319268b42f05d7e583b5dded9837da2723f/pandas-2.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:911580460fc4884d9b05254b38a6bfadddfcc6aaef856fb5859e7ca202e45275", size = 11309381, upload-time = "2025-07-07T19:19:31.436Z" },
- { url = "https://files.pythonhosted.org/packages/85/86/1fa345fc17caf5d7780d2699985c03dbe186c68fee00b526813939062bb0/pandas-2.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f4d6feeba91744872a600e6edbbd5b033005b431d5ae8379abee5bcfa479fab", size = 11883998, upload-time = "2025-07-07T19:19:34.267Z" },
- { url = "https://files.pythonhosted.org/packages/81/aa/e58541a49b5e6310d89474333e994ee57fea97c8aaa8fc7f00b873059bbf/pandas-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe37e757f462d31a9cd7580236a82f353f5713a80e059a29753cf938c6775d96", size = 12704705, upload-time = "2025-07-07T19:19:36.856Z" },
- { url = "https://files.pythonhosted.org/packages/d5/f9/07086f5b0f2a19872554abeea7658200824f5835c58a106fa8f2ae96a46c/pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444", size = 13189044, upload-time = "2025-07-07T19:19:39.999Z" },
-]
-
-[[package]]
-name = "psycopg2-binary"
-version = "2.9.10"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764, upload-time = "2024-10-16T11:24:58.126Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771, upload-time = "2024-10-16T11:20:35.234Z" },
- { url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336, upload-time = "2024-10-16T11:20:38.742Z" },
- { url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637, upload-time = "2024-10-16T11:20:42.145Z" },
- { url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097, upload-time = "2024-10-16T11:20:46.185Z" },
- { url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776, upload-time = "2024-10-16T11:20:50.879Z" },
- { url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968, upload-time = "2024-10-16T11:20:56.819Z" },
- { url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334, upload-time = "2024-10-16T11:21:02.411Z" },
- { url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722, upload-time = "2024-10-16T11:21:09.01Z" },
- { url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132, upload-time = "2024-10-16T11:21:16.339Z" },
- { url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312, upload-time = "2024-10-16T11:21:25.584Z" },
- { url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191, upload-time = "2024-10-16T11:21:29.912Z" },
- { url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031, upload-time = "2024-10-16T11:21:34.211Z" },
- { url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699, upload-time = "2024-10-16T11:21:42.841Z" },
- { url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245, upload-time = "2024-10-16T11:21:51.989Z" },
- { url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631, upload-time = "2024-10-16T11:21:57.584Z" },
- { url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140, upload-time = "2024-10-16T11:22:02.005Z" },
- { url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762, upload-time = "2024-10-16T11:22:06.412Z" },
- { url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967, upload-time = "2024-10-16T11:22:11.583Z" },
- { url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326, upload-time = "2024-10-16T11:22:16.406Z" },
- { url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712, upload-time = "2024-10-16T11:22:21.366Z" },
- { url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155, upload-time = "2024-10-16T11:22:25.684Z" },
- { url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356, upload-time = "2024-10-16T11:22:30.562Z" },
- { url = "https://files.pythonhosted.org/packages/08/50/d13ea0a054189ae1bc21af1d85b6f8bb9bbc5572991055d70ad9006fe2d6/psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", size = 2569224, upload-time = "2025-01-04T20:09:19.234Z" },
-]
-
-[[package]]
-name = "python-dateutil"
-version = "2.9.0.post0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "six" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
-]
-
-[[package]]
-name = "pytz"
-version = "2025.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
-]
-
-[[package]]
-name = "six"
-version = "1.17.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
-]
-
-[[package]]
-name = "sqlalchemy"
-version = "2.0.41"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" },
- { name = "typing-extensions" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/63/66/45b165c595ec89aa7dcc2c1cd222ab269bc753f1fc7a1e68f8481bd957bf/sqlalchemy-2.0.41.tar.gz", hash = "sha256:edba70118c4be3c2b1f90754d308d0b79c6fe2c0fdc52d8ddf603916f83f4db9", size = 9689424, upload-time = "2025-05-14T17:10:32.339Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/3e/2a/f1f4e068b371154740dd10fb81afb5240d5af4aa0087b88d8b308b5429c2/sqlalchemy-2.0.41-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:81f413674d85cfd0dfcd6512e10e0f33c19c21860342a4890c3a2b59479929f9", size = 2119645, upload-time = "2025-05-14T17:55:24.854Z" },
- { url = "https://files.pythonhosted.org/packages/9b/e8/c664a7e73d36fbfc4730f8cf2bf930444ea87270f2825efbe17bf808b998/sqlalchemy-2.0.41-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:598d9ebc1e796431bbd068e41e4de4dc34312b7aa3292571bb3674a0cb415dd1", size = 2107399, upload-time = "2025-05-14T17:55:28.097Z" },
- { url = "https://files.pythonhosted.org/packages/5c/78/8a9cf6c5e7135540cb682128d091d6afa1b9e48bd049b0d691bf54114f70/sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a104c5694dfd2d864a6f91b0956eb5d5883234119cb40010115fd45a16da5e70", size = 3293269, upload-time = "2025-05-14T17:50:38.227Z" },
- { url = "https://files.pythonhosted.org/packages/3c/35/f74add3978c20de6323fb11cb5162702670cc7a9420033befb43d8d5b7a4/sqlalchemy-2.0.41-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6145afea51ff0af7f2564a05fa95eb46f542919e6523729663a5d285ecb3cf5e", size = 3303364, upload-time = "2025-05-14T17:51:49.829Z" },
- { url = "https://files.pythonhosted.org/packages/6a/d4/c990f37f52c3f7748ebe98883e2a0f7d038108c2c5a82468d1ff3eec50b7/sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b46fa6eae1cd1c20e6e6f44e19984d438b6b2d8616d21d783d150df714f44078", size = 3229072, upload-time = "2025-05-14T17:50:39.774Z" },
- { url = "https://files.pythonhosted.org/packages/15/69/cab11fecc7eb64bc561011be2bd03d065b762d87add52a4ca0aca2e12904/sqlalchemy-2.0.41-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41836fe661cc98abfae476e14ba1906220f92c4e528771a8a3ae6a151242d2ae", size = 3268074, upload-time = "2025-05-14T17:51:51.736Z" },
- { url = "https://files.pythonhosted.org/packages/5c/ca/0c19ec16858585d37767b167fc9602593f98998a68a798450558239fb04a/sqlalchemy-2.0.41-cp312-cp312-win32.whl", hash = "sha256:a8808d5cf866c781150d36a3c8eb3adccfa41a8105d031bf27e92c251e3969d6", size = 2084514, upload-time = "2025-05-14T17:55:49.915Z" },
- { url = "https://files.pythonhosted.org/packages/7f/23/4c2833d78ff3010a4e17f984c734f52b531a8c9060a50429c9d4b0211be6/sqlalchemy-2.0.41-cp312-cp312-win_amd64.whl", hash = "sha256:5b14e97886199c1f52c14629c11d90c11fbb09e9334fa7bb5f6d068d9ced0ce0", size = 2111557, upload-time = "2025-05-14T17:55:51.349Z" },
- { url = "https://files.pythonhosted.org/packages/d3/ad/2e1c6d4f235a97eeef52d0200d8ddda16f6c4dd70ae5ad88c46963440480/sqlalchemy-2.0.41-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4eeb195cdedaf17aab6b247894ff2734dcead6c08f748e617bfe05bd5a218443", size = 2115491, upload-time = "2025-05-14T17:55:31.177Z" },
- { url = "https://files.pythonhosted.org/packages/cf/8d/be490e5db8400dacc89056f78a52d44b04fbf75e8439569d5b879623a53b/sqlalchemy-2.0.41-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d4ae769b9c1c7757e4ccce94b0641bc203bbdf43ba7a2413ab2523d8d047d8dc", size = 2102827, upload-time = "2025-05-14T17:55:34.921Z" },
- { url = "https://files.pythonhosted.org/packages/a0/72/c97ad430f0b0e78efaf2791342e13ffeafcbb3c06242f01a3bb8fe44f65d/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a62448526dd9ed3e3beedc93df9bb6b55a436ed1474db31a2af13b313a70a7e1", size = 3225224, upload-time = "2025-05-14T17:50:41.418Z" },
- { url = "https://files.pythonhosted.org/packages/5e/51/5ba9ea3246ea068630acf35a6ba0d181e99f1af1afd17e159eac7e8bc2b8/sqlalchemy-2.0.41-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc56c9788617b8964ad02e8fcfeed4001c1f8ba91a9e1f31483c0dffb207002a", size = 3230045, upload-time = "2025-05-14T17:51:54.722Z" },
- { url = "https://files.pythonhosted.org/packages/78/2f/8c14443b2acea700c62f9b4a8bad9e49fc1b65cfb260edead71fd38e9f19/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c153265408d18de4cc5ded1941dcd8315894572cddd3c58df5d5b5705b3fa28d", size = 3159357, upload-time = "2025-05-14T17:50:43.483Z" },
- { url = "https://files.pythonhosted.org/packages/fc/b2/43eacbf6ccc5276d76cea18cb7c3d73e294d6fb21f9ff8b4eef9b42bbfd5/sqlalchemy-2.0.41-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f67766965996e63bb46cfbf2ce5355fc32d9dd3b8ad7e536a920ff9ee422e23", size = 3197511, upload-time = "2025-05-14T17:51:57.308Z" },
- { url = "https://files.pythonhosted.org/packages/fa/2e/677c17c5d6a004c3c45334ab1dbe7b7deb834430b282b8a0f75ae220c8eb/sqlalchemy-2.0.41-cp313-cp313-win32.whl", hash = "sha256:bfc9064f6658a3d1cadeaa0ba07570b83ce6801a1314985bf98ec9b95d74e15f", size = 2082420, upload-time = "2025-05-14T17:55:52.69Z" },
- { url = "https://files.pythonhosted.org/packages/e9/61/e8c1b9b6307c57157d328dd8b8348ddc4c47ffdf1279365a13b2b98b8049/sqlalchemy-2.0.41-cp313-cp313-win_amd64.whl", hash = "sha256:82ca366a844eb551daff9d2e6e7a9e5e76d2612c8564f58db6c19a726869c1df", size = 2108329, upload-time = "2025-05-14T17:55:54.495Z" },
- { url = "https://files.pythonhosted.org/packages/1c/fc/9ba22f01b5cdacc8f5ed0d22304718d2c758fce3fd49a5372b886a86f37c/sqlalchemy-2.0.41-py3-none-any.whl", hash = "sha256:57df5dc6fdb5ed1a88a1ed2195fd31927e705cad62dedd86b46972752a80f576", size = 1911224, upload-time = "2025-05-14T17:39:42.154Z" },
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.14.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" },
-]
-
-[[package]]
-name = "tzdata"
-version = "2025.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" },
-]
-
-[[package]]
-name = "werkzeug"
-version = "3.1.3"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" },
-]
-
-[[package]]
-name = "wtforms"
-version = "3.2.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "markupsafe" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/01/e4/633d080897e769ed5712dcfad626e55dbd6cf45db0ff4d9884315c6a82da/wtforms-3.2.1.tar.gz", hash = "sha256:df3e6b70f3192e92623128123ec8dca3067df9cfadd43d59681e210cfb8d4682", size = 137801, upload-time = "2024-10-21T11:34:00.108Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/08/c9/2088fb5645cd289c99ebe0d4cdcc723922a1d8e1beaefb0f6f76dff9b21c/wtforms-3.2.1-py3-none-any.whl", hash = "sha256:583bad77ba1dd7286463f21e11aa3043ca4869d03575921d1a1698d0715e0fd4", size = 152454, upload-time = "2024-10-21T11:33:58.44Z" },
-]