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

Last change on this file since 17981 was 17979, checked in by Henrik Bettermann, 11 days ago

NUGA LEVY TO BE ADDED TO SCHOOL CHARGES (#1387)

  • Property svn:keywords set to Id
File size: 68.8 KB
Line 
1## $Id: test_browser.py 17979 2024-12-31 13:53:46Z 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, 650.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 51 credits
908        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
909        self.browser.getControl("Add course ticket").click()
910        self.assertMatches('...Maximum credits exceeded...',
911                           self.browser.contents)
912        # Final year students can't add ticket with 52 credits ...
913        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
914        self.student['studycourse'].certificate.end_level = 700
915        self.browser.getControl("Add course ticket").click()
916        self.assertMatches('...Maximum credits exceeded...',
917                           self.browser.contents)
918        # ... but with 51 credits
919        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
920        self.browser.getControl("Add course ticket").click()
921        self.assertMatches('...Successfully added COURSE1...',
922                           self.browser.contents)
923        # Non-final year special postgraduate students can't register
924        # course lists if their total credits are 51 and thus exceed 50 ...
925        self.student['studycourse'].certificate.end_level = 800
926        self.browser.getControl("Register course list").click()
927        self.assertMatches('...Maximum credits exceeded...',
928            self.browser.contents)
929        # ... but final year students can
930        self.student['studycourse'].certificate.end_level = 700
931        self.browser.getControl("Register course list").click()
932        self.assertMatches('...Course list has been registered...',
933            self.browser.contents)
934        self.assertEqual(self.student.state, 'courses registered')
935        return
936
937    def test_login(self):
938        # If suspended_comment is set this message will be flashed instead
939        self.student.suspended_comment = u'Aetsch baetsch!'
940        self.student.suspended = True
941        self.browser.open(self.login_path)
942        self.browser.getControl(name="form.login").value = self.student_id
943        self.browser.getControl(name="form.password").value = 'spwd'
944        self.browser.getControl("Login").click()
945        # Uniben does not display suspended_comment
946        self.assertMatches(
947            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
948            self.browser.contents)
949        self.student.suspended = False
950
951    def test_activate_deactivate_buttons(self):
952        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
953        self.browser.open(self.student_path)
954        self.browser.getLink("Deactivate").click()
955        self.assertTrue(
956            'Student account has been deactivated.' in self.browser.contents)
957        self.assertTrue(
958            'Base Data (account deactivated)' in self.browser.contents)
959        self.assertTrue(self.student.suspended)
960        self.browser.getLink("Activate").click()
961        self.assertTrue(
962            'Student account has been activated.' in self.browser.contents)
963        self.assertFalse(
964            'Base Data (account deactivated)' in self.browser.contents)
965        self.assertFalse(self.student.suspended)
966        # History messages have been added ...
967        self.browser.getLink("History").click()
968        # User is undisclosed
969        self.assertTrue(
970            'Student account deactivated<br />' in self.browser.contents)
971        self.assertTrue(
972            'Student account activated<br />' in self.browser.contents)
973        # ... and actions have been logged.
974        logfile = os.path.join(
975            self.app['datacenter'].storage, 'logs', 'students.log')
976        logcontent = open(logfile).read()
977        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
978                        'B1000000 - account deactivated' in logcontent)
979        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
980                        'B1000000 - account activated' in logcontent)
981
982    def test_manage_upload_fpm_file(self):
983        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
984        self.browser.open(self.manage_clearance_path)
985        image = open(SAMPLE_FPM, 'rb')
986        ctrl = self.browser.getControl(name='leftthumbprintupload')
987        file_ctrl = ctrl.mech_control
988        file_ctrl.add_file(image, filename='thumbprint.fpm')
989        self.browser.getControl(
990            name='upload_leftthumbprintupload').click()
991        self.assertTrue(
992            'File finger1.fpm uploaded.' in self.browser.contents)
993        self.assertTrue(
994            'http://localhost/app/students/B1000000/finger1.fpm'
995            in self.browser.contents)
996        self.browser.getControl(
997            name='delete_leftthumbprintupload').click()
998        self.assertTrue(
999            'finger1.fpm deleted'
1000            in self.browser.contents)
1001
1002    def test_handle_clearance_by_co(self):
1003        # Create clearance officer
1004        self.app['users'].addUser('mrclear', 'mrClearsecret1')
1005        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1006        self.app['users']['mrclear'].title = 'Carlo Pitter'
1007        department = self.app['faculties']['fac1']['dep1']
1008        prmlocal = IPrincipalRoleManager(department)
1009        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1010        notify(LocalRoleSetEvent(
1011            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1012        IWorkflowState(self.student).setState('clearance requested')
1013        # Login as clearance officer
1014        self.browser.open(self.login_path)
1015        self.browser.getControl(name="form.login").value = 'mrclear'
1016        self.browser.getControl(name="form.password").value = 'mrClearsecret1'
1017        self.browser.getControl("Login").click()
1018        self.assertMatches('...You logged in...', self.browser.contents)
1019        # CO can view the student ...
1020        self.browser.open(self.clearance_path)
1021        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1022        self.assertEqual(self.browser.url, self.clearance_path)
1023        # Clearance is disabled for this session for ug students ...
1024        self.browser.open(self.clearance_path)
1025        self.assertFalse('Clear student' in self.browser.contents)
1026        self.browser.open(self.student_path + '/clear')
1027        self.assertTrue('Clearance is disabled for this session'
1028            in self.browser.contents)
1029        # ... but not for
1030        self.certificate.study_mode = 'pg_ft'
1031        self.browser.open(self.clearance_path)
1032        self.assertTrue('Clear student' in self.browser.contents)
1033        self.browser.open(self.student_path + '/clear')
1034        self.assertTrue('Student has been cleared' in self.browser.contents)
1035
1036    def test_transcripts(self):
1037        studylevel = createObject(u'waeup.StudentStudyLevel')
1038        IWorkflowState(self.student).setState('transcript validated')
1039        studylevel.level = 100
1040        studylevel.level_session = 2005
1041        self.student['studycourse'].entry_mode = 'ug_ft'
1042        self.student['studycourse'].addStudentStudyLevel(
1043            self.certificate, studylevel)
1044        studylevel2 = createObject(u'waeup.StudentStudyLevel')
1045        studylevel2.level = 110
1046        studylevel2.level_session = 2006
1047        self.student['studycourse'].addStudentStudyLevel(
1048            self.certificate, studylevel2)
1049        # Add second course (COURSE has been added automatically)
1050        courseticket = createObject('waeup.CourseTicket')
1051        courseticket.code = 'ANYCODE'
1052        courseticket.title = u'Any TITLE'
1053        courseticket.credits = 13
1054        courseticket.score = 66
1055        courseticket.semester = 1
1056        courseticket.dcode = u'ANYDCODE'
1057        courseticket.fcode = u'ANYFCODE'
1058        self.student['studycourse']['110']['COURSE2'] = courseticket
1059        self.student['studycourse']['100']['COURSE1'].score = 55
1060        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
1061        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
1062        # Get transcript data
1063        td = self.student['studycourse'].getTranscriptData()
1064        self.assertEqual(td[0][0]['level_key'], '100')
1065        self.assertEqual(td[0][0]['sgpa'], 3.0)
1066        self.assertEqual(td[0][0]['level'].level, 100)
1067        self.assertEqual(td[0][0]['level'].level_session, 2005)
1068        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
1069        self.assertEqual(td[0][1]['level_key'], '110')
1070        self.assertEqual(td[0][1]['sgpa'], 4.0)
1071        self.assertEqual(td[0][1]['level'].level, 110)
1072        self.assertEqual(td[0][1]['level'].level_session, 2006)
1073        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
1074        self.assertEqual(td[1], 3.5652173913043477)
1075        self.browser.open(self.login_path)
1076        self.browser.getControl(name="form.login").value = self.student_id
1077        self.browser.getControl(name="form.password").value = 'spwd'
1078        self.browser.getControl("Login").click()
1079        # Students can't open the pdf transcript ...
1080        transcript_path = self.student_path + '/studycourse/transcript'
1081        self.assertRaises(
1082            Unauthorized, self.browser.open, transcript_path)
1083        # ... but managers can
1084        self.browser.open(self.student_path)
1085        self.browser.getLink("Logout").click()
1086        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1087        self.browser.open(transcript_path)
1088        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1089        self.assertTrue('Transcript' in self.browser.contents)
1090        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1091        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1092        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1093        path = os.path.join(samples_dir(), 'transcript.pdf')
1094        open(path, 'wb').write(self.browser.contents)
1095        print "Sample PDF transcript.pdf written to %s" % path
1096
1097    def test_student_accommodation(self):
1098        self.app['hostels'].allocation_expiration = 7
1099        self.student['accommodation'].desired_hostel = u'hall-1'
1100        bed = Bed()
1101        bed.bed_id = u'hall-1_A_101_C'
1102        bed.bed_number = 3
1103        bed.owner = NOT_OCCUPIED
1104        bed.bed_type = u'regular_male_fi'
1105        self.app['hostels']['hall-1'].addBed(bed)
1106        self.browser.open(self.login_path)
1107        self.browser.getControl(name="form.login").value = self.student_id
1108        self.browser.getControl(name="form.password").value = 'spwd'
1109        self.browser.getControl("Login").click()
1110        # Students can add online booking fee payment tickets and open the
1111        # callback view (see test_manage_payments).
1112        self.browser.getLink("Payments").click()
1113        self.browser.getLink("Add current session payment ticket").click()
1114        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1115        self.browser.getControl("Create ticket").click()
1116        p_ticket = self.student['payments'].values()[0]
1117        self.assertEqual(p_ticket.p_item, 'regular_male_fr (hall-1)')
1118        p_ticket.approveStudentPayment()
1119        # The new HOS-0 pin has been created.
1120        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1121        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1122        ac = self.app['accesscodes']['HOS-0'][pin]
1123        parts = pin.split('-')[1:]
1124        sfeseries, sfenumber = parts
1125        # Students can use HOS code and book a bed space with it ...
1126        self.browser.open(self.acco_path)
1127        # ... but not if booking period has expired ...
1128        self.app['hostels'].enddate = datetime.now(pytz.utc)
1129        self.browser.getControl("Book accommodation").click()
1130        self.assertMatches('...Outside booking period: ...',
1131                           self.browser.contents)
1132        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
1133        # ... or student data are incomplete ...
1134        self.student['studycourse'].current_level = None
1135        self.browser.getControl("Book accommodation").click()
1136        self.assertMatches('...Your data are incomplete...',
1137            self.browser.contents)
1138        self.student['studycourse'].current_level = 200
1139        # ... or student is not the an allowed state ...
1140        self.browser.getControl("Book accommodation").click()
1141        self.assertMatches('...You are in the wrong...',
1142                           self.browser.contents)
1143        self.app['hostels'].accommodation_states = ['admitted', 'school fee paid']
1144        IWorkflowState(self.student).setState('school fee paid')
1145        # ... or student has not appropriate verdict (Uniben only!)
1146        self.student['studycourse'].entry_session = 2000 # non-fresh
1147        self.student['studycourse'].current_level = 500 # final-year
1148        self.student['studycourse'].current_verdict = 'C'
1149        self.browser.getControl("Book accommodation").click()
1150        self.assertMatches('...Your are not eligible...',
1151            self.browser.contents)
1152        self.student['studycourse'].previous_verdict = 'A'
1153        self.browser.getControl("Book accommodation").click()
1154        self.assertMatches('...Activation Code:...',
1155                           self.browser.contents)
1156        # Student can't use faked ACs ...
1157        self.browser.getControl(name="ac_series").value = u'nonsense'
1158        self.browser.getControl(name="ac_number").value = sfenumber
1159        self.browser.getControl("Create bed ticket").click()
1160        self.assertMatches('...Activation code is invalid...',
1161                           self.browser.contents)
1162        # ... or ACs owned by somebody else.
1163        ac.owner = u'Anybody'
1164        self.browser.getControl(name="ac_series").value = sfeseries
1165        self.browser.getControl(name="ac_number").value = sfenumber
1166        self.browser.getControl("Create bed ticket").click()
1167        self.assertMatches('...You are not the owner of this access code...',
1168                           self.browser.contents)
1169        # The bed remains empty.
1170        bed = self.app['hostels']['hall-1']['hall-1_A_101_C']
1171        self.assertTrue(bed.owner == NOT_OCCUPIED)
1172        ac.owner = self.student_id
1173        self.browser.getControl(name="ac_series").value = sfeseries
1174        self.browser.getControl(name="ac_number").value = sfenumber
1175        self.browser.getControl("Create bed ticket").click()
1176        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1177                           self.browser.contents)
1178        # Bed has been allocated.
1179        self.assertTrue(bed.owner == self.student_id)
1180        # BedTicketAddPage is now blocked.
1181        self.browser.getControl("Book accommodation").click()
1182        self.assertMatches('...You already booked a bed space...',
1183            self.browser.contents)
1184        # The bed ticket displays the data correctly.
1185        self.browser.open(self.acco_path + '/2004')
1186        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1187                           self.browser.contents)
1188        self.assertMatches('...2004/2005...', self.browser.contents)
1189        self.assertMatches('...regular_male_fi...', self.browser.contents)
1190        self.assertMatches('...%s...' % pin, self.browser.contents)
1191        # Students can open the pdf slip.
1192        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
1193        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1194        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1195        path = os.path.join(samples_dir(), 'bed_allocation_slip.pdf')
1196        open(path, 'wb').write(self.browser.contents)
1197        print "Sample PDF bed_allocation_slip.pdf written to %s" % path
1198        # Students can't relocate themselves.
1199        self.assertFalse('Relocate' in self.browser.contents)
1200        relocate_path = self.acco_path + '/2004/relocate'
1201        self.assertRaises(
1202            Unauthorized, self.browser.open, relocate_path)
1203        # Students can't see the Remove button and check boxes.
1204        self.browser.open(self.acco_path)
1205        self.assertFalse('Remove' in self.browser.contents)
1206        self.assertFalse('val_id' in self.browser.contents)
1207        # Students can pay maintenance fee now.
1208        self.browser.open(self.payments_path)
1209        self.browser.open(self.payments_path + '/addop')
1210        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1211        self.browser.getControl("Create ticket").click()
1212        self.assertMatches('...Payment ticket created...',
1213                           self.browser.contents)
1214        # Maintennace fee is taken from the hostel object.
1215        self.assertEqual(self.student['payments'].values()[1].amount_auth, 876.0)
1216        # If the hostel's maintenance fee isn't set, the fee is
1217        # taken from the session configuration object.
1218        self.app['hostels']['hall-1'].maint_fee = 0.0
1219        self.browser.open(self.payments_path + '/addop')
1220        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1221        self.browser.getControl("Create ticket").click()
1222        self.assertEqual(self.student['payments'].values()[2].amount_auth, 987.0)
1223        return
1224
1225    def test_student_clearance(self):
1226        # create some passport file for `student`
1227        storage = getUtility(IExtFileStore)
1228        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
1229        self.image_contents = open(image_path, 'rb').read()
1230        file_id = IFileStoreNameChooser(self.student).chooseName(
1231            attr='passport.jpg')
1232        storage.createFile(file_id, StringIO(self.image_contents))
1233        IWorkflowInfo(self.student).fireTransition('admit')
1234        self.browser.open(self.login_path)
1235        self.browser.getControl(name="form.login").value = self.student_id
1236        self.browser.getControl(name="form.password").value = 'spwd'
1237        self.browser.getControl("Login").click()
1238        self.assertMatches(
1239            '...Did you know that with...', self.browser.contents)
1240        self.browser.getLink("Base Data").click()
1241        self.browser.getLink("Download admission letter").click()
1242        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1243        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1244        path = os.path.join(samples_dir(), 'admission_slip.pdf')
1245        open(path, 'wb').write(self.browser.contents)
1246        print "Sample PDF admission_slip.pdf written to %s" % path
1247        self.browser.open(self.student_path + '/start_clearance')
1248        # Regular students have to enter an access code
1249        self.browser.getControl("Start clearance now").click()
1250        self.assertTrue('Activation code is invalid' in self.browser.contents)
1251        # DCOEM students can start clearance without access code
1252        self.app['faculties']['fac1'].code = 'DCOEM'
1253        self.browser.open(self.student_path + '/start_clearance')
1254        self.browser.getControl("Start clearance now").click()
1255        self.assertMatches('...Clearance process has been started...',
1256                           self.browser.contents)
1257
1258    def test_student_medical_questionnaire(self):
1259        self.app['configuration']['2004'].medical_quest_fee = 1000.0
1260        IWorkflowInfo(self.student).fireTransition('admit')
1261        self.browser.open(self.login_path)
1262        self.browser.getControl(name="form.login").value = self.student_id
1263        self.browser.getControl(name="form.password").value = 'spwd'
1264        self.browser.getControl("Login").click()
1265        self.assertMatches(
1266            '...Did you know that with...', self.browser.contents)
1267        self.browser.getLink("Base Data").click()
1268        self.browser.getLink("Medical Questionnaire").click()
1269        self.assertTrue('Please pay medical questionnaire payment first' in
1270            self.browser.contents)
1271        self.assertEqual(self.browser.url, self.student_path)
1272        self.browser.open(self.payments_path)
1273        self.browser.getLink("Add current session payment ticket").click()
1274        self.browser.getControl(name="form.p_category").value = ['medical_quest']
1275        self.browser.getControl("Create ticket").click()
1276        p_ticket = self.student['payments'].values()[0]
1277        p_ticket.approveStudentPayment()
1278        self.browser.getLink("Base Data").click()
1279        self.browser.getLink("Medical Questionnaire").click()
1280        self.browser.getLink("Download medical questionnaire slip").click()
1281        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1282        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1283        path = os.path.join(samples_dir(), 'medical_questionnaire_slip.pdf')
1284        open(path, 'wb').write(self.browser.contents)
1285        print "Sample PDF medical_questionnaire_slip.pdf written to %s" % path
1286
1287        self.browser.open(self.student_path)
1288        self.browser.getLink("Medical Questionnaire").click()
1289        self.browser.getControl(name="form.bloodgroup").value = 'A'
1290        self.browser.getControl(name="form.genotype").value = 'XYZ'
1291        self.browser.getControl("Save").click()
1292        self.browser.getLink("Base Data").click()
1293        self.browser.getLink("Download TISHIP registration slip").click()
1294        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1295        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1296        path = os.path.join(samples_dir(), 'tiship_slip.pdf')
1297        open(path, 'wb').write(self.browser.contents)
1298        print "Sample PDF tiship_slip.pdf written to %s" % path
1299
1300    def test_fiancial_clearance_pdf_slip(self):
1301        payment1 = createObject(u'waeup.StudentOnlinePayment')
1302        timestamp = ("%d" % int(time()*10000))[1:]
1303        payment1.p_id = "LSCNEW-2-4153206270" # the longest possible p_id
1304        payment1.p_category = 'schoolfee'
1305        payment1.p_item = u'My School Fee'
1306        payment1.p_session = 2015
1307        payment1.p_level = 100
1308        payment1.p_current = True
1309        payment1.amount_auth = 23456.9
1310        payment1.approve()
1311        payment2 = createObject(u'waeup.StudentOnlinePayment')
1312        timestamp = ("%d" % int(time()*10000))[1:]
1313        payment2.p_id = "p%s" % timestamp
1314        payment2.p_category = 'clearance'
1315        payment2.p_item = u'My Clearance Fee'
1316        payment2.p_session = 2015
1317        payment2.p_level = 100
1318        payment2.p_current = True
1319        payment2.amount_auth = 5678.6
1320        payment2.approve()
1321        self.student['payments'][payment1.p_id] = payment1
1322        self.student['payments'][payment2.p_id] = payment2
1323        self.student.nysc = True
1324        self.student.nysc_updated = datetime.now(pytz.utc)
1325        self.student.nysc_senate_info = u'Senate Info test'
1326        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1327        self.browser.open(self.student_path + '/clear_financially')
1328        self.browser.getLink("Download fee payment history").click()
1329        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1330        self.assertEqual(self.browser.headers['Content-Type'],
1331                         'application/pdf')
1332        path = os.path.join(samples_dir(), 'fee_payment_history.pdf')
1333        open(path, 'wb').write(self.browser.contents)
1334        print "Sample PDF fee_payment_history.pdf written to %s" % path
1335        return
1336
1337    def test_affidavit_slip(self):
1338        self.browser.open(self.login_path)
1339        self.browser.getControl(name="form.login").value = self.student_id
1340        self.browser.getControl(name="form.password").value = 'spwd'
1341        self.browser.getControl("Login").click()
1342        self.browser.getLink("Base Data").click()
1343        self.browser.getLink("Upload affidavit of good conduct").click()
1344        self.browser.getLink("Download affidavit of good conduct form").click()
1345        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1346        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1347        path = os.path.join(samples_dir(), 'affidavit_good_conduct.pdf')
1348        open(path, 'wb').write(self.browser.contents)
1349        print "Sample PDF affidavit_good_conduct.pdf written to %s" % path
Note: See TracBrowser for help on using the repository browser.