= S3XRC Mini-Tutorial = - [wiki:S3XRC_Recipes S3XRC Recipes and Tutorials] == Report Function for your Resource == === The Idea === You have a module {{{xxx}}} and within it a resource {{{yyy}}}, which has a number of components. You're providing CRUD functions for the resource and its components using {{{shn_rest_controller()}}}. Now you want to provide a reporting function for this resource, where the user can select records using a search form and then generate a summary report that can be exported in various formats, among others XLS, PDF and SVG. This tutorial shows you how this can be integrated in the REST interface of your resource. === Creating a Custom Method Handler === First of all, implement your reporting function as a custom method handler. Add this function to your model file and add it as a method to your resource, like: {{{ def shn_xxx_yyy_report(r, **attr): # Report generating code goes here s3xrc.model.set_method("xxx", "yyy", method="report", action=shn_xxx_yyy_report) }}} A custom method handler has to take two arguments: - {{{r}}} is the respective S3Request object, which represents the current request - {{{attr}}} is the same dict of named arguments that have been passed to shn_rest_controller Once you have done this, you can invoke your method handler from the URL: {{{ http://localhost:8000/eden/xxx/yyy/report }}} The advantage of this is that this even understands URLs like: {{{ http://localhost:8000/eden/xxx/yyy/1/report http://localhost:8000/eden/xxx/yyy/report?yyy.id=1,2,3 http://localhost:8000/eden/xxx/yyy/report?yyy.field1__like=abc }}} meaning, it already implements a RESTful API for your reporting function, e.g. does the parsing/validating of the URL for you, implements the full range of URL queries for your resource and so forth. Nothing you need to care about now. === Providing different Report Formats === As mentioned before, you want to provide the report in various formats. To know which format has been requested, use r.representation: {{{ def shn_xxx_yyy_report(r, **attr): if r.representation == "html": # HTML report generating code goes here: # "output" gets a dict of variables to be passed to the view response.view = "xxx/yyy_report.html" # <== choose the view template return output # <== return the output to the view elif r.representation == "xls": # XLS report generating code goes here # "output" gets the XLS contents return output # <== return the output to the view elif r.representation == "pdf": # PDF report generating code goes here # "output" takes the PDF contents return output # <== return the output to the view elif r.representation == "svg": # SVG report generating code goes here # "output" takes the SVG as a string return output # <== return the output to the view else: # Unsupported format raise HTTP(501, body=s3xrc.ERROR.BAD_FORMAT) s3xrc.model.set_method("xxx", "yyy", method="report", action=shn_xxx_yyy_report) }}} Now your method handler recognises the requested format as specified in the URL by either a filename extension or the {{{format}}} variable: {{{ http://localhost:8000/eden/xxx/yyy/4/report.xls http://localhost:8000/eden/xxx/yyy.pdf/6 http://localhost:8000/eden/xxx/yyy/report?format=svg }}} === How to get at the data === Now, how can you find out which data have to be processed by your reporting function, and yet more important: how can you get at them? {{{r}}} (the {{{S3Request}}} object) contains an interface to your resource as {{{S3Resource}}} object in: {{{r.resource}}} And this is what you can use to access your data. Some examples: {{{ def shn_xxx_yyy_report(r, **attr): resource = r.resource # <== Get the resource if r.representation == "html": rows = resource.records() # <== get all records elif r.representation == "xls": for record in resource: # <== interate through the records ... elif r.representation == "pdf": # Access components record-wise: for record in resource: component_set = resource(record, component="component_name") for component_record in component_set: ... elif r.representation == "svg": # Modifying the resource query before accessing records filter = resource.get_query() filter = filter & (db.xxx_yyy.field5 == "value") resource.build_query(filter = filter) # ...and then iterate through the records: for record in resource: ... else: # Unsupported format raise HTTP(501, body=s3xrc.ERROR.BAD_FORMAT) s3xrc.model.set_method("xxx", "yyy", method="report", action=shn_xxx_yyy_report) }}} === The sum of all values in a field... === The case: Your resource contains a "timestmp" field (type datetime) and a field "cost" (type double), and now the user shall select a time interval in aform, and your report function shall provide the sum of all "cost" for those records with a timestmp within the selected time interval. Let's go: First of all, we implement our method handler as before: {{{ def shn_xxx_yyy_report(r, **attr): # Report generating code goes here s3xrc.model.set_method("xxx", "yyy", method="report", action=shn_xxx_yyy_report) }}} Now we add the HTML representation: {{{ def shn_xxx_yyy_report(r, **attr): if r.representation == "html": output = dict() return output s3xrc.model.set_method("xxx", "yyy", method="report", action=shn_xxx_yyy_report) }}} ...to be continued...