From 53858044dd61944bddf6854b99dae31b656735d0 Mon Sep 17 00:00:00 2001 From: ArnabNath1 Date: Sat, 16 May 2026 14:12:15 +0530 Subject: [PATCH 1/2] Fix: implement minimum skill threshold and improve empty state --- .gitignore | 2 ++ routes/main_routes.py | 5 +++-- static/script.js | 23 +++++++++++++++++++++++ static/style.css | 22 ++++++++++++++++++++++ templates/index.html | 4 ++++ tests/test_basic.py | 4 ++-- utils/recommender.py | 6 ++++++ 7 files changed, 62 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 6370395..1226c99 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.log *.pyc *.swp +.venv/ +__pycache__/ .buildlog/ .history diff --git a/routes/main_routes.py b/routes/main_routes.py index 4955f36..4a5dd86 100644 --- a/routes/main_routes.py +++ b/routes/main_routes.py @@ -52,8 +52,9 @@ def recommend(): return jsonify({ "projects": [], "message": ( - "No projects matched your inputs. " - "Try different skills or broaden your interest area." + "No projects matched your exact skills. " + "Try adding broader skills like Python, JavaScript, HTML, or CSS " + "to see more recommendations." ) }), 200 diff --git a/static/script.js b/static/script.js index 58b5d60..1ad9491 100644 --- a/static/script.js +++ b/static/script.js @@ -55,6 +55,8 @@ if (isIndexPage) { var resultsLoadingEl = document.getElementById("results-loading"); var resultsEmptyEl = document.getElementById("results-empty"); var emptyMessageEl = document.getElementById("empty-message"); + var suggestedSkillsContainer = document.getElementById("suggested-skills-container"); + var suggestedSkillsList = document.getElementById("suggested-skills-list"); var skillsHidden = document.getElementById("skills"); var skillsTextInput = document.getElementById("skills-input"); var chipsSelectedEl = document.getElementById("skill-chips-selected"); @@ -479,10 +481,31 @@ if (isIndexPage) { resultsGrid.style.display = "none"; resultsEmptyEl.style.display = "block"; if (message && emptyMessageEl) emptyMessageEl.textContent = message; + + // Suggest skills that have projects in the dataset + var suggestions = ["Python", "JavaScript", "HTML", "CSS"]; + if (suggestedSkillsList && suggestedSkillsContainer) { + suggestedSkillsList.innerHTML = ""; + suggestions.forEach(function (skill) { + var btn = document.createElement("button"); + btn.type = "button"; + btn.className = "skill-chip"; + btn.textContent = skill; + btn.addEventListener("click", function () { + addSkill(skill); + form.dispatchEvent(new Event("submit")); + }); + suggestedSkillsList.appendChild(btn); + }); + suggestedSkillsContainer.style.display = "block"; + } + resultsSection.scrollIntoView({ behavior: "smooth" }); return; } + if (suggestedSkillsContainer) suggestedSkillsContainer.style.display = "none"; + resultsEmptyEl.style.display = "none"; resultsGrid.style.display = "grid"; diff --git a/static/style.css b/static/style.css index 837c450..afb5e71 100644 --- a/static/style.css +++ b/static/style.css @@ -1654,3 +1654,25 @@ select:focus { .container { padding: 0 20px; } .section-sub { margin-bottom: 40px; } } +/* ---- Suggested Skills in Empty State ---------------------- */ +.suggested-skills-container { + margin: 24px 0; + padding: 20px; + background: var(--indigo-50); + border-radius: var(--r-md); + border: 1px dashed var(--indigo-200); +} + +.suggestion-text { + font-size: 0.88rem; + font-weight: 700; + color: var(--indigo-700); + margin-bottom: 12px; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.skill-chips-row--center { + justify-content: center; + gap: 10px; +} diff --git a/templates/index.html b/templates/index.html index 14796a3..bba99a5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -368,6 +368,10 @@

Recommended Projects

No Projects Found

Try adjusting your skills or selecting a different interest area.

+ diff --git a/tests/test_basic.py b/tests/test_basic.py index cc0a80f..d2bd66a 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -142,8 +142,8 @@ def test_get_recommendations_max_three(): def test_get_recommendations_no_match_returns_empty(): """A very unlikely skill/interest combo should return an empty list.""" results = get_recommendations("Rust", "Advanced", "Games", "High") - # Rust and Games are not in the dataset so this should be empty or minimal - assert isinstance(results, list) + # Rust is not in the dataset, so zero skill overlap means zero results + assert results == [] def test_get_recommendations_result_format(): diff --git a/utils/recommender.py b/utils/recommender.py index e96f052..f50744d 100644 --- a/utils/recommender.py +++ b/utils/recommender.py @@ -106,6 +106,12 @@ def get_recommendations(skills_string, level, interest, time_availability): scored_projects = [] for project in all_projects: + # Require at least one skill match for a project to be recommended + project_skills = [s.lower() for s in project.get("skills", [])] + has_skill_match = any(s in project_skills for s in user_skills) + if not has_skill_match: + continue # Never recommend a project with zero skill overlap + score = score_single_project( project, user_skills, level, interest, time_availability ) From 7ac2f3b03aa840c05a1526bab36482b99774d555 Mon Sep 17 00:00:00 2001 From: ArnabNath1 Date: Mon, 18 May 2026 18:57:21 +0530 Subject: [PATCH 2/2] feat: Add JSON-LD structured data for LearningResource --- templates/project.html | 15 +++++++++++++++ tests/test_basic.py | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/templates/project.html b/templates/project.html index d964fb7..c4abdae 100644 --- a/templates/project.html +++ b/templates/project.html @@ -17,6 +17,21 @@ + diff --git a/tests/test_basic.py b/tests/test_basic.py index 3302e50..739a03a 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -255,6 +255,14 @@ def test_project_detail_not_found(): assert response.status_code == 404 +def test_project_detail_json_ld_present(): + """Project detail page must include JSON-LD structured data.""" + client = get_client() + response = client.get("/project/1") + assert response.status_code == 200 + assert b"application/ld+json" in response.data + + def test_internal_server_error_page(): """The 500 handler should render the friendly internal error template.""" with app.app_context(): @@ -280,7 +288,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()