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

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

Reserve bed so that it cannot be automatically booked by someone else.

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