From 128464d43f46a43ec76688de5ecb34043c99b7a6 Mon Sep 17 00:00:00 2001 From: Fluffy-Bean Date: Fri, 7 Apr 2023 12:35:30 +0000 Subject: [PATCH] Format code with Black --- gallery/__init__.py | 67 +++--- gallery/api.py | 127 ++++++----- gallery/auth.py | 65 +++--- gallery/db.py | 95 ++++++--- gallery/utils/contrast.py | 5 +- gallery/utils/generate_image.py | 38 ++-- gallery/utils/metadata/__init__.py | 56 ++--- gallery/utils/metadata/helpers.py | 328 ++++++++++++++--------------- gallery/utils/metadata/mapping.py | 106 +++++----- gallery/views/group.py | 146 +++++++------ gallery/views/image.py | 74 ++++--- gallery/views/index.py | 50 +++-- gallery/views/profile.py | 12 +- gallery/views/settings.py | 18 +- run.py | 11 +- setup/args.py | 16 +- setup/configuration.py | 82 ++++---- setup/runner.py | 5 +- 18 files changed, 697 insertions(+), 604 deletions(-) diff --git a/gallery/__init__.py b/gallery/__init__.py index 03f252b..f3ef3d9 100644 --- a/gallery/__init__.py +++ b/gallery/__init__.py @@ -25,14 +25,14 @@ from sqlalchemy.orm import sessionmaker from gallery import db -USER_DIR = platformdirs.user_config_dir('onlylegs') +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}) +cache = Cache(config={"CACHE_TYPE": "SimpleCache", "CACHE_DEFAULT_TIMEOUT": 300}) compress = Compress() @@ -40,37 +40,37 @@ def create_app(test_config=None): """ Create and configure the main app """ - app = Flask(__name__, instance_path=os.path.join(USER_DIR, 'instance')) + app = Flask(__name__, instance_path=os.path.join(USER_DIR, "instance")) # Get environment variables - load_dotenv(os.path.join(USER_DIR, '.env')) + load_dotenv(os.path.join(USER_DIR, ".env")) print("Loaded environment variables") # Get config file - with open(os.path.join(USER_DIR, 'conf.yml'), encoding='utf-8', mode='r') as file: + with open(os.path.join(USER_DIR, "conf.yml"), encoding="utf-8", mode="r") as file: conf = safe_load(file) print("Loaded config") # App configuration app.config.from_mapping( - SECRET_KEY=os.environ.get('FLASK_SECRET'), - DATABASE=os.path.join(app.instance_path, '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'], - ADMIN_CONF=conf['admin'], - UPLOAD_CONF=conf['upload'], - WEBSITE_CONF=conf['website'], + SECRET_KEY=os.environ.get("FLASK_SECRET"), + DATABASE=os.path.join(app.instance_path, "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"], + ADMIN_CONF=conf["admin"], + UPLOAD_CONF=conf["upload"], + WEBSITE_CONF=conf["website"], ) if test_config is None: - app.config.from_pyfile('config.py', silent=True) + app.config.from_pyfile("config.py", silent=True) else: app.config.from_mapping(test_config) login_manager.init_app(app) - login_manager.login_view = 'gallery.index' - login_manager.session_protection = 'strong' + login_manager.login_view = "gallery.index" + login_manager.session_protection = "strong" @login_manager.user_loader def load_user(user_id): @@ -79,49 +79,46 @@ def create_app(test_config=None): @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 + msg = "You are not authorized to view this page!!!!" + return render_template("error.html", error=error, msg=msg), error js_pre = Bundle( - 'js/pre/*.js', - filters='jsmin', - output='gen/pre_packed.js', - depends='**' + "js/pre/*.js", filters="jsmin", output="gen/pre_packed.js", depends="**" ) js_post = Bundle( - 'js/post/*.js', - filters='jsmin', - output='gen/post_packed.js', - depends='**' + "js/post/*.js", filters="jsmin", output="gen/post_packed.js", depends="**" ) styles = Bundle( - 'sass/*.sass', - filters='libsass,cssmin', - output='gen/styles.css', - depends='**' + "sass/*.sass", filters="libsass,cssmin", output="gen/styles.css", depends="**" ) - assets.register('js_pre', js_pre) - assets.register('js_post', js_post) - assets.register('styles', styles) + assets.register("js_pre", js_pre) + assets.register("js_post", js_post) + assets.register("styles", styles) # Error handlers, if the error is not a HTTP error, return 500 @app.errorhandler(Exception) def error_page(err): # noqa if not isinstance(err, HTTPException): abort(500) - return render_template('error.html', error=err.code, msg=err.description), err.code + return ( + render_template("error.html", error=err.code, msg=err.description), + err.code, + ) # Load login, registration and logout manager from gallery import auth + 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) @@ -129,7 +126,7 @@ def create_app(test_config=None): app.register_blueprint(settings.blueprint) # Log to file that the app has started - logging.info('Gallery started successfully!') + logging.info("Gallery started successfully!") # Initialize extensions and return app assets.init_app(app) diff --git a/gallery/api.py b/gallery/api.py index f5c5a92..411c4b3 100644 --- a/gallery/api.py +++ b/gallery/api.py @@ -21,26 +21,28 @@ from gallery.utils import metadata as mt from gallery.utils.generate_image import generate_thumbnail -blueprint = Blueprint('api', __name__, url_prefix='/api') +blueprint = Blueprint("api", __name__, url_prefix="/api") db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/file/', methods=['GET']) +@blueprint.route("/file/", methods=["GET"]) def file(file_name): """ Returns a file from the uploads folder r for resolution, 400x400 or thumb for thumbnail """ - res = request.args.get('r', default=None, type=str) # Type of file (thumb, etc) - ext = request.args.get('e', default=None, type=str) # File extension + res = request.args.get("r", default=None, type=str) # Type of file (thumb, etc) + ext = request.args.get("e", default=None, type=str) # File extension file_name = secure_filename(file_name) # Sanitize file name # 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['UPLOAD_FOLDER'], file_name)): + if not os.path.exists( + os.path.join(current_app.config["UPLOAD_FOLDER"], file_name) + ): abort(404) - return send_from_directory(current_app.config['UPLOAD_FOLDER'], file_name) + return send_from_directory(current_app.config["UPLOAD_FOLDER"], file_name) thumb = generate_thumbnail(file_name, res, ext) if not thumb: @@ -57,7 +59,7 @@ def file(file_name): # """ # page_num = request.args.get('page', default=1, type=int) # limit = current_app.config['UPLOAD_CONF']['max-load'] - + # if not page_num: # abort(400, 'No page number specified') # if not isinstance(page_num, int): @@ -71,7 +73,7 @@ def file(file_name): # pages = ceil(max(total_images, limit) / limit) # if page_num > pages: # abort(404, 'You have gone above and beyond, but this isnt a fairy tale.') - + # # 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) @@ -89,15 +91,15 @@ def file(file_name): # 'created_at': image.created_at, # }) # return jsonify(image_list) - -@blueprint.route('/upload', methods=['POST']) + +@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_file = request.files["file"] form = request.form # If no image is uploaded, return 404 error @@ -105,41 +107,45 @@ def upload(): return abort(404) # Get file extension, generate random name and set file path - img_ext = pathlib.Path(form_file.filename).suffix.replace('.', '').lower() + 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) + 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) + if img_ext not in current_app.config["ALLOWED_EXTENSIONS"].keys(): + logging.info("File extension not allowed: %s", img_ext) abort(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) + logging.info("Error saving file %s because of %s", img_path, err) abort(500) img_exif = mt.Metadata(img_path).yoink() # Get EXIF data img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette # Save to database - query = db.Posts(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']) + query = db.Posts( + 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 'Gwa Gwa' # Return something so the browser doesn't show an error + return "Gwa Gwa" # Return something so the browser doesn't show an error -@blueprint.route('/delete/', methods=['POST']) +@blueprint.route("/delete/", methods=["POST"]) @login_required def delete_image(image_id): """ @@ -155,14 +161,16 @@ def delete_image(image_id): # Delete file try: - os.remove(os.path.join(current_app.config['UPLOAD_FOLDER'], img.filename)) + os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], img.filename)) except FileNotFoundError: - logging.warning('File not found: %s, already deleted or never existed', img.filename) + logging.warning( + "File not found: %s, already deleted or never existed", img.filename + ) # Delete cached files - cache_path = os.path.join(platformdirs.user_config_dir('onlylegs'), 'cache') - cache_name = img.filename.rsplit('.')[0] - for cache_file in pathlib.Path(cache_path).glob(cache_name + '*'): + cache_path = os.path.join(platformdirs.user_config_dir("onlylegs"), "cache") + cache_name = img.filename.rsplit(".")[0] + for cache_file in pathlib.Path(cache_path).glob(cache_name + "*"): os.remove(cache_file) # Delete from database @@ -176,36 +184,38 @@ def delete_image(image_id): # Commit all changes db_session.commit() - logging.info('Removed image (%s) %s', image_id, img.filename) - flash(['Image was all in Le Head!', '1']) - return 'Gwa Gwa' + logging.info("Removed image (%s) %s", image_id, img.filename) + flash(["Image was all in Le Head!", "1"]) + return "Gwa Gwa" -@blueprint.route('/group/create', methods=['POST']) +@blueprint.route("/group/create", methods=["POST"]) @login_required def create_group(): """ Creates a group """ - new_group = db.Groups(name=request.form['name'], - description=request.form['description'], - author_id=current_user.id) + new_group = db.Groups( + name=request.form["name"], + description=request.form["description"], + author_id=current_user.id, + ) db_session.add(new_group) db_session.commit() - return ':3' + return ":3" -@blueprint.route('/group/modify', methods=['POST']) +@blueprint.route("/group/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_id = request.form["group"] + image_id = request.form["image"] + action = request.form["action"] group = db_session.query(db.Groups).filter_by(id=group_id).first() @@ -214,28 +224,31 @@ def modify_group(): elif group.author_id != current_user.id: 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)) - elif request.form['action'] == 'remove': - (db_session.query(db.GroupJunction) - .filter_by(group_id=group_id, post_id=image_id) - .delete()) + 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)) + elif request.form["action"] == "remove": + ( + db_session.query(db.GroupJunction) + .filter_by(group_id=group_id, post_id=image_id) + .delete() + ) db_session.commit() - return ':3' + return ":3" -@blueprint.route('/group/delete', methods=['POST']) +@blueprint.route("/group/delete", methods=["POST"]) def delete_group(): """ Deletes a group """ - group_id = request.form['group'] + group_id = request.form["group"] group = db_session.query(db.Groups).filter_by(id=group_id).first() @@ -248,5 +261,5 @@ def delete_group(): db_session.query(db.GroupJunction).filter_by(group_id=group_id).delete() db_session.commit() - flash(['Group yeeted!', '1']) - return ':3' + flash(["Group yeeted!", "1"]) + return ":3" diff --git a/gallery/auth.py b/gallery/auth.py index 36e1bc7..163c3c4 100644 --- a/gallery/auth.py +++ b/gallery/auth.py @@ -14,39 +14,39 @@ from sqlalchemy.orm import sessionmaker from gallery import db -blueprint = Blueprint('auth', __name__, url_prefix='/auth') +blueprint = Blueprint("auth", __name__, url_prefix="/auth") db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/login', methods=['POST']) +@blueprint.route("/login", methods=["POST"]) def login(): """ Log in a registered user by adding the user id to the session """ error = [] - username = request.form['username'].strip() - password = request.form['password'].strip() - remember = bool(request.form['remember-me']) + username = request.form["username"].strip() + password = request.form["password"].strip() + remember = bool(request.form["remember-me"]) user = db_session.query(db.Users).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) - error.append('Username or Password is incorrect!') + logging.error("Login attempt from %s", request.remote_addr) + error.append("Username or Password is incorrect!") if error: abort(403) login_user(user, remember=remember) - logging.info('User %s logged in from %s', username, request.remote_addr) - flash(['Logged in successfully!', '4']) - return 'ok', 200 + logging.info("User %s logged in from %s", username, request.remote_addr) + flash(["Logged in successfully!", "4"]) + return "ok", 200 -@blueprint.route('/register', methods=['POST']) +@blueprint.route("/register", methods=["POST"]) def register(): """ Register a new user @@ -54,55 +54,58 @@ def register(): error = [] # Thanks Fennec for reminding me to strip out the whitespace lol - username = request.form['username'].strip() - email = request.form['email'].strip() - password = request.form['password'].strip() - password_repeat = request.form['password-repeat'].strip() + username = request.form["username"].strip() + email = request.form["email"].strip() + password = request.form["password"].strip() + password_repeat = request.form["password-repeat"].strip() - email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b') - username_regex = re.compile(r'\b[A-Za-z0-9._-]+\b') + email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b") + username_regex = re.compile(r"\b[A-Za-z0-9._-]+\b") # Validate the form if not username or not username_regex.match(username): - error.append('Username is invalid!') + error.append("Username is invalid!") if not email or not email_regex.match(email): - error.append('Email is invalid!') + error.append("Email is invalid!") if not password: - error.append('Password is empty!') + error.append("Password is empty!") elif len(password) < 8: - error.append('Password is too short! Longer than 8 characters pls') + error.append("Password is too short! Longer than 8 characters pls") if not password_repeat: - error.append('Enter password again!') + error.append("Enter password again!") elif password_repeat != password: - error.append('Passwords do not match!') + error.append("Passwords do not match!") user_exists = db_session.query(db.Users).filter_by(username=username).first() if user_exists: - error.append('User already exists!') + error.append("User already exists!") # If there are errors, return them if error: print(error) return jsonify(error), 400 - register_user = db.Users(username=username, email=email, - password=generate_password_hash(password, method='sha256')) + register_user = db.Users( + username=username, + email=email, + password=generate_password_hash(password, method="sha256"), + ) db_session.add(register_user) db_session.commit() - logging.info('User %s registered', username) - return 'ok', 200 + logging.info("User %s registered", username) + return "ok", 200 -@blueprint.route('/logout') +@blueprint.route("/logout") @login_required def logout(): """ Clear the current session, including the stored user id """ logout_user() - flash(['Goodbye!!!', '4']) - return redirect(url_for('gallery.index')) + flash(["Goodbye!!!", "4"]) + return redirect(url_for("gallery.index")) diff --git a/gallery/db.py b/gallery/db.py index d916bd7..8956c5e 100644 --- a/gallery/db.py +++ b/gallery/db.py @@ -5,27 +5,36 @@ from uuid import uuid4 import os import platformdirs -from sqlalchemy import (create_engine, Column, Integer, String, - DateTime, ForeignKey, PickleType, func) +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') +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) +engine = create_engine(f"sqlite:///{DB_PATH}", echo=False) base = declarative_base() -class Users (base, UserMixin): # pylint: disable=too-few-public-methods, C0103 +class Users(base, UserMixin): # pylint: disable=too-few-public-methods, C0103 """ User table Joins with post, groups, session and log """ - __tablename__ = 'users' + + __tablename__ = "users" # Gallery used information id = Column(Integer, primary_key=True) @@ -34,26 +43,31 @@ class Users (base, UserMixin): # pylint: disable=too-few-public-methods, C0103 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 + 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') + 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 +class Posts(base): # pylint: disable=too-few-public-methods, C0103 """ Post table Joins with group_junction """ - __tablename__ = 'posts' + + __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 + 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) @@ -61,66 +75,79 @@ class Posts (base): # pylint: disable=too-few-public-methods, C0103 description = Column(String, nullable=False) alt = Column(String, nullable=False) - junction = relationship('GroupJunction', backref='posts') + junction = relationship("GroupJunction", backref="posts") -class Groups (base): # pylint: disable=too-few-public-methods, C0103 + +class Groups(base): # pylint: disable=too-few-public-methods, C0103 """ Group table Joins with group_junction """ - __tablename__ = 'groups' + + __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 + 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') + junction = relationship("GroupJunction", backref="groups") -class GroupJunction (base): # pylint: disable=too-few-public-methods, C0103 +class GroupJunction(base): # pylint: disable=too-few-public-methods, C0103 """ Junction table for posts and groups Joins with posts and groups """ - __tablename__ = 'group_junction' + + __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')) + 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 +class Logs(base): # pylint: disable=too-few-public-methods, C0103 """ Log table Joins with user """ - __tablename__ = 'logs' + + __tablename__ = "logs" id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey('users.id')) + 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 + created_at = Column( + DateTime, nullable=False, server_default=func.now() + ) # pylint: disable=E1102 -class Bans (base): # pylint: disable=too-few-public-methods, C0103 +class Bans(base): # pylint: disable=too-few-public-methods, C0103 """ Bans table """ - __tablename__ = 'bans' + + __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 + 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') + print("Database created") diff --git a/gallery/utils/contrast.py b/gallery/utils/contrast.py index fb3115c..2872914 100644 --- a/gallery/utils/contrast.py +++ b/gallery/utils/contrast.py @@ -16,7 +16,10 @@ def contrast(background, light, dark, threshold=0.179): # Calculate contrast uicolors = [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 uicolors] + cont = [ + col / 12.92 if col <= 0.03928 else ((col + 0.055) / 1.055) ** 2.4 + for col in uicolors + ] lightness = (0.2126 * cont[0]) + (0.7152 * cont[1]) + (0.0722 * cont[2]) return light if lightness > threshold else dark diff --git a/gallery/utils/generate_image.py b/gallery/utils/generate_image.py index 3763fbb..7a172cc 100644 --- a/gallery/utils/generate_image.py +++ b/gallery/utils/generate_image.py @@ -8,8 +8,8 @@ from PIL import Image, ImageOps from werkzeug.utils import secure_filename -CACHE_PATH = platformdirs.user_config_dir('onlylegs') + '/cache' -UPLOAD_PATH = platformdirs.user_config_dir('onlylegs') + '/uploads' +CACHE_PATH = os.path.join(platformdirs.user_config_dir("onlylegs"), "cache") +UPLOAD_PATH = os.path.join(platformdirs.user_config_dir("onlylegs"), "uploads") def generate_thumbnail(file_name, resolution, ext=None): @@ -25,34 +25,34 @@ def generate_thumbnail(file_name, resolution, ext=None): os.makedirs(CACHE_PATH) # no sussy business - file_name, file_ext = secure_filename(file_name).rsplit('.') + file_name, file_ext = secure_filename(file_name).rsplit(".") if not ext: - ext = file_ext.strip('.') + ext = file_ext.strip(".") # PIL doesnt like jpg so we convert it to jpeg if ext.lower() == "jpg": ext = "jpeg" # Set resolution based on preset resolutions - if resolution in ['prev', 'preview']: + if resolution in ["prev", "preview"]: res_x, res_y = (1920, 1080) - elif resolution in ['thumb', 'thumbnail']: + elif resolution in ["thumb", "thumbnail"]: res_x, res_y = (400, 400) - elif resolution in ['icon', 'favicon']: + elif resolution in ["icon", "favicon"]: res_x, res_y = (10, 10) else: return None # If image has been already generated, return it from the cache - if os.path.exists(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}')): - return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}') + if os.path.exists(os.path.join(CACHE_PATH, f"{file_name}_{res_x}x{res_y}.{ext}")): + return os.path.join(CACHE_PATH, f"{file_name}_{res_x}x{res_y}.{ext}") # Check if image exists in the uploads directory - if not os.path.exists(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}')): + if not os.path.exists(os.path.join(UPLOAD_PATH, f"{file_name}.{file_ext}")): return None # Open image and rotate it based on EXIF data and get ICC profile so colors are correct - image = Image.open(os.path.join(UPLOAD_PATH, f'{file_name}.{file_ext}')) + image = Image.open(os.path.join(UPLOAD_PATH, f"{file_name}.{file_ext}")) image_icc = image.info.get("icc_profile") img_x, img_y = image.size @@ -62,16 +62,20 @@ def generate_thumbnail(file_name, resolution, ext=None): # Save image to cache directory try: - image.save(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}'), - icc_profile=image_icc) + image.save( + os.path.join(CACHE_PATH, f"{file_name}_{res_x}x{res_y}.{ext}"), + icc_profile=image_icc, + ) except OSError: # This usually happens when saving a JPEG with an ICC profile, # so we convert to RGB and try again - image = image.convert('RGB') - image.save(os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}'), - icc_profile=image_icc) + image = image.convert("RGB") + image.save( + os.path.join(CACHE_PATH, f"{file_name}_{res_x}x{res_y}.{ext}"), + icc_profile=image_icc, + ) # No need to keep the image in memory, learned the hard way image.close() - return os.path.join(CACHE_PATH, f'{file_name}_{res_x}x{res_y}.{ext}') + return os.path.join(CACHE_PATH, f"{file_name}_{res_x}x{res_y}.{ext}") diff --git a/gallery/utils/metadata/__init__.py b/gallery/utils/metadata/__init__.py index 801f4d2..f570b06 100644 --- a/gallery/utils/metadata/__init__.py +++ b/gallery/utils/metadata/__init__.py @@ -16,6 +16,7 @@ class Metadata: """ Metadata parser """ + def __init__(self, file_path): """ Initialize the metadata parser @@ -32,17 +33,17 @@ class Metadata: if tag in tags: img_exif[value] = tags[tag] - img_exif['FileName'] = os.path.basename(file_path) - img_exif['FileSize'] = os.path.getsize(file_path) - img_exif['FileFormat'] = img_exif['FileName'].split('.')[-1] - img_exif['FileWidth'], img_exif['FileHeight'] = file.size + img_exif["FileName"] = os.path.basename(file_path) + img_exif["FileSize"] = os.path.getsize(file_path) + img_exif["FileFormat"] = img_exif["FileName"].split(".")[-1] + img_exif["FileWidth"], img_exif["FileHeight"] = file.size file.close() except TypeError: - img_exif['FileName'] = os.path.basename(file_path) - img_exif['FileSize'] = os.path.getsize(file_path) - img_exif['FileFormat'] = img_exif['FileName'].split('.')[-1] - img_exif['FileWidth'], img_exif['FileHeight'] = file.size + img_exif["FileName"] = os.path.basename(file_path) + img_exif["FileSize"] = os.path.getsize(file_path) + img_exif["FileFormat"] = img_exif["FileName"].split(".")[-1] + img_exif["FileWidth"], img_exif["FileHeight"] = file.size self.encoded = img_exif @@ -60,10 +61,10 @@ class Metadata: Formats the data into a dictionary """ exif = { - 'Photographer': {}, - 'Camera': {}, - 'Software': {}, - 'File': {}, + "Photographer": {}, + "Camera": {}, + "Software": {}, + "File": {}, } # Thanks chatGPT xP @@ -73,26 +74,29 @@ class Metadata: if len(mapping_val[key]) == 2: # the helper function works, so not sure why it triggers pylint exif[mapping_name][mapping_val[key][0]] = { - 'raw': value, - 'formatted': ( - getattr(helpers, mapping_val[key][1]) # pylint: disable=E0602 - (value) - ), + "raw": value, + "formatted": ( + getattr( + helpers, mapping_val[key][1] + )( # pylint: disable=E0602 + value + ) + ), } else: exif[mapping_name][mapping_val[key][0]] = { - 'raw': value, + "raw": value, } continue # Remove empty keys - if not exif['Photographer']: - del exif['Photographer'] - if not exif['Camera']: - del exif['Camera'] - if not exif['Software']: - del exif['Software'] - if not exif['File']: - del exif['File'] + if not exif["Photographer"]: + del exif["Photographer"] + if not exif["Camera"]: + del exif["Camera"] + if not exif["Software"]: + del exif["Software"] + if not exif["File"]: + del exif["File"] return exif diff --git a/gallery/utils/metadata/helpers.py b/gallery/utils/metadata/helpers.py index e7b8cd3..0f2b349 100644 --- a/gallery/utils/metadata/helpers.py +++ b/gallery/utils/metadata/helpers.py @@ -21,28 +21,28 @@ def date_format(value): """ Formats the date into a standard format """ - return str(datetime.strptime(value, '%Y:%m:%d %H:%M:%S')) + return str(datetime.strptime(value, "%Y:%m:%d %H:%M:%S")) def fnumber(value): """ Formats the f-number into a standard format """ - return 'ƒ/' + str(value) + return "ƒ/" + str(value) def iso(value): """ Formats the ISO into a standard format """ - return 'ISO ' + str(value) + return "ISO " + str(value) def shutter(value): """ Formats the shutter speed into a standard format """ - return str(value) + 's' + return str(value) + "s" def focal_length(value): @@ -50,27 +50,23 @@ def focal_length(value): Formats the focal length into a standard format """ try: - return str(value[0] / value[1]) + 'mm' + return str(value[0] / value[1]) + "mm" except TypeError: - return str(value) + 'mm' + return str(value) + "mm" def exposure(value): """ Formats the exposure value into a standard format """ - return str(value) + 'EV' + return str(value) + "EV" def color_space(value): """ Maps the value of the color space to a human readable format """ - value_map = { - 0: 'Reserved', - 1: 'sRGB', - 65535: 'Uncalibrated' - } + value_map = {0: "Reserved", 1: "sRGB", 65535: "Uncalibrated"} try: return value_map[int(value)] except KeyError: @@ -82,28 +78,28 @@ def flash(value): Maps the value of the flash to a human readable format """ value_map = { - 0: 'Flash did not fire', - 1: 'Flash fired', - 5: 'Strobe return light not detected', - 7: 'Strobe return light detected', - 9: 'Flash fired, compulsory flash mode', - 13: 'Flash fired, compulsory flash mode, return light not detected', - 15: 'Flash fired, compulsory flash mode, return light detected', - 16: 'Flash did not fire, compulsory flash mode', - 24: 'Flash did not fire, auto mode', - 25: 'Flash fired, auto mode', - 29: 'Flash fired, auto mode, return light not detected', - 31: 'Flash fired, auto mode, return light detected', - 32: 'No flash function', - 65: 'Flash fired, red-eye reduction mode', - 69: 'Flash fired, red-eye reduction mode, return light not detected', - 71: 'Flash fired, red-eye reduction mode, return light detected', - 73: 'Flash fired, compulsory flash mode, red-eye reduction mode', - 77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', - 79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', - 89: 'Flash fired, auto mode, red-eye reduction mode', - 93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', - 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode' + 0: "Flash did not fire", + 1: "Flash fired", + 5: "Strobe return light not detected", + 7: "Strobe return light detected", + 9: "Flash fired, compulsory flash mode", + 13: "Flash fired, compulsory flash mode, return light not detected", + 15: "Flash fired, compulsory flash mode, return light detected", + 16: "Flash did not fire, compulsory flash mode", + 24: "Flash did not fire, auto mode", + 25: "Flash fired, auto mode", + 29: "Flash fired, auto mode, return light not detected", + 31: "Flash fired, auto mode, return light detected", + 32: "No flash function", + 65: "Flash fired, red-eye reduction mode", + 69: "Flash fired, red-eye reduction mode, return light not detected", + 71: "Flash fired, red-eye reduction mode, return light detected", + 73: "Flash fired, compulsory flash mode, red-eye reduction mode", + 77: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", + 79: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", + 89: "Flash fired, auto mode, red-eye reduction mode", + 93: "Flash fired, auto mode, return light not detected, red-eye reduction mode", + 95: "Flash fired, auto mode, return light detected, red-eye reduction mode", } try: return value_map[int(value)] @@ -116,15 +112,15 @@ def exposure_program(value): Maps the value of the exposure program to a human readable format """ value_map = { - 0: 'Not defined', - 1: 'Manual', - 2: 'Normal program', - 3: 'Aperture priority', - 4: 'Shutter priority', - 5: 'Creative program', - 6: 'Action program', - 7: 'Portrait mode', - 8: 'Landscape mode' + 0: "Not defined", + 1: "Manual", + 2: "Normal program", + 3: "Aperture priority", + 4: "Shutter priority", + 5: "Creative program", + 6: "Action program", + 7: "Portrait mode", + 8: "Landscape mode", } try: return value_map[int(value)] @@ -137,14 +133,14 @@ def metering_mode(value): Maps the value of the metering mode to a human readable format """ value_map = { - 0: 'Unknown', - 1: 'Average', - 2: 'Center-Weighted Average', - 3: 'Spot', - 4: 'Multi-Spot', - 5: 'Pattern', - 6: 'Partial', - 255: 'Other' + 0: "Unknown", + 1: "Average", + 2: "Center-Weighted Average", + 3: "Spot", + 4: "Multi-Spot", + 5: "Pattern", + 6: "Partial", + 255: "Other", } try: return value_map[int(value)] @@ -156,11 +152,7 @@ def resolution_unit(value): """ Maps the value of the resolution unit to a human readable format """ - value_map = { - 1: 'No absolute unit of measurement', - 2: 'Inch', - 3: 'Centimeter' - } + value_map = {1: "No absolute unit of measurement", 2: "Inch", 3: "Centimeter"} try: return value_map[int(value)] except KeyError: @@ -172,27 +164,27 @@ def light_source(value): Maps the value of the light source to a human readable format """ value_map = { - 0: 'Unknown', - 1: 'Daylight', - 2: 'Fluorescent', - 3: 'Tungsten (incandescent light)', - 4: 'Flash', - 9: 'Fine weather', - 10: 'Cloudy weather', - 11: 'Shade', - 12: 'Daylight fluorescent (D 5700 - 7100K)', - 13: 'Day white fluorescent (N 4600 - 5400K)', - 14: 'Cool white fluorescent (W 3900 - 4500K)', - 15: 'White fluorescent (WW 3200 - 3700K)', - 17: 'Standard light A', - 18: 'Standard light B', - 19: 'Standard light C', - 20: 'D55', - 21: 'D65', - 22: 'D75', - 23: 'D50', - 24: 'ISO studio tungsten', - 255: 'Other light source', + 0: "Unknown", + 1: "Daylight", + 2: "Fluorescent", + 3: "Tungsten (incandescent light)", + 4: "Flash", + 9: "Fine weather", + 10: "Cloudy weather", + 11: "Shade", + 12: "Daylight fluorescent (D 5700 - 7100K)", + 13: "Day white fluorescent (N 4600 - 5400K)", + 14: "Cool white fluorescent (W 3900 - 4500K)", + 15: "White fluorescent (WW 3200 - 3700K)", + 17: "Standard light A", + 18: "Standard light B", + 19: "Standard light C", + 20: "D55", + 21: "D65", + 22: "D75", + 23: "D50", + 24: "ISO studio tungsten", + 255: "Other light source", } try: return value_map[int(value)] @@ -205,10 +197,10 @@ def scene_capture_type(value): Maps the value of the scene capture type to a human readable format """ value_map = { - 0: 'Standard', - 1: 'Landscape', - 2: 'Portrait', - 3: 'Night scene', + 0: "Standard", + 1: "Landscape", + 2: "Portrait", + 3: "Night scene", } try: return value_map[int(value)] @@ -221,8 +213,8 @@ def white_balance(value): Maps the value of the white balance to a human readable format """ value_map = { - 0: 'Auto white balance', - 1: 'Manual white balance', + 0: "Auto white balance", + 1: "Manual white balance", } try: return value_map[int(value)] @@ -235,9 +227,9 @@ def exposure_mode(value): Maps the value of the exposure mode to a human readable format """ value_map = { - 0: 'Auto exposure', - 1: 'Manual exposure', - 2: 'Auto bracket', + 0: "Auto exposure", + 1: "Manual exposure", + 2: "Auto bracket", } try: return value_map[int(value)] @@ -250,22 +242,14 @@ def sensitivity_type(value): Maps the value of the sensitivity type to a human readable format """ value_map = { - 0: - 'Unknown', - 1: - 'Standard Output Sensitivity', - 2: - 'Recommended Exposure Index', - 3: - 'ISO Speed', - 4: - 'Standard Output Sensitivity and Recommended Exposure Index', - 5: - 'Standard Output Sensitivity and ISO Speed', - 6: - 'Recommended Exposure Index and ISO Speed', - 7: - 'Standard Output Sensitivity, Recommended Exposure Index and ISO Speed', + 0: "Unknown", + 1: "Standard Output Sensitivity", + 2: "Recommended Exposure Index", + 3: "ISO Speed", + 4: "Standard Output Sensitivity and Recommended Exposure Index", + 5: "Standard Output Sensitivity and ISO Speed", + 6: "Recommended Exposure Index and ISO Speed", + 7: "Standard Output Sensitivity, Recommended Exposure Index and ISO Speed", } try: return value_map[int(value)] @@ -278,7 +262,7 @@ def lens_specification(value): Maps the value of the lens specification to a human readable format """ try: - return str(value[0] / value[1]) + 'mm - ' + str(value[2] / value[3]) + 'mm' + return str(value[0] / value[1]) + "mm - " + str(value[2] / value[3]) + "mm" except TypeError: return None @@ -288,55 +272,55 @@ def compression_type(value): Maps the value of the compression type to a human readable format """ value_map = { - 1: 'Uncompressed', - 2: 'CCITT 1D', - 3: 'T4/Group 3 Fax', - 4: 'T6/Group 4 Fax', - 5: 'LZW', - 6: 'JPEG (old-style)', - 7: 'JPEG', - 8: 'Adobe Deflate', - 9: 'JBIG B&W', - 10: 'JBIG Color', - 99: 'JPEG', - 262: 'Kodak 262', - 32766: 'Next', - 32767: 'Sony ARW Compressed', - 32769: 'Packed RAW', - 32770: 'Samsung SRW Compressed', - 32771: 'CCIRLEW', - 32772: 'Samsung SRW Compressed 2', - 32773: 'PackBits', - 32809: 'Thunderscan', - 32867: 'Kodak KDC Compressed', - 32895: 'IT8CTPAD', - 32896: 'IT8LW', - 32897: 'IT8MP', - 32898: 'IT8BL', - 32908: 'PixarFilm', - 32909: 'PixarLog', - 32946: 'Deflate', - 32947: 'DCS', - 33003: 'Aperio JPEG 2000 YCbCr', - 33005: 'Aperio JPEG 2000 RGB', - 34661: 'JBIG', - 34676: 'SGILog', - 34677: 'SGILog24', - 34712: 'JPEG 2000', - 34713: 'Nikon NEF Compressed', - 34715: 'JBIG2 TIFF FX', - 34718: '(MDI) Binary Level Codec', - 34719: '(MDI) Progressive Transform Codec', - 34720: '(MDI) Vector', - 34887: 'ESRI Lerc', - 34892: 'Lossy JPEG', - 34925: 'LZMA2', - 34926: 'Zstd', - 34927: 'WebP', - 34933: 'PNG', - 34934: 'JPEG XR', - 65000: 'Kodak DCR Compressed', - 65535: 'Pentax PEF Compressed', + 1: "Uncompressed", + 2: "CCITT 1D", + 3: "T4/Group 3 Fax", + 4: "T6/Group 4 Fax", + 5: "LZW", + 6: "JPEG (old-style)", + 7: "JPEG", + 8: "Adobe Deflate", + 9: "JBIG B&W", + 10: "JBIG Color", + 99: "JPEG", + 262: "Kodak 262", + 32766: "Next", + 32767: "Sony ARW Compressed", + 32769: "Packed RAW", + 32770: "Samsung SRW Compressed", + 32771: "CCIRLEW", + 32772: "Samsung SRW Compressed 2", + 32773: "PackBits", + 32809: "Thunderscan", + 32867: "Kodak KDC Compressed", + 32895: "IT8CTPAD", + 32896: "IT8LW", + 32897: "IT8MP", + 32898: "IT8BL", + 32908: "PixarFilm", + 32909: "PixarLog", + 32946: "Deflate", + 32947: "DCS", + 33003: "Aperio JPEG 2000 YCbCr", + 33005: "Aperio JPEG 2000 RGB", + 34661: "JBIG", + 34676: "SGILog", + 34677: "SGILog24", + 34712: "JPEG 2000", + 34713: "Nikon NEF Compressed", + 34715: "JBIG2 TIFF FX", + 34718: "(MDI) Binary Level Codec", + 34719: "(MDI) Progressive Transform Codec", + 34720: "(MDI) Vector", + 34887: "ESRI Lerc", + 34892: "Lossy JPEG", + 34925: "LZMA2", + 34926: "Zstd", + 34927: "WebP", + 34933: "PNG", + 34934: "JPEG XR", + 65000: "Kodak DCR Compressed", + 65535: "Pentax PEF Compressed", } try: return value_map[int(value)] @@ -349,15 +333,15 @@ def orientation(value): Maps the value of the orientation to a human readable format """ value_map = { - 0: 'Undefined', - 1: 'Horizontal (normal)', - 2: 'Mirror horizontal', - 3: 'Rotate 180', - 4: 'Mirror vertical', - 5: 'Mirror horizontal and rotate 270 CW', - 6: 'Rotate 90 CW', - 7: 'Mirror horizontal and rotate 90 CW', - 8: 'Rotate 270 CW', + 0: "Undefined", + 1: "Horizontal (normal)", + 2: "Mirror horizontal", + 3: "Rotate 180", + 4: "Mirror vertical", + 5: "Mirror horizontal and rotate 270 CW", + 6: "Rotate 90 CW", + 7: "Mirror horizontal and rotate 90 CW", + 8: "Rotate 270 CW", } try: return value_map[int(value)] @@ -370,16 +354,16 @@ def components_configuration(value): Maps the value of the components configuration to a human readable format """ value_map = { - 0: '', - 1: 'Y', - 2: 'Cb', - 3: 'Cr', - 4: 'R', - 5: 'G', - 6: 'B', + 0: "", + 1: "Y", + 2: "Cb", + 3: "Cr", + 4: "R", + 5: "G", + 6: "B", } try: - return ''.join([value_map[int(x)] for x in value]) + return "".join([value_map[int(x)] for x in value]) except KeyError: return None @@ -388,18 +372,18 @@ def rating(value): """ Maps the value of the rating to a human readable format """ - return str(value) + ' stars' + return str(value) + " stars" def rating_percent(value): """ Maps the value of the rating to a human readable format """ - return str(value) + '%' + return str(value) + "%" def pixel_dimension(value): """ Maps the value of the pixel dimension to a human readable format """ - return str(value) + 'px' + return str(value) + "px" diff --git a/gallery/utils/metadata/mapping.py b/gallery/utils/metadata/mapping.py index 804374d..1e3ca43 100644 --- a/gallery/utils/metadata/mapping.py +++ b/gallery/utils/metadata/mapping.py @@ -4,66 +4,66 @@ Mapping for metadata """ PHOTOGRAHER_MAPPING = { - 'Artist': ['Artist'], - 'UserComment': ['Comment'], - 'ImageDescription': ['Description'], - 'Copyright': ['Copyright'], + "Artist": ["Artist"], + "UserComment": ["Comment"], + "ImageDescription": ["Description"], + "Copyright": ["Copyright"], } CAMERA_MAPPING = { - 'Model': ['Model'], - 'Make': ['Make'], - 'BodySerialNumber': ['Camera Type'], - 'LensMake': ['Lens Make'], - 'LenseModel': ['Lens Model'], - 'LensSpecification': ['Lens Specification', 'lens_specification'], - 'ComponentsConfiguration': ['Components Configuration', 'components_configuration'], - 'DateTime': ['Date Processed', 'date_format'], - 'DateTimeDigitized': ['Time Digitized', 'date_format'], - 'OffsetTime': ['Time Offset'], - 'OffsetTimeOriginal': ['Time Offset - Original'], - 'OffsetTimeDigitized': ['Time Offset - Digitized'], - 'DateTimeOriginal': ['Date Original', 'date_format'], - 'FNumber': ['F-Stop', 'fnumber'], - 'FocalLength': ['Focal Length', 'focal_length'], - 'FocalLengthIn35mmFilm': ['Focal Length (35mm format)', 'focal_length'], - 'MaxApertureValue': ['Max Aperture', 'fnumber'], - 'ApertureValue': ['Aperture', 'fnumber'], - 'ShutterSpeedValue': ['Shutter Speed', 'shutter'], - 'ISOSpeedRatings': ['ISO Speed Ratings', 'iso'], - 'ISOSpeed': ['ISO Speed', 'iso'], - 'SensitivityType': ['Sensitivity Type', 'sensitivity_type'], - 'ExposureBiasValue': ['Exposure Bias', 'exposure'], - 'ExposureTime': ['Exposure Time', 'shutter'], - 'ExposureMode': ['Exposure Mode', 'exposure_mode'], - 'ExposureProgram': ['Exposure Program', 'exposure_program'], - 'WhiteBalance': ['White Balance', 'white_balance'], - 'Flash': ['Flash', 'flash'], - 'MeteringMode': ['Metering Mode', 'metering_mode'], - 'LightSource': ['Light Source', 'light_source'], - 'SceneCaptureType': ['Scene Capture Type', 'scene_capture_type'], + "Model": ["Model"], + "Make": ["Make"], + "BodySerialNumber": ["Camera Type"], + "LensMake": ["Lens Make"], + "LenseModel": ["Lens Model"], + "LensSpecification": ["Lens Specification", "lens_specification"], + "ComponentsConfiguration": ["Components Configuration", "components_configuration"], + "DateTime": ["Date Processed", "date_format"], + "DateTimeDigitized": ["Time Digitized", "date_format"], + "OffsetTime": ["Time Offset"], + "OffsetTimeOriginal": ["Time Offset - Original"], + "OffsetTimeDigitized": ["Time Offset - Digitized"], + "DateTimeOriginal": ["Date Original", "date_format"], + "FNumber": ["F-Stop", "fnumber"], + "FocalLength": ["Focal Length", "focal_length"], + "FocalLengthIn35mmFilm": ["Focal Length (35mm format)", "focal_length"], + "MaxApertureValue": ["Max Aperture", "fnumber"], + "ApertureValue": ["Aperture", "fnumber"], + "ShutterSpeedValue": ["Shutter Speed", "shutter"], + "ISOSpeedRatings": ["ISO Speed Ratings", "iso"], + "ISOSpeed": ["ISO Speed", "iso"], + "SensitivityType": ["Sensitivity Type", "sensitivity_type"], + "ExposureBiasValue": ["Exposure Bias", "exposure"], + "ExposureTime": ["Exposure Time", "shutter"], + "ExposureMode": ["Exposure Mode", "exposure_mode"], + "ExposureProgram": ["Exposure Program", "exposure_program"], + "WhiteBalance": ["White Balance", "white_balance"], + "Flash": ["Flash", "flash"], + "MeteringMode": ["Metering Mode", "metering_mode"], + "LightSource": ["Light Source", "light_source"], + "SceneCaptureType": ["Scene Capture Type", "scene_capture_type"], } SOFTWARE_MAPPING = { - 'Software': ['Software'], - 'ColorSpace': ['Colour Space', 'color_space'], - 'Compression': ['Compression', 'compression_type'], + "Software": ["Software"], + "ColorSpace": ["Colour Space", "color_space"], + "Compression": ["Compression", "compression_type"], } FILE_MAPPING = { - 'FileName': ['Name'], - 'FileSize': ['Size', 'human_size'], - 'FileFormat': ['Format'], - 'FileWidth': ['Width', 'pixel_dimension'], - 'FileHeight': ['Height', 'pixel_dimension'], - 'Orientation': ['Orientation', 'orientation'], - 'XResolution': ['X-resolution'], - 'YResolution': ['Y-resolution'], - 'ResolutionUnit': ['Resolution Units', 'resolution_unit'], - 'Rating': ['Rating', 'rating'], - 'RatingPercent': ['Rating Percent', 'rating_percent'], + "FileName": ["Name"], + "FileSize": ["Size", "human_size"], + "FileFormat": ["Format"], + "FileWidth": ["Width", "pixel_dimension"], + "FileHeight": ["Height", "pixel_dimension"], + "Orientation": ["Orientation", "orientation"], + "XResolution": ["X-resolution"], + "YResolution": ["Y-resolution"], + "ResolutionUnit": ["Resolution Units", "resolution_unit"], + "Rating": ["Rating", "rating"], + "RatingPercent": ["Rating Percent", "rating_percent"], } EXIF_MAPPING = [ - ('Photographer', PHOTOGRAHER_MAPPING), - ('Camera', CAMERA_MAPPING), - ('Software', SOFTWARE_MAPPING), - ('File', FILE_MAPPING) + ("Photographer", PHOTOGRAHER_MAPPING), + ("Camera", CAMERA_MAPPING), + ("Software", SOFTWARE_MAPPING), + ("File", FILE_MAPPING), ] diff --git a/gallery/views/group.py b/gallery/views/group.py index 1218535..39b6325 100644 --- a/gallery/views/group.py +++ b/gallery/views/group.py @@ -10,12 +10,12 @@ from gallery import db from gallery.utils import contrast -blueprint = Blueprint('group', __name__, url_prefix='/group') +blueprint = Blueprint("group", __name__, url_prefix="/group") db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/', methods=['GET']) +@blueprint.route("/", methods=["GET"]) def groups(): """ Group overview, shows all image groups @@ -24,116 +24,134 @@ def groups(): # 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) - .first()[0]) + group.author_username = ( + db_session.query(db.Users.username) + .filter(db.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()) - .limit(3)) + images = ( + db_session.query(db.GroupJunction.post_id) + .filter(db.GroupJunction.group_id == group.id) + .order_by(db.GroupJunction.date_added.desc()) + .limit(3) + ) # For each image, get the image data and add it to the group item group.images = [] for image in images: - group.images.append(db_session.query(db.Posts.filename, db.Posts.alt, - db.Posts.colours, db.Posts.id) - .filter(db.Posts.id == image[0]) - .first()) + group.images.append( + db_session.query( + db.Posts.filename, db.Posts.alt, db.Posts.colours, db.Posts.id + ) + .filter(db.Posts.id == image[0]) + .first() + ) - return render_template('list.html', groups=groups) + return render_template("list.html", groups=groups) -@blueprint.route('/') +@blueprint.route("/") 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 = db_session.query(db.Groups).filter(db.Groups.id == group_id).first() if group is None: - abort(404, 'Group not found! D:') + 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) - .first()[0]) + group.author_username = ( + db_session.query(db.Users.username) + .filter(db.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()) - .all()) + junction = ( + db_session.query(db.GroupJunction.post_id) + .filter(db.GroupJunction.group_id == group_id) + .order_by(db.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( + db_session.query(db.Posts).filter(db.Posts.id == image[0]).first() + ) # Check contrast for the first image in the group for the banner - text_colour = 'rgb(var(--fg-black))' + text_colour = "rgb(var(--fg-black))" if images: - text_colour = contrast.contrast(images[0].colours[0], - 'rgb(var(--fg-black))', - 'rgb(var(--fg-white))') + text_colour = contrast.contrast( + images[0].colours[0], "rgb(var(--fg-black))", "rgb(var(--fg-white))" + ) - return render_template('group.html', - group=group, - images=images, - text_colour=text_colour) + return render_template( + "group.html", group=group, images=images, text_colour=text_colour + ) -@blueprint.route('//') +@blueprint.route("//") 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 = db_session.query(db.Posts).filter(db.Posts.id == image_id).first() if image is None: - abort(404, 'Image not found') + 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) - .first()[0]) + image.author_username = ( + db_session.query(db.Users.username) + .filter(db.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) - .all()) + groups = ( + db_session.query(db.GroupJunction.group_id) + .filter(db.GroupJunction.post_id == image_id) + .all() + ) # Get the group data for each group the image is in image.groups = [] for group in groups: - image.groups.append(db_session.query(db.Groups.id, db.Groups.name) - .filter(db.Groups.id == group[0]) - .first()) + image.groups.append( + db_session.query(db.Groups.id, db.Groups.name) + .filter(db.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()) - .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()) - .first()) + 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()) + .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()) + .first() + ) # If there is a next or previous image, get the URL for it if next_url: - next_url = url_for('group.group_post', group_id=group_id, image_id=next_url[0]) + next_url = url_for("group.group_post", group_id=group_id, image_id=next_url[0]) if prev_url: - prev_url = url_for('group.group_post', group_id=group_id, image_id=prev_url[0]) + prev_url = url_for("group.group_post", group_id=group_id, image_id=prev_url[0]) - return render_template('image.html', image=image, next_url=next_url, prev_url=prev_url) + return render_template( + "image.html", image=image, next_url=next_url, prev_url=prev_url + ) diff --git a/gallery/views/image.py b/gallery/views/image.py index b9c5d14..33303ec 100644 --- a/gallery/views/image.py +++ b/gallery/views/image.py @@ -9,12 +9,12 @@ from sqlalchemy.orm import sessionmaker from gallery import db -blueprint = Blueprint('image', __name__, url_prefix='/image') +blueprint = Blueprint("image", __name__, url_prefix="/image") db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/') +@blueprint.route("/") def image(image_id): """ Image view, shows the image and its metadata @@ -22,48 +22,55 @@ def image(image_id): # Get the image, if it doesn't exist, 404 image = db_session.query(db.Posts).filter(db.Posts.id == image_id).first() if not image: - abort(404, 'Image not found :<') + 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) - .first()[0]) + image.author_username = ( + db_session.query(db.Users.username) + .filter(db.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) - .all()) + groups = ( + db_session.query(db.GroupJunction.group_id) + .filter(db.GroupJunction.post_id == image_id) + .all() + ) # For each group, get the group data and add it to the image item image.groups = [] for group in groups: - image.groups.append(db_session.query(db.Groups.id, db.Groups.name) - .filter(db.Groups.id == group[0]) - .first()) + image.groups.append( + db_session.query(db.Groups.id, db.Groups.name) + .filter(db.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()) - .first()) - prev_url = (db_session.query(db.Posts.id) - .filter(db.Posts.id < image_id) - .order_by(db.Posts.id.desc()) - .first()) + next_url = ( + db_session.query(db.Posts.id) + .filter(db.Posts.id > image_id) + .order_by(db.Posts.id.asc()) + .first() + ) + prev_url = ( + db_session.query(db.Posts.id) + .filter(db.Posts.id < image_id) + .order_by(db.Posts.id.desc()) + .first() + ) # If there is a next or previous image, get the url if next_url: - next_url = url_for('image.image', image_id=next_url[0]) + next_url = url_for("image.image", image_id=next_url[0]) if prev_url: - prev_url = url_for('image.image', image_id=prev_url[0]) - - + 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()) - limit = current_app.config['UPLOAD_CONF']['max-load'] + total_images = db_session.query(db.Posts.id).order_by(db.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 if len(total_images) <= limit: @@ -72,11 +79,16 @@ def image(image_id): # How many pages should there be for i in range(ceil(len(total_images) / limit)): # Slice the list of IDs into chunks of the limit - for j in total_images[i * limit:(i + 1) * limit]: + for j in total_images[i * limit : (i + 1) * limit]: # Is our image in this chunk? if image_id in j: return_page = i + 1 break - return render_template('image.html', image=image, next_url=next_url, - prev_url=prev_url, return_page=return_page) + return render_template( + "image.html", + image=image, + next_url=next_url, + prev_url=prev_url, + return_page=return_page, + ) diff --git a/gallery/views/index.py b/gallery/views/index.py index b0c8e89..9093311 100644 --- a/gallery/views/index.py +++ b/gallery/views/index.py @@ -10,40 +10,50 @@ from sqlalchemy.orm import sessionmaker from gallery import db -blueprint = Blueprint('gallery', __name__) +blueprint = Blueprint("gallery", __name__) db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/') +@blueprint.route("/") def index(): """ Home page of the website, shows the feed of the latest images """ # meme - if request.args.get('coffee') == 'please': + 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'] - + 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 = db_session.query(db.Posts.id).count() pages = ceil(max(total_images, limit) / limit) if page > pages: - abort(404, 'You have reached the far and beyond, ' + - 'but you will not find your answers here.') - - # 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) - .order_by(db.Posts.id.desc()) - .offset((page - 1) * limit) - .limit(limit) - .all()) + abort( + 404, + "You have reached the far and beyond, " + + "but you will not find your answers here.", + ) - return render_template('index.html', images=images, - total_images=total_images, - pages=pages, page=page) + # 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, + ) + .order_by(db.Posts.id.desc()) + .offset((page - 1) * limit) + .limit(limit) + .all() + ) + + return render_template( + "index.html", images=images, total_images=total_images, pages=pages, page=page + ) diff --git a/gallery/views/profile.py b/gallery/views/profile.py index ac7c92f..0022e7e 100644 --- a/gallery/views/profile.py +++ b/gallery/views/profile.py @@ -9,31 +9,31 @@ from sqlalchemy.orm import sessionmaker from gallery import db -blueprint = Blueprint('profile', __name__, url_prefix='/profile') +blueprint = Blueprint("profile", __name__, url_prefix="/profile") db_session = sessionmaker(bind=db.engine) db_session = db_session() -@blueprint.route('/profile') +@blueprint.route("/profile") def profile(): """ Profile overview, shows all profiles on the onlylegs gallery """ - user_id = request.args.get('id', default=None, type=int) + user_id = request.args.get("id", default=None, type=int) # If there is no userID set, check if the user is logged in and display their profile if not user_id: if current_user.is_authenticated: user_id = current_user.id else: - abort(404, 'You must be logged in to view your own 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() if not user: - abort(404, 'User not found :c') + abort(404, "User not found :c") images = db_session.query(db.Posts).filter(db.Posts.author_id == user_id).all() - return render_template('profile.html', user=user, images=images) + return render_template("profile.html", user=user, images=images) diff --git a/gallery/views/settings.py b/gallery/views/settings.py index 94926e9..97a48d5 100644 --- a/gallery/views/settings.py +++ b/gallery/views/settings.py @@ -4,40 +4,40 @@ OnlyLegs - Settings page from flask import Blueprint, render_template from flask_login import login_required -blueprint = Blueprint('settings', __name__, url_prefix='/settings') +blueprint = Blueprint("settings", __name__, url_prefix="/settings") -@blueprint.route('/') +@blueprint.route("/") @login_required def general(): """ General settings page """ - return render_template('settings/general.html') + return render_template("settings/general.html") -@blueprint.route('/server') +@blueprint.route("/server") @login_required def server(): """ Server settings page """ - return render_template('settings/server.html') + return render_template("settings/server.html") -@blueprint.route('/account') +@blueprint.route("/account") @login_required def account(): """ Account settings page """ - return render_template('settings/account.html') + return render_template("settings/account.html") -@blueprint.route('/logs') +@blueprint.route("/logs") @login_required def logs(): """ Logs settings page """ - return render_template('settings/logs.html') + return render_template("settings/logs.html") diff --git a/run.py b/run.py index fe915c2..fa83ed7 100644 --- a/run.py +++ b/run.py @@ -5,7 +5,8 @@ from setup.args import PORT, ADDRESS, WORKERS, DEBUG from setup.configuration import Configuration -print(""" +print( + """ :::::::: :::: ::: ::: ::: ::: ::: ::::::::: ::::::::: :::::::: :+: :+: :+:+: :+: :+: :+: :+: :+: :+: :+: :+: :+: :+: +:+ +:+ :+:+:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ +:+ @@ -15,7 +16,8 @@ print(""" ######## ### #### ########## ### ########## ######### ######### ######## Created by Fluffy Bean - Version 23.04.06 -""") +""" +) # Run pre-startup checks and load configuration @@ -24,6 +26,7 @@ Configuration() if DEBUG: from gallery import create_app + create_app().run(host=ADDRESS, port=PORT, debug=True, threaded=True) else: from setup.runner import OnlyLegs # pylint: disable=C0412 @@ -33,8 +36,8 @@ else: sys.argv = [sys.argv[0]] options = { - 'bind': f'{ADDRESS}:{PORT}', - 'workers': WORKERS, + "bind": f"{ADDRESS}:{PORT}", + "workers": WORKERS, } OnlyLegs(options).run() diff --git a/setup/args.py b/setup/args.py index bfc299f..a443338 100644 --- a/setup/args.py +++ b/setup/args.py @@ -13,11 +13,17 @@ Startup arguments for the OnlyLegs gallery import argparse -parser = argparse.ArgumentParser(description='Run the OnlyLegs gallery') -parser.add_argument('-p', '--port', type=int, default=5000, help='Port to run on') -parser.add_argument('-a', '--address', type=str, default='127.0.0.0', help='Address to run on') -parser.add_argument('-w', '--workers', type=int, default=4, help='Number of workers to run') -parser.add_argument('-d', '--debug', action='store_true', help='Run as Flask app in debug mode') +parser = argparse.ArgumentParser(description="Run the OnlyLegs gallery") +parser.add_argument("-p", "--port", type=int, default=5000, help="Port to run on") +parser.add_argument( + "-a", "--address", type=str, default="127.0.0.0", help="Address to run on" +) +parser.add_argument( + "-w", "--workers", type=int, default=4, help="Number of workers to run" +) +parser.add_argument( + "-d", "--debug", action="store_true", help="Run as Flask app in debug mode" +) args = parser.parse_args() diff --git a/setup/configuration.py b/setup/configuration.py index 1be7fcc..d01ddf9 100644 --- a/setup/configuration.py +++ b/setup/configuration.py @@ -9,13 +9,14 @@ import platformdirs import yaml -USER_DIR = platformdirs.user_config_dir('onlylegs') +USER_DIR = platformdirs.user_config_dir("onlylegs") class Configuration: """ Setup the application on first run """ + def __init__(self): """ Main setup function @@ -27,11 +28,11 @@ class Configuration: self.make_dir() # Check if the .env file exists - if not os.path.exists(os.path.join(USER_DIR, '.env')): + if not os.path.exists(os.path.join(USER_DIR, ".env")): self.make_env() # Check if the conf.yml file exists - if not os.path.exists(os.path.join(USER_DIR, 'conf.yml')): + if not os.path.exists(os.path.join(USER_DIR, "conf.yml")): self.make_yaml() # Load the config files @@ -43,8 +44,8 @@ class Configuration: Create the user directory """ os.makedirs(USER_DIR) - os.makedirs(os.path.join(USER_DIR, 'instance')) - os.makedirs(os.path.join(USER_DIR, 'uploads')) + os.makedirs(os.path.join(USER_DIR, "instance")) + os.makedirs(os.path.join(USER_DIR, "uploads")) print("Created user directory at:", USER_DIR) @@ -54,21 +55,23 @@ class Configuration: Create the .env file with default values """ env_conf = { - 'FLASK_SECRET': os.urandom(32).hex(), + "FLASK_SECRET": os.urandom(32).hex(), } - with open(os.path.join(USER_DIR, '.env'), encoding='utf-8', mode='w+') as file: + with open(os.path.join(USER_DIR, ".env"), encoding="utf-8", mode="w+") as file: for key, value in env_conf.items(): file.write(f"{key}={value}\n") - print(""" + print( + """ #################################################### # A NEW KEY WAS GENERATED FOR YOU! PLEASE NOTE # # DOWN THE FLASK_SECRET KEY LOCATED IN YOUR # # .config/onlylegs/.env FOLDER! LOOSING THIS KEY # # WILL RESULT IN YOU BEING UNABLE TO LOG IN! # #################################################### - """) + """ + ) @staticmethod def make_yaml(): @@ -76,8 +79,8 @@ class Configuration: Create the YAML config file with default values """ is_correct = False - email_regex = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b') - username_regex = re.compile(r'\b[A-Za-z0-9._%+-]+\b') + email_regex = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b") + username_regex = re.compile(r"\b[A-Za-z0-9._%+-]+\b") print("\nNo config file found, please enter the following information:") while not is_correct: @@ -99,47 +102,52 @@ class Configuration: continue # Check if user is happy with the values - if input("Is this correct? (y/n): ").lower() == 'y': + if input("Is this correct? (y/n): ").lower() == "y": is_correct = True yaml_conf = { - 'admin': { - 'name': name, - 'username': username, - 'email': email, + "admin": { + "name": name, + "username": username, + "email": email, }, - 'upload': { - 'allowed-extensions': { - 'jpg': 'jpeg', - 'jpeg': 'jpeg', - 'png': 'png', - 'webp': 'webp', + "upload": { + "allowed-extensions": { + "jpg": "jpeg", + "jpeg": "jpeg", + "png": "png", + "webp": "webp", }, - 'max-size': 69, - 'max-load': 50, - 'rename': 'GWA_{{username}}_{{time}}', + "max-size": 69, + "max-load": 50, + "rename": "GWA_{{username}}_{{time}}", + }, + "website": { + "name": "OnlyLegs", + "motto": "A gallery built for fast and simple image management!", + "language": "en", }, - 'website': { - 'name': 'OnlyLegs', - 'motto': 'A gallery built for fast and simple image management!', - 'language': 'en', - } } - with open(os.path.join(USER_DIR, 'conf.yml'), encoding='utf-8', mode='w+') as file: + with open( + os.path.join(USER_DIR, "conf.yml"), encoding="utf-8", mode="w+" + ) as file: yaml.dump(yaml_conf, file, default_flow_style=False) - print("Generated config file, you can change these values in the settings of the app") + print( + "Generated config file, you can change these values in the settings of the app" + ) @staticmethod def logging_config(): """ Set the logging config """ - logging.getLogger('werkzeug').disabled = True + logging.getLogger("werkzeug").disabled = True logging.basicConfig( - filename=os.path.join(USER_DIR, 'only.log'), + filename=os.path.join(USER_DIR, "only.log"), level=logging.INFO, - datefmt='%Y-%m-%d %H:%M:%S', - format='%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s', - encoding='utf-8') + datefmt="%Y-%m-%d %H:%M:%S", + format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s", + encoding="utf-8", + ) diff --git a/setup/runner.py b/setup/runner.py index 20496d2..8a4f052 100644 --- a/setup/runner.py +++ b/setup/runner.py @@ -9,6 +9,7 @@ class OnlyLegs(Application): """ Gunicorn application """ + def __init__(self, options={}): # pylint: disable=W0102, W0231 self.usage = None self.callable = None @@ -27,7 +28,7 @@ class OnlyLegs(Application): @staticmethod def prog(): # pylint: disable=C0116, E0202 - return 'OnlyLegs' + return "OnlyLegs" def load(self): - return util.import_app('gallery:create_app()') + return util.import_app("gallery:create_app()")