source: main/waeup.aaue/trunk/src/waeup/aaue/students/tests/test_browser.py @ 18079

Last change on this file since 18079 was 18040, checked in by Henrik Bettermann, 3 months ago

Removal of EDOHIS and ACCESS card.

  • Property svn:keywords set to Id
File size: 75.5 KB
Line 
1## $Id: test_browser.py 18040 2025-03-13 12:31:04Z 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
22import grok
23from StringIO import StringIO
24from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
25from zope.securitypolicy.interfaces import IPrincipalRoleManager
26from datetime import datetime, timedelta, date
27from mechanize import LinkNotFoundError, ItemNotFoundError
28from hurry.workflow.interfaces import IWorkflowState
29from zope.event import notify
30from zope.component.hooks import setSite, clearSite
31from zope.component import getUtility, createObject, queryUtility
32from zope.catalog.interfaces import ICatalog
33from waeup.kofa.app import University
34from waeup.kofa.interfaces import VALIDATED, PAID
35from waeup.kofa.students.tests.test_browser import StudentsFullSetup
36from waeup.kofa.students.accommodation import BedTicket
37from waeup.kofa.testing import FunctionalTestCase
38from waeup.kofa.authentication import LocalRoleSetEvent
39from waeup.kofa.browser.tests.test_pdf import samples_dir
40from waeup.kofa.applicants.container import ApplicantsContainer
41from waeup.aaue.testing import FunctionalLayer
42
43SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
44
45
46UPLOAD_CSV_TEMPLATE = (
47    'matric_number,student_id,display_fullname,level,code,level_session,'
48    'score,ca,imported_ts\r\n'
49    '234,E1000000,Anna Tester,100,COURSE1,2004,%s,%s,%s\r\n')
50
51class OfficerUITests(StudentsFullSetup):
52    # Tests for Student class views and pages
53
54    layer = FunctionalLayer
55
56    def setUp(self):
57        super(OfficerUITests, self).setUp()
58        self.app['faculties']['fac1']['dep1'].certificates[
59            'CERT1']['COURSE1_100'].course_category = 'C'
60
61    def login_as_lecturer(self):
62        self.app['users'].addUser('mrslecturer', 'mrslecturerSecret1')
63        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
64        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
65        # Add course ticket
66        studylevel = createObject(u'waeup.StudentStudyLevel')
67        studylevel.level = 100
68        studylevel.level_session = 2004
69        self.student['studycourse'].addStudentStudyLevel(
70            self.certificate, studylevel)
71        # Assign local Lecturer role for a certificate.
72        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
73        prmlocal = IPrincipalRoleManager(course)
74        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
75        notify(LocalRoleSetEvent(
76            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
77        # Login as lecturer.
78        self.browser.open(self.login_path)
79        self.browser.getControl(name="form.login").value = 'mrslecturer'
80        self.browser.getControl(
81            name="form.password").value = 'mrslecturerSecret1'
82        self.browser.getControl("Login").click()
83        # Store reused urls/paths
84        self.course_url = (
85            'http://localhost/app/faculties/fac1/dep1/courses/COURSE1')
86        self.edit_scores_url = '%s/edit_scores' % self.course_url
87        self.edit_prev_scores_url = '%s/edit_prev_scores' % self.course_url
88        # Set standard parameters
89        self.app['configuration'].current_academic_session = 2004
90        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
91        IWorkflowState(self.student).setState(VALIDATED)
92
93
94    def test_gpa_calculation(self):
95        studylevel = createObject(u'waeup.StudentStudyLevel')
96        studylevel.level = 100
97        studylevel.level_session = 2005
98        self.student['studycourse'].entry_mode = 'ug_ft'
99        self.student['studycourse'].addStudentStudyLevel(
100            self.certificate, studylevel)
101        # First course has been added automatically.
102        # Set score.
103        studylevel['COURSE1'].score = 35
104        studylevel['COURSE1'].ca = 20
105        # GPA is 3.0.
106        self.assertEqual(studylevel.gpa_params[0], 3.0)
107        courseticket = createObject('waeup.CourseTicket')
108        courseticket.code = 'ANYCODE'
109        courseticket.title = u'Any TITLE'
110        courseticket.credits = 13
111        courseticket.score = 44
112        courseticket.ca = 22
113        courseticket.semester = 1
114        courseticket.dcode = u'ANYDCODE'
115        courseticket.fcode = u'ANYFCODE'
116        studylevel['COURSE2'] = courseticket
117        # total credits
118        self.assertEqual(self.student['studycourse']['100'].gpa_params[1], 23)
119        # weigheted credits = 3 * 10 + 4 * 13
120        self.assertEqual(self.student['studycourse']['100'].gpa_params[2], 82.0)
121        # sgpa = 82 / 23
122        self.assertEqual(
123            self.student['studycourse']['100'].gpa_params[0], 3.5652173913043477)
124        # imported gpa values override calculated values
125        studylevel.imported_gpa = 4.3
126        studylevel.imported_cgpa = 5.4
127        self.assertEqual(self.student['studycourse']['100'].gpa_params[0], 4.3)
128        self.assertEqual(
129            self.student['studycourse']['100'].cumulative_params[0], 5.4)
130        self.assertEqual(self.student['studycourse']['100'].gpa, '4.30')
131        self.student['studycourse'].imported_cgpa = 6.6
132        self.assertEqual(
133            self.student['studycourse'].getTranscriptData()[1], 6.6)
134        return
135
136    def test_grade_weight(self):
137        studylevel = createObject(u'waeup.StudentStudyLevel')
138        studylevel.level = 100
139        studylevel.level_session = 2005
140        self.course.passmark = 40
141        self.student['studycourse'].entry_mode = 'ug_ft'
142        self.student['studycourse'].addStudentStudyLevel(
143            self.certificate, studylevel)
144        studylevel['COURSE1'].score = 42
145        studylevel['COURSE1'].ca = 0
146        courseticket = createObject('waeup.CourseTicket')
147        self.assertEqual(studylevel['COURSE1'].weight, 1)
148        self.assertEqual(studylevel['COURSE1'].grade, 'E')
149        self.assertEqual(studylevel['COURSE1'].weight, 1)
150        self.assertEqual(studylevel['COURSE1'].grade, 'E')
151        studylevel['COURSE1'].score = 45
152        self.assertEqual(studylevel['COURSE1'].weight, 2)
153        self.assertEqual(studylevel['COURSE1'].grade, 'D')
154        return
155
156    def test_manage_payments(self):
157        # Add missing configuration data
158        self.app['configuration']['2004'].gown_fee = 150.0
159        self.app['configuration']['2004'].transfer_fee = 90.0
160        self.app['configuration']['2004'].booking_fee = 150.0
161        self.app['configuration']['2004'].maint_fee = 180.0
162        self.app['configuration']['2004'].late_fee = 80.0
163
164        # Managers can add online payment tickets
165        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
166        self.browser.open(self.payments_path)
167        self.browser.getLink("Add current session payment ticket").click()
168        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
169        self.browser.getControl("Create ticket").click()
170        self.assertMatches('...Wrong state...',
171                           self.browser.contents)
172        IWorkflowState(self.student).setState('cleared')
173        self.browser.open(self.payments_path + '/addop')
174        self.app['configuration']['2004'].clearance_fee = 666.0
175        self.browser.getControl(name="form.p_category").value = ['clearance_incl']
176        self.browser.getControl("Create ticket").click()
177        self.browser.open(self.payments_path)
178        ctrl = self.browser.getControl(name='val_id')
179        cpt_value = ctrl.options[0]
180        # School fee payment ticket can be added ...
181        #self.student['studycourse'].certificate.school_fee_3 = 6666.0
182        self.student.nationality = u'NG'
183        self.browser.open(self.payments_path + '/addop')
184        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
185        self.browser.getControl("Create ticket").click()
186        self.assertMatches('...ticket created...',
187                           self.browser.contents)
188        # ... but not paid through the query_history page.
189        self.browser.open(self.payments_path)
190        ctrl = self.browser.getControl(name='val_id')
191        sfpt_value = ctrl.options[1]
192        self.student['studycourse'].entry_session = 2013
193        self.student['payments'][sfpt_value].r_company = u'interswitch'
194        self.browser.open(self.payments_path + '/' + sfpt_value)
195
196        # eTranzact payments deactivated on 01/03/2019
197        #self.browser.getLink("Query eTranzact History").click()
198        #self.assertMatches('...alert-danger">Please pay acceptance fee first...',
199        #                   self.browser.contents)
200        # If clearance/acceptance fee is paid ...
201        self.student['payments'][cpt_value].approveStudentPayment()
202        #self.browser.getLink("Query eTranzact History").click()
203        ## ... query_history page is accessible.
204        #self.assertMatches(
205        #    '...<h1 class="kofa-content-label">Requery eTranzact History</h1>...',
206        #    self.browser.contents)
207
208        # Managers can open school fee payment slip
209        self.browser.open(self.payments_path + '/' + sfpt_value)
210        self.browser.getLink("Download payment slip").click()
211        self.assertEqual(self.browser.headers['Status'], '200 Ok')
212        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
213        # If school fee ticket is paid, the student is automatically set to
214        # school fee paid...
215        ticket = self.student['payments'][sfpt_value].approveStudentPayment()
216        self.assertEqual(self.student.state, 'school fee paid')
217        # ... no further school fee ticket can be added.
218        self.browser.open(self.payments_path + '/addop')
219        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
220        self.browser.getControl("Create ticket").click()
221        self.assertMatches('...Wrong state...',
222                           self.browser.contents)
223        self.browser.open(self.payments_path + '/addop')
224        #self.browser.getControl(name="form.p_category").value = ['late_registration']
225        #self.browser.getControl("Create ticket").click()
226        #self.assertMatches('...ticket created...',
227        #                   self.browser.contents)
228        return
229
230    def test_for_instalment_payments(self):
231        configuration_1 = createObject('waeup.SessionConfiguration')
232        configuration_1.academic_session = 2015
233        self.app['configuration'].addSessionConfiguration(configuration_1)
234        self.student['studycourse'].certificate.study_mode = 'ug_pt'
235        self.student.nationality = u'NG'
236        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
237        self.browser.open(self.payments_path + '/addop')
238        #self.assertFalse('schoolfee_1' in self.browser.contents)
239        #self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
240        #self.browser.getControl("Create ticket").click()
241        #self.assertTrue(
242        #    'Part-time students are not allowed' in self.browser.contents)
243        self.student['studycourse'].certificate.study_mode = 'ug_ft'
244        self.browser.open(self.payments_path + '/addop')
245        self.assertTrue('schoolfee_1' in self.browser.contents)
246        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
247        self.browser.getControl("Create ticket").click()
248        self.assertTrue('You are not allowed to pay by instalments.'
249            in self.browser.contents)
250        IWorkflowState(self.student).setState('cleared')
251        self.student['studycourse'].entry_session = 2015
252        self.student['studycourse'].current_session = 2015
253        self.browser.open(self.payments_path + '/addop')
254        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
255        self.browser.getControl("Create ticket").click()
256        self.assertTrue('ticket created' in self.browser.contents)
257        # We can't add the 2nd instalment ticket because the
258        # first one has not yet been approved.
259        #self.browser.open(self.payments_path + '/addop')
260        #self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
261        #self.browser.getControl("Create ticket").click()
262        #self.assertMatches('...1st school fee instalment has not yet been paid...',
263        #                   self.browser.contents)
264        # Ok, then we approve the first instalment ...
265        self.browser.open(self.payments_path)
266        ctrl = self.browser.getControl(name='val_id')
267        p_id = ctrl.options[0]
268        self.browser.open(self.payments_path + '/' + p_id + '/approve')
269        # ... add the second instalment ...
270        self.browser.open(self.payments_path + '/addop')
271        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
272        self.browser.getControl("Create ticket").click()
273        self.assertTrue('ticket created' in self.browser.contents)
274        # ... approve the second instalment ...
275        self.browser.open(self.payments_path)
276        ctrl = self.browser.getControl(name='val_id')
277        p_id = ctrl.options[1]
278        self.browser.open(self.payments_path + '/' + p_id + '/approve')
279        self.assertEqual(self.student['payments'].values()[0].p_category, 'schoolfee_1')
280        self.assertEqual(self.student['payments'].values()[1].p_category, 'schoolfee_2')
281        self.assertEqual(self.student['payments'].values()[0].amount_auth, 128000.0)
282        self.assertEqual(self.student['payments'].values()[1].amount_auth, 92000.0)
283        # The  two payments belong to the same session and level.
284        self.assertEqual(self.student['payments'].values()[0].p_session, 2015)
285        self.assertEqual(self.student['payments'].values()[0].p_level, 100)
286        self.assertEqual(self.student['payments'].values()[1].p_session, 2015)
287        self.assertEqual(self.student['payments'].values()[1].p_level, 100)
288
289        # Returning student
290        configuration_2 = createObject('waeup.SessionConfiguration')
291        configuration_2.academic_session = 2016
292        self.app['configuration'].addSessionConfiguration(configuration_2)
293        self.student.father_name = u'Albert'
294        IWorkflowState(self.student).setState('returning')
295        self.browser.open(self.payments_path + '/addop')
296        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
297        self.browser.getControl("Create ticket").click()
298        self.browser.open(self.payments_path + '/addop')
299        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
300        self.browser.getControl("Create ticket").click()
301        # Student is still in the first  session.
302        self.assertTrue('This type of payment' in self.browser.contents)
303        self.browser.open(self.payments_path)
304        ctrl = self.browser.getControl(name='val_id')
305        p_id = ctrl.options[2]
306        self.browser.open(self.payments_path + '/' + p_id + '/approve')
307        self.browser.open(self.payments_path + '/addop')
308        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
309        self.browser.getControl("Create ticket").click()
310        self.assertTrue('ticket created' in self.browser.contents)
311        self.assertEqual(self.student['payments'].values()[2].amount_auth, 128000.0)
312        self.assertEqual(self.student['payments'].values()[3].amount_auth, 92000.0)
313        # The last two payments belong to the same session and level.
314        self.assertEqual(self.student['payments'].values()[2].p_session, 2016)
315        self.assertEqual(self.student['payments'].values()[2].p_level, 200)
316        self.assertEqual(self.student['payments'].values()[3].p_session, 2016)
317        self.assertEqual(self.student['payments'].values()[3].p_level, 200)
318        return
319
320    def test_manage_payments_bypass_ac_creation(self):
321        #self.student['studycourse'].certificate.school_fee_3 = 6666.0
322        self.student.nationality = u'NG'
323        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
324        self.browser.open(self.payments_path)
325        IWorkflowState(self.student).setState('cleared')
326        self.browser.getLink("Add current session payment ticket").click()
327        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
328        self.browser.getControl("Create ticket").click()
329        self.browser.open(self.payments_path)
330        ctrl = self.browser.getControl(name='val_id')
331        value = ctrl.options[0]
332        self.browser.getLink(value).click()
333        payment_url = self.browser.url
334        logfile = os.path.join(
335            self.app['datacenter'].storage, 'logs', 'students.log')
336        # The ticket can be found in the payments_catalog
337        cat = queryUtility(ICatalog, name='payments_catalog')
338        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
339        self.assertTrue(len(results), 1)
340        self.assertTrue(results[0] is self.student['payments'][value])
341        # Managers can approve the payment
342        self.browser.open(payment_url)
343        self.browser.getLink("Approve payment").click()
344        self.assertMatches('...Payment approved...',
345                          self.browser.contents)
346        # Approval is logged in students.log ...
347        logcontent = open(logfile).read()
348        self.assertTrue(
349            'zope.mgr - students.browser.OnlinePaymentApproveView '
350            '- E1000000 - schoolfee_incl payment approved'
351            in logcontent)
352        # ... and in payments.log
353        logfile = os.path.join(
354            self.app['datacenter'].storage, 'logs', 'payments.log')
355        logcontent = open(logfile).read()
356        self.assertTrue(
357            '"zope.mgr",E1000000,%s,schoolfee_incl,220000.0,AP,,,,,,\n' % value
358            in logcontent)
359        # Student is in state school fee paid, no activation
360        # code was necessary.
361        self.assertEqual(self.student.state, 'school fee paid')
362        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
363        return
364
365    def test_scores_csv_upload_available(self):
366        # lecturers can upload a CSV file to set values.
367        self.login_as_lecturer()
368        # set value to change from
369        self.student['studycourse']['100']['COURSE1'].score = 55
370        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
371        self.browser.open(self.edit_scores_url)
372        upload_ctrl = self.browser.getControl(name='uploadfile:file')
373        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','52','77'))
374        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
375        self.browser.getControl("Update editable scores from").click()
376        self.assertTrue('TESTER, Anna have not be updated' in self.browser.contents)
377        # values have not been changed
378        self.assertEqual(
379            self.student['studycourse']['100']['COURSE1'].score, 55)
380        self.assertEqual(
381            self.student['studycourse']['100']['COURSE1'].ca, None)
382        self.assertEqual(
383            self.student['studycourse']['100']['COURSE1'].imported_ts, None)
384        self.browser.open(self.edit_scores_url)
385        upload_ctrl = self.browser.getControl(name='uploadfile:file')
386        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','22','77'))
387        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
388        self.browser.getControl("Update editable scores from").click()
389        # values changed
390        self.assertEqual(
391            self.student['studycourse']['100']['COURSE1'].score, 65)
392        self.assertEqual(
393            self.student['studycourse']['100']['COURSE1'].ca, 22)
394        self.assertEqual(
395            self.student['studycourse']['100']['COURSE1'].imported_ts, 77)
396
397    def test_scores_previous_session(self):
398        # lecturers can download a CSV file to set values.
399        self.login_as_lecturer()
400        self.student['studycourse']['100']['COURSE1'].score = 55
401        self.browser.open(self.edit_prev_scores_url)
402        self.assertTrue('No student found' in self.browser.contents)
403        configuration = createObject('waeup.SessionConfiguration')
404        configuration.academic_session = 2003
405        self.app['configuration'].addSessionConfiguration(configuration)
406        self.app['configuration']['2003'].score_editing_enabled = ['ug_ft']
407        self.student['studycourse']['100'].level_session = 2003
408        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['COURSE1']))
409        self.browser.open(self.edit_prev_scores_url)
410        self.browser.getLink("Download csv file").click()
411        self.assertEqual(self.browser.headers['Status'], '200 Ok')
412        self.assertEqual(self.browser.headers['Content-Type'],
413                         'text/csv; charset=UTF-8')
414        self.assertEqual(self.browser.contents,
415            'matric_number,student_id,display_fullname,'
416            'depcode,faccode,level,code,level_session,ca,score,'
417            'total_score,grade,imported_ts\r\n234,E1000000,"TESTER, Anna",dep1,fac1,'
418            '100,COURSE1,2003,,55,,,\r\n')
419        self.browser.open(self.edit_prev_scores_url)
420        upload_ctrl = self.browser.getControl(name='uploadfile:file')
421        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','22','77'))
422        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
423        self.browser.getControl("Update editable scores from").click()
424        # value changed
425        self.assertEqual(
426            self.student['studycourse']['100']['COURSE1'].score, 65)
427        self.assertEqual(
428            self.student['studycourse']['100']['COURSE1'].ca, 22)
429        self.assertEqual(
430            self.student['studycourse']['100']['COURSE1'].imported_ts, 77)
431
432    def test_lecturers_can_download_course_tickets(self):
433        # A course ticket slip can be downloaded
434        self.login_as_lecturer()
435        self.course.title = (u'Lorem ipsum dolor sit amet, consectetur '
436                            u'adipisici elit, sed eiusmod tempor incidunt')
437        self.student['studycourse']['100']['COURSE1'].score = 55
438        self.student['studycourse']['100']['COURSE1'].ca = 11
439        self.student.matric_number = u'CMS/FBM/NSG/16/28838'
440        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
441        pdf_url = '%s/coursetickets.pdf' % self.course_url
442        self.browser.open(pdf_url)
443        self.assertEqual(self.browser.headers['Status'], '200 Ok')
444        self.assertEqual(
445            self.browser.headers['Content-Type'], 'application/pdf')
446        path = os.path.join(samples_dir(), 'coursetickets.pdf')
447        open(path, 'wb').write(self.browser.contents)
448        print "Sample PDF coursetickets.pdf written to %s" % path
449        # The CA column is not shown if CA is not used
450        self.student['studycourse']['100']['COURSE1'].ca = 0
451        self.browser.open(pdf_url)
452        path = os.path.join(samples_dir(), 'coursetickets_wo_ca.pdf')
453        open(path, 'wb').write(self.browser.contents)
454        print "Sample PDF coursetickets_wo_ca.pdf written to %s" % path
455
456    def test_lecturers_do_only_see_selected_students(self):
457        # A course ticket slip can be downloaded
458        self.login_as_lecturer()
459        self.student['studycourse']['100']['COURSE1'].score = 55
460        self.student['studycourse']['100']['COURSE1'].ca = 11
461        self.browser.open(self.edit_scores_url)
462        self.assertTrue('No student found' in self.browser.contents)
463        pdf_url = '%s/coursetickets.pdf' % self.course_url
464        self.browser.open(pdf_url)
465        self.assertEqual(self.browser.headers['Status'], '200 Ok')
466        self.assertEqual(
467            self.browser.headers['Content-Type'], 'application/pdf')
468        path = os.path.join(samples_dir(), 'coursetickets_filtered.pdf')
469        open(path, 'wb').write(self.browser.contents)
470        print "Sample PDF coursetickets_filtered.pdf written to %s" % path
471        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
472        self.browser.open(self.edit_scores_url)
473        self.assertFalse('No student found' in self.browser.contents)
474        self.assertTrue('TESTER, Anna' in self.browser.contents)
475
476    def test_lecturers_can_download_attendance_sheet(self):
477        # A course ticket slip can be downloaded
478        self.course.title = (u'Lorem ipsum     dolor sit amet, consectetur     adipisici, '
479                             u'sed         eiusmod tempor    incidunt ut  labore et dolore')
480        self.student.firstname = u'Emmanuella Woyengifigha Mercy Onosemudiana'
481        self.student.lastname = u'OYAKEMIEGBEGHA'
482        self.student.matric_number = u'CMS/FBM/NSG/17/38186'
483        self.login_as_lecturer()
484        pdf_url = '%s/attendance.pdf' % self.course_url
485        self.browser.open(pdf_url)
486        self.assertEqual(self.browser.headers['Status'], '200 Ok')
487        self.assertEqual(
488            self.browser.headers['Content-Type'], 'application/pdf')
489        path = os.path.join(samples_dir(), 'attendance.pdf')
490        open(path, 'wb').write(self.browser.contents)
491        print "Sample PDF attendance.pdf written to %s" % path
492
493    def test_transcripts(self):
494        studylevel = createObject(u'waeup.StudentStudyLevel')
495        studylevel.level = 100
496        studylevel.level_session = 2005
497        self.student['studycourse'].entry_mode = 'ug_ft'
498        self.student['studycourse'].addStudentStudyLevel(
499            self.certificate, studylevel)
500        studylevel2 = createObject(u'waeup.StudentStudyLevel')
501        studylevel2.level = 200
502        studylevel2.level_session = 2006
503        self.student['studycourse']['100']['COURSE1'].score = 33 # no carry-over!
504        self.student['studycourse']['100']['COURSE1'].ca = 22
505        self.student['studycourse'].addStudentStudyLevel(
506            self.certificate, studylevel2)
507        # Add second course (COURSE has been added automatically)
508        courseticket = createObject('waeup.CourseTicket')
509        courseticket.code = 'ANYCODE'
510        courseticket.title = u'Any TITLE'
511        courseticket.credits = 13
512        courseticket.score = 55
513        courseticket.ca = 11
514        courseticket.semester = 1
515        courseticket.dcode = u'ANYDCODE'
516        courseticket.fcode = u'ANYFCODE'
517        self.student['studycourse']['200']['COURSE2'] = courseticket
518        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
519        self.assertEqual(self.student['studycourse']['200'].gpa_params_rectified[0], 4.0)
520        # Get transcript data
521        td = self.student['studycourse'].getTranscriptData()
522        self.assertEqual(td[0][0]['level_key'], '100')
523        self.assertEqual(td[0][0]['sgpa'], 3.0)
524        self.assertEqual(td[0][0]['level'].level, 100)
525        self.assertEqual(td[0][0]['level'].level_session, 2005)
526        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
527        self.assertEqual(td[0][1]['level_key'], '200')
528        self.assertEqual(td[0][1]['sgpa'], 4.0)
529        self.assertEqual(td[0][1]['level'].level, 200)
530        self.assertEqual(td[0][1]['level'].level_session, 2006)
531        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
532        self.assertEqual(td[1], 3.5652173913043477)
533        # Set Officer 1
534        self.app['faculties']['fac1']['dep1'].officer_1 = u'Transcript Boss'
535        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
536        self.browser.open(self.student_path + '/studycourse/transcript')
537        self.assertEqual(self.browser.headers['Status'], '200 Ok')
538        self.assertTrue('Transcript' in self.browser.contents)
539        # Officers can open the pdf transcript
540        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
541        self.assertEqual(self.browser.headers['Status'], '200 Ok')
542        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
543        path = os.path.join(samples_dir(), 'transcript.pdf')
544        open(path, 'wb').write(self.browser.contents)
545        print "Sample PDF transcript.pdf written to %s" % path
546        # Officers can open the pdf student viewtranscript
547        self.browser.open(self.student_path + '/studycourse/transcript_studview.pdf')
548        self.assertEqual(self.browser.headers['Status'], '200 Ok')
549        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
550        path = os.path.join(samples_dir(), 'transcript_studview.pdf')
551        open(path, 'wb').write(self.browser.contents)
552        print "Sample PDF transcript_studview.pdf written to %s" % path
553
554    def test_payment_disabled(self):
555        #self.student['studycourse'].certificate.school_fee_3 = 6666.0
556        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
557        self.browser.open(self.payments_path)
558        IWorkflowState(self.student).setState('cleared')
559        self.browser.getLink("Add current session payment ticket").click()
560        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
561        self.browser.getControl("Create ticket").click()
562        self.assertMatches('...ticket created...', self.browser.contents)
563        self.app['configuration']['2004'].payment_disabled = ['sf_all']
564        self.browser.open(self.payments_path)
565        self.browser.getLink("Add current session payment ticket").click()
566        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
567        self.browser.getControl("Create ticket").click()
568        self.assertMatches('...This category of payments has been disabled...',
569                           self.browser.contents)
570
571        self.app['configuration']['2004'].payment_disabled = ['sf_ug_pt']
572        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
573        self.browser.getControl("Create ticket").click()
574        self.assertMatches('...ticket created...', self.browser.contents)
575        self.certificate.study_mode = 'ug_pt'
576        self.browser.open(self.payments_path)
577        self.browser.getLink("Add current session payment ticket").click()
578        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
579        self.browser.getControl("Create ticket").click()
580        self.assertMatches('...This category of payments has been disabled...',
581                           self.browser.contents)
582
583        #self.app['configuration']['2004'].payment_disabled = ['sf_pg']
584        #self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
585        #self.browser.getControl("Create ticket").click()
586        #self.assertMatches('...ticket created...', self.browser.contents)
587        #self.certificate.study_mode = 'special_pg_ft'
588        #self.browser.open(self.payments_path)
589        #self.browser.getLink("Add current session payment ticket").click()
590        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
591        #self.browser.getControl("Create ticket").click()
592        #self.assertMatches('...This category of payments has been disabled...',
593        #                   self.browser.contents)
594        return
595
596class StudentUITests(StudentsFullSetup):
597    """Tests for customized student class views and pages
598    """
599
600    layer = FunctionalLayer
601
602    def setUp(self):
603        super(StudentUITests, self).setUp()
604        self.app['faculties']['fac1']['dep1'].certificates[
605            'CERT1']['COURSE1_100'].course_category = 'C'
606        bedticket = BedTicket()
607        bedticket.booking_session = 2004
608        bedticket.bed_type = u'any bed type'
609        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
610        bedticket.bed_coordinates = u'My bed coordinates'
611        self.student['accommodation'].addBedTicket(bedticket)
612
613    def test_maintenance_fee_payment(self):
614        self.certificate.study_mode = 'ug_ft'
615        self.student['studycourse'].entry_session = 2013
616        self.student.nationality = u'NG'
617        IWorkflowState(self.student).setState('cleared')
618        self.browser.open(self.login_path)
619        self.browser.getControl(name="form.login").value = self.student_id
620        self.browser.getControl(name="form.password").value = 'spwd'
621        self.browser.getControl("Login").click()
622        self.browser.open(self.student_path + '/payments')
623        self.browser.getLink("Add current session payment ticket").click()
624        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
625        self.browser.getControl("Create ticket").click()
626        self.assertTrue('ticket created' in self.browser.contents)
627        value = self.student['payments'].keys()[0]
628        self.browser.getLink(value).click()
629        self.assertTrue('<span>My bed coordinates</span>' in self.browser.contents)
630        self.assertEqual(self.student['payments'][value].amount_auth, 876.0)
631        return
632
633    def test_student_schoolfee_payments(self):
634        configuration_1 = createObject('waeup.SessionConfiguration')
635        configuration_1.academic_session = 2018
636        self.app['configuration'].addSessionConfiguration(configuration_1)
637        self.certificate.study_mode = 'ug_ft'
638        self.student['studycourse'].entry_session = 2018
639        self.student['studycourse'].current_session = 2018
640        self.student['studycourse'].entry_mode = 'ug_ft'
641        #self.student['studycourse'].certificate.school_fee_2 = 50200.0
642        self.student.nationality = u'NG'
643        # Login
644        IWorkflowState(self.student).setState('cleared')
645        self.browser.open(self.login_path)
646        self.browser.getControl(name="form.login").value = self.student_id
647        self.browser.getControl(name="form.password").value = 'spwd'
648        self.browser.getControl("Login").click()
649        # Test school fee payments
650        self.browser.open(self.student_path + '/payments')
651        self.browser.getLink("Add current session payment ticket").click()
652        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
653        self.browser.getControl("Create ticket").click()
654        self.assertTrue('ticket created' in self.browser.contents)
655        value = self.student['payments'].keys()[0]
656        self.student['payments'][value].p_state = 'paid'
657        self.browser.getLink(value).click()
658        self.assertTrue('Amount Authorized' in self.browser.contents)
659        self.assertEqual(self.student['payments'][value].amount_auth, 220000.0)
660        self.student['payments'][value].r_company = u'interswitch'
661        self.browser.getLink("Download").click()
662        self.assertEqual(self.browser.headers['Status'], '200 Ok')
663        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
664        path = os.path.join(samples_dir(), 'payment_slip.pdf')
665        open(path, 'wb').write(self.browser.contents)
666        print "Sample PDF payment_slip.pdf written to %s" % path
667        # Another school fee payment cannot be added
668        #self.student['payments'][value].approve()
669        #self.browser.open(self.student_path + '/payments')
670        #self.browser.getLink("Add current session payment ticket").click()
671        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
672        #self.browser.getControl("Create ticket").click()
673        #self.assertTrue(
674        #    'You must choose a payment which includes additional fees'
675        #    in self.browser.contents)
676        configuration_2 = createObject('waeup.SessionConfiguration')
677        configuration_2.academic_session = 2019
678        self.app['configuration'].addSessionConfiguration(configuration_2)
679        self.student.father_name = u'Rudolf'
680        IWorkflowState(self.student).setState('returning')
681        self.browser.open(self.student_path + '/payments')
682        self.browser.getLink("Add current session payment ticket").click()
683        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
684        self.browser.getControl("Create ticket").click()
685        self.assertTrue('ticket created' in self.browser.contents)
686        value = self.student['payments'].keys()[1]
687        self.student['payments'][value].p_state = 'paid'
688        self.browser.getLink(value).click()
689        self.assertTrue('Amount Authorized' in self.browser.contents)
690        # It's 1000 less
691        self.assertEqual(self.student['payments'][value].amount_auth, 220000.0)
692        return
693
694    def test_late_registration(self):
695        delta = timedelta(days=10)
696        self.app['configuration'][
697            '2004'].coursereg_deadline = datetime.now(pytz.utc) - delta
698        IWorkflowState(self.student).setState('school fee paid')
699        # Current session is 2004. Here we test course registration for
700        # returning students.
701        self.student['studycourse'].entry_session = 2003
702        self.student['studycourse'].current_session = 2016
703        # Login
704        self.browser.open(self.login_path)
705        self.browser.getControl(name="form.login").value = self.student_id
706        self.browser.getControl(name="form.password").value = 'spwd'
707        self.browser.getControl("Login").click()
708        # Make restitution fee payment
709        # Ivie: The restitution was only for returning students of 2016/2017.
710        # Hence, it is only valid for 2016 payment session returning students.
711        #configuration = createObject('waeup.SessionConfiguration')
712        #configuration.academic_session = 2016
713        #self.app['configuration'].addSessionConfiguration(configuration)
714        #self.app['configuration']['2016'].restitution_fee = 9999.0
715        #self.browser.open(self.payments_path + '/addop')
716        #self.browser.getControl(name="form.p_category").value = ['restitution']
717        #self.browser.getControl("Create ticket").click()
718        #self.student['payments'].values()[0].approveStudentPayment()
719        # Make late registration fee payment
720        self.student['studycourse'].current_session = 2004
721        self.browser.open(self.payments_path + '/addop')
722        self.browser.getControl(name="form.p_category").value = ['late_registration']
723        self.browser.getControl("Create ticket").click()
724        self.assertMatches('...ticket created...',
725                           self.browser.contents)
726        self.browser.getLink("Study Course").click()
727        self.browser.getLink("Add course list").click()
728        self.assertTrue('Add current level 100 (Year 1)' in self.browser.contents)
729        self.browser.getControl("Create course list now").click()
730        self.student['studycourse']['100']['COURSE1'].score = 67
731        self.browser.getLink("100").click()
732        # Course results can't be seen
733        self.assertFalse('<td>67</td>' in self.browser.contents)
734        self.browser.getLink("Edit course list").click()
735        self.assertFalse('<td>67</td>' in self.browser.contents)
736        self.app['configuration']['2004'].late_registration_fee = 0.0
737        self.browser.getControl("Register course list").click()
738        self.assertTrue('Course registration has been disabled.' in self.browser.contents)
739        self.app['configuration']['2004'].late_registration_fee = 345.0
740        self.browser.getControl("Register course list").click()
741        self.assertTrue('Course registration has ended. Please pay' in self.browser.contents)
742        self.student['payments'].values()[0].approve()
743        self.browser.getControl("Register course list").click()
744        self.assertTrue('Course list has been registered' in self.browser.contents)
745        self.assertEqual(self.student.state, 'courses registered')
746        # Reset student and check if fresh students are always allowed to
747        # register courses.
748        #self.student['studycourse'].entry_session = 2004
749        #del self.student['payments'][self.student['payments'].keys()[1]]
750        #IWorkflowState(self.student).setState('school fee paid')
751        #self.browser.open(self.studycourse_path + '/100/edit')
752        #self.browser.getControl("Register course list").click()
753        #self.assertTrue('Course list has been registered' in self.browser.contents)
754        return
755
756
757    def test_student_course_registration(self):
758        # Add more courses
759        self.course2 = createObject('waeup.Course')
760        self.course2.code = 'COURSE2'
761        self.course2.semester = 2
762        self.course2.credits = 10
763        self.course2.passmark = 40
764        self.app['faculties']['fac1']['dep1'].courses.addCourse(
765            self.course2)
766        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
767            self.course2, level=100)
768        self.course3 = createObject('waeup.Course')
769        self.course3.code = 'COURSE3'
770        self.course3.semester = 3
771        self.course3.credits = 10
772        self.course3.passmark = 40
773        self.app['faculties']['fac1']['dep1'].courses.addCourse(
774            self.course3)
775        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
776            self.course3, level=100)
777
778        # Login as student
779        self.browser.open(self.login_path)
780        IWorkflowState(self.student).setState('school fee paid')
781        self.browser.open(self.login_path)
782        self.browser.getControl(name="form.login").value = self.student_id
783        self.browser.getControl(name="form.password").value = 'spwd'
784        self.browser.getControl("Login").click()
785        # Students can add the current study level
786        self.browser.getLink("Study Course").click()
787        self.browser.getLink("Add course list").click()
788        self.assertMatches('...Add current level 100 (Year 1)...',
789                           self.browser.contents)
790        self.browser.getControl("Create course list now").click()
791        # Student has not paid second instalment, therefore a level
792        # with two course ticket was created (semester 1 and combined)
793        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
794        self.browser.getLink("100").click()
795        self.browser.getLink("Edit course list").click()
796        self.browser.getControl("Add course ticket").click()
797        # Student can't add second semester course
798        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
799        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
800        self.assertFalse('<option value="COURSE2">' in self.browser.contents)
801
802        # Let's remove level and see what happens after 2nd instalment payment
803        del(self.student['studycourse']['100'])
804        payment2 = createObject('waeup.StudentOnlinePayment')
805        payment2.p_category = u'schoolfee_2'
806        payment2.p_session = self.student.current_session
807        self.student['payments']['anykey'] = payment2
808        self.browser.open(self.studycourse_path)
809        self.browser.getLink("Add course list").click()
810        self.browser.getControl("Create course list now").click()
811        # Still only 2 tickets have been created since payment ticket
812        # was not paid
813        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
814        payment2.p_state = u'paid'
815        del(self.student['studycourse']['100'])
816        self.browser.open(self.studycourse_path)
817        self.browser.getLink("Add course list").click()
818        self.browser.getControl("Create course list now").click()
819        # Now 2nd semester course has been added
820        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 3)
821        # Student can add second semester course
822        self.browser.getLink("100").click()
823        self.browser.getLink("Edit course list").click()
824        self.browser.getControl("Add course ticket").click()
825        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
826        self.assertTrue('<option value="COURSE2">' in self.browser.contents)
827        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
828        return
829
830    def test_set_matric_number(self):
831        #payment = createObject('waeup.StudentOnlinePayment')
832        #payment.p_category = u'concessional'
833        #payment.p_id = u'anyid'
834        #payment.p_state = u'paid'
835        #self.student['payments']['anykey'] = payment
836        # Login as student
837        self.browser.open(self.login_path)
838        IWorkflowState(self.student).setState('school fee paid')
839        self.browser.open(self.login_path)
840        self.browser.getControl(name="form.login").value = self.student_id
841        self.browser.getControl(name="form.password").value = 'spwd'
842        self.browser.getControl("Login").click()
843        self.assertRaises(
844            LinkNotFoundError,
845            self.browser.getLink, 'Get Matriculation Number')
846        self.student.matric_number = None
847        site = grok.getSite()
848        site['configuration'].next_matric_integer = 1
849        self.student['studycourse'].certificate.study_mode = 'ug_pt'
850        self.browser.open(self.student_path)
851        self.assertRaises(
852            LinkNotFoundError,
853            self.browser.getLink, 'Download matriculation number slip')
854        self.browser.getLink("Get Matriculation Number").click()
855        self.assertTrue('Matriculation number PTP/fac1/dep1/04/00001 assigned.'
856            in self.browser.contents)
857        self.assertEqual(self.student.matric_number, 'PTP/fac1/dep1/04/00001')
858        self.assertRaises(
859            LinkNotFoundError,
860            self.browser.getLink, 'Get Matriculation Number')
861        # Setting matric number is logged.
862        logfile = os.path.join(
863            self.app['datacenter'].storage, 'logs', 'students.log')
864        logcontent = open(logfile).read()
865        self.assertTrue('E1000000 - waeup.aaue.students.browser.StudentGetMatricNumberPage - '
866                        'E1000000 - PTP/fac1/dep1/04/00001 assigned' in logcontent)
867        # Matric Number Slip can be downloaded
868        self.browser.getLink("Download matriculation number slip").click()
869        self.assertEqual(self.browser.headers['Status'], '200 Ok')
870        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
871        path = os.path.join(samples_dir(), 'matric_number_slip.pdf')
872        open(path, 'wb').write(self.browser.contents)
873        print "Sample PDF matric_number_slip.pdf written to %s" % path
874        return
875
876    def test_personal_data_slip(self):
877        # Login as student
878        self.browser.open(self.login_path)
879        IWorkflowState(self.student).setState('school fee paid')
880        self.browser.open(self.login_path)
881        self.browser.getControl(name="form.login").value = self.student_id
882        self.browser.getControl(name="form.password").value = 'spwd'
883        self.browser.getControl("Login").click()
884        self.browser.getLink("Personal Data").click()
885        self.assertRaises(
886            LinkNotFoundError,
887            self.browser.getLink, 'Download personal data slip')
888        self.student.father_name = u'Rudolf'
889        self.browser.open(self.personal_path)
890        self.browser.getLink("Download personal data slip").click()
891        self.assertEqual(self.browser.headers['Status'], '200 Ok')
892        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
893        path = os.path.join(samples_dir(), 'personal_data_slip.pdf')
894        open(path, 'wb').write(self.browser.contents)
895        print "Sample PDF personal_data_slip.pdf written to %s" % path
896        return
897
898    def test_student_course_registration(self):
899        IWorkflowState(self.student).setState('school fee paid')
900        self.browser.open(self.login_path)
901        self.browser.getControl(name="form.login").value = self.student_id
902        self.browser.getControl(name="form.password").value = 'spwd'
903        self.browser.getControl("Login").click()
904        # Now students can add the current study level
905        self.browser.getLink("Study Course").click()
906        self.browser.getLink("Add course list").click()
907        self.assertTrue('Add current level 100 (Year 1)' in self.browser.contents)
908        self.browser.getControl("Create course list now").click()
909        # Students can't open the customized pdf course registration slip
910        self.browser.open(
911            self.student_path + '/studycourse/100/course_registration_slip.pdf')
912        self.assertTrue('Forbidden' in self.browser.contents)
913        # They can open slips from the previous session ...
914        self.student['studycourse'].current_level = 200
915        self.browser.open(self.student_path + '/studycourse/100')
916        self.browser.getLink("Download course registration slip").click()
917        self.assertEqual(self.browser.headers['Status'], '200 Ok')
918        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
919        # or if they have registered their course list
920        self.student['studycourse'].current_level = 200
921        IWorkflowState(self.student).setState('courses registered')
922        self.browser.open(self.student_path + '/studycourse/100')
923        self.browser.getLink("Download course registration slip").click()
924        self.assertEqual(self.browser.headers['Status'], '200 Ok')
925        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
926        path = os.path.join(samples_dir(), 'ft_course_registration_slip.pdf')
927        open(path, 'wb').write(self.browser.contents)
928        print "Sample PDF ft_course_registration_slip.pdf written to %s" % path
929
930        self.certificate.study_mode = 'ug_pt'
931        self.browser.open(self.student_path + '/studycourse/100')
932        self.browser.getLink("Download course registration slip").click()
933        self.assertEqual(self.browser.headers['Status'], '200 Ok')
934        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
935        path = os.path.join(samples_dir(), 'pt_course_registration_slip.pdf')
936        open(path, 'wb').write(self.browser.contents)
937        print "Sample PDF pt_course_registration_slip.pdf written to %s" % path
938
939        self.certificate.study_mode = 'special_pg_ft'
940        self.student.matric_number = u'AAU/SPS/FLW/LAW/15/PHD/09504'
941        self.browser.open(self.student_path + '/studycourse/100')
942        self.browser.getLink("Download course registration slip").click()
943        self.assertEqual(self.browser.headers['Status'], '200 Ok')
944        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
945        path = os.path.join(samples_dir(), 'pg_course_registration_slip.pdf')
946        open(path, 'wb').write(self.browser.contents)
947        print "Sample PDF pg_course_registration_slip.pdf written to %s" % path
948
949        # Students cant' view scores, cas and grades.
950        self.browser.open(self.student_path + '/studycourse/100')
951        self.assertFalse('Score' in self.browser.contents)
952        self.assertFalse('CA' in self.browser.contents)
953        self.assertFalse('Grade' in self.browser.contents)
954        self.browser.getLink("COURSE1").click()
955        self.browser.open(self.student_path + '/studycourse/100')
956        self.assertFalse('Score' in self.browser.contents)
957        self.assertFalse('CA' in self.browser.contents)
958        self.assertFalse('Grade' in self.browser.contents)
959
960    def test_student_2nd_semester_course_registration(self):
961        IWorkflowState(self.student).setState('school fee paid')
962        self.student['studycourse'].entry_session = 2015
963        self.course.semester = 2
964        self.browser.open(self.login_path)
965        self.browser.getControl(name="form.login").value = self.student_id
966        self.browser.getControl(name="form.password").value = 'spwd'
967        self.browser.getControl("Login").click()
968        self.browser.getLink("Study Course").click()
969        self.browser.getLink("Add course list").click()
970        self.browser.getControl("Create course list now").click()
971        self.assertFalse('COURSE1' in self.browser.contents)
972        # 2nd semester tickets can't be added manually
973        self.browser.getLink("Edit course list").click()
974        self.browser.getLink("here").click()
975        self.browser.getControl(name="form.course").value = ['COURSE1']
976        self.browser.getControl("Add course ticket").click()
977        self.assertTrue(
978            'COURSE1 is a 2nd semester course which can only '
979            'be added if school fees have been fully paid.'
980            in self.browser.contents)
981        # 2nd instalment has to be paid first
982        #self.certificate.school_fee_3 = 678.0
983        self.browser.open(self.payments_path + '/addop')
984        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
985        self.browser.getControl("Create ticket").click()
986        self.student['payments'].values()[0].approve()
987        self.browser.open(self.studycourse_path + '/100/ctadd')
988        self.browser.getControl(name="form.course").value = ['COURSE1']
989        self.browser.getControl("Add course ticket").click()
990        self.assertTrue('Successfully added COURSE1' in self.browser.contents)
991        return
992
993    def test_student_GST_registration(self):
994        configuration_1 = createObject('waeup.SessionConfiguration')
995        configuration_1.academic_session = 2016
996        configuration_1.gst_registration_1_fee = 3333.0
997        configuration_1.gst_text_book_1_fee = 4444.0
998        configuration_1.gst_text_book_0_fee = 2222.0
999        self.app['configuration'].addSessionConfiguration(configuration_1)
1000        course = createObject('waeup.Course')
1001        course.code = 'GST101'
1002        course.semester = 1
1003        course.credits = 10
1004        course.passmark = 40
1005        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1006            course)
1007        self.app['faculties']['fac1']['dep1'].certificates[
1008            'CERT1'].addCertCourse(course, level=100)
1009        IWorkflowState(self.student).setState('school fee paid')
1010        self.student['studycourse'].entry_session = 2016
1011        self.student['studycourse'].current_session = 2016
1012        self.course.semester = 2
1013        self.browser.open(self.login_path)
1014        self.browser.getControl(name="form.login").value = self.student_id
1015        self.browser.getControl(name="form.password").value = 'spwd'
1016        self.browser.getControl("Login").click()
1017        self.browser.getLink("Study Course").click()
1018        self.browser.getLink("Add course list").click()
1019        self.browser.getControl("Create course list now").click()
1020        self.assertFalse('GST101' in self.browser.contents)
1021        # GST101 tickets can't be added manually
1022        self.browser.getLink("Edit course list").click()
1023        self.browser.getLink("here").click()
1024        self.browser.getControl(name="form.course").value = ['GST101']
1025        self.browser.getControl("Add course ticket").click()
1026        self.assertTrue(
1027            'GST101 can only be added if both registration fee and text'
1028            in self.browser.contents)
1029        # GST fees have to be paid first
1030        self.browser.open(self.payments_path + '/addop')
1031        self.browser.getControl(name="form.p_category").value = ['gst_registration_1']
1032        self.browser.getControl("Create ticket").click()
1033        self.student['payments'].values()[0].approve()
1034        self.browser.open(self.studycourse_path + '/100/ctadd')
1035        self.browser.getControl(name="form.course").value = ['GST101']
1036        self.browser.getControl("Add course ticket").click()
1037        self.assertTrue(
1038            'GST101 can only be added if both registration fee and text'
1039            in self.browser.contents)
1040        self.browser.open(self.payments_path + '/addop')
1041        self.browser.getControl(name="form.p_category").value = ['gst_text_book_0']
1042        self.browser.getControl("Create ticket").click()
1043        self.student['payments'].values()[1].approve()
1044        self.browser.open(self.studycourse_path + '/100/ctadd')
1045        self.browser.getControl(name="form.course").value = ['GST101']
1046        self.browser.getControl("Add course ticket").click()
1047        self.assertTrue('Successfully added GST101' in self.browser.contents)
1048        return
1049
1050    def test_course_registration_forbidden(self):
1051        IWorkflowState(self.student).setState('school fee paid')
1052        self.student['studycourse'].entry_session = 2016
1053        self.student['studycourse'].current_session = 2016
1054        self.browser.open(self.login_path)
1055        self.browser.getControl(name="form.login").value = self.student_id
1056        self.browser.getControl(name="form.password").value = 'spwd'
1057        self.browser.getControl("Login").click()
1058        self.browser.getLink("Study Course").click()
1059        self.browser.getLink("Add course list").click()
1060        self.browser.getControl("Create course list now").click()
1061        self.browser.getLink("Edit course list").click()
1062        self.browser.getControl("Register course list").click()
1063        #self.assertTrue('Please pay faculty and departmental dues first'
1064        #    in self.browser.contents)
1065        #configuration_1 = createObject('waeup.SessionConfiguration')
1066        #configuration_1.academic_session = 2016
1067        #configuration_1.fac_dep_fee = 9999.0
1068        #self.app['configuration'].addSessionConfiguration(configuration_1)
1069        #self.browser.open(self.payments_path + '/addop')
1070        #self.browser.getControl(name="form.p_category").value = ['fac_dep']
1071        #self.browser.getControl("Create ticket").click()
1072        #self.student['payments'].values()[0].approveStudentPayment()
1073        #self.browser.open(self.studycourse_path + '/100/edit')
1074        #self.browser.getControl("Register course list").click()
1075
1076        ######################################################
1077        # Temporarily disabled ug_ft course registration
1078        #self.assertTrue('Course registration has been disabled'
1079        #    in self.browser.contents)
1080        #return
1081        ######################################################
1082
1083        self.assertTrue('Course list has been registered'
1084            in self.browser.contents)
1085        return
1086
1087    def deactivated_test_course_registration_forbidden_2(self):
1088        IWorkflowState(self.student).setState('school fee paid')
1089        self.student['studycourse'].entry_session = 2004
1090        self.student['studycourse'].current_session = 2016
1091        self.browser.open(self.login_path)
1092        self.browser.getControl(name="form.login").value = self.student_id
1093        self.browser.getControl(name="form.password").value = 'spwd'
1094        self.browser.getControl("Login").click()
1095        self.browser.getLink("Study Course").click()
1096        self.browser.getLink("Add course list").click()
1097        self.browser.getControl("Create course list now").click()
1098        self.browser.getLink("Edit course list").click()
1099        self.browser.getControl("Register course list").click()
1100
1101        ######################################################
1102        # Temporarily disabled ug_ft course registration
1103        #self.assertTrue('Course registration has been disabled'
1104        #    in self.browser.contents)
1105        #return
1106        ######################################################
1107
1108        self.assertTrue('Please pay restitution fee first'
1109            in self.browser.contents)
1110        configuration = createObject('waeup.SessionConfiguration')
1111        configuration.academic_session = 2016
1112        self.app['configuration'].addSessionConfiguration(configuration)
1113        self.app['configuration']['2016'].restitution_fee = 9999.0
1114        self.browser.open(self.payments_path + '/addop')
1115        self.browser.getControl(name="form.p_category").value = ['restitution']
1116        self.browser.getControl("Create ticket").click()
1117        self.student['payments'].values()[0].approveStudentPayment()
1118        self.browser.open(self.studycourse_path + '/100/edit')
1119        self.browser.getControl("Register course list").click()
1120        self.assertTrue('Course list has been registered'
1121            in self.browser.contents)
1122        return
1123
1124    def test_student_access_course_results(self):
1125        IWorkflowState(self.student).setState('school fee paid')
1126        self.student['studycourse'].entry_session = 2004
1127        self.student['studycourse'].current_session = 2004
1128        self.browser.open(self.login_path)
1129        self.browser.getControl(name="form.login").value = self.student_id
1130        self.browser.getControl(name="form.password").value = 'spwd'
1131        self.browser.getControl("Login").click()
1132        self.browser.getLink("Study Course").click()
1133        self.browser.getLink("Add course list").click()
1134        self.browser.getControl("Create course list now").click()
1135        self.assertFalse('Score' in self.browser.contents)
1136        IWorkflowState(self.student).setState('returning')
1137        self.browser.open(self.studycourse_path + '/100')
1138        self.assertFalse('Score' in self.browser.contents)
1139        self.app['configuration']['2004'].show_results = ['ug_ft']
1140        self.browser.open(self.studycourse_path + '/100')
1141        self.assertTrue('Score' in self.browser.contents)
1142        return
1143
1144    def test_repair_course_list(self):
1145        IWorkflowState(self.student).setState('school fee paid')
1146        self.student['studycourse'].entry_session = 2016
1147        self.student['studycourse'].current_session = 2016
1148        self.browser.open(self.login_path)
1149        self.browser.getControl(name="form.login").value = self.student_id
1150        self.browser.getControl(name="form.password").value = 'spwd'
1151        self.browser.getControl("Login").click()
1152        self.browser.getLink("Study Course").click()
1153        self.browser.getLink("Add course list").click()
1154        self.browser.getControl("Create course list now").click()
1155        self.assertTrue('Edit course list' in self.browser.contents)
1156        self.assertFalse('Repair course list' in self.browser.contents)
1157        self.student['studycourse'].current_level = 200
1158        self.browser.open(self.studycourse_path + '/100')
1159        self.assertFalse('Edit course list' in self.browser.contents)
1160        self.assertFalse('Repair course list' in self.browser.contents)
1161        configuration = createObject('waeup.SessionConfiguration')
1162        configuration.academic_session = 2016
1163        self.app['configuration'].addSessionConfiguration(configuration)
1164        self.app['configuration']['2016'].studylevel_repair_enabled = True
1165        self.browser.open(self.studycourse_path + '/100')
1166        self.assertFalse('Edit course list' in self.browser.contents)
1167        self.assertTrue('Repair course list' in self.browser.contents)
1168        self.browser.getLink("Repair").click()
1169        self.assertEqual(self.browser.url, self.studycourse_path + '/100/repair')
1170        self.assertTrue('Repair course list of 100' in self.browser.contents)
1171        return
1172
1173    def test_student_clearance(self):
1174        # Student cant login if their password is not set
1175        IWorkflowInfo(self.student).fireTransition('admit')
1176        self.browser.open(self.login_path)
1177        self.browser.getControl(name="form.login").value = self.student_id
1178        self.browser.getControl(name="form.password").value = 'spwd'
1179        self.browser.getControl("Login").click()
1180        self.assertMatches(
1181            '...You logged in...', self.browser.contents)
1182        # Admitted student can upload a passport picture
1183        # disabled on 16/01/24
1184        #self.browser.open(self.student_path + '/change_portrait')
1185        #ctrl = self.browser.getControl(name='passportuploadedit')
1186        #file_obj = open(SAMPLE_IMAGE, 'rb')
1187        #file_ctrl = ctrl.mech_control
1188        #file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1189        #self.browser.getControl(
1190        #    name='upload_passportuploadedit').click()
1191        #self.assertTrue(
1192        #    'src="http://localhost/app/students/E1000000/passport.jpg"'
1193        #    in self.browser.contents)
1194        # Student is redirected to the personal data form because
1195        # personal data form is not properly filled.
1196        #self.browser.open(self.student_path + '/start_clearance')
1197        #self.assertMatches('...Personal data form is not properly filled...',
1198        #                   self.browser.contents)
1199        #self.assertEqual(self.browser.url, self.student_path + '/edit_personal')
1200        #self.student.father_name = u'Rudolf'
1201        #self.browser.open(self.student_path + '/start_clearance')
1202        #self.assertMatches(
1203        #    '...<h1 class="kofa-content-label">Start clearance</h1>...',
1204        #    self.browser.contents)
1205
1206    def test_student_accommodation(self):
1207        del self.student['accommodation']['2004']
1208        self.student['studycourse'].certificate.study_mode = 'dp_ft'
1209        # All beds can be assigned
1210        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1211        bed1.bed_type = u'regular_male_all'
1212        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1213        bed2.bed_type = u'regular_female_all'
1214        notify(grok.ObjectModifiedEvent(bed1))
1215        notify(grok.ObjectModifiedEvent(bed2))
1216        # Login
1217        self.browser.open(self.login_path)
1218        self.browser.getControl(name="form.login").value = self.student_id
1219        self.browser.getControl(name="form.password").value = 'spwd'
1220        self.browser.getControl("Login").click()
1221        self.browser.open(self.acco_path)
1222        IWorkflowInfo(self.student).fireTransition('admit')
1223        self.browser.getControl("Book accommodation").click()
1224        self.assertTrue(
1225            'You are not eligible to book accommodation.'
1226            in self.browser.contents)
1227        self.student['studycourse'].certificate.study_mode = 'ug_ft'
1228        self.app['hostels'].accommodation_states = [PAID]
1229        self.browser.getControl("Book accommodation").click()
1230        self.assertTrue(
1231            'You are in the wrong registration state.'
1232            in self.browser.contents)
1233        IWorkflowState(self.student).setState(PAID)
1234
1235        self.browser.getLink("Payments").click()
1236        self.browser.getLink("Add current session payment ticket").click()
1237        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1238        self.browser.getControl("Create ticket").click()
1239        self.student['payments'].values()[0].approveStudentPayment()
1240        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1241        ac = self.app['accesscodes']['HOS-0'][pin]
1242        parts = pin.split('-')[1:]
1243        sfeseries, sfenumber = parts
1244        self.browser.open(self.acco_path)
1245        self.browser.getControl("Book accommodation").click()
1246        #self.assertFalse('Activation Code:' in self.browser.contents)
1247
1248        self.assertMatches('...Activation Code:...',
1249                           self.browser.contents)
1250        self.browser.getControl(name="ac_series").value = sfeseries
1251        self.browser.getControl(name="ac_number").value = sfenumber
1252        self.browser.getControl("Create bed ticket").click()
1253        # Bed is randomly selected but, since there is only
1254        # one bed for this student, we know that
1255        self.assertEqual(self.student['accommodation']['2004'].bed_coordinates,
1256            'Hall 1, Block A, Room 101, Bed A (regular_male_all)')
1257        # Only the hall name is displayed
1258        self.assertEqual(self.student[
1259            'accommodation']['2004'].display_coordinates,
1260            'Hall 1')
1261        self.assertFalse('Hall 1, Block A, Room 101, Bed A'
1262            in self.browser.contents)
1263        self.assertTrue('<td>Hall 1</td>'
1264            in self.browser.contents)
1265        return
1266
1267    def test_handle_courses_by_lecturer(self):
1268        self.app['users'].addUser('mrslecturer', 'mrslecturerSecret1')
1269        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1270        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1271        # Add course ticket
1272        studylevel = createObject(u'waeup.StudentStudyLevel')
1273        studylevel.level = 100
1274        studylevel.level_session = 2004
1275        self.student['studycourse'].addStudentStudyLevel(
1276            self.certificate, studylevel)
1277        # Assign local Lecturer role for a certificate.
1278        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1279        prmlocal = IPrincipalRoleManager(course)
1280        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1281        notify(LocalRoleSetEvent(
1282            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1283        # Login as lecturer.
1284        self.browser.open(self.login_path)
1285        self.browser.getControl(name="form.login").value = 'mrslecturer'
1286        self.browser.getControl(name="form.password").value = 'mrslecturerSecret1'
1287        self.browser.getControl("Login").click()
1288        self.browser.getLink("My Courses").click()
1289        self.browser.getLink("COURSE1").click()
1290        # Course results can be batch edited via the edit_courses view.
1291        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
1292        self.app['configuration'].current_academic_session = 2004
1293        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
1294        IWorkflowState(self.student).setState('courses validated')
1295        self.browser.open(
1296            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1297        self.assertTrue(
1298            'input type="text" name="scores:list"'
1299            in self.browser.contents)
1300        self.browser.getControl(name="scores:list", index=0).value = '55'
1301        self.browser.getControl(name="cas:list", index=0).value = '22'
1302        self.browser.getControl("Update scores from").click()
1303        # New score and ca has been set.
1304        self.assertEqual(
1305            self.student['studycourse']['100']['COURSE1'].score, 55)
1306        self.assertEqual(
1307            self.student['studycourse']['100']['COURSE1'].ca, 22)
1308        # Score editing has been logged.
1309        logfile = os.path.join(
1310            self.app['datacenter'].storage, 'logs', 'students.log')
1311        logcontent = open(logfile).read()
1312        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1313                        'E1000000 100/COURSE1 score updated (55)' in logcontent)
1314        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1315                        'E1000000 100/COURSE1 ca updated (22)' in logcontent)
1316        # Non-integer scores won't be accepted.
1317        self.browser.open(
1318            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1319        self.assertTrue('value="55" />' in self.browser.contents)
1320        self.browser.getControl(name="scores:list", index=0).value = 'abc'
1321        self.browser.getControl("Update scores").click()
1322        self.assertTrue('Error: Score(s), CA(s) and Imported TS(s) of TESTER, Anna have not be updated.'
1323            in self.browser.contents)
1324        # Scores can be removed.
1325        self.browser.open(
1326            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1327        self.browser.getControl(name="scores:list", index=0).value = ''
1328        self.browser.getControl("Update scores").click()
1329        self.assertEqual(
1330            self.student['studycourse']['100']['COURSE1'].score, None)
1331        logcontent = open(logfile).read()
1332        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1333                        'E1000000 100/COURSE1 score updated (None)' in logcontent)
1334
1335
1336    def disabled_test_student_view_transcript(self):
1337        # Student cant login if their password is not set
1338        IWorkflowInfo(self.student).fireTransition('admit')
1339        self.browser.open(self.login_path)
1340        self.browser.getControl(name="form.login").value = self.student_id
1341        self.browser.getControl(name="form.password").value = 'spwd'
1342        self.browser.getControl("Login").click()
1343        self.assertMatches(
1344            '...You logged in...', self.browser.contents)
1345        # Students can view the transcript
1346        self.browser.open(self.studycourse_path)
1347        self.browser.getLink("Transcript").click()
1348        self.browser.getLink("Academic Transcript").click()
1349        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1350        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1351
1352    def test_alumni_request_pw(self):
1353        # Add an applicants container
1354        applicantscontainer = ApplicantsContainer()
1355        applicantscontainer.code = u'trans2017'
1356        applicantscontainer.prefix = 'trans'
1357        applicantscontainer.year = 2017
1358        applicantscontainer.title = u'This is the trans2017 container'
1359        applicantscontainer.application_category = 'no'
1360        applicantscontainer.mode = 'create'
1361        applicantscontainer.strict_deadline = True
1362        delta = timedelta(days=10)
1363        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1364        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1365        self.app['applicants']['trans2017'] = applicantscontainer
1366        self.applicantscontainer = self.app['applicants']['trans2017']
1367        # Student with wrong number can't be found.
1368        # Applicant is redirected to application section.
1369        self.browser.open('http://localhost/app/alumni_requestpw')
1370        self.browser.getControl(name="form.lastname").value = 'Tester'
1371        self.browser.getControl(name="form.number").value = 'anynumber'
1372        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1373        self.browser.getControl("Send login credentials").click()
1374        self.assertTrue('No student record found.'
1375            in self.browser.contents)
1376        self.assertEqual(self.browser.url,
1377            'http://localhost/app/applicants/trans2017/register')
1378
1379    def test_student_course_registration_outstanding(self):
1380        self.course = createObject('waeup.Course')
1381        self.course.code = 'COURSE2'
1382        self.course.semester = 1
1383        self.course.credits = 39
1384        self.course.passmark = 40
1385        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1386            self.course)
1387        IWorkflowState(self.student).setState('school fee paid')
1388        self.browser.open(self.login_path)
1389        self.browser.getControl(name="form.login").value = self.student_id
1390        self.browser.getControl(name="form.password").value = 'spwd'
1391        self.browser.getControl("Login").click()
1392        self.browser.open(self.student_path + '/studycourse/add')
1393        self.browser.getControl("Create course list now").click()
1394        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1395        self.student['studycourse'].current_level = 200
1396        self.browser.getLink("Study Course").click()
1397        self.browser.getLink("Add course list").click()
1398        self.assertMatches('...Add current level 200 (Year 2)...',
1399                           self.browser.contents)
1400        self.browser.getControl("Create course list now").click()
1401        self.browser.getLink("200").click()
1402        self.browser.getLink("Edit course list").click()
1403        self.browser.getLink("here").click()
1404        self.browser.getControl(name="form.course").value = ['COURSE2']
1405        self.browser.getControl("Add course ticket").click()
1406        # Carryover COURSE1 in level 200 already has 10 credits
1407        self.assertMatches(
1408            '...Maximum credits exceeded...', self.browser.contents)
1409        # If COURSE1 is outstanding, its credits won't be considered
1410        self.student['studycourse']['200']['COURSE1'].outstanding = True
1411        self.browser.getControl("Add course ticket").click()
1412        # Corresponding certificate course is missing
1413        self.assertTrue(
1414            'COURSE2 is not part of the CERT1 curriculum.'
1415            in self.browser.contents)
1416        self.app['faculties']['fac1']['dep1'].certificates[
1417            'CERT1'].addCertCourse(self.course, level=100)
1418        self.browser.getControl("Add course ticket").click()
1419        self.assertTrue(
1420            'Successfully added COURSE2' in self.browser.contents)
1421        return
1422
1423    def test_examination_schedule_slip(self):
1424        self.student.flash_notice = u'My Examination Date'
1425        self.browser.open(self.login_path)
1426        self.browser.getControl(name="form.login").value = self.student_id
1427        self.browser.getControl(name="form.password").value = 'spwd'
1428        self.browser.getControl("Login").click()
1429        self.browser.open(self.student_path)
1430        self.browser.getLink("Download examination schedule slip").click()
1431        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1432        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1433        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
1434        open(path, 'wb').write(self.browser.contents)
1435        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
1436        # If flash_notice does not contain exam' the button does not show up.
1437        self.student.flash_notice = u'anything'
1438        self.browser.open(self.student_path)
1439        self.assertFalse('examination schedule slip' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.