From 9b1012b3b852b866f69ffbee938e0d5dc4b24eaa Mon Sep 17 00:00:00 2001 From: anshul23102 Date: Mon, 18 May 2026 12:52:35 +0530 Subject: [PATCH 1/2] fix: implement thread-safe in-memory cache in data_loader.py _projects_cache was declared but load_all_projects() never read or wrote it, causing a redundant disk read on every request. Added double-checked locking with threading.Lock so the JSON file is read once and reused safely across concurrent requests. clear_cache() now acquires the same lock before resetting. Co-Authored-By: Claude Sonnet 4.6 --- utils/data_loader.py | 58 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/utils/data_loader.py b/utils/data_loader.py index 569199d1..23b53d14 100644 --- a/utils/data_loader.py +++ b/utils/data_loader.py @@ -1,58 +1,58 @@ -# utils/data_loader.py -# Handles all reading and lookup of project data from the JSON file. - import json import os +import threading -# Build the path to projects.json relative to this file's location DATA_FILE = os.path.join(os.path.dirname(__file__), "..", "data", "projects.json") +_projects_cache = None +_cache_lock = threading.Lock() + def load_all_projects(): - """Read and return the full list of projects from the JSON file.""" - with open(DATA_FILE, "r", encoding="utf-8") as f: - return json.load(f) + """Read and return the full list of projects from the JSON file. + + Results are cached in memory after the first read so subsequent calls + do not hit the filesystem. + """ + global _projects_cache + if _projects_cache is not None: + return _projects_cache + with _cache_lock: + if _projects_cache is None: + with open(DATA_FILE, "r", encoding="utf-8") as f: + _projects_cache = json.load(f) + return _projects_cache def find_project_by_id(project_id): - """ - Return the project dict whose 'id' matches the given integer. - Returns None if no match is found. - """ + """Return the project whose 'id' matches project_id, or None.""" for project in load_all_projects(): if project.get("id") == project_id: return project return None + def get_project_stats(): - """ - Calculate and return statistics about the projects. - Returns: { total_projects, unique_skills, beginner_friendly } - """ + """Return total_projects, unique_skills, and beginner_friendly counts.""" projects = load_all_projects() - total_projects = len(projects) - # Collect all unique skills all_skills = set() + beginner_friendly = 0 for p in projects: for s in p.get("skills", []): all_skills.add(s) - unique_skills = len(all_skills) - - # Count beginner projects - beginner_friendly = len([p for p in projects if p.get("level") == "Beginner"]) + if p.get("level") == "Beginner": + beginner_friendly += 1 return { - "total_projects": total_projects, - "unique_skills": unique_skills, - "beginner_friendly": beginner_friendly + "total_projects": len(projects), + "unique_skills": len(all_skills), + "beginner_friendly": beginner_friendly, } -# Cache for loaded projects -_projects_cache = None - def clear_cache(): - """Reset the in-memory project cache.""" + """Reset the in-memory project cache (used in tests).""" global _projects_cache - _projects_cache = None + with _cache_lock: + _projects_cache = None From dd96f528f2fe67097cef04afca55fc8452dbbc74 Mon Sep 17 00:00:00 2001 From: anshul23102 Date: Mon, 18 May 2026 12:57:28 +0530 Subject: [PATCH 2/2] fix: resolve missing pytest fixture in test_health_check test_health_check(client) declared a 'client' parameter expecting a pytest fixture that was never defined. Every other test in the suite uses the local get_client() helper instead. The error caused the entire test run to report an ERROR and the health endpoint was never actually tested. --- tests/test_basic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index a66c75b6..fc41aa75 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -280,7 +280,8 @@ def test_download_code_found(): response = client.get("/project/1/download") assert response.status_code == 200 -def test_health_check(client): +def test_health_check(): + client = get_client() response = client.get("/health") assert response.status_code == 200 data = response.get_json()