source: main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py @ 9726

Last change on this file since 9726 was 9698, checked in by Henrik Bettermann, 12 years ago

We need to customize if students are allowed to remove mandatory course tickets. Therefore I added a property attribute removable_by_student.

Allow manager to edit the mandatory attribute.

  • Property svn:keywords set to Id
File size: 7.4 KB
Line 
1## $Id: studylevel.py 9698 2012-11-20 09:08:13Z henrik $
2##
3## Copyright (C) 2011 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
23from zope.component.interfaces import IFactory
24from zope.component import createObject
25from zope.interface import implementedBy
26from waeup.kofa.students.interfaces import (
27    IStudentStudyLevel, IStudentNavigation, ICourseTicket)
28from waeup.kofa.utils.helpers import attrs_to_fields
29from waeup.kofa.students.vocabularies import StudyLevelSource
30
31def getGradeWeightFromScore(score):
32    if score is None:
33        return (None, None)
34    if score >= 70:
35        return ('A',5)
36    if score >= 60:
37        return ('B',4)
38    if score >= 50:
39        return ('C',3)
40    if score >= 45:
41        return ('D',2)
42    if score >= 40:
43        return ('E',1)
44    return ('F',0)
45
46class StudentStudyLevel(grok.Container):
47    """This is a container for course tickets.
48    """
49    grok.implements(IStudentStudyLevel, IStudentNavigation)
50    grok.provides(IStudentStudyLevel)
51
52    def __init__(self):
53        super(StudentStudyLevel, self).__init__()
54        self.level = None
55        return
56
57    @property
58    def student(self):
59        try:
60            return self.__parent__.__parent__
61        except AttributeError:
62            return None
63
64    @property
65    def certcode(self):
66        try:
67            return self.__parent__.certificate.code
68        except AttributeError:
69            return None
70
71    @property
72    def number_of_tickets(self):
73        return len(self)
74
75    @property
76    def total_credits(self):
77        total = 0
78        for ticket in self.values():
79            total += ticket.credits
80        return total
81
82    @property
83    def gpa(self):
84        gpa = 0
85        credits_counted = 0
86        for ticket in self.values():
87            if ticket.score:
88                credits_counted += ticket.credits
89                gpa += ticket.credits * ticket.weight
90        if credits_counted:
91            gpa = 100*gpa/credits_counted
92        return float(gpa)/100
93
94    @property
95    def is_current_level(self):
96        try:
97            return self.__parent__.current_level == self.level
98        except AttributeError:
99            return False
100
101    def writeLogMessage(self, view, message):
102        return self.__parent__.__parent__.writeLogMessage(view, message)
103
104    @property
105    def level_title(self):
106        studylevelsource = StudyLevelSource()
107        return studylevelsource.factory.getTitle(self.__parent__, self.level)
108
109    def addCourseTicket(self, ticket, course):
110        """Add a course ticket object.
111        """
112        if not ICourseTicket.providedBy(ticket):
113            raise TypeError(
114                'StudentStudyLeves contain only ICourseTicket instances')
115        ticket.code = course.code
116        ticket.title = course.title
117        ticket.fcode = course.__parent__.__parent__.__parent__.code
118        ticket.dcode = course.__parent__.__parent__.code
119        ticket.credits = course.credits
120        ticket.passmark = course.passmark
121        ticket.semester = course.semester
122        self[ticket.code] = ticket
123        return
124
125    def addCertCourseTickets(self, cert):
126        """Collect all certificate courses and create course
127        tickets automatically.
128        """
129        if cert is not None:
130            for key, val in cert.items():
131                if val.level != self.level:
132                    continue
133                ticket = createObject(u'waeup.CourseTicket')
134                ticket.automatic = True
135                ticket.mandatory = val.mandatory
136                ticket.carry_over = False
137                self.addCourseTicket(ticket, val.course)
138        return
139
140StudentStudyLevel = attrs_to_fields(
141    StudentStudyLevel, omit=['total_credits', 'gpa'])
142
143class StudentStudyLevelFactory(grok.GlobalUtility):
144    """A factory for student study levels.
145    """
146    grok.implements(IFactory)
147    grok.name(u'waeup.StudentStudyLevel')
148    title = u"Create a new student study level.",
149    description = u"This factory instantiates new student study level instances."
150
151    def __call__(self, *args, **kw):
152        return StudentStudyLevel()
153
154    def getInterfaces(self):
155        return implementedBy(StudentStudyLevel)
156
157class CourseTicket(grok.Model):
158    """This is a course ticket which allows the
159    student to attend the course. Lecturers will enter scores and more at
160    the end of the term.
161
162    A course ticket contains a copy of the original course and
163    certificate course data. If the courses and/or the referrin certificate
164    courses are removed, the corresponding tickets remain unchanged.
165    So we do not need any event
166    triggered actions on course tickets.
167    """
168    grok.implements(ICourseTicket, IStudentNavigation)
169    grok.provides(ICourseTicket)
170
171    def __init__(self):
172        super(CourseTicket, self).__init__()
173        self.code = None
174        return
175
176    @property
177    def student(self):
178        """Get the associated student object.
179        """
180        try:
181            return self.__parent__.__parent__.__parent__
182        except AttributeError:
183            return None
184
185    @property
186    def certcode(self):
187        try:
188            return self.__parent__.__parent__.certificate.code
189        except AttributeError:
190            return None
191
192    @property
193    def removable_by_student(self):
194        return not self.mandatory
195
196    def writeLogMessage(self, view, message):
197        return self.__parent__.__parent__.__parent__.writeLogMessage(view, message)
198
199    def getLevel(self):
200        """Returns the id of the level the ticket has been added to.
201        """
202        # XXX: shouldn't that be an attribute?
203        try:
204            return self.__parent__.level
205        except AttributeError:
206            return None
207
208    def getLevelSession(self):
209        """Returns the session of the level the ticket has been added to.
210        """
211        # XXX: shouldn't that be an attribute?
212        try:
213            return self.__parent__.level_session
214        except AttributeError:
215            return None
216
217    @property
218    def grade(self):
219        """Returns the grade calculated from score.
220        """
221        return getGradeWeightFromScore(self.score)[0]
222
223    @property
224    def weight(self):
225        """Returns the weight calculated from score.
226        """
227        return getGradeWeightFromScore(self.score)[1]
228
229CourseTicket = attrs_to_fields(CourseTicket)
230
231class CourseTicketFactory(grok.GlobalUtility):
232    """A factory for student study levels.
233    """
234    grok.implements(IFactory)
235    grok.name(u'waeup.CourseTicket')
236    title = u"Create a new course ticket.",
237    description = u"This factory instantiates new course ticket instances."
238
239    def __call__(self, *args, **kw):
240        return CourseTicket()
241
242    def getInterfaces(self):
243        return implementedBy(CourseTicket)
Note: See TracBrowser for help on using the repository browser.