From 9ef70e0436884909ccc90d2f9523a27beb7a47e5 Mon Sep 17 00:00:00 2001 From: calboo Date: Tue, 10 Feb 2026 19:46:25 +0100 Subject: [PATCH] Goals working well --- app.py | 142 ++++++++++++++++++++++++++++++++++++--- templates/dashboard.html | 75 +++++++++++++++++++-- templates/edit_goal.html | 84 +++++++++++++++++++++++ templates/goals.html | 111 ++++++++++++++++++++++++++++++ templates/layout.html | 1 + 5 files changed, 398 insertions(+), 15 deletions(-) create mode 100644 templates/edit_goal.html create mode 100644 templates/goals.html diff --git a/app.py b/app.py index 47a8d12..f13e864 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ from flask import Flask, render_template, request, redirect, url_for, session, f from flask_bcrypt import Bcrypt from pymongo import MongoClient from bson.objectid import ObjectId -from datetime import datetime +from datetime import datetime, timedelta import os from dotenv import load_dotenv @@ -109,18 +109,23 @@ def add_activity(): 'user_id': get_user_id(), 'name': name, 'color': color, - 'subcategories': [] # Initialize empty list + 'subcategories': [] }).inserted_id - # Add optional tasks - tasks_text = request.form.get('tasks', '') - if tasks_text: - tasks = [t.strip() for t in tasks_text.split(',') if t.strip()] + # Add optional default tasks (Revised from list input) + tasks_str = request.form.get('tasks_list_data', '') + if tasks_str: + tasks = [t.strip() for t in tasks_str.split(',') if t.strip()] for t in tasks: - db.task_templates.insert_one({ - 'activity_id': activity_id, + db.tasks.insert_one({ 'user_id': get_user_id(), - 'name': t + 'name': t, + 'activity_id': ObjectId(activity_id), + 'is_template': True, + 'status': 'open', + 'time_entry_id': None, + 'created_at': datetime.now(), + 'comments': [] }) return redirect(url_for('index')) @@ -183,7 +188,7 @@ def start_timer_bg(activity_id): 'user_id': user_id, 'name': t['name'], 'activity_id': ObjectId(activity_id), - 'time_entry_id': new_entry_id, + 'time_entry_id': new_entry_id, # Link to this specific session 'status': 'open', 'is_template': False, 'created_at': datetime.now(), @@ -498,5 +503,122 @@ def log_entry_detail(entry_id): return render_template('logbook_detail.html', entry=entry, tasks=tasks) +@app.route('/goals', methods=['GET', 'POST']) +def goals(): + if not is_logged_in(): return redirect(url_for('login')) + user_id = get_user_id() + + # Handle creating new goal + if request.method == 'POST': + name = request.form['name'] + target_hours = float(request.form['target_hours']) + frequency = request.form['frequency'] # daily, weekly, monthly, yearly + activity_id = request.form.get('activity_id') # Required now + subcategory = request.form.get('subcategory', '') + + if not activity_id: + flash("You must select an activity for this goal.") + return redirect(url_for('goals')) + + db.goals.insert_one({ + 'user_id': user_id, + 'name': name, + 'target_hours': target_hours, + 'frequency': frequency, + 'activity_id': ObjectId(activity_id), + 'subcategory': subcategory, + 'created_at': datetime.now() + }) + return redirect(url_for('goals')) + + # Fetch goals and calculate progress + goals_list = list(db.goals.find({'user_id': user_id})) + activities = list(db.activities.find({'user_id': user_id})) + + today = datetime.now() + start_of_today = today.replace(hour=0, minute=0, second=0, microsecond=0) + + # Determine date ranges + ranges = { + 'daily': start_of_today, + 'weekly': start_of_today - timedelta(days=today.weekday()), # Monday + 'monthly': start_of_today.replace(day=1), + 'yearly': start_of_today.replace(month=1, day=1) + } + + for goal in goals_list: + start_date = ranges.get(goal['frequency'], start_of_today) + + query = { + 'user_id': user_id, + 'start_time': {'$gte': start_date}, + 'end_time': {'$ne': None} + } + + if goal.get('activity_id'): + query['activity_id'] = goal['activity_id'] + + if goal.get('subcategory'): + query['subcategory'] = goal['subcategory'] + + entries = list(db.time_entries.find(query)) + + total_seconds = sum([(e['end_time'] - e['start_time']).total_seconds() for e in entries]) + current_hours = total_seconds / 3600 + + # Display formatting: Show minutes if < 1 hour, else show hours + if current_hours > 0 and current_hours < 1: + minutes = int(total_seconds / 60) + goal['display_progress'] = f"{minutes}m" + else: + goal['display_progress'] = f"{round(current_hours, 1)}h" + + goal['current_hours'] = round(current_hours, 1) # Keep purely numeric for math/sorting if needed + goal['percent'] = min(100, int((current_hours / goal['target_hours']) * 100)) + + # Link activity name for display + if goal.get('activity_id'): + act = db.activities.find_one({'_id': goal['activity_id']}) + goal['activity_name'] = act['name'] if act else 'Unknown' + goal['activity_color'] = act['color'] if act else '#ccc' + + return render_template('goals.html', goals=goals_list, activities=activities) + +@app.route('/edit_goal/', methods=['GET', 'POST']) +def edit_goal(goal_id): + if not is_logged_in(): return redirect(url_for('login')) + user_id = get_user_id() + + if request.method == 'POST': + name = request.form['name'] + target_hours = float(request.form['target_hours']) + frequency = request.form['frequency'] + activity_id = request.form.get('activity_id') + subcategory = request.form.get('subcategory', '') + + db.goals.update_one( + {'_id': ObjectId(goal_id), 'user_id': user_id}, + {'$set': { + 'name': name, + 'target_hours': target_hours, + 'frequency': frequency, + 'activity_id': ObjectId(activity_id), + 'subcategory': subcategory + }} + ) + return redirect(url_for('goals')) + + goal = db.goals.find_one({'_id': ObjectId(goal_id), 'user_id': user_id}) + if not goal: return "Goal not found", 404 + + activities = list(db.activities.find({'user_id': user_id})) + return render_template('edit_goal.html', goal=goal, activities=activities) + +@app.route('/delete_goal/') +def delete_goal(goal_id): + if not is_logged_in(): return redirect(url_for('login')) + db.goals.delete_one({'_id': ObjectId(goal_id), 'user_id': get_user_id()}) + return redirect(url_for('goals')) + if __name__ == '__main__': app.run(debug=True) diff --git a/templates/dashboard.html b/templates/dashboard.html index 1f5a8b4..771a06e 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -189,18 +189,83 @@ + + {% endblock %} diff --git a/templates/edit_goal.html b/templates/edit_goal.html new file mode 100644 index 0000000..ac258b2 --- /dev/null +++ b/templates/edit_goal.html @@ -0,0 +1,84 @@ +{% extends "layout.html" %} +{% block content %} +
+

Edit Goal

+
+ + + + + + + + + + + + + + +
+ + Cancel +
+
+
+ + +{% endblock %} diff --git a/templates/goals.html b/templates/goals.html new file mode 100644 index 0000000..93c2917 --- /dev/null +++ b/templates/goals.html @@ -0,0 +1,111 @@ +{% extends "layout.html" %} +{% block content %} +
+

My Goals

+
+ {% for goal in goals %} +
+
+ Edit + × +
+ +

{{ goal.name }}

+
+ {{ goal.frequency|capitalize }} Target: {{ goal.target_hours }}h +
+
+ + {{ goal.activity_name }} + + {% if goal.subcategory %} + > {{ goal.subcategory }} + {% endif %} +
+
+ +
+ + {{ goal.display_progress }} + {{ goal.percent }}% +
+ + +
+
+
+
+ {% else %} +

No goals set yet.

+ {% endfor %} +
+
+ +
+

Set New Goal

+
+ + + + + + + + + + + + + + + +
+
+ + +{% endblock %} diff --git a/templates/layout.html b/templates/layout.html index 64f6e9e..10fbc52 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -160,6 +160,7 @@ {% if session.user_id %} Tracker Tasks + Goals Logbook Logout {% else %}