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

Last change on this file since 15483 was 15429, checked in by Henrik Bettermann, 6 years ago

We check if there exists a certificate course in the certificate
container which refers to course ticket to be added. If such an object does
not exist, students and managers will be prevented from registering
the corresponding course.

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