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

Last change on this file since 17396 was 17313, checked in by Henrik Bettermann, 2 years 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.