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

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

Add only core courses.

  • Property svn:keywords set to Id
File size: 74.1 KB
Line 
1## $Id: test_browser.py 16900 2022-03-22 17:35:16Z 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
557    def test_payment_disabled(self):
558        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
559        self.browser.open(self.payments_path)
560        IWorkflowState(self.student).setState('cleared')
561        self.browser.getLink("Add current session payment ticket").click()
562        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
563        self.browser.getControl("Create ticket").click()
564        self.assertMatches('...ticket created...', self.browser.contents)
565        self.app['configuration']['2004'].payment_disabled = ['sf_all']
566        self.browser.open(self.payments_path)
567        self.browser.getLink("Add current session payment ticket").click()
568        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
569        self.browser.getControl("Create ticket").click()
570        self.assertMatches('...This category of payments has been disabled...',
571                           self.browser.contents)
572
573        self.app['configuration']['2004'].payment_disabled = ['sf_ug_pt']
574        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
575        self.browser.getControl("Create ticket").click()
576        self.assertMatches('...ticket created...', self.browser.contents)
577        self.certificate.study_mode = 'ug_pt'
578        self.browser.open(self.payments_path)
579        self.browser.getLink("Add current session payment ticket").click()
580        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
581        self.browser.getControl("Create ticket").click()
582        self.assertMatches('...This category of payments has been disabled...',
583                           self.browser.contents)
584
585        self.app['configuration']['2004'].payment_disabled = ['sf_pg']
586        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
587        self.browser.getControl("Create ticket").click()
588        self.assertMatches('...ticket created...', self.browser.contents)
589        self.certificate.study_mode = 'special_pg_ft'
590        self.browser.open(self.payments_path)
591        self.browser.getLink("Add current session payment ticket").click()
592        self.browser.getControl(name="form.p_category").value = ['schoolfee']
593        self.browser.getControl("Create ticket").click()
594        self.assertMatches('...This category of payments has been disabled...',
595                           self.browser.contents)
596        return
597
598class StudentUITests(StudentsFullSetup):
599    """Tests for customized student class views and pages
600    """
601
602    layer = FunctionalLayer
603
604    def setUp(self):
605        super(StudentUITests, self).setUp()
606        self.app['faculties']['fac1']['dep1'].certificates[
607            'CERT1']['COURSE1_100'].course_category = 'C'
608        bedticket = BedTicket()
609        bedticket.booking_session = 2004
610        bedticket.bed_type = u'any bed type'
611        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
612        bedticket.bed_coordinates = u'My bed coordinates'
613        self.student['accommodation'].addBedTicket(bedticket)
614
615    def test_maintenance_fee_payment(self):
616        self.certificate.study_mode = 'ug_ft'
617        self.student['studycourse'].entry_session = 2013
618        self.student.nationality = u'NG'
619        IWorkflowState(self.student).setState('cleared')
620        self.browser.open(self.login_path)
621        self.browser.getControl(name="form.login").value = self.student_id
622        self.browser.getControl(name="form.password").value = 'spwd'
623        self.browser.getControl("Login").click()
624        self.browser.open(self.student_path + '/payments')
625        self.browser.getLink("Add current session payment ticket").click()
626        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
627        self.browser.getControl("Create ticket").click()
628        self.assertTrue('ticket created' in self.browser.contents)
629        value = self.student['payments'].keys()[0]
630        self.browser.getLink(value).click()
631        self.assertTrue('<span>My bed coordinates</span>' in self.browser.contents)
632        self.assertEqual(self.student['payments'][value].amount_auth, 876.0)
633        return
634
635    def test_student_schoolfee_payments(self):
636        configuration_1 = createObject('waeup.SessionConfiguration')
637        configuration_1.academic_session = 2018
638        self.app['configuration'].addSessionConfiguration(configuration_1)
639        self.certificate.study_mode = 'ug_ft'
640        self.student['studycourse'].entry_session = 2018
641        self.student['studycourse'].current_session = 2018
642        self.student['studycourse'].entry_mode = 'ug_ft'
643        self.student['studycourse'].certificate.school_fee_1 = 50200.0
644        self.app['configuration']['2018'].union_fee = 1200.0
645        self.app['configuration']['2018'].welfare_fee = 700.0
646        #self.app['configuration']['2017'].id_card_fee = 300.0
647        self.app['configuration']['2018'].sports_fee = 300.0
648        self.app['configuration']['2018'].library_fee = 300.0
649        self.student.nationality = u'NG'
650        # Login
651        IWorkflowState(self.student).setState('cleared')
652        self.browser.open(self.login_path)
653        self.browser.getControl(name="form.login").value = self.student_id
654        self.browser.getControl(name="form.password").value = 'spwd'
655        self.browser.getControl("Login").click()
656        # Test school fee payments
657        self.browser.open(self.student_path + '/payments')
658        self.browser.getLink("Add current session payment ticket").click()
659        self.browser.getControl(name="form.p_category").value = ['schoolfee_incl']
660        self.browser.getControl("Create ticket").click()
661        self.assertTrue('ticket created' in self.browser.contents)
662        value = self.student['payments'].keys()[0]
663        self.student['payments'][value].p_state = 'paid'
664        self.browser.getLink(value).click()
665        self.assertTrue('Amount Authorized' in self.browser.contents)
666        # 50200 + 1200 + 700 + 300 + 300- 800 = 51900
667        self.assertEqual(self.student['payments'][value].amount_auth, 51900.0)
668        self.student['payments'][value].r_company = u'interswitch'
669        self.browser.getLink("Download").click()
670        self.assertEqual(self.browser.headers['Status'], '200 Ok')
671        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
672        path = os.path.join(samples_dir(), 'payment_slip.pdf')
673        open(path, 'wb').write(self.browser.contents)
674        print "Sample PDF payment_slip.pdf written to %s" % path
675        # Another school fee payment cannot be added
676        self.student['payments'][value].approve()
677        self.browser.open(self.student_path + '/payments')
678        self.browser.getLink("Add current session payment ticket").click()
679        self.browser.getControl(name="form.p_category").value = ['schoolfee']
680        self.browser.getControl("Create ticket").click()
681        self.assertTrue(
682            'You must choose a payment which includes additional fees'
683            in self.browser.contents)
684        return
685
686    def test_late_registration(self):
687        delta = timedelta(days=10)
688        self.app['configuration'][
689            '2004'].coursereg_deadline = datetime.now(pytz.utc) - delta
690        IWorkflowState(self.student).setState('school fee paid')
691        # Current session is 2004. Here we test course registration for
692        # returning students.
693        self.student['studycourse'].entry_session = 2003
694        self.student['studycourse'].current_session = 2016
695        # Login
696        self.browser.open(self.login_path)
697        self.browser.getControl(name="form.login").value = self.student_id
698        self.browser.getControl(name="form.password").value = 'spwd'
699        self.browser.getControl("Login").click()
700        # Make restitution fee payment
701        # Ivie: The restitution was only for returning students of 2016/2017.
702        # Hence, it is only valid for 2016 payment session returning students.
703        configuration = createObject('waeup.SessionConfiguration')
704        configuration.academic_session = 2016
705        self.app['configuration'].addSessionConfiguration(configuration)
706        self.app['configuration']['2016'].restitution_fee = 9999.0
707        self.browser.open(self.payments_path + '/addop')
708        self.browser.getControl(name="form.p_category").value = ['restitution']
709        self.browser.getControl("Create ticket").click()
710        self.student['payments'].values()[0].approveStudentPayment()
711        # Make late registration fee payment
712        self.student['studycourse'].current_session = 2004
713        self.browser.open(self.payments_path + '/addop')
714        self.browser.getControl(name="form.p_category").value = ['late_registration']
715        self.browser.getControl("Create ticket").click()
716        self.assertMatches('...ticket created...',
717                           self.browser.contents)
718        self.browser.getLink("Study Course").click()
719        self.browser.getLink("Add course list").click()
720        self.assertMatches('...Add current level 100 (Year 1)...',
721                           self.browser.contents)
722        self.browser.getControl("Create course list now").click()
723        self.student['studycourse']['100']['COURSE1'].score = 67
724        self.browser.getLink("100").click()
725        # Course results can't be seen
726        self.assertFalse('<td>67</td>' in self.browser.contents)
727        self.browser.getLink("Edit course list").click()
728        self.assertFalse('<td>67</td>' in self.browser.contents)
729        self.app['configuration']['2004'].late_registration_fee = 0.0
730        self.browser.getControl("Register course list").click()
731        self.assertTrue('Course registration has been disabled.' in self.browser.contents)
732        self.app['configuration']['2004'].late_registration_fee = 345.0
733        self.browser.getControl("Register course list").click()
734        self.assertTrue('Course registration has ended. Please pay' in self.browser.contents)
735        self.student['payments'].values()[1].approve()
736        self.browser.getControl("Register course list").click()
737        self.assertTrue('Course list has been registered' in self.browser.contents)
738        self.assertEqual(self.student.state, 'courses registered')
739        # Reset student and check if fresh students are always allowed to
740        # register courses.
741        #self.student['studycourse'].entry_session = 2004
742        #del self.student['payments'][self.student['payments'].keys()[1]]
743        #IWorkflowState(self.student).setState('school fee paid')
744        #self.browser.open(self.studycourse_path + '/100/edit')
745        #self.browser.getControl("Register course list").click()
746        #self.assertTrue('Course list has been registered' in self.browser.contents)
747        return
748
749
750    def deactivated_test_student_course_registration(self):
751        # Add more courses
752        self.course2 = createObject('waeup.Course')
753        self.course2.code = 'COURSE2'
754        self.course2.semester = 2
755        self.course2.credits = 10
756        self.course2.passmark = 40
757        self.app['faculties']['fac1']['dep1'].courses.addCourse(
758            self.course2)
759        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
760            self.course2, level=100)
761        self.course3 = createObject('waeup.Course')
762        self.course3.code = 'COURSE3'
763        self.course3.semester = 3
764        self.course3.credits = 10
765        self.course3.passmark = 40
766        self.app['faculties']['fac1']['dep1'].courses.addCourse(
767            self.course3)
768        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
769            self.course3, level=100)
770
771        # Login as student
772        self.browser.open(self.login_path)
773        IWorkflowState(self.student).setState('school fee paid')
774        self.browser.open(self.login_path)
775        self.browser.getControl(name="form.login").value = self.student_id
776        self.browser.getControl(name="form.password").value = 'spwd'
777        self.browser.getControl("Login").click()
778        # Students can add the current study level
779        self.browser.getLink("Study Course").click()
780        self.browser.getLink("Add course list").click()
781        self.assertMatches('...Add current level 100 (Year 1)...',
782                           self.browser.contents)
783        self.browser.getControl("Create course list now").click()
784        # Student has not paid second instalment, therefore a level
785        # with two course ticket was created (semester 1 and combined)
786        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
787        self.browser.getLink("100").click()
788        self.browser.getLink("Edit course list").click()
789        self.browser.getControl("Add course ticket").click()
790        # Student can't add second semester course
791        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
792        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
793        self.assertFalse('<option value="COURSE2">' in self.browser.contents)
794
795        # Let's remove level and see what happens after 2nd instalment payment
796        del(self.student['studycourse']['100'])
797        payment2 = createObject('waeup.StudentOnlinePayment')
798        payment2.p_category = u'schoolfee_2'
799        payment2.p_session = self.student.current_session
800        self.student['payments']['anykey'] = payment2
801        self.browser.open(self.studycourse_path)
802        self.browser.getLink("Add course list").click()
803        self.browser.getControl("Create course list now").click()
804        # Still only 2 tickets have been created since payment ticket
805        # was not paid
806        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
807        payment2.p_state = u'paid'
808        del(self.student['studycourse']['100'])
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        # Now 2nd semester course has been added
813        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 3)
814        # Student can add second semester course
815        self.browser.getLink("100").click()
816        self.browser.getLink("Edit course list").click()
817        self.browser.getControl("Add course ticket").click()
818        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
819        self.assertTrue('<option value="COURSE2">' in self.browser.contents)
820        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
821        return
822
823    def test_set_matric_number(self):
824        #payment = createObject('waeup.StudentOnlinePayment')
825        #payment.p_category = u'concessional'
826        #payment.p_id = u'anyid'
827        #payment.p_state = u'paid'
828        #self.student['payments']['anykey'] = payment
829        # Login as student
830        self.browser.open(self.login_path)
831        IWorkflowState(self.student).setState('school fee paid')
832        self.browser.open(self.login_path)
833        self.browser.getControl(name="form.login").value = self.student_id
834        self.browser.getControl(name="form.password").value = 'spwd'
835        self.browser.getControl("Login").click()
836        self.assertRaises(
837            LinkNotFoundError,
838            self.browser.getLink, 'Get Matriculation Number')
839        self.student.matric_number = None
840        site = grok.getSite()
841        site['configuration'].next_matric_integer = 1
842        self.student['studycourse'].certificate.study_mode = 'ug_pt'
843        self.browser.open(self.student_path)
844        self.assertRaises(
845            LinkNotFoundError,
846            self.browser.getLink, 'Download matriculation number slip')
847        self.browser.getLink("Get Matriculation Number").click()
848        self.assertTrue('Matriculation number PTP/fac1/dep1/04/00001 assigned.'
849            in self.browser.contents)
850        self.assertEqual(self.student.matric_number, 'PTP/fac1/dep1/04/00001')
851        self.assertRaises(
852            LinkNotFoundError,
853            self.browser.getLink, 'Get Matriculation Number')
854        # Setting matric number is logged.
855        logfile = os.path.join(
856            self.app['datacenter'].storage, 'logs', 'students.log')
857        logcontent = open(logfile).read()
858        self.assertTrue('E1000000 - waeup.aaue.students.browser.StudentGetMatricNumberPage - '
859                        'E1000000 - PTP/fac1/dep1/04/00001 assigned' in logcontent)
860        # Matric Number Slip can be downloaded
861        self.browser.getLink("Download matriculation number slip").click()
862        self.assertEqual(self.browser.headers['Status'], '200 Ok')
863        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
864        path = os.path.join(samples_dir(), 'matric_number_slip.pdf')
865        open(path, 'wb').write(self.browser.contents)
866        print "Sample PDF matric_number_slip.pdf written to %s" % path
867        return
868
869    def test_personal_data_slip(self):
870        # Login as student
871        self.browser.open(self.login_path)
872        IWorkflowState(self.student).setState('school fee paid')
873        self.browser.open(self.login_path)
874        self.browser.getControl(name="form.login").value = self.student_id
875        self.browser.getControl(name="form.password").value = 'spwd'
876        self.browser.getControl("Login").click()
877        self.browser.getLink("Personal Data").click()
878        self.assertRaises(
879            LinkNotFoundError,
880            self.browser.getLink, 'Download personal data slip')
881        self.student.father_name = u'Rudolf'
882        self.browser.open(self.personal_path)
883        self.browser.getLink("Download personal data slip").click()
884        self.assertEqual(self.browser.headers['Status'], '200 Ok')
885        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
886        path = os.path.join(samples_dir(), 'personal_data_slip.pdf')
887        open(path, 'wb').write(self.browser.contents)
888        print "Sample PDF personal_data_slip.pdf written to %s" % path
889        return
890
891    def test_student_course_registration(self):
892        IWorkflowState(self.student).setState('school fee paid')
893        self.browser.open(self.login_path)
894        self.browser.getControl(name="form.login").value = self.student_id
895        self.browser.getControl(name="form.password").value = 'spwd'
896        self.browser.getControl("Login").click()
897        # Now students can add the current study level
898        self.browser.getLink("Study Course").click()
899        self.browser.getLink("Add course list").click()
900        self.assertMatches('...Add current level 100 (Year 1)...',
901                           self.browser.contents)
902        self.browser.getControl("Create course list now").click()
903        # Students can't open the customized pdf course registration slip
904        self.browser.open(
905            self.student_path + '/studycourse/100/course_registration_slip.pdf')
906        self.assertTrue('Forbidden' in self.browser.contents)
907        # They can open slips from the previous session ...
908        self.student['studycourse'].current_level = 200
909        self.browser.open(self.student_path + '/studycourse/100')
910        self.browser.getLink("Download course registration slip").click()
911        self.assertEqual(self.browser.headers['Status'], '200 Ok')
912        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
913        # or if they have registered their course list
914        self.student['studycourse'].current_level = 200
915        IWorkflowState(self.student).setState('courses registered')
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        path = os.path.join(samples_dir(), 'ft_course_registration_slip.pdf')
921        open(path, 'wb').write(self.browser.contents)
922        print "Sample PDF ft_course_registration_slip.pdf written to %s" % path
923
924        self.certificate.study_mode = 'ug_pt'
925        self.browser.open(self.student_path + '/studycourse/100')
926        self.browser.getLink("Download course registration slip").click()
927        self.assertEqual(self.browser.headers['Status'], '200 Ok')
928        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
929        path = os.path.join(samples_dir(), 'pt_course_registration_slip.pdf')
930        open(path, 'wb').write(self.browser.contents)
931        print "Sample PDF pt_course_registration_slip.pdf written to %s" % path
932
933        self.certificate.study_mode = 'special_pg_ft'
934        self.student.matric_number = u'AAU/SPS/FLW/LAW/15/PHD/09504'
935        self.browser.open(self.student_path + '/studycourse/100')
936        self.browser.getLink("Download course registration slip").click()
937        self.assertEqual(self.browser.headers['Status'], '200 Ok')
938        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
939        path = os.path.join(samples_dir(), 'pg_course_registration_slip.pdf')
940        open(path, 'wb').write(self.browser.contents)
941        print "Sample PDF pg_course_registration_slip.pdf written to %s" % path
942
943        # Students cant' view scores, cas and grades.
944        self.browser.open(self.student_path + '/studycourse/100')
945        self.assertFalse('Score' in self.browser.contents)
946        self.assertFalse('CA' in self.browser.contents)
947        self.assertFalse('Grade' in self.browser.contents)
948        self.browser.getLink("COURSE1").click()
949        self.browser.open(self.student_path + '/studycourse/100')
950        self.assertFalse('Score' in self.browser.contents)
951        self.assertFalse('CA' in self.browser.contents)
952        self.assertFalse('Grade' in self.browser.contents)
953
954    def test_student_2nd_semester_course_registration(self):
955        IWorkflowState(self.student).setState('school fee paid')
956        self.student['studycourse'].entry_session = 2015
957        self.course.semester = 2
958        self.browser.open(self.login_path)
959        self.browser.getControl(name="form.login").value = self.student_id
960        self.browser.getControl(name="form.password").value = 'spwd'
961        self.browser.getControl("Login").click()
962        self.browser.getLink("Study Course").click()
963        self.browser.getLink("Add course list").click()
964        self.browser.getControl("Create course list now").click()
965        self.assertFalse('COURSE1' in self.browser.contents)
966        # 2nd semester tickets can't be added manually
967        self.browser.getLink("Edit course list").click()
968        self.browser.getLink("here").click()
969        self.browser.getControl(name="form.course").value = ['COURSE1']
970        self.browser.getControl("Add course ticket").click()
971        self.assertTrue(
972            'COURSE1 is a 2nd semester course which can only '
973            'be added if school fees have been fully paid.'
974            in self.browser.contents)
975        # 2nd instalment has to be paid first
976        self.certificate.school_fee_3 = 678.0
977        self.browser.open(self.payments_path + '/addop')
978        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
979        self.browser.getControl("Create ticket").click()
980        self.student['payments'].values()[0].approve()
981        self.browser.open(self.studycourse_path + '/100/ctadd')
982        self.browser.getControl(name="form.course").value = ['COURSE1']
983        self.browser.getControl("Add course ticket").click()
984        self.assertTrue('Successfully added COURSE1' in self.browser.contents)
985        return
986
987    def test_student_GST_registration(self):
988        configuration_1 = createObject('waeup.SessionConfiguration')
989        configuration_1.academic_session = 2016
990        configuration_1.gst_registration_1_fee = 3333.0
991        configuration_1.gst_text_book_1_fee = 4444.0
992        configuration_1.gst_text_book_0_fee = 2222.0
993        self.app['configuration'].addSessionConfiguration(configuration_1)
994        course = createObject('waeup.Course')
995        course.code = 'GST101'
996        course.semester = 1
997        course.credits = 10
998        course.passmark = 40
999        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1000            course)
1001        self.app['faculties']['fac1']['dep1'].certificates[
1002            'CERT1'].addCertCourse(course, level=100)
1003        IWorkflowState(self.student).setState('school fee paid')
1004        self.student['studycourse'].entry_session = 2016
1005        self.student['studycourse'].current_session = 2016
1006        self.course.semester = 2
1007        self.browser.open(self.login_path)
1008        self.browser.getControl(name="form.login").value = self.student_id
1009        self.browser.getControl(name="form.password").value = 'spwd'
1010        self.browser.getControl("Login").click()
1011        self.browser.getLink("Study Course").click()
1012        self.browser.getLink("Add course list").click()
1013        self.browser.getControl("Create course list now").click()
1014        self.assertFalse('GST101' in self.browser.contents)
1015        # GST101 tickets can't be added manually
1016        self.browser.getLink("Edit course list").click()
1017        self.browser.getLink("here").click()
1018        self.browser.getControl(name="form.course").value = ['GST101']
1019        self.browser.getControl("Add course ticket").click()
1020        self.assertTrue(
1021            'GST101 can only be added if both registration fee and text'
1022            in self.browser.contents)
1023        # GST fees have to be paid first
1024        self.browser.open(self.payments_path + '/addop')
1025        self.browser.getControl(name="form.p_category").value = ['gst_registration_1']
1026        self.browser.getControl("Create ticket").click()
1027        self.student['payments'].values()[0].approve()
1028        self.browser.open(self.studycourse_path + '/100/ctadd')
1029        self.browser.getControl(name="form.course").value = ['GST101']
1030        self.browser.getControl("Add course ticket").click()
1031        self.assertTrue(
1032            'GST101 can only be added if both registration fee and text'
1033            in self.browser.contents)
1034        self.browser.open(self.payments_path + '/addop')
1035        self.browser.getControl(name="form.p_category").value = ['gst_text_book_0']
1036        self.browser.getControl("Create ticket").click()
1037        self.student['payments'].values()[1].approve()
1038        self.browser.open(self.studycourse_path + '/100/ctadd')
1039        self.browser.getControl(name="form.course").value = ['GST101']
1040        self.browser.getControl("Add course ticket").click()
1041        self.assertTrue('Successfully added GST101' in self.browser.contents)
1042        return
1043
1044    def test_course_registration_forbidden(self):
1045        IWorkflowState(self.student).setState('school fee paid')
1046        self.student['studycourse'].entry_session = 2016
1047        self.student['studycourse'].current_session = 2016
1048        self.browser.open(self.login_path)
1049        self.browser.getControl(name="form.login").value = self.student_id
1050        self.browser.getControl(name="form.password").value = 'spwd'
1051        self.browser.getControl("Login").click()
1052        self.browser.getLink("Study Course").click()
1053        self.browser.getLink("Add course list").click()
1054        self.browser.getControl("Create course list now").click()
1055        self.browser.getLink("Edit course list").click()
1056        self.browser.getControl("Register course list").click()
1057        #self.assertTrue('Please pay faculty and departmental dues first'
1058        #    in self.browser.contents)
1059        #configuration_1 = createObject('waeup.SessionConfiguration')
1060        #configuration_1.academic_session = 2016
1061        #configuration_1.fac_dep_fee = 9999.0
1062        #self.app['configuration'].addSessionConfiguration(configuration_1)
1063        #self.browser.open(self.payments_path + '/addop')
1064        #self.browser.getControl(name="form.p_category").value = ['fac_dep']
1065        #self.browser.getControl("Create ticket").click()
1066        #self.student['payments'].values()[0].approveStudentPayment()
1067        #self.browser.open(self.studycourse_path + '/100/edit')
1068        #self.browser.getControl("Register course list").click()
1069
1070        ######################################################
1071        # Temporarily disabled ug_ft course registration
1072        #self.assertTrue('Course registration has been disabled'
1073        #    in self.browser.contents)
1074        #return
1075        ######################################################
1076
1077        self.assertTrue('Course list has been registered'
1078            in self.browser.contents)
1079        return
1080
1081    def test_course_registration_forbidden_2(self):
1082        IWorkflowState(self.student).setState('school fee paid')
1083        self.student['studycourse'].entry_session = 2004
1084        self.student['studycourse'].current_session = 2016
1085        self.browser.open(self.login_path)
1086        self.browser.getControl(name="form.login").value = self.student_id
1087        self.browser.getControl(name="form.password").value = 'spwd'
1088        self.browser.getControl("Login").click()
1089        self.browser.getLink("Study Course").click()
1090        self.browser.getLink("Add course list").click()
1091        self.browser.getControl("Create course list now").click()
1092        self.browser.getLink("Edit course list").click()
1093        self.browser.getControl("Register course list").click()
1094
1095        ######################################################
1096        # Temporarily disabled ug_ft course registration
1097        #self.assertTrue('Course registration has been disabled'
1098        #    in self.browser.contents)
1099        #return
1100        ######################################################
1101
1102        self.assertTrue('Please pay restitution fee first'
1103            in self.browser.contents)
1104        configuration = createObject('waeup.SessionConfiguration')
1105        configuration.academic_session = 2016
1106        self.app['configuration'].addSessionConfiguration(configuration)
1107        self.app['configuration']['2016'].restitution_fee = 9999.0
1108        self.browser.open(self.payments_path + '/addop')
1109        self.browser.getControl(name="form.p_category").value = ['restitution']
1110        self.browser.getControl("Create ticket").click()
1111        self.student['payments'].values()[0].approveStudentPayment()
1112        self.browser.open(self.studycourse_path + '/100/edit')
1113        self.browser.getControl("Register course list").click()
1114        self.assertTrue('Course list has been registered'
1115            in self.browser.contents)
1116        return
1117
1118    def test_student_access_course_results(self):
1119        IWorkflowState(self.student).setState('school fee paid')
1120        self.student['studycourse'].entry_session = 2004
1121        self.student['studycourse'].current_session = 2004
1122        self.browser.open(self.login_path)
1123        self.browser.getControl(name="form.login").value = self.student_id
1124        self.browser.getControl(name="form.password").value = 'spwd'
1125        self.browser.getControl("Login").click()
1126        self.browser.getLink("Study Course").click()
1127        self.browser.getLink("Add course list").click()
1128        self.browser.getControl("Create course list now").click()
1129        self.assertFalse('Score' in self.browser.contents)
1130        IWorkflowState(self.student).setState('returning')
1131        self.browser.open(self.studycourse_path + '/100')
1132        self.assertFalse('Score' in self.browser.contents)
1133        self.app['configuration']['2004'].show_results = ['ug_ft']
1134        self.browser.open(self.studycourse_path + '/100')
1135        self.assertTrue('Score' in self.browser.contents)
1136        return
1137
1138    def test_repair_course_list(self):
1139        IWorkflowState(self.student).setState('school fee paid')
1140        self.student['studycourse'].entry_session = 2016
1141        self.student['studycourse'].current_session = 2016
1142        self.browser.open(self.login_path)
1143        self.browser.getControl(name="form.login").value = self.student_id
1144        self.browser.getControl(name="form.password").value = 'spwd'
1145        self.browser.getControl("Login").click()
1146        self.browser.getLink("Study Course").click()
1147        self.browser.getLink("Add course list").click()
1148        self.browser.getControl("Create course list now").click()
1149        self.assertTrue('Edit course list' in self.browser.contents)
1150        self.assertFalse('Repair course list' in self.browser.contents)
1151        self.student['studycourse'].current_level = 200
1152        self.browser.open(self.studycourse_path + '/100')
1153        self.assertFalse('Edit course list' in self.browser.contents)
1154        self.assertFalse('Repair course list' in self.browser.contents)
1155        configuration = createObject('waeup.SessionConfiguration')
1156        configuration.academic_session = 2016
1157        self.app['configuration'].addSessionConfiguration(configuration)
1158        self.app['configuration']['2016'].studylevel_repair_enabled = True
1159        self.browser.open(self.studycourse_path + '/100')
1160        self.assertFalse('Edit course list' in self.browser.contents)
1161        self.assertTrue('Repair course list' in self.browser.contents)
1162        self.browser.getLink("Repair").click()
1163        self.assertEqual(self.browser.url, self.studycourse_path + '/100/repair')
1164        self.assertTrue('Repair course list of 100' in self.browser.contents)
1165        return
1166
1167    def test_student_clearance(self):
1168        # Student cant login if their password is not set
1169        IWorkflowInfo(self.student).fireTransition('admit')
1170        self.browser.open(self.login_path)
1171        self.browser.getControl(name="form.login").value = self.student_id
1172        self.browser.getControl(name="form.password").value = 'spwd'
1173        self.browser.getControl("Login").click()
1174        self.assertMatches(
1175            '...You logged in...', self.browser.contents)
1176        # Admitted student can upload a passport picture
1177        self.browser.open(self.student_path + '/change_portrait')
1178        ctrl = self.browser.getControl(name='passportuploadedit')
1179        file_obj = open(SAMPLE_IMAGE, 'rb')
1180        file_ctrl = ctrl.mech_control
1181        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1182        self.browser.getControl(
1183            name='upload_passportuploadedit').click()
1184        self.assertTrue(
1185            'src="http://localhost/app/students/E1000000/passport.jpg"'
1186            in self.browser.contents)
1187        # Student is redirected to the personal data form because
1188        # personal data form is not properly filled.
1189        self.browser.open(self.student_path + '/start_clearance')
1190        self.assertMatches('...Personal data form is not properly filled...',
1191                           self.browser.contents)
1192        self.assertEqual(self.browser.url, self.student_path + '/edit_personal')
1193        self.student.father_name = u'Rudolf'
1194        self.browser.open(self.student_path + '/start_clearance')
1195        self.assertMatches(
1196            '...<h1 class="kofa-content-label">Start clearance</h1>...',
1197            self.browser.contents)
1198
1199    def test_student_accommodation(self):
1200        del self.student['accommodation']['2004']
1201        self.student['studycourse'].certificate.study_mode = 'dp_ft'
1202        # All beds can be assigned
1203        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
1204        bed1.bed_type = u'regular_male_all'
1205        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
1206        bed2.bed_type = u'regular_female_all'
1207        notify(grok.ObjectModifiedEvent(bed1))
1208        notify(grok.ObjectModifiedEvent(bed2))
1209        # Login
1210        self.browser.open(self.login_path)
1211        self.browser.getControl(name="form.login").value = self.student_id
1212        self.browser.getControl(name="form.password").value = 'spwd'
1213        self.browser.getControl("Login").click()
1214        # Students can book accommodation without AC ...
1215        self.browser.open(self.acco_path)
1216        IWorkflowInfo(self.student).fireTransition('admit')
1217        self.browser.getControl("Book accommodation").click()
1218        self.assertTrue(
1219            'You are not eligible to book accommodation.'
1220            in self.browser.contents)
1221        self.student['studycourse'].certificate.study_mode = 'ug_ft'
1222        self.app['hostels'].accommodation_states = [PAID]
1223        self.browser.getControl("Book accommodation").click()
1224        self.assertTrue(
1225            'You are in the wrong registration state.'
1226            in self.browser.contents)
1227        IWorkflowState(self.student).setState(PAID)
1228        self.browser.getControl("Book accommodation").click()
1229        self.assertFalse('Activation Code:' in self.browser.contents)
1230        self.browser.getControl("Create bed ticket").click()
1231        # Bed is randomly selected but, since there is only
1232        # one bed for this student, we know that
1233        self.assertEqual(self.student['accommodation']['2004'].bed_coordinates,
1234            'Hall 1, Block A, Room 101, Bed A (regular_male_all)')
1235        # Only the hall name is displayed
1236        self.assertEqual(self.student[
1237            'accommodation']['2004'].display_coordinates,
1238            'Hall 1')
1239        self.assertFalse('Hall 1, Block A, Room 101, Bed A'
1240            in self.browser.contents)
1241        self.assertTrue('<td>Hall 1</td>'
1242            in self.browser.contents)
1243        return
1244
1245    def test_handle_courses_by_lecturer(self):
1246        self.app['users'].addUser('mrslecturer', 'mrslecturerSecret1')
1247        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1248        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1249        # Add course ticket
1250        studylevel = createObject(u'waeup.StudentStudyLevel')
1251        studylevel.level = 100
1252        studylevel.level_session = 2004
1253        self.student['studycourse'].addStudentStudyLevel(
1254            self.certificate, studylevel)
1255        # Assign local Lecturer role for a certificate.
1256        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1257        prmlocal = IPrincipalRoleManager(course)
1258        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1259        notify(LocalRoleSetEvent(
1260            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1261        # Login as lecturer.
1262        self.browser.open(self.login_path)
1263        self.browser.getControl(name="form.login").value = 'mrslecturer'
1264        self.browser.getControl(name="form.password").value = 'mrslecturerSecret1'
1265        self.browser.getControl("Login").click()
1266        self.browser.getLink("My Courses").click()
1267        self.browser.getLink("COURSE1").click()
1268        # Course results can be batch edited via the edit_courses view.
1269        self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
1270        self.app['configuration'].current_academic_session = 2004
1271        self.app['configuration']['2004'].score_editing_enabled = ['ug_ft']
1272        IWorkflowState(self.student).setState('courses validated')
1273        self.browser.open(
1274            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1275        self.assertTrue(
1276            'input type="text" name="scores:list"'
1277            in self.browser.contents)
1278        self.browser.getControl(name="scores:list", index=0).value = '55'
1279        self.browser.getControl(name="cas:list", index=0).value = '22'
1280        self.browser.getControl("Update scores from").click()
1281        # New score and ca has been set.
1282        self.assertEqual(
1283            self.student['studycourse']['100']['COURSE1'].score, 55)
1284        self.assertEqual(
1285            self.student['studycourse']['100']['COURSE1'].ca, 22)
1286        # Score editing has been logged.
1287        logfile = os.path.join(
1288            self.app['datacenter'].storage, 'logs', 'students.log')
1289        logcontent = open(logfile).read()
1290        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1291                        'E1000000 100/COURSE1 score updated (55)' in logcontent)
1292        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1293                        'E1000000 100/COURSE1 ca updated (22)' in logcontent)
1294        # Non-integer scores won't be accepted.
1295        self.browser.open(
1296            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1297        self.assertTrue('value="55" />' in self.browser.contents)
1298        self.browser.getControl(name="scores:list", index=0).value = 'abc'
1299        self.browser.getControl("Update scores").click()
1300        self.assertTrue('Error: Score(s), CA(s) and Imported TS(s) of TESTER, Anna have not be updated.'
1301            in self.browser.contents)
1302        # Scores can be removed.
1303        self.browser.open(
1304            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
1305        self.browser.getControl(name="scores:list", index=0).value = ''
1306        self.browser.getControl("Update scores").click()
1307        self.assertEqual(
1308            self.student['studycourse']['100']['COURSE1'].score, None)
1309        logcontent = open(logfile).read()
1310        self.assertTrue('mrslecturer - waeup.aaue.students.browser.CustomEditScoresPage - '
1311                        'E1000000 100/COURSE1 score updated (None)' in logcontent)
1312
1313
1314    def disabled_test_student_view_transcript(self):
1315        # Student cant login if their password is not set
1316        IWorkflowInfo(self.student).fireTransition('admit')
1317        self.browser.open(self.login_path)
1318        self.browser.getControl(name="form.login").value = self.student_id
1319        self.browser.getControl(name="form.password").value = 'spwd'
1320        self.browser.getControl("Login").click()
1321        self.assertMatches(
1322            '...You logged in...', self.browser.contents)
1323        # Students can view the transcript
1324        self.browser.open(self.studycourse_path)
1325        self.browser.getLink("Transcript").click()
1326        self.browser.getLink("Academic Transcript").click()
1327        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1328        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1329
1330    def test_alumni_request_pw(self):
1331        # Add an applicants container
1332        applicantscontainer = ApplicantsContainer()
1333        applicantscontainer.code = u'trans2017'
1334        applicantscontainer.prefix = 'trans'
1335        applicantscontainer.year = 2017
1336        applicantscontainer.title = u'This is the trans2017 container'
1337        applicantscontainer.application_category = 'no'
1338        applicantscontainer.mode = 'create'
1339        applicantscontainer.strict_deadline = True
1340        delta = timedelta(days=10)
1341        applicantscontainer.startdate = datetime.now(pytz.utc) - delta
1342        applicantscontainer.enddate = datetime.now(pytz.utc) + delta
1343        self.app['applicants']['trans2017'] = applicantscontainer
1344        self.applicantscontainer = self.app['applicants']['trans2017']
1345        # Student with wrong number can't be found.
1346        # Applicant is redirected to application section.
1347        self.browser.open('http://localhost/app/alumni_requestpw')
1348        self.browser.getControl(name="form.lastname").value = 'Tester'
1349        self.browser.getControl(name="form.number").value = 'anynumber'
1350        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
1351        self.browser.getControl("Send login credentials").click()
1352        self.assertTrue('No student record found.'
1353            in self.browser.contents)
1354        self.assertEqual(self.browser.url,
1355            'http://localhost/app/applicants/trans2017/register')
1356
1357    def test_student_course_registration_outstanding(self):
1358        self.course = createObject('waeup.Course')
1359        self.course.code = 'COURSE2'
1360        self.course.semester = 1
1361        self.course.credits = 39
1362        self.course.passmark = 40
1363        self.app['faculties']['fac1']['dep1'].courses.addCourse(
1364            self.course)
1365        IWorkflowState(self.student).setState('school fee paid')
1366        self.browser.open(self.login_path)
1367        self.browser.getControl(name="form.login").value = self.student_id
1368        self.browser.getControl(name="form.password").value = 'spwd'
1369        self.browser.getControl("Login").click()
1370        self.browser.open(self.student_path + '/studycourse/add')
1371        self.browser.getControl("Create course list now").click()
1372        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1373        self.student['studycourse'].current_level = 200
1374        self.browser.getLink("Study Course").click()
1375        self.browser.getLink("Add course list").click()
1376        self.assertMatches('...Add current level 200 (Year 2)...',
1377                           self.browser.contents)
1378        self.browser.getControl("Create course list now").click()
1379        self.browser.getLink("200").click()
1380        self.browser.getLink("Edit course list").click()
1381        self.browser.getLink("here").click()
1382        self.browser.getControl(name="form.course").value = ['COURSE2']
1383        self.browser.getControl("Add course ticket").click()
1384        # Carryover COURSE1 in level 200 already has 10 credits
1385        self.assertMatches(
1386            '...Maximum credits exceeded...', self.browser.contents)
1387        # If COURSE1 is outstanding, its credits won't be considered
1388        self.student['studycourse']['200']['COURSE1'].outstanding = True
1389        self.browser.getControl("Add course ticket").click()
1390        # Corresponding certificate course is missing
1391        self.assertTrue(
1392            'COURSE2 is not part of the CERT1 curriculum.'
1393            in self.browser.contents)
1394        self.app['faculties']['fac1']['dep1'].certificates[
1395            'CERT1'].addCertCourse(self.course, level=100)
1396        self.browser.getControl("Add course ticket").click()
1397        self.assertTrue(
1398            'Successfully added COURSE2' in self.browser.contents)
1399        return
1400
1401    def test_examination_schedule_slip(self):
1402        self.student.flash_notice = u'My Examination Date'
1403        self.browser.open(self.login_path)
1404        self.browser.getControl(name="form.login").value = self.student_id
1405        self.browser.getControl(name="form.password").value = 'spwd'
1406        self.browser.getControl("Login").click()
1407        self.browser.open(self.student_path)
1408        self.browser.getLink("Download examination schedule slip").click()
1409        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1410        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1411        path = os.path.join(samples_dir(), 'examination_schedule_slip.pdf')
1412        open(path, 'wb').write(self.browser.contents)
1413        print "Sample PDF examination_schedule_slip.pdf written to %s" % path
1414        # If flash_notice does not contain exam' the button does not show up.
1415        self.student.flash_notice = u'anything'
1416        self.browser.open(self.student_path)
1417        self.assertFalse('examination schedule slip' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.