From 6f363a104e12254e5d4147828cb2cec21ea102b6 Mon Sep 17 00:00:00 2001 From: Yapollon Date: Sun, 18 Feb 2024 04:53:15 +0100 Subject: [PATCH] Profile Pictures YES YOU HEARD RIGHT!! I ALSO DID THE HTML! This is the most awesome most epic addition, i sold my kidney for this. --- .gitignore | 3 + app.py | 72 ++++++++++++- db/SQLiteClient.py | 15 +-- .../1708105700_delete_users_table.sql | 1 + .../1708105768_create_users_table.sql | 10 ++ models/User.py | 17 +-- static/{main.css => css/background.css} | 0 static/css/profile.css | 45 ++++++++ templates/layouts/main.html | 4 +- templates/profile.html | 102 +++++++++++++----- 10 files changed, 222 insertions(+), 47 deletions(-) create mode 100644 db/migrations/1708105700_delete_users_table.sql create mode 100644 db/migrations/1708105768_create_users_table.sql rename static/{main.css => css/background.css} (100%) create mode 100644 static/css/profile.css diff --git a/.gitignore b/.gitignore index 5ac73e1..a930a3f 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,6 @@ cython_debug/ db/db.sqlite /HabitTracker.iml + +static/profile_images + diff --git a/app.py b/app.py index 028c8c6..09188a7 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,7 @@ import datetime import hashlib +import os +from PIL import Image from flask import Flask, render_template, redirect, url_for, request from flask_login import login_required, LoginManager, login_user, logout_user, current_user @@ -270,6 +272,7 @@ def profile(): "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, errors={} ) @@ -292,15 +295,15 @@ def profile_change(): if not oldPassword: errors['oldPassword'] = 'Du musst dein aktuelles Passwort angeben.' - else: - if hashlib.sha256(oldPassword.encode()).hexdigest() != current_user.password: - errors['oldPassword'] = 'Das Passwort ist falsch.' + elif hashlib.sha256(oldPassword.encode()).hexdigest() != current_user.password: + errors['oldPassword'] = 'Das Passwort ist falsch.' if errors: return render_template( "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, errors=errors ) @@ -316,6 +319,69 @@ def profile_change(): "profile.html", name=current_user.name, email=current_user.email, + profile_image_url=current_user.profile_image, + errors={} + ) + +UPLOAD_FOLDER = 'static/profile_images/' # Folder to store profile images +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + +def save_profile_image(image_file): + print("we are saving...") + filename = image_file.filename + if '.' not in filename: + # Ensure the filename has an extension + raise ValueError("Invalid filename") + + # Check if the file extension is allowed + allowed_extensions = {'jpg', 'jpeg', 'png', 'gif'} + file_extension = filename.rsplit('.', 1)[1].lower() + if file_extension not in allowed_extensions: + raise ValueError("Invalid file extension") + + # Open the uploaded image + image = Image.open(image_file) + + # Determine the size of the square image + min_dimension = min(image.size) + square_size = (min_dimension, min_dimension) + + # Calculate the coordinates for cropping + left = (image.width - min_dimension) / 2 + top = (image.height - min_dimension) / 2 + right = (image.width + min_dimension) / 2 + bottom = (image.height + min_dimension) / 2 + + # Crop the image to a square and resize it + image = image.crop((left, top, right, bottom)) + image.thumbnail(square_size) + image = image.resize((128, 128)) + + # Save the processed image + image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + image.save(image_path) + + return filename + +@app.route('/upload', methods=['POST']) +def upload_profile_image(): + if 'file' not in request.files: + return 'No file part' + + file = request.files['file'] + + filename = save_profile_image(file) + + # Update user + current_user.profile_image = url_for('static', filename=f'profile_images/{filename}') + current_user.update() + + # Back to profile + return render_template( + "profile.html", + name=current_user.name, + email=current_user.email, + profile_image_url=current_user.profile_image, errors={} ) diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py index 0173b9e..c407599 100644 --- a/db/SQLiteClient.py +++ b/db/SQLiteClient.py @@ -9,11 +9,11 @@ def con3(): ### User ### -def create_user(name: str, email: str, password: str): +def create_user(name: str, email: str, password: str, profile_image: str): password = hashlib.sha256(password.encode()).hexdigest() now = datetime.now().isoformat() - query = (f"INSERT INTO users (name, email, password, created_at, updated_at) VALUES ('{name}', '{email}', " - f"'{password}', '{now}', '{now}');") + query = (f"INSERT INTO users (name, email, password, profile_image, created_at, updated_at) VALUES " + f"('{name}', '{email}', '{password}', '{profile_image}', '{now}', '{now}');") conn = con3() cursor = conn.cursor() cursor.execute(query) @@ -42,13 +42,14 @@ def get_user_by_email(email: str): return user -def update_user(id: int, name: str, email: str, password: str): +def update_user(id: int, name: str, email: str, password: str, profile_image:str): now = datetime.now().isoformat() if password: - query = (f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', updated_at = '{now}' " - f"WHERE id = {id};") + query = (f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', " + f"profile_image = '{profile_image}', updated_at = '{now}' WHERE id = {id};") else: - query = f"UPDATE users SET name = '{name}', email = '{email}', updated_at = '{now}' WHERE id = {id};" + query = (f"UPDATE users SET name = '{name}', email = '{email}', profile_image = '{profile_image}'," + f" updated_at = '{now}' WHERE id = {id};") conn = con3() cursor = conn.cursor() cursor.execute(query) diff --git a/db/migrations/1708105700_delete_users_table.sql b/db/migrations/1708105700_delete_users_table.sql new file mode 100644 index 0000000..441087a --- /dev/null +++ b/db/migrations/1708105700_delete_users_table.sql @@ -0,0 +1 @@ +DROP TABLE users; \ No newline at end of file diff --git a/db/migrations/1708105768_create_users_table.sql b/db/migrations/1708105768_create_users_table.sql new file mode 100644 index 0000000..5210527 --- /dev/null +++ b/db/migrations/1708105768_create_users_table.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS users +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + profile_image TEXT NOT NULL, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL +); diff --git a/models/User.py b/models/User.py index 05a897b..16a24d9 100644 --- a/models/User.py +++ b/models/User.py @@ -4,31 +4,32 @@ from db.SQLiteClient import (create_user, get_user, get_user_by_email, update_us class User(UserMixin): - def __init__(self, id: int, name: str, email: str, password: str = None): + def __init__(self, id: int, name: str, email: str, password: str = None, profile_image:str = None): self.id = id self.name = name self.email = email self.password = password + self.profile_image = profile_image @staticmethod - def create(name: str, email: str, password: str): - id = create_user(name, email, password) - return User(id, name, email) + def create(name: str, email: str, password: str, profile_image: str=None): + id = create_user(name, email, password, profile_image) + return User(id=id, name=name, email=email, profile_image=profile_image) @staticmethod def get(id: int): user = get_user(id) - return User(user[0], user[1], user[2], user[3]) if user else None + return User(user[0], user[1], user[2], user[3], user[4]) if user else None @staticmethod def get_by_email(email: str): user = get_user_by_email(email) - return User(user[0], user[1], user[2], user[3]) if user else None + return User(user[0], user[1], user[2], user[3], user[4]) if user else None - # Updates: name, email, password + # Updates: name, email, password, profile_image def update(self): - update_user(self.id, self.name, self.email, self.password if self.password else None) + update_user(self.id, self.name, self.email, self.password if self.password else None, self.profile_image) # Deletes the User diff --git a/static/main.css b/static/css/background.css similarity index 100% rename from static/main.css rename to static/css/background.css diff --git a/static/css/profile.css b/static/css/profile.css new file mode 100644 index 0000000..55b1d07 --- /dev/null +++ b/static/css/profile.css @@ -0,0 +1,45 @@ +.profile-image-container { + position: relative; + width: 150px; /* Adjust the size as needed */ + height: 150px; /* Adjust the size as needed */ +} + +.profile-image { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; +} + +.profile-image-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Grey overlay */ + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + color: white; + font-size: 16px; + opacity: 0; /* Initially hidden */ + transition: opacity 0.3s ease; + cursor: pointer; +} + +.profile-image-overlay:hover { + opacity: 1; /* Show overlay on hover */ +} + +.profile-image-overlay span { + /* Center the text and make it bold */ + text-align: center; + font-weight: bold; +} + +.profile-image-overlay:hover span { + /* Style the text when hovering */ + color: white; +} \ No newline at end of file diff --git a/templates/layouts/main.html b/templates/layouts/main.html index c375f6a..43d392c 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -6,10 +6,12 @@ {{ title }} - HabitTracker - + + + diff --git a/templates/profile.html b/templates/profile.html index e5f81e4..7ae87be 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -2,45 +2,91 @@ {% block content %} -

Account Einstellungen👤

+
+

Account Einstellungen👤

- -
-
- - -
- {{ errors.get('newName', '') }} +
+
+
Profilbild
+
+ Profile Image +
+ Profilbild bearbeiten +
+
+
+ + + +
-
- - -
- {{ errors.get('newEmail', '') }} +
+
+ + +
+ {{ errors.get('newName', '') }} +
-
-
- - -
- {{ errors.get('newPassword', '') }} +
+ + +
+ {{ errors.get('newEmail', '') }} +
-
-
- - -
- {{ errors.get('oldPassword', '') }} +
+ + +
+ {{ errors.get('newPassword', '') }} +
-
- - +
+ + +
+ {{ errors.get('oldPassword', '') }} +
+
+ + +
+ {% endblock %} \ No newline at end of file