source: main/waeup.uniben/trunk/src/waeup/uniben/students/utils.py @ 13444

Last change on this file since 13444 was 13444, checked in by Henrik Bettermann, 9 years ago

Sort available bed. Beds are sorted by the sort id of the hostel and the bed number.
The first bed found in this sorted list is taken.

  • Property svn:keywords set to Id
File size: 17.2 KB
Line 
1## $Id: utils.py 13444 2015-11-12 06:30:43Z 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##
18import grok
19from time import time
20from zope.component import createObject, getUtility
21from waeup.kofa.interfaces import (IKofaUtils,
22    CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
23from waeup.kofa.utils.helpers import to_timezone
24from waeup.kofa.students.utils import trans
25from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
26from waeup.uniben.interfaces import MessageFactory as _
27
28class CustomStudentsUtils(NigeriaStudentsUtils):
29    """A collection of customized methods.
30
31    """
32
33    def getReturningData(self, student):
34        """ This method defines what happens after school fee payment
35        of returning students depending on the student's senate verdict.
36        """
37        prev_level = student['studycourse'].current_level
38        cur_verdict = student['studycourse'].current_verdict
39        if cur_verdict in ('A','B','L','M','N','Z',):
40            # Successful student
41            new_level = divmod(int(prev_level),100)[0]*100 + 100
42        elif cur_verdict == 'C':
43            # Student on probation
44            new_level = int(prev_level) + 10
45        else:
46            # Student is somehow in an undefined state.
47            # Level has to be set manually.
48            new_level = prev_level
49        new_session = student['studycourse'].current_session + 1
50        return new_session, new_level
51
52
53    def checkAccommodationRequirements(self, student, acc_details):
54        if acc_details.get('expired', False):
55            startdate = acc_details.get('startdate')
56            enddate = acc_details.get('enddate')
57            if startdate and enddate:
58                tz = getUtility(IKofaUtils).tzinfo
59                startdate = to_timezone(
60                    startdate, tz).strftime("%d/%m/%Y %H:%M:%S")
61                enddate = to_timezone(
62                    enddate, tz).strftime("%d/%m/%Y %H:%M:%S")
63                return _("Outside booking period: ${a} - ${b}",
64                         mapping = {'a': startdate, 'b': enddate})
65            else:
66                return _("Outside booking period.")
67        bt = acc_details.get('bt')
68        if not bt:
69            return _("Your data are incomplete.")
70        if not student.state in acc_details['allowed_states']:
71            return _("You are in the wrong registration state.")
72        if student['studycourse'].current_session != acc_details[
73            'booking_session']:
74            return _('Your current session does not '
75                     'match accommodation session.')
76        stage = bt.split('_')[2]
77        if stage not in ('fr', 'fi'):
78            return _("Only fresh and final year students are allowed to book accommodation.")
79
80        ####################################################################################
81        if stage == 'fi':
82            return _("Accommodation booking for final year students has not yet started.")
83        ####################################################################################
84
85
86        if stage != 'fr' and not student['studycourse'].previous_verdict in ('A', 'B'):
87            return _("Your are not eligible to book accommodation.")
88        if str(acc_details['booking_session']) in student['accommodation'].keys():
89            return _('You already booked a bed space in '
90                     'current accommodation session.')
91        return
92
93    def getAccommodationDetails(self, student):
94        """Determine the accommodation data of a student.
95        """
96        d = {}
97        d['error'] = u''
98        hostels = grok.getSite()['hostels']
99        d['booking_session'] = hostels.accommodation_session
100        d['allowed_states'] = hostels.accommodation_states
101        d['startdate'] = hostels.startdate
102        d['enddate'] = hostels.enddate
103        d['expired'] = hostels.expired
104        # Determine bed type
105        studycourse = student['studycourse']
106        certificate = getattr(studycourse,'certificate',None)
107        entry_session = studycourse.entry_session
108        current_level = studycourse.current_level
109        if None in (entry_session, current_level, certificate):
110            return d
111        end_level = certificate.end_level
112        if current_level == 10:
113            bt = 'pr'
114        elif entry_session == grok.getSite()['hostels'].accommodation_session:
115            bt = 'fr'
116        elif current_level >= end_level:
117            bt = 'fi'
118        else:
119            bt = 're'
120        if student.sex == 'f':
121            sex = 'female'
122        else:
123            sex = 'male'
124        special_handling = 'regular'
125        if student.faccode in ('MED', 'DEN'):
126            special_handling = 'clinical'
127        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
128        return d
129
130    def _paymentMade(self, student, session):
131        if len(student['payments']):
132            for ticket in student['payments'].values():
133                if ticket.p_state == 'paid' and \
134                    ticket.p_category == 'schoolfee' and \
135                    ticket.p_session == session:
136                    return True
137        return False
138
139    #def _hostelApplicationPaymentMade(self, student, session):
140    #    if len(student['payments']):
141    #        for ticket in student['payments'].values():
142    #            if ticket.p_state == 'paid' and \
143    #                ticket.p_category == 'hostel_application' and \
144    #                ticket.p_session == session:
145    #                return True
146    #    return False
147
148    def setPaymentDetails(self, category, student,
149            previous_session, previous_level):
150        """Create Payment object and set the payment data of a student for
151        the payment category specified.
152
153        """
154        p_item = u''
155        amount = 0.0
156        if previous_session:
157            if previous_session < student['studycourse'].entry_session:
158                return _('The previous session must not fall below '
159                         'your entry session.'), None
160            if category == 'schoolfee':
161                # School fee is always paid for the following session
162                if previous_session > student['studycourse'].current_session:
163                    return _('This is not a previous session.'), None
164            else:
165                if previous_session > student['studycourse'].current_session - 1:
166                    return _('This is not a previous session.'), None
167            p_session = previous_session
168            p_level = previous_level
169            p_current = False
170        else:
171            p_session = student['studycourse'].current_session
172            p_level = student['studycourse'].current_level
173            p_current = True
174        academic_session = self._getSessionConfiguration(p_session)
175        if academic_session == None:
176            return _(u'Session configuration object is not available.'), None
177        # Determine fee.
178        if category == 'transfer':
179            amount = academic_session.transfer_fee
180        elif category == 'transcript':
181            amount = academic_session.transcript_fee
182        elif category == 'gown':
183            amount = academic_session.gown_fee
184        elif category == 'bed_allocation':
185            p_item = self.getAccommodationDetails(student)['bt']
186            amount = academic_session.booking_fee
187            # Add student union dues
188            stage = self.getAccommodationDetails(student)['bt']
189            stage = stage.split('_')[2]
190
191
192            #####################################################
193            if stage == 'fi':
194                return _('Payment temporarily disabled.'), None
195            #####################################################
196
197
198            if stage == 'fr':
199                amount += 500.0
200            elif stage == 'fi' and student[
201                'studycourse'].previous_verdict in ('A', 'B'):
202                amount += 300.0
203            else:
204                amount = 0.0
205        elif category == 'hostel_maintenance':
206            amount = 0.0
207            bedticket = student['accommodation'].get(
208                str(student.current_session), None)
209            if bedticket:
210                p_item = bedticket.bed_coordinates
211                if bedticket.bed.__parent__.maint_fee > 0:
212                    amount = bedticket.bed.__parent__.maint_fee
213                else:
214                    # fallback
215                    amount = academic_session.maint_fee
216            else:
217                # Should not happen because this is already checked
218                # in the browser module, but anyway ...
219                portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
220                p_item = trans(_('no bed allocated'), portal_language)
221        #elif category == 'hostel_application':
222        #    amount = 1000.0
223        #elif category.startswith('tempmaint'):
224        #    if not self._hostelApplicationPaymentMade(
225        #        student, student.current_session):
226        #        return _(
227        #            'You have not yet paid the hostel application fee.'), None
228        #    if category == 'tempmaint_1':
229        #        amount = 8150.0
230        #    elif category == 'tempmaint_2':
231        #        amount = 12650.0
232        #    elif category == 'tempmaint_3':
233        #        amount = 9650.0
234        elif category == 'clearance':
235            p_item = student.certcode
236            if p_item is None:
237                return _('Study course data are incomplete.'), None
238            if student.faccode == 'JUPEB':
239                return _('No payment required.'), None
240            if student.faccode == 'FCETA':
241                amount = 22500.0
242            elif p_item in ('BSCANA', 'BSCMBC', 'BMLS', 'BSCNUR', 'BSCPHS', 'BDS',
243                'MBBSMED', 'MBBSNDU'):
244                amount = 65000.0
245            elif p_item in ('BEDCET', 'BIOEDCET', 'CHMEDCET', 'ISEDCET',
246                'MTHEDCET', 'PHYEDCET', 'ITECET', 'AGREDCET', 'HEEDCET'):
247                amount = 22500.0
248            else:
249                amount = 45000.0
250        elif category == 'schoolfee':
251            try:
252                certificate = student['studycourse'].certificate
253                p_item = certificate.code
254            except (AttributeError, TypeError):
255                return _('Study course data are incomplete.'), None
256
257            #####################################################
258            #if student.faccode == 'JUPEB':
259            #    return _('Payment temporarily disabled.'), None
260            #####################################################
261
262
263            if previous_session:
264                # Students can pay for previous sessions in all workflow states.
265                # Fresh students are excluded by the update method of the
266                # PreviousPaymentAddFormPage.
267                if previous_session == student['studycourse'].entry_session:
268                    if student.is_foreigner:
269                        amount = getattr(certificate, 'school_fee_3', 0.0)
270                    else:
271                        amount = getattr(certificate, 'school_fee_1', 0.0)
272                else:
273                    if student.is_foreigner:
274                        amount = getattr(certificate, 'school_fee_4', 0.0)
275                    else:
276                        amount = getattr(certificate, 'school_fee_2', 0.0)
277            else:
278                if student.state == CLEARED:
279                    if student.is_foreigner:
280                        amount = getattr(certificate, 'school_fee_3', 0.0)
281                    else:
282                        amount = getattr(certificate, 'school_fee_1', 0.0)
283                elif student.state in (PAID, REGISTERED, VALIDATED):
284                    p_session += 1
285                    # We don't know which level the student is paying for.
286                    p_level = None
287                    academic_session = self._getSessionConfiguration(p_session)
288                    if academic_session == None:
289                        return _(u'Session configuration object is not available.'), None
290
291                    # Students are only allowed to pay for the next session
292                    # if current session payment
293                    # has really been made, i.e. payment object exists.
294                    #if not self._paymentMade(
295                    #    student, student.current_session):
296                    #    return _('You have not yet paid your current/active' +
297                    #             ' session. Please use the previous session' +
298                    #             ' payment form first.'), None
299
300                    if student.is_foreigner:
301                        amount = getattr(certificate, 'school_fee_4', 0.0)
302                    else:
303                        amount = getattr(certificate, 'school_fee_2', 0.0)
304                elif student.state == RETURNING:
305                    # In case of returning school fee payment the payment session
306                    # and level contain the values of the session the student
307                    # has paid for.
308                    p_session, p_level = self.getReturningData(student)
309                    academic_session = self._getSessionConfiguration(p_session)
310                    if academic_session == None:
311                        return _(u'Session configuration object is not available.'), None
312
313                    # Students are only allowed to pay for the next session
314                    # if current session payment has really been made,
315                    # i.e. payment object exists and is paid.
316                    #if not self._paymentMade(
317                    #    student, student.current_session):
318                    #    return _('You have not yet paid your current/active' +
319                    #             ' session. Please use the previous session' +
320                    #             ' payment form first.'), None
321
322                    if student.is_foreigner:
323                        amount = getattr(certificate, 'school_fee_4', 0.0)
324                    else:
325                        amount = getattr(certificate, 'school_fee_2', 0.0)
326            # Give 50% school fee discount to staff members.
327            if student.is_staff:
328                amount /= 2
329        if amount in (0.0, None):
330            return _('Amount could not be determined.'), None
331        # Add session specific penalty fee.
332        if category == 'schoolfee' and student.is_postgrad:
333            amount += academic_session.penalty_pg
334        elif category == 'schoolfee':
335            amount += academic_session.penalty_ug
336        if category.startswith('tempmaint'):
337            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
338            p_item = unicode(p_item)
339            # Now we change the category because tempmaint payments
340            # will be obsolete when Uniben returns to Kofa bed allocation.
341            category = 'hostel_maintenance'
342        # Create ticket.
343        if self.samePaymentMade(student, category, p_item, p_session):
344            return _('This type of payment has already been made.'), None
345        if self._isPaymentDisabled(p_session, category, student):
346            return _('Payment temporarily disabled.'), None
347        payment = createObject(u'waeup.StudentOnlinePayment')
348        timestamp = ("%d" % int(time()*10000))[1:]
349        payment.p_id = "p%s" % timestamp
350        payment.p_category = category
351        payment.p_item = p_item
352        payment.p_session = p_session
353        payment.p_level = p_level
354        payment.p_current = p_current
355        payment.amount_auth = amount
356        return None, payment
357
358    def maxCredits(self, studylevel):
359        """Return maximum credits.
360
361        """
362        studycourse = studylevel.__parent__
363        certificate = getattr(studycourse,'certificate', None)
364        current_level = studycourse.current_level
365        if None in (current_level, certificate):
366            return 0
367        end_level = certificate.end_level
368        if current_level >= end_level:
369            return 51
370        return 50
371
372    def clearance_disabled_message(self, student):
373        if student.is_postgrad:
374            return None
375        try:
376            session_config = grok.getSite()[
377                'configuration'][str(student.current_session)]
378        except KeyError:
379            return _('Session configuration object is not available.')
380        if not session_config.clearance_enabled:
381            return _('Clearance is disabled for this session.')
382        return None
383
384    def selectBed(self, available_beds):
385        """Select a bed from a list of available beds.
386        Beds are sorted by the sort id of the hostel and the bed number.
387        The first bed found in this sorted list is taken.
388        """
389        sorted_beds = sorted(available_beds,
390            key=lambda bed: 1000 * bed.__parent__.sort_id + bed.bed_number)
391        return sorted_beds[0]
392
393    # Uniben prefix
394    STUDENT_ID_PREFIX = u'B'
Note: See TracBrowser for help on using the repository browser.