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

Last change on this file since 16238 was 16171, checked in by Henrik Bettermann, 4 years ago

Show signature boxes only in states 'transcript validated' and 'transcript released'.

  • Property svn:keywords set to Id
File size: 61.4 KB
Line 
1## $Id: test_browser.py 16171 2020-07-19 20:56:10Z 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')
47SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
48
49class OfficerUITests(StudentsFullSetup):
50    # Tests for Student class views and pages
51
52    layer = FunctionalLayer
53
54    def test_jhl_idcard_officer(self):
55        # Create library officer
56        self.app['users'].addUser('mrlibrary', SECRET)
57        self.app['users']['mrlibrary'].email = 'library@foo.ng'
58        self.app['users']['mrlibrary'].title = 'Carlo Pitter'
59        prmglobal = IPrincipalRoleManager(self.app)
60        prmglobal.assignRoleToPrincipal(
61            'waeup.LibraryClearanceOfficer', 'mrlibrary')
62        prmglobal.assignRoleToPrincipal(
63            'waeup.StudentsOfficer', 'mrlibrary')
64        self.browser.open(self.login_path)
65        self.browser.getControl(name="form.login").value = 'mrlibrary'
66        self.browser.getControl(name="form.password").value = SECRET
67        self.browser.getControl("Login").click()
68        self.assertMatches('...You logged in...', self.browser.contents)
69        self.browser.open(self.student_path)
70        self.assertFalse('JHL' in self.browser.contents)
71        self.browser.getLink("Switch library access").click()
72        self.assertTrue('Library access enabled' in self.browser.contents)
73        self.assertTrue('JHL' in self.browser.contents)
74        self.browser.getLink("Download JHL Id Card").click()
75        self.assertEqual(self.browser.headers['Status'], '200 Ok')
76        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
77        path = os.path.join(samples_dir(), 'jhl_idcard_officer.pdf')
78        open(path, 'wb').write(self.browser.contents)
79        print "Sample PDF jhl_idcard_officer.pdf written to %s" % path
80
81class StudentUITests(StudentsFullSetup):
82    """Tests for customized student class views and pages
83    """
84
85    layer = FunctionalLayer
86
87    def setUp(self):
88        super(StudentUITests, self).setUp()
89
90    def test_next_session_allowed(self):
91        # Let's see if next_session_allowed works as expected
92        # A, ug_ft, 100
93        IWorkflowState(self.student).setState('returning')
94        self.assertTrue(self.student['studycourse'].next_session_allowed)
95        # Uniben special PG programmes have the same workflow
96        # as UG students
97        self.certificate.study_mode = 'special_pg_pt'
98        self.assertTrue(self.student['studycourse'].next_session_allowed)
99        IWorkflowState(self.student).setState('school fee paid')
100        self.assertFalse(self.student['studycourse'].next_session_allowed)
101        # Now we convert the certificate into a 'regular
102        # postgraduate certificate ...
103        self.certificate.study_mode = 'pg_ft'
104        # ... and voila next session registration is allowed
105        self.assertTrue(self.student['studycourse'].next_session_allowed)
106
107    def test_manage_access(self):
108        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
109        # The student created in the base package is a ug student
110        self.browser.open(self.manage_clearance_path)
111        self.assertMatches('...First Sitting Record...',
112                           self.browser.contents)
113        # There is no pg field in the clearance form
114        self.assertFalse('Second Higher Education Record'
115            in self.browser.contents)
116        # Now we change the study mode ...
117        self.certificate.study_mode = 'pg_ft'
118        self.browser.open(self.clearance_path)
119        # ... and additional pg clearance fields appears
120        self.assertMatches('...Second Higher Education Record...',
121                           self.browser.contents)
122        # But also fields from the ug form are displayed
123        self.assertMatches('...First Sitting Record...',
124                           self.browser.contents)
125        # The same holds for Uniben's special pg students
126        self.certificate.study_mode = 'special_pg_pt'
127        self.browser.open(self.clearance_path)
128        self.assertMatches('...Second Higher Education Record...',
129                           self.browser.contents)
130        self.assertMatches('...First Sitting Record...',
131                           self.browser.contents)
132        # We want to see the signature fields.
133        IWorkflowState(self.student).setState('returning')
134        self.browser.open(self.student_path + '/clearance_slip.pdf')
135        self.assertEqual(self.browser.headers['Status'], '200 Ok')
136        self.assertEqual(self.browser.headers['Content-Type'],
137                         'application/pdf')
138        path = os.path.join(samples_dir(), 'clearance_slip.pdf')
139        open(path, 'wb').write(self.browser.contents)
140        print "Sample PDF clearance_slip.pdf written to %s" % path
141
142    def test_student_access(self):
143        # Students can edit clearance data
144        IWorkflowState(self.student).setState('clearance started')
145        self.student.nationality = u'NG'
146        file_store = getUtility(IExtFileStore)
147        self.browser.open(self.login_path)
148        self.browser.getControl(name="form.login").value = self.student_id
149        self.browser.getControl(name="form.password").value = 'spwd'
150        self.browser.getControl("Login").click()
151        self.browser.open(self.edit_clearance_path)
152        # UG students can't edit date_of_birth, nationality and lga
153        self.assertFalse('form.date_of_birth' in self.browser.contents)
154        self.assertFalse('form.nationality' in self.browser.contents)
155        self.assertFalse('form.lga' in self.browser.contents)
156        # Clearance can only be requested if all required documents
157        # have been uploaded.
158        self.browser.getControl("Save and request clearance").click()
159        self.assertTrue('No birth certificate uploaded'
160            in self.browser.contents)
161        birth_certificate = 'My birth certificate'
162        file_id = IFileStoreNameChooser(self.student).chooseName(
163            attr="birth_certificate.jpg")
164        file_store.createFile(file_id, StringIO(birth_certificate))
165        self.browser.open(self.edit_clearance_path)
166        self.browser.getControl("Save and request clearance").click()
167
168        self.assertTrue('No guarantor/referee letter uploaded'
169            in self.browser.contents)
170        ref_let = 'My ref let'
171        file_id = IFileStoreNameChooser(self.student).chooseName(
172            attr="ref_let.jpg")
173        file_store.createFile(file_id, StringIO(ref_let))
174        self.browser.open(self.edit_clearance_path)
175        self.browser.getControl("Save and request clearance").click()
176
177        self.assertTrue('No acceptance letter uploaded'
178            in self.browser.contents)
179        acc_let = 'My acc let'
180        file_id = IFileStoreNameChooser(self.student).chooseName(
181            attr="acc_let.jpg")
182        file_store.createFile(file_id, StringIO(acc_let))
183        self.browser.open(self.edit_clearance_path)
184        self.browser.getControl("Save and request clearance").click()
185
186        self.assertTrue('No first sitting result uploaded'
187            in self.browser.contents)
188        fst_sit_scan = 'My first sitting result'
189        file_id = IFileStoreNameChooser(self.student).chooseName(
190            attr="fst_sit_scan.jpg")
191        file_store.createFile(file_id, StringIO(fst_sit_scan))
192        self.browser.open(self.edit_clearance_path)
193        self.browser.getControl("Save and request clearance").click()
194
195        #self.assertTrue('No second sitting result uploaded'
196        #    in self.browser.contents)
197        #scd_sit_scan = 'My second sitting result'
198        #file_id = IFileStoreNameChooser(self.student).chooseName(
199        #    attr="scd_sit_scan.jpg")
200        #file_store.createFile(file_id, StringIO(scd_sit_scan))
201        #self.browser.open(self.edit_clearance_path)
202        #self.browser.getControl("Save and request clearance").click()
203
204        self.assertTrue('No affidavit of non-membership of secret cults uploaded'
205            in self.browser.contents)
206        secr_cults = 'My non-membership scan'
207        file_id = IFileStoreNameChooser(self.student).chooseName(
208            attr="secr_cults.jpg")
209        file_store.createFile(file_id, StringIO(secr_cults))
210        # Clearance invitation letter is not yet available
211        self.browser.open(self.clearance_path)
212        self.assertFalse('invitation slip' in self.browser.contents)
213        self.browser.open(self.student_path + '/clearance_invitation_slip.pdf')
214        self.assertTrue('Forbidden' in self.browser.contents)
215        self.browser.open(self.edit_clearance_path)
216        self.browser.getControl("Save and request clearance").click()
217        self.assertTrue('Clearance has been requested'
218            in self.browser.contents)
219        # Now student can export physical_clearance.slip
220        self.app['configuration'].name = u'University of Benin'
221        self.student.physical_clearance_date = u'January 5th, 2015'
222        self.browser.getLink("Clearance Data").click()
223        self.browser.getLink("Download clearance invitation slip").click()
224        self.assertEqual(self.browser.headers['Status'], '200 Ok')
225        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
226        path = os.path.join(samples_dir(), 'clearance_invitation_slip.pdf')
227        open(path, 'wb').write(self.browser.contents)
228        print "Sample PDF clearance_invitation_slip.pdf written to %s" % path
229        # Students can open the personal edit page and see the parent_email field.
230        self.browser.open(self.student_path + '/edit_personal')
231        self.assertTrue('parent_email' in self.browser.contents)
232
233    def test_examination_schedule_slip(self):
234        self.student.flash_notice = u'My Examination Date'
235        self.browser.open(self.login_path)
236        self.browser.getControl(name="form.login").value = self.student_id
237        self.browser.getControl(name="form.password").value = 'spwd'
238        self.browser.getControl("Login").click()
239        self.browser.getLink("Download examination schedule slip").click()
240        self.assertEqual(self.browser.headers['Status'], '200 Ok')
241        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
242        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
243        open(path, 'wb').write(self.browser.contents)
244        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
245        # If flash_notive does not contain exam' the button does not show up.
246        self.student.flash_notice = u'anything'
247        self.browser.open(self.student_path)
248        self.assertFalse('examination schedule slip' in self.browser.contents)
249
250    def test_jhl_idcard(self):
251        IWorkflowState(self.student).setState('returning')
252        self.browser.open(self.login_path)
253        self.browser.getControl(name="form.login").value = self.student_id
254        self.browser.getControl(name="form.password").value = 'spwd'
255        self.browser.getControl("Login").click()
256        self.assertFalse('JHL' in self.browser.contents)
257        self.student.library = True
258        self.browser.open(self.student_path)
259        self.assertTrue('JHL' in self.browser.contents)
260        self.browser.getLink("Download JHL Id Card").click()
261        self.assertEqual(self.browser.headers['Status'], '200 Ok')
262        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
263        path = os.path.join(samples_dir(), 'jhl_idcard_student.pdf')
264        open(path, 'wb').write(self.browser.contents)
265        print "Sample PDF jhl_idcard_student.pdf written to %s" % path
266        self.assertTrue(self.student.library)
267        IWorkflowInfo(self.student).fireTransition('pay_school_fee')
268        self.assertFalse(self.student.library)
269
270    def test_jupeb_result_slip(self):
271        self.student.flash_notice = u'My JUPEB results'
272        self.browser.open(self.login_path)
273        self.browser.getControl(name="form.login").value = self.student_id
274        self.browser.getControl(name="form.password").value = 'spwd'
275        self.browser.getControl("Login").click()
276        self.assertFalse('JUPEB result slip' in self.browser.contents)
277        # Create JUPEB faculty
278        cert = createObject('waeup.Certificate')
279        cert.code = u'xyz'
280        self.app['faculties']['JUPEB123'] = Faculty(code=u'JUPEB123')
281        self.app['faculties']['JUPEB123']['dep1'] = Department(code=u'dep1')
282        self.app['faculties']['JUPEB123']['dep1'].certificates.addCertificate(cert)
283        self.student['studycourse'].certificate = cert
284        self.browser.open(self.student_path)
285        self.browser.getLink("Download JUPEB result slip").click()
286        self.assertEqual(self.browser.headers['Status'], '200 Ok')
287        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
288        path = os.path.join(samples_dir(), 'jupeb_result_slip.pdf')
289        open(path, 'wb').write(self.browser.contents)
290        print "Sample PDF jupeb_result_slip.pdf written to %s" % path
291        self.student.flash_notice = u''
292        self.browser.open(self.student_path)
293        self.assertFalse('JUPEB result slip' in self.browser.contents)
294
295    def test_manage_payments(self):
296        # Add missing configuration data
297        self.app['configuration']['2004'].gown_fee = 150.0
298        self.app['configuration']['2004'].transfer_fee = 90.0
299        #self.app['configuration']['2004'].clearance_fee = 120.0
300        self.app['configuration']['2004'].booking_fee = 150.0
301        self.app['configuration']['2004'].maint_fee = 180.0
302
303        # Managers can add online payment tickets
304        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
305        self.browser.open(self.payments_path)
306        self.browser.getLink("Add current session payment ticket").click()
307        self.browser.getControl(name="form.p_category").value = ['schoolfee']
308        self.browser.getControl("Create ticket").click()
309        self.assertMatches('...Amount could not be determined...',
310                           self.browser.contents)
311        IWorkflowState(self.student).setState('cleared')
312        self.student.nationality = u'NG'
313        self.browser.open(self.payments_path + '/addop')
314        self.browser.getControl(name="form.p_category").value = ['schoolfee']
315        self.browser.getControl("Create ticket").click()
316        self.assertMatches('...ticket created...',
317                           self.browser.contents)
318        ctrl = self.browser.getControl(name='val_id')
319        value = ctrl.options[0]
320        self.browser.getLink(value).click()
321        self.assertMatches('...Amount Authorized...',
322                           self.browser.contents)
323        # Managers can open payment slip because we did not proceed to
324        # any payment gateway
325        self.assertFalse('Download payment slip' in self.browser.contents)
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, 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, None)
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, None)
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, None)
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, None)
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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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        IWorkflowState(self.student).setState('transcript validated')
995        studylevel.level = 100
996        studylevel.level_session = 2005
997        self.student['studycourse'].entry_mode = 'ug_ft'
998        self.student['studycourse'].addStudentStudyLevel(
999            self.certificate, studylevel)
1000        studylevel2 = createObject(u'waeup.StudentStudyLevel')
1001        studylevel2.level = 110
1002        studylevel2.level_session = 2006
1003        self.student['studycourse'].addStudentStudyLevel(
1004            self.certificate, studylevel2)
1005        # Add second course (COURSE has been added automatically)
1006        courseticket = createObject('waeup.CourseTicket')
1007        courseticket.code = 'ANYCODE'
1008        courseticket.title = u'Any TITLE'
1009        courseticket.credits = 13
1010        courseticket.score = 66
1011        courseticket.semester = 1
1012        courseticket.dcode = u'ANYDCODE'
1013        courseticket.fcode = u'ANYFCODE'
1014        self.student['studycourse']['110']['COURSE2'] = courseticket
1015        self.student['studycourse']['100']['COURSE1'].score = 55
1016        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
1017        self.assertEqual(self.student['studycourse']['110'].gpa_params_rectified[0], 4.0)
1018        # Get transcript data
1019        td = self.student['studycourse'].getTranscriptData()
1020        self.assertEqual(td[0][0]['level_key'], '100')
1021        self.assertEqual(td[0][0]['sgpa'], 3.0)
1022        self.assertEqual(td[0][0]['level'].level, 100)
1023        self.assertEqual(td[0][0]['level'].level_session, 2005)
1024        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
1025        self.assertEqual(td[0][1]['level_key'], '110')
1026        self.assertEqual(td[0][1]['sgpa'], 4.0)
1027        self.assertEqual(td[0][1]['level'].level, 110)
1028        self.assertEqual(td[0][1]['level'].level_session, 2006)
1029        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
1030        self.assertEqual(td[1], 3.5652173913043477)
1031        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1032        self.browser.open(self.student_path + '/studycourse/transcript')
1033        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1034        self.assertTrue('Transcript' in self.browser.contents)
1035        # Officers can open the pdf transcript
1036        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
1037        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1038        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1039        path = os.path.join(samples_dir(), 'transcript.pdf')
1040        open(path, 'wb').write(self.browser.contents)
1041        print "Sample PDF transcript.pdf written to %s" % path
1042
1043    def test_student_accommodation(self):
1044        self.app['hostels'].allocation_expiration = 7
1045        self.student['accommodation'].desired_hostel = u'hall-1'
1046        bed = Bed()
1047        bed.bed_id = u'hall-1_A_101_C'
1048        bed.bed_number = 3
1049        bed.owner = NOT_OCCUPIED
1050        bed.bed_type = u'regular_male_fi'
1051        self.app['hostels']['hall-1'].addBed(bed)
1052        self.browser.open(self.login_path)
1053        self.browser.getControl(name="form.login").value = self.student_id
1054        self.browser.getControl(name="form.password").value = 'spwd'
1055        self.browser.getControl("Login").click()
1056        # Students can add online booking fee payment tickets and open the
1057        # callback view (see test_manage_payments).
1058        self.browser.getLink("Payments").click()
1059        self.browser.getLink("Add current session payment ticket").click()
1060        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1061        self.browser.getControl("Create ticket").click()
1062        p_ticket = self.student['payments'].values()[0]
1063        self.assertEqual(p_ticket.p_item, 'regular_male_fr (hall-1)')
1064        p_ticket.approveStudentPayment()
1065        # The new HOS-0 pin has been created.
1066        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
1067        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1068        ac = self.app['accesscodes']['HOS-0'][pin]
1069        parts = pin.split('-')[1:]
1070        sfeseries, sfenumber = parts
1071        # Students can use HOS code and book a bed space with it ...
1072        self.browser.open(self.acco_path)
1073        # ... but not if booking period has expired ...
1074        self.app['hostels'].enddate = datetime.now(pytz.utc)
1075        self.browser.getControl("Book accommodation").click()
1076        self.assertMatches('...Outside booking period: ...',
1077                           self.browser.contents)
1078        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
1079        # ... or student data are incomplete ...
1080        self.student['studycourse'].current_level = None
1081        self.browser.getControl("Book accommodation").click()
1082        self.assertMatches('...Your data are incomplete...',
1083            self.browser.contents)
1084        self.student['studycourse'].current_level = 200
1085        # ... or student is not the an allowed state ...
1086        self.browser.getControl("Book accommodation").click()
1087        self.assertMatches('...You are in the wrong...',
1088                           self.browser.contents)
1089        self.app['hostels'].accommodation_states = ['admitted', 'school fee paid']
1090        IWorkflowState(self.student).setState('school fee paid')
1091        # ... or student has not appropriate verdict (Uniben only!)
1092        self.student['studycourse'].entry_session = 2000 # non-fresh
1093        self.student['studycourse'].current_level = 500 # final-year
1094        self.student['studycourse'].current_verdict = 'C'
1095        self.browser.getControl("Book accommodation").click()
1096        self.assertMatches('...Your are not eligible...',
1097            self.browser.contents)
1098        self.student['studycourse'].previous_verdict = 'A'
1099        self.browser.getControl("Book accommodation").click()
1100        self.assertMatches('...Activation Code:...',
1101                           self.browser.contents)
1102        # Student can't use faked ACs ...
1103        self.browser.getControl(name="ac_series").value = u'nonsense'
1104        self.browser.getControl(name="ac_number").value = sfenumber
1105        self.browser.getControl("Create bed ticket").click()
1106        self.assertMatches('...Activation code is invalid...',
1107                           self.browser.contents)
1108        # ... or ACs owned by somebody else.
1109        ac.owner = u'Anybody'
1110        self.browser.getControl(name="ac_series").value = sfeseries
1111        self.browser.getControl(name="ac_number").value = sfenumber
1112        self.browser.getControl("Create bed ticket").click()
1113        self.assertMatches('...You are not the owner of this access code...',
1114                           self.browser.contents)
1115        # The bed remains empty.
1116        bed = self.app['hostels']['hall-1']['hall-1_A_101_C']
1117        self.assertTrue(bed.owner == NOT_OCCUPIED)
1118        ac.owner = self.student_id
1119        self.browser.getControl(name="ac_series").value = sfeseries
1120        self.browser.getControl(name="ac_number").value = sfenumber
1121        self.browser.getControl("Create bed ticket").click()
1122        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1123                           self.browser.contents)
1124        # Bed has been allocated.
1125        self.assertTrue(bed.owner == self.student_id)
1126        # BedTicketAddPage is now blocked.
1127        self.browser.getControl("Book accommodation").click()
1128        self.assertMatches('...You already booked a bed space...',
1129            self.browser.contents)
1130        # The bed ticket displays the data correctly.
1131        self.browser.open(self.acco_path + '/2004')
1132        self.assertMatches('...Hall 1, Block/Unit A, Room 101, Bed C...',
1133                           self.browser.contents)
1134        self.assertMatches('...2004/2005...', self.browser.contents)
1135        self.assertMatches('...regular_male_fi...', self.browser.contents)
1136        self.assertMatches('...%s...' % pin, self.browser.contents)
1137        # Students can open the pdf slip.
1138        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
1139        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1140        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1141        path = os.path.join(samples_dir(), 'bed_allocation_slip.pdf')
1142        open(path, 'wb').write(self.browser.contents)
1143        print "Sample PDF bed_allocation_slip.pdf written to %s" % path
1144        # Students can't relocate themselves.
1145        self.assertFalse('Relocate' in self.browser.contents)
1146        relocate_path = self.acco_path + '/2004/relocate'
1147        self.assertRaises(
1148            Unauthorized, self.browser.open, relocate_path)
1149        # Students can't see the Remove button and check boxes.
1150        self.browser.open(self.acco_path)
1151        self.assertFalse('Remove' in self.browser.contents)
1152        self.assertFalse('val_id' in self.browser.contents)
1153        # Students can pay maintenance fee now.
1154        self.browser.open(self.payments_path)
1155        self.browser.open(self.payments_path + '/addop')
1156        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1157        self.browser.getControl("Create ticket").click()
1158        self.assertMatches('...Payment ticket created...',
1159                           self.browser.contents)
1160        # Maintennace fee is taken from the hostel object.
1161        self.assertEqual(self.student['payments'].values()[1].amount_auth, 876.0)
1162        # If the hostel's maintenance fee isn't set, the fee is
1163        # taken from the session configuration object.
1164        self.app['hostels']['hall-1'].maint_fee = 0.0
1165        self.browser.open(self.payments_path + '/addop')
1166        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
1167        self.browser.getControl("Create ticket").click()
1168        self.assertEqual(self.student['payments'].values()[2].amount_auth, 987.0)
1169        return
1170
1171    def test_student_clearance(self):
1172        # create some passport file for `student`
1173        storage = getUtility(IExtFileStore)
1174        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
1175        self.image_contents = open(image_path, 'rb').read()
1176        file_id = IFileStoreNameChooser(self.student).chooseName(
1177            attr='passport.jpg')
1178        storage.createFile(file_id, StringIO(self.image_contents))
1179        IWorkflowInfo(self.student).fireTransition('admit')
1180        self.browser.open(self.login_path)
1181        self.browser.getControl(name="form.login").value = self.student_id
1182        self.browser.getControl(name="form.password").value = 'spwd'
1183        self.browser.getControl("Login").click()
1184        self.assertMatches(
1185            '...You logged in...', self.browser.contents)
1186        self.browser.getLink("Base Data").click()
1187        self.browser.getLink("Download admission letter").click()
1188        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1189        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1190        path = os.path.join(samples_dir(), 'admission_slip.pdf')
1191        open(path, 'wb').write(self.browser.contents)
1192        print "Sample PDF admission_slip.pdf written to %s" % path
1193        self.browser.open(self.student_path + '/start_clearance')
1194        # Regular students have to enter an access code
1195        self.browser.getControl("Start clearance now").click()
1196        self.assertTrue('Activation code is invalid' in self.browser.contents)
1197        # DCOEM students can start clearance without access code
1198        self.app['faculties']['fac1'].code = 'DCOEM'
1199        self.browser.open(self.student_path + '/start_clearance')
1200        self.browser.getControl("Start clearance now").click()
1201        self.assertMatches('...Clearance process has been started...',
1202                           self.browser.contents)
Note: See TracBrowser for help on using the repository browser.