Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions routes/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@
from utils.file_server import read_starter_code, resolve_starter_file, get_starter_code_dir
import os

# Interest categories that currently have no project recommendations available
NO_PROJECT_INTERESTS = {
"machine learning/ai",
"devops",
"mobile",
"artificial intelligence",
"cloud computing",
"mobile app development",
}

def interest_has_no_projects(interest):
return interest and interest.strip().lower() in NO_PROJECT_INTERESTS

# Create the Blueprint that app.py will register
main = Blueprint("main", __name__)

Expand Down Expand Up @@ -58,6 +71,12 @@ def recommend():
# Return only the first error to keep the UI message clean
return jsonify({"error": errors[0]}), 400

if interest_has_no_projects(interest):
return jsonify({
"projects": [],
"message": "No projects are currently available for this interest area. Please check back later."
}), 200

results = get_recommendations(skills, level, interest, time_availability)

if not results:
Expand Down
20 changes: 15 additions & 5 deletions static/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -615,9 +615,19 @@ if (clearFiltersBtn) {
resultsEmptyEl.style.display = "block";
if (message && emptyMessageEl) emptyMessageEl.textContent = message;
if (!projects || projects.length === 0) { //if no projects returned from api, show the "no results" message and hide the grid
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "block";
if (message && emptyMessageEl) emptyMessageEl.textContent = message; //if api sent back a message (e.g. "no projects found matching your criteria"), show that
resultsGrid.style.display = "none";
resultsEmptyEl.style.display = "block";

// Show a friendly custom message when the user selected an interest
var selectedInterest = document.getElementById("interest")?.value;
if (selectedInterest) {
emptyMessageEl.textContent = "No projects are currently available for this interest. Please check back later or try a different area.";
} else if (message) {
emptyMessageEl.textContent = message;
} else {
emptyMessageEl.textContent = "Try adjusting your skills or choosing a different interest area.";
}

resultsSection.scrollIntoView({ behavior: "smooth" });
return;
}
Expand Down Expand Up @@ -654,8 +664,8 @@ if (clearFiltersBtn) {
var tagsRow = document.createElement("div");
tagsRow.className = "project-card-tags";

// Show the first two skills as tags
(project.skills || []).slice(0, 2).forEach(function (skill) {
// Show all project skills as tags so users can see the full match
(project.skills || []).forEach(function (skill) {
tagsRow.appendChild(createTag(skill, "skill"));
});

Expand Down
5 changes: 4 additions & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,6 @@ <h2 class="section-title">Find Your Next Project</h2>
</div>
<div class="skill-input-wrap" id="skill-input-wrap">
<div class="skill-chips-selected" id="skill-chips-selected"></div>

<input
type="text"
id="skills-input"
Expand Down Expand Up @@ -489,6 +488,10 @@ <h2 class="section-title">Find Your Next Project</h2>
<option value="Education">Education Tools</option>
<option value="Automation">Automation</option>
<option value="Games">Games</option>
<option value="Cybersecurity">CyberSecurity/Ethical Hacking</option>
<option value="Devops">Devops/Cloud Computing</option>
<option value="Mobile">Mobile Development</option>
<option value="Machine Learning/AI">Machine Learning/AI</option>
</select>
</div>

Expand Down
19 changes: 19 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# tests/conftest.py
# Pytest configuration and shared fixtures for DevPath tests.

import sys
import os

# Allow imports from the project root when running tests
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

import pytest
from app import app


@pytest.fixture
def client():
"""Provide a Flask test client for testing the application."""
app.config['TESTING'] = True
with app.test_client() as client:
yield client
16 changes: 16 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,22 @@ def test_recommend_api_valid():
assert len(data["projects"]) > 0


def test_recommend_api_interest_not_available():
"""The API should return no projects for blocked interest categories."""
client = get_client()
response = client.post("/api/recommend", json={
"skills": "Python, JavaScript",
"level": "Beginner",
"interest": "Machine Learning/AI",
"time": "Low"
})
assert response.status_code == 200
data = response.get_json()
assert data["projects"] == []
assert "message" in data
assert "no projects are currently available" in data["message"].lower()


def test_recommend_api_missing_field():
"""The API should return 400 when a required field is missing."""
client = get_client()
Expand Down
Loading