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

Last change on this file since 15554 was 15403, checked in by Henrik Bettermann, 6 years ago

Reset library flag after returning school fee payment.

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