MuhammadHaris01 commited on
Commit
edbf3a7
Β·
verified Β·
1 Parent(s): 5dae903

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -143
app.py CHANGED
@@ -1,144 +1,144 @@
1
- """
2
- app.py β€” Flask API for FYP Dashboard
3
- Serves precomputed_data.json generated by analyze.py
4
-
5
- Endpoints:
6
- GET /api/summary β†’ summary stats (cards)
7
- GET /api/titles β†’ top job titles + salary by title
8
- GET /api/skills β†’ top skills, skills by title, optimal skills
9
- GET /api/trends β†’ salary trends + skill trends over time
10
- GET /api/location β†’ remote breakdown + top countries
11
- GET /api/all β†’ everything in one call (used by dashboard)
12
- """
13
-
14
- import json
15
- import os
16
- from flask import Flask, jsonify
17
- from flask_cors import CORS
18
-
19
- app = Flask(__name__)
20
-
21
- # Allow your Vercel frontend domain β€” update this after deploying
22
- CORS(app, origins=[
23
- "http://localhost:3000", # local dev
24
- "https://*.vercel.app", # any vercel preview
25
- ])
26
-
27
- # ─────────────────────────────────────────────
28
- # Load precomputed data once at startup
29
- # ─────────────────────────────────────────────
30
- DATA_PATH = os.path.join(os.path.dirname(__file__), "precomputed_data.json")
31
-
32
- def load_data():
33
- if not os.path.exists(DATA_PATH):
34
- raise FileNotFoundError(
35
- f"precomputed_data.json not found at {DATA_PATH}\n"
36
- "Run analyze.py first to generate it."
37
- )
38
- with open(DATA_PATH, "r") as f:
39
- return json.load(f)
40
-
41
- try:
42
- DATA = load_data()
43
- print(f"[OK] Loaded precomputed data")
44
- print(f" Generated at: {DATA['meta']['generated_at']}")
45
- print(f" Total rows: {DATA['meta']['total_rows_processed']:,}")
46
- except FileNotFoundError as e:
47
- print(f"[ERROR] {e}")
48
- DATA = None
49
-
50
-
51
- def data_required(fn):
52
- """Decorator β€” returns 503 if data not loaded."""
53
- from functools import wraps
54
- @wraps(fn)
55
- def wrapper(*args, **kwargs):
56
- if DATA is None:
57
- return jsonify({
58
- "error": "Data not ready. Run analyze.py first."
59
- }), 503
60
- return fn(*args, **kwargs)
61
- return wrapper
62
-
63
-
64
- # ─────────────────────────────────────────────
65
- # Routes
66
- # ─────────────────────────────────────────────
67
-
68
- @app.route("/", methods=["GET"])
69
- def index():
70
- if DATA is None:
71
- return jsonify({"status": "error", "message": "Run analyze.py first"}), 503
72
- return jsonify({
73
- "status": "ok",
74
- "generated_at": DATA["meta"]["generated_at"],
75
- "total_rows": DATA["meta"]["total_rows_processed"],
76
- "endpoints": [
77
- "/api/summary",
78
- "/api/titles",
79
- "/api/skills",
80
- "/api/trends",
81
- "/api/location",
82
- "/api/all",
83
- ]
84
- })
85
-
86
-
87
- @app.route("/api/summary", methods=["GET"])
88
- @data_required
89
- def get_summary():
90
- return jsonify(DATA["summary_stats"])
91
-
92
-
93
- @app.route("/api/titles", methods=["GET"])
94
- @data_required
95
- def get_titles():
96
- return jsonify({
97
- "top_titles": DATA["top_titles"],
98
- "salary_by_title": DATA["salary_by_title"],
99
- })
100
-
101
-
102
- @app.route("/api/skills", methods=["GET"])
103
- @data_required
104
- def get_skills():
105
- return jsonify({
106
- "top_skills": DATA["top_skills"],
107
- "skills_by_title": DATA["skills_by_title"],
108
- "optimal_skills": DATA["optimal_skills"],
109
- })
110
-
111
-
112
- @app.route("/api/trends", methods=["GET"])
113
- @data_required
114
- def get_trends():
115
- return jsonify({
116
- "salary_trends": DATA["salary_trends"],
117
- "skill_trends": DATA["skill_trends"],
118
- })
119
-
120
-
121
- @app.route("/api/location", methods=["GET"])
122
- @data_required
123
- def get_location():
124
- return jsonify({
125
- "remote_breakdown": DATA["remote_breakdown"],
126
- "top_countries": DATA["top_countries"],
127
- })
128
-
129
-
130
- @app.route("/api/all", methods=["GET"])
131
- @data_required
132
- def get_all():
133
- """Single endpoint β€” dashboard calls this once on load."""
134
- return jsonify(DATA)
135
-
136
-
137
- # ─────────────────────────────────────────────
138
- # Run
139
- # ─────────────────────────────────────────────
140
- if __name__ == "__main__":
141
- port = int(os.environ.get("PORT", 5000))
142
- debug = os.environ.get("FLASK_ENV") != "production"
143
- print(f"Starting Flask on port {port} (debug={debug})")
144
  app.run(host="0.0.0.0", port=port, debug=debug)
 
1
+ """
2
+ app.py β€” Flask API for FYP Dashboard
3
+ Serves precomputed_data.json generated by analyze.py
4
+
5
+ Endpoints:
6
+ GET /api/summary β†’ summary stats (cards)
7
+ GET /api/titles β†’ top job titles + salary by title
8
+ GET /api/skills β†’ top skills, skills by title, optimal skills
9
+ GET /api/trends β†’ salary trends + skill trends over time
10
+ GET /api/location β†’ remote breakdown + top countries
11
+ GET /api/all β†’ everything in one call (used by dashboard)
12
+ """
13
+
14
+ import json
15
+ import os
16
+ from flask import Flask, jsonify
17
+ from flask_cors import CORS
18
+
19
+ app = Flask(__name__)
20
+
21
+ # Allow your Vercel frontend domain β€” update this after deploying
22
+ CORS(app, origins=[
23
+ "http://localhost:3000", # local dev
24
+ "https://*.vercel.app", # any vercel preview
25
+ ])
26
+
27
+ # ─────────────────────────────────────────────
28
+ # Load precomputed data once at startup
29
+ # ─────────────────────────────────────────────
30
+ DATA_PATH = os.path.join(os.path.dirname(__file__), "precomputed_data.json")
31
+
32
+ def load_data():
33
+ if not os.path.exists(DATA_PATH):
34
+ raise FileNotFoundError(
35
+ f"precomputed_data.json not found at {DATA_PATH}\n"
36
+ "Run analyze.py first to generate it."
37
+ )
38
+ with open(DATA_PATH, "r") as f:
39
+ return json.load(f)
40
+
41
+ try:
42
+ DATA = load_data()
43
+ print(f"[OK] Loaded precomputed data")
44
+ print(f" Generated at: {DATA['meta']['generated_at']}")
45
+ print(f" Total rows: {DATA['meta']['total_rows_processed']:,}")
46
+ except FileNotFoundError as e:
47
+ print(f"[ERROR] {e}")
48
+ DATA = None
49
+
50
+
51
+ def data_required(fn):
52
+ """Decorator β€” returns 503 if data not loaded."""
53
+ from functools import wraps
54
+ @wraps(fn)
55
+ def wrapper(*args, **kwargs):
56
+ if DATA is None:
57
+ return jsonify({
58
+ "error": "Data not ready. Run analyze.py first."
59
+ }), 503
60
+ return fn(*args, **kwargs)
61
+ return wrapper
62
+
63
+
64
+ # ─────────────────────────────────────────────
65
+ # Routes
66
+ # ─────────────────────────────────────────────
67
+
68
+ @app.route("/", methods=["GET"])
69
+ def index():
70
+ if DATA is None:
71
+ return jsonify({"status": "error", "message": "Run analyze.py first"}), 503
72
+ return jsonify({
73
+ "status": "ok",
74
+ "generated_at": DATA["meta"]["generated_at"],
75
+ "total_rows": DATA["meta"]["total_rows_processed"],
76
+ "endpoints": [
77
+ "/api/summary",
78
+ "/api/titles",
79
+ "/api/skills",
80
+ "/api/trends",
81
+ "/api/location",
82
+ "/api/all",
83
+ ]
84
+ })
85
+
86
+
87
+ @app.route("/api/summary", methods=["GET"])
88
+ @data_required
89
+ def get_summary():
90
+ return jsonify(DATA["summary_stats"])
91
+
92
+
93
+ @app.route("/api/titles", methods=["GET"])
94
+ @data_required
95
+ def get_titles():
96
+ return jsonify({
97
+ "top_titles": DATA["top_titles"],
98
+ "salary_by_title": DATA["salary_by_title"],
99
+ })
100
+
101
+
102
+ @app.route("/api/skills", methods=["GET"])
103
+ @data_required
104
+ def get_skills():
105
+ return jsonify({
106
+ "top_skills": DATA["top_skills"],
107
+ "skills_by_title": DATA["skills_by_title"],
108
+ "optimal_skills": DATA["optimal_skills"],
109
+ })
110
+
111
+
112
+ @app.route("/api/trends", methods=["GET"])
113
+ @data_required
114
+ def get_trends():
115
+ return jsonify({
116
+ "salary_trends": DATA["salary_trends"],
117
+ "skill_trends": DATA["skill_trends"],
118
+ })
119
+
120
+
121
+ @app.route("/api/location", methods=["GET"])
122
+ @data_required
123
+ def get_location():
124
+ return jsonify({
125
+ "remote_breakdown": DATA["remote_breakdown"],
126
+ "top_countries": DATA["top_countries"],
127
+ })
128
+
129
+
130
+ @app.route("/api/all", methods=["GET"])
131
+ @data_required
132
+ def get_all():
133
+ """Single endpoint β€” dashboard calls this once on load."""
134
+ return jsonify(DATA)
135
+
136
+
137
+ # ─────────────────────────────────────────────
138
+ # Run
139
+ # ─────────────────────────────────────────────
140
+ if __name__ == "__main__":
141
+ port = int(os.environ.get("PORT", 7860))
142
+ debug = os.environ.get("FLASK_ENV") != "production"
143
+ print(f"Starting Flask on port {port} (debug={debug})")
144
  app.run(host="0.0.0.0", port=port, debug=debug)