import datetime import hashlib import os from PIL import Image from flask import Flask, render_template, redirect, url_for, request, jsonify from flask_login import login_required, LoginManager, login_user, logout_user, current_user from models.Habit import Habit from models.HabitList import HabitList from models.HabitTracking import HabitTracking from models.User import User from utils import anonymous_required # Create a new Flask instance app = Flask(__name__) app.secret_key = 'PSSSSSHHHT!' # Initialize the Flask-Login extension login_manager = LoginManager() login_manager.login_view = 'login' login_manager.init_app(app) @login_manager.user_loader def load_user(user_id): return User.get(user_id) @app.context_processor def inject_user(): return dict(user=current_user) @app.route('/login') @anonymous_required def login(): return render_template('auth/login.html', errors={}) @app.route('/signup') @anonymous_required def signup(): return render_template('auth/signup.html', errors={}) @app.route('/login', methods=['POST']) def login_post(): email = request.form.get('email') password = request.form.get('password') # Check for errors errors = {} if not email: errors['email'] = 'Die E-Mail Adresse ist erforderlich.' if not password: errors['password'] = 'Das Passwort ist erforderlich.' # Check if user exists user = User.get_by_email(email) if not user: errors['email'] = 'E-Mail Adresse nicht gefunden.' elif user.password is None or hashlib.sha256(password.encode()).hexdigest() != user.password: errors['password'] = 'Das Passwort ist falsch.' if errors: return render_template( 'auth/login.html', email=email, password=password, errors=errors ) login_user(user) # Redirect to login page return redirect(url_for('index')) @app.route('/signup', methods=['POST']) def signup_post(): email = request.form.get('email') name = request.form.get('name') password = request.form.get('password') # Check for errors errors = {} if not email: errors['email'] = 'Die E-Mail Adresse ist erforderlich.' if not name: errors['name'] = 'Der Name ist erforderlich.' if not password: errors['password'] = 'Das Passwort ist erforderlich.' if errors: return render_template( 'auth/signup.html', email=email, name=name, password=password, errors=errors ) # Save user to database. Maybe log the user in directly. user = User.create(name, email, password) login_user(user) # Redirect to login page return redirect(url_for('index')) @app.route('/logout') @login_required def logout(): # Log out functionality logout_user() return redirect(url_for('index')) # Create a new route @app.route('/') def index(): if current_user.is_authenticated: habit_lists = current_user.get_habitLists() name = "Hallo " + current_user.name heatmap_values = current_user.get_heatmap() else: habit_lists = [] name = "Bitte melde dich an." heatmap_values = [] # Sort habits by whether they have been checked today and then by slot for habit_list in habit_lists: habit_list.habits = sorted(habit_list.get_habits(), key=lambda habit: (not habit.checked, habit.slot)) days = {"Monday": "Montag", "Tuesday": "Dienstag", "Wednesday": "Mittwoch", "Thursday": "Donnerstag", "Friday": "Freitag", "Saturday": "Samstag", "Sunday": "Sonntag"} date = datetime.datetime.now().strftime("%d.%m.%Y %H:%M ") + days[datetime.datetime.now().strftime("%A")] return render_template( 'index.html', title=name, utc_dt=date, habit_lists=habit_lists, heatmap_values=heatmap_values, errors={}, ) @app.route('/habit') @login_required def habit_creation(): return render_template( 'habit.html', title='Erstelle ein Habit', unit="Woche", errors={}, ) @app.route('/habit', methods=['POST']) @login_required def habit_create(): name = request.form.get('name') note = request.form.get('note') times = request.form.get('times') unit = request.form.get('unit') list_id = request.form.get('list_query') # Check for errors errors = {} if not name: errors['name'] = 'Der Name ist erforderlich.' if not times: errors['times'] = 'Die Anzahl ist erforderlich.' if not note: note = '' if not unit: errors['unit'] = 'Die Einheit ist erforderlich.' if not list_id: errors['list_query'] = 'Die Habitliste ist erforderlich.' # Check if times is an integer try: times = int(times) # Check that times is greater than 0 if times <= 0: errors['times'] = 'Die Anzahl muss größer als 0 sein.' except ValueError: errors['times'] = 'Die Anzahl muss eine Zahl sein.' # Check that unit is valid if unit not in ['Tag', 'Woche', 'Monat', 'Jahr']: errors['unit'] = 'Die Einheit ist ungültig.' # check if list_id is an int try: list_id = int(list_id) except ValueError: errors['list_query'] = 'Die Anzahl muss eine Zahl sein.' if errors: return render_template( 'habit.html', title='Erstelle ein Habit', name=name, note=note, times=times, unit=unit, errors=errors, list_id=list_id ) # Map unit to integer if unit == 'Tag': unit = 0 elif unit == 'Woche': unit = 1 elif unit == 'Monat': unit = 2 elif unit == 'Jahr': unit = 3 else: unit = 1 # Save habit to database habit = Habit.create(list_id, name, times, note, unit) # Back to index return redirect(url_for('index')) @app.route('/habit-list') @login_required def habit_list_creation(): return render_template( 'habit-list.html', title='Erstelle eine Habitliste', errors={}, ) @app.route('/habit-list', methods=['POST']) @login_required def habit_list_create(): name = request.form.get('name') description = request.form.get('description') # Check for errors errors = {} if not name: errors['name'] = 'Der Name ist erforderlich.' if not description: note = '' if errors: return render_template( 'habit-list.html', title='Erstelle eine Habitliste', name=name, description=description, errors=errors ) # Save habit to database habit = HabitList.create(current_user.id, name, description) # Back to index return redirect(url_for('index')) @app.route('/profile') @login_required def profile(): return render_template( "profile.html", name=current_user.name, email=current_user.email, profile_image_url=current_user.profile_image, title="Profil", ) @app.route('/profile', methods=['POST']) @login_required def profile_change(): newName = request.form.get('newName') newEmail = request.form.get('newEmail') # Update user current_user.name = newName current_user.email = newEmail 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, ) @app.route('/edit-habit') @login_required def edit_habit(): habit_id = request.args.get("habit") #habit_id = request.get_json()["editHabitId"] habit = Habit.get(habit_id) return render_template( "edit-habit.html", title=habit.name ) ''' @app.route('/edit-habit', methods=['POST']) @login_required def edit_habit_change(): #habit_id = request.get_json()["habitId"] #habit = Habit.get(habit_id) return render_template( "edit-habit.html" ) ''' @app.route('/check_password', methods=['POST']) @login_required def check_password(): # Get the password sent from the client password = request.json.get('password') if hashlib.sha256(password.encode()).hexdigest() == current_user.password: return jsonify({"valid": True}) else: return jsonify({"valid": False}) @app.route('/password', methods=['POST']) @login_required def password_change(): newPassword = request.form.get('newPassword') # Update user if newPassword: current_user.password = hashlib.sha256(newPassword.encode()).hexdigest() 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, ) def save_profile_image(image_file): UPLOAD_FOLDER = 'static/profile_images/' # Folder to store profile images app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER 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") # Get the filename from the image path saved in the user filename = os.path.basename(current_user.profile_image) # Open the uploaded image image = Image.open(image_file) # Convert the image to RGB mode (required for JPEG) image = image.convert('RGB') # 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((256, 256)) # Save the processed image image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) image.save(image_path, 'JPEG', quality=100) @app.route('/upload', methods=['POST']) def upload_profile_image(): if 'file' not in request.files: return 'No file part' file = request.files['file'] save_profile_image(file) # Back to profile return render_template( "profile.html", name=current_user.name, email=current_user.email, profile_image_url=current_user.profile_image, errors={} ) @app.route('/check', methods=['POST']) @login_required def check_habit(): habit_id = request.get_json()["habitId"] habit = Habit.get(habit_id) if habit is None: return {"error": "Habit not found"} # Check if habit belongs to user users = habit.habit_list().get_users() if current_user not in users: return {"error": "Habit does not belong to user"} trackings = habit.get_habitTrackings() # Check if habit has been tracked today delete_tracking = None for tracking in trackings: if tracking.created_at.date() == datetime.date.today(): delete_tracking = tracking if not delete_tracking: HabitTracking.create(habit_id) habit.fill_statistics() else: delete_tracking.delete() habit.reset_statistics() habit.load_statistics() return { "habitId": habit_id, "unchecked": not delete_tracking, "percentage": habit.percentage, "streak": habit.streak } @app.route('/delete', methods=['POST']) @login_required def delete_habit(): habit_id = request.get_json()["habitId"] habit = Habit.get(habit_id) if habit is None: return {"error": "Habit not found"} # Check if habit belongs to user if current_user not in habit.habit_list().get_users(): return {"error": "Habit does not belong to user"} habit.delete() return {} @app.route('/reorder', methods=['POST']) @login_required def reorder_habits(): new_index = request.get_json()["newIndex"] + 1 habit = Habit.get(request.get_json()["habitId"]) if habit is None: return {"error": "Habit not found"} # Check if habit belongs to user users = habit.habit_list().get_users() if current_user not in users: return {"error": "Habit does not belong to user"} habit.update_slot(new_index) return {} @app.route('/users') @login_required def users(): habit_list_id = request.args.get('habit_list', current_user.get_habitLists()[0].id) habit_list = HabitList.get(int(habit_list_id)) users = habit_list.get_users() # Remove the current user from the list users = [user for user in users if user.id != current_user.id] return render_template( 'users.html', title='Teilnehmer', habit_list=habit_list, users=users, errors={}, ) @app.route('/users', methods=['POST']) @login_required def add_user(): email = request.form.get('email') habit_list_id = request.form.get('habit_list_id') habit_list = HabitList.get(int(habit_list_id)) # Check for errors errors = {} if not email: errors['email'] = 'Die E-Mail Adresse ist erforderlich.' if not habit_list_id: errors['habit_list'] = 'Die Habitliste ist erforderlich.' if errors: return render_template( 'users.html', title='Teilnehmer', email=email, habit_list=habit_list, errors=errors, users=habit_list.get_users(), ) # Check if user exists user = User.get_by_email(email) if not user: errors['email'] = 'E-Mail Adresse nicht gefunden.' if user.id == current_user.id: errors['email'] = 'Du kannst dich nicht selbst hinzufügen.' # Check if user is already in the habit list already = False for u in habit_list.get_users(): if u.id == user.id: already = True break if already: errors['email'] = 'Teilnehmer ist bereits in der Liste.' if errors: return render_template( 'users.html', title='Teilnehmer', email=email, habit_list=habit_list, errors=errors, users=habit_list.get_users(), ) # Add user to habit list habit_list = HabitList.get(int(habit_list_id)) habit_list.add_user(user) return redirect(url_for('index', habit_list=habit_list.id)) # Run the application if __name__ == '__main__': app.run(port=5000, debug=True)