Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return table from plugin #22

Merged
merged 9 commits into from
Jan 16, 2018
Merged

Return table from plugin #22

merged 9 commits into from
Jan 16, 2018

Conversation

tobiaslindulf
Copy link
Contributor

Support for returning table from plugin.
Updated protocol with TableDescription.
Python Examples updated with examples of returning a table.
Prepared for release of version 1.1.0.

Status

[ ] Under development
[x ] Waiting for code review
[x ] Waiting for merge

Information

[ ] Contains breaking changes
[x ] Contains new API(s)
[x ] Contains documentation
[x ] Contains test
[x ] Contains examples

To-do list

[ ] [Fix this thing]
[ ] [Fix another thing]
[ ] ...

@@ -10,9 +31,9 @@ There are three possible RPC methods that Qlik can call.

But what is the difference between a _script function_ and a _plugin function_?

For script functions you write any script directly in the Qlik client, and pass it as a parameter to the SSE plugin in one of the many pre-defined script functions. The plugin functions, on the other hand, are defined and implemented on the plugin side. The user calls the function directly by name and inserts the data as parameters in the Qlik client. The two options are described in detail below.
For script functions you write any script directly in the Qlik client, and pass it as a parameter to the SSE plugin in one of the many pre-defined script functions. The plugin functions, on the other hand, are defined and implemented on the plugin side. The user calls the function directly by name and inserts the data as parameters in the Qlik client. The two options are described in details below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the previous text was correct (but don't trust my English too much). I would prefer "The two options are described in detail below" without the added "s".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, I'll fix that :)

# Iterating over rows
for row in request_rows.rows:
# Retrieve numerical value of parameter and append to the params variable
# Length of param is 1 since one column is received, the [0] collects the first value in the list
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is a bit confusing, is 'the params variable' the result? And 'Length of param is 1 since one column is received' kind of implies that the function only expects one column but the function returns two columns...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In earlier examples/versions of the example, 'row' has been called 'param'. Updating the comment must have been missed in the change. I can change that as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okej :). The code is included in one .md document as well, you might want to change that one too.

Should it be length of row is 2 then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out, I'll change it there as well. I plan to change the documentation for the python examples to avoid this issue with duplicated information. We'll see when I have the time to do that.

Yes in this case, 'row' will be of length 2.

@josefinestal
Copy link
Contributor

josefinestal commented Dec 19, 2017

For your information, the commits will be squashed later.

def send_table_description(table, context):
"""
# TableDescription is only handled in Qlik if sent from a 'Load ... Extension ...' script.
# If tableDescription is set when evaluating an expression the header will be neglected
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ignored instead of neglected maybe?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, I'll fix it!

README.md Outdated

| __Latest Product Version__ | __SSE Supported__ |
| ----- | ----- |
| Qlik Sense February 2018 | v1.1.0 |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to quickly be able to find which new features are supported in each version. Mostly to make it clearer that Table Load is supported in v1.1.0 and that it is not supported in v1.0.0.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This information will be included in the release notes to v1.1.0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, that should be fine then.

- If less rows, or lower cardinality, is sent back from the plugin, Qlik will add null values to match the number of rows sent from Qlik to the plugin originally. Note that the mapping may not work as intended if less data is sent back.
- If higher-order data is sent back, either too many rows or more than one column, Qlik will neglect the additional data and only take the first _n_ rows in the first column, assuming _n_ is the number of rows sent to the plugin. Note that the mapping may not work as intended if more data is sent back.
#### Expressions using SSE must persist the cardinality
If using SSE in a chart expression or in the Qlik load script (the `LOAD ... EXTENSION ...` statement excluded), you should preserve the cardinality and return a single column. In the case of aggregations, the response column should contain a single value (one row). In the case of tensor functions, the response column should contain the same number of rows as the request.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you use SSE in a chart expression or in the Qlik load script (the LOAD ... EXTENSION ... statement excluded), you must preserve the cardinality and return a single column. In the case of aggregations, the response column must contain a single value (one row). In the case of tensor functions, the response column must contain the same number of rows as the request.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They actually do not have to, therefore we exchanged the word must to should, since it still works in some way even if they are doing it wrong. So I think we stick to the word should.

}
```

### `SumOfRows` function
The first function is a tensor function adding two columns row wise. We iterate over the `BundledRows` and extract the numerical values, which we then add togehter and transform into the desired form.
The `SumOfRows` function is a tensor function summing two columns row wise. We iterate over the `BundledRows` and extract the numerical values, which we then add togehter and transform into the desired form.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

row wise --> row-wise
togehter --> together

@@ -205,13 +208,59 @@ The second function is an aggregation and sums the values in a column. We iterat
# Yield the row data constructed
yield SSE.BundledRows(rows=[SSE.Row(duals=duals)])
```
### `MaxOfColumns_2` function
The `MaxOfColumns_2` function computes the maximum in each of two columns and returns the maximum values in two columns, therefore making it appropriate to be used from the Qlik load script. As you can see the function also set the TableDescription header before sending the result.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see, the function also sets the TableDescription header before sending the result.


The aggregating script function is called with the expression `Column.ScriptAggr('sum(args[0])', A)`, where `'sum(args[0])'` is the script and `A` is the data field. The script returns a single value when evaluated. The second script call is `Column.ScriptEval('args[0]+args[1]',A,B)` with the script `'args[0]+args[1]'` adding the two parameters `A` and `B` row-wise. The result is an array with five values, each a sum of the corresponding values in `A` and `B`.

The user defined function calls are straightforward as implemented. The aggregating function `SumOfColumn` is called with `Column.SumOfColumn(A)` and the tensor function `SumOfRows` is called with `Column.SumOfRows(A,B)`.
The user defined function calls are straightforward to call since they become integrated in the script syntax.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user-defined functions are straightforward to call, since they become integrated in the script syntax.


It is possible to make an SSE call from the load script or from a chart expression. The syntax of the call is the same regardless of where you choose to use it.
Returning a table from the SSE plugin can be done using the `LOAD ... EXTENSION ...` statement in the Qlik Load script. Read more about the syntax for table load using SSE in the [Qlik Sense help](http://help.qlik.com/en-US/sense/February2018/Subsystems/Hub/Content/Scripting/ScriptRegularStatements/Load.htm) .

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can return a table from the SSE plugin using the LOAD ... EXTENSION ... statement in the Qlik Load script.

If the parameter is of type _Dual_ the plugin will create two additional columns in the `q` data frame, with the string and numerical representation. The column names will have the base as the parameter name but will end with '_str' and '_num' respectively. For example, a parameter called `Bar` with datatype _Dual_ will result in three columns in `q`: `Bar`, `Bar_str` and `Bar_num`. `Bar` will contain strings and numerics, `Bar_str` will contain only strings and `Bar_num` only numerics.

### TableDescription
In the load script, when using the `Load ... Extension ...` syntax you can create the `TableDescription` message within the script. This can be useful if you for instance want to name, set tags, or change datatype of the fields you are sending back to Qlik. Read more about what metadata can be included in the `TableDescription` in in the [SSE_Protocol.md](../../../docs/SSE_Protocol.md).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be useful if, for example, you want to name, set tags for, or change the datatype of the fields you are sending back to Qlik.

Note that if a `TableDescription` is sent, the number of fields in the message must match the number of fields of data sent back to Qlik.

### Result
With the change to using the `exec` method to evaluate the script, there are some changes regarding what's possible to write in the script. See the Python documentation of `exec` [here](https://docs.python.org/3/library/functions.html#exec). One of them being that the `exec` method does not return anything. We must therefore set the result to a specific variable which we have chosen to call `qResult`. If nothing was set to the variable no data will be returned to Qlik. Note that `qResult` is not required to be a pandas data frame.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One change is that the exec method does not return anything. We must therefore set the result to a specific variable, which we have chosen to call qResult. If nothing was set to the variable, no data will be returned to Qlik. Note that qResult is not required to be a Pandas data frame.



## Qlik documents
An example document is given for Qlik Sense (SSE_Full_Script_Support_pandas.qvf) and it's the same as the original Full Script Support example but with modified scripts to work with the pandas implementation and the use of `exec`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We provide an example Qlik Sense document (SSE_Full_Script_Support_pandas.qvf). It's the same as the original Full Script Support example, but with modified scripts to work with the Pandas implementation and the use of exec.


## Qlik documents
An example document is given for Qlik Sense (SSE_Column_Operations.qvf) and QlikView (SSE_Column_Operations.qvw). There are are two sheets in this example. One contains script calls and the other contains user defined function calls. We demonstrate a tensor function, which sums two columns row-wise, and an aggregating function, which sums all rows in a column returning a single value. The data loaded in the Data Load Editor are two fields, *A* and *B*, each of which contain five numeric values.
An example document is given for Qlik Sense (SSE_Column_Operations.qvf) and QlikView (SSE_Column_Operations.qvw).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We provide example documents for Qlik Sense (SSE_Column_Operations.qvf) and QlikView (SSE_Column_Operations.qvw).

## Qlik documents
An example document is given for Qlik Sense (SSE_Hello_World.qvf) and QlikView (SSE_Hello_World.qvw). The example consists of three sheets, one with script function calls and two with user defined function calls. The function calls on each of the first two sheets demonstrate the same functionality. We use a table for the tensor call and a KPI object for the aggregation call. On the third sheet we demonstrate enabling and disabling the cache for a specific function. A field called __HelloWorldData__ consisting of two rows of strings is loaded into the Qlik engine.
An example document is given for Qlik Sense (SSE_Hello_World.qvf) and QlikView (SSE_Hello_World.qvw). The example consists of four sheets, one with script function calls, two with user defined function calls and one with the result of table load. The function calls on each of the first two sheets demonstrate the same functionality. We use a table for the tensor call and a KPI object for the aggregation call. On the third sheet we demonstrate enabling and disabling the cache for a specific function. A field called __HelloWorldData__ consisting of two rows of strings is loaded into the Qlik engine.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We provide example documents for Qlik Sense (SSE_Hello_World.qvf) and QlikView (SSE_Hello_World.qvw).

@josefinestal josefinestal force-pushed the table-support branch 5 times, most recently from c064516 to f27f0e3 Compare December 28, 2017 10:47
@josefinestal
Copy link
Contributor

Please note that the history of the branch is rewritten due to my cleanup of all commits. If you plan to make any changes, make sure your local repository is updated with the latest changes. Thanks!

Copy link
Contributor Author

@tobiaslindulf tobiaslindulf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reviewed all changes and it looks good.
I have also run all the examples and it works good.
Waiting for the last change before approving.

Copy link
Contributor Author

@tobiaslindulf tobiaslindulf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good. Approved.

@josefinestal josefinestal merged commit 92b91ec into master Jan 16, 2018
@josefinestal josefinestal deleted the table-support branch January 16, 2018 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants