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

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

We can use the hash symbol at the end of p_id in import files
to avoid annoying automatic number transformation
by Excel or Calc.

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