source: main/waeup.aaue/trunk/src/waeup/aaue/students/studylevel.py @ 14195

Last change on this file since 14195 was 14161, checked in by Henrik Bettermann, 8 years ago

Remove deprecated final_remark method.

Rework remark method.

  • Property svn:keywords set to Id
File size: 8.0 KB
RevLine 
[8326]1## $Id: studylevel.py 14161 2016-09-05 08:42:20Z henrik $
2##
3## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Container which holds the data of a student study level
20and contains the course tickets.
21"""
22import grok
[13036]23import pytz
24from datetime import datetime
[8326]25from zope.component.interfaces import IFactory
[9502]26from zope.component import createObject
[8326]27from zope.interface import implementedBy
28from waeup.kofa.utils.helpers import attrs_to_fields
[13036]29from waeup.kofa.interfaces import CREATED
[8326]30from waeup.kofa.students.studylevel import (
31    StudentStudyLevel, CourseTicket,
32    CourseTicketFactory, StudentStudyLevelFactory)
[14075]33from waeup.kofa.students.interfaces import IStudentNavigation, ICourseTicket
[8444]34from waeup.aaue.students.interfaces import (
[8867]35    ICustomStudentStudyLevel, ICustomCourseTicket)
[8326]36
37
[14136]38def getGradeWeightFromScore(total, student):
39    if total is None:
[13834]40        return (None, None)
41    if total >= 70:
42        return ('A',5)
43    if total >= 60:
44        return ('B',4)
45    if total >= 50:
46        return ('C',3)
47    if total >= 45:
48        return ('D',2)
[14075]49    if total >= 40 and student.entry_session < 2013:
[13834]50        return ('E',1)
51    return ('F',0)
52
53
[8326]54class CustomStudentStudyLevel(StudentStudyLevel):
55    """This is a container for course tickets.
56    """
57    grok.implements(ICustomStudentStudyLevel, IStudentNavigation)
58    grok.provides(ICustomStudentStudyLevel)
59
[9914]60    @property
61    def total_credits_s1(self):
62        total = 0
63        for ticket in self.values():
64            if ticket.semester == 1:
65                total += ticket.credits
66        return total
67
68    @property
69    def total_credits_s2(self):
70        total = 0
71        for ticket in self.values():
72            if ticket.semester == 2:
73                total += ticket.credits
74        return total
75
[10443]76    @property
[13834]77    def gpa_params(self):
78        """Calculate gpa parameters for this level.
79        """
80        credits_weighted = 0.0
81        credits_counted = 0
82        level_gpa = 0.0
83        for ticket in self.values():
84            if None not in (ticket.score, ticket.ca):
85                credits_counted += ticket.credits
86                credits_weighted += ticket.credits * ticket.weight
87        if credits_counted:
88            level_gpa = round(credits_weighted/credits_counted, 3)
89        return level_gpa, credits_counted, credits_weighted
90
91    @property
[10480]92    def gpa_params_rectified(self):
93        return self.gpa_params
[10443]94
[13036]95    @property
96    def course_registration_allowed(self):
97        if self.student.is_fresh:
98            return True
99        try:
[14117]100            if self.student.is_postgrad:
101                deadline = grok.getSite()['configuration'][
102                        str(self.level_session)].coursereg_deadline_pg
103            elif self.student.current_mode.startswith('dp'):
104                deadline = grok.getSite()['configuration'][
105                        str(self.level_session)].coursereg_deadline_dp
106            elif self.student.current_mode.endswith('_pt'):
107                deadline = grok.getSite()['configuration'][
108                        str(self.level_session)].coursereg_deadline_pt
109            elif self.student.current_mode == 'found':
110                deadline = grok.getSite()['configuration'][
111                        str(self.level_session)].coursereg_deadline_found
112            else:
113                deadline = grok.getSite()['configuration'][
114                        str(self.level_session)].coursereg_deadline
[13071]115        except (TypeError, KeyError):
[13036]116            return True
[13070]117        if not deadline or deadline > datetime.now(pytz.utc):
118            return True
[13036]119        payment_made = False
120        if len(self.student['payments']):
121            for ticket in self.student['payments'].values():
122                if ticket.p_category == 'late_registration' and \
123                    ticket.p_session == self.level_session and \
124                    ticket.p_state == 'paid':
125                        payment_made = True
[13070]126        if payment_made:
127            return True
128        return False
[13036]129
[14082]130    # only AAUE
131    @property
132    def remark(self):
[14161]133        certificate = getattr(self.__parent__,'certificate',None)
134        end_level = getattr(certificate, 'end_level', None)
135        # final student remark
136        if end_level and self.student.current_level >= end_level:
137            failed_courses = self.passed_params[4]
138            if '_m' in failed_courses:
139                return 'FRNS'
140            if self.cumulative_params[0] < 1.5:
141                return 'Fail'
142            if self.cumulative_params[0] < 2.4:
143                return '3rd'
144            if self.cumulative_params[0] < 3.5:
145                return '2nd Lower'
146            if self.cumulative_params[0] < 4.5:
147                return '2nd Upper'
148            if self.cumulative_params[0] < 5.1:
149                return '1st'
150            return 'N/A'
151        # returning student remark
[14082]152        if self.cumulative_params[0] < 1.5:
153            return 'Probation'
154        if self.cumulative_params[0] < 5.1:
155            return 'Proceed'
156        return 'N/A'
157
[14075]158    def addCourseTicket(self, ticket, course):
159        """Add a course ticket object.
160        """
161        if not ICourseTicket.providedBy(ticket):
162            raise TypeError(
163                'StudentStudyLeves contain only ICourseTicket instances')
164        ticket.code = course.code
165        ticket.title = course.title
166        ticket.fcode = course.__parent__.__parent__.__parent__.code
167        ticket.dcode = course.__parent__.__parent__.code
168        ticket.credits = course.credits
169        if self.student.entry_session < 2013:
170            ticket.passmark = course.passmark - 5
171        else:
172            ticket.passmark = course.passmark
173        ticket.semester = course.semester
174        self[ticket.code] = ticket
175        return
176
[9692]177CustomStudentStudyLevel = attrs_to_fields(
[9914]178    CustomStudentStudyLevel, omit=[
[10480]179    'total_credits', 'total_credits_s1', 'total_credits_s2', 'gpa'])
[8326]180
181class CustomStudentStudyLevelFactory(StudentStudyLevelFactory):
182    """A factory for student study levels.
183    """
184
185    def __call__(self, *args, **kw):
186        return CustomStudentStudyLevel()
187
188    def getInterfaces(self):
189        return implementedBy(CustomStudentStudyLevel)
190
191class CustomCourseTicket(CourseTicket):
192    """This is a course ticket which allows the
193    student to attend the course. Lecturers will enter scores and more at
194    the end of the term.
195
196    A course ticket contains a copy of the original course and
197    course referrer data. If the courses and/or their referrers are removed, the
198    corresponding tickets remain unchanged. So we do not need any event
199    triggered actions on course tickets.
200    """
201    grok.implements(ICustomCourseTicket, IStudentNavigation)
202    grok.provides(ICustomCourseTicket)
203
[13834]204    @property
205    def grade(self):
206        """Returns the grade calculated from score.
207        """
[14136]208        return getGradeWeightFromScore(self.total_score, self.student)[0]
[13834]209
210    @property
211    def weight(self):
212        """Returns the weight calculated from score.
213        """
[14136]214        return getGradeWeightFromScore(self.total_score, self.student)[1]
[13834]215
[14136]216    @property
217    def total_score(self):
218        """Returns ca + score.
219        """
220        if not None in (self.score, self.ca):
221            return self.score + self.ca
222
[8326]223CustomCourseTicket = attrs_to_fields(CustomCourseTicket)
224
225class CustomCourseTicketFactory(CourseTicketFactory):
226    """A factory for student study levels.
227    """
228
229    def __call__(self, *args, **kw):
230        return CustomCourseTicket()
231
232    def getInterfaces(self):
233        return implementedBy(CustomCourseTicket)
Note: See TracBrowser for help on using the repository browser.