source: main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py @ 7439

Last change on this file since 7439 was 7369, checked in by Henrik Bettermann, 13 years ago

Implement ChangePasswordRequestPage?.

Catch traceback, if student data are not complete and a test student presses the 'Book accommodation' button (regression test needed).

  • Property svn:keywords set to Id
File size: 12.6 KB
Line 
1## $Id: utils.py 7369 2011-12-18 08:24:04Z 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"""General helper functions and utilities for the student section.
19"""
20import grok
21from random import SystemRandom as r
22from datetime import datetime
23from reportlab.pdfgen import canvas
24from reportlab.lib import colors
25from reportlab.lib.units import cm
26from reportlab.lib.enums import TA_RIGHT
27from reportlab.lib.pagesizes import A4
28from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
29from reportlab.platypus import (Frame, Paragraph, Image,
30    Table, Spacer)
31from reportlab.platypus.tables import TableStyle
32from reportlab.platypus.flowables import PageBreak
33from zope.component import getUtility
34from zope.formlib.form import setUpEditWidgets
35from waeup.sirp.interfaces import IExtFileStore
36from waeup.sirp.students.interfaces import IStudentsUtils
37
38SLIP_STYLE = [
39    ('VALIGN',(0,0),(-1,-1),'TOP'),
40    #('FONT', (0,0), (-1,-1), 'Helvetica', 11),
41    ]
42
43CONTENT_STYLE = [
44    ('VALIGN',(0,0),(-1,-1),'TOP'),
45    #('FONT', (0,0), (-1,-1), 'Helvetica', 8),
46    #('TEXTCOLOR',(0,0),(-1,0),colors.white),
47    ('BACKGROUND',(0,0),(-1,0),colors.black),
48    ]
49
50FONT_SIZE = 10
51FONT_COLOR = 'black'
52
53def formatted_label(color=FONT_COLOR, size=FONT_SIZE):
54    tag1 ='<font color=%s size=%d>' % (color, size)
55    return tag1 + '%s:</font>'
56
57def formatted_text(color=FONT_COLOR, size=FONT_SIZE):
58    tag1 ='<font color=%s size=%d>' % (color, size)
59    return tag1 + '%s</font>'
60
61def generate_student_id(students,letter):
62    if letter == '?':
63        letter= r().choice('ABCDEFGHKLMNPQRSTUVWXY')
64    sid = u"%c%d" % (letter,r().randint(99999,1000000))
65    while sid in students.keys():
66        sid = u"%c%d" % (letter,r().randint(99999,1000000))
67    return sid
68
69def set_returning_data(student):
70    student['studycourse'].current_level += 100
71    student['studycourse'].current_session += 1
72    verdict = student['studycourse'].current_verdict
73    student['studycourse'].current_verdict = '0'
74    student['studycourse'].previous_verdict = verdict
75    return
76
77def set_up_widgets(view, ignore_request=False):
78    view.adapters = {}
79    view.widgets = setUpEditWidgets(
80        view.form_fields, view.prefix, view.context, view.request,
81        adapters=view.adapters, for_display=True,
82        ignore_request=ignore_request
83        )
84
85def render_student_data(studentview):
86    """Render student table for an existing frame.
87    """
88    width, height = A4
89    set_up_widgets(studentview, ignore_request=True)
90    data_left = []
91    data_right = []
92    style = getSampleStyleSheet()
93    img = getUtility(IExtFileStore).getFileByContext(
94        studentview.context, attr='passport.jpg')
95    if img is None:
96        from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH
97        img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb')
98    doc_img = Image(img.name, width=4*cm, height=4*cm, kind='bound')
99    data_left.append([doc_img])
100    #data.append([Spacer(1, 12)])
101    for widget in studentview.widgets:
102        if widget.name == 'form.adm_code':
103            continue
104        f_label = formatted_label() % widget.label.strip()
105        f_label = Paragraph(f_label, style["Normal"])
106        f_text = formatted_text() % widget()
107        f_text = Paragraph(f_text, style["Normal"])
108        data_right.append([f_label,f_text])
109    table_left = Table(data_left,style=SLIP_STYLE)
110    table_right = Table(data_right,style=SLIP_STYLE, colWidths=[4*cm, 10*cm])
111    table = Table([[table_left, table_right],],style=SLIP_STYLE)
112    return table
113
114def render_table_data(tableheader,tabledata):
115    """Render children table for an existing frame.
116    """
117    data = []
118    #data.append([Spacer(1, 12)])
119    line = []
120    style = getSampleStyleSheet()
121    for element in tableheader:
122        field = formatted_text(color='white') % element[0]
123        field = Paragraph(field, style["Normal"])
124        line.append(field)
125    data.append(line)
126    for ticket in tabledata:
127        line = []
128        for element in tableheader:
129              field = formatted_text() % str(getattr(ticket,element[1],u' '))
130              field = Paragraph(field, style["Normal"])
131              line.append(field)
132        data.append(line)
133    table = Table(data,colWidths=[
134        element[2]*cm for element in tableheader], style=CONTENT_STYLE)
135    return table
136
137
138def insert_footer(pdf,width,style,text=None, number_of_pages=1):
139      """Render the whole footer frame.
140      """
141      story = []
142      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
143      timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
144      left_text = '<font size=10>%s</font>' % timestamp
145      story.append(Paragraph(left_text, style["Normal"]))
146      frame_footer.addFromList(story,pdf)
147      story = []
148      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
149      right_text = '<font size=10>%s Page %s of %s</font>' % (
150          text, pdf.getPageNumber(), number_of_pages)
151      story.append(Paragraph(right_text, style["Right"]))
152      frame_footer.addFromList(story,pdf)
153
154class StudentsUtils(grok.GlobalUtility):
155    """A collection of methods subject to customization.
156    """
157    grok.implements(IStudentsUtils)
158
159    def getPaymentDetails(self,category, student):
160        d = {}
161        d['p_item'] = u''
162        d['amount'] = 0
163        d['error'] = u''
164        d['p_session'] = student['studycourse'].current_session
165        session = str(d['p_session'])
166        try:
167            academic_session = grok.getSite()['configuration'][session]
168        except KeyError:
169            d['error'] = u'Session configuration object is not available.'
170            return d
171        d['surcharge_1'] = academic_session.surcharge_1
172        d['surcharge_2'] = academic_session.surcharge_2
173        d['surcharge_3'] = academic_session.surcharge_3
174        if category == 'schoolfee':
175            d['amount'] = academic_session.school_fee_base
176            d['p_item'] = student['studycourse'].certificate.code
177        elif category == 'clearance':
178            d['p_item'] = student['studycourse'].certificate.code
179            d['amount'] = academic_session.clearance_fee
180        elif category == 'bed_allocation':
181            d['p_item'] = self.getAccommodationDetails(student)['bt']
182            d['amount'] = academic_session.booking_fee
183        return d
184
185    def getAccommodationDetails(self, student):
186        d = {}
187        d['error'] = u''
188        site_configuration = grok.getSite()['configuration']
189        d['booking_session'] = site_configuration.accommodation_session
190        d['allowed_states'] = site_configuration.accommodation_states
191        # Determine bed type
192        studycourse = student['studycourse']
193        certificate = getattr(studycourse,'certificate',None)
194        entry_session = studycourse.entry_session
195        current_level = studycourse.current_level
196        if not (entry_session and current_level and certificate):
197            return
198        end_level = certificate.end_level
199        if entry_session == grok.getSite()['configuration'].accommodation_session:
200            bt = 'fr'
201        elif current_level >= end_level:
202            bt = 'fi'
203        else:
204            bt = 're'
205        if student.sex == 'f':
206            sex = 'female'
207        else:
208            sex = 'male'
209        special_handling = 'regular'
210        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
211        return d
212
213    # In the standard configuration we select the first bed found,
214    # but can also randomize the selection if we like.
215    def selectBed(self, available_beds):
216        return available_beds[0]
217
218    def renderPDF(self, view, filename='slip.pdf',
219        student=None, studentview=None, tableheader=None, tabledata=None):
220        # (0,0),(-1,-1) = whole table
221        # (0,0),(0,-1) = first column
222        # (-1,0),(-1,-1) = last column
223        # (0,0),(-1,0) = first row
224        # (0,-1),(-1,-1) = last row
225
226        pdf = canvas.Canvas(filename,pagesize=A4)
227        pdf.setTitle(view.label)
228        pdf.setSubject(view.title)
229        pdf.setAuthor('%s (%s)' % (view.request.principal.title,
230            view.request.principal.id))
231        pdf.setCreator('WAeUP SIRP')
232        width, height = A4
233        footer_text = view.label
234        if student is not None:
235            footer_text = "%s - %s - " % (student.student_id, footer_text)
236        style = getSampleStyleSheet()
237        style.add(ParagraphStyle(name='Right', alignment=TA_RIGHT))
238        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
239
240        story = []
241        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
242        header_title = getattr(grok.getSite(), 'name', u'Sample University')
243        story.append(Paragraph(header_title, style["Heading1"]))
244        frame_header.addFromList(story,pdf)
245
246        # Insert student data table
247        story = []
248        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
249        story.append(Paragraph(view.label, style["Heading2"]))
250        if student is not None:
251            #story.append(Spacer(1, 12))
252            story.append(Paragraph('Student Base Data', style["Heading3"]))
253            studenttable = render_student_data(studentview)
254            story.append(studenttable)
255
256        # Insert widgets
257        story.append(Paragraph(view.title, style["Heading3"]))
258        set_up_widgets(view)
259        data = []
260        for widget in view.widgets:
261            f_label = formatted_label() % widget.label.strip()
262            f_label = Paragraph(f_label, style["Normal"])
263            f_text = formatted_text() % str(widget())
264            f_text = Paragraph(f_text, style["Normal"])
265            data.append([f_label,f_text])
266        table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
267        story.append(table)
268
269        # Import browser components locally
270        # to avoid circular import
271        from waeup.sirp.students.viewlets import FileManager
272        from waeup.sirp.browser import DEFAULT_IMAGE_PATH
273        data = []
274        # Collect viewlets
275        fm = FileManager(view.context, view.request, view)
276        fm.update()
277        if fm.viewlets:
278            story.append(Paragraph('Scanned Documents', style["Heading3"]))
279            # Insert list of scanned documents
280            for viewlet in fm.viewlets:
281                f_label = formatted_label() % viewlet.label
282                f_label = Paragraph(f_label, style["Normal"])
283                if viewlet.template.__grok_name__ == 'imagedisplay':
284                    # In case we define FileDisplay viewlets with
285                    # an imagedisplay template in the customization package
286                    img = getUtility(IExtFileStore).getFileByContext(
287                        view.context, attr=viewlet.download_name)
288                    if img is None:
289                        img = open(DEFAULT_IMAGE_PATH, 'rb')
290                    doc_img = Image(img.name, width=2*cm, height=1*cm, kind='bound')
291                    data.append([f_label,doc_img])
292                else:
293                    f_text = formatted_text() % viewlet.title
294                    f_text = Paragraph(f_text, style["Normal"])
295                    data.append([f_label,f_text])
296            table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
297            story.append(table)
298
299        try:
300            frame_body.addFromList(story,pdf)
301        except IOError:
302            view.flash('Error in image file.')
303            return view.redirect(view.url(view.context))
304
305        if tabledata and tableheader:
306            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
307        else:
308            insert_footer(pdf,width,style,footer_text)
309
310        # Insert content table on second page
311        if tabledata and tableheader:
312            pdf.showPage()
313            frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(2*cm))
314            story = []
315            story.append(Paragraph(view.content_title, style["Heading3"]))
316            #story.append(Spacer(1, 12))
317            contenttable = render_table_data(tableheader,tabledata)
318            story.append(contenttable)
319            frame_body.addFromList(story,pdf)
320            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
321
322        view.response.setHeader(
323            'Content-Type', 'application/pdf')
324        return pdf.getpdfdata()
Note: See TracBrowser for help on using the repository browser.