from flask import Blueprint, render_template, redirect, url_for, request, flash, jsonify from flask_login import login_required, current_user from datetime import datetime, date from . import db from .models import init_db_models # Import model classes (stored on init_db_models function) DailyLog = init_db_models.DailyLog DailyLogItem = init_db_models.DailyLogItem UserSettings = init_db_models.UserSettings bp = Blueprint('main', __name__) # ─── Step 2: Daily Log (main page) ─────────────────────────────────── @bp.route('/') @login_required def index(): """Daily Log screen — main page (Step 2).""" today = date.today() log = DailyLog.query.filter_by(date=today, user_id=current_user.id).first() if not log: log = DailyLog(date=today, user_id=current_user.id) db.session.add(log) db.session.commit() # Get settings settings = UserSettings.query.filter_by(user_id=current_user.id).first() if not settings: settings = UserSettings(user_id=current_user.id) db.session.add(settings) db.session.commit() return render_template('daily_log.html', log=log, settings=settings) # ─── Step 2: All Logs ──────────────────────────────────────────────── @bp.route('/logs') @login_required def log_list(): """All Logs screen — list of dates (Step 2).""" logs = DailyLog.query.filter_by(user_id=current_user.id)\ .order_by(DailyLog.date.desc()).all() return render_template('log_list.html', logs=logs) # ─── Step 2: Daily Log Detail (by date) ────────────────────────────── @bp.route('/logs/') @login_required def log_detail(log_id): """Open the Daily Log for a specific date.""" log = DailyLog.query.get_or_404(log_id) if log.user_id != current_user.id: flash('Access denied.') return redirect(url_for('main.log_list')) return render_template('daily_log.html', log=log, settings=None) # ─── Step 4: Create / Edit Log ─────────────────────────────────────── @bp.route('/logs/new', methods=['GET', 'POST']) @login_required def log_new(): """Create a new Daily Log entry.""" if request.method == 'POST': log_date = request.form.get('date') try: log_date = datetime.strptime(log_date, '%Y-%m-%d').date() except ValueError: flash('Invalid date format.') return render_template('daily_log_form.html', action='New') existing = DailyLog.query.filter_by(date=log_date, user_id=current_user.id).first() if existing: flash('Log for this date already exists.') return redirect(url_for('main.log_detail', log_id=existing.id)) log = DailyLog( date=log_date, prompt=request.form.get('prompt', ''), notes=request.form.get('notes', ''), bedtime=request.form.get('bedtime', ''), wake_time=request.form.get('wake_time', ''), sleep_quality=request.form.get('sleep_quality'), mood=request.form.get('mood', ''), user_id=current_user.id, ) db.session.add(log) db.session.commit() # Handle gratitude (3 fields) gratitude = [request.form.get(f'gratitude_{i}', '') for i in range(3)] log.gratitude = gratitude # Handle bullets / items item_types = request.form.getlist('item_type[]') item_symbols = request.form.getlist('item_symbol[]') item_texts = request.form.getlist('item_text[]') item_statuses = request.form.getlist('item_status[]') for i in range(len(item_types)): if item_types[i] and item_texts[i].strip(): item = DailyLogItem( log_id=log.id, type=item_types[i], symbol=item_symbols[i] if item_symbols[i] else '•', text=item_texts[i].strip(), status=item_statuses[i] if item_statuses[i] in ['todo', 'doing', 'done', 'cancelled'] else 'todo', ) db.session.add(item) db.session.commit() flash('Log saved!') return redirect(url_for('main.log_detail', log_id=log.id)) return render_template('daily_log_form.html', action='New') @bp.route('/logs//edit', methods=['GET', 'POST']) @login_required def log_edit(log_id): """Edit an existing Daily Log entry.""" log = DailyLog.query.get_or_404(log_id) if log.user_id != current_user.id: flash('Access denied.') return redirect(url_for('main.log_list')) if request.method == 'POST': log.prompt = request.form.get('prompt', '') log.notes = request.form.get('notes', '') log.bedtime = request.form.get('bedtime', '') log.wake_time = request.form.get('wake_time', '') log.sleep_quality = request.form.get('sleep_quality') log.mood = request.form.get('mood', '') # Handle gratitude gratitude = [request.form.get(f'gratitude_{i}', '') for i in range(3)] log.gratitude = gratitude # Handle existing items and new items item_ids = request.form.getlist('item_id[]') item_types = request.form.getlist('item_type[]') item_symbols = request.form.getlist('item_symbol[]') item_texts = request.form.getlist('item_text[]') item_statuses = request.form.getlist('item_status[]') # Remove items not in item_ids existing_ids = [int(i) for i in item_ids if i.isdigit()] for item in log.items: if item.id not in existing_ids: db.session.delete(item) for i in range(len(item_types)): if item_ids[i] and item_ids[i].isdigit(): item = DailyLogItem.query.get(int(item_ids[i])) if item: item.type = item_types[i] item.symbol = item_symbols[i] if item_symbols[i] else '•' item.text = item_texts[i].strip() item.status = item_statuses[i] if item_statuses[i] in ['todo', 'doing', 'done', 'cancelled'] else 'todo' elif item_types[i] and item_texts[i].strip(): item = DailyLogItem( log_id=log.id, type=item_types[i], symbol=item_symbols[i] if item_symbols[i] else '•', text=item_texts[i].strip(), status=item_statuses[i] if item_statuses[i] in ['todo', 'doing', 'done', 'cancelled'] else 'todo', ) db.session.add(item) db.session.commit() flash('Log updated!') return redirect(url_for('main.log_detail', log_id=log.id)) return render_template('daily_log_form.html', log=log, action='Edit') # ─── Step 4: Add / Update Bullet (AJAX) ────────────────────────────── @bp.route('/logs//items', methods=['POST']) @login_required def add_log_item(log_id): """Add a bullet/item to a log (AJAX endpoint).""" log = DailyLog.query.get_or_404(log_id) if log.user_id != current_user.id: return jsonify({'success': False, 'error': 'Access denied.'}), 403 data = request.get_json() item_type = data.get('type', 'bullet') symbol = data.get('symbol', '•') text = data.get('text', '').strip() status = data.get('status', 'todo') if not text: return jsonify({'success': False, 'error': 'Text is required.'}), 400 item = DailyLogItem( log_id=log.id, type=item_type, symbol=symbol, text=text, status=status, ) db.session.add(item) db.session.commit() return jsonify({ 'success': True, 'id': item.id, 'type': item.type, 'symbol': item.symbol, 'text': item.text, 'status': item.status, }) @bp.route('/logs/items/', methods=['PUT']) @login_required def update_log_item(item_id): """Update a bullet/item status or text (AJAX endpoint).""" item = DailyLogItem.query.get_or_404(item_id) if item.log.user_id != current_user.id: return jsonify({'success': False, 'error': 'Access denied.'}), 403 data = request.get_json() if 'status' in data: item.status = data['status'] if 'text' in data: item.text = data['text'].strip() db.session.commit() return jsonify({'success': True, 'status': item.status, 'text': item.text}) @bp.route('/logs/items/', methods=['DELETE']) @login_required def delete_log_item(item_id): """Delete a bullet/item (AJAX endpoint).""" item = DailyLogItem.query.get_or_404(item_id) if item.log.user_id != current_user.id: return jsonify({'success': False, 'error': 'Access denied.'}), 403 db.session.delete(item) db.session.commit() return jsonify({'success': True}) # ─── Step 4: Export / Import ───────────────────────────────────────── @bp.route('/export') @login_required def export_logs(): """Export all logs as JSON (Step 4).""" logs = DailyLog.query.filter_by(user_id=current_user.id).all() data = [] for log in logs: log_data = { 'date': log.date.isoformat(), 'prompt': log.prompt, 'notes': log.notes, 'gratitude': log.gratitude or [], 'bedtime': log.bedtime, 'wake_time': log.wake_time, 'sleep_quality': log.sleep_quality, 'mood': log.mood, 'items': [ { 'type': item.type.value, 'symbol': item.symbol, 'text': item.text, 'status': item.status.value, } for item in log.items ], } data.append(log_data) return jsonify(data), 200, {'Content-Type': 'application/json', 'Content-Disposition': 'attachment; filename="logs.json"'} @bp.route('/import', methods=['POST']) @login_required def import_logs(): """Import logs from JSON file (Step 4).""" if 'file' not in request.files: flash('No file uploaded.') return redirect(url_for('main.settings')) file = request.files['file'] if not file or not file.filename.endswith('.json'): flash('Invalid file. Please upload a JSON file.') return redirect(url_for('main.settings')) import json as json_module try: data = file.read().decode('utf-8') logs_data = json_module.loads(data) imported = 0 for log_data in logs_data: log_date = datetime.strptime(log_data['date'], '%Y-%m-%d').date() existing = DailyLog.query.filter_by(date=log_date, user_id=current_user.id).first() if existing: continue # Skip existing log = DailyLog( date=log_date, prompt=log_data.get('prompt', ''), notes=log_data.get('notes', ''), gratitude=log_data.get('gratitude', []), bedtime=log_data.get('bedtime', ''), wake_time=log_data.get('wake_time', ''), sleep_quality=log_data.get('sleep_quality'), mood=log_data.get('mood', ''), user_id=current_user.id, ) db.session.add(log) db.session.flush() for item_data in log_data.get('items', []): item = DailyLogItem( log_id=log.id, type=item_data.get('type', 'bullet'), symbol=item_data.get('symbol', '•'), text=item_data.get('text', ''), status=item_data.get('status', 'todo'), ) db.session.add(item) imported += 1 db.session.commit() flash(f'{imported} logs imported!') except Exception as e: db.session.rollback() flash(f'Import failed: {str(e)}') return redirect(url_for('main.settings')) # ─── Step 4: Settings ──────────────────────────────────────────────── @bp.route('/settings') @login_required def settings(): """Settings screen (Step 2).""" s = UserSettings.query.filter_by(user_id=current_user.id).first() if not s: s = UserSettings(user_id=current_user.id) db.session.add(s) db.session.commit() return render_template('settings.html', settings=s) @bp.route('/settings/update', methods=['POST']) @login_required def update_settings(): """Update user settings.""" s = UserSettings.query.filter_by(user_id=current_user.id).first() if not s: s = UserSettings(user_id=current_user.id) db.session.add(s) s.theme = request.form.get('theme', 'light') s.font_size = int(request.form.get('font_size', 18)) db.session.commit() flash('Settings saved!') return redirect(url_for('main.settings'))