root/relatorio/reporting.py @ 62:0d45497d8f76

Revision 62:0d45497d8f76, 5.6 kB (checked in by Nicolas ?vrard <nicoe@…>, 5 years ago)

PyLinted? the sources

Line 
1###############################################################################
2#
3# Copyright (c) 2007, 2008 OpenHex SPRL. (http://openhex.com) All Rights
4# Reserved.
5#
6# This program is free software; you can redistribute it and/or modify it under
7# the terms of the GNU General Public License as published by the Free Software
8# Foundation; either version 2 of the License, or (at your option) any later
9# version.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14# details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19###############################################################################
20
21__metaclass__ = type
22
23import os, sys
24import warnings
25
26import pkg_resources
27from genshi.template import TemplateLoader
28
29def _absolute(path):
30    "Compute the absolute path of path relative to the caller file"
31    if os.path.isabs(path):
32        return path
33    caller_fname = sys._getframe(2).f_globals['__file__']
34    caller_dir = os.path.dirname(caller_fname)
35    return os.path.abspath(os.path.join(caller_dir, path))
36
37def _guess_type(mime):
38    """
39    Returns the codename used by relatorio to identify which template plugin
40    it should use to render a mimetype
41    """
42    mime = mime.lower()
43    major, stype = mime.split('/', 1)
44    if major == 'application':
45        if 'opendocument' in stype:
46            return 'oo.org'
47        else:
48            return stype
49    elif major == 'text':
50        if stype in ('xml', 'html', 'xhtml'):
51            return 'markup'
52        else:
53            return 'text'
54
55
56class MIMETemplateLoader(TemplateLoader):
57    """This subclass of TemplateLoader use mimetypes to search and find
58    templates to load.
59    """
60
61    factories = {}
62
63    mime_func = [_guess_type]
64
65    def get_type(self, mime):
66        "finds the codename used by relatorio to work on a mimetype"
67        for func in reversed(self.mime_func):
68            codename = func(mime)
69            if codename is not None:
70                return codename
71
72    def load(self, path, mime):
73        "returns a template object based on path"
74        rtype = self.get_type(mime)
75        return super(MIMETemplateLoader, self).load(path,
76                                                    cls=self.factories[rtype])
77
78    @classmethod
79    def add_factory(cls, abbr_mimetype, template_factory, id_function=None):
80        """adds a template factory to the already known factories"""
81        if abbr_mimetype in cls.factories:
82            warnings.warn('You are overriding an already defined link.')
83        cls.factories[abbr_mimetype] = template_factory
84        if id_function is not None:
85            cls.mime_func.append(id_function)
86
87    @classmethod
88    def load_template_engines(cls):
89        """loads template engines found via PEAK's pkg_resources"""
90        for entrypoint in pkg_resources.iter_entry_points(
91                                        'relatorio.templates.engines'):
92            try:
93                engine = entrypoint.load()
94                if hasattr(engine, 'id_function'):
95                    cls.add_factory(entrypoint.name, engine, engine.id_function)
96                else:
97                    cls.add_factory(entrypoint.name, engine)
98            except ImportError:
99                warnings.warn('We were not able to load %s. You will not '
100                              'be able to use its functonlities' %
101                              entrypoint.module_name)
102
103
104class Report:
105    """Report is a simple interface on top of a rendering template.
106    """
107
108    def __init__(self, path, mimetype, factory, loader):
109        self.fpath = path
110        self.mimetype = mimetype
111        self.data_factory = factory
112        self.tmpl_loader = loader
113        self.filters = []
114
115    def __call__(self, **kwargs):
116        template = self.tmpl_loader.load(self.fpath, self.mimetype)
117        data = self.data_factory(**kwargs)
118        return template.generate(**data).filter(*self.filters)
119
120    def __repr__(self):
121        return '<relatorio report on %s>' % self.fpath
122
123
124class DefaultFactory:
125    """This is the default factory used by relatorio.
126   
127    It just returns a copy of the data it receives"""
128
129    def __call__(self, **kwargs):
130        data = kwargs.copy()
131        return data
132
133
134class ReportRepository:
135    """ReportRepository stores the report definition associated to objects.
136
137    The report are indexed in this object by the object class they are working
138    on and the name given to it by the user.
139    """
140
141    def __init__(self, datafactory=DefaultFactory):
142        self.reports = {}
143        self.default_factory = datafactory
144        self.loader = MIMETemplateLoader(auto_reload=True)
145
146    def add_report(self, klass, mimetype, template_path, data_factory=None,
147                   report_name='default'):
148        """adds a report to the repository.
149
150        You will be able to find the report via
151            - the class it is working on
152            - the mimetype it outputs
153            - the name of the report
154       
155        You also have the opportunity to define a specific data_factory.
156        """
157        if data_factory is None:
158            data_factory = self.default_factory
159        reports = self.reports.setdefault(klass, {})
160        report = Report(_absolute(template_path), mimetype, data_factory(),
161                        self.loader)
162        reports[report_name] = report, mimetype
163        reports.setdefault(mimetype, []).append((report_name, report))
Note: See TracBrowser for help on using the browser.