source: main/waeup.kofa/trunk/src/waeup/kofa/students/accommodation.py @ 15309

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

Emergency fix: Remove expired bed ticket before adding a new one in same session.

  • Property svn:keywords set to Id
File size: 7.3 KB
Line 
1## $Id: accommodation.py 15309 2019-01-28 13:21:48Z 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"""
19Student accommodation components.
20"""
21from datetime import datetime
22import grok
23from zope.component import getUtility, queryUtility
24from zope.component.interfaces import IFactory
25from zope.event import notify
26from zope.i18n import translate
27from zope.catalog.interfaces import ICatalog
28from zope.interface import implementedBy
29from waeup.kofa.interfaces import academic_sessions_vocab, IKofaUtils
30from waeup.kofa.interfaces import MessageFactory as _
31from waeup.kofa.students.interfaces import (
32    IStudentAccommodation, IStudentNavigation, IBedTicket, IStudentsUtils)
33from waeup.kofa.utils.helpers import attrs_to_fields
34from waeup.kofa.hostels.hostel import NOT_OCCUPIED
35
36
37class StudentAccommodation(grok.Container):
38    """This is a container for bed tickets.
39    """
40    grok.implements(IStudentAccommodation, IStudentNavigation)
41    grok.provides(IStudentAccommodation)
42
43    def __init__(self):
44        super(StudentAccommodation, self).__init__()
45        return
46
47    def addBedTicket(self, bedticket):
48        """Add a bed ticket object.
49        """
50        if not IBedTicket.providedBy(bedticket):
51            raise TypeError(
52                'StudentAccommodation containers contain only IBedTicket instances')
53        try:
54            self[str(bedticket.booking_session)] = bedticket
55        except KeyError:
56            if 'booking expired' in self[
57                    str(bedticket.booking_session).bed_coordinates]:
58                del self[str(bedticket.booking_session)]
59        return
60
61    @property
62    def student(self):
63        return self.__parent__
64
65    def writeLogMessage(self, view, message):
66        return self.__parent__.writeLogMessage(view, message)
67
68StudentAccommodation = attrs_to_fields(StudentAccommodation)
69
70class BedTicket(grok.Model):
71    """This is a bed ticket which shows that the student has booked a bed.
72    """
73    grok.implements(IBedTicket, IStudentNavigation)
74    grok.provides(IBedTicket)
75
76    def __init__(self):
77        super(BedTicket, self).__init__()
78        self.booking_date = datetime.utcnow()
79        self.bed = None
80        return
81
82    @property
83    def student(self):
84        try:
85            return self.__parent__.__parent__
86        except AttributeError:
87            return None
88
89    @property
90    def display_coordinates(self):
91        students_utils = getUtility(IStudentsUtils)
92        return students_utils.getBedCoordinates(self)
93
94    @property
95    def maint_payment_made(self):
96        try:
97            if len(self.student['payments']):
98                for ticket in self.student['payments'].values():
99                    if ticket.p_category == 'hostel_maintenance' and \
100                        ticket.p_session == self.booking_session and \
101                        ticket.p_state == 'paid':
102                            return True
103        except TypeError: # in unit tests
104            pass
105        return False
106
107    def relocateStudent(self):
108        """Relocate student if student parameters have changed or the bed_type
109        of the bed has changed.
110        """
111        if self.booking_session != grok.getSite()[
112            'hostels'].accommodation_session:
113            return False, _("Previous session bookings can't be changed.")
114        student = self.student
115        students_utils = getUtility(IStudentsUtils)
116        acc_details  = students_utils.getAccommodationDetails(student)
117        if self.bed != None and \
118              'reserved' in self.bed.bed_type:
119            return False, _("Students in reserved beds can't be relocated.")
120        if acc_details['bt'] == self.bed_type and \
121                self.bed != None and \
122                self.bed.bed_type == self.bed_type and \
123                self.bed.__parent__.__parent__:
124            return False, _("Student can't be relocated.")
125        # Search a bed
126        cat = queryUtility(ICatalog, name='beds_catalog', default=None)
127        entries = cat.searchResults(
128            owner=(student.student_id,student.student_id))
129        if len(entries) and self.bed == None:
130            # If booking has been cancelled but other bed space has been
131            # manually allocated after cancellation use this bed
132            new_bed = [entry for entry in entries][0]
133        else:
134            # Search for other available beds
135            entries = cat.searchResults(
136                bed_type=(acc_details['bt'],acc_details['bt']))
137            available_beds = [
138                entry for entry in entries if entry.owner == NOT_OCCUPIED]
139            if available_beds:
140                new_bed = students_utils.selectBed(
141                    available_beds, self.__parent__.desired_hostel)
142                if new_bed is None:
143                    return False, _(
144                        'There is no free bed in your desired hostel.')
145                new_bed.bookBed(student.student_id)
146            else:
147                return False, _('There is no free bed in your category ${a}.',
148                                 mapping = {'a':acc_details['bt']})
149        # Release old bed if exists
150        if self.bed != None:
151            self.bed.owner = NOT_OCCUPIED
152            notify(grok.ObjectModifiedEvent(self.bed))
153        # Designate new bed in ticket
154        self.bed_type = acc_details['bt']
155        self.bed = new_bed
156        hall_title = new_bed.__parent__.hostel_name
157        coordinates = new_bed.coordinates[1:]
158        block, room_nr, bed_nr = coordinates
159        bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = {
160            'a':hall_title, 'b':block,
161            'c':room_nr, 'd':bed_nr,
162            'e':new_bed.bed_type})
163        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
164        self.bed_coordinates = translate(
165            bc, 'waeup.kofa',target_language=portal_language)
166        self.writeLogMessage(self, 'relocated: %s' % new_bed.bed_id)
167        return True, _('Student relocated: ${a}',
168                 mapping = {'a':self.display_coordinates})
169
170    def writeLogMessage(self, view, message):
171        return self.__parent__.__parent__.writeLogMessage(view, message)
172
173    def getSessionString(self):
174        return academic_sessions_vocab.getTerm(
175            self.booking_session).title
176
177BedTicket = attrs_to_fields(BedTicket, omit=['display_coordinates'])
178
179
180# Bed tickets must be importable. So we might need a factory.
181class BedTicketFactory(grok.GlobalUtility):
182    """A factory for bed tickets.
183    """
184    grok.implements(IFactory)
185    grok.name(u'waeup.BedTicket')
186    title = u"Create a new bed ticket.",
187    description = u"This factory instantiates new bed ticket instances."
188
189    def __call__(self, *args, **kw):
190        return BedTicket()
191
192    def getInterfaces(self):
193        return implementedBy(BedTicket)
Note: See TracBrowser for help on using the repository browser.