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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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})
Exemple #7
0
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
Exemple #9
0
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
Exemple #12
0
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
Exemple #13
0
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")
Exemple #15
0
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
Exemple #16
0
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
Exemple #18
0
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
Exemple #19
0
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
Exemple #20
0
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
Exemple #21
0
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
        },
    )