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
3 changes: 3 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
# Business logic, recommendation scoring, and data loading all live in
# the utils/ and routes/ packages, not here.

import os

from flask import Flask, render_template
from routes.main_routes import main


app = Flask(__name__)

# Register all routes defined in the main Blueprint
Expand Down
2 changes: 2 additions & 0 deletions data/projects.json
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,5 @@





95 changes: 51 additions & 44 deletions routes/main_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
# Each route is kept thin: it validates input, calls a utility function,
# and returns a response. No business logic lives here.

from flask import Blueprint, render_template, request, jsonify, send_from_directory, abort, make_response
import json

from flask import Blueprint, render_template, request, jsonify, send_from_directory, abort

from utils.recommender import get_recommendations, validate_recommendation_inputs
from utils.data_loader import find_project_by_id, load_all_projects, get_project_stats
from utils.file_server import read_starter_code, resolve_starter_file, get_starter_code_dir
import os
from groq import Groq
from dotenv import load_dotenv

load_dotenv()
groq_client = Groq(api_key=os.environ["GROQ_API_KEY"])

# Interest categories that currently have no project recommendations available
NO_PROJECT_INTERESTS = {
Expand Down Expand Up @@ -44,52 +51,52 @@ def health_check():
}), 200



@main.route("/api/recommend", methods=["POST"])
def recommend():
"""
Accept a JSON body with user inputs and return matching project recommendations.

Expected JSON fields:
skills (str) - comma-separated list of skills
level (str) - Beginner | Intermediate | Advanced
interest (str) - Web | Data | Education | Automation | Games
time (str) - Low | Medium | High
"""
payload = request.get_json()

if not payload:
return jsonify({"error": "Request body must be valid JSON."}), 400

skills = payload.get("skills", "").strip()
level = payload.get("level", "").strip()
interest = payload.get("interest", "").strip()
time_availability = payload.get("time", "").strip()

# Validate before running the recommendation engine
errors = validate_recommendation_inputs(skills, level, interest, time_availability)
if errors:
# Return only the first error to keep the UI message clean
return jsonify({"error": errors[0]}), 400

if interest_has_no_projects(interest):
try:
data = request.json or {}
skills = data.get("skills", "")
level = data.get("level", "")
interest = data.get("interest", "")
time = data.get("time", "")

prompt = f"""Generate 3 UNIQUE coding projects.
Return ONLY a raw JSON array like this:
[
{{
"id": 1,
"title": "Project name",
"description": "What it does",
"skills": ["skill1", "skill2"],
"level": "{level}",
"time": "{time}"
}}
]

Skills: {skills}
Level: {level}
Interest: {interest}
Time: {time}"""

response = groq_client.chat.completions.create(
model="llama-3.3-70b-versatile", # ye use karo
messages=[{"role": "user", "content": prompt}]
)
raw = response.choices[0].message.content.strip()
raw = raw.replace("```json", "").replace("```", "").strip()
projects = json.loads(raw)

return jsonify({"projects": projects, "source": "ai"})

except Exception as e:
results = get_recommendations(skills, level, interest, time)
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:
return jsonify({
"projects": [],
"message": (
"No projects matched your inputs. "
"Try different skills or broaden your interest area."
)
}), 200

return jsonify({"projects": results}), 200

"projects": results,
"source": "stored",
"error": str(e)
})


@main.route("/project/<int:project_id>")
def project_detail(project_id):
Expand Down
5 changes: 5 additions & 0 deletions static/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ if (clearFiltersBtn) {
link.href = "/project/" + project.id; //each project has a unique id

footer.appendChild(link);


// Add starter code block
var codeBlock = document.createElement("pre");
codeBlock.textContent = project.code || "";

// Assemble the card in order
card.appendChild(title);
Expand Down
Loading