def password_reset(token: str): """Resets the password (while not authenticated) Parameters ---------- token : str The reset token Returns ------- dict The view response """ if current_user.is_authenticated: return error( f"Wrong route, use {url_for('auth.change_password')}."), 303 user = User.verify_reset_token(token) if user is None: return error("That is an expired or incorrect link."), 410 user = User.from_dict(user) try: new_password = request.form["new_password"] user.password = new_password if not user.add(): return error("Unknown error while changing the password."), 500 except KeyError: return error("Not all fields satisfied"), 400 else: return response(["Password changed"]), 200
def activate_account(token: str): """Activates the account (while not authenticated) Parameters ---------- token : str The activation token Returns ------- dict The view response """ student = Student.verify_activation_token(token) if student is None: return error("That is an expired or incorrect link."), 400 else: if request.form["password_confirmation"] == request.form["password"]: if student.activate() and student.set_password( request.form["password"]): logger.info(f"Student {student._id} activated their account") return response(["Account activated!", "Password set!"]), 200 else: return error("Unknown error while activating account"), 400 else: return response(["Passwords don't match!"]), 400 db.students.update({"id": ObjectId(student._id)}, {"$set": { "activated": True }}) return response(["Account activated!"]), 200
def register_courses(): """Adds a course to the system. Returns ------- dict Flashes, course data from the form """ flashes = list() try: if Course.get_by_department_number(request.form["number"]): course = Course(request.form["department"], request.form["number"], request.form["name"]) else: return error("Course already exists"), 400 except KeyError: return error("Not all fields satisfied."), 400 if Admin.add_course(course=course): logger.info(f"Course {request.form['number']} added") flashes.append("Course added!") return response(flashes), 200 else: flashes.append("There was a problem adding your course") return response(flashes), 400
def submit(course_id: str, assignment_id: str): """Submit work for an assignment Parameters ---------- course_id : str The ID of the class for which the assignment was set assignment_id : str The ID of the assignment Returns ------- dict The view response """ assignment = db.courses.find_one( {"assignments._id": ObjectId(assignment_id)}, { "_id": 0, "assignments": { "$elemMatch": { "_id": ObjectId(assignment_id) } } }, )["assignments"][0] if assignment is not None and course_id in current_user.courses: try: file_list = [] files = request.files.getlist("files") if files[0].filename: for file_ in files: filename = file_.filename blob = upload_blob( uuid.uuid4().hex + "." + file_.content_type.split("/")[-1], file_, ) file_list.append((blob.name, filename)) submission = Submission( date_submitted=datetime.utcnow(), content=request.form["content"], filenames=file_list, student_id=current_user.id, assignment_id=assignment_id, ) current_user.add_submission(current_user.id, course_id, submission=submission) except KeyError: return error("Not all fields satisfied"), 400 else: logger.info(f"Submission {submission.id} made") return response(["Submission was a success"]), 200 else: return error("No assignment found"), 404
def edit_assignment(course_id: str, assignment_id: str): """Edits assignment for the class Parameters ------- course_id: str The course ID to look up in the database assignment_id: str The assignment ID to look up in the database Returns ------- dict Edited assignment data """ course = Course.get_by_id(course_id) assignments = course.get_assignments() assignment: Assignment = list( filter(lambda a: str(a.id) == assignment_id, assignments))[0] if assignment is None: return error("Could not find assignment"), 400 try: file_list = get_existing_assignment_files() edited_assignment = Assignment( date_assigned=assignment.date_assigned, assigned_by=assignment.assigned_by, assigned_to=request.form["assigned_to"], due_by=request.form["due_by"], title=request.form["title"], content=request.form["content"], filenames=file_list, estimated_time=request.form["estimated_time"], # weight=request.form['weight'] ) edited_assignment.id = assignment.id course.edit_assignment(edited_assignment) # Assign to 'assignment' so form has new details assignment = edited_assignment except KeyError: return error("Not all fields satisfied"), 400 # Set default values for form. request.form["assigned_to"].default = assignment.assigned_to request.form["due_by"].default = assignment.due_by request.form["estimated_time"].default = assignment.estimated_time request.form["title"].default = assignment.title request.form["content"].default = assignment.content # request.form['weight'].default = assignment.weight request.files.default = assignment.filenames return response(data={"assignment": assignment.to_json()})
def view_submissions_by_assignment(course_id: str, assignment_id: str): """Collects all submissions for a specific assignment of a class Parameters ------- course_id: str The course ID to look up in the database assignment_id: str The assignment ID to look up in the database Returns ------- dict Assignment submissions """ course = Course.get_by_id(course_id) assignments = course.get_assignments() assignment: Assignment = list( filter(lambda a: str(a.id) == assignment_id, assignments))[0] if assignment is None: return error("Could not find assignment"), 400 else: return response(data={"submissions": assignment.submissions})
def get_info_for_new_course(): """Gets department and teacher info for adding a new course to the database. Returns ------- dict Flashes, department and teacher data from the database """ flashes = list() try: departments = db.courses.find({}, {"department": 1, "_id": 0}) teachers = db.courses.find({}, { "name": 1, "email": 1, "department": 1, "_id": 0 }) except: return ( error( "Unknown error while getting info for departments and teachers" ), 400, ) return response(flashes, { "departments": departments, "teachers": teachers }), 200
def add_assignment(): """Adds new assignment for the class Returns ------- dict The view response """ request.form["assigned_to"].choices = current_user.get_class_names() try: file_list = get_existing_assignment_files() new_assignment = Assignment( date_assigned=datetime.utcnow(), assigned_by=current_user.ID, assigned_to=request.form["assigned_to"], due_by=request.form["due_by"], title=request.form["title"], content=request.form["content"], filenames=file_list, estimated_time=request.form["estimated_time"], # weight=request.form['weight'] ) Course.get_by_id( request.form["assigned_to"]).add_assignment(new_assignment) logger.info(f"Assignment {request.form['title']} added") return response(flashes=["Assignment sent!"]) except KeyError: return error("Not all fields satisfied"), 400
def student_search_info(): r"""This method is called when the user clicks on a result on the search bar Returns ------- dict Flashes, student data """ try: return response(None, Student.get_by_id(request.form["user_id"])), 200 except: return error("There was a problem finding this user"), 404
def request_password_reset(): """Request a password reset (while not authenticated) Returns ------- dict The view response """ if current_user.is_authenticated: return error( f"Wrong route, use {url_for('auth.change_password')}."), 303 try: email = request.form["email"].lower() send_reset_email(User.from_dict(User.get_by_email(email))) except KeyError: return error("Not all fields satisfied"), 400 else: return response(["An email has been sent to reset your password." ]), 200
def change_password(): """Changes the user password (while authenticated) Returns ------- dict The view response """ try: new_password = request.form["new_password"] current_user.password = new_password # TODO: I think this should definitely be update, not add, because add() can potentially add a new one if not current_user.add(): return error("Unknown error while changing the password."), 500 except KeyError: return error("Not all fields satisfied"), 400 else: return response(["Password changed"]), 200
def add_student_to_parent(): r"""Adds a student to a parent. Returns ------- dict Flashes """ if Admin.add_student_to_parent(request.form["parent_id"], request.form["student_id"]): return response(["Added student to parent"]), 200 else: return error("There was an error adding this student"), 400
def remove_student_from_parent(): r"""Removes a student from a parent. Returns ------- dict Flashes """ if Admin.remove_student_from_parent(request.form["parent_id"], request.form["student_id"]): return response(["Removed student from parent"]), 200 else: return error("There was an error removing this student"), 400
def mark_submission(course_id: str, assignment_id: str, submission_id: str): flashes = [] course = Course.get_by_id(course_id) assignments = course.get_assignments() assignment: Assignment = get(assignments, id=assignment_id) submission: Submission = get(assignment.submissions, id=submission_id) min_, max_ = course.grade_range if min_ < request.form["grade"] < max_: submission.update_grade(request.form["grade"]) flashes.append("Grade updated!") return response(flashes), 200 return error("Grade outside course grade boundary")
def get_course_info(course_id: str): """Gets all info for course. Returns ------- dict Flashes, all course info """ flashes = list() try: course_info = db.courses.find({"_id": ObjectId(course_id)}) except: return error("Unknown error while getting course info"), 400 return response(flashes, {"course_info": course_info}), 200
def get_names_by_search(): """Shows full names of people the user is searching Returns ------- dict Student names """ try: students = Student.get_by_keyword(request.form["first_name"]) possible_students = list() for student in students: student_data = { "full_name": student.first_name + " " + student.last_name, } possible_students.append(student_data) return response(data={"possible_students": possible_students}), 200 except: return error("There are no students by that name"), 404
def get_names_by_search(): """Shows full names of people the user is searching Returns ------- dict Teacher names """ try: teachers = Teacher.get_by_keyword(request.form["first_name"]) possible_teachers = list() for teacher in teachers: teacher_data = { "full_name": teacher.first_name + " " + teacher.last_name, } possible_teachers.append(teacher_data) return response(data={"possible_teachers": possible_teachers}), 200 except: return error("There are no teachers by that name"), 404
def get_names_by_search(): """Shows full names of people the user is searching Returns ------- dict Admin names """ try: admins = Admin.get_by_keyword(request.form["first_name"]) possible_admins = list() for admin in admins: admin_data = { "full_name": admin.first_name + " " + admin.last_name, } possible_admins.append(admin_data) return response(data={"possible_admins": possible_admins}), 200 except: return error("There are no admins by that name"), 404
def add_student(): """Adds a student account to the system and sends an activation email. Returns ------- dict Flashes, student data from the form """ flashes = list() try: if not Student.get_by_email(request.form["email"]): student = Student( request.form["email"], request.form["first_name"], request.form["last_name"], ) student.password(request.form["password"]) except KeyError: return error("Not all fields satisfied"), 400 if student.add(): flashes.append("Student added!") logger.info(f"Student {student.email} added") token = current_user.get_activation_token() app = current_app._get_current_object() msg = Message( app.config["MAIL_SUBJECT_PREFIX"] + " " + "Account Activation Link", sender=app.config["MAIL_SENDER"], recipients=[student.email], ) msg.body = f"""Here is your account activation link: { url_for('student.activate_account', token=token, _external=True) } If you did not register for this account, you can ignore this email. If you need any further assistance, please contact [email protected]. """ mail.send(msg) return response(flashes), 200 else: logger.info(f"Error adding Student {student.email}") flashes.append("There was a problem adding this account"), 400 return response(flashes), 400
def manage_courses_by_id(course_id: str): """Provides options to edit the course. Returns ------- dict Flashes, course data from the form """ flashes = list() course = Course.get_by_id(course_id) if course: if request.form.get("file"): syllabus_file = request.form["file"] filename = syllabus_file.filename blob = upload_blob( uuid.uuid4().hex + "." + syllabus_file.content_type.split("/")[-1], syllabus_file, ) syllabus = (blob.name, filename) course.update_syllabus(syllabus) logger.info(f"Course {course._id} updated") Course.update( request.form.get("department"), request.form.get("number"), request.form.get("name"), request.form.get("teacher"), request.form.get("teacher"), request.form.get("description"), request.form.get("schedule_time"), request.form.get("schedule_days"), request.form.get("syllabus"), request.form.get("student"), ) flashes.append("Course information successfully updated!") return response(flashes), 200 else: return error("Course does not exist"), 404
def enter_info(): """Enters description, date of birth and profile picture for student Returns ------- dict Flashes, student data from the form """ flashes = list() user = Student.get_by_id(current_user.id) if request.form.get("description"): user.description = request.form["description"] flashes.append("Description updated") if request.form.get("date_of_birth"): user.date_of_birth = request.form["date_of_birth"] flashes.append("Date of birth updated") try: profile_picture_file = request.files["profile_picture"] filename = profile_picture_file.filename blob = upload_blob( uuid.uuid4().hex + "." + profile_picture_file.content_type.split("/")[-1], profile_picture_file, ) profile_picture = (blob.name, filename) except KeyError: return error("Not all fields satisfied"), 400 user.profile_picture = profile_picture flashes.append("Profile picture updated") logger.info(f"User info {user.id} updated") return response(flashes), 200
def login(): """Login endpoint. Handles user logins with LoginForm Returns ------- dict The view response """ if current_user.is_authenticated: logger.info(f"The user {current_user.id} is already authenticated.") # TODO: this should definitely be a method in a class current_user_info = { "userName": current_user.first_name + " " + current_user.last_name, "userType": current_user._type, "loggedIn": True, "dob": "", } return response(user_info=current_user_info), 200 elif request.method == "GET": # If it's a GET (i.e., a user hasn't entered any info yet, the user is not logged in) logger.info("The user is not logged in.") return response(user_info={"loggedIn": False}), 200 try: req_data = request.get_json() email = req_data["email"].lower() password = req_data["password"] remember_me = req_data["remember_me"] for scope in [Student, Teacher, Admin, Parent]: logger.info( f"Trying to find {scope.__name__} with email {email}...") user = scope.get_by_email(email) if user is not None: logger.info(f"User: {user.first_name}") if user.validate_password(password): login_user(user, remember_me) logger.info( f"LOGGED IN: {user.first_name} {user.last_name} - ACCESS: {user._type}" ) current_user_info = { "userName": current_user.first_name + " " + current_user.last_name, "userType": current_user._type, "loggedIn": True, "dob": "", } return ( response( flashes=[ "Log in succesful! Redirecting to dashboard..." ], user_info=current_user_info, ), 200, ) logger.info( f"Failed to validate the password for the {scope.__name__} with email {email}" ) return error("Invalid password,"), 400 logger.info(f"Could not find {str(scope)} with email {email}") logger.info(f"Could not find any users with email {email}") return error("The user with this email does not exist."), 400 except (KeyError, TypeError): logger.info("Not all fields satisfied") return error("Not all fields satisfied"), 400
def manage_classes_by_id(course_id: str): """Updates a specified course's information Parameters ------- course_id: str The course ID to look up in the database Returns ------- dict Class data (id and name) Current class description """ course = Course.get_by_id(course_id) syllabus_name = course.get_syllabus_name() try: syllabus = [] if request.form and request.files: syllabus_file = request.files["syllabus_file"] syllabus_name = request.form.get("syllabus_name") description = request.form.get("description") if syllabus_file is not None: blob = upload_blob( uuid.uuid4().hex + "." + syllabus_file.content_type.split("/")[-1], syllabus_file, ) syllabus = [blob.name, syllabus_name] course.update_description(description) course.update_syllabus(syllabus) logger.info(f"Syllabus updated") return response( flashes=["Course information successfully updated!"]) else: print("Specify syllabus information") return error("Please specify the syllabus information"), 400 except KeyError: print("Not all fields satisfied") return error("Not all fields satisfied"), 400 courses = [] for course_id in current_user.courses: course_data = { "id": str(course_id), "name": Course.get_by_id(course_id).name, } courses.append(course_data) return response( flashes=["Course information successfully updated!"], data={ "courses": courses, "current_description": course.description }, )