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

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

Show gpa on course result slip.

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