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

198 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-07-24 12:19 +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 courseStatus = courseStatus, 

81 redirectTarget = getRedirectTarget()) 

82 

83 else: 

84 abort(403) 

85 

86 

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

88def slcCreateCourse(): 

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

90 course = createCourse(g.current_user) 

91 

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

93 

94 

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

96def slcExitView(): 

97 if getRedirectTarget(): 

98 return redirect(getRedirectTarget(True)) 

99 else: 

100 return redirect("/serviceLearning/courseManagement") 

101 

102 

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

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

105def slcSaveContinue(): 

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

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

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

109 

110 if not course: 

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

112 else: 

113 course.status = CourseStatus.IN_PROGRESS 

114 course.save() 

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

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

117 if getRedirectTarget(): 

118 return redirect(getRedirectTarget(True)) 

119 return redirect("/serviceLearning/courseManagement") 

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

121 

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

123def slcCreateOrEdit(): 

124 if request.method == "POST": 

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

126 if not course: 

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

128 else: 

129 if getRedirectTarget(False): 

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

131 return redirect('/serviceLearning/courseManagement') 

132 

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

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

135 terms = terms, 

136 courseData = None, 

137 redirectTarget = getRedirectTarget(True)) 

138 

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

140def approveCourse(): 

141 """ 

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

143 approve button. 

144 return: empty string because AJAX needs to receive something 

145 """ 

146 

147 try: 

148 # We are only approving, and not updating 

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

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

151 

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

153 else: 

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

155 

156 course.status = CourseStatus.APPROVED 

157 course.save() 

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

159 

160 except Exception as e: 

161 print(e) 

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

163 return "" 

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

165def unapproveCourse(): 

166 """ 

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

168 unapprove button. 

169 return: empty string because AJAX needs to receive something 

170 """ 

171 

172 try: 

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

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

175 else: 

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

177 

178 course.status = CourseStatus.SUBMITTED 

179 course.save() 

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

181 

182 except Exception as e: 

183 print(e) 

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

185 

186 return "" 

187 

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

189def updateInstructorPhone(): 

190 instructorData = request.get_json() 

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

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

193 return "success" 

194 

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

196def withdrawCourse(courseID): 

197 try: 

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

199 withdrawProposal(courseID) 

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

201 else: 

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

203 except Exception as e: 

204 print(e) 

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

206 return "" 

207 

208 

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

210def reviewProposal() -> str: 

211 """ 

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

213 """ 

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

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

216 instructorsData: List[CourseInstructor] = course.courseInstructors 

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

218 course=course, 

219 instructorsData=instructorsData) 

220 

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

222def renewCourse(courseID, termID): 

223 """ 

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

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

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

227 """ 

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

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

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

231 try: 

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

233 renewedProposal = renewProposal(courseID, termID) 

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

235 return str(renewedProposal.id) 

236 else: 

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

238 except Exception as e: 

239 print(e) 

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

241 

242 return "", 500 

243 

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

245def printCourse(courseID): 

246 """ 

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

248 """ 

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

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

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

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

253 try: 

254 course = Course.get_by_id(courseID) 

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

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

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

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

259 

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

261 course = course, 

262 pdfCourse = pdfCourse, 

263 pdfInstructor = pdfInstructor, 

264 pdfQuestions = pdfQuestions, 

265 questionanswers=questionanswers 

266 ) 

267 except Exception as e: 

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

269 print(e) 

270 return "", 500 

271 else: 

272 abort(403) 

273 

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

275def uploadCourseFile(): 

276 try: 

277 attachment = getFilesFromRequest(request) 

278 courseID = request.form["courseID"] 

279 addFile = FileHandler(attachment, courseId=courseID) 

280 addFile.saveFiles() 

281 except: 

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

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

284 

285 

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

287def deleteCourseFile(): 

288 fileData= request.form 

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

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

291 return "" 

292 

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

294def downloadApprovedCourses(termID): 

295 """ 

296 This function allows the download of csv file 

297 """ 

298 try: 

299 designator = "downloadApprovedCourses" 

300 csvInfo = approvedCourses(termID) 

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

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

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

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

305 

306 except Exception as e: 

307 print(e) 

308 return ""