From d1cd964ed24203750ed45d1a8e20e61b22613c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D1=83=D0=B7=D0=B0=D0=BB=D0=B5=D0=B2=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20=D0=90=D0=BB=D0=B5?= =?UTF-8?q?=D0=BA=D1=81=D0=B5=D0=B5=D0=B2=D0=B8=D1=87?= Date: Sun, 29 Mar 2026 16:33:31 +0300 Subject: [PATCH] Upload --- .gitignore | 219 ++++++++++++++++++++++++++++ .python-version | 1 + app.py | 7 + app/__init__.py | 22 +++ app/auth.py | 26 ++++ app/database.py | 53 +++++++ app/orders/__init__.py | 1 + app/orders/admin.py | 35 +++++ app/orders/shop.py | 165 ++++++++++++++++++++++ app/orders/user.py | 30 ++++ app/services/__init__.py | 1 + app/services/order.py | 48 +++++++ app/services/user.py | 34 +++++ pyproject.toml | 15 ++ static/css/styles.css | 267 +++++++++++++++++++++++++++++++++++ static/js/app.js | 19 +++ static/placeholder.png | Bin 0 -> 57440 bytes templates/admin.html | 45 ++++++ templates/base.html | 29 ++++ templates/cart.html | 22 +++ templates/index.html | 17 +++ templates/order.html | 18 +++ templates/order_success.html | 10 ++ templates/register.html | 18 +++ uv.lock | 214 ++++++++++++++++++++++++++++ 25 files changed, 1316 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 app.py create mode 100644 app/__init__.py create mode 100644 app/auth.py create mode 100644 app/database.py create mode 100644 app/orders/__init__.py create mode 100644 app/orders/admin.py create mode 100644 app/orders/shop.py create mode 100644 app/orders/user.py create mode 100644 app/services/__init__.py create mode 100644 app/services/order.py create mode 100644 app/services/user.py create mode 100644 pyproject.toml create mode 100644 static/css/styles.css create mode 100644 static/js/app.js create mode 100644 static/placeholder.png create mode 100644 templates/admin.html create mode 100644 templates/base.html create mode 100644 templates/cart.html create mode 100644 templates/index.html create mode 100644 templates/order.html create mode 100644 templates/order_success.html create mode 100644 templates/register.html create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba45027 --- /dev/null +++ b/.gitignore @@ -0,0 +1,219 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[codz] +*$py.class + +# C extensions +*.so + +# 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 + +# UV +# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# uv.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 +# poetry.toml + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python. +# https://pdm-project.org/en/latest/usage/project/#working-with-version-control +# pdm.lock +# pdm.toml +.pdm-python +.pdm-build/ + +# pixi +# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control. +# pixi.lock +# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one +# in the .venv directory. It is recommended not to include this directory in version control. +.pixi + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# Redis +*.rdb +*.aof +*.pid + +# RabbitMQ +mnesia/ +rabbitmq/ +rabbitmq-data/ + +# ActiveMQ +activemq-data/ + +# SageMath parsed files +*.sage.py + +# Environments +.env +.envrc +.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/ + +# Abstra +# Abstra is an AI-powered process automation framework. +# Ignore directories containing user credentials, local state, and settings. +# Learn more at https://abstra.io/docs +.abstra/ + +# Visual Studio Code +# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore +# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore +# and can be added to the global gitignore or merged into this file. However, if you prefer, +# you could uncomment the following to ignore the entire vscode folder +# .vscode/ + +# Ruff stuff: +.ruff_cache/ + +# PyPI configuration file +.pypirc + +# Marimo +marimo/_static/ +marimo/_lsp/ +__marimo__/ + +# Streamlit +.streamlit/secrets.toml + +# SQL Lite +.db \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/app.py b/app.py new file mode 100644 index 0000000..e9d3a59 --- /dev/null +++ b/app.py @@ -0,0 +1,7 @@ +from app import create_app + +app = create_app() + + +if __name__ == "__main__": + app.run(debug=True) diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..309c875 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,22 @@ +from flask import Flask + +from app.database import init_db +from app.orders.admin import admin_bp +from app.orders.shop import shop_bp +from app.orders.user import user_bp + + +def create_app() -> Flask: + app = Flask( + __name__, + template_folder="../templates", + static_folder="../static", + ) + app.secret_key = "super-secret-key" + + init_db() + app.register_blueprint(shop_bp) + app.register_blueprint(admin_bp) + app.register_blueprint(user_bp) + + return app diff --git a/app/auth.py b/app/auth.py new file mode 100644 index 0000000..275b4ae --- /dev/null +++ b/app/auth.py @@ -0,0 +1,26 @@ +from functools import wraps + +from flask import Response, request + +from app.services.user import verify_basic_admin + + +def _authenticate_response() -> Response: + return Response( + "Требуется авторизация", + 401, + {"WWW-Authenticate": 'Basic realm="Admin Login Required"'}, + ) + + +def requires_admin_basic_auth(view_func): + @wraps(view_func) + def wrapped(*args, **kwargs): + auth = request.authorization + if not auth: + return _authenticate_response() + if not verify_basic_admin(auth.username, auth.password): + return _authenticate_response() + return view_func(*args, **kwargs) + + return wrapped diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..5fadb79 --- /dev/null +++ b/app/database.py @@ -0,0 +1,53 @@ +import sqlite3 + +from werkzeug.security import generate_password_hash + +DB_PATH = "food_delivery.db" + + +def get_db_connection() -> sqlite3.Connection: + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +def init_db() -> None: + conn = get_db_connection() + conn.execute(""" + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'user', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + conn.execute(""" + CREATE TABLE IF NOT EXISTS orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + customer_name TEXT NOT NULL, + customer_phone TEXT NOT NULL, + customer_address TEXT NOT NULL, + customer_comment TEXT DEFAULT '', + order_items TEXT NOT NULL, + total_price INTEGER NOT NULL, + status TEXT DEFAULT 'Новый', + order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + columns = [row["name"] for row in conn.execute("PRAGMA table_info(orders)").fetchall()] + if "customer_comment" not in columns: + conn.execute("ALTER TABLE orders ADD COLUMN customer_comment TEXT DEFAULT ''") + + admin_user = conn.execute( + "SELECT id FROM users WHERE username = ?", + ("admin",), + ).fetchone() + if admin_user is None: + conn.execute( + "INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)", + ("admin", generate_password_hash("12345"), "admin"), + ) + + conn.commit() + conn.close() diff --git a/app/orders/__init__.py b/app/orders/__init__.py new file mode 100644 index 0000000..148b6b3 --- /dev/null +++ b/app/orders/__init__.py @@ -0,0 +1 @@ +# Orders and user routes package diff --git a/app/orders/admin.py b/app/orders/admin.py new file mode 100644 index 0000000..9d37bb1 --- /dev/null +++ b/app/orders/admin.py @@ -0,0 +1,35 @@ +import time + +from flask import Blueprint, Response, redirect, render_template, request + +from app.auth import requires_admin_basic_auth +from app.services.order import get_all_orders, update_order_status + +admin_bp = Blueprint("admin", __name__) + + +@admin_bp.route("/admin") +@requires_admin_basic_auth +def admin(): + orders = get_all_orders() + return render_template("admin.html", orders=orders) + + +@admin_bp.route("/update_status/", methods=["POST"]) +@requires_admin_basic_auth +def update_status(order_id: int): + status = request.form.get("status", "Новый") + update_order_status(order_id, status) + return redirect("/admin") + + +@admin_bp.route("/logout", methods=["POST"]) +def logout(): + response = Response( + 'Вы вышли из админ-панели. Войти снова', + 401, + {"WWW-Authenticate": f'Basic realm="Admin Logout {int(time.time())}"'}, + ) + response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" + response.headers["Pragma"] = "no-cache" + return response diff --git a/app/orders/shop.py b/app/orders/shop.py new file mode 100644 index 0000000..b993150 --- /dev/null +++ b/app/orders/shop.py @@ -0,0 +1,165 @@ +from flask import Blueprint, redirect, render_template, request, session + +from app.services.order import create_order + +shop_bp = Blueprint("shop", __name__) + +MENU = [ + { + "id": 1, + "name": "Пепперони", + "price": 450, + "category": "Пицца", + }, + { + "id": 2, + "name": "Маргарита", + "price": 350, + "category": "Пицца", + }, + { + "id": 3, + "name": "Цезарь", + "price": 320, + "category": "Салаты", + }, + { + "id": 4, + "name": "Греческий", + "price": 280, + "category": "Салаты", + }, + { + "id": 5, + "name": "Кола 0.5", + "price": 100, + "category": "Напитки", + }, + { + "id": 6, + "name": "Сок апельсиновый", + "price": 150, + "category": "Напитки", + }, + { + "id": 7, + "name": "Сок яблочный", + "price": 150, + "category": "Напитки", + }, + { + "id": 8, + "name": "Сок ягодный", + "price": 150, + "category": "Напитки", + }, + { + "id": 9, + "name": "Сок цитрусовый", + "price": 150, + "category": "Напитки", + }, +] + + +def _get_item(item_id: int): + return next((item for item in MENU if item["id"] == item_id), None) + + +def _collect_cart(): + items = [] + item_names = [] + total = 0 + for item_id in session.get("cart", []): + item = _get_item(item_id) + if item: + items.append(item) + item_names.append(item["name"]) + total += item["price"] + return items, item_names, total + + +@shop_bp.route("/") +def index(): + return render_template("index.html", menu=MENU) + + +@shop_bp.route("/add_to_cart/", methods=["POST"]) +def add_to_cart(item_id: int): + if "cart" not in session: + session["cart"] = [] + session["cart"].append(item_id) + return redirect("/") + + +@shop_bp.route("/remove_from_cart/", methods=["POST"]) +def remove_from_cart(item_id: int): + if "cart" in session and item_id in session["cart"]: + session["cart"].remove(item_id) + return redirect("/cart") + + +@shop_bp.route("/cart") +def cart(): + cart_items, _, total = _collect_cart() + return render_template("cart.html", items=cart_items, total=total) + + +@shop_bp.route("/order") +def order(): + _, _, total = _collect_cart() + return render_template( + "order.html", + cart_total=total, + form_data={"name": "", "phone": "", "address": "", "comment": ""}, + ) + + +@shop_bp.route("/place_order", methods=["POST"]) +def place_order(): + name = request.form.get("name", "").strip() + phone = request.form.get("phone", "").strip() + address = request.form.get("address", "").strip() + comment = request.form.get("comment", "").strip() + _, cart_item_names, total = _collect_cart() + + if not cart_item_names: + return render_template( + "order.html", + error="Корзина пуста. Добавьте товары перед оформлением.", + cart_total=total, + form_data={ + "name": name, + "phone": phone, + "address": address, + "comment": comment, + }, + ), 400 + + if not name or not phone or not address: + return render_template( + "order.html", + error="Заполните имя, телефон и адрес.", + cart_total=total, + form_data={ + "name": name, + "phone": phone, + "address": address, + "comment": comment, + }, + ), 400 + + create_order( + customer_name=name, + customer_phone=phone, + customer_address=address, + customer_comment=comment, + order_items=", ".join(cart_item_names), + total_price=total, + ) + session.pop("cart", None) + return render_template( + "order_success.html", + customer_name=name, + total=total, + ) diff --git a/app/orders/user.py b/app/orders/user.py new file mode 100644 index 0000000..9fcb255 --- /dev/null +++ b/app/orders/user.py @@ -0,0 +1,30 @@ +from flask import Blueprint, redirect, render_template, request, url_for + +from app.services.user import create_user + +user_bp = Blueprint("user", __name__) + + +@user_bp.route("/register", methods=["GET", "POST"]) +def register(): + error = None + success = None + if request.method == "POST": + username = request.form.get("username", "").strip() + password = request.form.get("password", "").strip() + + if not username or not password: + error = "Введите логин и пароль." + elif len(password) < 4: + error = "Пароль должен быть не короче 4 символов." + elif create_user(username=username, password=password): + success = "Регистрация успешна. Теперь можно оформлять заказы." + else: + error = "Пользователь с таким логином уже существует." + + return render_template("register.html", error=error, success=success) + + +@user_bp.route("/register/success") +def register_success(): + return redirect(url_for("user.register")) diff --git a/app/services/__init__.py b/app/services/__init__.py new file mode 100644 index 0000000..a70b302 --- /dev/null +++ b/app/services/__init__.py @@ -0,0 +1 @@ +# Services package diff --git a/app/services/order.py b/app/services/order.py new file mode 100644 index 0000000..650aae1 --- /dev/null +++ b/app/services/order.py @@ -0,0 +1,48 @@ +from app.database import get_db_connection + + +def create_order( + customer_name: str, + customer_phone: str, + customer_address: str, + customer_comment: str, + order_items: str, + total_price: int, +) -> None: + conn = get_db_connection() + conn.execute( + """ + INSERT INTO orders ( + customer_name, + customer_phone, + customer_address, + customer_comment, + order_items, + total_price + ) VALUES (?, ?, ?, ?, ?, ?) + """, + ( + customer_name, + customer_phone, + customer_address, + customer_comment, + order_items, + total_price, + ), + ) + conn.commit() + conn.close() + + +def get_all_orders(): + conn = get_db_connection() + orders = conn.execute("SELECT * FROM orders ORDER BY order_date DESC").fetchall() + conn.close() + return orders + + +def update_order_status(order_id: int, status: str) -> None: + conn = get_db_connection() + conn.execute("UPDATE orders SET status = ? WHERE id = ?", (status, order_id)) + conn.commit() + conn.close() diff --git a/app/services/user.py b/app/services/user.py new file mode 100644 index 0000000..05e4742 --- /dev/null +++ b/app/services/user.py @@ -0,0 +1,34 @@ +from werkzeug.security import check_password_hash, generate_password_hash + +from app.database import get_db_connection + + +def create_user(username: str, password: str, role: str = "user") -> bool: + conn = get_db_connection() + existing = conn.execute( + "SELECT id FROM users WHERE username = ?", + (username,), + ).fetchone() + if existing is not None: + conn.close() + return False + + conn.execute( + "INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)", + (username, generate_password_hash(password), role), + ) + conn.commit() + conn.close() + return True + + +def verify_basic_admin(username: str, password: str) -> bool: + conn = get_db_connection() + user = conn.execute( + "SELECT password_hash, role FROM users WHERE username = ?", + (username,), + ).fetchone() + conn.close() + if user is None: + return False + return user["role"] == "admin" and check_password_hash(user["password_hash"], password) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a9681aa --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "grob" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.14" +dependencies = [ + "flask>=3.1.3", +] + +[dependency-groups] +dev = [ + "ruff>=0.15.8", + "types-flask>=1.1.6", +] diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..0c2bbc8 --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,267 @@ +:root { + --bg: #f8f4ec; + --surface: #fffdf7; + --ink: #212121; + --muted: #626262; + --accent: #ea580c; + --accent-2: #fb923c; + --line: #f0dfc9; + --shadow: 0 14px 36px rgba(112, 71, 29, 0.14); +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: "Manrope", "Trebuchet MS", sans-serif; + color: var(--ink); + background: + radial-gradient(circle at 80% 5%, #ffe2bf 0, #f8f4ec 38%), + radial-gradient(circle at 15% 20%, #fff6e7 0, #f8f4ec 35%); + min-height: 100vh; +} + +.layout { + width: min(1100px, 94%); + margin: 0 auto; + padding: 22px 0 34px; +} + +.nav { + background: rgba(255, 253, 247, 0.9); + border: 1px solid var(--line); + border-radius: 18px; + padding: 14px 18px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + box-shadow: var(--shadow); + backdrop-filter: blur(4px); +} + +.brand { + margin: 0; + font-family: "Unbounded", sans-serif; + font-size: 20px; +} + +.nav-links { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.nav a { + color: var(--ink); + text-decoration: none; + font-weight: 700; + padding: 8px 12px; + border-radius: 999px; + border: 1px solid transparent; + transition: all 0.2s ease; +} + +.nav a:hover { + border-color: var(--line); + background: #fff2de; + color: var(--accent); +} + +.content { + margin-top: 18px; + background: var(--surface); + border: 1px solid var(--line); + border-radius: 22px; + padding: 20px; + box-shadow: var(--shadow); +} + +h1 { + margin: 6px 0 16px; + font-family: "Unbounded", sans-serif; + font-size: clamp(22px, 4vw, 34px); +} + +h2, +h3 { + margin: 0 0 8px; +} + +.title-reset { + margin: 0; +} + +.subtitle { + color: var(--muted); + margin: -6px 0 18px; +} + +.btn { + display: inline-block; + border: none; + background: linear-gradient(135deg, var(--accent), var(--accent-2)); + color: #fff; + text-decoration: none; + padding: 10px 14px; + border-radius: 12px; + font-weight: 800; + cursor: pointer; + transition: transform 0.2s ease, box-shadow 0.2s ease; + box-shadow: 0 10px 20px rgba(234, 88, 12, 0.25); +} + +.btn:hover { + transform: translateY(-1px); +} + +.btn-ghost { + background: #fff; + color: var(--ink); + border: 1px solid var(--line); + box-shadow: none; +} + +input, +textarea, +select { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--line); + border-radius: 10px; + font: inherit; + margin: 0 0 10px; + background: #fff; +} + +textarea { + min-height: 88px; + resize: vertical; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, +td { + border-bottom: 1px solid var(--line); + text-align: left; + vertical-align: top; + padding: 10px 8px; +} + +th { + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--muted); +} + +.grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; +} + +.card { + border: 1px solid var(--line); + border-radius: 16px; + padding: 14px; + background: linear-gradient(180deg, #fffefb, #fff7ec); +} + +.card-image { + width: 100%; + height: 160px; + object-fit: cover; + border-radius: 12px; + border: 1px solid var(--line); + margin-bottom: 12px; + background: #fff; +} + +.meta { + color: var(--muted); + font-size: 14px; + margin-bottom: 12px; +} + +.list { + display: grid; + gap: 12px; + padding: 0; + margin: 0 0 14px; + list-style: none; +} + +.list li { + display: flex; + gap: 12px; + justify-content: space-between; + border: 1px solid var(--line); + border-radius: 12px; + padding: 12px; + background: #fff; +} + +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 12px; +} + +.status-form { + display: flex; + gap: 8px; + align-items: center; +} + +.message { + font-weight: 700; +} + +.message-error { + color: #b42318; +} + +.message-success { + color: #067647; +} + +@media (max-width: 900px) { + .grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 640px) { + .layout { + width: min(96%, 960px); + padding: 14px 0 24px; + } + + .content { + padding: 14px; + } + + .grid { + grid-template-columns: 1fr; + } + + .list li { + flex-direction: column; + align-items: flex-start; + } + + .status-form { + flex-direction: column; + align-items: stretch; + } +} diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..4f8c451 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,19 @@ +document.addEventListener("DOMContentLoaded", () => { + const forms = document.querySelectorAll("form"); + forms.forEach((form) => { + form.addEventListener("submit", () => { + const submitButton = form.querySelector('button[type="submit"]'); + if (!submitButton) { + return; + } + submitButton.disabled = true; + const originalText = submitButton.textContent || ""; + submitButton.dataset.originalText = originalText; + submitButton.textContent = "Отправка..."; + setTimeout(() => { + submitButton.disabled = false; + submitButton.textContent = submitButton.dataset.originalText || originalText; + }, 2000); + }); + }); +}); diff --git a/static/placeholder.png b/static/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae306d1928b6bb0c380502889bf36a052f6ecfa GIT binary patch literal 57440 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ð@Kq=r;LOJoi;3qXRA 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 +

Панель администратора

+
+ +
+ + + + + + + + + + + + + + {% for order in orders %} + + + + + + + + + + + + {% endfor %} +
IDКлиентТелефонАдресКомментарийЗаказСуммаСтатусДействие
{{ order.id }}{{ order.customer_name }}{{ order.customer_phone }}{{ order.customer_address }}{{ order.customer_comment }}{{ order.order_items }}{{ order.total_price }} ₽{{ order.status }}
+{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..54ee282 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,29 @@ + + + + + + Доставка еды + + + + + + + +
+ +
+ {% block content %}{% endblock %} +
+
+ + diff --git a/templates/cart.html b/templates/cart.html new file mode 100644 index 0000000..2dad3e2 --- /dev/null +++ b/templates/cart.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block content %} +

Корзина

+

Проверьте состав заказа перед оформлением.

+{% if not items %} +

Корзина пока пустая.

+{% endif %} +
    + {% for item in items %} +
  • + {{ item.name }} · {{ item.price }} ₽ +
    + +
    +
  • + {% endfor %} +
+
+

Итого: {{ total }} ₽

+ Оформить заказ +
+{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..43b88fa --- /dev/null +++ b/templates/index.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} +{% block content %} +

Меню

+

Выберите блюда и добавьте в корзину за один клик.

+
+ {% for item in menu %} +
+ {{ item.name }} +

{{ item.name }}

+

{{ item.category }} · {{ item.price }} ₽

+
+ +
+
+ {% endfor %} +
+{% endblock %} diff --git a/templates/order.html b/templates/order.html new file mode 100644 index 0000000..507b58d --- /dev/null +++ b/templates/order.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block content %} +

Оформление заказа

+

Заполните контактные данные, и мы быстро свяжемся с вами.

+{% if error %} +

{{ error }}

+{% endif %} +{% if cart_total is defined %} +

Сумма заказа: {{ cart_total }} ₽

+{% endif %} +
+ + + + + +
+{% endblock %} diff --git a/templates/order_success.html b/templates/order_success.html new file mode 100644 index 0000000..534ef50 --- /dev/null +++ b/templates/order_success.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block content %} +

Заказ оформлен

+

Спасибо, {{ customer_name }}! Мы приняли заказ.

+

Итоговая сумма: {{ total }} ₽

+ +{% endblock %} diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000..a347dc3 --- /dev/null +++ b/templates/register.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block content %} +

Регистрация

+

Создайте учетную запись пользователя для оформления заказов.

+ +{% if error %} +

{{ error }}

+{% endif %} +{% if success %} +

{{ success }}

+{% endif %} + +
+ + + +
+{% endblock %} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..1806995 --- /dev/null +++ b/uv.lock @@ -0,0 +1,214 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[[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.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[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.3" +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/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "grob" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "flask" }, +] + +[package.dev-dependencies] +dev = [ + { name = "ruff" }, + { name = "types-flask" }, +] + +[package.metadata] +requires-dist = [{ name = "flask", specifier = ">=3.1.3" }] + +[package.metadata.requires-dev] +dev = [ + { name = "ruff", specifier = ">=0.15.8" }, + { name = "types-flask", specifier = ">=1.1.6" }, +] + +[[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 = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/b0/73cf7550861e2b4824950b8b52eebdcc5adc792a00c514406556c5b80817/ruff-0.15.8.tar.gz", hash = "sha256:995f11f63597ee362130d1d5a327a87cb6f3f5eae3094c620bcc632329a4d26e", size = 4610921, upload-time = "2026-03-26T18:39:38.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/92/c445b0cd6da6e7ae51e954939cb69f97e008dbe750cfca89b8cedc081be7/ruff-0.15.8-py3-none-linux_armv6l.whl", hash = "sha256:cbe05adeba76d58162762d6b239c9056f1a15a55bd4b346cfd21e26cd6ad7bc7", size = 10527394, upload-time = "2026-03-26T18:39:41.566Z" }, + { url = "https://files.pythonhosted.org/packages/eb/92/f1c662784d149ad1414cae450b082cf736430c12ca78367f20f5ed569d65/ruff-0.15.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d3e3d0b6ba8dca1b7ef9ab80a28e840a20070c4b62e56d675c24f366ef330570", size = 10905693, upload-time = "2026-03-26T18:39:30.364Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f2/7a631a8af6d88bcef997eb1bf87cc3da158294c57044aafd3e17030613de/ruff-0.15.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ee3ae5c65a42f273f126686353f2e08ff29927b7b7e203b711514370d500de3", size = 10323044, upload-time = "2026-03-26T18:39:33.37Z" }, + { url = "https://files.pythonhosted.org/packages/67/18/1bf38e20914a05e72ef3b9569b1d5c70a7ef26cd188d69e9ca8ef588d5bf/ruff-0.15.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdce027ada77baa448077ccc6ebb2fa9c3c62fd110d8659d601cf2f475858d94", size = 10629135, upload-time = "2026-03-26T18:39:44.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/e9/138c150ff9af60556121623d41aba18b7b57d95ac032e177b6a53789d279/ruff-0.15.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12e617fc01a95e5821648a6df341d80456bd627bfab8a829f7cfc26a14a4b4a3", size = 10348041, upload-time = "2026-03-26T18:39:52.178Z" }, + { url = "https://files.pythonhosted.org/packages/02/f1/5bfb9298d9c323f842c5ddeb85f1f10ef51516ac7a34ba446c9347d898df/ruff-0.15.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:432701303b26416d22ba696c39f2c6f12499b89093b61360abc34bcc9bf07762", size = 11121987, upload-time = "2026-03-26T18:39:55.195Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/6da2e538704e753c04e8d86b1fc55712fdbdcc266af1a1ece7a51fff0d10/ruff-0.15.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d910ae974b7a06a33a057cb87d2a10792a3b2b3b35e33d2699fdf63ec8f6b17a", size = 11951057, upload-time = "2026-03-26T18:39:19.18Z" }, + { url = "https://files.pythonhosted.org/packages/83/f0/c9208c5fd5101bf87002fed774ff25a96eea313d305f1e5d5744698dc314/ruff-0.15.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2033f963c43949d51e6fdccd3946633c6b37c484f5f98c3035f49c27395a8ab8", size = 11464613, upload-time = "2026-03-26T18:40:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/22/d7f2fabdba4fae9f3b570e5605d5eb4500dcb7b770d3217dca4428484b17/ruff-0.15.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f29b989a55572fb885b77464cf24af05500806ab4edf9a0fd8977f9759d85b1", size = 11257557, upload-time = "2026-03-26T18:39:57.972Z" }, + { url = "https://files.pythonhosted.org/packages/71/8c/382a9620038cf6906446b23ce8632ab8c0811b8f9d3e764f58bedd0c9a6f/ruff-0.15.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:ac51d486bf457cdc985a412fb1801b2dfd1bd8838372fc55de64b1510eff4bec", size = 11169440, upload-time = "2026-03-26T18:39:22.205Z" }, + { url = "https://files.pythonhosted.org/packages/4d/0d/0994c802a7eaaf99380085e4e40c845f8e32a562e20a38ec06174b52ef24/ruff-0.15.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c9861eb959edab053c10ad62c278835ee69ca527b6dcd72b47d5c1e5648964f6", size = 10605963, upload-time = "2026-03-26T18:39:46.682Z" }, + { url = "https://files.pythonhosted.org/packages/19/aa/d624b86f5b0aad7cef6bbf9cd47a6a02dfdc4f72c92a337d724e39c9d14b/ruff-0.15.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8d9a5b8ea13f26ae90838afc33f91b547e61b794865374f114f349e9036835fb", size = 10357484, upload-time = "2026-03-26T18:39:49.176Z" }, + { url = "https://files.pythonhosted.org/packages/35/c3/e0b7835d23001f7d999f3895c6b569927c4d39912286897f625736e1fd04/ruff-0.15.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c2a33a529fb3cbc23a7124b5c6ff121e4d6228029cba374777bd7649cc8598b8", size = 10830426, upload-time = "2026-03-26T18:40:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/f0/51/ab20b322f637b369383adc341d761eaaa0f0203d6b9a7421cd6e783d81b9/ruff-0.15.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:75e5cd06b1cf3f47a3996cfc999226b19aa92e7cce682dcd62f80d7035f98f49", size = 11345125, upload-time = "2026-03-26T18:39:27.799Z" }, + { url = "https://files.pythonhosted.org/packages/37/e6/90b2b33419f59d0f2c4c8a48a4b74b460709a557e8e0064cf33ad894f983/ruff-0.15.8-py3-none-win32.whl", hash = "sha256:bc1f0a51254ba21767bfa9a8b5013ca8149dcf38092e6a9eb704d876de94dc34", size = 10571959, upload-time = "2026-03-26T18:39:36.117Z" }, + { url = "https://files.pythonhosted.org/packages/1f/a2/ef467cb77099062317154c63f234b8a7baf7cb690b99af760c5b68b9ee7f/ruff-0.15.8-py3-none-win_amd64.whl", hash = "sha256:04f79eff02a72db209d47d665ba7ebcad609d8918a134f86cb13dd132159fc89", size = 11743893, upload-time = "2026-03-26T18:39:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/15/e2/77be4fff062fa78d9b2a4dea85d14785dac5f1d0c1fb58ed52331f0ebe28/ruff-0.15.8-py3-none-win_arm64.whl", hash = "sha256:cf891fa8e3bb430c0e7fac93851a5978fc99c8fa2c053b57b118972866f8e5f2", size = 11048175, upload-time = "2026-03-26T18:40:01.06Z" }, +] + +[[package]] +name = "types-click" +version = "7.1.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/ff/0e6a56108d45c80c61cdd4743312d0304d8192482aea4cce96c554aaa90d/types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092", size = 10015, upload-time = "2021-11-23T12:28:01.701Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/ad/607454a5f991c5b3e14693a7113926758f889138371058a5f72f567fa131/types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81", size = 12929, upload-time = "2021-11-23T12:27:59.493Z" }, +] + +[[package]] +name = "types-flask" +version = "1.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-click" }, + { name = "types-jinja2" }, + { name = "types-werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/65/728a104973133a45fba50f3d1e1ee832287666ac74cfd47004cea8402ea3/types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf", size = 9829, upload-time = "2021-11-26T06:21:31.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/6c/a98a0c29c39d8a6283ac704f3d36f0d570d8dee931e9d46d6cc60d436bec/types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087", size = 13733, upload-time = "2021-11-26T06:21:30.365Z" }, +] + +[[package]] +name = "types-jinja2" +version = "2.11.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/b82309bfed8195de7997672deac301bd6f5bd5cbb6a3e392b7fe780d7852/types-Jinja2-2.11.9.tar.gz", hash = "sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81", size = 13302, upload-time = "2021-11-26T06:21:17.496Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b0/e79d84748f1d34304f13191424348a719c3febaa3493835370fe9528e1e6/types_Jinja2-2.11.9-py3-none-any.whl", hash = "sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2", size = 18190, upload-time = "2021-11-26T06:21:16.18Z" }, +] + +[[package]] +name = "types-markupsafe" +version = "1.1.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/31/b5f059142d058aec41e913d8e0eff0a967e7bc46f9a2ba2f31bc11cff059/types-MarkupSafe-1.1.10.tar.gz", hash = "sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1", size = 2986, upload-time = "2021-11-27T03:18:07.558Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/d6/b8effb1c48539260a5eb4196afc55efac4ea1684a4991977555eb266b2ef/types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5", size = 3998, upload-time = "2021-11-27T03:18:06.398Z" }, +] + +[[package]] +name = "types-werkzeug" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/43/161261d2ac1fc20e944aa108e48a98ff0d994e19b498d6fb19d6637caf05/types-Werkzeug-1.0.9.tar.gz", hash = "sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c", size = 23909, upload-time = "2021-11-26T06:21:28.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/c1/eaf8426126eafa46d649afb2fddede327043fbc2e84021b8b09a7fa15115/types_Werkzeug-1.0.9-py3-none-any.whl", hash = "sha256:194bd5715a13c598f05c63e8a739328657590943bce941e8a3619a6b5d4a54ec", size = 36186, upload-time = "2021-11-26T06:21:27.37Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/f1/ee81806690a87dab5f5653c1f146c92bc066d7f4cebc603ef88eb9e13957/werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25", size = 864736, upload-time = "2026-02-19T15:17:18.884Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/ec/d58832f89ede95652fd01f4f24236af7d32b70cab2196dfcc2d2fd13c5c2/werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131", size = 225166, upload-time = "2026-02-19T15:17:17.475Z" }, +]