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

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

Continue internat. of applicants package including pdf exports.

  • Property svn:keywords set to Id
File size: 15.2 KB
Line 
1## $Id: utils.py 7714 2012-02-28 10:34:16Z 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.sirp.interfaces import IExtFileStore, ISIRPUtils
38from waeup.sirp.interfaces import MessageFactory as _
39from waeup.sirp.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    The returned snippet is unicode type.
70    """
71    if not isinstance(text, unicode):
72        if isinstance(text, basestring):
73            text = text.decode('utf-8')
74        else:
75            text = unicode(text)
76    tag1 = u'<font color="%s" size="%d">' % (color, size)
77    return tag1 + u'%s</font>' % text
78
79def generate_student_id(students,letter):
80    if letter == '?':
81        letter= r().choice('ABCDEFGHKLMNPQRSTUVWXY')
82    sid = u"%c%d" % (letter,r().randint(99999,1000000))
83    while sid in students.keys():
84        sid = u"%c%d" % (letter,r().randint(99999,1000000))
85    return sid
86
87def set_up_widgets(view, ignore_request=False):
88    view.adapters = {}
89    view.widgets = setUpEditWidgets(
90        view.form_fields, view.prefix, view.context, view.request,
91        adapters=view.adapters, for_display=True,
92        ignore_request=ignore_request
93        )
94
95def render_student_data(studentview):
96    """Render student table for an existing frame.
97    """
98    width, height = A4
99    set_up_widgets(studentview, ignore_request=True)
100    data_left = []
101    data_right = []
102    style = getSampleStyleSheet()
103    img = getUtility(IExtFileStore).getFileByContext(
104        studentview.context, attr='passport.jpg')
105    if img is None:
106        from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH
107        img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb')
108    doc_img = Image(img.name, width=4*cm, height=4*cm, kind='bound')
109    data_left.append([doc_img])
110    #data.append([Spacer(1, 12)])
111    portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE
112    for widget in studentview.widgets:
113        if widget.name == 'form.adm_code':
114            continue
115        f_label = formatted_label(size=12) % translate(
116            widget.label.strip(), 'waeup.sirp',
117            target_language=portal_language)
118        f_label = Paragraph(f_label, style["Normal"])
119        f_text = formatted_text(widget(), size=12)
120        f_text = Paragraph(f_text, style["Normal"])
121        data_right.append([f_label,f_text])
122    table_left = Table(data_left,style=SLIP_STYLE)
123    table_right = Table(data_right,style=SLIP_STYLE, colWidths=[4*cm, 10*cm])
124    table = Table([[table_left, table_right],],style=SLIP_STYLE)
125    return table
126
127def render_table_data(tableheader,tabledata):
128    """Render children table for an existing frame.
129    """
130    data = []
131    #data.append([Spacer(1, 12)])
132    line = []
133    style = getSampleStyleSheet()
134    for element in tableheader:
135        field = formatted_text(element[0], color='white')
136        field = Paragraph(field, style["Normal"])
137        line.append(field)
138    data.append(line)
139    for ticket in tabledata:
140        line = []
141        for element in tableheader:
142              field = formatted_text(getattr(ticket,element[1],u' '))
143              field = Paragraph(field, style["Normal"])
144              line.append(field)
145        data.append(line)
146    table = Table(data,colWidths=[
147        element[2]*cm for element in tableheader], style=CONTENT_STYLE)
148    return table
149
150
151def insert_footer(pdf,width,style,text=None, number_of_pages=1):
152      """Render the whole footer frame.
153      """
154      story = []
155      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
156      timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
157      left_text = '<font size=10>%s</font>' % timestamp
158      story.append(Paragraph(left_text, style["Normal"]))
159      frame_footer.addFromList(story,pdf)
160      story = []
161      frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
162      portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE
163      right_text = translate(_('<font size=10>${a} Page ${b} of ${c}</font>',
164          mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}),
165          'waeup.sirp', target_language=portal_language)
166      story.append(Paragraph(right_text, style["Right"]))
167      frame_footer.addFromList(story,pdf)
168
169class StudentsUtils(grok.GlobalUtility):
170    """A collection of methods subject to customization.
171    """
172    grok.implements(IStudentsUtils)
173
174    def setReturningData(self, student):
175        student['studycourse'].current_level += 100
176        student['studycourse'].current_session += 1
177        verdict = student['studycourse'].current_verdict
178        student['studycourse'].current_verdict = '0'
179        student['studycourse'].previous_verdict = verdict
180        return
181
182    def getPaymentDetails(self, category, student):
183        d = {}
184        d['p_item'] = u''
185        d['amount'] = 0
186        d['error'] = u''
187        d['p_session'] = student['studycourse'].current_session
188        session = str(d['p_session'])
189        try:
190            academic_session = grok.getSite()['configuration'][session]
191        except KeyError:
192            d['error'] = u'Session configuration object is not available.'
193            return d
194        d['surcharge_1'] = academic_session.surcharge_1
195        d['surcharge_2'] = academic_session.surcharge_2
196        d['surcharge_3'] = academic_session.surcharge_3
197        if category == 'schoolfee':
198            d['amount'] = academic_session.school_fee_base
199            d['p_item'] = student['studycourse'].certificate.code
200        elif category == 'clearance':
201            d['p_item'] = student['studycourse'].certificate.code
202            d['amount'] = academic_session.clearance_fee
203        elif category == 'bed_allocation':
204            d['p_item'] = self.getAccommodationDetails(student)['bt']
205            d['amount'] = academic_session.booking_fee
206        return d
207
208    def getAccommodationDetails(self, student):
209        d = {}
210        d['error'] = u''
211        site_configuration = grok.getSite()['configuration']
212        d['booking_session'] = site_configuration.accommodation_session
213        d['allowed_states'] = site_configuration.accommodation_states
214        # Determine bed type
215        studycourse = student['studycourse']
216        certificate = getattr(studycourse,'certificate',None)
217        entry_session = studycourse.entry_session
218        current_level = studycourse.current_level
219        if not (entry_session and current_level and certificate):
220            return
221        end_level = certificate.end_level
222        if entry_session == grok.getSite()['configuration'].accommodation_session:
223            bt = 'fr'
224        elif current_level >= end_level:
225            bt = 'fi'
226        else:
227            bt = 're'
228        if student.sex == 'f':
229            sex = 'female'
230        else:
231            sex = 'male'
232        special_handling = 'regular'
233        d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
234        return d
235
236    # In the standard configuration we select the first bed found,
237    # but can also randomize the selection if we like.
238    def selectBed(self, available_beds):
239        return available_beds[0]
240
241    def renderPDF(self, view, filename='slip.pdf',
242        student=None, studentview=None, tableheader=None, tabledata=None):
243        # (0,0),(-1,-1) = whole table
244        # (0,0),(0,-1) = first column
245        # (-1,0),(-1,-1) = last column
246        # (0,0),(-1,0) = first row
247        # (0,-1),(-1,-1) = last row
248
249        pdf = canvas.Canvas(filename,pagesize=A4)
250        pdf.setTitle(view.label)
251        pdf.setSubject(view.title)
252        pdf.setAuthor('%s (%s)' % (view.request.principal.title,
253            view.request.principal.id))
254        pdf.setCreator('WAeUP SIRP')
255        width, height = A4
256        footer_text = view.label
257        if getattr(student, 'student_id', None) is not None:
258            footer_text = "%s - %s - " % (student.student_id, footer_text)
259        style = getSampleStyleSheet()
260        style.add(ParagraphStyle(name='Right', alignment=TA_RIGHT))
261        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
262
263        story = []
264        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
265        header_title = getattr(grok.getSite(), 'name', u'Sample University')
266        story.append(Paragraph(header_title, style["Heading1"]))
267        frame_header.addFromList(story,pdf)
268
269        # Insert student data table
270        story = []
271        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
272        story.append(Paragraph(view.label, style["Heading2"]))
273        portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE
274        if student is not None:
275            #story.append(Spacer(1, 12))
276            bd_translation = translate(_('Base Data'),
277                'waeup.sirp', target_language=portal_language)
278            story.append(Paragraph(bd_translation, style["Heading3"]))
279            studenttable = render_student_data(studentview)
280            story.append(studenttable)
281
282        # Insert widgets
283        story.append(Paragraph(view.title, style["Heading3"]))
284        set_up_widgets(view)
285        data = []
286        portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE
287        for widget in view.widgets:
288            f_label = '<font size=12>%s</font>:' % translate(
289                widget.label.strip(), 'waeup.sirp',
290                target_language=portal_language)
291            f_label = Paragraph(f_label, style["Normal"])
292            f_text = formatted_text(widget(), size=12)
293            f_text = Paragraph(f_text, style["Normal"])
294            data.append([f_label,f_text])
295        table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
296        story.append(table)
297
298        # Import browser components locally
299        # to avoid circular import
300        from waeup.sirp.students.viewlets import FileManager
301        from waeup.sirp.browser import DEFAULT_IMAGE_PATH
302        data = []
303        # Collect viewlets
304        fm = FileManager(view.context, view.request, view)
305        fm.update()
306        if fm.viewlets:
307            story.append(Paragraph('Scanned Documents', style["Heading3"]))
308            # Insert list of scanned documents
309            for viewlet in fm.viewlets:
310                f_label = formatted_label(size=12) % viewlet.label
311                f_label = Paragraph(f_label, style["Normal"])
312                if viewlet.template.__grok_name__ == 'imagedisplay':
313                    # In case we define FileDisplay viewlets with
314                    # an imagedisplay template in the customization package
315                    img = getUtility(IExtFileStore).getFileByContext(
316                        view.context, attr=viewlet.download_name)
317                    if img is None:
318                        img = open(DEFAULT_IMAGE_PATH, 'rb')
319                    doc_img = Image(img.name, width=2*cm, height=1*cm, kind='bound')
320                    data.append([f_label,doc_img])
321                else:
322                    f_text = formatted_text(viewlet.title, size=12)
323                    f_text = Paragraph(f_text, style["Normal"])
324                    data.append([f_label,f_text])
325            table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
326            story.append(table)
327
328        try:
329            frame_body.addFromList(story,pdf)
330        except IOError:
331            view.flash('Error in image file.')
332            return view.redirect(view.url(view.context))
333
334        if tabledata and tableheader:
335            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
336        else:
337            insert_footer(pdf,width,style,footer_text)
338
339        # Insert content table on second page
340        if tabledata and tableheader:
341            pdf.showPage()
342            frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(2*cm))
343            story = []
344            story.append(Paragraph(view.content_title, style["Heading3"]))
345            #story.append(Spacer(1, 12))
346            contenttable = render_table_data(tableheader,tabledata)
347            story.append(contenttable)
348            frame_body.addFromList(story,pdf)
349            insert_footer(pdf,width,style,footer_text,number_of_pages=2)
350
351        view.response.setHeader(
352            'Content-Type', 'application/pdf')
353        return pdf.getpdfdata()
354
355    def getVerdictsDict(self):
356        """Provide a dictionary of verdicts.
357        """
358        return {
359            '0': 'not yet',
360            'A': 'Successful student',
361            'B': 'Student with carryover courses',
362            'C': 'Student on probation',
363            'D': 'Withdrawn from the faculty',
364            'E': 'Student who were previously on probation',
365            'F': 'Medical case',
366            'G': 'Absent from examination',
367            'H': 'Withheld results',
368            'I': 'Expelled/rusticated/suspended student',
369            'J': 'Temporary withdrawn from the university',
370            'K': 'Unregistered student',
371            'L': 'Referred student',
372            'M': 'Reinstatement',
373            'N': 'Student on transfer',
374            'O': 'NCE-III repeater',
375            'Y': 'No previous verdict',
376            'X': 'New 300 level student',
377            'Z': 'Successful student (provisional)',
378            'A1': 'First Class',
379            'A2': 'Second Class Upper',
380            'A3': 'Second Class Lower',
381            'A4': 'Third Class',
382            'A5': 'Pass',
383            'A6': 'Distinction',
384            'A7': 'Credit',
385            'A8': 'Merit',
386            }
Note: See TracBrowser for help on using the repository browser.