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

Last change on this file since 18103 was 18103, checked in by Henrik Bettermann, 3 days ago

Transcript officers see original transcript without watermark.

  • Property svn:keywords set to Id
File size: 71.7 KB
Line 
1## $Id: test_browser.py 18103 2025-07-01 12:45: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
21import pytz
22from time import time
23from datetime import datetime, timedelta, date
24from StringIO import StringIO
25from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
26from zope.securitypolicy.interfaces import IPrincipalRoleManager
27from zope.security.interfaces import Unauthorized
28from zope.component.hooks import setSite, clearSite
29from zope.component import getUtility, createObject
30from zope.interface import verify
31from zope.event import notify
32from waeup.kofa.authentication import LocalRoleSetEvent
33from waeup.kofa.app import University
34from waeup.kofa.university.faculty import Faculty
35from waeup.kofa.university.department import Department
36from waeup.kofa.students.tests.test_browser import StudentsFullSetup
37from waeup.kofa.students.accommodation import BedTicket
38from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
39from waeup.kofa.testing import FunctionalTestCase
40from waeup.kofa.browser.tests.test_pdf import samples_dir
41from waeup.kofa.interfaces import (
42    IExtFileStore, IFileStoreNameChooser)
43from waeup.kofa.students.interfaces import IStudentsUtils
44from waeup.kofa.tests.test_authentication import SECRET
45from waeup.uniben.testing import FunctionalLayer
46
47SAMPLE_FPM = os.path.join(os.path.dirname(__file__), 'sample.fpm')
48SAMPLE_PASSPORT = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
49
50class OfficerUITests(StudentsFullSetup):
51    # Tests for Student class views and pages
52
53    layer = FunctionalLayer
54
55    def test_jhl_idcard_officer(self):
56        # Create library officer
57        self.app['users'].addUser('mrlibrary', SECRET)
58        self.app['users']['mrlibrary'].email = 'library@foo.ng'
59        self.app['users']['mrlibrary'].title = 'Carlo Pitter'
60        prmglobal = IPrincipalRoleManager(self.app)
61        prmglobal.assignRoleToPrincipal(
62            'waeup.LibraryClearanceOfficer', 'mrlibrary')
63        prmglobal.assignRoleToPrincipal(
64            'waeup.StudentsOfficer', 'mrlibrary')
65        self.browser.open(self.login_path)
66        self.browser.getControl(name="form.login").value = 'mrlibrary'
67        self.browser.getControl(name="form.password").value = SECRET
68        self.browser.getControl("Login").click()
69        self.assertMatches('...You logged in...', self.browser.contents)
70        self.browser.open(self.student_path)
71        self.assertFalse('JHL' in self.browser.contents)
72        self.browser.getLink("Switch library access").click()
73        self.assertTrue('Library access enabled' in self.browser.contents)
74        self.assertTrue('JHL' in self.browser.contents)
75        self.browser.getLink("Download JHL Id Card").click()
76        self.assertEqual(self.browser.headers['Status'], '200 Ok')
77        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
78        path = os.path.join(samples_dir(), 'jhl_idcard_officer.pdf')
79        open(path, 'wb').write(self.browser.contents)
80        print "Sample PDF jhl_idcard_officer.pdf written to %s" % path
81
82class StudentUITests(StudentsFullSetup):
83    """Tests for customized student class views and pages
84    """
85
86    layer = FunctionalLayer
87
88    def setUp(self):
89        super(StudentUITests, self).setUp()
90
91    def test_next_session_allowed(self):
92        # Let's see if next_session_allowed works as expected
93        # A, ug_ft, 100
94        IWorkflowState(self.student).setState('returning')
95        self.assertTrue(self.student['studycourse'].next_session_allowed)
96        # Uniben special PG programmes have the same workflow
97        # as UG students
98        self.certificate.study_mode = 'special_pg_pt'
99        self.assertTrue(self.student['studycourse'].next_session_allowed)
100        IWorkflowState(self.student).setState('school fee paid')
101        self.assertFalse(self.student['studycourse'].next_session_allowed)
102        # Now we convert the certificate into a 'regular
103        # postgraduate certificate ...
104        self.certificate.study_mode = 'pg_ft'
105        # ... and voila next session registration is allowed
106        self.assertTrue(self.student['studycourse'].next_session_allowed)
107
108    def test_manage_access(self):
109        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
110        # The student created in the base package is a ug student
111        self.browser.open(self.manage_clearance_path)
112        self.assertMatches('...First Sitting Record...',
113                           self.browser.contents)
114        # There is no pg field in the clearance form
115        self.assertFalse('Second Higher Education Record'
116            in self.browser.contents)
117        # Now we change the study mode ...
118        self.certificate.study_mode = 'pg_ft'
119        self.browser.open(self.clearance_path)
120        # ... and additional pg clearance fields appears
121        self.assertMatches('...Second Higher Education Record...',
122                           self.browser.contents)
123        # But also fields from the ug form are displayed
124        self.assertMatches('...First Sitting Record...',
125                           self.browser.contents)
126        # The same holds for Uniben's special pg students
127        self.certificate.study_mode = 'special_pg_pt'
128        self.browser.open(self.clearance_path)
129        self.assertMatches('...Second Higher Education Record...',
130                           self.browser.contents)
131        self.assertMatches('...First Sitting Record...',
132                           self.browser.contents)
133        # We want to see the signature fields.
134        IWorkflowState(self.student).setState('returning')
135        self.browser.open(self.student_path + '/clearance_slip.pdf')
136        self.assertEqual(self.browser.headers['Status'], '200 Ok')
137        self.assertEqual(self.browser.headers['Content-Type'],
138                         'application/pdf')
139        path = os.path.join(samples_dir(), 'clearance_slip.pdf')
140        open(path, 'wb').write(self.browser.contents)
141        print "Sample PDF clearance_slip.pdf written to %s" % path
142
143    def test_student_access(self):
144        # Students can edit clearance data
145        IWorkflowState(self.student).setState('clearance started')
146        self.student.nationality = u'NG'
147        self.student['studycourse'].entry_mode = 'ug_ft'
148        file_store = getUtility(IExtFileStore)
149        self.browser.open(self.login_path)
150        self.browser.getControl(name="form.login").value = self.student_id
151        self.browser.getControl(name="form.password").value = 'spwd'
152        self.browser.getControl("Login").click()
153        self.browser.open(self.edit_clearance_path)
154        # UG students can't edit date_of_birth, nationality and lga
155        self.assertFalse('form.date_of_birth' in self.browser.contents)
156        self.assertFalse('form.nationality' in self.browser.contents)
157        self.assertFalse('form.lga' in self.browser.contents)
158        # Clearance can only be requested if all required documents
159        # have been uploaded.
160        self.browser.getControl("Save and request clearance").click()
161
162        self.assertTrue('No red background passport picture uploaded'
163            in self.browser.contents)
164        passport = 'My passport picture'
165        file_id = IFileStoreNameChooser(self.student).chooseName(
166            attr="passport.jpg")
167        image = open(SAMPLE_PASSPORT, 'rb')
168        file_store.createFile(file_id, image)
169        self.browser.open(self.edit_clearance_path)
170        self.browser.getControl("Save and request clearance").click()
171
172        self.assertTrue('No birth certificate uploaded'
173            in self.browser.contents)
174        birth_certificate = 'My birth certificate'
175        file_id = IFileStoreNameChooser(self.student).chooseName(
176            attr="birth_certificate.jpg")
177        file_store.createFile(file_id, StringIO(birth_certificate))
178        self.browser.open(self.edit_clearance_path)
179        self.browser.getControl("Save and request clearance").click()
180
181        self.assertTrue('No guarantor/referee letter uploaded'
182            in self.browser.contents)
183        ref_let = 'My ref let'
184        file_id = IFileStoreNameChooser(self.student).chooseName(
185            attr="ref_let.jpg")
186        file_store.createFile(file_id, StringIO(ref_let))
187        self.browser.open(self.edit_clearance_path)
188        self.browser.getControl("Save and request clearance").click()
189
190        self.assertTrue('No acceptance letter uploaded'
191            in self.browser.contents)
192        acc_let = 'My acc let'
193        file_id = IFileStoreNameChooser(self.student).chooseName(
194            attr="acc_let.jpg")
195        file_store.createFile(file_id, StringIO(acc_let))
196        self.browser.open(self.edit_clearance_path)
197        self.browser.getControl("Save and request clearance").click()
198
199        self.assertTrue('No first sitting result uploaded'
200            in self.browser.contents)
201        fst_sit_scan = 'My first sitting result'
202        file_id = IFileStoreNameChooser(self.student).chooseName(
203            attr="fst_sit_scan.jpg")
204        file_store.createFile(file_id, StringIO(fst_sit_scan))
205        self.browser.open(self.edit_clearance_path)
206        self.browser.getControl("Save and request clearance").click()
207
208        self.assertTrue('No affidavit of non-membership of secret cults uploaded'
209            in self.browser.contents)
210        secr_cults = 'My non-membership scan'
211        file_id = IFileStoreNameChooser(self.student).chooseName(
212            attr="secr_cults.jpg")
213        file_store.createFile(file_id, StringIO(secr_cults))
214        self.browser.open(self.edit_clearance_path)
215        self.browser.getControl("Save and request clearance").click()
216
217        self.assertTrue('No O level results scratch card uploaded'
218            in self.browser.contents)
219        olevel_sc = 'My O level results scratch card'
220        file_id = IFileStoreNameChooser(self.student).chooseName(
221            attr="olevel_sc.jpg")
222        file_store.createFile(file_id, StringIO(olevel_sc))
223
224        # Clearance invitation letter is not yet available
225        self.browser.open(self.clearance_path)
226        self.assertFalse('invitation slip' in self.browser.contents)
227        self.browser.open(self.student_path + '/clearance_invitation_slip.pdf')
228        self.assertTrue('Forbidden' in self.browser.contents)
229        self.browser.open(self.edit_clearance_path)
230        self.browser.getControl("Save and request clearance").click()
231        self.assertTrue('Clearance has been requested'
232            in self.browser.contents)
233        # Now student can export physical_clearance_slip
234        self.app['configuration'].name = u'University of Benin'
235        self.student.physical_clearance_date = u'January 5th, 2015'
236        self.browser.getLink("Clearance Data").click()
237        self.browser.getLink("Download clearance invitation slip").click()
238        self.assertEqual(self.browser.headers['Status'], '200 Ok')
239        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
240        path = os.path.join(samples_dir(), 'clearance_invitation_slip.pdf')
241        open(path, 'wb').write(self.browser.contents)
242        print "Sample PDF clearance_invitation_slip.pdf written to %s" % path
243
244    def test_medical_examination_slip(self):
245        # Student can export medical_examination_slip
246        self.app['configuration']['2004'].medical_clearance_fee = 1000.0
247        self.app['configuration'].name = u'University of Benin'
248        IWorkflowState(self.student).setState('cleared')
249        self.student.perm_address = u'My Address in Nigeria'
250        self.student.physical_clearance_date = u'January 5th, 2015'
251        self.browser.open(self.login_path)
252        self.browser.getControl(name="form.login").value = self.student_id
253        self.browser.getControl(name="form.password").value = 'spwd'
254        self.browser.getControl("Login").click()
255        self.browser.open(self.clearance_path)
256        self.browser.getLink("Download medical examination slip").click()
257        self.assertTrue('Please pay medical clearance fee first' in
258            self.browser.contents)
259        self.browser.open(self.payments_path)
260        self.browser.getLink("Add current session payment ticket").click()
261        self.browser.getControl(name="form.p_category").value = ['medical_clearance']
262        self.browser.getControl("Create ticket").click()
263        p_ticket = self.student['payments'].values()[0]
264        p_ticket.approveStudentPayment()
265        self.browser.open(self.clearance_path)
266        self.browser.getLink("Download medical examination slip").click()
267        self.assertEqual(self.browser.headers['Status'], '200 Ok')
268        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
269        path = os.path.join(samples_dir(), 'medical_examination_slip.pdf')
270        open(path, 'wb').write(self.browser.contents)
271        print "Sample PDF medical_examination_slip.pdf written to %s" % path
272
273        # Students can open the personal edit page and see the parent_email field.
274        self.browser.open(self.student_path + '/edit_personal')
275        self.assertTrue('parent_email' in self.browser.contents)
276
277    def test_examination_schedule_slip(self):
278        self.student.flash_notice = u'My Examination Date'
279        self.browser.open(self.login_path)
280        self.browser.getControl(name="form.login").value = self.student_id
281        self.browser.getControl(name="form.password").value = 'spwd'
282        self.browser.getControl("Login").click()
283        self.browser.open(self.student_path)
284        self.browser.getLink("Download examination schedule slip").click()
285        self.assertEqual(self.browser.headers['Status'], '200 Ok')
286        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
287        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
288        open(path, 'wb').write(self.browser.contents)
289        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
290        # If flash_notive does not contain exam' the button does not show up.
291        self.student.flash_notice = u'anything'
292        self.browser.open(self.student_path)
293        self.assertFalse('examination schedule slip' in self.browser.contents)
294
295    def test_jhl_idcard(self):
296        IWorkflowState(self.student).setState('returning')
297        self.browser.open(self.login_path)
298        self.browser.getControl(name="form.login").value = self.student_id
299        self.browser.getControl(name="form.password").value = 'spwd'
300        self.browser.getControl("Login").click()
301        self.assertFalse('JHL' in self.browser.contents)
302        self.student.library = True
303        self.browser.open(self.student_path)
304        self.assertTrue('JHL' in self.browser.contents)
305        self.browser.getLink("Download JHL Id Card").click()
306        self.assertEqual(self.browser.headers['Status'], '200 Ok')
307        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
308        path = os.path.join(samples_dir(), 'jhl_idcard_student.pdf')
309        open(path, 'wb').write(self.browser.contents)
310        print "Sample PDF jhl_idcard_student.pdf written to %s" % path
311        self.assertTrue(self.student.library)
312        IWorkflowInfo(self.student).fireTransition('pay_school_fee')
313        self.assertFalse(self.student.library)
314
315    def test_jupeb_result_slip(self):
316        self.student.flash_notice = u'My JUPEB results'
317        self.browser.open(self.login_path)
318        self.browser.getControl(name="form.login").value = self.student_id
319        self.browser.getControl(name="form.password").value = 'spwd'
320        self.browser.getControl("Login").click()
321        self.assertFalse('JUPEB result slip' in self.browser.contents)
322        # Create JUPEB faculty
323        cert = createObject('waeup.Certificate')
324        cert.code = u'xyz'
325        cert.study_mode = 'found'
326        self.app['faculties']['JUPEB123'] = Faculty(code=u'JUPEB123')
327        self.app['faculties']['JUPEB123']['dep1'] = Department(code=u'dep1')
328        self.app['faculties']['JUPEB123']['dep1'].certificates.addCertificate(cert)
329        self.student['studycourse'].certificate = cert
330        self.browser.open(self.student_path)
331        self.browser.getLink("Download JUPEB result slip").click()
332        self.assertEqual(self.browser.headers['Status'], '200 Ok')
333        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
334        path = os.path.join(samples_dir(), 'jupeb_result_slip.pdf')
335        open(path, 'wb').write(self.browser.contents)
336        print "Sample PDF jupeb_result_slip.pdf written to %s" % path
337        self.student.flash_notice = u''
338        self.browser.open(self.student_path)
339        self.assertFalse('JUPEB result slip' in self.browser.contents)
340
341    def test_manage_payments(self):
342        # Add missing configuration data
343        self.app['configuration']['2004'].gown_fee = 150.0
344        self.app['configuration']['2004'].transfer_fee = 90.0
345        #self.app['configuration']['2004'].clearance_fee = 120.0
346        self.app['configuration']['2004'].booking_fee = 150.0
347        self.app['configuration']['2004'].maint_fee = 180.0
348
349        # Managers can add online payment tickets
350        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
351        self.browser.open(self.payments_path)
352        self.browser.getLink("Add current session payment ticket").click()
353        self.browser.getControl(name="form.p_category").value = ['schoolfee']
354        self.browser.getControl("Create ticket").click()
355        self.assertMatches('...Amount could not be determined...',
356                           self.browser.contents)
357        IWorkflowState(self.student).setState('cleared')
358        self.student.nationality = u'NG'
359        self.browser.open(self.payments_path + '/addop')
360        self.browser.getControl(name="form.p_category").value = ['schoolfee']
361        self.browser.getControl("Create ticket").click()
362        self.assertMatches('...ticket created...',
363                           self.browser.contents)
364        self.assertMatches('...Amount Authorized...',
365                           self.browser.contents)
366        # Managers can open payment slip because we did not proceed to
367        # any payment gateway
368        self.assertFalse('Download payment slip' in self.browser.contents)
369        # Set ticket paid
370        ticket = self.student['payments'].items()[0][1]
371        ticket.p_state = 'paid'
372        self.browser.open(self.payments_path + '/addop')
373        self.browser.getControl(name="form.p_category").value = ['schoolfee']
374        self.browser.getControl("Create ticket").click()
375        self.assertMatches('...This type of payment has already been made...',
376                           self.browser.contents)
377        # Remove all payments so that we can add a school fee payment again
378        keys = [i for i in self.student['payments'].keys()]
379        for payment in keys:
380            del self.student['payments'][payment]
381        self.browser.open(self.payments_path + '/addop')
382        self.browser.getControl(name="form.p_category").value = ['schoolfee']
383        self.browser.getControl("Create ticket").click()
384        self.assertMatches('...ticket created...',
385                           self.browser.contents)
386        schoolfee_ticket = self.student['payments'].values()[0]
387        self.browser.open(self.payments_path + '/addop')
388        self.browser.getControl(name="form.p_category").value = ['gown']
389        self.browser.getControl("Create ticket").click()
390        self.assertMatches('...ticket created...',
391                           self.browser.contents)
392        self.browser.open(self.payments_path + '/addop')
393        self.browser.getControl(name="form.p_category").value = ['clearance']
394        self.browser.getControl("Create ticket").click()
395        self.assertMatches('...ticket created...',
396                           self.browser.contents)
397        self.assertTrue('<span>60000.0</span>' in self.browser.contents)
398        self.browser.open(self.payments_path + '/addop')
399        self.browser.getControl(name="form.p_category").value = ['schoolfee']
400        self.browser.getControl("Create ticket").click()
401        self.assertMatches('...ticket created...',
402                           self.browser.contents)
403        # In state returning we can add a new school fee ticket since
404        # p_session and p_level is different
405        IWorkflowState(self.student).setState('returning')
406        self.browser.open(self.payments_path + '/addop')
407        self.browser.getControl(name="form.p_category").value = ['schoolfee']
408        self.browser.getControl("Create ticket").click()
409        # Uups, we forgot to add a session configuration for next session
410        self.assertMatches('...Session configuration object is not...',
411                           self.browser.contents)
412        configuration = createObject('waeup.SessionConfiguration')
413        configuration.academic_session = 2005
414        self.app['configuration'].addSessionConfiguration(configuration)
415        self.browser.open(self.payments_path + '/addop')
416        self.browser.getControl(name="form.p_category").value = ['schoolfee']
417        self.browser.getControl("Create ticket").click()
418
419        #self.assertMatches('...You have not yet paid your current/active session...',
420        #                   self.browser.contents)
421        ## Ok, let's pay the first schoolfee ticket.
422        #schoolfee_ticket.approve()
423        #self.browser.open(self.payments_path + '/addop')
424        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
425        #self.browser.getControl("Create ticket").click()
426
427        self.assertMatches('...ticket created...',
428                           self.browser.contents)
429        # In state admitted school fee can't be determined
430        IWorkflowState(self.student).setState('admitted')
431        self.browser.open(self.payments_path + '/addop')
432        self.browser.getControl(name="form.p_category").value = ['schoolfee']
433        self.browser.getControl("Create ticket").click()
434        self.assertMatches('...Amount could not be determined...',
435                           self.browser.contents)
436
437        # If the session configuration doesn't exist an error message will
438        # be shown. No other requirement is being checked.
439        del self.app['configuration']['2004']
440        self.browser.open(self.payments_path)
441        self.browser.getLink("Add current session payment ticket").click()
442        self.browser.getControl("Create ticket").click()
443        self.assertMatches('...Session configuration object is not...',
444                           self.browser.contents)
445
446    def test_student_course_registration(self):
447        # Uniben students see grade instead of score on all level pages
448        # and on course ticket page.
449        IWorkflowState(self.student).setState('school fee paid')
450        self.browser.open(self.login_path)
451        self.browser.getControl(name="form.login").value = self.student_id
452        self.browser.getControl(name="form.password").value = 'spwd'
453        self.browser.getControl("Login").click()
454        # Now students can add the current study level
455        self.browser.getLink("Study Course").click()
456        self.browser.getLink("Add course list").click()
457        self.assertMatches('...Add current level 100 (Year 1)...',
458                           self.browser.contents)
459        self.browser.getControl("Create course list now").click()
460        # A level with one course ticket was created
461        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
462        self.student['studycourse']['100']['COURSE1'].score = 55
463        self.browser.getLink("100").click()
464        # GPA has been properly calculated
465        self.assertEqual(self.student['studycourse']['100'].gpa_params[0], 3.0)
466        # Score is not shown but grade
467        self.assertTrue('<th>Grade</th>' in self.browser.contents)
468        self.assertFalse('<th>Score</th>' in self.browser.contents)
469        self.browser.getLink("Edit course list").click()
470        self.assertTrue('<th>Grade</th>' in self.browser.contents)
471        self.assertFalse('<th>Score</th>' in self.browser.contents)
472        self.browser.getLink("COURSE1").click()
473        self.assertFalse('Score' in self.browser.contents)
474        # Students can open the special Uniben pdf course result slip
475        self.browser.open(self.student_path + '/studycourse/100')
476        self.browser.getLink("Download course result slip").click()
477        self.assertEqual(self.browser.headers['Status'], '200 Ok')
478        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
479        # Even if course is mandatory, students can remove the course
480        self.student['studycourse']['100']['COURSE1'].mandatory = True
481        self.browser.open(self.student_path + '/studycourse/100')
482        self.browser.getLink("Edit course list").click()
483        ctrl = self.browser.getControl(name='val_id')
484        ctrl.getControl(value='COURSE1').selected = True
485        self.browser.getControl("Remove selected", index=0).click()
486        self.assertTrue('Successfully removed' in self.browser.contents)
487        # Students can open the customized pdf course registration slip
488        # if they have registered their course list
489        self.browser.open(
490            self.student_path + '/studycourse/100/course_registration_slip.pdf')
491        self.assertTrue('Forbidden' in self.browser.contents)
492        IWorkflowState(self.student).setState('courses registered')
493        self.browser.open(self.student_path + '/studycourse/100')
494        self.browser.getLink("Download course registration slip").click()
495        self.assertEqual(self.browser.headers['Status'], '200 Ok')
496        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
497        path = os.path.join(samples_dir(), 'course_registration_slip.pdf')
498        open(path, 'wb').write(self.browser.contents)
499        print "Sample PDF course_registration_slip.pdf written to %s" % path
500       # Students can always download pdf course result slip
501        self.browser.open(
502            self.student_path + '/studycourse/100/course_result_slip.pdf')
503        self.assertEqual(self.browser.headers['Status'], '200 Ok')
504        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
505        path = os.path.join(samples_dir(), 'course_result_slip.pdf')
506        open(path, 'wb').write(self.browser.contents)
507        print "Sample PDF course_result_slip.pdf written to %s" % path
508
509    def test_get_returning_data(self):
510        # Student is in level 100, session 2004 with verdict A
511        utils = getUtility(IStudentsUtils)
512        self.assertEqual(utils.getReturningData(self.student),(2005, 200))
513        self.student['studycourse'].current_verdict = 'C'
514        self.assertEqual(utils.getReturningData(self.student),(2005, 110))
515        self.student['studycourse'].current_verdict = 'D'
516        self.assertEqual(utils.getReturningData(self.student),(2005, 100))
517        return
518
519    def test_set_returning_data(self):
520        # Student is in level 100, session 2004 with verdict A
521        utils = getUtility(IStudentsUtils)
522
523        utils.setReturningData(self.student)
524        self.assertEqual(self.student['studycourse'].current_session, 2005)
525        self.assertEqual(self.student['studycourse'].current_level, 200)
526
527        self.student['studycourse'].current_session = 2004
528        self.student['studycourse'].current_level = 100
529        self.student['studycourse'].current_verdict = 'C'
530        utils.setReturningData(self.student)
531        self.assertEqual(self.student['studycourse'].current_session, 2005)
532        self.assertEqual(self.student['studycourse'].current_level, 110)
533
534        self.student['studycourse'].current_session = 2004
535        self.student['studycourse'].current_level = 100
536        self.student['studycourse'].current_verdict = 'D'
537        utils.setReturningData(self.student)
538        self.assertEqual(self.student['studycourse'].current_session, 2005)
539        self.assertEqual(self.student['studycourse'].current_level, 100)
540        return
541
542    def test_set_payment_details(self):
543        self.app['configuration']['2004'].gown_fee = 150.0
544        self.app['configuration']['2004'].transfer_fee = 90.0
545        self.app['configuration']['2004'].booking_fee = 150.0
546        self.app['configuration']['2004'].maint_fee = 180.0
547
548        configuration = createObject('waeup.SessionConfiguration')
549        configuration.academic_session = 2000
550        self.app['configuration'].addSessionConfiguration(configuration)
551        configuration2 = createObject('waeup.SessionConfiguration')
552        configuration2.academic_session = 2002
553        self.app['configuration'].addSessionConfiguration(configuration2)
554        configuration3 = createObject('waeup.SessionConfiguration')
555        configuration3.academic_session = 2003
556        self.app['configuration'].addSessionConfiguration(configuration3)
557        configuration4 = createObject('waeup.SessionConfiguration')
558        configuration4.academic_session = 2005
559        self.app['configuration'].addSessionConfiguration(configuration4)
560        utils = getUtility(IStudentsUtils)
561        self.student['studycourse'].entry_session = 2002
562        self.student.nationality = u'NG'
563
564        error, payment = utils.setPaymentDetails('schoolfee',
565            self.student, None, None, None)
566        self.assertEqual(payment, None)
567        # Student is in state 'created' and can thus not pay.
568        self.assertTrue(u'Amount could not be determined.' in error)
569
570        # Previous session must be valid.
571        error, payment = utils.setPaymentDetails('schoolfee',
572            self.student, 2000, 300, None)
573        self.assertEqual(payment, None)
574        self.assertTrue(u'The previous session must not fall below' in error)
575        error, payment = utils.setPaymentDetails('schoolfee',
576            self.student, 2005, 300, None)
577        self.assertEqual(payment, None)
578        self.assertTrue(u'This is not a previous session' in error)
579
580        # Previous session schoolfee payment; fresh and returning
581        # are distinguished by their entry_level
582        error, payment = utils.setPaymentDetails('schoolfee',
583            self.student, 2002, 300, None)
584        self.assertEqual(payment.amount_auth, 63000.0)
585        self.assertEqual(payment.p_session, 2002)
586        self.assertEqual(payment.p_level, 300)
587        self.assertFalse(payment.p_current)
588        error, payment = utils.setPaymentDetails('schoolfee',
589            self.student, 2003, 300, None)
590        self.assertEqual(payment.amount_auth, 26800.0)
591        self.assertEqual(payment.p_session, 2003)
592        self.assertEqual(payment.p_level, 300)
593        self.assertFalse(payment.p_current)
594
595        # Current schoolfee payment; fresh and returning
596        # are distinguished by their state
597        IWorkflowState(self.student).setState('cleared')
598        error, payment = utils.setPaymentDetails('schoolfee',
599            self.student, None, None, None)
600        self.assertEqual(payment.p_level, 100)
601        self.assertEqual(payment.p_session, 2004)
602        self.assertEqual(payment.amount_auth, 63000.0)
603        self.assertEqual(payment.p_item, u'CERT1')
604        self.assertEqual(error, None)
605        self.assertTrue(payment.p_current)
606
607        # Add additional fee
608        self.certificate.school_fee_3 = 1000.0
609        error, payment = utils.setPaymentDetails('schoolfee',
610            self.student, None, None, None)
611        self.assertEqual(payment.amount_auth, 64000.0)
612        self.assertEqual(error, None)
613        self.assertTrue(payment.p_current)
614
615        # Add penalty fee ...
616        # ... for cleared
617        self.app['configuration']['2004'].penalty_ug_ft = 99.0
618        # ... for returning
619        self.app['configuration']['2005'].penalty_ug_ft = 88.0
620        error, payment = utils.setPaymentDetails('schoolfee',
621            self.student, None, None, None)
622        self.assertEqual(payment.amount_auth, 64099.0)
623
624        IWorkflowState(self.student).setState('returning')
625
626        #error, payment = utils.setPaymentDetails('schoolfee',
627        #    self.student, None, None, None)
628        #self.assertTrue(
629        #    u'You have not yet paid your current/active session.' in error)
630        ## Ok, that means we have to add paid payment ticket first.
631        #payment = createObject('waeup.StudentOnlinePayment')
632        #payment.p_category = u'schoolfee'
633        #payment.p_session = self.student.current_session
634        #payment.p_item = u'My Certificate'
635        #payment.p_id = u'anyid'
636        #payment.p_state = u'paid'
637        #self.student['payments']['anykey'] = payment
638
639        error, payment = utils.setPaymentDetails('schoolfee',
640            self.student, None, None, None)
641        self.assertEqual(payment.p_level, 200)
642        self.assertEqual(payment.p_session, 2005)
643        self.assertEqual(payment.amount_auth, 26888.0)
644        self.assertEqual(payment.p_item, u'CERT1')
645        self.assertEqual(error, None)
646
647        # Old returning students may pay less.
648        #self.certificate.school_fee_2 = 50000.0
649        #self.certificate.custom_float_2 = 30000.0
650        error, payment = utils.setPaymentDetails(
651            'schoolfee', self.student, None, None, None)
652        self.assertEqual(payment.amount_auth, 26888.0)
653
654        # Staff members pay less.
655        self.student.is_staff = True
656        error, payment = utils.setPaymentDetails('schoolfee',
657            self.student, None, None, None)
658        self.assertEqual(payment.p_level, 200)
659        self.assertEqual(payment.p_session, 2005)
660        self.assertEqual(payment.amount_auth, 13488.0)
661        self.assertEqual(payment.p_item, u'CERT1')
662        self.assertEqual(error, None)
663
664        # Foreigners pay more.
665        IWorkflowState(self.student).setState('cleared')
666        self.student.is_staff = False
667        self.student.nationality = u'DE'
668        error, payment = utils.setPaymentDetails(
669            'schoolfee', self.student, None, None, None)
670        self.assertEqual(payment.p_level, 100)
671        self.assertEqual(payment.p_session, 2004)
672        self.assertEqual(payment.amount_auth, 301099.0)
673        self.assertEqual(payment.p_item, u'CERT1')
674        self.assertEqual(error, None)
675        IWorkflowState(self.student).setState('returning')
676        self.student.is_staff = False
677        error, payment = utils.setPaymentDetails(
678            'schoolfee', self.student, None, None, None)
679        self.assertEqual(payment.p_level, 200)
680        self.assertEqual(payment.p_session, 2005)
681        self.assertEqual(payment.amount_auth, 277088.0)
682        self.assertEqual(payment.p_item, u'CERT1')
683        self.assertEqual(error, None)
684
685        # In Uniben students can pay school fee in all states no matter
686        # if they are ug or pg students.
687        # diabled on 02/10/2017, see ticket 1108
688        #IWorkflowState(self.student).setState('school fee paid')
689        #self.student.is_staff = False
690        #self.student.nationality = u'NG'
691        #self.certificate.school_fee_2 = 10000.0
692        #error, payment = utils.setPaymentDetails(
693        #    'schoolfee', self.student, None, None, None)
694        #self.assertEqual(payment.p_level, None)
695        #self.assertEqual(payment.p_session, 2005)
696        #self.assertEqual(payment.amount_auth, 10088.0)
697        #self.assertEqual(payment.p_item, u'CERT1')
698        #self.assertEqual(error, None)
699        #IWorkflowState(self.student).setState('courses registered')
700        #self.certificate.study_mode = 'special_pg_pt'
701        #error, payment = utils.setPaymentDetails(
702        #    'schoolfee', self.student, None, None, None)
703        #self.assertEqual(payment.p_level, None)
704        #self.assertEqual(payment.p_session, 2005)
705        #self.assertEqual(payment.amount_auth, 10000.0)
706        #self.assertEqual(payment.p_item, u'CERT1')
707        #self.assertEqual(error, None)
708        #IWorkflowState(self.student).setState('courses validated')
709        #error, payment = utils.setPaymentDetails(
710        #    'schoolfee', self.student, None, None, None)
711        #self.assertEqual(payment.p_level, None)
712        #self.assertEqual(payment.p_session, 2005)
713        #self.assertEqual(payment.amount_auth, 10000.0)
714        #self.assertEqual(payment.p_item, u'CERT1')
715        #self.assertEqual(error, None)
716
717        error, payment = utils.setPaymentDetails('clearance',
718            self.student, None, None, None)
719        self.assertEqual(payment.p_level, 100)
720        self.assertEqual(payment.p_session, 2004)
721        self.assertEqual(payment.amount_auth, 60000.0)
722        self.assertEqual(payment.p_item, u'CERT1')
723        self.assertEqual(error, None)
724
725        error, payment = utils.setPaymentDetails('gown',
726            self.student, None, None, None)
727        self.assertEqual(payment.p_level, 100)
728        self.assertEqual(payment.p_session, 2004)
729        self.assertEqual(payment.amount_auth, 150.0)
730        self.assertEqual(payment.p_item, u'')
731        self.assertEqual(error, None)
732
733        bedticket = BedTicket()
734        bedticket.booking_session = 2004
735        bedticket.bed_type = u'any bed type'
736        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
737        bedticket.bed_coordinates = u'My bed coordinates'
738        self.student['accommodation'].addBedTicket(bedticket)
739
740        error, payment = utils.setPaymentDetails('hostel_maintenance',
741            self.student, None, None, None)
742        self.assertEqual(payment.p_level, 100)
743        self.assertEqual(payment.p_session, 2004)
744        self.assertEqual(payment.amount_auth, 876.0)
745        self.assertEqual(payment.p_item, u'My bed coordinates')
746        self.assertEqual(error, None)
747
748        #self.certificate.study_mode = 'ug_ft'
749        #error, payment = utils.setPaymentDetails('bed_allocation',
750        #    self.student, None, None, None)
751        #self.assertTrue(u'Amount could not be determined.' in error)
752        #self.student['studycourse'].current_session = 2004
753        #self.student['studycourse'].entry_session = 2004
754        #self.student['studycourse'].current_level = 100
755        #error, payment = utils.setPaymentDetails('bed_allocation',
756        #    self.student, None, None, None)
757        #self.assertEqual(payment.p_level, 100)
758        #self.assertEqual(payment.p_session, 2004)
759        #self.assertEqual(payment.amount_auth, 650.0) # plus 500 student union
760        #self.assertEqual(payment.p_item, u'regular_male_fr')
761        #self.assertEqual(error, None)
762
763        self.certificate.study_mode = 'pg_ft'
764        error, payment = utils.setPaymentDetails('bed_allocation',
765            self.student, None, None, None)
766        self.assertEqual(error, u'Select your favoured hostel first.')
767        self.student['accommodation'].desired_hostel = u'no'
768        error, payment = utils.setPaymentDetails('bed_allocation',
769            self.student, None, None, None)
770        self.assertEqual(payment.p_level, 100)
771        self.assertEqual(payment.p_session, 2004)
772        self.assertEqual(payment.amount_auth, 1150.0)
773        self.assertEqual(payment.p_item, u'pg_male_all')
774        self.assertEqual(error, None)
775
776        #error, payment = utils.setPaymentDetails('hostel_application',
777        #    self.student, None, None, None)
778        #self.assertEqual(payment.p_level, 100)
779        #self.assertEqual(payment.p_session, 2004)
780        #self.assertEqual(payment.amount_auth, 1000.0)
781        #self.assertEqual(payment.p_item, u'')
782        #self.assertEqual(error, None)
783
784        #payment.approve()
785        #self.student['payments'][payment.p_id] = payment
786
787        #error, payment = utils.setPaymentDetails('tempmaint_1',
788        #    self.student, None, None, None)
789        #self.assertEqual(payment.p_level, 100)
790        #self.assertEqual(payment.p_session, 2004)
791        #self.assertEqual(payment.amount_auth, 8150.0)
792        #self.assertEqual(payment.p_item, u'Hall 1-4 M/F Ekehuan')
793        #self.assertEqual(error, None)
794
795        #error, payment = utils.setPaymentDetails('tempmaint_2',
796        #    self.student, None, None, None)
797        #self.assertEqual(payment.p_level, 100)
798        #self.assertEqual(payment.p_session, 2004)
799        #self.assertEqual(payment.amount_auth, 12650.0)
800        #self.assertEqual(payment.p_item, u'Hall 5 M/F')
801        #self.assertEqual(error, None)
802
803        #error, payment = utils.setPaymentDetails('tempmaint_3',
804        #    self.student, None, None, None)
805        #self.assertEqual(payment.p_level, 100)
806        #self.assertEqual(payment.p_session, 2004)
807        #self.assertEqual(payment.amount_auth, 9650.0)
808        #self.assertEqual(payment.p_item, u'Clinical Hostel')
809        #self.assertEqual(error, None)
810
811        error, payment = utils.setPaymentDetails('transfer',
812            self.student, None, None, None)
813        self.assertEqual(payment.p_level, 100)
814        self.assertEqual(payment.p_session, 2004)
815        self.assertEqual(payment.amount_auth, 90.0)
816        self.assertEqual(payment.p_item, u'')
817        self.assertEqual(error, None)
818        return
819
820    def test_set_payment_details_jupeb(self):
821        # JUPEB students can pay by instalments
822        utils = getUtility(IStudentsUtils)
823        self.certificate.study_mode = 'found'
824        IWorkflowState(self.student).setState('cleared')
825        error, payment = utils.setPaymentDetails(
826            'schoolfee', self.student, None, None, None)
827        self.assertEqual(payment.p_level, 100)
828        self.assertEqual(payment.p_session, 2004)
829        self.assertEqual(payment.amount_auth, 220000.0)
830        self.assertEqual(payment.p_item, u'CERT1')
831        self.assertEqual(error, None)
832        error, payment = utils.setPaymentDetails(
833            'schoolfee_1', self.student, None, None, None)
834        self.assertEqual(payment.p_level, 100)
835        self.assertEqual(payment.p_session, 2004)
836        self.assertEqual(payment.amount_auth, 110000.0)
837        self.assertEqual(payment.p_item, u'CERT1')
838        self.assertEqual(error, None)
839        return
840        error, payment = utils.setPaymentDetails(
841            'secondinstall', self.student, None, None, None)
842        self.assertEqual(payment.p_level, 100)
843        self.assertEqual(payment.p_session, 2004)
844        self.assertEqual(payment.amount_auth, 110000.0)
845        self.assertEqual(payment.p_item, u'CERT1')
846        self.assertEqual(error, None)
847
848    def test_edit_level_by_co(self):
849        # Create clearance officer
850        self.app['users'].addUser('mrclear', 'mrClearsecret1')
851        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
852        self.app['users']['mrclear'].title = 'Carlo Pitter'
853        # Assign local ClearanceOfficer role
854        department = self.app['faculties']['fac1']['dep1']
855        prmlocal = IPrincipalRoleManager(department)
856        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
857        notify(LocalRoleSetEvent(
858            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
859        IWorkflowState(self.student).setState('clearance started')
860        # Login as clearance officer
861        self.browser.open(self.login_path)
862        self.browser.getControl(name="form.login").value = 'mrclear'
863        self.browser.getControl(name="form.password").value = 'mrClearsecret1'
864        self.browser.getControl("Login").click()
865        self.assertMatches('...You logged in...', self.browser.contents)
866        # Only in state clearance requested the CO does see the
867        # 'Edit level' button ...
868        self.browser.open(self.studycourse_path)
869        self.assertFalse('Edit level' in self.browser.contents)
870        # ... and can open the edit_level view
871        self.browser.open(self.studycourse_path + '/edit_level')
872        self.assertMatches('...is locked...', self.browser.contents)
873        self.assertEqual(self.browser.url, self.studycourse_path)
874        IWorkflowInfo(self.student).fireTransition('request_clearance')
875        self.browser.open(self.studycourse_path)
876        self.assertTrue('Edit level' in self.browser.contents)
877        self.browser.getLink("Edit level").click()
878        self.browser.getControl(name="form.current_level").value = ['200']
879        self.browser.getControl("Save").click()
880        self.assertMatches('...has been saved...', self.browser.contents)
881        self.assertEqual(self.student.current_level, 200)
882
883    def test_postgraduate_student_access(self):
884        self.certificate.study_mode = 'special_pg_pt'
885        self.certificate.start_level = 700
886        self.certificate.end_level = 800
887        self.student['studycourse'].current_level = 700
888        IWorkflowState(self.student).setState('school fee paid')
889        self.browser.open(self.login_path)
890        self.browser.getControl(name="form.login").value = self.student_id
891        self.browser.getControl(name="form.password").value = 'spwd'
892        self.browser.getControl("Login").click()
893        self.assertTrue(
894            'Did you know that with' in self.browser.contents)
895        # Now students can add the current study level
896        self.browser.getLink("Study Course").click()
897        self.browser.getLink("Add course list").click()
898        self.assertMatches('...Add current level 700...',
899                           self.browser.contents)
900        self.browser.getControl("Create course list now").click()
901        # A level with no course ticket was created
902        self.assertEqual(self.student['studycourse']['700'].number_of_tickets, 0)
903        self.browser.getLink("700").click()
904        self.browser.getLink("Edit course list").click()
905        self.browser.getLink("here").click()
906        self.browser.getControl(name="form.course").value = ['COURSE1']
907        # Non-final year students can't add ticket with 52 credits
908        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
909        self.browser.getControl("Add course ticket").click()
910        self.assertTrue('Maximum credits exceeded' in self.browser.contents)
911        # Final year students can't add ticket with 54 credits ...
912        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 54
913        self.student['studycourse'].certificate.end_level = 700
914        self.browser.getControl("Add course ticket").click()
915        self.assertTrue('Maximum credits exceeded.' in self.browser.contents)
916        # ... but with 53 credits
917        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 53
918        self.browser.getControl("Add course ticket").click()
919        self.assertMatches('...Successfully added COURSE1...',
920                           self.browser.contents)
921        # Non-final year special postgraduate students can't register
922        # course lists if their total credits are 51 and thus exceed 50 ...
923        self.student['studycourse'].certificate.end_level = 800
924        self.browser.getControl("Register course list").click()
925        self.assertMatches('...Maximum credits exceeded...',
926            self.browser.contents)
927        # ... but final year students can
928        self.student['studycourse'].certificate.end_level = 700
929        self.browser.getControl("Register course list").click()
930        self.assertMatches('...Course list has been registered...',
931            self.browser.contents)
932        self.assertEqual(self.student.state, 'courses registered')
933        return
934
935    def test_login(self):
936        # If suspended_comment is set this message will be flashed instead
937        self.student.suspended_comment = u'Aetsch baetsch!'
938        self.student.suspended = True
939        self.browser.open(self.login_path)
940        self.browser.getControl(name="form.login").value = self.student_id
941        self.browser.getControl(name="form.password").value = 'spwd'
942        self.browser.getControl("Login").click()
943        # Uniben does not display suspended_comment
944        self.assertMatches(
945            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
946            self.browser.contents)
947        self.student.suspended = False
948
949    def test_activate_deactivate_buttons(self):
950        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
951        self.browser.open(self.student_path)
952        self.browser.getLink("Deactivate").click()
953        self.assertTrue(
954            'Student account has been deactivated.' in self.browser.contents)
955        self.assertTrue(
956            'Base Data (account deactivated)' in self.browser.contents)
957        self.assertTrue(self.student.suspended)
958        self.browser.getLink("Activate").click()
959        self.assertTrue(
960            'Student account has been activated.' in self.browser.contents)
961        self.assertFalse(
962            'Base Data (account deactivated)' in self.browser.contents)
963        self.assertFalse(self.student.suspended)
964        # History messages have been added ...
965        self.browser.getLink("History").click()
966        # User is undisclosed
967        self.assertTrue(
968            'Student account deactivated<br />' in self.browser.contents)
969        self.assertTrue(
970            'Student account activated<br />' in self.browser.contents)
971        # ... and actions have been logged.
972        logfile = os.path.join(
973            self.app['datacenter'].storage, 'logs', 'students.log')
974        logcontent = open(logfile).read()
975        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
976                        'B1000000 - account deactivated' in logcontent)
977        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
978                        'B1000000 - account activated' in logcontent)
979
980    def test_manage_upload_fpm_file(self):
981        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
982        self.browser.open(self.manage_clearance_path)
983        image = open(SAMPLE_FPM, 'rb')
984        ctrl = self.browser.getControl(name='leftthumbprintupload')
985        file_ctrl = ctrl.mech_control
986        file_ctrl.add_file(image, filename='thumbprint.fpm')
987        self.browser.getControl(
988            name='upload_leftthumbprintupload').click()
989        self.assertTrue(
990            'File finger1.fpm uploaded.' in self.browser.contents)
991        self.assertTrue(
992            'http://localhost/app/students/B1000000/finger1.fpm'
993            in self.browser.contents)
994        self.browser.getControl(
995            name='delete_leftthumbprintupload').click()
996        self.assertTrue(
997            'finger1.fpm deleted'
998            in self.browser.contents)
999
1000    def test_handle_clearance_by_co(self):
1001        # Create clearance officer
1002        self.app['users'].addUser('mrclear', 'mrClearsecret1')
1003        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1004        self.app['users']['mrclear'].title = 'Carlo Pitter'
1005        department = self.app['faculties']['fac1']['dep1']
1006        prmlocal = IPrincipalRoleManager(department)
1007        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1008        notify(LocalRoleSetEvent(
1009            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1010        IWorkflowState(self.student).setState('clearance requested')
1011        # Login as clearance officer
1012        self.browser.open(self.login_path)
1013        self.browser.getControl(name="form.login").value = 'mrclear'
1014        self.browser.getControl(name="form.password").value = 'mrClearsecret1'
1015        self.browser.getControl("Login").click()
1016        self.assertMatches('...You logged in...', self.browser.contents)
1017        # CO can view the student ...
1018        self.browser.open(self.clearance_path)
1019        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1020        self.assertEqual(self.browser.url, self.clearance_path)
1021        # Clearance is disabled for this session for ug students ...
1022        self.browser.open(self.clearance_path)
1023        self.assertFalse('Clear student' in self.browser.contents)
1024        self.browser.open(self.student_path + '/clear')
1025        self.assertTrue('Clearance is disabled for this session'
1026            in self.browser.contents)
1027        # ... but not for
1028        self.certificate.study_mode = 'pg_ft'
1029        self.browser.open(self.clearance_path)
1030        self.assertTrue('Clear student' in self.browser.contents)
1031        self.browser.open(self.student_path + '/clear')
1032        self.assertTrue('Student has been cleared' in self.browser.contents)
1033
1034    def test_transcripts(self):
1035        studylevel = createObject(u'waeup.StudentStudyLevel')
1036        IWorkflowState(self.student).setState('transcript validated')
1037        studylevel.level = 100
1038        studylevel.level_session = 2005
1039        self.student['studycourse'].entry_mode = 'ug_ft'
1040        self.student['studycourse'].addStudentStudyLevel(
1041            self.certificate, studylevel)
1042        studylevel2 = createObject(u'waeup.StudentStudyLevel')
1043        studylevel2.level = 110
1044        studylevel2.level_session = 2006
1045        self.student['studycourse'].addStudentStudyLevel(
1046            self.certificate, studylevel2)
1047        # Add second course (COURSE has been added automatically)
1048        courseticket = createObject('waeup.CourseTicket')
1049        courseticket.code = 'ANYCODE'
1050        courseticket.title = u'Any TITLE'
1051        courseticket.credits = 13
1052        courseticket.score = 66
1053        courseticket.semester = 1
1054        courseticket.dcode = u'ANYDCODE'
1055        courseticket.fcode = u'ANYFCODE'
1056        self.student['studycourse']['110']['COURSE2'] = courseticket
1057        self.student['studycourse']['100']['COURSE1'].score = 55
1058        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
1059        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
1060        # Get transcript data
1061        td = self.student['studycourse'].getTranscriptData()
1062        self.assertEqual(td[0][0]['level_key'], '100')
1063        self.assertEqual(td[0][0]['sgpa'], 3.0)
1064        self.assertEqual(td[0][0]['level'].level, 100)
1065        self.assertEqual(td[0][0]['level'].level_session, 2005)
1066        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
1067        self.assertEqual(td[0][1]['level_key'], '110')
1068        self.assertEqual(td[0][1]['sgpa'], 4.0)
1069        self.assertEqual(td[0][1]['level'].level, 110)
1070        self.assertEqual(td[0][1]['level'].level_session, 2006)
1071        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
1072        self.assertEqual(td[1], 3.5652173913043477)
1073        self.browser.open(self.login_path)
1074        self.browser.getControl(name="form.login").value = self.student_id
1075        self.browser.getControl(name="form.password").value = 'spwd'
1076        self.browser.getControl("Login").click()
1077
1078        # Students can't open the pdf transcript ...
1079        #transcript_path = self.student_path + '/studycourse/transcript'
1080        #self.assertRaises(
1081        #    Unauthorized, self.browser.open, transcript_path)
1082        # ... but managers can
1083        #self.browser.open(self.student_path)
1084        #self.browser.getLink("Logout").click()
1085        #self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1086        #self.browser.open(transcript_path)
1087        #self.assertEqual(self.browser.headers['Status'], '200 Ok')
1088        #self.assertTrue('Transcript' in self.browser.contents)
1089        #self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1090        #self.assertEqual(self.browser.headers['Status'], '200 Ok')
1091        #self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1092        #path = os.path.join(samples_dir(), 'transcript.pdf')
1093        #open(path, 'wb').write(self.browser.contents)
1094        #print "Sample PDF transcript.pdf written to %s" % path
1095
1096        # Students and officers can open the pdf transcript ...
1097        self.student['studycourse'].current_level = 500
1098        transcript_path = self.student_path + '/studycourse/transcript'
1099        self.browser.open(transcript_path)
1100        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1101        self.assertTrue('Transcript' in self.browser.contents)
1102        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1103        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1104        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1105        path = os.path.join(samples_dir(), 'transcript_student.pdf')
1106        open(path, 'wb').write(self.browser.contents)
1107        print "Sample PDF transcript.pdf written to %s" % path
1108        self.browser.open(transcript_path)
1109        self.browser.getLink("Logout").click()
1110        # Create officer
1111        self.app['users'].addUser('mrtranscript', SECRET)
1112        self.app['users']['mrtranscript'].email = 'mrtranscript@foo.ng'
1113        self.app['users']['mrtranscript'].title = 'Ruth Gordon'
1114        prmglobal = IPrincipalRoleManager(self.app)
1115        prmglobal.assignRoleToPrincipal('waeup.TranscriptOfficer', 'mrtranscript')
1116        # Login
1117        self.browser.open(self.login_path)
1118        self.browser.getControl(name="form.login").value = 'mrtranscript'
1119        self.browser.getControl(name="form.password").value = SECRET
1120        self.browser.getControl("Login").click()
1121        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1122        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1123        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1124        path = os.path.join(samples_dir(), 'transcript_officer.pdf')
1125        open(path, 'wb').write(self.browser.contents)
1126        print "Sample PDF transcript.pdf written to %s" % path
1127
1128
1129    def test_student_accommodation(self):
1130        self.app['hostels'].allocation_expiration = 7
1131        self.student['accommodation'].desired_hostel = u'hall-1'
1132        bed = Bed()
1133        bed.bed_id = u'hall-1_A_101_C'
1134        bed.bed_number = 3
1135        bed.owner = NOT_OCCUPIED
1136        bed.bed_type = u'regular_male_fi'
1137        self.app['hostels']['hall-1'].addBed(bed)
1138        self.browser.open(self.login_path)
1139        self.browser.getControl(name="form.login").value = self.student_id
1140        self.browser.getControl(name="form.password").value = 'spwd'
1141        self.browser.getControl("Login").click()
1142        # Students can add online booking fee payment tickets and open the
1143        # callback view (see test_manage_payments).
1144        self.browser.getLink("Payments").click()
1145        self.browser.getLink("Add current session payment ticket").click()
1146        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1147        self.browser.getControl("Create ticket").click()
1148        p_ticket = self.student['payments'].values()[0]
1149        self.assertEqual(p_ticket.p_item, 'regular_male_fr (hall-1)')
1150        p_ticket.approveStudentPayment()
1151        # The new HOS-0 pin has been created.
1152        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1153        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1154        ac = self.app['accesscodes']['HOS-0'][pin]
1155        parts = pin.split('-')[1:]
1156        sfeseries, sfenumber = parts
1157        # Students can use HOS code and book a bed space with it ...
1158        self.browser.open(self.acco_path)
1159        # ... but not if booking period has expired ...
1160        self.app['hostels'].enddate = datetime.now(pytz.utc)
1161        self.browser.getControl("Book accommodation").click()
1162        self.assertMatches('...Outside booking period: ...',
1163                           self.browser.contents)
1164        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
1165        # ... or student data are incomplete ...
1166        self.student['studycourse'].current_level = None
1167        self.browser.getControl("Book accommodation").click()
1168        self.assertMatches('...Your data are incomplete...',
1169            self.browser.contents)
1170        self.student['studycourse'].current_level = 200
1171        # ... or student is not the an allowed state ...
1172        self.browser.getControl("Book accommodation").click()
1173        self.assertMatches('...You are in the wrong...',
1174                           self.browser.contents)
1175        self.app['hostels'].accommodation_states = ['admitted', 'school fee paid']
1176        IWorkflowState(self.student).setState('school fee paid')
1177        # ... or student has not appropriate verdict (Uniben only!)
1178        self.student['studycourse'].entry_session = 2000 # non-fresh
1179        self.student['studycourse'].current_level = 500 # final-year
1180        self.student['studycourse'].current_verdict = 'C'
1181        self.browser.getControl("Book accommodation").click()
1182        self.assertMatches('...Your are not eligible...',
1183            self.browser.contents)
1184        self.student['studycourse'].previous_verdict = 'A'
1185        self.browser.getControl("Book accommodation").click()
1186        self.assertMatches('...Activation Code:...',
1187                           self.browser.contents)
1188        # Student can't use faked ACs ...
1189        self.browser.getControl(name="ac_series").value = u'nonsense'
1190        self.browser.getControl(name="ac_number").value = sfenumber
1191        self.browser.getControl("Create bed ticket").click()
1192        self.assertMatches('...Activation code is invalid...',
1193                           self.browser.contents)
1194        # ... or ACs owned by somebody else.
1195        ac.owner = u'Anybody'
1196        self.browser.getControl(name="ac_series").value = sfeseries
1197        self.browser.getControl(name="ac_number").value = sfenumber
1198        self.browser.getControl("Create bed ticket").click()
1199        self.assertMatches('...You are not the owner of this access code...',
1200                           self.browser.contents)
1201        # The bed remains empty.
1202        bed = self.app['hostels']['hall-1']['hall-1_A_101_C']
1203        self.assertTrue(bed.owner == NOT_OCCUPIED)
1204        ac.owner = self.student_id
1205        self.browser.getControl(name="ac_series").value = sfeseries
1206        self.browser.getControl(name="ac_number").value = sfenumber
1207        self.browser.getControl("Create bed ticket").click()
1208        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1209                           self.browser.contents)
1210        # Bed has been allocated.
1211        self.assertTrue(bed.owner == self.student_id)
1212        # BedTicketAddPage is now blocked.
1213        self.browser.getControl("Book accommodation").click()
1214        self.assertMatches('...You already booked a bed space...',
1215            self.browser.contents)
1216        # The bed ticket displays the data correctly.
1217        self.browser.open(self.acco_path + '/2004')
1218        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1219                           self.browser.contents)
1220        self.assertMatches('...2004/2005...', self.browser.contents)
1221        self.assertMatches('...regular_male_fi...', self.browser.contents)
1222        self.assertMatches('...%s...' % pin, self.browser.contents)
1223        # Students can open the pdf slip.
1224        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
1225        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1226        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1227        path = os.path.join(samples_dir(), 'bed_allocation_slip.pdf')
1228        open(path, 'wb').write(self.browser.contents)
1229        print "Sample PDF bed_allocation_slip.pdf written to %s" % path
1230        # Students can't relocate themselves.
1231        self.assertFalse('Relocate' in self.browser.contents)
1232        relocate_path = self.acco_path + '/2004/relocate'
1233        self.assertRaises(
1234            Unauthorized, self.browser.open, relocate_path)
1235        # Students can't see the Remove button and check boxes.
1236        self.browser.open(self.acco_path)
1237        self.assertFalse('Remove' in self.browser.contents)
1238        self.assertFalse('val_id' in self.browser.contents)
1239        # Students can pay maintenance fee now.
1240        self.browser.open(self.payments_path)
1241        self.browser.open(self.payments_path + '/addop')
1242        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1243        self.browser.getControl("Create ticket").click()
1244        self.assertMatches('...Payment ticket created...',
1245                           self.browser.contents)
1246        # Maintennace fee is taken from the hostel object.
1247        self.assertEqual(self.student['payments'].values()[1].amount_auth, 876.0)
1248        # If the hostel's maintenance fee isn't set, the fee is
1249        # taken from the session configuration object.
1250        self.app['hostels']['hall-1'].maint_fee = 0.0
1251        self.browser.open(self.payments_path + '/addop')
1252        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1253        self.browser.getControl("Create ticket").click()
1254        self.assertEqual(self.student['payments'].values()[2].amount_auth, 987.0)
1255        return
1256
1257    def test_student_clearance(self):
1258        # create some passport file for `student`
1259        storage = getUtility(IExtFileStore)
1260        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
1261        self.image_contents = open(image_path, 'rb').read()
1262        file_id = IFileStoreNameChooser(self.student).chooseName(
1263            attr='passport.jpg')
1264        storage.createFile(file_id, StringIO(self.image_contents))
1265        IWorkflowInfo(self.student).fireTransition('admit')
1266        self.browser.open(self.login_path)
1267        self.browser.getControl(name="form.login").value = self.student_id
1268        self.browser.getControl(name="form.password").value = 'spwd'
1269        self.browser.getControl("Login").click()
1270        self.assertMatches(
1271            '...Did you know that with...', self.browser.contents)
1272        self.browser.getLink("Base Data").click()
1273        self.browser.getLink("Download admission letter").click()
1274        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1275        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1276        path = os.path.join(samples_dir(), 'admission_slip.pdf')
1277        open(path, 'wb').write(self.browser.contents)
1278        print "Sample PDF admission_slip.pdf written to %s" % path
1279        self.browser.open(self.student_path + '/start_clearance')
1280        # Regular students have to enter an access code
1281        self.browser.getControl("Start clearance now").click()
1282        self.assertTrue('Activation code is invalid' in self.browser.contents)
1283        # DCOEM students can start clearance without access code
1284        self.app['faculties']['fac1'].code = 'DCOEM'
1285        self.browser.open(self.student_path + '/start_clearance')
1286        self.browser.getControl("Start clearance now").click()
1287        self.assertMatches('...Clearance process has been started...',
1288                           self.browser.contents)
1289
1290    def test_student_medical_questionnaire(self):
1291        self.app['configuration']['2004'].medical_quest_fee = 1000.0
1292        IWorkflowInfo(self.student).fireTransition('admit')
1293        self.browser.open(self.login_path)
1294        self.browser.getControl(name="form.login").value = self.student_id
1295        self.browser.getControl(name="form.password").value = 'spwd'
1296        self.browser.getControl("Login").click()
1297        self.assertMatches(
1298            '...Did you know that with...', self.browser.contents)
1299        self.browser.getLink("Base Data").click()
1300        self.browser.getLink("Medical Questionnaire").click()
1301        self.assertTrue('Please pay medical questionnaire payment first' in
1302            self.browser.contents)
1303        self.assertEqual(self.browser.url, self.student_path)
1304        self.browser.open(self.payments_path)
1305        self.browser.getLink("Add current session payment ticket").click()
1306        self.browser.getControl(name="form.p_category").value = ['medical_quest']
1307        self.browser.getControl("Create ticket").click()
1308        p_ticket = self.student['payments'].values()[0]
1309        p_ticket.approveStudentPayment()
1310        self.browser.getLink("Base Data").click()
1311        self.browser.getLink("Medical Questionnaire").click()
1312        self.browser.getLink("Download medical questionnaire slip").click()
1313        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1314        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1315        path = os.path.join(samples_dir(), 'medical_questionnaire_slip.pdf')
1316        open(path, 'wb').write(self.browser.contents)
1317        print "Sample PDF medical_questionnaire_slip.pdf written to %s" % path
1318
1319        self.browser.open(self.student_path)
1320        self.browser.getLink("Medical Questionnaire").click()
1321        self.browser.getControl(name="form.bloodgroup").value = 'A'
1322        self.browser.getControl(name="form.genotype").value = 'XYZ'
1323        self.browser.getControl("Save").click()
1324        self.browser.getLink("Base Data").click()
1325        self.browser.getLink("Download TISHIP registration slip").click()
1326        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1327        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1328        path = os.path.join(samples_dir(), 'tiship_slip.pdf')
1329        open(path, 'wb').write(self.browser.contents)
1330        print "Sample PDF tiship_slip.pdf written to %s" % path
1331
1332    def test_fiancial_clearance_pdf_slip(self):
1333        payment1 = createObject(u'waeup.StudentOnlinePayment')
1334        timestamp = ("%d" % int(time()*10000))[1:]
1335        payment1.p_id = "LSCNEW-2-4153206270" # the longest possible p_id
1336        payment1.p_category = 'schoolfee'
1337        payment1.p_item = u'My School Fee'
1338        payment1.p_session = 2015
1339        payment1.p_level = 100
1340        payment1.p_current = True
1341        payment1.amount_auth = 23456.9
1342        payment1.approve()
1343        payment2 = createObject(u'waeup.StudentOnlinePayment')
1344        timestamp = ("%d" % int(time()*10000))[1:]
1345        payment2.p_id = "p%s" % timestamp
1346        payment2.p_category = 'clearance'
1347        payment2.p_item = u'My Clearance Fee'
1348        payment2.p_session = 2015
1349        payment2.p_level = 100
1350        payment2.p_current = True
1351        payment2.amount_auth = 5678.6
1352        payment2.approve()
1353        self.student['payments'][payment1.p_id] = payment1
1354        self.student['payments'][payment2.p_id] = payment2
1355        self.student.nysc = True
1356        self.student.nysc_updated = datetime.now(pytz.utc)
1357        self.student.nysc_senate_info = u'Senate Info test'
1358        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1359        self.browser.open(self.student_path + '/clear_financially')
1360        self.browser.getLink("Download fee payment history").click()
1361        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1362        self.assertEqual(self.browser.headers['Content-Type'],
1363                         'application/pdf')
1364        path = os.path.join(samples_dir(), 'fee_payment_history.pdf')
1365        open(path, 'wb').write(self.browser.contents)
1366        print "Sample PDF fee_payment_history.pdf written to %s" % path
1367        return
1368
1369    def test_affidavit_slip(self):
1370        self.browser.open(self.login_path)
1371        self.browser.getControl(name="form.login").value = self.student_id
1372        self.browser.getControl(name="form.password").value = 'spwd'
1373        self.browser.getControl("Login").click()
1374        self.browser.getLink("Base Data").click()
1375        self.browser.getLink("Upload affidavit of good conduct").click()
1376        self.browser.getLink("Download affidavit of good conduct form").click()
1377        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1378        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1379        path = os.path.join(samples_dir(), 'affidavit_good_conduct.pdf')
1380        open(path, 'wb').write(self.browser.contents)
1381        print "Sample PDF affidavit_good_conduct.pdf written to %s" % path
1382
1383    def test_download_mark_sheet(self):
1384        # A course ticket slip can be downloaded
1385        self.course.title = (u'Lorem ipsum     dolor sit amet, consectetur     adipisici, '
1386                             u'sed         eiusmod tempor    incidunt ut  labore et dolore')
1387        self.student.firstname = u'Emmanuella Woyengifigha Mercy Onosemudiana'
1388        self.student.lastname = u'OYAKEMIEGBEGHA'
1389        self.student.matric_number = u'hdk7gd62i872z27zt27ge'
1390        self.app['configuration'].current_academic_session = 2004
1391        course_url = (
1392            'http://localhost/app/faculties/fac1/dep1/courses/COURSE1')
1393        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1394        pdf_url = '%s/marksheet.pdf' % course_url
1395        self.browser.open(pdf_url)
1396        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1397        self.assertEqual(
1398            self.browser.headers['Content-Type'], 'application/pdf')
1399        path = os.path.join(samples_dir(), 'marksheet.pdf')
1400        open(path, 'wb').write(self.browser.contents)
1401        print "Sample PDF marksheet.pdf written to %s" % path
Note: See TracBrowser for help on using the repository browser.