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

Last change on this file since 16982 was 16967, checked in by Henrik Bettermann, 2 years ago

Implement transcript slip student view.

  • Property svn:keywords set to Id
File size: 74.6 KB
Line 
1## $Id: test_browser.py 16967 2022-06-16 07:19:57Z 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_1 = 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['studycourse'].certificate.school_fee_1 = 6666.0
236        self.app['configuration']['2015'].union_fee = 1250.0
237        self.app['configuration']['2015'].welfare_fee = 750.0
238        self.student.nationality = u'NG'
239        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
240        self.browser.open(self.payments_path + '/addop')
241        self.assertFalse('schoolfee_1' in self.browser.contents)
242        #self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
243        #self.browser.getControl("Create ticket").click()
244        #self.assertTrue(
245        #    'Part-time students are not allowed' in self.browser.contents)
246        self.student['studycourse'].certificate.study_mode = 'ug_ft'
247        self.browser.open(self.payments_path + '/addop')
248        self.assertTrue('schoolfee_1' in self.browser.contents)
249        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
250        self.browser.getControl("Create ticket").click()
251        self.assertTrue('You are not allowed to pay by instalments.'
252            in self.browser.contents)
253        IWorkflowState(self.student).setState('cleared')
254        self.student['studycourse'].entry_session = 2015
255        self.student['studycourse'].current_session = 2015
256        self.browser.open(self.payments_path + '/addop')
257        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
258        self.browser.getControl("Create ticket").click()
259        self.assertTrue('ticket created' in self.browser.contents)
260        # We can't add the 2nd instalment ticket because the
261        # first one has not yet been approved.
262        #self.browser.open(self.payments_path + '/addop')
263        #self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
264        #self.browser.getControl("Create ticket").click()
265        #self.assertMatches('...1st school fee instalment has not yet been paid...',
266        #                   self.browser.contents)
267        # Ok, then we approve the first instalment ...
268        self.browser.open(self.payments_path)
269        ctrl = self.browser.getControl(name='val_id')
270        p_id = ctrl.options[0]
271        self.browser.open(self.payments_path + '/' + p_id + '/approve')
272        # ... add the second instalment ...
273        self.browser.open(self.payments_path + '/addop')
274        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
275        self.browser.getControl("Create ticket").click()
276        self.assertTrue('ticket created' in self.browser.contents)
277        # ... approve the second instalment ...
278        self.browser.open(self.payments_path)
279        ctrl = self.browser.getControl(name='val_id')
280        p_id = ctrl.options[1]
281        self.browser.open(self.payments_path + '/' + p_id + '/approve')
282        self.assertEqual(self.student['payments'].values()[0].p_category, 'schoolfee_1')
283        self.assertEqual(self.student['payments'].values()[1].p_category, 'schoolfee_2')
284        # (6666-200)/2 + 1250 + 750 - 400 + 200
285        self.assertEqual(self.student['payments'].values()[0].amount_auth, 5033.0)
286        # (6666-200)/2 + 200
287        self.assertEqual(self.student['payments'].values()[1].amount_auth, 3433.0)
288        # The  two payments belong to the same session and level.
289        self.assertEqual(self.student['payments'].values()[0].p_session, 2015)
290        self.assertEqual(self.student['payments'].values()[0].p_level, 100)
291        self.assertEqual(self.student['payments'].values()[1].p_session, 2015)
292        self.assertEqual(self.student['payments'].values()[1].p_level, 100)
293
294        # Returning student
295        configuration_2 = createObject('waeup.SessionConfiguration')
296        configuration_2.academic_session = 2016
297        self.app['configuration'].addSessionConfiguration(configuration_2)
298        self.student['studycourse'].certificate.school_fee_2 = 5666.0
299        self.app['configuration']['2016'].union_fee = 1250.0
300        self.app['configuration']['2016'].welfare_fee = 750.0
301        self.student.father_name = u'Albert'
302        IWorkflowState(self.student).setState('returning')
303        self.browser.open(self.payments_path + '/addop')
304        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
305        self.browser.getControl("Create ticket").click()
306        self.browser.open(self.payments_path + '/addop')
307        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
308        self.browser.getControl("Create ticket").click()
309        # Student is still in the first  session.
310        self.assertTrue('This type of payment' in self.browser.contents)
311        self.browser.open(self.payments_path)
312        ctrl = self.browser.getControl(name='val_id')
313        p_id = ctrl.options[2]
314        self.browser.open(self.payments_path + '/' + p_id + '/approve')
315        self.browser.open(self.payments_path + '/addop')
316        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
317        self.browser.getControl("Create ticket").click()
318        self.assertTrue('ticket created' in self.browser.contents)
319        # (5666-200)/2 + 1250 + 750 - 400 + 200
320        self.assertEqual(self.student['payments'].values()[2].amount_auth, 4533.0)
321        # (5666-200)/2 + 200
322        self.assertEqual(self.student['payments'].values()[3].amount_auth, 2933.0)
323        # The last two payments belong to the same session and level.
324        self.assertEqual(self.student['payments'].values()[2].p_session, 2016)
325        self.assertEqual(self.student['payments'].values()[2].p_level, 200)
326        self.assertEqual(self.student['payments'].values()[3].p_session, 2016)
327        self.assertEqual(self.student['payments'].values()[3].p_level, 200)
328        return
329
330    def test_manage_payments_bypass_ac_creation(self):
331        self.student['studycourse'].certificate.school_fee_1 = 6666.0
332        self.student.nationality = u'NG'
333        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
334        self.browser.open(self.payments_path)
335        IWorkflowState(self.student).setState('cleared')
336        self.browser.getLink("Add current session payment ticket").click()
337        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
338        self.browser.getControl("Create ticket").click()
339        self.browser.open(self.payments_path)
340        ctrl = self.browser.getControl(name='val_id')
341        value = ctrl.options[0]
342        self.browser.getLink(value).click()
343        payment_url = self.browser.url
344        logfile = os.path.join(
345            self.app['datacenter'].storage, 'logs', 'students.log')
346        # The ticket can be found in the payments_catalog
347        cat = queryUtility(ICatalog, name='payments_catalog')
348        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
349        self.assertTrue(len(results), 1)
350        self.assertTrue(results[0] is self.student['payments'][value])
351        # Managers can approve the payment
352        self.browser.open(payment_url)
353        self.browser.getLink("Approve payment").click()
354        self.assertMatches('...Payment approved...',
355                          self.browser.contents)
356        # Approval is logged in students.log ...
357        logcontent = open(logfile).read()
358        self.assertTrue(
359            'zope.mgr - students.browser.OnlinePaymentApproveView '
360            '- E1000000 - schoolfee_incl payment approved'
361            in logcontent)
362        # ... and in payments.log
363        logfile = os.path.join(
364            self.app['datacenter'].storage, 'logs', 'payments.log')
365        logcontent = open(logfile).read()
366        self.assertTrue(
367            '"zope.mgr",E1000000,%s,schoolfee_incl,6666.0,AP,,,,,,\n' % value
368            in logcontent)
369        # Student is in state school fee paid, no activation
370        # code was necessary.
371        self.assertEqual(self.student.state, 'school fee paid')
372        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
373        return
374
375    def test_scores_csv_upload_available(self):
376        # lecturers can upload a CSV file to set values.
377        self.login_as_lecturer()
378        # set value to change from
379        self.student['studycourse']['100']['COURSE1'].score = 55
380        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
381        self.browser.open(self.edit_scores_url)
382        upload_ctrl = self.browser.getControl(name='uploadfile:file')
383        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','52','77'))
384        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
385        self.browser.getControl("Update editable scores from").click()
386        self.assertTrue('TESTER, Anna have not be updated' in self.browser.contents)
387        # values have not been changed
388        self.assertEqual(
389            self.student['studycourse']['100']['COURSE1'].score, 55)
390        self.assertEqual(
391            self.student['studycourse']['100']['COURSE1'].ca, None)
392        self.assertEqual(
393            self.student['studycourse']['100']['COURSE1'].imported_ts, None)
394        self.browser.open(self.edit_scores_url)
395        upload_ctrl = self.browser.getControl(name='uploadfile:file')
396        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','22','77'))
397        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
398        self.browser.getControl("Update editable scores from").click()
399        # values changed
400        self.assertEqual(
401            self.student['studycourse']['100']['COURSE1'].score, 65)
402        self.assertEqual(
403            self.student['studycourse']['100']['COURSE1'].ca, 22)
404        self.assertEqual(
405            self.student['studycourse']['100']['COURSE1'].imported_ts, 77)
406
407    def test_scores_previous_session(self):
408        # lecturers can download a CSV file to set values.
409        self.login_as_lecturer()
410        self.student['studycourse']['100']['COURSE1'].score = 55
411        self.browser.open(self.edit_prev_scores_url)
412        self.assertTrue('No student found' in self.browser.contents)
413        configuration = createObject('waeup.SessionConfiguration')
414        configuration.academic_session = 2003
415        self.app['configuration'].addSessionConfiguration(configuration)
416        self.app['configuration']['2003'].score_editing_enabled = ['ug_ft']
417        self.student['studycourse']['100'].level_session = 2003
418        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['COURSE1']))
419        self.browser.open(self.edit_prev_scores_url)
420        self.browser.getLink("Download csv file").click()
421        self.assertEqual(self.browser.headers['Status'], '200 Ok')
422        self.assertEqual(self.browser.headers['Content-Type'],
423                         'text/csv; charset=UTF-8')
424        self.assertEqual(self.browser.contents,
425            'matric_number,student_id,display_fullname,'
426            'depcode,faccode,level,code,level_session,ca,score,'
427            'total_score,grade,imported_ts\r\n234,E1000000,"TESTER, Anna",dep1,fac1,'
428            '100,COURSE1,2003,,55,,,\r\n')
429        self.browser.open(self.edit_prev_scores_url)
430        upload_ctrl = self.browser.getControl(name='uploadfile:file')
431        upload_file = StringIO(UPLOAD_CSV_TEMPLATE % ('65','22','77'))
432        upload_ctrl.add_file(upload_file, 'text/csv', 'myscores.csv')
433        self.browser.getControl("Update editable scores from").click()
434        # value changed
435        self.assertEqual(
436            self.student['studycourse']['100']['COURSE1'].score, 65)
437        self.assertEqual(
438            self.student['studycourse']['100']['COURSE1'].ca, 22)
439        self.assertEqual(
440            self.student['studycourse']['100']['COURSE1'].imported_ts, 77)
441
442    def test_lecturers_can_download_course_tickets(self):
443        # A course ticket slip can be downloaded
444        self.login_as_lecturer()
445        self.course.title = (u'Lorem ipsum dolor sit amet, consectetur '
446                            u'adipisici elit, sed eiusmod tempor incidunt')
447        self.student['studycourse']['100']['COURSE1'].score = 55
448        self.student['studycourse']['100']['COURSE1'].ca = 11
449        self.student.matric_number = u'CMS/FBM/NSG/16/28838'
450        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
451        pdf_url = '%s/coursetickets.pdf' % self.course_url
452        self.browser.open(pdf_url)
453        self.assertEqual(self.browser.headers['Status'], '200 Ok')
454        self.assertEqual(
455            self.browser.headers['Content-Type'], 'application/pdf')
456        path = os.path.join(samples_dir(), 'coursetickets.pdf')
457        open(path, 'wb').write(self.browser.contents)
458        print "Sample PDF coursetickets.pdf written to %s" % path
459        # The CA column is not shown if CA is not used
460        self.student['studycourse']['100']['COURSE1'].ca = 0
461        self.browser.open(pdf_url)
462        path = os.path.join(samples_dir(), 'coursetickets_wo_ca.pdf')
463        open(path, 'wb').write(self.browser.contents)
464        print "Sample PDF coursetickets_wo_ca.pdf written to %s" % path
465
466    def test_lecturers_do_only_see_selected_students(self):
467        # A course ticket slip can be downloaded
468        self.login_as_lecturer()
469        self.student['studycourse']['100']['COURSE1'].score = 55
470        self.student['studycourse']['100']['COURSE1'].ca = 11
471        self.browser.open(self.edit_scores_url)
472        self.assertTrue('No student found' in self.browser.contents)
473        pdf_url = '%s/coursetickets.pdf' % self.course_url
474        self.browser.open(pdf_url)
475        self.assertEqual(self.browser.headers['Status'], '200 Ok')
476        self.assertEqual(
477            self.browser.headers['Content-Type'], 'application/pdf')
478        path = os.path.join(samples_dir(), 'coursetickets_filtered.pdf')
479        open(path, 'wb').write(self.browser.contents)
480        print "Sample PDF coursetickets_filtered.pdf written to %s" % path
481        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
482        self.browser.open(self.edit_scores_url)
483        self.assertFalse('No student found' in self.browser.contents)
484        self.assertTrue('TESTER, Anna' in self.browser.contents)
485
486    def test_lecturers_can_download_attendance_sheet(self):
487        # A course ticket slip can be downloaded
488        self.course.title = (u'Lorem ipsum     dolor sit amet, consectetur     adipisici, '
489                             u'sed         eiusmod tempor    incidunt ut  labore et dolore')
490        self.student.firstname = u'Emmanuella Woyengifigha Mercy Onosemudiana'
491        self.student.lastname = u'OYAKEMIEGBEGHA'
492        self.student.matric_number = u'CMS/FBM/NSG/17/38186'
493        self.login_as_lecturer()
494        pdf_url = '%s/attendance.pdf' % self.course_url
495        self.browser.open(pdf_url)
496        self.assertEqual(self.browser.headers['Status'], '200 Ok')
497        self.assertEqual(
498            self.browser.headers['Content-Type'], 'application/pdf')
499        path = os.path.join(samples_dir(), 'attendance.pdf')
500        open(path, 'wb').write(self.browser.contents)
501        print "Sample PDF attendance.pdf written to %s" % path
502
503    def test_transcripts(self):
504        studylevel = createObject(u'waeup.StudentStudyLevel')
505        studylevel.level = 100
506        studylevel.level_session = 2005
507        self.student['studycourse'].entry_mode = 'ug_ft'
508        self.student['studycourse'].addStudentStudyLevel(
509            self.certificate, studylevel)
510        studylevel2 = createObject(u'waeup.StudentStudyLevel')
511        studylevel2.level = 200
512        studylevel2.level_session = 2006
513        self.student['studycourse']['100']['COURSE1'].score = 33 # no carry-over!
514        self.student['studycourse']['100']['COURSE1'].ca = 22
515        self.student['studycourse'].addStudentStudyLevel(
516            self.certificate, studylevel2)
517        # Add second course (COURSE has been added automatically)
518        courseticket = createObject('waeup.CourseTicket')
519        courseticket.code = 'ANYCODE'
520        courseticket.title = u'Any TITLE'
521        courseticket.credits = 13
522        courseticket.score = 55
523        courseticket.ca = 11
524        courseticket.semester = 1
525        courseticket.dcode = u'ANYDCODE'
526        courseticket.fcode = u'ANYFCODE'
527        self.student['studycourse']['200']['COURSE2'] = courseticket
528        self.assertEqual(self.student['studycourse']['100'].gpa_params_rectified[0], 3.0)
529        self.assertEqual(self.student['studycourse']['200'].gpa_params_rectified[0], 4.0)
530        # Get transcript data
531        td = self.student['studycourse'].getTranscriptData()
532        self.assertEqual(td[0][0]['level_key'], '100')
533        self.assertEqual(td[0][0]['sgpa'], 3.0)
534        self.assertEqual(td[0][0]['level'].level, 100)
535        self.assertEqual(td[0][0]['level'].level_session, 2005)
536        self.assertEqual(td[0][0]['tickets_1'][0].code, 'COURSE1')
537        self.assertEqual(td[0][1]['level_key'], '200')
538        self.assertEqual(td[0][1]['sgpa'], 4.0)
539        self.assertEqual(td[0][1]['level'].level, 200)
540        self.assertEqual(td[0][1]['level'].level_session, 2006)
541        self.assertEqual(td[0][1]['tickets_1'][0].code, 'ANYCODE')
542        self.assertEqual(td[1], 3.5652173913043477)
543        # Set Officer 1
544        self.app['faculties']['fac1']['dep1'].officer_1 = u'Transcript Boss'
545        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
546        self.browser.open(self.student_path + '/studycourse/transcript')
547        self.assertEqual(self.browser.headers['Status'], '200 Ok')
548        self.assertTrue('Transcript' in self.browser.contents)
549        # Officers can open the pdf transcript
550        self.browser.open(self.student_path + '/studycourse/transcript.pdf')
551        self.assertEqual(self.browser.headers['Status'], '200 Ok')
552        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
553        path = os.path.join(samples_dir(), 'transcript.pdf')
554        open(path, 'wb').write(self.browser.contents)
555        print "Sample PDF transcript.pdf written to %s" % path
556        # Officers can open the pdf student viewtranscript
557        self.browser.open(self.student_path + '/studycourse/transcript_studview.pdf')
558        self.assertEqual(self.browser.headers['Status'], '200 Ok')
559        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
560        path = os.path.join(samples_dir(), 'transcript_studview.pdf')
561        open(path, 'wb').write(self.browser.contents)
562        print "Sample PDF transcript_studview.pdf written to %s" % path
563
564    def test_payment_disabled(self):
565        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
566        self.browser.open(self.payments_path)
567        IWorkflowState(self.student).setState('cleared')
568        self.browser.getLink("Add current session payment ticket").click()
569        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
570        self.browser.getControl("Create ticket").click()
571        self.assertMatches('...ticket created...', self.browser.contents)
572        self.app['configuration']['2004'].payment_disabled = ['sf_all']
573        self.browser.open(self.payments_path)
574        self.browser.getLink("Add current session payment ticket").click()
575        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
576        self.browser.getControl("Create ticket").click()
577        self.assertMatches('...This category of payments has been disabled...',
578                           self.browser.contents)
579
580        self.app['configuration']['2004'].payment_disabled = ['sf_ug_pt']
581        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
582        self.browser.getControl("Create ticket").click()
583        self.assertMatches('...ticket created...', self.browser.contents)
584        self.certificate.study_mode = 'ug_pt'
585        self.browser.open(self.payments_path)
586        self.browser.getLink("Add current session payment ticket").click()
587        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
588        self.browser.getControl("Create ticket").click()
589        self.assertMatches('...This category of payments has been disabled...',
590                           self.browser.contents)
591
592        self.app['configuration']['2004'].payment_disabled = ['sf_pg']
593        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
594        self.browser.getControl("Create ticket").click()
595        self.assertMatches('...ticket created...', self.browser.contents)
596        self.certificate.study_mode = 'special_pg_ft'
597        self.browser.open(self.payments_path)
598        self.browser.getLink("Add current session payment ticket").click()
599        self.browser.getControl(name="form.p_category").value = ['schoolfee']
600        self.browser.getControl("Create ticket").click()
601        self.assertMatches('...This category of payments has been disabled...',
602                           self.browser.contents)
603        return
604
605class StudentUITests(StudentsFullSetup):
606    """Tests for customized student class views and pages
607    """
608
609    layer = FunctionalLayer
610
611    def setUp(self):
612        super(StudentUITests, self).setUp()
613        self.app['faculties']['fac1']['dep1'].certificates[
614            'CERT1']['COURSE1_100'].course_category = 'C'
615        bedticket = BedTicket()
616        bedticket.booking_session = 2004
617        bedticket.bed_type = u'any bed type'
618        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
619        bedticket.bed_coordinates = u'My bed coordinates'
620        self.student['accommodation'].addBedTicket(bedticket)
621
622    def test_maintenance_fee_payment(self):
623        self.certificate.study_mode = 'ug_ft'
624        self.student['studycourse'].entry_session = 2013
625        self.student.nationality = u'NG'
626        IWorkflowState(self.student).setState('cleared')
627        self.browser.open(self.login_path)
628        self.browser.getControl(name="form.login").value = self.student_id
629        self.browser.getControl(name="form.password").value = 'spwd'
630        self.browser.getControl("Login").click()
631        self.browser.open(self.student_path + '/payments')
632        self.browser.getLink("Add current session payment ticket").click()
633        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
634        self.browser.getControl("Create ticket").click()
635        self.assertTrue('ticket created' in self.browser.contents)
636        value = self.student['payments'].keys()[0]
637        self.browser.getLink(value).click()
638        self.assertTrue('<span>My bed coordinates</span>' in self.browser.contents)
639        self.assertEqual(self.student['payments'][value].amount_auth, 876.0)
640        return
641
642    def test_student_schoolfee_payments(self):
643        configuration_1 = createObject('waeup.SessionConfiguration')
644        configuration_1.academic_session = 2018
645        self.app['configuration'].addSessionConfiguration(configuration_1)
646        self.certificate.study_mode = 'ug_ft'
647        self.student['studycourse'].entry_session = 2018
648        self.student['studycourse'].current_session = 2018
649        self.student['studycourse'].entry_mode = 'ug_ft'
650        self.student['studycourse'].certificate.school_fee_1 = 50200.0
651        self.app['configuration']['2018'].union_fee = 1200.0
652        self.app['configuration']['2018'].welfare_fee = 700.0
653        #self.app['configuration']['2017'].id_card_fee = 300.0
654        self.app['configuration']['2018'].sports_fee = 300.0
655        self.app['configuration']['2018'].library_fee = 300.0
656        self.student.nationality = u'NG'
657        # Login
658        IWorkflowState(self.student).setState('cleared')
659        self.browser.open(self.login_path)
660        self.browser.getControl(name="form.login").value = self.student_id
661        self.browser.getControl(name="form.password").value = 'spwd'
662        self.browser.getControl("Login").click()
663        # Test school fee payments
664        self.browser.open(self.student_path + '/payments')
665        self.browser.getLink("Add current session payment ticket").click()
666        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
667        self.browser.getControl("Create ticket").click()
668        self.assertTrue('ticket created' in self.browser.contents)
669        value = self.student['payments'].keys()[0]
670        self.student['payments'][value].p_state = 'paid'
671        self.browser.getLink(value).click()
672        self.assertTrue('Amount Authorized' in self.browser.contents)
673        # 50200 + 1200 + 700 + 300 + 300- 800 = 51900
674        self.assertEqual(self.student['payments'][value].amount_auth, 51900.0)
675        self.student['payments'][value].r_company = u'interswitch'
676        self.browser.getLink("Download").click()
677        self.assertEqual(self.browser.headers['Status'], '200 Ok')
678        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
679        path = os.path.join(samples_dir(), 'payment_slip.pdf')
680        open(path, 'wb').write(self.browser.contents)
681        print "Sample PDF payment_slip.pdf written to %s" % path
682        # Another school fee payment cannot be added
683        self.student['payments'][value].approve()
684        self.browser.open(self.student_path + '/payments')
685        self.browser.getLink("Add current session payment ticket").click()
686        self.browser.getControl(name="form.p_category").value = ['schoolfee']
687        self.browser.getControl("Create ticket").click()
688        self.assertTrue(
689            'You must choose a payment which includes additional fees'
690            in self.browser.contents)
691        return
692
693    def test_late_registration(self):
694        delta = timedelta(days=10)
695        self.app['configuration'][
696            '2004'].coursereg_deadline = datetime.now(pytz.utc) - delta
697        IWorkflowState(self.student).setState('school fee paid')
698        # Current session is 2004. Here we test course registration for
699        # returning students.
700        self.student['studycourse'].entry_session = 2003
701        self.student['studycourse'].current_session = 2016
702        # Login
703        self.browser.open(self.login_path)
704        self.browser.getControl(name="form.login").value = self.student_id
705        self.browser.getControl(name="form.password").value = 'spwd'
706        self.browser.getControl("Login").click()
707        # Make restitution fee payment
708        # Ivie: The restitution was only for returning students of 2016/2017.
709        # Hence, it is only valid for 2016 payment session returning students.
710        configuration = createObject('waeup.SessionConfiguration')
711        configuration.academic_session = 2016
712        self.app['configuration'].addSessionConfiguration(configuration)
713        self.app['configuration']['2016'].restitution_fee = 9999.0
714        self.browser.open(self.payments_path + '/addop')
715        self.browser.getControl(name="form.p_category").value = ['restitution']
716        self.browser.getControl("Create ticket").click()
717        self.student['payments'].values()[0].approveStudentPayment()
718        # Make late registration fee payment
719        self.student['studycourse'].current_session = 2004
720        self.browser.open(self.payments_path + '/addop')
721        self.browser.getControl(name="form.p_category").value = ['late_registration']
722        self.browser.getControl("Create ticket").click()
723        self.assertMatches('...ticket created...',
724                           self.browser.contents)
725        self.browser.getLink("Study Course").click()
726        self.browser.getLink("Add course list").click()
727        self.assertMatches('...Add current level 100 (Year 1)...',
728                           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()[1].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 deactivated_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.assertMatches('...Add current level 100 (Year 1)...',
908                           self.browser.contents)
909        self.browser.getControl("Create course list now").click()
910        # Students can't open the customized pdf course registration slip
911        self.browser.open(
912            self.student_path + '/studycourse/100/course_registration_slip.pdf')
913        self.assertTrue('Forbidden' in self.browser.contents)
914        # They can open slips from the previous session ...
915        self.student['studycourse'].current_level = 200
916        self.browser.open(self.student_path + '/studycourse/100')
917        self.browser.getLink("Download course registration slip").click()
918        self.assertEqual(self.browser.headers['Status'], '200 Ok')
919        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
920        # or if they have registered their course list
921        self.student['studycourse'].current_level = 200
922        IWorkflowState(self.student).setState('courses registered')
923        self.browser.open(self.student_path + '/studycourse/100')
924        self.browser.getLink("Download course registration slip").click()
925        self.assertEqual(self.browser.headers['Status'], '200 Ok')
926        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
927        path = os.path.join(samples_dir(), 'ft_course_registration_slip.pdf')
928        open(path, 'wb').write(self.browser.contents)
929        print "Sample PDF ft_course_registration_slip.pdf written to %s" % path
930
931        self.certificate.study_mode = 'ug_pt'
932        self.browser.open(self.student_path + '/studycourse/100')
933        self.browser.getLink("Download course registration slip").click()
934        self.assertEqual(self.browser.headers['Status'], '200 Ok')
935        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
936        path = os.path.join(samples_dir(), 'pt_course_registration_slip.pdf')
937        open(path, 'wb').write(self.browser.contents)
938        print "Sample PDF pt_course_registration_slip.pdf written to %s" % path
939
940        self.certificate.study_mode = 'special_pg_ft'
941        self.student.matric_number = u'AAU/SPS/FLW/LAW/15/PHD/09504'
942        self.browser.open(self.student_path + '/studycourse/100')
943        self.browser.getLink("Download course registration slip").click()
944        self.assertEqual(self.browser.headers['Status'], '200 Ok')
945        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
946        path = os.path.join(samples_dir(), 'pg_course_registration_slip.pdf')
947        open(path, 'wb').write(self.browser.contents)
948        print "Sample PDF pg_course_registration_slip.pdf written to %s" % path
949
950        # Students cant' view scores, cas and grades.
951        self.browser.open(self.student_path + '/studycourse/100')
952        self.assertFalse('Score' in self.browser.contents)
953        self.assertFalse('CA' in self.browser.contents)
954        self.assertFalse('Grade' in self.browser.contents)
955        self.browser.getLink("COURSE1").click()
956        self.browser.open(self.student_path + '/studycourse/100')
957        self.assertFalse('Score' in self.browser.contents)
958        self.assertFalse('CA' in self.browser.contents)
959        self.assertFalse('Grade' in self.browser.contents)
960
961    def test_student_2nd_semester_course_registration(self):
962        IWorkflowState(self.student).setState('school fee paid')
963        self.student['studycourse'].entry_session = 2015
964        self.course.semester = 2
965        self.browser.open(self.login_path)
966        self.browser.getControl(name="form.login").value = self.student_id
967        self.browser.getControl(name="form.password").value = 'spwd'
968        self.browser.getControl("Login").click()
969        self.browser.getLink("Study Course").click()
970        self.browser.getLink("Add course list").click()
971        self.browser.getControl("Create course list now").click()
972        self.assertFalse('COURSE1' in self.browser.contents)
973        # 2nd semester tickets can't be added manually
974        self.browser.getLink("Edit course list").click()
975        self.browser.getLink("here").click()
976        self.browser.getControl(name="form.course").value = ['COURSE1']
977        self.browser.getControl("Add course ticket").click()
978        self.assertTrue(
979            'COURSE1 is a 2nd semester course which can only '
980            'be added if school fees have been fully paid.'
981            in self.browser.contents)
982        # 2nd instalment has to be paid first
983        self.certificate.school_fee_3 = 678.0
984        self.browser.open(self.payments_path + '/addop')
985        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
986        self.browser.getControl("Create ticket").click()
987        self.student['payments'].values()[0].approve()
988        self.browser.open(self.studycourse_path + '/100/ctadd')
989        self.browser.getControl(name="form.course").value = ['COURSE1']
990        self.browser.getControl("Add course ticket").click()
991        self.assertTrue('Successfully added COURSE1' in self.browser.contents)
992        return
993
994    def test_student_GST_registration(self):
995        configuration_1 = createObject('waeup.SessionConfiguration')
996        configuration_1.academic_session = 2016
997        configuration_1.gst_registration_1_fee = 3333.0
998        configuration_1.gst_text_book_1_fee = 4444.0
999        configuration_1.gst_text_book_0_fee = 2222.0
1000        self.app['configuration'].addSessionConfiguration(configuration_1)
1001        course = createObject('waeup.Course')
1002        course.code = 'GST101'
1003        course.semester = 1
1004        course.credits = 10
1005        course.passmark = 40
1006        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1007            course)
1008        self.app['faculties']['fac1']['dep1'].certificates[
1009            'CERT1'].addCertCourse(course, level=100)
1010        IWorkflowState(self.student).setState('school fee paid')
1011        self.student['studycourse'].entry_session = 2016
1012        self.student['studycourse'].current_session = 2016
1013        self.course.semester = 2
1014        self.browser.open(self.login_path)
1015        self.browser.getControl(name="form.login").value = self.student_id
1016        self.browser.getControl(name="form.password").value = 'spwd'
1017        self.browser.getControl("Login").click()
1018        self.browser.getLink("Study Course").click()
1019        self.browser.getLink("Add course list").click()
1020        self.browser.getControl("Create course list now").click()
1021        self.assertFalse('GST101' in self.browser.contents)
1022        # GST101 tickets can't be added manually
1023        self.browser.getLink("Edit course list").click()
1024        self.browser.getLink("here").click()
1025        self.browser.getControl(name="form.course").value = ['GST101']
1026        self.browser.getControl("Add course ticket").click()
1027        self.assertTrue(
1028            'GST101 can only be added if both registration fee and text'
1029            in self.browser.contents)
1030        # GST fees have to be paid first
1031        self.browser.open(self.payments_path + '/addop')
1032        self.browser.getControl(name="form.p_category").value = ['gst_registration_1']
1033        self.browser.getControl("Create ticket").click()
1034        self.student['payments'].values()[0].approve()
1035        self.browser.open(self.studycourse_path + '/100/ctadd')
1036        self.browser.getControl(name="form.course").value = ['GST101']
1037        self.browser.getControl("Add course ticket").click()
1038        self.assertTrue(
1039            'GST101 can only be added if both registration fee and text'
1040            in self.browser.contents)
1041        self.browser.open(self.payments_path + '/addop')
1042        self.browser.getControl(name="form.p_category").value = ['gst_text_book_0']
1043        self.browser.getControl("Create ticket").click()
1044        self.student['payments'].values()[1].approve()
1045        self.browser.open(self.studycourse_path + '/100/ctadd')
1046        self.browser.getControl(name="form.course").value = ['GST101']
1047        self.browser.getControl("Add course ticket").click()
1048        self.assertTrue('Successfully added GST101' in self.browser.contents)
1049        return
1050
1051    def test_course_registration_forbidden(self):
1052        IWorkflowState(self.student).setState('school fee paid')
1053        self.student['studycourse'].entry_session = 2016
1054        self.student['studycourse'].current_session = 2016
1055        self.browser.open(self.login_path)
1056        self.browser.getControl(name="form.login").value = self.student_id
1057        self.browser.getControl(name="form.password").value = 'spwd'
1058        self.browser.getControl("Login").click()
1059        self.browser.getLink("Study Course").click()
1060        self.browser.getLink("Add course list").click()
1061        self.browser.getControl("Create course list now").click()
1062        self.browser.getLink("Edit course list").click()
1063        self.browser.getControl("Register course list").click()
1064        #self.assertTrue('Please pay faculty and departmental dues first'
1065        #    in self.browser.contents)
1066        #configuration_1 = createObject('waeup.SessionConfiguration')
1067        #configuration_1.academic_session = 2016
1068        #configuration_1.fac_dep_fee = 9999.0
1069        #self.app['configuration'].addSessionConfiguration(configuration_1)
1070        #self.browser.open(self.payments_path + '/addop')
1071        #self.browser.getControl(name="form.p_category").value = ['fac_dep']
1072        #self.browser.getControl("Create ticket").click()
1073        #self.student['payments'].values()[0].approveStudentPayment()
1074        #self.browser.open(self.studycourse_path + '/100/edit')
1075        #self.browser.getControl("Register course list").click()
1076
1077        ######################################################
1078        # Temporarily disabled ug_ft course registration
1079        #self.assertTrue('Course registration has been disabled'
1080        #    in self.browser.contents)
1081        #return
1082        ######################################################
1083
1084        self.assertTrue('Course list has been registered'
1085            in self.browser.contents)
1086        return
1087
1088    def test_course_registration_forbidden_2(self):
1089        IWorkflowState(self.student).setState('school fee paid')
1090        self.student['studycourse'].entry_session = 2004
1091        self.student['studycourse'].current_session = 2016
1092        self.browser.open(self.login_path)
1093        self.browser.getControl(name="form.login").value = self.student_id
1094        self.browser.getControl(name="form.password").value = 'spwd'
1095        self.browser.getControl("Login").click()
1096        self.browser.getLink("Study Course").click()
1097        self.browser.getLink("Add course list").click()
1098        self.browser.getControl("Create course list now").click()
1099        self.browser.getLink("Edit course list").click()
1100        self.browser.getControl("Register course list").click()
1101
1102        ######################################################
1103        # Temporarily disabled ug_ft course registration
1104        #self.assertTrue('Course registration has been disabled'
1105        #    in self.browser.contents)
1106        #return
1107        ######################################################
1108
1109        self.assertTrue('Please pay restitution fee first'
1110            in self.browser.contents)
1111        configuration = createObject('waeup.SessionConfiguration')
1112        configuration.academic_session = 2016
1113        self.app['configuration'].addSessionConfiguration(configuration)
1114        self.app['configuration']['2016'].restitution_fee = 9999.0
1115        self.browser.open(self.payments_path + '/addop')
1116        self.browser.getControl(name="form.p_category").value = ['restitution']
1117        self.browser.getControl("Create ticket").click()
1118        self.student['payments'].values()[0].approveStudentPayment()
1119        self.browser.open(self.studycourse_path + '/100/edit')
1120        self.browser.getControl("Register course list").click()
1121        self.assertTrue('Course list has been registered'
1122            in self.browser.contents)
1123        return
1124
1125    def test_student_access_course_results(self):
1126        IWorkflowState(self.student).setState('school fee paid')
1127        self.student['studycourse'].entry_session = 2004
1128        self.student['studycourse'].current_session = 2004
1129        self.browser.open(self.login_path)
1130        self.browser.getControl(name="form.login").value = self.student_id
1131        self.browser.getControl(name="form.password").value = 'spwd'
1132        self.browser.getControl("Login").click()
1133        self.browser.getLink("Study Course").click()
1134        self.browser.getLink("Add course list").click()
1135        self.browser.getControl("Create course list now").click()
1136        self.assertFalse('Score' in self.browser.contents)
1137        IWorkflowState(self.student).setState('returning')
1138        self.browser.open(self.studycourse_path + '/100')
1139        self.assertFalse('Score' in self.browser.contents)
1140        self.app['configuration']['2004'].show_results = ['ug_ft']
1141        self.browser.open(self.studycourse_path + '/100')
1142        self.assertTrue('Score' in self.browser.contents)
1143        return
1144
1145    def test_repair_course_list(self):
1146        IWorkflowState(self.student).setState('school fee paid')
1147        self.student['studycourse'].entry_session = 2016
1148        self.student['studycourse'].current_session = 2016
1149        self.browser.open(self.login_path)
1150        self.browser.getControl(name="form.login").value = self.student_id
1151        self.browser.getControl(name="form.password").value = 'spwd'
1152        self.browser.getControl("Login").click()
1153        self.browser.getLink("Study Course").click()
1154        self.browser.getLink("Add course list").click()
1155        self.browser.getControl("Create course list now").click()
1156        self.assertTrue('Edit course list' in self.browser.contents)
1157        self.assertFalse('Repair course list' in self.browser.contents)
1158        self.student['studycourse'].current_level = 200
1159        self.browser.open(self.studycourse_path + '/100')
1160        self.assertFalse('Edit course list' in self.browser.contents)
1161        self.assertFalse('Repair course list' in self.browser.contents)
1162        configuration = createObject('waeup.SessionConfiguration')
1163        configuration.academic_session = 2016
1164        self.app['configuration'].addSessionConfiguration(configuration)
1165        self.app['configuration']['2016'].studylevel_repair_enabled = True
1166        self.browser.open(self.studycourse_path + '/100')
1167        self.assertFalse('Edit course list' in self.browser.contents)
1168        self.assertTrue('Repair course list' in self.browser.contents)
1169        self.browser.getLink("Repair").click()
1170        self.assertEqual(self.browser.url, self.studycourse_path + '/100/repair')
1171        self.assertTrue('Repair course list of 100' in self.browser.contents)
1172        return
1173
1174    def test_student_clearance(self):
1175        # Student cant login if their password is not set
1176        IWorkflowInfo(self.student).fireTransition('admit')
1177        self.browser.open(self.login_path)
1178        self.browser.getControl(name="form.login").value = self.student_id
1179        self.browser.getControl(name="form.password").value = 'spwd'
1180        self.browser.getControl("Login").click()
1181        self.assertMatches(
1182            '...You logged in...', self.browser.contents)
1183        # Admitted student can upload a passport picture
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        # Students can book accommodation without AC ...
1222        self.browser.open(self.acco_path)
1223        IWorkflowInfo(self.student).fireTransition('admit')
1224        self.browser.getControl("Book accommodation").click()
1225        self.assertTrue(
1226            'You are not eligible to book accommodation.'
1227            in self.browser.contents)
1228        self.student['studycourse'].certificate.study_mode = 'ug_ft'
1229        self.app['hostels'].accommodation_states = [PAID]
1230        self.browser.getControl("Book accommodation").click()
1231        self.assertTrue(
1232            'You are in the wrong registration state.'
1233            in self.browser.contents)
1234        IWorkflowState(self.student).setState(PAID)
1235        self.browser.getControl("Book accommodation").click()
1236        self.assertFalse('Activation Code:' in self.browser.contents)
1237        self.browser.getControl("Create bed ticket").click()
1238        # Bed is randomly selected but, since there is only
1239        # one bed for this student, we know that
1240        self.assertEqual(self.student['accommodation']['2004'].bed_coordinates,
1241            'Hall 1, Block A, Room 101, Bed A (regular_male_all)')
1242        # Only the hall name is displayed
1243        self.assertEqual(self.student[
1244            'accommodation']['2004'].display_coordinates,
1245            'Hall 1')
1246        self.assertFalse('Hall 1, Block A, Room 101, Bed A'
1247            in self.browser.contents)
1248        self.assertTrue('<td>Hall 1</td>'
1249            in self.browser.contents)
1250        return
1251
1252    def test_handle_courses_by_lecturer(self):
1253        self.app['users'].addUser('mrslecturer', 'mrslecturerSecret1')
1254        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1255        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1256        # Add course ticket
1257        studylevel = createObject(u'waeup.StudentStudyLevel')
1258        studylevel.level = 100
1259        studylevel.level_session = 2004
1260        self.student['studycourse'].addStudentStudyLevel(
1261            self.certificate, studylevel)
1262        # Assign local Lecturer role for a certificate.
1263        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1264        prmlocal = IPrincipalRoleManager(course)
1265        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1266        notify(LocalRoleSetEvent(
1267            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1268        # Login as lecturer.
1269        self.browser.open(self.login_path)
1270        self.browser.getControl(name="form.login").value = 'mrslecturer'
1271        self.browser.getControl(name="form.password").value = 'mrslecturerSecret1'
1272        self.browser.getControl("Login").click()
1273        self.browser.getLink("My Courses").click()
1274        self.browser.getLink("COURSE1").click()
1275        # Course results can be batch edited via the edit_courses view.
1276        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
1277        self.app['configuration'].current_academic_session = 2004
1278        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
1279        IWorkflowState(self.student).setState('courses validated')
1280        self.browser.open(
1281            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1282        self.assertTrue(
1283            'input type="text" name="scores:list"'
1284            in self.browser.contents)
1285        self.browser.getControl(name="scores:list", index=0).value = '55'
1286        self.browser.getControl(name="cas:list", index=0).value = '22'
1287        self.browser.getControl("Update scores from").click()
1288        # New score and ca has been set.
1289        self.assertEqual(
1290            self.student['studycourse']['100']['COURSE1'].score, 55)
1291        self.assertEqual(
1292            self.student['studycourse']['100']['COURSE1'].ca, 22)
1293        # Score editing has been logged.
1294        logfile = os.path.join(
1295            self.app['datacenter'].storage, 'logs', 'students.log')
1296        logcontent = open(logfile).read()
1297        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1298                        'E1000000 100/COURSE1 score updated (55)' in logcontent)
1299        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1300                        'E1000000 100/COURSE1 ca updated (22)' in logcontent)
1301        # Non-integer scores won't be accepted.
1302        self.browser.open(
1303            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1304        self.assertTrue('value="55" />' in self.browser.contents)
1305        self.browser.getControl(name="scores:list", index=0).value = 'abc'
1306        self.browser.getControl("Update scores").click()
1307        self.assertTrue('Error: Score(s), CA(s) and Imported TS(s) of TESTER, Anna have not be updated.'
1308            in self.browser.contents)
1309        # Scores can be removed.
1310        self.browser.open(
1311            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1312        self.browser.getControl(name="scores:list", index=0).value = ''
1313        self.browser.getControl("Update scores").click()
1314        self.assertEqual(
1315            self.student['studycourse']['100']['COURSE1'].score, None)
1316        logcontent = open(logfile).read()
1317        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1318                        'E1000000 100/COURSE1 score updated (None)' in logcontent)
1319
1320
1321    def disabled_test_student_view_transcript(self):
1322        # Student cant login if their password is not set
1323        IWorkflowInfo(self.student).fireTransition('admit')
1324        self.browser.open(self.login_path)
1325        self.browser.getControl(name="form.login").value = self.student_id
1326        self.browser.getControl(name="form.password").value = 'spwd'
1327        self.browser.getControl("Login").click()
1328        self.assertMatches(
1329            '...You logged in...', self.browser.contents)
1330        # Students can view the transcript
1331        self.browser.open(self.studycourse_path)
1332        self.browser.getLink("Transcript").click()
1333        self.browser.getLink("Academic Transcript").click()
1334        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1335        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1336
1337    def test_alumni_request_pw(self):
1338        # Add an applicants container
1339        applicantscontainer = ApplicantsContainer()
1340        applicantscontainer.code = u'trans2017'
1341        applicantscontainer.prefix = 'trans'
1342        applicantscontainer.year = 2017
1343        applicantscontainer.title = u'This is the trans2017 container'
1344        applicantscontainer.application_category = 'no'
1345        applicantscontainer.mode = 'create'
1346        applicantscontainer.strict_deadline = True
1347        delta = timedelta(days=10)
1348        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1349        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1350        self.app['applicants']['trans2017'] = applicantscontainer
1351        self.applicantscontainer = self.app['applicants']['trans2017']
1352        # Student with wrong number can't be found.
1353        # Applicant is redirected to application section.
1354        self.browser.open('http://localhost/app/alumni_requestpw')
1355        self.browser.getControl(name="form.lastname").value = 'Tester'
1356        self.browser.getControl(name="form.number").value = 'anynumber'
1357        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1358        self.browser.getControl("Send login credentials").click()
1359        self.assertTrue('No student record found.'
1360            in self.browser.contents)
1361        self.assertEqual(self.browser.url,
1362            'http://localhost/app/applicants/trans2017/register')
1363
1364    def test_student_course_registration_outstanding(self):
1365        self.course = createObject('waeup.Course')
1366        self.course.code = 'COURSE2'
1367        self.course.semester = 1
1368        self.course.credits = 39
1369        self.course.passmark = 40
1370        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1371            self.course)
1372        IWorkflowState(self.student).setState('school fee paid')
1373        self.browser.open(self.login_path)
1374        self.browser.getControl(name="form.login").value = self.student_id
1375        self.browser.getControl(name="form.password").value = 'spwd'
1376        self.browser.getControl("Login").click()
1377        self.browser.open(self.student_path + '/studycourse/add')
1378        self.browser.getControl("Create course list now").click()
1379        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1380        self.student['studycourse'].current_level = 200
1381        self.browser.getLink("Study Course").click()
1382        self.browser.getLink("Add course list").click()
1383        self.assertMatches('...Add current level 200 (Year 2)...',
1384                           self.browser.contents)
1385        self.browser.getControl("Create course list now").click()
1386        self.browser.getLink("200").click()
1387        self.browser.getLink("Edit course list").click()
1388        self.browser.getLink("here").click()
1389        self.browser.getControl(name="form.course").value = ['COURSE2']
1390        self.browser.getControl("Add course ticket").click()
1391        # Carryover COURSE1 in level 200 already has 10 credits
1392        self.assertMatches(
1393            '...Maximum credits exceeded...', self.browser.contents)
1394        # If COURSE1 is outstanding, its credits won't be considered
1395        self.student['studycourse']['200']['COURSE1'].outstanding = True
1396        self.browser.getControl("Add course ticket").click()
1397        # Corresponding certificate course is missing
1398        self.assertTrue(
1399            'COURSE2 is not part of the CERT1 curriculum.'
1400            in self.browser.contents)
1401        self.app['faculties']['fac1']['dep1'].certificates[
1402            'CERT1'].addCertCourse(self.course, level=100)
1403        self.browser.getControl("Add course ticket").click()
1404        self.assertTrue(
1405            'Successfully added COURSE2' in self.browser.contents)
1406        return
1407
1408    def test_examination_schedule_slip(self):
1409        self.student.flash_notice = u'My Examination Date'
1410        self.browser.open(self.login_path)
1411        self.browser.getControl(name="form.login").value = self.student_id
1412        self.browser.getControl(name="form.password").value = 'spwd'
1413        self.browser.getControl("Login").click()
1414        self.browser.open(self.student_path)
1415        self.browser.getLink("Download examination schedule slip").click()
1416        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1417        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1418        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
1419        open(path, 'wb').write(self.browser.contents)
1420        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
1421        # If flash_notice does not contain exam' the button does not show up.
1422        self.student.flash_notice = u'anything'
1423        self.browser.open(self.student_path)
1424        self.assertFalse('examination schedule slip' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.