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

Last change on this file since 13351 was 13351, checked in by Henrik Bettermann, 9 years ago

Extend personal data form and force student to fill the form.

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