source: main/waeup.kofa/trunk/src/waeup/kofa/students/reports.py @ 9834

Last change on this file since 9834 was 9680, checked in by Henrik Bettermann, 12 years ago

Set Id svn keyword.

Extend IStudentsReport.

  • Property svn:keywords set to Id
File size: 8.9 KB
Line 
1## $Id: reports.py 9680 2012-11-18 16:58:48Z henrik $
2##
3## Copyright (C) 2012 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##
18import grok
19from zope.catalog.interfaces import ICatalog
20from zope.component import queryUtility, getUtility
21from zope.interface import implementer, Interface, Attribute
22from waeup.kofa.interfaces import (
23    IKofaUtils,
24    academic_sessions_vocab, registration_states_vocab)
25from waeup.kofa.interfaces import MessageFactory as _
26from waeup.kofa.reports import IReport
27
28class IStudentsReport(IReport):
29
30    session = Attribute('Session to report')
31    mode = Attribute('Study modes group to report')
32    creation_dt_string = Attribute('Human readable report creation datetime')
33
34def get_students_by(session, mode):
35    """Get students in a certain session and study mode.
36
37    Returns a table ordered by faculty code (one per row) and
38    registration state (cols). The result is a 3-tuple representing
39    ((<FACULTY_CODES>), (<STATES>), (<NUM_OF_STUDENTS>)). The
40    (<NUM_OF_STUDENTS>) is an n-tuple with each entry containing the
41    number of students found in that faculty and with the respective
42    state.
43
44    Sample result:
45
46      >>> ((u'FAC1', u'FAC2'),
47      ...  ('created', 'accepted', 'registered'),
48      ...  ((12, 10, 1), (0, 5, 25)))
49
50    This result means: there are 5 students in FAC2 in state
51    'accepted' while 12 students in 'FAC1' are in state 'created'.
52    """
53    site = grok.getSite()
54    states = tuple([x.value for x in registration_states_vocab])
55    states = states + (u'Total',)
56    fac_codes = tuple(sorted([x for x in site['faculties'].keys()],
57                             key=lambda x: x.lower()))
58    fac_codes = fac_codes + (u'Total',)
59    # XXX: Here we do _one_ query and then examine the single
60    #   students. One could also do multiple queries and just look for
61    #   the result length (not introspecting the delivered students
62    #   further).
63    cat = queryUtility(ICatalog, name="students_catalog")
64    result = cat.searchResults(current_session=(session, session))
65    table = [[0 for x in xrange(len(states))] for y in xrange(len(fac_codes))]
66    mode_groups = getUtility(IKofaUtils).MODE_GROUPS
67    for stud in result:
68        if mode != 'All' and stud.current_mode not in mode_groups[mode]:
69            continue
70        row = fac_codes.index(stud.faccode)
71        col = states.index(stud.state)
72        table[row][col] += 1
73        table[-1][col] += 1
74        table[row][-1] += 1
75        table[-1][-1] += 1
76    # turn lists into tuples
77    table = tuple([tuple(row) for row in table])
78    return (fac_codes, states, table)
79
80from reportlab.lib import colors
81from reportlab.lib.styles import getSampleStyleSheet
82from reportlab.lib.units import cm
83from reportlab.platypus import Paragraph, Table, Spacer
84from waeup.kofa.reports import IReport, IReportGenerator
85from waeup.kofa.reports import Report
86from waeup.kofa.browser.interfaces import IPDFCreator
87
88STYLE = getSampleStyleSheet()
89
90def tbl_data_to_table(row_names, col_names, data):
91    result = []
92    new_col_names = []
93    for name in col_names:
94        new_col_names.append(name.replace(' ', '\n'))
95    head = [''] + list(new_col_names)
96    result = [head]
97    for idx, row_name in enumerate(row_names):
98        row = [row_name] + list(data[idx])
99        result.append(row)
100    return result
101
102TABLE_STYLE = [
103    ('FONT', (0,0), (-1,-1), 'Helvetica', 8),
104    ('FONT', (0,0), (0,-1), 'Helvetica-Bold', 8),
105    ('FONT', (0,0), (-1,0), 'Helvetica-Bold', 8),
106    ('FONT', (0,-1), (-1,-1), 'Helvetica-Bold', 8),
107    ('FONT', (-1,0), (-1,-1), 'Helvetica-Bold', 8),
108    ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
109    ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
110    ('LINEBELOW', (0,-1), (-1,-1), 0.25, colors.black),
111    ('LINEAFTER', (-1,0), (-1,-1), 0.25, colors.black),
112    ('LINEBEFORE', (-1,0), (-1,-1), 1.0, colors.black),
113    ('LINEABOVE', (0,-1), (-1,-1), 1.0, colors.black),
114    ('LINEABOVE', (0,0), (-1,0), 0.25, colors.black),
115    ]
116
117@implementer(IStudentsReport)
118class StudentsReport(Report):
119    data = None
120    session = None
121    mode = None
122
123    def __init__(self, session, mode, author='System'):
124        super(StudentsReport, self).__init__(
125            args=[session, mode], kwargs={'author':author})
126        self.session = academic_sessions_vocab.getTerm(session).title
127        self.mode = mode
128        self.author = author
129        self.creation_dt_string = self.creation_dt.astimezone(
130            getUtility(IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
131        self.data = get_students_by(session, mode)
132
133    def create_pdf(self):
134        creator = getUtility(IPDFCreator)
135        table_data = tbl_data_to_table(*self.data)
136        col_widths = [None,] + [1.6*cm] * len(self.data[1]) + [None,]
137        pdf_data = [Paragraph('<b>%s</b>' % self.creation_dt_string,
138                              STYLE["Normal"]),
139                    Spacer(1, 12),]
140        pdf_data += [
141            Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
142        doc_title = '%s Students in Session %s' % (self.mode, self.session)
143        pdf = creator.create_pdf(
144            pdf_data, None, doc_title, self.author,
145            'Students in Session %s' % self.session)
146        return pdf
147
148@implementer(IReportGenerator)
149class StudentsReportGenerator(grok.GlobalUtility):
150
151    title = _('Students')
152    grok.name('students_by')
153
154    def generate(self, site, session=None, mode=None, author=None):
155        result = StudentsReport(session=session, mode=mode, author=author)
156        return result
157
158###############################################################
159## Browser related stuff
160##
161## XXX: move to local browser module
162###############################################################
163from waeup.kofa.browser.layout import KofaPage
164from waeup.kofa.interfaces import academic_sessions_vocab
165from waeup.kofa.reports import get_generators
166grok.templatedir('browser_templates')
167class StudentsReportGeneratorPage(KofaPage):
168
169    grok.context(StudentsReportGenerator)
170    grok.name('index.html')
171    grok.require('waeup.manageReports')
172
173    label = _('Create students report')
174
175    @property
176    def generator_name(self):
177        for name, gen in get_generators():
178            if gen == self.context:
179                return name
180        return None
181
182    def update(self, CREATE=None, session=None, mode=None):
183        self.parent_url = self.url(self.context.__parent__)
184        self._set_session_values()
185        self._set_mode_values()
186        if CREATE and session:
187            # create a new report job for students by session
188            container = self.context.__parent__
189            user_id = self.request.principal.id
190            kw = dict(
191                session=int(session),
192                mode=mode)
193            self.flash(_('New report is being created in background'))
194            job_id = container.start_report_job(
195                self.generator_name, user_id, kw=kw)
196            ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
197            grok.getSite().logger.info(
198                '%s - report %s created: %s (session=%s, mode=%s)' % (
199                ob_class, job_id, self.context.title, session, mode))
200            self.redirect(self.parent_url)
201            return
202        return
203
204    def _set_session_values(self):
205        vocab_terms = academic_sessions_vocab.by_value.values()
206        self.sessions = [(x.title, x.token) for x in vocab_terms]
207        return
208
209    def _set_mode_values(self):
210        mode_groups = getUtility(IKofaUtils).MODE_GROUPS
211        self.modes = sorted([(key, key) for key in mode_groups.keys()])
212        return
213
214class StudentsReportPDFView(grok.View):
215
216    grok.context(IStudentsReport)
217    grok.name('pdf')
218    grok.require('waeup.Public')
219
220    def render(self):
221        filename = 'StudentsReport_%s_%s_%s.pdf' % (
222            self.context.session, self.context.mode,
223            self.context.creation_dt_string)
224        filename = filename.replace(
225            '/', '_').replace(' ','_').replace(':', '-')
226        self.response.setHeader(
227            'Content-Type', 'application/pdf')
228        self.response.setHeader(
229            'Content-Disposition:', 'attachment; filename="%s' % filename)
230        pdf_stream = self.context.create_pdf()
231        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
232        grok.getSite().logger.info('%s - report %s downloaded: %s' % (
233            ob_class, self.context.__name__, filename))
234        return pdf_stream
Note: See TracBrowser for help on using the repository browser.