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

Last change on this file since 14625 was 13457, checked in by Henrik Bettermann, 9 years ago

Add option which allows students to select a desired hostel before booking accommodation.

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