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

Last change on this file since 17977 was 17963, checked in by Henrik Bettermann, 4 weeks ago

Let JUPEB students pay by instalments.

  • Property svn:keywords set to Id
File size: 68.5 KB
Line 
1## $Id: test_browser.py 17963 2024-11-26 02:34:19Z 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 penalty fee ...
608        # ... for cleared
609        self.app['configuration']['2004'].penalty_ug_ft = 99.0
610        # ... for returning
611        self.app['configuration']['2005'].penalty_ug_ft = 88.0
612        error, payment = utils.setPaymentDetails('schoolfee',
613            self.student, None, None, None)
614        self.assertEqual(payment.amount_auth, 63099.0)
615
616        IWorkflowState(self.student).setState('returning')
617
618        #error, payment = utils.setPaymentDetails('schoolfee',
619        #    self.student, None, None, None)
620        #self.assertTrue(
621        #    u'You have not yet paid your current/active session.' in error)
622        ## Ok, that means we have to add paid payment ticket first.
623        #payment = createObject('waeup.StudentOnlinePayment')
624        #payment.p_category = u'schoolfee'
625        #payment.p_session = self.student.current_session
626        #payment.p_item = u'My Certificate'
627        #payment.p_id = u'anyid'
628        #payment.p_state = u'paid'
629        #self.student['payments']['anykey'] = payment
630
631        error, payment = utils.setPaymentDetails('schoolfee',
632            self.student, None, None, None)
633        self.assertEqual(payment.p_level, 200)
634        self.assertEqual(payment.p_session, 2005)
635        self.assertEqual(payment.amount_auth, 26888.0)
636        self.assertEqual(payment.p_item, u'CERT1')
637        self.assertEqual(error, None)
638
639        # Old returning students may pay less.
640        #self.certificate.school_fee_2 = 50000.0
641        #self.certificate.custom_float_2 = 30000.0
642        error, payment = utils.setPaymentDetails(
643            'schoolfee', self.student, None, None, None)
644        self.assertEqual(payment.amount_auth, 26888.0)
645
646        # Staff members pay less.
647        self.student.is_staff = True
648        error, payment = utils.setPaymentDetails('schoolfee',
649            self.student, None, None, None)
650        self.assertEqual(payment.p_level, 200)
651        self.assertEqual(payment.p_session, 2005)
652        self.assertEqual(payment.amount_auth, 13488.0)
653        self.assertEqual(payment.p_item, u'CERT1')
654        self.assertEqual(error, None)
655
656        # Foreigners pay more.
657        IWorkflowState(self.student).setState('cleared')
658        self.student.is_staff = False
659        self.student.nationality = u'DE'
660        error, payment = utils.setPaymentDetails(
661            'schoolfee', self.student, None, None, None)
662        self.assertEqual(payment.p_level, 100)
663        self.assertEqual(payment.p_session, 2004)
664        self.assertEqual(payment.amount_auth, 300099.0)
665        self.assertEqual(payment.p_item, u'CERT1')
666        self.assertEqual(error, None)
667        IWorkflowState(self.student).setState('returning')
668        self.student.is_staff = False
669        error, payment = utils.setPaymentDetails(
670            'schoolfee', self.student, None, None, None)
671        self.assertEqual(payment.p_level, 200)
672        self.assertEqual(payment.p_session, 2005)
673        self.assertEqual(payment.amount_auth, 277088.0)
674        self.assertEqual(payment.p_item, u'CERT1')
675        self.assertEqual(error, None)
676
677        # In Uniben students can pay school fee in all states no matter
678        # if they are ug or pg students.
679        # diabled on 02/10/2017, see ticket 1108
680        #IWorkflowState(self.student).setState('school fee paid')
681        #self.student.is_staff = False
682        #self.student.nationality = u'NG'
683        #self.certificate.school_fee_2 = 10000.0
684        #error, payment = utils.setPaymentDetails(
685        #    'schoolfee', self.student, None, None, None)
686        #self.assertEqual(payment.p_level, None)
687        #self.assertEqual(payment.p_session, 2005)
688        #self.assertEqual(payment.amount_auth, 10088.0)
689        #self.assertEqual(payment.p_item, u'CERT1')
690        #self.assertEqual(error, None)
691        #IWorkflowState(self.student).setState('courses registered')
692        #self.certificate.study_mode = 'special_pg_pt'
693        #error, payment = utils.setPaymentDetails(
694        #    'schoolfee', self.student, None, None, None)
695        #self.assertEqual(payment.p_level, None)
696        #self.assertEqual(payment.p_session, 2005)
697        #self.assertEqual(payment.amount_auth, 10000.0)
698        #self.assertEqual(payment.p_item, u'CERT1')
699        #self.assertEqual(error, None)
700        #IWorkflowState(self.student).setState('courses validated')
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
709        error, payment = utils.setPaymentDetails('clearance',
710            self.student, None, None, None)
711        self.assertEqual(payment.p_level, 100)
712        self.assertEqual(payment.p_session, 2004)
713        self.assertEqual(payment.amount_auth, 60000.0)
714        self.assertEqual(payment.p_item, u'CERT1')
715        self.assertEqual(error, None)
716
717        error, payment = utils.setPaymentDetails('gown',
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, 150.0)
722        self.assertEqual(payment.p_item, u'')
723        self.assertEqual(error, None)
724
725        bedticket = BedTicket()
726        bedticket.booking_session = 2004
727        bedticket.bed_type = u'any bed type'
728        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
729        bedticket.bed_coordinates = u'My bed coordinates'
730        self.student['accommodation'].addBedTicket(bedticket)
731
732        error, payment = utils.setPaymentDetails('hostel_maintenance',
733            self.student, None, None, None)
734        self.assertEqual(payment.p_level, 100)
735        self.assertEqual(payment.p_session, 2004)
736        self.assertEqual(payment.amount_auth, 876.0)
737        self.assertEqual(payment.p_item, u'My bed coordinates')
738        self.assertEqual(error, None)
739
740        #self.certificate.study_mode = 'ug_ft'
741        #error, payment = utils.setPaymentDetails('bed_allocation',
742        #    self.student, None, None, None)
743        #self.assertTrue(u'Amount could not be determined.' in error)
744        #self.student['studycourse'].current_session = 2004
745        #self.student['studycourse'].entry_session = 2004
746        #self.student['studycourse'].current_level = 100
747        #error, payment = utils.setPaymentDetails('bed_allocation',
748        #    self.student, None, None, None)
749        #self.assertEqual(payment.p_level, 100)
750        #self.assertEqual(payment.p_session, 2004)
751        #self.assertEqual(payment.amount_auth, 650.0) # plus 500 student union
752        #self.assertEqual(payment.p_item, u'regular_male_fr')
753        #self.assertEqual(error, None)
754
755        self.certificate.study_mode = 'pg_ft'
756        error, payment = utils.setPaymentDetails('bed_allocation',
757            self.student, None, None, None)
758        self.assertEqual(error, u'Select your favoured hostel first.')
759        self.student['accommodation'].desired_hostel = u'no'
760        error, payment = utils.setPaymentDetails('bed_allocation',
761            self.student, None, None, None)
762        self.assertEqual(payment.p_level, 100)
763        self.assertEqual(payment.p_session, 2004)
764        self.assertEqual(payment.amount_auth, 650.0)
765        self.assertEqual(payment.p_item, u'pg_male_all')
766        self.assertEqual(error, None)
767
768        #error, payment = utils.setPaymentDetails('hostel_application',
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, 1000.0)
773        #self.assertEqual(payment.p_item, u'')
774        #self.assertEqual(error, None)
775
776        #payment.approve()
777        #self.student['payments'][payment.p_id] = payment
778
779        #error, payment = utils.setPaymentDetails('tempmaint_1',
780        #    self.student, None, None, None)
781        #self.assertEqual(payment.p_level, 100)
782        #self.assertEqual(payment.p_session, 2004)
783        #self.assertEqual(payment.amount_auth, 8150.0)
784        #self.assertEqual(payment.p_item, u'Hall 1-4 M/F Ekehuan')
785        #self.assertEqual(error, None)
786
787        #error, payment = utils.setPaymentDetails('tempmaint_2',
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, 12650.0)
792        #self.assertEqual(payment.p_item, u'Hall 5 M/F')
793        #self.assertEqual(error, None)
794
795        #error, payment = utils.setPaymentDetails('tempmaint_3',
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, 9650.0)
800        #self.assertEqual(payment.p_item, u'Clinical Hostel')
801        #self.assertEqual(error, None)
802
803        error, payment = utils.setPaymentDetails('transfer',
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, 90.0)
808        self.assertEqual(payment.p_item, u'')
809        self.assertEqual(error, None)
810        return
811
812    def test_set_payment_details_jupeb(self):
813        # JUPEB students can pay by instalments
814        utils = getUtility(IStudentsUtils)
815        self.certificate.study_mode = 'found'
816        IWorkflowState(self.student).setState('cleared')
817        error, payment = utils.setPaymentDetails(
818            'schoolfee', self.student, None, None, None)
819        self.assertEqual(payment.p_level, 100)
820        self.assertEqual(payment.p_session, 2004)
821        self.assertEqual(payment.amount_auth, 220000.0)
822        self.assertEqual(payment.p_item, u'CERT1')
823        self.assertEqual(error, None)
824        error, payment = utils.setPaymentDetails(
825            'schoolfee_1', self.student, None, None, None)
826        self.assertEqual(payment.p_level, 100)
827        self.assertEqual(payment.p_session, 2004)
828        self.assertEqual(payment.amount_auth, 110000.0)
829        self.assertEqual(payment.p_item, u'CERT1')
830        self.assertEqual(error, None)
831        return
832        error, payment = utils.setPaymentDetails(
833            'secondinstall', 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
840    def test_edit_level_by_co(self):
841        # Create clearance officer
842        self.app['users'].addUser('mrclear', 'mrClearsecret1')
843        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
844        self.app['users']['mrclear'].title = 'Carlo Pitter'
845        # Assign local ClearanceOfficer role
846        department = self.app['faculties']['fac1']['dep1']
847        prmlocal = IPrincipalRoleManager(department)
848        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
849        notify(LocalRoleSetEvent(
850            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
851        IWorkflowState(self.student).setState('clearance started')
852        # Login as clearance officer
853        self.browser.open(self.login_path)
854        self.browser.getControl(name="form.login").value = 'mrclear'
855        self.browser.getControl(name="form.password").value = 'mrClearsecret1'
856        self.browser.getControl("Login").click()
857        self.assertMatches('...You logged in...', self.browser.contents)
858        # Only in state clearance requested the CO does see the
859        # 'Edit level' button ...
860        self.browser.open(self.studycourse_path)
861        self.assertFalse('Edit level' in self.browser.contents)
862        # ... and can open the edit_level view
863        self.browser.open(self.studycourse_path + '/edit_level')
864        self.assertMatches('...is locked...', self.browser.contents)
865        self.assertEqual(self.browser.url, self.studycourse_path)
866        IWorkflowInfo(self.student).fireTransition('request_clearance')
867        self.browser.open(self.studycourse_path)
868        self.assertTrue('Edit level' in self.browser.contents)
869        self.browser.getLink("Edit level").click()
870        self.browser.getControl(name="form.current_level").value = ['200']
871        self.browser.getControl("Save").click()
872        self.assertMatches('...has been saved...', self.browser.contents)
873        self.assertEqual(self.student.current_level, 200)
874
875    def test_postgraduate_student_access(self):
876        self.certificate.study_mode = 'special_pg_pt'
877        self.certificate.start_level = 700
878        self.certificate.end_level = 800
879        self.student['studycourse'].current_level = 700
880        IWorkflowState(self.student).setState('school fee paid')
881        self.browser.open(self.login_path)
882        self.browser.getControl(name="form.login").value = self.student_id
883        self.browser.getControl(name="form.password").value = 'spwd'
884        self.browser.getControl("Login").click()
885        self.assertTrue(
886            'Did you know that with' in self.browser.contents)
887        # Now students can add the current study level
888        self.browser.getLink("Study Course").click()
889        self.browser.getLink("Add course list").click()
890        self.assertMatches('...Add current level 700...',
891                           self.browser.contents)
892        self.browser.getControl("Create course list now").click()
893        # A level with no course ticket was created
894        self.assertEqual(self.student['studycourse']['700'].number_of_tickets, 0)
895        self.browser.getLink("700").click()
896        self.browser.getLink("Edit course list").click()
897        self.browser.getLink("here").click()
898        self.browser.getControl(name="form.course").value = ['COURSE1']
899        # Non-final year students can't add ticket with 51 credits
900        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
901        self.browser.getControl("Add course ticket").click()
902        self.assertMatches('...Maximum credits exceeded...',
903                           self.browser.contents)
904        # Final year students can't add ticket with 52 credits ...
905        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 52
906        self.student['studycourse'].certificate.end_level = 700
907        self.browser.getControl("Add course ticket").click()
908        self.assertMatches('...Maximum credits exceeded...',
909                           self.browser.contents)
910        # ... but with 51 credits
911        self.app['faculties']['fac1']['dep1'].courses['COURSE1'].credits = 51
912        self.browser.getControl("Add course ticket").click()
913        self.assertMatches('...Successfully added COURSE1...',
914                           self.browser.contents)
915        # Non-final year special postgraduate students can't register
916        # course lists if their total credits are 51 and thus exceed 50 ...
917        self.student['studycourse'].certificate.end_level = 800
918        self.browser.getControl("Register course list").click()
919        self.assertMatches('...Maximum credits exceeded...',
920            self.browser.contents)
921        # ... but final year students can
922        self.student['studycourse'].certificate.end_level = 700
923        self.browser.getControl("Register course list").click()
924        self.assertMatches('...Course list has been registered...',
925            self.browser.contents)
926        self.assertEqual(self.student.state, 'courses registered')
927        return
928
929    def test_login(self):
930        # If suspended_comment is set this message will be flashed instead
931        self.student.suspended_comment = u'Aetsch baetsch!'
932        self.student.suspended = True
933        self.browser.open(self.login_path)
934        self.browser.getControl(name="form.login").value = self.student_id
935        self.browser.getControl(name="form.password").value = 'spwd'
936        self.browser.getControl("Login").click()
937        # Uniben does not display suspended_comment
938        self.assertMatches(
939            '...<div class="alert alert-warning">Your account has been deactivated.</div>...',
940            self.browser.contents)
941        self.student.suspended = False
942
943    def test_activate_deactivate_buttons(self):
944        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
945        self.browser.open(self.student_path)
946        self.browser.getLink("Deactivate").click()
947        self.assertTrue(
948            'Student account has been deactivated.' in self.browser.contents)
949        self.assertTrue(
950            'Base Data (account deactivated)' in self.browser.contents)
951        self.assertTrue(self.student.suspended)
952        self.browser.getLink("Activate").click()
953        self.assertTrue(
954            'Student account has been activated.' in self.browser.contents)
955        self.assertFalse(
956            'Base Data (account deactivated)' in self.browser.contents)
957        self.assertFalse(self.student.suspended)
958        # History messages have been added ...
959        self.browser.getLink("History").click()
960        # User is undisclosed
961        self.assertTrue(
962            'Student account deactivated<br />' in self.browser.contents)
963        self.assertTrue(
964            'Student account activated<br />' in self.browser.contents)
965        # ... and actions have been logged.
966        logfile = os.path.join(
967            self.app['datacenter'].storage, 'logs', 'students.log')
968        logcontent = open(logfile).read()
969        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentDeactivateView - '
970                        'B1000000 - account deactivated' in logcontent)
971        self.assertTrue('zope.mgr - waeup.uniben.students.browser.CustomStudentActivateView - '
972                        'B1000000 - account activated' in logcontent)
973
974    def test_manage_upload_fpm_file(self):
975        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
976        self.browser.open(self.manage_clearance_path)
977        image = open(SAMPLE_FPM, 'rb')
978        ctrl = self.browser.getControl(name='leftthumbprintupload')
979        file_ctrl = ctrl.mech_control
980        file_ctrl.add_file(image, filename='thumbprint.fpm')
981        self.browser.getControl(
982            name='upload_leftthumbprintupload').click()
983        self.assertTrue(
984            'File finger1.fpm uploaded.' in self.browser.contents)
985        self.assertTrue(
986            'http://localhost/app/students/B1000000/finger1.fpm'
987            in self.browser.contents)
988        self.browser.getControl(
989            name='delete_leftthumbprintupload').click()
990        self.assertTrue(
991            'finger1.fpm deleted'
992            in self.browser.contents)
993
994    def test_handle_clearance_by_co(self):
995        # Create clearance officer
996        self.app['users'].addUser('mrclear', 'mrClearsecret1')
997        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
998        self.app['users']['mrclear'].title = 'Carlo Pitter'
999        department = self.app['faculties']['fac1']['dep1']
1000        prmlocal = IPrincipalRoleManager(department)
1001        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1002        notify(LocalRoleSetEvent(
1003            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1004        IWorkflowState(self.student).setState('clearance requested')
1005        # Login as clearance officer
1006        self.browser.open(self.login_path)
1007        self.browser.getControl(name="form.login").value = 'mrclear'
1008        self.browser.getControl(name="form.password").value = 'mrClearsecret1'
1009        self.browser.getControl("Login").click()
1010        self.assertMatches('...You logged in...', self.browser.contents)
1011        # CO can view the student ...
1012        self.browser.open(self.clearance_path)
1013        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1014        self.assertEqual(self.browser.url, self.clearance_path)
1015        # Clearance is disabled for this session for ug students ...
1016        self.browser.open(self.clearance_path)
1017        self.assertFalse('Clear student' in self.browser.contents)
1018        self.browser.open(self.student_path + '/clear')
1019        self.assertTrue('Clearance is disabled for this session'
1020            in self.browser.contents)
1021        # ... but not for
1022        self.certificate.study_mode = 'pg_ft'
1023        self.browser.open(self.clearance_path)
1024        self.assertTrue('Clear student' in self.browser.contents)
1025        self.browser.open(self.student_path + '/clear')
1026        self.assertTrue('Student has been cleared' in self.browser.contents)
1027
1028    def test_transcripts(self):
1029        studylevel = createObject(u'waeup.StudentStudyLevel')
1030        IWorkflowState(self.student).setState('transcript validated')
1031        studylevel.level = 100
1032        studylevel.level_session = 2005
1033        self.student['studycourse'].entry_mode = 'ug_ft'
1034        self.student['studycourse'].addStudentStudyLevel(
1035            self.certificate, studylevel)
1036        studylevel2 = createObject(u'waeup.StudentStudyLevel')
1037        studylevel2.level = 110
1038        studylevel2.level_session = 2006
1039        self.student['studycourse'].addStudentStudyLevel(
1040            self.certificate, studylevel2)
1041        # Add second course (COURSE has been added automatically)
1042        courseticket = createObject('waeup.CourseTicket')
1043        courseticket.code = 'ANYCODE'
1044        courseticket.title = u'Any TITLE'
1045        courseticket.credits = 13
1046        courseticket.score = 66
1047        courseticket.semester = 1
1048        courseticket.dcode = u'ANYDCODE'
1049        courseticket.fcode = u'ANYFCODE'
1050        self.student['studycourse']['110']['COURSE2'] = courseticket
1051        self.student['studycourse']['100']['COURSE1'].score = 55
1052        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
1053        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
1054        # Get transcript data
1055        td = self.student['studycourse'].getTranscriptData()
1056        self.assertEqual(td[0][0]['level_key'], '100')
1057        self.assertEqual(td[0][0]['sgpa'], 3.0)
1058        self.assertEqual(td[0][0]['level'].level, 100)
1059        self.assertEqual(td[0][0]['level'].level_session, 2005)
1060        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
1061        self.assertEqual(td[0][1]['level_key'], '110')
1062        self.assertEqual(td[0][1]['sgpa'], 4.0)
1063        self.assertEqual(td[0][1]['level'].level, 110)
1064        self.assertEqual(td[0][1]['level'].level_session, 2006)
1065        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
1066        self.assertEqual(td[1], 3.5652173913043477)
1067        self.browser.open(self.login_path)
1068        self.browser.getControl(name="form.login").value = self.student_id
1069        self.browser.getControl(name="form.password").value = 'spwd'
1070        self.browser.getControl("Login").click()
1071        # Students can't open the pdf transcript ...
1072        transcript_path = self.student_path + '/studycourse/transcript'
1073        self.assertRaises(
1074            Unauthorized, self.browser.open, transcript_path)
1075        # ... but managers can
1076        self.browser.open(self.student_path)
1077        self.browser.getLink("Logout").click()
1078        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1079        self.browser.open(transcript_path)
1080        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1081        self.assertTrue('Transcript' in self.browser.contents)
1082        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1083        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1084        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1085        path = os.path.join(samples_dir(), 'transcript.pdf')
1086        open(path, 'wb').write(self.browser.contents)
1087        print "Sample PDF transcript.pdf written to %s" % path
1088
1089    def test_student_accommodation(self):
1090        self.app['hostels'].allocation_expiration = 7
1091        self.student['accommodation'].desired_hostel = u'hall-1'
1092        bed = Bed()
1093        bed.bed_id = u'hall-1_A_101_C'
1094        bed.bed_number = 3
1095        bed.owner = NOT_OCCUPIED
1096        bed.bed_type = u'regular_male_fi'
1097        self.app['hostels']['hall-1'].addBed(bed)
1098        self.browser.open(self.login_path)
1099        self.browser.getControl(name="form.login").value = self.student_id
1100        self.browser.getControl(name="form.password").value = 'spwd'
1101        self.browser.getControl("Login").click()
1102        # Students can add online booking fee payment tickets and open the
1103        # callback view (see test_manage_payments).
1104        self.browser.getLink("Payments").click()
1105        self.browser.getLink("Add current session payment ticket").click()
1106        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1107        self.browser.getControl("Create ticket").click()
1108        p_ticket = self.student['payments'].values()[0]
1109        self.assertEqual(p_ticket.p_item, 'regular_male_fr (hall-1)')
1110        p_ticket.approveStudentPayment()
1111        # The new HOS-0 pin has been created.
1112        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1113        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1114        ac = self.app['accesscodes']['HOS-0'][pin]
1115        parts = pin.split('-')[1:]
1116        sfeseries, sfenumber = parts
1117        # Students can use HOS code and book a bed space with it ...
1118        self.browser.open(self.acco_path)
1119        # ... but not if booking period has expired ...
1120        self.app['hostels'].enddate = datetime.now(pytz.utc)
1121        self.browser.getControl("Book accommodation").click()
1122        self.assertMatches('...Outside booking period: ...',
1123                           self.browser.contents)
1124        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
1125        # ... or student data are incomplete ...
1126        self.student['studycourse'].current_level = None
1127        self.browser.getControl("Book accommodation").click()
1128        self.assertMatches('...Your data are incomplete...',
1129            self.browser.contents)
1130        self.student['studycourse'].current_level = 200
1131        # ... or student is not the an allowed state ...
1132        self.browser.getControl("Book accommodation").click()
1133        self.assertMatches('...You are in the wrong...',
1134                           self.browser.contents)
1135        self.app['hostels'].accommodation_states = ['admitted', 'school fee paid']
1136        IWorkflowState(self.student).setState('school fee paid')
1137        # ... or student has not appropriate verdict (Uniben only!)
1138        self.student['studycourse'].entry_session = 2000 # non-fresh
1139        self.student['studycourse'].current_level = 500 # final-year
1140        self.student['studycourse'].current_verdict = 'C'
1141        self.browser.getControl("Book accommodation").click()
1142        self.assertMatches('...Your are not eligible...',
1143            self.browser.contents)
1144        self.student['studycourse'].previous_verdict = 'A'
1145        self.browser.getControl("Book accommodation").click()
1146        self.assertMatches('...Activation Code:...',
1147                           self.browser.contents)
1148        # Student can't use faked ACs ...
1149        self.browser.getControl(name="ac_series").value = u'nonsense'
1150        self.browser.getControl(name="ac_number").value = sfenumber
1151        self.browser.getControl("Create bed ticket").click()
1152        self.assertMatches('...Activation code is invalid...',
1153                           self.browser.contents)
1154        # ... or ACs owned by somebody else.
1155        ac.owner = u'Anybody'
1156        self.browser.getControl(name="ac_series").value = sfeseries
1157        self.browser.getControl(name="ac_number").value = sfenumber
1158        self.browser.getControl("Create bed ticket").click()
1159        self.assertMatches('...You are not the owner of this access code...',
1160                           self.browser.contents)
1161        # The bed remains empty.
1162        bed = self.app['hostels']['hall-1']['hall-1_A_101_C']
1163        self.assertTrue(bed.owner == NOT_OCCUPIED)
1164        ac.owner = self.student_id
1165        self.browser.getControl(name="ac_series").value = sfeseries
1166        self.browser.getControl(name="ac_number").value = sfenumber
1167        self.browser.getControl("Create bed ticket").click()
1168        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1169                           self.browser.contents)
1170        # Bed has been allocated.
1171        self.assertTrue(bed.owner == self.student_id)
1172        # BedTicketAddPage is now blocked.
1173        self.browser.getControl("Book accommodation").click()
1174        self.assertMatches('...You already booked a bed space...',
1175            self.browser.contents)
1176        # The bed ticket displays the data correctly.
1177        self.browser.open(self.acco_path + '/2004')
1178        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1179                           self.browser.contents)
1180        self.assertMatches('...2004/2005...', self.browser.contents)
1181        self.assertMatches('...regular_male_fi...', self.browser.contents)
1182        self.assertMatches('...%s...' % pin, self.browser.contents)
1183        # Students can open the pdf slip.
1184        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
1185        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1186        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1187        path = os.path.join(samples_dir(), 'bed_allocation_slip.pdf')
1188        open(path, 'wb').write(self.browser.contents)
1189        print "Sample PDF bed_allocation_slip.pdf written to %s" % path
1190        # Students can't relocate themselves.
1191        self.assertFalse('Relocate' in self.browser.contents)
1192        relocate_path = self.acco_path + '/2004/relocate'
1193        self.assertRaises(
1194            Unauthorized, self.browser.open, relocate_path)
1195        # Students can't see the Remove button and check boxes.
1196        self.browser.open(self.acco_path)
1197        self.assertFalse('Remove' in self.browser.contents)
1198        self.assertFalse('val_id' in self.browser.contents)
1199        # Students can pay maintenance fee now.
1200        self.browser.open(self.payments_path)
1201        self.browser.open(self.payments_path + '/addop')
1202        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1203        self.browser.getControl("Create ticket").click()
1204        self.assertMatches('...Payment ticket created...',
1205                           self.browser.contents)
1206        # Maintennace fee is taken from the hostel object.
1207        self.assertEqual(self.student['payments'].values()[1].amount_auth, 876.0)
1208        # If the hostel's maintenance fee isn't set, the fee is
1209        # taken from the session configuration object.
1210        self.app['hostels']['hall-1'].maint_fee = 0.0
1211        self.browser.open(self.payments_path + '/addop')
1212        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1213        self.browser.getControl("Create ticket").click()
1214        self.assertEqual(self.student['payments'].values()[2].amount_auth, 987.0)
1215        return
1216
1217    def test_student_clearance(self):
1218        # create some passport file for `student`
1219        storage = getUtility(IExtFileStore)
1220        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
1221        self.image_contents = open(image_path, 'rb').read()
1222        file_id = IFileStoreNameChooser(self.student).chooseName(
1223            attr='passport.jpg')
1224        storage.createFile(file_id, StringIO(self.image_contents))
1225        IWorkflowInfo(self.student).fireTransition('admit')
1226        self.browser.open(self.login_path)
1227        self.browser.getControl(name="form.login").value = self.student_id
1228        self.browser.getControl(name="form.password").value = 'spwd'
1229        self.browser.getControl("Login").click()
1230        self.assertMatches(
1231            '...Did you know that with...', self.browser.contents)
1232        self.browser.getLink("Base Data").click()
1233        self.browser.getLink("Download admission letter").click()
1234        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1235        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1236        path = os.path.join(samples_dir(), 'admission_slip.pdf')
1237        open(path, 'wb').write(self.browser.contents)
1238        print "Sample PDF admission_slip.pdf written to %s" % path
1239        self.browser.open(self.student_path + '/start_clearance')
1240        # Regular students have to enter an access code
1241        self.browser.getControl("Start clearance now").click()
1242        self.assertTrue('Activation code is invalid' in self.browser.contents)
1243        # DCOEM students can start clearance without access code
1244        self.app['faculties']['fac1'].code = 'DCOEM'
1245        self.browser.open(self.student_path + '/start_clearance')
1246        self.browser.getControl("Start clearance now").click()
1247        self.assertMatches('...Clearance process has been started...',
1248                           self.browser.contents)
1249
1250    def test_student_medical_questionnaire(self):
1251        self.app['configuration']['2004'].medical_quest_fee = 1000.0
1252        IWorkflowInfo(self.student).fireTransition('admit')
1253        self.browser.open(self.login_path)
1254        self.browser.getControl(name="form.login").value = self.student_id
1255        self.browser.getControl(name="form.password").value = 'spwd'
1256        self.browser.getControl("Login").click()
1257        self.assertMatches(
1258            '...Did you know that with...', self.browser.contents)
1259        self.browser.getLink("Base Data").click()
1260        self.browser.getLink("Medical Questionnaire").click()
1261        self.assertTrue('Please pay medical questionnaire payment first' in
1262            self.browser.contents)
1263        self.assertEqual(self.browser.url, self.student_path)
1264        self.browser.open(self.payments_path)
1265        self.browser.getLink("Add current session payment ticket").click()
1266        self.browser.getControl(name="form.p_category").value = ['medical_quest']
1267        self.browser.getControl("Create ticket").click()
1268        p_ticket = self.student['payments'].values()[0]
1269        p_ticket.approveStudentPayment()
1270        self.browser.getLink("Base Data").click()
1271        self.browser.getLink("Medical Questionnaire").click()
1272        self.browser.getLink("Download medical questionnaire slip").click()
1273        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1274        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1275        path = os.path.join(samples_dir(), 'medical_questionnaire_slip.pdf')
1276        open(path, 'wb').write(self.browser.contents)
1277        print "Sample PDF medical_questionnaire_slip.pdf written to %s" % path
1278
1279        self.browser.open(self.student_path)
1280        self.browser.getLink("Medical Questionnaire").click()
1281        self.browser.getControl(name="form.bloodgroup").value = 'A'
1282        self.browser.getControl(name="form.genotype").value = 'XYZ'
1283        self.browser.getControl("Save").click()
1284        self.browser.getLink("Base Data").click()
1285        self.browser.getLink("Download TISHIP registration slip").click()
1286        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1287        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1288        path = os.path.join(samples_dir(), 'tiship_slip.pdf')
1289        open(path, 'wb').write(self.browser.contents)
1290        print "Sample PDF tiship_slip.pdf written to %s" % path
1291
1292    def test_fiancial_clearance_pdf_slip(self):
1293        payment1 = createObject(u'waeup.StudentOnlinePayment')
1294        timestamp = ("%d" % int(time()*10000))[1:]
1295        payment1.p_id = "LSCNEW-2-4153206270" # the longest possible p_id
1296        payment1.p_category = 'schoolfee'
1297        payment1.p_item = u'My School Fee'
1298        payment1.p_session = 2015
1299        payment1.p_level = 100
1300        payment1.p_current = True
1301        payment1.amount_auth = 23456.9
1302        payment1.approve()
1303        payment2 = createObject(u'waeup.StudentOnlinePayment')
1304        timestamp = ("%d" % int(time()*10000))[1:]
1305        payment2.p_id = "p%s" % timestamp
1306        payment2.p_category = 'clearance'
1307        payment2.p_item = u'My Clearance Fee'
1308        payment2.p_session = 2015
1309        payment2.p_level = 100
1310        payment2.p_current = True
1311        payment2.amount_auth = 5678.6
1312        payment2.approve()
1313        self.student['payments'][payment1.p_id] = payment1
1314        self.student['payments'][payment2.p_id] = payment2
1315        self.student.nysc = True
1316        self.student.nysc_updated = datetime.now(pytz.utc)
1317        self.student.nysc_senate_info = u'Senate Info test'
1318        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1319        self.browser.open(self.student_path + '/clear_financially')
1320        self.browser.getLink("Download fee payment history").click()
1321        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1322        self.assertEqual(self.browser.headers['Content-Type'],
1323                         'application/pdf')
1324        path = os.path.join(samples_dir(), 'fee_payment_history.pdf')
1325        open(path, 'wb').write(self.browser.contents)
1326        print "Sample PDF fee_payment_history.pdf written to %s" % path
1327        return
1328
1329    def test_affidavit_slip(self):
1330        self.browser.open(self.login_path)
1331        self.browser.getControl(name="form.login").value = self.student_id
1332        self.browser.getControl(name="form.password").value = 'spwd'
1333        self.browser.getControl("Login").click()
1334        self.browser.getLink("Base Data").click()
1335        self.browser.getLink("Upload affidavit of good conduct").click()
1336        self.browser.getLink("Download affidavit of good conduct form").click()
1337        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1338        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1339        path = os.path.join(samples_dir(), 'affidavit_good_conduct.pdf')
1340        open(path, 'wb').write(self.browser.contents)
1341        print "Sample PDF affidavit_good_conduct.pdf written to %s" % path
Note: See TracBrowser for help on using the repository browser.