root/relatorio/reporting.py @ 106:f157cddb2684

Revision 106:f157cddb2684, 5.3 kB (checked in by Ga?tan de Menten <ged@…>, 4 years ago)

we don't need the class in the default factory

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
24
25import pkg_resources
26from genshi.template import TemplateLoader
27
28def _absolute(path):
29    "Compute the absolute path of path relative to the caller file"
30    if os.path.isabs(path):
31        return path
32    caller_fname = sys._getframe(2).f_globals['__file__']
33    caller_dir = os.path.dirname(caller_fname)
34    return os.path.abspath(os.path.join(caller_dir, path))
35
36def _guess_type(mime):
37    """
38    Returns the codename used by relatorio to identify which template plugin
39    it should use to render a mimetype
40    """
41    mime = mime.lower()
42    major, stype = mime.split('/', 1)
43    if major == 'application':
44        if 'opendocument' in stype:
45            return 'oo.org'
46        else:
47            return stype
48    elif major == 'text':
49        if stype in ('xml', 'html', 'xhtml'):
50            return 'markup'
51        else:
52            return 'text'
53
54
55class MIMETemplateLoader(TemplateLoader):
56    """This subclass of TemplateLoader use mimetypes to search and find
57    templates to load.
58    """
59
60    factories = {}
61
62    mime_func = [_guess_type]
63
64    def get_type(self, mime):
65        "finds the codename used by relatorio to work on a mimetype"
66        for func in reversed(self.mime_func):
67            codename = func(mime)
68            if codename is not None:
69                return codename
70
71    def load(self, path, mime):
72        "returns a template object based on path"
73        rtype = self.get_type(mime)
74        return super(MIMETemplateLoader, self).load(path,
75                                                    cls=self.factories[rtype])
76
77    @classmethod
78    def add_factory(cls, abbr_mimetype, template_factory, id_function=None):
79        """adds a template factory to the already known factories"""
80        cls.factories[abbr_mimetype] = template_factory
81        if id_function is not None:
82            cls.mime_func.append(id_function)
83
84default_loader = MIMETemplateLoader(auto_reload=True)
85
86
87class DefaultFactory:
88    """This is the default factory used by relatorio.
89
90    It just returns a copy of the data it receives"""
91
92    def __call__(self, **kwargs):
93        data = kwargs.copy()
94        return data
95
96default_factory = DefaultFactory()
97
98
99class Report:
100    """Report is a simple interface on top of a rendering template.
101    """
102
103    def __init__(self, path, mimetype,
104                 factory=default_factory, loader=default_loader):
105        self.fpath = path
106        self.mimetype = mimetype
107        self.data_factory = factory
108        self.tmpl_loader = loader
109        self.filters = []
110
111    def __call__(self, **kwargs):
112        template = self.tmpl_loader.load(self.fpath, self.mimetype)
113        data = self.data_factory(**kwargs)
114        return template.generate(**data).filter(*self.filters)
115
116    def __repr__(self):
117        return '<relatorio report on %s>' % self.fpath
118
119
120
121class ReportDict:
122
123    def __init__(self, *args, **kwargs):
124        self.mimetypes = {}
125        self.ids = {}
126
127
128class ReportRepository:
129    """ReportRepository stores the report definition associated to objects.
130
131    The report are indexed in this object by the object class they are working
132    on and the name given to it by the user.
133    """
134
135    def __init__(self, datafactory=DefaultFactory):
136        self.classes = {}
137        self.default_factory = datafactory
138        self.loader = default_loader
139
140    def add_report(self, klass, mimetype, template_path, data_factory=None,
141                   report_name='default'):
142        """adds a report to the repository.
143
144        You will be able to find the report via
145            - the class it is working on
146            - the mimetype it outputs
147            - the name of the report
148
149        You also have the opportunity to define a specific data_factory.
150        """
151        if data_factory is None:
152            data_factory = self.default_factory
153        reports = self.classes.setdefault(klass, ReportDict())
154        report = Report(_absolute(template_path), mimetype, data_factory(),
155                        self.loader)
156        reports.ids[report_name] = report, mimetype
157        reports.mimetypes.setdefault(mimetype, []) \
158                         .append((report, report_name))
159
160    def by_mime(self, klass, mimetype):
161        """gets a list of report related to a class by specifying the mimetype
162        """
163        return self.classes[klass].mimetypes[mimetype]
164
165    def by_id(self, klass, id):
166        """get a report related to a class by its id
167        """
168        return self.classes[klass].ids[id]
Note: See TracBrowser for help on using the browser.