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

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

Add two property attributes: remark and final_remark.

  • Property svn:keywords set to Id
File size: 6.9 KB
Line 
1## $Id: studylevel.py 14082 2016-08-16 11:14:45Z 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
23import pytz
24from datetime import datetime
25from zope.component.interfaces import IFactory
26from zope.component import createObject
27from zope.interface import implementedBy
28from waeup.kofa.utils.helpers import attrs_to_fields
29from waeup.kofa.interfaces import CREATED
30from waeup.kofa.students.studylevel import (
31    StudentStudyLevel, CourseTicket,
32    CourseTicketFactory, StudentStudyLevelFactory)
33from waeup.kofa.students.interfaces import IStudentNavigation, ICourseTicket
34from waeup.aaue.students.interfaces import (
35    ICustomStudentStudyLevel, ICustomCourseTicket)
36
37
38def getGradeWeightFromScore(score, ca, student):
39    if None in (score, ca):
40        return (None, None)
41    total = score + ca
42    if total >= 70:
43        return ('A',5)
44    if total >= 60:
45        return ('B',4)
46    if total >= 50:
47        return ('C',3)
48    if total >= 45:
49        return ('D',2)
50    if total >= 40 and student.entry_session < 2013:
51        return ('E',1)
52    return ('F',0)
53
54
55class CustomStudentStudyLevel(StudentStudyLevel):
56    """This is a container for course tickets.
57    """
58    grok.implements(ICustomStudentStudyLevel, IStudentNavigation)
59    grok.provides(ICustomStudentStudyLevel)
60
61    @property
62    def total_credits_s1(self):
63        total = 0
64        for ticket in self.values():
65            if ticket.semester == 1:
66                total += ticket.credits
67        return total
68
69    @property
70    def total_credits_s2(self):
71        total = 0
72        for ticket in self.values():
73            if ticket.semester == 2:
74                total += ticket.credits
75        return total
76
77    @property
78    def gpa_params(self):
79        """Calculate gpa parameters for this level.
80        """
81        credits_weighted = 0.0
82        credits_counted = 0
83        level_gpa = 0.0
84        for ticket in self.values():
85            if None not in (ticket.score, ticket.ca):
86                credits_counted += ticket.credits
87                credits_weighted += ticket.credits * ticket.weight
88        if credits_counted:
89            level_gpa = round(credits_weighted/credits_counted, 3)
90        return level_gpa, credits_counted, credits_weighted
91
92    @property
93    def gpa_params_rectified(self):
94        return self.gpa_params
95
96    @property
97    def course_registration_allowed(self):
98        if self.student.is_fresh:
99            return True
100        try:
101            deadline = grok.getSite()['configuration'][
102                    str(self.level_session)].coursereg_deadline
103        except (TypeError, KeyError):
104            return True
105        if not deadline or deadline > datetime.now(pytz.utc):
106            return True
107        payment_made = False
108        if len(self.student['payments']):
109            for ticket in self.student['payments'].values():
110                if ticket.p_category == 'late_registration' and \
111                    ticket.p_session == self.level_session and \
112                    ticket.p_state == 'paid':
113                        payment_made = True
114        if payment_made:
115            return True
116        return False
117
118    # only AAUE
119    @property
120    def remark(self):
121        if self.cumulative_params[0] < 1.5:
122            return 'Probation'
123        if self.cumulative_params[0] < 5.1:
124            return 'Proceed'
125        return 'N/A'
126
127    # only AAUE
128    @property
129    def final_remark(self):
130        if self.cumulative_params[0] < 1.5:
131            return 'Fail'
132        if self.cumulative_params[0] < 2.4:
133            return 'Third Class (Honours)'
134        if self.cumulative_params[0] < 3.5:
135            return 'Second Class (Honours) Lower Division'
136        if self.cumulative_params[0] < 4.5:
137            return 'Second Class (Honours) Upper Division'
138        if self.cumulative_params[0] < 5.1:
139            return 'First Class Honours'
140        return 'N/A'
141
142    def addCourseTicket(self, ticket, course):
143        """Add a course ticket object.
144        """
145        if not ICourseTicket.providedBy(ticket):
146            raise TypeError(
147                'StudentStudyLeves contain only ICourseTicket instances')
148        ticket.code = course.code
149        ticket.title = course.title
150        ticket.fcode = course.__parent__.__parent__.__parent__.code
151        ticket.dcode = course.__parent__.__parent__.code
152        ticket.credits = course.credits
153        if self.student.entry_session < 2013:
154            ticket.passmark = course.passmark - 5
155        else:
156            ticket.passmark = course.passmark
157        ticket.semester = course.semester
158        self[ticket.code] = ticket
159        return
160
161CustomStudentStudyLevel = attrs_to_fields(
162    CustomStudentStudyLevel, omit=[
163    'total_credits', 'total_credits_s1', 'total_credits_s2', 'gpa'])
164
165class CustomStudentStudyLevelFactory(StudentStudyLevelFactory):
166    """A factory for student study levels.
167    """
168
169    def __call__(self, *args, **kw):
170        return CustomStudentStudyLevel()
171
172    def getInterfaces(self):
173        return implementedBy(CustomStudentStudyLevel)
174
175class CustomCourseTicket(CourseTicket):
176    """This is a course ticket which allows the
177    student to attend the course. Lecturers will enter scores and more at
178    the end of the term.
179
180    A course ticket contains a copy of the original course and
181    course referrer data. If the courses and/or their referrers are removed, the
182    corresponding tickets remain unchanged. So we do not need any event
183    triggered actions on course tickets.
184    """
185    grok.implements(ICustomCourseTicket, IStudentNavigation)
186    grok.provides(ICustomCourseTicket)
187
188    @property
189    def grade(self):
190        """Returns the grade calculated from score.
191        """
192        return getGradeWeightFromScore(self.score, self.ca, self.student)[0]
193
194    @property
195    def weight(self):
196        """Returns the weight calculated from score.
197        """
198        return getGradeWeightFromScore(self.score, self.ca, self.student)[1]
199
200CustomCourseTicket = attrs_to_fields(CustomCourseTicket)
201
202class CustomCourseTicketFactory(CourseTicketFactory):
203    """A factory for student study levels.
204    """
205
206    def __call__(self, *args, **kw):
207        return CustomCourseTicket()
208
209    def getInterfaces(self):
210        return implementedBy(CustomCourseTicket)
Note: See TracBrowser for help on using the repository browser.