import datetime import hashlib from flask import Flask, render_template, redirect, url_for, request 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.HabitTrackings import HabitTrackings 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 else: habit_lists = [] name = "Bitte melde dich an." # 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)) return render_template( 'index.html', title=name, utc_dt=datetime.datetime.now().strftime("%d.%m.%Y %H:%M %A"), habit_lists=habit_lists, 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, errors={} ) @app.route('/profile', methods=['POST']) @login_required def profile_change(): newName = request.form.get('newName') newEmail = request.form.get('newEmail') newPassword = request.form.get('newPassword') oldPassword = request.form.get('oldPassword') # Check for errors errors = {} if not newName: errors['newName'] = 'Der Name ist erforderlich.' if not newEmail: errors['newEmail'] = 'Die E-Mail Adresse ist erforderlich.' 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.' if errors: return render_template( "profile.html", name=current_user.name, email=current_user.email, errors=errors ) # Update user current_user.name = newName current_user.email = newEmail 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, 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: HabitTrackings.create(habit_id, 1) else: delete_tracking.delete() # Update habit habit.fill_statistics() return { "habitId": habit_id, "unchecked": not delete_tracking, "percentage": habit.percentage, } @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 {} # Run the application if __name__ == '__main__': app.run(port=5000, debug=True)