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

Last change on this file since 17954 was 17837, checked in by Henrik Bettermann, 4 months ago

Update fees.

  • Property svn:keywords set to Id
File size: 75.5 KB
Line 
1## $Id: test_browser.py 17837 2024-07-08 17:41:00Z 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, 148500.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, 148500.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,240500.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, 240500.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, 240500.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.assertMatches('...Add current level 100 (Year 1)...',
729                           self.browser.contents)
730        self.browser.getControl("Create course list now").click()
731        self.student['studycourse']['100']['COURSE1'].score = 67
732        self.browser.getLink("100").click()
733        # Course results can't be seen
734        self.assertFalse('<td>67</td>' in self.browser.contents)
735        self.browser.getLink("Edit course list").click()
736        self.assertFalse('<td>67</td>' in self.browser.contents)
737        self.app['configuration']['2004'].late_registration_fee = 0.0
738        self.browser.getControl("Register course list").click()
739        self.assertTrue('Course registration has been disabled.' in self.browser.contents)
740        self.app['configuration']['2004'].late_registration_fee = 345.0
741        self.browser.getControl("Register course list").click()
742        self.assertTrue('Course registration has ended. Please pay' in self.browser.contents)
743        self.student['payments'].values()[0].approve()
744        self.browser.getControl("Register course list").click()
745        self.assertTrue('Course list has been registered' in self.browser.contents)
746        self.assertEqual(self.student.state, 'courses registered')
747        # Reset student and check if fresh students are always allowed to
748        # register courses.
749        #self.student['studycourse'].entry_session = 2004
750        #del self.student['payments'][self.student['payments'].keys()[1]]
751        #IWorkflowState(self.student).setState('school fee paid')
752        #self.browser.open(self.studycourse_path + '/100/edit')
753        #self.browser.getControl("Register course list").click()
754        #self.assertTrue('Course list has been registered' in self.browser.contents)
755        return
756
757
758    def test_student_course_registration(self):
759        # Add more courses
760        self.course2 = createObject('waeup.Course')
761        self.course2.code = 'COURSE2'
762        self.course2.semester = 2
763        self.course2.credits = 10
764        self.course2.passmark = 40
765        self.app['faculties']['fac1']['dep1'].courses.addCourse(
766            self.course2)
767        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
768            self.course2, level=100)
769        self.course3 = createObject('waeup.Course')
770        self.course3.code = 'COURSE3'
771        self.course3.semester = 3
772        self.course3.credits = 10
773        self.course3.passmark = 40
774        self.app['faculties']['fac1']['dep1'].courses.addCourse(
775            self.course3)
776        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
777            self.course3, level=100)
778
779        # Login as student
780        self.browser.open(self.login_path)
781        IWorkflowState(self.student).setState('school fee paid')
782        self.browser.open(self.login_path)
783        self.browser.getControl(name="form.login").value = self.student_id
784        self.browser.getControl(name="form.password").value = 'spwd'
785        self.browser.getControl("Login").click()
786        # Students can add the current study level
787        self.browser.getLink("Study Course").click()
788        self.browser.getLink("Add course list").click()
789        self.assertMatches('...Add current level 100 (Year 1)...',
790                           self.browser.contents)
791        self.browser.getControl("Create course list now").click()
792        # Student has not paid second instalment, therefore a level
793        # with two course ticket was created (semester 1 and combined)
794        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
795        self.browser.getLink("100").click()
796        self.browser.getLink("Edit course list").click()
797        self.browser.getControl("Add course ticket").click()
798        # Student can't add second semester course
799        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
800        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
801        self.assertFalse('<option value="COURSE2">' in self.browser.contents)
802
803        # Let's remove level and see what happens after 2nd instalment payment
804        del(self.student['studycourse']['100'])
805        payment2 = createObject('waeup.StudentOnlinePayment')
806        payment2.p_category = u'schoolfee_2'
807        payment2.p_session = self.student.current_session
808        self.student['payments']['anykey'] = payment2
809        self.browser.open(self.studycourse_path)
810        self.browser.getLink("Add course list").click()
811        self.browser.getControl("Create course list now").click()
812        # Still only 2 tickets have been created since payment ticket
813        # was not paid
814        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
815        payment2.p_state = u'paid'
816        del(self.student['studycourse']['100'])
817        self.browser.open(self.studycourse_path)
818        self.browser.getLink("Add course list").click()
819        self.browser.getControl("Create course list now").click()
820        # Now 2nd semester course has been added
821        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 3)
822        # Student can add second semester course
823        self.browser.getLink("100").click()
824        self.browser.getLink("Edit course list").click()
825        self.browser.getControl("Add course ticket").click()
826        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
827        self.assertTrue('<option value="COURSE2">' in self.browser.contents)
828        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
829        return
830
831    def test_set_matric_number(self):
832        #payment = createObject('waeup.StudentOnlinePayment')
833        #payment.p_category = u'concessional'
834        #payment.p_id = u'anyid'
835        #payment.p_state = u'paid'
836        #self.student['payments']['anykey'] = payment
837        # Login as student
838        self.browser.open(self.login_path)
839        IWorkflowState(self.student).setState('school fee paid')
840        self.browser.open(self.login_path)
841        self.browser.getControl(name="form.login").value = self.student_id
842        self.browser.getControl(name="form.password").value = 'spwd'
843        self.browser.getControl("Login").click()
844        self.assertRaises(
845            LinkNotFoundError,
846            self.browser.getLink, 'Get Matriculation Number')
847        self.student.matric_number = None
848        site = grok.getSite()
849        site['configuration'].next_matric_integer = 1
850        self.student['studycourse'].certificate.study_mode = 'ug_pt'
851        self.browser.open(self.student_path)
852        self.assertRaises(
853            LinkNotFoundError,
854            self.browser.getLink, 'Download matriculation number slip')
855        self.browser.getLink("Get Matriculation Number").click()
856        self.assertTrue('Matriculation number PTP/fac1/dep1/04/00001 assigned.'
857            in self.browser.contents)
858        self.assertEqual(self.student.matric_number, 'PTP/fac1/dep1/04/00001')
859        self.assertRaises(
860            LinkNotFoundError,
861            self.browser.getLink, 'Get Matriculation Number')
862        # Setting matric number is logged.
863        logfile = os.path.join(
864            self.app['datacenter'].storage, 'logs', 'students.log')
865        logcontent = open(logfile).read()
866        self.assertTrue('E1000000 - waeup.aaue.students.browser.StudentGetMatricNumberPage - '
867                        'E1000000 - PTP/fac1/dep1/04/00001 assigned' in logcontent)
868        # Matric Number Slip can be downloaded
869        self.browser.getLink("Download matriculation number slip").click()
870        self.assertEqual(self.browser.headers['Status'], '200 Ok')
871        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
872        path = os.path.join(samples_dir(), 'matric_number_slip.pdf')
873        open(path, 'wb').write(self.browser.contents)
874        print "Sample PDF matric_number_slip.pdf written to %s" % path
875        return
876
877    def test_personal_data_slip(self):
878        # Login as student
879        self.browser.open(self.login_path)
880        IWorkflowState(self.student).setState('school fee paid')
881        self.browser.open(self.login_path)
882        self.browser.getControl(name="form.login").value = self.student_id
883        self.browser.getControl(name="form.password").value = 'spwd'
884        self.browser.getControl("Login").click()
885        self.browser.getLink("Personal Data").click()
886        self.assertRaises(
887            LinkNotFoundError,
888            self.browser.getLink, 'Download personal data slip')
889        self.student.father_name = u'Rudolf'
890        self.browser.open(self.personal_path)
891        self.browser.getLink("Download personal data slip").click()
892        self.assertEqual(self.browser.headers['Status'], '200 Ok')
893        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
894        path = os.path.join(samples_dir(), 'personal_data_slip.pdf')
895        open(path, 'wb').write(self.browser.contents)
896        print "Sample PDF personal_data_slip.pdf written to %s" % path
897        return
898
899    def test_student_course_registration(self):
900        IWorkflowState(self.student).setState('school fee paid')
901        self.browser.open(self.login_path)
902        self.browser.getControl(name="form.login").value = self.student_id
903        self.browser.getControl(name="form.password").value = 'spwd'
904        self.browser.getControl("Login").click()
905        # Now students can add the current study level
906        self.browser.getLink("Study Course").click()
907        self.browser.getLink("Add course list").click()
908        self.assertMatches('...Add current level 100 (Year 1)...',
909                           self.browser.contents)
910        self.browser.getControl("Create course list now").click()
911        # Students can't open the customized pdf course registration slip
912        self.browser.open(
913            self.student_path + '/studycourse/100/course_registration_slip.pdf')
914        self.assertTrue('Forbidden' in self.browser.contents)
915        # They can open slips from the previous session ...
916        self.student['studycourse'].current_level = 200
917        self.browser.open(self.student_path + '/studycourse/100')
918        self.browser.getLink("Download course registration slip").click()
919        self.assertEqual(self.browser.headers['Status'], '200 Ok')
920        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
921        # or if they have registered their course list
922        self.student['studycourse'].current_level = 200
923        IWorkflowState(self.student).setState('courses registered')
924        self.browser.open(self.student_path + '/studycourse/100')
925        self.browser.getLink("Download course registration slip").click()
926        self.assertEqual(self.browser.headers['Status'], '200 Ok')
927        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
928        path = os.path.join(samples_dir(), 'ft_course_registration_slip.pdf')
929        open(path, 'wb').write(self.browser.contents)
930        print "Sample PDF ft_course_registration_slip.pdf written to %s" % path
931
932        self.certificate.study_mode = 'ug_pt'
933        self.browser.open(self.student_path + '/studycourse/100')
934        self.browser.getLink("Download course registration slip").click()
935        self.assertEqual(self.browser.headers['Status'], '200 Ok')
936        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
937        path = os.path.join(samples_dir(), 'pt_course_registration_slip.pdf')
938        open(path, 'wb').write(self.browser.contents)
939        print "Sample PDF pt_course_registration_slip.pdf written to %s" % path
940
941        self.certificate.study_mode = 'special_pg_ft'
942        self.student.matric_number = u'AAU/SPS/FLW/LAW/15/PHD/09504'
943        self.browser.open(self.student_path + '/studycourse/100')
944        self.browser.getLink("Download course registration slip").click()
945        self.assertEqual(self.browser.headers['Status'], '200 Ok')
946        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
947        path = os.path.join(samples_dir(), 'pg_course_registration_slip.pdf')
948        open(path, 'wb').write(self.browser.contents)
949        print "Sample PDF pg_course_registration_slip.pdf written to %s" % path
950
951        # Students cant' view scores, cas and grades.
952        self.browser.open(self.student_path + '/studycourse/100')
953        self.assertFalse('Score' in self.browser.contents)
954        self.assertFalse('CA' in self.browser.contents)
955        self.assertFalse('Grade' in self.browser.contents)
956        self.browser.getLink("COURSE1").click()
957        self.browser.open(self.student_path + '/studycourse/100')
958        self.assertFalse('Score' in self.browser.contents)
959        self.assertFalse('CA' in self.browser.contents)
960        self.assertFalse('Grade' in self.browser.contents)
961
962    def test_student_2nd_semester_course_registration(self):
963        IWorkflowState(self.student).setState('school fee paid')
964        self.student['studycourse'].entry_session = 2015
965        self.course.semester = 2
966        self.browser.open(self.login_path)
967        self.browser.getControl(name="form.login").value = self.student_id
968        self.browser.getControl(name="form.password").value = 'spwd'
969        self.browser.getControl("Login").click()
970        self.browser.getLink("Study Course").click()
971        self.browser.getLink("Add course list").click()
972        self.browser.getControl("Create course list now").click()
973        self.assertFalse('COURSE1' in self.browser.contents)
974        # 2nd semester tickets can't be added manually
975        self.browser.getLink("Edit course list").click()
976        self.browser.getLink("here").click()
977        self.browser.getControl(name="form.course").value = ['COURSE1']
978        self.browser.getControl("Add course ticket").click()
979        self.assertTrue(
980            'COURSE1 is a 2nd semester course which can only '
981            'be added if school fees have been fully paid.'
982            in self.browser.contents)
983        # 2nd instalment has to be paid first
984        #self.certificate.school_fee_3 = 678.0
985        self.browser.open(self.payments_path + '/addop')
986        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
987        self.browser.getControl("Create ticket").click()
988        self.student['payments'].values()[0].approve()
989        self.browser.open(self.studycourse_path + '/100/ctadd')
990        self.browser.getControl(name="form.course").value = ['COURSE1']
991        self.browser.getControl("Add course ticket").click()
992        self.assertTrue('Successfully added COURSE1' in self.browser.contents)
993        return
994
995    def test_student_GST_registration(self):
996        configuration_1 = createObject('waeup.SessionConfiguration')
997        configuration_1.academic_session = 2016
998        configuration_1.gst_registration_1_fee = 3333.0
999        configuration_1.gst_text_book_1_fee = 4444.0
1000        configuration_1.gst_text_book_0_fee = 2222.0
1001        self.app['configuration'].addSessionConfiguration(configuration_1)
1002        course = createObject('waeup.Course')
1003        course.code = 'GST101'
1004        course.semester = 1
1005        course.credits = 10
1006        course.passmark = 40
1007        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1008            course)
1009        self.app['faculties']['fac1']['dep1'].certificates[
1010            'CERT1'].addCertCourse(course, level=100)
1011        IWorkflowState(self.student).setState('school fee paid')
1012        self.student['studycourse'].entry_session = 2016
1013        self.student['studycourse'].current_session = 2016
1014        self.course.semester = 2
1015        self.browser.open(self.login_path)
1016        self.browser.getControl(name="form.login").value = self.student_id
1017        self.browser.getControl(name="form.password").value = 'spwd'
1018        self.browser.getControl("Login").click()
1019        self.browser.getLink("Study Course").click()
1020        self.browser.getLink("Add course list").click()
1021        self.browser.getControl("Create course list now").click()
1022        self.assertFalse('GST101' in self.browser.contents)
1023        # GST101 tickets can't be added manually
1024        self.browser.getLink("Edit course list").click()
1025        self.browser.getLink("here").click()
1026        self.browser.getControl(name="form.course").value = ['GST101']
1027        self.browser.getControl("Add course ticket").click()
1028        self.assertTrue(
1029            'GST101 can only be added if both registration fee and text'
1030            in self.browser.contents)
1031        # GST fees have to be paid first
1032        self.browser.open(self.payments_path + '/addop')
1033        self.browser.getControl(name="form.p_category").value = ['gst_registration_1']
1034        self.browser.getControl("Create ticket").click()
1035        self.student['payments'].values()[0].approve()
1036        self.browser.open(self.studycourse_path + '/100/ctadd')
1037        self.browser.getControl(name="form.course").value = ['GST101']
1038        self.browser.getControl("Add course ticket").click()
1039        self.assertTrue(
1040            'GST101 can only be added if both registration fee and text'
1041            in self.browser.contents)
1042        self.browser.open(self.payments_path + '/addop')
1043        self.browser.getControl(name="form.p_category").value = ['gst_text_book_0']
1044        self.browser.getControl("Create ticket").click()
1045        self.student['payments'].values()[1].approve()
1046        self.browser.open(self.studycourse_path + '/100/ctadd')
1047        self.browser.getControl(name="form.course").value = ['GST101']
1048        self.browser.getControl("Add course ticket").click()
1049        self.assertTrue('Successfully added GST101' in self.browser.contents)
1050        return
1051
1052    def test_course_registration_forbidden(self):
1053        IWorkflowState(self.student).setState('school fee paid')
1054        self.student['studycourse'].entry_session = 2016
1055        self.student['studycourse'].current_session = 2016
1056        self.browser.open(self.login_path)
1057        self.browser.getControl(name="form.login").value = self.student_id
1058        self.browser.getControl(name="form.password").value = 'spwd'
1059        self.browser.getControl("Login").click()
1060        self.browser.getLink("Study Course").click()
1061        self.browser.getLink("Add course list").click()
1062        self.browser.getControl("Create course list now").click()
1063        self.browser.getLink("Edit course list").click()
1064        self.browser.getControl("Register course list").click()
1065        #self.assertTrue('Please pay faculty and departmental dues first'
1066        #    in self.browser.contents)
1067        #configuration_1 = createObject('waeup.SessionConfiguration')
1068        #configuration_1.academic_session = 2016
1069        #configuration_1.fac_dep_fee = 9999.0
1070        #self.app['configuration'].addSessionConfiguration(configuration_1)
1071        #self.browser.open(self.payments_path + '/addop')
1072        #self.browser.getControl(name="form.p_category").value = ['fac_dep']
1073        #self.browser.getControl("Create ticket").click()
1074        #self.student['payments'].values()[0].approveStudentPayment()
1075        #self.browser.open(self.studycourse_path + '/100/edit')
1076        #self.browser.getControl("Register course list").click()
1077
1078        ######################################################
1079        # Temporarily disabled ug_ft course registration
1080        #self.assertTrue('Course registration has been disabled'
1081        #    in self.browser.contents)
1082        #return
1083        ######################################################
1084
1085        self.assertTrue('Course list has been registered'
1086            in self.browser.contents)
1087        return
1088
1089    def deactivated_test_course_registration_forbidden_2(self):
1090        IWorkflowState(self.student).setState('school fee paid')
1091        self.student['studycourse'].entry_session = 2004
1092        self.student['studycourse'].current_session = 2016
1093        self.browser.open(self.login_path)
1094        self.browser.getControl(name="form.login").value = self.student_id
1095        self.browser.getControl(name="form.password").value = 'spwd'
1096        self.browser.getControl("Login").click()
1097        self.browser.getLink("Study Course").click()
1098        self.browser.getLink("Add course list").click()
1099        self.browser.getControl("Create course list now").click()
1100        self.browser.getLink("Edit course list").click()
1101        self.browser.getControl("Register course list").click()
1102
1103        ######################################################
1104        # Temporarily disabled ug_ft course registration
1105        #self.assertTrue('Course registration has been disabled'
1106        #    in self.browser.contents)
1107        #return
1108        ######################################################
1109
1110        self.assertTrue('Please pay restitution fee first'
1111            in self.browser.contents)
1112        configuration = createObject('waeup.SessionConfiguration')
1113        configuration.academic_session = 2016
1114        self.app['configuration'].addSessionConfiguration(configuration)
1115        self.app['configuration']['2016'].restitution_fee = 9999.0
1116        self.browser.open(self.payments_path + '/addop')
1117        self.browser.getControl(name="form.p_category").value = ['restitution']
1118        self.browser.getControl("Create ticket").click()
1119        self.student['payments'].values()[0].approveStudentPayment()
1120        self.browser.open(self.studycourse_path + '/100/edit')
1121        self.browser.getControl("Register course list").click()
1122        self.assertTrue('Course list has been registered'
1123            in self.browser.contents)
1124        return
1125
1126    def test_student_access_course_results(self):
1127        IWorkflowState(self.student).setState('school fee paid')
1128        self.student['studycourse'].entry_session = 2004
1129        self.student['studycourse'].current_session = 2004
1130        self.browser.open(self.login_path)
1131        self.browser.getControl(name="form.login").value = self.student_id
1132        self.browser.getControl(name="form.password").value = 'spwd'
1133        self.browser.getControl("Login").click()
1134        self.browser.getLink("Study Course").click()
1135        self.browser.getLink("Add course list").click()
1136        self.browser.getControl("Create course list now").click()
1137        self.assertFalse('Score' in self.browser.contents)
1138        IWorkflowState(self.student).setState('returning')
1139        self.browser.open(self.studycourse_path + '/100')
1140        self.assertFalse('Score' in self.browser.contents)
1141        self.app['configuration']['2004'].show_results = ['ug_ft']
1142        self.browser.open(self.studycourse_path + '/100')
1143        self.assertTrue('Score' in self.browser.contents)
1144        return
1145
1146    def test_repair_course_list(self):
1147        IWorkflowState(self.student).setState('school fee paid')
1148        self.student['studycourse'].entry_session = 2016
1149        self.student['studycourse'].current_session = 2016
1150        self.browser.open(self.login_path)
1151        self.browser.getControl(name="form.login").value = self.student_id
1152        self.browser.getControl(name="form.password").value = 'spwd'
1153        self.browser.getControl("Login").click()
1154        self.browser.getLink("Study Course").click()
1155        self.browser.getLink("Add course list").click()
1156        self.browser.getControl("Create course list now").click()
1157        self.assertTrue('Edit course list' in self.browser.contents)
1158        self.assertFalse('Repair course list' in self.browser.contents)
1159        self.student['studycourse'].current_level = 200
1160        self.browser.open(self.studycourse_path + '/100')
1161        self.assertFalse('Edit course list' in self.browser.contents)
1162        self.assertFalse('Repair course list' in self.browser.contents)
1163        configuration = createObject('waeup.SessionConfiguration')
1164        configuration.academic_session = 2016
1165        self.app['configuration'].addSessionConfiguration(configuration)
1166        self.app['configuration']['2016'].studylevel_repair_enabled = True
1167        self.browser.open(self.studycourse_path + '/100')
1168        self.assertFalse('Edit course list' in self.browser.contents)
1169        self.assertTrue('Repair course list' in self.browser.contents)
1170        self.browser.getLink("Repair").click()
1171        self.assertEqual(self.browser.url, self.studycourse_path + '/100/repair')
1172        self.assertTrue('Repair course list of 100' in self.browser.contents)
1173        return
1174
1175    def test_student_clearance(self):
1176        # Student cant login if their password is not set
1177        IWorkflowInfo(self.student).fireTransition('admit')
1178        self.browser.open(self.login_path)
1179        self.browser.getControl(name="form.login").value = self.student_id
1180        self.browser.getControl(name="form.password").value = 'spwd'
1181        self.browser.getControl("Login").click()
1182        self.assertMatches(
1183            '...You logged in...', self.browser.contents)
1184        # Admitted student can upload a passport picture
1185        # disabled on 16/01/24
1186        #self.browser.open(self.student_path + '/change_portrait')
1187        #ctrl = self.browser.getControl(name='passportuploadedit')
1188        #file_obj = open(SAMPLE_IMAGE, 'rb')
1189        #file_ctrl = ctrl.mech_control
1190        #file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1191        #self.browser.getControl(
1192        #    name='upload_passportuploadedit').click()
1193        #self.assertTrue(
1194        #    'src="http://localhost/app/students/E1000000/passport.jpg"'
1195        #    in self.browser.contents)
1196        # Student is redirected to the personal data form because
1197        # personal data form is not properly filled.
1198        #self.browser.open(self.student_path + '/start_clearance')
1199        #self.assertMatches('...Personal data form is not properly filled...',
1200        #                   self.browser.contents)
1201        #self.assertEqual(self.browser.url, self.student_path + '/edit_personal')
1202        #self.student.father_name = u'Rudolf'
1203        #self.browser.open(self.student_path + '/start_clearance')
1204        #self.assertMatches(
1205        #    '...<h1 class="kofa-content-label">Start clearance</h1>...',
1206        #    self.browser.contents)
1207
1208    def test_student_accommodation(self):
1209        del self.student['accommodation']['2004']
1210        self.student['studycourse'].certificate.study_mode = 'dp_ft'
1211        # All beds can be assigned
1212        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1213        bed1.bed_type = u'regular_male_all'
1214        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1215        bed2.bed_type = u'regular_female_all'
1216        notify(grok.ObjectModifiedEvent(bed1))
1217        notify(grok.ObjectModifiedEvent(bed2))
1218        # Login
1219        self.browser.open(self.login_path)
1220        self.browser.getControl(name="form.login").value = self.student_id
1221        self.browser.getControl(name="form.password").value = 'spwd'
1222        self.browser.getControl("Login").click()
1223        self.browser.open(self.acco_path)
1224        IWorkflowInfo(self.student).fireTransition('admit')
1225        self.browser.getControl("Book accommodation").click()
1226        self.assertTrue(
1227            'You are not eligible to book accommodation.'
1228            in self.browser.contents)
1229        self.student['studycourse'].certificate.study_mode = 'ug_ft'
1230        self.app['hostels'].accommodation_states = [PAID]
1231        self.browser.getControl("Book accommodation").click()
1232        self.assertTrue(
1233            'You are in the wrong registration state.'
1234            in self.browser.contents)
1235        IWorkflowState(self.student).setState(PAID)
1236
1237        self.browser.getLink("Payments").click()
1238        self.browser.getLink("Add current session payment ticket").click()
1239        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
1240        self.browser.getControl("Create ticket").click()
1241        self.student['payments'].values()[0].approveStudentPayment()
1242        pin = self.app['accesscodes']['HOS-0'].keys()[0]
1243        ac = self.app['accesscodes']['HOS-0'][pin]
1244        parts = pin.split('-')[1:]
1245        sfeseries, sfenumber = parts
1246        self.browser.open(self.acco_path)
1247        self.browser.getControl("Book accommodation").click()
1248        #self.assertFalse('Activation Code:' in self.browser.contents)
1249
1250        self.assertMatches('...Activation Code:...',
1251                           self.browser.contents)
1252        self.browser.getControl(name="ac_series").value = sfeseries
1253        self.browser.getControl(name="ac_number").value = sfenumber
1254        self.browser.getControl("Create bed ticket").click()
1255        # Bed is randomly selected but, since there is only
1256        # one bed for this student, we know that
1257        self.assertEqual(self.student['accommodation']['2004'].bed_coordinates,
1258            'Hall 1, Block A, Room 101, Bed A (regular_male_all)')
1259        # Only the hall name is displayed
1260        self.assertEqual(self.student[
1261            'accommodation']['2004'].display_coordinates,
1262            'Hall 1')
1263        self.assertFalse('Hall 1, Block A, Room 101, Bed A'
1264            in self.browser.contents)
1265        self.assertTrue('<td>Hall 1</td>'
1266            in self.browser.contents)
1267        return
1268
1269    def test_handle_courses_by_lecturer(self):
1270        self.app['users'].addUser('mrslecturer', 'mrslecturerSecret1')
1271        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1272        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1273        # Add course ticket
1274        studylevel = createObject(u'waeup.StudentStudyLevel')
1275        studylevel.level = 100
1276        studylevel.level_session = 2004
1277        self.student['studycourse'].addStudentStudyLevel(
1278            self.certificate, studylevel)
1279        # Assign local Lecturer role for a certificate.
1280        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1281        prmlocal = IPrincipalRoleManager(course)
1282        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1283        notify(LocalRoleSetEvent(
1284            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1285        # Login as lecturer.
1286        self.browser.open(self.login_path)
1287        self.browser.getControl(name="form.login").value = 'mrslecturer'
1288        self.browser.getControl(name="form.password").value = 'mrslecturerSecret1'
1289        self.browser.getControl("Login").click()
1290        self.browser.getLink("My Courses").click()
1291        self.browser.getLink("COURSE1").click()
1292        # Course results can be batch edited via the edit_courses view.
1293        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
1294        self.app['configuration'].current_academic_session = 2004
1295        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
1296        IWorkflowState(self.student).setState('courses validated')
1297        self.browser.open(
1298            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1299        self.assertTrue(
1300            'input type="text" name="scores:list"'
1301            in self.browser.contents)
1302        self.browser.getControl(name="scores:list", index=0).value = '55'
1303        self.browser.getControl(name="cas:list", index=0).value = '22'
1304        self.browser.getControl("Update scores from").click()
1305        # New score and ca has been set.
1306        self.assertEqual(
1307            self.student['studycourse']['100']['COURSE1'].score, 55)
1308        self.assertEqual(
1309            self.student['studycourse']['100']['COURSE1'].ca, 22)
1310        # Score editing has been logged.
1311        logfile = os.path.join(
1312            self.app['datacenter'].storage, 'logs', 'students.log')
1313        logcontent = open(logfile).read()
1314        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1315                        'E1000000 100/COURSE1 score updated (55)' in logcontent)
1316        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1317                        'E1000000 100/COURSE1 ca updated (22)' in logcontent)
1318        # Non-integer scores won't be accepted.
1319        self.browser.open(
1320            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1321        self.assertTrue('value="55" />' in self.browser.contents)
1322        self.browser.getControl(name="scores:list", index=0).value = 'abc'
1323        self.browser.getControl("Update scores").click()
1324        self.assertTrue('Error: Score(s), CA(s) and Imported TS(s) of TESTER, Anna have not be updated.'
1325            in self.browser.contents)
1326        # Scores can be removed.
1327        self.browser.open(
1328            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1329        self.browser.getControl(name="scores:list", index=0).value = ''
1330        self.browser.getControl("Update scores").click()
1331        self.assertEqual(
1332            self.student['studycourse']['100']['COURSE1'].score, None)
1333        logcontent = open(logfile).read()
1334        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1335                        'E1000000 100/COURSE1 score updated (None)' in logcontent)
1336
1337
1338    def disabled_test_student_view_transcript(self):
1339        # Student cant login if their password is not set
1340        IWorkflowInfo(self.student).fireTransition('admit')
1341        self.browser.open(self.login_path)
1342        self.browser.getControl(name="form.login").value = self.student_id
1343        self.browser.getControl(name="form.password").value = 'spwd'
1344        self.browser.getControl("Login").click()
1345        self.assertMatches(
1346            '...You logged in...', self.browser.contents)
1347        # Students can view the transcript
1348        self.browser.open(self.studycourse_path)
1349        self.browser.getLink("Transcript").click()
1350        self.browser.getLink("Academic Transcript").click()
1351        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1352        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1353
1354    def test_alumni_request_pw(self):
1355        # Add an applicants container
1356        applicantscontainer = ApplicantsContainer()
1357        applicantscontainer.code = u'trans2017'
1358        applicantscontainer.prefix = 'trans'
1359        applicantscontainer.year = 2017
1360        applicantscontainer.title = u'This is the trans2017 container'
1361        applicantscontainer.application_category = 'no'
1362        applicantscontainer.mode = 'create'
1363        applicantscontainer.strict_deadline = True
1364        delta = timedelta(days=10)
1365        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1366        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1367        self.app['applicants']['trans2017'] = applicantscontainer
1368        self.applicantscontainer = self.app['applicants']['trans2017']
1369        # Student with wrong number can't be found.
1370        # Applicant is redirected to application section.
1371        self.browser.open('http://localhost/app/alumni_requestpw')
1372        self.browser.getControl(name="form.lastname").value = 'Tester'
1373        self.browser.getControl(name="form.number").value = 'anynumber'
1374        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1375        self.browser.getControl("Send login credentials").click()
1376        self.assertTrue('No student record found.'
1377            in self.browser.contents)
1378        self.assertEqual(self.browser.url,
1379            'http://localhost/app/applicants/trans2017/register')
1380
1381    def test_student_course_registration_outstanding(self):
1382        self.course = createObject('waeup.Course')
1383        self.course.code = 'COURSE2'
1384        self.course.semester = 1
1385        self.course.credits = 39
1386        self.course.passmark = 40
1387        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1388            self.course)
1389        IWorkflowState(self.student).setState('school fee paid')
1390        self.browser.open(self.login_path)
1391        self.browser.getControl(name="form.login").value = self.student_id
1392        self.browser.getControl(name="form.password").value = 'spwd'
1393        self.browser.getControl("Login").click()
1394        self.browser.open(self.student_path + '/studycourse/add')
1395        self.browser.getControl("Create course list now").click()
1396        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1397        self.student['studycourse'].current_level = 200
1398        self.browser.getLink("Study Course").click()
1399        self.browser.getLink("Add course list").click()
1400        self.assertMatches('...Add current level 200 (Year 2)...',
1401                           self.browser.contents)
1402        self.browser.getControl("Create course list now").click()
1403        self.browser.getLink("200").click()
1404        self.browser.getLink("Edit course list").click()
1405        self.browser.getLink("here").click()
1406        self.browser.getControl(name="form.course").value = ['COURSE2']
1407        self.browser.getControl("Add course ticket").click()
1408        # Carryover COURSE1 in level 200 already has 10 credits
1409        self.assertMatches(
1410            '...Maximum credits exceeded...', self.browser.contents)
1411        # If COURSE1 is outstanding, its credits won't be considered
1412        self.student['studycourse']['200']['COURSE1'].outstanding = True
1413        self.browser.getControl("Add course ticket").click()
1414        # Corresponding certificate course is missing
1415        self.assertTrue(
1416            'COURSE2 is not part of the CERT1 curriculum.'
1417            in self.browser.contents)
1418        self.app['faculties']['fac1']['dep1'].certificates[
1419            'CERT1'].addCertCourse(self.course, level=100)
1420        self.browser.getControl("Add course ticket").click()
1421        self.assertTrue(
1422            'Successfully added COURSE2' in self.browser.contents)
1423        return
1424
1425    def test_examination_schedule_slip(self):
1426        self.student.flash_notice = u'My Examination Date'
1427        self.browser.open(self.login_path)
1428        self.browser.getControl(name="form.login").value = self.student_id
1429        self.browser.getControl(name="form.password").value = 'spwd'
1430        self.browser.getControl("Login").click()
1431        self.browser.open(self.student_path)
1432        self.browser.getLink("Download examination schedule slip").click()
1433        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1434        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1435        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
1436        open(path, 'wb').write(self.browser.contents)
1437        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
1438        # If flash_notice does not contain exam' the button does not show up.
1439        self.student.flash_notice = u'anything'
1440        self.browser.open(self.student_path)
1441        self.assertFalse('examination schedule slip' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.