Coverage for skills/models.py: 83%
194 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"""
2Skills model.
3"""
5from html import escape
6import tempfile
7import uuid
8from flask import jsonify, send_file
9import pandas as pd
11from core import handlers
14class Skill:
15 """
16 A class used to represent and manage skills in the database."""
18 def find_skill(self, skill_name="", skill_id=""):
19 """Check if a skill exists in the database."""
20 from app import DATABASE_MANAGER
22 if skill_name:
23 return DATABASE_MANAGER.get_one_by_field("skills", "skill_name", skill_name)
24 if skill_id:
25 return DATABASE_MANAGER.get_one_by_id("skills", skill_id)
27 return None
29 def add_skill(self, skill):
30 """Add Skill to database"""
31 # skill = {
32 # "_id": uuid.uuid1().hex,
33 # "skill_name": request.form.get("skill_name"),
34 # "skill_description": request.form.get("skill_description"),
35 # }
37 # Check if skill already exists#
38 from app import DATABASE_MANAGER
40 if self.find_skill(skill["skill_name"], None):
41 return jsonify({"error": "Skill already in database"}), 400
43 DATABASE_MANAGER.insert("skills", skill)
45 return jsonify(skill), 200
47 def delete_skill(self, skill_id):
48 """Delete kill from database"""
49 from app import DATABASE_MANAGER
51 result = DATABASE_MANAGER.delete_by_id("skills", skill_id)
53 if result.deleted_count == 0:
54 return jsonify({"error": "Skill not found"}), 404
56 students = DATABASE_MANAGER.get_all("students")
58 for student in students:
59 if skill_id in student.get("skills", []):
60 student["skills"].remove(skill_id)
61 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
63 return jsonify({"message": "Deleted"}), 200
65 def get_skill_by_id(self, skill_id):
66 """Get skill by ID tag"""
67 skill = self.find_skill(None, skill_id)
69 if skill:
70 return skill
72 return None
74 def get_skill_name_by_id(self, skill_id):
75 """Get skill name by id"""
76 skill = self.get_skill_by_id(skill_id)
77 if not skill:
78 return None
79 return skill["skill_name"]
81 def get_skills(self):
82 """Get full list of skills if cached get that instead"""
83 from app import DATABASE_MANAGER
85 # Fetch skills from the database
86 skills = DATABASE_MANAGER.get_all("skills")
88 return skills
90 def get_skills_map(self):
91 """Get skills map"""
92 skills = self.get_skills()
93 return {skill["_id"]: skill for skill in skills}
95 def attempt_add_skill(self, skill_name):
96 """Add skill to attempted skills"""
97 from app import DATABASE_MANAGER
99 found_skill = DATABASE_MANAGER.get_one_by_field(
100 "attempted_skills", "skill_name", skill_name
101 )
103 if found_skill:
104 DATABASE_MANAGER.increment(
105 "attempted_skills", found_skill["_id"], "used", 1
106 )
107 return jsonify(found_skill), 200
109 new_skill = {
110 "_id": uuid.uuid1().hex,
111 "skill_name": skill_name,
112 "skill_description": "",
113 "used": 1,
114 }
116 DATABASE_MANAGER.insert("attempted_skills", new_skill)
117 return jsonify(new_skill), 200
119 def get_list_attempted_skills(self):
120 """Get list of attempted skills"""
121 from app import DATABASE_MANAGER
123 attempted_skills = DATABASE_MANAGER.get_all("attempted_skills")
125 if not attempted_skills:
126 return []
128 return attempted_skills
130 def get_attempted_skill(self, skill_id):
131 """Get attempted skill"""
132 from app import DATABASE_MANAGER
134 skill = DATABASE_MANAGER.get_one_by_id("attempted_skills", skill_id)
136 if not skill:
137 return None
139 return skill
141 def approve_skill(self, skill_id, description):
142 """Approve skill"""
143 from app import DATABASE_MANAGER
145 skill = DATABASE_MANAGER.get_one_by_id("attempted_skills", skill_id)
147 if not skill:
148 return jsonify({"error": "Attempted skill not found"}), 404
150 del skill["used"]
151 if description == "":
152 return jsonify({"error": "Description is empty"}), 400
153 skill["skill_description"] = description
154 skill["skill_name"] = skill["skill_name"].capitalize()
155 DATABASE_MANAGER.insert("skills", skill)
156 DATABASE_MANAGER.delete_by_id("attempted_skills", skill_id)
158 # Update students
159 students = DATABASE_MANAGER.get_all("students")
160 for student in students:
161 if skill_id in student.get("attempted_skills", []):
162 student["skills"].append(skill_id)
163 student["attempted_skills"].remove(skill_id)
164 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
166 return jsonify({"message": "Approved"}), 200
168 def reject_skill(self, skill_id):
169 """Reject skill"""
170 from app import DATABASE_MANAGER
172 skill = DATABASE_MANAGER.get_one_by_id("attempted_skills", skill_id)
174 if not skill:
175 return jsonify({"error": "Attempted skill not found"}), 404
177 DATABASE_MANAGER.delete_by_id("attempted_skills", skill_id)
179 # Update students
180 students = DATABASE_MANAGER.get_all("students")
181 for student in students:
182 if skill_id in student.get("attempted_skills", []):
183 student["attempted_skills"].remove(skill_id)
184 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
186 return jsonify({"message": "Rejected"}), 200
188 def update_attempt_skill(self, skill_id, skill_name, skill_description):
189 """Update attempted skill"""
190 from app import DATABASE_MANAGER
192 skill = DATABASE_MANAGER.get_one_by_id("attempted_skills", skill_id)
194 if not skill:
195 return jsonify({"error": "Skill not found"}), 404
197 skill["skill_name"] = skill_name
198 skill["skill_description"] = skill_description
199 DATABASE_MANAGER.update_one_by_id("attempted_skills", skill_id, skill)
201 return jsonify({"message": "Updated"}), 200
203 def update_skill(self, skill_id, skill_name: str, skill_description):
204 """Updates a skill"""
206 from app import DATABASE_MANAGER
208 original = self.find_skill(None, skill_id)
209 if not original:
210 return jsonify({"error": "Skill not found"}), 404
212 skills = DATABASE_MANAGER.get_all_by_field(
213 "skills", "skill_name", skill_name.capitalize()
214 )
215 skills.extend(
216 DATABASE_MANAGER.get_all_by_field("skills", "skill_name", skill_name)
217 )
218 for skill in skills:
219 if skill["_id"] != original["_id"]:
220 return jsonify({"error": "Skill name already in use"}), 400
222 updated_skill = {
223 "_id": skill_id,
224 "skill_name": skill_name.capitalize(),
225 "skill_description": skill_description,
226 }
228 DATABASE_MANAGER.update_one_by_id("skills", skill_id, updated_skill)
230 return jsonify({"message": "Updated"}), 200
232 def download_all(self):
233 """Returns a xlsx file with all skills"""
234 from app import DATABASE_MANAGER
236 skills = DATABASE_MANAGER.get_all("skills")
237 clean_data = []
239 for skill in skills:
240 temp = {}
241 temp["Skill_Name"] = skill.pop("skill_name")
242 temp["Skill_Description"] = skill.pop("skill_description")
243 clean_data.append(temp)
245 df = pd.DataFrame(clean_data)
247 with tempfile.NamedTemporaryFile(suffix=".xlsx") as tmp:
248 df.to_excel(tmp.name, index=False)
249 tmp_file = tmp.name
251 return send_file(tmp_file, as_attachment=True, download_name="skills.xlsx")
253 def download_attempted(self):
254 """Returns a xlsx file with all attempted skills"""
255 from app import DATABASE_MANAGER
257 skills = DATABASE_MANAGER.get_all("attempted_skills")
258 clean_data = []
259 for skill in skills:
260 temp = {}
261 temp["Skill_Name"] = skill.pop("skill_name")
262 if "skill_description" in skill:
263 temp["Skill_Description"] = skill.pop("skill_description")
264 else:
265 temp["Skill_Description"] = ""
266 temp["Used"] = skill.pop("used")
267 clean_data.append(temp)
269 df = pd.DataFrame(clean_data)
270 with tempfile.NamedTemporaryFile(suffix=".xlsx") as tmp:
271 df.to_excel(tmp.name, index=False)
272 tmp_file = tmp.name
274 return send_file(
275 tmp_file, as_attachment=True, download_name="attempted_skills.xlsx"
276 )
278 def upload_skills(self, file):
279 """Upload skills"""
281 try:
282 df = handlers.excel_verifier_and_reader(
283 file, {"Skill_Name", "Skill_Description"}
284 )
285 except Exception as e:
286 return jsonify({"error": f"Failed to read file: {str(e)}"}), 400
288 skills = df.to_dict(orient="records")
290 from app import DATABASE_MANAGER
292 current_skills = set(
293 skill["skill_name"].lower() for skill in DATABASE_MANAGER.get_all("skills")
294 )
295 skill_names = set()
296 clean_data = []
297 try:
298 for skill in skills:
299 temp = {
300 "_id": uuid.uuid4().hex,
301 "skill_name": skill["Skill_Name"],
302 "skill_description": skill["Skill_Description"],
303 }
305 if (
306 temp["skill_name"].lower() in current_skills
307 or temp["skill_name"].lower() in skill_names
308 ):
309 continue
310 skill_names.add(temp["skill_name"].lower())
312 temp["skill_name"] = escape(temp["skill_name"])
313 temp["skill_description"] = escape(temp["skill_description"])
315 clean_data.append(temp)
316 except Exception:
317 return jsonify({"error": "Invalid file"}), 400
319 DATABASE_MANAGER.insert_many("skills", clean_data)
321 return jsonify({"message": "Uploaded"}), 200
323 def delete_all_skills(self):
324 """Delete all skills"""
325 from app import DATABASE_MANAGER
327 DATABASE_MANAGER.delete_all("skills")
329 students = DATABASE_MANAGER.get_all("students")
331 for student in students:
332 if "skills" in student:
333 student["skills"] = []
334 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
336 return jsonify({"message": "Deleted"}), 200
338 def delete_all_attempted_skill(self):
339 """Delete all attempted skills"""
340 from app import DATABASE_MANAGER
342 DATABASE_MANAGER.delete_all("attempted_skills")
344 students = DATABASE_MANAGER.get_all("students")
346 for student in students:
347 if "attempted_skills" in student:
348 student["attempted_skills"] = []
349 DATABASE_MANAGER.update_one_by_id("students", student["_id"], student)
351 return jsonify({"message": "Deleted"}), 200