问题描述
我们的组织使用 Qlik Sense Enterprise,我们正在寻求自动化用于可视化的数据下载过程(格式可以是 excel 或 csv),而不是导致以下结果的手动过程(显示了裁剪的屏幕截图):
>对于我们的用例,假设只有一个应用,里面有一张工作表,并且该工作表有 3 个可视化效果。
我编写了一个 python 脚本,该脚本当前已连接到本地主机,并且我能够使用 Qlik Engine JSON API 检索当前 3 个图表的 app_id、sheet_id 和 id。代码的工作方式如下:
- 获取 doc_list (app_list)
- 选择应用,因为我们只有一个应用,所以我们选择索引为 0
- 创建一个会话对象(我在 Dev Hub 上看到 Qlik Engine 表现出这种行为,这就是我执行此步骤的原因)
- 获取应用的布局
- 选择工作表,因为我们有一张工作表,所以我们选择索引为 0
- 遍历可视化并打印其名称
我提供了以下代码供您参考,并且可以访问 pastebin 链接 here
import requests
import websocket,ssl
import json,csv
import os,time
from pprint import pprint
#Connecting to the server. The lines below will be replaced by the certificates and headers for enterprise usage later
ws = websocket.WebSocket()
ws.connect("ws://localhost:4848/app/")
#For getting the doc (app) list
doclist_req = {
"handle": -1,"method": "GetDocList","params": [],"outKey": -1,"id": 1
}
ws.send(json.dumps(doclist_req))
result = ws.recv()
ws.send(json.dumps(doclist_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
#For opening the doc (app)
app_req = {
"jsonrpc": "2.0","method": "OpenDoc","handle": -1,"params": [
#Can iterate if multiple apps are there
#Since only one app was present we used the index 0
result_json['result']['qDocList'][0]['qDocId']
],"id": 2
}
#The first call seems to be for establishing the connection and second to actually send the request body
ws.send(json.dumps(app_req))
result = ws.recv()
ws.send(json.dumps(app_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
app_req_handle = result_json['result']['qReturn']['qHandle']
#For creating the session object necessary for fetching dimensions,fields,etc.
session_req = {
"jsonrpc": "2.0","method": "CreateSessionObject","handle": app_req_handle,"params": [
{
"qInfo": {
"qType": "SheetList"
},"qAppObjectListDef": {
"qType": "sheet","qData": {
"title": "/qMetaDef/title","description": "/qMetaDef/description","thumbnail": "/thumbnail","cells": "/cells","rank": "/rank","columns": "/columns","rows": "/rows"
}
}
}
],"id": 3
}
ws.send(json.dumps(session_req))
result = ws.recv()
ws.send(json.dumps(session_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
session_req_handle = result_json['result']['qReturn']['qHandle']
#For fetching the layout of the sheets
layout_req = {
"jsonrpc": "2.0","method": "GetLayout","handle": session_req_handle,"id": 4
}
ws.send(json.dumps(layout_req))
result = ws.recv()
ws.send(json.dumps(layout_req))
result = ws.recv()
result_json = json.loads(result)
print(result_json)
print()
#Since only one sheet was present we used the index 0
list_of_charts = result_json['result']['qLayout']['qAppObjectList']['qItems'][0]['qData']['cells']
for chart in list_of_charts:
print(chart['name'])
print()
ws.close()
我已经探索了 Qlik 社区以及 SO 上的很多页面,ExportData 似乎是要走的路,但我无法为其编写正确的 JSON 请求。作为参考,我在终端中执行 python 脚本时得到的输出如下。我对此很陌生,如果您能提供任何帮助,我将非常感谢。
{'jsonrpc': '2.0','id': 1,'result': {'qDocList': [{'qDocName': 'Test_2.qvf','qConnectedUsers': 0,'qFileTime': 44188.190983796296,'qFileSize': 851968,'qDocId': 'C:\Users\mohdm\Documents\Qlik\Sense\Apps\Test_2.qvf','qMeta': {'hassectionaccess': False,'encrypted': False},'qLastReloadTime': '2020-12-22T19:12:22.245Z','qTitle': 'Test_2','qThumbnail': {}}]}}
{'jsonrpc': '2.0 ','id': 2,'result': {'qReturn': {'qType': 'Doc','qHandle': 1,'qGenericId': 'C:\Users\mohdm\Documents\Qlik\Sense\ Apps\Test_2.qvf'}},'change': 1}
{'jsonrpc': '2.0','id': 3,'result': {'qReturn' :{'qType':'GenericObject','qHandle':2,'qGenericType':'SheetList','qGenericId':'4b344780-a350-48db-8b65-27bb5a2c62b2'}},'更改':{{3} }}
{'jsonrpc': '2.0','id': 4,'result': {'qLayout': {'qInfo': {'qId': '4b344780-a350-48db -8b65-27bb5a2c62b2','qType':'SheetList'},'qMeta':{'privileges':['read','update','delete','exportdata']},'qSelectionInfo':{}, 'qAppObjectList ': {'qItems': [{'qInfo': {'qId': '8a0f6a01-ef89-4d65-821f-371c26208dcf','qType': 'sheet'},'qMeta': {'privileges': [' read','update','delete','exportdata'],'title': 'Sheet_1','description': ''},'qData': {'rank': None,'thumbnail': {'qStaticContentUrl ': {}},'columns': 24,'rows': 12,'cells': [{'name': 'kfxNpV','type': 'auto-chart','col': 0,'row ': 0,'colspan': 15,'rowspan': 6,'bounds': {'y': 0,'x': 0,'width': 62.5,'height': 50}},{'name ':'qHzmARQ','type':'qlik-barplus-chart','col':0,'row':6,'colspan':21,'rowspan':6,'bounds':{'y' : 50,'width': 87.5,{'name': 'BXBQmw','col': 15,'colspan': 9,'x': 62.5,'width': 37.5,'height': 50}}],'title ':
'Sheet_1','description': ''}}]}}}}
kfxNpV
qHzmARQ
BXBQmw
解决方法
我不确定 Python,但我发现通常困难的部分是正确进行身份验证。像 JavaScript 那样的库通常会大大简化这一过程。就个人而言,我倾向于将 C# 用于此类小工具,其中 .NET SDK 提供了为您完成管道工作的运算符。这是使用这两个 nuget 包的示例:
- https://www.nuget.org/packages/QlikSense.NetSDK/
- https://www.nuget.org/packages/QlikSenseRestClient/
此代码下载应用第一张工作表上所有可视化的 xlsx 文件。
var location = Location.FromUri(uri);
location.AsNtlmUserViaProxy();
var restClient = new RestClient(uri);
restClient.AsNtlmUserViaProxy();
using (var app = location.App(new AppIdentifier {AppId = appId}))
{
var theSheet = app.GetSheets().First();
var objs = theSheet.GetChildInfos().Select(info => app.GetGenericObject(info.Id));
foreach (var o in objs)
{
var exportResult = o.ExportData(NxExportFileType.EXPORT_OOXML);
var data = restClient.GetBytes(exportResult.Url);
using (var writer = new BinaryWriter(new FileStream(o.Id + ".xlsx",FileMode.OpenOrCreate)))
{
writer.Write(data);
}
}
}