Coverage for app/controllers/serviceLearning/routes.py: 27%

198 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-07-11 17:51 +0000

1from flask import request, render_template, g, url_for, abort, redirect, flash, session, send_from_directory, send_file, jsonify 

2from werkzeug.utils import safe_join 

3import os 

4from peewee import * 

5from typing import Dict, Any, List 

6 

7from app.models.user import User 

8from app.models.term import Term 

9from app.models.course import Course 

10from app.models.courseStatus import CourseStatus 

11from app.models.courseInstructor import CourseInstructor 

12from app.models.courseQuestion import CourseQuestion 

13from app.models.attachmentUpload import AttachmentUpload 

14from app.logic.utils import selectSurroundingTerms, getFilesFromRequest 

15from app.logic.fileHandler import FileHandler 

16from app.logic.serviceLearningCourses import getSLProposalInfoForUser, withdrawProposal, renewProposal, updateCourse, createCourse, approvedCourses 

17from app.logic.downloadFile import * 

18from app.logic.utils import getRedirectTarget, setRedirectTarget 

19from app.controllers.serviceLearning import serviceLearning_bp 

20 

21 

22@serviceLearning_bp.route('/serviceLearning/courseManagement', methods = ['GET']) 

23@serviceLearning_bp.route('/serviceLearning/courseManagement/<username>', methods = ['GET']) 

24def serviceCourseManagement(username=None): 

25 try: 

26 user = User.get(User.username==username) if username else g.current_user 

27 except DoesNotExist: 

28 abort(404) 

29 

30 isRequestingForSelf = g.current_user == user 

31 if g.current_user.isCeltsAdmin or (g.current_user.isFaculty and isRequestingForSelf): 

32 setRedirectTarget(request.full_path) 

33 courseDict = getSLProposalInfoForUser(user) 

34 termList = selectSurroundingTerms(g.current_term, prevTerms=0) 

35 return render_template('serviceLearning/slcManagement.html', 

36 user=user, 

37 courseDict=courseDict, 

38 termList=termList) 

39 else: 

40 abort(403) 

41 

42@serviceLearning_bp.route('/serviceLearning/viewProposal/<courseID>', methods=['GET']) 

43@serviceLearning_bp.route('/serviceLearning/editProposal/upload/<courseID>', methods=['GET']) 

44@serviceLearning_bp.route('/serviceLearning/editProposal/<courseID>', methods=['GET']) 

45def slcEditProposal(courseID): 

46 """ 

47 Route for editing proposals, it will fill the form with the data found in the database 

48 given a courseID. 

49 """ 

50 instructors = CourseInstructor.select().where(CourseInstructor.course==courseID) 

51 courseInstructors = [instructor.user for instructor in instructors] 

52 isCourseCreator = Course.select().where(Course.createdBy == g.current_user, Course.id==courseID).exists() 

53 

54 if g.current_user.isCeltsAdmin or g.current_user in courseInstructors or isCourseCreator: 

55 course = Course.get_by_id(courseID) 

56 courseStatus = CourseStatus.get_by_id(course.status) 

57 courseStatusInt = courseStatus.get_id() 

58 approved = 3 

59 # Condition to check the route you are comming from 

60 if courseStatusInt==approved and request.path == f"/serviceLearning/editProposal/{courseID}": 

61 return redirect(f"/serviceLearning/viewProposal/{courseID}") 

62 else: 

63 statusOfCourse = Course.select(Course.status) 

64 questionData = (CourseQuestion.select().where(CourseQuestion.course == course)) 

65 questionAnswers = [question.questionContent for question in questionData] 

66 courseInstructor = CourseInstructor.select().where(CourseInstructor.course == courseID) 

67 associatedAttachments = AttachmentUpload.select().where(AttachmentUpload.course == course.id) 

68 

69 filePaths = FileHandler(courseId=course.id).retrievePath(associatedAttachments) 

70 

71 terms = selectSurroundingTerms(g.current_term, 0) 

72 

73 return render_template('serviceLearning/slcNewProposal.html', 

74 course = course, 

75 questionanswers = questionAnswers, 

76 terms = terms, 

77 statusOfCourse = statusOfCourse, 

78 courseInstructor = courseInstructor, 

79 filePaths = filePaths, 

80 redirectTarget = getRedirectTarget()) 

81 

82 else: 

83 abort(403) 

84 

85 

86@serviceLearning_bp.route('/serviceLearning/createCourse', methods=['POST']) 

87def slcCreateCourse(): 

88 """will give a new course ID so that it can redirect to an edit page""" 

89 course = createCourse(g.current_user) 

90 

91 return redirect(url_for('serviceLearning.slcEditProposal', courseID = course.id)) 

92 

93 

94@serviceLearning_bp.route('/serviceLearning/exit', methods=['GET']) 

95def slcExitView(): 

96 if getRedirectTarget(): 

97 return redirect(getRedirectTarget(True)) 

98 else: 

99 return redirect("/serviceLearning/courseManagement") 

100 

101 

102@serviceLearning_bp.route('/serviceLearning/saveExit', methods=['POST']) 

103@serviceLearning_bp.route('/serviceLearning/saveProposal', methods=['POST']) 

104def slcSaveContinue(): 

105 """Will update the the course proposal and return an empty string since ajax request needs a response 

106 Also, it updates the course status as 'in progress'""" 

107 course = updateCourse(request.form.copy(), attachments=getFilesFromRequest(request)) 

108 

109 if not course: 

110 flash("Error saving changes", "danger") 

111 else: 

112 course.status = CourseStatus.IN_PROGRESS 

113 course.save() 

114 flash(f"Proposal has been saved.", "success") 

115 if request.path == "/serviceLearning/saveExit": 

116 if getRedirectTarget(): 

117 return redirect(getRedirectTarget(True)) 

118 return redirect("/serviceLearning/courseManagement") 

119 return redirect(f'/serviceLearning/editProposal/{request.form["courseID"]}?tab=2') 

120 

121@serviceLearning_bp.route('/serviceLearning/newProposal', methods=['GET', 'POST']) 

122def slcCreateOrEdit(): 

123 if request.method == "POST": 

124 course = updateCourse(request.form.copy()) 

125 if not course: 

126 flash("Error saving changes", "danger") 

127 else: 

128 if getRedirectTarget(False): 

129 return redirect('' + getRedirectTarget(True) + '') 

130 return redirect('/serviceLearning/courseManagement') 

131 

132 terms = Term.select().where(Term.year >= g.current_term.year) 

133 return render_template('serviceLearning/slcNewProposal.html', 

134 terms = terms, 

135 courseData = None, 

136 redirectTarget = getRedirectTarget(True)) 

137 

138@serviceLearning_bp.route('/serviceLearning/approveCourse', methods=['POST']) 

139def approveCourse(): 

140 """ 

141 This function updates and approves a Service-Learning Course when using the 

142 approve button. 

143 return: empty string because AJAX needs to receive something 

144 """ 

145 

146 try: 

147 # We are only approving, and not updating 

148 if len(request.form) == 1: 

149 course = Course.get_by_id(request.form['courseID']) 

150 

151 # We have data and need to update the course first 

152 else: 

153 course = updateCourse(request.form.copy()) 

154 

155 course.status = CourseStatus.APPROVED 

156 course.save() 

157 flash("Course approved!", "success") 

158 

159 except Exception as e: 

160 print(e) 

161 flash("Course not approved!", "danger") 

162 return "" 

163@serviceLearning_bp.route('/serviceLearning/unapproveCourse', methods=['POST']) 

164def unapproveCourse(): 

165 """ 

166 This function updates and unapproves a Service-Learning Course when using the 

167 unapprove button. 

168 return: empty string because AJAX needs to receive something 

169 """ 

170 

171 try: 

172 if len(request.form) == 1: 

173 course = Course.get_by_id(request.form['courseID']) 

174 else: 

175 course = updateCourse(request.form.copy()) 

176 

177 course.status = CourseStatus.SUBMITTED 

178 course.save() 

179 flash("Course unapproved!", "success") 

180 

181 except Exception as e: 

182 print(e) 

183 flash("Course was not unapproved!", "danger") 

184 

185 return "" 

186 

187@serviceLearning_bp.route('/updateInstructorPhone', methods=['POST']) 

188def updateInstructorPhone(): 

189 instructorData = request.get_json() 

190 (User.update(phoneNumber=instructorData[1]) 

191 .where(User.username == instructorData[0])).execute() 

192 return "success" 

193 

194@serviceLearning_bp.route('/serviceLearning/withdraw/<courseID>', methods = ['POST']) 

195def withdrawCourse(courseID): 

196 try: 

197 if g.current_user.isAdmin or g.current_user.isFaculty: 

198 withdrawProposal(courseID) 

199 flash("Course successfully withdrawn", 'success') 

200 else: 

201 flash("Unauthorized to perform this action", 'warning') 

202 except Exception as e: 

203 print(e) 

204 flash("Withdrawal Unsuccessful", 'warning') 

205 return "" 

206 

207 

208@serviceLearning_bp.route('/proposalReview/', methods = ['GET', 'POST']) 

209def reviewProposal() -> str: 

210 """ 

211 this function gets the submitted course id and returns the its data to the review proposal modal 

212 """ 

213 courseID: Dict[str, Any] = request.form 

214 course: Course = Course.get_by_id(courseID["course_id"]) 

215 instructorsData: List[CourseInstructor] = course.courseInstructors 

216 return render_template('/serviceLearning/reviewProposal.html', 

217 course=course, 

218 instructorsData=instructorsData) 

219 

220@serviceLearning_bp.route('/serviceLearning/renew/<courseID>/<termID>/', methods = ['POST']) 

221def renewCourse(courseID, termID): 

222 """ 

223 This function checks to see if the user is a CELTS admin or is 

224 an instructor of a course (faculty) and allows courses to be renewed. 

225 :return: empty string because AJAX needs to receive something 

226 """ 

227 instructors = CourseInstructor.select().where(CourseInstructor.course==courseID) 

228 courseInstructors = [instructor.user for instructor in instructors] 

229 isCourseCreator = Course.select().where(Course.createdBy == g.current_user, Course.id==courseID).exists() 

230 try: 

231 if g.current_user.isCeltsAdmin or g.current_user in courseInstructors or isCourseCreator: 

232 renewedProposal = renewProposal(courseID, termID) 

233 flash("Course successfully renewed", 'success') 

234 return str(renewedProposal.id) 

235 else: 

236 flash("Unauthorized to perform this action", 'warning') 

237 except Exception as e: 

238 print(e) 

239 flash("Renewal Unsuccessful", 'warning') 

240 

241 return "", 500 

242 

243@serviceLearning_bp.route('/serviceLearning/print/<courseID>', methods=['GET']) 

244def printCourse(courseID): 

245 """ 

246 This function will print a PDF of an SLC proposal. 

247 """ 

248 instructors = CourseInstructor.select().where(CourseInstructor.course==courseID) 

249 courseInstructors = [instructor.user for instructor in instructors] 

250 isCreator = Course.select().where(Course.createdBy == g.current_user, Course.id==courseID).exists() 

251 if g.current_user.isCeltsAdmin or g.current_user in courseInstructors or isCreator: 

252 try: 

253 course = Course.get_by_id(courseID) 

254 pdfCourse = Course.select().where(Course.id == courseID) 

255 pdfInstructor = CourseInstructor.select().where(CourseInstructor.course == courseID) 

256 pdfQuestions = (CourseQuestion.select().where(CourseQuestion.course == course)) 

257 questionanswers = [question.questionContent for question in pdfQuestions] 

258 

259 return render_template('serviceLearning/slcFormPrint.html', 

260 course = course, 

261 pdfCourse = pdfCourse, 

262 pdfInstructor = pdfInstructor, 

263 pdfQuestions = pdfQuestions, 

264 questionanswers=questionanswers 

265 ) 

266 except Exception as e: 

267 flash("An error was encountered when printing, please try again.", 'warning') 

268 print(e) 

269 return "", 500 

270 else: 

271 abort(403) 

272 

273@serviceLearning_bp.route("/uploadCourseFile", methods=['GET', "POST"]) 

274def uploadCourseFile(): 

275 try: 

276 attachment = getFilesFromRequest(request) 

277 courseID = request.form["courseID"] 

278 addFile = FileHandler(attachment, courseId=courseID) 

279 addFile.saveFiles() 

280 except: 

281 flash("No file selected.", "warning") 

282 return redirect('/serviceLearning/editProposal/upload/'+courseID) 

283 

284 

285@serviceLearning_bp.route("/deleteCourseFile", methods=["POST"]) 

286def deleteCourseFile(): 

287 fileData= request.form 

288 courseFile=FileHandler(courseId=fileData["databaseId"]) 

289 courseFile.deleteFile(fileData["fileId"]) 

290 return "" 

291 

292@serviceLearning_bp.route('/serviceLearning/downloadApprovedCourses/<termID>', methods = ['GET']) 

293def downloadApprovedCourses(termID): 

294 """ 

295 This function allows the download of csv file 

296 """ 

297 try: 

298 designator = "downloadApprovedCourses" 

299 csvInfo = approvedCourses(termID) 

300 fileFormat = {"headers":["Course Name", "Course Number", "Faculty", "Term", "Previously Approved Course?"]} 

301 filePath = safe_join(os.getcwd(), app.config['files']['base_path']) 

302 newFile = fileMaker(designator, csvInfo, "CSV", fileFormat) 

303 return send_from_directory(filePath, 'ApprovedCourses.csv', as_attachment=True) 

304 

305 except Exception as e: 

306 print(e) 

307 return ""