source: main/waeup.kwarapoly/trunk/src/waeup/kwarapoly/students/utils.py @ 10778

Last change on this file since 10778 was 10756, checked in by Henrik Bettermann, 11 years ago

Allow students in state cleared to upload a passport picture.

  • Property svn:keywords set to Id
File size: 11.9 KB
Line 
1## $Id: utils.py 10756 2013-11-18 12:24:51Z 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
19import random
20from time import time
21from zope.component import createObject, getUtility
22from zope.catalog.interfaces import ICatalog
23from waeup.kofa.interfaces import CLEARED, RETURNING, PAID
24from kofacustom.nigeria.students.utils import NigeriaStudentsUtils
25from waeup.kofa.accesscodes import create_accesscode
26from waeup.kofa.interfaces import CLEARED, RETURNING, ADMITTED, IKofaUtils
27from waeup.kofa.fees import FeeTable
28from waeup.kofa.hostels.hostel import NOT_OCCUPIED
29from waeup.kwarapoly.interfaces import MessageFactory as _
30
31
32# 10  = PreND (1)
33# 100 = ND1 (2)
34# 110 = ND1R (3)
35# 200 = ND2 (4)
36# 210 = ND2R (5)
37# 300 = ND3 (6)
38# 400 = HND1 (7)
39# 410 = HND1R (8)
40# 500 = HND2 (9)
41# 510 = HND2R (10)
42# 600 = HND3 (11)
43# 999 = PGD (12)
44
45PAYMENT_LEVELS = (10, 100, 110, 200, 210, 300, 400, 410, 500, 510, 600, 999)
46
47FEES_PARAMS = (
48        ('ft', 'pt'),
49        ('local', 'non-local'),
50        ('science','arts'),
51        PAYMENT_LEVELS
52    )
53
54FEES_VALUES = (
55        (
56          ( # 10       100      110      200      210     300    400     410      500      510     600   999
57            (34500.0, 39400.0, 28500.0, 29500.0, 28500.0, 0.0, 40000.0, 29400.0, 31700.0, 29400.0, 0.0, 56600.0), # science
58            (34500.0, 37900.0, 27000.0, 28000.0, 27000.0, 0.0, 38500.0, 27900.0, 30200.0, 27900.0, 0.0, 55100.0)  # arts
59          ), # local
60          ( # 10       100      110      200      210     300    400     410      500      510     600   999
61            (49600.0, 53900.0, 37600.0, 32090.0, 37600.0, 0.0, 56400.0, 39100.0, 34900.0, 39100.0, 0.0, 83250.0), # science
62            (49600.0, 52400.0, 36100.0, 30590.0, 36100.0, 0.0, 54900.0, 37600.0, 33400.0, 37600.0, 0.0, 81750.0)  # arts
63          ), # non-local
64        ), # ft
65        (
66          ( # 10   100     110    200    210   300      400     410   500     510   600     999
67            (0.0, 40700.0, 0.0, 29900.0, 0.0, 29900.0, 41100.0, 0.0, 31050.0, 0.0, 31050.0, 0.0), # science
68            (0.0, 39200.0, 0.0, 28400.0, 0.0, 28400.0, 39600.0, 0.0, 29550.0, 0.0, 29550.0, 0.0)  # arts
69          ), # local
70          ( # 10   100     110    200    210   300      400     410   500     510   600     999
71            (0.0, 55400.0, 0.0, 33850.0, 0.0, 33850.0, 58300.0, 0.0, 42350.0, 0.0, 42350.0, 0.0), # science
72            (0.0, 53900.0, 0.0, 32350.0, 0.0, 32350.0, 56800.0, 0.0, 40850.0, 0.0, 40850.0, 0.0)  # arts
73          ), # non-local
74        ), # pt
75    )
76
77SCHOOL_FEES = FeeTable(FEES_PARAMS, FEES_VALUES)
78
79def local_nonlocal(student):
80    lga = getattr(student, 'lga')
81    if lga and lga.startswith('kwara'):
82        return 'local'
83    else:
84        return 'non-local'
85
86def arts_science(student):
87    if student.faccode == 'IFMS':
88        return 'arts'
89    else:
90        return 'science'
91
92def pt_ft(student):
93    if student.current_mode.endswith('pt'):
94        return 'pt'
95    else:
96        return 'ft'
97
98class CustomStudentsUtils(NigeriaStudentsUtils):
99    """A collection of customized methods.
100
101    """
102
103
104    def selectBed(self, available_beds):
105        """Randomly select a bed from a list of available beds.
106
107        """
108        return random.choice(available_beds)
109
110    def getReturningData(self, student):
111        """ This method defines what happens after school fee payment
112        of returning students depending on the student's senate verdict.
113        """
114        prev_level = student['studycourse'].current_level
115        cur_verdict = student['studycourse'].current_verdict
116        if cur_verdict in ('A','B','L','M','N','Z',):
117            # Successful student
118            new_level = divmod(int(prev_level),100)[0]*100 + 100
119        elif cur_verdict == 'C':
120            # Student on probation
121            new_level = int(prev_level) + 10
122        else:
123            # Student is somehow in an undefined state.
124            # Level has to be set manually.
125            new_level = prev_level
126        new_session = student['studycourse'].current_session + 1
127        return new_session, new_level
128
129    def _maintPaymentMade(self, student, session):
130        if len(student['payments']):
131            for ticket in student['payments'].values():
132                if ticket.p_category == 'hostel_maintenance' and \
133                    ticket.p_session == session and ticket.p_state == 'paid':
134                        return True
135        return False
136
137    def _bedAvailable(self, student):
138        acc_details  = self.getAccommodationDetails(student)
139        cat = getUtility(ICatalog, name='beds_catalog')
140        entries = cat.searchResults(
141            owner=(student.student_id,student.student_id))
142        if len(entries):
143            # Bed has already been booked.
144            return True
145        entries = cat.searchResults(
146            bed_type=(acc_details['bt'],acc_details['bt']))
147        available_beds = [
148            entry for entry in entries if entry.owner == NOT_OCCUPIED]
149        if available_beds:
150            # Bed has not yet been booked but beds are available.
151            return True
152        return False
153
154    def setPaymentDetails(self, category, student,
155            previous_session=None, previous_level=None):
156        """Create Payment object and set the payment data of a student for
157        the payment category specified.
158
159        """
160        details = {}
161        p_item = u''
162        amount = 0.0
163        error = u''
164        if previous_session:
165            return _('Previous session payment not yet implemented.'), None
166        p_session = student['studycourse'].current_session
167        p_level = student['studycourse'].current_level
168        p_current = True
169        academic_session = self._getSessionConfiguration(p_session)
170        if academic_session == None:
171            return _(u'Session configuration object is not available.'), None
172        # Determine fee.
173        if category == 'transfer':
174            amount = academic_session.transfer_fee
175        elif category == 'gown':
176            amount = academic_session.gown_fee
177        elif category == 'bed_allocation':
178            amount = academic_session.booking_fee
179        elif category == 'hostel_maintenance':
180            amount = 0.0
181            bedticket = student['accommodation'].get(
182                str(student.current_session), None)
183            if bedticket:
184                p_item = bedticket.bed_coordinates
185                if bedticket.bed.__parent__.maint_fee > 0:
186                    amount = bedticket.bed.__parent__.maint_fee
187                else:
188                    # fallback
189                    amount = academic_session.maint_fee
190            else:
191                # Should not happen because this is already checked
192                # in the browser module, but anyway ...
193                portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
194                p_item = trans(_('no bed allocated'), portal_language)
195        elif category == 'clearance':
196            amount = academic_session.clearance_fee
197            try:
198                p_item = student['studycourse'].certificate.code
199            except (AttributeError, TypeError):
200                return _('Study course data are incomplete.'), None
201        elif category == 'schoolfee':
202            try:
203                certificate = student['studycourse'].certificate
204                p_item = certificate.code
205            except (AttributeError, TypeError):
206                return _('Study course data are incomplete.'), None
207            if student.state == RETURNING:
208                # Override p_session and p_level
209                p_session, p_level = self.getReturningData(student)
210                academic_session = self._getSessionConfiguration(p_session)
211                if academic_session == None:
212                    return _(u'Session configuration object '
213                              'is not available.'), None
214            if student.state == CLEARED and student.current_mode in (
215                                                            'hnd_ft', 'nd_ft'):
216                # Fresh students must have booked and paid for accommodation.
217                if self._bedAvailable(student):
218                    if not self._maintPaymentMade(student, p_session):
219                        return _('Book and pay for accommodation first '
220                                 'before making school fee payments.'), None
221            if student.state in (RETURNING, CLEARED):
222                if p_level in PAYMENT_LEVELS:
223                    amount = SCHOOL_FEES.get_fee(
224                        (pt_ft(student),
225                         local_nonlocal(student),
226                         arts_science(student),
227                         p_level)
228                        )
229        elif category == 'carryover1':
230            amount = 6000.0
231        elif category == 'carryover2':
232            amount = 7000.0
233        elif category == 'carryover3':
234            amount = 8000.0
235
236        else:
237            fee_name = category + '_fee'
238            amount = getattr(academic_session, fee_name, 0.0)
239        if amount in (0.0, None):
240            return _(u'Amount could not be determined.'), None
241        for key in student['payments'].keys():
242            ticket = student['payments'][key]
243            if ticket.p_state == 'paid' and\
244               ticket.p_category == category and \
245               ticket.p_item == p_item and \
246               ticket.p_session == p_session:
247                  return _('This type of payment has already been made.'), None
248        if category.startswith('carryover'):
249            p_item = getUtility(IKofaUtils).PAYMENT_CATEGORIES[category]
250            p_item = unicode(p_item)
251            # Now we change the category to reduce the number of categories.
252            category = 'schoolfee'
253        payment = createObject(u'waeup.StudentOnlinePayment')
254        timestamp = ("%d" % int(time()*10000))[1:]
255        payment.p_id = "p%s" % timestamp
256        payment.p_category = category
257        payment.p_item = p_item
258        payment.p_session = p_session
259        payment.p_level = p_level
260        payment.p_current = p_current
261        payment.amount_auth = float(amount)
262        return None, payment
263
264    def getAccommodationDetails(self, student):
265        """Determine the accommodation data of a student.
266        """
267        d = {}
268        d['error'] = u''
269        hostels = grok.getSite()['hostels']
270        d['booking_session'] = hostels.accommodation_session
271        d['allowed_states'] = hostels.accommodation_states
272        d['startdate'] = hostels.startdate
273        d['enddate'] = hostels.enddate
274        d['expired'] = hostels.expired
275        # Determine bed type
276        studycourse = student['studycourse']
277        certificate = getattr(studycourse,'certificate',None)
278        current_level = studycourse.current_level
279        if None in (current_level, certificate):
280            return d
281        end_level = certificate.end_level
282        if current_level == 10:
283            bt = 'pr'
284        elif current_level in (100, 400):
285            bt = 'fr'
286        elif current_level in (300, 600):
287            bt = 'fi'
288        else:
289            bt = 're'
290        if student.sex == 'f':
291            sex = 'female'
292        else:
293            sex = 'male'
294        special_handling = 'regular'
295        if student.faccode == 'ITCH':
296            special_handling = 'itch'
297        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
298        return d
299
300    PWCHANGE_STATES = (ADMITTED, CLEARED, RETURNING)
301
302    # KwaraPoly prefix
303    STUDENT_ID_PREFIX = u'W'
Note: See TracBrowser for help on using the repository browser.