Compare commits
10 Commits
71901dc199
...
6ee79d26fa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ee79d26fa | ||
|
|
4852e2c153 | ||
|
|
d7532b7134 | ||
|
|
7f9612493c | ||
|
|
4a0166b80e | ||
|
|
6a8eb826a6 | ||
|
|
34a7a59899 | ||
|
|
668dbf3012 | ||
|
|
f4d32ff9ee | ||
|
|
01623ac850 |
11
.replit
11
.replit
@@ -12,14 +12,6 @@ deploymentTarget = "cloudrun"
|
|||||||
localPort = 5000
|
localPort = 5000
|
||||||
externalPort = 80
|
externalPort = 80
|
||||||
|
|
||||||
[[ports]]
|
|
||||||
localPort = 33703
|
|
||||||
externalPort = 3001
|
|
||||||
|
|
||||||
[[ports]]
|
|
||||||
localPort = 37833
|
|
||||||
externalPort = 3000
|
|
||||||
|
|
||||||
[agent]
|
[agent]
|
||||||
expertMode = true
|
expertMode = true
|
||||||
|
|
||||||
@@ -46,3 +38,6 @@ outputType = "webview"
|
|||||||
task = "shell.exec"
|
task = "shell.exec"
|
||||||
args = "python app.py"
|
args = "python app.py"
|
||||||
waitForPort = 5000
|
waitForPort = 5000
|
||||||
|
|
||||||
|
[objectStorage]
|
||||||
|
defaultBucketID = "replit-objstore-47c46bdf-5951-434e-80ca-840f0bc61270"
|
||||||
|
|||||||
105
app.py
105
app.py
@@ -4,6 +4,7 @@ from psycopg2.extras import RealDictCursor
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify
|
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from object_storage import ObjectStorageService
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = os.environ.get('SECRET_KEY', 'techturb-secret-key-change-in-production')
|
app.secret_key = os.environ.get('SECRET_KEY', 'techturb-secret-key-change-in-production')
|
||||||
@@ -12,10 +13,59 @@ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
|
|||||||
|
|
||||||
ADMIN_PASSWORD = 'techturb123'
|
ADMIN_PASSWORD = 'techturb123'
|
||||||
|
|
||||||
|
# Initialize Object Storage Service
|
||||||
|
try:
|
||||||
|
storage_service = ObjectStorageService()
|
||||||
|
USING_OBJECT_STORAGE = True
|
||||||
|
print(f"Object Storage initialized successfully - using bucket: {storage_service.bucket_name}")
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Warning: Object Storage not configured - {e}")
|
||||||
|
print("Files will be saved locally (ephemeral storage)")
|
||||||
|
storage_service = None
|
||||||
|
USING_OBJECT_STORAGE = False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error initializing Object Storage: {e}")
|
||||||
|
storage_service = None
|
||||||
|
USING_OBJECT_STORAGE = False
|
||||||
|
|
||||||
def get_db_connection():
|
def get_db_connection():
|
||||||
conn = psycopg2.connect(os.environ['DATABASE_URL'])
|
conn = psycopg2.connect(os.environ['DATABASE_URL'])
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
|
@app.template_filter('image_url')
|
||||||
|
def image_url_filter(path):
|
||||||
|
"""Convert image path to proper URL - handles both storage and local paths"""
|
||||||
|
if not path:
|
||||||
|
return url_for('static', filename='images/default.jpg')
|
||||||
|
if path.startswith('/storage/'):
|
||||||
|
return path
|
||||||
|
return url_for('static', filename=path)
|
||||||
|
|
||||||
|
def upload_file_to_storage(file, folder='uploads'):
|
||||||
|
"""
|
||||||
|
Upload file to object storage if available, otherwise fall back to local storage
|
||||||
|
Returns the file path/URL
|
||||||
|
"""
|
||||||
|
if not file or not file.filename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if USING_OBJECT_STORAGE and storage_service:
|
||||||
|
try:
|
||||||
|
result = storage_service.upload_file(file, folder)
|
||||||
|
print(f"File uploaded to object storage: {result}")
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error uploading to object storage: {e}")
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
file.save(filepath)
|
||||||
|
return f'images/{filename}'
|
||||||
|
else:
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
|
||||||
|
file.save(filepath)
|
||||||
|
return f'images/{filename}'
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@app.route('/home')
|
@app.route('/home')
|
||||||
def home():
|
def home():
|
||||||
@@ -155,13 +205,13 @@ def add_member():
|
|||||||
role = request.form.get('role')
|
role = request.form.get('role')
|
||||||
member_type = request.form.get('type')
|
member_type = request.form.get('type')
|
||||||
|
|
||||||
|
# Upload image to object storage
|
||||||
image_path = 'images/default.jpg'
|
image_path = 'images/default.jpg'
|
||||||
if 'image' in request.files:
|
if 'image' in request.files:
|
||||||
file = request.files['image']
|
file = request.files['image']
|
||||||
if file and file.filename:
|
uploaded_path = upload_file_to_storage(file, folder='members')
|
||||||
filename = secure_filename(file.filename)
|
if uploaded_path:
|
||||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
image_path = uploaded_path
|
||||||
image_path = f'images/{filename}'
|
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
@@ -189,13 +239,11 @@ def update_member():
|
|||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# Upload image to object storage
|
||||||
image_path = None
|
image_path = None
|
||||||
if 'image' in request.files:
|
if 'image' in request.files:
|
||||||
file = request.files['image']
|
file = request.files['image']
|
||||||
if file and file.filename:
|
image_path = upload_file_to_storage(file, folder='members')
|
||||||
filename = secure_filename(file.filename)
|
|
||||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
|
||||||
image_path = f'images/{filename}'
|
|
||||||
|
|
||||||
if member_type == 'mentor':
|
if member_type == 'mentor':
|
||||||
if image_path:
|
if image_path:
|
||||||
@@ -259,13 +307,11 @@ def add_competition():
|
|||||||
awards_raw = request.form.get('awards')
|
awards_raw = request.form.get('awards')
|
||||||
awards = '|'.join([line.strip() for line in awards_raw.split('\n') if line.strip()])
|
awards = '|'.join([line.strip() for line in awards_raw.split('\n') if line.strip()])
|
||||||
|
|
||||||
|
# Upload image to object storage
|
||||||
image_path = None
|
image_path = None
|
||||||
if 'image' in request.files:
|
if 'image' in request.files:
|
||||||
file = request.files['image']
|
file = request.files['image']
|
||||||
if file and file.filename:
|
image_path = upload_file_to_storage(file, folder='competitions')
|
||||||
filename = secure_filename(file.filename)
|
|
||||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
|
||||||
image_path = f'images/{filename}'
|
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
@@ -297,13 +343,12 @@ def edit_competition():
|
|||||||
current_comp = cur.fetchone()
|
current_comp = cur.fetchone()
|
||||||
image_path = current_comp['image_path'] if current_comp else None
|
image_path = current_comp['image_path'] if current_comp else None
|
||||||
|
|
||||||
# Handle new image upload
|
# Handle new image upload to object storage
|
||||||
if 'image' in request.files:
|
if 'image' in request.files:
|
||||||
file = request.files['image']
|
file = request.files['image']
|
||||||
if file and file.filename:
|
uploaded_path = upload_file_to_storage(file, folder='competitions')
|
||||||
filename = secure_filename(file.filename)
|
if uploaded_path:
|
||||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
image_path = uploaded_path
|
||||||
image_path = f'images/{filename}'
|
|
||||||
|
|
||||||
# Update competition
|
# Update competition
|
||||||
cur.execute('''UPDATE competitions
|
cur.execute('''UPDATE competitions
|
||||||
@@ -350,13 +395,11 @@ def add_sponsor():
|
|||||||
name = request.form.get('name')
|
name = request.form.get('name')
|
||||||
website_url = request.form.get('website_url')
|
website_url = request.form.get('website_url')
|
||||||
|
|
||||||
|
# Upload logo to object storage
|
||||||
logo_path = None
|
logo_path = None
|
||||||
if 'logo' in request.files:
|
if 'logo' in request.files:
|
||||||
file = request.files['logo']
|
file = request.files['logo']
|
||||||
if file and file.filename:
|
logo_path = upload_file_to_storage(file, folder='sponsors')
|
||||||
filename = secure_filename(file.filename)
|
|
||||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
|
||||||
logo_path = f'images/{filename}'
|
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
@@ -383,5 +426,25 @@ def delete_sponsor():
|
|||||||
flash('Sponsor deleted successfully', 'success')
|
flash('Sponsor deleted successfully', 'success')
|
||||||
return redirect(url_for('admin_sponsors'))
|
return redirect(url_for('admin_sponsors'))
|
||||||
|
|
||||||
|
@app.route('/storage/<path:filepath>')
|
||||||
|
def serve_storage_file(filepath):
|
||||||
|
"""Serve files from object storage"""
|
||||||
|
try:
|
||||||
|
if USING_OBJECT_STORAGE and storage_service:
|
||||||
|
file_bytes = storage_service.get_file_bytes(f"/storage/{filepath}")
|
||||||
|
ext = filepath.rsplit('.', 1)[-1].lower() if '.' in filepath else ''
|
||||||
|
content_types = {
|
||||||
|
'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png',
|
||||||
|
'gif': 'image/gif', 'webp': 'image/webp', 'svg': 'image/svg+xml'
|
||||||
|
}
|
||||||
|
content_type = content_types.get(ext, 'application/octet-stream')
|
||||||
|
from flask import Response
|
||||||
|
return Response(file_bytes, mimetype=content_type)
|
||||||
|
else:
|
||||||
|
return "Object storage not configured", 404
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error serving file {filepath}: {e}")
|
||||||
|
return "File not found", 404
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True, host='0.0.0.0', port=5000)
|
app.run(debug=True, host='0.0.0.0', port=5000)
|
||||||
|
|||||||
BIN
attached_assets/image_1763011882367.png
Normal file
BIN
attached_assets/image_1763011882367.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
BIN
attached_assets/image_1763011889293.png
Normal file
BIN
attached_assets/image_1763011889293.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
attached_assets/image_1765661730127.png
Normal file
BIN
attached_assets/image_1765661730127.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
BIN
attached_assets/image_1765661747621.png
Normal file
BIN
attached_assets/image_1765661747621.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
attached_assets/image_1765661887413.png
Normal file
BIN
attached_assets/image_1765661887413.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
59
object_storage.py
Normal file
59
object_storage.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Object Storage Service for FTC Team Website
|
||||||
|
Uses official Replit Object Storage SDK
|
||||||
|
"""
|
||||||
|
|
||||||
|
from replit.object_storage import Client
|
||||||
|
from uuid import uuid4
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectStorageService:
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize the Replit Object Storage client"""
|
||||||
|
self.client = Client()
|
||||||
|
self.bucket_name = os.environ.get('OBJECT_STORAGE_BUCKET', 'default')
|
||||||
|
|
||||||
|
def upload_file(self, file, folder='uploads'):
|
||||||
|
"""
|
||||||
|
Upload a file to object storage
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: Werkzeug FileStorage object
|
||||||
|
folder: Folder name (prefix) for organization
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Path to the uploaded file for serving
|
||||||
|
"""
|
||||||
|
if not file or not file.filename:
|
||||||
|
return None
|
||||||
|
|
||||||
|
original_filename = secure_filename(file.filename)
|
||||||
|
file_extension = os.path.splitext(original_filename)[1]
|
||||||
|
unique_filename = f"{uuid4()}{file_extension}"
|
||||||
|
object_name = f"{folder}/{unique_filename}"
|
||||||
|
|
||||||
|
file_content = file.read()
|
||||||
|
|
||||||
|
self.client.upload_from_bytes(object_name, file_content)
|
||||||
|
|
||||||
|
return f"/storage/{object_name}"
|
||||||
|
|
||||||
|
def get_file_bytes(self, path):
|
||||||
|
"""Download a file as bytes"""
|
||||||
|
object_name = path.replace("/storage/", "")
|
||||||
|
return self.client.download_as_bytes(object_name)
|
||||||
|
|
||||||
|
def delete_file(self, path):
|
||||||
|
"""Delete a file from object storage"""
|
||||||
|
try:
|
||||||
|
object_name = path.replace("/storage/", "")
|
||||||
|
self.client.delete(object_name)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def list_files(self):
|
||||||
|
"""List all files in the bucket"""
|
||||||
|
return self.client.list()
|
||||||
448
poetry.lock
generated
448
poetry.lock
generated
@@ -22,6 +22,150 @@ files = [
|
|||||||
{file = "cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48"},
|
{file = "cachelib-0.13.0.tar.gz", hash = "sha256:209d8996e3c57595bee274ff97116d1d73c4980b2fd9a34c7846cd07fd2e1a48"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cachetools"
|
||||||
|
version = "6.2.1"
|
||||||
|
description = "Extensible memoizing collections and decorators"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701"},
|
||||||
|
{file = "cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "certifi"
|
||||||
|
version = "2025.11.12"
|
||||||
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"},
|
||||||
|
{file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "charset-normalizer"
|
||||||
|
version = "3.4.4"
|
||||||
|
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"},
|
||||||
|
{file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"},
|
||||||
|
{file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"},
|
||||||
|
{file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.1.7"
|
version = "8.1.7"
|
||||||
@@ -93,6 +237,195 @@ mongodb = ["pymongo (>=4.6.2)"]
|
|||||||
redis = ["redis (>=5.0.3)"]
|
redis = ["redis (>=5.0.3)"]
|
||||||
sqlalchemy = ["flask-sqlalchemy (>=3.0.5)"]
|
sqlalchemy = ["flask-sqlalchemy (>=3.0.5)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-api-core"
|
||||||
|
version = "2.28.1"
|
||||||
|
description = "Google API client core library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "google_api_core-2.28.1-py3-none-any.whl", hash = "sha256:4021b0f8ceb77a6fb4de6fde4502cecab45062e66ff4f2895169e0b35bc9466c"},
|
||||||
|
{file = "google_api_core-2.28.1.tar.gz", hash = "sha256:2b405df02d68e68ce0fbc138559e6036559e685159d148ae5861013dc201baf8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
google-auth = ">=2.14.1,<3.0.0"
|
||||||
|
googleapis-common-protos = ">=1.56.2,<2.0.0"
|
||||||
|
proto-plus = [
|
||||||
|
{version = ">=1.22.3,<2.0.0", markers = "python_version < \"3.13\""},
|
||||||
|
{version = ">=1.25.0,<2.0.0", markers = "python_version >= \"3.13\""},
|
||||||
|
]
|
||||||
|
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||||
|
requests = ">=2.18.0,<3.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.0)"]
|
||||||
|
grpc = ["grpcio (>=1.33.2,<2.0.0)", "grpcio (>=1.49.1,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.33.2,<2.0.0)", "grpcio-status (>=1.49.1,<2.0.0)", "grpcio-status (>=1.75.1,<2.0.0)"]
|
||||||
|
grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
|
||||||
|
grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-auth"
|
||||||
|
version = "2.43.0"
|
||||||
|
description = "Google Authentication Library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16"},
|
||||||
|
{file = "google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
cachetools = ">=2.0.0,<7.0"
|
||||||
|
pyasn1-modules = ">=0.2.1"
|
||||||
|
rsa = ">=3.1.4,<5"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"]
|
||||||
|
enterprise-cert = ["cryptography", "pyopenssl"]
|
||||||
|
pyjwt = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"]
|
||||||
|
pyopenssl = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
|
||||||
|
reauth = ["pyu2f (>=0.1.5)"]
|
||||||
|
requests = ["requests (>=2.20.0,<3.0.0)"]
|
||||||
|
testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0)", "cryptography (<39.0.0)", "cryptography (>=38.0.3)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"]
|
||||||
|
urllib3 = ["packaging", "urllib3"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-cloud-core"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "Google Cloud API client core library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "google_cloud_core-2.5.0-py3-none-any.whl", hash = "sha256:67d977b41ae6c7211ee830c7912e41003ea8194bff15ae7d72fd6f51e57acabc"},
|
||||||
|
{file = "google_cloud_core-2.5.0.tar.gz", hash = "sha256:7c1b7ef5c92311717bd05301aa1a91ffbc565673d3b0b4163a52d8413a186963"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0"
|
||||||
|
google-auth = ">=1.25.0,<3.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
grpc = ["grpcio (>=1.38.0,<2.0.0)", "grpcio (>=1.75.1,<2.0.0)", "grpcio-status (>=1.38.0,<2.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-cloud-storage"
|
||||||
|
version = "3.5.0"
|
||||||
|
description = "Google Cloud Storage API client library"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "google_cloud_storage-3.5.0-py3-none-any.whl", hash = "sha256:e28fd6ad8764e60dbb9a398a7bc3296e7920c494bc329057d828127e5f9630d3"},
|
||||||
|
{file = "google_cloud_storage-3.5.0.tar.gz", hash = "sha256:10b89e1d1693114b3e0ca921bdd28c5418701fd092e39081bb77e5cee0851ab7"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
google-api-core = ">=2.27.0,<3.0.0"
|
||||||
|
google-auth = ">=2.26.1,<3.0.0"
|
||||||
|
google-cloud-core = ">=2.4.2,<3.0.0"
|
||||||
|
google-crc32c = ">=1.1.3,<2.0.0"
|
||||||
|
google-resumable-media = ">=2.7.2,<3.0.0"
|
||||||
|
requests = ">=2.22.0,<3.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
protobuf = ["protobuf (>=3.20.2,<7.0.0)"]
|
||||||
|
tracing = ["opentelemetry-api (>=1.1.0,<2.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-crc32c"
|
||||||
|
version = "1.7.1"
|
||||||
|
description = "A python wrapper of the C library 'Google CRC32C'"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:b07d48faf8292b4db7c3d64ab86f950c2e94e93a11fd47271c28ba458e4a0d76"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7cc81b3a2fbd932a4313eb53cc7d9dde424088ca3a0337160f35d91826880c1d"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1c67ca0a1f5b56162951a9dae987988679a7db682d6f97ce0f6381ebf0fbea4c"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc5319db92daa516b653600794d5b9f9439a9a121f3e162f94b0e1891c7933cb"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcdf5a64adb747610140572ed18d011896e3b9ae5195f2514b7ff678c80f1603"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:754561c6c66e89d55754106739e22fdaa93fafa8da7221b29c8b8e8270c6ec8a"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6fbab4b935989e2c3610371963ba1b86afb09537fd0c633049be82afe153ac06"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:ed66cbe1ed9cbaaad9392b5259b3eba4a9e565420d734e6238813c428c3336c9"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6547b657621b6cbed3562ea7826c3e11cab01cd33b74e1f677690652883e77"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d68e17bad8f7dd9a49181a1f5a8f4b251c6dbc8cc96fb79f1d321dfd57d66f53"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:6335de12921f06e1f774d0dd1fbea6bf610abe0887a1638f64d694013138be5d"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:2d73a68a653c57281401871dd4aeebbb6af3191dcac751a76ce430df4d403194"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:22beacf83baaf59f9d3ab2bbb4db0fb018da8e5aebdce07ef9f09fce8220285e"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19eafa0e4af11b0a4eb3974483d55d2d77ad1911e6cf6f832e1574f6781fd337"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d86616faaea68101195c6bdc40c494e4d76f41e07a37ffdef270879c15fb65"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:b7491bdc0c7564fcf48c0179d2048ab2f7c7ba36b84ccd3a3e1c3f7a72d3bba6"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:df8b38bdaf1629d62d51be8bdd04888f37c451564c2042d36e5812da9eff3c35"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:e42e20a83a29aa2709a0cf271c7f8aefaa23b7ab52e53b322585297bb94d4638"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:905a385140bf492ac300026717af339790921f411c0dfd9aa5a9e69a08ed32eb"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b211ddaf20f7ebeec5c333448582c224a7c90a9d98826fbab82c0ddc11348e6"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:0f99eaa09a9a7e642a61e06742856eec8b19fc0037832e03f941fe7cf0c8e4db"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32d1da0d74ec5634a05f53ef7df18fc646666a25efaaca9fc7dcfd4caf1d98c3"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e10554d4abc5238823112c2ad7e4560f96c7bf3820b202660373d769d9e6e4c9"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:9fc196f0b8d8bd2789352c6a522db03f89e83a0ed6b64315923c396d7a932315"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:bb5e35dcd8552f76eed9461a23de1030920a3c953c1982f324be8f97946e7127"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f2226b6a8da04f1d9e61d3e357f2460b9551c5e6950071437e122c958a18ae14"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f2b3522222746fff0e04a9bd0a23ea003ba3cccc8cf21385c564deb1f223242"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bda0fcb632d390e3ea8b6b07bf6b4f4a66c9d02dcd6fbf7ba00a197c143f582"},
|
||||||
|
{file = "google_crc32c-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:713121af19f1a617054c41f952294764e0c5443d5a5d9034b2cd60f5dd7e0349"},
|
||||||
|
{file = "google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8e9afc74168b0b2232fb32dd202c93e46b7d5e4bf03e66ba5dc273bb3559589"},
|
||||||
|
{file = "google_crc32c-1.7.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa8136cc14dd27f34a3221c0f16fd42d8a40e4778273e61a3c19aedaa44daf6b"},
|
||||||
|
{file = "google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85fef7fae11494e747c9fd1359a527e5970fc9603c90764843caabd3a16a0a48"},
|
||||||
|
{file = "google_crc32c-1.7.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6efb97eb4369d52593ad6f75e7e10d053cf00c48983f7a973105bc70b0ac4d82"},
|
||||||
|
{file = "google_crc32c-1.7.1.tar.gz", hash = "sha256:2bff2305f98846f3e825dbeec9ee406f89da7962accdb29356e4eadc251bd472"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["pytest"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-resumable-media"
|
||||||
|
version = "2.7.2"
|
||||||
|
description = "Utilities for Google Media Downloads and Resumable Uploads"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "google_resumable_media-2.7.2-py2.py3-none-any.whl", hash = "sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa"},
|
||||||
|
{file = "google_resumable_media-2.7.2.tar.gz", hash = "sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
google-crc32c = ">=1.0,<2.0dev"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "google-auth (>=1.22.0,<2.0dev)"]
|
||||||
|
requests = ["requests (>=2.18.0,<3.0.0dev)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "googleapis-common-protos"
|
||||||
|
version = "1.72.0"
|
||||||
|
description = "Common protobufs used in Google APIs"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038"},
|
||||||
|
{file = "googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
grpc = ["grpcio (>=1.44.0,<2.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "3.11"
|
||||||
|
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"},
|
||||||
|
{file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itsdangerous"
|
name = "itsdangerous"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -242,6 +575,42 @@ test = ["attrs", "eval-type-backport", "msgpack", "pytest", "pyyaml", "tomli", "
|
|||||||
toml = ["tomli", "tomli_w"]
|
toml = ["tomli", "tomli_w"]
|
||||||
yaml = ["pyyaml"]
|
yaml = ["pyyaml"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proto-plus"
|
||||||
|
version = "1.26.1"
|
||||||
|
description = "Beautiful, Pythonic protocol buffers"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "proto_plus-1.26.1-py3-none-any.whl", hash = "sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66"},
|
||||||
|
{file = "proto_plus-1.26.1.tar.gz", hash = "sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
protobuf = ">=3.19.0,<7.0.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
testing = ["google-api-core (>=1.31.5)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "protobuf"
|
||||||
|
version = "6.33.0"
|
||||||
|
description = ""
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "protobuf-6.33.0-cp310-abi3-win32.whl", hash = "sha256:d6101ded078042a8f17959eccd9236fb7a9ca20d3b0098bbcb91533a5680d035"},
|
||||||
|
{file = "protobuf-6.33.0-cp310-abi3-win_amd64.whl", hash = "sha256:9a031d10f703f03768f2743a1c403af050b6ae1f3480e9c140f39c45f81b13ee"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:905b07a65f1a4b72412314082c7dbfae91a9e8b68a0cc1577515f8df58ecf455"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e0697ece353e6239b90ee43a9231318302ad8353c70e6e45499fa52396debf90"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:e0a1715e4f27355afd9570f3ea369735afc853a6c3951a6afe1f80d8569ad298"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:35be49fd3f4fefa4e6e2aacc35e8b837d6703c37a2168a55ac21e9b1bc7559ef"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-cp39-win32.whl", hash = "sha256:cd33a8e38ea3e39df66e1bbc462b076d6e5ba3a4ebbde58219d777223a7873d3"},
|
||||||
|
{file = "protobuf-6.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:c963e86c3655af3a917962c9619e1a6b9670540351d7af9439d06064e3317cc9"},
|
||||||
|
{file = "protobuf-6.33.0-py3-none-any.whl", hash = "sha256:25c9e1963c6734448ea2d308cfa610e692b801304ba0908d7bfa564ac5132995"},
|
||||||
|
{file = "protobuf-6.33.0.tar.gz", hash = "sha256:140303d5c8d2037730c548f8c7b93b20bb1dc301be280c378b82b8894589c954"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psycopg2-binary"
|
name = "psycopg2-binary"
|
||||||
version = "2.9.10"
|
version = "2.9.10"
|
||||||
@@ -319,6 +688,83 @@ files = [
|
|||||||
{file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"},
|
{file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyasn1"
|
||||||
|
version = "0.6.1"
|
||||||
|
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
|
||||||
|
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyasn1-modules"
|
||||||
|
version = "0.4.2"
|
||||||
|
description = "A collection of ASN.1-based protocols modules"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"},
|
||||||
|
{file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pyasn1 = ">=0.6.1,<0.7.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "requests"
|
||||||
|
version = "2.32.5"
|
||||||
|
description = "Python HTTP for Humans."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"},
|
||||||
|
{file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
certifi = ">=2017.4.17"
|
||||||
|
charset_normalizer = ">=2,<4"
|
||||||
|
idna = ">=2.5,<4"
|
||||||
|
urllib3 = ">=1.21.1,<3"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||||
|
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsa"
|
||||||
|
version = "4.9.1"
|
||||||
|
description = "Pure-Python RSA implementation"
|
||||||
|
optional = false
|
||||||
|
python-versions = "<4,>=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"},
|
||||||
|
{file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pyasn1 = ">=0.1.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urllib3"
|
||||||
|
version = "2.5.0"
|
||||||
|
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
files = [
|
||||||
|
{file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
|
||||||
|
{file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
||||||
|
h2 = ["h2 (>=4,<5)"]
|
||||||
|
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
|
zstd = ["zstandard (>=0.18.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "werkzeug"
|
name = "werkzeug"
|
||||||
version = "3.1.3"
|
version = "3.1.3"
|
||||||
@@ -339,4 +785,4 @@ watchdog = ["watchdog (>=2.3)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "466c14d77e1b8cc59742d5929922403a3b91b09caf2287b1e00e03b3973ab5bf"
|
content-hash = "fbeb4364beecc62c381c712260dae214b83bb7bd958a697399cca7f422533673"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ flask = "^3.0.3"
|
|||||||
werkzeug = "^3.1.3"
|
werkzeug = "^3.1.3"
|
||||||
psycopg2-binary = "^2.9.10"
|
psycopg2-binary = "^2.9.10"
|
||||||
flask-session = "^0.8.0"
|
flask-session = "^0.8.0"
|
||||||
|
google-cloud-storage = "^3.5.0"
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
|||||||
23
replit.md
23
replit.md
@@ -3,6 +3,29 @@
|
|||||||
## Overview
|
## Overview
|
||||||
Flask-based website for FTC Team 23344 with a modern dark theme (#000000 pure black background), comprehensive content management system, PostgreSQL database integration, and premium smooth-scrolling animations.
|
Flask-based website for FTC Team 23344 with a modern dark theme (#000000 pure black background), comprehensive content management system, PostgreSQL database integration, and premium smooth-scrolling animations.
|
||||||
|
|
||||||
|
## Recent Changes (December 13, 2025)
|
||||||
|
|
||||||
|
### Complete Object Storage Migration
|
||||||
|
All uploaded images are now stored permanently in Replit Object Storage, preventing the issue where files would disappear after a few hours.
|
||||||
|
|
||||||
|
**What Was Done:**
|
||||||
|
1. Installed `replit-object-storage` official Python package
|
||||||
|
2. Created `object_storage.py` using official Replit SDK
|
||||||
|
3. Added `/storage/<path>` route to serve files from object storage
|
||||||
|
4. Added `|image_url` Jinja template filter to handle both local and storage paths
|
||||||
|
5. Migrated all existing member, mentor, competition, and sponsor images to object storage
|
||||||
|
6. Updated database paths from `images/filename.jpg` to `/storage/folder/uuid.ext`
|
||||||
|
|
||||||
|
**How It Works:**
|
||||||
|
- New uploads go directly to object storage via `upload_file_to_storage()` helper
|
||||||
|
- Database stores paths like `/storage/members/uuid.png`
|
||||||
|
- Templates use `{{ image_path|image_url }}` filter which handles both formats
|
||||||
|
- `/storage/` route serves files from object storage bucket
|
||||||
|
- Background images (hero, team, etc.) remain local as they don't get erased
|
||||||
|
|
||||||
|
**Bucket:** `ftc-team-images`
|
||||||
|
**Package:** `replit-object-storage` (installed via pip)
|
||||||
|
|
||||||
## Recent Changes (November 12, 2025)
|
## Recent Changes (November 12, 2025)
|
||||||
|
|
||||||
### Admin Panel Enhancement: Competition Editing
|
### Admin Panel Enhancement: Competition Editing
|
||||||
|
|||||||
BIN
static/images/dr5qp.jpg
Normal file
BIN
static/images/dr5qp.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
@@ -186,7 +186,7 @@
|
|||||||
<div class="members-grid">
|
<div class="members-grid">
|
||||||
{% for mentor in mentors %}
|
{% for mentor in mentors %}
|
||||||
<div class="member-card-admin">
|
<div class="member-card-admin">
|
||||||
<img src="{{ url_for('static', filename=mentor.image_path) }}" class="member-image-admin" alt="{{ mentor.name }}">
|
<img src="{{ mentor.image_path|image_url }}" class="member-image-admin" alt="{{ mentor.name }}">
|
||||||
<h3 class="member-name-admin">{{ mentor.name }}</h3>
|
<h3 class="member-name-admin">{{ mentor.name }}</h3>
|
||||||
<p class="member-role-admin">{{ mentor.role }}</p>
|
<p class="member-role-admin">{{ mentor.role }}</p>
|
||||||
<button onclick="openEditModal('mentor', {{ mentor.id }}, '{{ mentor.name }}', '{{ mentor.role }}')" class="btn btn-edit">Edit</button>
|
<button onclick="openEditModal('mentor', {{ mentor.id }}, '{{ mentor.name }}', '{{ mentor.role }}')" class="btn btn-edit">Edit</button>
|
||||||
@@ -205,7 +205,7 @@
|
|||||||
<div class="members-grid">
|
<div class="members-grid">
|
||||||
{% for member in members %}
|
{% for member in members %}
|
||||||
<div class="member-card-admin">
|
<div class="member-card-admin">
|
||||||
<img src="{{ url_for('static', filename=member.image_path) }}" class="member-image-admin" alt="{{ member.name }}">
|
<img src="{{ member.image_path|image_url }}" class="member-image-admin" alt="{{ member.name }}">
|
||||||
<h3 class="member-name-admin">{{ member.name }}</h3>
|
<h3 class="member-name-admin">{{ member.name }}</h3>
|
||||||
<p class="member-role-admin">{{ member.role }}</p>
|
<p class="member-role-admin">{{ member.role }}</p>
|
||||||
<button onclick="openEditModal('member', {{ member.id }}, '{{ member.name }}', '{{ member.role }}')" class="btn btn-edit">Edit</button>
|
<button onclick="openEditModal('member', {{ member.id }}, '{{ member.name }}', '{{ member.role }}')" class="btn btn-edit">Edit</button>
|
||||||
|
|||||||
@@ -146,7 +146,7 @@
|
|||||||
{% for sponsor in sponsors %}
|
{% for sponsor in sponsors %}
|
||||||
<div class="sponsor-card-admin">
|
<div class="sponsor-card-admin">
|
||||||
{% if sponsor.logo_path %}
|
{% if sponsor.logo_path %}
|
||||||
<img src="{{ url_for('static', filename=sponsor.logo_path) }}" class="sponsor-logo-admin" alt="{{ sponsor.name }}">
|
<img src="{{ sponsor.logo_path|image_url }}" class="sponsor-logo-admin" alt="{{ sponsor.name }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h3 class="sponsor-name-admin">{{ sponsor.name }}</h3>
|
<h3 class="sponsor-name-admin">{{ sponsor.name }}</h3>
|
||||||
{% if sponsor.website_url %}
|
{% if sponsor.website_url %}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<div class="competition-card">
|
<div class="competition-card">
|
||||||
{% if comp.image_path %}
|
{% if comp.image_path %}
|
||||||
<div class="competition-card-img">
|
<div class="competition-card-img">
|
||||||
<img src="{{ url_for('static', filename=comp.image_path) }}">
|
<img src="{{ comp.image_path|image_url }}">
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="competition-header">
|
<div class="competition-header">
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
{% for mentor in mentors %}
|
{% for mentor in mentors %}
|
||||||
<div class="member-card">
|
<div class="member-card">
|
||||||
{% if mentor.image_path %}
|
{% if mentor.image_path %}
|
||||||
<img class="member-image" src="{{ url_for('static', filename=mentor.image_path) }}">
|
<img class="member-image" src="{{ mentor.image_path|image_url }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 class="member-name">{{ mentor.name }}</h2>
|
<h2 class="member-name">{{ mentor.name }}</h2>
|
||||||
<p class="member-role">{{ mentor.role }}</p>
|
<p class="member-role">{{ mentor.role }}</p>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
{% for member in members %}
|
{% for member in members %}
|
||||||
<div class="member-card">
|
<div class="member-card">
|
||||||
{% if member.image_path %}
|
{% if member.image_path %}
|
||||||
<img class="member-image" src="{{ url_for('static', filename=member.image_path) }}">
|
<img class="member-image" src="{{ member.image_path|image_url }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 class="member-name">{{ member.name }}</h2>
|
<h2 class="member-name">{{ member.name }}</h2>
|
||||||
<p class="member-role">{{ member.role }}</p>
|
<p class="member-role">{{ member.role }}</p>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
{% for sponsor in sponsors %}
|
{% for sponsor in sponsors %}
|
||||||
<a href="{{ sponsor.website_url }}" target="_blank" class="card-sponsors">
|
<a href="{{ sponsor.website_url }}" target="_blank" class="card-sponsors">
|
||||||
<div class="card-content-sponsors">
|
<div class="card-content-sponsors">
|
||||||
<img src="{{ url_for('static', filename=sponsor.logo_path) }}" alt="{{ sponsor.name }}">
|
<img src="{{ sponsor.logo_path|image_url }}" alt="{{ sponsor.name }}">
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user