## $Id: studylevel.py 14161 2016-09-05 08:42:20Z henrik $
##
## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""
Container which holds the data of a student study level
and contains the course tickets.
"""
import grok
import pytz
from datetime import datetime
from zope.component.interfaces import IFactory
from zope.component import createObject
from zope.interface import implementedBy
from waeup.kofa.utils.helpers import attrs_to_fields
from waeup.kofa.interfaces import CREATED
from waeup.kofa.students.studylevel import (
    StudentStudyLevel, CourseTicket,
    CourseTicketFactory, StudentStudyLevelFactory)
from waeup.kofa.students.interfaces import IStudentNavigation, ICourseTicket
from waeup.aaue.students.interfaces import (
    ICustomStudentStudyLevel, ICustomCourseTicket)


def getGradeWeightFromScore(total, student):
    if total is None:
        return (None, None)
    if total >= 70:
        return ('A',5)
    if total >= 60:
        return ('B',4)
    if total >= 50:
        return ('C',3)
    if total >= 45:
        return ('D',2)
    if total >= 40 and student.entry_session < 2013:
        return ('E',1)
    return ('F',0)


class CustomStudentStudyLevel(StudentStudyLevel):
    """This is a container for course tickets.
    """
    grok.implements(ICustomStudentStudyLevel, IStudentNavigation)
    grok.provides(ICustomStudentStudyLevel)

    @property
    def total_credits_s1(self):
        total = 0
        for ticket in self.values():
            if ticket.semester == 1:
                total += ticket.credits
        return total

    @property
    def total_credits_s2(self):
        total = 0
        for ticket in self.values():
            if ticket.semester == 2:
                total += ticket.credits
        return total

    @property
    def gpa_params(self):
        """Calculate gpa parameters for this level.
        """
        credits_weighted = 0.0
        credits_counted = 0
        level_gpa = 0.0
        for ticket in self.values():
            if None not in (ticket.score, ticket.ca):
                credits_counted += ticket.credits
                credits_weighted += ticket.credits * ticket.weight
        if credits_counted:
            level_gpa = round(credits_weighted/credits_counted, 3)
        return level_gpa, credits_counted, credits_weighted

    @property
    def gpa_params_rectified(self):
        return self.gpa_params

    @property
    def course_registration_allowed(self):
        if self.student.is_fresh:
            return True
        try:
            if self.student.is_postgrad:
                deadline = grok.getSite()['configuration'][
                        str(self.level_session)].coursereg_deadline_pg
            elif self.student.current_mode.startswith('dp'):
                deadline = grok.getSite()['configuration'][
                        str(self.level_session)].coursereg_deadline_dp
            elif self.student.current_mode.endswith('_pt'):
                deadline = grok.getSite()['configuration'][
                        str(self.level_session)].coursereg_deadline_pt
            elif self.student.current_mode == 'found':
                deadline = grok.getSite()['configuration'][
                        str(self.level_session)].coursereg_deadline_found
            else:
                deadline = grok.getSite()['configuration'][
                        str(self.level_session)].coursereg_deadline
        except (TypeError, KeyError):
            return True
        if not deadline or deadline > datetime.now(pytz.utc):
            return True
        payment_made = False
        if len(self.student['payments']):
            for ticket in self.student['payments'].values():
                if ticket.p_category == 'late_registration' and \
                    ticket.p_session == self.level_session and \
                    ticket.p_state == 'paid':
                        payment_made = True
        if payment_made:
            return True
        return False

    # only AAUE
    @property
    def remark(self):
        certificate = getattr(self.__parent__,'certificate',None)
        end_level = getattr(certificate, 'end_level', None)
        # final student remark
        if end_level and self.student.current_level >= end_level:
            failed_courses = self.passed_params[4]
            if '_m' in failed_courses:
                return 'FRNS'
            if self.cumulative_params[0] < 1.5:
                return 'Fail'
            if self.cumulative_params[0] < 2.4:
                return '3rd'
            if self.cumulative_params[0] < 3.5:
                return '2nd Lower'
            if self.cumulative_params[0] < 4.5:
                return '2nd Upper'
            if self.cumulative_params[0] < 5.1:
                return '1st'
            return 'N/A'
        # returning student remark
        if self.cumulative_params[0] < 1.5:
            return 'Probation'
        if self.cumulative_params[0] < 5.1:
            return 'Proceed'
        return 'N/A'

    def addCourseTicket(self, ticket, course):
        """Add a course ticket object.
        """
        if not ICourseTicket.providedBy(ticket):
            raise TypeError(
                'StudentStudyLeves contain only ICourseTicket instances')
        ticket.code = course.code
        ticket.title = course.title
        ticket.fcode = course.__parent__.__parent__.__parent__.code
        ticket.dcode = course.__parent__.__parent__.code
        ticket.credits = course.credits
        if self.student.entry_session < 2013:
            ticket.passmark = course.passmark - 5
        else:
            ticket.passmark = course.passmark
        ticket.semester = course.semester
        self[ticket.code] = ticket
        return

CustomStudentStudyLevel = attrs_to_fields(
    CustomStudentStudyLevel, omit=[
    'total_credits', 'total_credits_s1', 'total_credits_s2', 'gpa'])

class CustomStudentStudyLevelFactory(StudentStudyLevelFactory):
    """A factory for student study levels.
    """

    def __call__(self, *args, **kw):
        return CustomStudentStudyLevel()

    def getInterfaces(self):
        return implementedBy(CustomStudentStudyLevel)

class CustomCourseTicket(CourseTicket):
    """This is a course ticket which allows the
    student to attend the course. Lecturers will enter scores and more at
    the end of the term.

    A course ticket contains a copy of the original course and
    course referrer data. If the courses and/or their referrers are removed, the
    corresponding tickets remain unchanged. So we do not need any event
    triggered actions on course tickets.
    """
    grok.implements(ICustomCourseTicket, IStudentNavigation)
    grok.provides(ICustomCourseTicket)

    @property
    def grade(self):
        """Returns the grade calculated from score.
        """
        return getGradeWeightFromScore(self.total_score, self.student)[0]

    @property
    def weight(self):
        """Returns the weight calculated from score.
        """
        return getGradeWeightFromScore(self.total_score, self.student)[1]

    @property
    def total_score(self):
        """Returns ca + score.
        """
        if not None in (self.score, self.ca):
            return self.score + self.ca

CustomCourseTicket = attrs_to_fields(CustomCourseTicket)

class CustomCourseTicketFactory(CourseTicketFactory):
    """A factory for student study levels.
    """

    def __call__(self, *args, **kw):
        return CustomCourseTicket()

    def getInterfaces(self):
        return implementedBy(CustomCourseTicket)
