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

Last change on this file since 17432 was 17431, checked in by Henrik Bettermann, 17 months ago

Rewrite payment configuration.

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