core
In this section, we will see how to expand and modify pyreports core objects.
Expand Executor
It is possible that in some particular case, it is necessary to have custom methods not included in the objects at our disposal. This concept extends to python in general, but we will focus on this library.
Custom map method
The map
method of the Executor
class accepts a function as an argument that it will call for each element of each row of the Dataset
included in the Executor
object.
def map(self, key, column=None):
"""Apply function to data
:param key: function that takes a single argument
:param column: select column name or index number
:return: None
"""
if callable(key):
ret_data = tablib.Dataset(headers=self.data.headers)
for row in self:
# Apply function to data
new_row = list()
for field in row:
new_row.append(key(field))
ret_data.append(new_row)
self.data = ret_data
else:
raise ValueError(f"{key} isn't function object")
# Return all data or single column
if column and self.data.headers:
self.data = self.select_column(column)
There may be a need to apply the function on the entire row. Personalization could be done like this:
import pyreports
import tablib
# Define my Executor class
class MyExecutor(pyreports.Executor):
# My custom map method
def map(self, key, column=None):
if callable(key):
ret_data = tablib.Dataset(headers=self.data.headers)
for row in self:
# Apply function to data
ret_data.append(key(row))
self.data = ret_data
else:
raise ValueError(f"{key} isn't function object")
# Return all data or single column
if column and self.data.headers:
self.data = self.select_column(column)
# Test my map
exec = MyExecutor([('Arthur', 'Dent', 55000), ('Ford', 'Prefect', 65000)], header=['name', 'surname', 'salary'])
# Function than accept row (iterable)
def stringify(row):
return [str(item) for item in row]
exec.map(stringify)
Add method
You can also add new functionality to the Executor
object. We are going to add a method to view the data content of an Executor
.
import pyreports
# Define my Executor class
class MyExecutor(pyreports.Executor):
def __str__(self):
return self.data
# Print data
exec = MyExecutor([('Arthur', 'Dent', 55000), ('Ford', 'Prefect', 65000)], header=['name', 'surname', 'salary'])
print(exec)
Expand Report
The Report
object is really versatile. It is a representation of the report’s workflow in full. However, there may be greater needs.
For example, before saving the output, make a certain request or save the output before and after processing.
To “break” the working process of the Report object, you need to expand it and re-implement its methods.
Save the origin
As anticipated, sometimes we need to save the data before it is processed.
To do this, we need to implement a new method to augment or modify the workflow.
In this way, we are going to run this worklow:
[INPUT] -> [SAVE ORIGIN] -> [PROCESS] -> [OUTPUT]
import pyreports
import tablib
import os
# Define my Executor class
class MyReport(pyreports.Report):
def save_origin(self):
# Save origin in origin file
if self.output:
self.output.write(self.data)
os.rename(self.output.file, 'origin_' + self.output.file)
# Process report
self.export()
# Test MyReport
salary55k = pyreports.manager('csv', '/tmp/salary55k.csv')
mydata = tablib.Dataset([('Arthur', 'Dent', 55000), ('Ford', 'Prefect', 65000)], headers=['name', 'surname', 'salary'])
report_only_55k = MyReport(mydata, filters=[55000], title='Report salary 55k', output=salary55k)
# My workflow report: [INPUT] -> [SAVE ORIGIN] -> [PROCESS] -> [OUTPUT]
report_only_55k.save_origin()
Always print
Another highly requested feature is to save and print at the same time. Much like the Unix tee
shell command,
we will implement the new functionality in our custom Report object.
import pyreports
import tablib
# Define my Executor class
class MyReport(pyreports.Report):
def tee(self):
# Print data...
print(self)
# ...and save!
self.export()
# Test MyReport
salary55k = pyreports.manager('csv', '/tmp/salary55k.csv')
mydata = tablib.Dataset([('Arthur', 'Dent', 55000), ('Ford', 'Prefect', 65000)], headers=['name', 'surname', 'salary'])
report_only_55k = MyReport(mydata, filters=[55000], title='Report salary 55k', output=salary55k)
# Print and export
report_only_55k.tee()
Extend ReportBook
The ReportBook
object is a collection of Report
type objects.
When you iterate over an object of this type, you get a generator that returns the Report objects it contains one at a time.
Note
Nothing prevents that you can also insert the MyReport
classes created previously. They are also subclasses of Reports
.
Book to dict
One of the features that might interest you is to export a ReportBook as if it were a dictionary.
import pyreports
import tablib
# Instantiate the Report objects
mydata = tablib.Dataset([('Arthur', 'Dent', 55000), ('Ford', 'Prefect', 65000)], headers=['name', 'surname', 'salary'])
report_only_55k = pyreports.Report(mydata, filters=[55000], title='Report salary 55k')
report_only_65k = pyreports.Report(mydata, filters=[65000], title='Report salary 65k')
class MyReportBook(pyreports.ReportBook):
def to_dict(self):
return {report.title: report for report in self if report.title}
# Test my book
salary = MyReportBook([report_only_55k, report_only_65k])
salary.to_dict() # {'Report salary 55k': <Report object, title=Report salary 55k>, 'Report salary 65k': <Report object, title=Report salary 65k>}