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
« 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
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
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)
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)
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()
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)
69 filePaths = FileHandler(courseId=course.id).retrievePath(associatedAttachments)
71 terms = selectSurroundingTerms(g.current_term, 0)
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())
82 else:
83 abort(403)
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)
91 return redirect(url_for('serviceLearning.slcEditProposal', courseID = course.id))
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")
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))
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')
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')
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))
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 """
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'])
151 # We have data and need to update the course first
152 else:
153 course = updateCourse(request.form.copy())
155 course.status = CourseStatus.APPROVED
156 course.save()
157 flash("Course approved!", "success")
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 """
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())
177 course.status = CourseStatus.SUBMITTED
178 course.save()
179 flash("Course unapproved!", "success")
181 except Exception as e:
182 print(e)
183 flash("Course was not unapproved!", "danger")
185 return ""
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"
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 ""
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)
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')
241 return "", 500
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]
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)
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)
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 ""
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)
305 except Exception as e:
306 print(e)
307 return ""