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

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

Don't allow the total outcome of course grade exceed 100 (ie if CA + Score > 100, return "total score is greater than 100").

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