Пример #1
0
def _delete_assignments(ids, delete_class):
    if delete_class:
        delete_class = ObjectId(delete_class)

    # Convert all of the IDs we were given to ObjectIDs in one go
    ids = [ObjectId(i) for i in ids]

    logger.info("Deleting assignments %s.", str(ids))

    # Query the database for all the assignments we are supposed to delete
    assignments = list(Assignment.objects(id__in = ids))

    # Query the database for all the submissions we are supposed to delete, this
    # will potentially be an absolutely massive list, so we do not want to
    # place all the results into a list immediately like we did for the
    # assignments.
    submissions = Submission.objects(assignment__in = ids)

    # Delete assignments directory on the filesystem
    for i in assignments:
        try:
            shutil.rmtree(
                os.path.join(config["SUBMISSION_DIRECTORY"], str(i.id))
            )
        except OSError as e:
            # We don't want to explode if the directory has been deleted for us
            # but it is weird so log a warning.
            if e.errno == errno.ENOENT:
                logger.warning("Assignment directory missing for %s",
                    str(i.id))
            else:
                raise

    # Actually delete the submissions from the database
    Submission.objects(assignment__in = ids).delete()

    # Delete the assignments
    Assignment.objects(id__in = ids).delete()

    if delete_class:
        # Unenroll all students in the class
        User.objects(classes = delete_class).update(
            pull__classes = delete_class
        )

        # Delete the class
        Class.objects(id = delete_class).delete()
Пример #2
0
def _delete_assignments(ids, delete_class):
    if delete_class:
        delete_class = ObjectId(delete_class)

    # Convert all of the IDs we were given to ObjectIDs in one go
    ids = [ObjectId(i) for i in ids]

    logger.info("Deleting assignments %s.", str(ids))

    # Query the database for all the assignments we are supposed to delete
    assignments = list(Assignment.objects(id__in = ids))

    # Query the database for all the submissions we are supposed to delete, this
    # will potentially be an absolutely massive list, so we do not want to
    # place all the results into a list immediately like we did for the
    # assignments.
    submissions = Submission.objects(assignment__in = ids)

    # Delete assignments directory on the filesystem
    for i in assignments:
        try:
            shutil.rmtree(
                os.path.join(config["SUBMISSION_DIRECTORY"], str(i.id))
            )
        except OSError as e:
            # We don't want to explode if the directory has been deleted for us
            # but it is weird so log a warning.
            if e.errno == errno.ENOENT:
                logger.warning("Assignment directory missing for %s",
                    str(i.id))
            else:
                raise

    # Actually delete the submissions from the database
    Submission.objects(assignment__in = ids).delete()

    # Delete the assignments
    Assignment.objects(id__in = ids).delete()

    if delete_class:
        # Unenroll all students in the class
        User.objects(classes = delete_class).update(
            pull__classes = delete_class
        )

        # Delete the class
        Class.objects(id = delete_class).delete()
Пример #3
0
def _delete_assignments(ids, delete_class):
    if delete_class:
        delete_class = ObjectId(delete_class)

    # Convert all of the IDs we were given to ObjectIDs in one go
    ids = [ObjectId(i) for i in ids]

    logger.debug("Deleting assignments %s.", ids)

    # Query the database for all the assignments we are supposed to delete
    assignments = list(Assignment.objects(id__in = ids))

    # Query the database for all the submissions we are supposed to delete, this
    # will potentially be an absolutely massive list, so we do not want to
    # place all the results into a list immediately like we did for the
    # assignments.
    submissions = Submission.objects(assignment__in = ids)

    # Go through all of the submissions and delete the files from the
    # filesystem. We will tell the database to delete all of the submissions
    # in one go afterwards.
    for i in submissions:
        # Delete the submission on the filesystem.
        try:
            shutil.rmtree(
                os.path.join(config["SUBMISSION_DIRECTORY"], str(i.id))
            )
        except OSError as e:
            logger.warn(
                "Failed to delete submission with id %s: %s", str(i.id), str(e)
            )

    # Actually delete the submissions from the database
    Submission.objects(assignment__in = ids).delete()

    # Delete the assignments
    Assignment.objects(id__in = ids).delete()

    if delete_class:
        # Unenroll all students in the class
        User.objects(classes = delete_class).update(
            pull__classes = delete_class
        )

        # Delete the class
        Class.objects(id = delete_class).delete()
Пример #4
0
def _delete_assignments(ids, delete_class):
    if delete_class:
        delete_class = ObjectId(delete_class)

    # Convert all of the IDs we were given to ObjectIDs in one go
    ids = [ObjectId(i) for i in ids]

    logger.debug("Deleting assignments %s.", ids)

    # Query the database for all the assignments we are supposed to delete
    assignments = list(Assignment.objects(id__in=ids))

    # Query the database for all the submissions we are supposed to delete, this
    # will potentially be an absolutely massive list, so we do not want to
    # place all the results into a list immediately like we did for the
    # assignments.
    submissions = Submission.objects(assignment__in=ids)

    # Go through all of the submissions and delete the files from the
    # filesystem. We will tell the database to delete all of the submissions
    # in one go afterwards.
    for i in submissions:
        # Delete the submission on the filesystem.
        try:
            shutil.rmtree(
                os.path.join(config["SUBMISSION_DIRECTORY"], str(i.id)))
        except OSError as e:
            logger.warn("Failed to delete submission with id %s: %s",
                        str(i.id), str(e))

    # Actually delete the submissions from the database
    Submission.objects(assignment__in=ids).delete()

    # Delete the assignments
    Assignment.objects(id__in=ids).delete()

    if delete_class:
        # Unenroll all students in the class
        User.objects(classes=delete_class).update(pull__classes=delete_class)

        # Delete the class
        Class.objects(id=delete_class).delete()
Пример #5
0
def _create_assignment_csv(csv_id, requester, assignment):
    csv_id = ObjectId(csv_id)

    csv_file = temp_directory = ""

    # Find any expired archives and remove them
    deleted_files = []
    for i in CSV.objects(expires__lt=datetime.datetime.today()):
        deleted_files.append(i.file_location)

        if i.file_location:
            try:
                os.remove(i.file_location)
            except OSError as e:
                logger.warning("Could not remove expired csv file at %s: %s.",
                               i.file_location, str(e))

        i.delete()

    if deleted_files:
        logger.info("Deleted csv files %s.", str(deleted_files))

    # This is the CSV object that will be added to the database
    new_csv = CSV(id=csv_id, requester=requester)

    temp_directory = csv_file = None
    try:
        assn = Assignment.objects.get(id=ObjectId(assignment))

        # Grab all student users for this class.
        users = list(
            User.objects(account_type="student", classes=assn.for_class))

        # Form the query
        query = {
            "assignment": ObjectId(assignment),
            "most_recent": True,
            "user__in": [i.id for i in users]
        }

        # Grab the most recent submissions from each user.
        submissions = list(Submission.objects(**query))

        # Create the actual csv file.
        csv_file = open(os.path.join(config["CSV_DIRECTORY"], str(csv_id)),
                        "w")

        for i in submissions:
            score = "None"
            if i.test_results:
                test_result = TestResult.objects.get(id=i.test_results)
                score = str(test_result.score)

            print >> csv_file, "%s,%s,%s" % \
                (i.user, score, i.timestamp.strftime("%Y-%m-%d-%H-%M-%S"))

        csv_file.close()

        new_csv.file_location = os.path.join(config["CSV_DIRECTORY"],
                                             str(csv_id))

        new_csv.expires = \
            datetime.datetime.today() + config["TEACHER_CSV_LIFETIME"]

        new_csv.save(force_insert=True)
    except Exception as e:
        new_csv.file_location = None
        os.remove(os.path.join(config["CSV_DIRECTORY"], str(csv_id)))

        new_csv.error_string = str(e)
        new_csv.save(force_insert=True)

        raise
Пример #6
0
def view_assignment(assignment_id):
    simple_archive_form = SimpleArchiveForm()

    # Convert the assignment in the URL into an ObjectId
    try:
        id = ObjectId(assignment_id)
    except InvalidId:
        logger.info("Invalid Assignment ID requested.")

        abort(404)

    # Retrieve the assignment
    try:
        assignment = Assignment.objects.get(id=id)
    except Assignment.DoesNotExist:
        logger.info("Non-extant ID requested.")

        abort(404)

    assignment.apply_personal_deadlines(current_user)

    # Get all of the submissions for this assignment
    submissions = list(
        Submission.objects(user=current_user.id,
                           assignment=id).order_by("-most_recent",
                                                   "-timestamp"))

    # Get student submissions if being viewed as teacher
    student_submissions = []
    students = []
    if current_user.account_type in ["teacher", "teaching_assistant"]:
        students = list(
            User.objects(classes=assignment.for_class,
                         account_type="student").order_by("-email"))

        student_submissions = list(
            Submission.objects(user__in=[i.email for i in students],
                               assignment=assignment_id,
                               most_recent=True))

    test_results = list(
        TestResult.objects(
            id__in=[i.test_results for i in submissions if i.test_results]))

    # Match test results to submissions
    for i in submissions:
        for j in test_results:
            if i.test_results == j.id:
                i.test_results_obj = j

    # Current time to be compared to submission test_request_timestamp
    now = datetime.datetime.now()

    # Flag to set refresh trigger if user is waiting on test results
    wait_and_refresh = False

    # Add the pretty version of each submissions timestamp
    for i in submissions:
        i.timestamp_pretty = pretty_time(i.timestamp)
        i.status = "Submitted"

        # If the user submitted a test request and there aren't any results
        if (i.test_request_timestamp and not i.test_results):
            timedelta = now - i.test_request_timestamp
            i.show_resubmit = (timedelta > config["STUDENT_RETRY_INTERVAL"])
            if not i.show_resubmit:
                i.status = "Waiting for test results..."
            else:
                i.status = "Test request timed out"
        elif (i.test_results and i.test_results_obj.failed):
            i.status = "Tests Failed"
            i.show_resubmit = True
        elif (i.test_results and not i.test_results_obj.failed):
            i.status = "Tests Completed"

    wait_and_refresh = \
        any(i.status == "Waiting for test results..." for i in submissions)

    return render_template(
        "assignment.html",
        now=datetime.datetime.today(),
        create_time_element=create_time_element,
        assignment=assignment,
        submissions=submissions,
        simple_archive_form=simple_archive_form,
        wait_and_refresh=wait_and_refresh,
        new_submissions=[
            v for k, v in get_flashed_messages(with_categories=True)
            if k == "new_submission"
        ],
        view_as_teacher=(current_user.account_type
                         in ["teacher", "teaching_assistant"]),
        students=students,
        student_submissions=student_submissions,
        enumerate=enumerate)
Пример #7
0
def _create_assignment_csv(csv_id, requester, assignment):
    csv_id = ObjectId(csv_id)

    csv_file = temp_directory = ""

    # Find any expired archives and remove them
    deleted_files = []
    for i in CSV.objects(expires__lt = datetime.datetime.today()):
        deleted_files.append(i.file_location)

        if i.file_location:
            try:
                os.remove(i.file_location)
            except OSError as e:
                logger.warning(
                    "Could not remove expired csv file at %s: %s.",
                    i.file_location, str(e)
                )

        i.delete()

    if deleted_files:
        logger.info("Deleted csv files %s.", str(deleted_files))

    # This is the CSV object that will be added to the database
    new_csv = CSV(
        id = csv_id,
        requester = requester
    )

    temp_directory = csv_file = None
    try:
        assn = Assignment.objects.get(id = ObjectId(assignment))

        # Grab all student users for this class.
        users = list(
            User.objects(
                account_type = "student",
                classes = assn.for_class
            )
        )

        # Form the query
        query = {
            "assignment": ObjectId(assignment),
            "most_recent": True,
            "user__in": [i.id for i in users]
        }

        # Grab the most recent submissions from each user.
        submissions = list(Submission.objects(**query))

        # Create the actual csv file.
        csv_file = open(os.path.join(config["CSV_DIRECTORY"], str(csv_id)), "w")

        for i in submissions:
            score = "None"
            if i.test_results:
                test_result = TestResult.objects.get(id = i.test_results)
                score = str(test_result.score)

            print >> csv_file, "%s,%s,%s" % \
                (i.user, score, i.timestamp.strftime("%Y-%m-%d-%H-%M-%S"))

        csv_file.close()

        new_csv.file_location = os.path.join(config["CSV_DIRECTORY"], str(csv_id))

        new_csv.expires = \
            datetime.datetime.today() + config["TEACHER_CSV_LIFETIME"]

        new_csv.save(force_insert = True)
    except Exception as e:
        new_csv.file_location = None
        os.remove(os.path.join(config["CSV_DIRECTORY"], str(csv_id)))

        new_csv.error_string = str(e)
        new_csv.save(force_insert = True)

        raise
Пример #8
0
def view_assignment(assignment_id):
    simple_archive_form = SimpleArchiveForm()

    # Convert the assignment in the URL into an ObjectId
    try:
        id = ObjectId(assignment_id)
    except InvalidId:
        logger.info("Invalid Assignment ID requested.")

        abort(404)

    # Retrieve the assignment
    try:
        assignment = Assignment.objects.get(id=id)
    except Assignment.DoesNotExist:
        logger.info("Non-extant ID requested.")

        abort(404)

    assignment.apply_personal_deadlines(current_user)

    # Get all of the submissions for this assignment
    submissions = list(Submission.objects(user=current_user.id, assignment=id).order_by("-most_recent", "-timestamp"))

    # Get student submissions if being viewed as teacher
    student_submissions = []
    students = []
    if current_user.account_type in ["teacher", "teaching_assistant"]:
        students = list(User.objects(classes=assignment.for_class, account_type="student").order_by("-email"))

        student_submissions = list(
            Submission.objects(user__in=[i.email for i in students], assignment=assignment_id, most_recent=True)
        )

    test_results = list(TestResult.objects(id__in=[i.test_results for i in submissions if i.test_results]))

    # Match test results to submissions
    for i in submissions:
        for j in test_results:
            if i.test_results == j.id:
                i.test_results_obj = j

    # Current time to be compared to submission test_request_timestamp
    now = datetime.datetime.now()

    # Flag to set refresh trigger if user is waiting on test results
    wait_and_refresh = False

    # Add the pretty version of each submissions timestamp
    for i in submissions:
        i.timestamp_pretty = pretty_time(i.timestamp)
        i.status = "Submitted"

        # If the user submitted a test request and there aren't any results
        if i.test_request_timestamp and not i.test_results:
            timedelta = now - i.test_request_timestamp
            i.show_resubmit = timedelta > config["STUDENT_RETRY_INTERVAL"]
            if not i.show_resubmit:
                i.status = "Waiting for test results..."
            else:
                i.status = "Test request timed out"
        elif i.test_results and i.test_results_obj.failed:
            i.status = "Tests Failed"
            i.show_resubmit = True
        elif i.test_results and not i.test_results_obj.failed:
            i.status = "Tests Completed"

    wait_and_refresh = any(i.status == "Waiting for test results..." for i in submissions)

    return render_template(
        "assignment.html",
        now=datetime.datetime.today(),
        create_time_element=create_time_element,
        assignment=assignment,
        submissions=submissions,
        simple_archive_form=simple_archive_form,
        wait_and_refresh=wait_and_refresh,
        new_submissions=[v for k, v in get_flashed_messages(with_categories=True) if k == "new_submission"],
        view_as_teacher=(current_user.account_type in ["teacher", "teaching_assistant"]),
        students=students,
        student_submissions=student_submissions,
        enumerate=enumerate,
    )
Пример #9
0
def _zip_bulk_submissions(archive_id, requester, assignment, email = ""):
    archive_id = ObjectId(archive_id)

    archive_file = temp_directory = ""

    # Find any expired archives and remove them
    deleted_files = []
    for i in Archive.objects(expires__lt = datetime.datetime.today()):
        deleted_files.append(i.file_location)

        if i.file_location:
            try:
                os.remove(i.file_location)
            except OSError as e:
                logger.warning(
                    "Could not remove expired archive at %s: %s.",
                    i.file_location, str(e)
                )

        i.delete()

    if deleted_files:
        logger.info("Deleted archives %s.", str(deleted_files))

    # This is the archive object we will eventually add to the database
    new_archive = Archive(
        id = archive_id,
        requester = requester,
        archive_type = "assignment_package"
    )

    temp_directory = archive_file = None
    try:
        # Form the query
        query = {"assignment": ObjectId(assignment)}

        # Only mention email in the query if it's not None or the empty
        # string, otherwise mongo will look for submissions that list the
        # user as None or the empty string (which should be exactly none of
        # the submission in the system).
        if email:
            query["user"] = email
        else:
            # Otherwise, we need to be careful not to get teacher/TA submissions.
            assn = Assignment.objects.get(id = ObjectId(assignment))
            students = User.objects(
                account_type="student",
                classes = assn.for_class
            )
            query["user__in"] = [i.id for i in students]

        # Grab all the submissions
        submissions = list(Submission.objects(**query))

        if not submissions:
            logger.info("No submissions found matching query.")
            return

        # Organize all the submissions by user name, as this will closely
        # match the structure of the archive we will build.
        submission_map = {}
        for i in submissions:
            if i.user in submission_map:
                submission_map[i.user].append(i)
            else:
                submission_map[i.user] = [i]

        # Create a temporary directory we will create our archive in.
        temp_directory = tempfile.mkdtemp()

        # Create our directory tree. Instead of making new folders for each
        # submission and copying the user's files over however, we will
        # create symlinks to save space and time.
        for user, user_submissions in submission_map.items():
            # Create a directory for the user
            os.makedirs(os.path.join(temp_directory, user))

            # Create symlinks for all his submissions. Each symlink is
            # named after the submission date.
            for i in user_submissions:
                time_stamp = i.timestamp.strftime("%Y-%m-%d-%H-%M-%S")
                symlink_path = \
                    os.path.join(temp_directory, user, time_stamp)

                # In the highly unlikely event that two of the same user's
                # submissions have the same exact time stamp, we'll need to
                # add a marker to the end of the timestamp.
                marker = 0
                while os.path.exists(symlink_path +
                        ("-%d" % marker if marker > 0 else "")):
                    marker += 1

                if marker > 0:
                    symlink_path += "-%d" % marker

                original_path = i.getFilePath()

                # Detect if the submission's files are still on the filesystem
                if os.path.isdir(original_path):
                    # Create a symlink pointing to the actual submission
                    # directory with the name we gnerated
                    os.symlink(original_path, symlink_path)
                else:
                    # Create an empty text file marking the fact that a
                    # submissions existed but is no longer available.
                    open(symlink_path, "w").close()

        # Create the actual archive file.
        # TODO: Create it in galah's /var/ directory
        file_descriptor, archive_file = tempfile.mkstemp(suffix = ".zip")
        os.close(file_descriptor)

        # Run zip and do the actual archiving. Will block until it's finished.
        zipdir(temp_directory, archive_file)

        new_archive.file_location = archive_file

        new_archive.expires = \
            datetime.datetime.today() + config["TEACHER_ARCHIVE_LIFETIME"]

        new_archive.save(force_insert = True)
    except Exception as e:
        # If we created a temporary archive file we need to delete it.
        new_archive.file_location = None
        if archive_file:
            os.remove(archive_file)

        new_archive.error_string = str(e)
        new_archive.save(force_insert = True)

        raise
    finally:
        if temp_directory:
            shutil.rmtree(temp_directory)
Пример #10
0
def _create_gradebook_csv(csv_id, requester, class_id, fill=0):
    csv_id = ObjectId(csv_id)

    csv_file = temp_directory = ""

    # Find any expired archives and remove them
    deleted_files = []
    for i in CSV.objects(expires__lt = datetime.datetime.today()):
        deleted_files.append(i.file_location)

        if i.file_location:
            try:
                os.remove(i.file_location)
            except OSError as e:
                logger.warning(
                    "Could not remove expired csv file at %s: %s.",
                    i.file_location, str(e)
                )

        i.delete()

    if deleted_files:
        logger.info("Deleted csv files %s.", str(deleted_files))

    # This is the CSV object that will be added to the database
    new_csv = CSV(
        id = csv_id,
        requester = requester
    )

    temp_directory = csv_file = None
    try:
        # Create the actual csv file.
        csv_file = open(os.path.join(config["CSV_DIRECTORY"], str(csv_id)), "w")

        the_class = Class.objects.get(id = ObjectId(class_id))

        # Grab all assignments in this class
        assns = list(
            Assignment.objects(for_class = the_class.id)
        )

        print >> csv_file, "%s,%s" % \
            ("Username", ",".join('"{0}"'.format(i.name) for i in assns))

        # Grab all student users for this class.
        users = list(
            User.objects(
                account_type = "student",
                classes = the_class.id
            )
        )

        assn_ids = [i.id for i in assns]
        for user in users:
            # Query for user's most recent submissions in the known assignments
            query = {
                "assignment__in": assn_ids,
                "most_recent": True,
                "user": user.id
            }

            submissions = list(Submission.objects(**query))

            # Initialize each assignment score to empty at first.
            assn_to_score = OrderedDict((i, str(fill)) for i in assn_ids)

            # Go through submissions, associating scores with assignment
            for sub in submissions:
                if sub.test_results:
                    test_result = TestResult.objects.get(id = sub.test_results)
                    if test_result.score is not None:
                        assn_to_score[sub.assignment] = str(test_result.score)

            # Write gradebook results to csv file.
            print >> csv_file, "%s,%s" % \
                (user.email, ",".join(assn_to_score.values()))

        csv_file.close()

        new_csv.file_location = os.path.join(config["CSV_DIRECTORY"], str(csv_id))

        new_csv.expires = \
            datetime.datetime.today() + config["TEACHER_CSV_LIFETIME"]

        new_csv.save(force_insert = True)
    except Exception as e:
        new_csv.file_location = None
        os.remove(os.path.join(config["CSV_DIRECTORY"], str(csv_id)))

        new_csv.error_string = str(e)
        new_csv.save(force_insert = True)

        raise