qe-api-client 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qe_api_client-1.0.0/LICENSE +19 -0
- qe_api_client-1.0.0/PKG-INFO +45 -0
- qe_api_client-1.0.0/README.md +30 -0
- qe_api_client-1.0.0/qe_api_client/__init__.py +1 -0
- qe_api_client-1.0.0/qe_api_client/engine.py +145 -0
- qe_api_client-1.0.0/qe_api_client/engine_app_api.py +810 -0
- qe_api_client-1.0.0/qe_api_client/engine_communicator.py +42 -0
- qe_api_client-1.0.0/qe_api_client/engine_field_api.py +81 -0
- qe_api_client-1.0.0/qe_api_client/engine_generic_object_api.py +101 -0
- qe_api_client-1.0.0/qe_api_client/engine_global_api.py +376 -0
- qe_api_client-1.0.0/qe_api_client/engine_helper.py +67 -0
- qe_api_client-1.0.0/qe_api_client/structs.py +107 -0
- qe_api_client-1.0.0/qe_api_client.egg-info/PKG-INFO +45 -0
- qe_api_client-1.0.0/qe_api_client.egg-info/SOURCES.txt +22 -0
- qe_api_client-1.0.0/qe_api_client.egg-info/dependency_links.txt +1 -0
- qe_api_client-1.0.0/qe_api_client.egg-info/requires.txt +1 -0
- qe_api_client-1.0.0/qe_api_client.egg-info/top_level.txt +1 -0
- qe_api_client-1.0.0/setup.cfg +4 -0
- qe_api_client-1.0.0/setup.py +26 -0
- qe_api_client-1.0.0/test/test_app_api.py +102 -0
- qe_api_client-1.0.0/test/test_field_api.py +71 -0
- qe_api_client-1.0.0/test/test_global_api.py +154 -0
- qe_api_client-1.0.0/test/test_labs.py +24 -0
- qe_api_client-1.0.0/test/test_pyqlikengine.py +74 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: qe-api-client
|
3
|
+
Version: 1.0.0
|
4
|
+
Summary: Python wrapper around Qlik Engine JSON API
|
5
|
+
Home-page: https://github.com/lr-bicc/qe-api-client
|
6
|
+
Author: Rumen Vasilev
|
7
|
+
Author-email: R.Vasilev@LRWorld.com
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.6
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
License-File: LICENSE
|
14
|
+
Requires-Dist: websocket-client>=0.47.0
|
15
|
+
|
16
|
+
# pyqlikengine
|
17
|
+
|
18
|
+
Python wrapper around [Qlik Engine JSON API](https://help.qlik.com/en-US/sense-developer/February2024/Subsystems/EngineAPI/Content/Sense_EngineAPI/introducing-engine-API.htm)
|
19
|
+
|
20
|
+
Forked from [jhettler/pyqlikengine](https://github.com/jhettler/pyqlikengine)
|
21
|
+
|
22
|
+
## Requirements
|
23
|
+
* Python 3.6+
|
24
|
+
* websocket-client>=0.47.0
|
25
|
+
|
26
|
+
## Example of usage
|
27
|
+
```bash
|
28
|
+
pip install qe-api-client
|
29
|
+
```
|
30
|
+
```python
|
31
|
+
from qe_api_client.engine import QixEngine
|
32
|
+
|
33
|
+
url = 'qlik-1.ad.xxx.xxx'
|
34
|
+
user_directory = 'UserDomainToQlikLogin'
|
35
|
+
user_id = 'sense'
|
36
|
+
ca_certs = 'qlik_certs/qlik-1_root.pem'
|
37
|
+
certfile = 'qlik_certs/qlik-1_client.pem'
|
38
|
+
keyfile = 'qlik_certs/qlik-1_client_key.pem'
|
39
|
+
qixe = QixEngine(url=url, user_directory=user_directory,
|
40
|
+
user_id=user_id, ca_certs=ca_certs,
|
41
|
+
certfile=certfile, keyfile=keyfile)
|
42
|
+
|
43
|
+
# print all apps in Qlik Server
|
44
|
+
print(qixe.ega.get_doc_list())
|
45
|
+
```
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# pyqlikengine
|
2
|
+
|
3
|
+
Python wrapper around [Qlik Engine JSON API](https://help.qlik.com/en-US/sense-developer/February2024/Subsystems/EngineAPI/Content/Sense_EngineAPI/introducing-engine-API.htm)
|
4
|
+
|
5
|
+
Forked from [jhettler/pyqlikengine](https://github.com/jhettler/pyqlikengine)
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
* Python 3.6+
|
9
|
+
* websocket-client>=0.47.0
|
10
|
+
|
11
|
+
## Example of usage
|
12
|
+
```bash
|
13
|
+
pip install qe-api-client
|
14
|
+
```
|
15
|
+
```python
|
16
|
+
from qe_api_client.engine import QixEngine
|
17
|
+
|
18
|
+
url = 'qlik-1.ad.xxx.xxx'
|
19
|
+
user_directory = 'UserDomainToQlikLogin'
|
20
|
+
user_id = 'sense'
|
21
|
+
ca_certs = 'qlik_certs/qlik-1_root.pem'
|
22
|
+
certfile = 'qlik_certs/qlik-1_client.pem'
|
23
|
+
keyfile = 'qlik_certs/qlik-1_client_key.pem'
|
24
|
+
qixe = QixEngine(url=url, user_directory=user_directory,
|
25
|
+
user_id=user_id, ca_certs=ca_certs,
|
26
|
+
certfile=certfile, keyfile=keyfile)
|
27
|
+
|
28
|
+
# print all apps in Qlik Server
|
29
|
+
print(qixe.ega.get_doc_list())
|
30
|
+
```
|
@@ -0,0 +1 @@
|
|
1
|
+
name = "qe_api_client"
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import qe_api_client.engine_app_api as engine_app_api
|
2
|
+
import qe_api_client.engine_communicator as engine_communicator
|
3
|
+
import qe_api_client.engine_field_api as engine_field_api
|
4
|
+
import qe_api_client.engine_generic_object_api as engine_generic_object_api
|
5
|
+
import qe_api_client.engine_global_api as engine_global_api
|
6
|
+
import qe_api_client.structs as structs
|
7
|
+
|
8
|
+
|
9
|
+
class QixEngine:
|
10
|
+
|
11
|
+
def __init__(self, url, user_directory,
|
12
|
+
user_id, ca_certs, certfile,
|
13
|
+
keyfile, app_id=None):
|
14
|
+
self.url = url
|
15
|
+
self.conn = engine_communicator.SecureEngineCommunicator(
|
16
|
+
url, user_directory,
|
17
|
+
user_id, ca_certs, certfile,
|
18
|
+
keyfile, app_id
|
19
|
+
)
|
20
|
+
self.ega = engine_global_api.EngineGlobalApi(self.conn)
|
21
|
+
self.eaa = engine_app_api.EngineAppApi(self.conn)
|
22
|
+
self.egoa = engine_generic_object_api.EngineGenericObjectApi(self.conn)
|
23
|
+
self.efa = engine_field_api.EngineFieldApi(self.conn)
|
24
|
+
self.Structs = structs.Structs()
|
25
|
+
self.app_handle = ''
|
26
|
+
|
27
|
+
def create_app(self, app_name='my_app'):
|
28
|
+
app = self.ega.create_app(app_name)
|
29
|
+
try:
|
30
|
+
return app['qAppId']
|
31
|
+
except KeyError:
|
32
|
+
return app['message']
|
33
|
+
|
34
|
+
def load_script(self, script):
|
35
|
+
self.eaa.set_script(self.app_handle, script)
|
36
|
+
return self.eaa.do_reload_ex(self.app_handle)['qResult']['qSuccess']
|
37
|
+
|
38
|
+
def open_app(self, app_obj):
|
39
|
+
opened_app = self.ega.open_doc(app_obj)['qReturn']
|
40
|
+
self.app_handle = self.ega.get_handle(opened_app)
|
41
|
+
return opened_app['qGenericId']
|
42
|
+
|
43
|
+
def create_hypercube(self, list_of_dimensions=[],
|
44
|
+
list_of_measures=[], rows_to_return=1000):
|
45
|
+
no_of_columns = len(list_of_dimensions) + len(list_of_measures)
|
46
|
+
hc_dim = []
|
47
|
+
for d in list_of_dimensions:
|
48
|
+
hc_inline_dim = self.Structs.nx_inline_dimension_def([d])
|
49
|
+
hc_dim.append(self.Structs.nx_hypercube_dimensions(hc_inline_dim))
|
50
|
+
hc_mes = []
|
51
|
+
for m in list_of_measures:
|
52
|
+
hc_mes_sort = self.Structs.nx_sort_by()
|
53
|
+
hc_inline_mes = self.Structs.nx_inline_measure_def(m)
|
54
|
+
hc_mes.append(self.Structs.nx_hypercube_measure(hc_mes_sort,
|
55
|
+
hc_inline_mes))
|
56
|
+
nx_page = self.Structs.nx_page(0, 0, rows_to_return, no_of_columns)
|
57
|
+
hc_def = self.Structs.hypercube_def("$", hc_dim, hc_mes, [nx_page])
|
58
|
+
hc_response = self.eaa.create_object(self.app_handle, "CH01",
|
59
|
+
"Chart", "qHyperCubeDef", hc_def)
|
60
|
+
hc_handle = self.ega.get_handle(hc_response["qReturn"])
|
61
|
+
self.egoa.get_layout(hc_handle)
|
62
|
+
hc_data = self.egoa.get_hypercube_data(hc_handle, "/qHyperCubeDef",
|
63
|
+
[nx_page])
|
64
|
+
no_of_columns = len(list_of_dimensions)+len(list_of_measures)
|
65
|
+
return hc_data, no_of_columns
|
66
|
+
|
67
|
+
@staticmethod
|
68
|
+
def convert_hypercube_to_matrix(hc_data, no_of_columns):
|
69
|
+
rows = hc_data["qDataPages"][0]['qMatrix']
|
70
|
+
matrix = [[0 for x in range(no_of_columns)] for y in range(len(rows))]
|
71
|
+
for col_idx, row in enumerate(rows):
|
72
|
+
for cell_idx, cell_val in enumerate(row):
|
73
|
+
matrix[col_idx][cell_idx] = cell_val['qText']
|
74
|
+
return [list(i) for i in zip(*matrix)]
|
75
|
+
|
76
|
+
@staticmethod
|
77
|
+
def convert_hypercube_to_inline_table(hc_data, table_name):
|
78
|
+
rows = hc_data["qDataPages"][0]['qMatrix']
|
79
|
+
script = str.format('{0}:{1}Load * Inline [{1}', table_name, '\n')
|
80
|
+
inline_rows = ''
|
81
|
+
header_row = ''
|
82
|
+
for col_idx in range(len(rows[0])):
|
83
|
+
header_row = header_row + str.format('Column{0}{1}', col_idx, ',')
|
84
|
+
header_row = header_row[:-1] + '\n'
|
85
|
+
for row in rows:
|
86
|
+
for cell_val in row:
|
87
|
+
inline_rows = inline_rows + "'" + cell_val['qText'] + "'" + ','
|
88
|
+
inline_rows = inline_rows[:-1] + '\n'
|
89
|
+
return script + header_row + inline_rows + '];'
|
90
|
+
|
91
|
+
def select_in_dimension(self, dimension_name, list_of_values):
|
92
|
+
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
93
|
+
fld_handle = self.ega.get_handle(lb_field["qReturn"])
|
94
|
+
values_to_select = []
|
95
|
+
for val in list_of_values:
|
96
|
+
val = {'qText': val}
|
97
|
+
values_to_select.append(val)
|
98
|
+
return self.efa.select_values(fld_handle, values_to_select)
|
99
|
+
|
100
|
+
def select_excluded_in_dimension(self, dimension_name):
|
101
|
+
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
102
|
+
fld_handle = self.ega.get_handle(lb_field["qReturn"])
|
103
|
+
return self.efa.select_excluded(fld_handle)
|
104
|
+
|
105
|
+
def select_possible_in_dimension(self, dimension_name):
|
106
|
+
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
107
|
+
fld_handle = self.ega.get_handle(lb_field["qReturn"])
|
108
|
+
return self.efa.select_possible(fld_handle)
|
109
|
+
|
110
|
+
# return a list of tuples where first value in tuple is the actual
|
111
|
+
# data value and the second tuple value is that
|
112
|
+
# values selection state
|
113
|
+
def get_list_object_data(self, dimension_name):
|
114
|
+
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
115
|
+
fld_handle = self.ega.get_handle(lb_field["qReturn"])
|
116
|
+
nx_page = self.Structs.nx_page(0, 0,
|
117
|
+
self.efa.get_cardinal(
|
118
|
+
fld_handle)["qReturn"]
|
119
|
+
)
|
120
|
+
lb_def = self.Structs.list_object_def("$", "",
|
121
|
+
[dimension_name],
|
122
|
+
None, None, [nx_page]
|
123
|
+
)
|
124
|
+
lb_param = {"qInfo": {"qId": "SLB01", "qType": "ListObject"},
|
125
|
+
"qListObjectDef": lb_def}
|
126
|
+
listobj_handle = self.eaa.create_session_object(self.app_handle, lb_param)["qReturn"]["qHandle"] # NOQA
|
127
|
+
val_list = self.egoa.get_layout(listobj_handle)["qLayout"]["qListObject"]["qDataPages"][0]["qMatrix"] # NOQA
|
128
|
+
val_n_state_list = []
|
129
|
+
for val in val_list:
|
130
|
+
val_n_state_list.append((val[0]["qText"], val[0]["qState"]))
|
131
|
+
return val_n_state_list
|
132
|
+
|
133
|
+
def clear_selection_in_dimension(self, dimension_name):
|
134
|
+
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
135
|
+
fld_handle = self.ega.get_handle(lb_field["qReturn"])
|
136
|
+
return self.efa.clear(fld_handle)['qReturn']
|
137
|
+
|
138
|
+
def clear_all_selections(self):
|
139
|
+
return self.eaa.clear_all(self.app_handle, True)
|
140
|
+
|
141
|
+
def delete_app(self, app_name):
|
142
|
+
return self.ega.delete_app(app_name)['qSuccess']
|
143
|
+
|
144
|
+
def disconnect(self):
|
145
|
+
self.conn.close_qvengine_connection(self.conn)
|