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

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

Remove trash.

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