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

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

Restructure school fee calculation completely.

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