source: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py @ 7871

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

Reduce dictionaries in base package. They will be part of the custom package.

  • Property svn:keywords set to Id
File size: 15.1 KB
Line 
1## $Id: utils.py 7843 2012-03-12 11:19:57Z 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 zope.i18n import translate
24from zope.component import getUtility
25from reportlab.pdfgen import canvas
26from reportlab.lib import colors
27from reportlab.lib.units import cm
28from reportlab.lib.enums import TA_RIGHT
29from reportlab.lib.pagesizes import A4
30from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
31from reportlab.platypus import (Frame, Paragraph, Image,
32    Table, Spacer)
33from reportlab.platypus.tables import TableStyle
34from reportlab.platypus.flowables import PageBreak
35from zope.component import getUtility
36from zope.formlib.form import setUpEditWidgets
37from waeup.kofa.interfaces import IExtFileStore, IKofaUtils
38from waeup.kofa.interfaces import MessageFactory as _
39from waeup.kofa.students.interfaces import IStudentsUtils
40
41SLIP_STYLE = [
42    ('VALIGN',(0,0),(-1,-1),'TOP'),
43    #('FONT', (0,0), (-1,-1), 'Helvetica', 11),
44    ]
45
46CONTENT_STYLE = [
47    ('VALIGN',(0,0),(-1,-1),'TOP'),
48    #('FONT', (0,0), (-1,-1), 'Helvetica', 8),
49    #('TEXTCOLOR',(0,0),(-1,0),colors.white),
50    ('BACKGROUND',(0,0),(-1,0),colors.black),
51    ]
52
53FONT_SIZE = 10
54FONT_COLOR = 'black'
55
56def formatted_label(color=FONT_COLOR, size=FONT_SIZE):
57    tag1 ='<font color=%s size=%d>' % (color, size)
58    return tag1 + '%s:</font>'
59
60def formatted_text(text, color=FONT_COLOR, size=FONT_SIZE):
61    """Turn `text`, `color` and `size` into an HTML snippet.
62
63    The snippet is suitable for use with reportlab and generating PDFs.
64    Wraps the `text` into a ``<font>`` tag with passed attributes.
65
66    Also non-strings are converted. Raw strings are expected to be
67    utf-8 encoded (usually the case for widgets etc.).
68
69    Finally, a br tag is added if widgets contain div tags
70    which are not supported by reportlab.
71
72    The returned snippet is unicode type.
73    """
74    if not isinstance(text, unicode):
75        if isinstance(text, basestring):
76            text = text.decode('utf-8')
77        else:
78            text = unicode(text)
79    text = text.replace('</div>', '<br /></div>')
80    tag1 = u'<font color="%s" size="%d">' % (color, size)
81    return tag1 + u'%s</font>' % text
82
83def generate_student_id(students,letter):
84    if letter == '?':
85        letter= r().choice('ABCDEFGHKLMNPQRSTUVWXY')
86    sid = u"%c%d" % (letter,r().randint(99999,1000000))
87    while sid in students.keys():
88        sid = u"%c%d" % (letter,r().randint(99999,1000000))
89    return sid
90
91def set_up_widgets(view, ignore_request=False):
92    view.adapters = {}
93    view.widgets = setUpEditWidgets(
94        view.form_fields, view.prefix, view.context, view.request,
95        adapters=view.adapters, for_display=True,
96        ignore_request=ignore_request
97        )
98
99def render_student_data(studentview):
100    """Render student table for an existing frame.
101    """
102    width, height = A4
103    set_up_widgets(studentview, ignore_request=True)
104    data_left = []
105    data_right = []
106    style = getSampleStyleSheet()
107    img = getUtility(IExtFileStore).getFileByContext(
108        studentview.context, attr='passport.jpg')
109    if img is None:
110        from waeup.kofa.browser import DEFAULT_PASSPORT_IMAGE_PATH
111        img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb')
112    doc_img = Image(img.name, width=4*cm, height=4*cm, kind='bound')
113    data_left.append([doc_img])
114    #data.append([Spacer(1, 12)])
115    portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
116    for widget in studentview.widgets:
117        if widget.name == 'form.adm_code':
118            continue
119        f_label = formatted_label(size=12) % translate(
120            widget.label.strip(), 'waeup.kofa',
121            target_language=portal_language)
122        f_label = Paragraph(f_label, style["Normal"])
123        f_text = formatted_text(widget(), size=12)
124        f_text = Paragraph(f_text, style["Normal"])
125        data_right.append([f_label,f_text])
126    table_left = Table(data_left,style=SLIP_STYLE)
127    table_right = Table(data_right,style=SLIP_STYLE, colWidths=[6*cm, 8*cm])
128    table = Table([[table_left, table_right],],style=SLIP_STYLE)
129    return table
130
131def render_table_data(tableheader,tabledata):
132    """Render children table for an existing frame.
133    """
134    data = []
135    #data.append([Spacer(1, 12)])
136    line = []
137    style = getSampleStyleSheet()
138    for element in tableheader:
139        field = formatted_text(element[0], color='white')
140        field = Paragraph(field, style["Normal"])
141        line.append(field)
142    data.append(line)
143    for ticket in tabledata:
144        line = []
145        for element in tableheader:
146              field = formatted_text(getattr(ticket,element[1],u' '))
147              field = Paragraph(field, style["Normal"])
148              line.append(field)
149        data.append(line)
150    table = Table(data,colWidths=[
151        element[2]*cm for element in tableheader], style=CONTENT_STYLE)
152    return table
153
154
155def insert_footer(pdf,width,style,text=None, number_of_pages=1):
156      """Render the whole footer frame.
157      """
158      story = []
159      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
160      timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
161      left_text = '<font size=10>%s</font>' % timestamp
162      story.append(Paragraph(left_text, style["Normal"]))
163      frame_footer.addFromList(story,pdf)
164      story = []
165      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
166      portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
167      right_text = translate(_('<font size=10>${a} Page ${b} of ${c}</font>',
168          mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}),
169          'waeup.kofa', target_language=portal_language)
170      story.append(Paragraph(right_text, style["Right"]))
171      frame_footer.addFromList(story,pdf)
172
173class StudentsUtils(grok.GlobalUtility):
174    """A collection of methods subject to customization.
175    """
176    grok.implements(IStudentsUtils)
177
178    def setReturningData(self, student):
179        """ This method defines what happens after school fee payment
180        depending on the student's senate verdict.
181
182        In the base configuration current level is always increased
183        by 100 no matter which verdict has been assigned.
184        """
185        student['studycourse'].current_level += 100
186        student['studycourse'].current_session += 1
187        verdict = student['studycourse'].current_verdict
188        student['studycourse'].current_verdict = '0'
189        student['studycourse'].previous_verdict = verdict
190        return
191
192    def getPaymentDetails(self, category, student):
193        """Get the payment dates of a student for the payment category
194        specified.
195        """
196        d = {}
197        d['p_item'] = u''
198        d['amount'] = 0
199        d['error'] = u''
200        d['p_session'] = student['studycourse'].current_session
201        session = str(d['p_session'])
202        try:
203            academic_session = grok.getSite()['configuration'][session]
204        except KeyError:
205            d['error'] = u'Session configuration object is not available.'
206            return d
207        d['surcharge_1'] = academic_session.surcharge_1
208        d['surcharge_2'] = academic_session.surcharge_2
209        d['surcharge_3'] = academic_session.surcharge_3
210        if category == 'schoolfee':
211            d['amount'] = academic_session.school_fee_base
212            d['p_item'] = student['studycourse'].certificate.code
213        elif category == 'clearance':
214            d['p_item'] = student['studycourse'].certificate.code
215            d['amount'] = academic_session.clearance_fee
216        elif category == 'bed_allocation':
217            d['p_item'] = self.getAccommodationDetails(student)['bt']
218            d['amount'] = academic_session.booking_fee
219        return d
220
221    def getAccommodationDetails(self, student):
222        """Determine the accommodation dates of a student.
223        """
224        d = {}
225        d['error'] = u''
226        site_configuration = grok.getSite()['configuration']
227        d['booking_session'] = site_configuration.accommodation_session
228        d['allowed_states'] = site_configuration.accommodation_states
229        # Determine bed type
230        studycourse = student['studycourse']
231        certificate = getattr(studycourse,'certificate',None)
232        entry_session = studycourse.entry_session
233        current_level = studycourse.current_level
234        if not (entry_session and current_level and certificate):
235            return
236        end_level = certificate.end_level
237        if entry_session == grok.getSite()['configuration'].accommodation_session:
238            bt = 'fr'
239        elif current_level >= end_level:
240            bt = 'fi'
241        else:
242            bt = 're'
243        if student.sex == 'f':
244            sex = 'female'
245        else:
246            sex = 'male'
247        special_handling = 'regular'
248        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
249        return d
250
251    def selectBed(self, available_beds):
252        """Select a bed from a list of available beds.
253
254        In the base configuration we select the first bed found,
255        but can also randomize the selection if we like.
256        """
257        return available_beds[0]
258
259    def renderPDF(self, view, filename='slip.pdf',
260        student=None, studentview=None, tableheader=None, tabledata=None):
261        """Render pdf slips for various pages.
262        """
263        # (0,0),(-1,-1) = whole table
264        # (0,0),(0,-1) = first column
265        # (-1,0),(-1,-1) = last column
266        # (0,0),(-1,0) = first row
267        # (0,-1),(-1,-1) = last row
268
269        pdf = canvas.Canvas(filename,pagesize=A4)
270        pdf.setTitle(view.label)
271        pdf.setSubject(view.title)
272        pdf.setAuthor('%s (%s)' % (view.request.principal.title,
273            view.request.principal.id))
274        pdf.setCreator('WAeUP Kofa')
275        width, height = A4
276        footer_text = view.label
277        if getattr(student, 'student_id', None) is not None:
278            footer_text = "%s - %s - " % (student.student_id, footer_text)
279        style = getSampleStyleSheet()
280        style.add(ParagraphStyle(name='Right', alignment=TA_RIGHT))
281        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
282
283        story = []
284        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
285        header_title = getattr(grok.getSite(), 'name', u'Sample University')
286        story.append(Paragraph(header_title, style["Heading1"]))
287        frame_header.addFromList(story,pdf)
288
289        # Insert student data table
290        story = []
291        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
292        story.append(Paragraph(view.label, style["Heading2"]))
293        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
294        if student is not None:
295            #story.append(Spacer(1, 12))
296            bd_translation = translate(_('Base Data'),
297                'waeup.kofa', target_language=portal_language)
298            story.append(Paragraph(bd_translation, style["Heading3"]))
299            studenttable = render_student_data(studentview)
300            story.append(studenttable)
301
302        # Insert widgets
303        story.append(Paragraph(view.title, style["Heading3"]))
304        set_up_widgets(view)
305        data = []
306        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
307        for widget in view.widgets:
308            f_label = '<font size=12>%s</font>:' % translate(
309                widget.label.strip(), 'waeup.kofa',
310                target_language=portal_language)
311            f_label = Paragraph(f_label, style["Normal"])
312            f_text = formatted_text(widget(), size=12)
313            f_text = Paragraph(f_text, style["Normal"])
314            data.append([f_label,f_text])
315        table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
316        story.append(table)
317
318        # Import browser components locally
319        # to avoid circular import
320        from waeup.kofa.students.viewlets import FileManager
321        from waeup.kofa.browser import DEFAULT_IMAGE_PATH
322        data = []
323        # Collect viewlets
324        fm = FileManager(view.context, view.request, view)
325        fm.update()
326        if fm.viewlets:
327            sc_translation = translate(_('Scanned Documents'), 'waeup.kofa',
328                target_language=portal_language)
329            story.append(Paragraph(sc_translation, style["Heading3"]))
330            # Insert list of scanned documents
331            for viewlet in fm.viewlets:
332                f_label = formatted_label(size=12) % translate(
333                viewlet.label, 'waeup.kofa',
334                target_language=portal_language)
335                f_label = Paragraph(f_label, style["Normal"])
336                if viewlet.template.__grok_name__ == 'imagedisplay':
337                    # In case we define FileDisplay viewlets with
338                    # an imagedisplay template in the customization package
339                    img = getUtility(IExtFileStore).getFileByContext(
340                        view.context, attr=viewlet.download_name)
341                    if img is None:
342                        img = open(DEFAULT_IMAGE_PATH, 'rb')
343                    doc_img = Image(img.name, width=2*cm, height=1*cm, kind='bound')
344                    data.append([f_label,doc_img])
345                else:
346                    f_text = formatted_text(viewlet.title, size=12)
347                    f_text = Paragraph(f_text, style["Normal"])
348                    data.append([f_label,f_text])
349            table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
350            story.append(table)
351
352        try:
353            frame_body.addFromList(story,pdf)
354        except IOError:
355            view.flash('Error in image file.')
356            return view.redirect(view.url(view.context))
357
358        if tabledata and tableheader:
359            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
360        else:
361            insert_footer(pdf,width,style,footer_text)
362
363        # Insert content table on second page
364        if tabledata and tableheader:
365            pdf.showPage()
366            frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(2*cm))
367            story = []
368            story.append(Paragraph(view.content_title, style["Heading3"]))
369            #story.append(Spacer(1, 12))
370            contenttable = render_table_data(tableheader,tabledata)
371            story.append(contenttable)
372            frame_body.addFromList(story,pdf)
373            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
374
375        view.response.setHeader(
376            'Content-Type', 'application/pdf')
377        return pdf.getpdfdata()
378
379    VERDICTS_DICT = {
380        '0': 'not yet',
381        'A': 'Successful student',
382        'B': 'Student with carryover courses',
383        'C': 'Student on probation',
384        }
Note: See TracBrowser for help on using the repository browser.