source: main/waeup.uniben/trunk/src/waeup/uniben/students/tests/test_browser.py @ 13237

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

Adjust test.

  • Property svn:keywords set to Id
File size: 44.7 KB
Line 
1## $Id: test_browser.py 13110 2015-06-28 05:06:39Z 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 os
19import shutil
20import tempfile
21from StringIO import StringIO
22from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
23from zope.securitypolicy.interfaces import IPrincipalRoleManager
24from zope.component.hooks import setSite, clearSite
25from zope.component import getUtility, createObject
26from zope.interface import verify
27from zope.event import notify
28from waeup.kofa.authentication import LocalRoleSetEvent
29from waeup.kofa.app import University
30from waeup.kofa.students.tests.test_browser import StudentsFullSetup
31from waeup.kofa.students.accommodation import BedTicket
32from waeup.kofa.testing import FunctionalTestCase
33from waeup.kofa.browser.tests.test_pdf import samples_dir
34from waeup.kofa.interfaces import (
35    IExtFileStore, IFileStoreNameChooser)
36from waeup.kofa.students.interfaces import IStudentsUtils
37from waeup.uniben.testing import FunctionalLayer
38
39SAMPLE_FPM = os.path.join(os.path.dirname(__file__), 'sample.fpm')
40
41class StudentUITests(StudentsFullSetup):
42    """Tests for customized student class views and pages
43    """
44
45    layer = FunctionalLayer
46
47    def setUp(self):
48        super(StudentUITests, self).setUp()
49
50        bedticket = BedTicket()
51        bedticket.booking_session = 2004
52        bedticket.bed_type = u'any bed type'
53        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
54        bedticket.bed_coordinates = u'My bed coordinates'
55        self.student['accommodation'].addBedTicket(bedticket)
56
57    def test_next_session_allowed(self):
58        # Let's see if next_session_allowed works as expected
59        # A, ug_ft, 100
60        IWorkflowState(self.student).setState('returning')
61        self.assertTrue(self.student['studycourse'].next_session_allowed)
62        # Uniben special PG programmes have the same workflow
63        # as UG students
64        self.certificate.study_mode = 'special_pg_pt'
65        self.assertTrue(self.student['studycourse'].next_session_allowed)
66        IWorkflowState(self.student).setState('school fee paid')
67        self.assertFalse(self.student['studycourse'].next_session_allowed)
68        # Now we convert the certificate into a 'regular
69        # postgraduate certificate ...
70        self.certificate.study_mode = 'pg_ft'
71        # ... and voila next session registration is allowed
72        self.assertTrue(self.student['studycourse'].next_session_allowed)
73
74    def test_manage_access(self):
75        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
76        # The student created in the base package is a ug student
77        self.browser.open(self.manage_clearance_path)
78        self.assertMatches('...First Sitting Record...',
79                           self.browser.contents)
80        # There is no pg field in the clearance form
81        self.assertFalse('Second Higher Education Record'
82            in self.browser.contents)
83        # Now we change the study mode ...
84        self.certificate.study_mode = 'pg_ft'
85        self.browser.open(self.clearance_path)
86        # ... and additional pg clearance fields appears
87        self.assertMatches('...Second Higher Education Record...',
88                           self.browser.contents)
89        # But also fields from the ug form are displayed
90        self.assertMatches('...First Sitting Record...',
91                           self.browser.contents)
92        # The same holds for Uniben's special pg students
93        self.certificate.study_mode = 'special_pg_pt'
94        self.browser.open(self.clearance_path)
95        self.assertMatches('...Second Higher Education Record...',
96                           self.browser.contents)
97        self.assertMatches('...First Sitting Record...',
98                           self.browser.contents)
99        # We want to see the signature fields.
100        IWorkflowState(self.student).setState('returning')
101        self.browser.open(self.student_path + '/clearance_slip.pdf')
102        self.assertEqual(self.browser.headers['Status'], '200 Ok')
103        self.assertEqual(self.browser.headers['Content-Type'],
104                         'application/pdf')
105        path = os.path.join(samples_dir(), 'clearance_slip.pdf')
106        open(path, 'wb').write(self.browser.contents)
107        print "Sample PDF clearance_slip.pdf written to %s" % path
108
109    def test_student_access(self):
110        # Students can edit clearance data
111        IWorkflowState(self.student).setState('clearance started')
112        self.student.nationality = u'NG'
113        file_store = getUtility(IExtFileStore)
114        self.browser.open(self.login_path)
115        self.browser.getControl(name="form.login").value = self.student_id
116        self.browser.getControl(name="form.password").value = 'spwd'
117        self.browser.getControl("Login").click()
118        self.browser.open(self.edit_clearance_path)
119        # UG students can't edit date_of_birth, nationality and lga
120        self.assertFalse('form.date_of_birth' in self.browser.contents)
121        self.assertFalse('form.nationality' in self.browser.contents)
122        self.assertFalse('form.lga' in self.browser.contents)
123        # Clearance can only be requested if all required documents
124        # have been uploaded.
125        self.browser.getControl("Save and request clearance").click()
126        self.assertTrue('No birth certificate uploaded'
127            in self.browser.contents)
128        birth_certificate = 'My birth certificate'
129        file_id = IFileStoreNameChooser(self.student).chooseName(
130            attr="birth_certificate.jpg")
131        file_store.createFile(file_id, StringIO(birth_certificate))
132        self.browser.open(self.edit_clearance_path)
133        self.browser.getControl("Save and request clearance").click()
134
135        self.assertTrue('No guarantor/referee letter uploaded'
136            in self.browser.contents)
137        ref_let = 'My ref let'
138        file_id = IFileStoreNameChooser(self.student).chooseName(
139            attr="ref_let.jpg")
140        file_store.createFile(file_id, StringIO(ref_let))
141        self.browser.open(self.edit_clearance_path)
142        self.browser.getControl("Save and request clearance").click()
143
144        self.assertTrue('No acceptance letter uploaded'
145            in self.browser.contents)
146        acc_let = 'My acc let'
147        file_id = IFileStoreNameChooser(self.student).chooseName(
148            attr="acc_let.jpg")
149        file_store.createFile(file_id, StringIO(acc_let))
150        self.browser.open(self.edit_clearance_path)
151        self.browser.getControl("Save and request clearance").click()
152
153        self.assertTrue('No first sitting result uploaded'
154            in self.browser.contents)
155        fst_sit_scan = 'My first sitting result'
156        file_id = IFileStoreNameChooser(self.student).chooseName(
157            attr="fst_sit_scan.jpg")
158        file_store.createFile(file_id, StringIO(fst_sit_scan))
159        self.browser.open(self.edit_clearance_path)
160        self.browser.getControl("Save and request clearance").click()
161
162        #self.assertTrue('No second sitting result uploaded'
163        #    in self.browser.contents)
164        #scd_sit_scan = 'My second sitting result'
165        #file_id = IFileStoreNameChooser(self.student).chooseName(
166        #    attr="scd_sit_scan.jpg")
167        #file_store.createFile(file_id, StringIO(scd_sit_scan))
168        #self.browser.open(self.edit_clearance_path)
169        #self.browser.getControl("Save and request clearance").click()
170
171        self.assertTrue('No affidavit of non-membership of secret cults uploaded'
172            in self.browser.contents)
173        secr_cults = 'My non-membership scan'
174        file_id = IFileStoreNameChooser(self.student).chooseName(
175            attr="secr_cults.jpg")
176        file_store.createFile(file_id, StringIO(secr_cults))
177        # Clearance invitation letter is not yet available
178        self.browser.open(self.clearance_path)
179        self.assertFalse('invitation slip' in self.browser.contents)
180        self.browser.open(self.student_path + '/clearance_invitation_slip.pdf')
181        self.assertTrue('Forbidden' in self.browser.contents)
182        self.browser.open(self.edit_clearance_path)
183        self.browser.getControl("Save and request clearance").click()
184        self.assertTrue('Clearance has been requested'
185            in self.browser.contents)
186        # Now student can export physical_clearance.slip
187        self.app['configuration'].name = u'University of Benin'
188        self.student.physical_clearance_date = u'January 5th, 2015'
189        self.browser.getLink("Clearance Data").click()
190        self.browser.getLink("Download clearance invitation slip").click()
191        self.assertEqual(self.browser.headers['Status'], '200 Ok')
192        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
193        path = os.path.join(samples_dir(), 'clearance_invitation_slip.pdf')
194        open(path, 'wb').write(self.browser.contents)
195        print "Sample PDF clearance_invitation_slip.pdf written to %s" % path
196        # Students can open the personal edit page and see the parent_email field.
197        self.browser.open(self.student_path + '/edit_personal')
198        self.assertTrue('parent_email' in self.browser.contents)
199
200    def test_manage_payments(self):
201        # Add missing configuration data
202        self.app['configuration']['2004'].gown_fee = 150.0
203        self.app['configuration']['2004'].transfer_fee = 90.0
204        #self.app['configuration']['2004'].clearance_fee = 120.0
205        self.app['configuration']['2004'].booking_fee = 150.0
206        self.app['configuration']['2004'].maint_fee = 180.0
207
208        # Managers can add online payment tickets
209        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
210        self.browser.open(self.payments_path)
211        self.browser.getLink("Add current session payment ticket").click()
212        self.browser.getControl("Create ticket").click()
213        self.assertMatches('...Amount could not be determined...',
214                           self.browser.contents)
215        IWorkflowState(self.student).setState('cleared')
216        self.student.nationality = u'NG'
217        self.browser.open(self.payments_path + '/addop')
218        self.browser.getControl(name="form.p_category").value = ['schoolfee']
219        self.browser.getControl("Create ticket").click()
220        self.assertMatches('...ticket created...',
221                           self.browser.contents)
222        ctrl = self.browser.getControl(name='val_id')
223        value = ctrl.options[0]
224        self.browser.getLink(value).click()
225        self.assertMatches('...Amount Authorized...',
226                           self.browser.contents)
227        # Managers can open payment slip
228        self.browser.getLink("Download payment slip").click()
229        self.assertEqual(self.browser.headers['Status'], '200 Ok')
230        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
231        # Set ticket paid
232        ticket = self.student['payments'].items()[0][1]
233        ticket.p_state = 'paid'
234        self.browser.open(self.payments_path + '/addop')
235        self.browser.getControl(name="form.p_category").value = ['schoolfee']
236        self.browser.getControl("Create ticket").click()
237        self.assertMatches('...This type of payment has already been made...',
238                           self.browser.contents)
239        # Remove all payments so that we can add a school fee payment again
240        keys = [i for i in self.student['payments'].keys()]
241        for payment in keys:
242            del self.student['payments'][payment]
243        self.browser.open(self.payments_path + '/addop')
244        self.browser.getControl(name="form.p_category").value = ['schoolfee']
245        self.browser.getControl("Create ticket").click()
246        self.assertMatches('...ticket created...',
247                           self.browser.contents)
248        schoolfee_ticket = self.student['payments'].values()[0]
249        self.browser.open(self.payments_path + '/addop')
250        self.browser.getControl(name="form.p_category").value = ['gown']
251        self.browser.getControl("Create ticket").click()
252        self.assertMatches('...ticket created...',
253                           self.browser.contents)
254        self.browser.open(self.payments_path + '/addop')
255        self.browser.getControl(
256            name="form.p_category").value = ['tempmaint_1']
257        self.browser.getControl("Create ticket").click()
258        self.assertMatches('...You have not yet paid the hostel application fee...',
259                           self.browser.contents)
260        self.browser.open(self.payments_path + '/addop')
261        self.browser.getControl(name="form.p_category").value = ['hostel_application']
262        self.browser.getControl("Create ticket").click()
263        self.assertMatches('...ticket created...',
264                           self.browser.contents)
265        ha_ticket = self.student['payments'].values()[2]
266        ha_ticket.approve()
267        self.browser.open(self.payments_path + '/addop')
268        self.browser.getControl(
269            name="form.p_category").value = ['tempmaint_1']
270        self.browser.getControl("Create ticket").click()
271        self.assertMatches('...ticket created...',
272                           self.browser.contents)
273        self.browser.open(self.payments_path + '/addop')
274        self.browser.getControl(
275            name="form.p_category").value = ['tempmaint_2']
276        self.browser.getControl("Create ticket").click()
277        self.assertMatches('...ticket created...',
278                           self.browser.contents)
279        self.browser.open(self.payments_path + '/addop')
280        self.browser.getControl(
281            name="form.p_category").value = ['tempmaint_3']
282        self.browser.getControl("Create ticket").click()
283        self.assertMatches('...ticket created...',
284                           self.browser.contents)
285        self.browser.open(self.payments_path + '/addop')
286        # We can't test clearance payment ticket creation at the moment,
287        # since Uniben decided to deactivate clearance for ug students.
288        #self.browser.getControl(name="form.p_category").value = ['clearance']
289        #self.browser.getControl("Create ticket").click()
290        #self.assertMatches('...ticket created...',
291        #                   self.browser.contents)
292        self.browser.open(self.payments_path + '/addop')
293        self.browser.getControl(name="form.p_category").value = ['schoolfee']
294        self.browser.getControl("Create ticket").click()
295        self.assertMatches('...ticket created...',
296                           self.browser.contents)
297        # In state returning we can add a new school fee ticket since
298        # p_session and p_level is different
299        IWorkflowState(self.student).setState('returning')
300        self.browser.open(self.payments_path + '/addop')
301        self.browser.getControl(name="form.p_category").value = ['schoolfee']
302        self.browser.getControl("Create ticket").click()
303        # Uups, we forgot to add a session configuration for next session
304        self.assertMatches('...Session configuration object is not...',
305                           self.browser.contents)
306        configuration = createObject('waeup.SessionConfiguration')
307        configuration.academic_session = 2005
308        self.app['configuration'].addSessionConfiguration(configuration)
309        self.browser.open(self.payments_path + '/addop')
310        self.browser.getControl(name="form.p_category").value = ['schoolfee']
311        self.browser.getControl("Create ticket").click()
312
313
314        #self.assertMatches('...You have not yet paid your current/active session...',
315        #                   self.browser.contents)
316        ## Ok, let's pay the first schoolfee ticket.
317        #schoolfee_ticket.approve()
318        #self.browser.open(self.payments_path + '/addop')
319        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
320        #self.browser.getControl("Create ticket").click()
321
322
323        self.assertMatches('...ticket created...',
324                           self.browser.contents)
325        # In state admitted school fee can't be determined
326        IWorkflowState(self.student).setState('admitted')
327        self.browser.open(self.payments_path + '/addop')
328        self.browser.getControl(name="form.p_category").value = ['schoolfee']
329        self.browser.getControl("Create ticket").click()
330        self.assertMatches('...Amount could not be determined...',
331                           self.browser.contents)
332
333        # If the session configuration doesn't exist an error message will
334        # be shown. No other requirement is being checked.
335        del self.app['configuration']['2004']
336        self.browser.open(self.payments_path)
337        self.browser.getLink("Add current session payment ticket").click()
338        self.browser.getControl("Create ticket").click()
339        self.assertMatches('...Session configuration object is not...',
340                           self.browser.contents)
341
342    def test_student_course_registration(self):
343        # Uniben students see grade instead of score on all level pages
344        # and on course ticket page.
345        IWorkflowState(self.student).setState('school fee paid')
346        self.browser.open(self.login_path)
347        self.browser.getControl(name="form.login").value = self.student_id
348        self.browser.getControl(name="form.password").value = 'spwd'
349        self.browser.getControl("Login").click()
350        # Now students can add the current study level
351        self.browser.getLink("Study Course").click()
352        self.browser.getLink("Add course list").click()
353        self.assertMatches('...Add current level 100 (Year 1)...',
354                           self.browser.contents)
355        self.browser.getControl("Create course list now").click()
356        # A level with one course ticket was created
357        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
358        self.student['studycourse']['100']['COURSE1'].score = 55
359        self.browser.getLink("100").click()
360        # GPA has been properly calculated
361        self.assertEqual(self.student['studycourse']['100'].gpa_params[0], 3.0)
362        # Score is not shown but grade
363        self.assertTrue('<th>Grade</th>' in self.browser.contents)
364        self.assertFalse('<th>Score</th>' in self.browser.contents)
365        self.browser.getLink("Edit course list").click()
366        self.assertTrue('<th>Grade</th>' in self.browser.contents)
367        self.assertFalse('<th>Score</th>' in self.browser.contents)
368        self.browser.getLink("COURSE1").click()
369        self.assertFalse('Score' in self.browser.contents)
370        # Students can open the special Uniben pdf course result slip
371        self.browser.open(self.student_path + '/studycourse/100')
372        self.browser.getLink("Download course result slip").click()
373        self.assertEqual(self.browser.headers['Status'], '200 Ok')
374        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
375        # Even if course is mandatory, students can remove the course
376        self.student['studycourse']['100']['COURSE1'].mandatory = True
377        self.browser.open(self.student_path + '/studycourse/100')
378        self.browser.getLink("Edit course list").click()
379        ctrl = self.browser.getControl(name='val_id')
380        ctrl.getControl(value='COURSE1').selected = True
381        self.browser.getControl("Remove selected", index=0).click()
382        self.assertTrue('Successfully removed' in self.browser.contents)
383        # Students can open the customized pdf course registration slip
384        # if they have registered their course list
385        self.browser.open(
386            self.student_path + '/studycourse/100/course_registration_slip.pdf')
387        self.assertTrue('Forbidden' in self.browser.contents)
388        IWorkflowState(self.student).setState('courses registered')
389        self.browser.open(self.student_path + '/studycourse/100')
390        self.browser.getLink("Download course registration slip").click()
391        self.assertEqual(self.browser.headers['Status'], '200 Ok')
392        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
393
394    def test_get_returning_data(self):
395        # Student is in level 100, session 2004 with verdict A
396        utils = getUtility(IStudentsUtils)
397        self.assertEqual(utils.getReturningData(self.student),(2005, 200))
398        self.student['studycourse'].current_verdict = 'C'
399        self.assertEqual(utils.getReturningData(self.student),(2005, 110))
400        self.student['studycourse'].current_verdict = 'D'
401        self.assertEqual(utils.getReturningData(self.student),(2005, 100))
402        return
403
404    def test_set_returning_data(self):
405        # Student is in level 100, session 2004 with verdict A
406        utils = getUtility(IStudentsUtils)
407
408        utils.setReturningData(self.student)
409        self.assertEqual(self.student['studycourse'].current_session, 2005)
410        self.assertEqual(self.student['studycourse'].current_level, 200)
411
412        self.student['studycourse'].current_session = 2004
413        self.student['studycourse'].current_level = 100
414        self.student['studycourse'].current_verdict = 'C'
415        utils.setReturningData(self.student)
416        self.assertEqual(self.student['studycourse'].current_session, 2005)
417        self.assertEqual(self.student['studycourse'].current_level, 110)
418
419        self.student['studycourse'].current_session = 2004
420        self.student['studycourse'].current_level = 100
421        self.student['studycourse'].current_verdict = 'D'
422        utils.setReturningData(self.student)
423        self.assertEqual(self.student['studycourse'].current_session, 2005)
424        self.assertEqual(self.student['studycourse'].current_level, 100)
425        return
426
427    def test_set_payment_details(self):
428        self.app['configuration']['2004'].gown_fee = 150.0
429        self.app['configuration']['2004'].transfer_fee = 90.0
430        self.app['configuration']['2004'].booking_fee = 150.0
431        self.app['configuration']['2004'].maint_fee = 180.0
432
433        configuration = createObject('waeup.SessionConfiguration')
434        configuration.academic_session = 2000
435        self.app['configuration'].addSessionConfiguration(configuration)
436        configuration2 = createObject('waeup.SessionConfiguration')
437        configuration2.academic_session = 2002
438        self.app['configuration'].addSessionConfiguration(configuration2)
439        configuration3 = createObject('waeup.SessionConfiguration')
440        configuration3.academic_session = 2003
441        self.app['configuration'].addSessionConfiguration(configuration3)
442        configuration4 = createObject('waeup.SessionConfiguration')
443        configuration4.academic_session = 2005
444        self.app['configuration'].addSessionConfiguration(configuration4)
445        utils = getUtility(IStudentsUtils)
446        self.student['studycourse'].entry_session = 2002
447        self.student.nationality = u'NG'
448
449        error, payment = utils.setPaymentDetails('schoolfee',
450            self.student, None, None)
451        self.assertEqual(payment, None)
452        # Student is in state 'created' and can thus not pay.
453        self.assertTrue(u'Amount could not be determined.' in error)
454
455        # Previous session must be valid.
456        error, payment = utils.setPaymentDetails('schoolfee',
457            self.student, 2000, 300)
458        self.assertEqual(payment, None)
459        self.assertTrue(u'The previous session must not fall below' in error)
460        error, payment = utils.setPaymentDetails('schoolfee',
461            self.student, 2005, 300)
462        self.assertEqual(payment, None)
463        self.assertTrue(u'This is not a previous session' in error)
464
465        # Previous session schoolfee payment; fresh and returning
466        # are distinguished by their entry_level
467        error, payment = utils.setPaymentDetails('schoolfee',
468            self.student, 2002, 300)
469        self.assertEqual(payment.amount_auth, 40000.0)
470        self.assertEqual(payment.p_session, 2002)
471        self.assertEqual(payment.p_level, 300)
472        self.assertFalse(payment.p_current)
473        error, payment = utils.setPaymentDetails('schoolfee',
474            self.student, 2003, 300)
475        self.assertEqual(payment.amount_auth, 20000.0)
476        self.assertEqual(payment.p_session, 2003)
477        self.assertEqual(payment.p_level, 300)
478        self.assertFalse(payment.p_current)
479
480        # Current schoolfee payment; fresh and returning
481        # are distinguished by their state
482        IWorkflowState(self.student).setState('cleared')
483        error, payment = utils.setPaymentDetails('schoolfee',
484            self.student, None, None)
485        self.assertEqual(payment.p_level, 100)
486        self.assertEqual(payment.p_session, 2004)
487        self.assertEqual(payment.amount_auth, 40000.0)
488        self.assertEqual(payment.p_item, u'CERT1')
489        self.assertEqual(error, None)
490        self.assertTrue(payment.p_current)
491
492        # Add penalty fee ...
493        # ... for cleared
494        self.app['configuration']['2004'].penalty_ug = 99.0
495        # ... for returning
496        self.app['configuration']['2005'].penalty_ug = 88.0
497        error, payment = utils.setPaymentDetails('schoolfee',
498            self.student, None, None)
499        self.assertEqual(payment.amount_auth, 40099.0)
500
501        IWorkflowState(self.student).setState('returning')
502
503
504        #error, payment = utils.setPaymentDetails('schoolfee',
505        #    self.student, None, None)
506        #self.assertTrue(
507        #    u'You have not yet paid your current/active session.' in error)
508        ## Ok, that means we have to add paid payment ticket first.
509        #payment = createObject('waeup.StudentOnlinePayment')
510        #payment.p_category = u'schoolfee'
511        #payment.p_session = self.student.current_session
512        #payment.p_item = u'My Certificate'
513        #payment.p_id = u'anyid'
514        #payment.p_state = u'paid'
515        #self.student['payments']['anykey'] = payment
516
517
518        error, payment = utils.setPaymentDetails('schoolfee',
519            self.student, None, None)
520        self.assertEqual(payment.p_level, 200)
521        self.assertEqual(payment.p_session, 2005)
522        self.assertEqual(payment.amount_auth, 20088.0)
523        self.assertEqual(payment.p_item, u'CERT1')
524        self.assertEqual(error, None)
525
526        # Staff members pay less.
527        self.student.is_staff = True
528        error, payment = utils.setPaymentDetails('schoolfee',
529            self.student, None, None)
530        self.assertEqual(payment.p_level, 200)
531        self.assertEqual(payment.p_session, 2005)
532        self.assertEqual(payment.amount_auth, 10088.0)
533        self.assertEqual(payment.p_item, u'CERT1')
534        self.assertEqual(error, None)
535
536        # Foreigners pay more.
537        IWorkflowState(self.student).setState('cleared')
538        self.student.is_staff = False
539        self.student.nationality = u'DE'
540        self.certificate.school_fee_3 = 60000.0
541        error, payment = utils.setPaymentDetails(
542            'schoolfee', self.student, None, None)
543        self.assertEqual(payment.p_level, 100)
544        self.assertEqual(payment.p_session, 2004)
545        self.assertEqual(payment.amount_auth, 60099.0)
546        self.assertEqual(payment.p_item, u'CERT1')
547        self.assertEqual(error, None)
548        IWorkflowState(self.student).setState('returning')
549        self.student.is_staff = False
550        self.certificate.school_fee_4 = 20000.0
551        error, payment = utils.setPaymentDetails(
552            'schoolfee', self.student, None, None)
553        self.assertEqual(payment.p_level, 200)
554        self.assertEqual(payment.p_session, 2005)
555        self.assertEqual(payment.amount_auth, 20088.0)
556        self.assertEqual(payment.p_item, u'CERT1')
557        self.assertEqual(error, None)
558
559        # In Uniben students can pay school fee in all states no matter
560        # if they are ug or pg students.
561        IWorkflowState(self.student).setState('school fee paid')
562        self.student.is_staff = False
563        self.student.nationality = u'NG'
564        self.certificate.school_fee_2 = 10000.0
565        error, payment = utils.setPaymentDetails(
566            'schoolfee', self.student, None, None)
567        self.assertEqual(payment.p_level, None)
568        self.assertEqual(payment.p_session, 2005)
569        self.assertEqual(payment.amount_auth, 10088.0)
570        self.assertEqual(payment.p_item, u'CERT1')
571        self.assertEqual(error, None)
572        IWorkflowState(self.student).setState('courses registered')
573        self.certificate.study_mode = 'special_pg_pt'
574        error, payment = utils.setPaymentDetails(
575            'schoolfee', self.student, None, None)
576        self.assertEqual(payment.p_level, None)
577        self.assertEqual(payment.p_session, 2005)
578        self.assertEqual(payment.amount_auth, 10000.0)
579        self.assertEqual(payment.p_item, u'CERT1')
580        self.assertEqual(error, None)
581        IWorkflowState(self.student).setState('courses validated')
582        error, payment = utils.setPaymentDetails(
583            'schoolfee', self.student, None, None)
584        self.assertEqual(payment.p_level, None)
585        self.assertEqual(payment.p_session, 2005)
586        self.assertEqual(payment.amount_auth, 10000.0)
587        self.assertEqual(payment.p_item, u'CERT1')
588        self.assertEqual(error, None)
589
590        error, payment = utils.setPaymentDetails('clearance',
591            self.student, None, None)
592        self.assertEqual(payment.p_level, 100)
593        self.assertEqual(payment.p_session, 2004)
594        self.assertEqual(payment.amount_auth, 45000.0)
595        self.assertEqual(payment.p_item, u'CERT1')
596        self.assertEqual(error, None)
597
598        error, payment = utils.setPaymentDetails('gown',
599            self.student, None, None)
600        self.assertEqual(payment.p_level, 100)
601        self.assertEqual(payment.p_session, 2004)
602        self.assertEqual(payment.amount_auth, 150.0)
603        self.assertEqual(payment.p_item, u'')
604        self.assertEqual(error, None)
605
606        #error, payment = utils.setPaymentDetails('hostel_maintenance',
607        #    self.student, None, None)
608        #self.assertEqual(payment.p_level, 100)
609        #self.assertEqual(payment.p_session, 2004)
610        #self.assertEqual(payment.amount_auth, 180.0)
611        #self.assertEqual(payment.p_item, u'')
612        #self.assertEqual(error, None)
613
614        #error, payment = utils.setPaymentDetails('bed_allocation',
615        #    self.student, None, None)
616        #self.assertEqual(payment.p_level, 100)
617        #self.assertEqual(payment.p_session, 2004)
618        #self.assertEqual(payment.amount_auth, 150.0)
619        #self.assertEqual(payment.p_item, u'')
620        #self.assertEqual(error, None)
621
622        error, payment = utils.setPaymentDetails('hostel_application',
623            self.student, None, None)
624        self.assertEqual(payment.p_level, 100)
625        self.assertEqual(payment.p_session, 2004)
626        self.assertEqual(payment.amount_auth, 1000.0)
627        self.assertEqual(payment.p_item, u'')
628        self.assertEqual(error, None)
629
630        payment.approve()
631        self.student['payments'][payment.p_id] = payment
632
633        error, payment = utils.setPaymentDetails('tempmaint_1',
634            self.student, None, None)
635        self.assertEqual(payment.p_level, 100)
636        self.assertEqual(payment.p_session, 2004)
637        self.assertEqual(payment.amount_auth, 8150.0)
638        self.assertEqual(payment.p_item, u'Hall 1-4 M/F Ekehuan')
639        self.assertEqual(error, None)
640
641        error, payment = utils.setPaymentDetails('tempmaint_2',
642            self.student, None, None)
643        self.assertEqual(payment.p_level, 100)
644        self.assertEqual(payment.p_session, 2004)
645        self.assertEqual(payment.amount_auth, 12650.0)
646        self.assertEqual(payment.p_item, u'Hall 5 M/F')
647        self.assertEqual(error, None)
648
649        error, payment = utils.setPaymentDetails('tempmaint_3',
650            self.student, None, None)
651        self.assertEqual(payment.p_level, 100)
652        self.assertEqual(payment.p_session, 2004)
653        self.assertEqual(payment.amount_auth, 9650.0)
654        self.assertEqual(payment.p_item, u'Clinical Hostel')
655        self.assertEqual(error, None)
656
657        error, payment = utils.setPaymentDetails('transfer',
658            self.student, None, None)
659        self.assertEqual(payment.p_level, 100)
660        self.assertEqual(payment.p_session, 2004)
661        self.assertEqual(payment.amount_auth, 90.0)
662        self.assertEqual(payment.p_item, u'')
663        self.assertEqual(error, None)
664        return
665
666    def test_edit_level_by_co(self):
667        # Create clearance officer
668        self.app['users'].addUser('mrclear', 'mrclearsecret')
669        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
670        self.app['users']['mrclear'].title = 'Carlo Pitter'
671        # Assign local ClearanceOfficer role
672        department = self.app['faculties']['fac1']['dep1']
673        prmlocal = IPrincipalRoleManager(department)
674        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
675        notify(LocalRoleSetEvent(
676            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
677        IWorkflowState(self.student).setState('clearance started')
678        # Login as clearance officer
679        self.browser.open(self.login_path)
680        self.browser.getControl(name="form.login").value = 'mrclear'
681        self.browser.getControl(name="form.password").value = 'mrclearsecret'
682        self.browser.getControl("Login").click()
683        self.assertMatches('...You logged in...', self.browser.contents)
684        # Only in state clearance requested the CO does see the
685        # 'Edit level' button ...
686        self.browser.open(self.studycourse_path)
687        self.assertFalse('Edit level' in self.browser.contents)
688        # ... and can open the edit_level view
689        self.browser.open(self.studycourse_path + '/edit_level')
690        self.assertMatches('...is locked...', self.browser.contents)
691        self.assertEqual(self.browser.url, self.studycourse_path)
692        IWorkflowInfo(self.student).fireTransition('request_clearance')
693        self.browser.open(self.studycourse_path)
694        self.assertTrue('Edit level' in self.browser.contents)
695        self.browser.getLink("Edit level").click()
696        self.browser.getControl(name="form.current_level").value = ['200']
697        self.browser.getControl("Save").click()
698        self.assertMatches('...has been saved...', self.browser.contents)
699        self.assertEqual(self.student.current_level, 200)
700
701    def test_postgraduate_student_access(self):
702        self.certificate.study_mode = 'special_pg_pt'
703        self.certificate.start_level = 700
704        self.certificate.end_level = 800
705        self.student['studycourse'].current_level = 700
706        IWorkflowState(self.student).setState('school fee paid')
707        self.browser.open(self.login_path)
708        self.browser.getControl(name="form.login").value = self.student_id
709        self.browser.getControl(name="form.password").value = 'spwd'
710        self.browser.getControl("Login").click()
711        self.assertTrue(
712            'You logged in.' in self.browser.contents)
713        # Now students can add the current study level
714        self.browser.getLink("Study Course").click()
715        self.browser.getLink("Add course list").click()
716        self.assertMatches('...Add current level 700...',
717                           self.browser.contents)
718        self.browser.getControl("Create course list now").click()
719        # A level with no course ticket was created
720        self.assertEqual(self.student['studycourse']['700'].number_of_tickets, 0)
721        self.browser.getLink("700").click()
722        self.browser.getLink("Edit course list").click()
723        self.browser.getLink("here").click()
724        self.browser.getControl(name="form.course").value = ['COURSE1']
725        # Non-final year students can't add ticket with 51 credits
726        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
727        self.browser.getControl("Add course ticket").click()
728        self.assertMatches('...Total credits exceed 50...',
729                           self.browser.contents)
730        # Final year students can't add ticket with 52 credits ...
731        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
732        self.student['studycourse'].certificate.end_level = 700
733        self.browser.getControl("Add course ticket").click()
734        self.assertMatches('...Total credits exceed 51...',
735                           self.browser.contents)
736        # ... but with 51 credits
737        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
738        self.browser.getControl("Add course ticket").click()
739        self.assertMatches('...Successfully added COURSE1...',
740                           self.browser.contents)
741        # Non-final year special postgraduate students can't register
742        # course lists if their total credits are 51 and thus exceed 50 ...
743        self.student['studycourse'].certificate.end_level = 800
744        self.browser.getControl("Register course list").click()
745        self.assertMatches('...Maximum credits of 50 exceeded...',
746            self.browser.contents)
747        # ... but final year students can
748        self.student['studycourse'].certificate.end_level = 700
749        self.browser.getControl("Register course list").click()
750        self.assertMatches('...Course list has been registered...',
751            self.browser.contents)
752        self.assertEqual(self.student.state, 'courses registered')
753        return
754
755    def test_login(self):
756        # If suspended_comment is set this message will be flashed instead
757        self.student.suspended_comment = u'Aetsch baetsch!'
758        self.student.suspended = True
759        self.browser.open(self.login_path)
760        self.browser.getControl(name="form.login").value = self.student_id
761        self.browser.getControl(name="form.password").value = 'spwd'
762        self.browser.getControl("Login").click()
763        # Uniben does not display suspended_comment
764        self.assertMatches(
765            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
766            self.browser.contents)
767        self.student.suspended = False
768
769    def test_activate_deactivate_buttons(self):
770        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
771        self.browser.open(self.student_path)
772        self.browser.getLink("Deactivate").click()
773        self.assertTrue(
774            'Student account has been deactivated.' in self.browser.contents)
775        self.assertTrue(
776            'Base Data (account deactivated)' in self.browser.contents)
777        self.assertTrue(self.student.suspended)
778        self.browser.getLink("Activate").click()
779        self.assertTrue(
780            'Student account has been activated.' in self.browser.contents)
781        self.assertFalse(
782            'Base Data (account deactivated)' in self.browser.contents)
783        self.assertFalse(self.student.suspended)
784        # History messages have been added ...
785        self.browser.getLink("History").click()
786        # User is undisclosed
787        self.assertTrue(
788            'Student account deactivated<br />' in self.browser.contents)
789        self.assertTrue(
790            'Student account activated<br />' in self.browser.contents)
791        # ... and actions have been logged.
792        logfile = os.path.join(
793            self.app['datacenter'].storage, 'logs', 'students.log')
794        logcontent = open(logfile).read()
795        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
796                        'B1000000 - account deactivated' in logcontent)
797        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
798                        'B1000000 - account activated' in logcontent)
799
800    def test_manage_upload_fpm_file(self):
801        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
802        self.browser.open(self.manage_clearance_path)
803        image = open(SAMPLE_FPM, 'rb')
804        ctrl = self.browser.getControl(name='leftthumbprintupload')
805        file_ctrl = ctrl.mech_control
806        file_ctrl.add_file(image, filename='thumbprint.fpm')
807        self.browser.getControl(
808            name='upload_leftthumbprintupload').click()
809        self.assertTrue(
810            'File finger1.fpm uploaded.' in self.browser.contents)
811        self.assertTrue(
812            'http://localhost/app/students/B1000000/finger1.fpm'
813            in self.browser.contents)
814        self.browser.getControl(
815            name='delete_leftthumbprintupload').click()
816        self.assertTrue(
817            'finger1.fpm deleted'
818            in self.browser.contents)
819
820    def test_handle_clearance_by_co(self):
821        # Create clearance officer
822        self.app['users'].addUser('mrclear', 'mrclearsecret')
823        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
824        self.app['users']['mrclear'].title = 'Carlo Pitter'
825        department = self.app['faculties']['fac1']['dep1']
826        prmlocal = IPrincipalRoleManager(department)
827        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
828        notify(LocalRoleSetEvent(
829            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
830        IWorkflowState(self.student).setState('clearance requested')
831        # Login as clearance officer
832        self.browser.open(self.login_path)
833        self.browser.getControl(name="form.login").value = 'mrclear'
834        self.browser.getControl(name="form.password").value = 'mrclearsecret'
835        self.browser.getControl("Login").click()
836        self.assertMatches('...You logged in...', self.browser.contents)
837        # CO can view the student ...
838        self.browser.open(self.clearance_path)
839        self.assertEqual(self.browser.headers['Status'], '200 Ok')
840        self.assertEqual(self.browser.url, self.clearance_path)
841        # Clearance is disabled for this session for ug students ...
842        self.browser.open(self.clearance_path)
843        self.assertFalse('Clear student' in self.browser.contents)
844        self.browser.open(self.student_path + '/clear')
845        self.assertTrue('Clearance is disabled for this session'
846            in self.browser.contents)
847        # ... but not for
848        self.certificate.study_mode = 'pg_ft'
849        self.browser.open(self.clearance_path)
850        self.assertTrue('Clear student' in self.browser.contents)
851        self.browser.open(self.student_path + '/clear')
852        self.assertTrue('Student has been cleared' in self.browser.contents)
853
854    def test_transcripts(self):
855        studylevel = createObject(u'waeup.StudentStudyLevel')
856        studylevel.level = 100
857        studylevel.level_session = 2005
858        self.student['studycourse'].entry_mode = 'ug_ft'
859        self.student['studycourse'].addStudentStudyLevel(
860            self.certificate, studylevel)
861        studylevel2 = createObject(u'waeup.StudentStudyLevel')
862        studylevel2.level = 110
863        studylevel2.level_session = 2006
864        self.student['studycourse'].addStudentStudyLevel(
865            self.certificate, studylevel2)
866        # Add second course (COURSE has been added automatically)
867        courseticket = createObject('waeup.CourseTicket')
868        courseticket.code = 'ANYCODE'
869        courseticket.title = u'Any TITLE'
870        courseticket.credits = 13
871        courseticket.score = 66
872        courseticket.semester = 1
873        courseticket.dcode = u'ANYDCODE'
874        courseticket.fcode = u'ANYFCODE'
875        self.student['studycourse']['110']['COURSE2'] = courseticket
876        self.student['studycourse']['100']['COURSE1'].score = 55
877        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
878        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
879        # Get transcript data
880        td = self.student['studycourse'].getTranscriptData()
881        self.assertEqual(td[0][0]['level_key'], '100')
882        self.assertEqual(td[0][0]['sgpa'], 3.0)
883        self.assertEqual(td[0][0]['level'].level, 100)
884        self.assertEqual(td[0][0]['level'].level_session, 2005)
885        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
886        self.assertEqual(td[0][1]['level_key'], '110')
887        self.assertEqual(td[0][1]['sgpa'], 4.0)
888        self.assertEqual(td[0][1]['level'].level, 110)
889        self.assertEqual(td[0][1]['level'].level_session, 2006)
890        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
891        self.assertEqual(td[1], 3.57)
892        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
893        self.browser.open(self.student_path + '/studycourse/transcript')
894        self.assertEqual(self.browser.headers['Status'], '200 Ok')
895        self.assertTrue('Transcript' in self.browser.contents)
896        # Officers can open the pdf transcript
897        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
898        self.assertEqual(self.browser.headers['Status'], '200 Ok')
899        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
900        path = os.path.join(samples_dir(), 'transcript.pdf')
901        open(path, 'wb').write(self.browser.contents)
902        print "Sample PDF transcript.pdf written to %s" % path
Note: See TracBrowser for help on using the repository browser.