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

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

Customize and use total_score.

  • Property svn:keywords set to Id
File size: 7.8 KB
RevLine 
[8326]1## $Id: studylevel.py 14136 2016-08-27 06:52:44Z 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):
133        if self.cumulative_params[0] < 1.5:
134            return 'Probation'
135        if self.cumulative_params[0] < 5.1:
136            return 'Proceed'
137        return 'N/A'
138
139    # only AAUE
140    @property
141    def final_remark(self):
142        if self.cumulative_params[0] < 1.5:
143            return 'Fail'
144        if self.cumulative_params[0] < 2.4:
145            return 'Third Class (Honours)'
146        if self.cumulative_params[0] < 3.5:
147            return 'Second Class (Honours) Lower Division'
148        if self.cumulative_params[0] < 4.5:
149            return 'Second Class (Honours) Upper Division'
150        if self.cumulative_params[0] < 5.1:
151            return 'First Class Honours'
152        return 'N/A'
153
[14075]154    def addCourseTicket(self, ticket, course):
155        """Add a course ticket object.
156        """
157        if not ICourseTicket.providedBy(ticket):
158            raise TypeError(
159                'StudentStudyLeves contain only ICourseTicket instances')
160        ticket.code = course.code
161        ticket.title = course.title
162        ticket.fcode = course.__parent__.__parent__.__parent__.code
163        ticket.dcode = course.__parent__.__parent__.code
164        ticket.credits = course.credits
165        if self.student.entry_session < 2013:
166            ticket.passmark = course.passmark - 5
167        else:
168            ticket.passmark = course.passmark
169        ticket.semester = course.semester
170        self[ticket.code] = ticket
171        return
172
[9692]173CustomStudentStudyLevel = attrs_to_fields(
[9914]174    CustomStudentStudyLevel, omit=[
[10480]175    'total_credits', 'total_credits_s1', 'total_credits_s2', 'gpa'])
[8326]176
177class CustomStudentStudyLevelFactory(StudentStudyLevelFactory):
178    """A factory for student study levels.
179    """
180
181    def __call__(self, *args, **kw):
182        return CustomStudentStudyLevel()
183
184    def getInterfaces(self):
185        return implementedBy(CustomStudentStudyLevel)
186
187class CustomCourseTicket(CourseTicket):
188    """This is a course ticket which allows the
189    student to attend the course. Lecturers will enter scores and more at
190    the end of the term.
191
192    A course ticket contains a copy of the original course and
193    course referrer data. If the courses and/or their referrers are removed, the
194    corresponding tickets remain unchanged. So we do not need any event
195    triggered actions on course tickets.
196    """
197    grok.implements(ICustomCourseTicket, IStudentNavigation)
198    grok.provides(ICustomCourseTicket)
199
[13834]200    @property
201    def grade(self):
202        """Returns the grade calculated from score.
203        """
[14136]204        return getGradeWeightFromScore(self.total_score, self.student)[0]
[13834]205
206    @property
207    def weight(self):
208        """Returns the weight calculated from score.
209        """
[14136]210        return getGradeWeightFromScore(self.total_score, self.student)[1]
[13834]211
[14136]212    @property
213    def total_score(self):
214        """Returns ca + score.
215        """
216        if not None in (self.score, self.ca):
217            return self.score + self.ca
218
[8326]219CustomCourseTicket = attrs_to_fields(CustomCourseTicket)
220
221class CustomCourseTicketFactory(CourseTicketFactory):
222    """A factory for student study levels.
223    """
224
225    def __call__(self, *args, **kw):
226        return CustomCourseTicket()
227
228    def getInterfaces(self):
229        return implementedBy(CustomCourseTicket)
Note: See TracBrowser for help on using the repository browser.