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

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

Next try (without tests!)

  • Property svn:keywords set to Id
File size: 7.4 KB
Line 
1## $Id: accommodation.py 15310 2019-01-28 13:29: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        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            if available_beds:
141                new_bed = students_utils.selectBed(
142                    available_beds, self.__parent__.desired_hostel)
143                if new_bed is None:
144                    return False, _(
145                        'There is no free bed in your desired hostel.')
146                new_bed.bookBed(student.student_id)
147            else:
148                return False, _('There is no free bed in your category ${a}.',
149                                 mapping = {'a':acc_details['bt']})
150        # Release old bed if exists
151        if self.bed != None:
152            self.bed.owner = NOT_OCCUPIED
153            notify(grok.ObjectModifiedEvent(self.bed))
154        # Designate new bed in ticket
155        self.bed_type = acc_details['bt']
156        self.bed = new_bed
157        hall_title = new_bed.__parent__.hostel_name
158        coordinates = new_bed.coordinates[1:]
159        block, room_nr, bed_nr = coordinates
160        bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = {
161            'a':hall_title, 'b':block,
162            'c':room_nr, 'd':bed_nr,
163            'e':new_bed.bed_type})
164        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
165        self.bed_coordinates = translate(
166            bc, 'waeup.kofa',target_language=portal_language)
167        self.writeLogMessage(self, 'relocated: %s' % new_bed.bed_id)
168        return True, _('Student relocated: ${a}',
169                 mapping = {'a':self.display_coordinates})
170
171    def writeLogMessage(self, view, message):
172        return self.__parent__.__parent__.writeLogMessage(view, message)
173
174    def getSessionString(self):
175        return academic_sessions_vocab.getTerm(
176            self.booking_session).title
177
178BedTicket = attrs_to_fields(BedTicket, omit=['display_coordinates'])
179
180
181# Bed tickets must be importable. So we might need a factory.
182class BedTicketFactory(grok.GlobalUtility):
183    """A factory for bed tickets.
184    """
185    grok.implements(IFactory)
186    grok.name(u'waeup.BedTicket')
187    title = u"Create a new bed ticket.",
188    description = u"This factory instantiates new bed ticket instances."
189
190    def __call__(self, *args, **kw):
191        return BedTicket()
192
193    def getInterfaces(self):
194        return implementedBy(BedTicket)
Note: See TracBrowser for help on using the repository browser.