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
Line 
1## $Id: hostel.py 9414 2012-10-25 09:44:02Z henrik $
2##
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
22from zope.event import notify
23from zope.component import getUtility
24from zope.component.interfaces import IFactory
25from datetime import datetime
26from waeup.kofa.utils.helpers import attrs_to_fields
27from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
28from waeup.kofa.hostels.interfaces import IHostel, IBed
29from waeup.kofa.students.interfaces import IBedTicket
30from waeup.kofa.interfaces import IKofaUtils
31from waeup.kofa.interfaces import MessageFactory as _
32from waeup.kofa.utils.helpers import now
33
34class Hostel(grok.Container):
35    """This is a hostel.
36    """
37    grok.implements(IHostel)
38    grok.provides(IHostel)
39
40    def loggerInfo(self, ob_class, comment=None):
41        target = self.__name__
42        return grok.getSite()['hostels'].logger_info(ob_class,target,comment)
43
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):
54        """Remove all beds
55        """
56        keys = [i for i in self.keys()] # create deep copy
57        for bed in keys:
58            del self[bed]
59        return
60
61    def addBed(self, bed):
62        """Add a bed.
63        """
64        if not IBed.providedBy(bed):
65            raise TypeError(
66                'Hostels contain only IBed instances')
67        self[bed.bed_id] = bed
68        return
69
70    def updateBeds(self):
71        """Fill hostel with beds or update beds.
72        """
73        added_counter = 0
74        modified_counter = 0
75        removed_counter = 0
76        modified_beds = u''
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
85            else:
86                self[key].bed_number = 9999
87        remaining = len(keys) - removed_counter
88
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',[])
95        beds_for_all = getattr(self,'beds_for_all',[])
96        beds_reserved = getattr(self,'beds_reserved',[])
97        all_blocks = blocks_for_female + blocks_for_male
98        all_beds = (beds_for_pre + beds_for_fresh +
99            beds_for_returning + beds_for_final + beds_for_all)
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'
109                        if '%s_%s_%s' % (block,room_nr,bed) in beds_reserved:
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'
119                        bt = u'%s_%s_%s' % (self.special_handling,sex,bt)
120                        uid = u'%s_%s_%d_%s' % (self.hostel_id,block,room_nr,bed)
121                        if self.has_key(uid):
122                            bed = self[uid]
123                            # Renumber remaining beds
124                            bed.bed_number = len(self) + 1 - remaining
125                            remaining -= 1
126                            if bed.bed_type != bt:
127                                bed.bed_type = bt
128                                modified_counter += 1
129                                modified_beds += '%s ' % uid
130                        else:
131                            bed = Bed()
132                            bed.bed_id = uid
133                            bed.bed_type = bt
134                            bed.bed_number = len(self) + 1 - remaining
135                            bed.owner = NOT_OCCUPIED
136                            self.addBed(bed)
137                            added_counter +=1
138        return removed_counter, added_counter, modified_counter, modified_beds
139
140Hostel = attrs_to_fields(Hostel)
141
142class Bed(grok.Container):
143    """This is a bed.
144    """
145    grok.implements(IBed)
146    grok.provides(IBed)
147
148    @property
149    def coordinates(self):
150        """Determine the coordinates from the bed_id.
151        """
152        return self.bed_id.split('_')
153
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
186    def bookBed(self, student_id):
187        if self.owner == NOT_OCCUPIED:
188            self.owner = student_id
189            notify(grok.ObjectModifiedEvent(self))
190            return None
191        else:
192            return self.owner
193
194    def switchReservation(self):
195        """Reserves or unreserve bed respectively.
196        """
197        sh, sex, bt = self.bed_type.split('_')
198        hostel_id, block, room_nr, bed = self.coordinates
199        hostel = self.__parent__
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',[])
204        bed_string = u'%s_%s_%s' % (block, room_nr, bed)
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)
216
217            # Comment of Martijn:
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
226            # for indexes (BTrees), that don't have this issue.
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')
244        self.bed_type = bt
245        return message
246
247    def releaseBed(self):
248        if self.owner == NOT_OCCUPIED:
249            return
250        else:
251            old_owner = self.owner
252            self.owner = NOT_OCCUPIED
253            notify(grok.ObjectModifiedEvent(self))
254            accommodation_session = grok.getSite()[
255                'hostels'].accommodation_session
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
262            tz = getUtility(IKofaUtils).tzinfo
263            timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
264            bedticket.bed_coordinates = u'-- booking cancelled on %s --' % (
265                timestamp,)
266            return old_owner
267
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)
273
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
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    """
296    if bedticket.bed != None:
297        bedticket.bed.owner = NOT_OCCUPIED
298        notify(grok.ObjectModifiedEvent(bedticket.bed))
299
Note: See TracBrowser for help on using the repository browser.