4. Using Files¶
This tutorial is based on example code which can be found in the TRAC GitHub Repository under examples/models/python.
Not every model input or output is a table. The runtime also supports files - images, documents, or any other binary content - as first-class inputs and outputs alongside tabular datasets.
Writing a file output¶
File inputs and outputs are defined with define_input() and
define_output(), using a
FileType in place of a schema.
CommonFileTypes provides a set of ready-made file types
for common formats, or a custom one can be built with
define_file_type().
This example reads a tabular dataset and writes out an SVG chart built with Matplotlib:
32 def define_inputs(self) -> _tp.Dict[str, trac.ModelInputSchema]:
33
34 quarterly_sales_schema = trac.load_schema(schemas, "profit_by_region.csv")
35 quarterly_sales = trac.define_input(quarterly_sales_schema, label="Quarterly sales data")
36
37 return { "quarterly_sales": quarterly_sales }
38
39 def define_outputs(self) -> _tp.Dict[str, trac.ModelOutputSchema]:
40
41 sales_report = trac.define_output(trac.CommonFileTypes.SVG, label="Quarterly sales report")
42
43 return { "sales_report": sales_report }
To write a file output, use put_file_stream(),
which gives a writable binary stream for the output. The stream must be used in a with block, as
shown here where the Matplotlib figure is saved directly into the output stream.
45 def run_model(self, ctx: trac.TracContext):
46
47 matplotlib.use("agg")
48
49 quarterly_sales = ctx.get_pandas_table("quarterly_sales")
50
51 regions = quarterly_sales["region"]
52 values = quarterly_sales["gross_profit"]
53
54 fig = plt.figure()
55
56 plt.bar(regions, values)
57 plt.title("Profit by region report")
58 plt.xlabel("Region")
59 plt.ylabel("Gross profit")
60
61 with ctx.put_file_stream("sales_report") as sales_report:
62 plt.savefig(sales_report, format='svg')
63
64 plt.close(fig)
There is also a put_file() method that takes the
file content directly as bytes, for cases where the whole file is already available in memory rather
than being written incrementally to a stream.
Reading and writing files¶
Reading a file input works the same way, in reverse. This example reads a PowerPoint template as a file input, along with a tabular dataset, and produces a new PowerPoint file with a chart added to it:
34 def define_inputs(self) -> _tp.Dict[str, trac.ModelInputSchema]:
35
36 quarterly_sales_schema = trac.load_schema(schemas, "profit_by_region.csv")
37 quarterly_sales = trac.define_input(quarterly_sales_schema, label="Quarterly sales data")
38 report_template = trac.define_input(trac.CommonFileTypes.POWERPOINT, label="Quarterly sales report template")
39
40 return {
41 "quarterly_sales": quarterly_sales,
42 "report_template": report_template }
43
44 def define_outputs(self) -> _tp.Dict[str, trac.ModelOutputSchema]:
45
46 sales_report = trac.define_output(trac.CommonFileTypes.POWERPOINT, label="Quarterly sales report")
47
48 return { "sales_report": sales_report }
get_file_stream() gives a readable binary
stream for a file input, again used in a with block. The runtime guarantees that the file’s
recorded mime type and extension match what was declared in define_input(), but does not inspect or guarantee anything about the content of the
file itself.
50 def run_model(self, ctx: trac.TracContext):
51
52 quarterly_sales = ctx.get_pandas_table("quarterly_sales")
53
54 with ctx.get_file_stream("report_template") as report_template:
55 presentation = pptx.Presentation(report_template)
56
57 slide = presentation.slides.add_slide(presentation.slide_layouts[5])
58
59 title_frame = slide.shapes[0].text_frame
60 title_frame.text = 'Profit by Region Report'
61
62 # define chart data ---------------------
63 chart_data = CategoryChartData()
64 chart_data.categories = quarterly_sales["region"]
65 chart_data.add_series('Gross Profit', quarterly_sales["gross_profit"])
66
67 # add chart to slide --------------------
68 x, y, cx, cy = Inches(2), Inches(2), Inches(6), Inches(4.5)
69 slide.shapes.add_chart(XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, chart_data)
70
71 with ctx.put_file_stream("sales_report") as sales_report:
72 presentation.save(sales_report)
As with writing, there is a get_file() method that
returns the whole file as bytes, for cases where streaming isn’t needed.
See also
Full source code is available for the Matplotlib File IO example and the PowerPoint File IO example on GitHub.