├── Basket
├── Python Basket Analysis AAI.qvf
├── QlikDataFiles
│ ├── orders.qvd
│ ├── products.qvd
│ └── users.qvd
├── ServerSideExtension_pb2.py
├── __init__.py
├── __main__.py
├── functions.json
├── logger.config
├── logs
│ ├── SSEPlugin.log
│ └── SSEPlugin.log.2019-01-28_14
├── readme.md
├── scripteval.py
└── ssedata.py
├── LICENSE
├── LinearRegression
├── ExtensionService_linearRegression.py
├── FuncDefs_linearRegression.json
├── Python Linear Regression.qvf
├── README.md
├── SSEData_linearRegression.py
├── ScriptEval_linearRegression.py
├── ServerSideExtension_pb2.py
├── logger.config
└── logs
│ └── SSEPlugin.log
└── README.md
/Basket/Python Basket Analysis AAI.qvf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/Basket/Python Basket Analysis AAI.qvf
--------------------------------------------------------------------------------
/Basket/QlikDataFiles/orders.qvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/Basket/QlikDataFiles/orders.qvd
--------------------------------------------------------------------------------
/Basket/QlikDataFiles/products.qvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/Basket/QlikDataFiles/products.qvd
--------------------------------------------------------------------------------
/Basket/QlikDataFiles/users.qvd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/Basket/QlikDataFiles/users.qvd
--------------------------------------------------------------------------------
/Basket/ServerSideExtension_pb2.py:
--------------------------------------------------------------------------------
1 | # Generated by the protocol buffer compiler. DO NOT EDIT!
2 | # source: ServerSideExtension.proto
3 |
4 | import sys
5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
6 | from google.protobuf.internal import enum_type_wrapper
7 | from google.protobuf import descriptor as _descriptor
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | from google.protobuf import descriptor_pb2
12 | # @@protoc_insertion_point(imports)
13 |
14 | _sym_db = _symbol_database.Default()
15 |
16 |
17 |
18 |
19 | DESCRIPTOR = _descriptor.FileDescriptor(
20 | name='ServerSideExtension.proto',
21 | package='qlik.sse',
22 | syntax='proto3',
23 | serialized_pb=_b('\n\x19ServerSideExtension.proto\x12\x08qlik.sse\"\x07\n\x05\x45mpty\"?\n\tParameter\x12$\n\x08\x64\x61taType\x18\x01 \x01(\x0e\x32\x12.qlik.sse.DataType\x12\x0c\n\x04name\x18\x02 \x01(\t\"T\n\x10\x46ieldDescription\x12$\n\x08\x64\x61taType\x18\x01 \x01(\x0e\x32\x12.qlik.sse.DataType\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04tags\x18\x03 \x03(\t\"\xb1\x01\n\x12\x46unctionDefinition\x12\x0c\n\x04name\x18\x01 \x01(\t\x12,\n\x0c\x66unctionType\x18\x02 \x01(\x0e\x32\x16.qlik.sse.FunctionType\x12&\n\nreturnType\x18\x03 \x01(\x0e\x32\x12.qlik.sse.DataType\x12#\n\x06params\x18\x04 \x03(\x0b\x32\x13.qlik.sse.Parameter\x12\x12\n\nfunctionId\x18\x05 \x01(\x05\"\x85\x01\n\x0c\x43\x61pabilities\x12\x13\n\x0b\x61llowScript\x18\x01 \x01(\x08\x12/\n\tfunctions\x18\x02 \x03(\x0b\x32\x1c.qlik.sse.FunctionDefinition\x12\x18\n\x10pluginIdentifier\x18\x03 \x01(\t\x12\x15\n\rpluginVersion\x18\x04 \x01(\t\"(\n\x04\x44ual\x12\x0f\n\x07numData\x18\x01 \x01(\x01\x12\x0f\n\x07strData\x18\x02 \x01(\t\"$\n\x03Row\x12\x1d\n\x05\x64uals\x18\x01 \x03(\x0b\x32\x0e.qlik.sse.Dual\"*\n\x0b\x42undledRows\x12\x1b\n\x04rows\x18\x01 \x03(\x0b\x32\r.qlik.sse.Row\"\xa0\x01\n\x13ScriptRequestHeader\x12\x0e\n\x06script\x18\x01 \x01(\t\x12,\n\x0c\x66unctionType\x18\x02 \x01(\x0e\x32\x16.qlik.sse.FunctionType\x12&\n\nreturnType\x18\x03 \x01(\x0e\x32\x12.qlik.sse.DataType\x12#\n\x06params\x18\x04 \x03(\x0b\x32\x13.qlik.sse.Parameter\"<\n\x15\x46unctionRequestHeader\x12\x12\n\nfunctionId\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\t\"I\n\x13\x43ommonRequestHeader\x12\r\n\x05\x61ppId\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rdinality\x18\x03 \x01(\x03\"b\n\x10TableDescription\x12*\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x1a.qlik.sse.FieldDescription\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x14\n\x0cnumberOfRows\x18\x03 \x01(\x03*-\n\x08\x44\x61taType\x12\n\n\x06STRING\x10\x00\x12\x0b\n\x07NUMERIC\x10\x01\x12\x08\n\x04\x44UAL\x10\x02*7\n\x0c\x46unctionType\x12\n\n\x06SCALAR\x10\x00\x12\x0f\n\x0b\x41GGREGATION\x10\x01\x12\n\n\x06TENSOR\x10\x02\x32\xd6\x01\n\tConnector\x12<\n\x0fGetCapabilities\x12\x0f.qlik.sse.Empty\x1a\x16.qlik.sse.Capabilities\"\x00\x12\x45\n\x0f\x45xecuteFunction\x12\x15.qlik.sse.BundledRows\x1a\x15.qlik.sse.BundledRows\"\x00(\x01\x30\x01\x12\x44\n\x0e\x45valuateScript\x12\x15.qlik.sse.BundledRows\x1a\x15.qlik.sse.BundledRows\"\x00(\x01\x30\x01\x42\x03\xf8\x01\x01\x62\x06proto3')
24 | )
25 | _sym_db.RegisterFileDescriptor(DESCRIPTOR)
26 |
27 | _DATATYPE = _descriptor.EnumDescriptor(
28 | name='DataType',
29 | full_name='qlik.sse.DataType',
30 | filename=None,
31 | file=DESCRIPTOR,
32 | values=[
33 | _descriptor.EnumValueDescriptor(
34 | name='STRING', index=0, number=0,
35 | options=None,
36 | type=None),
37 | _descriptor.EnumValueDescriptor(
38 | name='NUMERIC', index=1, number=1,
39 | options=None,
40 | type=None),
41 | _descriptor.EnumValueDescriptor(
42 | name='DUAL', index=2, number=2,
43 | options=None,
44 | type=None),
45 | ],
46 | containing_type=None,
47 | options=None,
48 | serialized_start=1039,
49 | serialized_end=1084,
50 | )
51 | _sym_db.RegisterEnumDescriptor(_DATATYPE)
52 |
53 | DataType = enum_type_wrapper.EnumTypeWrapper(_DATATYPE)
54 | _FUNCTIONTYPE = _descriptor.EnumDescriptor(
55 | name='FunctionType',
56 | full_name='qlik.sse.FunctionType',
57 | filename=None,
58 | file=DESCRIPTOR,
59 | values=[
60 | _descriptor.EnumValueDescriptor(
61 | name='SCALAR', index=0, number=0,
62 | options=None,
63 | type=None),
64 | _descriptor.EnumValueDescriptor(
65 | name='AGGREGATION', index=1, number=1,
66 | options=None,
67 | type=None),
68 | _descriptor.EnumValueDescriptor(
69 | name='TENSOR', index=2, number=2,
70 | options=None,
71 | type=None),
72 | ],
73 | containing_type=None,
74 | options=None,
75 | serialized_start=1086,
76 | serialized_end=1141,
77 | )
78 | _sym_db.RegisterEnumDescriptor(_FUNCTIONTYPE)
79 |
80 | FunctionType = enum_type_wrapper.EnumTypeWrapper(_FUNCTIONTYPE)
81 | STRING = 0
82 | NUMERIC = 1
83 | DUAL = 2
84 | SCALAR = 0
85 | AGGREGATION = 1
86 | TENSOR = 2
87 |
88 |
89 |
90 | _EMPTY = _descriptor.Descriptor(
91 | name='Empty',
92 | full_name='qlik.sse.Empty',
93 | filename=None,
94 | file=DESCRIPTOR,
95 | containing_type=None,
96 | fields=[
97 | ],
98 | extensions=[
99 | ],
100 | nested_types=[],
101 | enum_types=[
102 | ],
103 | options=None,
104 | is_extendable=False,
105 | syntax='proto3',
106 | extension_ranges=[],
107 | oneofs=[
108 | ],
109 | serialized_start=39,
110 | serialized_end=46,
111 | )
112 |
113 |
114 | _PARAMETER = _descriptor.Descriptor(
115 | name='Parameter',
116 | full_name='qlik.sse.Parameter',
117 | filename=None,
118 | file=DESCRIPTOR,
119 | containing_type=None,
120 | fields=[
121 | _descriptor.FieldDescriptor(
122 | name='dataType', full_name='qlik.sse.Parameter.dataType', index=0,
123 | number=1, type=14, cpp_type=8, label=1,
124 | has_default_value=False, default_value=0,
125 | message_type=None, enum_type=None, containing_type=None,
126 | is_extension=False, extension_scope=None,
127 | options=None),
128 | _descriptor.FieldDescriptor(
129 | name='name', full_name='qlik.sse.Parameter.name', index=1,
130 | number=2, type=9, cpp_type=9, label=1,
131 | has_default_value=False, default_value=_b("").decode('utf-8'),
132 | message_type=None, enum_type=None, containing_type=None,
133 | is_extension=False, extension_scope=None,
134 | options=None),
135 | ],
136 | extensions=[
137 | ],
138 | nested_types=[],
139 | enum_types=[
140 | ],
141 | options=None,
142 | is_extendable=False,
143 | syntax='proto3',
144 | extension_ranges=[],
145 | oneofs=[
146 | ],
147 | serialized_start=48,
148 | serialized_end=111,
149 | )
150 |
151 |
152 | _FIELDDESCRIPTION = _descriptor.Descriptor(
153 | name='FieldDescription',
154 | full_name='qlik.sse.FieldDescription',
155 | filename=None,
156 | file=DESCRIPTOR,
157 | containing_type=None,
158 | fields=[
159 | _descriptor.FieldDescriptor(
160 | name='dataType', full_name='qlik.sse.FieldDescription.dataType', index=0,
161 | number=1, type=14, cpp_type=8, label=1,
162 | has_default_value=False, default_value=0,
163 | message_type=None, enum_type=None, containing_type=None,
164 | is_extension=False, extension_scope=None,
165 | options=None),
166 | _descriptor.FieldDescriptor(
167 | name='name', full_name='qlik.sse.FieldDescription.name', index=1,
168 | number=2, type=9, cpp_type=9, label=1,
169 | has_default_value=False, default_value=_b("").decode('utf-8'),
170 | message_type=None, enum_type=None, containing_type=None,
171 | is_extension=False, extension_scope=None,
172 | options=None),
173 | _descriptor.FieldDescriptor(
174 | name='tags', full_name='qlik.sse.FieldDescription.tags', index=2,
175 | number=3, type=9, cpp_type=9, label=3,
176 | has_default_value=False, default_value=[],
177 | message_type=None, enum_type=None, containing_type=None,
178 | is_extension=False, extension_scope=None,
179 | options=None),
180 | ],
181 | extensions=[
182 | ],
183 | nested_types=[],
184 | enum_types=[
185 | ],
186 | options=None,
187 | is_extendable=False,
188 | syntax='proto3',
189 | extension_ranges=[],
190 | oneofs=[
191 | ],
192 | serialized_start=113,
193 | serialized_end=197,
194 | )
195 |
196 |
197 | _FUNCTIONDEFINITION = _descriptor.Descriptor(
198 | name='FunctionDefinition',
199 | full_name='qlik.sse.FunctionDefinition',
200 | filename=None,
201 | file=DESCRIPTOR,
202 | containing_type=None,
203 | fields=[
204 | _descriptor.FieldDescriptor(
205 | name='name', full_name='qlik.sse.FunctionDefinition.name', index=0,
206 | number=1, type=9, cpp_type=9, label=1,
207 | has_default_value=False, default_value=_b("").decode('utf-8'),
208 | message_type=None, enum_type=None, containing_type=None,
209 | is_extension=False, extension_scope=None,
210 | options=None),
211 | _descriptor.FieldDescriptor(
212 | name='functionType', full_name='qlik.sse.FunctionDefinition.functionType', index=1,
213 | number=2, type=14, cpp_type=8, label=1,
214 | has_default_value=False, default_value=0,
215 | message_type=None, enum_type=None, containing_type=None,
216 | is_extension=False, extension_scope=None,
217 | options=None),
218 | _descriptor.FieldDescriptor(
219 | name='returnType', full_name='qlik.sse.FunctionDefinition.returnType', index=2,
220 | number=3, type=14, cpp_type=8, label=1,
221 | has_default_value=False, default_value=0,
222 | message_type=None, enum_type=None, containing_type=None,
223 | is_extension=False, extension_scope=None,
224 | options=None),
225 | _descriptor.FieldDescriptor(
226 | name='params', full_name='qlik.sse.FunctionDefinition.params', index=3,
227 | number=4, type=11, cpp_type=10, label=3,
228 | has_default_value=False, default_value=[],
229 | message_type=None, enum_type=None, containing_type=None,
230 | is_extension=False, extension_scope=None,
231 | options=None),
232 | _descriptor.FieldDescriptor(
233 | name='functionId', full_name='qlik.sse.FunctionDefinition.functionId', index=4,
234 | number=5, type=5, cpp_type=1, label=1,
235 | has_default_value=False, default_value=0,
236 | message_type=None, enum_type=None, containing_type=None,
237 | is_extension=False, extension_scope=None,
238 | options=None),
239 | ],
240 | extensions=[
241 | ],
242 | nested_types=[],
243 | enum_types=[
244 | ],
245 | options=None,
246 | is_extendable=False,
247 | syntax='proto3',
248 | extension_ranges=[],
249 | oneofs=[
250 | ],
251 | serialized_start=200,
252 | serialized_end=377,
253 | )
254 |
255 |
256 | _CAPABILITIES = _descriptor.Descriptor(
257 | name='Capabilities',
258 | full_name='qlik.sse.Capabilities',
259 | filename=None,
260 | file=DESCRIPTOR,
261 | containing_type=None,
262 | fields=[
263 | _descriptor.FieldDescriptor(
264 | name='allowScript', full_name='qlik.sse.Capabilities.allowScript', index=0,
265 | number=1, type=8, cpp_type=7, label=1,
266 | has_default_value=False, default_value=False,
267 | message_type=None, enum_type=None, containing_type=None,
268 | is_extension=False, extension_scope=None,
269 | options=None),
270 | _descriptor.FieldDescriptor(
271 | name='functions', full_name='qlik.sse.Capabilities.functions', index=1,
272 | number=2, type=11, cpp_type=10, label=3,
273 | has_default_value=False, default_value=[],
274 | message_type=None, enum_type=None, containing_type=None,
275 | is_extension=False, extension_scope=None,
276 | options=None),
277 | _descriptor.FieldDescriptor(
278 | name='pluginIdentifier', full_name='qlik.sse.Capabilities.pluginIdentifier', index=2,
279 | number=3, type=9, cpp_type=9, label=1,
280 | has_default_value=False, default_value=_b("").decode('utf-8'),
281 | message_type=None, enum_type=None, containing_type=None,
282 | is_extension=False, extension_scope=None,
283 | options=None),
284 | _descriptor.FieldDescriptor(
285 | name='pluginVersion', full_name='qlik.sse.Capabilities.pluginVersion', index=3,
286 | number=4, type=9, cpp_type=9, label=1,
287 | has_default_value=False, default_value=_b("").decode('utf-8'),
288 | message_type=None, enum_type=None, containing_type=None,
289 | is_extension=False, extension_scope=None,
290 | options=None),
291 | ],
292 | extensions=[
293 | ],
294 | nested_types=[],
295 | enum_types=[
296 | ],
297 | options=None,
298 | is_extendable=False,
299 | syntax='proto3',
300 | extension_ranges=[],
301 | oneofs=[
302 | ],
303 | serialized_start=380,
304 | serialized_end=513,
305 | )
306 |
307 |
308 | _DUAL = _descriptor.Descriptor(
309 | name='Dual',
310 | full_name='qlik.sse.Dual',
311 | filename=None,
312 | file=DESCRIPTOR,
313 | containing_type=None,
314 | fields=[
315 | _descriptor.FieldDescriptor(
316 | name='numData', full_name='qlik.sse.Dual.numData', index=0,
317 | number=1, type=1, cpp_type=5, label=1,
318 | has_default_value=False, default_value=float(0),
319 | message_type=None, enum_type=None, containing_type=None,
320 | is_extension=False, extension_scope=None,
321 | options=None),
322 | _descriptor.FieldDescriptor(
323 | name='strData', full_name='qlik.sse.Dual.strData', index=1,
324 | number=2, type=9, cpp_type=9, label=1,
325 | has_default_value=False, default_value=_b("").decode('utf-8'),
326 | message_type=None, enum_type=None, containing_type=None,
327 | is_extension=False, extension_scope=None,
328 | options=None),
329 | ],
330 | extensions=[
331 | ],
332 | nested_types=[],
333 | enum_types=[
334 | ],
335 | options=None,
336 | is_extendable=False,
337 | syntax='proto3',
338 | extension_ranges=[],
339 | oneofs=[
340 | ],
341 | serialized_start=515,
342 | serialized_end=555,
343 | )
344 |
345 |
346 | _ROW = _descriptor.Descriptor(
347 | name='Row',
348 | full_name='qlik.sse.Row',
349 | filename=None,
350 | file=DESCRIPTOR,
351 | containing_type=None,
352 | fields=[
353 | _descriptor.FieldDescriptor(
354 | name='duals', full_name='qlik.sse.Row.duals', index=0,
355 | number=1, type=11, cpp_type=10, label=3,
356 | has_default_value=False, default_value=[],
357 | message_type=None, enum_type=None, containing_type=None,
358 | is_extension=False, extension_scope=None,
359 | options=None),
360 | ],
361 | extensions=[
362 | ],
363 | nested_types=[],
364 | enum_types=[
365 | ],
366 | options=None,
367 | is_extendable=False,
368 | syntax='proto3',
369 | extension_ranges=[],
370 | oneofs=[
371 | ],
372 | serialized_start=557,
373 | serialized_end=593,
374 | )
375 |
376 |
377 | _BUNDLEDROWS = _descriptor.Descriptor(
378 | name='BundledRows',
379 | full_name='qlik.sse.BundledRows',
380 | filename=None,
381 | file=DESCRIPTOR,
382 | containing_type=None,
383 | fields=[
384 | _descriptor.FieldDescriptor(
385 | name='rows', full_name='qlik.sse.BundledRows.rows', index=0,
386 | number=1, type=11, cpp_type=10, label=3,
387 | has_default_value=False, default_value=[],
388 | message_type=None, enum_type=None, containing_type=None,
389 | is_extension=False, extension_scope=None,
390 | options=None),
391 | ],
392 | extensions=[
393 | ],
394 | nested_types=[],
395 | enum_types=[
396 | ],
397 | options=None,
398 | is_extendable=False,
399 | syntax='proto3',
400 | extension_ranges=[],
401 | oneofs=[
402 | ],
403 | serialized_start=595,
404 | serialized_end=637,
405 | )
406 |
407 |
408 | _SCRIPTREQUESTHEADER = _descriptor.Descriptor(
409 | name='ScriptRequestHeader',
410 | full_name='qlik.sse.ScriptRequestHeader',
411 | filename=None,
412 | file=DESCRIPTOR,
413 | containing_type=None,
414 | fields=[
415 | _descriptor.FieldDescriptor(
416 | name='script', full_name='qlik.sse.ScriptRequestHeader.script', index=0,
417 | number=1, type=9, cpp_type=9, label=1,
418 | has_default_value=False, default_value=_b("").decode('utf-8'),
419 | message_type=None, enum_type=None, containing_type=None,
420 | is_extension=False, extension_scope=None,
421 | options=None),
422 | _descriptor.FieldDescriptor(
423 | name='functionType', full_name='qlik.sse.ScriptRequestHeader.functionType', index=1,
424 | number=2, type=14, cpp_type=8, label=1,
425 | has_default_value=False, default_value=0,
426 | message_type=None, enum_type=None, containing_type=None,
427 | is_extension=False, extension_scope=None,
428 | options=None),
429 | _descriptor.FieldDescriptor(
430 | name='returnType', full_name='qlik.sse.ScriptRequestHeader.returnType', index=2,
431 | number=3, type=14, cpp_type=8, label=1,
432 | has_default_value=False, default_value=0,
433 | message_type=None, enum_type=None, containing_type=None,
434 | is_extension=False, extension_scope=None,
435 | options=None),
436 | _descriptor.FieldDescriptor(
437 | name='params', full_name='qlik.sse.ScriptRequestHeader.params', index=3,
438 | number=4, type=11, cpp_type=10, label=3,
439 | has_default_value=False, default_value=[],
440 | message_type=None, enum_type=None, containing_type=None,
441 | is_extension=False, extension_scope=None,
442 | options=None),
443 | ],
444 | extensions=[
445 | ],
446 | nested_types=[],
447 | enum_types=[
448 | ],
449 | options=None,
450 | is_extendable=False,
451 | syntax='proto3',
452 | extension_ranges=[],
453 | oneofs=[
454 | ],
455 | serialized_start=640,
456 | serialized_end=800,
457 | )
458 |
459 |
460 | _FUNCTIONREQUESTHEADER = _descriptor.Descriptor(
461 | name='FunctionRequestHeader',
462 | full_name='qlik.sse.FunctionRequestHeader',
463 | filename=None,
464 | file=DESCRIPTOR,
465 | containing_type=None,
466 | fields=[
467 | _descriptor.FieldDescriptor(
468 | name='functionId', full_name='qlik.sse.FunctionRequestHeader.functionId', index=0,
469 | number=1, type=5, cpp_type=1, label=1,
470 | has_default_value=False, default_value=0,
471 | message_type=None, enum_type=None, containing_type=None,
472 | is_extension=False, extension_scope=None,
473 | options=None),
474 | _descriptor.FieldDescriptor(
475 | name='version', full_name='qlik.sse.FunctionRequestHeader.version', index=1,
476 | number=2, type=9, cpp_type=9, label=1,
477 | has_default_value=False, default_value=_b("").decode('utf-8'),
478 | message_type=None, enum_type=None, containing_type=None,
479 | is_extension=False, extension_scope=None,
480 | options=None),
481 | ],
482 | extensions=[
483 | ],
484 | nested_types=[],
485 | enum_types=[
486 | ],
487 | options=None,
488 | is_extendable=False,
489 | syntax='proto3',
490 | extension_ranges=[],
491 | oneofs=[
492 | ],
493 | serialized_start=802,
494 | serialized_end=862,
495 | )
496 |
497 |
498 | _COMMONREQUESTHEADER = _descriptor.Descriptor(
499 | name='CommonRequestHeader',
500 | full_name='qlik.sse.CommonRequestHeader',
501 | filename=None,
502 | file=DESCRIPTOR,
503 | containing_type=None,
504 | fields=[
505 | _descriptor.FieldDescriptor(
506 | name='appId', full_name='qlik.sse.CommonRequestHeader.appId', index=0,
507 | number=1, type=9, cpp_type=9, label=1,
508 | has_default_value=False, default_value=_b("").decode('utf-8'),
509 | message_type=None, enum_type=None, containing_type=None,
510 | is_extension=False, extension_scope=None,
511 | options=None),
512 | _descriptor.FieldDescriptor(
513 | name='userId', full_name='qlik.sse.CommonRequestHeader.userId', index=1,
514 | number=2, type=9, cpp_type=9, label=1,
515 | has_default_value=False, default_value=_b("").decode('utf-8'),
516 | message_type=None, enum_type=None, containing_type=None,
517 | is_extension=False, extension_scope=None,
518 | options=None),
519 | _descriptor.FieldDescriptor(
520 | name='cardinality', full_name='qlik.sse.CommonRequestHeader.cardinality', index=2,
521 | number=3, type=3, cpp_type=2, label=1,
522 | has_default_value=False, default_value=0,
523 | message_type=None, enum_type=None, containing_type=None,
524 | is_extension=False, extension_scope=None,
525 | options=None),
526 | ],
527 | extensions=[
528 | ],
529 | nested_types=[],
530 | enum_types=[
531 | ],
532 | options=None,
533 | is_extendable=False,
534 | syntax='proto3',
535 | extension_ranges=[],
536 | oneofs=[
537 | ],
538 | serialized_start=864,
539 | serialized_end=937,
540 | )
541 |
542 |
543 | _TABLEDESCRIPTION = _descriptor.Descriptor(
544 | name='TableDescription',
545 | full_name='qlik.sse.TableDescription',
546 | filename=None,
547 | file=DESCRIPTOR,
548 | containing_type=None,
549 | fields=[
550 | _descriptor.FieldDescriptor(
551 | name='fields', full_name='qlik.sse.TableDescription.fields', index=0,
552 | number=1, type=11, cpp_type=10, label=3,
553 | has_default_value=False, default_value=[],
554 | message_type=None, enum_type=None, containing_type=None,
555 | is_extension=False, extension_scope=None,
556 | options=None),
557 | _descriptor.FieldDescriptor(
558 | name='name', full_name='qlik.sse.TableDescription.name', index=1,
559 | number=2, type=9, cpp_type=9, label=1,
560 | has_default_value=False, default_value=_b("").decode('utf-8'),
561 | message_type=None, enum_type=None, containing_type=None,
562 | is_extension=False, extension_scope=None,
563 | options=None),
564 | _descriptor.FieldDescriptor(
565 | name='numberOfRows', full_name='qlik.sse.TableDescription.numberOfRows', index=2,
566 | number=3, type=3, cpp_type=2, label=1,
567 | has_default_value=False, default_value=0,
568 | message_type=None, enum_type=None, containing_type=None,
569 | is_extension=False, extension_scope=None,
570 | options=None),
571 | ],
572 | extensions=[
573 | ],
574 | nested_types=[],
575 | enum_types=[
576 | ],
577 | options=None,
578 | is_extendable=False,
579 | syntax='proto3',
580 | extension_ranges=[],
581 | oneofs=[
582 | ],
583 | serialized_start=939,
584 | serialized_end=1037,
585 | )
586 |
587 | _PARAMETER.fields_by_name['dataType'].enum_type = _DATATYPE
588 | _FIELDDESCRIPTION.fields_by_name['dataType'].enum_type = _DATATYPE
589 | _FUNCTIONDEFINITION.fields_by_name['functionType'].enum_type = _FUNCTIONTYPE
590 | _FUNCTIONDEFINITION.fields_by_name['returnType'].enum_type = _DATATYPE
591 | _FUNCTIONDEFINITION.fields_by_name['params'].message_type = _PARAMETER
592 | _CAPABILITIES.fields_by_name['functions'].message_type = _FUNCTIONDEFINITION
593 | _ROW.fields_by_name['duals'].message_type = _DUAL
594 | _BUNDLEDROWS.fields_by_name['rows'].message_type = _ROW
595 | _SCRIPTREQUESTHEADER.fields_by_name['functionType'].enum_type = _FUNCTIONTYPE
596 | _SCRIPTREQUESTHEADER.fields_by_name['returnType'].enum_type = _DATATYPE
597 | _SCRIPTREQUESTHEADER.fields_by_name['params'].message_type = _PARAMETER
598 | _TABLEDESCRIPTION.fields_by_name['fields'].message_type = _FIELDDESCRIPTION
599 | DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY
600 | DESCRIPTOR.message_types_by_name['Parameter'] = _PARAMETER
601 | DESCRIPTOR.message_types_by_name['FieldDescription'] = _FIELDDESCRIPTION
602 | DESCRIPTOR.message_types_by_name['FunctionDefinition'] = _FUNCTIONDEFINITION
603 | DESCRIPTOR.message_types_by_name['Capabilities'] = _CAPABILITIES
604 | DESCRIPTOR.message_types_by_name['Dual'] = _DUAL
605 | DESCRIPTOR.message_types_by_name['Row'] = _ROW
606 | DESCRIPTOR.message_types_by_name['BundledRows'] = _BUNDLEDROWS
607 | DESCRIPTOR.message_types_by_name['ScriptRequestHeader'] = _SCRIPTREQUESTHEADER
608 | DESCRIPTOR.message_types_by_name['FunctionRequestHeader'] = _FUNCTIONREQUESTHEADER
609 | DESCRIPTOR.message_types_by_name['CommonRequestHeader'] = _COMMONREQUESTHEADER
610 | DESCRIPTOR.message_types_by_name['TableDescription'] = _TABLEDESCRIPTION
611 | DESCRIPTOR.enum_types_by_name['DataType'] = _DATATYPE
612 | DESCRIPTOR.enum_types_by_name['FunctionType'] = _FUNCTIONTYPE
613 |
614 | Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict(
615 | DESCRIPTOR = _EMPTY,
616 | __module__ = 'ServerSideExtension_pb2'
617 | # @@protoc_insertion_point(class_scope:qlik.sse.Empty)
618 | ))
619 | _sym_db.RegisterMessage(Empty)
620 |
621 | Parameter = _reflection.GeneratedProtocolMessageType('Parameter', (_message.Message,), dict(
622 | DESCRIPTOR = _PARAMETER,
623 | __module__ = 'ServerSideExtension_pb2'
624 | # @@protoc_insertion_point(class_scope:qlik.sse.Parameter)
625 | ))
626 | _sym_db.RegisterMessage(Parameter)
627 |
628 | FieldDescription = _reflection.GeneratedProtocolMessageType('FieldDescription', (_message.Message,), dict(
629 | DESCRIPTOR = _FIELDDESCRIPTION,
630 | __module__ = 'ServerSideExtension_pb2'
631 | # @@protoc_insertion_point(class_scope:qlik.sse.FieldDescription)
632 | ))
633 | _sym_db.RegisterMessage(FieldDescription)
634 |
635 | FunctionDefinition = _reflection.GeneratedProtocolMessageType('FunctionDefinition', (_message.Message,), dict(
636 | DESCRIPTOR = _FUNCTIONDEFINITION,
637 | __module__ = 'ServerSideExtension_pb2'
638 | # @@protoc_insertion_point(class_scope:qlik.sse.FunctionDefinition)
639 | ))
640 | _sym_db.RegisterMessage(FunctionDefinition)
641 |
642 | Capabilities = _reflection.GeneratedProtocolMessageType('Capabilities', (_message.Message,), dict(
643 | DESCRIPTOR = _CAPABILITIES,
644 | __module__ = 'ServerSideExtension_pb2'
645 | # @@protoc_insertion_point(class_scope:qlik.sse.Capabilities)
646 | ))
647 | _sym_db.RegisterMessage(Capabilities)
648 |
649 | Dual = _reflection.GeneratedProtocolMessageType('Dual', (_message.Message,), dict(
650 | DESCRIPTOR = _DUAL,
651 | __module__ = 'ServerSideExtension_pb2'
652 | # @@protoc_insertion_point(class_scope:qlik.sse.Dual)
653 | ))
654 | _sym_db.RegisterMessage(Dual)
655 |
656 | Row = _reflection.GeneratedProtocolMessageType('Row', (_message.Message,), dict(
657 | DESCRIPTOR = _ROW,
658 | __module__ = 'ServerSideExtension_pb2'
659 | # @@protoc_insertion_point(class_scope:qlik.sse.Row)
660 | ))
661 | _sym_db.RegisterMessage(Row)
662 |
663 | BundledRows = _reflection.GeneratedProtocolMessageType('BundledRows', (_message.Message,), dict(
664 | DESCRIPTOR = _BUNDLEDROWS,
665 | __module__ = 'ServerSideExtension_pb2'
666 | # @@protoc_insertion_point(class_scope:qlik.sse.BundledRows)
667 | ))
668 | _sym_db.RegisterMessage(BundledRows)
669 |
670 | ScriptRequestHeader = _reflection.GeneratedProtocolMessageType('ScriptRequestHeader', (_message.Message,), dict(
671 | DESCRIPTOR = _SCRIPTREQUESTHEADER,
672 | __module__ = 'ServerSideExtension_pb2'
673 | # @@protoc_insertion_point(class_scope:qlik.sse.ScriptRequestHeader)
674 | ))
675 | _sym_db.RegisterMessage(ScriptRequestHeader)
676 |
677 | FunctionRequestHeader = _reflection.GeneratedProtocolMessageType('FunctionRequestHeader', (_message.Message,), dict(
678 | DESCRIPTOR = _FUNCTIONREQUESTHEADER,
679 | __module__ = 'ServerSideExtension_pb2'
680 | # @@protoc_insertion_point(class_scope:qlik.sse.FunctionRequestHeader)
681 | ))
682 | _sym_db.RegisterMessage(FunctionRequestHeader)
683 |
684 | CommonRequestHeader = _reflection.GeneratedProtocolMessageType('CommonRequestHeader', (_message.Message,), dict(
685 | DESCRIPTOR = _COMMONREQUESTHEADER,
686 | __module__ = 'ServerSideExtension_pb2'
687 | # @@protoc_insertion_point(class_scope:qlik.sse.CommonRequestHeader)
688 | ))
689 | _sym_db.RegisterMessage(CommonRequestHeader)
690 |
691 | TableDescription = _reflection.GeneratedProtocolMessageType('TableDescription', (_message.Message,), dict(
692 | DESCRIPTOR = _TABLEDESCRIPTION,
693 | __module__ = 'ServerSideExtension_pb2'
694 | # @@protoc_insertion_point(class_scope:qlik.sse.TableDescription)
695 | ))
696 | _sym_db.RegisterMessage(TableDescription)
697 |
698 |
699 | DESCRIPTOR.has_options = True
700 | DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\370\001\001'))
701 | try:
702 | # THESE ELEMENTS WILL BE DEPRECATED.
703 | # Please use the generated *_pb2_grpc.py files instead.
704 | import grpc
705 | from grpc.beta import implementations as beta_implementations
706 | from grpc.beta import interfaces as beta_interfaces
707 | from grpc.framework.common import cardinality
708 | from grpc.framework.interfaces.face import utilities as face_utilities
709 |
710 |
711 | class ConnectorStub(object):
712 | """*
713 | The communication service provided between the Qlik engine and the plugin.
714 | """
715 |
716 | def __init__(self, channel):
717 | """Constructor.
718 |
719 | Args:
720 | channel: A grpc.Channel.
721 | """
722 | self.GetCapabilities = channel.unary_unary(
723 | '/qlik.sse.Connector/GetCapabilities',
724 | request_serializer=Empty.SerializeToString,
725 | response_deserializer=Capabilities.FromString,
726 | )
727 | self.ExecuteFunction = channel.stream_stream(
728 | '/qlik.sse.Connector/ExecuteFunction',
729 | request_serializer=BundledRows.SerializeToString,
730 | response_deserializer=BundledRows.FromString,
731 | )
732 | self.EvaluateScript = channel.stream_stream(
733 | '/qlik.sse.Connector/EvaluateScript',
734 | request_serializer=BundledRows.SerializeToString,
735 | response_deserializer=BundledRows.FromString,
736 | )
737 |
738 |
739 | class ConnectorServicer(object):
740 | """*
741 | The communication service provided between the Qlik engine and the plugin.
742 | """
743 |
744 | def GetCapabilities(self, request, context):
745 | """/ A handshake call for the Qlik engine to retrieve the capability of the plugin.
746 | """
747 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
748 | context.set_details('Method not implemented!')
749 | raise NotImplementedError('Method not implemented!')
750 |
751 | def ExecuteFunction(self, request_iterator, context):
752 | """/ Requests a function to be executed as specified in the header.
753 | """
754 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
755 | context.set_details('Method not implemented!')
756 | raise NotImplementedError('Method not implemented!')
757 |
758 | def EvaluateScript(self, request_iterator, context):
759 | """/ Requests a script to be evaluated as specified in the header.
760 | """
761 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
762 | context.set_details('Method not implemented!')
763 | raise NotImplementedError('Method not implemented!')
764 |
765 |
766 | def add_ConnectorServicer_to_server(servicer, server):
767 | rpc_method_handlers = {
768 | 'GetCapabilities': grpc.unary_unary_rpc_method_handler(
769 | servicer.GetCapabilities,
770 | request_deserializer=Empty.FromString,
771 | response_serializer=Capabilities.SerializeToString,
772 | ),
773 | 'ExecuteFunction': grpc.stream_stream_rpc_method_handler(
774 | servicer.ExecuteFunction,
775 | request_deserializer=BundledRows.FromString,
776 | response_serializer=BundledRows.SerializeToString,
777 | ),
778 | 'EvaluateScript': grpc.stream_stream_rpc_method_handler(
779 | servicer.EvaluateScript,
780 | request_deserializer=BundledRows.FromString,
781 | response_serializer=BundledRows.SerializeToString,
782 | ),
783 | }
784 | generic_handler = grpc.method_handlers_generic_handler(
785 | 'qlik.sse.Connector', rpc_method_handlers)
786 | server.add_generic_rpc_handlers((generic_handler,))
787 |
788 |
789 | class BetaConnectorServicer(object):
790 | """The Beta API is deprecated for 0.15.0 and later.
791 |
792 | It is recommended to use the GA API (classes and functions in this
793 | file not marked beta) for all further purposes. This class was generated
794 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
795 | """*
796 | The communication service provided between the Qlik engine and the plugin.
797 | """
798 | def GetCapabilities(self, request, context):
799 | """/ A handshake call for the Qlik engine to retrieve the capability of the plugin.
800 | """
801 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
802 | def ExecuteFunction(self, request_iterator, context):
803 | """/ Requests a function to be executed as specified in the header.
804 | """
805 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
806 | def EvaluateScript(self, request_iterator, context):
807 | """/ Requests a script to be evaluated as specified in the header.
808 | """
809 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
810 |
811 |
812 | class BetaConnectorStub(object):
813 | """The Beta API is deprecated for 0.15.0 and later.
814 |
815 | It is recommended to use the GA API (classes and functions in this
816 | file not marked beta) for all further purposes. This class was generated
817 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
818 | """*
819 | The communication service provided between the Qlik engine and the plugin.
820 | """
821 | def GetCapabilities(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
822 | """/ A handshake call for the Qlik engine to retrieve the capability of the plugin.
823 | """
824 | raise NotImplementedError()
825 | GetCapabilities.future = None
826 | def ExecuteFunction(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
827 | """/ Requests a function to be executed as specified in the header.
828 | """
829 | raise NotImplementedError()
830 | def EvaluateScript(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
831 | """/ Requests a script to be evaluated as specified in the header.
832 | """
833 | raise NotImplementedError()
834 |
835 |
836 | def beta_create_Connector_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
837 | """The Beta API is deprecated for 0.15.0 and later.
838 |
839 | It is recommended to use the GA API (classes and functions in this
840 | file not marked beta) for all further purposes. This function was
841 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
842 | request_deserializers = {
843 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.FromString,
844 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.FromString,
845 | ('qlik.sse.Connector', 'GetCapabilities'): Empty.FromString,
846 | }
847 | response_serializers = {
848 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.SerializeToString,
849 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.SerializeToString,
850 | ('qlik.sse.Connector', 'GetCapabilities'): Capabilities.SerializeToString,
851 | }
852 | method_implementations = {
853 | ('qlik.sse.Connector', 'EvaluateScript'): face_utilities.stream_stream_inline(servicer.EvaluateScript),
854 | ('qlik.sse.Connector', 'ExecuteFunction'): face_utilities.stream_stream_inline(servicer.ExecuteFunction),
855 | ('qlik.sse.Connector', 'GetCapabilities'): face_utilities.unary_unary_inline(servicer.GetCapabilities),
856 | }
857 | server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
858 | return beta_implementations.server(method_implementations, options=server_options)
859 |
860 |
861 | def beta_create_Connector_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
862 | """The Beta API is deprecated for 0.15.0 and later.
863 |
864 | It is recommended to use the GA API (classes and functions in this
865 | file not marked beta) for all further purposes. This function was
866 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
867 | request_serializers = {
868 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.SerializeToString,
869 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.SerializeToString,
870 | ('qlik.sse.Connector', 'GetCapabilities'): Empty.SerializeToString,
871 | }
872 | response_deserializers = {
873 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.FromString,
874 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.FromString,
875 | ('qlik.sse.Connector', 'GetCapabilities'): Capabilities.FromString,
876 | }
877 | cardinalities = {
878 | 'EvaluateScript': cardinality.Cardinality.STREAM_STREAM,
879 | 'ExecuteFunction': cardinality.Cardinality.STREAM_STREAM,
880 | 'GetCapabilities': cardinality.Cardinality.UNARY_UNARY,
881 | }
882 | stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
883 | return beta_implementations.dynamic_stub(channel, 'qlik.sse.Connector', cardinalities, options=stub_options)
884 | except ImportError:
885 | pass
886 | # @@protoc_insertion_point(module_scope)
887 |
--------------------------------------------------------------------------------
/Basket/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/Basket/__init__.py
--------------------------------------------------------------------------------
/Basket/__main__.py:
--------------------------------------------------------------------------------
1 |
2 | #! /usr/bin/env python3
3 | import argparse
4 | import json
5 | import logging
6 | import logging.config
7 | import os
8 | import sys
9 | import time
10 | import re
11 | from concurrent import futures
12 | from datetime import datetime
13 | import pandas as pd
14 | import numpy as np
15 | import json
16 | from mlxtend.frequent_patterns import apriori
17 | from mlxtend.frequent_patterns import association_rules
18 |
19 | # Add Generated folder to module path.
20 | PARENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
21 | sys.path.append(os.path.join(PARENT_DIR, 'generated'))
22 |
23 | import ServerSideExtension_pb2 as SSE
24 | import grpc
25 | from ssedata import FunctionType
26 | from scripteval import ScriptEval
27 |
28 | _ONE_DAY_IN_SECONDS = 60 * 60 * 24
29 |
30 |
31 | class ExtensionService(SSE.ConnectorServicer):
32 | """
33 | A simple SSE-plugin created for the HelloWorld example.
34 | """
35 |
36 | def __init__(self, funcdef_file):
37 | """
38 | Class initializer.
39 | :param funcdef_file: a function definition JSON file
40 | """
41 | self._function_definitions = funcdef_file
42 | self.ScriptEval = ScriptEval()
43 | os.makedirs('logs', exist_ok=True)
44 | log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'logger.config')
45 | logging.config.fileConfig(log_file)
46 | logging.info('Logging enabled')
47 |
48 | @property
49 | def function_definitions(self):
50 | """
51 | :return: json file with function definitions
52 | """
53 | return self._function_definitions
54 |
55 | @property
56 | def functions(self):
57 | """
58 | :return: Mapping of function id and implementation
59 | """
60 | return {
61 | 0: '_marketBasket' #ADD YOUR FUNCTION HERE
62 | }
63 |
64 |
65 |
66 | @staticmethod
67 | def _get_function_id(context):
68 | """
69 | Retrieve function id from header.
70 | :param context: context
71 | :return: function id
72 | """
73 | metadata = dict(context.invocation_metadata())
74 | header = SSE.FunctionRequestHeader()
75 | header.ParseFromString(metadata['qlik-functionrequestheader-bin'])
76 |
77 | return header.functionId
78 |
79 | """
80 | Implementation of added functions.
81 | """
82 |
83 | @staticmethod
84 | def _marketBasket(request, context):
85 | """
86 | Mirrors the input and sends back the same data.
87 | :param request: iterable sequence of bundled rows
88 | :return: the same iterable sequence as received
89 | """
90 | orderIdList = []
91 | productIdList = []
92 | purchasedList = []
93 | for request_rows in request:
94 | #print(request_rows)
95 | for row in request_rows.rows:
96 | # the first numData contains the orderIds
97 | orderIdList.append([d.numData for d in row.duals][0])
98 |
99 | # the second numData contains the figures
100 | productIdList.append([d.numData for d in row.duals][1])
101 |
102 | # the third numData contains the figures
103 | purchasedList.append([d.numData for d in row.duals][2])
104 |
105 | #print(orderIdList[:10]) #first 10 entries of array
106 | datafrm = pd.DataFrame({'orderId': orderIdList, 'productId': productIdList, 'purchased': purchasedList})
107 | #print(datafrm.head()) #.head returns only the first 5 elements
108 | basket = (
109 | datafrm.groupby(['orderId', 'productId'])['purchased']
110 | .sum().unstack().reset_index().fillna(0)
111 | .set_index('orderId'))
112 | #print(basket.head())
113 |
114 | # Create
115 | frequent_itemsets = apriori(basket, min_support=0.005, use_colnames=True)
116 | #print(frequent_itemsets.head())
117 |
118 | rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1)
119 | #print(rules.head())
120 |
121 | antList = rules['antecedents'].values.tolist()
122 | antList = [list(x) for x in antList]
123 | antList = [int(x[0]) for x in antList]
124 | conList = rules['consequents'].values.tolist()
125 | conList = [list(x) for x in conList]
126 | conList = [int(x[0]) for x in conList]
127 | dualsList = []
128 | dualsList.append([SSE.Dual(numData=d) for d in antList])
129 | dualsList.append([SSE.Dual(numData=d) for d in conList])
130 | dualsList.append([SSE.Dual(numData=d) for d in rules['support'].values.tolist()])
131 | dualsList.append([SSE.Dual(numData=d) for d in rules['confidence'].values.tolist()])
132 | dualsList.append([SSE.Dual(numData=d) for d in rules['lift'].values.tolist()])
133 | #print(dualsList)
134 |
135 | response_rows = []
136 | for i in range(len(antList)):
137 | duals = [dualsList[z][i] for z in range(len(dualsList))]
138 | response_rows.append(SSE.Row(duals=iter(duals)))
139 |
140 | print(request_rows)
141 | yield SSE.BundledRows(rows=response_rows)
142 |
143 |
144 |
145 |
146 | def GetCapabilities(self, request, context):
147 | """
148 | Get capabilities.
149 | Note that either request or context is used in the implementation of this method, but still added as
150 | parameters. The reason is that gRPC always sends both when making a function call and therefore we must include
151 | them to avoid error messages regarding too many parameters provided from the client.
152 | :param request: the request, not used in this method.
153 | :param context: the context, not used in this method.
154 | :return: the capabilities.
155 | """
156 | logging.info('GetCapabilities')
157 | # Create an instance of the Capabilities grpc message
158 | # Enable(or disable) script evaluation
159 | # Set values for pluginIdentifier and pluginVersion
160 | capabilities = SSE.Capabilities(allowScript=True,
161 | pluginIdentifier='Sentiment',
162 | pluginVersion='v1.1.0')
163 |
164 | # If user defined functions supported, add the definitions to the message
165 | with open(self.function_definitions) as json_file:
166 | # Iterate over each function definition and add data to the capabilities grpc message
167 | for definition in json.load(json_file)['Functions']:
168 | function = capabilities.functions.add()
169 | function.name = definition['Name']
170 | function.functionId = definition['Id']
171 | function.functionType = definition['Type']
172 | function.returnType = definition['ReturnType']
173 |
174 | # Retrieve name and type of each parameter
175 | for param_name, param_type in sorted(definition['Params'].items()):
176 | function.params.add(name=param_name, dataType=param_type)
177 |
178 | logging.info('Adding to capabilities: {}({})'.format(function.name,
179 | [p.name for p in function.params]))
180 |
181 | return capabilities
182 |
183 | def ExecuteFunction(self, request_iterator, context):
184 | """
185 | Execute function call.
186 | :param request_iterator: an iterable sequence of Row.
187 | :param context: the context.
188 | :return: an iterable sequence of Row.
189 | """
190 | # Retrieve function id
191 | func_id = self._get_function_id(context)
192 |
193 | # Call corresponding function
194 | logging.info('ExecuteFunction (functionId: {})'.format(func_id))
195 |
196 | return getattr(self, self.functions[func_id])(request_iterator, context)
197 |
198 | def EvaluateScript(self, request, context):
199 | """
200 | This plugin provides functionality only for script calls with no parameters and tensor script calls.
201 | :param request:
202 | :param context:
203 | :return:
204 | """
205 | # Parse header for script request
206 | metadata = dict(context.invocation_metadata())
207 | header = SSE.ScriptRequestHeader()
208 | header.ParseFromString(metadata['qlik-scriptrequestheader-bin'])
209 |
210 | # Retrieve function type
211 | func_type = self.ScriptEval.get_func_type(header)
212 |
213 | # Verify function type
214 | if (func_type == FunctionType.Aggregation) or (func_type == FunctionType.Tensor):
215 | return self.ScriptEval.EvaluateScript(header, request, context, func_type)
216 | else:
217 | # This plugin does not support other function types than aggregation and tensor.
218 | # Make sure the error handling, including logging, works as intended in the client
219 | msg = 'Function type {} is not supported in this plugin.'.format(func_type.name)
220 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
221 | context.set_details(msg)
222 | # Raise error on the plugin-side
223 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED, msg)
224 |
225 | """
226 | Implementation of the Server connecting to gRPC.
227 | """
228 |
229 | def Serve(self, port, pem_dir):
230 | """
231 | Sets up the gRPC Server with insecure connection on port
232 | :param port: port to listen on.
233 | :param pem_dir: Directory including certificates
234 | :return: None
235 | """
236 | # Create gRPC server
237 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
238 | SSE.add_ConnectorServicer_to_server(self, server)
239 |
240 | if pem_dir:
241 | # Secure connection
242 | with open(os.path.join(pem_dir, 'sse_server_key.pem'), 'rb') as f:
243 | private_key = f.read()
244 | with open(os.path.join(pem_dir, 'sse_server_cert.pem'), 'rb') as f:
245 | cert_chain = f.read()
246 | with open(os.path.join(pem_dir, 'root_cert.pem'), 'rb') as f:
247 | root_cert = f.read()
248 | credentials = grpc.ssl_server_credentials([(private_key, cert_chain)], root_cert, True)
249 | server.add_secure_port('[::]:{}'.format(port), credentials)
250 | logging.info('*** Running server in secure mode on port: {} ***'.format(port))
251 | else:
252 | # Insecure connection
253 | server.add_insecure_port('[::]:{}'.format(port))
254 | logging.info('*** Running server in insecure mode on port: {} ***'.format(port))
255 |
256 | # Start gRPC server
257 | server.start()
258 | try:
259 | while True:
260 | time.sleep(_ONE_DAY_IN_SECONDS)
261 | except KeyboardInterrupt:
262 | server.stop(0)
263 |
264 |
265 | if __name__ == '__main__':
266 | parser = argparse.ArgumentParser()
267 | parser.add_argument('--port', nargs='?', default='50088')
268 | parser.add_argument('--pem_dir', nargs='?')
269 | parser.add_argument('--definition_file', nargs='?', default='functions.json')
270 | args = parser.parse_args()
271 |
272 | # need to locate the file when script is called from outside it's location dir.
273 | def_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), args.definition_file)
274 |
275 | calc = ExtensionService(def_file)
276 | calc.Serve(args.port, args.pem_dir)
277 |
--------------------------------------------------------------------------------
/Basket/functions.json:
--------------------------------------------------------------------------------
1 | {
2 | "Functions" : [
3 | {
4 | "Id" : 0,
5 | "Name" : "MarketBasketAnalysis",
6 | "Type" : 2,
7 | "ReturnType": 1,
8 | "Params" : {
9 | "orderId" : 1,
10 | "productId" : 1,
11 | "purchased": 1
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Basket/logger.config:
--------------------------------------------------------------------------------
1 | [loggers]
2 | keys=root
3 |
4 | [logger_root]
5 | handlers=console,file
6 | level=NOTSET
7 |
8 | [formatters]
9 | keys=simple,complex
10 |
11 | [formatter_simple]
12 | format=%(asctime)s - %(levelname)s - %(message)s
13 |
14 | [formatter_complex]
15 | format=%(asctime)s - %(levelname)s - %(module)s : %(lineno)d - %(message)s
16 |
17 | [handlers]
18 | keys=file,console
19 |
20 | [handler_file]
21 | class=handlers.TimedRotatingFileHandler
22 | interval=midnight
23 | backupCount=5
24 | formatter=complex
25 | level=DEBUG
26 | args=('logs/SSEPlugin.log',)
27 |
28 | [handler_console]
29 | class=StreamHandler
30 | formatter=simple
31 | level=INFO
32 | args=(sys.stdout,)
33 |
--------------------------------------------------------------------------------
/Basket/logs/SSEPlugin.log:
--------------------------------------------------------------------------------
1 | 2019-01-28 15:03:33,786 - INFO - __main__ : 46 - Logging enabled
2 | 2019-01-28 15:03:33,786 - INFO - __main__ : 254 - *** Running server in insecure mode on port: 50088 ***
3 | 2019-01-28 15:12:43,984 - INFO - __main__ : 156 - GetCapabilities
4 | 2019-01-28 15:12:43,991 - INFO - __main__ : 179 - Adding to capabilities: MarketBasketAnalysis(['orderId', 'productId', 'purchased'])
5 | 2019-01-28 15:41:17,270 - INFO - __main__ : 194 - ExecuteFunction (functionId: 0)
6 |
--------------------------------------------------------------------------------
/Basket/logs/SSEPlugin.log.2019-01-28_14:
--------------------------------------------------------------------------------
1 | 2019-01-28 14:00:42,263 - INFO - __main__ : 46 - Logging enabled
2 | 2019-01-28 14:00:42,310 - INFO - __main__ : 254 - *** Running server in insecure mode on port: 50088 ***
3 |
--------------------------------------------------------------------------------
/Basket/readme.md:
--------------------------------------------------------------------------------
1 | # Basket Analysis Server-Side-Extension
2 |
3 | This is a python project that acts as a Server-Side Extension for Qlik Sense. The python code was developed by Riley MD, thanks for the genious work.
4 |
5 | If you've already set up this example and re-run it, go to Restart
6 |
7 | ## 1st time setup
8 | * Run Command Prompt
9 | * Download/clone this project
10 | * Go to the folder where you downloaded/cloned the LinearRegression to and also create a environment with the same name
11 | ```
12 | cd "C:\Users\admincsw\Documents\GitHub\qs-python-samples\Basket"
13 | mkvirtualenv Basket
14 | setprojectdir .
15 | ```
16 | 
17 |
18 | * install Python packages
19 | ```
20 | pip install grpcio
21 | pip install grpcio-tools
22 | pip install mlxtend
23 | pip install pandas
24 | ```
25 | * Run the Python app
26 | ```
27 | python __main__.py
28 | ```
29 | ### Setup on Qlik Sense Desktop
30 | * Edit Settings.ini file found under your Documents\Qlik\Sense e.g. C:\Users\csw\Documents\Qlik\Sense\Settings.ini
31 | * If there is no such section "Settings 7" create it and add the entry for "SSEPlugin="
32 | ```
33 | [Settings 7]
34 | SSEPlugin=PythonBasket,localhost:50088
35 | ```
36 | * if there is already another SSE plugin, add this using a ; to separate multiple entries, e.g.
37 | ```
38 | [Settings 7]
39 | SSEPlugin=PythonRegression,localhost:50059;PythonBasket,localhost:50088
40 | ```
41 | * Copy the app "Python Basket Analysis AAI.qvf" from this project folder into Documents\Qlik\Sense\Apps e.g. C:\Users\csw\Documents\Qlik\Sense\Apps
42 | * Run or restart Qlik Sense Desktop and log in
43 | * Open app "Python Basket Analysis AAI" from the hub
44 | * Find the Data connection "PythonBasketSampleQVDs" on the right and click the pen icon (edit)
45 | 
46 | * change this (invalid) path to where the qvd files are located, they are in a subfolder of this project
47 | * Reload the app (this is where the integration happens, the Qlik load script submits data to Python and waits for the response)
48 |
49 | ### Setup on Qlik Sense Server
50 | * Open QMC of your Sense Server
51 | * Create a new Analytical Connection with the following settings
52 | 
53 | * Import the app "Python Basket Analysis AAI.qvf" found in this folder
54 | 
55 | * Open this app from the hub
56 | * Go to DataloadEditor (Load Script)
57 | * Find the Data connection "PythonBasketSampleQVDs" on the right and click the pen icon (edit)
58 | 
59 | * change this (invalid) path to where the qvd files are located, they are in a subfolder of this project
60 | * Since the connection automatically gets renamed and "(machine_user)" gets added to the connection name, please adjust the variable vLib in row 21 accordingly, e.g.
61 | ```
62 | SET vLib = 'lib://PythonBasketSampleQVDs (qse-csw_admincsw)\';
63 | ```
64 | * Reload the app (this is where the integration happens, the Qlik load script submits data to Python and waits for the response)
65 |
66 | ## Restart next time
67 | * Run Command Prompt
68 | ```(python)
69 | workon Basket
70 | python __main__.py
71 | ```
72 |
--------------------------------------------------------------------------------
/Basket/scripteval.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import logging.config
3 |
4 | import grpc
5 | from ssedata import ArgType, ReturnType, FunctionType
6 |
7 | import ServerSideExtension_pb2 as SSE
8 |
9 |
10 | class ScriptEval:
11 | """
12 | Class for SSE plugin ScriptEval functionality.
13 | """
14 |
15 | def EvaluateScript(self, header, request, context, func_type):
16 | """
17 | Evaluates script provided in the header, given the
18 | arguments provided in the sequence of RowData objects, the request.
19 |
20 | :param header:
21 | :param request: an iterable sequence of RowData.
22 | :param context: the context sent from client
23 | :param func_type: function type.
24 | :return: an iterable sequence of RowData.
25 | """
26 | # Retrieve data types from header
27 | arg_types = self.get_arg_types(header)
28 | ret_type = self.get_return_type(header)
29 |
30 | logging.info('EvaluateScript: {} ({} {}) {}'
31 | .format(header.script, arg_types, ret_type, func_type))
32 |
33 | aggr = (func_type == FunctionType.Aggregation)
34 |
35 | # Check if parameters are provided
36 | if header.params:
37 | # Verify argument type
38 | if arg_types == ArgType.String:
39 | # Create an empty list if tensor function
40 | if aggr:
41 | all_rows = []
42 |
43 | # Iterate over bundled rows
44 | for request_rows in request:
45 | # Iterate over rows
46 | for row in request_rows.rows:
47 | # Retrieve numerical data from duals
48 | params = self.get_arguments(context, arg_types, row.duals)
49 |
50 | if aggr:
51 | # Append value to list, for later aggregation
52 | all_rows.append(params)
53 | else:
54 | # Evaluate script row wise
55 | yield self.evaluate(context, header.script, ret_type, params=params)
56 |
57 | # Evaluate script based on data from all rows
58 | if aggr:
59 | params = [list(param) for param in zip(*all_rows)]
60 | yield self.evaluate(context, header.script, ret_type, params=params)
61 | else:
62 | # This plugin does not support other argument types than string.
63 | # Make sure the error handling, including logging, works as intended in the client
64 | msg = 'Argument type: {} not supported in this plugin.'.format(arg_types)
65 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
66 | context.set_details(msg)
67 | # Raise error on the plugin-side
68 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED, msg)
69 |
70 | else:
71 | # This plugin does not support script evaluation without parameters
72 | # Make sure the error handling, including logging, works as intended in the client
73 | msg = 'Script evaluation with no parameters is not supported in this plugin.'
74 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
75 | context.set_details(msg)
76 | # Raise error on the plugin-side
77 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED, msg)
78 |
79 | @staticmethod
80 | def get_func_type(header):
81 | """
82 | Retrieves the function type.
83 | :param header:
84 | :return:
85 | """
86 | func_type = header.functionType
87 | if func_type == SSE.SCALAR:
88 | return FunctionType.Scalar
89 | elif func_type == SSE.AGGREGATION:
90 | return FunctionType.Aggregation
91 | elif func_type == SSE.TENSOR:
92 | return FunctionType.Tensor
93 |
94 | @staticmethod
95 | def get_arguments(context, arg_types, duals):
96 | """
97 | Gets the array of arguments based on
98 | the duals, and the type (string, numeric)
99 | specified in the header.
100 | :param context: the context sent from client
101 | :param arg_types: argument types
102 | :param duals: an iterable sequence of duals.
103 | :return: list of string arguments
104 | """
105 | if arg_types == ArgType.String:
106 | # All parameters are of string type
107 | script_args = [d.strData for d in duals]
108 | else:
109 | # This plugin does not support other arg types than string
110 | # Make sure the error handling, including logging, works as intended in the client
111 | msg = 'Argument type {} is not supported in this plugin.'.format(arg_types)
112 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
113 | context.set_details(msg)
114 | # Raise error on the plugin-side
115 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED, msg)
116 |
117 | return script_args
118 |
119 | @staticmethod
120 | def get_arg_types(header):
121 | """
122 | Determines the argument types for all parameters.
123 | :param header:
124 | :return: ArgType
125 | """
126 | data_types = [param.dataType for param in header.params]
127 |
128 | if not data_types:
129 | return ArgType.Empty
130 | elif len(set(data_types)) > 1 or all(data_type == SSE.DUAL for data_type in data_types):
131 | return ArgType.Mixed
132 | elif all(data_type == SSE.STRING for data_type in data_types):
133 | return ArgType.String
134 | elif all(data_type == SSE.NUMERIC for data_type in data_types):
135 | return ArgType.Numeric
136 | else:
137 | return ArgType.Undefined
138 |
139 | @staticmethod
140 | def get_return_type(header):
141 | """
142 | :param header:
143 | :return: Return type
144 | """
145 | if header.returnType == SSE.STRING:
146 | return ReturnType.String
147 | elif header.returnType == SSE.NUMERIC:
148 | return ReturnType.Numeric
149 | elif header.returnType == SSE.DUAL:
150 | return ReturnType.Dual
151 | else:
152 | return ReturnType.Undefined
153 |
154 | @staticmethod
155 | def evaluate(context, script, ret_type, params=[]):
156 | """
157 | Evaluates a script with given parameters.
158 | :param context: the context sent from client
159 | :param script: script to evaluate
160 | :param ret_type: return data type
161 | :param params: params to evaluate. Default: []
162 | :return: a RowData of string dual
163 | """
164 | if ret_type == ReturnType.String:
165 | # Evaluate script
166 | result = eval(script, {'args': params})
167 | # Transform the result to an iterable of Dual data with a string value
168 | duals = iter([SSE.Dual(strData=result)])
169 |
170 | # Create row data out of duals
171 | return SSE.BundledRows(rows=[SSE.Row(duals=duals)])
172 | else:
173 | # This plugin does not support other return types than string
174 | # Make sure the error handling, including logging, works as intended in the client
175 | msg = 'Return type {} is not supported in this plugin.'.format(ret_type)
176 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
177 | context.set_details(msg)
178 | # Raise error on the plugin-side
179 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED, msg)
180 |
181 |
--------------------------------------------------------------------------------
/Basket/ssedata.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class ArgType(Enum):
5 | """
6 | Represents data types that can be used
7 | as arguments in different script functions.
8 | """
9 | Undefined = -1
10 | Empty = 0
11 | String = 1
12 | Numeric = 2
13 | Mixed = 3
14 |
15 |
16 | class ReturnType(Enum):
17 | """
18 | Represents return types that can
19 | be used in script evaluation.
20 | """
21 | Undefined = -1
22 | String = 0
23 | Numeric = 1
24 | Dual = 2
25 |
26 |
27 | class FunctionType(Enum):
28 | """
29 | Represents function types.
30 | """
31 | Scalar = 0
32 | Aggregation = 1
33 | Tensor = 2
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Christof Schwarz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LinearRegression/ExtensionService_linearRegression.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python3
2 | import argparse
3 | import json
4 | import logging
5 | import logging.config
6 | import os
7 | import sys
8 | import time
9 | from concurrent import futures
10 | from datetime import datetime
11 |
12 | import numpy as np
13 | from sklearn.linear_model import LinearRegression
14 | from datetime import datetime
15 |
16 | import ServerSideExtension_pb2 as SSE
17 | import grpc
18 | from SSEData_linearRegression import FunctionType, \
19 | get_func_type
20 | from ScriptEval_linearRegression import ScriptEval
21 |
22 | _ONE_DAY_IN_SECONDS = 60 * 60 * 24
23 | ##logger = os.getcwd().replace('\\','/') + '/' + 'linearRegressionLogger.txt'
24 |
25 | class ExtensionService(SSE.ConnectorServicer):
26 | """
27 | A simple SSE-plugin created for the ARIMA example.
28 | """
29 |
30 | def __init__(self, funcdef_file):
31 | """
32 | Class initializer.
33 | :param funcdef_file: a function definition JSON file
34 | """
35 | self._function_definitions = funcdef_file
36 | self.scriptEval = ScriptEval()
37 | if not os.path.exists('logs'):
38 | os.mkdir('logs')
39 | logging.config.fileConfig('logger.config')
40 | logging.info('Logging enabled')
41 |
42 | @property
43 | def function_definitions(self):
44 | """
45 | :return: json file with function definitions
46 | """
47 | return self._function_definitions
48 |
49 | @property
50 | def functions(self):
51 | """
52 | :return: Mapping of function id and implementation
53 | """
54 | return {
55 | 0: '_linearRegression'
56 | }
57 |
58 | @staticmethod
59 | def _get_function_id(context):
60 | """
61 | Retrieve function id from header.
62 | :param context: context
63 | :return: function id
64 | """
65 | metadata = dict(context.invocation_metadata())
66 | header = SSE.FunctionRequestHeader()
67 | header.ParseFromString(metadata['qlik-functionrequestheader-bin'])
68 |
69 | return header.functionId
70 |
71 | """
72 | Implementation of added functions.
73 | """
74 |
75 | @staticmethod
76 | def _linearRegression(request, context):
77 | # clear the log for ARIMA details
78 | ## f = open(logger,'w')
79 | ## f.write('New function call\n')
80 |
81 | # instantiate a list for measure data
82 | dataList = []
83 |
84 | for request_rows in request:
85 | # iterate over each request row (contains rows, duals, numData)
86 | ## f.write('Request Rows: ' + str(request_rows) + '\n')
87 |
88 | # pull duals from each row, and the numData from duals
89 | for row in request_rows.rows:
90 | # the first numData contains the measure data
91 | data = [d.numData for d in row.duals][0]
92 |
93 | # try to convert number to float
94 | try:
95 | float(data)
96 | except ValueError:
97 | data = 0
98 |
99 | # append each data point to a list and log it
100 | dataList.append(data)
101 | ## f.write('Row: ' + str(data) + '\n')
102 |
103 | # grab the length of the data list and convert
104 | X_len = len(dataList)
105 | X = np.asarray(range(X_len))
106 |
107 | # convert the data into an array
108 | Y = np.asarray(dataList)
109 |
110 | # fit linear regression model
111 | mdl = LinearRegression().fit(X.reshape(-1, 1),Y)
112 |
113 | # grab m and b from y = mx + b
114 | m = mdl.coef_[0]
115 | b = mdl.intercept_
116 |
117 | # calculate regression line points
118 | regressionResults = []
119 | gen = (i for i in range(X_len))
120 |
121 | for i in gen:
122 | y = m * i + b
123 | regressionResults.append(y)
124 |
125 |
126 | # Create an iterable of dual with the result
127 | duals = iter([[SSE.Dual(numData=d)] for d in regressionResults])
128 |
129 | # Yield the row data as bundled rows
130 | yield SSE.BundledRows(rows=[SSE.Row(duals=d) for d in duals])
131 |
132 |
133 | """
134 | Implementation of rpc functions.
135 | """
136 |
137 | def GetCapabilities(self, request, context):
138 | """
139 | Get capabilities.
140 | Note that either request or context is used in the implementation of this method, but still added as
141 | parameters. The reason is that gRPC always sends both when making a function call and therefore we must include
142 | them to avoid error messages regarding too many parameters provided from the client.
143 | :param request: the request, not used in this method.
144 | :param context: the context, not used in this method.
145 | :return: the capabilities.
146 | """
147 | logging.info('GetCapabilities')
148 | # Create an instance of the Capabilities grpc message
149 | # Enable(or disable) script evaluation
150 | # Set values for pluginIdentifier and pluginVersion
151 | capabilities = SSE.Capabilities(allowScript=True,
152 | pluginIdentifier='Hello World - Qlik',
153 | pluginVersion='v1.0.0-beta1')
154 |
155 | # If user defined functions supported, add the definitions to the message
156 | with open(self.function_definitions) as json_file:
157 | # Iterate over each function definition and add data to the capabilities grpc message
158 | for definition in json.load(json_file)['Functions']:
159 | function = capabilities.functions.add()
160 | function.name = definition['Name']
161 | function.functionId = definition['Id']
162 | function.functionType = definition['Type']
163 | function.returnType = definition['ReturnType']
164 |
165 | # Retrieve name and type of each parameter
166 | for param_name, param_type in sorted(definition['Params'].items()):
167 | function.params.add(name=param_name, dataType=param_type)
168 |
169 | logging.info('Adding to capabilities: {}({})'.format(function.name,
170 | [p.name for p in function.params]))
171 |
172 | return capabilities
173 |
174 | def ExecuteFunction(self, request_iterator, context):
175 | """
176 | Execute function call.
177 | :param request_iterator: an iterable sequence of Row.
178 | :param context: the context.
179 | :return: an iterable sequence of Row.
180 | """
181 | # Retrieve function id
182 | func_id = self._get_function_id(context)
183 |
184 | # Call corresponding function
185 | logging.info('ExecuteFunction (functionId: {})'.format(func_id))
186 |
187 | return getattr(self, self.functions[func_id])(request_iterator, context)
188 |
189 | def EvaluateScript(self, request, context):
190 | """
191 | This plugin provides functionality only for script calls with no parameters and tensor script calls.
192 | :param request:
193 | :param context:
194 | :return:
195 | """
196 | # Parse header for script request
197 | metadata = dict(context.invocation_metadata())
198 | header = SSE.ScriptRequestHeader()
199 | header.ParseFromString(metadata['qlik-scriptrequestheader-bin'])
200 |
201 | # Retrieve function type
202 | func_type = get_func_type(header)
203 |
204 | # Verify function type
205 | if (func_type == FunctionType.Aggregation) or (func_type == FunctionType.Tensor):
206 | return self.scriptEval.EvaluateScript(header, request, func_type)
207 | else:
208 | # This plugin does not support other function types than aggregation and tensor.
209 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED,
210 | 'Function type {} is not supported in this plugin.'.format(func_type.name))
211 |
212 | """
213 | Implementation of the Server connecting to gRPC.
214 | """
215 |
216 | def Serve(self, port, pem_dir):
217 | """
218 | Sets up the gRPC Server with insecure connection on port
219 | :param port: port to listen on.
220 | :param pem_dir: Directory including certificates
221 | :return: None
222 | """
223 | # Create gRPC server
224 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
225 | SSE.add_ConnectorServicer_to_server(self, server)
226 |
227 | if pem_dir:
228 | # Secure connection
229 | with open(os.path.join(pem_dir, 'sse_server_key.pem'), 'rb') as f:
230 | private_key = f.read()
231 | with open(os.path.join(pem_dir, 'sse_server_cert.pem'), 'rb') as f:
232 | cert_chain = f.read()
233 | with open(os.path.join(pem_dir, 'root_cert.pem'), 'rb') as f:
234 | root_cert = f.read()
235 | credentials = grpc.ssl_server_credentials([(private_key, cert_chain)], root_cert, True)
236 | server.add_secure_port('[::]:{}'.format(port), credentials)
237 | logging.info('*** Running server in secure mode on port: {} ***'.format(port))
238 | else:
239 | # Insecure connection
240 | server.add_insecure_port('[::]:{}'.format(port))
241 | logging.info('*** Running server in insecure mode on port: {} ***'.format(port))
242 |
243 | # Start gRPC server
244 | server.start()
245 | try:
246 | while True:
247 | time.sleep(_ONE_DAY_IN_SECONDS)
248 | except KeyboardInterrupt:
249 | server.stop(0)
250 |
251 |
252 | if __name__ == '__main__':
253 | parser = argparse.ArgumentParser()
254 | parser.add_argument('--port', nargs='?', default='50059')
255 | parser.add_argument('--pem_dir', nargs='?')
256 | parser.add_argument('--definition-file', nargs='?', default='FuncDefs_linearRegression.json')
257 | args = parser.parse_args()
258 |
259 | # need to locate the file when script is called from outside it's location dir.
260 | def_file = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), args.definition_file)
261 |
262 | calc = ExtensionService(def_file)
263 | calc.Serve(args.port, args.pem_dir)
264 |
--------------------------------------------------------------------------------
/LinearRegression/FuncDefs_linearRegression.json:
--------------------------------------------------------------------------------
1 | {
2 | "Functions" : [
3 | {
4 | "Id" : 0,
5 | "Name" : "LinearRegression",
6 | "Type" : 2,
7 | "ReturnType": 1,
8 | "Params" : {
9 | "data" : 1
10 | }
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/LinearRegression/Python Linear Regression.qvf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChristofSchwarz/qs-python-samples/5707587fa5439d5a2891abe251926bee804ce4b2/LinearRegression/Python Linear Regression.qvf
--------------------------------------------------------------------------------
/LinearRegression/README.md:
--------------------------------------------------------------------------------
1 | # Linear Regression live-integration with QS
2 |
3 | Qlik Sense Server-side Extension to call Python linear regression algorythm from App GUI.
4 |
5 |
6 | ## 1st time setup
7 | * Run Command Prompt
8 | * Download/clone this project
9 | * Go to the folder where you downloaded/cloned the LinearRegression to and also create a environment with the same name
10 | ```
11 | cd "C:\Users\admincsw\Documents\GitHub\qs-python-samples\LinearRegression"
12 | mkvirtualenv LinearRegression
13 | setprojectdir .
14 | ```
15 | * install Python packages
16 | ```
17 | pip install grpcio
18 | python -m pip install grpcio-tools
19 | pip install sklearn
20 | pip install pandas
21 | ```
22 | * Run the Python app
23 | ```
24 | python ExtensionService_linearRegression.py
25 | ```
26 | ### Setup on Qlik Sense Desktop
27 | * Edit Settings.ini file found under your Documents\Qlik\Sense e.g. C:\Users\csw\Documents\Qlik\Sense\Settings.ini
28 | * If there is no such section \[Settings 7\] create it and add the entry for "SSEPlugin="
29 | ```
30 | [Settings 7]
31 | SSEPlugin=PythonRegression,localhost:50059
32 | ```
33 | * if there is already another SSE plugin, add this using a ; to separate multiple entries, e.g.
34 | ```
35 | [Settings 7]
36 | SSEPlugin=PythonBasket,localhost:50088;PythonRegression,localhost:50059
37 | ```
38 | * Copy the app "Python Basket Analysis AAI.qvf" from this project folder into Documents\Qlik\Sense\Apps e.g. C:\Users\csw\Documents\Qlik\Sense\Apps
39 | * Run or restart Qlik Sense Desktop and log in
40 | * Open app "Python Linear Regression" from the hub and see the trend line on first sheet
41 | 
42 |
43 | ### Setup on Qlik Sense Server
44 | * Open QMC of your Sense Server
45 | * Create a new Analytical Connection with the following settings
46 | 
47 | * Import the app "Python Linear Regression.qvf" found in this folder
48 | * Open app "Python Linear Regression" from the hub and see the trend line on first sheet
49 |
50 | ## Restart next time
51 | * Run Command Prompt
52 | ```
53 | workon LinearRegression
54 | python ExtensionService_linearRegression.py
55 | ```
56 |
--------------------------------------------------------------------------------
/LinearRegression/SSEData_linearRegression.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from enum import Enum
3 |
4 | import ServerSideExtension_pb2 as SSE
5 | import grpc
6 |
7 |
8 | class ArgType(Enum):
9 | """
10 | Represents data types that can be used
11 | as arguments in different script functions.
12 | """
13 | Undefined = -1
14 | Empty = 0
15 | String = 1
16 | Numeric = 2
17 | Mixed = 3
18 |
19 |
20 | class ReturnType(Enum):
21 | """
22 | Represents return types that can
23 | be used in script evaluation.
24 | """
25 | Undefined = -1
26 | String = 0
27 | Numeric = 1
28 | Dual = 2
29 |
30 |
31 | class FunctionType(Enum):
32 | """
33 | Represents function types.
34 | """
35 | Scalar = 0
36 | Aggregation = 1
37 | Tensor = 2
38 |
39 |
40 | def get_func_type(header):
41 | """
42 | Retrieves the function type.
43 | :param header:
44 | :return:
45 | """
46 | func_type = header.functionType
47 | if func_type == SSE.SCALAR:
48 | return FunctionType.Scalar
49 | elif func_type == SSE.AGGREGATION:
50 | return FunctionType.Aggregation
51 | elif func_type == SSE.TENSOR:
52 | return FunctionType.Tensor
53 |
54 |
55 | def get_arguments(arg_types, duals):
56 | """
57 | Gets the array of arguments based on
58 | the duals, and the type (string, numeric)
59 | specified in the header.
60 | :param arg_types: argument types
61 | :param duals: an iterable sequence of duals.
62 | :return: list of string arguments
63 | """
64 | if arg_types == ArgType.String:
65 | # All parameters are of string type
66 | script_args = [d.strData for d in duals]
67 | else:
68 | # This plugin does not support other arg types than string
69 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED,
70 | 'Argument type {} is not supported in this plugin.'.format(arg_types))
71 |
72 | return script_args
73 |
74 |
75 | def get_arg_types(header):
76 | """
77 | Determines the argument types for all parameters.
78 | :param header:
79 | :return: ArgType
80 | """
81 | data_types = [param.dataType for param in header.params]
82 |
83 | if not data_types:
84 | return ArgType.Empty
85 | elif len(set(data_types)) > 1 or all(data_type == SSE.DUAL for data_type in data_types):
86 | return ArgType.Mixed
87 | elif all(data_type == SSE.STRING for data_type in data_types):
88 | return ArgType.String
89 | elif all(data_type == SSE.NUMERIC for data_type in data_types):
90 | return ArgType.Numeric
91 | else:
92 | return ArgType.Undefined
93 |
94 |
95 | def get_return_type(header):
96 | """
97 | :param header:
98 | :return: Return type
99 | """
100 | if header.returnType == SSE.STRING:
101 | return ReturnType.String
102 | elif header.returnType == SSE.NUMERIC:
103 | return ReturnType.Numeric
104 | elif header.returnType == SSE.DUAL:
105 | return ReturnType.Dual
106 | else:
107 | return ReturnType.Undefined
108 |
109 |
110 | def evaluate(script, ret_type, params=[]):
111 | """
112 | Evaluates a script with given parameters.
113 | :param script: script to evaluate
114 | :param ret_type: return data type
115 | :param params: params to evaluate. Default: []
116 | :return: a RowData of string dual
117 | """
118 | if ret_type == ReturnType.String:
119 | # Evaluate script
120 | result = eval(script, {'args': params})
121 | # Transform the result to an iterable of Dual data with a string value
122 | duals = iter([SSE.Dual(strData=result)])
123 |
124 | # Create row data out of duals
125 | return SSE.BundledRows(rows=[SSE.Row(duals=duals)])
126 | else:
127 | # This plugin does not support other return types than string
128 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED,
129 | 'Return type {} is not supported in this plugin.'.format(ret_type))
130 |
--------------------------------------------------------------------------------
/LinearRegression/ScriptEval_linearRegression.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import logging.config
3 |
4 | import grpc
5 |
6 | from SSEData_linearRegression import ArgType, \
7 | evaluate, \
8 | get_arg_types, \
9 | get_return_type, \
10 | FunctionType, \
11 | get_arguments
12 |
13 |
14 | class ScriptEval:
15 | """
16 | Class for SSE plugin ScriptEval functionality.
17 | """
18 |
19 | def EvaluateScript(self, header, request, func_type):
20 | """
21 | Evaluates script provided in the header, given the
22 | arguments provided in the sequence of RowData objects, the request.
23 |
24 | :param header:
25 | :param request: an iterable sequence of RowData.
26 | :param func_type: function type.
27 | :return: an iterable sequence of RowData.
28 | """
29 | # Retrieve data types from header
30 | arg_types = get_arg_types(header)
31 | ret_type = get_return_type(header)
32 |
33 | logging.info('EvaluateScript: {} ({} {}) {}'
34 | .format(header.script, arg_types, ret_type, func_type))
35 |
36 | aggr = (func_type == FunctionType.Aggregation)
37 |
38 | # Check if parameters are provided
39 | if header.params:
40 | # Verify argument type
41 | if arg_types == ArgType.String:
42 | # Create an empty list if tensor function
43 | if aggr:
44 | all_rows = []
45 |
46 | # Iterate over bundled rows
47 | for request_rows in request:
48 | # Iterate over rows
49 | for row in request_rows.rows:
50 | # Retrieve numerical data from duals
51 | params = get_arguments(arg_types, row.duals)
52 |
53 | if aggr:
54 | # Append value to list, for later aggregation
55 | all_rows.append(params)
56 | else:
57 | # Evaluate script row wise
58 | yield evaluate(header.script, ret_type, params=params)
59 |
60 | # Evaluate script based on data from all rows
61 | if aggr:
62 | params = [list(param) for param in zip(*all_rows)]
63 | yield evaluate(header.script, ret_type, params=params)
64 | else:
65 | # This plugin does not support other argument types than string.
66 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED,
67 | 'Argument type: {} not supported in this plugin.'.format(arg_types))
68 | else:
69 | # This plugin does not support script evaluation without parameters
70 | raise grpc.RpcError(grpc.StatusCode.UNIMPLEMENTED,
71 | 'Script evaluation with no parameters is not supported in this plugin.')
72 |
--------------------------------------------------------------------------------
/LinearRegression/ServerSideExtension_pb2.py:
--------------------------------------------------------------------------------
1 | # Generated by the protocol buffer compiler. DO NOT EDIT!
2 | # source: ServerSideExtension.proto
3 |
4 | import sys
5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
6 | from google.protobuf.internal import enum_type_wrapper
7 | from google.protobuf import descriptor as _descriptor
8 | from google.protobuf import message as _message
9 | from google.protobuf import reflection as _reflection
10 | from google.protobuf import symbol_database as _symbol_database
11 | from google.protobuf import descriptor_pb2
12 | # @@protoc_insertion_point(imports)
13 |
14 | _sym_db = _symbol_database.Default()
15 |
16 |
17 |
18 |
19 | DESCRIPTOR = _descriptor.FileDescriptor(
20 | name='ServerSideExtension.proto',
21 | package='qlik.sse',
22 | syntax='proto3',
23 | serialized_pb=_b('\n\x19ServerSideExtension.proto\x12\x08qlik.sse\"\x07\n\x05\x45mpty\"?\n\tParameter\x12$\n\x08\x64\x61taType\x18\x01 \x01(\x0e\x32\x12.qlik.sse.DataType\x12\x0c\n\x04name\x18\x02 \x01(\t\"\xb1\x01\n\x12\x46unctionDefinition\x12\x0c\n\x04name\x18\x01 \x01(\t\x12,\n\x0c\x66unctionType\x18\x02 \x01(\x0e\x32\x16.qlik.sse.FunctionType\x12&\n\nreturnType\x18\x03 \x01(\x0e\x32\x12.qlik.sse.DataType\x12#\n\x06params\x18\x04 \x03(\x0b\x32\x13.qlik.sse.Parameter\x12\x12\n\nfunctionId\x18\x05 \x01(\x05\"\x85\x01\n\x0c\x43\x61pabilities\x12\x13\n\x0b\x61llowScript\x18\x01 \x01(\x08\x12/\n\tfunctions\x18\x02 \x03(\x0b\x32\x1c.qlik.sse.FunctionDefinition\x12\x18\n\x10pluginIdentifier\x18\x03 \x01(\t\x12\x15\n\rpluginVersion\x18\x04 \x01(\t\"(\n\x04\x44ual\x12\x0f\n\x07numData\x18\x01 \x01(\x01\x12\x0f\n\x07strData\x18\x02 \x01(\t\"$\n\x03Row\x12\x1d\n\x05\x64uals\x18\x01 \x03(\x0b\x32\x0e.qlik.sse.Dual\"*\n\x0b\x42undledRows\x12\x1b\n\x04rows\x18\x01 \x03(\x0b\x32\r.qlik.sse.Row\"\xa0\x01\n\x13ScriptRequestHeader\x12\x0e\n\x06script\x18\x01 \x01(\t\x12,\n\x0c\x66unctionType\x18\x02 \x01(\x0e\x32\x16.qlik.sse.FunctionType\x12&\n\nreturnType\x18\x03 \x01(\x0e\x32\x12.qlik.sse.DataType\x12#\n\x06params\x18\x04 \x03(\x0b\x32\x13.qlik.sse.Parameter\"<\n\x15\x46unctionRequestHeader\x12\x12\n\nfunctionId\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\t\"I\n\x13\x43ommonRequestHeader\x12\r\n\x05\x61ppId\x18\x01 \x01(\t\x12\x0e\n\x06userId\x18\x02 \x01(\t\x12\x13\n\x0b\x63\x61rdinality\x18\x03 \x01(\x03*-\n\x08\x44\x61taType\x12\n\n\x06STRING\x10\x00\x12\x0b\n\x07NUMERIC\x10\x01\x12\x08\n\x04\x44UAL\x10\x02*7\n\x0c\x46unctionType\x12\n\n\x06SCALAR\x10\x00\x12\x0f\n\x0b\x41GGREGATION\x10\x01\x12\n\n\x06TENSOR\x10\x02\x32\xd6\x01\n\tConnector\x12<\n\x0fGetCapabilities\x12\x0f.qlik.sse.Empty\x1a\x16.qlik.sse.Capabilities\"\x00\x12\x45\n\x0f\x45xecuteFunction\x12\x15.qlik.sse.BundledRows\x1a\x15.qlik.sse.BundledRows\"\x00(\x01\x30\x01\x12\x44\n\x0e\x45valuateScript\x12\x15.qlik.sse.BundledRows\x1a\x15.qlik.sse.BundledRows\"\x00(\x01\x30\x01\x42\x03\xf8\x01\x01\x62\x06proto3')
24 | )
25 | _sym_db.RegisterFileDescriptor(DESCRIPTOR)
26 |
27 | _DATATYPE = _descriptor.EnumDescriptor(
28 | name='DataType',
29 | full_name='qlik.sse.DataType',
30 | filename=None,
31 | file=DESCRIPTOR,
32 | values=[
33 | _descriptor.EnumValueDescriptor(
34 | name='STRING', index=0, number=0,
35 | options=None,
36 | type=None),
37 | _descriptor.EnumValueDescriptor(
38 | name='NUMERIC', index=1, number=1,
39 | options=None,
40 | type=None),
41 | _descriptor.EnumValueDescriptor(
42 | name='DUAL', index=2, number=2,
43 | options=None,
44 | type=None),
45 | ],
46 | containing_type=None,
47 | options=None,
48 | serialized_start=853,
49 | serialized_end=898,
50 | )
51 | _sym_db.RegisterEnumDescriptor(_DATATYPE)
52 |
53 | DataType = enum_type_wrapper.EnumTypeWrapper(_DATATYPE)
54 | _FUNCTIONTYPE = _descriptor.EnumDescriptor(
55 | name='FunctionType',
56 | full_name='qlik.sse.FunctionType',
57 | filename=None,
58 | file=DESCRIPTOR,
59 | values=[
60 | _descriptor.EnumValueDescriptor(
61 | name='SCALAR', index=0, number=0,
62 | options=None,
63 | type=None),
64 | _descriptor.EnumValueDescriptor(
65 | name='AGGREGATION', index=1, number=1,
66 | options=None,
67 | type=None),
68 | _descriptor.EnumValueDescriptor(
69 | name='TENSOR', index=2, number=2,
70 | options=None,
71 | type=None),
72 | ],
73 | containing_type=None,
74 | options=None,
75 | serialized_start=900,
76 | serialized_end=955,
77 | )
78 | _sym_db.RegisterEnumDescriptor(_FUNCTIONTYPE)
79 |
80 | FunctionType = enum_type_wrapper.EnumTypeWrapper(_FUNCTIONTYPE)
81 | STRING = 0
82 | NUMERIC = 1
83 | DUAL = 2
84 | SCALAR = 0
85 | AGGREGATION = 1
86 | TENSOR = 2
87 |
88 |
89 |
90 | _EMPTY = _descriptor.Descriptor(
91 | name='Empty',
92 | full_name='qlik.sse.Empty',
93 | filename=None,
94 | file=DESCRIPTOR,
95 | containing_type=None,
96 | fields=[
97 | ],
98 | extensions=[
99 | ],
100 | nested_types=[],
101 | enum_types=[
102 | ],
103 | options=None,
104 | is_extendable=False,
105 | syntax='proto3',
106 | extension_ranges=[],
107 | oneofs=[
108 | ],
109 | serialized_start=39,
110 | serialized_end=46,
111 | )
112 |
113 |
114 | _PARAMETER = _descriptor.Descriptor(
115 | name='Parameter',
116 | full_name='qlik.sse.Parameter',
117 | filename=None,
118 | file=DESCRIPTOR,
119 | containing_type=None,
120 | fields=[
121 | _descriptor.FieldDescriptor(
122 | name='dataType', full_name='qlik.sse.Parameter.dataType', index=0,
123 | number=1, type=14, cpp_type=8, label=1,
124 | has_default_value=False, default_value=0,
125 | message_type=None, enum_type=None, containing_type=None,
126 | is_extension=False, extension_scope=None,
127 | options=None),
128 | _descriptor.FieldDescriptor(
129 | name='name', full_name='qlik.sse.Parameter.name', index=1,
130 | number=2, type=9, cpp_type=9, label=1,
131 | has_default_value=False, default_value=_b("").decode('utf-8'),
132 | message_type=None, enum_type=None, containing_type=None,
133 | is_extension=False, extension_scope=None,
134 | options=None),
135 | ],
136 | extensions=[
137 | ],
138 | nested_types=[],
139 | enum_types=[
140 | ],
141 | options=None,
142 | is_extendable=False,
143 | syntax='proto3',
144 | extension_ranges=[],
145 | oneofs=[
146 | ],
147 | serialized_start=48,
148 | serialized_end=111,
149 | )
150 |
151 |
152 | _FUNCTIONDEFINITION = _descriptor.Descriptor(
153 | name='FunctionDefinition',
154 | full_name='qlik.sse.FunctionDefinition',
155 | filename=None,
156 | file=DESCRIPTOR,
157 | containing_type=None,
158 | fields=[
159 | _descriptor.FieldDescriptor(
160 | name='name', full_name='qlik.sse.FunctionDefinition.name', index=0,
161 | number=1, type=9, cpp_type=9, label=1,
162 | has_default_value=False, default_value=_b("").decode('utf-8'),
163 | message_type=None, enum_type=None, containing_type=None,
164 | is_extension=False, extension_scope=None,
165 | options=None),
166 | _descriptor.FieldDescriptor(
167 | name='functionType', full_name='qlik.sse.FunctionDefinition.functionType', index=1,
168 | number=2, type=14, cpp_type=8, label=1,
169 | has_default_value=False, default_value=0,
170 | message_type=None, enum_type=None, containing_type=None,
171 | is_extension=False, extension_scope=None,
172 | options=None),
173 | _descriptor.FieldDescriptor(
174 | name='returnType', full_name='qlik.sse.FunctionDefinition.returnType', index=2,
175 | number=3, type=14, cpp_type=8, label=1,
176 | has_default_value=False, default_value=0,
177 | message_type=None, enum_type=None, containing_type=None,
178 | is_extension=False, extension_scope=None,
179 | options=None),
180 | _descriptor.FieldDescriptor(
181 | name='params', full_name='qlik.sse.FunctionDefinition.params', index=3,
182 | number=4, type=11, cpp_type=10, label=3,
183 | has_default_value=False, default_value=[],
184 | message_type=None, enum_type=None, containing_type=None,
185 | is_extension=False, extension_scope=None,
186 | options=None),
187 | _descriptor.FieldDescriptor(
188 | name='functionId', full_name='qlik.sse.FunctionDefinition.functionId', index=4,
189 | number=5, type=5, cpp_type=1, label=1,
190 | has_default_value=False, default_value=0,
191 | message_type=None, enum_type=None, containing_type=None,
192 | is_extension=False, extension_scope=None,
193 | options=None),
194 | ],
195 | extensions=[
196 | ],
197 | nested_types=[],
198 | enum_types=[
199 | ],
200 | options=None,
201 | is_extendable=False,
202 | syntax='proto3',
203 | extension_ranges=[],
204 | oneofs=[
205 | ],
206 | serialized_start=114,
207 | serialized_end=291,
208 | )
209 |
210 |
211 | _CAPABILITIES = _descriptor.Descriptor(
212 | name='Capabilities',
213 | full_name='qlik.sse.Capabilities',
214 | filename=None,
215 | file=DESCRIPTOR,
216 | containing_type=None,
217 | fields=[
218 | _descriptor.FieldDescriptor(
219 | name='allowScript', full_name='qlik.sse.Capabilities.allowScript', index=0,
220 | number=1, type=8, cpp_type=7, label=1,
221 | has_default_value=False, default_value=False,
222 | message_type=None, enum_type=None, containing_type=None,
223 | is_extension=False, extension_scope=None,
224 | options=None),
225 | _descriptor.FieldDescriptor(
226 | name='functions', full_name='qlik.sse.Capabilities.functions', index=1,
227 | number=2, type=11, cpp_type=10, label=3,
228 | has_default_value=False, default_value=[],
229 | message_type=None, enum_type=None, containing_type=None,
230 | is_extension=False, extension_scope=None,
231 | options=None),
232 | _descriptor.FieldDescriptor(
233 | name='pluginIdentifier', full_name='qlik.sse.Capabilities.pluginIdentifier', index=2,
234 | number=3, type=9, cpp_type=9, label=1,
235 | has_default_value=False, default_value=_b("").decode('utf-8'),
236 | message_type=None, enum_type=None, containing_type=None,
237 | is_extension=False, extension_scope=None,
238 | options=None),
239 | _descriptor.FieldDescriptor(
240 | name='pluginVersion', full_name='qlik.sse.Capabilities.pluginVersion', index=3,
241 | number=4, type=9, cpp_type=9, label=1,
242 | has_default_value=False, default_value=_b("").decode('utf-8'),
243 | message_type=None, enum_type=None, containing_type=None,
244 | is_extension=False, extension_scope=None,
245 | options=None),
246 | ],
247 | extensions=[
248 | ],
249 | nested_types=[],
250 | enum_types=[
251 | ],
252 | options=None,
253 | is_extendable=False,
254 | syntax='proto3',
255 | extension_ranges=[],
256 | oneofs=[
257 | ],
258 | serialized_start=294,
259 | serialized_end=427,
260 | )
261 |
262 |
263 | _DUAL = _descriptor.Descriptor(
264 | name='Dual',
265 | full_name='qlik.sse.Dual',
266 | filename=None,
267 | file=DESCRIPTOR,
268 | containing_type=None,
269 | fields=[
270 | _descriptor.FieldDescriptor(
271 | name='numData', full_name='qlik.sse.Dual.numData', index=0,
272 | number=1, type=1, cpp_type=5, label=1,
273 | has_default_value=False, default_value=float(0),
274 | message_type=None, enum_type=None, containing_type=None,
275 | is_extension=False, extension_scope=None,
276 | options=None),
277 | _descriptor.FieldDescriptor(
278 | name='strData', full_name='qlik.sse.Dual.strData', index=1,
279 | number=2, type=9, cpp_type=9, label=1,
280 | has_default_value=False, default_value=_b("").decode('utf-8'),
281 | message_type=None, enum_type=None, containing_type=None,
282 | is_extension=False, extension_scope=None,
283 | options=None),
284 | ],
285 | extensions=[
286 | ],
287 | nested_types=[],
288 | enum_types=[
289 | ],
290 | options=None,
291 | is_extendable=False,
292 | syntax='proto3',
293 | extension_ranges=[],
294 | oneofs=[
295 | ],
296 | serialized_start=429,
297 | serialized_end=469,
298 | )
299 |
300 |
301 | _ROW = _descriptor.Descriptor(
302 | name='Row',
303 | full_name='qlik.sse.Row',
304 | filename=None,
305 | file=DESCRIPTOR,
306 | containing_type=None,
307 | fields=[
308 | _descriptor.FieldDescriptor(
309 | name='duals', full_name='qlik.sse.Row.duals', index=0,
310 | number=1, type=11, cpp_type=10, label=3,
311 | has_default_value=False, default_value=[],
312 | message_type=None, enum_type=None, containing_type=None,
313 | is_extension=False, extension_scope=None,
314 | options=None),
315 | ],
316 | extensions=[
317 | ],
318 | nested_types=[],
319 | enum_types=[
320 | ],
321 | options=None,
322 | is_extendable=False,
323 | syntax='proto3',
324 | extension_ranges=[],
325 | oneofs=[
326 | ],
327 | serialized_start=471,
328 | serialized_end=507,
329 | )
330 |
331 |
332 | _BUNDLEDROWS = _descriptor.Descriptor(
333 | name='BundledRows',
334 | full_name='qlik.sse.BundledRows',
335 | filename=None,
336 | file=DESCRIPTOR,
337 | containing_type=None,
338 | fields=[
339 | _descriptor.FieldDescriptor(
340 | name='rows', full_name='qlik.sse.BundledRows.rows', index=0,
341 | number=1, type=11, cpp_type=10, label=3,
342 | has_default_value=False, default_value=[],
343 | message_type=None, enum_type=None, containing_type=None,
344 | is_extension=False, extension_scope=None,
345 | options=None),
346 | ],
347 | extensions=[
348 | ],
349 | nested_types=[],
350 | enum_types=[
351 | ],
352 | options=None,
353 | is_extendable=False,
354 | syntax='proto3',
355 | extension_ranges=[],
356 | oneofs=[
357 | ],
358 | serialized_start=509,
359 | serialized_end=551,
360 | )
361 |
362 |
363 | _SCRIPTREQUESTHEADER = _descriptor.Descriptor(
364 | name='ScriptRequestHeader',
365 | full_name='qlik.sse.ScriptRequestHeader',
366 | filename=None,
367 | file=DESCRIPTOR,
368 | containing_type=None,
369 | fields=[
370 | _descriptor.FieldDescriptor(
371 | name='script', full_name='qlik.sse.ScriptRequestHeader.script', index=0,
372 | number=1, type=9, cpp_type=9, label=1,
373 | has_default_value=False, default_value=_b("").decode('utf-8'),
374 | message_type=None, enum_type=None, containing_type=None,
375 | is_extension=False, extension_scope=None,
376 | options=None),
377 | _descriptor.FieldDescriptor(
378 | name='functionType', full_name='qlik.sse.ScriptRequestHeader.functionType', index=1,
379 | number=2, type=14, cpp_type=8, label=1,
380 | has_default_value=False, default_value=0,
381 | message_type=None, enum_type=None, containing_type=None,
382 | is_extension=False, extension_scope=None,
383 | options=None),
384 | _descriptor.FieldDescriptor(
385 | name='returnType', full_name='qlik.sse.ScriptRequestHeader.returnType', index=2,
386 | number=3, type=14, cpp_type=8, label=1,
387 | has_default_value=False, default_value=0,
388 | message_type=None, enum_type=None, containing_type=None,
389 | is_extension=False, extension_scope=None,
390 | options=None),
391 | _descriptor.FieldDescriptor(
392 | name='params', full_name='qlik.sse.ScriptRequestHeader.params', index=3,
393 | number=4, type=11, cpp_type=10, label=3,
394 | has_default_value=False, default_value=[],
395 | message_type=None, enum_type=None, containing_type=None,
396 | is_extension=False, extension_scope=None,
397 | options=None),
398 | ],
399 | extensions=[
400 | ],
401 | nested_types=[],
402 | enum_types=[
403 | ],
404 | options=None,
405 | is_extendable=False,
406 | syntax='proto3',
407 | extension_ranges=[],
408 | oneofs=[
409 | ],
410 | serialized_start=554,
411 | serialized_end=714,
412 | )
413 |
414 |
415 | _FUNCTIONREQUESTHEADER = _descriptor.Descriptor(
416 | name='FunctionRequestHeader',
417 | full_name='qlik.sse.FunctionRequestHeader',
418 | filename=None,
419 | file=DESCRIPTOR,
420 | containing_type=None,
421 | fields=[
422 | _descriptor.FieldDescriptor(
423 | name='functionId', full_name='qlik.sse.FunctionRequestHeader.functionId', index=0,
424 | number=1, type=5, cpp_type=1, label=1,
425 | has_default_value=False, default_value=0,
426 | message_type=None, enum_type=None, containing_type=None,
427 | is_extension=False, extension_scope=None,
428 | options=None),
429 | _descriptor.FieldDescriptor(
430 | name='version', full_name='qlik.sse.FunctionRequestHeader.version', index=1,
431 | number=2, type=9, cpp_type=9, label=1,
432 | has_default_value=False, default_value=_b("").decode('utf-8'),
433 | message_type=None, enum_type=None, containing_type=None,
434 | is_extension=False, extension_scope=None,
435 | options=None),
436 | ],
437 | extensions=[
438 | ],
439 | nested_types=[],
440 | enum_types=[
441 | ],
442 | options=None,
443 | is_extendable=False,
444 | syntax='proto3',
445 | extension_ranges=[],
446 | oneofs=[
447 | ],
448 | serialized_start=716,
449 | serialized_end=776,
450 | )
451 |
452 |
453 | _COMMONREQUESTHEADER = _descriptor.Descriptor(
454 | name='CommonRequestHeader',
455 | full_name='qlik.sse.CommonRequestHeader',
456 | filename=None,
457 | file=DESCRIPTOR,
458 | containing_type=None,
459 | fields=[
460 | _descriptor.FieldDescriptor(
461 | name='appId', full_name='qlik.sse.CommonRequestHeader.appId', index=0,
462 | number=1, type=9, cpp_type=9, label=1,
463 | has_default_value=False, default_value=_b("").decode('utf-8'),
464 | message_type=None, enum_type=None, containing_type=None,
465 | is_extension=False, extension_scope=None,
466 | options=None),
467 | _descriptor.FieldDescriptor(
468 | name='userId', full_name='qlik.sse.CommonRequestHeader.userId', index=1,
469 | number=2, type=9, cpp_type=9, label=1,
470 | has_default_value=False, default_value=_b("").decode('utf-8'),
471 | message_type=None, enum_type=None, containing_type=None,
472 | is_extension=False, extension_scope=None,
473 | options=None),
474 | _descriptor.FieldDescriptor(
475 | name='cardinality', full_name='qlik.sse.CommonRequestHeader.cardinality', index=2,
476 | number=3, type=3, cpp_type=2, label=1,
477 | has_default_value=False, default_value=0,
478 | message_type=None, enum_type=None, containing_type=None,
479 | is_extension=False, extension_scope=None,
480 | options=None),
481 | ],
482 | extensions=[
483 | ],
484 | nested_types=[],
485 | enum_types=[
486 | ],
487 | options=None,
488 | is_extendable=False,
489 | syntax='proto3',
490 | extension_ranges=[],
491 | oneofs=[
492 | ],
493 | serialized_start=778,
494 | serialized_end=851,
495 | )
496 |
497 | _PARAMETER.fields_by_name['dataType'].enum_type = _DATATYPE
498 | _FUNCTIONDEFINITION.fields_by_name['functionType'].enum_type = _FUNCTIONTYPE
499 | _FUNCTIONDEFINITION.fields_by_name['returnType'].enum_type = _DATATYPE
500 | _FUNCTIONDEFINITION.fields_by_name['params'].message_type = _PARAMETER
501 | _CAPABILITIES.fields_by_name['functions'].message_type = _FUNCTIONDEFINITION
502 | _ROW.fields_by_name['duals'].message_type = _DUAL
503 | _BUNDLEDROWS.fields_by_name['rows'].message_type = _ROW
504 | _SCRIPTREQUESTHEADER.fields_by_name['functionType'].enum_type = _FUNCTIONTYPE
505 | _SCRIPTREQUESTHEADER.fields_by_name['returnType'].enum_type = _DATATYPE
506 | _SCRIPTREQUESTHEADER.fields_by_name['params'].message_type = _PARAMETER
507 | DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY
508 | DESCRIPTOR.message_types_by_name['Parameter'] = _PARAMETER
509 | DESCRIPTOR.message_types_by_name['FunctionDefinition'] = _FUNCTIONDEFINITION
510 | DESCRIPTOR.message_types_by_name['Capabilities'] = _CAPABILITIES
511 | DESCRIPTOR.message_types_by_name['Dual'] = _DUAL
512 | DESCRIPTOR.message_types_by_name['Row'] = _ROW
513 | DESCRIPTOR.message_types_by_name['BundledRows'] = _BUNDLEDROWS
514 | DESCRIPTOR.message_types_by_name['ScriptRequestHeader'] = _SCRIPTREQUESTHEADER
515 | DESCRIPTOR.message_types_by_name['FunctionRequestHeader'] = _FUNCTIONREQUESTHEADER
516 | DESCRIPTOR.message_types_by_name['CommonRequestHeader'] = _COMMONREQUESTHEADER
517 | DESCRIPTOR.enum_types_by_name['DataType'] = _DATATYPE
518 | DESCRIPTOR.enum_types_by_name['FunctionType'] = _FUNCTIONTYPE
519 |
520 | Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict(
521 | DESCRIPTOR = _EMPTY,
522 | __module__ = 'ServerSideExtension_pb2'
523 | # @@protoc_insertion_point(class_scope:qlik.sse.Empty)
524 | ))
525 | _sym_db.RegisterMessage(Empty)
526 |
527 | Parameter = _reflection.GeneratedProtocolMessageType('Parameter', (_message.Message,), dict(
528 | DESCRIPTOR = _PARAMETER,
529 | __module__ = 'ServerSideExtension_pb2'
530 | # @@protoc_insertion_point(class_scope:qlik.sse.Parameter)
531 | ))
532 | _sym_db.RegisterMessage(Parameter)
533 |
534 | FunctionDefinition = _reflection.GeneratedProtocolMessageType('FunctionDefinition', (_message.Message,), dict(
535 | DESCRIPTOR = _FUNCTIONDEFINITION,
536 | __module__ = 'ServerSideExtension_pb2'
537 | # @@protoc_insertion_point(class_scope:qlik.sse.FunctionDefinition)
538 | ))
539 | _sym_db.RegisterMessage(FunctionDefinition)
540 |
541 | Capabilities = _reflection.GeneratedProtocolMessageType('Capabilities', (_message.Message,), dict(
542 | DESCRIPTOR = _CAPABILITIES,
543 | __module__ = 'ServerSideExtension_pb2'
544 | # @@protoc_insertion_point(class_scope:qlik.sse.Capabilities)
545 | ))
546 | _sym_db.RegisterMessage(Capabilities)
547 |
548 | Dual = _reflection.GeneratedProtocolMessageType('Dual', (_message.Message,), dict(
549 | DESCRIPTOR = _DUAL,
550 | __module__ = 'ServerSideExtension_pb2'
551 | # @@protoc_insertion_point(class_scope:qlik.sse.Dual)
552 | ))
553 | _sym_db.RegisterMessage(Dual)
554 |
555 | Row = _reflection.GeneratedProtocolMessageType('Row', (_message.Message,), dict(
556 | DESCRIPTOR = _ROW,
557 | __module__ = 'ServerSideExtension_pb2'
558 | # @@protoc_insertion_point(class_scope:qlik.sse.Row)
559 | ))
560 | _sym_db.RegisterMessage(Row)
561 |
562 | BundledRows = _reflection.GeneratedProtocolMessageType('BundledRows', (_message.Message,), dict(
563 | DESCRIPTOR = _BUNDLEDROWS,
564 | __module__ = 'ServerSideExtension_pb2'
565 | # @@protoc_insertion_point(class_scope:qlik.sse.BundledRows)
566 | ))
567 | _sym_db.RegisterMessage(BundledRows)
568 |
569 | ScriptRequestHeader = _reflection.GeneratedProtocolMessageType('ScriptRequestHeader', (_message.Message,), dict(
570 | DESCRIPTOR = _SCRIPTREQUESTHEADER,
571 | __module__ = 'ServerSideExtension_pb2'
572 | # @@protoc_insertion_point(class_scope:qlik.sse.ScriptRequestHeader)
573 | ))
574 | _sym_db.RegisterMessage(ScriptRequestHeader)
575 |
576 | FunctionRequestHeader = _reflection.GeneratedProtocolMessageType('FunctionRequestHeader', (_message.Message,), dict(
577 | DESCRIPTOR = _FUNCTIONREQUESTHEADER,
578 | __module__ = 'ServerSideExtension_pb2'
579 | # @@protoc_insertion_point(class_scope:qlik.sse.FunctionRequestHeader)
580 | ))
581 | _sym_db.RegisterMessage(FunctionRequestHeader)
582 |
583 | CommonRequestHeader = _reflection.GeneratedProtocolMessageType('CommonRequestHeader', (_message.Message,), dict(
584 | DESCRIPTOR = _COMMONREQUESTHEADER,
585 | __module__ = 'ServerSideExtension_pb2'
586 | # @@protoc_insertion_point(class_scope:qlik.sse.CommonRequestHeader)
587 | ))
588 | _sym_db.RegisterMessage(CommonRequestHeader)
589 |
590 |
591 | DESCRIPTOR.has_options = True
592 | DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\370\001\001'))
593 | try:
594 | # THESE ELEMENTS WILL BE DEPRECATED.
595 | # Please use the generated *_pb2_grpc.py files instead.
596 | import grpc
597 | from grpc.framework.common import cardinality
598 | from grpc.framework.interfaces.face import utilities as face_utilities
599 | from grpc.beta import implementations as beta_implementations
600 | from grpc.beta import interfaces as beta_interfaces
601 |
602 |
603 | class ConnectorStub(object):
604 | """*
605 | The communication service provited between Qlik Engine and the plugin.
606 | """
607 |
608 | def __init__(self, channel):
609 | """Constructor.
610 |
611 | Args:
612 | channel: A grpc.Channel.
613 | """
614 | self.GetCapabilities = channel.unary_unary(
615 | '/qlik.sse.Connector/GetCapabilities',
616 | request_serializer=Empty.SerializeToString,
617 | response_deserializer=Capabilities.FromString,
618 | )
619 | self.ExecuteFunction = channel.stream_stream(
620 | '/qlik.sse.Connector/ExecuteFunction',
621 | request_serializer=BundledRows.SerializeToString,
622 | response_deserializer=BundledRows.FromString,
623 | )
624 | self.EvaluateScript = channel.stream_stream(
625 | '/qlik.sse.Connector/EvaluateScript',
626 | request_serializer=BundledRows.SerializeToString,
627 | response_deserializer=BundledRows.FromString,
628 | )
629 |
630 |
631 | class ConnectorServicer(object):
632 | """*
633 | The communication service provited between Qlik Engine and the plugin.
634 | """
635 |
636 | def GetCapabilities(self, request, context):
637 | """/ A handshake call for the Qlik Engine to understand the capability of the plugin.
638 | """
639 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
640 | context.set_details('Method not implemented!')
641 | raise NotImplementedError('Method not implemented!')
642 |
643 | def ExecuteFunction(self, request_iterator, context):
644 | """/ Requests a function to be executed as specified in header.
645 | """
646 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
647 | context.set_details('Method not implemented!')
648 | raise NotImplementedError('Method not implemented!')
649 |
650 | def EvaluateScript(self, request_iterator, context):
651 | """/ Requests a script to be evaluated as specified in header.
652 | """
653 | context.set_code(grpc.StatusCode.UNIMPLEMENTED)
654 | context.set_details('Method not implemented!')
655 | raise NotImplementedError('Method not implemented!')
656 |
657 |
658 | def add_ConnectorServicer_to_server(servicer, server):
659 | rpc_method_handlers = {
660 | 'GetCapabilities': grpc.unary_unary_rpc_method_handler(
661 | servicer.GetCapabilities,
662 | request_deserializer=Empty.FromString,
663 | response_serializer=Capabilities.SerializeToString,
664 | ),
665 | 'ExecuteFunction': grpc.stream_stream_rpc_method_handler(
666 | servicer.ExecuteFunction,
667 | request_deserializer=BundledRows.FromString,
668 | response_serializer=BundledRows.SerializeToString,
669 | ),
670 | 'EvaluateScript': grpc.stream_stream_rpc_method_handler(
671 | servicer.EvaluateScript,
672 | request_deserializer=BundledRows.FromString,
673 | response_serializer=BundledRows.SerializeToString,
674 | ),
675 | }
676 | generic_handler = grpc.method_handlers_generic_handler(
677 | 'qlik.sse.Connector', rpc_method_handlers)
678 | server.add_generic_rpc_handlers((generic_handler,))
679 |
680 |
681 | class BetaConnectorServicer(object):
682 | """The Beta API is deprecated for 0.15.0 and later.
683 |
684 | It is recommended to use the GA API (classes and functions in this
685 | file not marked beta) for all further purposes. This class was generated
686 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
687 | """*
688 | The communication service provited between Qlik Engine and the plugin.
689 | """
690 | def GetCapabilities(self, request, context):
691 | """/ A handshake call for the Qlik Engine to understand the capability of the plugin.
692 | """
693 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
694 | def ExecuteFunction(self, request_iterator, context):
695 | """/ Requests a function to be executed as specified in header.
696 | """
697 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
698 | def EvaluateScript(self, request_iterator, context):
699 | """/ Requests a script to be evaluated as specified in header.
700 | """
701 | context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)
702 |
703 |
704 | class BetaConnectorStub(object):
705 | """The Beta API is deprecated for 0.15.0 and later.
706 |
707 | It is recommended to use the GA API (classes and functions in this
708 | file not marked beta) for all further purposes. This class was generated
709 | only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0."""
710 | """*
711 | The communication service provited between Qlik Engine and the plugin.
712 | """
713 | def GetCapabilities(self, request, timeout, metadata=None, with_call=False, protocol_options=None):
714 | """/ A handshake call for the Qlik Engine to understand the capability of the plugin.
715 | """
716 | raise NotImplementedError()
717 | GetCapabilities.future = None
718 | def ExecuteFunction(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
719 | """/ Requests a function to be executed as specified in header.
720 | """
721 | raise NotImplementedError()
722 | def EvaluateScript(self, request_iterator, timeout, metadata=None, with_call=False, protocol_options=None):
723 | """/ Requests a script to be evaluated as specified in header.
724 | """
725 | raise NotImplementedError()
726 |
727 |
728 | def beta_create_Connector_server(servicer, pool=None, pool_size=None, default_timeout=None, maximum_timeout=None):
729 | """The Beta API is deprecated for 0.15.0 and later.
730 |
731 | It is recommended to use the GA API (classes and functions in this
732 | file not marked beta) for all further purposes. This function was
733 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
734 | request_deserializers = {
735 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.FromString,
736 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.FromString,
737 | ('qlik.sse.Connector', 'GetCapabilities'): Empty.FromString,
738 | }
739 | response_serializers = {
740 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.SerializeToString,
741 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.SerializeToString,
742 | ('qlik.sse.Connector', 'GetCapabilities'): Capabilities.SerializeToString,
743 | }
744 | method_implementations = {
745 | ('qlik.sse.Connector', 'EvaluateScript'): face_utilities.stream_stream_inline(servicer.EvaluateScript),
746 | ('qlik.sse.Connector', 'ExecuteFunction'): face_utilities.stream_stream_inline(servicer.ExecuteFunction),
747 | ('qlik.sse.Connector', 'GetCapabilities'): face_utilities.unary_unary_inline(servicer.GetCapabilities),
748 | }
749 | server_options = beta_implementations.server_options(request_deserializers=request_deserializers, response_serializers=response_serializers, thread_pool=pool, thread_pool_size=pool_size, default_timeout=default_timeout, maximum_timeout=maximum_timeout)
750 | return beta_implementations.server(method_implementations, options=server_options)
751 |
752 |
753 | def beta_create_Connector_stub(channel, host=None, metadata_transformer=None, pool=None, pool_size=None):
754 | """The Beta API is deprecated for 0.15.0 and later.
755 |
756 | It is recommended to use the GA API (classes and functions in this
757 | file not marked beta) for all further purposes. This function was
758 | generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0"""
759 | request_serializers = {
760 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.SerializeToString,
761 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.SerializeToString,
762 | ('qlik.sse.Connector', 'GetCapabilities'): Empty.SerializeToString,
763 | }
764 | response_deserializers = {
765 | ('qlik.sse.Connector', 'EvaluateScript'): BundledRows.FromString,
766 | ('qlik.sse.Connector', 'ExecuteFunction'): BundledRows.FromString,
767 | ('qlik.sse.Connector', 'GetCapabilities'): Capabilities.FromString,
768 | }
769 | cardinalities = {
770 | 'EvaluateScript': cardinality.Cardinality.STREAM_STREAM,
771 | 'ExecuteFunction': cardinality.Cardinality.STREAM_STREAM,
772 | 'GetCapabilities': cardinality.Cardinality.UNARY_UNARY,
773 | }
774 | stub_options = beta_implementations.stub_options(host=host, metadata_transformer=metadata_transformer, request_serializers=request_serializers, response_deserializers=response_deserializers, thread_pool=pool, thread_pool_size=pool_size)
775 | return beta_implementations.dynamic_stub(channel, 'qlik.sse.Connector', cardinalities, options=stub_options)
776 | except ImportError:
777 | pass
778 | # @@protoc_insertion_point(module_scope)
779 |
--------------------------------------------------------------------------------
/LinearRegression/logger.config:
--------------------------------------------------------------------------------
1 | [loggers]
2 | keys=root
3 |
4 | [logger_root]
5 | handlers=console,file
6 | level=NOTSET
7 |
8 | [formatters]
9 | keys=simple,complex
10 |
11 | [formatter_simple]
12 | format=%(asctime)s - %(levelname)s - %(message)s
13 |
14 | [formatter_complex]
15 | format=%(asctime)s - %(levelname)s - %(module)s : %(lineno)d - %(message)s
16 |
17 | [handlers]
18 | keys=file,console
19 |
20 | [handler_file]
21 | class=handlers.TimedRotatingFileHandler
22 | interval=midnight
23 | backupCount=5
24 | formatter=complex
25 | level=DEBUG
26 | args=('logs/SSEPlugin.log',)
27 |
28 | [handler_console]
29 | class=StreamHandler
30 | formatter=simple
31 | level=INFO
32 | args=(sys.stdout,)
33 |
--------------------------------------------------------------------------------
/LinearRegression/logs/SSEPlugin.log:
--------------------------------------------------------------------------------
1 | 2019-01-28 14:24:43,575 - INFO - ExtensionService_linearRegression : 40 - Logging enabled
2 | 2019-01-28 14:24:43,607 - INFO - ExtensionService_linearRegression : 241 - *** Running server in insecure mode on port: 50059 ***
3 | 2019-01-28 14:35:01,487 - INFO - ExtensionService_linearRegression : 147 - GetCapabilities
4 | 2019-01-28 14:35:42,496 - INFO - ExtensionService_linearRegression : 147 - GetCapabilities
5 | 2019-01-28 14:36:43,501 - INFO - ExtensionService_linearRegression : 147 - GetCapabilities
6 | 2019-01-28 14:37:16,836 - INFO - ExtensionService_linearRegression : 170 - Adding to capabilities: LinearRegression(['data'])
7 | 2019-01-28 14:37:16,851 - INFO - ExtensionService_linearRegression : 170 - Adding to capabilities: LinearRegression(['data'])
8 | 2019-01-28 14:37:16,851 - INFO - ExtensionService_linearRegression : 170 - Adding to capabilities: LinearRegression(['data'])
9 | 2019-01-28 14:38:24,503 - INFO - ExtensionService_linearRegression : 147 - GetCapabilities
10 | 2019-01-28 14:38:24,503 - INFO - ExtensionService_linearRegression : 170 - Adding to capabilities: LinearRegression(['data'])
11 | 2019-01-28 14:39:16,482 - INFO - ExtensionService_linearRegression : 147 - GetCapabilities
12 | 2019-01-28 14:39:16,482 - INFO - ExtensionService_linearRegression : 170 - Adding to capabilities: LinearRegression(['data'])
13 | 2019-01-28 14:39:57,296 - INFO - ExtensionService_linearRegression : 185 - ExecuteFunction (functionId: 0)
14 | 2019-01-28 14:39:57,302 - INFO - ExtensionService_linearRegression : 185 - ExecuteFunction (functionId: 0)
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Examples of integrating Qlik Sense and Python
2 |
3 | To see this live watch this 8 minutes video https://youtu.be/nI-Bgal-dSM
4 |
5 | Instructions below are for Python on Windows
6 |
7 | In this example we will set up 2 Server-side Extensions for Qlik, each of which is a Python project
8 | * Basket
9 | * LinearRegression
10 |
11 | Steps to install Python (1st time)
12 | * download and install Python from https://www.python.org/downloads and make sure you get the 64 bit version, no 32 bit
13 | * add Python to the path
14 | 
15 | * install python libraries for easier project encapsulation (pip is the python package manager). From a Command Prompt run
16 | ```
17 | pip install virtualenv
18 | pip install virtualenvwrapper-win
19 | ```
20 | 
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------