mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2024-12-26 17:36:17 +00:00
🦞
This commit is contained in:
parent
4c7bf9706f
commit
d19a33501a
|
@ -1,140 +0,0 @@
|
|||
"""
|
||||
Onlylegs Gallery
|
||||
This is the main app file, it loads all the other files and sets up the app
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
from flask_assets import Bundle
|
||||
from flask_migrate import init as migrate_init
|
||||
|
||||
from flask import Flask, render_template, abort
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from onlylegs.extensions import db, migrate, login_manager, assets, compress, cache
|
||||
from onlylegs.config import INSTANCE_DIR, MIGRATIONS_DIR
|
||||
from onlylegs.models import User
|
||||
from onlylegs.views import (
|
||||
index as view_index,
|
||||
image as view_image,
|
||||
group as view_group,
|
||||
settings as view_settings,
|
||||
profile as view_profile,
|
||||
)
|
||||
from onlylegs.api import media as api_media, group as api_group, account as api_account
|
||||
from onlylegs import auth as view_auth
|
||||
from onlylegs import filters
|
||||
from onlylegs import gwagwa
|
||||
|
||||
|
||||
def create_app(): # pylint: disable=R0914
|
||||
"""
|
||||
Create and configure the main app
|
||||
"""
|
||||
app = Flask(__name__, instance_path=INSTANCE_DIR)
|
||||
app.config.from_pyfile("config.py")
|
||||
|
||||
# DATABASE
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db, directory=MIGRATIONS_DIR)
|
||||
|
||||
# If database file doesn't exist, create it
|
||||
if not os.path.exists(os.path.join(INSTANCE_DIR, "gallery.sqlite3")):
|
||||
print("Creating database")
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
register_user = User(
|
||||
username=app.config["ADMIN_CONF"]["username"],
|
||||
email=app.config["ADMIN_CONF"]["email"],
|
||||
password=generate_password_hash("changeme!", method="sha256"),
|
||||
)
|
||||
db.session.add(register_user)
|
||||
db.session.commit()
|
||||
|
||||
print(
|
||||
"""
|
||||
####################################################
|
||||
# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #
|
||||
# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED, #
|
||||
# PLEASE UPDATE IT IN THE SETTINGS! #
|
||||
####################################################
|
||||
"""
|
||||
)
|
||||
|
||||
# Check if migrations directory exists, if not create it
|
||||
with app.app_context():
|
||||
if not os.path.exists(MIGRATIONS_DIR):
|
||||
print("Creating migrations directory")
|
||||
migrate_init(directory=MIGRATIONS_DIR)
|
||||
|
||||
# LOGIN MANAGER
|
||||
# can also set session_protection to "strong"
|
||||
# this would protect against session hijacking
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "onlylegs.index"
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return User.query.filter_by(alt_id=user_id).first()
|
||||
|
||||
@login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
error = 401
|
||||
msg = "You are not authorized to view this page!!!!"
|
||||
return render_template("error.html", error=error, msg=msg), error
|
||||
|
||||
# ERROR HANDLERS
|
||||
@app.errorhandler(Exception)
|
||||
def error_page(err):
|
||||
"""
|
||||
Error handlers, if the error is not a HTTP error, return 500
|
||||
"""
|
||||
if not isinstance(err, HTTPException):
|
||||
abort(500)
|
||||
return (
|
||||
render_template("error.html", error=err.code, msg=err.description),
|
||||
err.code,
|
||||
)
|
||||
|
||||
# ASSETS
|
||||
assets.init_app(app)
|
||||
|
||||
scripts = Bundle(
|
||||
"js/*.js", output="gen/js.js", depends="js/*.js"
|
||||
) # filter jsmin is broken :c
|
||||
styles = Bundle(
|
||||
"sass/style.sass",
|
||||
filters="libsass, cssmin",
|
||||
output="gen/styles.css",
|
||||
depends="sass/**/*.sass",
|
||||
)
|
||||
|
||||
assets.register("scripts", scripts)
|
||||
assets.register("styles", styles)
|
||||
|
||||
# BLUEPRINTS
|
||||
app.register_blueprint(view_auth.blueprint)
|
||||
app.register_blueprint(view_index.blueprint)
|
||||
app.register_blueprint(view_image.blueprint)
|
||||
app.register_blueprint(view_group.blueprint)
|
||||
app.register_blueprint(view_profile.blueprint)
|
||||
app.register_blueprint(view_settings.blueprint)
|
||||
|
||||
# APIS
|
||||
app.register_blueprint(api_media.blueprint)
|
||||
app.register_blueprint(api_group.blueprint)
|
||||
app.register_blueprint(api_account.blueprint)
|
||||
|
||||
# FILTERS
|
||||
app.register_blueprint(filters.blueprint)
|
||||
|
||||
# CACHE AND COMPRESS
|
||||
cache.init_app(app)
|
||||
compress.init_app(app)
|
||||
|
||||
# Yupee! We got there :3
|
||||
print("Done!")
|
||||
logging.info("Gallery started successfully!")
|
||||
return app
|
293
onlylegs/api.py
Normal file
293
onlylegs/api.py
Normal file
|
@ -0,0 +1,293 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
"""
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import logging
|
||||
from uuid import uuid4
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
flash,
|
||||
abort,
|
||||
send_from_directory,
|
||||
jsonify,
|
||||
request,
|
||||
current_app,
|
||||
)
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from colorthief import ColorThief
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import Users, Pictures, Albums, AlbumJunction
|
||||
from onlylegs.utils.metadata import yoink
|
||||
from onlylegs.utils.generate_image import generate_thumbnail
|
||||
|
||||
|
||||
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
||||
|
||||
|
||||
@blueprint.route("/account/picture/<int:user_id>", methods=["POST"])
|
||||
@login_required
|
||||
def account_picture(user_id):
|
||||
"""
|
||||
Returns the profile of a user
|
||||
"""
|
||||
user = db.get_or_404(Users, user_id)
|
||||
file = request.files.get("file", None)
|
||||
|
||||
# If no image is uploaded, return 404 error
|
||||
if not file:
|
||||
return jsonify({"error": "No file uploaded"}), 400
|
||||
if user.id != current_user.id:
|
||||
return jsonify({"error": "You are not allowed to do this, go away"}), 403
|
||||
|
||||
# Get file extension, generate random name and set file path
|
||||
img_ext = pathlib.Path(file.filename).suffix.replace(".", "").lower()
|
||||
img_name = str(user.id)
|
||||
img_path = os.path.join(current_app.config["PFP_FOLDER"], img_name + "." + img_ext)
|
||||
|
||||
# Check if file extension is allowed
|
||||
if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
|
||||
logging.info("File extension not allowed: %s", img_ext)
|
||||
return jsonify({"error": "File extension not allowed"}), 403
|
||||
|
||||
if user.picture:
|
||||
# Delete cached files and old image
|
||||
os.remove(os.path.join(current_app.config["PFP_FOLDER"], user.picture))
|
||||
cache_name = user.picture.rsplit(".")[0]
|
||||
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
||||
cache_name + "*"
|
||||
):
|
||||
os.remove(cache_file)
|
||||
|
||||
# Save file
|
||||
try:
|
||||
file.save(img_path)
|
||||
except OSError as err:
|
||||
logging.info("Error saving file %s because of %s", img_path, err)
|
||||
return jsonify({"error": "Error saving file"}), 500
|
||||
|
||||
img_colors = ColorThief(img_path).get_color()
|
||||
|
||||
# Save to database
|
||||
user.colour = img_colors
|
||||
user.picture = str(img_name + "." + img_ext)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "File uploaded"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/account/username/<int:user_id>", methods=["POST"])
|
||||
@login_required
|
||||
def account_username(user_id):
|
||||
"""
|
||||
Returns the profile of a user
|
||||
"""
|
||||
user = db.get_or_404(Users, user_id)
|
||||
new_name = request.form["name"]
|
||||
|
||||
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
|
||||
|
||||
# Validate the form
|
||||
if not new_name or not username_regex.match(new_name):
|
||||
return jsonify({"error": "Username is invalid"}), 400
|
||||
if user.id != current_user.id:
|
||||
return jsonify({"error": "You are not allowed to do this, go away"}), 403
|
||||
|
||||
# Save to database
|
||||
user.username = new_name
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "Username changed"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/media/<path:path>", methods=["GET"])
|
||||
def media(path):
|
||||
"""
|
||||
Returns image from media folder
|
||||
r for resolution, thumb for thumbnail etc
|
||||
e for extension, jpg, png etc
|
||||
"""
|
||||
res = request.args.get("r", default=None).strip()
|
||||
ext = request.args.get("e", default=None).strip()
|
||||
|
||||
# if no args are passed, return the raw file
|
||||
if not res and not ext:
|
||||
if not os.path.exists(os.path.join(current_app.config["MEDIA_FOLDER"], path)):
|
||||
abort(404)
|
||||
return send_from_directory(current_app.config["MEDIA_FOLDER"], path)
|
||||
|
||||
# Generate thumbnail, if None is returned a server error occured
|
||||
thumb = generate_thumbnail(path, res, ext)
|
||||
if not thumb:
|
||||
abort(500)
|
||||
|
||||
response = send_from_directory(os.path.dirname(thumb), os.path.basename(thumb))
|
||||
response.headers["Cache-Control"] = "public, max-age=31536000"
|
||||
response.headers["Expires"] = "31536000"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@blueprint.route("/media/upload", methods=["POST"])
|
||||
@login_required
|
||||
def upload():
|
||||
"""
|
||||
Uploads an image to the server and saves it to the database
|
||||
"""
|
||||
form_file = request.files.get("file", None)
|
||||
form = request.form
|
||||
|
||||
if not form_file:
|
||||
return jsonify({"message": "No file"}), 400
|
||||
|
||||
# Get file extension, generate random name and set file path
|
||||
img_ext = pathlib.Path(form_file.filename).suffix.replace(".", "").lower()
|
||||
img_name = "GWAGWA_" + str(uuid4())
|
||||
img_path = os.path.join(
|
||||
current_app.config["UPLOAD_FOLDER"], img_name + "." + img_ext
|
||||
)
|
||||
|
||||
# Check if file extension is allowed
|
||||
if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
|
||||
logging.info("File extension not allowed: %s", img_ext)
|
||||
return jsonify({"message": "File extension not allowed"}), 403
|
||||
|
||||
# Save file
|
||||
try:
|
||||
form_file.save(img_path)
|
||||
except OSError as err:
|
||||
logging.info("Error saving file %s because of %s", img_path, err)
|
||||
return jsonify({"message": "Error saving file"}), 500
|
||||
|
||||
img_exif = yoink(img_path) # Get EXIF data
|
||||
img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette
|
||||
|
||||
# Save to database
|
||||
query = Pictures(
|
||||
author_id=current_user.id,
|
||||
filename=img_name + "." + img_ext,
|
||||
mimetype=img_ext,
|
||||
exif=img_exif,
|
||||
colours=img_colors,
|
||||
description=form["description"],
|
||||
alt=form["alt"],
|
||||
)
|
||||
|
||||
db.session.add(query)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "File uploaded"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/media/delete/<int:image_id>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_image(image_id):
|
||||
"""
|
||||
Deletes an image from the server and database
|
||||
"""
|
||||
post = db.get_or_404(Pictures, image_id)
|
||||
|
||||
# Check if image exists and if user is allowed to delete it (author)
|
||||
if post.author_id != current_user.id:
|
||||
logging.info("User %s tried to delete image %s", current_user.id, image_id)
|
||||
return (
|
||||
jsonify({"message": "You are not allowed to delete this image, heck off"}),
|
||||
403,
|
||||
)
|
||||
|
||||
# Delete file
|
||||
try:
|
||||
os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], post.filename))
|
||||
except FileNotFoundError:
|
||||
logging.warning(
|
||||
"File not found: %s, already deleted or never existed", post.filename
|
||||
)
|
||||
|
||||
# Delete cached files
|
||||
cache_name = post.filename.rsplit(".")[0]
|
||||
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
||||
cache_name + "*"
|
||||
):
|
||||
os.remove(cache_file)
|
||||
|
||||
AlbumJunction.query.filter_by(picture_id=image_id).delete()
|
||||
db.session.delete(post)
|
||||
db.session.commit()
|
||||
|
||||
logging.info("Removed image (%s) %s", image_id, post.filename)
|
||||
flash(["Image was all in Le Head!", "1"])
|
||||
return jsonify({"message": "Image deleted"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/group/create", methods=["POST"])
|
||||
@login_required
|
||||
def create_group():
|
||||
"""
|
||||
Creates a group
|
||||
"""
|
||||
group_name = request.form.get("name", "").strip()
|
||||
group_description = request.form.get("description", "").strip()
|
||||
group_author = current_user.id
|
||||
|
||||
new_group = Albums(
|
||||
name=group_name,
|
||||
description=group_description,
|
||||
author_id=group_author,
|
||||
)
|
||||
|
||||
db.session.add(new_group)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "Group created", "id": new_group.id})
|
||||
|
||||
|
||||
@blueprint.route("/group/modify", methods=["POST"])
|
||||
@login_required
|
||||
def modify_group():
|
||||
"""
|
||||
Changes the images in a group
|
||||
"""
|
||||
group_id = request.form.get("group", "").strip()
|
||||
image_id = request.form.get("image", "").strip()
|
||||
action = request.form.get("action", "").strip()
|
||||
|
||||
group = db.get_or_404(Albums, group_id)
|
||||
db.get_or_404(Pictures, image_id) # Check if image exists
|
||||
|
||||
if group.author_id != current_user.id:
|
||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||
|
||||
junction_exist = AlbumJunction.query.filter_by(
|
||||
album_id=group_id, picture_id=image_id
|
||||
).first()
|
||||
|
||||
if action == "add" and not junction_exist:
|
||||
db.session.add(AlbumJunction(album_id=group_id, picture_id=image_id))
|
||||
elif request.form["action"] == "remove":
|
||||
AlbumJunction.query.filter_by(album_id=group_id, picture_id=image_id).delete()
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Group modified"})
|
||||
|
||||
|
||||
@blueprint.route("/group/delete", methods=["POST"])
|
||||
def delete_group():
|
||||
"""
|
||||
Deletes a group
|
||||
"""
|
||||
group_id = request.form.get("group", "").strip()
|
||||
group = db.get_or_404(Albums, group_id)
|
||||
|
||||
if group.author_id != current_user.id:
|
||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||
|
||||
AlbumJunction.query.filter_by(album_id=group_id).delete()
|
||||
db.session.delete(group)
|
||||
db.session.commit()
|
||||
|
||||
flash(["Group yeeted!", "1"])
|
||||
return jsonify({"message": "Group deleted"})
|
|
@ -1,93 +0,0 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
"""
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import logging
|
||||
|
||||
from flask import Blueprint, jsonify, request, current_app
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from colorthief import ColorThief
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import User
|
||||
|
||||
|
||||
blueprint = Blueprint("account_api", __name__, url_prefix="/api/account")
|
||||
|
||||
|
||||
@blueprint.route("/picture/<int:user_id>", methods=["POST"])
|
||||
@login_required
|
||||
def account_picture(user_id):
|
||||
"""
|
||||
Returns the profile of a user
|
||||
"""
|
||||
user = db.get_or_404(User, user_id)
|
||||
file = request.files["file"]
|
||||
|
||||
# If no image is uploaded, return 404 error
|
||||
if not file:
|
||||
return jsonify({"error": "No file uploaded"}), 400
|
||||
if user.id != current_user.id:
|
||||
return jsonify({"error": "You are not allowed to do this, go away"}), 403
|
||||
|
||||
# Get file extension, generate random name and set file path
|
||||
img_ext = pathlib.Path(file.filename).suffix.replace(".", "").lower()
|
||||
img_name = str(user.id)
|
||||
img_path = os.path.join(current_app.config["PFP_FOLDER"], img_name + "." + img_ext)
|
||||
|
||||
# Check if file extension is allowed
|
||||
if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
|
||||
logging.info("File extension not allowed: %s", img_ext)
|
||||
return jsonify({"error": "File extension not allowed"}), 403
|
||||
|
||||
if user.picture:
|
||||
# Delete cached files and old image
|
||||
os.remove(os.path.join(current_app.config["PFP_FOLDER"], user.picture))
|
||||
cache_name = user.picture.rsplit(".")[0]
|
||||
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
||||
cache_name + "*"
|
||||
):
|
||||
os.remove(cache_file)
|
||||
|
||||
# Save file
|
||||
try:
|
||||
file.save(img_path)
|
||||
except OSError as err:
|
||||
logging.info("Error saving file %s because of %s", img_path, err)
|
||||
return jsonify({"error": "Error saving file"}), 500
|
||||
|
||||
img_colors = ColorThief(img_path).get_color()
|
||||
|
||||
# Save to database
|
||||
user.colour = img_colors
|
||||
user.picture = str(img_name + "." + img_ext)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "File uploaded"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/username/<int:user_id>", methods=["POST"])
|
||||
@login_required
|
||||
def account_username(user_id):
|
||||
"""
|
||||
Returns the profile of a user
|
||||
"""
|
||||
user = db.get_or_404(User, user_id)
|
||||
new_name = request.form["name"]
|
||||
|
||||
username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b")
|
||||
|
||||
# Validate the form
|
||||
if not new_name or not username_regex.match(new_name):
|
||||
return jsonify({"error": "Username is invalid"}), 400
|
||||
if user.id != current_user.id:
|
||||
return jsonify({"error": "You are not allowed to do this, go away"}), 403
|
||||
|
||||
# Save to database
|
||||
user.username = new_name
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "Username changed"}), 200
|
|
@ -1,78 +0,0 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
"""
|
||||
from flask import Blueprint, flash, jsonify, request
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import Post, Group, GroupJunction
|
||||
|
||||
|
||||
blueprint = Blueprint("group_api", __name__, url_prefix="/api/group")
|
||||
|
||||
|
||||
@blueprint.route("/create", methods=["POST"])
|
||||
@login_required
|
||||
def create_group():
|
||||
"""
|
||||
Creates a group
|
||||
"""
|
||||
new_group = Group(
|
||||
name=request.form["name"],
|
||||
description=request.form["description"],
|
||||
author_id=current_user.id,
|
||||
)
|
||||
|
||||
db.session.add(new_group)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "Group created", "id": new_group.id})
|
||||
|
||||
|
||||
@blueprint.route("/modify", methods=["POST"])
|
||||
@login_required
|
||||
def modify_group():
|
||||
"""
|
||||
Changes the images in a group
|
||||
"""
|
||||
group_id = request.form["group"]
|
||||
image_id = request.form["image"]
|
||||
action = request.form["action"]
|
||||
|
||||
group = db.get_or_404(Group, group_id)
|
||||
db.get_or_404(Post, image_id) # Check if image exists
|
||||
|
||||
if group.author_id != current_user.id:
|
||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||
|
||||
if (
|
||||
action == "add"
|
||||
and not GroupJunction.query.filter_by(
|
||||
group_id=group_id, post_id=image_id
|
||||
).first()
|
||||
):
|
||||
db.session.add(GroupJunction(group_id=group_id, post_id=image_id))
|
||||
elif request.form["action"] == "remove":
|
||||
GroupJunction.query.filter_by(group_id=group_id, post_id=image_id).delete()
|
||||
|
||||
db.session.commit()
|
||||
return jsonify({"message": "Group modified"})
|
||||
|
||||
|
||||
@blueprint.route("/delete", methods=["POST"])
|
||||
def delete_group():
|
||||
"""
|
||||
Deletes a group
|
||||
"""
|
||||
group_id = request.form["group"]
|
||||
group = db.get_or_404(Group, group_id)
|
||||
|
||||
if group.author_id != current_user.id:
|
||||
return jsonify({"message": "You are not the owner of this group"}), 403
|
||||
|
||||
GroupJunction.query.filter_by(group_id=group_id).delete()
|
||||
db.session.delete(group)
|
||||
db.session.commit()
|
||||
|
||||
flash(["Group yeeted!", "1"])
|
||||
return jsonify({"message": "Group deleted"})
|
|
@ -1,148 +0,0 @@
|
|||
"""
|
||||
Onlylegs - API endpoints
|
||||
Media upload and retrieval
|
||||
"""
|
||||
from uuid import uuid4
|
||||
import os
|
||||
import pathlib
|
||||
import logging
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
flash,
|
||||
abort,
|
||||
send_from_directory,
|
||||
jsonify,
|
||||
request,
|
||||
current_app,
|
||||
)
|
||||
from flask_login import login_required, current_user
|
||||
|
||||
from colorthief import ColorThief
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import Post, GroupJunction
|
||||
from onlylegs.utils.metadata import yoink
|
||||
from onlylegs.utils.generate_image import generate_thumbnail
|
||||
|
||||
|
||||
blueprint = Blueprint("media_api", __name__, url_prefix="/api/media")
|
||||
|
||||
|
||||
@blueprint.route("/<path:path>", methods=["GET"])
|
||||
def media(path):
|
||||
"""
|
||||
Returns image from media folder
|
||||
r for resolution, thumb for thumbnail etc
|
||||
e for extension, jpg, png etc
|
||||
"""
|
||||
res = request.args.get("r", default=None, type=str)
|
||||
ext = request.args.get("e", default=None, type=str)
|
||||
|
||||
# if no args are passed, return the raw file
|
||||
if not res and not ext:
|
||||
if not os.path.exists(os.path.join(current_app.config["MEDIA_FOLDER"], path)):
|
||||
abort(404)
|
||||
return send_from_directory(current_app.config["MEDIA_FOLDER"], path)
|
||||
|
||||
# Generate thumbnail, if None is returned a server error occured
|
||||
thumb = generate_thumbnail(path, res, ext)
|
||||
if not thumb:
|
||||
abort(500)
|
||||
|
||||
response = send_from_directory(os.path.dirname(thumb), os.path.basename(thumb))
|
||||
response.headers["Cache-Control"] = "public, max-age=31536000"
|
||||
response.headers["Expires"] = "31536000"
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@blueprint.route("/upload", methods=["POST"])
|
||||
@login_required
|
||||
def upload():
|
||||
"""
|
||||
Uploads an image to the server and saves it to the database
|
||||
"""
|
||||
form_file = request.files["file"]
|
||||
form = request.form
|
||||
|
||||
if not form_file:
|
||||
return jsonify({"message": "No file"}), 400
|
||||
|
||||
# Get file extension, generate random name and set file path
|
||||
img_ext = pathlib.Path(form_file.filename).suffix.replace(".", "").lower()
|
||||
img_name = "GWAGWA_" + str(uuid4())
|
||||
img_path = os.path.join(
|
||||
current_app.config["UPLOAD_FOLDER"], img_name + "." + img_ext
|
||||
)
|
||||
|
||||
# Check if file extension is allowed
|
||||
if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys():
|
||||
logging.info("File extension not allowed: %s", img_ext)
|
||||
return jsonify({"message": "File extension not allowed"}), 403
|
||||
|
||||
# Save file
|
||||
try:
|
||||
form_file.save(img_path)
|
||||
except OSError as err:
|
||||
logging.info("Error saving file %s because of %s", img_path, err)
|
||||
return jsonify({"message": "Error saving file"}), 500
|
||||
|
||||
img_exif = yoink(img_path) # Get EXIF data
|
||||
img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette
|
||||
|
||||
# Save to database
|
||||
query = Post(
|
||||
author_id=current_user.id,
|
||||
filename=img_name + "." + img_ext,
|
||||
mimetype=img_ext,
|
||||
exif=img_exif,
|
||||
colours=img_colors,
|
||||
description=form["description"],
|
||||
alt=form["alt"],
|
||||
)
|
||||
|
||||
db.session.add(query)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({"message": "File uploaded"}), 200
|
||||
|
||||
|
||||
@blueprint.route("/delete/<int:image_id>", methods=["POST"])
|
||||
@login_required
|
||||
def delete_image(image_id):
|
||||
"""
|
||||
Deletes an image from the server and database
|
||||
"""
|
||||
post = db.get_or_404(Post, image_id)
|
||||
|
||||
# Check if image exists and if user is allowed to delete it (author)
|
||||
if post.author_id != current_user.id:
|
||||
logging.info("User %s tried to delete image %s", current_user.id, image_id)
|
||||
return (
|
||||
jsonify({"message": "You are not allowed to delete this image, heck off"}),
|
||||
403,
|
||||
)
|
||||
|
||||
# Delete file
|
||||
try:
|
||||
os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], post.filename))
|
||||
except FileNotFoundError:
|
||||
logging.warning(
|
||||
"File not found: %s, already deleted or never existed", post.filename
|
||||
)
|
||||
|
||||
# Delete cached files
|
||||
cache_name = post.filename.rsplit(".")[0]
|
||||
for cache_file in pathlib.Path(current_app.config["CACHE_FOLDER"]).glob(
|
||||
cache_name + "*"
|
||||
):
|
||||
os.remove(cache_file)
|
||||
|
||||
GroupJunction.query.filter_by(post_id=image_id).delete()
|
||||
db.session.delete(post)
|
||||
db.session.commit()
|
||||
|
||||
logging.info("Removed image (%s) %s", image_id, post.filename)
|
||||
flash(["Image was all in Le Head!", "1"])
|
||||
return jsonify({"message": "Image deleted"}), 200
|
134
onlylegs/app.py
Normal file
134
onlylegs/app.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
"""
|
||||
Onlylegs Gallery
|
||||
This is the main app file, it loads all the other files and sets up the app
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
|
||||
from flask_assets import Bundle
|
||||
from flask_migrate import init as migrate_init
|
||||
|
||||
from flask import Flask, render_template, abort
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from onlylegs.extensions import db, migrate, login_manager, assets, compress, cache
|
||||
from onlylegs.config import INSTANCE_DIR, MIGRATIONS_DIR
|
||||
from onlylegs.models import Users
|
||||
from onlylegs.views import (
|
||||
index as view_index,
|
||||
image as view_image,
|
||||
group as view_group,
|
||||
settings as view_settings,
|
||||
profile as view_profile,
|
||||
)
|
||||
from onlylegs import api
|
||||
from onlylegs import auth as view_auth
|
||||
from onlylegs import filters
|
||||
|
||||
app = Flask(__name__, instance_path=INSTANCE_DIR)
|
||||
app.config.from_pyfile("config.py")
|
||||
|
||||
# DATABASE
|
||||
db.init_app(app)
|
||||
migrate.init_app(app, db, directory=MIGRATIONS_DIR)
|
||||
|
||||
# If database file doesn't exist, create it
|
||||
if not os.path.exists(os.path.join(INSTANCE_DIR, "gallery.sqlite3")):
|
||||
print("Creating database")
|
||||
with app.app_context():
|
||||
db.create_all()
|
||||
|
||||
register_user = Users(
|
||||
username=app.config["ADMIN_CONF"]["username"],
|
||||
email=app.config["ADMIN_CONF"]["email"],
|
||||
password=generate_password_hash("changeme!", method="sha256"),
|
||||
)
|
||||
db.session.add(register_user)
|
||||
db.session.commit()
|
||||
|
||||
print(
|
||||
"""
|
||||
####################################################
|
||||
# DEFAULT ADMIN USER GENERATED WITH GIVEN USERNAME #
|
||||
# THE DEFAULT PASSWORD "changeme!" HAS BEEN USED, #
|
||||
# PLEASE UPDATE IT IN THE SETTINGS! #
|
||||
####################################################
|
||||
"""
|
||||
)
|
||||
|
||||
# Check if migrations directory exists, if not create it
|
||||
with app.app_context():
|
||||
if not os.path.exists(MIGRATIONS_DIR):
|
||||
print("Creating migrations directory")
|
||||
migrate_init(directory=MIGRATIONS_DIR)
|
||||
|
||||
# LOGIN MANAGER
|
||||
# can also set session_protection to "strong"
|
||||
# this would protect against session hijacking
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "onlylegs.index"
|
||||
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return Users.query.filter_by(alt_id=user_id).first()
|
||||
|
||||
|
||||
@login_manager.unauthorized_handler
|
||||
def unauthorized():
|
||||
error = 401
|
||||
msg = "You are not authorized to view this page!!!!"
|
||||
return render_template("error.html", error=error, msg=msg), error
|
||||
|
||||
|
||||
# ERROR HANDLERS
|
||||
@app.errorhandler(Exception)
|
||||
def error_page(err):
|
||||
"""
|
||||
Error handlers, if the error is not a HTTP error, return 500
|
||||
"""
|
||||
if not isinstance(err, HTTPException):
|
||||
abort(500)
|
||||
return (
|
||||
render_template("error.html", error=err.code, msg=err.description),
|
||||
err.code,
|
||||
)
|
||||
|
||||
|
||||
# ASSETS
|
||||
assets.init_app(app)
|
||||
|
||||
scripts = Bundle(
|
||||
"js/*.js", output="gen/js.js", depends="js/*.js"
|
||||
) # filter jsmin is broken :c
|
||||
styles = Bundle(
|
||||
"sass/style.sass",
|
||||
filters="libsass, cssmin",
|
||||
output="gen/styles.css",
|
||||
depends="sass/**/*.sass",
|
||||
)
|
||||
|
||||
assets.register("scripts", scripts)
|
||||
assets.register("styles", styles)
|
||||
|
||||
# BLUEPRINTS
|
||||
app.register_blueprint(view_auth.blueprint)
|
||||
app.register_blueprint(view_index.blueprint)
|
||||
app.register_blueprint(view_image.blueprint)
|
||||
app.register_blueprint(view_group.blueprint)
|
||||
app.register_blueprint(view_profile.blueprint)
|
||||
app.register_blueprint(view_settings.blueprint)
|
||||
|
||||
app.register_blueprint(api.blueprint)
|
||||
|
||||
# FILTERS
|
||||
app.register_blueprint(filters.blueprint)
|
||||
|
||||
# CACHE AND COMPRESS
|
||||
cache.init_app(app)
|
||||
compress.init_app(app)
|
||||
|
||||
# Yupee! We got there :3
|
||||
print("Done!")
|
||||
logging.info("Gallery started successfully!")
|
|
@ -11,7 +11,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
|
|||
from flask_login import login_user, logout_user, login_required
|
||||
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import User
|
||||
from onlylegs.models import Users
|
||||
|
||||
|
||||
blueprint = Blueprint("auth", __name__, url_prefix="/auth")
|
||||
|
@ -28,7 +28,7 @@ def login():
|
|||
password = request.form["password"].strip()
|
||||
remember = bool(request.form["remember-me"])
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
user = Users.query.filter_by(username=username).first()
|
||||
|
||||
if not user or not check_password_hash(user.password, password):
|
||||
logging.error("Login attempt from %s", request.remote_addr)
|
||||
|
@ -77,7 +77,7 @@ def register():
|
|||
elif password_repeat != password:
|
||||
error.append("Passwords do not match!")
|
||||
|
||||
user_exists = User.query.filter_by(username=username).first()
|
||||
user_exists = Users.query.filter_by(username=username).first()
|
||||
if user_exists:
|
||||
error.append("User already exists!")
|
||||
|
||||
|
@ -86,7 +86,7 @@ def register():
|
|||
print(error)
|
||||
return jsonify(error), 400
|
||||
|
||||
register_user = User(
|
||||
register_user = Users(
|
||||
username=username,
|
||||
email=email,
|
||||
password=generate_password_hash(password, method="sha256"),
|
||||
|
|
|
@ -13,4 +13,4 @@ migrate = Migrate()
|
|||
login_manager = LoginManager()
|
||||
assets = Environment()
|
||||
compress = Compress()
|
||||
cache = Cache(config={'CACHE_TYPE': 'simple', "CACHE_DEFAULT_TIMEOUT": 300})
|
||||
cache = Cache(config={"CACHE_TYPE": "simple", "CACHE_DEFAULT_TIMEOUT": 300})
|
||||
|
|
|
@ -3,7 +3,7 @@ OnlyLegs filters
|
|||
Custom Jinja2 filters
|
||||
"""
|
||||
from flask import Blueprint
|
||||
from onlylegs.utils.colour import contrast
|
||||
from onlylegs.utils import colour as colour_utils
|
||||
|
||||
|
||||
blueprint = Blueprint("filters", __name__)
|
||||
|
@ -16,7 +16,5 @@ def colour_contrast(colour):
|
|||
a css variable based on the contrast of text required to be readable
|
||||
"color: var(--fg-white);" or "color: var(--fg-black);"
|
||||
"""
|
||||
bright = "var(--fg-white)"
|
||||
dark = "var(--fg-black)"
|
||||
|
||||
return "color: RGB(" + contrast(colour, dark, bright) + ");"
|
||||
colour_obj = colour_utils.Colour(colour)
|
||||
return "rgb(var(--fg-black));" if colour_obj.is_light() else "rgb(var(--fg-white));"
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
"""
|
||||
Gwa Gwa!
|
||||
"""
|
||||
print("Gwa Gwa!")
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"IMAGES_UPLOADED": "%s images uploaded!",
|
||||
"DONT USE THIS": "variable:format(data), jinja2 doesnt use the same method as Django does, odd"
|
||||
}
|
|
@ -6,18 +6,18 @@ from flask_login import UserMixin
|
|||
from onlylegs.extensions import db
|
||||
|
||||
|
||||
class GroupJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class AlbumJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Junction table for posts and groups
|
||||
Joins with posts and groups
|
||||
Junction table for picturess and albums
|
||||
Joins with picturess and albums
|
||||
"""
|
||||
|
||||
__tablename__ = "group_junction"
|
||||
__tablename__ = "album_junction"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
|
||||
post_id = db.Column(db.Integer, db.ForeignKey("post.id"))
|
||||
album_id = db.Column(db.Integer, db.ForeignKey("albums.id"))
|
||||
picture_id = db.Column(db.Integer, db.ForeignKey("pictures.id"))
|
||||
|
||||
date_added = db.Column(
|
||||
db.DateTime,
|
||||
|
@ -26,16 +26,15 @@ class GroupJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
)
|
||||
|
||||
|
||||
class Post(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class Pictures(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Post table
|
||||
Pictures table
|
||||
"""
|
||||
|
||||
__tablename__ = "post"
|
||||
__tablename__ = "pictures"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
||||
author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
|
||||
|
||||
filename = db.Column(db.String, unique=True, nullable=False)
|
||||
mimetype = db.Column(db.String, nullable=False)
|
||||
|
@ -51,37 +50,37 @@ class Post(db.Model): # pylint: disable=too-few-public-methods, C0103
|
|||
server_default=db.func.now(), # pylint: disable=E1102
|
||||
)
|
||||
|
||||
junction = db.relationship("GroupJunction", backref="posts")
|
||||
album_fk = db.relationship("AlbumJunction", backref="pictures")
|
||||
|
||||
|
||||
class Group(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
class Albums(db.Model): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
Group table
|
||||
albums table
|
||||
"""
|
||||
|
||||
__tablename__ = "group"
|
||||
__tablename__ = "albums"
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
name = db.Column(db.String, nullable=False)
|
||||
description = db.Column(db.String, nullable=False)
|
||||
|
||||
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
||||
author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
server_default=db.func.now(), # pylint: disable=E1102
|
||||
)
|
||||
|
||||
junction = db.relationship("GroupJunction", backref="groups")
|
||||
album_fk = db.relationship("AlbumJunction", backref="albums")
|
||||
|
||||
|
||||
class User(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103
|
||||
class Users(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103
|
||||
"""
|
||||
User table
|
||||
Users table
|
||||
"""
|
||||
|
||||
__tablename__ = "user"
|
||||
__tablename__ = "users"
|
||||
|
||||
# Gallery used information
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
@ -100,8 +99,8 @@ class User(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C010
|
|||
server_default=db.func.now(), # pylint: disable=E1102
|
||||
)
|
||||
|
||||
posts = db.relationship("Post", backref="author")
|
||||
groups = db.relationship("Group", backref="author")
|
||||
pictures_fk = db.relationship("Pictures", backref="author")
|
||||
albums_fk = db.relationship("Albums", backref="author")
|
||||
|
||||
def get_id(self):
|
||||
return str(self.alt_id)
|
||||
|
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
src: url('./Rubik.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
font-weight: 300 900;
|
||||
}
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 7.9 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.1 KiB |
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"name": "OnlyLegs",
|
||||
"short_name": "OnlyLegs",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#151515",
|
||||
"theme_color": "#151515",
|
||||
"description": "A gallery built for fast and simple image management!",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon.png",
|
||||
"sizes": "621x621",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"splash_pages": null
|
||||
}
|
||||
|
|
@ -23,9 +23,8 @@
|
|||
|
||||
*
|
||||
box-sizing: border-box
|
||||
font-family: $font
|
||||
|
||||
scrollbar-color: RGB($primary) transparent
|
||||
font-family: $font
|
||||
|
||||
::-webkit-scrollbar
|
||||
width: 0.5rem
|
||||
|
|
|
@ -1,37 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ config.WEBSITE_CONF.name }}</title>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>{{ config.WEBSITE_CONF.name }}</title>
|
||||
<meta name="description" content="{{ config.WEBSITE_CONF.motto }}">
|
||||
<meta name="author" content="{{ config.WEBSITE_CONF.author }}">
|
||||
|
||||
<meta name="description" content="{{ config.WEBSITE_CONF.motto }}"/>
|
||||
<meta name="author" content="{{ config.WEBSITE_CONF.author }}"/>
|
||||
<meta property="og:title" content="{{ config.WEBSITE_CONF.name }}">
|
||||
<meta property="og:description" content="{{ config.WEBSITE_CONF.motto }}">
|
||||
<meta property="og:type" content="website">
|
||||
|
||||
<meta property="og:title" content="{{ config.WEBSITE_CONF.name }}"/>
|
||||
<meta property="og:description" content="{{ config.WEBSITE_CONF.motto }}"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta name="twitter:title" content="{{ config.WEBSITE_CONF.name }}">
|
||||
<meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}">
|
||||
|
||||
<meta name="twitter:title" content="{{ config.WEBSITE_CONF.name }}"/>
|
||||
<meta name="twitter:description" content="{{ config.WEBSITE_CONF.motto }}"/>
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap">
|
||||
|
||||
<!-- phosphor icons!!! -->
|
||||
<!-- phosphor icons -->
|
||||
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
||||
|
||||
<link
|
||||
href="{{url_for('static', filename='logo-black.svg')}}"
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
media="(prefers-color-scheme: light)"/>
|
||||
<link
|
||||
href="{{url_for('static', filename='logo-white.svg')}}"
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
media="(prefers-color-scheme: dark)"/>
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" href="{{url_for('static', filename='icon.png')}}" type="image/png">
|
||||
|
||||
<link rel="manifest" href="static/manifest.json"/>
|
||||
<link rel="prefetch" href="{{url_for('static', filename='fonts/font.css')}}" type="stylesheet"/>
|
||||
{% assets "scripts" %} <script type="text/javascript" src="{{ ASSET_URL }}"></script> {% endassets %}
|
||||
{% assets "styles" %} <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> {% endassets %}
|
||||
{% block head %}{% endblock %}
|
||||
|
@ -70,14 +65,14 @@
|
|||
{% if current_user.picture %}
|
||||
<span class="nav-pfp">
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='pfp/' + current_user.picture) }}?r=pfp&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='pfp/' + current_user.picture) }}?r=pfp&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='pfp/' + current_user.picture) }}?r=pfp&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='pfp/' + current_user.picture) }}?r=pfp&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='pfp/' + current_user.picture) }}?r=icon"
|
||||
src="{{ url_for('api.media', path='pfp/' + current_user.picture) }}?r=icon"
|
||||
alt="Profile picture"
|
||||
onload="imgFade(this)"
|
||||
style="opacity:0;"
|
||||
/>
|
||||
>
|
||||
</picture>
|
||||
</span>
|
||||
{% else %}
|
||||
|
@ -109,12 +104,12 @@
|
|||
<button class="fileDrop-block" type="button">
|
||||
<i class="ph ph-upload"></i>
|
||||
<span class="status">Choose or Drop file</span>
|
||||
<input type="file" id="file" tab-index="-1"/>
|
||||
<input type="file" id="file" tab-index="-1">
|
||||
</button>
|
||||
|
||||
<input class="input-block" type="text" placeholder="alt" id="alt"/>
|
||||
<input class="input-block" type="text" placeholder="description" id="description"/>
|
||||
<input class="input-block" type="text" placeholder="tags" id="tags"/>
|
||||
<input class="input-block" type="text" placeholder="alt" id="alt">
|
||||
<input class="input-block" type="text" placeholder="description" id="description">
|
||||
<input class="input-block" type="text" placeholder="tags" id="tags">
|
||||
<button class="btn-block primary" type="submit">Upload</button>
|
||||
</form>
|
||||
<div class="upload-jobs"></div>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
{% block head %}
|
||||
{% if images %}
|
||||
<meta property="og:image" content="{{ url_for('media_api.media', path='uploads/' + images.0.filename) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('media_api.media', path='uploads/' + images.0.filename) }}">
|
||||
<meta property="og:image" content="{{ url_for('api.media', path='uploads/' + images.0.filename) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('api.media', path='uploads/' + images.0.filename) }}">
|
||||
<meta name="theme-color" content="rgb{{ images.0.colours.0 }}"/>
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
{% endif %}
|
||||
|
@ -47,7 +47,7 @@
|
|||
const formData = new FormData();
|
||||
formData.append("group", formID);
|
||||
|
||||
fetch('{{ url_for('group_api.delete_group') }}', {
|
||||
fetch('{{ url_for('api.delete_group') }}', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
|
@ -143,7 +143,7 @@
|
|||
formData.append("image", formImage);
|
||||
formData.append("action", formAction);
|
||||
|
||||
fetch('{{ url_for('group_api.modify_group') }}', {
|
||||
fetch('{{ url_for('api.modify_group') }}', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
|
@ -185,9 +185,9 @@
|
|||
|
||||
.navigation-item.selected { color: {{ text_colour }} !important; }
|
||||
|
||||
.banner-header,
|
||||
.banner-info,
|
||||
.banner-subtitle {
|
||||
.banner .banner-content .banner-header,
|
||||
.banner .banner-content .banner-info,
|
||||
.banner .banner-content .banner-subtitle {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
.banner-content .link {
|
||||
|
@ -215,10 +215,10 @@
|
|||
{% if images %}
|
||||
<div class="banner">
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + images.0.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + images.0.filename) }}?r=prev&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + images.0.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + images.0.filename) }}?r=prev&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='uploads/' + images.0.filename) }}?r=prev"
|
||||
src="{{ url_for('api.media', path='uploads/' + images.0.filename) }}?r=prev"
|
||||
alt="{% if images.0.alt %}{{ images.0.alt }}{% else %}Group Banner{% endif %}"
|
||||
onload="imgFade(this)" style="opacity:0;"
|
||||
/>
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
{% if return_page %}?page={{ return_page }}{% endif %}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<meta property="og:image" content="{{ url_for('media_api.media', path='uploads/' + image.filename) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('media_api.media', path='uploads/' + image.filename) }}">
|
||||
<meta property="og:image" content="{{ url_for('api.media', path='uploads/' + image.filename) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('api.media', path='uploads/' + image.filename) }}">
|
||||
<meta name="theme-color" content="rgb{{ image.colours.0 }}"/>
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
|||
function deleteConfirm() {
|
||||
popupDissmiss();
|
||||
|
||||
fetch('{{ url_for('media_api.delete_image', image_id=image['id']) }}', {
|
||||
fetch('{{ url_for('api.delete_image', image_id=image['id']) }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
@ -87,7 +87,7 @@
|
|||
<div>
|
||||
<button class="pill-item" onclick="fullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
|
||||
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
|
||||
<a class="pill-item" href="{{ url_for('media_api.media', path='uploads/' + image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
|
||||
<a class="pill-item" href="{{ url_for('api.media', path='uploads/' + image.filename) }}" download onclick="addNotification('Download started!', 4)"><i class="ph ph-file-arrow-down"></i></a>
|
||||
</div>
|
||||
{% if current_user.id == image.author.id %}
|
||||
<div>
|
||||
|
@ -104,18 +104,18 @@
|
|||
{% block content %}
|
||||
<div class="background">
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev&e=png">
|
||||
<img src="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev" alt="{{ image.alt }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev&e=png">
|
||||
<img src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev" alt="{{ image.alt }}" onload="imgFade(this)" style="opacity:0;"/>
|
||||
</picture>
|
||||
</div>
|
||||
|
||||
<div class="image-container {% if close_tab %}collapsed{% endif %}">
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=prev"
|
||||
src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=prev"
|
||||
alt="{{ image.alt }}"
|
||||
onload="imgFade(this)"
|
||||
style="opacity:0;"
|
||||
|
@ -138,7 +138,9 @@
|
|||
<tr>
|
||||
<td>Author</td>
|
||||
<td>
|
||||
<img src="{{ url_for('media_api.media', path='pfp/' + image.author.picture) }}" alt="Profile Picture" class="pfp" onload="imgFade(this)" style="opacity: 0;"/>
|
||||
{% if image.author.picture %}
|
||||
<img src="{{ url_for('api.media', path='pfp/' + image.author.picture) }}" alt="Profile Picture" class="pfp" onload="imgFade(this)" style="opacity: 0;"/>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('profile.profile', id=image.author.id) }}" class="link">{{ image.author.username }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -155,7 +157,9 @@
|
|||
</table>
|
||||
<div class="img-colours">
|
||||
{% for col in image.colours %}
|
||||
<button style="background-color: rgb{{ col }}" onclick="copyToClipboard('rgb{{ col }}')"><i class="ph-fill ph-paint-bucket" style="{{ col|colour_contrast }}"></i></button>
|
||||
<button style="background-color: rgb{{ col }}" onclick="copyToClipboard('rgb{{ col }}')">
|
||||
<i class="ph-fill ph-paint-bucket" style="color:{{ col|colour_contrast }};"></i>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if image.groups %}
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
formData.append("name", formName);
|
||||
formData.append("description", formDescription);
|
||||
|
||||
fetch('{{ url_for('group_api.create_group') }}', {
|
||||
fetch('{{ url_for('api.create_group') }}', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(response => {
|
||||
|
@ -134,10 +134,10 @@
|
|||
{% if group.images|length > 0 %}
|
||||
{% for image in group.images %}
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb"
|
||||
src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb"
|
||||
alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}"
|
||||
class="data-{{ loop.index }}"
|
||||
onload="imgFade(this)"
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
{% macro gallery_item(image) %}
|
||||
<a id="image-{{ image.id }}" class="gallery-item square" href="{{ url_for('image.image', image_id=image.id) }}" style="background-color: rgb{{ image.colours.0 }}">
|
||||
<div class="image-filter"><p class="image-title"><span class="time">{{ image.created_at }}</span></p></div>
|
||||
<a
|
||||
id="image-{{ image.id }}"
|
||||
class="gallery-item square"
|
||||
href="{{ url_for('image.image', image_id=image.id) }}"
|
||||
style="background-color: rgb{{ image.colours.0 }}"
|
||||
draggable="false">
|
||||
<div class="image-filter">
|
||||
<p class="image-subtitle">By {{ image.username }}</p>
|
||||
<p class="image-title"><span class="time">{{ image.created_at }}</span></p>
|
||||
</div>
|
||||
<picture>
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='uploads/' + image.filename) }}?r=thumb"
|
||||
src="{{ url_for('api.media', path='uploads/' + image.filename) }}?r=thumb"
|
||||
alt="{% if image.alt %}{{ image.alt }}{% else %}Image Thumbnail{% endif %}"
|
||||
onload="imgFade(this)"
|
||||
style="opacity:0;"
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
{% block head %}
|
||||
{% if user.picture %}
|
||||
<meta property="og:image" content="{{ url_for('media_api.media', path='pfp/' + user.picture) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('media_api.media', path='pfp/' + user.picture) }}">
|
||||
<meta property="og:image" content="{{ url_for('api.media', path='pfp/' + user.picture) }}"/>
|
||||
<meta name="twitter:image" content="{{ url_for('api.media', path='pfp/' + user.picture) }}">
|
||||
{% endif %}
|
||||
{% if user.colour %}<meta name="theme-color" content="rgb{{ user.colour }}"/>{% endif %}
|
||||
<meta name="twitter:card" content="summary">
|
||||
|
@ -37,10 +37,10 @@
|
|||
<div class="banner-content">
|
||||
{% if user.picture %}
|
||||
<picture class="banner-picture">
|
||||
<source srcset="{{ url_for('media_api.media', path='pfp/' + user.picture) }}?r=pfp&e=webp">
|
||||
<source srcset="{{ url_for('media_api.media', path='pfp/' + user.picture) }}?r=pfp&e=png">
|
||||
<source srcset="{{ url_for('api.media', path='pfp/' + user.picture) }}?r=pfp&e=webp">
|
||||
<source srcset="{{ url_for('api.media', path='pfp/' + user.picture) }}?r=pfp&e=png">
|
||||
<img
|
||||
src="{{ url_for('media_api.media', path='pfp/' + user.picture) }}?r=pfp"
|
||||
src="{{ url_for('api.media', path='pfp/' + user.picture) }}?r=pfp"
|
||||
alt="Profile picture"
|
||||
onload="imgFade(this)"
|
||||
style="opacity:0;"
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
<button class="collapse-indicator"><i class="ph ph-caret-down"></i></button>
|
||||
</div>
|
||||
<div class="info-table">
|
||||
<form method="POST" action="{{ url_for('account_api.account_picture', user_id=current_user.id) }}" enctype="multipart/form-data">
|
||||
<form method="POST" action="{{ url_for('api.account_picture', user_id=current_user.id) }}" enctype="multipart/form-data">
|
||||
<h3>Profile Picture</h3>
|
||||
<input type="file" name="file" tab-index="-1"/>
|
||||
<input type="submit" value="Upload" class="btn-block">
|
||||
</form>
|
||||
<form method="POST" action="{{ url_for('account_api.account_username', user_id=current_user.id) }}" enctype="multipart/form-data">
|
||||
<form method="POST" action="{{ url_for('api.account_username', user_id=current_user.id) }}" enctype="multipart/form-data">
|
||||
<h3>Username</h3>
|
||||
<input type="text" name="name" class="input-block" value="{{ current_user.username }}" />
|
||||
<input type="submit" value="Upload" class="btn-block"/>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# :3
|
|
@ -3,129 +3,67 @@ Colour tools used by OnlyLegs
|
|||
|
||||
Source 1: https://gist.github.com/mathebox/e0805f72e7db3269ec22
|
||||
"""
|
||||
import math
|
||||
|
||||
|
||||
def contrast(background, light, dark, threshold=0.179):
|
||||
"""
|
||||
background: tuple of (r, g, b) values
|
||||
light: color to use if the background is light
|
||||
dark: color to use if the background is dark
|
||||
threshold: the threshold to use for determining lightness, the default is w3 recommended
|
||||
"""
|
||||
red = background[0]
|
||||
green = background[1]
|
||||
blue = background[2]
|
||||
class Colour:
|
||||
def __init__(self, rgb):
|
||||
self.rgb = rgb
|
||||
|
||||
# Calculate contrast
|
||||
colors = [red / 255, green / 255, blue / 255]
|
||||
cont = [
|
||||
col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4
|
||||
for col in colors
|
||||
]
|
||||
lightness = (0.2126 * cont[0]) + (0.7152 * cont[1]) + (0.0722 * cont[2])
|
||||
def is_light(self, threshold=0.179):
|
||||
"""
|
||||
returns True if background is light, False if dark
|
||||
threshold: the threshold to use for determining lightness, the default is w3 recommended
|
||||
"""
|
||||
red, green, blue = self.rgb
|
||||
|
||||
return light if lightness > threshold else dark
|
||||
# Calculate contrast
|
||||
colors = [red / 255, green / 255, blue / 255]
|
||||
cont = [
|
||||
col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4
|
||||
for col in colors
|
||||
]
|
||||
lightness = (0.2126 * cont[0]) + (0.7152 * cont[1]) + (0.0722 * cont[2])
|
||||
|
||||
return True if lightness > threshold else False
|
||||
|
||||
def rgb_to_hsv(r, g, b):
|
||||
r = float(r)
|
||||
g = float(g)
|
||||
b = float(b)
|
||||
high = max(r, g, b)
|
||||
low = min(r, g, b)
|
||||
h, s, v = high, high, high
|
||||
def to_hsv(self):
|
||||
r, g, b = self.rgb
|
||||
high = max(r, g, b)
|
||||
low = min(r, g, b)
|
||||
h, s, v = high, high, high
|
||||
|
||||
d = high - low
|
||||
s = 0 if high == 0 else d/high
|
||||
|
||||
if high == low:
|
||||
h = 0.0
|
||||
else:
|
||||
h = {
|
||||
r: (g - b) / d + (6 if g < b else 0),
|
||||
g: (b - r) / d + 2,
|
||||
b: (r - g) / d + 4,
|
||||
}[high]
|
||||
h /= 6
|
||||
|
||||
return h, s, v
|
||||
|
||||
|
||||
def hsv_to_rgb(h, s, v):
|
||||
i = math.floor(h*6)
|
||||
f = h*6 - i
|
||||
p = v * (1-s)
|
||||
q = v * (1-f*s)
|
||||
t = v * (1-(1-f)*s)
|
||||
|
||||
r, g, b = [
|
||||
(v, t, p),
|
||||
(q, v, p),
|
||||
(p, v, t),
|
||||
(p, q, v),
|
||||
(t, p, v),
|
||||
(v, p, q),
|
||||
][int(i % 6)]
|
||||
|
||||
return r, g, b
|
||||
|
||||
|
||||
def rgb_to_hsl(r, g, b):
|
||||
r = float(r)
|
||||
g = float(g)
|
||||
b = float(b)
|
||||
high = max(r, g, b)
|
||||
low = min(r, g, b)
|
||||
h, s, v = ((high + low) / 2,)*3
|
||||
|
||||
if high == low:
|
||||
h = 0.0
|
||||
s = 0.0
|
||||
else:
|
||||
d = high - low
|
||||
s = d / (2 - high - low) if l > 0.5 else d / (high + low)
|
||||
h = {
|
||||
r: (g - b) / d + (6 if g < b else 0),
|
||||
g: (b - r) / d + 2,
|
||||
b: (r - g) / d + 4,
|
||||
}[high]
|
||||
h /= 6
|
||||
s = 0 if high == 0 else d / high
|
||||
|
||||
return h, s, v
|
||||
if high == low:
|
||||
h = 0.0
|
||||
else:
|
||||
h = {
|
||||
r: (g - b) / d + (6 if g < b else 0),
|
||||
g: (b - r) / d + 2,
|
||||
b: (r - g) / d + 4,
|
||||
}[high]
|
||||
h /= 6
|
||||
|
||||
return h, s, v
|
||||
|
||||
def hsl_to_rgb(h, s, l):
|
||||
def hue_to_rgb(p, q, t):
|
||||
t += 1 if t < 0 else 0
|
||||
t -= 1 if t > 1 else 0
|
||||
if t < 1/6:
|
||||
return p + (q - p) * 6 * t
|
||||
if t < 1/2:
|
||||
return q
|
||||
if t < 2/3:
|
||||
p + (q - p) * (2/3 - t) * 6
|
||||
return p
|
||||
def to_hsl(self):
|
||||
r, g, b = self.rgb
|
||||
high = max(r, g, b)
|
||||
low = min(r, g, b)
|
||||
h, s, v = ((high + low) / 2,) * 3
|
||||
|
||||
if s == 0:
|
||||
r, g, b = l, l, l
|
||||
else:
|
||||
q = l * (1 + s) if l < 0.5 else l + s - l * s
|
||||
p = 2 * l - q
|
||||
r = hue_to_rgb(p, q, h + 1/3)
|
||||
g = hue_to_rgb(p, q, h)
|
||||
b = hue_to_rgb(p, q, h - 1/3)
|
||||
if high == low:
|
||||
h = 0.0
|
||||
s = 0.0
|
||||
else:
|
||||
d = high - low
|
||||
s = d / (2 - high - low) if l > 0.5 else d / (high + low)
|
||||
h = {
|
||||
r: (g - b) / d + (6 if g < b else 0),
|
||||
g: (b - r) / d + 2,
|
||||
b: (r - g) / d + 4,
|
||||
}[high]
|
||||
h /= 6
|
||||
|
||||
return r, g, b
|
||||
|
||||
|
||||
def hsv_to_hsl(h, s, v):
|
||||
l = 0.5 * v * (2 - s)
|
||||
s = v * s / (1 - math.fabs(2*l-1))
|
||||
return h, s, l
|
||||
|
||||
|
||||
def hsl_to_hsv(h, s, l):
|
||||
v = (2*l + s*(1-math.fabs(2*l-1)))/2
|
||||
s = 2*(v-l)/v
|
||||
return h, s, v
|
||||
return h, s, v
|
||||
|
|
|
@ -18,10 +18,10 @@ def yoink(file_path):
|
|||
"""
|
||||
if not os.path.isfile(file_path):
|
||||
return None
|
||||
|
||||
|
||||
img_exif = {}
|
||||
file = Image.open(file_path)
|
||||
|
||||
|
||||
img_exif["FileName"] = os.path.basename(file_path)
|
||||
img_exif["FileSize"] = os.path.getsize(file_path)
|
||||
img_exif["FileFormat"] = img_exif["FileName"].split(".")[-1]
|
||||
|
@ -34,9 +34,9 @@ def yoink(file_path):
|
|||
img_exif[value] = tags[tag]
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
file.close()
|
||||
|
||||
|
||||
return _format_data(img_exif)
|
||||
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ sounds more limiting that it actually is in this gallery
|
|||
"""
|
||||
from flask import Blueprint, render_template, url_for, request
|
||||
|
||||
from onlylegs.models import Post, User, GroupJunction, Group
|
||||
from onlylegs.models import Pictures, Users, AlbumJunction, Albums
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.utils.colour import contrast
|
||||
from onlylegs.utils import colour
|
||||
|
||||
|
||||
blueprint = Blueprint("group", __name__, url_prefix="/group")
|
||||
|
@ -18,21 +18,21 @@ def groups():
|
|||
"""
|
||||
Group overview, shows all image groups
|
||||
"""
|
||||
groups = Group.query.all()
|
||||
groups = Albums.query.all()
|
||||
|
||||
# For each group, get the 3 most recent images
|
||||
for group in groups:
|
||||
group.author_username = (
|
||||
User.query.with_entities(User.username)
|
||||
.filter(User.id == group.author_id)
|
||||
Users.query.with_entities(Users.username)
|
||||
.filter(Users.id == group.author_id)
|
||||
.first()[0]
|
||||
)
|
||||
|
||||
# Get the 3 most recent images
|
||||
images = (
|
||||
GroupJunction.query.with_entities(GroupJunction.post_id)
|
||||
.filter(GroupJunction.group_id == group.id)
|
||||
.order_by(GroupJunction.date_added.desc())
|
||||
AlbumJunction.query.with_entities(AlbumJunction.picture_id)
|
||||
.filter(AlbumJunction.album_id == group.id)
|
||||
.order_by(AlbumJunction.date_added.desc())
|
||||
.limit(3)
|
||||
)
|
||||
|
||||
|
@ -40,8 +40,10 @@ def groups():
|
|||
group.images = []
|
||||
for image in images:
|
||||
group.images.append(
|
||||
Post.query.with_entities(Post.filename, Post.alt, Post.colours, Post.id)
|
||||
.filter(Post.id == image[0])
|
||||
Pictures.query.with_entities(
|
||||
Pictures.filename, Pictures.alt, Pictures.colours, Pictures.id
|
||||
)
|
||||
.filter(Pictures.id == image[0])
|
||||
.first()
|
||||
)
|
||||
|
||||
|
@ -54,26 +56,29 @@ def group(group_id):
|
|||
Group view, shows all images in a group
|
||||
"""
|
||||
# Get the group, if it doesn't exist, 404
|
||||
group = db.get_or_404(Group, group_id, description="Group not found! D:")
|
||||
group = db.get_or_404(Albums, group_id, description="Group not found! D:")
|
||||
|
||||
# Get all images in the group from the junction table
|
||||
junction = (
|
||||
GroupJunction.query.with_entities(GroupJunction.post_id)
|
||||
.filter(GroupJunction.group_id == group_id)
|
||||
.order_by(GroupJunction.date_added.desc())
|
||||
AlbumJunction.query.with_entities(AlbumJunction.picture_id)
|
||||
.filter(AlbumJunction.album_id == group_id)
|
||||
.order_by(AlbumJunction.date_added.desc())
|
||||
.all()
|
||||
)
|
||||
|
||||
# Get the image data for each image in the group
|
||||
images = []
|
||||
for image in junction:
|
||||
images.append(Post.query.filter(Post.id == image[0]).first())
|
||||
images.append(Pictures.query.filter(Pictures.id == image[0]).first())
|
||||
|
||||
# Check contrast for the first image in the group for the banner
|
||||
text_colour = "rgb(var(--fg-black))"
|
||||
if images:
|
||||
text_colour = contrast(
|
||||
images[0].colours[0], "rgb(var(--fg-black))", "rgb(var(--fg-white))"
|
||||
colour_obj = colour.Colour(images[0].colours[0])
|
||||
text_colour = (
|
||||
"rgb(var(--fg-black));"
|
||||
if colour_obj.is_light()
|
||||
else "rgb(var(--fg-white));"
|
||||
)
|
||||
|
||||
return render_template(
|
||||
|
@ -87,12 +92,12 @@ def group_post(group_id, image_id):
|
|||
Image view, shows the image and its metadata from a specific group
|
||||
"""
|
||||
# Get the image, if it doesn't exist, 404
|
||||
image = db.get_or_404(Post, image_id, description="Image not found :<")
|
||||
image = db.get_or_404(Pictures, image_id, description="Image not found :<")
|
||||
|
||||
# Get all groups the image is in
|
||||
groups = (
|
||||
GroupJunction.query.with_entities(GroupJunction.group_id)
|
||||
.filter(GroupJunction.post_id == image_id)
|
||||
AlbumJunction.query.with_entities(AlbumJunction.album_id)
|
||||
.filter(AlbumJunction.picture_id == image_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
|
@ -100,24 +105,24 @@ def group_post(group_id, image_id):
|
|||
image.groups = []
|
||||
for group in groups:
|
||||
image.groups.append(
|
||||
Group.query.with_entities(Group.id, Group.name)
|
||||
.filter(Group.id == group[0])
|
||||
Albums.query.with_entities(Albums.id, Albums.name)
|
||||
.filter(Albums.id == group[0])
|
||||
.first()
|
||||
)
|
||||
|
||||
# Get the next and previous images in the group
|
||||
next_url = (
|
||||
GroupJunction.query.with_entities(GroupJunction.post_id)
|
||||
.filter(GroupJunction.group_id == group_id)
|
||||
.filter(GroupJunction.post_id > image_id)
|
||||
.order_by(GroupJunction.date_added.asc())
|
||||
AlbumJunction.query.with_entities(AlbumJunction.picture_id)
|
||||
.filter(AlbumJunction.album_id == group_id)
|
||||
.filter(AlbumJunction.picture_id > image_id)
|
||||
.order_by(AlbumJunction.date_added.asc())
|
||||
.first()
|
||||
)
|
||||
prev_url = (
|
||||
GroupJunction.query.with_entities(GroupJunction.post_id)
|
||||
.filter(GroupJunction.group_id == group_id)
|
||||
.filter(GroupJunction.post_id < image_id)
|
||||
.order_by(GroupJunction.date_added.desc())
|
||||
AlbumJunction.query.with_entities(AlbumJunction.picture_id)
|
||||
.filter(AlbumJunction.album_id == group_id)
|
||||
.filter(AlbumJunction.picture_id < image_id)
|
||||
.order_by(AlbumJunction.date_added.desc())
|
||||
.first()
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ Onlylegs - Image View
|
|||
"""
|
||||
from math import ceil
|
||||
from flask import Blueprint, render_template, url_for, current_app, request
|
||||
from onlylegs.models import Post, GroupJunction, Group
|
||||
from onlylegs.models import Pictures, AlbumJunction, Albums
|
||||
from onlylegs.extensions import db
|
||||
|
||||
|
||||
|
@ -16,12 +16,12 @@ def image(image_id):
|
|||
Image view, shows the image and its metadata
|
||||
"""
|
||||
# Get the image, if it doesn't exist, 404
|
||||
image = db.get_or_404(Post, image_id, description="Image not found :<")
|
||||
image = db.get_or_404(Pictures, image_id, description="Image not found :<")
|
||||
|
||||
# Get all groups the image is in
|
||||
groups = (
|
||||
GroupJunction.query.with_entities(GroupJunction.group_id)
|
||||
.filter(GroupJunction.post_id == image_id)
|
||||
AlbumJunction.query.with_entities(AlbumJunction.album_id)
|
||||
.filter(AlbumJunction.picture_id == image_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
|
@ -29,23 +29,23 @@ def image(image_id):
|
|||
image.groups = []
|
||||
for group in groups:
|
||||
image.groups.append(
|
||||
Group.query.with_entities(Group.id, Group.name)
|
||||
.filter(Group.id == group[0])
|
||||
Albums.query.with_entities(Albums.id, Albums.name)
|
||||
.filter(Albums.id == group[0])
|
||||
.first()
|
||||
)
|
||||
|
||||
# Get the next and previous images
|
||||
# Check if there is a group ID set
|
||||
next_url = (
|
||||
Post.query.with_entities(Post.id)
|
||||
.filter(Post.id > image_id)
|
||||
.order_by(Post.id.asc())
|
||||
Pictures.query.with_entities(Pictures.id)
|
||||
.filter(Pictures.id > image_id)
|
||||
.order_by(Pictures.id.asc())
|
||||
.first()
|
||||
)
|
||||
prev_url = (
|
||||
Post.query.with_entities(Post.id)
|
||||
.filter(Post.id < image_id)
|
||||
.order_by(Post.id.desc())
|
||||
Pictures.query.with_entities(Pictures.id)
|
||||
.filter(Pictures.id < image_id)
|
||||
.order_by(Pictures.id.desc())
|
||||
.first()
|
||||
)
|
||||
|
||||
|
@ -56,7 +56,9 @@ def image(image_id):
|
|||
prev_url = url_for("image.image", image_id=prev_url[0])
|
||||
|
||||
# Yoink all the images in the database
|
||||
total_images = Post.query.with_entities(Post.id).order_by(Post.id.desc()).all()
|
||||
total_images = (
|
||||
Pictures.query.with_entities(Pictures.id).order_by(Pictures.id.desc()).all()
|
||||
)
|
||||
limit = current_app.config["UPLOAD_CONF"]["max-load"]
|
||||
|
||||
# If the number of items is less than the limit, no point of calculating the page
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
Onlylegs Gallery - Index view
|
||||
"""
|
||||
from math import ceil
|
||||
|
||||
from flask import Blueprint, render_template, request, current_app
|
||||
from werkzeug.exceptions import abort
|
||||
|
||||
from onlylegs.models import Post
|
||||
from onlylegs.models import Pictures, Users
|
||||
|
||||
|
||||
blueprint = Blueprint("gallery", __name__)
|
||||
|
@ -17,31 +15,32 @@ def index():
|
|||
"""
|
||||
Home page of the website, shows the feed of the latest images
|
||||
"""
|
||||
# meme
|
||||
if request.args.get("coffee") == "please":
|
||||
abort(418)
|
||||
|
||||
# pagination, defaults to page 1 if no page is specified
|
||||
page = request.args.get("page", default=1, type=int)
|
||||
limit = current_app.config["UPLOAD_CONF"]["max-load"]
|
||||
|
||||
# get the total number of images in the database
|
||||
# calculate the total number of pages, and make sure the page number is valid
|
||||
total_images = Post.query.with_entities(Post.id).count()
|
||||
total_images = Pictures.query.with_entities(Pictures.id).count()
|
||||
pages = ceil(max(total_images, limit) / limit)
|
||||
if page > pages:
|
||||
abort(
|
||||
return abort(
|
||||
404,
|
||||
"You have reached the far and beyond, "
|
||||
+ "but you will not find your answers here.",
|
||||
"You have reached the far and beyond, but you will not find your answers here.",
|
||||
)
|
||||
|
||||
# get the images for the current page
|
||||
images = (
|
||||
Post.query.with_entities(
|
||||
Post.filename, Post.alt, Post.colours, Post.created_at, Post.id
|
||||
Pictures.query.with_entities(
|
||||
Pictures.filename,
|
||||
Pictures.alt,
|
||||
Pictures.colours,
|
||||
Pictures.created_at,
|
||||
Pictures.id,
|
||||
Users.username,
|
||||
)
|
||||
.order_by(Post.id.desc())
|
||||
.join(Users)
|
||||
.order_by(Pictures.id.desc())
|
||||
.offset((page - 1) * limit)
|
||||
.limit(limit)
|
||||
.all()
|
||||
|
|
|
@ -5,7 +5,7 @@ from flask import Blueprint, render_template, request
|
|||
from werkzeug.exceptions import abort
|
||||
from flask_login import current_user
|
||||
|
||||
from onlylegs.models import Post, User, Group
|
||||
from onlylegs.models import Pictures, Users, Albums
|
||||
from onlylegs.extensions import db
|
||||
|
||||
|
||||
|
@ -27,9 +27,9 @@ def profile():
|
|||
abort(404, "You must be logged in to view your own profile!")
|
||||
|
||||
# Get the user's data
|
||||
user = db.get_or_404(User, user_id, description="User not found :<")
|
||||
user = db.get_or_404(Users, user_id, description="User not found :<")
|
||||
|
||||
images = Post.query.filter(Post.author_id == user_id).all()
|
||||
groups = Group.query.filter(Group.author_id == user_id).all()
|
||||
images = Pictures.query.filter(Pictures.author_id == user_id).all()
|
||||
groups = Albums.query.filter(Albums.author_id == user_id).all()
|
||||
|
||||
return render_template("profile.html", user=user, images=images, groups=groups)
|
||||
|
|
307
poetry.lock
generated
307
poetry.lock
generated
|
@ -2,14 +2,14 @@
|
|||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.11.1"
|
||||
version = "1.11.2"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "alembic-1.11.1-py3-none-any.whl", hash = "sha256:dc871798a601fab38332e38d6ddb38d5e734f60034baeb8e2db5b642fccd8ab8"},
|
||||
{file = "alembic-1.11.1.tar.gz", hash = "sha256:6a810a6b012c88b33458fceb869aef09ac75d6ace5291915ba7fae44de372c01"},
|
||||
{file = "alembic-1.11.2-py3-none-any.whl", hash = "sha256:7981ab0c4fad4fe1be0cf183aae17689fe394ff874fd2464adb774396faf0796"},
|
||||
{file = "alembic-1.11.2.tar.gz", hash = "sha256:678f662130dc540dac12de0ea73de9f89caea9dbea138f60ef6263149bf84657"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -24,14 +24,14 @@ tz = ["python-dateutil"]
|
|||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.15.5"
|
||||
version = "2.15.6"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "astroid-2.15.5-py3-none-any.whl", hash = "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324"},
|
||||
{file = "astroid-2.15.5.tar.gz", hash = "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f"},
|
||||
{file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"},
|
||||
{file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -44,37 +44,34 @@ wrapt = [
|
|||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "23.3.0"
|
||||
version = "23.7.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"},
|
||||
{file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"},
|
||||
{file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"},
|
||||
{file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"},
|
||||
{file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"},
|
||||
{file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"},
|
||||
{file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"},
|
||||
{file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"},
|
||||
{file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"},
|
||||
{file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"},
|
||||
{file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"},
|
||||
{file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"},
|
||||
{file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"},
|
||||
{file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"},
|
||||
{file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"},
|
||||
{file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"},
|
||||
{file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"},
|
||||
{file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"},
|
||||
{file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"},
|
||||
{file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"},
|
||||
{file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"},
|
||||
{file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"},
|
||||
{file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"},
|
||||
{file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"},
|
||||
{file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
|
||||
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
|
||||
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b5b0ee6d96b345a8b420100b7d71ebfdd19fab5e8301aff48ec270042cd40ac2"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:893695a76b140881531062d48476ebe4a48f5d1e9388177e175d76234ca247cd"},
|
||||
{file = "black-23.7.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:c333286dc3ddca6fdff74670b911cccedacb4ef0a60b34e491b8a67c833b343a"},
|
||||
{file = "black-23.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831d8f54c3a8c8cf55f64d0422ee875eecac26f5f649fb6c1df65316b67c8926"},
|
||||
{file = "black-23.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f3bf2dec7d541b4619b8ce526bda74a6b0bffc480a163fed32eb8b3c9aed8ad"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:f9062af71c59c004cd519e2fb8f5d25d39e46d3af011b41ab43b9c74e27e236f"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:01ede61aac8c154b55f35301fac3e730baf0c9cf8120f65a9cd61a81cfb4a0c3"},
|
||||
{file = "black-23.7.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:327a8c2550ddc573b51e2c352adb88143464bb9d92c10416feb86b0f5aee5ff6"},
|
||||
{file = "black-23.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1c6022b86f83b632d06f2b02774134def5d4d4f1dac8bef16d90cda18ba28a"},
|
||||
{file = "black-23.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:27eb7a0c71604d5de083757fbdb245b1a4fae60e9596514c6ec497eb63f95320"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:8417dbd2f57b5701492cd46edcecc4f9208dc75529bcf76c514864e48da867d9"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:47e56d83aad53ca140da0af87678fb38e44fd6bc0af71eebab2d1f59b1acf1d3"},
|
||||
{file = "black-23.7.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:25cc308838fe71f7065df53aedd20327969d05671bac95b38fdf37ebe70ac087"},
|
||||
{file = "black-23.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:642496b675095d423f9b8448243336f8ec71c9d4d57ec17bf795b67f08132a91"},
|
||||
{file = "black-23.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:ad0014efc7acf0bd745792bd0d8857413652979200ab924fbf239062adc12491"},
|
||||
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
|
||||
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -210,14 +207,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
version = "8.1.6"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||
{file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
|
||||
{file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -263,14 +260,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "dill"
|
||||
version = "0.3.6"
|
||||
description = "serialize all of python"
|
||||
version = "0.3.7"
|
||||
description = "serialize all of Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"},
|
||||
{file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"},
|
||||
{file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"},
|
||||
{file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
|
@ -493,14 +490,14 @@ tornado = ["tornado (>=0.2)"]
|
|||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "6.7.0"
|
||||
version = "6.8.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"},
|
||||
{file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"},
|
||||
{file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"},
|
||||
{file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -509,26 +506,26 @@ zipp = ">=0.5"
|
|||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
perf = ["ipython"]
|
||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
|
||||
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-resources"
|
||||
version = "5.12.0"
|
||||
version = "6.0.0"
|
||||
description = "Read resources from Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"},
|
||||
{file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"},
|
||||
{file = "importlib_resources-6.0.0-py3-none-any.whl", hash = "sha256:d952faee11004c045f785bb5636e8f885bed30dc3c940d5d42798a2a4541c185"},
|
||||
{file = "importlib_resources-6.0.0.tar.gz", hash = "sha256:4cf94875a8368bd89531a756df9a9ebe1f150e0f885030b461237bc7f2d905f2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
|
@ -768,14 +765,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"},
|
||||
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"},
|
||||
{file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
|
||||
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -860,34 +857,34 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
|
|||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.8.0"
|
||||
version = "3.10.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"},
|
||||
{file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"},
|
||||
{file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
|
||||
{file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.17.4"
|
||||
version = "2.17.5"
|
||||
description = "python code static checker"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"},
|
||||
{file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"},
|
||||
{file = "pylint-2.17.5-py3-none-any.whl", hash = "sha256:73995fb8216d3bed149c8d51bba25b2c52a8251a2c8ac846ec668ce38fab5413"},
|
||||
{file = "pylint-2.17.5.tar.gz", hash = "sha256:f7b601cbc06fef7e62a754e2b41294c2aa31f1cb659624b9a85bcba29eaf8252"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.15.4,<=2.17.0-dev0"
|
||||
astroid = ">=2.15.6,<=2.17.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = [
|
||||
{version = ">=0.2", markers = "python_version < \"3.11\""},
|
||||
|
@ -921,52 +918,52 @@ cli = ["click (>=5.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
|
||||
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
|
||||
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
|
||||
{file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
|
||||
{file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
|
||||
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
|
||||
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
|
||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
|
||||
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
|
||||
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
|
||||
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
|
||||
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
|
||||
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -988,53 +985,53 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
|
|||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.17"
|
||||
version = "2.0.19"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:04383f1e3452f6739084184e427e9d5cb4e68ddc765d52157bf5ef30d5eca14f"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:724355973297bbe547f3eb98b46ade65a67a3d5a6303f17ab59a2dc6fb938943"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf07ff9920cb3ca9d73525dfd4f36ddf9e1a83734ea8b4f724edfd9a2c6e82d9"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f389f77c68dc22cb51f026619291c4a38aeb4b7ecb5f998fd145b2d81ca513"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba03518e64d86f000dc24ab3d3a1aa876bcbaa8aa15662ac2df5e81537fa3394"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:218fb20c01e95004f50a3062bf4c447dcb360cab8274232f31947e254f118298"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-win32.whl", hash = "sha256:b47be4c6281a86670ea5cfbbbe6c3a65366a8742f5bc8b986f790533c60b5ddb"},
|
||||
{file = "SQLAlchemy-2.0.17-cp310-cp310-win_amd64.whl", hash = "sha256:74ddcafb6488f382854a7da851c404c394be3729bb3d91b02ad86c5458140eff"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:51736cfb607cf4e8fafb693906f9bc4e5ee55be0b096d44bd7f20cd8489b8571"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8741d3d401383e54b2aada37cbd10f55c5d444b360eae3a82f74a2be568a7710"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ead58cae2a089eee1b0569060999cb5f2b2462109498a0937cc230a7556945a1"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f40e3a7d0a464f1c8593f2991e5520b2f5b26da24e88000bbd4423f86103d4f"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:21583808d37f126a647652c90332ac1d3a102edf3c94bcc3319edcc0ea2300cc"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f593170fc09c5abb1205a738290b39532f7380094dc151805009a07ae0e85330"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-win32.whl", hash = "sha256:b0eaf82cc844f6b46defe15ad243ea00d1e39ed3859df61130c263dc7204da6e"},
|
||||
{file = "SQLAlchemy-2.0.17-cp311-cp311-win_amd64.whl", hash = "sha256:1822620c89779b85f7c23d535c8e04b79c517739ae07aaed48c81e591ed5498e"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2269b1f9b8be47e52b70936069a25a3771eff53367aa5cc59bb94f28a6412e13"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48111d56afea5699bab72c38ec95561796b81befff9e13d1dd5ce251ab25f51d"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28da17059ecde53e2d10ba813d38db942b9f6344360b2958b25872d5cb729d35"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:48b40dc2895841ea89d89df9eb3ac69e2950a659db20a369acf4259f68e6dc1f"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7f31d4e7ca1dd8ca5a27fd5eaa0f9e2732fe769ff7dd35bf7bba179597e4df07"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-win32.whl", hash = "sha256:7830e01b02d440c27f2a5be68296e74ccb55e6a5b5962ffafd360b98930b2e5e"},
|
||||
{file = "SQLAlchemy-2.0.17-cp37-cp37m-win_amd64.whl", hash = "sha256:234678ed6576531b8e4be255b980f20368bf07241a2e67b84e6b0fe679edb9c4"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c6ff5767d954f6091113fedcaaf49cdec2197ae4c5301fe83d5ae4393c82f33"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:aa995b21f853864996e4056d9fde479bcecf8b7bff4beb3555eebbbba815f35d"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:125f9f7e62ddf8b590c069729080ffe18b68a20d9882eb0947f72e06274601d7"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b114a16bc03dfe20b625062e456affd7b9938286e05a3f904a025b9aacc29dd4"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf175d26f6787cce30fe6c04303ca0aeeb0ad40eeb22e3391f24b32ec432a1e1"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e2d5c3596254cf1a96474b98e7ce20041c74c008b0f101c1cb4f8261cb77c6d3"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-win32.whl", hash = "sha256:513411d73503a6fc5804f01fae3b3d44f267c1b3a06cfeac02e9286a7330e857"},
|
||||
{file = "SQLAlchemy-2.0.17-cp38-cp38-win_amd64.whl", hash = "sha256:40a3dc52b2b16f08b5c16b9ee7646329e4b3411e9280e5e8d57b19eaa51cbef4"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e3189432db2f5753b4fde1aa90a61c69976f4e7e31d1cf4611bfe3514ed07478"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6150560fcffc6aee5ec9a97419ac768c7a9f56baf7a7eb59cb4b1b6a4d463ad9"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910d45bf3673f0e4ef13858674bd23cfdafdc8368b45b948bf511797dbbb401d"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0aeb3afaa19f187a70fa592fbe3c20a056b57662691fd3abf60f016aa5c1848"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:36a87e26fe8fa8c466fae461a8fcb780d0a1cbf8206900759fc6fe874475a3ce"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e3a6b2788f193756076061626679c5c5a6d600ddf8324f986bc72004c3e9d92e"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-win32.whl", hash = "sha256:af7e2ba75bf84b64adb331918188dda634689a2abb151bc1a583e488363fd2f8"},
|
||||
{file = "SQLAlchemy-2.0.17-cp39-cp39-win_amd64.whl", hash = "sha256:394ac3adf3676fad76d4b8fcecddf747627f17f0738dc94bac15f303d05b03d4"},
|
||||
{file = "SQLAlchemy-2.0.17-py3-none-any.whl", hash = "sha256:cc9c2630c423ac4973492821b2969f5fe99d9736f3025da670095668fbfcd4d5"},
|
||||
{file = "SQLAlchemy-2.0.17.tar.gz", hash = "sha256:e186e9e95fb5d993b075c33fe4f38a22105f7ce11cecb5c17b5618181e356702"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9deaae357edc2091a9ed5d25e9ee8bba98bcfae454b3911adeaf159c2e9ca9e3"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bf0fd65b50a330261ec7fe3d091dfc1c577483c96a9fa1e4323e932961aa1b5"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d90ccc15ba1baa345796a8fb1965223ca7ded2d235ccbef80a47b85cea2d71a"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4e688f6784427e5f9479d1a13617f573de8f7d4aa713ba82813bcd16e259d1"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:584f66e5e1979a7a00f4935015840be627e31ca29ad13f49a6e51e97a3fb8cae"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c69ce70047b801d2aba3e5ff3cba32014558966109fecab0c39d16c18510f15"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-win32.whl", hash = "sha256:96f0463573469579d32ad0c91929548d78314ef95c210a8115346271beeeaaa2"},
|
||||
{file = "SQLAlchemy-2.0.19-cp310-cp310-win_amd64.whl", hash = "sha256:22bafb1da60c24514c141a7ff852b52f9f573fb933b1e6b5263f0daa28ce6db9"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6894708eeb81f6d8193e996257223b6bb4041cb05a17cd5cf373ed836ef87a2"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8f2afd1aafded7362b397581772c670f20ea84d0a780b93a1a1529da7c3d369"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15afbf5aa76f2241184c1d3b61af1a72ba31ce4161013d7cb5c4c2fca04fd6e"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc05b59142445a4efb9c1fd75c334b431d35c304b0e33f4fa0ff1ea4890f92e"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5831138f0cc06b43edf5f99541c64adf0ab0d41f9a4471fd63b54ae18399e4de"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3afa8a21a9046917b3a12ffe016ba7ebe7a55a6fc0c7d950beb303c735c3c3ad"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-win32.whl", hash = "sha256:c896d4e6ab2eba2afa1d56be3d0b936c56d4666e789bfc59d6ae76e9fcf46145"},
|
||||
{file = "SQLAlchemy-2.0.19-cp311-cp311-win_amd64.whl", hash = "sha256:024d2f67fb3ec697555e48caeb7147cfe2c08065a4f1a52d93c3d44fc8e6ad1c"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:89bc2b374ebee1a02fd2eae6fd0570b5ad897ee514e0f84c5c137c942772aa0c"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4d410a76c3762511ae075d50f379ae09551d92525aa5bb307f8343bf7c2c12"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f469f15068cd8351826df4080ffe4cc6377c5bf7d29b5a07b0e717dddb4c7ea2"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cda283700c984e699e8ef0fcc5c61f00c9d14b6f65a4f2767c97242513fcdd84"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:43699eb3f80920cc39a380c159ae21c8a8924fe071bccb68fc509e099420b148"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win32.whl", hash = "sha256:61ada5831db36d897e28eb95f0f81814525e0d7927fb51145526c4e63174920b"},
|
||||
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win_amd64.whl", hash = "sha256:57d100a421d9ab4874f51285c059003292433c648df6abe6c9c904e5bd5b0828"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:16a310f5bc75a5b2ce7cb656d0e76eb13440b8354f927ff15cbaddd2523ee2d1"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7b5e3856cbf1876da4e9d9715546fa26b6e0ba1a682d5ed2fc3ca4c7c3ec5b"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e7b69d9ced4b53310a87117824b23c509c6fc1f692aa7272d47561347e133b6"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9eb4575bfa5afc4b066528302bf12083da3175f71b64a43a7c0badda2be365"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6b54d1ad7a162857bb7c8ef689049c7cd9eae2f38864fc096d62ae10bc100c7d"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5d6afc41ca0ecf373366fd8e10aee2797128d3ae45eb8467b19da4899bcd1ee0"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-win32.whl", hash = "sha256:430614f18443b58ceb9dedec323ecddc0abb2b34e79d03503b5a7579cd73a531"},
|
||||
{file = "SQLAlchemy-2.0.19-cp38-cp38-win_amd64.whl", hash = "sha256:eb60699de43ba1a1f77363f563bb2c652f7748127ba3a774f7cf2c7804aa0d3d"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a752b7a9aceb0ba173955d4f780c64ee15a1a991f1c52d307d6215c6c73b3a4c"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7351c05db355da112e056a7b731253cbeffab9dfdb3be1e895368513c7d70106"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa51ce4aea583b0c6b426f4b0563d3535c1c75986c4373a0987d84d22376585b"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae7473a67cd82a41decfea58c0eac581209a0aa30f8bc9190926fbf628bb17f7"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851a37898a8a39783aab603c7348eb5b20d83c76a14766a43f56e6ad422d1ec8"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539010665c90e60c4a1650afe4ab49ca100c74e6aef882466f1de6471d414be7"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-win32.whl", hash = "sha256:f82c310ddf97b04e1392c33cf9a70909e0ae10a7e2ddc1d64495e3abdc5d19fb"},
|
||||
{file = "SQLAlchemy-2.0.19-cp39-cp39-win_amd64.whl", hash = "sha256:8e712cfd2e07b801bc6b60fdf64853bc2bd0af33ca8fa46166a23fe11ce0dbb0"},
|
||||
{file = "SQLAlchemy-2.0.19-py3-none-any.whl", hash = "sha256:314145c1389b021a9ad5aa3a18bac6f5d939f9087d7fc5443be28cba19d2c972"},
|
||||
{file = "SQLAlchemy-2.0.19.tar.gz", hash = "sha256:77a14fa20264af73ddcdb1e2b9c5a829b8cc6b8304d0f093271980e36c200a3f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1079,14 +1076,14 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.11.8"
|
||||
version = "0.12.1"
|
||||
description = "Style preserving TOML library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"},
|
||||
{file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"},
|
||||
{file = "tomlkit-0.12.1-py3-none-any.whl", hash = "sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899"},
|
||||
{file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1218,19 +1215,19 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.15.0"
|
||||
version = "3.16.2"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"},
|
||||
{file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"},
|
||||
{file = "zipp-3.16.2-py3-none-any.whl", hash = "sha256:679e51dd4403591b2d6838a48de3d283f3d188412a9782faadf845f298736ba0"},
|
||||
{file = "zipp-3.16.2.tar.gz", hash = "sha256:ebc15946aa78bd63458992fc81ec3b6f7b1e92d51c35e6de1c3804e73b799147"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OnlyLegs"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
repository = "https://github.com/Fluffy-Bean/onlylegs"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
|
|
4
run.py
4
run.py
|
@ -25,9 +25,9 @@ Configuration()
|
|||
|
||||
|
||||
if DEBUG:
|
||||
from onlylegs import create_app
|
||||
from onlylegs.app import app
|
||||
|
||||
create_app().run(host=ADDRESS, port=PORT, debug=True, threaded=True)
|
||||
app.run(host=ADDRESS, port=PORT, debug=True, threaded=True)
|
||||
else:
|
||||
from setup.runner import OnlyLegs # pylint: disable=C0412
|
||||
import sys
|
||||
|
|
Loading…
Reference in a new issue