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

Last change on this file since 13402 was 13352, checked in by Henrik Bettermann, 10 years ago

Adjust bed coordinate system if there are more than 99 rooms per floor.

  • Property svn:keywords set to Id
File size: 10.5 KB
Line 
1## $Id: hostel.py 13352 2015-10-26 17:36:32Z 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    @property
41    def bed_statistics(self):
42        total = len(self.keys())
43        booked = 0
44        for value in self.values():
45            if value.owner != NOT_OCCUPIED:
46                booked += 1
47        return {'booked':booked, 'total':total}
48
49    def clearHostel(self):
50        """Remove all beds
51        """
52        keys = [i for i in self.keys()] # create deep copy
53        for bed in keys:
54            del self[bed]
55        return
56
57    def addBed(self, bed):
58        """Add a bed.
59        """
60        if not IBed.providedBy(bed):
61            raise TypeError(
62                'Hostels contain only IBed instances')
63        self[bed.bed_id] = bed
64        return
65
66    def updateBeds(self):
67        """Fill hostel with beds or update beds.
68        """
69        added_counter = 0
70        modified_counter = 0
71        removed_counter = 0
72        modified_beds = u''
73
74        # Remove all empty beds. Occupied beds remain in hostel!
75        keys = list(self.keys()) # create list copy
76        for key in keys:
77            if self[key].owner == NOT_OCCUPIED:
78                del self[key]
79                self._p_changed = True
80                removed_counter += 1
81            else:
82                self[key].bed_number = 9999
83        remaining = len(keys) - removed_counter
84
85        blocks_for_female = getattr(self,'blocks_for_female',[])
86        blocks_for_male = getattr(self,'blocks_for_male',[])
87        beds_for_fresh = getattr(self,'beds_for_fresh',[])
88        beds_for_pre = getattr(self,'beds_for_pre',[])
89        beds_for_returning = getattr(self,'beds_for_returning',[])
90        beds_for_final = getattr(self,'beds_for_final',[])
91        beds_for_all = getattr(self,'beds_for_all',[])
92        all_blocks = blocks_for_female + blocks_for_male
93        all_beds = (beds_for_pre + beds_for_fresh +
94            beds_for_returning + beds_for_final + beds_for_all)
95        floor_base = 100
96        if self.rooms_per_floor > 99:
97            floor_base = 1000
98        for block in all_blocks:
99            sex = 'male'
100            if block in blocks_for_female:
101                sex = 'female'
102            for floor in range(1,int(self.floors_per_block)+1):
103                for room in range(1,int(self.rooms_per_floor)+1):
104                    for bed in all_beds:
105                        room_nr = floor*floor_base + room
106                        bt = 'all'
107                        if bed in beds_for_fresh:
108                            bt = 'fr'
109                        elif bed in beds_for_pre:
110                            bt = 'pr'
111                        elif bed in beds_for_final:
112                            bt = 'fi'
113                        elif bed in beds_for_returning:
114                            bt = 're'
115                        bt = u'%s_%s_%s' % (self.special_handling,sex,bt)
116                        uid = u'%s_%s_%d_%s' % (
117                            self.hostel_id,block,room_nr,bed)
118                        if uid in self:
119                            bed = self[uid]
120                            # Renumber remaining bed
121                            bed.bed_number = len(self) + 1 - remaining
122                            remaining -= 1
123                            if bed.bed_type != bt:
124                                bed.bed_type = bt
125                                modified_counter += 1
126                                modified_beds += '%s, ' % uid
127                                notify(grok.ObjectModifiedEvent(bed))
128                        else:
129                            bed = Bed()
130                            bed.bed_id = uid
131                            bed.bed_type = bt
132                            bed.bed_number = len(self) + 1 - remaining
133                            bed.owner = NOT_OCCUPIED
134                            self.addBed(bed)
135                            added_counter +=1
136        return removed_counter, added_counter, modified_counter, modified_beds
137
138    def writeLogMessage(self, view, message):
139        ob_class = view.__implemented__.__name__.replace('waeup.kofa.','')
140        self.__parent__.logger.info(
141            '%s - %s - %s' % (ob_class, self.__name__, message))
142        return
143
144Hostel = attrs_to_fields(Hostel)
145
146class Bed(grok.Container):
147    """This is a bed.
148    """
149    grok.implements(IBed)
150    grok.provides(IBed)
151
152    @property
153    def coordinates(self):
154        """Determine the coordinates from the bed_id.
155        """
156        return self.bed_id.split('_')
157
158    # The following property attributes are only needed
159    # for the exporter to ease evaluation with Excel.
160
161    @property
162    def hall(self):
163        return self.coordinates[0]
164
165    @property
166    def block(self):
167        return self.coordinates[1]
168
169    @property
170    def room(self):
171        return self.coordinates[2]
172
173    @property
174    def bed(self):
175        return self.coordinates[3]
176
177    @property
178    def special_handling(self):
179        return self.bed_type.split('_')[0]
180
181    @property
182    def sex(self):
183        return self.bed_type.split('_')[1]
184
185    @property
186    def bt(self):
187        return self.bed_type.split('_')[2]
188
189
190    def bookBed(self, student_id):
191        if self.owner == NOT_OCCUPIED:
192            self.owner = student_id
193            notify(grok.ObjectModifiedEvent(self))
194            return None
195        else:
196            return self.owner
197
198    def switchReservation(self):
199        """Reserves or unreserve bed respectively.
200        """
201        sh, sex, bt = self.bed_type.split('_')
202        hostel_id, block, room_nr, bed = self.coordinates
203        hostel = self.__parent__
204        beds_for_fresh = getattr(hostel,'beds_for_fresh',[])
205        beds_for_pre = getattr(hostel,'beds_for_pre',[])
206        beds_for_returning = getattr(hostel,'beds_for_returning',[])
207        beds_for_final = getattr(hostel,'beds_for_final',[])
208        bed_string = u'%s_%s_%s' % (block, room_nr, bed)
209        if bt == 'reserved':
210            bt = 'all'
211            if bed in beds_for_fresh:
212                bt = 'fr'
213            elif bed in beds_for_pre:
214                bt = 'pr'
215            elif bed in beds_for_final:
216                bt = 'fi'
217            elif bed in beds_for_returning:
218                bt = 're'
219            bt = u'%s_%s_%s' % (sh, sex, bt)
220            message = _(u'unreserved')
221        else:
222            bt = u'%s_%s_reserved' % (sh, sex)
223            message = _(u'reserved')
224        self.bed_type = bt
225        notify(grok.ObjectModifiedEvent(self))
226        return message
227
228    def releaseBed(self):
229        if self.owner == NOT_OCCUPIED:
230            return
231        old_owner = self.owner
232        self.owner = NOT_OCCUPIED
233        notify(grok.ObjectModifiedEvent(self))
234        accommodation_session = grok.getSite()[
235            'hostels'].accommodation_session
236        try:
237            bedticket = grok.getSite()['students'][old_owner][
238                          'accommodation'][str(accommodation_session)]
239        except KeyError:
240            return '%s without bed ticket' % old_owner
241        bedticket.bed = None
242        tz = getUtility(IKofaUtils).tzinfo
243        timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
244        bedticket.bed_coordinates = u'-- booking cancelled on %s --' % (
245            timestamp,)
246        return old_owner
247
248    def releaseBedIfMaintenanceNotPaid(self, n=7):
249        if self.owner == NOT_OCCUPIED:
250            return
251        accommodation_session = grok.getSite()[
252            'hostels'].accommodation_session
253        try:
254            bedticket = grok.getSite()['students'][self.owner][
255                          'accommodation'][str(accommodation_session)]
256        except KeyError:
257            return
258        if bedticket.maint_payment_made:
259            return
260        jetzt = datetime.utcnow()
261        days_ago = getattr(jetzt - bedticket.booking_date, 'days')
262        if days_ago > n:
263            old_owner = self.owner
264            self.owner = NOT_OCCUPIED
265            notify(grok.ObjectModifiedEvent(self))
266            bedticket.bed = None
267            tz = getUtility(IKofaUtils).tzinfo
268            timestamp = now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
269            bedticket.bed_coordinates = u'-- booking expired (%s) --' % (
270                timestamp,)
271            return old_owner
272        return
273
274    def writeLogMessage(self, view, message):
275        ob_class = view.__implemented__.__name__.replace('waeup.kofa.','')
276        self.__parent__.__parent__.logger.info(
277            '%s - %s - %s' % (ob_class, self.__name__, message))
278        return
279
280Bed = attrs_to_fields(Bed)
281
282class HostelFactory(grok.GlobalUtility):
283    """A factory for hostels.
284
285    We need this factory for the hostel processor.
286    """
287    grok.implements(IFactory)
288    grok.name(u'waeup.Hostel')
289    title = u"Create a new hostel.",
290    description = u"This factory instantiates new hostel instances."
291
292    def __call__(self, *args, **kw):
293        return Hostel()
294
295    def getInterfaces(self):
296        return implementedBy(Hostel)
297
298
299@grok.subscribe(IBedTicket, grok.IObjectRemovedEvent)
300def handle_bedticket_removed(bedticket, event):
301    """If a bed ticket is deleted, we make sure that also the owner attribute
302    of the bed is cleared (set to NOT_OCCUPIED).
303    """
304    if bedticket.bed != None:
305        bedticket.bed.owner = NOT_OCCUPIED
306        notify(grok.ObjectModifiedEvent(bedticket.bed))
307
Note: See TracBrowser for help on using the repository browser.