Coverage for opportunities/routes_opportunities.py: 73%

116 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-05 14:02 +0000

1"""Routes for opportunities""" 

2 

3from html import escape 

4import uuid 

5from flask import ( 

6 flash, 

7 jsonify, 

8 redirect, 

9 render_template, 

10 request, 

11 send_file, 

12 session, 

13 url_for, 

14) 

15from core import handlers 

16from course_modules.models import Module 

17from courses.models import Course 

18from employers.models import Employers 

19from .models import Opportunity 

20 

21 

22def add_opportunities_routes(app): 

23 """Add routes for opportunities""" 

24 

25 @app.route("/opportunities/search", methods=["GET"]) 

26 @handlers.admin_or_employers_required 

27 def search_opportunities(): 

28 """ 

29 Unified route for searching opportunities by admins and employers. 

30 Determines user type from session. 

31 """ 

32 # Fetch user session details 

33 from app import DEADLINE_MANAGER 

34 

35 user_type = handlers.get_user_type() 

36 employer = session.get("employer", None) 

37 print(f"[DEBUG] User type: {user_type}") 

38 # Fetch opportunities based on user type 

39 if user_type == "admin": 

40 opportunities = Opportunity().get_opportunities_for_search(_id=None) 

41 else: 

42 opportunities = Opportunity().get_opportunities_for_search( 

43 _id=employer.get("_id", None) 

44 ) 

45 

46 # Prepare context for rendering 

47 context = { 

48 "opportunities": opportunities, 

49 "user_type": user_type, 

50 "courses": Course().get_courses(), 

51 "modules": Module().get_modules(), 

52 } 

53 

54 # Add employers map if admin 

55 if user_type == "admin": 

56 employers_map = { 

57 employer["_id"]: employer for employer in Employers().get_employers() 

58 } 

59 context["employers_map"] = employers_map 

60 context["page"] = "opportunities" 

61 

62 return render_template( 

63 "opportunities/search.html", 

64 **context, 

65 deadline_type=DEADLINE_MANAGER.get_deadline_type(), 

66 ) 

67 

68 @app.route( 

69 "/opportunities/employer_add_update_opportunity", methods=["GET", "POST"] 

70 ) 

71 @handlers.admin_or_employers_required 

72 def employer_add_update_opportunity(): 

73 # Check if the details deadline has passed and the employer is in the session 

74 from app import DEADLINE_MANAGER 

75 

76 if DEADLINE_MANAGER.is_past_details_deadline() and "employer" in session: 

77 return render_template( 

78 "employers/past_deadline.html", 

79 data=( 

80 "Adding/Updating details deadline has passed as of " 

81 f"{DEADLINE_MANAGER.get_details_deadline()}" 

82 ), 

83 referrer=request.referrer, 

84 employer=session["employer"], # Pass employer to the template 

85 user_type="employer", 

86 deadline_type=DEADLINE_MANAGER.get_deadline_type(), 

87 ) 

88 

89 if request.method == "POST": 

90 try: 

91 opportunity = { 

92 "_id": request.form.get("_id"), 

93 "title": request.form.get("title"), 

94 "description": request.form.get("description"), 

95 "url": request.form.get("url", ""), 

96 "employer_id": None, 

97 "location": request.form.get("location"), 

98 "modules_required": [ 

99 module.strip() 

100 for module in request.form.get("modules_required")[1:-1] 

101 .replace('"', "") 

102 .split(",") 

103 ], 

104 "courses_required": [ 

105 course.strip() 

106 for course in request.form.get("courses_required")[1:-1] 

107 .replace('"', "") 

108 .split(",") 

109 ], 

110 "spots_available": int(request.form.get("spots_available")), 

111 "duration": request.form.get("duration"), 

112 } 

113 

114 # Check if any required field is empty 

115 for key, value in opportunity.items(): 

116 if not value and key not in { 

117 "employer_id", 

118 "url", 

119 "modules_required", 

120 "courses_required", 

121 }: 

122 raise ValueError( 

123 f"Field {key} is required and cannot be empty." 

124 ) 

125 if opportunity["modules_required"] == [""]: 

126 opportunity["modules_required"] = [] 

127 if opportunity["courses_required"] == [""]: 

128 opportunity["courses_required"] = [] 

129 

130 if opportunity["spots_available"] < 1: 

131 raise ValueError("Spots available must be at least 1.") 

132 if opportunity["duration"] not in [ 

133 "1_day", 

134 "1_week", 

135 "1_month", 

136 "3_months", 

137 "6_months", 

138 "12_months", 

139 ]: 

140 raise ValueError("Invalid duration value.") 

141 except Exception as e: 

142 return jsonify({"error": str(e)}), 400 

143 

144 opportunity["title"] = escape(opportunity["title"]) 

145 opportunity["description"] = escape(opportunity["description"]) 

146 opportunity["url"] = escape(opportunity["url"]) 

147 opportunity["location"] = escape(opportunity["location"]) 

148 opportunity["duration"] = escape(opportunity["duration"]) 

149 opportunity["modules_required"] = [ 

150 escape(module) for module in opportunity["modules_required"] 

151 ] 

152 opportunity["courses_required"] = [ 

153 escape(course) for course in opportunity["courses_required"] 

154 ] 

155 

156 if handlers.is_admin(): 

157 opportunity["employer_id"] = request.form.get("company") 

158 return Opportunity().add_update_opportunity(opportunity, True) 

159 

160 original = Opportunity().get_opportunity_by_id(opportunity["_id"]) 

161 if original and original["employer_id"] != session["employer"]["_id"]: 

162 return jsonify({"error": "Unauthorized Access."}), 401 

163 opportunity["employer_id"] = session["employer"]["_id"] 

164 return Opportunity().add_update_opportunity(opportunity, False) 

165 

166 # Get the opportunity by ID if it exists 

167 opportunity_id = request.args.get("opportunity_id") 

168 if opportunity_id is not None: 

169 opportunity = Opportunity().get_opportunity_by_id(opportunity_id) 

170 else: 

171 opportunity = {"_id": uuid.uuid4().hex, "spots_available": 1} 

172 

173 # Include employer in the context 

174 employer = session.get("employer", None) 

175 user_type = "admin" if handlers.is_admin() else "employer" 

176 employers = Employers().get_employers() 

177 return render_template( 

178 "opportunities/employer_add_update_opportunity.html", 

179 opportunity=opportunity, 

180 courses=Course().get_courses(), 

181 modules=Module().get_modules(), 

182 user_type=user_type, 

183 employer=employer, # Add employer to the template context 

184 page="opportunities", 

185 employers=employers, 

186 deadline_type=DEADLINE_MANAGER.get_deadline_type(), 

187 ) 

188 

189 @app.route("/opportunities/employer_delete_opportunity", methods=["POST", "GET"]) 

190 @handlers.admin_or_employers_required 

191 def employer_delete_opportunity(): 

192 opportunity_id = request.args.get("opportunity_id") 

193 if not opportunity_id: 

194 flash("Opportunity ID is required", "error") 

195 return redirect(request.referrer) 

196 

197 Opportunity().delete_opportunity_by_id(opportunity_id) 

198 flash("Opportunity deleted successfully", "success") 

199 return redirect(url_for("search_opportunities")) 

200 

201 @app.route("/opportunities/upload", methods=["GET", "POST"]) 

202 @handlers.admin_or_employers_required 

203 def upload_opportunities(): 

204 from app import DEADLINE_MANAGER 

205 

206 user_type = "admin" if handlers.is_admin() else "employer" 

207 

208 if request.method == "POST": 

209 file = request.files["file"] 

210 if not file: 

211 return jsonify({"error": "No file provided"}), 400 

212 if not handlers.allowed_file(file.filename, ["xlsx", "xls"]): 

213 return jsonify({"error": "Invalid file type"}), 400 

214 

215 if user_type == "admin": 

216 return Opportunity().upload_opportunities(file, True) 

217 

218 return Opportunity().upload_opportunities(file, False) 

219 

220 return render_template( 

221 "opportunities/upload.html", 

222 user_type=user_type, 

223 page="opportunities", 

224 deadline_type=DEADLINE_MANAGER.get_deadline_type(), 

225 ) 

226 

227 @app.route("/opportunities/download_all", methods=["GET"]) 

228 @handlers.admin_or_employers_required 

229 def download_opportunities(): 

230 user_type = "admin" if handlers.is_admin() else "employer" 

231 if user_type == "admin": 

232 return Opportunity().download_opportunities(True) 

233 return Opportunity().download_opportunities(False) 

234 

235 @app.route("/opportunities/download_template", methods=["GET"]) 

236 @handlers.admin_or_employers_required 

237 def download_opportunities_template(): 

238 user_type = "admin" if handlers.is_admin() else "employer" 

239 

240 if user_type == "admin": 

241 return send_file( 

242 "data_model_upload_template/admin_opportunities_template.xlsx", 

243 as_attachment=True, 

244 ) 

245 return send_file( 

246 "data_model_upload_template/employer_opportunities_template.xlsx", 

247 as_attachment=True, 

248 ) 

249 

250 @app.route("/opportunities/delete_all", methods=["DELETE"]) 

251 @handlers.admin_or_employers_required 

252 def delete_all_opportunities(): 

253 user_type = "admin" if handlers.is_admin() else "employer" 

254 if user_type == "admin": 

255 return Opportunity().delete_all_opportunities(True) 

256 return Opportunity().delete_all_opportunities(False)