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

Last change on this file since 16058 was 15705, checked in by Henrik Bettermann, 5 years ago

Add getAvailableBeds method.

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