Example #1
0
def refresh_background(course_id):
    """
    Look up existing extensions and apply them to new quizzes.

    :param course_id: The Canvas ID of the Course.
    :type course_id: int
    :rtype: dict
    :returns: A dictionary containing two parts:

        - success `bool` False if there was an error, True otherwise.
        - message `str` A long description of success or failure.
    """
    job = get_current_job()

    update_job(job, 0, 'Starting...', 'started')

    with app.app_context():
        course, created = get_or_create(
            db.session,
            Course,
            canvas_id=course_id
        )

        try:
            course_name = get_course(course_id).get(
                'name',
                '<UNNAMED COURSE>'
            )
            course.course_name = course_name
            db.session.commit()
        except requests.exceptions.HTTPError:
            update_job(
                job,
                0,
                'Course not found.',
                'failed',
                error=True
            )
            logger.exception('Unable to find course #{}'.format(course_id))

            return job.meta

        # quiz stuff
        quizzes = missing_quizzes(course_id)

        num_quizzes = len(quizzes)

        if num_quizzes < 1:
            update_job(
                job,
                100,
                'Complete. No quizzes required updates.',
                'complete',
                error=False
            )

            return job.meta

        percent_user_map = defaultdict(list)

        inactive_list = []

        update_job(job, 0, 'Getting past extensions.', 'processing', False)
        for extension in course.extensions:
            # If extension is inactive, ignore.
            if not extension.active:
                inactive_list.append(extension.user.sortable_name)
                logger.debug('Extension #{} is inactive.'.format(
                    extension.id
                ))
                continue

            user_canvas_id = User.query.filter_by(
                id=extension.user_id
            ).first().canvas_id

            # Check if user is in course. If not, deactivate extension.
            try:
                canvas_user = get_user(course_id, user_canvas_id)

                # Skip user if not a student. Fixes an edge case where a
                # student that previously recieved an extension changes roles.
                enrolls = canvas_user.get('enrollments', [])
                type_list = [e['type'] for e in enrolls if e['enrollment_state'] == 'active']
                if not any(t == 'StudentEnrollment' for t in type_list):
                    logger.info((
                        "User #{} was found in course #{}, but is not an "
                        "active student. Deactivating extension #{}. Roles "
                        "found: {}"
                    ).format(
                        user_canvas_id,
                        course_id,
                        extension.id,
                        ", ".join(type_list) if len(enrolls) > 0 else None
                    ))
                    extension.active = False
                    db.session.commit()
                    inactive_list.append(extension.user.sortable_name)
                    continue

            except requests.exceptions.HTTPError:
                log_str = (
                    'User #{} not in course #{}. Deactivating extension #{}.'
                )
                logger.info(
                    log_str.format(user_canvas_id, course_id, extension.id)
                )
                extension.active = False
                db.session.commit()
                inactive_list.append(extension.user.sortable_name)
                continue

            percent_user_map[extension.percent].append(user_canvas_id)

        if len(percent_user_map) < 1:
            msg_str = 'No active extensions were found.<br>'

            if len(inactive_list) > 0:
                msg_str += ' Extensions for the following students are inactive:<br>{}'
                msg_str = msg_str.format("<br>".join(inactive_list))

            update_job(
                job,
                100,
                msg_str,
                'complete',
                error=False
            )
            return job.meta

        for index, quiz in enumerate(quizzes):
            quiz_id = quiz.get('id', None)
            quiz_title = quiz.get('title', '[UNTITLED QUIZ]')

            comp_perc = int(((float(index)) / float(num_quizzes)) * 100)
            refreshing_str = 'Refreshing quiz #{} - {} [{} of {}]'
            update_job(
                job,
                comp_perc,
                refreshing_str.format(
                    quiz_id,
                    quiz_title,
                    index + 1,
                    num_quizzes
                ),
                'processing',
                error=False
            )

            for percent, user_list in percent_user_map.iteritems():
                extension_response = extend_quiz(
                    course_id,
                    quiz,
                    percent,
                    user_list
                )

                if extension_response.get('success', False) is True:
                    # add/update quiz
                    quiz_obj, created = get_or_create(
                        db.session,
                        Quiz,
                        canvas_id=quiz_id,
                        course_id=course.id
                    )
                    quiz_obj.title = quiz_title

                    db.session.commit()
                else:
                    error_message = 'Some quizzes couldn\'t be updated. '
                    error_message += extension_response.get('message', '')
                    update_job(
                        job,
                        comp_perc,
                        error_message,
                        'failed',
                        error=True,
                    )
                    return job.meta

        msg = '{} quizzes have been updated.'.format(len(quizzes))
        update_job(job, 100, msg, 'complete', error=False)
        return job.meta
Example #2
0
def update_background(course_id, extension_dict):
    """
    Update time on selected students' quizzes to a specified percentage.

    :param course_id: The Canvas ID of the Course to update in
    :type course_id: int
    :param extension_dict: A dictionary that includes the percent of
        time and a list of canvas user ids.

        Example:
        {
            'percent': '300',
            'user_ids': [
                '0123456',
                '1234567',
                '9867543',
                '5555555'
            ]
        }
    :type extension_dict: dict
    """
    job = get_current_job()

    update_job(job, 0, 'Starting...', 'started')

    with app.app_context():
        if not extension_dict:
            update_job(
                job,
                0,
                'Invalid Request',
                'failed',
                error=True
            )
            logger.warning('Invalid Request: {}'.format(extension_dict))
            return job.meta

        try:
            course_json = get_course(course_id)
        except requests.exceptions.HTTPError:
            update_job(
                job,
                0,
                'Course not found.',
                'failed',
                error=True
            )
            logger.exception('Unable to find course #{}'.format(course_id))
            return job.meta

        course_name = course_json.get('name', '<UNNAMED COURSE>')

        user_ids = extension_dict.get('user_ids', [])
        percent = extension_dict.get('percent', None)

        if not percent:
            update_job(
                job,
                0,
                '`percent` field required.',
                'failed',
                error=True
            )
            logger.warning('Percent field not provided. Request: {}'.format(
                extension_dict
            ))
            return job.meta

        course, created = get_or_create(db.session, Course, canvas_id=course_id)
        course.course_name = course_name
        db.session.commit()

        for user_id in user_ids:
            try:
                canvas_user = get_user(course_id, user_id)

                sortable_name = canvas_user.get('sortable_name', '<MISSING NAME>')
                sis_id = canvas_user.get('sis_user_id')

            except requests.exceptions.HTTPError:
                # Unable to find user. Log and skip them.
                logger.warning(
                    "Unable to find user #{} in course #{}".format(
                        user_id,
                        course_id
                    )
                )
                continue

            user, created = get_or_create(db.session, User, canvas_id=user_id)

            user.sortable_name = sortable_name
            user.sis_id = sis_id

            db.session.commit()

            # create/update extension
            extension, created = get_or_create(
                db.session,
                Extension,
                course_id=course.id,
                user_id=user.id
            )
            extension.percent = percent

            db.session.commit()

        quizzes = get_quizzes(course_id)
        num_quizzes = len(quizzes)
        quiz_time_list = []
        unchanged_quiz_time_list = []

        if num_quizzes < 1:
            update_job(
                job,
                0,
                'Sorry, there are no quizzes for this course.',
                'failed',
                error=True
            )
            logger.warning(
                "No quizzes found for course {}. Unable to update.".format(
                    course_id
                )
            )
            return job.meta

        for index, quiz in enumerate(quizzes):
            quiz_id = quiz.get('id', None)
            quiz_title = quiz.get('title', "[UNTITLED QUIZ]")

            comp_perc = int(((float(index)) / float(num_quizzes)) * 100)
            updating_str = 'Updating quiz #{} - {} [{} of {}]'
            update_job(
                job,
                comp_perc,
                updating_str.format(quiz_id, quiz_title, index + 1, num_quizzes),
                'processing',
                error=False
            )

            extension_response = extend_quiz(course_id, quiz, percent, user_ids)

            if extension_response.get('success', False) is True:
                # add/update quiz
                quiz_obj, created = get_or_create(
                    db.session,
                    Quiz,
                    canvas_id=quiz_id,
                    course_id=course.id
                )
                quiz_obj.title = quiz_title

                db.session.commit()

                added_time = extension_response.get('added_time', None)
                if added_time is not None:
                    quiz_time_list.append({
                        "title": quiz_title,
                        "added_time": added_time
                    })
                else:
                    unchanged_quiz_time_list.append({"title": quiz_title})
            else:
                update_job(
                    job,
                    comp_perc,
                    extension_response.get(
                        'message',
                        'An unknown error occured.'
                    ),
                    'failed',
                    error=True
                )
                logger.error("Extension failed: {}".format(extension_response))
                return job.meta

        msg_str = (
            'Success! {} {} been updated for {} student(s) to have {}% time. '
            '{} {} no time limit and were left unchanged.'
        )

        message = msg_str.format(
            len(quiz_time_list),
            "quizzes have" if len(quiz_time_list) != 1 else "quiz has",
            len(user_ids),
            percent,
            len(unchanged_quiz_time_list),
            "quizzes have" if len(unchanged_quiz_time_list) != 1 else "quiz has"
        )

        update_job(job, 100, message, 'complete', error=False)
        job.meta['quiz_list'] = quiz_time_list
        job.meta['unchanged_list'] = unchanged_quiz_time_list
        job.save()

        return job.meta