Add new 'reporting' mini-framework
just does some basic stuff, but useful nonetheless to consolidate logic
This commit is contained in:
parent
86c3621d4d
commit
50768ed573
30
rattail/reporting/__init__.py
Normal file
30
rattail/reporting/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail 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 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail 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
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Reporting Framework
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from .reports import Report, ReportParam
|
||||
from .handlers import ReportHandler, get_report_handler
|
87
rattail/reporting/handlers.py
Normal file
87
rattail/reporting/handlers.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail 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 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail 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
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Report Handlers
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.util import load_entry_points, load_object
|
||||
|
||||
|
||||
class ReportHandler(object):
|
||||
"""
|
||||
Base class for all report handlers. Also provides default implementation,
|
||||
which is assumed to be sufficient for most needs.
|
||||
"""
|
||||
entry_point_section = 'rattail.reports'
|
||||
|
||||
def __init__(self, config=None, **kwargs):
|
||||
self.config = config
|
||||
self.enum = config.get_enum() if config else None
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def get_reports(self):
|
||||
"""
|
||||
Returns a dict of available reports, which are registered via setuptools
|
||||
entry points.
|
||||
"""
|
||||
return load_entry_points(self.entry_point_section)
|
||||
|
||||
def get_report(self, key):
|
||||
"""
|
||||
Fetch a report by key. If the report can be located, this will return an
|
||||
instance thereof; otherwise returns ``None``.
|
||||
"""
|
||||
report = self.get_reports().get(key)
|
||||
if report:
|
||||
return report(self.config)
|
||||
|
||||
def generate_output(self, session, report, params, user, progress=None, **kwargs):
|
||||
"""
|
||||
Generate and return output for the given report and params.
|
||||
"""
|
||||
data = report.make_data(session, params, progress=progress, **kwargs)
|
||||
|
||||
output = model.ReportOutput()
|
||||
output.report_name = report.make_report_name(session, params, data, **kwargs)
|
||||
output.filename = report.make_filename(session, params, data, **kwargs)
|
||||
output.created_by = user
|
||||
session.add(output)
|
||||
session.flush()
|
||||
|
||||
path = output.filepath(self.config, makedirs=True)
|
||||
report.write_file(session, params, data, path, progress=progress, **kwargs)
|
||||
return output
|
||||
|
||||
|
||||
def get_report_handler(config):
|
||||
"""
|
||||
Create and return the configured :class:`ReportHandler` instance.
|
||||
"""
|
||||
spec = config.get('rattail.reports', 'handler')
|
||||
if spec:
|
||||
return load_object(spec)(config)
|
||||
return ReportHandler(config)
|
127
rattail/reporting/reports.py
Normal file
127
rattail/reporting/reports.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail 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 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail 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
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Report Definitions
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import six
|
||||
|
||||
from rattail.util import progress_loop
|
||||
|
||||
|
||||
@six.python_2_unicode_compatible
|
||||
class Report(object):
|
||||
"""
|
||||
Base class for all reports.
|
||||
"""
|
||||
type_key = None
|
||||
name = None
|
||||
param_sequence = []
|
||||
|
||||
def __init__(self, config=None, **kwargs):
|
||||
self.config = config
|
||||
self.enum = config.get_enum() if config else None
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name or "")
|
||||
|
||||
def collect_static_params(self, **kwargs):
|
||||
"""
|
||||
Returns the list of "static" params defined on the report class.
|
||||
"""
|
||||
params = []
|
||||
possible_params = self.param_sequence or dir(self)
|
||||
for name in possible_params:
|
||||
|
||||
# skip anything private-looking
|
||||
if name.startswith('_'):
|
||||
continue
|
||||
|
||||
obj = getattr(self, name)
|
||||
if isinstance(obj, ReportParam):
|
||||
|
||||
# make sure parameter knows its own name
|
||||
obj.name = name
|
||||
params.append(obj)
|
||||
|
||||
return params
|
||||
|
||||
def make_params(self, session, **kwargs):
|
||||
"""
|
||||
Should return a list of :class:`ReportParam` objects, which will define
|
||||
user input options when generating a new report. Default behavior is
|
||||
to return the list of "static" params which are defined on the report
|
||||
class itself, i.e. the result of :meth:`collect_static_params()`.
|
||||
"""
|
||||
return self.collect_static_params(**kwargs)
|
||||
|
||||
def make_data(self, session, params, progress=None, **kwargs):
|
||||
"""
|
||||
Should generate and return a dict which contains all relevant data for
|
||||
the report, and which will be used when writing the output file.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def make_report_name(self, session, params, data, **kwargs):
|
||||
"""
|
||||
Should return a "name" for the specific report being generated. This
|
||||
name is set on the :class:`~rattail.db.model.reports.ReportOutput`
|
||||
database record, for instance. It needn't be unique as far as the DB
|
||||
schema is concerned.
|
||||
"""
|
||||
return self.name or self.type_key or ""
|
||||
|
||||
def make_filename(self, session, params, data, **kwargs):
|
||||
"""
|
||||
Should generate and return a filename for the report output, according
|
||||
to the given params and data.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def write_file(self, session, params, data, path, progress=None, **kwargs):
|
||||
"""
|
||||
Should write the output file for the report with the given data and to
|
||||
the given filename.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def progress_loop(self, func, items, factory, **kwargs):
|
||||
return progress_loop(func, items, factory, **kwargs)
|
||||
|
||||
|
||||
class ReportParam(object):
|
||||
"""
|
||||
Base class for report parameter definitions.
|
||||
"""
|
||||
|
||||
def __init__(self, typ=str, required=True, doc="", **kwargs):
|
||||
self.type = typ
|
||||
self.required = required
|
||||
self.__doc__ == doc
|
||||
kwargs.pop('__doc__', None)
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
Loading…
Reference in a new issue