from flask import Flask, render_template, request, jsonify from tinydb import TinyDB, Query from datetime import datetime, timedelta app = Flask(__name__) db = TinyDB('habits.json') Habit = Query() # Helper function to get the current date string def get_current_date(): return datetime.now().strftime('%Y-%m-%d') # Helper function to get a list of dates for the last 30 days def get_last_30_days(): dates = [] for i in range(30): date = datetime.now() - timedelta(days=i) dates.append(date.strftime('%Y-%m-%d')) return dates[::-1] # Reverse to have oldest first @app.route('/') def index(): return render_template('index.html') @app.route('/habits', methods=['GET']) def get_habits(): raw_habits = db.all() formatted_habits = [] last_30_days = get_last_30_days() for habit_doc in raw_habits: # Handle migration from old format (list) to new format (dict) completed_dates = habit_doc.get('completed_dates', {}) if isinstance(completed_dates, list): # Convert old format to new format new_completed_dates = {} for date in completed_dates: new_completed_dates[date] = 1 completed_dates = new_completed_dates # Update in database db.update({'completed_dates': completed_dates}, doc_ids=[habit_doc.doc_id]) current_habit_data = { 'id': habit_doc.doc_id, 'name': habit_doc.get('name'), 'completed_dates': completed_dates, 'color': habit_doc.get('color', '#4CAF50'), 'daily_target': habit_doc.get('daily_target', 1) } completion_history = {} for date_str in last_30_days: completed_count = completed_dates.get(date_str, 0) target_count = current_habit_data['daily_target'] completion_history[date_str] = { 'completed': completed_count, 'target': target_count, 'is_complete': completed_count >= target_count } current_habit_data['completion_history'] = completion_history formatted_habits.append(current_habit_data) return jsonify(formatted_habits) @app.route('/habits', methods=['POST']) def add_habit(): data = request.json name = data.get('name') color = data.get('color', '#4CAF50') # Default green color daily_target = data.get('daily_target', 1) # Default 1x per day if not name: return jsonify({'error': 'Habit name is required'}), 400 habit_id = db.insert({'name': name, 'completed_dates': {}, 'color': color, 'daily_target': daily_target}) return jsonify({'id': habit_id, 'name': name, 'completed_dates': {}, 'color': color, 'daily_target': daily_target}), 201 @app.route('/habits//complete', methods=['POST']) def complete_habit(habit_id): date_to_complete = request.json.get('date', get_current_date()) habit = db.get(doc_id=habit_id) if not habit: return jsonify({'error': 'Habit not found'}), 404 # Handle migration from old format (list) to new format (dict) completed_dates = habit.get('completed_dates', {}) if isinstance(completed_dates, list): new_completed_dates = {} for date in completed_dates: new_completed_dates[date] = 1 completed_dates = new_completed_dates # Increment completion count for the date current_count = completed_dates.get(date_to_complete, 0) completed_dates[date_to_complete] = current_count + 1 db.update({'completed_dates': completed_dates}, doc_ids=[habit_id]) return jsonify({ 'message': f'Habit {habit_id} marked as completed for {date_to_complete}', 'completed_dates': completed_dates, 'current_count': completed_dates[date_to_complete] }) @app.route('/habits//uncomplete', methods=['POST']) def uncomplete_habit(habit_id): date_to_uncomplete = request.json.get('date', get_current_date()) habit = db.get(doc_id=habit_id) if not habit: return jsonify({'error': 'Habit not found'}), 404 # Handle migration from old format (list) to new format (dict) completed_dates = habit.get('completed_dates', {}) if isinstance(completed_dates, list): new_completed_dates = {} for date in completed_dates: new_completed_dates[date] = 1 completed_dates = new_completed_dates # Decrement completion count for the date if date_to_uncomplete in completed_dates: current_count = completed_dates[date_to_uncomplete] if current_count > 1: completed_dates[date_to_uncomplete] = current_count - 1 else: del completed_dates[date_to_uncomplete] db.update({'completed_dates': completed_dates}, doc_ids=[habit_id]) return jsonify({ 'message': f'Habit {habit_id} marked as uncompleted for {date_to_uncomplete}', 'completed_dates': completed_dates, 'current_count': completed_dates.get(date_to_uncomplete, 0) }) @app.route('/habits/', methods=['PUT']) def update_habit(habit_id): data = request.json name = data.get('name') color = data.get('color') daily_target = data.get('daily_target') if not name and not color and not daily_target: return jsonify({'error': 'Habit name, color, or daily target is required'}), 400 habit = db.get(doc_id=habit_id) if not habit: return jsonify({'error': 'Habit not found'}), 404 update_data = {} if name: update_data['name'] = name if color: update_data['color'] = color if daily_target: update_data['daily_target'] = int(daily_target) db.update(update_data, doc_ids=[habit_id]) return jsonify({'message': f'Habit {habit_id} updated', **update_data}) @app.route('/habits/', methods=['DELETE']) def delete_habit(habit_id): habit = db.get(doc_id=habit_id) if not habit: return jsonify({'error': 'Habit not found'}), 404 db.remove(doc_ids=[habit_id]) return jsonify({'message': f'Habit {habit_id} deleted'}) if __name__ == '__main__': app.run(debug=True)