Coverage for courses/models.py: 88%
150 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-05 14:02 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-05 14:02 +0000
1"""
2Courses model."""
4from datetime import datetime, timedelta
5from html import escape
6import tempfile
7import uuid
8from flask import jsonify, send_file
9import pandas as pd
11from core import handlers
14# Cache to store courses and the last update time
15courses_cache = {"data": None, "last_updated": None}
18class Course:
19 """Course data model"""
21 def reset_cache(self):
22 """Resets the courses cache."""
23 from app import DATABASE_MANAGER
25 courses = DATABASE_MANAGER.get_all("courses")
26 courses_cache["data"] = courses
27 courses_cache["last_updated"] = datetime.now()
28 return courses
30 def add_course(self, course):
31 """Adds a course to the database."""
32 from app import DATABASE_MANAGER
34 if DATABASE_MANAGER.get_one_by_field(
35 "courses", "course_id", course["course_id"]
36 ):
37 return jsonify({"error": "Course already in database"}), 400
39 DATABASE_MANAGER.insert("courses", course)
41 if course:
42 # Update cache
43 self.reset_cache()
44 return jsonify(course), 200
46 return jsonify({"error": "Course not added"}), 400
48 def delete_course_by_uuid(self, id_val):
49 """Deletes a course from the database."""
50 from app import DATABASE_MANAGER
52 course = DATABASE_MANAGER.get_one_by_id("courses", id_val)
54 if not course:
55 return jsonify({"error": "Course not found"}), 404
57 students = DATABASE_MANAGER.get_all_by_field(
58 "students", "course", course["course_id"]
59 )
61 if students and len(students) > 0:
62 return jsonify({"error": "Course has students enrolled"}), 400
64 opportunities = DATABASE_MANAGER.get_all_by_in_list(
65 "opportunities", "courses_required", [course["course_id"]]
66 )
68 for opportunity in opportunities:
69 if (
70 "courses_required" in opportunity
71 and course["course_id"] in opportunity["courses_required"]
72 ):
73 opportunity["courses_required"].remove(course["course_id"])
74 DATABASE_MANAGER.update_one_by_id(
75 "opportunities", opportunity["_id"], opportunity
76 )
78 DATABASE_MANAGER.delete_by_id("courses", course["_id"])
79 # Update cache
80 self.reset_cache()
82 return jsonify(course), 200
84 def get_course_by_id(self, course_id):
85 """Retrieves a course by its ID."""
86 from app import DATABASE_MANAGER
88 course = DATABASE_MANAGER.get_one_by_field("courses", "course_id", course_id)
90 if course:
91 return course
93 return None
95 def get_course_by_uuid(self, uuid):
96 """Get course by uuid"""
97 from app import DATABASE_MANAGER
99 course = DATABASE_MANAGER.get_one_by_id("courses", uuid)
100 if course:
101 return course
103 return None
105 def get_course_name_by_id(self, course_id):
106 """Get course name by id"""
107 course = self.get_course_by_id(course_id)
108 if not course:
109 return None
110 return course["course_name"]
112 def get_courses(self):
113 """Retrieves all courses."""
114 from app import DATABASE_MANAGER
116 current_time = datetime.now()
117 one_week_ago = current_time - timedelta(weeks=1)
119 # Check if cache is valid
120 if (
121 courses_cache["data"]
122 and courses_cache["last_updated"]
123 and courses_cache["last_updated"] > one_week_ago
124 ):
125 return courses_cache["data"]
127 # Fetch courses from the database
128 courses = DATABASE_MANAGER.get_all("courses")
130 if courses:
131 # Update cache
132 self.reset_cache()
133 return courses
135 return []
137 def get_courses_map(self):
138 """Get courses map"""
139 courses = self.get_courses()
140 return {course["course_id"]: course for course in courses}
142 def update_course(self, uuid, updated_course):
143 """Update course"""
144 from app import DATABASE_MANAGER
146 original = DATABASE_MANAGER.get_one_by_id("courses", uuid)
147 if not original:
148 return jsonify({"error": "Course not found"}), 404
150 if (
151 "course_id" in updated_course
152 and updated_course["course_id"] != original["course_id"]
153 ):
154 match = DATABASE_MANAGER.get_one_by_field(
155 "courses", "course_id", updated_course["course_id"]
156 )
157 if match:
158 return jsonify({"error": "Course ID already exists"}), 400
159 DATABASE_MANAGER.update_one_by_id("courses", uuid, updated_course)
161 students = DATABASE_MANAGER.get_all("students")
162 for student in students:
163 if "course" in student and original["course_id"] == student["course"]:
164 student["course"] = updated_course["course_id"]
165 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
167 opportunities = DATABASE_MANAGER.get_all("opportunities")
168 for opportunity in opportunities:
169 if (
170 "courses_required" in opportunity
171 and original["course_id"] in opportunity["courses_required"]
172 ):
173 opportunity["courses_required"].remove(original["course_id"])
174 opportunity["courses_required"].append(updated_course["course_id"])
175 DATABASE_MANAGER.update_one_by_id(
176 "opportunities", opportunity["_id"], opportunity
177 )
178 self.reset_cache()
179 return jsonify({"message": "Course was updated"}), 200
181 def delete_all_courses(self):
182 """Deletes all courses from the database."""
183 from app import DATABASE_MANAGER
185 DATABASE_MANAGER.delete_all("courses")
186 courses_cache["data"] = []
187 courses_cache["last_updated"] = datetime.now()
189 DATABASE_MANAGER.delete_all("students")
191 opportunities = DATABASE_MANAGER.get_all("opportunities")
192 DATABASE_MANAGER.delete_all("opportunities")
193 updated_opportunities = []
194 for opp in opportunities:
195 if "courses_required" in opp:
196 opp["courses_required"] = []
197 updated_opportunities.append(opp)
199 if updated_opportunities:
200 DATABASE_MANAGER.insert_many("opportunities", updated_opportunities)
202 return jsonify({"message": "All courses deleted"}), 200
204 def download_all_courses(self):
205 """Download all courses"""
206 from app import DATABASE_MANAGER
208 courses = DATABASE_MANAGER.get_all("courses")
210 for course in courses:
211 course_data = course["course_name"].rsplit(", ", 1)
212 course["Course_name"] = course_data[0]
213 course["Qualification"] = course_data[1] if len(course_data) > 1 else ""
214 course["UCAS_code"] = course.pop("course_id")
215 course["Course_description"] = course.pop("course_description")
217 del course["_id"]
218 # Create a DataFrame from the courses
219 df = pd.DataFrame(courses)
221 with tempfile.NamedTemporaryFile(suffix=".xlsx") as temp_file:
222 file_path = temp_file.name
224 # Save the DataFrame to the temporary Excel file
225 df.to_excel(
226 file_path,
227 index=False,
228 columns=[
229 "UCAS_code",
230 "Course_name",
231 "Qualification",
232 "Course_description",
233 ],
234 )
236 # Send the file as an attachment
237 return send_file(
238 file_path, download_name="courses.xlsx", as_attachment=True
239 )
241 def upload_course_data(self, file):
242 """Add courses from an Excel file."""
243 from app import DATABASE_MANAGER
245 # Read the Excel file
246 try:
247 df = handlers.excel_verifier_and_reader(
248 file,
249 {"UCAS_code", "Course_name", "Qualification", "Course_description"},
250 )
251 except Exception as e:
252 return jsonify({"error": f"Failed to read file: {str(e)}"}), 400
254 # Convert the DataFrame to a list of dictionaries
255 courses = df.to_dict(orient="records")
257 clean_data = []
258 current_ids = set(
259 course["course_id"] for course in DATABASE_MANAGER.get_all("courses")
260 )
261 ids = set()
263 for i, course in enumerate(courses):
264 temp = {
265 "_id": uuid.uuid4().hex,
266 "course_id": escape(course.get("UCAS_code", "")),
267 "course_name": escape(
268 f"{course.get('Course_name', '')}, {course.get('Qualification', '')}"
269 ),
270 "course_description": escape(course.get("Course_description", "")),
271 }
272 if not temp["course_id"] or not temp["course_name"]:
273 return jsonify({"error": "Invalid data in row " + str(i + 1)}), 400
274 if temp["course_id"] in ids:
275 return (
276 jsonify({"error": "Duplicate course ID in row " + str(i + 1)}),
277 400,
278 )
279 if temp["course_id"] in current_ids:
280 return jsonify({"error": "Course ID already exists"}), 400
281 clean_data.append(temp)
282 ids.add(temp["course_id"])
284 DATABASE_MANAGER.insert_many("courses", clean_data)
286 # Update cache
287 courses = DATABASE_MANAGER.get_all("courses")
288 courses_cache["data"] = courses
289 courses_cache["last_updated"] = datetime.now()
291 return jsonify({"message": "Uploaded"}), 200