From 235072af97ae6c74e70c54b928e3b612a268c206 Mon Sep 17 00:00:00 2001 From: Stef Date: Thu, 17 Apr 2025 13:59:42 +0200 Subject: [PATCH] Cleanip init.py, utils.py and decorators.py --- app.py | 4 +--- application/__init__.py | 13 ++++++++++--- application/decorators.py | 3 +++ application/utils.py | 10 ++++++++-- test/123123123.png | Bin 11202 -> 0 bytes 5 files changed, 22 insertions(+), 8 deletions(-) delete mode 100644 test/123123123.png diff --git a/app.py b/app.py index 89ab1c5..32c493b 100644 --- a/app.py +++ b/app.py @@ -1,11 +1,9 @@ from application import app from flask import redirect, url_for -from flask_login import login_required # type: ignore -# home route +# home route, place holder in case we want a home page @app.route("/") -@login_required def index(): return redirect(url_for("dash.index")) diff --git a/application/__init__.py b/application/__init__.py index d4cd08f..67c3339 100644 --- a/application/__init__.py +++ b/application/__init__.py @@ -20,9 +20,6 @@ db.init_app(app) migrate = Migrate(app, db) -# bp import -from application.auth.views import auth_blueprint -from application.dash.views import dash_blueprint # Login manager from application.auth.models import User @@ -32,6 +29,7 @@ login_manager.init_app(app) # type: ignore login_manager.login_view = "auth.login" # type: ignore +# Gets all the user data @login_manager.user_loader # type: ignore def load_user(user_id): # type: ignore return User.query.get(int(user_id)) # type: ignore @@ -39,5 +37,14 @@ def load_user(user_id): # type: ignore # Blueprint magic +# bp import +# Would like to do this at the top of the file, +# but can't easily figure out how to do this. +# I think everything that the views depend on have to be moved +# into a seperate .py and imported. +from application.auth.views import auth_blueprint +from application.dash.views import dash_blueprint + +# Register blueprints app.register_blueprint(dash_blueprint, url_prefix="/dash") app.register_blueprint(auth_blueprint, url_prefix="/auth") diff --git a/application/decorators.py b/application/decorators.py index 285930e..a56be7c 100644 --- a/application/decorators.py +++ b/application/decorators.py @@ -4,6 +4,9 @@ from functools import wraps from flask import redirect, url_for +# Decorator that checks if the current user is logged in and an admin +# Could be shortened by adding the login_required decorator +# and removing the logic here def admin_required(f: Callable[..., Any]) -> Callable[..., Any]: @wraps(f) def decorated_function(*args: ..., **kwargs: ...): diff --git a/application/utils.py b/application/utils.py index 9cbc656..55a63a9 100644 --- a/application/utils.py +++ b/application/utils.py @@ -1,17 +1,23 @@ -from werkzeug.utils import secure_filename import os +from werkzeug.utils import secure_filename from application import app from flask_login import current_user # type: ignore +# save image to static folder def saveImage(image: ...): filename = secure_filename(image.filename) + # Path should be /application/static/[user_id]/[filename] save_path = os.path.join( app.config["UPLOAD_FOLDER"], # type: ignore str(current_user.id), filename, ) + # Create path is it doesn't exist os.makedirs(os.path.dirname(save_path), exist_ok=True) + # Save the image image.save(save_path) # type: ignore - filename2 = str(current_user.id) + "/" + filename + # Return the filename that is stored in database. + # Only done to keep a single default image, this should be done differently + filename2 = str(current_user.id) + "/" + filename # [user_id]/[filename] return filename2 diff --git a/test/123123123.png b/test/123123123.png deleted file mode 100644 index dfef930d528e35aae1868a6b5f80139ca9d8ee47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11202 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