This commit is contained in:
Ryan Chen
2025-06-13 16:49:51 -04:00
commit bd3265e6a2
14 changed files with 640 additions and 0 deletions

46
.dockerignore Normal file
View File

@@ -0,0 +1,46 @@
# Git
.git
.gitignore
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# Virtual Environment
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
# Database
*.db
# Uploads
static/uploads/*
!static/uploads/.gitkeep
# Docker
Dockerfile
.dockerignore

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.13

34
Dockerfile Normal file
View File

@@ -0,0 +1,34 @@
# Use Python 3.13 slim image as base
FROM python:3.13-slim
# Set working directory
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Install uv for Python package management
RUN pip install uv
# Copy project files
COPY pyproject.toml .
COPY main.py .
COPY templates/ templates/
# Create uploads directory
RUN mkdir -p static/uploads
# Install Python dependencies
RUN uv pip install -e .
# Set environment variables
ENV FLASK_APP=main.py
ENV FLASK_ENV=production
# Expose port
EXPOSE 5000
# Run the application
CMD ["python", "main.py"]

73
README.md Normal file
View File

@@ -0,0 +1,73 @@
# Pet Picture Queue
A web application to manage pet pictures from subscribers. Built with Flask, SQLite, and Jinja2 templates.
## Features
- Upload pet pictures with subscriber information
- View all uploaded pictures in a grid layout
- Mark pictures as posted
- Modern, responsive UI using Tailwind CSS
- Click to view full-size images
- Download original images
## Setup
### Local Development
1. Install dependencies:
```bash
uv pip install -e .
```
2. Run the application:
```bash
python main.py
```
The application will be available at `http://localhost:5000`
### Docker Deployment
1. Build the Docker image:
```bash
docker build -t pet-picture-queue .
```
2. Run the container:
```bash
docker run -d \
-p 5000:5000 \
-v $(pwd)/static/uploads:/app/static/uploads \
-v $(pwd)/pet_pictures.db:/app/pet_pictures.db \
--name pet-picture-queue \
pet-picture-queue
```
The application will be available at `http://localhost:5000`
## Usage
1. Visit the homepage to see all uploaded pet pictures
2. Click "Upload New" to add a new pet picture
3. Fill in the subscriber name and select a picture
4. View uploaded pictures on the homepage
5. Click on any picture to view it in full size
6. Use the "Download Original" button to download the original file
7. Mark pictures as posted using the "Mark as Posted" button
## File Structure
- `main.py` - Main Flask application
- `templates/` - Jinja2 templates
- `base.html` - Base template with common layout
- `index.html` - Homepage with picture grid
- `upload.html` - Upload form
- `static/uploads/` - Directory for uploaded pictures
- `pet_pictures.db` - SQLite database (created automatically)
- `Dockerfile` - Docker configuration
- `.dockerignore` - Docker build exclusions

120
main.py Normal file
View File

@@ -0,0 +1,120 @@
import os
import sqlite3
from datetime import datetime
from flask import (
Flask,
render_template,
request,
redirect,
url_for,
flash,
send_from_directory,
)
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.secret_key = os.urandom(24)
# Configure upload folder
UPLOAD_FOLDER = "static/uploads"
ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"}
app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER
# Ensure upload directory exists
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
def get_db():
db = sqlite3.connect("pet_pictures.db")
db.row_factory = sqlite3.Row
return db
def init_db():
with get_db() as db:
db.execute(
"""
CREATE TABLE IF NOT EXISTS pet_pictures (
id INTEGER PRIMARY KEY AUTOINCREMENT,
filename TEXT NOT NULL,
subscriber_name TEXT NOT NULL,
uploaded_at TIMESTAMP NOT NULL,
posted BOOLEAN DEFAULT 0
)
"""
)
db.commit()
def allowed_file(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route("/")
def index():
with get_db() as db:
pictures = db.execute(
"SELECT * FROM pet_pictures ORDER BY uploaded_at DESC"
).fetchall()
return render_template("index.html", pictures=pictures)
@app.route("/upload", methods=["GET", "POST"])
def upload():
if request.method == "POST":
if "picture" not in request.files:
flash("No file selected")
return redirect(request.url)
file = request.files["picture"]
subscriber_name = request.form.get("subscriber_name")
if file.filename == "":
flash("No file selected")
return redirect(request.url)
if not subscriber_name:
flash("Subscriber name is required")
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_")
filename = timestamp + filename
file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename))
with get_db() as db:
db.execute(
"INSERT INTO pet_pictures (filename, subscriber_name, uploaded_at) VALUES (?, ?, ?)",
(filename, subscriber_name, datetime.now()),
)
db.commit()
flash("Picture uploaded successfully!")
return redirect(url_for("index"))
flash("Invalid file type")
return redirect(request.url)
return render_template("upload.html")
@app.route("/mark_posted/<int:picture_id>", methods=["POST"])
def mark_posted(picture_id):
with get_db() as db:
db.execute("UPDATE pet_pictures SET posted = 1 WHERE id = ?", (picture_id,))
db.commit()
flash("Picture marked as posted!")
return redirect(url_for("index"))
@app.route("/download/<filename>")
def download_file(filename):
return send_from_directory(
app.config["UPLOAD_FOLDER"], filename, as_attachment=True
)
if __name__ == "__main__":
init_db()
app.run(debug=True)

BIN
pet_pictures.db Normal file

Binary file not shown.

17
pyproject.toml Normal file
View File

@@ -0,0 +1,17 @@
[project]
name = "pet-picture-queue"
version = "0.1.0"
description = "A web application to manage pet pictures from subscribers"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"flask>=3.0.0",
"werkzeug>=3.0.0",
"python-dotenv>=1.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

1
static/uploads/.gitkeep Normal file
View File

@@ -0,0 +1 @@

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

47
templates/base.html Normal file
View File

@@ -0,0 +1,47 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}Pet Picture Queue{% endblock %}</title>
<link
href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"
rel="stylesheet"
/>
</head>
<body class="bg-gray-100 min-h-screen">
<nav class="bg-white shadow-lg">
<div class="max-w-6xl mx-auto px-4">
<div class="flex justify-between items-center py-4">
<div class="text-xl font-semibold text-gray-800">
<a href="{{ url_for('index') }}">Pet Picture Queue</a>
</div>
<div class="space-x-4">
<a
href="{{ url_for('index') }}"
class="text-gray-600 hover:text-gray-800"
>View Pictures</a
>
<a
href="{{ url_for('upload') }}"
class="text-gray-600 hover:text-gray-800"
>Upload New</a
>
</div>
</div>
</div>
</nav>
<main class="max-w-6xl mx-auto px-4 py-8">
{% with messages = get_flashed_messages() %} {% if messages %} {% for
message in messages %}
<div
class="bg-blue-100 border-l-4 border-blue-500 text-blue-700 p-4 mb-4"
role="alert"
>
{{ message }}
</div>
{% endfor %} {% endif %} {% endwith %} {% block content %}{% endblock %}
</main>
</body>
</html>

72
templates/index.html Normal file
View File

@@ -0,0 +1,72 @@
{% extends "base.html" %} {% block title %}Pet Pictures - Pet Picture Queue{%
endblock %} {% block content %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{% for picture in pictures %}
<div
class="bg-white rounded-lg shadow-md overflow-hidden {% if picture.posted %}opacity-50{% endif %}"
>
<div class="relative group">
<a
href="{{ url_for('static', filename='uploads/' + picture.filename) }}"
target="_blank"
class="block"
>
<img
src="{{ url_for('static', filename='uploads/' + picture.filename) }}"
alt="Pet picture from {{ picture.subscriber_name }}"
class="w-full h-64 object-cover transition-transform duration-300 group-hover:scale-105"
/>
<div
class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity duration-300 flex items-center justify-center"
>
<span
class="text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300"
>
Click to view
</span>
</div>
</a>
</div>
<div class="p-4">
<h3 class="text-lg font-semibold text-gray-800">
From: {{ picture.subscriber_name }}
</h3>
<p class="text-sm text-gray-600">Uploaded: {{ picture.uploaded_at }}</p>
<div class="mt-4 space-y-2">
<a
href="{{ url_for('download_file', filename=picture.filename) }}"
class="block w-full bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 transition-colors text-center"
>
Download Original
</a>
{% if not picture.posted %}
<form
action="{{ url_for('mark_posted', picture_id=picture.id) }}"
method="POST"
>
<button
type="submit"
class="w-full bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 transition-colors"
>
Mark as Posted
</button>
</form>
{% else %}
<div class="text-center text-green-600 font-semibold">✓ Posted</div>
{% endif %}
</div>
</div>
</div>
{% else %}
<div class="col-span-full text-center py-12">
<p class="text-gray-600 text-lg">No pet pictures uploaded yet.</p>
<a
href="{{ url_for('upload') }}"
class="mt-4 inline-block bg-blue-500 text-white px-6 py-2 rounded hover:bg-blue-600 transition-colors"
>
Upload Your First Picture
</a>
</div>
{% endfor %}
</div>
{% endblock %}

82
templates/upload.html Normal file
View File

@@ -0,0 +1,82 @@
{% extends "base.html" %} {% block title %}Upload Pet Picture - Pet Picture
Queue{% endblock %} {% block content %}
<div class="max-w-2xl mx-auto">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Upload Pet Picture</h2>
<form
action="{{ url_for('upload') }}"
method="POST"
enctype="multipart/form-data"
class="space-y-6"
>
<div>
<label
for="subscriber_name"
class="block text-sm font-medium text-gray-700"
>Subscriber Name</label
>
<input
type="text"
name="subscriber_name"
id="subscriber_name"
required
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
/>
</div>
<div>
<label for="picture" class="block text-sm font-medium text-gray-700"
>Pet Picture</label
>
<div
class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md"
>
<div class="space-y-1 text-center">
<svg
class="mx-auto h-12 w-12 text-gray-400"
stroke="currentColor"
fill="none"
viewBox="0 0 48 48"
>
<path
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<div class="flex text-sm text-gray-600">
<label
for="picture"
class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-blue-500"
>
<span>Upload a file</span>
<input
id="picture"
name="picture"
type="file"
class="sr-only"
accept="image/*"
required
/>
</label>
<p class="pl-1">or drag and drop</p>
</div>
<p class="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p>
</div>
</div>
</div>
<div>
<button
type="submit"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Upload Picture
</button>
</div>
</form>
</div>
</div>
{% endblock %}

137
uv.lock generated Normal file
View File

@@ -0,0 +1,137 @@
version = 1
revision = 2
requires-python = ">=3.13"
[[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 = "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.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/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 = "pet-picture-queue"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "flask" },
{ name = "python-dotenv" },
{ name = "werkzeug" },
]
[package.metadata]
requires-dist = [
{ name = "flask", specifier = ">=3.0.0" },
{ name = "python-dotenv", specifier = ">=1.0.0" },
{ name = "werkzeug", specifier = ">=3.0.0" },
]
[[package]]
name = "python-dotenv"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920, upload-time = "2025-03-25T10:14:56.835Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256, upload-time = "2025-03-25T10:14:55.034Z" },
]
[[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" },
]