source: main/waeup.kofa/trunk/src/waeup/kofa/hostels/tests.py @ 17388

Last change on this file since 17388 was 17313, checked in by Henrik Bettermann, 20 months ago

Allow beds to be blocked so that no student can be allocated to such a bed space (in contrast to reserved beds)

  • Property svn:keywords set to Id
File size: 33.8 KB
RevLine 
[7195]1## $Id: tests.py 17313 2023-01-25 08:52:51Z henrik $
2##
[6951]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.
[7195]8##
[6951]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.
[7195]13##
[6951]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##
18"""
[6988]19Tests for hostels and their UI components.
[6951]20"""
[9197]21import os
[6961]22import shutil
23import tempfile
[7003]24import grok
[8686]25import pytz
26from datetime import datetime, timedelta
[7003]27from zope.event import notify
[6951]28from zope.interface.verify import verifyClass, verifyObject
[6961]29from zope.component.hooks import setSite, clearSite
30from zope.testbrowser.testing import Browser
31from zope.security.interfaces import Unauthorized
[6972]32from zope.catalog.interfaces import ICatalog
[17313]33from zope.component import queryUtility, getUtility, createObject
[7811]34from waeup.kofa.app import University
35from waeup.kofa.hostels.interfaces import (
[13483]36    IHostelsContainer, IHostel, IBed, IHostelsUtils)
[13316]37from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
[7811]38from waeup.kofa.hostels.container import HostelsContainer
39from waeup.kofa.hostels.hostel import Hostel, Bed
[13432]40from waeup.kofa.hostels.batching import HostelProcessor, BedProcessor
[9200]41from waeup.kofa.hostels.export import BedExporter, HostelExporter
[7811]42from waeup.kofa.testing import (FunctionalLayer, FunctionalTestCase)
43from waeup.kofa.students.student import Student
44from waeup.kofa.students.accommodation import BedTicket
45from waeup.kofa.university.department import Department
[6951]46
[9202]47HOSTEL_SAMPLE_DATA = open(
48    os.path.join(os.path.dirname(__file__), 'sample_hostel_data.csv'),
49    'rb').read()
50
51HOSTEL_HEADER_FIELDS = HOSTEL_SAMPLE_DATA.split(
52    '\n')[0].split(',')
53
[13432]54BED_SAMPLE_DATA = open(
55    os.path.join(os.path.dirname(__file__), 'sample_bed_data.csv'),
56    'rb').read()
57
[17313]58BED_SAMPLE_DATA_2 = open(
59    os.path.join(os.path.dirname(__file__), 'sample_bed_data_2.csv'),
60    'rb').read()
61
[13432]62BED_HEADER_FIELDS = BED_SAMPLE_DATA.split(
63    '\n')[0].split(',')
64
[6951]65class HostelsContainerTestCase(FunctionalTestCase):
66
67    layer = FunctionalLayer
68
69    def test_interfaces(self):
70        # Make sure the correct interfaces are implemented.
71        self.assertTrue(
72            verifyClass(
73                IHostelsContainer, HostelsContainer)
74            )
75        self.assertTrue(
76            verifyObject(
77                IHostelsContainer, HostelsContainer())
78            )
79        self.assertTrue(
80            verifyClass(
81                IHostel, Hostel)
82            )
83        self.assertTrue(
84            verifyObject(
85                IHostel, Hostel())
86            )
[6963]87        self.assertTrue(
88            verifyClass(
89                IBed, Bed)
90            )
[13170]91        bed = Bed()
92        bed.bed_id = u'a_b_c_d'
93        bed.bed_type = u'a_b_c'
[6963]94        self.assertTrue(
95            verifyObject(
[13170]96                IBed, bed)
[6963]97            )
[6951]98        return
99
100    def test_base(self):
101        # We cannot call the fundamental methods of a base in that case
102        container = HostelsContainer()
[7077]103        hostel = Hostel()
104        # We cannot add arbitrary objects
105        department = Department()
106        self.assertRaises(
107            TypeError, container.addHostel, department)
108        self.assertRaises(
109            TypeError, hostel.addBed, department)
[8686]110        # Application is expired if startdate or enddate are not set
111        # or current datetime is outside application period.
112        self.assertTrue(container.expired)
113        delta = timedelta(days=10)
114        container.startdate = datetime.now(pytz.utc) - delta
115        self.assertTrue(container.expired)
116        container.enddate = datetime.now(pytz.utc) + delta
117        self.assertFalse(container.expired)
[6961]118
119class HostelsFullSetup(FunctionalTestCase):
120
121    def setUp(self):
122        super(HostelsFullSetup, self).setUp()
123
124        # Setup a sample site for each test
125        app = University()
126        self.dc_root = tempfile.mkdtemp()
127        app['datacenter'].setStoragePath(self.dc_root)
128
129        # Prepopulate the ZODB...
130        self.getRootFolder()['app'] = app
131        # we add the site immediately after creation to the
132        # ZODB. Catalogs and other local utilities are not setup
133        # before that step.
134        self.app = self.getRootFolder()['app']
135        # Set site here. Some of the following setup code might need
136        # to access grok.getSite() and should get our new app then
137        setSite(app)
138
[7045]139        # Add student with subobjects
140        student = Student()
[7357]141        student.firstname = u'Anna'
142        student.lastname = u'Tester'
[7045]143        student.reg_number = u'123'
144        student.matric_number = u'234'
145        student.sex = u'f'
146        self.app['students'].addStudent(student)
147        self.student_id = student.student_id
148        self.student = self.app['students'][self.student_id]
149        self.student['studycourse'].current_session = 2004
150        self.student['studycourse'].entry_session = 2004
[13440]151
[7068]152        # The students_catalog must be informed that the
153        # session attribute has changed
154        notify(grok.ObjectModifiedEvent(self.student))
[7045]155
156        # Set accommodation_session
[8685]157        self.app['hostels'].accommodation_session = 2004
[7045]158
[6972]159        # Create a hostel
160        hostel = Hostel()
[6973]161        hostel.hostel_id = u'hall-x'
[6972]162        self.app['hostels'][hostel.hostel_id] = hostel
163
164        # Create a bed
165        bed = Bed()
[9200]166        bed.bed_id = u'hall_block_room_bed'
[6972]167        bed.bed_number = 1
[13483]168        bed.bed_type = u'regular_male_fr'
[6972]169        self.app['hostels'][hostel.hostel_id][bed.bed_id] = bed
170
[6961]171        self.container_path = 'http://localhost/app/hostels'
[7068]172        self.student_path = 'http://localhost/app/students/%s' % self.student_id
[6961]173        self.manage_container_path = self.container_path + '/@@manage'
174        self.add_hostel_path = self.container_path + '/addhostel'
175
176        # Put the prepopulated site into test ZODB and prepare test
177        # browser
178        self.browser = Browser()
179        self.browser.handleErrors = False
180
[9202]181        self.logfile = os.path.join(
182            self.app['datacenter'].storage, 'logs', 'hostels.log')
183
[6961]184    def tearDown(self):
185        super(HostelsFullSetup, self).tearDown()
186        clearSite()
187        shutil.rmtree(self.dc_root)
188
[13316]189class HostelsContainerTests(HostelsFullSetup):
190
191    layer = FunctionalLayer
192
193    def test_release_expired_allocations(self):
[15250]194        self.app['hostels'].allocation_expiration = 7
[13319]195        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
[13316]196        cat = queryUtility(ICatalog, name='beds_catalog')
197        bedticket = BedTicket()
198        bedticket.booking_session = 2004
199        bedticket.bed_coordinates = u'anything'
200        self.student['accommodation'].addBedTicket(bedticket)
201        self.app[
202            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
203        notify(grok.ObjectModifiedEvent(
204            self.app['hostels']['hall-x']['hall_block_room_bed']))
[13533]205        self.assertEqual(
206            self.app['hostels']['hall-x']['hall_block_room_bed'].bed_type,
207            'regular_male_fr')
[13316]208        results = cat.searchResults(owner=(self.student_id, self.student_id))
209        self.assertEqual(len(results), 1)
[13319]210        self.browser.open(self.container_path + '/releaseexpired')
[13529]211        self.assertTrue('Portal must be in maintenance mode for releasing expired bed allocations'
212            in self.browser.contents)
213        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
214        self.browser.open(self.container_path + '/releaseexpired')
[13319]215        self.assertTrue('No bed released' in self.browser.contents)
[13316]216        delta = timedelta(days=10)
217        bedticket.booking_date = datetime.utcnow() - delta
[13319]218        self.browser.open(self.container_path + '/releaseexpired')
219        self.assertTrue(
220            'Successfully released beds: hall_block_room_bed (K1000000)'
221            in self.browser.contents)
[13316]222        results = cat.searchResults(owner=(self.student_id, self.student_id))
223        self.assertEqual(len(results), 0)
224        self.assertMatches(bedticket.display_coordinates,
225            '-- booking expired (2015-10-14 08:35:38 UTC) --')
[13533]226        # The owner has been removed and the bed reserved.
[13316]227        self.assertEqual(
228            self.app['hostels']['hall-x']['hall_block_room_bed'].owner,
229            NOT_OCCUPIED)
[13533]230        self.assertEqual(
231            self.app['hostels']['hall-x']['hall_block_room_bed'].bed_type,
232            'regular_male_reserved')
[13445]233        # Accommodation session can't be changed if hostels are not empty.
234        self.browser.open(self.manage_container_path)
235        self.browser.getControl(name="form.accommodation_session").value = ['2005']
236        self.browser.getControl("Save").click()
237        self.assertTrue(
238            'You can\'t change the booking session before clearing all hostels'
239            in self.browser.contents)
[13318]240        # Releasing is logged.
241        logcontent = open(self.logfile).read()
242        self.assertTrue(
[13319]243            'hostels.browser.ReleaseExpiredAllocationsPage - hostels - '
[13318]244            'released: hall_block_room_bed (K1000000)'
245            in logcontent)
[13316]246        return
247
[13483]248    def test_bed_statistics(self):
249        utils = getUtility(IHostelsUtils)
250        self.app['hostels']['hall-x'][
251            'hall_block_room_bed'].owner = NOT_OCCUPIED
252        notify(grok.ObjectModifiedEvent(
253            self.app['hostels']['hall-x']['hall_block_room_bed']))
254        stats = utils.getBedStatistics()
255        self.assertEqual(stats,
[13484]256            {'regular_male_fr': (0, 1, 1),
257             'regular_female_fi': (0, 0, 0),
258             'regular_male_re': (0, 0, 0),
259             'regular_female_fr': (0, 0, 0),
260             'regular_female_all': (0, 0, 0),
261             'regular_female_re': (0, 0, 0),
262             'regular_female_reserved': (0, 0, 0),
263             'regular_male_reserved': (0, 0, 0),
264             'regular_male_fi': (0, 0, 0),
265             'regular_male_all': (0, 0, 0)}
[13483]266             )
267        self.app[
268            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
269        notify(grok.ObjectModifiedEvent(
270            self.app['hostels']['hall-x']['hall_block_room_bed']))
271        stats = utils.getBedStatistics()
272        self.assertEqual(stats,
[13484]273            {'regular_male_fr': (1, 0, 1),
274             'regular_female_fi': (0, 0, 0),
275             'regular_male_re': (0, 0, 0),
276             'regular_female_fr': (0, 0, 0),
277             'regular_female_all': (0, 0, 0),
278             'regular_female_re': (0, 0, 0),
279             'regular_female_reserved': (0, 0, 0),
280             'regular_male_reserved': (0, 0, 0),
281             'regular_male_fi': (0, 0, 0),
282             'regular_male_all': (0, 0, 0)}
[13483]283             )
284
[6972]285class BedCatalogTests(HostelsFullSetup):
286
287    layer = FunctionalLayer
288
289    def test_get_catalog(self):
[7045]290        # We can get a beds catalog if we wish
[6972]291        cat = queryUtility(ICatalog, name='beds_catalog')
292        assert cat is not None
293
294    def test_search_by_type(self):
295        # We can find a certain bed
296        cat = queryUtility(ICatalog, name='beds_catalog')
[13483]297        results = cat.searchResults(
298            bed_type=(u'regular_male_fr', u'regular_male_fr'))
[6972]299        results = [x for x in results] # Turn results generator into list
300        assert len(results) == 1
[9200]301        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
[6972]302
[7003]303    def test_search_by_owner(self):
304        # We can find a certain bed
[9200]305        myobj = self.app['hostels']['hall-x']['hall_block_room_bed']
[7003]306        myobj.owner = u'abc'
307        notify(grok.ObjectModifiedEvent(myobj))
308        cat = queryUtility(ICatalog, name='beds_catalog')
309        results = cat.searchResults(owner=(u'abc', u'abc'))
310        results = [x for x in results] # Turn results generator into list
311        assert len(results) == 1
[9200]312        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
[7003]313
[6961]314class HostelsUITests(HostelsFullSetup):
315
316    layer = FunctionalLayer
317
318    def test_anonymous_access(self):
319        # Anonymous users can't access hostels containers
320        self.assertRaises(
321            Unauthorized, self.browser.open, self.manage_container_path)
322        return
323
[6985]324    def test_add_search_edit_delete_manage_hostels(self):
[6961]325        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
326        self.browser.open(self.container_path)
327        self.browser.getLink("Manage accommodation").click()
328        self.assertEqual(self.browser.headers['Status'], '200 Ok')
329        self.assertEqual(self.browser.url, self.manage_container_path)
330        self.browser.getControl("Add hostel").click()
331        self.assertEqual(self.browser.headers['Status'], '200 Ok')
332        self.assertEqual(self.browser.url, self.add_hostel_path)
333        self.browser.getControl("Create hostel").click()
334        self.assertEqual(self.browser.headers['Status'], '200 Ok')
335        self.assertTrue('Hostel created' in self.browser.contents)
[6988]336        self.browser.open(self.container_path + '/addhostel')
337        self.browser.getControl("Create hostel").click()
338        self.assertTrue('The hostel already exists' in self.browser.contents)
[6973]339        hall = self.app['hostels']['hall-1']
[6961]340        hall.blocks_for_female = ['A','B']
[6973]341        self.browser.open(self.container_path + '/hall-1')
[13276]342        expected = '...<ul id="form.blocks_for_female" ><li>Block A</li>...'
[6961]343        self.assertMatches(expected,self.browser.contents)
[6973]344        self.browser.open(self.container_path + '/hall-1/manage')
[6985]345        self.browser.getControl(name="form.rooms_per_floor").value = '1'
[6962]346        self.browser.getControl("Save").click()
347        self.assertTrue('Form has been saved' in self.browser.contents)
[6985]348        # Since the testbrowser does not support Javascrip the
349        # save action cleared the settings above and we have to set them
[13176]350        # again.
[6985]351        self.assertTrue(len(hall.blocks_for_female) == 0)
352        hall.blocks_for_female = ['A','B']
[6988]353        hall.beds_for_fresh = ['A']
354        hall.beds_for_returning = ['B']
355        hall.beds_for_final = ['C']
[6985]356        hall.beds_for_all = ['D','E']
357        self.browser.getControl("Update all beds").click()
[13529]358        self.assertTrue('Portal must be in maintenance mode for bed updates'
359            in self.browser.contents)
360        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
361        self.browser.getControl("Update all beds").click()
[6988]362        expected = '...0 empty beds removed, 10 beds added, 0 occupied beds modified ()...'
[6985]363        self.assertMatches(expected,self.browser.contents)
364        cat = queryUtility(ICatalog, name='beds_catalog')
365        results = cat.searchResults(
366            bed_type=('regular_female_all', 'regular_female_all'))
[9448]367        results = [(x.bed_id, x.bed_type) for x in results]
368        self.assertEqual(results,
369            [(u'hall-1_A_101_D', u'regular_female_all'),
370             (u'hall-1_A_101_E', u'regular_female_all'),
371             (u'hall-1_B_101_D', u'regular_female_all'),
372             (u'hall-1_B_101_E', u'regular_female_all')])
[13176]373        # Reserve beds.
[6988]374        self.browser.getControl("Switch reservation", index=0).click()
375        self.assertTrue('No item selected' in self.browser.contents)
[6985]376        ctrl = self.browser.getControl(name='val_id')
[6988]377        ctrl.getControl(value='hall-1_A_101_A').selected = True
378        ctrl.getControl(value='hall-1_A_101_B').selected = True
379        ctrl.getControl(value='hall-1_A_101_C').selected = True
[6985]380        ctrl.getControl(value='hall-1_A_101_D').selected = True
381        self.browser.getControl("Switch reservation", index=0).click()
[6988]382        self.assertTrue('Successfully switched beds: hall-1_A_101_A (reserved)'
[6985]383            in self.browser.contents)
[9448]384        self.assertEqual(self.app['hostels']['hall-1'][
385            'hall-1_A_101_D'].bed_type, 'regular_female_reserved')
[13176]386        # The catalog has been updated.
[9448]387        results = cat.searchResults(
388            bed_type=('regular_female_all', 'regular_female_all'))
389        results = [(x.bed_id, x.bed_type) for x in results]
390        self.assertEqual(results,
391            [(u'hall-1_A_101_E', u'regular_female_all'),
392             (u'hall-1_B_101_D', u'regular_female_all'),
393             (u'hall-1_B_101_E', u'regular_female_all')])
394        results = cat.searchResults(
395            bed_type=('regular_female_reserved', 'regular_female_reserved'))
396        results = [(x.bed_id, x.bed_type) for x in results]
397        self.assertEqual(results,
398            [(u'hall-1_A_101_A', u'regular_female_reserved'),
399             (u'hall-1_A_101_B', u'regular_female_reserved'),
400             (u'hall-1_A_101_C', u'regular_female_reserved'),
401             (u'hall-1_A_101_D', u'regular_female_reserved')])
[17313]402        # Block beds.
403        self.browser.getControl("Switch blockade", index=0).click()
404        self.assertTrue('No item selected' in self.browser.contents)
405        ctrl = self.browser.getControl(name='val_id')
406        ctrl.getControl(value='hall-1_A_101_A').selected = True
407        ctrl.getControl(value='hall-1_A_101_B').selected = True
408        ctrl.getControl(value='hall-1_A_101_C').selected = True
409        ctrl.getControl(value='hall-1_A_101_D').selected = True
410        self.browser.getControl("Switch blockade", index=0).click()
411        self.assertTrue('Successfully switched beds: hall-1_A_101_A (blocked)'
412            in self.browser.contents)
413        self.assertEqual(self.app['hostels']['hall-1'][
414            'hall-1_A_101_D'].bed_type, 'regular_female_blocked')
415        # The catalog has been updated.
416        results = cat.searchResults(
417            bed_type=('regular_female_all', 'regular_female_all'))
418        results = [(x.bed_id, x.bed_type) for x in results]
419        self.assertEqual(results,
420            [(u'hall-1_A_101_E', u'regular_female_all'),
421             (u'hall-1_B_101_D', u'regular_female_all'),
422             (u'hall-1_B_101_E', u'regular_female_all')])
423        results = cat.searchResults(
424            bed_type=('regular_female_blocked', 'regular_female_blocked'))
425        results = [(x.bed_id, x.bed_type) for x in results]
426        self.assertEqual(results,
427            [(u'hall-1_A_101_A', u'regular_female_blocked'),
428             (u'hall-1_A_101_B', u'regular_female_blocked'),
429             (u'hall-1_A_101_C', u'regular_female_blocked'),
430             (u'hall-1_A_101_D', u'regular_female_blocked')])
[13176]431        # Change hostel configuration with one bed booked.
[9448]432        hall['hall-1_A_101_E'].owner = u'anyid'
433        notify(grok.ObjectModifiedEvent(hall['hall-1_A_101_E']))
434        hall.beds_for_fresh = ['A', 'E']
[6985]435        hall.beds_for_all = ['D']
436        self.browser.getControl("Update all beds").click()
[9448]437        expected = '...9 empty beds removed, 9 beds added, 1 occupied beds modified...'
[6985]438        self.assertMatches(expected,self.browser.contents)
[13176]439        # Updating beds (including booked beds!) does update catalog.
[6985]440        results = cat.searchResults(
441            bed_type=('regular_female_all', 'regular_female_all'))
[9448]442        results = [(x.bed_id, x.bed_type) for x in results]
[13346]443        # The reservation of hall-1_A_101_D has been cancelled.
[9448]444        self.assertEqual(results,
[13346]445            [(u'hall-1_A_101_D', u'regular_female_all'),
446             (u'hall-1_B_101_D', u'regular_female_all')])
[13176]447        # Release bed which has previously been booked.
[7045]448        bedticket = BedTicket()
[9423]449        bedticket.booking_session = 2004
[7045]450        bedticket.bed_coordinates = u'anything'
451        self.student['accommodation'].addBedTicket(bedticket)
452        self.app['hostels']['hall-1']['hall-1_A_101_D'].owner = self.student_id
[13315]453        notify(grok.ObjectModifiedEvent(self.app['hostels']['hall-1']['hall-1_A_101_D']))
[7045]454        self.browser.open(self.container_path + '/hall-1/manage')
455        ctrl = self.browser.getControl(name='val_id')
[7068]456        self.browser.getControl("Release selected beds", index=0).click()
457        self.assertMatches("...No item selected...", self.browser.contents)
458        ctrl = self.browser.getControl(name='val_id')
[7045]459        ctrl.getControl(value='hall-1_A_101_D').selected = True
460        self.browser.getControl("Release selected beds", index=0).click()
461        self.assertMatches(
462          '...Successfully released beds: hall-1_A_101_D (%s)...' % self.student_id,
463          self.browser.contents)
464        self.assertMatches(bedticket.bed_coordinates,
[8234]465          u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> UTC --')
[13316]466        # The catalog has been updated.
[13315]467        results = cat.searchResults(owner=(self.student_id, self.student_id))
468        assert len(results) == 0
469        # If we release a free bed, nothing will happen.
[7070]470        ctrl = self.browser.getControl(name='val_id')
471        ctrl.getControl(value='hall-1_A_101_D').selected = True
472        self.browser.getControl("Release selected beds", index=0).click()
473        self.assertMatches(
474          '...No allocated bed selected...', self.browser.contents)
[13176]475        # Managers can manually allocate eligible students after cancellation.
[7068]476        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
[13176]477        # 'not occupied' is not accepted.
[9416]478        self.browser.getControl("Save").click()
479        self.assertMatches(
480            "...No valid student id...",
481            self.browser.contents)
[13176]482        # Invalid student ids are not accepted.
[9414]483        self.browser.getControl(name="form.owner").value = 'nonsense'
[7068]484        self.browser.getControl("Save").click()
[9414]485        self.assertMatches(
[9416]486            "...Either student does not exist or student "
[17176]487            "is not eligible to book accommodation...",
[9414]488            self.browser.contents)
489        self.browser.getControl(name="form.owner").value = self.student_id
490        self.browser.getControl("Save").click()
[7068]491        self.assertMatches("...Form has been saved...", self.browser.contents)
[13176]492        # Students can only be allocated once.
[9414]493        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_B')
494        self.browser.getControl(name="form.owner").value = self.student_id
495        self.browser.getControl("Save").click()
496        self.assertMatches(
497            "...This student resides in bed hall-1_A_101_A...",
498            self.browser.contents)
[7070]499        # If we open the same form again, we will be redirected to hostel
500        # manage page. Beds must be released first before they can be
501        # allocated to other students.
502        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
503        self.assertEqual(self.browser.url,
[11254]504            self.container_path + '/hall-1/@@manage#tab2')
[7070]505        # Updating the beds again will not affect the allocation and also
[13176]506        # the bed numbering remains the same.
[7070]507        old_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
508        old_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
509        self.browser.getControl("Update all beds").click()
[9448]510        # 8 beds have been removed and re-added, 2 beds remains untouched
[13176]511        # because they are occupied.
[9448]512        expected = '...8 empty beds removed, 8 beds added, 0 occupied beds modified...'
[7070]513        self.assertMatches(expected,self.browser.contents)
514        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
515        new_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
516        self.assertEqual(new_number, old_number)
517        self.assertEqual(new_owner, old_owner)
518        # If we change the bed type of an allocated bed, the modification will
[13176]519        # be indicated.
[7070]520        hall.blocks_for_female = ['B']
521        hall.blocks_for_male = ['A']
522        self.browser.getControl("Update all beds").click()
[9448]523        expected = '...8 empty beds removed, 8 beds added, ' + \
524            '2 occupied beds modified (hall-1_A_101_A, hall-1_A_101_E, )...'
[7070]525        self.assertMatches(expected,self.browser.contents)
526        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
527        # Also the number of the bed has changed.
528        self.assertFalse(new_number == old_number)
[9414]529        # The number of occupied beds are displayed on container page.
[9196]530        self.browser.open(self.container_path)
[9448]531        self.assertTrue('2 of 10' in self.browser.contents)
[13440]532        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
[13484]533        # Managers can open the bed statistics page
534        self.browser.getLink("Bed statistics").click()
535        self.assertTrue('Bed Statistics</h1>' in self.browser.contents)
[13176]536        # Remove entire hostel.
[6962]537        self.browser.open(self.manage_container_path)
538        ctrl = self.browser.getControl(name='val_id')
539        value = ctrl.options[0]
540        ctrl.getControl(value=value).selected = True
541        self.browser.getControl("Remove selected", index=0).click()
542        self.assertTrue('Successfully removed' in self.browser.contents)
[13176]543        # Catalog is empty.
[6985]544        results = cat.searchResults(
545            bed_type=('regular_female_all', 'regular_female_all'))
546        results = [x for x in results]
547        assert len(results) == 0
[13440]548        # Bed has been removed from bedticket
549        self.assertEqual(bedticket.bed, None)
[12894]550        # Actions are logged.
551        logcontent = open(self.logfile).read()
552        self.assertTrue(
553            'hall-1 - 9 empty beds removed, 9 beds added, 1 occupied '
554            'beds modified (hall-1_A_101_E, )'
555            in logcontent)
[9197]556
557    def test_clear_hostels(self):
[13529]558        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
[9197]559        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
560        self.browser.open(self.container_path)
561        self.browser.getLink("Manage accommodation").click()
562        self.browser.getControl("Add hostel").click()
563        self.browser.getControl("Create hostel").click()
564        hall = self.app['hostels']['hall-1']
565        hall.blocks_for_female = ['A','B']
566        hall.rooms_per_floor = 1
567        hall.beds_for_fresh = ['A']
568        hall.beds_for_returning = ['B']
569        hall.beds_for_final = ['C']
570        hall.beds_for_all = ['D','E']
571        self.browser.open(self.container_path + '/hall-1/manage')
572        self.browser.getControl("Update all beds").click()
573        cat = queryUtility(ICatalog, name='beds_catalog')
574        results = cat.searchResults(bed_type=(None, None))
575        self.assertEqual(len(results), 11)
576        self.browser.getControl("Clear hostel").click()
577        self.assertEqual(len(self.app['hostels']['hall-1']), 0)
578        # Only the bed in hall-x remains in the catalog.
579        results = cat.searchResults(bed_type=(None, None))
580        self.assertEqual(len(results), 1)
581        # We can clear all hostels at the same time.
582        self.browser.open(self.manage_container_path)
583        self.browser.getControl("Clear all hostels").click()
584        results = cat.searchResults(bed_type=(None, None))
585        self.assertEqual(len(results), 0)
586        # Both actions have been logged.
[9202]587        logcontent = open(self.logfile).read()
[9197]588        self.assertTrue('INFO - zope.mgr - hostels.browser.HostelManageFormPage'
589                        ' - hall-1 - cleared' in logcontent)
590        self.assertTrue('zope.mgr - hostels.browser.HostelsContainerManagePage'
591                        ' - hostels - all hostels cleared' in logcontent)
[9200]592
593class ExportTests(HostelsFullSetup):
594
595    layer = FunctionalLayer
596
597    def setUp(self):
598        super(ExportTests, self).setUp()
599        self.workdir = tempfile.mkdtemp()
600        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
601        return
602
603    def test_export_hostels(self):
604        exporter = HostelExporter()
605        exporter.export_all(self.app, self.outfile)
606        result = open(self.outfile, 'rb').read()
607        self.assertEqual(
608            result,
609            'beds_for_all,beds_for_final,beds_for_fresh,beds_for_pre,'
[13346]610            'beds_for_returning,blocks_for_female,'
[10680]611            'blocks_for_male,floors_per_block,hostel_id,hostel_name,maint_fee,'
[13346]612            'rooms_per_floor,sort_id,special_handling\r\n[],[],[],[],[],[],[],'
613            '1,hall-x,Hall 1,0.0,2,10,regular\r\n'
[9200]614            )
615        return
616
617    def test_export_beds(self):
618        exporter = BedExporter()
619        exporter.export_all(self.app, self.outfile)
620        result = open(self.outfile, 'rb').read()
621        self.assertEqual(
622            result,
623            'bed_id,bed_number,bed_type,owner,hall,block,room,bed,'
[13483]624            'special_handling,sex,bt\r\nhall_block_room_bed,1,regular_male_fr,,'
625            'hall,block,room,bed,regular,male,fr\r\n'
[9200]626            )
[9202]627        return
628
[9283]629    def tearDown(self):
630        super(ExportTests, self).tearDown()
631        clearSite()
632        shutil.rmtree(os.path.dirname(self.outfile))
633
[13432]634class HostelBedProcessorTest(HostelsFullSetup):
[9202]635
636    layer = FunctionalLayer
637
638    def test_import(self):
639        self.processor = HostelProcessor()
640        self.workdir = tempfile.mkdtemp()
[9283]641        self.csv_file = os.path.join(self.workdir, 'sample_hostel_data.csv')
[9202]642        open(self.csv_file, 'wb').write(HOSTEL_SAMPLE_DATA)
643        num, num_warns, fin_file, fail_file = self.processor.doImport(
644            self.csv_file, HOSTEL_HEADER_FIELDS)
645        self.assertEqual(num_warns,0)
646        self.assertEqual(len(self.app['hostels'].keys()), 11) # including hall-x
647        self.assertEqual(self.app['hostels'][
648            'block-a-upper-hostel'].hostel_id,'block-a-upper-hostel')
649        self.assertEqual(self.app['hostels'][
650            'block-a-upper-hostel'].beds_for_final, ['A', 'B'])
651        logcontent = open(self.logfile).read()
652        self.assertTrue(
[9706]653            "Hostel Processor - sample_hostel_data - block-a-upper-hostel - "
654            "updated: "
655            "beds_for_pre=['G'], floors_per_block=1, "
[13346]656            "special_handling=regular, "
[9202]657            "beds_for_final=['A', 'B'], rooms_per_floor=32, "
[12981]658            "hostel_id=block-a-upper-hostel, "
[9202]659            "sort_id=20, beds_for_returning=['C', 'D'], "
660            "hostel_name=Block A Upper Hostel, beds_for_fresh=['E', 'F'], "
[12981]661            "blocks_for_female=['A']"
[9202]662            in logcontent)
663        shutil.rmtree(os.path.dirname(fin_file))
[9283]664        shutil.rmtree(self.workdir)
[13346]665        # The manage page can be opened.
666        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
667        self.browser.open(self.container_path + '/block-a-upper-hostel')
668        self.browser.getLink("Manage").click()
669        self.assertEqual(self.browser.headers['Status'], '200 Ok')
[10683]670        return
671
[13432]672    def test_import_update_hostel_and_beds(self):
[10683]673        self.processor = HostelProcessor()
674        self.workdir = tempfile.mkdtemp()
675        self.csv_file = os.path.join(self.workdir, 'sample_hostel_data.csv')
676        open(self.csv_file, 'wb').write(HOSTEL_SAMPLE_DATA)
677        num, num_warns, fin_file, fail_file = self.processor.doImport(
678            self.csv_file, HOSTEL_HEADER_FIELDS)
679        # We import the same file in update mode
680        num, num_warns, fin_file, fail_file = self.processor.doImport(
681            self.csv_file, HOSTEL_HEADER_FIELDS, 'update')
682        self.assertEqual(num_warns,0)
683        logcontent = open(self.logfile).read()
684        self.assertTrue(
685            "Hostel Processor - sample_hostel_data - block-a-upper-hostel - "
686            "updated: "
687            "beds_for_pre=['G'], floors_per_block=1, "
[13346]688            "special_handling=regular, "
[10683]689            "beds_for_final=['A', 'B'], rooms_per_floor=32, "
[12981]690            "hostel_id=block-a-upper-hostel, "
[10683]691            "sort_id=20, beds_for_returning=['C', 'D'], "
692            "hostel_name=Block A Upper Hostel, beds_for_fresh=['E', 'F'], "
[12981]693            "blocks_for_female=['A']"
[10683]694            in logcontent)
[13432]695        # Update the beds of first hostel
696        self.app['hostels']['block-a-upper-hostel'].updateBeds()
697        # Import beds
698        self.processor = BedProcessor()
699        self.csv_file = os.path.join(self.workdir, 'sample_bed_data.csv')
700        open(self.csv_file, 'wb').write(BED_SAMPLE_DATA)
701        num, num_warns, fin_file, fail_file = self.processor.doImport(
702            self.csv_file, BED_HEADER_FIELDS, 'update')
703        self.assertEqual(num_warns,2)
704        fail_file_content = open(fail_file).read()
705        logcontent = open(self.logfile).read()
706        self.assertEqual(
[17313]707            'owner,bed_id,hostel_id,reserved,blocked,--ERRORS--\r\n'
708            '<IGNORE>,block-a-upper-hostel_A_101_C,block-a-upper-hostel,2,0,'
[13432]709            'reserved: invalid value\r\n'
[17313]710            'K999,block-a-upper-hostel_A_101_E,block-a-upper-hostel,0,0,'
[13432]711            'owner: student does not exist\r\n',
712            fail_file_content)
713        self.assertTrue(
714            'system - Bed Processor (update only) - sample_bed_data - '
[17313]715            'block-a-upper-hostel_A_101_A - updated: blocked'
[13432]716            in logcontent)
717        self.assertTrue(
718            'system - Bed Processor (update only) - sample_bed_data - '
719            'block-a-upper-hostel_A_101_B - updated: reserved'
720            in logcontent)
721        self.assertTrue(
722            'system - Bed Processor (update only) - sample_bed_data - '
723            'block-a-upper-hostel_A_101_D - updated: owner=K1000000'
724            in logcontent)
725        self.assertEqual(
726            self.app['hostels']['block-a-upper-hostel']
727                    ['block-a-upper-hostel_A_101_D'].owner, 'K1000000')
728        self.assertEqual(
729            self.app['hostels']['block-a-upper-hostel']
730                    ['block-a-upper-hostel_A_101_B'].bed_type,
731                    'regular_female_reserved')
[17313]732        # Add another student K1000001
733        student = createObject('waeup.Student')
734        student.firstname = u'Claus'
735        student.lastname = u'Tester'
736        self.app['students'].addStudent(student)
737        # Try to allocate this student to a blocked bed
738
739        open(self.csv_file, 'wb').write(BED_SAMPLE_DATA_2)
740        num, num_warns, fin_file, fail_file = self.processor.doImport(
741            self.csv_file, BED_HEADER_FIELDS, 'update')
742        fail_file_content = open(fail_file).read()
743        self.assertEqual(
744            'owner,bed_id,hostel_id,reserved,blocked,--ERRORS--\r\n'
745            'K1000001,block-a-upper-hostel_A_101_A,block-a-upper-hostel,'
746            '<IGNORE>,<IGNORE>,Bed is blocked.\r\n',
747            fail_file_content)
[10683]748        shutil.rmtree(os.path.dirname(fin_file))
749        shutil.rmtree(self.workdir)
[13432]750        return
Note: See TracBrowser for help on using the repository browser.