qe-api-client 2.5.0__tar.gz → 2.6.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-2.5.0 → qe_api_client-2.6.0}/PKG-INFO +4 -3
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/README.md +1 -1
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_app_api.py +3 -3
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_generic_object_api.py +35 -5
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/engine.py +411 -16
- qe_api_client-2.6.0/qe_api_client/structs.py +435 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client.egg-info/PKG-INFO +4 -3
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/setup.py +1 -1
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_app_api.py +2 -2
- qe_api_client-2.5.0/qe_api_client/structs.py +0 -122
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/LICENSE +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/__init__.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/__init__.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_field_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_generic_dimension_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_generic_measure_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_generic_variable_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_global_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/engine_communicator.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client.egg-info/SOURCES.txt +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client.egg-info/dependency_links.txt +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client.egg-info/requires.txt +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client.egg-info/top_level.txt +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/setup.cfg +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_chart_content.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_field_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_global_api.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_labs.py +0 -0
- {qe_api_client-2.5.0 → qe_api_client-2.6.0}/test/test_pyqlikengine.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: qe-api-client
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.6.0
|
4
4
|
Summary: Python client for the Qlik Engine JSON API
|
5
5
|
Home-page: https://github.com/lr-bicc/qe-api-client
|
6
6
|
Author: Rumen Vasilev
|
@@ -19,13 +19,14 @@ Dynamic: classifier
|
|
19
19
|
Dynamic: description
|
20
20
|
Dynamic: description-content-type
|
21
21
|
Dynamic: home-page
|
22
|
+
Dynamic: license-file
|
22
23
|
Dynamic: requires-dist
|
23
24
|
Dynamic: requires-python
|
24
25
|
Dynamic: summary
|
25
26
|
|
26
27
|
# Qlik Engine API Client
|
27
28
|
|
28
|
-
Python
|
29
|
+
Python client for the [Qlik Engine JSON API](https://help.qlik.com/en-US/sense-developer/November2024/Subsystems/EngineAPI/Content/Sense_EngineAPI/introducing-engine-API.htm)
|
29
30
|
|
30
31
|
Forked from [jhettler/pyqlikengine](https://github.com/jhettler/pyqlikengine)
|
31
32
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Qlik Engine API Client
|
2
2
|
|
3
|
-
Python
|
3
|
+
Python client for the [Qlik Engine JSON API](https://help.qlik.com/en-US/sense-developer/November2024/Subsystems/EngineAPI/Content/Sense_EngineAPI/introducing-engine-API.htm)
|
4
4
|
|
5
5
|
Forked from [jhettler/pyqlikengine](https://github.com/jhettler/pyqlikengine)
|
6
6
|
|
@@ -115,18 +115,18 @@ class EngineAppApi:
|
|
115
115
|
except KeyError:
|
116
116
|
return response['error']
|
117
117
|
|
118
|
-
def get_object(self,
|
118
|
+
def get_object(self, app_handle, object_id):
|
119
119
|
"""
|
120
120
|
Retrieves a specific object from the app identified by the document handle.
|
121
121
|
|
122
122
|
Parameters:
|
123
|
-
|
123
|
+
app_handle (int): The handle identifying the app document.
|
124
124
|
object_id (str): The ID of the object to retrieve.
|
125
125
|
|
126
126
|
Returns:
|
127
127
|
dict: The retrieved object (qReturn). In case of an error, returns the error information.
|
128
128
|
"""
|
129
|
-
msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle":
|
129
|
+
msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": app_handle, "method": "GetObject",
|
130
130
|
"params": {"qId": object_id}})
|
131
131
|
response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
|
132
132
|
try:
|
{qe_api_client-2.5.0 → qe_api_client-2.6.0}/qe_api_client/api_classes/engine_generic_object_api.py
RENAMED
@@ -24,24 +24,54 @@ class EngineGenericObjectApi:
|
|
24
24
|
socket (object): The socket connection to the Qlik Sense engine.
|
25
25
|
"""
|
26
26
|
self.engine_socket = socket
|
27
|
-
|
27
|
+
|
28
|
+
def apply_patches(self, handle: int, patches: list, soft_patch: bool = False):
|
28
29
|
"""
|
29
|
-
|
30
|
+
Applies a patch to the properties of an object. Allows an update to some of the properties. It is possible to
|
31
|
+
apply a patch to the properties of a generic object, that is not persistent. Such a patch is called a soft patch.
|
32
|
+
In that case, the result of the operation on the properties (add, remove or delete) is not shown when doing
|
33
|
+
GetProperties, and only a GetLayout call shows the result of the operation. Properties that are not persistent
|
34
|
+
are called soft properties. Once the engine session is over, soft properties are cleared. It should not be
|
35
|
+
possible to patch "/qInfo/qId", and it will be forbidden in the near future.
|
30
36
|
|
31
37
|
Parameters:
|
32
38
|
handle (int): The handle identifying the generic object.
|
33
|
-
|
39
|
+
patches (list): List of patches.
|
40
|
+
soft_patch (bool, optional): If set to true, it means that the properties to be applied are not persistent.
|
41
|
+
The patch is a soft patch. The default value is false.
|
34
42
|
|
35
43
|
Returns:
|
36
|
-
dict:
|
44
|
+
dict: Operation succeeded.
|
37
45
|
"""
|
38
|
-
msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "
|
46
|
+
msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "ApplyPatches",
|
47
|
+
"params": {"qPatches": patches, "qSoftPatch": soft_patch}})
|
39
48
|
response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
|
40
49
|
try:
|
41
50
|
return response["result"]
|
42
51
|
except KeyError:
|
43
52
|
return response["error"]
|
44
53
|
|
54
|
+
def create_child(self, handle: int, prop: dict, prop_for_this: dict = None):
|
55
|
+
"""
|
56
|
+
Creates a generic object that is a child of another generic object.
|
57
|
+
|
58
|
+
Parameters:
|
59
|
+
handle (int): The handle identifying the generic object.
|
60
|
+
prop (dict): Information about the child. It is possible to create a child that is linked to another object.
|
61
|
+
prop_for_this (dict, optional): Identifier of the parent's object. Should be set to update the properties of
|
62
|
+
the parent's object at the same time the child is created.
|
63
|
+
|
64
|
+
Returns:
|
65
|
+
dict: The layout structure of the generic object (qLayout). In case of an error, returns the error information.
|
66
|
+
"""
|
67
|
+
msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "CreateChild",
|
68
|
+
"params": {"qProp": prop, "qPropForThis": prop_for_this}})
|
69
|
+
response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
|
70
|
+
try:
|
71
|
+
return response["result"]["qReturn"]
|
72
|
+
except KeyError:
|
73
|
+
return response["error"]
|
74
|
+
|
45
75
|
def get_layout(self, handle):
|
46
76
|
"""
|
47
77
|
Retrieves the layout structure of a specific generic object.
|
@@ -111,6 +111,8 @@ class QixEngine:
|
|
111
111
|
"""
|
112
112
|
if dim_tags is None:
|
113
113
|
dim_tags = []
|
114
|
+
|
115
|
+
# Define of the single dimension properties
|
114
116
|
nx_info = self.structs.nx_info(obj_type="dimension")
|
115
117
|
nx_library_dimension_def = self.structs.nx_library_dimension_def(grouping="N", field_definitions=[dim_def],
|
116
118
|
field_labels=[dim_title],
|
@@ -118,6 +120,8 @@ class QixEngine:
|
|
118
120
|
gen_dim_props = self.structs.generic_dimension_properties(nx_info=nx_info,
|
119
121
|
nx_library_dimension_def=nx_library_dimension_def,
|
120
122
|
title=dim_title, description=dim_desc, tags=dim_tags)
|
123
|
+
|
124
|
+
# Create the single dimension
|
121
125
|
master_dim = self.eaa.create_dimension(app_handle, gen_dim_props)
|
122
126
|
return master_dim
|
123
127
|
|
@@ -139,15 +143,211 @@ class QixEngine:
|
|
139
143
|
"""
|
140
144
|
if mes_tags is None:
|
141
145
|
mes_tags = []
|
146
|
+
|
147
|
+
# Define of the measure properties
|
142
148
|
nx_info = self.structs.nx_info(obj_type="measure")
|
143
149
|
nx_library_measure_def = self.structs.nx_library_measure_def(label=mes_title, mes_def=mes_def,
|
144
150
|
label_expression=mes_label)
|
145
151
|
gen_mes_props = self.structs.generic_measure_properties(nx_info=nx_info,
|
146
152
|
nx_library_measure_def=nx_library_measure_def,
|
147
153
|
title=mes_title, description=mes_desc, tags=mes_tags)
|
154
|
+
|
155
|
+
# Create the measure
|
148
156
|
master_mes = self.eaa.create_measure(app_handle, gen_mes_props)
|
157
|
+
|
149
158
|
return master_mes
|
150
159
|
|
160
|
+
def create_sheet(self, app_handle: int, sheet_title: str, sheet_desc: str = "", no_of_rows: int = 18):
|
161
|
+
"""
|
162
|
+
Creates a sheet.
|
163
|
+
|
164
|
+
Parameters:
|
165
|
+
app_handle (int): The handle of the app.
|
166
|
+
sheet_title (str): The title of the sheet.
|
167
|
+
sheet_desc (str, optional): The description of the sheet.
|
168
|
+
no_of_rows (int, optional): TThe number of the sheet rows. Min. 8 rows and max. 42 rows.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
dict: The handle and Id of the sheet.
|
172
|
+
"""
|
173
|
+
# Define of the sheet properties
|
174
|
+
nx_info = self.structs.nx_info(obj_type="sheet")
|
175
|
+
sheet_def = {"title": sheet_title, "description": sheet_desc}
|
176
|
+
sheet_props = self.structs.generic_object_properties(info=nx_info, prop_name="qMetaDef", prop_def=sheet_def)
|
177
|
+
|
178
|
+
# Add row and column attributes. The number of the row should be between 8 and 42.
|
179
|
+
if no_of_rows not in range(8, 43):
|
180
|
+
no_of_rows = 18
|
181
|
+
no_of_columns = no_of_rows * 2
|
182
|
+
|
183
|
+
# Derive the grid_resolution property
|
184
|
+
if no_of_rows == 12:
|
185
|
+
grid_resolution = "small"
|
186
|
+
elif no_of_rows == 15:
|
187
|
+
grid_resolution = "medium"
|
188
|
+
elif no_of_rows == 18:
|
189
|
+
grid_resolution = "large"
|
190
|
+
else:
|
191
|
+
grid_resolution = "customrows"
|
192
|
+
|
193
|
+
# Add new properties
|
194
|
+
sheet_props.update(
|
195
|
+
{
|
196
|
+
"thumbnail": {"qStaticContentUrlDef": {"qUrl": ""}}, "columns": no_of_columns, "rows": no_of_rows,
|
197
|
+
"customRowBase": no_of_rows, "gridResolution": grid_resolution, "layoutOptions": {"mobileLayout": "LIST"},
|
198
|
+
"qChildListDef": {"qData": {"title": "/title"}}
|
199
|
+
}
|
200
|
+
)
|
201
|
+
|
202
|
+
# Create the sheet
|
203
|
+
sheet = self.eaa.create_object(app_handle, sheet_props)
|
204
|
+
|
205
|
+
return sheet
|
206
|
+
|
207
|
+
def create_list_object(self, handle: int, dim_id: str = "", field_def: str = "", field_title: str = ""):
|
208
|
+
"""
|
209
|
+
Creates a list object.
|
210
|
+
|
211
|
+
Parameters:
|
212
|
+
handle (int): The handle of the parent object.
|
213
|
+
dim_id (str, optional): The ID of the master dimension. Let this parameter empty, if you passed the "field_def".
|
214
|
+
field_def (str, optional): The definition of the field. Let this parameter empty, if you passed the "dim_id".
|
215
|
+
field_title (int, optional): The title of the field. Let this parameter empty, if you passed the "dim_id".
|
216
|
+
|
217
|
+
Returns:
|
218
|
+
dict: The handle and Id of the list object.
|
219
|
+
"""
|
220
|
+
if field_def is None:
|
221
|
+
field_def = []
|
222
|
+
|
223
|
+
nx_info = self.structs.nx_info(obj_type="listbox")
|
224
|
+
sort_criterias = self.structs.sort_criteria()
|
225
|
+
|
226
|
+
nx_library_dimension_def = self.structs.nx_inline_dimension_def(grouping="N", field_definitions=[field_def],
|
227
|
+
field_labels=[field_def],
|
228
|
+
sort_criterias=[sort_criterias])
|
229
|
+
list_object_def = self.structs.list_object_def(library_id=dim_id, definition=nx_library_dimension_def)
|
230
|
+
list_object_props = self.structs.generic_object_properties(info=nx_info, prop_name="qListObjectDef",
|
231
|
+
prop_def=list_object_def)
|
232
|
+
list_object_props.update(
|
233
|
+
{"showTitles": True, "title": field_title, "subtitle": "", "footnote": "", "disableNavMenu": False,
|
234
|
+
"showDetails": True, "showDetailsExpression": False, "visualization": "listbox"})
|
235
|
+
list_object = self.egoa.create_child(handle=handle, prop=list_object_props)
|
236
|
+
|
237
|
+
return list_object
|
238
|
+
|
239
|
+
def create_filterpane_frame(self, handle: int, no_of_rows_sheet: int, col: int, row: int, colspan: int, rowspan: int):
|
240
|
+
"""
|
241
|
+
Creates a filterpane frame.
|
242
|
+
|
243
|
+
Parameters:
|
244
|
+
handle (int): The handle of the parent object.
|
245
|
+
no_of_rows_sheet (int): The number of the sheet rows.
|
246
|
+
col (int): First column the filterpane visualisation starts.
|
247
|
+
row (int): First row the filterpane visualisation starts.
|
248
|
+
colspan (int): The width of the filterpane in columns.
|
249
|
+
rowspan (int): The height of the filterpane in rows.
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
dict: The handle and Id of the filterpane frame.
|
253
|
+
"""
|
254
|
+
nx_info = self.structs.nx_info(obj_type="filterpane")
|
255
|
+
filterpane_props = self.structs.generic_object_properties(info=nx_info, prop_name="qMetaDef")
|
256
|
+
filterpane_props.update({"qChildListDef": {"qData": {}}})
|
257
|
+
filterpane = self.egoa.create_child(handle=handle, prop=filterpane_props)
|
258
|
+
|
259
|
+
filterpane_id = self.get_id(filterpane)
|
260
|
+
|
261
|
+
no_of_cols_sheet = no_of_rows_sheet * 2
|
262
|
+
width = colspan / no_of_cols_sheet * 100
|
263
|
+
height = rowspan / no_of_rows_sheet * 100
|
264
|
+
y = row / no_of_rows_sheet * 100
|
265
|
+
x = col / no_of_cols_sheet * 100
|
266
|
+
|
267
|
+
if col >= 0 and colspan > 0 and no_of_cols_sheet >= col + colspan and row >= 0 and rowspan > 0 and no_of_rows_sheet >= row + rowspan:
|
268
|
+
filterpane_layout = self.structs.object_position_size(obj_id=filterpane_id, obj_type="filterpane",
|
269
|
+
col=col, row=row, colspan=colspan,
|
270
|
+
rowspan=rowspan, y=y, x=x, width=width,
|
271
|
+
height=height)
|
272
|
+
|
273
|
+
sheet_layout = self.egoa.get_layout(handle=handle)
|
274
|
+
|
275
|
+
if "cells" not in sheet_layout:
|
276
|
+
patch_value = str([filterpane_layout]).replace("'", "\"")
|
277
|
+
patch_cell = self.structs.nx_patch(op="add", path="/cells", value=patch_value)
|
278
|
+
else:
|
279
|
+
cells = sheet_layout["cells"]
|
280
|
+
cells.append(filterpane_layout)
|
281
|
+
patch_value = str(cells).replace("'", "\"")
|
282
|
+
patch_cell = self.structs.nx_patch(op="replace", path="/cells", value=patch_value)
|
283
|
+
|
284
|
+
self.egoa.apply_patches(handle=handle, patches=[patch_cell])
|
285
|
+
else:
|
286
|
+
print("The position of filterpane \"" + filterpane_id + "\" is out of range. This one will not be created.")
|
287
|
+
|
288
|
+
return filterpane
|
289
|
+
|
290
|
+
|
291
|
+
def create_chart(self, handle: int, obj_type: str, hypercube_def: dict, no_of_rows_sheet: int, col: int, row: int,
|
292
|
+
colspan: int, rowspan: int):
|
293
|
+
"""
|
294
|
+
Creates a chart object.
|
295
|
+
|
296
|
+
Parameters:
|
297
|
+
handle (int): The handle of the parent object.
|
298
|
+
obj_type (str): The type of the chart.
|
299
|
+
hypercube_def (dict): Chart hypercube definition.
|
300
|
+
no_of_rows_sheet (int): The number of the sheet rows.
|
301
|
+
col (int): First column the chart visualisation starts.
|
302
|
+
row (int): First row the chart visualisation starts.
|
303
|
+
colspan (int): The width of the chart in columns.
|
304
|
+
rowspan (int): The height of the chart in rows.
|
305
|
+
|
306
|
+
Returns:
|
307
|
+
dict: The handle and Id of the filterpane frame.
|
308
|
+
"""
|
309
|
+
|
310
|
+
nx_info = self.structs.nx_info(obj_type=obj_type)
|
311
|
+
if obj_type == "table":
|
312
|
+
chart_props = self.structs.table_properties(info=nx_info, hypercube_def=hypercube_def)
|
313
|
+
elif obj_type == "pivot-table":
|
314
|
+
chart_props = self.structs.pivot_table_properties(info=nx_info, hypercube_def=hypercube_def)
|
315
|
+
else:
|
316
|
+
print("Not valid object type.")
|
317
|
+
|
318
|
+
chart = self.egoa.create_child(handle=handle, prop=chart_props)
|
319
|
+
|
320
|
+
chart_id = self.get_id(chart)
|
321
|
+
|
322
|
+
no_of_cols_sheet = no_of_rows_sheet * 2
|
323
|
+
width = colspan / no_of_cols_sheet * 100
|
324
|
+
height = rowspan / no_of_rows_sheet * 100
|
325
|
+
y = row / no_of_rows_sheet * 100
|
326
|
+
x = col / no_of_cols_sheet * 100
|
327
|
+
|
328
|
+
if col >= 0 and colspan > 0 and no_of_cols_sheet >= col + colspan and row >= 0 and rowspan > 0 and no_of_rows_sheet >= row + rowspan:
|
329
|
+
chart_layout = self.structs.object_position_size(obj_id=chart_id, obj_type=obj_type, col=col, row=row,
|
330
|
+
colspan=colspan, rowspan=rowspan, y=y, x=x, width=width,
|
331
|
+
height=height)
|
332
|
+
|
333
|
+
sheet_layout = self.egoa.get_layout(handle=handle)
|
334
|
+
|
335
|
+
if "cells" not in sheet_layout:
|
336
|
+
patch_value = str([chart_layout]).replace("'", "\"")
|
337
|
+
patch_cell = self.structs.nx_patch(op="add", path="/cells", value=patch_value)
|
338
|
+
else:
|
339
|
+
cells = sheet_layout["cells"]
|
340
|
+
cells.append(chart_layout)
|
341
|
+
patch_value = str(cells).replace("'", "\"")
|
342
|
+
patch_cell = self.structs.nx_patch(op="replace", path="/cells", value=patch_value)
|
343
|
+
|
344
|
+
self.egoa.apply_patches(handle=handle, patches=[patch_cell])
|
345
|
+
else:
|
346
|
+
print("The position of chart \"" + chart_id + "\" is out of range. This one will not be created.")
|
347
|
+
|
348
|
+
return chart
|
349
|
+
|
350
|
+
|
151
351
|
def get_app_lineage_info(self, app_handle):
|
152
352
|
"""
|
153
353
|
Gets the lineage information of the app. The lineage information includes the LOAD and STORE statements from
|
@@ -190,6 +390,26 @@ class QixEngine:
|
|
190
390
|
except ValueError:
|
191
391
|
return "Bad handle value in " + obj
|
192
392
|
|
393
|
+
@staticmethod
|
394
|
+
def get_id(obj):
|
395
|
+
"""
|
396
|
+
Retrieves the id from a given object.
|
397
|
+
|
398
|
+
Parameters:
|
399
|
+
obj : dict
|
400
|
+
The object containing the handle.
|
401
|
+
|
402
|
+
Returns:
|
403
|
+
int: The handle value.
|
404
|
+
|
405
|
+
Raises:
|
406
|
+
ValueError: If the handle value is invalid.
|
407
|
+
"""
|
408
|
+
try:
|
409
|
+
return obj["qGenericId"]
|
410
|
+
except ValueError:
|
411
|
+
return "Bad id value in " + obj
|
412
|
+
|
193
413
|
def get_chart_data(self, app_handle, obj_id):
|
194
414
|
"""
|
195
415
|
Retrieves the data from a given chart object.
|
@@ -355,7 +575,7 @@ class QixEngine:
|
|
355
575
|
hc_mes.append(self.structs.nx_measure(library_id=measure))
|
356
576
|
|
357
577
|
# Create hypercube structure
|
358
|
-
hc_def = self.structs.hypercube_def(state_name="$",
|
578
|
+
hc_def = self.structs.hypercube_def(state_name="$", dimensions=hc_dim, measures=hc_mes)
|
359
579
|
|
360
580
|
# Create info structure
|
361
581
|
nx_info = self.structs.nx_info(obj_type="table")
|
@@ -542,39 +762,39 @@ class QixEngine:
|
|
542
762
|
df_dimension_list.loc[len(df_dimension_list)] = dim_layout
|
543
763
|
|
544
764
|
# Resolve the dictionary structure of attribute "qInfo"
|
545
|
-
df_dimension_list_expanded = (df_dimension_list["qInfo"].apply(pd.Series).add_prefix("qInfo_"))
|
765
|
+
df_dimension_list_expanded = (df_dimension_list["qInfo"].dropna().apply(pd.Series).add_prefix("qInfo_"))
|
546
766
|
df_dimension_list = df_dimension_list.drop(columns=["qInfo"]).join(df_dimension_list_expanded)
|
547
767
|
|
548
768
|
# Resolve the dictionary structure of attribute "qMeta"
|
549
|
-
df_dimension_list_expanded = (df_dimension_list["qMeta"].apply(pd.Series).add_prefix("qMeta_"))
|
769
|
+
df_dimension_list_expanded = (df_dimension_list["qMeta"].dropna().apply(pd.Series).add_prefix("qMeta_"))
|
550
770
|
df_dimension_list = df_dimension_list.drop(columns=["qMeta"]).join(df_dimension_list_expanded)
|
551
771
|
|
552
772
|
# Resolve the dictionary structure of attribute "qDim"
|
553
|
-
df_dimension_list_expanded = (df_dimension_list["qDim"].apply(pd.Series).add_prefix("qDim_"))
|
773
|
+
df_dimension_list_expanded = (df_dimension_list["qDim"].dropna().apply(pd.Series).add_prefix("qDim_"))
|
554
774
|
df_dimension_list = df_dimension_list.drop(columns=["qDim"]).join(df_dimension_list_expanded)
|
555
775
|
|
556
776
|
# Resolve the dictionary structure of attribute "qDim_coloring"
|
557
777
|
try:
|
558
778
|
df_dimension_list_expanded = (
|
559
|
-
df_dimension_list["qDim_coloring"].apply(pd.Series).add_prefix("qDim_coloring_"))
|
779
|
+
df_dimension_list["qDim_coloring"].dropna().apply(pd.Series).add_prefix("qDim_coloring_"))
|
560
780
|
df_dimension_list = df_dimension_list.drop(columns=["qDim_coloring"]).join(df_dimension_list_expanded)
|
561
781
|
except KeyError:
|
562
|
-
df_dimension_list["qDim_coloring"] =
|
782
|
+
df_dimension_list["qDim_coloring"] = None
|
563
783
|
|
564
784
|
# Resolve the dictionary structure of attribute "qDim_coloring_baseColor"
|
565
785
|
try:
|
566
786
|
df_dimension_list_expanded = (
|
567
|
-
df_dimension_list["qDim_coloring_baseColor"].apply(pd.Series).add_prefix("qDim_coloring_baseColor_"))
|
787
|
+
df_dimension_list["qDim_coloring_baseColor"].dropna().apply(pd.Series).add_prefix("qDim_coloring_baseColor_"))
|
568
788
|
df_dimension_list = df_dimension_list.drop(columns=["qDim_coloring_baseColor"]).join(
|
569
789
|
df_dimension_list_expanded)
|
570
790
|
except KeyError:
|
571
|
-
df_dimension_list["qDim_coloring_baseColor"] =
|
791
|
+
df_dimension_list["qDim_coloring_baseColor"] = None
|
572
792
|
|
573
793
|
# Resolve the list structure of attribute
|
574
794
|
df_dimension_list = df_dimension_list.explode(['qDimInfos', 'qDim_qFieldDefs', 'qDim_qFieldLabels'])
|
575
795
|
|
576
796
|
# Resolve the dictionary structure of attribute "qDimInfos"
|
577
|
-
df_dimension_list_expanded = (df_dimension_list["qDimInfos"].apply(pd.Series).add_prefix("qDimInfos_"))
|
797
|
+
df_dimension_list_expanded = (df_dimension_list["qDimInfos"].dropna().apply(pd.Series).add_prefix("qDimInfos_"))
|
578
798
|
index = df_dimension_list_expanded.index
|
579
799
|
df_dimension_list_expanded = df_dimension_list_expanded[~index.duplicated(keep="first")]
|
580
800
|
df_dimension_list = df_dimension_list.drop(columns=["qDimInfos"]).join(df_dimension_list_expanded)
|
@@ -627,11 +847,11 @@ class QixEngine:
|
|
627
847
|
df_measure_list.loc[len(df_measure_list)] = measure_layout
|
628
848
|
|
629
849
|
# Resolve the dictionary structure of attribute "qInfo"
|
630
|
-
df_measure_list_expanded = (df_measure_list["qInfo"].apply(pd.Series).add_prefix("qInfo_"))
|
850
|
+
df_measure_list_expanded = (df_measure_list["qInfo"].dropna().apply(pd.Series).add_prefix("qInfo_"))
|
631
851
|
df_measure_list = df_measure_list.drop(columns=["qInfo"]).join(df_measure_list_expanded)
|
632
852
|
|
633
853
|
# Resolve the dictionary structure of attribute "qMeasure"
|
634
|
-
df_measure_list_expanded = (df_measure_list["qMeasure"].apply(pd.Series).add_prefix("qMeasure_"))
|
854
|
+
df_measure_list_expanded = (df_measure_list["qMeasure"].dropna().apply(pd.Series).add_prefix("qMeasure_"))
|
635
855
|
df_measure_list = df_measure_list.drop(columns=["qMeasure"]).join(df_measure_list_expanded)
|
636
856
|
|
637
857
|
# Resolve the dictionary structure of attribute "qMeta"
|
@@ -640,29 +860,204 @@ class QixEngine:
|
|
640
860
|
|
641
861
|
# Resolve the dictionary structure of attribute "qMeasure_qNumFormat"
|
642
862
|
df_measure_list_expanded = (
|
643
|
-
df_measure_list["qMeasure_qNumFormat"].apply(pd.Series).add_prefix("qMeasure_qNumFormat_"))
|
863
|
+
df_measure_list["qMeasure_qNumFormat"].dropna().apply(pd.Series).add_prefix("qMeasure_qNumFormat_"))
|
644
864
|
df_measure_list = df_measure_list.drop(columns=["qMeasure_qNumFormat"]).join(df_measure_list_expanded)
|
645
865
|
|
646
866
|
# Resolve the dictionary structure of attribute "qMeasure_coloring"
|
647
867
|
try:
|
648
868
|
df_measure_list_expanded = (
|
649
|
-
df_measure_list["qMeasure_coloring"].apply(pd.Series).add_prefix("qMeasure_coloring_"))
|
869
|
+
df_measure_list["qMeasure_coloring"].dropna().apply(pd.Series).add_prefix("qMeasure_coloring_"))
|
650
870
|
df_measure_list = df_measure_list.drop(columns=["qMeasure_coloring"]).join(df_measure_list_expanded)
|
651
871
|
except KeyError:
|
652
|
-
df_measure_list["qMeasure_coloring"] =
|
872
|
+
df_measure_list["qMeasure_coloring"] = None
|
653
873
|
|
654
874
|
# Resolve the dictionary structure of attribute "qMeasure_coloring_baseColor"
|
655
875
|
try:
|
656
|
-
df_measure_list_expanded = (df_measure_list["qMeasure_coloring_baseColor"].apply(pd.Series).add_prefix(
|
876
|
+
df_measure_list_expanded = (df_measure_list["qMeasure_coloring_baseColor"].dropna().apply(pd.Series).add_prefix(
|
657
877
|
"qMeasure_coloring_baseColor_"))
|
658
878
|
df_measure_list = df_measure_list.drop(columns=["qMeasure_coloring_baseColor"]).join(
|
659
879
|
df_measure_list_expanded)
|
660
880
|
except KeyError:
|
661
|
-
df_measure_list["qMeasure_coloring_baseColor"] =
|
881
|
+
df_measure_list["qMeasure_coloring_baseColor"] = None
|
662
882
|
|
663
883
|
return df_measure_list
|
664
884
|
|
665
885
|
|
886
|
+
def get_app_sheets(self, app_handle):
|
887
|
+
"""
|
888
|
+
Retrieves a list with all app sheets and their content containing metadata.
|
889
|
+
|
890
|
+
Parameters:
|
891
|
+
app_handle (int): The handle of the app.
|
892
|
+
|
893
|
+
Returns:
|
894
|
+
DataFrame: A table with all sheets and their content from an app.
|
895
|
+
"""
|
896
|
+
# Define the parameters of the session object
|
897
|
+
nx_info = self.structs.nx_info(obj_type="SheetList")
|
898
|
+
sheet_list_def = self.structs.sheet_list_def()
|
899
|
+
gen_obj_props = self.structs.generic_object_properties(info=nx_info, prop_name="qAppObjectListDef",
|
900
|
+
prop_def=sheet_list_def)
|
901
|
+
|
902
|
+
# Create session object
|
903
|
+
session = self.eaa.create_session_object(app_handle, gen_obj_props)
|
904
|
+
|
905
|
+
# Get session handle
|
906
|
+
session_handle = self.get_handle(session)
|
907
|
+
|
908
|
+
# Get session object data
|
909
|
+
session_layout = self.egoa.get_layout(session_handle)
|
910
|
+
|
911
|
+
# Get the sheet list as Dictionary structure
|
912
|
+
sheet_list = session_layout["qAppObjectList"]["qItems"]
|
913
|
+
|
914
|
+
# Define the DataFrame structure
|
915
|
+
df_sheet_list = pd.DataFrame(columns=['qInfo', 'qMeta', 'qSelectionInfo', 'rank', 'thumbnail', 'columns', 'rows', 'cells', 'qChildList', 'gridResolution', 'layoutOptions', 'gridMode', 'customRowBase'])
|
916
|
+
|
917
|
+
for sheet in sheet_list:
|
918
|
+
# Get sheet ID
|
919
|
+
sheet_id = sheet["qInfo"]["qId"]
|
920
|
+
# Get sheet
|
921
|
+
sheet_result = self.eaa.get_object(app_handle=app_handle, object_id=sheet_id)
|
922
|
+
# Get sheet handle
|
923
|
+
sheet_handle = self.get_handle(sheet_result)
|
924
|
+
# Get session object data
|
925
|
+
sheet_layout = self.egoa.get_layout(sheet_handle)
|
926
|
+
|
927
|
+
# Concatenate the measure metadata to the DataFrame structure
|
928
|
+
df_sheet_list.loc[len(df_sheet_list)] = sheet_layout
|
929
|
+
|
930
|
+
# Resolve the dictionary structure of attribute "qInfo"
|
931
|
+
df_sheet_list_expanded = (df_sheet_list["qInfo"].dropna().apply(pd.Series).add_prefix("qInfo_"))
|
932
|
+
df_sheet_list = df_sheet_list.drop(columns=["qInfo"]).join(df_sheet_list_expanded)
|
933
|
+
|
934
|
+
# Resolve the dictionary structure of attribute "qMeta"
|
935
|
+
df_sheet_list_expanded = (df_sheet_list["qMeta"].dropna().apply(pd.Series).add_prefix("qMeta_"))
|
936
|
+
df_sheet_list = df_sheet_list.drop(columns=["qMeta"]).join(df_sheet_list_expanded)
|
937
|
+
|
938
|
+
# Resolve the dictionary structure of attribute "qSelectionInfo"
|
939
|
+
df_sheet_list["qSelectionInfo"] = df_sheet_list["qSelectionInfo"].apply(
|
940
|
+
lambda x: None if isinstance(x, dict) and len(x) == 0 else x
|
941
|
+
)
|
942
|
+
df_sheet_list_expanded = (df_sheet_list["qSelectionInfo"].dropna().apply(pd.Series).add_prefix("qSelectionInfo_"))
|
943
|
+
df_sheet_list = df_sheet_list.drop(columns=["qSelectionInfo"]).join(df_sheet_list_expanded)
|
944
|
+
|
945
|
+
# Resolve the dictionary structure of attribute "thumbnail"
|
946
|
+
df_sheet_list_expanded = (df_sheet_list["thumbnail"].dropna().apply(pd.Series).add_prefix("thumbnail_"))
|
947
|
+
df_sheet_list = df_sheet_list.drop(columns=["thumbnail"]).join(df_sheet_list_expanded)
|
948
|
+
|
949
|
+
# Resolve the dictionary structure of attribute "thumbnail_qStaticContentUrl"
|
950
|
+
df_sheet_list["thumbnail_qStaticContentUrl"] = df_sheet_list["thumbnail_qStaticContentUrl"].apply(
|
951
|
+
lambda x: None if isinstance(x, dict) and len(x) == 0 else x
|
952
|
+
)
|
953
|
+
df_sheet_list_expanded = (df_sheet_list["thumbnail_qStaticContentUrl"].dropna().apply(pd.Series).add_prefix("thumbnail_qStaticContentUrl_"))
|
954
|
+
df_sheet_list = df_sheet_list.drop(columns=["thumbnail_qStaticContentUrl"]).join(df_sheet_list_expanded)
|
955
|
+
|
956
|
+
# Resolve the dictionary structure of attribute "qChildList"
|
957
|
+
df_sheet_list_expanded = (df_sheet_list["qChildList"].dropna().apply(pd.Series).add_prefix("qChildList_"))
|
958
|
+
df_sheet_list = df_sheet_list.drop(columns=["qChildList"]).join(df_sheet_list_expanded)
|
959
|
+
|
960
|
+
# Resolve the dictionary structure of attribute "layoutOptions"
|
961
|
+
df_sheet_list_expanded = (df_sheet_list["layoutOptions"].dropna().apply(pd.Series).add_prefix("layoutOptions_"))
|
962
|
+
df_sheet_list = df_sheet_list.drop(columns=["layoutOptions"]).join(df_sheet_list_expanded)
|
963
|
+
|
964
|
+
# Resolve the list structure of attribute
|
965
|
+
df_sheet_list = df_sheet_list.explode(['cells', 'qChildList_qItems'])
|
966
|
+
|
967
|
+
# Resolve the dictionary structure of attribute "cells"
|
968
|
+
df_sheet_list_expanded = (df_sheet_list["cells"].dropna().apply(pd.Series).add_prefix("cells_"))
|
969
|
+
index = df_sheet_list_expanded.index
|
970
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
971
|
+
df_sheet_list = df_sheet_list.drop(columns=["cells"]).join(df_sheet_list_expanded)
|
972
|
+
|
973
|
+
# Resolve the dictionary structure of attribute "cells_bounds"
|
974
|
+
df_sheet_list_expanded = (
|
975
|
+
df_sheet_list["cells_bounds"].dropna().apply(pd.Series).add_prefix("cells_bounds_"))
|
976
|
+
index = df_sheet_list_expanded.index
|
977
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
978
|
+
df_sheet_list = df_sheet_list.drop(columns=["cells_bounds"]).join(df_sheet_list_expanded)
|
979
|
+
|
980
|
+
# Resolve the dictionary structure of attribute "qChildList_qItems"
|
981
|
+
df_sheet_list_expanded = (
|
982
|
+
df_sheet_list["qChildList_qItems"].dropna().apply(pd.Series).add_prefix("qChildList_qItems_"))
|
983
|
+
index = df_sheet_list_expanded.index
|
984
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
985
|
+
df_sheet_list = df_sheet_list.drop(columns=["qChildList_qItems"]).join(df_sheet_list_expanded)
|
986
|
+
|
987
|
+
# Resolve the dictionary structure of attribute "qChildList_qItems_qInfo"
|
988
|
+
df_sheet_list_expanded = (
|
989
|
+
df_sheet_list["qChildList_qItems_qInfo"].dropna().apply(pd.Series).add_prefix("qChildList_qItems_qInfo_"))
|
990
|
+
index = df_sheet_list_expanded.index
|
991
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
992
|
+
df_sheet_list = df_sheet_list.drop(columns=["qChildList_qItems_qInfo"]).join(df_sheet_list_expanded)
|
993
|
+
|
994
|
+
# Resolve the dictionary structure of attribute "qChildList_qItems_qMeta"
|
995
|
+
df_sheet_list_expanded = (
|
996
|
+
df_sheet_list["qChildList_qItems_qMeta"].dropna().apply(pd.Series).add_prefix("qChildList_qItems_qMeta_"))
|
997
|
+
index = df_sheet_list_expanded.index
|
998
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
999
|
+
df_sheet_list = df_sheet_list.drop(columns=["qChildList_qItems_qMeta"]).join(df_sheet_list_expanded)
|
1000
|
+
|
1001
|
+
# Resolve the dictionary structure of attribute "qChildList_qItems_qData"
|
1002
|
+
df_sheet_list_expanded = (
|
1003
|
+
df_sheet_list["qChildList_qItems_qData"].dropna().apply(pd.Series).add_prefix("qChildList_qItems_qData_"))
|
1004
|
+
index = df_sheet_list_expanded.index
|
1005
|
+
df_sheet_list_expanded = df_sheet_list_expanded[~index.duplicated(keep="first")]
|
1006
|
+
df_sheet_list = df_sheet_list.drop(columns=["qChildList_qItems_qData"]).join(df_sheet_list_expanded)
|
1007
|
+
|
1008
|
+
return df_sheet_list
|
1009
|
+
|
1010
|
+
|
1011
|
+
def get_app_variables(self, app_handle):
|
1012
|
+
"""
|
1013
|
+
Retrieves a list with all app variables containing metadata.
|
1014
|
+
|
1015
|
+
Parameters:
|
1016
|
+
app_handle (int): The handle of the app.
|
1017
|
+
|
1018
|
+
Returns:
|
1019
|
+
DataFrame: A table with all variables from an app.
|
1020
|
+
"""
|
1021
|
+
# Define the parameters of the session object
|
1022
|
+
nx_info = self.structs.nx_info(obj_type="VariableList")
|
1023
|
+
variable_list_def = self.structs.variable_list_def()
|
1024
|
+
gen_obj_props = self.structs.generic_object_properties(info=nx_info, prop_name="qVariableListDef",
|
1025
|
+
prop_def=variable_list_def)
|
1026
|
+
|
1027
|
+
# Create session object
|
1028
|
+
session = self.eaa.create_session_object(app_handle, gen_obj_props)
|
1029
|
+
|
1030
|
+
# Get session handle
|
1031
|
+
session_handle = self.get_handle(session)
|
1032
|
+
|
1033
|
+
# Get session object data
|
1034
|
+
session_layout = self.egoa.get_layout(session_handle)
|
1035
|
+
|
1036
|
+
# Get the variable list as Dictionary structure
|
1037
|
+
variable_list = session_layout["qVariableList"]["qItems"]
|
1038
|
+
|
1039
|
+
# Define the DataFrame structure
|
1040
|
+
df_variable_list = pd.DataFrame(columns=["qName", "qDefinition", "qMeta", "qInfo", "qData", "qIsScriptCreated", "qIsReserved"])
|
1041
|
+
|
1042
|
+
for variable in variable_list:
|
1043
|
+
# Concatenate the measure metadata to the DataFrame structure
|
1044
|
+
df_variable_list.loc[len(df_variable_list)] = variable
|
1045
|
+
|
1046
|
+
# Resolve the dictionary structure of attribute "qInfo"
|
1047
|
+
df_variable_list_expanded = (df_variable_list["qInfo"].dropna().apply(pd.Series).add_prefix("qInfo_"))
|
1048
|
+
df_variable_list = df_variable_list.drop(columns=["qInfo"]).join(df_variable_list_expanded)
|
1049
|
+
|
1050
|
+
# Resolve the dictionary structure of attribute "qMeta"
|
1051
|
+
df_variable_list_expanded = (df_variable_list["qMeta"].dropna().apply(pd.Series).add_prefix("qMeta_"))
|
1052
|
+
df_variable_list = df_variable_list.drop(columns=["qMeta"]).join(df_variable_list_expanded)
|
1053
|
+
|
1054
|
+
# Resolve the dictionary structure of attribute "qData"
|
1055
|
+
df_variable_list_expanded = (df_variable_list["qData"].dropna().apply(pd.Series).add_prefix("qData_"))
|
1056
|
+
df_variable_list = df_variable_list.drop(columns=["qData"]).join(df_variable_list_expanded)
|
1057
|
+
|
1058
|
+
return df_variable_list
|
1059
|
+
|
1060
|
+
|
666
1061
|
def get_app_lineage(self, app_handle):
|
667
1062
|
"""
|
668
1063
|
Retrieves a list with an app lineage data.
|