qe-api-client 2.7.0__py3-none-any.whl → 2.9.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.
@@ -26,7 +26,7 @@ class EngineAppApi:
26
26
  """
27
27
  self.engine_socket = socket
28
28
 
29
- def get_script(self, doc_handle):
29
+ def get_script(self, doc_handle: int):
30
30
  """
31
31
  Retrieves the script of the app identified by the document handle.
32
32
 
@@ -43,7 +43,7 @@ class EngineAppApi:
43
43
  except KeyError:
44
44
  return response['error']
45
45
 
46
- def set_script(self, doc_handle, script):
46
+ def set_script(self, doc_handle: int, script):
47
47
  """
48
48
  Sets the script of the app identified by the document handle.
49
49
 
@@ -61,7 +61,7 @@ class EngineAppApi:
61
61
  except KeyError:
62
62
  return response['error']
63
63
 
64
- def do_reload(self, doc_handle, param_list=[]):
64
+ def do_reload(self, doc_handle: int, param_list=[]):
65
65
  """
66
66
  Triggers a reload of the app identified by the document handle.
67
67
 
@@ -79,7 +79,7 @@ class EngineAppApi:
79
79
  except KeyError:
80
80
  return response['error']
81
81
 
82
- def do_reload_ex(self, doc_handle, param_list={}):
82
+ def do_reload_ex(self, doc_handle: int, param_list={}):
83
83
  """
84
84
  Triggers an extended reload of the app identified by the document handle.
85
85
 
@@ -98,7 +98,7 @@ class EngineAppApi:
98
98
  except KeyError:
99
99
  return response['error']
100
100
 
101
- def get_app_layout(self, doc_handle):
101
+ def get_app_layout(self, doc_handle: int):
102
102
  """
103
103
  Retrieves the layout structure of the app identified by the document handle.
104
104
 
@@ -115,7 +115,7 @@ class EngineAppApi:
115
115
  except KeyError:
116
116
  return response['error']
117
117
 
118
- def get_object(self, app_handle, object_id):
118
+ def get_object(self, app_handle: int, object_id: str):
119
119
  """
120
120
  Retrieves a specific object from the app identified by the document handle.
121
121
 
@@ -134,7 +134,7 @@ class EngineAppApi:
134
134
  except KeyError:
135
135
  return response['error']
136
136
 
137
- def get_field(self, doc_handle, field_name, state_name=""):
137
+ def get_field(self, doc_handle: int, field_name, state_name=""):
138
138
  """
139
139
  Retrieves a specific field from the app identified by the document handle.
140
140
 
@@ -154,7 +154,7 @@ class EngineAppApi:
154
154
  except KeyError:
155
155
  return response['error']
156
156
 
157
- def create_object(self, doc_handle, prop):
157
+ def create_object(self, doc_handle: int, prop):
158
158
  """
159
159
  Creates a new object in the app identified by the document handle.
160
160
 
@@ -177,7 +177,7 @@ class EngineAppApi:
177
177
  # You can create multiple states within a Qlik Sense app and apply these states to specific objects within the app. # NOQA
178
178
  # Objects in a given state are not affected by user selections in the other states. # NOQA
179
179
  # Call GetAppLayout() afterwards to get the latest states
180
- def add_alternate_state(self, doc_handle, state_name):
180
+ def add_alternate_state(self, doc_handle: int, state_name):
181
181
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "AddAlternateState",
182
182
  "params": [state_name]})
183
183
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -188,7 +188,7 @@ class EngineAppApi:
188
188
 
189
189
  # AddFieldFromExpression method: Adds a field on the fly. !! The expression of a field on the fly is persisted but # NOQA
190
190
  # not its values. !!
191
- def add_field_from_expression(self, doc_handle, field_name, expr_value):
191
+ def add_field_from_expression(self, doc_handle: int, field_name, expr_value):
192
192
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "AddFieldFromExpression",
193
193
  "params": [field_name, expr_value]})
194
194
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -199,7 +199,7 @@ class EngineAppApi:
199
199
 
200
200
  # CheckExpression method: Checks whether an expression is valid or not
201
201
  # qErrorMsg is empty if it's valid
202
- def check_expression(self, doc_handle, expr_value):
202
+ def check_expression(self, doc_handle: int, expr_value):
203
203
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CheckExpression",
204
204
  "params": [expr_value]})
205
205
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -211,7 +211,7 @@ class EngineAppApi:
211
211
  # CheckScriptSyntax method: Checks whether a load script is valid or not
212
212
  # Used AFTER doing SetScript method
213
213
  # errors are displayed in an array discussing positions of characters in script where failing # NOQA
214
- def check_script(self, doc_handle):
214
+ def check_script(self, doc_handle: int):
215
215
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CheckScriptSyntax", "params": {}})
216
216
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
217
217
  try:
@@ -219,7 +219,7 @@ class EngineAppApi:
219
219
  except KeyError:
220
220
  return response['error']
221
221
 
222
- def clear_all(self, doc_handle, locked_also=False, alt_state=""):
222
+ def clear_all(self, doc_handle: int, locked_also=False, alt_state=""):
223
223
  """
224
224
  Clear selections in fields for current state. Locked fields are not cleared by default.
225
225
 
@@ -239,7 +239,7 @@ class EngineAppApi:
239
239
  # CreateConnection method: Creates a connection. A connection indicates from which data source, the data should # NOQA
240
240
  # be taken. The connection can be: an ODBC connection, OLEDB connection, a custom connection, a folder connection # NOQA
241
241
  # (lib connection), an internet connection, Single Sign-On
242
- def create_connection(self, doc_handle, connect_name, connect_string, connect_type, user_name, password,
242
+ def create_connection(self, doc_handle: int, connect_name, connect_string, connect_type, user_name, password,
243
243
  mod_date="", meta="", sso_passthrough="LOG_ON_SERVICE_USER"):
244
244
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CreateConnection",
245
245
  "params": [{"qName": connect_name, "qMeta": meta, "qConnectionString": connect_string,
@@ -255,7 +255,7 @@ class EngineAppApi:
255
255
  # can contain the same dimension.
256
256
  # Parameters:
257
257
  # qProp (MANDATORY: send dim_id, dim_title, dim_grouping, dim_field, dim_label, meta_def (optional) # NOQA
258
- def create_dimension(self, doc_handle, prop):
258
+ def create_dimension(self, doc_handle: int, prop):
259
259
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CreateDimension",
260
260
  "params": {"qProp": prop}})
261
261
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -265,7 +265,7 @@ class EngineAppApi:
265
265
  return response['error']
266
266
 
267
267
  # DestroyDimension method: Removes a dimension
268
- def destroy_dimension(self, doc_handle, dim_id):
268
+ def destroy_dimension(self, doc_handle: int, dim_id):
269
269
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroyDimension",
270
270
  "params": {"qId": dim_id}})
271
271
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -275,7 +275,7 @@ class EngineAppApi:
275
275
  return response["error"]
276
276
 
277
277
  # DestroyMeasure method: Removes a measure
278
- def destroy_measure(self, doc_handle, measure_id):
278
+ def destroy_measure(self, doc_handle: int, measure_id):
279
279
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroyDimension",
280
280
  "params": [{measure_id}]})
281
281
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -285,7 +285,7 @@ class EngineAppApi:
285
285
  return response['error']
286
286
 
287
287
  # DestroyObject method: Removes an app object. The children of the object (if any) are removed as well. # NOQA
288
- def destroy_object(self, doc_handle, object_id):
288
+ def destroy_object(self, doc_handle: int, object_id):
289
289
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroyObject",
290
290
  "params": [{object_id}]})
291
291
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -295,7 +295,7 @@ class EngineAppApi:
295
295
  return response['error']
296
296
 
297
297
  # DestroySessionObject method: Removes a session object. The children of the object (if any) are removed as well. # NOQA
298
- def destroy_session_object(self, doc_handle, object_id):
298
+ def destroy_session_object(self, doc_handle: int, object_id):
299
299
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroySessionObject",
300
300
  "params": [{object_id}]})
301
301
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -305,7 +305,7 @@ class EngineAppApi:
305
305
  return response['error']
306
306
 
307
307
  # DestroySessionVariable method: Removes an transient variable.
308
- def destroy_session_variable(self, doc_handle, var_id):
308
+ def destroy_session_variable(self, doc_handle: int, var_id):
309
309
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroySessionVariable",
310
310
  "params": [{var_id}]})
311
311
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -317,7 +317,7 @@ class EngineAppApi:
317
317
  # DestroyVariableById method: Removes a varable..
318
318
  # Script-defined variables cannot be removed using the DestroyVariableById method or the # NOQA
319
319
  # DestroyVariableByName method.
320
- def destroy_variable_by_id(self, doc_handle, var_name):
320
+ def destroy_variable_by_id(self, doc_handle: int, var_name):
321
321
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DestroyVariableById",
322
322
  "params": [{var_name}]})
323
323
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -331,7 +331,7 @@ class EngineAppApi:
331
331
  # can contain the same dimension.
332
332
  # Parameters:
333
333
  # qProp (MANDATORY: send dim_id, dim_title, dim_grouping, dim_field, dim_label, meta_def (optional) # NOQA
334
- def create_measure(self, doc_handle, prop):
334
+ def create_measure(self, doc_handle: int, prop):
335
335
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CreateMeasure",
336
336
  "params": {"qProp": prop}})
337
337
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -364,7 +364,7 @@ class EngineAppApi:
364
364
  # ### Example: The variable x contains the text string Sum(Sales). In a chart, you define the expression $(x)/12. # NOQA
365
365
  # The effect is exactly the same as having the chart expression Sum(Sales)/12. However, if you change the value of the variable x to Sum(Budget), # NOQA
366
366
  # the data in the chart are immediately recalculated with the expression interpreted as Sum(Budget)/12. # NOQA
367
- def create_session_variable(self, doc_handle, var_id="", var_name="", var_comment="", var_def=""):
367
+ def create_session_variable(self, doc_handle: int, var_id="", var_name="", var_comment="", var_def=""):
368
368
  msg = json.dumps(
369
369
  {"jsonrpc": "2.0", "id": 0, "handle": doc_handle,
370
370
  "method": "CreateSessionVariable", "params": [{
@@ -393,7 +393,7 @@ class EngineAppApi:
393
393
  # The effect is exactly the same as having the chart expression Sum(Sales)/12. # NOQA
394
394
  # However, if you change the value of the variable x to Sum(Budget),
395
395
  # the data in the chart are immediately recalculated with the expression interpreted as Sum(Budget)/12. # NOQA
396
- def create_variable(self, doc_handle, var_id="", var_name="", var_comment="", var_def=""):
396
+ def create_variable(self, doc_handle: int, var_id="", var_name="", var_comment="", var_def=""):
397
397
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle,
398
398
  "method": "CreateVariable", "params": [{
399
399
  "qInfo": {
@@ -435,7 +435,7 @@ class EngineAppApi:
435
435
 
436
436
  # DoSave method: Saves an app - All objects and data in the data model are saved. # NOQA
437
437
  # Desktop only - server auto saves
438
- def do_save(self, doc_handle, file_name=""):
438
+ def do_save(self, doc_handle: int, file_name=""):
439
439
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "DoSave",
440
440
  "params": {"qFileName": file_name}})
441
441
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -446,7 +446,7 @@ class EngineAppApi:
446
446
 
447
447
  # Evaluate method: Evaluates an expression as a string. (Actually uses EvaluateEx, which is better for giving the data type back to python) # NOQA
448
448
  # Parameters: qExpression
449
- def expr_eval(self, doc_handle, expr):
449
+ def expr_eval(self, doc_handle: int, expr):
450
450
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "EvaluateEx",
451
451
  "params": {"qExpression": expr}})
452
452
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -456,7 +456,7 @@ class EngineAppApi:
456
456
  return response['error']
457
457
 
458
458
  # GetAllInfos method: Get the identifier and the type of any generic object in an app by using the GetAllInfos method. # NOQA
459
- def get_all_infos(self, doc_handle):
459
+ def get_all_infos(self, doc_handle: int):
460
460
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetAllInfos", "params": []})
461
461
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
462
462
  try:
@@ -465,7 +465,7 @@ class EngineAppApi:
465
465
  return response['error']
466
466
 
467
467
  # GetAppProperties method: Gets the properties of an app.
468
- def get_app_properties(self, doc_handle):
468
+ def get_app_properties(self, doc_handle: int):
469
469
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetAppProperties", "params": []})
470
470
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
471
471
  try:
@@ -475,7 +475,7 @@ class EngineAppApi:
475
475
 
476
476
  # GetConnection method: Retrieves a connection and returns: The creation time of the connection, The identifier of # NOQA
477
477
  # the connection, The type of the connection, The name of the connection, The connection string # NOQA
478
- def get_connection(self, doc_handle, connection_id):
478
+ def get_connection(self, doc_handle: int, connection_id):
479
479
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetConnection",
480
480
  "params": [connection_id]})
481
481
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -485,7 +485,7 @@ class EngineAppApi:
485
485
  return response['error']
486
486
 
487
487
  # GetConnections method: Lists the connections in an app
488
- def get_connections(self, doc_handle):
488
+ def get_connections(self, doc_handle: int):
489
489
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetConnections", "params": []})
490
490
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
491
491
  try:
@@ -494,7 +494,7 @@ class EngineAppApi:
494
494
  return response['error']
495
495
 
496
496
  # GetDatabaseInfo: Get information about an ODBC, OLEDB or CUSTOM connection # NOQA
497
- def get_db_info(self, doc_handle, connection_id):
497
+ def get_db_info(self, doc_handle: int, connection_id):
498
498
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabaseInfo",
499
499
  "params": [connection_id]})
500
500
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -504,7 +504,7 @@ class EngineAppApi:
504
504
  return response['error']
505
505
 
506
506
  # GetDatabaseOwners: List the owners of a database for a ODBC, OLEDB or CUSTOM connection # NOQA
507
- def get_db_owners(self, doc_handle, connection_id):
507
+ def get_db_owners(self, doc_handle: int, connection_id):
508
508
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabaseOwners",
509
509
  "params": [connection_id]})
510
510
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -514,7 +514,7 @@ class EngineAppApi:
514
514
  return response['error']
515
515
 
516
516
  # GetDatabases: List the databases of a ODBC, OLEDB or CUSTOM connection
517
- def get_databases(self, doc_handle, connection_id):
517
+ def get_databases(self, doc_handle: int, connection_id):
518
518
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabases",
519
519
  "params": [connection_id]})
520
520
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -525,7 +525,7 @@ class EngineAppApi:
525
525
 
526
526
  # GetDatabaseTableFields: List the fields in a table for a ODBC, OLEDB or CUSTOM connection # NOQA
527
527
  # Parameters taken are: connection_id (mandatory), db_name, db_owner, table_name (mandatory) # NOQA
528
- def get_db_table_fields(self, doc_handle, connection_id, db_name="", db_owner="", table_name=""):
528
+ def get_db_table_fields(self, doc_handle: int, connection_id, db_name="", db_owner="", table_name=""):
529
529
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabaseTableFields",
530
530
  "params": [connection_id, db_name, db_owner, table_name]})
531
531
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -536,7 +536,7 @@ class EngineAppApi:
536
536
 
537
537
  # GetDatabaseTablePreview: Preview the data in the fields in a table for a ODBC, OLEDB or CUSTOM connection # NOQA
538
538
  # Parameters taken are: connection_id (mandatory), db_name, db_owner, table_name (mandatory) # NOQA
539
- def get_db_table_preview(self, doc_handle, connection_id, db_name="", db_owner="", table_name=""):
539
+ def get_db_table_preview(self, doc_handle: int, connection_id, db_name="", db_owner="", table_name=""):
540
540
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabaseTablePreview",
541
541
  "params": [connection_id, db_name, db_owner, table_name]})
542
542
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -547,7 +547,7 @@ class EngineAppApi:
547
547
 
548
548
  # GetDatabaseTables: List the tables in a database for a specific owner and for a ODBC, OLEDB or CUSTOM connection # NOQA
549
549
  # Parameters taken are: connection_id (mandatory), db_name, db_owner
550
- def get_db_tables(self, doc_handle, connection_id, db_name="", db_owner=""):
550
+ def get_db_tables(self, doc_handle: int, connection_id, db_name="", db_owner=""):
551
551
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetDatabaseTables",
552
552
  "params": [connection_id, db_name, db_owner]})
553
553
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -560,7 +560,7 @@ class EngineAppApi:
560
560
  # GetEmptyScript: Creates a script that contains one section. This section contains Set statements that give # NOQA
561
561
  # localized information from the regional settings of the computer.
562
562
  # Parameter: none
563
- def get_empty_script(self, doc_handle):
563
+ def get_empty_script(self, doc_handle: int):
564
564
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetEmptyScript", "params": []})
565
565
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
566
566
  try:
@@ -570,7 +570,7 @@ class EngineAppApi:
570
570
 
571
571
  # GetFieldDescription: Get the description of a field
572
572
  # Parameter: field name
573
- def get_field_descr(self, doc_handle, field_name):
573
+ def get_field_descr(self, doc_handle: int, field_name):
574
574
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetFieldDescription",
575
575
  "params": {"qFieldName": field_name}})
576
576
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -585,7 +585,7 @@ class EngineAppApi:
585
585
  # qRelativePath: Path of the connection file
586
586
  # qDataFormat: Type of the file
587
587
  # qTable (MOSTLY MANDATORY): Name of the table ***This parameter must be set for XLS, XLSX, HTML and XML files.*** # NOQA
588
- def get_file_table_fields(self, doc_handle, connection_id,
588
+ def get_file_table_fields(self, doc_handle: int, connection_id,
589
589
  rel_path="", data_fmt="", table_name=""):
590
590
  msg = json.dumps(
591
591
  {"jsonrpc": "2.0", "id": 0, "handle": doc_handle,
@@ -605,7 +605,7 @@ class EngineAppApi:
605
605
  # qRelativePath: Path of the connection file
606
606
  # qDataFormat: Type of the file
607
607
  # qTable (MOSTLY MANDATORY): Name of the table ***This parameter must be set for XLS, XLSX, HTML and XML files.*** # NOQA
608
- def get_file_table_preview(self, doc_handle, connection_id, rel_path="", data_fmt="", table_name=""):
608
+ def get_file_table_preview(self, doc_handle: int, connection_id, rel_path="", data_fmt="", table_name=""):
609
609
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetFileTablePreview",
610
610
  "params": [connection_id, rel_path, {"qType": data_fmt}, table_name]})
611
611
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -620,7 +620,7 @@ class EngineAppApi:
620
620
  # qRelativePath: Path of the connection file
621
621
  # qDataFormat: Type of the file (XML, JSON)
622
622
  # qTable (MOSTLY MANDATORY): Name of the table ***This parameter must be set for XLS, XLSX, HTML and XML files.*** # NOQA
623
- def get_file_table_ex(self, doc_handle, connection_id,
623
+ def get_file_table_ex(self, doc_handle: int, connection_id,
624
624
  rel_path="", data_fmt=""):
625
625
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetFileTablesEx",
626
626
  "params": [connection_id, rel_path, {"qType": data_fmt}]})
@@ -635,7 +635,7 @@ class EngineAppApi:
635
635
  # qConnectionId (MANDATORY): Identifier of the connection.
636
636
  # qRelativePath: Path of the connection file
637
637
  # qDataFormat: Type of the file (XML, JSON)
638
- def get_file_tables(self, doc_handle, connection_id, rel_path="", data_fmt=""):
638
+ def get_file_tables(self, doc_handle: int, connection_id, rel_path="", data_fmt=""):
639
639
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetFileTables",
640
640
  "params": [connection_id, rel_path, {"qType": data_fmt}]})
641
641
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -646,7 +646,7 @@ class EngineAppApi:
646
646
 
647
647
  # GetFolderItemsForConnection method: List the items for a folder connection # NOQA
648
648
  # Parameter: connection_id
649
- def get_folder_items_for_connection(self, doc_handle, connection_id):
649
+ def get_folder_items_for_connection(self, doc_handle: int, connection_id):
650
650
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetFolderItemsForConnection",
651
651
  "params": [connection_id]})
652
652
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -656,7 +656,7 @@ class EngineAppApi:
656
656
  return response['error']
657
657
 
658
658
  # GetAllInfos method: Get the identifier and the type of any generic object in an app by using the GetAllInfos method. # NOQA
659
- def get_lineage(self, doc_handle):
659
+ def get_lineage(self, doc_handle: int):
660
660
  """
661
661
  Gets the lineage information of the app. The lineage information includes the LOAD and STORE statements from
662
662
  the data load script associated with this app.
@@ -674,7 +674,7 @@ class EngineAppApi:
674
674
  except KeyError:
675
675
  return response['error']
676
676
 
677
- def create_session_object(self, doc_handle, prop):
677
+ def create_session_object(self, doc_handle: int, prop):
678
678
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CreateSessionObject",
679
679
  "params": {"qProp": prop}})
680
680
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -683,7 +683,7 @@ class EngineAppApi:
683
683
  except KeyError:
684
684
  return response['error']
685
685
 
686
- def get_set_analysis(self, doc_handle, state_name="", bookmark_id=""):
686
+ def get_set_analysis(self, doc_handle: int, state_name="", bookmark_id=""):
687
687
  msg = json.dumps({"jsonrpc": "2.0", "id": 3, "handle": doc_handle, "method": "GetSetAnalysis",
688
688
  "params": {"qStateName": state_name, "qBookmarkId": bookmark_id}})
689
689
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
@@ -701,7 +701,7 @@ class EngineAppApi:
701
701
  except KeyError:
702
702
  return response['error']
703
703
 
704
- def get_variable_by_id(self, doc_handle, variable_id):
704
+ def get_variable_by_id(self, doc_handle: int, variable_id):
705
705
  """
706
706
  Gets the handle of a variable.
707
707
 
@@ -715,6 +715,66 @@ class EngineAppApi:
715
715
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetVariableById",
716
716
  "params": {"qId": variable_id}})
717
717
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
718
+ try:
719
+ return response['result']['qReturn']
720
+ except KeyError:
721
+ return response['error']
722
+
723
+
724
+ def create_bookmark(self, doc_handle: int, prop: dict):
725
+ """
726
+ Creates a bookmark.
727
+
728
+ Parameters:
729
+ doc_handle (int): The handle identifying the document.
730
+ prop (dict): Bookmark properties.
731
+
732
+ Returns:
733
+ dict: The handle of the generic bookmark.
734
+ """
735
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "CreateBookmark",
736
+ "params": {"qProp": prop}})
737
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
738
+ try:
739
+ return response['result']['qReturn']
740
+ except KeyError:
741
+ return response['error']
742
+
743
+
744
+ def get_bookmarks(self, doc_handle: int, options: dict):
745
+ """
746
+ Returns all bookmarks compatible with options.
747
+
748
+ Parameters:
749
+ doc_handle (int): The handle identifying the document.
750
+ qOptions (dict): Bookmark type filter and requested properties.
751
+
752
+ Returns:
753
+ list: The resulting list.
754
+ """
755
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": doc_handle, "method": "GetBookmarks",
756
+ "params": {"qOptions": options}})
757
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
758
+ try:
759
+ return response['result']
760
+ except KeyError:
761
+ return response['error']
762
+
763
+
764
+ def get_bookmark(self, app_handle: int, bookmark_id: str):
765
+ """
766
+ Retrieves a specific bookmark from the app identified by the document handle.
767
+
768
+ Parameters:
769
+ app_handle (int): The handle identifying the app document.
770
+ bookmark_id (str): The ID of the bookmark to retrieve.
771
+
772
+ Returns:
773
+ dict: The retrieved object (qReturn). In case of an error, returns the error information.
774
+ """
775
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": app_handle, "method": "GetBookmark",
776
+ "params": {"qId": bookmark_id}})
777
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
718
778
  try:
719
779
  return response['result']['qReturn']
720
780
  except KeyError:
@@ -124,7 +124,7 @@ class EngineGenericObjectApi:
124
124
  except KeyError:
125
125
  return response["error"]
126
126
 
127
- def get_hypercube_data(self, handle, path="/qHyperCubeDef", pages={}):
127
+ def get_hypercube_data(self, handle: int, path: str, pages: list):
128
128
  """
129
129
  Retrieves the data from a specific hypercube in a generic object.
130
130
 
@@ -137,14 +137,14 @@ class EngineGenericObjectApi:
137
137
  dict: The data from the hypercube. In case of an error, returns the error information.
138
138
  """
139
139
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "GetHyperCubeData",
140
- "params": {"qPath": path, "qPages": [pages]}})
140
+ "params": {"qPath": path, "qPages": pages}})
141
141
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
142
142
  try:
143
143
  return response["result"]
144
144
  except KeyError:
145
145
  return response["error"]
146
146
 
147
- def get_hypercube_pivot_data(self, handle, path="/qHyperCubeDef", pages={}):
147
+ def get_hypercube_pivot_data(self, handle: int, path: str, pages: list):
148
148
  """
149
149
  Retrieves the pivot data from a specific hypercube in a generic object.
150
150
 
@@ -157,14 +157,14 @@ class EngineGenericObjectApi:
157
157
  dict: The pivot data from the hypercube. In case of an error, returns the error information.
158
158
  """
159
159
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "GetHyperCubePivotData",
160
- "params": {"qPath": path, "qPages": [pages]}})
160
+ "params": {"qPath": path, "qPages": pages}})
161
161
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
162
162
  try:
163
163
  return response["result"]
164
164
  except KeyError:
165
165
  return response["error"]
166
166
 
167
- def get_hypercube_stack_data(self, handle, path="/qHyperCubeDef", pages={}, max_no_cells=10000):
167
+ def get_hypercube_stack_data(self, handle: int, path: str, pages: list, max_no_cells: int = 10000):
168
168
  """
169
169
  Retrieves the values of a stacked pivot table. It is possible to retrieve specific pages of data.
170
170
 
@@ -179,7 +179,7 @@ class EngineGenericObjectApi:
179
179
  dict: The pivot data from the hypercube. In case of an error, returns the error information.
180
180
  """
181
181
  msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "GetHyperCubeStackData",
182
- "params": {"qPath": path, "qPages": [pages], "qMaxNbrCells": max_no_cells}})
182
+ "params": {"qPath": path, "qPages": pages, "qMaxNbrCells": max_no_cells}})
183
183
  response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
184
184
  try:
185
185
  return response["result"]
@@ -208,3 +208,60 @@ class EngineGenericObjectApi:
208
208
  return response["result"]
209
209
  except KeyError:
210
210
  return response["error"]
211
+
212
+
213
+ def get_properties(self, handle: int):
214
+ """
215
+ Retrieves the properties of a specific generic object.
216
+
217
+ Parameters:
218
+ handle (int): The handle identifying the generic object.
219
+
220
+ Returns:
221
+ dict: The properties of the generic object (qLayout). In case of an error, returns the error information.
222
+ """
223
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "GetProperties", "params": {}})
224
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
225
+ try:
226
+ return response["result"]["qProp"]
227
+ except KeyError:
228
+ return response["error"]
229
+
230
+
231
+ def embed_snapshot_object(self, handle: int, snapshot_id: str):
232
+ """
233
+ Adds a snapshot to a generic object. Only one snapshot can be embedded in a generic object. If you embed a
234
+ snapshot in an object that already contains a snapshot, the new snapshot overwrites the previous one.
235
+
236
+ Parameters:
237
+ handle (int): The handle identifying the generic object.
238
+ snapshot_id (str): The id of the snapshot to be embeded.
239
+
240
+ Returns:
241
+ update
242
+ """
243
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "EmbedSnapshotObject",
244
+ "params": {"qId": snapshot_id}})
245
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
246
+ try:
247
+ return response["result"]
248
+ except KeyError:
249
+ return response["error"]
250
+
251
+
252
+ def get_parent(self, handle: int):
253
+ """
254
+ Returns the type of the object and the corresponding handle to the parent object in the hiearchy.
255
+
256
+ Parameters:
257
+ handle (int): The handle identifying the generic object.
258
+
259
+ Returns:
260
+ { "qType": "GenericObject", "qHandle": <handle of the object> }
261
+ """
262
+ msg = json.dumps({"jsonrpc": "2.0", "id": 0, "handle": handle, "method": "GetParent", "params": {}})
263
+ response = json.loads(self.engine_socket.send_call(self.engine_socket, msg))
264
+ try:
265
+ return response["result"]["qReturn"]
266
+ except KeyError:
267
+ return response["error"]
qe_api_client/engine.py CHANGED
@@ -12,6 +12,8 @@ import qe_api_client.structs as structs
12
12
  import math
13
13
  import pandas as pd
14
14
  import numpy as np
15
+ from datetime import datetime, timezone
16
+ import time
15
17
 
16
18
 
17
19
  class QixEngine:
@@ -135,9 +137,9 @@ class QixEngine:
135
137
  # Define of the single dimension properties
136
138
  nx_info = self.structs.nx_info(obj_type="dimension")
137
139
  if dim_color is None:
138
- coloring = self.structs.coloring()
140
+ coloring = self.structs.dim_coloring()
139
141
  else:
140
- coloring = self.structs.coloring(base_color={"color": dim_color, "index": dim_color_index})
142
+ coloring = self.structs.dim_coloring(base_color={"color": dim_color, "index": dim_color_index})
141
143
 
142
144
  nx_library_dimension_def = self.structs.nx_library_dimension_def(grouping="N", field_definitions=[dim_def],
143
145
  field_labels=[dim_title],
@@ -213,7 +215,8 @@ class QixEngine:
213
215
 
214
216
 
215
217
  def create_master_measure(self, app_handle: int, mes_title: str, mes_def: str, mes_label: str = "",
216
- mes_desc: str = "", mes_tags: list = None):
218
+ mes_desc: str = "", mes_tags: list = None, mes_color: str = None,
219
+ mes_color_index: int = -1, gradient: dict = None):
217
220
  """
218
221
  Creates a master measure.
219
222
 
@@ -224,6 +227,8 @@ class QixEngine:
224
227
  mes_label (str, optional): The label of the measure.
225
228
  mes_desc (str, optional): The description of the measure.
226
229
  mes_tags (list, optional): The tags of the measure.
230
+ mes_color (str, optional): The color of the measure.
231
+ mes_color_index (int, optional): The index of the color of the measure.
227
232
 
228
233
  Returns:
229
234
  dict: The handle and Id of the measure.
@@ -233,8 +238,17 @@ class QixEngine:
233
238
 
234
239
  # Define of the measure properties
235
240
  nx_info = self.structs.nx_info(obj_type="measure")
241
+
242
+ if mes_color is None:
243
+ coloring = self.structs.mes_coloring()
244
+ else:
245
+ coloring = self.structs.mes_coloring(base_color={"color": mes_color, "index": mes_color_index})
246
+
247
+ if gradient is not None:
248
+ coloring.update({"gradient": gradient})
249
+
236
250
  nx_library_measure_def = self.structs.nx_library_measure_def(label=mes_title, mes_def=mes_def,
237
- label_expression=mes_label)
251
+ label_expression=mes_label, coloring=coloring)
238
252
  gen_mes_props = self.structs.generic_measure_properties(nx_info=nx_info,
239
253
  nx_library_measure_def=nx_library_measure_def,
240
254
  title=mes_title, description=mes_desc, tags=mes_tags)
@@ -439,6 +453,203 @@ class QixEngine:
439
453
  return chart
440
454
 
441
455
 
456
+ def create_snapshot(self, app_handle: int, object_id: str, snapshot_title: str = "", snapshot_description: str = "",
457
+ object_width: float = 1280, object_height: float = 720, bounding_client_width: float = 1280,
458
+ bounding_client_height: float = 720, rtl: bool = False, parent_width: float = 1280, parent_height: float = 720,
459
+ content_width: float = 1280, content_height: float = 720, chart_data_scroll_offset_start: int = 0,
460
+ chart_data_scroll_offset_end: int = 53, chart_data_legend_scroll_offset: int = 0, chart_data_zoom_min = 0,
461
+ chart_data_zoom_max = 0):
462
+ """
463
+ Creates a snapshot object.
464
+
465
+ Parameters:
466
+ app_handle (int): The handle of the app.
467
+ object_id (str): The id of the object.
468
+ snapshot_title (str): The title of the snapshot.
469
+ snapshot_description (str): The description of the snapshot.
470
+ object_width (float): The width of the snapshot object.
471
+ object_height (float): The height of the snapshot object.
472
+ bounding_client_width (float): The width of the bounding client.
473
+ bounding_client_height (float): The height of the bounding client.
474
+ rtl (bool): Controls the rendering of content with right-to-left (RTL) language support.
475
+ parent_width (float): The width of the parent object.
476
+ parent_height (float): The height of the parent object.
477
+ content_width (float): The width of the content object.
478
+ content_height (float): The height of the content object.
479
+ chart_data_scroll_offset_start (int): Scroll offset start.
480
+ chart_data_scroll_offset_end (int): Scroll offset end.
481
+ chart_data_legend_scroll_offset (int): Legend scroll offset.
482
+ chart_data_zoom_min: Minimum chart data zoom.
483
+ chart_data_zoom_max: Maximum chart data zoom.
484
+
485
+ Returns:
486
+ dict: The handle and Id of the created snapshot.
487
+ """
488
+ # Get chart object
489
+ chart_obj = self.eaa.get_object(app_handle=app_handle, object_id=object_id)
490
+ chart_obj_handle = self.get_handle(chart_obj)
491
+
492
+ # Get sheet object
493
+ sheet_obj = self.get_object_sheet(app_handle=app_handle, obj_id=object_id)
494
+ sheet_id = self.get_id(sheet_obj)
495
+
496
+ # Get the visualization type
497
+ chart_obj_layout = self.egoa.get_layout(handle=chart_obj_handle)
498
+ visualization = chart_obj_layout["visualization"]
499
+
500
+ # Attribut "qInfo" changed
501
+ chart_obj_layout["qInfo"] = {"qType": "snapshot"}
502
+
503
+ # Attribut "qMetaDef" added
504
+ chart_obj_layout["qMetaDef"] = {"title": snapshot_title, "description": snapshot_description}
505
+
506
+ # Attribut "creationDate" added
507
+ chart_obj_layout["creationDate"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
508
+
509
+ # Attribut "permissions" added
510
+ chart_obj_layout["permissions"] = {"update": True, "publish": False, "export": False, "exportData": True,
511
+ "changeOwner": False, "remove": True}
512
+
513
+ # Attribut "visualizationType" added
514
+ chart_obj_layout["visualizationType"] = visualization
515
+
516
+ # Attribut "sourceObjectId" added
517
+ chart_obj_layout["sourceObjectId"] = object_id
518
+
519
+ # Attribut "sheetId" added
520
+ chart_obj_layout["sheetId"] = sheet_id
521
+
522
+ # Attribut "timestamp" added
523
+ chart_obj_layout["timestamp"] = int(time.time() * 1000)
524
+
525
+ # Attribut "isClone" added
526
+ chart_obj_layout["isClone"] = False
527
+
528
+ # Attribut "supportExport" added
529
+ chart_obj_layout["supportExport"] = True
530
+
531
+ # Attribut "qIncludeVariables" added
532
+ chart_obj_layout["qIncludeVariables"] = True
533
+
534
+ # Build the special snapshot parameters for the different chart types.
535
+ if visualization in ["sn-table", "pivot-table"]:
536
+ # Attribut "snapshotData" added
537
+ chart_obj_layout["snapshotData"] = {
538
+ "object": {
539
+ "size": {
540
+ "w": object_width,
541
+ "h": object_height,
542
+ "boundingClientWidth": bounding_client_width,
543
+ "boundingClientHeight": bounding_client_height
544
+ }
545
+ },
546
+ "rtl": rtl,
547
+ "parent": {
548
+ "h": parent_height,
549
+ "w": parent_width
550
+ }
551
+ }
552
+
553
+ elif visualization in ["combochart", "barchart"]:
554
+ # Attribut "snapshotData" added
555
+ chart_obj_layout["snapshotData"] = {
556
+ "object": {
557
+ "size": {
558
+ "w": object_width,
559
+ "h": object_height,
560
+ "boundingClientWidth": bounding_client_width,
561
+ "boundingClientHeight": bounding_client_height
562
+ }
563
+ },
564
+ "rtl": rtl,
565
+ "content": {
566
+ "size": {
567
+ "w": content_width,
568
+ "h": content_height
569
+ },
570
+ "chartData": {
571
+ "scrollOffset": {
572
+ "start": chart_data_scroll_offset_start,
573
+ "end": chart_data_scroll_offset_end
574
+ },
575
+ "legendScrollOffset": chart_data_legend_scroll_offset
576
+ }
577
+ },
578
+ "parent": {
579
+ "h": parent_height,
580
+ "w": parent_width
581
+ }
582
+ }
583
+
584
+ elif visualization in ["linechart"]:
585
+ # Attribut "snapshotData" added
586
+ chart_obj_layout["snapshotData"] = {
587
+ "object": {
588
+ "size": {
589
+ "w": object_width,
590
+ "h": object_height,
591
+ "boundingClientWidth": bounding_client_width,
592
+ "boundingClientHeight": bounding_client_height
593
+ }
594
+ },
595
+ "rtl": rtl,
596
+ "content": {
597
+ "size": {
598
+ "w": content_width,
599
+ "h": content_height
600
+ },
601
+ "chartData": {
602
+ "zoom": {
603
+ "min": chart_data_zoom_min,
604
+ "max": chart_data_zoom_max
605
+ }
606
+ }
607
+ },
608
+ "parent": {
609
+ "h": parent_height,
610
+ "w": parent_width
611
+ }
612
+ }
613
+
614
+ else:
615
+ print("Chart type not supported.")
616
+
617
+ # Create snapshot
618
+ snapshot = self.eaa.create_bookmark(doc_handle=app_handle, prop=chart_obj_layout)
619
+ snapshot.update({"visualization": visualization})
620
+
621
+ return snapshot
622
+
623
+
624
+ def embed_snapshot(self, app_handle: int, snapshot_id: str, slide_id: str):
625
+ """
626
+ Embeds a created snapshot object on a slide.
627
+
628
+ Parameters:
629
+ app_handle (int): The handle of the app.
630
+ snapshot_id (str): The id of the snapshot.
631
+ slide_id (str): The id of the slide to embed.
632
+ """
633
+ # Get the slide, where the snapshot should be embeded.
634
+ slide = self.eaa.get_object(app_handle=app_handle, object_id=slide_id)
635
+ slide_handle = self.get_handle(slide)
636
+
637
+ # Get the visualization type of the snapshot
638
+ snapshot = self.eaa.get_bookmark(app_handle=app_handle, bookmark_id=snapshot_id)
639
+ snapshot_handle = self.get_handle(snapshot)
640
+ snapshot_layout = self.egoa.get_layout(handle=snapshot_handle)
641
+ visualization_type = snapshot_layout["visualizationType"]
642
+
643
+ # create the snapshot
644
+ slideitem_snapshot_properties = self.structs.slideitem_snapshot_properties(snapshot_id=snapshot_id,
645
+ visualization_type=visualization_type)
646
+ slideitem_snapshot = self.egoa.create_child(handle=slide_handle, prop=slideitem_snapshot_properties)
647
+ slideitem_snapshot_handle = self.get_handle(slideitem_snapshot)
648
+
649
+ slideitem_snapshot_embeded = self.egoa.embed_snapshot_object(handle=slideitem_snapshot_handle,
650
+ snapshot_id=snapshot_id)
651
+
652
+
442
653
  def get_app_lineage_info(self, app_handle):
443
654
  """
444
655
  Gets the lineage information of the app. The lineage information includes the LOAD and STORE statements from
@@ -488,19 +699,60 @@ class QixEngine:
488
699
 
489
700
  Parameters:
490
701
  obj : dict
491
- The object containing the handle.
702
+ The object containing the id.
492
703
 
493
704
  Returns:
494
- int: The handle value.
705
+ int: The id value.
495
706
 
496
707
  Raises:
497
- ValueError: If the handle value is invalid.
708
+ ValueError: If the id value is invalid.
498
709
  """
499
710
  try:
500
711
  return obj["qGenericId"]
501
712
  except ValueError:
502
713
  return "Bad id value in " + obj
503
714
 
715
+ @staticmethod
716
+ def get_type(obj):
717
+ """
718
+ Retrieves the type from a given object.
719
+
720
+ Parameters:
721
+ obj : dict
722
+ The object containing the type.
723
+
724
+ Returns:
725
+ int: The type value.
726
+
727
+ Raises:
728
+ ValueError: If the type value is invalid.
729
+ """
730
+ try:
731
+ return obj["qGenericType"]
732
+ except ValueError:
733
+ return "Bad type value in " + obj
734
+
735
+
736
+ def get_object_sheet(self, app_handle: int, obj_id: str):
737
+ """
738
+ Retrieves the sheet from a given chart object.
739
+
740
+ Parameters:
741
+ app_handle (int): The handle of the app.
742
+ obj_id (str): The ID of the object.
743
+
744
+ Returns:
745
+ dict: The sheet object with handle and id.
746
+ """
747
+ parent_obj = self.eaa.get_object(app_handle=app_handle, object_id=obj_id)
748
+ while self.get_type(parent_obj) != "sheet":
749
+ obj = parent_obj
750
+ obj_handle = self.get_handle(obj)
751
+ parent_obj = self.egoa.get_parent(handle=obj_handle)
752
+
753
+ return parent_obj
754
+
755
+
504
756
  def get_chart_data(self, app_handle, obj_id):
505
757
  """
506
758
  Retrieves the data from a given chart object.
@@ -526,14 +778,25 @@ class QixEngine:
526
778
  # Determine the number of the columns and the rows the table has and splits in certain circumstances the table
527
779
  # calls
528
780
  no_of_columns = obj_layout['qHyperCube']['qSize']['qcx']
781
+
782
+ if no_of_columns == 0:
783
+ return 'The chart either contains no columns or has a calculation condition!'
784
+
529
785
  width = no_of_columns
530
786
  no_of_rows = obj_layout['qHyperCube']['qSize']['qcy']
531
787
  height = int(math.floor(10000 / no_of_columns))
532
788
 
533
789
  # Extract the dimension and measure titles and concat them to column names.
534
- dimension_titles = [dim['qFallbackTitle'] for dim in obj_layout['qHyperCube']['qDimensionInfo']]
535
- measure_titles = [measure['qFallbackTitle'] for measure in obj_layout['qHyperCube']['qMeasureInfo']]
536
- column_names = dimension_titles + measure_titles
790
+ dimension_info = obj_layout['qHyperCube'].get('qDimensionInfo', [])
791
+ measure_info = obj_layout['qHyperCube'].get('qMeasureInfo', [])
792
+ column_info = dimension_info + measure_info
793
+
794
+ # Build the column mapping using qEffectiveInterColumnSortOrder
795
+ sort_order = sorted(obj_layout['qHyperCube']['qEffectiveInterColumnSortOrder'])
796
+ sort_order_positive = [x for x in sort_order if x >= 0]
797
+ column_names = []
798
+ for i in sort_order_positive:
799
+ column_names.append(column_info[i]["qFallbackTitle"])
537
800
 
538
801
  # if the type of the charts has a straight data structure
539
802
  if (obj_layout['qInfo']['qType'] in ['table', 'sn-table', 'piechart', 'scatterplot', 'combochart', 'barchart']
@@ -546,7 +809,7 @@ class QixEngine:
546
809
  # Retrieves the hypercube data in a loop (because of limitation from 10.000 cells per call)
547
810
  while no_of_rows > page * height:
548
811
  nx_page = self.structs.nx_page(left=0, top=page * height, width=width, height=height)
549
- hc_data = self.egoa.get_hypercube_data(obj_handle, '/qHyperCubeDef', nx_page)[
812
+ hc_data = self.egoa.get_hypercube_data(handle=obj_handle, path='/qHyperCubeDef', pages=[nx_page])[
550
813
  'qDataPages'][0]['qMatrix']
551
814
  data_values.extend(hc_data)
552
815
  page += 1
@@ -563,9 +826,10 @@ class QixEngine:
563
826
 
564
827
  # Supporting function to traverse all subnodes to get all dimensions
565
828
  def get_all_dimensions(node):
566
- dimensions = [node['qText']]
567
- # if 'qSubNodes' in node and node['qSubNodes']:
568
- if node['qSubNodes']:
829
+ label = node.get('qText', '') # Leerer String, falls nicht vorhanden
830
+ dimensions = [label]
831
+
832
+ if 'qSubNodes' in node and node['qSubNodes']:
569
833
  sub_dimensions = []
570
834
  for sub_node in node['qSubNodes']:
571
835
  sub_dimensions.extend([dimensions + d for d in get_all_dimensions(sub_node)])
@@ -573,13 +837,25 @@ class QixEngine:
573
837
  else:
574
838
  return [dimensions]
575
839
 
576
- # Gets the column headers for the pivot table
840
+ # Supporting function to get all column headers for the pivot table
841
+ def get_column_paths(node):
842
+ label = node.get('qText', '')
843
+ current_path = [label]
844
+
845
+ if 'qSubNodes' in node and node['qSubNodes']:
846
+ paths = []
847
+ for sub in node['qSubNodes']:
848
+ for path in get_column_paths(sub):
849
+ paths.append(current_path + path)
850
+ return paths
851
+ else:
852
+ return [current_path]
853
+
577
854
  col_headers = []
578
855
  nx_page_top = self.structs.nx_page(left=0, top=0, width=width, height=1)
579
- hc_top = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page_top)[
580
- 'qDataPages'][0]['qTop']
856
+ hc_top = self.egoa.get_hypercube_pivot_data(handle=obj_handle, path='/qHyperCubeDef', pages=[nx_page_top])['qDataPages'][0]['qTop']
581
857
  for top_node in hc_top:
582
- col_headers.extend(get_all_dimensions(top_node))
858
+ col_headers.extend(get_column_paths(top_node))
583
859
 
584
860
  # Paging variables
585
861
  page = 0
@@ -591,13 +867,13 @@ class QixEngine:
591
867
  nx_page = self.structs.nx_page(left=0, top=page * height, width=width, height=height)
592
868
 
593
869
  # Retrieves the row headers for the pivot table
594
- hc_left = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page)[
870
+ hc_left = self.egoa.get_hypercube_pivot_data(handle=obj_handle, path='/qHyperCubeDef', pages=[nx_page])[
595
871
  'qDataPages'][0]['qLeft']
596
872
  for left_node in hc_left:
597
873
  row_headers.extend(get_all_dimensions(left_node))
598
874
 
599
875
  # Retrieves the data for the pivot table
600
- hc_data = self.egoa.get_hypercube_pivot_data(obj_handle, '/qHyperCubeDef', nx_page)[
876
+ hc_data = self.egoa.get_hypercube_pivot_data(handle=obj_handle, path='/qHyperCubeDef', pages=[nx_page])[
601
877
  'qDataPages'][0]['qData']
602
878
  for row in hc_data:
603
879
  data_values.append([cell['qText'] for cell in row])
@@ -610,12 +886,15 @@ class QixEngine:
610
886
 
611
887
  # Creates the Dataframe
612
888
  df = pd.DataFrame(data_values, index=row_index, columns=col_index)
889
+ index_levels = df.index.nlevels
890
+ df.index.names = column_names[:index_levels]
891
+ df = df.reset_index()
613
892
 
614
893
  # if the type of the charts has a stacked data structure
615
894
  elif obj_layout['qInfo']['qType'] in ['barchart'] and obj_layout['qHyperCube']['qStackedDataPages'] != []:
616
895
  max_no_cells = no_of_columns * no_of_rows
617
896
  nx_page = self.structs.nx_page(left=0, top=0, width=no_of_columns, height=no_of_rows)
618
- hc_data = self.egoa.get_hypercube_stack_data(obj_handle, '/qHyperCubeDef', nx_page, max_no_cells)[
897
+ hc_data = self.egoa.get_hypercube_stack_data(handle=obj_handle, path='/qHyperCubeDef', pages=[nx_page], max_no_cells=max_no_cells)[
619
898
  'qDataPages'][0]['qData'][0]['qSubNodes']
620
899
 
621
900
  # Transform the nested structure into a flat DataFrame
@@ -701,7 +980,7 @@ class QixEngine:
701
980
  # Retrieves the hypercube data in a loop (because of limitation from 10.000 cells per call)
702
981
  while no_of_rows > page * height:
703
982
  nx_page = self.structs.nx_page(left=0, top=page * height, width=width, height=height)
704
- hc_data = self.egoa.get_hypercube_data(hc_obj_handle, '/qHyperCubeDef', nx_page)['qDataPages'][0]['qMatrix']
983
+ hc_data = self.egoa.get_hypercube_data(handle=hc_obj_handle, path='/qHyperCubeDef', pages=[nx_page])['qDataPages'][0]['qMatrix']
705
984
  data_values.extend(hc_data)
706
985
  page += 1
707
986
 
qe_api_client/structs.py CHANGED
@@ -277,14 +277,18 @@ def nx_library_dimension_def(grouping: str = "N", field_definitions: list = None
277
277
 
278
278
 
279
279
  def nx_library_measure_def(label: str, mes_def: str, grouping: str = "N", expressions: list = None,
280
- active_expression: int = 0, label_expression:str = "", num_format: dict = None):
280
+ active_expression: int = 0, label_expression:str = "", num_format: dict = None,
281
+ coloring: dict = None):
282
+ if coloring is None:
283
+ coloring = {}
281
284
  if num_format is None:
282
285
  num_format = {}
283
286
  if expressions is None:
284
287
  expressions = []
285
288
  return {
286
289
  "qLabel": label, "qDef": mes_def,"qGrouping": grouping, "qExpressions": expressions,
287
- "qActiveExpression": active_expression, "qLabelExpression": label_expression, "qNumFormat": num_format
290
+ "qActiveExpression": active_expression, "qLabelExpression": label_expression, "qNumFormat": num_format,
291
+ "coloring": coloring
288
292
  }
289
293
 
290
294
 
@@ -509,7 +513,7 @@ def color_map(colors: list = None, nul: dict = None, oth: dict = None, pal: str
509
513
  }
510
514
 
511
515
 
512
- def coloring(change_hash: str = None, color_map_ref: str = "", has_value_colors: bool = False, base_color: dict = None):
516
+ def dim_coloring(change_hash: str = None, color_map_ref: str = "", has_value_colors: bool = False, base_color: dict = None):
513
517
  if base_color is None:
514
518
  base_color = {"color": "none", "index": 0}
515
519
  return {
@@ -520,6 +524,15 @@ def coloring(change_hash: str = None, color_map_ref: str = "", has_value_colors:
520
524
  }
521
525
 
522
526
 
527
+ def mes_coloring(base_color: dict = None, _gradient: dict = None):
528
+ coloring = {}
529
+ if base_color is not None:
530
+ coloring.update({"baseColor": base_color})
531
+ if _gradient is not None:
532
+ coloring.update({"gradient": _gradient})
533
+ return coloring
534
+
535
+
523
536
  def color_map_properties(dim_id: str, prop_def:dict = None, extends_id: str = "", state_name: str = "",
524
537
  _color_map: dict = None):
525
538
 
@@ -539,3 +552,115 @@ def value_color(value: str, color: str, index: int = -1):
539
552
  "value": value,
540
553
  "baseColor": {"color": color, "index": index}
541
554
  }
555
+
556
+
557
+ def color(_color: str, index: int = -1):
558
+ return {"color": _color, "index": index}
559
+
560
+
561
+ def gradient(colors: list = None, break_types: list = None, limits: list = None, limit_type: str = "percent"):
562
+ if colors is None:
563
+ colors = [color(_color="#332288"), color(_color="#117733")]
564
+ if break_types is None:
565
+ break_types = [False]
566
+ if limits is None:
567
+ limits = [0.5]
568
+ return {"colors": colors, "breakTypes": break_types, "limits": limits, "limitType": limit_type}
569
+
570
+
571
+ def static_content_url_def(url: str = None):
572
+ if url is None:
573
+ return {}
574
+ else:
575
+ return {"qUrl": url}
576
+
577
+
578
+ def story_properties(title: str = "", description: str = "", extends_id: str = "", state_name: str = "", rank: int = 0,
579
+ thumbnail_url: str = None):
580
+
581
+ info = nx_info(obj_type="story")
582
+ prop_def = {"title": title, "description": description}
583
+ if thumbnail_url is None:
584
+ thumbnail = {"qStaticContentUrlDef": static_content_url_def()}
585
+ else:
586
+ thumbnail = {"qStaticContentUrlDef": static_content_url_def(url=thumbnail_url)}
587
+ child_list_def = {"qData": {"title": "/title", "rank": "/rank"}}
588
+
589
+ return {
590
+ "qInfo": info, "qExtendsId": extends_id, "qMetaDef": prop_def, "qStateName": state_name, "rank": rank,
591
+ "thumbnail": thumbnail, "qChildListDef": child_list_def
592
+ }
593
+
594
+
595
+ def slide_properties(extends_id: str = "", prop_def: dict = None, state_name: str = ""):
596
+
597
+ if prop_def is None:
598
+ prop_def = {}
599
+ info = nx_info(obj_type="slide")
600
+ child_list_def = {"qData": {"title": "/title", "sheetId": "/sheetId", "ratio": "/ratio", "position": "/position",
601
+ "dataPath": "/dataPath", "srcPath": "/srcPath", "visualization": "/visualization",
602
+ "visualizationType": "/visualizationType", "style": "/style"}}
603
+
604
+ return {
605
+ "qInfo": info, "qExtendsId": extends_id, "qMetaDef": prop_def, "qStateName": state_name, "qChildListDef": child_list_def
606
+ }
607
+
608
+
609
+ def slideitem_text_properties(ratio: bool = True, position_top: str = "3.69985%", position_left: str = "31.25000%",
610
+ position_width: str = "39.57903%", position_height: str = "11.28086%",
611
+ position_z_index: int = 1, position_right: str = "auto",
612
+ visualization_type: str = "", style_color: str = "#6E6E6E",
613
+ style_text: str = ""):
614
+
615
+ position = {"top": position_top, "left": position_left, "width": position_width, "height": position_height,
616
+ "z-index": position_z_index, "right": position_right}
617
+ info = nx_info(obj_type="slideitem")
618
+
619
+ return {
620
+ "qInfo": info, "qExtendsId": "", "qMetaDef": {}, "qStateName": "",
621
+ "qEmbeddedSnapshotDef": {}, "title": "", "sheetId": "", "ratio": ratio,
622
+ "position": position, "visualization": "text", "visualizationType": visualization_type,
623
+ "style": {"color": style_color, "text": style_text}
624
+ }
625
+
626
+
627
+ def slideitem_shape_properties(ratio: bool = True, position_top: str = "3.69985%", position_left: str = "31.25000%",
628
+ position_width: str = "39.57903%", position_height: str = "11.28086%",
629
+ position_z_index: int = 1, position_right: str = "auto",
630
+ visualization_type: str = "", style_color: str = "#000000"):
631
+
632
+ position = {"top": position_top, "left": position_left, "width": position_width, "height": position_height,
633
+ "z-index": position_z_index, "right": position_right}
634
+ info = nx_info(obj_type="slideitem")
635
+
636
+ return {
637
+ "qInfo": info, "qExtendsId": "", "qMetaDef": {}, "qStateName": "",
638
+ "qEmbeddedSnapshotDef": {}, "title": "", "sheetId": "", "ratio": ratio,
639
+ "position": position, "dataPath": "../resources/img/storytelling/shapes/" + visualization_type + ".svg",
640
+ "visualization": "shape", "visualizationType": visualization_type,
641
+ "style": {"color": style_color, "colorPaletteIndex": -1}
642
+ }
643
+
644
+
645
+ def slideitem_snapshot_properties(snapshot_id: str, visualization_type: str, ratio: bool = True,
646
+ position_top: str = "14.81481%", position_left: str = "2.08333%",
647
+ position_width: str = "95.83334%", position_height: str = "81.4814875%",
648
+ position_z_index: int = 1):
649
+
650
+ position = {"top": position_top, "left": position_left, "width": position_width, "height": position_height,
651
+ "z-index": position_z_index}
652
+ info = nx_info(obj_type="slideitem")
653
+
654
+ return {
655
+ "qInfo": info, "qExtendsId": "", "qMetaDef": {}, "qStateName": "", "qEmbeddedSnapshotDef": {}, "title": "",
656
+ "sheetId": "", "ratio": ratio, "position": position, "visualization": "snapshot",
657
+ "visualizationType": visualization_type, "style": {"id": snapshot_id}
658
+ }
659
+
660
+
661
+ def nx_get_bookmark_options(types: list, data: dict = None):
662
+ if data is None:
663
+ data = {}
664
+ return {
665
+ "qTypes": types, "qData": data
666
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qe-api-client
3
- Version: 2.7.0
3
+ Version: 2.9.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
@@ -11,8 +11,10 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.6
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
- Requires-Dist: websocket-client>=0.47.0
14
+ Requires-Dist: websocket-client>=1.0.0
15
15
  Requires-Dist: pandas>=2.2.0
16
+ Requires-Dist: numpy>=2.0.0
17
+ Requires-Dist: uuid>=1.0
16
18
  Dynamic: author
17
19
  Dynamic: author-email
18
20
  Dynamic: classifier
@@ -1,17 +1,17 @@
1
1
  qe_api_client/__init__.py,sha256=bypB4CIjpHtf5Pu_NwtJajC69zqQD7qB9jo8cCX0B54,23
2
- qe_api_client/engine.py,sha256=4pll0uoFPph5WgRufpP4OsYlypWKQhMfJQAaaWPWWUU,56738
2
+ qe_api_client/engine.py,sha256=ErLb7KXpnH6UC7jNvZWdkW2_aeW7GCmvH8IzTcCDj0Q,68103
3
3
  qe_api_client/engine_communicator.py,sha256=q6x7ix2Ev8yGmTTm7cf1vHcidOihKM0HjDXeJ-dZYjk,1133
4
- qe_api_client/structs.py,sha256=Gh7aGNLN58kYeBY-6Nb7yagQpHnd1f-IlfaE8aEF9ns,22621
4
+ qe_api_client/structs.py,sha256=iZRy54jDyAk8ZD4rtrKxefnBcXcB1aRE0oxpOxoYcDM,28078
5
5
  qe_api_client/api_classes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- qe_api_client/api_classes/engine_app_api.py,sha256=qobuSdLV5I-DOXto0Qi5ckMG3q5Dll0Lto3TyjZSbso,37493
6
+ qe_api_client/api_classes/engine_app_api.py,sha256=umVY3jtNyk4am_9XH1yAbng2BRGAF-sg_JSssbXcF5M,39962
7
7
  qe_api_client/api_classes/engine_field_api.py,sha256=zCLIR7rmxqwIrJYK_-uHVEhMvBcEP2qofuX8ZPygqCA,5479
8
8
  qe_api_client/api_classes/engine_generic_dimension_api.py,sha256=oSZoRT-j4hsCVnUm1OSg7XZPWXhUzFLY_53kM0KwPHs,2122
9
9
  qe_api_client/api_classes/engine_generic_measure_api.py,sha256=uj4i_ykX9F9Dtk78fOidMBhzSP8vEucEfrB6MrLwgPI,1340
10
- qe_api_client/api_classes/engine_generic_object_api.py,sha256=iasoNYSSsBTr_S3ExoorfCip9fhy7sCQ_J-ru2JYal8,10130
10
+ qe_api_client/api_classes/engine_generic_object_api.py,sha256=_69Pf-Tp5vRB_LX76uQr8x03DstS_OOBVVw8h-9nwlA,12345
11
11
  qe_api_client/api_classes/engine_generic_variable_api.py,sha256=sWXZpE-GLfcMijmfORnDNrJ6lmXX3x5TRHlkEu_i0BQ,2027
12
12
  qe_api_client/api_classes/engine_global_api.py,sha256=G6QQHI36WOo7W25zg4Uz__gMSLC2ptNTvbBdElPzgZI,27535
13
- qe_api_client-2.7.0.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
14
- qe_api_client-2.7.0.dist-info/METADATA,sha256=5-VkSmGbK-92mrMlEqFgozK8FtbUfmpW1o5_zQZrLlA,2385
15
- qe_api_client-2.7.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
16
- qe_api_client-2.7.0.dist-info/top_level.txt,sha256=m_43YagP8UtZgJHmZEfu0vlBNwt36M01-Qby2jByMnk,14
17
- qe_api_client-2.7.0.dist-info/RECORD,,
13
+ qe_api_client-2.9.0.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
14
+ qe_api_client-2.9.0.dist-info/METADATA,sha256=g4aMmiMpkgRMvNe60MHsywPsl1dT8FLmmvH-VorJ9Wk,2437
15
+ qe_api_client-2.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ qe_api_client-2.9.0.dist-info/top_level.txt,sha256=m_43YagP8UtZgJHmZEfu0vlBNwt36M01-Qby2jByMnk,14
17
+ qe_api_client-2.9.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5