source: main/waeup.kofa/trunk/src/waeup/kofa/hostels/browser.py @ 9415

Last change on this file since 9415 was 9414, checked in by Henrik Bettermann, 12 years ago

Reorganize allocation of students to beds. We can't use the StudentSource? in live systems. The select box would be filled with ten thousands of students.

  • Property svn:keywords set to Id
File size: 12.8 KB
Line 
1## $Id: browser.py 9414 2012-10-25 09:44:02Z 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"""UI components for hostels and related components.
19"""
20import grok
21import sys
22from zope.i18n import translate
23from zope.component import getUtility
24from waeup.kofa.browser.layout import (
25    KofaEditFormPage, KofaAddFormPage, KofaDisplayFormPage,
26    NullValidator)
27from waeup.kofa.browser.breadcrumbs import Breadcrumb
28from waeup.kofa.browser.resources import datepicker, datatable, tabs, warning
29from waeup.kofa.browser.layout import default_primary_nav_template
30from waeup.kofa.browser.pages import delSubobjects
31from waeup.kofa.browser.viewlets import (
32    ManageActionButton, PrimaryNavTab)
33from waeup.kofa.browser.layout import jsaction, action
34from waeup.kofa.interfaces import IKofaObject, IKofaUtils
35from waeup.kofa.interfaces import MessageFactory as _
36from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
37from waeup.kofa.hostels.hostel import Hostel
38from waeup.kofa.hostels.interfaces import (
39    IHostelsContainer, IHostel, IBed)
40from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
41
42def write_log_message(view, message):
43    ob_class = view.__implemented__.__name__.replace('waeup.kofa.','')
44    view.context.loggerInfo(ob_class, message)
45    return
46
47# Save function used for save methods in manager pages
48def msave(view, **data):
49    changed_fields = view.applyData(view.context, **data)
50    # Turn list of lists into single list
51    if changed_fields:
52        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
53    fields_string = ' + '.join(changed_fields)
54    view.context._p_changed = True
55    view.flash(_('Form has been saved.'))
56    if fields_string:
57        write_log_message(view, 'saved: % s' % fields_string)
58    return
59
60class HostelsTab(PrimaryNavTab):
61    """Hostels tab in primary navigation.
62    """
63
64    grok.context(IKofaObject)
65    grok.order(5)
66    grok.require('waeup.viewHostels')
67    template = default_primary_nav_template
68    pnav = 5
69    tab_title = _(u'Hostels')
70
71    @property
72    def link_target(self):
73        return self.view.application_url('hostels')
74
75class HostelsBreadcrumb(Breadcrumb):
76    """A breadcrumb for the hostels container.
77    """
78    grok.context(IHostelsContainer)
79    title = _(u'Hostels')
80
81class HostelBreadcrumb(Breadcrumb):
82    """A breadcrumb for the hostel container.
83    """
84    grok.context(IHostel)
85
86    def title(self):
87        return self.context.hostel_name
88
89class BedBreadcrumb(Breadcrumb):
90    """A breadcrumb for the hostel container.
91    """
92    grok.context(IBed)
93
94    def title(self):
95        co = self.context.coordinates
96        return _('Block ${a}, Room ${b}, Bed ${c}',
97            mapping = {'a':co[1], 'b':co[2], 'c':co[3]})
98
99class HostelsContainerPage(KofaDisplayFormPage):
100    """The standard view for hostels containers.
101    """
102    grok.context(IHostelsContainer)
103    grok.name('index')
104    grok.require('waeup.viewHostels')
105    grok.template('containerpage')
106    label = _('Accommodation Section')
107    pnav = 5
108    form_fields = grok.AutoFields(IHostelsContainer)
109    form_fields[
110        'startdate'].custom_widget = FriendlyDatetimeDisplayWidget('le')
111    form_fields[
112        'enddate'].custom_widget = FriendlyDatetimeDisplayWidget('le')
113
114class HostelsContainerManageActionButton(ManageActionButton):
115    grok.order(1)
116    grok.context(IHostelsContainer)
117    grok.view(HostelsContainerPage)
118    grok.require('waeup.manageHostels')
119    text = _('Manage accommodation section')
120
121class HostelsContainerManagePage(KofaEditFormPage):
122    """The manage page for hostel containers.
123    """
124    grok.context(IHostelsContainer)
125    grok.name('manage')
126    grok.require('waeup.manageHostels')
127    grok.template('containermanagepage')
128    pnav = 5
129    label = _('Manage accommodation section')
130    form_fields = grok.AutoFields(IHostelsContainer)
131    taboneactions = [_('Save')]
132    tabtwoactions = [_('Add hostel'),
133        _('Clear all hostels'),
134        _('Remove selected')]
135
136    def update(self):
137        tabs.need()
138        self.tab1 = self.tab2 = self.tab3 = self.tab4 = ''
139        qs = self.request.get('QUERY_STRING', '')
140        if not qs:
141            qs = 'tab1'
142        setattr(self, qs, 'active')
143        warning.need()
144        datatable.need()
145        return super(HostelsContainerManagePage, self).update()
146
147    # It's quite dangerous to remove entire hostels with its content (beds).
148    # Thus, this remove method should be combined with an archiving function.
149    @jsaction(_('Remove selected'))
150    def delHostels(self, **data):
151        form = self.request.form
152        if form.has_key('val_id'):
153            deleted = []
154            child_id = form['val_id']
155            if not isinstance(child_id, list):
156                child_id = [child_id]
157            for id in child_id:
158                deleted.append(id)
159            write_log_message(self, 'deleted: % s' % ', '.join(deleted))
160        delSubobjects(self, redirect='@@manage', tab='2')
161        return
162
163    @action(_('Add hostel'), validator=NullValidator)
164    def addSubunit(self, **data):
165        self.redirect(self.url(self.context, 'addhostel'))
166        return
167
168    @jsaction(_('Clear all hostels'))
169    def clearHostels(self, **data):
170        self.context.clearAllHostels()
171        self.flash(_('All hostels cleared.'))
172        write_log_message(self, 'all hostels cleared')
173        self.redirect(self.url(self.context, '@@manage')+'?tab2')
174        return
175
176    @action(_('Save'), style='primary')
177    def save(self, **data):
178        self.applyData(self.context, **data)
179        self.flash(_('Settings have been saved.'))
180        return
181
182class HostelAddFormPage(KofaAddFormPage):
183    """Add-form to add a hostel.
184    """
185    grok.context(IHostelsContainer)
186    grok.require('waeup.manageHostels')
187    grok.name('addhostel')
188    #grok.template('hosteladdpage')
189    form_fields = grok.AutoFields(IHostel).omit('beds_reserved')
190    label = _('Add hostel')
191    pnav = 5
192
193    @action(_('Create hostel'))
194    def addHostel(self, **data):
195        hostel = Hostel()
196        self.applyData(hostel, **data)
197        hostel.hostel_id = data[
198            'hostel_name'].lower().replace(' ','-').replace('_','-')
199        try:
200            self.context.addHostel(hostel)
201        except KeyError:
202            self.flash(_('The hostel already exists.'))
203            return
204        self.flash(_('Hostel created.'))
205        write_log_message(self, 'added: % s' % data['hostel_name'])
206        self.redirect(self.url(self.context[hostel.hostel_id], 'index'))
207        return
208
209class HostelDisplayFormPage(KofaDisplayFormPage):
210    """ Page to display hostel data
211    """
212    grok.context(IHostel)
213    grok.name('index')
214    grok.require('waeup.viewHostels')
215    #grok.template('hostelpage')
216    pnav = 5
217
218    @property
219    def label(self):
220        return self.context.hostel_name
221
222class HostelManageActionButton(ManageActionButton):
223    grok.order(1)
224    grok.context(IHostel)
225    grok.view(HostelDisplayFormPage)
226    grok.require('waeup.manageHostels')
227    text = _('Manage')
228    target = 'manage'
229
230class HostelManageFormPage(KofaEditFormPage):
231    """ View to edit hostel data
232    """
233    grok.context(IHostel)
234    grok.name('manage')
235    grok.require('waeup.manageHostels')
236    form_fields = grok.AutoFields(IHostel).omit('hostel_id')
237    form_fields['beds_reserved'].for_display = True
238    grok.template('hostelmanagepage')
239    label = _('Manage hostel')
240    pnav = 5
241    taboneactions = [_('Save')]
242    tabtwoactions = [_('Update all beds'),
243        _('Switch reservation of selected beds'),
244        _('Release selected beds'),
245        _('Clear hostel')]
246    not_occupied = NOT_OCCUPIED
247
248    @property
249    def students_url(self):
250        return self.url(grok.getSite(),'students')
251
252    def update(self):
253        datepicker.need() # Enable jQuery datepicker in date fields.
254        tabs.need()
255        datatable.need()
256        warning.need()
257        self.tab1 = self.tab2 = ''
258        qs = self.request.get('QUERY_STRING', '')
259        if not qs:
260            qs = 'tab1'
261        setattr(self, qs, 'active')
262        super(HostelManageFormPage, self).update()
263        return
264
265    @action(_('Save'))
266    def save(self, **data):
267        msave(self, **data)
268        return
269
270    @action(_('Update all beds'))
271    def updateBeds(self, **data):
272        removed, added, modified, modified_beds = self.context.updateBeds()
273        message = '%d empty beds removed, %d beds added, %d occupied beds modified (%s)' % (
274            removed, added, modified, modified_beds)
275        flash_message = _(
276            '${a} empty beds removed, ${b} beds added, '
277            + '${c} occupied beds modified (${d})',
278            mapping = {'a':removed, 'b':added, 'c':modified, 'd':modified_beds})
279        self.flash(flash_message)
280        write_log_message(self, message)
281        self.redirect(self.url(self.context, '@@manage')+'?tab2')
282        return
283
284    @action(_('Switch reservation of selected beds'))
285    def switchReservations(self, **data):
286        form = self.request.form
287        if form.has_key('val_id'):
288            child_id = form['val_id']
289        else:
290            self.flash(_('No item selected.'))
291            self.redirect(self.url(self.context, '@@manage')+'?tab2')
292            return
293        if not isinstance(child_id, list):
294            child_id = [child_id]
295        switched = [] # for log file
296        switched_translated = [] # for flash message
297        # Here we know that the cookie has been set
298        preferred_language = self.request.cookies.get('kofa.language')
299        for bed_id in child_id:
300            message = self.context[bed_id].switchReservation()
301            switched.append('%s (%s)' % (bed_id,message))
302            m_translated = translate(message, 'waeup.kofa',
303                target_language=preferred_language)
304            switched_translated.append('%s (%s)' % (bed_id,m_translated))
305        if len(switched):
306            message = ', '.join(switched)
307            m_translated = ', '.join(switched_translated)
308            self.flash(_('Successfully switched beds: ${a}',
309                mapping = {'a':m_translated}))
310            write_log_message(self, 'switched: %s' % message)
311            self.redirect(self.url(self.context, '@@manage')+'?tab2')
312        return
313
314    @action(_('Release selected beds'))
315    def releaseBeds(self, **data):
316        form = self.request.form
317        if form.has_key('val_id'):
318            child_id = form['val_id']
319        else:
320            self.flash(_('No item selected.'))
321            self.redirect(self.url(self.context, '@@manage')+'?tab2')
322            return
323        if not isinstance(child_id, list):
324            child_id = [child_id]
325        released = []
326        for bed_id in child_id:
327            message = self.context[bed_id].releaseBed()
328            if message:
329                released.append('%s (%s)' % (bed_id,message))
330        if len(released):
331            message = ', '.join(released)
332            self.flash(_('Successfully released beds: ${a}',
333                mapping = {'a':message}))
334            write_log_message(self, 'released: %s' % message)
335            self.redirect(self.url(self.context, '@@manage')+'?tab2')
336        else:
337            self.flash(_('No allocated bed selected.'))
338            self.redirect(self.url(self.context, '@@manage')+'?tab2')
339        return
340
341    @jsaction(_('Clear hostel'))
342    def clearHostel(self, **data):
343        self.context.clearHostel()
344        self.flash(_('Hostel cleared.'))
345        write_log_message(self, 'cleared')
346        self.redirect(self.url(self.context, '@@manage')+'?tab2')
347        return
348
349class BedManageFormPage(KofaEditFormPage):
350    """ View to edit bed data
351    """
352    grok.context(IBed)
353    grok.name('index')
354    grok.require('waeup.manageHostels')
355    form_fields = grok.AutoFields(IBed).omit(
356        'bed_id', 'bed_number', 'bed_type')
357    label = _('Allocate student')
358    pnav = 5
359
360    @action(_('Save'))
361    def save(self, **data):
362        msave(self, **data)
363        self.redirect(self.url(self.context.__parent__, '@@manage')+'?tab2')
364        return
365
366    def update(self):
367        if self.context.owner != NOT_OCCUPIED:
368            # Don't use this form for exchanging students.
369            # Beds must be released first before they can be allocated to
370            # other students.
371            self.redirect(self.url(self.context.__parent__, '@@manage')+'?tab2')
372        return
Note: See TracBrowser for help on using the repository browser.