jobmarket-api / app.py
MuhammadHaris01's picture
Update app.py
edbf3a7 verified
"""
app.py β€” Flask API for FYP Dashboard
Serves precomputed_data.json generated by analyze.py
Endpoints:
GET /api/summary β†’ summary stats (cards)
GET /api/titles β†’ top job titles + salary by title
GET /api/skills β†’ top skills, skills by title, optimal skills
GET /api/trends β†’ salary trends + skill trends over time
GET /api/location β†’ remote breakdown + top countries
GET /api/all β†’ everything in one call (used by dashboard)
"""
import json
import os
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
# Allow your Vercel frontend domain β€” update this after deploying
CORS(app, origins=[
"http://localhost:3000", # local dev
"https://*.vercel.app", # any vercel preview
])
# ─────────────────────────────────────────────
# Load precomputed data once at startup
# ─────────────────────────────────────────────
DATA_PATH = os.path.join(os.path.dirname(__file__), "precomputed_data.json")
def load_data():
if not os.path.exists(DATA_PATH):
raise FileNotFoundError(
f"precomputed_data.json not found at {DATA_PATH}\n"
"Run analyze.py first to generate it."
)
with open(DATA_PATH, "r") as f:
return json.load(f)
try:
DATA = load_data()
print(f"[OK] Loaded precomputed data")
print(f" Generated at: {DATA['meta']['generated_at']}")
print(f" Total rows: {DATA['meta']['total_rows_processed']:,}")
except FileNotFoundError as e:
print(f"[ERROR] {e}")
DATA = None
def data_required(fn):
"""Decorator β€” returns 503 if data not loaded."""
from functools import wraps
@wraps(fn)
def wrapper(*args, **kwargs):
if DATA is None:
return jsonify({
"error": "Data not ready. Run analyze.py first."
}), 503
return fn(*args, **kwargs)
return wrapper
# ─────────────────────────────────────────────
# Routes
# ─────────────────────────────────────────────
@app.route("/", methods=["GET"])
def index():
if DATA is None:
return jsonify({"status": "error", "message": "Run analyze.py first"}), 503
return jsonify({
"status": "ok",
"generated_at": DATA["meta"]["generated_at"],
"total_rows": DATA["meta"]["total_rows_processed"],
"endpoints": [
"/api/summary",
"/api/titles",
"/api/skills",
"/api/trends",
"/api/location",
"/api/all",
]
})
@app.route("/api/summary", methods=["GET"])
@data_required
def get_summary():
return jsonify(DATA["summary_stats"])
@app.route("/api/titles", methods=["GET"])
@data_required
def get_titles():
return jsonify({
"top_titles": DATA["top_titles"],
"salary_by_title": DATA["salary_by_title"],
})
@app.route("/api/skills", methods=["GET"])
@data_required
def get_skills():
return jsonify({
"top_skills": DATA["top_skills"],
"skills_by_title": DATA["skills_by_title"],
"optimal_skills": DATA["optimal_skills"],
})
@app.route("/api/trends", methods=["GET"])
@data_required
def get_trends():
return jsonify({
"salary_trends": DATA["salary_trends"],
"skill_trends": DATA["skill_trends"],
})
@app.route("/api/location", methods=["GET"])
@data_required
def get_location():
return jsonify({
"remote_breakdown": DATA["remote_breakdown"],
"top_countries": DATA["top_countries"],
})
@app.route("/api/all", methods=["GET"])
@data_required
def get_all():
"""Single endpoint β€” dashboard calls this once on load."""
return jsonify(DATA)
# ─────────────────────────────────────────────
# Run
# ─────────────────────────────────────────────
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
debug = os.environ.get("FLASK_ENV") != "production"
print(f"Starting Flask on port {port} (debug={debug})")
app.run(host="0.0.0.0", port=port, debug=debug)