"""
Functionality for course-level grades.
"""
from collections import namedtuple
from logging import getLogger

import dogstats_wrapper as dog_stats_api

from opaque_keys.edx.keys import CourseKey
from courseware.courses import get_course_by_id
from .new.course_grade import CourseGradeFactory


log = getLogger(__name__)


GradeResult = namedtuple('GradeResult', ['student', 'gradeset', 'err_msg'])


def iterate_grades_for(course_or_id, students):
    """
    Given a course_id and an iterable of students (User), yield a GradeResult
    for every student enrolled in the course.  GradeResult is a named tuple of:

    (student, gradeset, err_msg)

    If an error occurred, gradeset will be an empty dict and err_msg will be an
    exception message. If there was no error, err_msg is an empty string.

    The gradeset is a dictionary with the following fields:

    - grade : A final letter grade.
    - percent : The final percent for the class (rounded up).
        - section_breakdown : A breakdown of each section that makes
        up the grade. (For display)
    - grade_breakdown : A breakdown of the major components that
        make up the final grade. (For display)
    - raw_scores: contains scores for every graded module
    """
    if isinstance(course_or_id, (basestring, CourseKey)):
        course = get_course_by_id(course_or_id)
    else:
        course = course_or_id

    for student in students:
        with dog_stats_api.timer('lms.grades.iterate_grades_for', tags=[u'action:{}'.format(course.id)]):
            try:
                gradeset = summary(student, course)
                yield GradeResult(student, gradeset, "")
            except Exception as exc:  # pylint: disable=broad-except
                # Keep marching on even if this student couldn't be graded for
                # some reason, but log it for future reference.
                log.exception(
                    'Cannot grade student %s (%s) in course %s because of exception: %s',
                    student.username,
                    student.id,
                    course.id,
                    exc.message
                )
                yield GradeResult(student, {}, exc.message)


def summary(student, course):
    """
    Returns the grade summary of the student for the given course.

    Also sends a signal to update the minimum grade requirement status.
    """
    return CourseGradeFactory(student).create(course).summary
