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

Last change on this file since 13238 was 13059, checked in by Henrik Bettermann, 10 years ago

Rename views according to changes in base package.

Add test_manage_payments_bypass_ac_creation.

  • Property svn:keywords set to Id
File size: 24.2 KB
Line 
1## $Id: test_browser.py 13059 2015-06-16 13:31:37Z 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 datetime import datetime, timedelta, date
24from mechanize import LinkNotFoundError
25from hurry.workflow.interfaces import IWorkflowState
26from zope.component.hooks import setSite, clearSite
27from zope.component import getUtility, createObject, queryUtility
28from zope.catalog.interfaces import ICatalog
29from waeup.kofa.app import University
30from waeup.kofa.students.tests.test_browser import StudentsFullSetup
31from waeup.kofa.students.accommodation import BedTicket
32from waeup.kofa.testing import FunctionalTestCase
33from waeup.kofa.browser.tests.test_pdf import samples_dir
34from waeup.aaue.testing import FunctionalLayer
35
36
37class StudentProcessorTest(FunctionalTestCase):
38    """Perform some batching tests.
39    """
40
41    layer = FunctionalLayer
42
43    def setUp(self):
44        super(StudentProcessorTest, self).setUp()
45        # Setup a sample site for each test
46        app = University()
47        self.dc_root = tempfile.mkdtemp()
48        app['datacenter'].setStoragePath(self.dc_root)
49
50        # Prepopulate the ZODB...
51        self.getRootFolder()['app'] = app
52        # we add the site immediately after creation to the
53        # ZODB. Catalogs and other local utilities are not setup
54        # before that step.
55        self.app = self.getRootFolder()['app']
56        # Set site here. Some of the following setup code might need
57        # to access grok.getSite() and should get our new app then
58        setSite(app)
59
60
61    def tearDown(self):
62        super(StudentProcessorTest, self).tearDown()
63        shutil.rmtree(self.workdir)
64        shutil.rmtree(self.dc_root)
65        clearSite()
66        return
67
68class OfficerUITests(StudentsFullSetup):
69    # Tests for Student class views and pages
70
71    layer = FunctionalLayer
72
73    def test_gpa_calculation(self):
74        studylevel = createObject(u'waeup.StudentStudyLevel')
75        studylevel.level = 100
76        studylevel.level_session = 2005
77        self.student['studycourse'].entry_mode = 'ug_ft'
78        self.student['studycourse'].addStudentStudyLevel(
79            self.certificate, studylevel)
80        # First course has been added automatically.
81        # Set score.
82        studylevel['COURSE1'].score = 55
83        # GPA is 3.0.
84        self.assertEqual(studylevel.gpa_params[0], 3.0)
85        courseticket = createObject('waeup.CourseTicket')
86        courseticket.code = 'ANYCODE'
87        courseticket.title = u'Any TITLE'
88        courseticket.credits = 13
89        courseticket.score = 66
90        courseticket.semester = 1
91        courseticket.dcode = u'ANYDCODE'
92        courseticket.fcode = u'ANYFCODE'
93        studylevel['COURSE2'] = courseticket
94        # total credits
95        self.assertEqual(self.student['studycourse']['100'].gpa_params[1], 23)
96        # weigheted credits = 3 * 10 + 4 * 13
97        self.assertEqual(self.student['studycourse']['100'].gpa_params[2], 82.0)
98        # sgpa = 82 / 23
99        self.assertEqual(self.student['studycourse']['100'].gpa_params[0], 3.565)
100        return
101
102    def test_manage_payments(self):
103        # Add missing configuration data
104        self.app['configuration']['2004'].gown_fee = 150.0
105        self.app['configuration']['2004'].transfer_fee = 90.0
106        self.app['configuration']['2004'].booking_fee = 150.0
107        self.app['configuration']['2004'].maint_fee = 180.0
108        self.app['configuration']['2004'].late_fee = 80.0
109
110        # Managers can add online payment tickets
111        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
112        self.browser.open(self.payments_path)
113        self.browser.getLink("Add current session payment ticket").click()
114        self.browser.getControl(name="form.p_category").value = ['schoolfee']
115        self.browser.getControl("Create ticket").click()
116        self.assertMatches('...Wrong state...',
117                           self.browser.contents)
118        IWorkflowState(self.student).setState('cleared')
119        self.browser.open(self.payments_path + '/addop')
120        self.browser.getControl("Create ticket").click()
121        self.assertMatches('...Amount could not be determined...',
122                           self.browser.contents)
123        self.app['configuration']['2004'].school_fee_1 = 6666.0
124        #self.browser.getControl(name="form.p_category").value = ['schoolfee']
125        # Accepotance fee must be paid first
126        #self.browser.getControl("Create ticket").click()
127        #self.assertMatches('...Please pay acceptance fee first...',
128        #                   self.browser.contents)
129        self.app['configuration']['2004'].clearance_fee = 666.0
130        self.browser.getControl(name="form.p_category").value = ['clearance']
131        self.browser.getControl("Create ticket").click()
132        ctrl = self.browser.getControl(name='val_id')
133        cpt_value = ctrl.options[0]
134        # School fee payment ticket can be added ...
135        self.browser.open(self.payments_path + '/addop')
136        self.browser.getControl(name="form.p_category").value = ['schoolfee']
137        self.browser.getControl("Create ticket").click()
138        self.assertMatches('...ticket created...',
139                           self.browser.contents)
140        # ... but not paid through the query_history page.
141        ctrl = self.browser.getControl(name='val_id')
142        sfpt_value = ctrl.options[1]
143        self.student['studycourse'].entry_session = 2013
144        self.browser.open(self.payments_path + '/' + sfpt_value)
145        self.browser.getLink("Query eTranzact History").click()
146        self.assertMatches('...alert-danger">Please pay acceptance fee first...',
147                           self.browser.contents)
148        # If clearance/acceptance fee is paid ...
149        self.student['payments'][cpt_value].approveStudentPayment()
150        self.browser.getLink("Query eTranzact History").click()
151        # ... query_history page is accessible.
152        self.assertMatches(
153            '...<h1 class="kofa-content-label">Requery eTranzact History</h1>...',
154            self.browser.contents)
155        # Managers can open school fee payment slip
156        self.browser.open(self.payments_path + '/' + sfpt_value)
157        self.browser.getLink("Download payment slip").click()
158        self.assertEqual(self.browser.headers['Status'], '200 Ok')
159        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
160        # If school fee ticket is paid, the student is automatically set to
161        # school fee paid...
162        ticket = self.student['payments'][sfpt_value].approveStudentPayment()
163        self.assertEqual(self.student.state, 'school fee paid')
164        # ... no further school fee ticket can be added.
165        self.browser.open(self.payments_path + '/addop')
166        self.browser.getControl(name="form.p_category").value = ['schoolfee']
167        self.browser.getControl("Create ticket").click()
168        self.assertMatches('...Wrong state...',
169                           self.browser.contents)
170        self.browser.open(self.payments_path + '/addop')
171        self.browser.getControl(name="form.p_category").value = ['late_registration']
172        self.browser.getControl("Create ticket").click()
173        self.assertMatches('...ticket created...',
174                           self.browser.contents)
175
176    def deactivated_test_for_instalment_payments(self):
177        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
178        self.browser.open(self.payments_path)
179        self.browser.open(self.payments_path + '/addop')
180        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
181        self.browser.getControl("Create ticket").click()
182        self.assertMatches('...ticket created...',
183                           self.browser.contents)
184        # We can't add the 2nd instalment ticket because the
185        # first one has not yet been approved.
186        self.browser.open(self.payments_path + '/addop')
187        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
188        self.browser.getControl("Create ticket").click()
189        self.assertMatches('...1st school fee instalment has not yet been paid...',
190                           self.browser.contents)
191        # Ok, then we approve the first instalment ...
192        self.browser.open(self.payments_path)
193        ctrl = self.browser.getControl(name='val_id')
194        p_id = ctrl.options[0]
195        self.browser.open(self.payments_path + '/' + p_id + '/approve')
196        # ... add the second instalment ...
197        self.browser.open(self.payments_path + '/addop')
198        self.browser.getControl(name="form.p_category").value = ['schoolfee_2']
199        self.browser.getControl("Create ticket").click()
200        self.assertMatches('...ticket created...',
201                           self.browser.contents)
202        # ... approve the second instalment ...
203        ctrl = self.browser.getControl(name='val_id')
204        p_id = ctrl.options[1]
205        self.browser.open(self.payments_path + '/' + p_id + '/approve')
206        # ... and finally add the 1st instalment for the next session
207        # provided that student is returning.
208        IWorkflowState(self.student).setState('returning')
209        self.browser.open(self.payments_path + '/addop')
210        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
211        self.browser.getControl("Create ticket").click()
212        self.assertMatches('...Session configuration object is not...',
213                           self.browser.contents)
214        # Uups, we forgot to add a session configuration for next session
215        configuration = createObject('waeup.SessionConfiguration')
216        configuration.academic_session = 2005
217        self.app['configuration'].addSessionConfiguration(configuration)
218        self.app['configuration']['2005'].school_base = 7777.0
219        self.browser.open(self.payments_path + '/addop')
220        self.browser.getControl(name="form.p_category").value = ['schoolfee_1']
221        self.browser.getControl("Create ticket").click()
222        self.assertMatches('...ticket created...',
223                           self.browser.contents)
224        # If the session configuration doesn't exist an error message will
225        # be shown. No other requirement is being checked.
226        del self.app['configuration']['2004']
227        self.browser.open(self.payments_path)
228        self.browser.getLink("Add current session payment ticket").click()
229        self.browser.getControl("Create ticket").click()
230        self.assertMatches('...Session configuration object is not...',
231                           self.browser.contents)
232
233    def test_manage_payments_bypass_ac_creation(self):
234        self.app['configuration']['2004'].school_fee_1 = 6666.0
235        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
236        self.browser.open(self.payments_path)
237        IWorkflowState(self.student).setState('cleared')
238        self.browser.getLink("Add current session payment ticket").click()
239        self.browser.getControl(name="form.p_category").value = ['schoolfee']
240        self.browser.getControl("Create ticket").click()
241        ctrl = self.browser.getControl(name='val_id')
242        value = ctrl.options[0]
243        self.browser.getLink(value).click()
244        payment_url = self.browser.url
245        logfile = os.path.join(
246            self.app['datacenter'].storage, 'logs', 'students.log')
247        # The ticket can be found in the payments_catalog
248        cat = queryUtility(ICatalog, name='payments_catalog')
249        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
250        self.assertTrue(len(results), 1)
251        self.assertTrue(results[0] is self.student['payments'][value])
252        # Managers can approve the payment
253        self.browser.open(payment_url)
254        self.browser.getLink("Approve payment").click()
255        self.assertMatches('...Payment approved...',
256                          self.browser.contents)
257        # Approval is logged in students.log ...
258        logcontent = open(logfile).read()
259        self.assertTrue(
260            'zope.mgr - students.browser.OnlinePaymentApproveView '
261            '- E1000000 - schoolfee payment approved'
262            in logcontent)
263        # ... and in payments.log
264        logfile = os.path.join(
265            self.app['datacenter'].storage, 'logs', 'payments.log')
266        logcontent = open(logfile).read()
267        self.assertTrue(
268            '"zope.mgr",E1000000,%s,schoolfee,6666.0,AP,,,,,,\n' % value
269            in logcontent)
270        # Student is in state school fee paid, no activation
271        # code was necessary.
272        self.assertEqual(self.student.state, 'school fee paid')
273        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
274        return
275
276class StudentUITests(StudentsFullSetup):
277    """Tests for customized student class views and pages
278    """
279
280    layer = FunctionalLayer
281
282    def setUp(self):
283        super(StudentUITests, self).setUp()
284
285        bedticket = BedTicket()
286        bedticket.booking_session = 2004
287        bedticket.bed_type = u'any bed type'
288        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
289        bedticket.bed_coordinates = u'My bed coordinates'
290        self.student['accommodation'].addBedTicket(bedticket)
291
292    def test_student_payments(self):
293        # Login
294        IWorkflowState(self.student).setState('returning')
295        self.browser.open(self.login_path)
296        self.browser.getControl(name="form.login").value = self.student_id
297        self.browser.getControl(name="form.password").value = 'spwd'
298        self.browser.getControl("Login").click()
299        self.browser.open(self.student_path + '/payments')
300        self.assertTrue(
301          'Add current session payment ticket' in self.browser.contents)
302        self.assertFalse(
303          'Add previous session payment ticket' in self.browser.contents)
304        return
305
306    def test_late_registration(self):
307        # Login
308        delta = timedelta(days=10)
309        self.app['configuration'][
310            '2004'].coursereg_deadline = datetime.now(pytz.utc) - delta
311        IWorkflowState(self.student).setState('school fee paid')
312        # Current session is 2004. Here we test course registration for
313        # returning students.
314        self.student['studycourse'].entry_session = 2003
315        self.browser.open(self.login_path)
316        self.browser.getControl(name="form.login").value = self.student_id
317        self.browser.getControl(name="form.password").value = 'spwd'
318        self.browser.getControl("Login").click()
319        self.browser.open(self.payments_path)
320        self.browser.open(self.payments_path + '/addop')
321        self.browser.getControl(name="form.p_category").value = ['late_registration']
322        self.browser.getControl("Create ticket").click()
323        self.assertMatches('...ticket created...',
324                           self.browser.contents)
325        self.browser.open(self.payments_path)
326        ctrl = self.browser.getControl(name='val_id')
327        value = ctrl.options[0]
328        self.browser.getLink("Study Course").click()
329        self.browser.getLink("Add course list").click()
330        self.assertMatches('...Add current level 100 (Year 1)...',
331                           self.browser.contents)
332        self.browser.getControl("Create course list now").click()
333        self.browser.getLink("100").click()
334        self.browser.getLink("Edit course list").click()
335        self.browser.getControl("Register course list").click()
336        self.assertTrue('Course registration has ended. Please pay' in self.browser.contents)
337        self.student['payments'][value].approve()
338        self.browser.getControl("Register course list").click()
339        self.assertTrue('Course list has been registered' in self.browser.contents)
340        self.assertEqual(self.student.state, 'courses registered')
341        # Reset student and check if fresh students are always allowed to
342        # register courses.
343        self.student['studycourse'].entry_session = 2004
344        del self.student['payments'][value]
345        IWorkflowState(self.student).setState('school fee paid')
346        self.browser.open(self.studycourse_path + '/100/edit')
347        self.browser.getControl("Register course list").click()
348        self.assertTrue('Course list has been registered' in self.browser.contents)
349        return
350
351
352    def deactivated_test_student_course_registration(self):
353        # Add more courses
354        self.course2 = createObject('waeup.Course')
355        self.course2.code = 'COURSE2'
356        self.course2.semester = 2
357        self.course2.credits = 10
358        self.course2.passmark = 40
359        self.app['faculties']['fac1']['dep1'].courses.addCourse(
360            self.course2)
361        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
362            self.course2, level=100)
363        self.course3 = createObject('waeup.Course')
364        self.course3.code = 'COURSE3'
365        self.course3.semester = 3
366        self.course3.credits = 10
367        self.course3.passmark = 40
368        self.app['faculties']['fac1']['dep1'].courses.addCourse(
369            self.course3)
370        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
371            self.course3, level=100)
372
373        # Login as student
374        self.browser.open(self.login_path)
375        IWorkflowState(self.student).setState('school fee paid')
376        self.browser.open(self.login_path)
377        self.browser.getControl(name="form.login").value = self.student_id
378        self.browser.getControl(name="form.password").value = 'spwd'
379        self.browser.getControl("Login").click()
380        # Students can add the current study level
381        self.browser.getLink("Study Course").click()
382        self.browser.getLink("Add course list").click()
383        self.assertMatches('...Add current level 100 (Year 1)...',
384                           self.browser.contents)
385        self.browser.getControl("Create course list now").click()
386        # Student has not paid second instalment, therefore a level
387        # with two course ticket was created (semester 1 and combined)
388        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
389        self.browser.getLink("100").click()
390        self.browser.getLink("Edit course list").click()
391        self.browser.getControl("Add course ticket").click()
392        # Student can't add second semester course
393        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
394        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
395        self.assertFalse('<option value="COURSE2">' in self.browser.contents)
396
397        # Let's remove level and see what happens after 2nd instalment payment
398        del(self.student['studycourse']['100'])
399        payment2 = createObject('waeup.StudentOnlinePayment')
400        payment2.p_category = u'schoolfee_2'
401        payment2.p_session = self.student.current_session
402        self.student['payments']['anykey'] = payment2
403        self.browser.open(self.studycourse_path)
404        self.browser.getLink("Add course list").click()
405        self.browser.getControl("Create course list now").click()
406        # Still only 2 tickets have been created since payment ticket
407        # was not paid
408        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 2)
409        payment2.p_state = u'paid'
410        del(self.student['studycourse']['100'])
411        self.browser.open(self.studycourse_path)
412        self.browser.getLink("Add course list").click()
413        self.browser.getControl("Create course list now").click()
414        # Now 2nd semester course has been added
415        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 3)
416        # Student can add second semester course
417        self.browser.getLink("100").click()
418        self.browser.getLink("Edit course list").click()
419        self.browser.getControl("Add course ticket").click()
420        self.assertTrue('<option value="COURSE1">' in self.browser.contents)
421        self.assertTrue('<option value="COURSE2">' in self.browser.contents)
422        self.assertTrue('<option value="COURSE3">' in self.browser.contents)
423        return
424
425    def test_set_matric_number(self):
426        # Login as student
427        self.browser.open(self.login_path)
428        IWorkflowState(self.student).setState('school fee paid')
429        self.browser.open(self.login_path)
430        self.browser.getControl(name="form.login").value = self.student_id
431        self.browser.getControl(name="form.password").value = 'spwd'
432        self.browser.getControl("Login").click()
433        self.assertRaises(
434            LinkNotFoundError,
435            self.browser.getLink, 'Get Matriculation Number')
436        self.student.matric_number = None
437        site = grok.getSite()
438        site['configuration'].next_matric_integer = 1
439        self.student['studycourse'].certificate.study_mode = 'ug_pt'
440        self.browser.open(self.student_path)
441        self.assertRaises(
442            LinkNotFoundError,
443            self.browser.getLink, 'Download matriculation number slip')
444        self.browser.getLink("Get Matriculation Number").click()
445        self.assertTrue('Matriculation number PTP/fac1/dep1/04/00001 assigned.'
446            in self.browser.contents)
447        self.assertEqual(self.student.matric_number, 'PTP/fac1/dep1/04/00001')
448        self.assertRaises(
449            LinkNotFoundError,
450            self.browser.getLink, 'Get Matriculation Number')
451        # Setting matric number is logged.
452        logfile = os.path.join(
453            self.app['datacenter'].storage, 'logs', 'students.log')
454        logcontent = open(logfile).read()
455        self.assertTrue('E1000000 - waeup.aaue.students.browser.StudentGetMatricNumberPage - '
456                        'E1000000 - PTP/fac1/dep1/04/00001 assigned' in logcontent)
457        # Matric Number Slip can be downloaded
458        self.browser.getLink("Download matriculation number slip").click()
459        self.assertEqual(self.browser.headers['Status'], '200 Ok')
460        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
461        path = os.path.join(samples_dir(), 'transcript.pdf')
462        open(path, 'wb').write(self.browser.contents)
463        print "Sample PDF matric_number_slip.pdf written to %s" % path
464        return
465
466    def test_student_course_registration(self):
467        IWorkflowState(self.student).setState('school fee paid')
468        self.browser.open(self.login_path)
469        self.browser.getControl(name="form.login").value = self.student_id
470        self.browser.getControl(name="form.password").value = 'spwd'
471        self.browser.getControl("Login").click()
472        # Now students can add the current study level
473        self.browser.getLink("Study Course").click()
474        self.browser.getLink("Add course list").click()
475        self.assertMatches('...Add current level 100 (Year 1)...',
476                           self.browser.contents)
477        self.browser.getControl("Create course list now").click()
478        # Students can't open the customized pdf course registration slip
479        self.browser.open(
480            self.student_path + '/studycourse/100/course_registration_slip.pdf')
481        self.assertTrue('Forbidden' in self.browser.contents)
482        # They can open slips from the previous session ...
483        self.student['studycourse'].current_level = 200
484        self.browser.open(self.student_path + '/studycourse/100')
485        self.browser.getLink("Download course registration slip").click()
486        self.assertEqual(self.browser.headers['Status'], '200 Ok')
487        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
488        # or if they have registered their course list
489        self.student['studycourse'].current_level = 200
490        IWorkflowState(self.student).setState('courses registered')
491        self.browser.open(self.student_path + '/studycourse/100')
492        self.browser.getLink("Download course registration slip").click()
493        self.assertEqual(self.browser.headers['Status'], '200 Ok')
494        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
Note: See TracBrowser for help on using the repository browser.