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

Last change on this file since 16164 was 15250, checked in by Henrik Bettermann, 6 years ago

Add ReleaseExpiredAllocationsActionButton? which has previously only been used by Uniben.

  • Property svn:keywords set to Id
File size: 31.3 KB
RevLine 
[7195]1## $Id: tests.py 15250 2018-11-23 11:10:19Z 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):
[15250]190        self.app['hostels'].allocation_expiration = 7
[13319]191        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
[13316]192        cat = queryUtility(ICatalog, name='beds_catalog')
193        bedticket = BedTicket()
194        bedticket.booking_session = 2004
195        bedticket.bed_coordinates = u'anything'
196        self.student['accommodation'].addBedTicket(bedticket)
197        self.app[
198            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
199        notify(grok.ObjectModifiedEvent(
200            self.app['hostels']['hall-x']['hall_block_room_bed']))
[13533]201        self.assertEqual(
202            self.app['hostels']['hall-x']['hall_block_room_bed'].bed_type,
203            'regular_male_fr')
[13316]204        results = cat.searchResults(owner=(self.student_id, self.student_id))
205        self.assertEqual(len(results), 1)
[13319]206        self.browser.open(self.container_path + '/releaseexpired')
[13529]207        self.assertTrue('Portal must be in maintenance mode for releasing expired bed allocations'
208            in self.browser.contents)
209        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
210        self.browser.open(self.container_path + '/releaseexpired')
[13319]211        self.assertTrue('No bed released' in self.browser.contents)
[13316]212        delta = timedelta(days=10)
213        bedticket.booking_date = datetime.utcnow() - delta
[13319]214        self.browser.open(self.container_path + '/releaseexpired')
215        self.assertTrue(
216            'Successfully released beds: hall_block_room_bed (K1000000)'
217            in self.browser.contents)
[13316]218        results = cat.searchResults(owner=(self.student_id, self.student_id))
219        self.assertEqual(len(results), 0)
220        self.assertMatches(bedticket.display_coordinates,
221            '-- booking expired (2015-10-14 08:35:38 UTC) --')
[13533]222        # The owner has been removed and the bed reserved.
[13316]223        self.assertEqual(
224            self.app['hostels']['hall-x']['hall_block_room_bed'].owner,
225            NOT_OCCUPIED)
[13533]226        self.assertEqual(
227            self.app['hostels']['hall-x']['hall_block_room_bed'].bed_type,
228            'regular_male_reserved')
[13445]229        # Accommodation session can't be changed if hostels are not empty.
230        self.browser.open(self.manage_container_path)
231        self.browser.getControl(name="form.accommodation_session").value = ['2005']
232        self.browser.getControl("Save").click()
233        self.assertTrue(
234            'You can\'t change the booking session before clearing all hostels'
235            in self.browser.contents)
[13318]236        # Releasing is logged.
237        logcontent = open(self.logfile).read()
238        self.assertTrue(
[13319]239            'hostels.browser.ReleaseExpiredAllocationsPage - hostels - '
[13318]240            'released: hall_block_room_bed (K1000000)'
241            in logcontent)
[13316]242        return
243
[13483]244    def test_bed_statistics(self):
245        utils = getUtility(IHostelsUtils)
246        self.app['hostels']['hall-x'][
247            'hall_block_room_bed'].owner = NOT_OCCUPIED
248        notify(grok.ObjectModifiedEvent(
249            self.app['hostels']['hall-x']['hall_block_room_bed']))
250        stats = utils.getBedStatistics()
251        self.assertEqual(stats,
[13484]252            {'regular_male_fr': (0, 1, 1),
253             'regular_female_fi': (0, 0, 0),
254             'regular_male_re': (0, 0, 0),
255             'regular_female_fr': (0, 0, 0),
256             'regular_female_all': (0, 0, 0),
257             'regular_female_re': (0, 0, 0),
258             'regular_female_reserved': (0, 0, 0),
259             'regular_male_reserved': (0, 0, 0),
260             'regular_male_fi': (0, 0, 0),
261             'regular_male_all': (0, 0, 0)}
[13483]262             )
263        self.app[
264            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
265        notify(grok.ObjectModifiedEvent(
266            self.app['hostels']['hall-x']['hall_block_room_bed']))
267        stats = utils.getBedStatistics()
268        self.assertEqual(stats,
[13484]269            {'regular_male_fr': (1, 0, 1),
270             'regular_female_fi': (0, 0, 0),
271             'regular_male_re': (0, 0, 0),
272             'regular_female_fr': (0, 0, 0),
273             'regular_female_all': (0, 0, 0),
274             'regular_female_re': (0, 0, 0),
275             'regular_female_reserved': (0, 0, 0),
276             'regular_male_reserved': (0, 0, 0),
277             'regular_male_fi': (0, 0, 0),
278             'regular_male_all': (0, 0, 0)}
[13483]279             )
280
[6972]281class BedCatalogTests(HostelsFullSetup):
282
283    layer = FunctionalLayer
284
285    def test_get_catalog(self):
[7045]286        # We can get a beds catalog if we wish
[6972]287        cat = queryUtility(ICatalog, name='beds_catalog')
288        assert cat is not None
289
290    def test_search_by_type(self):
291        # We can find a certain bed
292        cat = queryUtility(ICatalog, name='beds_catalog')
[13483]293        results = cat.searchResults(
294            bed_type=(u'regular_male_fr', u'regular_male_fr'))
[6972]295        results = [x for x in results] # Turn results generator into list
296        assert len(results) == 1
[9200]297        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
[6972]298
[7003]299    def test_search_by_owner(self):
300        # We can find a certain bed
[9200]301        myobj = self.app['hostels']['hall-x']['hall_block_room_bed']
[7003]302        myobj.owner = u'abc'
303        notify(grok.ObjectModifiedEvent(myobj))
304        cat = queryUtility(ICatalog, name='beds_catalog')
305        results = cat.searchResults(owner=(u'abc', u'abc'))
306        results = [x for x in results] # Turn results generator into list
307        assert len(results) == 1
[9200]308        assert results[0] is self.app['hostels']['hall-x']['hall_block_room_bed']
[7003]309
[6961]310class HostelsUITests(HostelsFullSetup):
311
312    layer = FunctionalLayer
313
314    def test_anonymous_access(self):
315        # Anonymous users can't access hostels containers
316        self.assertRaises(
317            Unauthorized, self.browser.open, self.manage_container_path)
318        return
319
[6985]320    def test_add_search_edit_delete_manage_hostels(self):
[6961]321        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
322        self.browser.open(self.container_path)
323        self.browser.getLink("Manage accommodation").click()
324        self.assertEqual(self.browser.headers['Status'], '200 Ok')
325        self.assertEqual(self.browser.url, self.manage_container_path)
326        self.browser.getControl("Add hostel").click()
327        self.assertEqual(self.browser.headers['Status'], '200 Ok')
328        self.assertEqual(self.browser.url, self.add_hostel_path)
329        self.browser.getControl("Create hostel").click()
330        self.assertEqual(self.browser.headers['Status'], '200 Ok')
331        self.assertTrue('Hostel created' in self.browser.contents)
[6988]332        self.browser.open(self.container_path + '/addhostel')
333        self.browser.getControl("Create hostel").click()
334        self.assertTrue('The hostel already exists' in self.browser.contents)
[6973]335        hall = self.app['hostels']['hall-1']
[6961]336        hall.blocks_for_female = ['A','B']
[6973]337        self.browser.open(self.container_path + '/hall-1')
[13276]338        expected = '...<ul id="form.blocks_for_female" ><li>Block A</li>...'
[6961]339        self.assertMatches(expected,self.browser.contents)
[6973]340        self.browser.open(self.container_path + '/hall-1/manage')
[6985]341        self.browser.getControl(name="form.rooms_per_floor").value = '1'
[6962]342        self.browser.getControl("Save").click()
343        self.assertTrue('Form has been saved' in self.browser.contents)
[6985]344        # Since the testbrowser does not support Javascrip the
345        # save action cleared the settings above and we have to set them
[13176]346        # again.
[6985]347        self.assertTrue(len(hall.blocks_for_female) == 0)
348        hall.blocks_for_female = ['A','B']
[6988]349        hall.beds_for_fresh = ['A']
350        hall.beds_for_returning = ['B']
351        hall.beds_for_final = ['C']
[6985]352        hall.beds_for_all = ['D','E']
353        self.browser.getControl("Update all beds").click()
[13529]354        self.assertTrue('Portal must be in maintenance mode for bed updates'
355            in self.browser.contents)
356        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
357        self.browser.getControl("Update all beds").click()
[6988]358        expected = '...0 empty beds removed, 10 beds added, 0 occupied beds modified ()...'
[6985]359        self.assertMatches(expected,self.browser.contents)
360        cat = queryUtility(ICatalog, name='beds_catalog')
361        results = cat.searchResults(
362            bed_type=('regular_female_all', 'regular_female_all'))
[9448]363        results = [(x.bed_id, x.bed_type) for x in results]
364        self.assertEqual(results,
365            [(u'hall-1_A_101_D', u'regular_female_all'),
366             (u'hall-1_A_101_E', u'regular_female_all'),
367             (u'hall-1_B_101_D', u'regular_female_all'),
368             (u'hall-1_B_101_E', u'regular_female_all')])
[13176]369        # Reserve beds.
[6988]370        self.browser.getControl("Switch reservation", index=0).click()
371        self.assertTrue('No item selected' in self.browser.contents)
[6985]372        ctrl = self.browser.getControl(name='val_id')
[6988]373        ctrl.getControl(value='hall-1_A_101_A').selected = True
374        ctrl.getControl(value='hall-1_A_101_B').selected = True
375        ctrl.getControl(value='hall-1_A_101_C').selected = True
[6985]376        ctrl.getControl(value='hall-1_A_101_D').selected = True
377        self.browser.getControl("Switch reservation", index=0).click()
[6988]378        self.assertTrue('Successfully switched beds: hall-1_A_101_A (reserved)'
[6985]379            in self.browser.contents)
[9448]380        self.assertEqual(self.app['hostels']['hall-1'][
381            'hall-1_A_101_D'].bed_type, 'regular_female_reserved')
[13176]382        # The catalog has been updated.
[9448]383        results = cat.searchResults(
384            bed_type=('regular_female_all', 'regular_female_all'))
385        results = [(x.bed_id, x.bed_type) for x in results]
386        self.assertEqual(results,
387            [(u'hall-1_A_101_E', u'regular_female_all'),
388             (u'hall-1_B_101_D', u'regular_female_all'),
389             (u'hall-1_B_101_E', u'regular_female_all')])
390        results = cat.searchResults(
391            bed_type=('regular_female_reserved', 'regular_female_reserved'))
392        results = [(x.bed_id, x.bed_type) for x in results]
393        self.assertEqual(results,
394            [(u'hall-1_A_101_A', u'regular_female_reserved'),
395             (u'hall-1_A_101_B', u'regular_female_reserved'),
396             (u'hall-1_A_101_C', u'regular_female_reserved'),
397             (u'hall-1_A_101_D', u'regular_female_reserved')])
[13176]398        # Change hostel configuration with one bed booked.
[9448]399        hall['hall-1_A_101_E'].owner = u'anyid'
400        notify(grok.ObjectModifiedEvent(hall['hall-1_A_101_E']))
401        hall.beds_for_fresh = ['A', 'E']
[6985]402        hall.beds_for_all = ['D']
403        self.browser.getControl("Update all beds").click()
[9448]404        expected = '...9 empty beds removed, 9 beds added, 1 occupied beds modified...'
[6985]405        self.assertMatches(expected,self.browser.contents)
[13176]406        # Updating beds (including booked beds!) does update catalog.
[6985]407        results = cat.searchResults(
408            bed_type=('regular_female_all', 'regular_female_all'))
[9448]409        results = [(x.bed_id, x.bed_type) for x in results]
[13346]410        # The reservation of hall-1_A_101_D has been cancelled.
[9448]411        self.assertEqual(results,
[13346]412            [(u'hall-1_A_101_D', u'regular_female_all'),
413             (u'hall-1_B_101_D', u'regular_female_all')])
[13176]414        # Release bed which has previously been booked.
[7045]415        bedticket = BedTicket()
[9423]416        bedticket.booking_session = 2004
[7045]417        bedticket.bed_coordinates = u'anything'
418        self.student['accommodation'].addBedTicket(bedticket)
419        self.app['hostels']['hall-1']['hall-1_A_101_D'].owner = self.student_id
[13315]420        notify(grok.ObjectModifiedEvent(self.app['hostels']['hall-1']['hall-1_A_101_D']))
[7045]421        self.browser.open(self.container_path + '/hall-1/manage')
422        ctrl = self.browser.getControl(name='val_id')
[7068]423        self.browser.getControl("Release selected beds", index=0).click()
424        self.assertMatches("...No item selected...", self.browser.contents)
425        ctrl = self.browser.getControl(name='val_id')
[7045]426        ctrl.getControl(value='hall-1_A_101_D').selected = True
427        self.browser.getControl("Release selected beds", index=0).click()
428        self.assertMatches(
429          '...Successfully released beds: hall-1_A_101_D (%s)...' % self.student_id,
430          self.browser.contents)
431        self.assertMatches(bedticket.bed_coordinates,
[8234]432          u' -- booking cancelled on <YYYY-MM-DD hh:mm:ss> UTC --')
[13316]433        # The catalog has been updated.
[13315]434        results = cat.searchResults(owner=(self.student_id, self.student_id))
435        assert len(results) == 0
436        # If we release a free bed, nothing will happen.
[7070]437        ctrl = self.browser.getControl(name='val_id')
438        ctrl.getControl(value='hall-1_A_101_D').selected = True
439        self.browser.getControl("Release selected beds", index=0).click()
440        self.assertMatches(
441          '...No allocated bed selected...', self.browser.contents)
[13176]442        # Managers can manually allocate eligible students after cancellation.
[7068]443        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
[13176]444        # 'not occupied' is not accepted.
[9416]445        self.browser.getControl("Save").click()
446        self.assertMatches(
447            "...No valid student id...",
448            self.browser.contents)
[13176]449        # Invalid student ids are not accepted.
[9414]450        self.browser.getControl(name="form.owner").value = 'nonsense'
[7068]451        self.browser.getControl("Save").click()
[9414]452        self.assertMatches(
[9416]453            "...Either student does not exist or student "
454            "is not in accommodation session...",
[9414]455            self.browser.contents)
456        self.browser.getControl(name="form.owner").value = self.student_id
457        self.browser.getControl("Save").click()
[7068]458        self.assertMatches("...Form has been saved...", self.browser.contents)
[13176]459        # Students can only be allocated once.
[9414]460        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_B')
461        self.browser.getControl(name="form.owner").value = self.student_id
462        self.browser.getControl("Save").click()
463        self.assertMatches(
464            "...This student resides in bed hall-1_A_101_A...",
465            self.browser.contents)
[7070]466        # If we open the same form again, we will be redirected to hostel
467        # manage page. Beds must be released first before they can be
468        # allocated to other students.
469        self.browser.open(self.container_path + '/hall-1/hall-1_A_101_A')
470        self.assertEqual(self.browser.url,
[11254]471            self.container_path + '/hall-1/@@manage#tab2')
[7070]472        # Updating the beds again will not affect the allocation and also
[13176]473        # the bed numbering remains the same.
[7070]474        old_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
475        old_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
476        self.browser.getControl("Update all beds").click()
[9448]477        # 8 beds have been removed and re-added, 2 beds remains untouched
[13176]478        # because they are occupied.
[9448]479        expected = '...8 empty beds removed, 8 beds added, 0 occupied beds modified...'
[7070]480        self.assertMatches(expected,self.browser.contents)
481        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
482        new_owner = self.app['hostels']['hall-1']['hall-1_A_101_A'].owner
483        self.assertEqual(new_number, old_number)
484        self.assertEqual(new_owner, old_owner)
485        # If we change the bed type of an allocated bed, the modification will
[13176]486        # be indicated.
[7070]487        hall.blocks_for_female = ['B']
488        hall.blocks_for_male = ['A']
489        self.browser.getControl("Update all beds").click()
[9448]490        expected = '...8 empty beds removed, 8 beds added, ' + \
491            '2 occupied beds modified (hall-1_A_101_A, hall-1_A_101_E, )...'
[7070]492        self.assertMatches(expected,self.browser.contents)
493        new_number = self.app['hostels']['hall-1']['hall-1_A_101_A'].bed_number
494        # Also the number of the bed has changed.
495        self.assertFalse(new_number == old_number)
[9414]496        # The number of occupied beds are displayed on container page.
[9196]497        self.browser.open(self.container_path)
[9448]498        self.assertTrue('2 of 10' in self.browser.contents)
[13440]499        bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
[13484]500        # Managers can open the bed statistics page
501        self.browser.getLink("Bed statistics").click()
502        self.assertTrue('Bed Statistics</h1>' in self.browser.contents)
[13176]503        # Remove entire hostel.
[6962]504        self.browser.open(self.manage_container_path)
505        ctrl = self.browser.getControl(name='val_id')
506        value = ctrl.options[0]
507        ctrl.getControl(value=value).selected = True
508        self.browser.getControl("Remove selected", index=0).click()
509        self.assertTrue('Successfully removed' in self.browser.contents)
[13176]510        # Catalog is empty.
[6985]511        results = cat.searchResults(
512            bed_type=('regular_female_all', 'regular_female_all'))
513        results = [x for x in results]
514        assert len(results) == 0
[13440]515        # Bed has been removed from bedticket
516        self.assertEqual(bedticket.bed, None)
[12894]517        # Actions are logged.
518        logcontent = open(self.logfile).read()
519        self.assertTrue(
520            'hall-1 - 9 empty beds removed, 9 beds added, 1 occupied '
521            'beds modified (hall-1_A_101_E, )'
522            in logcontent)
[9197]523
524    def test_clear_hostels(self):
[13529]525        grok.getSite()['configuration'].maintmode_enabled_by = u'any_id'
[9197]526        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
527        self.browser.open(self.container_path)
528        self.browser.getLink("Manage accommodation").click()
529        self.browser.getControl("Add hostel").click()
530        self.browser.getControl("Create hostel").click()
531        hall = self.app['hostels']['hall-1']
532        hall.blocks_for_female = ['A','B']
533        hall.rooms_per_floor = 1
534        hall.beds_for_fresh = ['A']
535        hall.beds_for_returning = ['B']
536        hall.beds_for_final = ['C']
537        hall.beds_for_all = ['D','E']
538        self.browser.open(self.container_path + '/hall-1/manage')
539        self.browser.getControl("Update all beds").click()
540        cat = queryUtility(ICatalog, name='beds_catalog')
541        results = cat.searchResults(bed_type=(None, None))
542        self.assertEqual(len(results), 11)
543        self.browser.getControl("Clear hostel").click()
544        self.assertEqual(len(self.app['hostels']['hall-1']), 0)
545        # Only the bed in hall-x remains in the catalog.
546        results = cat.searchResults(bed_type=(None, None))
547        self.assertEqual(len(results), 1)
548        # We can clear all hostels at the same time.
549        self.browser.open(self.manage_container_path)
550        self.browser.getControl("Clear all hostels").click()
551        results = cat.searchResults(bed_type=(None, None))
552        self.assertEqual(len(results), 0)
553        # Both actions have been logged.
[9202]554        logcontent = open(self.logfile).read()
[9197]555        self.assertTrue('INFO - zope.mgr - hostels.browser.HostelManageFormPage'
556                        ' - hall-1 - cleared' in logcontent)
557        self.assertTrue('zope.mgr - hostels.browser.HostelsContainerManagePage'
558                        ' - hostels - all hostels cleared' in logcontent)
[9200]559
560class ExportTests(HostelsFullSetup):
561
562    layer = FunctionalLayer
563
564    def setUp(self):
565        super(ExportTests, self).setUp()
566        self.workdir = tempfile.mkdtemp()
567        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
568        return
569
570    def test_export_hostels(self):
571        exporter = HostelExporter()
572        exporter.export_all(self.app, self.outfile)
573        result = open(self.outfile, 'rb').read()
574        self.assertEqual(
575            result,
576            'beds_for_all,beds_for_final,beds_for_fresh,beds_for_pre,'
[13346]577            'beds_for_returning,blocks_for_female,'
[10680]578            'blocks_for_male,floors_per_block,hostel_id,hostel_name,maint_fee,'
[13346]579            'rooms_per_floor,sort_id,special_handling\r\n[],[],[],[],[],[],[],'
580            '1,hall-x,Hall 1,0.0,2,10,regular\r\n'
[9200]581            )
582        return
583
584    def test_export_beds(self):
585        exporter = BedExporter()
586        exporter.export_all(self.app, self.outfile)
587        result = open(self.outfile, 'rb').read()
588        self.assertEqual(
589            result,
590            'bed_id,bed_number,bed_type,owner,hall,block,room,bed,'
[13483]591            'special_handling,sex,bt\r\nhall_block_room_bed,1,regular_male_fr,,'
592            'hall,block,room,bed,regular,male,fr\r\n'
[9200]593            )
[9202]594        return
595
[9283]596    def tearDown(self):
597        super(ExportTests, self).tearDown()
598        clearSite()
599        shutil.rmtree(os.path.dirname(self.outfile))
600
[13432]601class HostelBedProcessorTest(HostelsFullSetup):
[9202]602
603    layer = FunctionalLayer
604
605    def test_import(self):
606        self.processor = HostelProcessor()
607        self.workdir = tempfile.mkdtemp()
[9283]608        self.csv_file = os.path.join(self.workdir, 'sample_hostel_data.csv')
[9202]609        open(self.csv_file, 'wb').write(HOSTEL_SAMPLE_DATA)
610        num, num_warns, fin_file, fail_file = self.processor.doImport(
611            self.csv_file, HOSTEL_HEADER_FIELDS)
612        self.assertEqual(num_warns,0)
613        self.assertEqual(len(self.app['hostels'].keys()), 11) # including hall-x
614        self.assertEqual(self.app['hostels'][
615            'block-a-upper-hostel'].hostel_id,'block-a-upper-hostel')
616        self.assertEqual(self.app['hostels'][
617            'block-a-upper-hostel'].beds_for_final, ['A', 'B'])
618        logcontent = open(self.logfile).read()
619        self.assertTrue(
[9706]620            "Hostel Processor - sample_hostel_data - block-a-upper-hostel - "
621            "updated: "
622            "beds_for_pre=['G'], floors_per_block=1, "
[13346]623            "special_handling=regular, "
[9202]624            "beds_for_final=['A', 'B'], rooms_per_floor=32, "
[12981]625            "hostel_id=block-a-upper-hostel, "
[9202]626            "sort_id=20, beds_for_returning=['C', 'D'], "
627            "hostel_name=Block A Upper Hostel, beds_for_fresh=['E', 'F'], "
[12981]628            "blocks_for_female=['A']"
[9202]629            in logcontent)
630        shutil.rmtree(os.path.dirname(fin_file))
[9283]631        shutil.rmtree(self.workdir)
[13346]632        # The manage page can be opened.
633        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
634        self.browser.open(self.container_path + '/block-a-upper-hostel')
635        self.browser.getLink("Manage").click()
636        self.assertEqual(self.browser.headers['Status'], '200 Ok')
[10683]637        return
638
[13432]639    def test_import_update_hostel_and_beds(self):
[10683]640        self.processor = HostelProcessor()
641        self.workdir = tempfile.mkdtemp()
642        self.csv_file = os.path.join(self.workdir, 'sample_hostel_data.csv')
643        open(self.csv_file, 'wb').write(HOSTEL_SAMPLE_DATA)
644        num, num_warns, fin_file, fail_file = self.processor.doImport(
645            self.csv_file, HOSTEL_HEADER_FIELDS)
646        # We import the same file in update mode
647        num, num_warns, fin_file, fail_file = self.processor.doImport(
648            self.csv_file, HOSTEL_HEADER_FIELDS, 'update')
649        self.assertEqual(num_warns,0)
650        logcontent = open(self.logfile).read()
651        self.assertTrue(
652            "Hostel Processor - sample_hostel_data - block-a-upper-hostel - "
653            "updated: "
654            "beds_for_pre=['G'], floors_per_block=1, "
[13346]655            "special_handling=regular, "
[10683]656            "beds_for_final=['A', 'B'], rooms_per_floor=32, "
[12981]657            "hostel_id=block-a-upper-hostel, "
[10683]658            "sort_id=20, beds_for_returning=['C', 'D'], "
659            "hostel_name=Block A Upper Hostel, beds_for_fresh=['E', 'F'], "
[12981]660            "blocks_for_female=['A']"
[10683]661            in logcontent)
[13432]662        # Update the beds of first hostel
663        self.app['hostels']['block-a-upper-hostel'].updateBeds()
664        # Import beds
665        self.processor = BedProcessor()
666        self.csv_file = os.path.join(self.workdir, 'sample_bed_data.csv')
667        open(self.csv_file, 'wb').write(BED_SAMPLE_DATA)
668        num, num_warns, fin_file, fail_file = self.processor.doImport(
669            self.csv_file, BED_HEADER_FIELDS, 'update')
670        self.assertEqual(num_warns,2)
671        fail_file_content = open(fail_file).read()
672        logcontent = open(self.logfile).read()
673        self.assertEqual(
674            'owner,bed_id,hostel_id,reserved,--ERRORS--\r\n'
675            '<IGNORE>,block-a-upper-hostel_A_101_C,block-a-upper-hostel,2,'
676            'reserved: invalid value\r\n'
677            'K999,block-a-upper-hostel_A_101_E,block-a-upper-hostel,0,'
678            'owner: student does not exist\r\n',
679            fail_file_content)
680        self.assertTrue(
681            'system - Bed Processor (update only) - sample_bed_data - '
682            'block-a-upper-hostel_A_101_A - updated: nothing'
683            in logcontent)
684        self.assertTrue(
685            'system - Bed Processor (update only) - sample_bed_data - '
686            'block-a-upper-hostel_A_101_B - updated: reserved'
687            in logcontent)
688        self.assertTrue(
689            'system - Bed Processor (update only) - sample_bed_data - '
690            'block-a-upper-hostel_A_101_D - updated: owner=K1000000'
691            in logcontent)
692        self.assertEqual(
693            self.app['hostels']['block-a-upper-hostel']
694                    ['block-a-upper-hostel_A_101_D'].owner, 'K1000000')
695        self.assertEqual(
696            self.app['hostels']['block-a-upper-hostel']
697                    ['block-a-upper-hostel_A_101_B'].bed_type,
698                    'regular_female_reserved')
[10683]699        shutil.rmtree(os.path.dirname(fin_file))
700        shutil.rmtree(self.workdir)
[13432]701        return
Note: See TracBrowser for help on using the repository browser.