From 7c553e99b87b25d52286dffd10ea445858445ddf Mon Sep 17 00:00:00 2001 From: Fluffy-Bean Date: Sun, 9 Apr 2023 19:12:35 +0000 Subject: [PATCH] Switch to Flask-SQLAlchemy Add FLask-Migrate for next step in the Migration :wink: --- gallery/__init__.py | 81 ++++++------------ gallery/api.py | 64 +++++++------- gallery/auth.py | 14 ++-- gallery/db.py | 153 ---------------------------------- gallery/extensions.py | 13 +++ gallery/models.py | 138 ++++++++++++++++++++++++++++++ gallery/views/group.py | 69 +++++++-------- gallery/views/image.py | 33 ++++---- gallery/views/index.py | 17 ++-- gallery/views/profile.py | 9 +- poetry.lock | 175 ++++++++++++++++++++++----------------- pyproject.toml | 5 +- 12 files changed, 368 insertions(+), 403 deletions(-) delete mode 100644 gallery/db.py create mode 100644 gallery/extensions.py create mode 100644 gallery/models.py diff --git a/gallery/__init__.py b/gallery/__init__.py index 1a23ff6..833570e 100644 --- a/gallery/__init__.py +++ b/gallery/__init__.py @@ -2,41 +2,31 @@ Onlylegs Gallery This is the main app file, it loads all the other files and sets up the app """ - # Import system modules import os import logging # Flask -from flask_compress import Compress -from flask_caching import Cache -from flask_assets import Environment, Bundle -from flask_login import LoginManager +from flask_assets import Bundle from flask import Flask, render_template, abort from werkzeug.exceptions import HTTPException +from gallery.extensions import db, migrate, login_manager, assets, compress, cache +from gallery.models import Users +from gallery.views import index, image, group, settings, profile +from gallery import api +from gallery import auth + # Configuration import platformdirs from dotenv import load_dotenv from yaml import safe_load -# Import database -from sqlalchemy.orm import sessionmaker -from gallery import db - USER_DIR = platformdirs.user_config_dir("onlylegs") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() -login_manager = LoginManager() -assets = Environment() -cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 300}) -compress = Compress() - - -def create_app(test_config=None): # pylint: disable=R0914 +def create_app(): # pylint: disable=R0914 """ Create and configure the main app """ @@ -54,7 +44,7 @@ def create_app(test_config=None): # pylint: disable=R0914 # App configuration app.config.from_mapping( SECRET_KEY=os.environ.get("FLASK_SECRET"), - DATABASE=os.path.join(app.instance_path, "gallery.sqlite3"), + SQLALCHEMY_DATABASE_URI=("sqlite:///gallery.sqlite3"), UPLOAD_FOLDER=os.path.join(USER_DIR, "uploads"), ALLOWED_EXTENSIONS=conf["upload"]["allowed-extensions"], MAX_CONTENT_LENGTH=1024 * 1024 * conf["upload"]["max-size"], @@ -63,10 +53,8 @@ def create_app(test_config=None): # pylint: disable=R0914 WEBSITE_CONF=conf["website"], ) - if test_config is None: - app.config.from_pyfile("config.py", silent=True) - else: - app.config.from_mapping(test_config) + db.init_app(app) + migrate.init_app(app, db) login_manager.init_app(app) login_manager.login_view = "gallery.index" @@ -74,7 +62,7 @@ def create_app(test_config=None): # pylint: disable=R0914 @login_manager.user_loader def load_user(user_id): - return db_session.query(db.Users).filter_by(alt_id=user_id).first() + return Users.query.filter_by(alt_id=user_id).first() @login_manager.unauthorized_handler def unauthorized(): @@ -82,23 +70,6 @@ def create_app(test_config=None): # pylint: disable=R0914 msg = "You are not authorized to view this page!!!!" return render_template("error.html", error=error, msg=msg), error - scripts = Bundle( - "js/*.js", - filters="jsmin", - output="gen/js.js", - depends="js/*.js" - ) - - styles = Bundle( - "sass/*.sass", - filters="libsass, cssmin", - output="gen/styles.css", - depends="sass/**/*.sass", - ) - - assets.register("scripts", scripts) - assets.register("styles", styles) - # Error handlers, if the error is not a HTTP error, return 500 @app.errorhandler(Exception) def error_page(err): # noqa @@ -109,30 +80,30 @@ def create_app(test_config=None): # pylint: disable=R0914 err.code, ) - # Load login, registration and logout manager - from gallery import auth + scripts = Bundle("js/*.js", filters="jsmin", output="gen/js.js", depends="js/*.js") + styles = Bundle( + "sass/*.sass", + filters="libsass, cssmin", + output="gen/styles.css", + depends="sass/**/*.sass", + ) + + assets.register("scripts", scripts) + assets.register("styles", styles) + + # Load all the blueprints app.register_blueprint(auth.blueprint) - - # Load the API - from gallery import api - app.register_blueprint(api.blueprint) - - # Load the different views - from gallery.views import index, image, group, settings, profile - app.register_blueprint(index.blueprint) app.register_blueprint(image.blueprint) app.register_blueprint(group.blueprint) app.register_blueprint(profile.blueprint) app.register_blueprint(settings.blueprint) - # Log to file that the app has started - logging.info("Gallery started successfully!") - - # Initialize extensions and return app assets.init_app(app) cache.init_app(app) compress.init_app(app) + + logging.info("Gallery started successfully!") return app diff --git a/gallery/api.py b/gallery/api.py index 7515d41..a3ac1fc 100644 --- a/gallery/api.py +++ b/gallery/api.py @@ -14,16 +14,14 @@ from flask_login import login_required, current_user from colorthief import ColorThief -from sqlalchemy.orm import sessionmaker +from gallery.extensions import db +from gallery.models import Posts, Groups, GroupJunction -from gallery import db from gallery.utils import metadata as mt from gallery.utils.generate_image import generate_thumbnail blueprint = Blueprint("api", __name__, url_prefix="/api") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/file/", methods=["GET"]) @@ -87,7 +85,7 @@ def upload(): img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette # Save to database - query = db.Posts( + query = Posts( author_id=current_user.id, filename=img_name + "." + img_ext, mimetype=img_ext, @@ -97,8 +95,8 @@ def upload(): alt=form["alt"], ) - db_session.add(query) - db_session.commit() + db.session.add(query) + db.session.commit() return "Gwa Gwa" # Return something so the browser doesn't show an error @@ -109,7 +107,7 @@ def delete_image(image_id): """ Deletes an image from the server and database """ - img = db_session.query(db.Posts).filter_by(id=image_id).first() + img = Posts.query.filter_by(id=image_id).first() # Check if image exists and if user is allowed to delete it (author) if img is None: @@ -131,16 +129,15 @@ def delete_image(image_id): for cache_file in pathlib.Path(cache_path).glob(cache_name + "*"): os.remove(cache_file) - # Delete from database - db_session.query(db.Posts).filter_by(id=image_id).delete() - - # Remove all entries in junction table - groups = db_session.query(db.GroupJunction).filter_by(post_id=image_id).all() + post = Posts.query.filter_by(id=image_id).first() + db.session.delete(post) + + groups = GroupJunction.query.filter_by(post_id=image_id).all() for group in groups: - db_session.delete(group) + db.session.delete(group) # Commit all changes - db_session.commit() + db.session.commit() logging.info("Removed image (%s) %s", image_id, img.filename) flash(["Image was all in Le Head!", "1"]) @@ -153,14 +150,14 @@ def create_group(): """ Creates a group """ - new_group = db.Groups( + new_group = Groups( name=request.form["name"], description=request.form["description"], author_id=current_user.id, ) - db_session.add(new_group) - db_session.commit() + db.session.add(new_group) + db.session.commit() return ":3" @@ -175,7 +172,7 @@ def modify_group(): image_id = request.form["image"] action = request.form["action"] - group = db_session.query(db.Groups).filter_by(id=group_id).first() + group = Groups.query.filter_by(id=group_id).first() if group is None: abort(404) @@ -183,20 +180,12 @@ def modify_group(): abort(403) if action == "add": - if not ( - db_session.query(db.GroupJunction) - .filter_by(group_id=group_id, post_id=image_id) - .first() - ): - db_session.add(db.GroupJunction(group_id=group_id, post_id=image_id)) + if 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": - ( - db_session.query(db.GroupJunction) - .filter_by(group_id=group_id, post_id=image_id) - .delete() - ) + db.session.delete(GroupJunction.query.filter_by(group_id=group_id, post_id=image_id).first()) - db_session.commit() + db.session.commit() return ":3" @@ -208,16 +197,21 @@ def delete_group(): """ group_id = request.form["group"] - group = db_session.query(db.Groups).filter_by(id=group_id).first() + group = Groups.query.filter_by(id=group_id).first() if group is None: abort(404) elif group.author_id != current_user.id: abort(403) - db_session.query(db.Groups).filter_by(id=group_id).delete() - db_session.query(db.GroupJunction).filter_by(group_id=group_id).delete() - db_session.commit() + group_del = Groups.query.filter_by(id=group_id).first() + db.session.delete(group_del) + + junction_del = GroupJunction.query.filter_by(group_id=group_id).all() + for junction in junction_del: + db.session.delete(junction) + + db.session.commit() flash(["Group yeeted!", "1"]) return ":3" diff --git a/gallery/auth.py b/gallery/auth.py index 163c3c4..a5a4c5a 100644 --- a/gallery/auth.py +++ b/gallery/auth.py @@ -10,13 +10,11 @@ from werkzeug.security import check_password_hash, generate_password_hash from flask_login import login_user, logout_user, login_required -from sqlalchemy.orm import sessionmaker -from gallery import db +from gallery.extensions import db +from gallery.models import Users blueprint = Blueprint("auth", __name__, url_prefix="/auth") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/login", methods=["POST"]) @@ -30,7 +28,7 @@ def login(): password = request.form["password"].strip() remember = bool(request.form["remember-me"]) - user = db_session.query(db.Users).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) @@ -79,7 +77,7 @@ def register(): elif password_repeat != password: error.append("Passwords do not match!") - user_exists = db_session.query(db.Users).filter_by(username=username).first() + user_exists = Users.query.filter_by(username=username).first() if user_exists: error.append("User already exists!") @@ -93,8 +91,8 @@ def register(): email=email, password=generate_password_hash(password, method="sha256"), ) - db_session.add(register_user) - db_session.commit() + db.session.add(register_user) + db.session.commit() logging.info("User %s registered", username) return "ok", 200 diff --git a/gallery/db.py b/gallery/db.py deleted file mode 100644 index a6b2638..0000000 --- a/gallery/db.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -OnlyLegs - Database models and ions for SQLAlchemy -""" -from uuid import uuid4 -import os -import platformdirs - -from sqlalchemy import ( - create_engine, - Column, - Integer, - String, - DateTime, - ForeignKey, - PickleType, - func, -) -from sqlalchemy.orm import declarative_base, relationship -from flask_login import UserMixin - - -USER_DIR = platformdirs.user_config_dir("onlylegs") -DB_PATH = os.path.join(USER_DIR, "instance", "gallery.sqlite3") - - -# In the future, I want to add support for other databases -engine = create_engine(f"sqlite:///{DB_PATH}", echo=False) -base = declarative_base() - - -class Users(base, UserMixin): # pylint: disable=too-few-public-methods, C0103 - """ - User table - Joins with post, groups, session and log - """ - - __tablename__ = "users" - - # Gallery used information - id = Column(Integer, primary_key=True) - alt_id = Column(String, unique=True, nullable=False, default=str(uuid4())) - profile_picture = Column(String, nullable=True, default=None) - username = Column(String, unique=True, nullable=False) - email = Column(String, unique=True, nullable=False) - password = Column(String, nullable=False) - joined_at = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - - posts = relationship("Posts", backref="users") - groups = relationship("Groups", backref="users") - log = relationship("Logs", backref="users") - - def get_id(self): - return str(self.alt_id) - - -class Posts(base): # pylint: disable=too-few-public-methods, C0103 - """ - Post table - Joins with group_junction - """ - - __tablename__ = "posts" - - id = Column(Integer, primary_key=True) - author_id = Column(Integer, ForeignKey("users.id")) - created_at = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - filename = Column(String, unique=True, nullable=False) - mimetype = Column(String, nullable=False) - exif = Column(PickleType, nullable=False) - colours = Column(PickleType, nullable=False) - description = Column(String, nullable=False) - alt = Column(String, nullable=False) - - junction = relationship("GroupJunction", backref="posts") - - -class Groups(base): # pylint: disable=too-few-public-methods, C0103 - """ - Group table - Joins with group_junction - """ - - __tablename__ = "groups" - - id = Column(Integer, primary_key=True) - name = Column(String, nullable=False) - description = Column(String, nullable=False) - author_id = Column(Integer, ForeignKey("users.id")) - created_at = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - - junction = relationship("GroupJunction", backref="groups") - - -class GroupJunction(base): # pylint: disable=too-few-public-methods, C0103 - """ - Junction table for posts and groups - Joins with posts and groups - """ - - __tablename__ = "group_junction" - - id = Column(Integer, primary_key=True) - date_added = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - group_id = Column(Integer, ForeignKey("groups.id")) - post_id = Column(Integer, ForeignKey("posts.id")) - - -class Logs(base): # pylint: disable=too-few-public-methods, C0103 - """ - Log table - Joins with user - """ - - __tablename__ = "logs" - - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey("users.id")) - ip_address = Column(String, nullable=False) - code = Column(Integer, nullable=False) - note = Column(String, nullable=False) - created_at = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - - -class Bans(base): # pylint: disable=too-few-public-methods, C0103 - """ - Bans table - """ - - __tablename__ = "bans" - - id = Column(Integer, primary_key=True) - ip_address = Column(String, nullable=False) - code = Column(Integer, nullable=False) - note = Column(String, nullable=False) - banned_at = Column( - DateTime, nullable=False, server_default=func.now() # pylint: disable=E1102 - ) - - -# check if database file exists, if not create it -if not os.path.isfile(DB_PATH): - base.metadata.create_all(engine) - print("Database created") diff --git a/gallery/extensions.py b/gallery/extensions.py new file mode 100644 index 0000000..2fa8082 --- /dev/null +++ b/gallery/extensions.py @@ -0,0 +1,13 @@ +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +from flask_login import LoginManager +from flask_assets import Environment +from flask_compress import Compress +from flask_caching import Cache + +db = SQLAlchemy() +migrate = Migrate() +login_manager = LoginManager() +assets = Environment() +compress = Compress() +cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 300}) diff --git a/gallery/models.py b/gallery/models.py new file mode 100644 index 0000000..0988cb1 --- /dev/null +++ b/gallery/models.py @@ -0,0 +1,138 @@ +""" +OnlyLegs - Database models and ions for SQLAlchemy +""" +from uuid import uuid4 + +from flask_login import UserMixin +from .extensions import db + + +class Users(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103 + """ + User table + Joins with post, groups, session and log + """ + + __tablename__ = "users" + + # Gallery used information + id = db.Column(db.Integer, primary_key=True) + alt_id = db.Column(db.String, unique=True, nullable=False, default=str(uuid4())) + profile_picture = db.Column(db.String, nullable=True, default=None) + username = db.Column(db.String, unique=True, nullable=False) + email = db.Column(db.String, unique=True, nullable=False) + password = db.Column(db.String, nullable=False) + joined_at = db.Column( + db.DateTime, + nullable=False, + server_default=db.func.now(), # pylint: disable=E1102 + ) + + posts = db.relationship("Posts", backref="users") + groups = db.relationship("Groups", backref="users") + log = db.relationship("Logs", backref="users") + + def get_id(self): + return str(self.alt_id) + + +class Posts(db.Model): # pylint: disable=too-few-public-methods, C0103 + """ + Post table + Joins with group_junction + """ + + __tablename__ = "posts" + + id = db.Column(db.Integer, primary_key=True) + 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 + ) + filename = db.Column(db.String, unique=True, nullable=False) + mimetype = db.Column(db.String, nullable=False) + exif = db.Column(db.PickleType, nullable=False) + colours = db.Column(db.PickleType, nullable=False) + description = db.Column(db.String, nullable=False) + alt = db.Column(db.String, nullable=False) + + junction = db.relationship("GroupJunction", backref="posts") + + +class Groups(db.Model): # pylint: disable=too-few-public-methods, C0103 + """ + Group table + Joins with group_junction + """ + + __tablename__ = "groups" + + 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("users.id")) + created_at = db.Column( + db.DateTime, + nullable=False, + server_default=db.func.now(), # pylint: disable=E1102 + ) + + junction = db.relationship("GroupJunction", backref="groups") + + +class GroupJunction(db.Model): # pylint: disable=too-few-public-methods, C0103 + """ + Junction table for posts and groups + Joins with posts and groups + """ + + __tablename__ = "group_junction" + + id = db.Column(db.Integer, primary_key=True) + date_added = db.Column( + db.DateTime, + nullable=False, + server_default=db.func.now(), # pylint: disable=E1102 + ) + group_id = db.Column(db.Integer, db.ForeignKey("groups.id")) + post_id = db.Column(db.Integer, db.ForeignKey("posts.id")) + + +class Logs(db.Model): # pylint: disable=too-few-public-methods, C0103 + """ + Log table + Joins with user + """ + + __tablename__ = "logs" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id")) + ip_address = db.Column(db.String, nullable=False) + code = db.Column(db.Integer, nullable=False) + note = db.Column(db.String, nullable=False) + created_at = db.Column( + db.DateTime, + nullable=False, + server_default=db.func.now(), # pylint: disable=E1102 + ) + + +class Bans(db.Model): # pylint: disable=too-few-public-methods, C0103 + """ + Bans table + """ + + __tablename__ = "bans" + + id = db.Column(db.Integer, primary_key=True) + ip_address = db.Column(db.String, nullable=False) + code = db.Column(db.Integer, nullable=False) + note = db.Column(db.String, nullable=False) + banned_at = db.Column( + db.DateTime, + nullable=False, + server_default=db.func.now(), # pylint: disable=E1102 + ) diff --git a/gallery/views/group.py b/gallery/views/group.py index 39b6325..4c91b00 100644 --- a/gallery/views/group.py +++ b/gallery/views/group.py @@ -5,14 +5,11 @@ sounds more limiting that it actually is in this gallery """ from flask import Blueprint, abort, render_template, url_for -from sqlalchemy.orm import sessionmaker -from gallery import db +from gallery.models import Posts, Users, GroupJunction, Groups from gallery.utils import contrast blueprint = Blueprint("group", __name__, url_prefix="/group") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/", methods=["GET"]) @@ -20,21 +17,21 @@ def groups(): """ Group overview, shows all image groups """ - groups = db_session.query(db.Groups).all() + groups = Groups.query.all() # For each group, get the 3 most recent images for group in groups: group.author_username = ( - db_session.query(db.Users.username) - .filter(db.Users.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 = ( - db_session.query(db.GroupJunction.post_id) - .filter(db.GroupJunction.group_id == group.id) - .order_by(db.GroupJunction.date_added.desc()) + GroupJunction.query.with_entities(GroupJunction.post_id) + .filter(GroupJunction.group_id == group.id) + .order_by(GroupJunction.date_added.desc()) .limit(3) ) @@ -42,10 +39,10 @@ def groups(): group.images = [] for image in images: group.images.append( - db_session.query( - db.Posts.filename, db.Posts.alt, db.Posts.colours, db.Posts.id + Posts.query.with_entities( + Posts.filename, Posts.alt, Posts.colours, Posts.id ) - .filter(db.Posts.id == image[0]) + .filter(Posts.id == image[0]) .first() ) @@ -58,32 +55,30 @@ def group(group_id): Group view, shows all images in a group """ # Get the group, if it doesn't exist, 404 - group = db_session.query(db.Groups).filter(db.Groups.id == group_id).first() + group = Groups.query.filter(Groups.id == group_id).first() if group is None: abort(404, "Group not found! D:") # Get the group's author username group.author_username = ( - db_session.query(db.Users.username) - .filter(db.Users.id == group.author_id) + Users.query.with_entities(Users.username) + .filter(Users.id == group.author_id) .first()[0] ) # Get all images in the group from the junction table junction = ( - db_session.query(db.GroupJunction.post_id) - .filter(db.GroupJunction.group_id == group_id) - .order_by(db.GroupJunction.date_added.desc()) + GroupJunction.query.with_entities(GroupJunction.post_id) + .filter(GroupJunction.group_id == group_id) + .order_by(GroupJunction.date_added.desc()) .all() ) # Get the image data for each image in the group images = [] for image in junction: - images.append( - db_session.query(db.Posts).filter(db.Posts.id == image[0]).first() - ) + images.append(Posts.query.filter(Posts.id == image[0]).first()) # Check contrast for the first image in the group for the banner text_colour = "rgb(var(--fg-black))" @@ -103,21 +98,21 @@ 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_session.query(db.Posts).filter(db.Posts.id == image_id).first() + image = Posts.query.filter(Posts.id == image_id).first() if image is None: abort(404, "Image not found") # Get the image's author username image.author_username = ( - db_session.query(db.Users.username) - .filter(db.Users.id == image.author_id) + Users.query.with_entities(Users.username) + .filter(Users.id == image.author_id) .first()[0] ) # Get all groups the image is in groups = ( - db_session.query(db.GroupJunction.group_id) - .filter(db.GroupJunction.post_id == image_id) + GroupJunction.query.with_entities(GroupJunction.group_id) + .filter(GroupJunction.post_id == image_id) .all() ) @@ -125,24 +120,24 @@ def group_post(group_id, image_id): image.groups = [] for group in groups: image.groups.append( - db_session.query(db.Groups.id, db.Groups.name) - .filter(db.Groups.id == group[0]) + Groups.query.with_entities(Groups.id, Groups.name) + .filter(Groups.id == group[0]) .first() ) # Get the next and previous images in the group next_url = ( - db_session.query(db.GroupJunction.post_id) - .filter(db.GroupJunction.group_id == group_id) - .filter(db.GroupJunction.post_id > image_id) - .order_by(db.GroupJunction.date_added.asc()) + 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()) .first() ) prev_url = ( - db_session.query(db.GroupJunction.post_id) - .filter(db.GroupJunction.group_id == group_id) - .filter(db.GroupJunction.post_id < image_id) - .order_by(db.GroupJunction.date_added.desc()) + 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()) .first() ) diff --git a/gallery/views/image.py b/gallery/views/image.py index b6e00b8..9473d7f 100644 --- a/gallery/views/image.py +++ b/gallery/views/image.py @@ -5,13 +5,10 @@ from math import ceil from flask import Blueprint, abort, render_template, url_for, current_app -from sqlalchemy.orm import sessionmaker -from gallery import db +from gallery.models import Posts, Users, GroupJunction, Groups blueprint = Blueprint("image", __name__, url_prefix="/image") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/") @@ -20,21 +17,21 @@ def image(image_id): Image view, shows the image and its metadata """ # Get the image, if it doesn't exist, 404 - image = db_session.query(db.Posts).filter(db.Posts.id == image_id).first() + image = Posts.query.filter(Posts.id == image_id).first() if not image: abort(404, "Image not found :<") # Get the image's author username image.author_username = ( - db_session.query(db.Users.username) - .filter(db.Users.id == image.author_id) + Users.query.with_entities(Users.username) + .filter(Users.id == image.author_id) .first()[0] ) # Get the image's groups groups = ( - db_session.query(db.GroupJunction.group_id) - .filter(db.GroupJunction.post_id == image_id) + GroupJunction.query.with_entities(GroupJunction.group_id) + .filter(GroupJunction.post_id == image_id) .all() ) @@ -42,23 +39,23 @@ def image(image_id): image.groups = [] for group in groups: image.groups.append( - db_session.query(db.Groups.id, db.Groups.name) - .filter(db.Groups.id == group[0]) + Groups.query.with_entities(Groups.name, Groups.id) + .filter(Groups.id == group[0]) .first() ) # Get the next and previous images # Check if there is a group ID set next_url = ( - db_session.query(db.Posts.id) - .filter(db.Posts.id > image_id) - .order_by(db.Posts.id.asc()) + Posts.query.with_entities(Posts.id) + .filter(Posts.id > image_id) + .order_by(Posts.id.asc()) .first() ) prev_url = ( - db_session.query(db.Posts.id) - .filter(db.Posts.id < image_id) - .order_by(db.Posts.id.desc()) + Posts.query.with_entities(Posts.id) + .filter(Posts.id < image_id) + .order_by(Posts.id.desc()) .first() ) @@ -69,7 +66,7 @@ def image(image_id): prev_url = url_for("image.image", image_id=prev_url[0]) # Yoink all the images in the database - total_images = db_session.query(db.Posts.id).order_by(db.Posts.id.desc()).all() + total_images = Posts.query.with_entities(Posts.id).order_by(Posts.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 diff --git a/gallery/views/index.py b/gallery/views/index.py index 9093311..6dfa43f 100644 --- a/gallery/views/index.py +++ b/gallery/views/index.py @@ -6,13 +6,10 @@ from math import ceil from flask import Blueprint, render_template, request, current_app from werkzeug.exceptions import abort -from sqlalchemy.orm import sessionmaker -from gallery import db +from gallery.models import Posts blueprint = Blueprint("gallery", __name__) -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/") @@ -30,7 +27,7 @@ def index(): # 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 = db_session.query(db.Posts.id).count() + total_images = Posts.query.with_entities(Posts.id).count() pages = ceil(max(total_images, limit) / limit) if page > pages: abort( @@ -41,14 +38,10 @@ def index(): # get the images for the current page images = ( - db_session.query( - db.Posts.filename, - db.Posts.alt, - db.Posts.colours, - db.Posts.created_at, - db.Posts.id, + Posts.query.with_entities( + Posts.filename, Posts.alt, Posts.colours, Posts.created_at, Posts.id ) - .order_by(db.Posts.id.desc()) + .order_by(Posts.id.desc()) .offset((page - 1) * limit) .limit(limit) .all() diff --git a/gallery/views/profile.py b/gallery/views/profile.py index 0022e7e..7949735 100644 --- a/gallery/views/profile.py +++ b/gallery/views/profile.py @@ -5,13 +5,10 @@ from flask import Blueprint, render_template, request from werkzeug.exceptions import abort from flask_login import current_user -from sqlalchemy.orm import sessionmaker -from gallery import db +from gallery.models import Posts, Users blueprint = Blueprint("profile", __name__, url_prefix="/profile") -db_session = sessionmaker(bind=db.engine) -db_session = db_session() @blueprint.route("/profile") @@ -29,11 +26,11 @@ def profile(): abort(404, "You must be logged in to view your own profile!") # Get the user's data - user = db_session.query(db.Users).filter(db.Users.id == user_id).first() + user = Users.query.filter(Users.id == user_id).first() if not user: abort(404, "User not found :c") - images = db_session.query(db.Posts).filter(db.Posts.author_id == user_id).all() + images = Posts.query.filter(Posts.author_id == user_id).all() return render_template("profile.html", user=user, images=images) diff --git a/poetry.lock b/poetry.lock index a291140..fa80bad 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,27 @@ # This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. +[[package]] +name = "alembic" +version = "1.10.3" +description = "A database migration tool for SQLAlchemy." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "alembic-1.10.3-py3-none-any.whl", hash = "sha256:b2e0a6cfd3a8ce936a1168320bcbe94aefa3f4463cd773a968a55071beb3cd37"}, + {file = "alembic-1.10.3.tar.gz", hash = "sha256:32a69b13a613aeb7e8093f242da60eff9daed13c0df02fff279c1b06c32965d2"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} +importlib-resources = {version = "*", markers = "python_version < \"3.9\""} +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + +[package.extras] +tz = ["python-dateutil"] + [[package]] name = "astroid" version = "2.15.2" @@ -192,79 +214,6 @@ files = [ [package.extras] graph = ["objgraph (>=1.7.2)"] -[[package]] -name = "dukpy" -version = "0.3.0" -description = "Simple JavaScript interpreter for Python" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "dukpy-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47ed8813baf52ad3e3a7d4c7416173af0693bbfab1f3b685cbf0165e0e376769"}, - {file = "dukpy-0.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4b3a69977d89c83d74e64a5feb7264acb007c251e2eb83bc4e79c818b73b4fc"}, - {file = "dukpy-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:782e60979db86f7ae9d5e84185cf6c252954cbcfda982353dd30ff6a17fef0be"}, - {file = "dukpy-0.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3698f35c184b3319257d4d7bfa796ef109e8f78fc3cef8e22a3bf0f2d0eef774"}, - {file = "dukpy-0.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bd7f6ded168548d808e3a3ac97ccf98ee1a97c327e7e67c13229932f3c923f85"}, - {file = "dukpy-0.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9e044f3e78881f3c1fc0b939349551a9be2e2519d4e670038ce497d7cc780c69"}, - {file = "dukpy-0.3.0-cp310-cp310-win32.whl", hash = "sha256:c1827f1f7282bb0cc329c7f687c0f58d87f5736777e553f483c26636e9bd1960"}, - {file = "dukpy-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:d9697701eb1a01c0044479b3fa501685adc1a699ffe1acbb39b0b724bc1a7bac"}, - {file = "dukpy-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c189ae4b5c5deb2b576cd0b0ae0193dbd7e15a1499491b3798f3ed7aae8274b1"}, - {file = "dukpy-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:531db11c50326c1baa00711a8221995ec0935418c690d02a84ef9ce537968686"}, - {file = "dukpy-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c072d28ff58db698eb5bfa4556f59e5ce4d4f219b176c93375bfda87c117253f"}, - {file = "dukpy-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8171990e640625ad5876a0548072220ebd34c9f0705510144082ce34a2e777b"}, - {file = "dukpy-0.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:74e0a194e8908bfa64ea2e2e353cf28184d498ed675174a96d948ac2dd6db24e"}, - {file = "dukpy-0.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0aebd4be1109e58126ff4e959415f3198390b92dc48cf6144b97caeb786cf0df"}, - {file = "dukpy-0.3.0-cp311-cp311-win32.whl", hash = "sha256:ff24928cf9c14af226cf575640e2166611a79d8fd14ea498183ca7cd7ab349e5"}, - {file = "dukpy-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:a96a600ce653c5fb9c7190af8c1e82b7d212709dfdd31ce65a2e328cbd923dd6"}, - {file = "dukpy-0.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:581bbd180a7d69149a1b3171d987a8d1eedf988ce3d138ca2e1730888012e41a"}, - {file = "dukpy-0.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0636f4658024033427907b3a67b9bbc9c405fd7ee1f924ec1b1eca070d7a6efb"}, - {file = "dukpy-0.3.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1628e9171e900d2b97e45a22709e68f91bb88ef8dbabfc0c1f4f92524eeb900e"}, - {file = "dukpy-0.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:2c1576a480656ee4bce9bb2f471623b894c8ab809617bdf08b8f547a990df063"}, - {file = "dukpy-0.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:87202891b5dd85053321b561173ebbe84ceab58f9cd4e6c028686e5793bfc976"}, - {file = "dukpy-0.3.0-cp36-cp36m-win32.whl", hash = "sha256:6b2ef5b42a666d4cd73618dce1b9b182c02f15cf52598aef4047e0ecbed2f4ed"}, - {file = "dukpy-0.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6e16d07a506e79af132a7d1b4d28b7846d1e980a8a965130bfe755f56922f35e"}, - {file = "dukpy-0.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7708973d15bc01c91e68195338f9db0a6d4b1e663e2a778da2db00b8c27e7488"}, - {file = "dukpy-0.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:218c26430b424cd2fa4a8a0e252acf835719ee2107937d01c7bbc15615b07e0d"}, - {file = "dukpy-0.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d1f25e485a77e1318b95db43717454001e412adec0ba268dfc8eecf3b893d45"}, - {file = "dukpy-0.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5a1614b73884c14a00b496384d2e793bfd07dfcac425eb1fe768e5b870118111"}, - {file = "dukpy-0.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005579d0e3fd7ab35e18138da3172baa59e3305f22a55fbe9961c67204b1ddd"}, - {file = "dukpy-0.3.0-cp37-cp37m-win32.whl", hash = "sha256:e59a93c819cb818251e7d8ad0b548163227fec3b8485c4cdcecfac59abd9db87"}, - {file = "dukpy-0.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d10c0cd5035e3e2dc27d193734537546f1910d2dc0ccd468bb510924313bbaa2"}, - {file = "dukpy-0.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3bee97f928e0477a197fcc66f25a8d46d1ebc7068ddda2f657445cced303111b"}, - {file = "dukpy-0.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bd7e8b90590122b92e8057052e485afdcc4a6145e50036cc55deac045dd6568f"}, - {file = "dukpy-0.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2feb5c2d05b3c9b8fafc9088c5c025a14c9e239f96abb1aa75ebc022f1777e9c"}, - {file = "dukpy-0.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09ae9309490fc578da96611fc50e46f02c32616e53f55f2bc9c864f67e6c759e"}, - {file = "dukpy-0.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf7412d2d6883fe0ff498cbdb0e67e16804972cf216c169d83aa8d5bad50d109"}, - {file = "dukpy-0.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a8e06c2402031e030924088b37bbde27cf43936bf8ff0ff65c9bdfd9bf4ae89c"}, - {file = "dukpy-0.3.0-cp38-cp38-win32.whl", hash = "sha256:d87b932a387d4015d9acdb99b94c788453b19b5aa5fd10584098e042d8c7118a"}, - {file = "dukpy-0.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e31213f8cbbf85d0386f0ea0e478cd0e4dd918a8747d568a6936044dbd21330c"}, - {file = "dukpy-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49f6390bbc47b618fdb19d7af89e73f643f308a2ab9f5d5e0eb161d4508f23c6"}, - {file = "dukpy-0.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a95ff658b7400e71acaab453359ea74d1a1625cebb937d0294a053b6aac3e507"}, - {file = "dukpy-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9087a3321649beb17f91afa6ffde991d477aa0029c3be5ce908369517ac85251"}, - {file = "dukpy-0.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6acc3a3ce997aef380f79f1985636d87701c1841707c0748ee5eff65e396f0b2"}, - {file = "dukpy-0.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1329be71ce19fdda899a0b59cd531b711adc0d30867488f7401b38b518415a9"}, - {file = "dukpy-0.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0c6ebf8e495f9750f2820cdedfe384621369ebef562ed52770d7a9f070e5991e"}, - {file = "dukpy-0.3.0-cp39-cp39-win32.whl", hash = "sha256:6ae877b9d439941e2afcaaaa410ef168c51e885f99665bf591b97a71eafaeb0f"}, - {file = "dukpy-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:1a8df866eb0af6b55f1a27786f5217334a4e904fd04b7c285c4ee5b684072abe"}, - {file = "dukpy-0.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b895adaab9feaec6e33ba221bcfc16bd50710b18346077b8cec06e843355fb6"}, - {file = "dukpy-0.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2edf126a5c8da0b1ffc39381323d3129cf922d041c74c78402652c9efdf74c99"}, - {file = "dukpy-0.3.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f0f517d245b69781ad91dcb6d9d1a9550b2dbb0d8b636b9e8899838780ad211"}, - {file = "dukpy-0.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:99f76adf6f9c40b0501d7fffc1570a7b7dc4eaf8b2d3cb38ac738068ba2731e6"}, - {file = "dukpy-0.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eca56334b67370427c503b65f21424d317b7560620e28809b4852828a9fcea54"}, - {file = "dukpy-0.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3da5a1bc3ce7788ed05cc16fc67f9be5e187ed4f6fedcf1fd6574633a5230be"}, - {file = "dukpy-0.3.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1cb574f3b71545adbccbb7688059b1a63eca057c59ac00004a6de196eb95844a"}, - {file = "dukpy-0.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b4b068796a4d81c37673e9d949a6307dbadc2cd6c2062b6010bd6561a24895fb"}, - {file = "dukpy-0.3.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:87a9ea4cb2593220e0c6abc6a0b5849e940de78c1e464fe6a4339efe655fd3af"}, - {file = "dukpy-0.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1185f27c920889a41e189ba8a2c76211ee84be1ea1bb4c1f7cc4343f9a1a3d2c"}, - {file = "dukpy-0.3.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a60d5b3537800944cb6e8bedbf68a724dea92a6f9a8ce9a48530838e68478716"}, - {file = "dukpy-0.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:157280c79833f223f3dc6effe8981525e68cd262e26947b2cddd57addac9a3d8"}, - {file = "dukpy-0.3.0.tar.gz", hash = "sha256:ca5772e9373f3cf7772a711e65db765c4361dcf6d4e65c5d88cb879e9ee3f5a6"}, -] - -[package.extras] -testing = ["mock", "pytest", "pytest-cov"] -webassets = ["webassets"] - [[package]] name = "flask" version = "2.2.3" @@ -352,6 +301,39 @@ files = [ Flask = ">=1.0.4" Werkzeug = ">=1.0.1" +[[package]] +name = "flask-migrate" +version = "4.0.4" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Flask-Migrate-4.0.4.tar.gz", hash = "sha256:73293d40b10ac17736e715b377e7b7bde474cb8105165d77474df4c3619b10b3"}, + {file = "Flask_Migrate-4.0.4-py3-none-any.whl", hash = "sha256:77580f27ab39bc68be4906a43c56d7674b45075bc4f883b1d0b985db5164d58f"}, +] + +[package.dependencies] +alembic = ">=1.9.0" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "flask-sqlalchemy" +version = "3.0.3" +description = "Add SQLAlchemy support to your Flask application." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Flask-SQLAlchemy-3.0.3.tar.gz", hash = "sha256:2764335f3c9d7ebdc9ed6044afaf98aae9fa50d7a074cef55dde307ec95903ec"}, + {file = "Flask_SQLAlchemy-3.0.3-py3-none-any.whl", hash = "sha256:add5750b2f9cd10512995261ee2aa23fab85bd5626061aa3c564b33bb4aa780a"}, +] + +[package.dependencies] +Flask = ">=2.2" +SQLAlchemy = ">=1.4.18" + [[package]] name = "greenlet" version = "2.0.2" @@ -449,14 +431,14 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "importlib-metadata" -version = "6.2.0" +version = "6.2.1" description = "Read metadata from Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_metadata-6.2.0-py3-none-any.whl", hash = "sha256:8388b74023a138c605fddd0d47cb81dd706232569f56c9aca7d9c7fdb54caeba"}, - {file = "importlib_metadata-6.2.0.tar.gz", hash = "sha256:9127aad2f49d7203e7112098c12b92e4fd1061ccd18548cdfdc49171a8c073cc"}, + {file = "importlib_metadata-6.2.1-py3-none-any.whl", hash = "sha256:f65e478a7c2177bd19517a3a15dac094d253446d8690c5f3e71e735a04312374"}, + {file = "importlib_metadata-6.2.1.tar.gz", hash = "sha256:5a66966b39ff1c14ef5b2d60c1d842b0141fefff0f4cc6365b4bc9446c652807"}, ] [package.dependencies] @@ -467,6 +449,25 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker perf = ["ipython"] testing = ["flake8 (<5)", "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-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +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"}, +] + +[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)"] + [[package]] name = "isort" version = "5.12.0" @@ -587,6 +588,26 @@ files = [ {file = "libsass-0.22.0.tar.gz", hash = "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425"}, ] +[[package]] +name = "mako" +version = "1.2.4" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + [[package]] name = "markupsafe" version = "2.1.2" @@ -1115,4 +1136,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "8ff06b3873e6466deca84a8258f87f8aa250072d02e30564fbd554e826754e61" +content-hash = "88387c49c901feebd4685ee75f6b79c0bbc8cc9c1a64798cd0394b140e165603" diff --git a/pyproject.toml b/pyproject.toml index 1988cbb..67fccd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,19 +9,20 @@ readme = ".github/README.md" [tool.poetry.dependencies] python = "^3.8" Flask = "^2.2.2" +Flask-Sqlalchemy = "^3.0.3" +Flask-Migrate = "^4.0.4" Flask-Compress = "^1.13" Flask-Caching = "^2.0.2" Flask-Assets = "^2.0" Flask-Login = "^0.6.2" -SQLAlchemy = "^2.0.3" python-dotenv = "^0.21.0" gunicorn = "^20.1.0" pyyaml = "^6.0" -libsass = "^0.22.0" colorthief = "^0.2.1" Pillow = "^9.4.0" platformdirs = "^3.0.0" pylint = "^2.16.3" +libsass = "^0.22.0" jsmin = "^3.0.1" cssmin = "^0.2.0"