From 9f93e26df494c6be652620c71afc5fb311a31270 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:20:01 +0000 Subject: [PATCH 1/4] Initial plan From 15d7a3993b3068001ab00691f406eb8b744e641a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:27:40 +0000 Subject: [PATCH 2/4] Implement Flask backend API with routes and tests Co-authored-by: moulongzhang <39043782+moulongzhang@users.noreply.github.com> --- .gitignore | 39 ++++++++++++++ main.py | 81 ++++++++++++++++++++++++++++ requirements.txt | 2 + test_main.py | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 256 insertions(+) create mode 100644 .gitignore create mode 100644 requirements.txt create mode 100644 test_main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5a5ca9af --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +*.egg +*.egg-info/ +dist/ +build/ + +# Virtual Environment +venv/ +env/ +ENV/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ + +# Flask +instance/ +.webassets-cache +flask.log + +# Progress data +progress.json + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db diff --git a/main.py b/main.py index e69de29b..8c54d74e 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,81 @@ +from flask import Flask, jsonify, request +import json +import os + +app = Flask(__name__) + +# In-memory storage for progress data +progress_data = [] + +# File path for persistence +PROGRESS_FILE = 'progress.json' + +# Load progress from file if it exists +def load_progress(): + global progress_data + if os.path.exists(PROGRESS_FILE): + try: + with open(PROGRESS_FILE, 'r', encoding='utf-8') as f: + progress_data = json.load(f) + except Exception as e: + print(f"Error loading progress: {e}") + progress_data = [] + +# Save progress to file +def save_progress(): + try: + with open(PROGRESS_FILE, 'w', encoding='utf-8') as f: + json.dump(progress_data, f, ensure_ascii=False, indent=2) + except Exception as e: + print(f"Error saving progress: {e}") + +# Load progress on startup +load_progress() + +@app.route('/') +def index(): + """Top page route""" + return jsonify({ + "message": "Welcome to the Progress API", + "endpoints": { + "/": "This page", + "/api/progress": "GET - Retrieve progress data, POST - Save progress data" + } + }) + +@app.route('/api/progress', methods=['GET']) +def get_progress(): + """Get progress data""" + return jsonify({ + "success": True, + "data": progress_data + }) + +@app.route('/api/progress', methods=['POST']) +def post_progress(): + """Save progress data""" + try: + data = request.get_json() + + if not data: + return jsonify({ + "success": False, + "error": "No data provided" + }), 400 + + progress_data.append(data) + save_progress() + + return jsonify({ + "success": True, + "message": "Progress saved successfully", + "data": data + }), 201 + except Exception as e: + return jsonify({ + "success": False, + "error": str(e) + }), 500 + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0', port=5000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..dfb43b0f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.0 +pytest==7.4.3 diff --git a/test_main.py b/test_main.py new file mode 100644 index 00000000..05fade6e --- /dev/null +++ b/test_main.py @@ -0,0 +1,134 @@ +import pytest +import json +import os +from main import app, progress_data, PROGRESS_FILE + +@pytest.fixture +def client(): + """Create a test client for the Flask app""" + app.config['TESTING'] = True + with app.test_client() as client: + yield client + +@pytest.fixture(autouse=True) +def cleanup(): + """Clean up progress data before and after each test""" + # Clear in-memory data + progress_data.clear() + + # Remove progress file if it exists + if os.path.exists(PROGRESS_FILE): + os.remove(PROGRESS_FILE) + + yield + + # Clean up after test + progress_data.clear() + if os.path.exists(PROGRESS_FILE): + os.remove(PROGRESS_FILE) + +def test_index_route(client): + """Test the index (/) route""" + response = client.get('/') + assert response.status_code == 200 + data = json.loads(response.data) + assert 'message' in data + assert 'endpoints' in data + assert data['message'] == "Welcome to the Progress API" + +def test_get_progress_empty(client): + """Test GET /api/progress with no data""" + response = client.get('/api/progress') + assert response.status_code == 200 + data = json.loads(response.data) + assert data['success'] is True + assert data['data'] == [] + +def test_post_progress_valid(client): + """Test POST /api/progress with valid data""" + test_data = { + "task": "Implement Flask API", + "status": "completed", + "percentage": 100 + } + + response = client.post('/api/progress', + data=json.dumps(test_data), + content_type='application/json') + + assert response.status_code == 201 + data = json.loads(response.data) + assert data['success'] is True + assert data['message'] == "Progress saved successfully" + assert data['data'] == test_data + +def test_post_progress_no_data(client): + """Test POST /api/progress with no data""" + response = client.post('/api/progress', + data=json.dumps(None), + content_type='application/json') + + assert response.status_code == 400 + data = json.loads(response.data) + assert data['success'] is False + assert 'error' in data + +def test_get_progress_after_post(client): + """Test GET /api/progress after posting data""" + test_data = { + "task": "Write tests", + "status": "in progress", + "percentage": 50 + } + + # Post data + client.post('/api/progress', + data=json.dumps(test_data), + content_type='application/json') + + # Get data + response = client.get('/api/progress') + assert response.status_code == 200 + data = json.loads(response.data) + assert data['success'] is True + assert len(data['data']) == 1 + assert data['data'][0] == test_data + +def test_multiple_progress_entries(client): + """Test posting multiple progress entries""" + entries = [ + {"task": "Task 1", "status": "completed"}, + {"task": "Task 2", "status": "in progress"}, + {"task": "Task 3", "status": "pending"} + ] + + for entry in entries: + response = client.post('/api/progress', + data=json.dumps(entry), + content_type='application/json') + assert response.status_code == 201 + + # Verify all entries are stored + response = client.get('/api/progress') + data = json.loads(response.data) + assert len(data['data']) == 3 + assert data['data'] == entries + +def test_progress_persistence(client): + """Test that progress data persists to file""" + test_data = {"task": "Test persistence", "status": "saved"} + + # Post data + client.post('/api/progress', + data=json.dumps(test_data), + content_type='application/json') + + # Check that file was created + assert os.path.exists(PROGRESS_FILE) + + # Read file and verify content + with open(PROGRESS_FILE, 'r', encoding='utf-8') as f: + file_data = json.load(f) + + assert len(file_data) == 1 + assert file_data[0] == test_data From 4f63534fdd297c227aaa8d62f6d1473033af02e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:30:06 +0000 Subject: [PATCH 3/4] Add thread safety with locks for progress data operations Co-authored-by: moulongzhang <39043782+moulongzhang@users.noreply.github.com> --- main.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/main.py b/main.py index 8c54d74e..c40067f8 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,16 @@ from flask import Flask, jsonify, request import json import os +import threading app = Flask(__name__) # In-memory storage for progress data progress_data = [] +# Thread lock for synchronization +progress_lock = threading.Lock() + # File path for persistence PROGRESS_FILE = 'progress.json' @@ -46,10 +50,11 @@ def index(): @app.route('/api/progress', methods=['GET']) def get_progress(): """Get progress data""" - return jsonify({ - "success": True, - "data": progress_data - }) + with progress_lock: + return jsonify({ + "success": True, + "data": progress_data.copy() + }) @app.route('/api/progress', methods=['POST']) def post_progress(): @@ -63,8 +68,9 @@ def post_progress(): "error": "No data provided" }), 400 - progress_data.append(data) - save_progress() + with progress_lock: + progress_data.append(data) + save_progress() return jsonify({ "success": True, From 002d0c11328191f09a9e14e9635eeede5cd864e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 08:32:19 +0000 Subject: [PATCH 4/4] Disable Flask debug mode for security Co-authored-by: moulongzhang <39043782+moulongzhang@users.noreply.github.com> --- main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.py b/main.py index c40067f8..0dae20f5 100644 --- a/main.py +++ b/main.py @@ -84,4 +84,4 @@ def post_progress(): }), 500 if __name__ == '__main__': - app.run(debug=True, host='0.0.0.0', port=5000) + app.run(debug=False, host='0.0.0.0', port=5000)