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

Last change on this file since 13107 was 13085, checked in by Henrik Bettermann, 10 years ago

Allow students to edit parent_email.

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