source: main/waeup.kofa/trunk/src/waeup/kofa/hostels/hostel.py @ 9414

Last change on this file since 9414 was 9414, checked in by Henrik Bettermann, 12 years ago

Reorganize allocation of students to beds. We can't use the StudentSource? in live systems. The select box would be filled with ten thousands of students.

  • Property svn:keywords set to Id
File size: 10.5 KB
RevLine 
[7195]1## $Id: hostel.py 9414 2012-10-25 09:44:02Z 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.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19These are the hostels.
20"""
21import grok
[7003]22from zope.event import notify
[8183]23from zope.component import getUtility
[9202]24from zope.component.interfaces import IFactory
[6951]25from datetime import datetime
[7811]26from waeup.kofa.utils.helpers import attrs_to_fields
27from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
[9414]28from waeup.kofa.hostels.interfaces import IHostel, IBed
[7811]29from waeup.kofa.students.interfaces import IBedTicket
[8183]30from waeup.kofa.interfaces import IKofaUtils
[7811]31from waeup.kofa.interfaces import MessageFactory as _
[8186]32from waeup.kofa.utils.helpers import now
[6951]33
34class Hostel(grok.Container):
35    """This is a hostel.
36    """
37    grok.implements(IHostel)
38    grok.provides(IHostel)
39
[6952]40    def loggerInfo(self, ob_class, comment=None):
41        target = self.__name__
42        return grok.getSite()['hostels'].logger_info(ob_class,target,comment)
43
[9196]44    @property
45    def bed_statistics(self):
46        total = len(self.keys())
47        booked = 0
48        for value in self.values():
49            if value.owner != NOT_OCCUPIED:
50                booked += 1
51        return {'booked':booked, 'total':total}
52
53    def clearHostel(self):
[9197]54        """Remove all beds
[9196]55        """
[9197]56        keys = [i for i in self.keys()] # create deep copy
57        for bed in keys:
58            del self[bed]
59        return
[9196]60
[6963]61    def addBed(self, bed):
[6970]62        """Add a bed.
[6963]63        """
64        if not IBed.providedBy(bed):
65            raise TypeError(
66                'Hostels contain only IBed instances')
[6970]67        self[bed.bed_id] = bed
[6963]68        return
69
[6970]70    def updateBeds(self):
71        """Fill hostel with beds or update beds.
72        """
73        added_counter = 0
74        modified_counter = 0
[6978]75        removed_counter = 0
[6988]76        modified_beds = u''
[6978]77
78        # Remove all empty beds. Occupied beds remain in hostel!
79        keys = list(self.keys()) # create list copy
80        for key in keys:
81            if self[key].owner == NOT_OCCUPIED:
82                del self[key]
83                self._p_changed = True
84                removed_counter += 1
[6998]85            else:
86                self[key].bed_number = 9999
87        remaining = len(keys) - removed_counter
[6978]88
[6970]89        blocks_for_female = getattr(self,'blocks_for_female',[])
90        blocks_for_male = getattr(self,'blocks_for_male',[])
91        beds_for_fresh = getattr(self,'beds_for_fresh',[])
92        beds_for_pre = getattr(self,'beds_for_pre',[])
93        beds_for_returning = getattr(self,'beds_for_returning',[])
94        beds_for_final = getattr(self,'beds_for_final',[])
[6971]95        beds_for_all = getattr(self,'beds_for_all',[])
[6976]96        beds_reserved = getattr(self,'beds_reserved',[])
[6970]97        all_blocks = blocks_for_female + blocks_for_male
98        all_beds = (beds_for_pre + beds_for_fresh +
[6971]99            beds_for_returning + beds_for_final + beds_for_all)
[6970]100        for block in all_blocks:
101            sex = 'male'
102            if block in blocks_for_female:
103                sex = 'female'
104            for floor in range(1,int(self.floors_per_block)+1):
105                for room in range(1,int(self.rooms_per_floor)+1):
106                    for bed in all_beds:
107                        room_nr = floor*100 + room
108                        bt = 'all'
[6976]109                        if '%s_%s_%s' % (block,room_nr,bed) in beds_reserved:
[6970]110                            bt = "reserved"
111                        elif bed in beds_for_fresh:
112                            bt = 'fr'
113                        elif bed in beds_for_pre:
114                            bt = 'pr'
115                        elif bed in beds_for_final:
116                            bt = 'fi'
117                        elif bed in beds_for_returning:
118                            bt = 're'
[6973]119                        bt = u'%s_%s_%s' % (self.special_handling,sex,bt)
[6970]120                        uid = u'%s_%s_%d_%s' % (self.hostel_id,block,room_nr,bed)
121                        if self.has_key(uid):
122                            bed = self[uid]
[6998]123                            # Renumber remaining beds
124                            bed.bed_number = len(self) + 1 - remaining
125                            remaining -= 1
[6970]126                            if bed.bed_type != bt:
127                                bed.bed_type = bt
128                                modified_counter += 1
[6988]129                                modified_beds += '%s ' % uid
[6970]130                        else:
131                            bed = Bed()
132                            bed.bed_id = uid
133                            bed.bed_type = bt
[6998]134                            bed.bed_number = len(self) + 1 - remaining
[6970]135                            bed.owner = NOT_OCCUPIED
136                            self.addBed(bed)
137                            added_counter +=1
[6988]138        return removed_counter, added_counter, modified_counter, modified_beds
[6970]139
[6951]140Hostel = attrs_to_fields(Hostel)
[6963]141
142class Bed(grok.Container):
143    """This is a bed.
144    """
[9414]145    grok.implements(IBed)
[6963]146    grok.provides(IBed)
147
[9199]148    @property
149    def coordinates(self):
[6974]150        """Determine the coordinates from the bed_id.
[6963]151        """
[6974]152        return self.bed_id.split('_')
[6963]153
[9199]154    # The following property attributes are only needed
155    # for the exporter to ease evaluation with  Excel.
156
157    @property
158    def hall(self):
159        return self.coordinates[0]
160
161    @property
162    def block(self):
163        return self.coordinates[1]
164
165    @property
166    def room(self):
167        return self.coordinates[2]
168
169    @property
170    def bed(self):
171        return self.coordinates[3]
172
173    @property
174    def special_handling(self):
175        return self.bed_type.split('_')[0]
176
177    @property
178    def sex(self):
179        return self.bed_type.split('_')[1]
180
181    @property
182    def bt(self):
183        return self.bed_type.split('_')[2]
184
185
[6996]186    def bookBed(self, student_id):
[6998]187        if self.owner == NOT_OCCUPIED:
188            self.owner = student_id
[7003]189            notify(grok.ObjectModifiedEvent(self))
[6998]190            return None
191        else:
192            return self.owner
[6996]193
[6974]194    def switchReservation(self):
[7003]195        """Reserves or unreserve bed respectively.
[6974]196        """
197        sh, sex, bt = self.bed_type.split('_')
[9199]198        hostel_id, block, room_nr, bed = self.coordinates
[6975]199        hostel = self.__parent__
[6988]200        beds_for_fresh = getattr(hostel,'beds_for_fresh',[])
201        beds_for_pre = getattr(hostel,'beds_for_pre',[])
202        beds_for_returning = getattr(hostel,'beds_for_returning',[])
203        beds_for_final = getattr(hostel,'beds_for_final',[])
[6976]204        bed_string = u'%s_%s_%s' % (block, room_nr, bed)
[6974]205        if bt == 'reserved':
206            bt = 'all'
207            if bed in beds_for_fresh:
208                bt = 'fr'
209            elif bed in beds_for_pre:
210                bt = 'pr'
211            elif bed in beds_for_final:
212                bt = 'fi'
213            elif bed in beds_for_returning:
214                bt = 're'
215            bt = u'%s_%s_%s' % (sh, sex, bt)
[7718]216
[6991]217            # Comment of Martijn:
[8186]218            # If you have a non-Persistent subobject (like a list) and
219            # you change it, you need to manually flag the persistence
220            # machinery on the object that its subobject changed, with
221            # _p_changed. This is only necessary if some of the
222            # objects are not sublclasses of Persistent. For common
223            # built-in collections in Python such as list and
224            # dictionary there are replacements (PersistentList,
225            # PersistentMapping), and more advanced building blocks
[6991]226            # for indexes (BTrees), that don't have this issue.
[7718]227            #hostel._p_changed = True
228
229            # Henrik: Unfortunately, this doesn't work in our case.
230            # After restarting the portal,
231            # added or removed list items are gone. The only way to ensure
232            # persistance is to reassign the list attribute.
233            br = hostel.beds_reserved
234            br.remove(bed_string)
235            hostel.beds_reserved = br
236            message = _(u'unreserved')
237        else:
238            bt = u'%s_%s_reserved' % (sh, sex)
239            # See comment above
240            br = hostel.beds_reserved
241            br.append(bed_string)
242            hostel.beds_reserved = br
243            message = _(u'reserved')
[6974]244        self.bed_type = bt
[6988]245        return message
[6974]246
[7042]247    def releaseBed(self):
248        if self.owner == NOT_OCCUPIED:
[7070]249            return
[7042]250        else:
251            old_owner = self.owner
252            self.owner = NOT_OCCUPIED
253            notify(grok.ObjectModifiedEvent(self))
254            accommodation_session = grok.getSite()[
[8685]255                'hostels'].accommodation_session
[7042]256            try:
257                bedticket = grok.getSite()['students'][old_owner][
258                              'accommodation'][str(accommodation_session)]
259            except KeyError:
260                return '%s without bed ticket' % old_owner
261            bedticket.bed = None
[8183]262            tz = getUtility(IKofaUtils).tzinfo
[8234]263            timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
[8186]264            bedticket.bed_coordinates = u'-- booking cancelled on %s --' % (
265                timestamp,)
[7042]266            return old_owner
267
[6963]268    def loggerInfo(self, ob_class, comment=None):
269        target = self.__name__
270        return grok.getSite()['hostels'].logger_info(ob_class,target,comment)
271
272Bed = attrs_to_fields(Bed)
[7006]273
[9202]274class HostelFactory(grok.GlobalUtility):
275    """A factory for hostels.
276
277    We need this factory for the hostel processor.
278    """
279    grok.implements(IFactory)
280    grok.name(u'waeup.Hostel')
281    title = u"Create a new hostel.",
282    description = u"This factory instantiates new hostel instances."
283
284    def __call__(self, *args, **kw):
285        return Hostel()
286
287    def getInterfaces(self):
288        return implementedBy(Hostel)
289
290
[7006]291@grok.subscribe(IBedTicket, grok.IObjectRemovedEvent)
292def handle_bedticket_removed(bedticket, event):
293    """If a bed ticket is deleted, we make sure that also the owner attribute
294    of the bed is cleared (set to NOT_OCCUPIED).
295    """
[7068]296    if bedticket.bed != None:
297        bedticket.bed.owner = NOT_OCCUPIED
298        notify(grok.ObjectModifiedEvent(bedticket.bed))
[9202]299
Note: See TracBrowser for help on using the repository browser.