'Generate a CSV containing all students\' problem grades within a given
`course_id`.'

@classmethod
def generate(cls, _xmodule_instance_args, _entry_id, course_id, _task_input, action_name):

    start_time = time()
    start_date = datetime.now(UTC)
    status_interval = 100
    enrolled_students = CourseEnrollment.objects.users_enrolled_in(course_id, include_inactive=True)
    task_progress = TaskProgress(action_name, enrolled_students.count(), start_time)
    header_row = OrderedDict([('id', 'Student    ID'), ('email', 'Email'), ('username', 'Username')])
    graded_scorable_blocks = cls._graded_scorable_blocks_to_header(course_id)
    rows = [((list(header_row.values()) + ['Enrollment    Status', 'Grade']) + _flatten(graded_scorable_blocks.values()))]
    error_rows = [(list(header_row.values()) + ['error_msg'])]
    current_step = {'step': 'Calculating    Grades'}
    CourseEnrollment.bulk_fetch_enrollment_states(enrolled_students, course_id)
    course = get_course_by_id(course_id)
    for (student, course_grade, error) in CourseGradeFactory().iter(enrolled_students, course):
        student_fields = [getattr(student, field_name) for field_name in header_row]
        task_progress.attempted += 1
        if (not course_grade):
            err_msg = error.message
            if (not err_msg):
                err_msg = u'Unknown    error'
            error_rows.append((student_fields + [err_msg]))
            task_progress.failed += 1
            continue
        enrollment_status = _user_enrollment_status(student, course_id)
        earned_possible_values = []
        for block_location in graded_scorable_blocks:
            try:
                problem_score = course_grade.problem_scores[block_location]
            except KeyError:
                earned_possible_values.append([u'Not    Available', u'Not    Available'])
            else:
                if problem_score.first_attempted:
                    earned_possible_values.append([problem_score.earned, problem_score.possible])
                else:
                    earned_possible_values.append([u'Not    Attempted', problem_score.possible])
        rows.append(((student_fields + [enrollment_status, course_grade.percent]) + _flatten(earned_possible_values)))
        task_progress.succeeded += 1
        if ((task_progress.attempted % status_interval) == 0):
            task_progress.update_task_state(extra_meta=current_step)
    if (len(rows) > 1):
        upload_csv_to_report_store(rows, 'problem_grade_report', course_id, start_date)
    if (len(error_rows) > 1):
        upload_csv_to_report_store(error_rows, 'problem_grade_report_err', course_id, start_date)
    return task_progress.update_task_state(extra_meta={'step': 'Uploading    CSV'})
