## $Id: utils.py 7714 2012-02-28 10:34:16Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """General helper functions and utilities for the student section. """ import grok from random import SystemRandom as r from datetime import datetime from zope.i18n import translate from zope.component import getUtility from reportlab.pdfgen import canvas from reportlab.lib import colors from reportlab.lib.units import cm from reportlab.lib.enums import TA_RIGHT from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.platypus import (Frame, Paragraph, Image, Table, Spacer) from reportlab.platypus.tables import TableStyle from reportlab.platypus.flowables import PageBreak from zope.component import getUtility from zope.formlib.form import setUpEditWidgets from waeup.sirp.interfaces import IExtFileStore, ISIRPUtils from waeup.sirp.interfaces import MessageFactory as _ from waeup.sirp.students.interfaces import IStudentsUtils SLIP_STYLE = [ ('VALIGN',(0,0),(-1,-1),'TOP'), #('FONT', (0,0), (-1,-1), 'Helvetica', 11), ] CONTENT_STYLE = [ ('VALIGN',(0,0),(-1,-1),'TOP'), #('FONT', (0,0), (-1,-1), 'Helvetica', 8), #('TEXTCOLOR',(0,0),(-1,0),colors.white), ('BACKGROUND',(0,0),(-1,0),colors.black), ] FONT_SIZE = 10 FONT_COLOR = 'black' def formatted_label(color=FONT_COLOR, size=FONT_SIZE): tag1 ='' % (color, size) return tag1 + '%s:' def formatted_text(text, color=FONT_COLOR, size=FONT_SIZE): """Turn `text`, `color` and `size` into an HTML snippet. The snippet is suitable for use with reportlab and generating PDFs. Wraps the `text` into a ```` tag with passed attributes. Also non-strings are converted. Raw strings are expected to be utf-8 encoded (usually the case for widgets etc.). The returned snippet is unicode type. """ if not isinstance(text, unicode): if isinstance(text, basestring): text = text.decode('utf-8') else: text = unicode(text) tag1 = u'' % (color, size) return tag1 + u'%s' % text def generate_student_id(students,letter): if letter == '?': letter= r().choice('ABCDEFGHKLMNPQRSTUVWXY') sid = u"%c%d" % (letter,r().randint(99999,1000000)) while sid in students.keys(): sid = u"%c%d" % (letter,r().randint(99999,1000000)) return sid def set_up_widgets(view, ignore_request=False): view.adapters = {} view.widgets = setUpEditWidgets( view.form_fields, view.prefix, view.context, view.request, adapters=view.adapters, for_display=True, ignore_request=ignore_request ) def render_student_data(studentview): """Render student table for an existing frame. """ width, height = A4 set_up_widgets(studentview, ignore_request=True) data_left = [] data_right = [] style = getSampleStyleSheet() img = getUtility(IExtFileStore).getFileByContext( studentview.context, attr='passport.jpg') if img is None: from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb') doc_img = Image(img.name, width=4*cm, height=4*cm, kind='bound') data_left.append([doc_img]) #data.append([Spacer(1, 12)]) portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE for widget in studentview.widgets: if widget.name == 'form.adm_code': continue f_label = formatted_label(size=12) % translate( widget.label.strip(), 'waeup.sirp', target_language=portal_language) f_label = Paragraph(f_label, style["Normal"]) f_text = formatted_text(widget(), size=12) f_text = Paragraph(f_text, style["Normal"]) data_right.append([f_label,f_text]) table_left = Table(data_left,style=SLIP_STYLE) table_right = Table(data_right,style=SLIP_STYLE, colWidths=[4*cm, 10*cm]) table = Table([[table_left, table_right],],style=SLIP_STYLE) return table def render_table_data(tableheader,tabledata): """Render children table for an existing frame. """ data = [] #data.append([Spacer(1, 12)]) line = [] style = getSampleStyleSheet() for element in tableheader: field = formatted_text(element[0], color='white') field = Paragraph(field, style["Normal"]) line.append(field) data.append(line) for ticket in tabledata: line = [] for element in tableheader: field = formatted_text(getattr(ticket,element[1],u' ')) field = Paragraph(field, style["Normal"]) line.append(field) data.append(line) table = Table(data,colWidths=[ element[2]*cm for element in tableheader], style=CONTENT_STYLE) return table def insert_footer(pdf,width,style,text=None, number_of_pages=1): """Render the whole footer frame. """ story = [] frame_footer = Frame(1*cm,0,width-(2*cm),1*cm) timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") left_text = '%s' % timestamp story.append(Paragraph(left_text, style["Normal"])) frame_footer.addFromList(story,pdf) story = [] frame_footer = Frame(1*cm,0,width-(2*cm),1*cm) portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE right_text = translate(_('${a} Page ${b} of ${c}', mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}), 'waeup.sirp', target_language=portal_language) story.append(Paragraph(right_text, style["Right"])) frame_footer.addFromList(story,pdf) class StudentsUtils(grok.GlobalUtility): """A collection of methods subject to customization. """ grok.implements(IStudentsUtils) def setReturningData(self, student): student['studycourse'].current_level += 100 student['studycourse'].current_session += 1 verdict = student['studycourse'].current_verdict student['studycourse'].current_verdict = '0' student['studycourse'].previous_verdict = verdict return def getPaymentDetails(self, category, student): d = {} d['p_item'] = u'' d['amount'] = 0 d['error'] = u'' d['p_session'] = student['studycourse'].current_session session = str(d['p_session']) try: academic_session = grok.getSite()['configuration'][session] except KeyError: d['error'] = u'Session configuration object is not available.' return d d['surcharge_1'] = academic_session.surcharge_1 d['surcharge_2'] = academic_session.surcharge_2 d['surcharge_3'] = academic_session.surcharge_3 if category == 'schoolfee': d['amount'] = academic_session.school_fee_base d['p_item'] = student['studycourse'].certificate.code elif category == 'clearance': d['p_item'] = student['studycourse'].certificate.code d['amount'] = academic_session.clearance_fee elif category == 'bed_allocation': d['p_item'] = self.getAccommodationDetails(student)['bt'] d['amount'] = academic_session.booking_fee return d def getAccommodationDetails(self, student): d = {} d['error'] = u'' site_configuration = grok.getSite()['configuration'] d['booking_session'] = site_configuration.accommodation_session d['allowed_states'] = site_configuration.accommodation_states # Determine bed type studycourse = student['studycourse'] certificate = getattr(studycourse,'certificate',None) entry_session = studycourse.entry_session current_level = studycourse.current_level if not (entry_session and current_level and certificate): return end_level = certificate.end_level if entry_session == grok.getSite()['configuration'].accommodation_session: bt = 'fr' elif current_level >= end_level: bt = 'fi' else: bt = 're' if student.sex == 'f': sex = 'female' else: sex = 'male' special_handling = 'regular' d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt) return d # In the standard configuration we select the first bed found, # but can also randomize the selection if we like. def selectBed(self, available_beds): return available_beds[0] def renderPDF(self, view, filename='slip.pdf', student=None, studentview=None, tableheader=None, tabledata=None): # (0,0),(-1,-1) = whole table # (0,0),(0,-1) = first column # (-1,0),(-1,-1) = last column # (0,0),(-1,0) = first row # (0,-1),(-1,-1) = last row pdf = canvas.Canvas(filename,pagesize=A4) pdf.setTitle(view.label) pdf.setSubject(view.title) pdf.setAuthor('%s (%s)' % (view.request.principal.title, view.request.principal.id)) pdf.setCreator('WAeUP SIRP') width, height = A4 footer_text = view.label if getattr(student, 'student_id', None) is not None: footer_text = "%s - %s - " % (student.student_id, footer_text) style = getSampleStyleSheet() style.add(ParagraphStyle(name='Right', alignment=TA_RIGHT)) pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm)) story = [] frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm)) header_title = getattr(grok.getSite(), 'name', u'Sample University') story.append(Paragraph(header_title, style["Heading1"])) frame_header.addFromList(story,pdf) # Insert student data table story = [] frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm)) story.append(Paragraph(view.label, style["Heading2"])) portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE if student is not None: #story.append(Spacer(1, 12)) bd_translation = translate(_('Base Data'), 'waeup.sirp', target_language=portal_language) story.append(Paragraph(bd_translation, style["Heading3"])) studenttable = render_student_data(studentview) story.append(studenttable) # Insert widgets story.append(Paragraph(view.title, style["Heading3"])) set_up_widgets(view) data = [] portal_language = getUtility(ISIRPUtils).PORTAL_LANGUAGE for widget in view.widgets: f_label = '%s:' % translate( widget.label.strip(), 'waeup.sirp', target_language=portal_language) f_label = Paragraph(f_label, style["Normal"]) f_text = formatted_text(widget(), size=12) f_text = Paragraph(f_text, style["Normal"]) data.append([f_label,f_text]) table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm]) story.append(table) # Import browser components locally # to avoid circular import from waeup.sirp.students.viewlets import FileManager from waeup.sirp.browser import DEFAULT_IMAGE_PATH data = [] # Collect viewlets fm = FileManager(view.context, view.request, view) fm.update() if fm.viewlets: story.append(Paragraph('Scanned Documents', style["Heading3"])) # Insert list of scanned documents for viewlet in fm.viewlets: f_label = formatted_label(size=12) % viewlet.label f_label = Paragraph(f_label, style["Normal"]) if viewlet.template.__grok_name__ == 'imagedisplay': # In case we define FileDisplay viewlets with # an imagedisplay template in the customization package img = getUtility(IExtFileStore).getFileByContext( view.context, attr=viewlet.download_name) if img is None: img = open(DEFAULT_IMAGE_PATH, 'rb') doc_img = Image(img.name, width=2*cm, height=1*cm, kind='bound') data.append([f_label,doc_img]) else: f_text = formatted_text(viewlet.title, size=12) f_text = Paragraph(f_text, style["Normal"]) data.append([f_label,f_text]) table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm]) story.append(table) try: frame_body.addFromList(story,pdf) except IOError: view.flash('Error in image file.') return view.redirect(view.url(view.context)) if tabledata and tableheader: insert_footer(pdf,width,style,footer_text,number_of_pages=2) else: insert_footer(pdf,width,style,footer_text) # Insert content table on second page if tabledata and tableheader: pdf.showPage() frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(2*cm)) story = [] story.append(Paragraph(view.content_title, style["Heading3"])) #story.append(Spacer(1, 12)) contenttable = render_table_data(tableheader,tabledata) story.append(contenttable) frame_body.addFromList(story,pdf) insert_footer(pdf,width,style,footer_text,number_of_pages=2) view.response.setHeader( 'Content-Type', 'application/pdf') return pdf.getpdfdata() def getVerdictsDict(self): """Provide a dictionary of verdicts. """ return { '0': 'not yet', 'A': 'Successful student', 'B': 'Student with carryover courses', 'C': 'Student on probation', 'D': 'Withdrawn from the faculty', 'E': 'Student who were previously on probation', 'F': 'Medical case', 'G': 'Absent from examination', 'H': 'Withheld results', 'I': 'Expelled/rusticated/suspended student', 'J': 'Temporary withdrawn from the university', 'K': 'Unregistered student', 'L': 'Referred student', 'M': 'Reinstatement', 'N': 'Student on transfer', 'O': 'NCE-III repeater', 'Y': 'No previous verdict', 'X': 'New 300 level student', 'Z': 'Successful student (provisional)', 'A1': 'First Class', 'A2': 'Second Class Upper', 'A3': 'Second Class Lower', 'A4': 'Third Class', 'A5': 'Pass', 'A6': 'Distinction', 'A7': 'Credit', 'A8': 'Merit', }