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

Last change on this file since 9668 was 9668, checked in by uli, 12 years ago

Add a str for StudentsReport? and add a local creation_dt string.

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