Fatma12Atef commited on
Commit
a1b2ba8
Β·
verified Β·
1 Parent(s): 8698ec1

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +15 -0
  2. main.py +242 -0
  3. requirements.txt +8 -0
Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime
2
+ FROM python:3.10
3
+
4
+ # Set the working directory
5
+ WORKDIR /code
6
+
7
+ # Copy requirements and install them
8
+ COPY ./requirements.txt /code/requirements.txt
9
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
10
+
11
+ # Copy all your code and folders
12
+ COPY . .
13
+
14
+ # Run the FastAPI app on port 7860
15
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ import uuid
5
+ import json
6
+ import os
7
+ import uvicorn
8
+ import shutil
9
+ from pymongo import MongoClient
10
+ from dotenv import load_dotenv
11
+ from pathlib import Path
12
+
13
+ # Import your scripts
14
+ import generator
15
+ import judge
16
+
17
+ # ==========================================
18
+ # 1. SETUP ENV & DATABASE
19
+ # ==========================================
20
+
21
+ # Locate and load .env from backend folder
22
+ current_file = Path(__file__).resolve()
23
+ project_root = current_file.parents[2]
24
+ env_path = project_root / "backend" / ".env"
25
+
26
+ if env_path.exists():
27
+ load_dotenv(dotenv_path=env_path)
28
+ print("βœ… [PS Service] .env loaded successfully.")
29
+ else:
30
+ print(f"⚠️ [PS Service] Warning: .env not found at {env_path}")
31
+
32
+ MONGO_URI = os.getenv("MONGO_URI")
33
+
34
+ # Initialize MongoDB
35
+ problems_collection = None
36
+ if MONGO_URI:
37
+ try:
38
+ client = MongoClient(MONGO_URI)
39
+ db = client["acemock"]
40
+ problems_collection = db["coding_problems"]
41
+ print("βœ… [PS Service] Connected to MongoDB.")
42
+ except Exception as e:
43
+ print(f"❌ [PS Service] MongoDB Connection Error: {e}")
44
+ else:
45
+ print("❌ [PS Service] MONGO_URI not found. Database features will fail.")
46
+
47
+ app = FastAPI(title="AceMock Problem Solving")
48
+
49
+ # Enable CORS
50
+ app.add_middleware(
51
+ CORSMiddleware,
52
+ allow_origins=["http://localhost:3001", "http://127.0.0.1:3001"],
53
+ allow_credentials=True,
54
+ allow_methods=["*"],
55
+ allow_headers=["*"],
56
+ )
57
+
58
+ # Ensure temp directory exists for running code
59
+ os.makedirs("temp", exist_ok=True)
60
+
61
+ # --- HELPER: Normalize Language ---
62
+ def normalize_lang(lang: str):
63
+ lang = lang.lower()
64
+ if lang == "javascript": return "js"
65
+ if lang == "c++": return "cpp"
66
+ return lang
67
+
68
+ # --- DATA MODELS ---
69
+ class ProblemRequest(BaseModel):
70
+ level: str # "Fresh", "Junior", "Senior"
71
+ language: str # "cpp", "python", "javascript"
72
+
73
+ class ExecuteRequest(BaseModel):
74
+ language: str
75
+ code: str
76
+ input: str = "" # User's custom input
77
+
78
+ class SubmissionRequest(BaseModel):
79
+ problem_id: str
80
+ user_code: str
81
+ language: str
82
+
83
+ # --- ENDPOINT 1: GENERATE PROBLEM (MongoDB) ---
84
+ @app.post("/generate-problem")
85
+ def get_problem(req: ProblemRequest):
86
+ # βœ… FIX: Explicit None check for PyMongo 4+ compatibility
87
+ if problems_collection is None:
88
+ raise HTTPException(status_code=500, detail="Database connection not available.")
89
+
90
+ print(f"Generating {req.level} problem in {req.language}...")
91
+
92
+ problem_data = None
93
+ # Retry logic (AI can fail occasionally)
94
+ for _ in range(3):
95
+ try:
96
+ problem_data = generator.generate_problem_for_api(req.level, normalize_lang(req.language))
97
+ if problem_data: break
98
+ except Exception as e:
99
+ print(f"Generation attempt failed: {e}")
100
+
101
+ if not problem_data:
102
+ raise HTTPException(status_code=500, detail="AI generation failed. Please try again.")
103
+
104
+ # Generate a unique ID for this session
105
+ problem_id = str(uuid.uuid4())
106
+
107
+ # Add ID to data
108
+ problem_data["_id"] = problem_id
109
+ problem_data["level"] = req.level
110
+ problem_data["language"] = req.language
111
+ problem_data["created_at"] = str(uuid.uuid1()) # Timestamp roughly
112
+
113
+ # # βœ… SAVE TO LOCAL JSON FILE
114
+ # try:
115
+ # # Ensure the 'problems' directory exists
116
+ # problems_dir = os.path.join(os.path.dirname(__file__), "problems")
117
+ # os.makedirs(problems_dir, exist_ok=True)
118
+
119
+ # # Save the file using the problem_id as the name
120
+ # file_path = os.path.join(problems_dir, f"{problem_id}.json")
121
+ # with open(file_path, "w", encoding="utf-8") as f:
122
+ # json.dump(problem_data, f, indent=4, ensure_ascii=False)
123
+ # print(f"βœ… Problem saved to local JSON: {file_path}")
124
+ # except Exception as e:
125
+ # print(f"⚠️ Failed to save problem to JSON file: {e}")
126
+
127
+ # βœ… SAVE TO MONGODB
128
+ try:
129
+ problems_collection.insert_one(problem_data)
130
+ print(f"βœ… Problem saved to MongoDB with ID: {problem_id}")
131
+ except Exception as e:
132
+ print(f"❌ Failed to save to DB: {e}")
133
+ raise HTTPException(status_code=500, detail="Database save failed.")
134
+
135
+ # Return ONLY what the user needs to see (Hide test cases!)
136
+ return {
137
+ "problem_id": problem_id,
138
+ "title": problem_data['title'],
139
+ "description": problem_data['description'],
140
+ "input_format": problem_data['input_format'],
141
+ "output_format": problem_data['output_format'],
142
+ "samples": problem_data['samples'],
143
+ "solution_code": problem_data.get('solution_code')
144
+ }
145
+
146
+ # --- ENDPOINT 2: EXECUTE CODE (Run Button - No DB needed) ---
147
+ @app.post("/execute")
148
+ def execute_code(req: ExecuteRequest):
149
+ lang_key = normalize_lang(req.language)
150
+
151
+ # Create temp file
152
+ ext_map = {"cpp": ".cpp", "python": ".py", "js": ".js"}
153
+ filename = f"run_{uuid.uuid4()}{ext_map.get(lang_key, '.txt')}"
154
+ filepath = f"temp/{filename}"
155
+
156
+ with open(filepath, "w") as f:
157
+ f.write(req.code)
158
+
159
+ try:
160
+ # Run using the exact same Judge logic as submit
161
+ result = judge.run_test_case(filepath, lang_key, req.input)
162
+
163
+ # Cleanup
164
+ if os.path.exists(filepath): os.remove(filepath)
165
+
166
+ return result
167
+
168
+ except Exception as e:
169
+ if os.path.exists(filepath): os.remove(filepath)
170
+ return {"status": "Error", "output": str(e)}
171
+
172
+ # --- ENDPOINT 3: SUBMIT SOLUTION (Grading via MongoDB) ---
173
+ @app.post("/submit")
174
+ def submit_solution(req: SubmissionRequest):
175
+ if problems_collection is None:
176
+ raise HTTPException(status_code=500, detail="Database connection not available.")
177
+
178
+ # 1. Fetch Problem from MongoDB
179
+ problem_data = problems_collection.find_one({"_id": req.problem_id})
180
+
181
+ if not problem_data:
182
+ raise HTTPException(status_code=404, detail="Problem ID not found in database.")
183
+
184
+ lang_key = normalize_lang(req.language)
185
+ ext_map = {"cpp": ".cpp", "python": ".py", "js": ".js"}
186
+ filename = f"sub_{req.problem_id}{ext_map.get(lang_key, '.txt')}"
187
+ user_file_path = f"temp/{filename}"
188
+
189
+ with open(user_file_path, "w") as f:
190
+ f.write(req.user_code)
191
+
192
+ # 3. Run Test Cases (Retrieved from DB)
193
+ results = []
194
+ passed_count = 0
195
+ total_tests = len(problem_data['test_cases'])
196
+
197
+ for test in problem_data['test_cases']:
198
+ run_res = judge.run_test_case(
199
+ filepath=user_file_path,
200
+ lang=lang_key,
201
+ input_str=test['input']
202
+ )
203
+
204
+ if run_res['status'] == "Success":
205
+ if run_res['output'].strip() == test['expected_output'].strip():
206
+ results.append({"id": test['id'], "status": "Passed"})
207
+ passed_count += 1
208
+ else:
209
+ results.append({
210
+ "id": test['id'],
211
+ "status": "Wrong Answer",
212
+ "your_output": run_res['output'][:50],
213
+ "expected": test['expected_output'].strip()[:100]
214
+ })
215
+ else:
216
+ results.append({"id": test['id'], "status": run_res['status'], "details": run_res['output']})
217
+
218
+ # 4. Cleanup (Delete temp file)
219
+ if os.path.exists(user_file_path):
220
+ os.remove(user_file_path)
221
+
222
+ db_solutions = problem_data.get("solution_code", {})
223
+
224
+ if isinstance(db_solutions, dict):
225
+ # Fetch the requested language. Fallback to python if it's missing.
226
+ final_solution = db_solutions.get(lang_key, db_solutions.get("python", "# Solution missing"))
227
+ else:
228
+ # Fallback logic just in case an old problem (string format) is tested
229
+ final_solution = str(db_solutions)
230
+
231
+ # 5. Return Score AND The Match Solution
232
+ return {
233
+ "passed": passed_count,
234
+ "total": total_tests,
235
+ "score": round((passed_count / total_tests) * 100, 1),
236
+ "details": results,
237
+ "solution_code": final_solution
238
+ }
239
+
240
+ # --- RUN ON PORT 8003 ---
241
+ if __name__ == "__main__":
242
+ uvicorn.run(app, host="0.0.0.0", port=8003)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ pandas
4
+ python-dotenv
5
+ pydantic
6
+ pymongo
7
+ google-genai
8
+ python-multipart