# app.py (With JSON Download Feature) from flask import Flask, render_template, request, redirect, url_for, session, flash, abort, Response from datetime import datetime import copy import json # Import the json library # --- App Configuration --- app = Flask(__name__) app.config['SECRET_KEY'] = 'a-key-for-json-download-demo' # --- In-Memory Data Store --- chirps_data = {} chirp_id_counter = 0 # --- Helper Function to Build the Comment Tree --- def get_chirp_tree(): nodes = copy.deepcopy(chirps_data) for chirp_id in nodes: nodes[chirp_id]['children'] = [] nested_chirps = [] for chirp_id, chirp in nodes.items(): if chirp['parent_id'] is not None: parent = nodes.get(chirp['parent_id']) if parent: parent['children'].append(chirp) else: nested_chirps.append(chirp) for chirp_id in nodes: nodes[chirp_id]['children'].sort(key=lambda x: x['timestamp']) nested_chirps.sort(key=lambda x: x['timestamp'], reverse=True) return nested_chirps # --- Routes --- @app.route('/', methods=['GET', 'POST']) def index(): if 'username' not in session: return redirect(url_for('join')) if request.method == 'POST': global chirp_id_counter content = request.form.get('content') if content: chirp_id_counter += 1 new_chirp = { 'id': chirp_id_counter, 'parent_id': None, 'username': session['username'], 'content': content, 'timestamp': datetime.utcnow() } chirps_data[new_chirp['id']] = new_chirp flash('Your new chirp has been posted!', 'success') return redirect(url_for('index')) top_level_posts = get_chirp_tree() return render_template('index.html', posts=top_level_posts) @app.route('/chirp/') def view_chirp(chirp_id): if 'username' not in session: return redirect(url_for('join')) all_chirps_tree = get_chirp_tree() def find_chirp_in_tree(chirp_id, chirps): for chirp in chirps: if chirp['id'] == chirp_id: return chirp found = find_chirp_in_tree(chirp_id, chirp.get('children', [])) if found: return found return None main_chirp = find_chirp_in_tree(chirp_id, all_chirps_tree) if not main_chirp: abort(404) return render_template('view_chirp.html', main_chirp=main_chirp) @app.route('/reply/', methods=['POST']) def reply(parent_id): if 'username' not in session: return redirect(url_for('join')) global chirp_id_counter content = request.form.get('content') if parent_id not in chirps_data: flash('Cannot reply to a non-existent chirp.', 'danger') return redirect(url_for('index')) if content: chirp_id_counter += 1 new_comment = { 'id': chirp_id_counter, 'parent_id': parent_id, 'username': session['username'], 'content': content, 'timestamp': datetime.utcnow() } chirps_data[new_comment['id']] = new_comment flash('Your reply was posted!', 'success') else: flash('Reply cannot be empty.', 'danger') return redirect(url_for('view_chirp', chirp_id=parent_id)) # --- NEW DOWNLOAD ROUTE --- @app.route('/download_data') def download_data(): """Filters user's chirps and provides them as a JSON file download.""" if 'username' not in session: flash("You must be logged in to download your data.", "danger") return redirect(url_for('join')) current_username = session['username'] # Filter the chirps_data dictionary to get only the user's posts and comments user_chirps = [ chirp for chirp in chirps_data.values() if chirp['username'] == current_username ] # Sort the user's data by time for consistency user_chirps.sort(key=lambda x: x['timestamp']) # Prepare the JSON data. `indent=2` makes it human-readable. # `default=str` is crucial to handle datetime objects, which are not JSON serializable by default. json_data = json.dumps(user_chirps, indent=2, default=str) # Create a dynamic filename filename = f"chirp_data_{current_username}.json" # Create a Flask Response object return Response( json_data, mimetype="application/json", headers={"Content-Disposition": f"attachment;filename={filename}"} ) # --- Standard User Routes --- @app.route('/join', methods=['GET', 'POST']) def join(): if 'username' in session: return redirect(url_for('index')) if request.method == 'POST': username = request.form.get('username') if username: session['username'] = username flash(f'Welcome, {username}!', 'success') return redirect(url_for('index')) else: flash('Please enter a name to join.', 'danger') return render_template('join.html') @app.route('/logout') def logout(): session.pop('username', None) flash('You have been logged out.', 'success') return redirect(url_for('join')) if __name__ == '__main__': app.run(debug=True,host="0.0.0.0",port=7860)