qe-api-client 1.2.0__py3-none-any.whl → 2.1.0__py3-none-any.whl
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/api_classes/__init__.py +0 -0
- qe_api_client/{engine_app_api.py → api_classes/engine_app_api.py} +249 -329
- qe_api_client/api_classes/engine_field_api.py +127 -0
- qe_api_client/api_classes/engine_generic_dimension_api.py +38 -0
- qe_api_client/api_classes/engine_generic_measure_api.py +37 -0
- qe_api_client/api_classes/engine_generic_object_api.py +180 -0
- qe_api_client/api_classes/engine_generic_variable_api.py +56 -0
- qe_api_client/{engine_global_api.py → api_classes/engine_global_api.py} +286 -134
- qe_api_client/engine.py +253 -72
- qe_api_client/engine_helper.py +15 -15
- qe_api_client/structs.py +58 -107
- {qe_api_client-1.2.0.dist-info → qe_api_client-2.1.0.dist-info}/METADATA +26 -6
- qe_api_client-2.1.0.dist-info/RECORD +18 -0
- {qe_api_client-1.2.0.dist-info → qe_api_client-2.1.0.dist-info}/WHEEL +1 -1
- qe_api_client/engine_field_api.py +0 -81
- qe_api_client/engine_generic_dimension_api.py +0 -16
- qe_api_client/engine_generic_measure_api.py +0 -15
- qe_api_client/engine_generic_object_api.py +0 -101
- qe_api_client/engine_generic_variable_api.py +0 -24
- qe_api_client-1.2.0.dist-info/RECORD +0 -17
- {qe_api_client-1.2.0.dist-info → qe_api_client-2.1.0.dist-info}/LICENSE +0 -0
- {qe_api_client-1.2.0.dist-info → qe_api_client-2.1.0.dist-info}/top_level.txt +0 -0
qe_api_client/engine.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
import qe_api_client.engine_app_api as engine_app_api
|
1
|
+
import qe_api_client.api_classes.engine_app_api as engine_app_api
|
2
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.engine_generic_variable_api as engine_generic_variable_api
|
7
|
-
import qe_api_client.engine_generic_dimension_api as engine_generic_dimension_api
|
8
|
-
import qe_api_client.engine_generic_measure_api as engine_generic_measure_api
|
3
|
+
import qe_api_client.api_classes.engine_field_api as engine_field_api
|
4
|
+
import qe_api_client.api_classes.engine_generic_object_api as engine_generic_object_api
|
5
|
+
import qe_api_client.api_classes.engine_global_api as engine_global_api
|
6
|
+
import qe_api_client.api_classes.engine_generic_variable_api as engine_generic_variable_api
|
7
|
+
import qe_api_client.api_classes.engine_generic_dimension_api as engine_generic_dimension_api
|
8
|
+
import qe_api_client.api_classes.engine_generic_measure_api as engine_generic_measure_api
|
9
9
|
import qe_api_client.structs as structs
|
10
|
+
import math
|
11
|
+
import pandas as pd
|
10
12
|
|
11
13
|
|
12
14
|
class QixEngine:
|
@@ -28,7 +30,7 @@ class QixEngine:
|
|
28
30
|
self.egva = engine_generic_variable_api.EngineGenericVariableApi(self.conn)
|
29
31
|
self.egda = engine_generic_dimension_api.EngineGenericDimensionApi(self.conn)
|
30
32
|
self.egma = engine_generic_measure_api.EngineGenericMeasureApi(self.conn)
|
31
|
-
self.
|
33
|
+
self.structs = structs
|
32
34
|
self.app_handle = ''
|
33
35
|
|
34
36
|
def create_app(self, app_name='my_app'):
|
@@ -47,57 +49,9 @@ class QixEngine:
|
|
47
49
|
self.app_handle = self.ega.get_handle(opened_app)
|
48
50
|
return opened_app['qGenericId']
|
49
51
|
|
50
|
-
def create_hypercube(self, list_of_dimensions=[],
|
51
|
-
list_of_measures=[], rows_to_return=1000):
|
52
|
-
no_of_columns = len(list_of_dimensions) + len(list_of_measures)
|
53
|
-
hc_dim = []
|
54
|
-
for d in list_of_dimensions:
|
55
|
-
hc_inline_dim = self.Structs.nx_inline_dimension_def([d])
|
56
|
-
hc_dim.append(self.Structs.nx_hypercube_dimensions(hc_inline_dim))
|
57
|
-
hc_mes = []
|
58
|
-
for m in list_of_measures:
|
59
|
-
hc_mes_sort = self.Structs.nx_sort_by()
|
60
|
-
hc_inline_mes = self.Structs.nx_inline_measure_def(m)
|
61
|
-
hc_mes.append(self.Structs.nx_hypercube_measure(hc_mes_sort,
|
62
|
-
hc_inline_mes))
|
63
|
-
nx_page = self.Structs.nx_page(0, 0, rows_to_return, no_of_columns)
|
64
|
-
hc_def = self.Structs.hypercube_def("$", hc_dim, hc_mes, [nx_page])
|
65
|
-
hc_response = self.eaa.create_object(self.app_handle, "CH01",
|
66
|
-
"Chart", "qHyperCubeDef", hc_def)
|
67
|
-
hc_handle = self.ega.get_handle(hc_response["qReturn"])
|
68
|
-
self.egoa.get_layout(hc_handle)
|
69
|
-
hc_data = self.egoa.get_hypercube_data(hc_handle, "/qHyperCubeDef",
|
70
|
-
[nx_page])
|
71
|
-
no_of_columns = len(list_of_dimensions)+len(list_of_measures)
|
72
|
-
return hc_data, no_of_columns
|
73
|
-
|
74
|
-
@staticmethod
|
75
|
-
def convert_hypercube_to_matrix(hc_data, no_of_columns):
|
76
|
-
rows = hc_data["qDataPages"][0]['qMatrix']
|
77
|
-
matrix = [[0 for x in range(no_of_columns)] for y in range(len(rows))]
|
78
|
-
for col_idx, row in enumerate(rows):
|
79
|
-
for cell_idx, cell_val in enumerate(row):
|
80
|
-
matrix[col_idx][cell_idx] = cell_val['qText']
|
81
|
-
return [list(i) for i in zip(*matrix)]
|
82
|
-
|
83
|
-
@staticmethod
|
84
|
-
def convert_hypercube_to_inline_table(hc_data, table_name):
|
85
|
-
rows = hc_data["qDataPages"][0]['qMatrix']
|
86
|
-
script = str.format('{0}:{1}Load * Inline [{1}', table_name, '\n')
|
87
|
-
inline_rows = ''
|
88
|
-
header_row = ''
|
89
|
-
for col_idx in range(len(rows[0])):
|
90
|
-
header_row = header_row + str.format('Column{0}{1}', col_idx, ',')
|
91
|
-
header_row = header_row[:-1] + '\n'
|
92
|
-
for row in rows:
|
93
|
-
for cell_val in row:
|
94
|
-
inline_rows = inline_rows + "'" + cell_val['qText'] + "'" + ','
|
95
|
-
inline_rows = inline_rows[:-1] + '\n'
|
96
|
-
return script + header_row + inline_rows + '];'
|
97
|
-
|
98
52
|
def select_in_dimension(self, dimension_name, list_of_values):
|
99
53
|
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
100
|
-
fld_handle = self.ega.get_handle(lb_field
|
54
|
+
fld_handle = self.ega.get_handle(lb_field)
|
101
55
|
values_to_select = []
|
102
56
|
for val in list_of_values:
|
103
57
|
val = {'qText': val}
|
@@ -106,12 +60,12 @@ class QixEngine:
|
|
106
60
|
|
107
61
|
def select_excluded_in_dimension(self, dimension_name):
|
108
62
|
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
109
|
-
fld_handle = self.ega.get_handle(lb_field
|
63
|
+
fld_handle = self.ega.get_handle(lb_field)
|
110
64
|
return self.efa.select_excluded(fld_handle)
|
111
65
|
|
112
66
|
def select_possible_in_dimension(self, dimension_name):
|
113
67
|
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
114
|
-
fld_handle = self.ega.get_handle(lb_field
|
68
|
+
fld_handle = self.ega.get_handle(lb_field)
|
115
69
|
return self.efa.select_possible(fld_handle)
|
116
70
|
|
117
71
|
# return a list of tuples where first value in tuple is the actual
|
@@ -119,18 +73,12 @@ class QixEngine:
|
|
119
73
|
# values selection state
|
120
74
|
def get_list_object_data(self, dimension_name):
|
121
75
|
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
122
|
-
fld_handle = self.ega.get_handle(lb_field
|
123
|
-
nx_page = self.
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
[dimension_name],
|
129
|
-
None, None, [nx_page]
|
130
|
-
)
|
131
|
-
lb_param = {"qInfo": {"qId": "SLB01", "qType": "ListObject"},
|
132
|
-
"qListObjectDef": lb_def}
|
133
|
-
listobj_handle = self.eaa.create_session_object(self.app_handle, lb_param)["qReturn"]["qHandle"] # NOQA
|
76
|
+
fld_handle = self.ega.get_handle(lb_field)
|
77
|
+
nx_page = self.structs.nx_page(0, 0, self.efa.get_cardinal(fld_handle)["qReturn"])
|
78
|
+
lb_def = self.structs.list_object_def("$", "",[dimension_name], None,
|
79
|
+
None, [nx_page])
|
80
|
+
lb_param = {"qInfo": {"qId": "SLB01", "qType": "ListObject"}, "qListObjectDef": lb_def}
|
81
|
+
listobj_handle = self.eaa.create_session_object(self.app_handle, lb_param)["qHandle"] # NOQA
|
134
82
|
val_list = self.egoa.get_layout(listobj_handle)["qListObject"]["qDataPages"][0]["qMatrix"] # NOQA
|
135
83
|
val_n_state_list = []
|
136
84
|
for val in val_list:
|
@@ -139,7 +87,7 @@ class QixEngine:
|
|
139
87
|
|
140
88
|
def clear_selection_in_dimension(self, dimension_name):
|
141
89
|
lb_field = self.eaa.get_field(self.app_handle, dimension_name)
|
142
|
-
fld_handle = self.ega.get_handle(lb_field
|
90
|
+
fld_handle = self.ega.get_handle(lb_field)
|
143
91
|
return self.efa.clear(fld_handle)['qReturn']
|
144
92
|
|
145
93
|
def clear_all_selections(self):
|
@@ -150,3 +98,236 @@ class QixEngine:
|
|
150
98
|
|
151
99
|
def disconnect(self):
|
152
100
|
self.conn.close_qvengine_connection(self.conn)
|
101
|
+
|
102
|
+
@staticmethod
|
103
|
+
def get_handle(obj):
|
104
|
+
"""
|
105
|
+
Retrieves the handle from a given object.
|
106
|
+
|
107
|
+
Parameters:
|
108
|
+
obj : dict
|
109
|
+
The object containing the handle.
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
int: The handle value.
|
113
|
+
|
114
|
+
Raises:
|
115
|
+
ValueError: If the handle value is invalid.
|
116
|
+
"""
|
117
|
+
try:
|
118
|
+
return obj["qHandle"]
|
119
|
+
except ValueError:
|
120
|
+
return "Bad handle value in " + obj
|
121
|
+
|
122
|
+
def get_chart_data(self, app_handle, obj_id):
|
123
|
+
"""
|
124
|
+
Retrieves the data from a given chart object.
|
125
|
+
|
126
|
+
Parameters:
|
127
|
+
app_handle (int): The handle of the app.
|
128
|
+
obj_id (str): The ID of the chart object.
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
DataFrame: A table of the chart content.
|
132
|
+
"""
|
133
|
+
# Get object ID
|
134
|
+
obj = self.eaa.get_object(app_handle, obj_id)
|
135
|
+
if obj['qType'] is None:
|
136
|
+
return 'Chart ID does not exists!'
|
137
|
+
|
138
|
+
|
139
|
+
# Get object handle
|
140
|
+
obj_handle = self.get_handle(obj)
|
141
|
+
# Get object layout
|
142
|
+
obj_layout = self.egoa.get_layout(obj_handle)
|
143
|
+
|
144
|
+
# Determine the number of the columns and the rows the table has and splits in certain circumstances the table
|
145
|
+
# calls
|
146
|
+
no_of_columns = obj_layout['qHyperCube']['qSize']['qcx']
|
147
|
+
width = no_of_columns
|
148
|
+
no_of_rows = obj_layout['qHyperCube']['qSize']['qcy']
|
149
|
+
height = int(math.floor(10000 / no_of_columns))
|
150
|
+
|
151
|
+
# Extract the dimension and measure titles and concat them to column names.
|
152
|
+
dimension_titles = [dim['qFallbackTitle'] for dim in obj_layout['qHyperCube']['qDimensionInfo']]
|
153
|
+
measure_titles = [measure['qFallbackTitle'] for measure in obj_layout['qHyperCube']['qMeasureInfo']]
|
154
|
+
column_names = dimension_titles + measure_titles
|
155
|
+
|
156
|
+
# if the type of the charts has a straight data structure
|
157
|
+
if (obj_layout['qInfo']['qType'] in ['table', 'sn-table', 'piechart', 'scatterplot', 'combochart', 'barchart']
|
158
|
+
and obj_layout['qHyperCube']['qDataPages'] != []):
|
159
|
+
|
160
|
+
# Paging variables
|
161
|
+
page = 0
|
162
|
+
data_values = []
|
163
|
+
|
164
|
+
# Retrieves the hypercube data in a loop (because of limitation from 10.000 cells per call)
|
165
|
+
while no_of_rows > page * height:
|
166
|
+
nx_page = self.structs.nx_page(0, page * height, width, height)
|
167
|
+
hc_data = self.egoa.get_hypercube_data(obj_handle, '/qHyperCubeDef', nx_page)[
|
168
|
+
'qDataPages'][0]['qMatrix']
|
169
|
+
data_values.extend(hc_data)
|
170
|
+
page += 1
|
171
|
+
|
172
|
+
# Creates Dataframe from the content of the attribute 'qText'.
|
173
|
+
df = pd.DataFrame([[d['qText'] for d in sublist] for sublist in data_values])
|
174
|
+
|
175
|
+
# Assign titles zu Dataframe columns
|
176
|
+
df.columns = column_names
|
177
|
+
|
178
|
+
# if the type of the charts has a pivot data structure
|
179
|
+
elif (obj_layout['qInfo']['qType'] in ['pivot-table', 'sn-pivot-table']
|
180
|
+
and obj_layout['qHyperCube']['qPivotDataPages'] != []):
|
181
|
+
|
182
|
+
# Supporting function to traverse all subnodes to get all dimensions
|
183
|
+
def get_all_dimensions(node):
|
184
|
+
dimensions = [node['qText']]
|
185
|
+
# if 'qSubNodes' in node and node['qSubNodes']:
|
186
|
+
if node['qSubNodes']:
|
187
|
+
sub_dimensions = []
|
188
|
+
for sub_node in node['qSubNodes']:
|
189
|
+
sub_dimensions.extend([dimensions + d for d in get_all_dimensions(sub_node)])
|
190
|
+
return sub_dimensions
|
191
|
+
else:
|
192
|
+
return [dimensions]
|
193
|
+
|
194
|
+
# Gets the column headers for the pivot table
|
195
|
+
col_headers = []
|
196
|
+
nx_page_top = self.structs.nx_page(0, 0, width, 1)
|
197
|
+
hc_top = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page_top)[
|
198
|
+
'qDataPages'][0]['qTop']
|
199
|
+
for top_node in hc_top:
|
200
|
+
col_headers.extend(get_all_dimensions(top_node))
|
201
|
+
|
202
|
+
# Paging variables
|
203
|
+
page = 0
|
204
|
+
row_headers = []
|
205
|
+
data_values = []
|
206
|
+
|
207
|
+
# Retrieves the hypercube data in a loop (bacause of limitation from 10.000 cells per call)
|
208
|
+
while no_of_rows > page * height:
|
209
|
+
nx_page = self.structs.nx_page(0, page * height, width, height)
|
210
|
+
|
211
|
+
# Retrieves the row headers for the pivot table
|
212
|
+
hc_left = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page)[
|
213
|
+
'qDataPages'][0]['qLeft']
|
214
|
+
for left_node in hc_left:
|
215
|
+
row_headers.extend(get_all_dimensions(left_node))
|
216
|
+
|
217
|
+
# Retrieves the data for the pivot table
|
218
|
+
hc_data = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page)[
|
219
|
+
'qDataPages'][0]['qData']
|
220
|
+
for row in hc_data:
|
221
|
+
data_values.append([cell['qText'] for cell in row])
|
222
|
+
|
223
|
+
page += 1
|
224
|
+
|
225
|
+
# Creates multi indes for rows and columns
|
226
|
+
row_index = pd.MultiIndex.from_tuples(row_headers)
|
227
|
+
col_index = pd.MultiIndex.from_tuples(col_headers)
|
228
|
+
|
229
|
+
# Creates the Dataframe
|
230
|
+
df = pd.DataFrame(data_values, index=row_index, columns=col_index)
|
231
|
+
|
232
|
+
# if the type of the charts has a stacked data structure
|
233
|
+
elif obj_layout['qInfo']['qType'] in ['barchart'] and obj_layout['qHyperCube']['qStackedDataPages'] != []:
|
234
|
+
max_no_cells = no_of_columns * no_of_rows
|
235
|
+
nx_page = self.structs.nx_page(0, 0, no_of_columns, no_of_rows)
|
236
|
+
hc_data = self.egoa.get_hypercube_stack_data(obj_handle, '/qHyperCubeDef', nx_page, max_no_cells)[
|
237
|
+
'qDataPages'][0]['qData'][0]['qSubNodes']
|
238
|
+
|
239
|
+
# Transform the nested structure into a flat DataFrame
|
240
|
+
data_values = []
|
241
|
+
for node in hc_data:
|
242
|
+
for sub_node in node['qSubNodes']:
|
243
|
+
value = sub_node['qSubNodes'][0]['qValue'] if sub_node['qSubNodes'] else None
|
244
|
+
data_values.append([node['qText'], sub_node['qText'], value])
|
245
|
+
|
246
|
+
# Creates the Dataframe
|
247
|
+
df = pd.DataFrame(data_values, columns=column_names)
|
248
|
+
|
249
|
+
else:
|
250
|
+
return 'Chart type not supported.'
|
251
|
+
|
252
|
+
# Returns the Dataframe
|
253
|
+
return df
|
254
|
+
|
255
|
+
def get_constructed_table_data(self, app_handle, list_of_dimensions = [], list_of_measures = [],
|
256
|
+
list_of_master_dimensions = [], list_of_master_measures = []):
|
257
|
+
"""
|
258
|
+
Creates a table from given fields, expressions, dimensions or measures and retrieves the data from it.
|
259
|
+
|
260
|
+
Parameters:
|
261
|
+
app_handle (int): The handle of the app.
|
262
|
+
list_of_dimensions (list): A list of dimensions.
|
263
|
+
list_of_measures (list): A list of measures.
|
264
|
+
list_of_master_dimensions (list): A list of master dimensions.
|
265
|
+
list_of_master_measures (list): A list of master measures.
|
266
|
+
|
267
|
+
Returns:
|
268
|
+
DataFrame: A table of the chart content.
|
269
|
+
"""
|
270
|
+
# Create dimension property
|
271
|
+
hc_dim = []
|
272
|
+
for dimension in list_of_dimensions:
|
273
|
+
hc_inline_dim_def = self.structs.nx_inline_dimension_def([dimension])
|
274
|
+
hc_dim.append(self.structs.nx_dimension("", hc_inline_dim_def))
|
275
|
+
for dimension in list_of_master_dimensions:
|
276
|
+
hc_dim.append(self.structs.nx_dimension(dimension))
|
277
|
+
|
278
|
+
# Create measure property
|
279
|
+
hc_mes = []
|
280
|
+
for measure in list_of_measures:
|
281
|
+
hc_inline_mes = self.structs.nx_inline_measure_def(measure)
|
282
|
+
hc_mes.append(self.structs.nx_measure("", hc_inline_mes))
|
283
|
+
for measure in list_of_master_measures:
|
284
|
+
hc_mes.append(self.structs.nx_measure(measure))
|
285
|
+
|
286
|
+
# Create hypercube structure
|
287
|
+
hc_def = self.structs.hypercube_def("$", hc_dim, hc_mes)
|
288
|
+
|
289
|
+
# Create info structure
|
290
|
+
nx_info = self.structs.nx_info("table")
|
291
|
+
|
292
|
+
# Create generic object properties structure
|
293
|
+
gen_obj_props = self.structs.generic_object_properties(nx_info, "qHyperCubeDef", hc_def)
|
294
|
+
|
295
|
+
# Create session object
|
296
|
+
hc_obj = self.eaa.create_session_object(app_handle, gen_obj_props)
|
297
|
+
|
298
|
+
# Get object handle
|
299
|
+
hc_obj_handle = self.get_handle(hc_obj)
|
300
|
+
|
301
|
+
# Get object layout
|
302
|
+
hc_obj_layout = self.egoa.get_layout(hc_obj_handle)
|
303
|
+
|
304
|
+
# Determine the number of the columns and the rows the table has and splits in certain circumstances the table calls
|
305
|
+
no_of_columns = hc_obj_layout['qHyperCube']['qSize']['qcx']
|
306
|
+
width = no_of_columns
|
307
|
+
no_of_rows = hc_obj_layout['qHyperCube']['qSize']['qcy']
|
308
|
+
height = int(math.floor(10000 / no_of_columns))
|
309
|
+
|
310
|
+
# Extract the dimension and measure titles and concat them to column names.
|
311
|
+
dimension_titles = [dim['qFallbackTitle'] for dim in hc_obj_layout['qHyperCube']['qDimensionInfo']]
|
312
|
+
measure_titles = [measure['qFallbackTitle'] for measure in hc_obj_layout['qHyperCube']['qMeasureInfo']]
|
313
|
+
column_names = dimension_titles + measure_titles
|
314
|
+
|
315
|
+
# Paging variables
|
316
|
+
page = 0
|
317
|
+
data_values = []
|
318
|
+
|
319
|
+
# Retrieves the hypercube data in a loop (because of limitation from 10.000 cells per call)
|
320
|
+
while no_of_rows > page * height:
|
321
|
+
nx_page = self.structs.nx_page(0, page * height, width, height)
|
322
|
+
hc_data = self.egoa.get_hypercube_data(hc_obj_handle, '/qHyperCubeDef', nx_page)['qDataPages'][0]['qMatrix']
|
323
|
+
data_values.extend(hc_data)
|
324
|
+
page += 1
|
325
|
+
|
326
|
+
# Creates Dataframe from the content of the attribute 'qText'.
|
327
|
+
df = pd.DataFrame([[d['qText'] for d in sublist] for sublist in data_values])
|
328
|
+
|
329
|
+
# Assign titles zu Dataframe columns
|
330
|
+
df.columns = column_names
|
331
|
+
|
332
|
+
# Returns the Dataframe
|
333
|
+
return df
|
qe_api_client/engine_helper.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import math
|
2
2
|
|
3
|
-
from qe_api_client.engine_app_api import EngineAppApi
|
4
|
-
from qe_api_client.engine_field_api import EngineFieldApi
|
5
|
-
from qe_api_client.engine_generic_object_api import EngineGenericObjectApi
|
6
|
-
from qe_api_client.engine_global_api import EngineGlobalApi
|
7
|
-
|
3
|
+
from qe_api_client.api_classes.engine_app_api import EngineAppApi
|
4
|
+
from qe_api_client.api_classes.engine_field_api import EngineFieldApi
|
5
|
+
from qe_api_client.api_classes.engine_generic_object_api import EngineGenericObjectApi
|
6
|
+
from qe_api_client.api_classes.engine_global_api import EngineGlobalApi
|
7
|
+
import qe_api_client.structs as structs
|
8
8
|
|
9
9
|
import pandas as pd
|
10
10
|
|
@@ -12,26 +12,26 @@ import pandas as pd
|
|
12
12
|
def getDataFrame(connection, appHandle, measures, dimensions, selections={}):
|
13
13
|
engineGlobalApi = EngineGlobalApi(connection)
|
14
14
|
# Define Dimensions of hypercube
|
15
|
-
hc_inline_dim =
|
15
|
+
hc_inline_dim = structs.nx_inline_dimension_def(dimensions)
|
16
16
|
|
17
17
|
# Set sorting of Dimension by Measure
|
18
|
-
hc_mes_sort =
|
18
|
+
hc_mes_sort = structs.sort_criteria()
|
19
19
|
|
20
20
|
# Define Measure of hypercube
|
21
|
-
hc_inline_mes =
|
21
|
+
hc_inline_mes = structs.nx_inline_measure_def(measures)
|
22
22
|
|
23
23
|
# Build hypercube from above definition
|
24
|
-
hc_dim =
|
25
|
-
hc_mes =
|
24
|
+
hc_dim = structs.nx_dimension(hc_inline_dim)
|
25
|
+
hc_mes = structs.nx_measure("", hc_inline_mes, hc_mes_sort)
|
26
26
|
|
27
27
|
width = len(measures) + len(dimensions)
|
28
28
|
height = int(math.floor(10000 / width))
|
29
|
-
nx_page =
|
30
|
-
hc_def =
|
29
|
+
nx_page = structs.nx_page(0, 0, width, height)
|
30
|
+
hc_def = structs.hypercube_def("$", [hc_dim], [hc_mes], [nx_page])
|
31
31
|
|
32
32
|
engineAppApi = EngineAppApi(connection)
|
33
33
|
hc_response = engineAppApi.create_object(appHandle, "CH01", "Chart", "qHyperCubeDef", hc_def) # NOQA
|
34
|
-
hc_handle = engineGlobalApi.get_handle(hc_response)
|
34
|
+
hc_handle = engineGlobalApi.get_handle(hc_response['qReturn'])
|
35
35
|
|
36
36
|
engineGenericObjectApi = EngineGenericObjectApi(connection)
|
37
37
|
|
@@ -47,8 +47,8 @@ def getDataFrame(connection, appHandle, measures, dimensions, selections={}):
|
|
47
47
|
|
48
48
|
i = 0
|
49
49
|
while i % height == 0:
|
50
|
-
nx_page =
|
51
|
-
hc_data = engineGenericObjectApi.get_hypercube_data(hc_handle, "/qHyperCubeDef",
|
50
|
+
nx_page = structs.nx_page(0, i, width, height)
|
51
|
+
hc_data = engineGenericObjectApi.get_hypercube_data(hc_handle, "/qHyperCubeDef", nx_page) # NOQA
|
52
52
|
elems = hc_data["qDataPages"][0]['qMatrix']
|
53
53
|
|
54
54
|
df = pd.DataFrame()
|
qe_api_client/structs.py
CHANGED
@@ -1,107 +1,58 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@staticmethod
|
60
|
-
def nx_inline_dimension_def(field_definitions=[],
|
61
|
-
grouping='N',
|
62
|
-
field_labels=[]
|
63
|
-
):
|
64
|
-
return {"qGrouping": grouping,
|
65
|
-
"qFieldDefs": field_definitions,
|
66
|
-
"qFieldLabels": field_labels
|
67
|
-
}
|
68
|
-
|
69
|
-
@staticmethod
|
70
|
-
def nx_hypercube_measure(sort_by={}, nx_inline_measures_def=""):
|
71
|
-
return {"qSortBy": sort_by,
|
72
|
-
"qDef": nx_inline_measures_def
|
73
|
-
}
|
74
|
-
|
75
|
-
@staticmethod
|
76
|
-
def nx_sort_by(state=0, freq=0, numeric=0, ascii=0, load_order=1):
|
77
|
-
return {"qSortByState": state,
|
78
|
-
"qSortByFrequency": freq,
|
79
|
-
"qSortByNumeric": numeric,
|
80
|
-
"qSortByAscii": ascii,
|
81
|
-
"qSortByLoadOrder": load_order,
|
82
|
-
"qSortByExpression": 0,
|
83
|
-
"qExpression": {
|
84
|
-
"qv": ""
|
85
|
-
}
|
86
|
-
}
|
87
|
-
|
88
|
-
@staticmethod
|
89
|
-
def nx_inline_measure_def(definition, label="",
|
90
|
-
description="",
|
91
|
-
tags=[],
|
92
|
-
grouping="N"
|
93
|
-
):
|
94
|
-
return {"qLabel": label,
|
95
|
-
"qDescription": description,
|
96
|
-
"qTags": tags,
|
97
|
-
"qGrouping": grouping,
|
98
|
-
"qDef": definition
|
99
|
-
}
|
100
|
-
|
101
|
-
@staticmethod
|
102
|
-
def nx_page(top=0, left=0, height=2, width=2):
|
103
|
-
return {"qTop": top,
|
104
|
-
"qLeft": left,
|
105
|
-
"qHeight": height,
|
106
|
-
"qWidth": width
|
107
|
-
}
|
1
|
+
def list_object_def(state_name="$", library_id="", field_defs=None, field_labels=None, sort_criterias=None,
|
2
|
+
initial_data_fetch=None):
|
3
|
+
if initial_data_fetch is None:
|
4
|
+
initial_data_fetch = []
|
5
|
+
if sort_criterias is None:
|
6
|
+
sort_criterias = []
|
7
|
+
if field_labels is None:
|
8
|
+
field_labels = []
|
9
|
+
if field_defs is None:
|
10
|
+
field_defs = []
|
11
|
+
return {"qStateName": state_name, "qLibraryId": library_id,
|
12
|
+
"qDef": {"qFieldDefs": field_defs, "qFieldLabels": field_labels, "qSortCriterias": sort_criterias},
|
13
|
+
"qInitialDataFetch": initial_data_fetch}
|
14
|
+
|
15
|
+
def hypercube_def(state_name="$", nx_dims=[], nx_meas=[], nx_page=[], inter_column_sort=[0, 1, 2], suppress_zero=False,
|
16
|
+
suppress_missing=False):
|
17
|
+
return {"qStateName": state_name, "qDimensions": nx_dims, "qMeasures": nx_meas,
|
18
|
+
"qInterColumnSortOrder": inter_column_sort, "qSuppressZero": suppress_zero,
|
19
|
+
"qSuppressMissing": suppress_missing, "qInitialDataFetch": nx_page, "qMode": 'S', "qNoOfLeftDims": -1,
|
20
|
+
"qAlwaysFullyExpanded": False, "qMaxStackedCells": 5000, "qPopulateMissing": False,
|
21
|
+
"qShowTotalsAbove": False, "qIndentMode": False, "qCalcCond": "", "qSortbyYValue": 0}
|
22
|
+
|
23
|
+
def nx_inline_dimension_def(field_definitions=[], field_labels=[], sort_criterias=[], grouping='N'):
|
24
|
+
return {"qGrouping": grouping, "qFieldDefs": field_definitions, "qFieldLabels": field_labels,
|
25
|
+
"qSortCriterias": sort_criterias, "qReverseSort": False}
|
26
|
+
|
27
|
+
def nx_inline_measure_def(definition, label="", description="", tags=[], grouping="N"):
|
28
|
+
return {"qLabel": label, "qDescription": description, "qTags": tags, "qGrouping": grouping, "qDef": definition}
|
29
|
+
|
30
|
+
def nx_page(left=0, top=0, width=2, height=2):
|
31
|
+
return {"qLeft": left, "qTop": top, "qWidth": width, "qHeight": height}
|
32
|
+
|
33
|
+
def nx_info(obj_type, obj_id=""):
|
34
|
+
"""
|
35
|
+
Retrieves the data from a specific list object in a generic object.
|
36
|
+
|
37
|
+
Parameters:
|
38
|
+
obj_type (str): Type of the object. This parameter is mandatory.
|
39
|
+
obj_id (str): Identifier of the object. If the chosen identifier is already in use, the engine automatically
|
40
|
+
sets another one. If an identifier is not set, the engine automatically sets one. This parameter is optional.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
dict: Struct "nxInfo"
|
44
|
+
"""
|
45
|
+
return {"qId": obj_id, "qType": obj_type}
|
46
|
+
|
47
|
+
def nx_dimension(library_id="", dim_def={}, null_suppression=False):
|
48
|
+
return {"qLibraryId": library_id, "qDef": dim_def, "qNullSuppression": null_suppression}
|
49
|
+
|
50
|
+
def nx_measure(library_id="", mes_def={}, sort_by={}):
|
51
|
+
return {"qLibraryId": library_id, "qDef": mes_def, "qSortBy": sort_by}
|
52
|
+
|
53
|
+
def generic_object_properties(info, prop_name, prop_def, extends_id="", state_name="$"):
|
54
|
+
return {"qInfo": info, "qExtendsId": extends_id, prop_name: prop_def, "qStateName": state_name}
|
55
|
+
|
56
|
+
def sort_criteria(state=0, freq=0, numeric=0, ascii=0, load_order=1):
|
57
|
+
return {"qSortByState": state, "qSortByFrequency": freq, "qSortByNumeric": numeric, "qSortByAscii": ascii,
|
58
|
+
"qSortByLoadOrder": load_order, "qSortByExpression": 0, "qExpression": {"qv": ""}}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qe-api-client
|
3
|
-
Version: 1.
|
3
|
+
Version: 2.1.0
|
4
4
|
Summary: Python wrapper around Qlik Engine JSON API
|
5
5
|
Home-page: https://github.com/lr-bicc/qe-api-client
|
6
6
|
Author: Rumen Vasilev
|
@@ -13,7 +13,7 @@ Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
14
14
|
Requires-Dist: websocket-client >=0.47.0
|
15
15
|
|
16
|
-
#
|
16
|
+
# Qlik Engine API Client
|
17
17
|
|
18
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
19
|
|
@@ -23,10 +23,15 @@ Forked from [jhettler/pyqlikengine](https://github.com/jhettler/pyqlikengine)
|
|
23
23
|
* Python 3.6+
|
24
24
|
* websocket-client>=0.47.0
|
25
25
|
|
26
|
-
##
|
26
|
+
## Installation
|
27
27
|
```bash
|
28
28
|
pip install qe-api-client
|
29
29
|
```
|
30
|
+
|
31
|
+
## Example of usage on Qlik Sense Enterprise Server
|
32
|
+
You need to export the Qlik Sense certificates in PEM format from the Qlik Sense Enterprise server to a local folder in
|
33
|
+
order to authenticate on the server.
|
34
|
+
|
30
35
|
```python
|
31
36
|
from qe_api_client.engine import QixEngine
|
32
37
|
|
@@ -36,10 +41,25 @@ user_id = 'sense'
|
|
36
41
|
ca_certs = 'qlik_certs/qlik-1_root.pem'
|
37
42
|
certfile = 'qlik_certs/qlik-1_client.pem'
|
38
43
|
keyfile = 'qlik_certs/qlik-1_client_key.pem'
|
39
|
-
qixe = QixEngine(url=url, user_directory=user_directory,
|
40
|
-
|
41
|
-
|
44
|
+
qixe = QixEngine(url=url, user_directory=user_directory, user_id=user_id, ca_certs=ca_certs, certfile=certfile,
|
45
|
+
keyfile=keyfile)
|
46
|
+
|
47
|
+
# print all apps in Qlik Server
|
48
|
+
print(qixe.ega.get_doc_list())
|
49
|
+
```
|
50
|
+
|
51
|
+
## Example of usage on Qlik Sense Desktop
|
52
|
+
You need to start your Qlik Sense Desktop client on your local PC.
|
53
|
+
|
54
|
+
```python
|
55
|
+
from qe_api_client.engine import QixEngine
|
56
|
+
|
57
|
+
url = 'ws://localhost:4848/app'
|
58
|
+
qixe = QixEngine(url=url)
|
42
59
|
|
43
60
|
# print all apps in Qlik Server
|
44
61
|
print(qixe.ega.get_doc_list())
|
45
62
|
```
|
63
|
+
|
64
|
+
## API reference
|
65
|
+
Please click on this [link](https://lr-bicc.github.io/qe-api-client) for full API reference documentation .
|