使用Beatbox在Pandas数据框中解析Salesforce报告

使用Beatbox在Pandas数据框中解析Salesforce报告,pandas,salesforce,beatbox,Pandas,Salesforce,Beatbox,有人尝试过使用Beatbox将SalesForce报告解析到Pandas数据框中吗?在这方面有几个例子,但没有一个提供了全面的解决方案,或者至少我认为没有 #!/usr/bin/env python3 import beatbox import pandas as pd sf = beatbox._tPartnerNS service = beatbox.Client() service.serverUrl = 'https://login.salesforce.com/services/

有人尝试过使用Beatbox将SalesForce报告解析到Pandas数据框中吗?在这方面有几个例子,但没有一个提供了全面的解决方案,或者至少我认为没有

#!/usr/bin/env python3

import beatbox
import pandas as pd


sf = beatbox._tPartnerNS
service = beatbox.Client()
service.serverUrl = 'https://login.salesforce.com/services/Soap/u/38.0'
service.login('my-username', 'my-password')

report_id = '00myreport4G3V'

query = "SELECT Name FROM Report where id = '{}'".format(report_id)
query_result = service.query(query)

这只是选择名称,但理想情况下,我希望将报告的内容加载到数据帧中。有什么帮助吗?

可以通过检索报告数据。 自2015年夏季(34.0版)起,Salesforce就开始使用此功能

由于RESTAPI,我用包编写了一个示例。(但是,不需要简单的salesforce就可以重写它,并且可以使用Beatbox的api会话,再编写至少10行代码,只安装软件包。)

通用代码

from collections import OrderedDict
from simple_salesforce import Salesforce
import pandas as pd
import json

class SfReportsApi(Salesforce):
    def __init__(self, *args, **kwargs):
        super(SfReportsApi, self).__init__(*args, **kwargs)

    def describe_report(self, report_id):
        return self._call_report(report_id, command='/describe')

    def to_pandas_dataframe(self, report_id, metadata=None):
        """SF report details exported to DataFrame, can be modified by metadata"""
        resp = self._call_report(report_id, metadata=metadata)
        if not resp['allData']:
            print("Detailed data have been truncated to the usual report limit (2000).")
        columns = []
        converters = []
        get_label = lambda x: x['label']
        sf_pandas_map = {
            'boolean': lambda x: x['value'],
            'currency': lambda x: x['value']['amount'],
            'date': lambda x: pd.Timestamp(x['value']),
            'datetime': lambda x: pd.Timestamp(x['value']),
            'double': lambda x: x['value'],
            'picklist': get_label,
            'string': get_label,
            'textarea': get_label,
        }
        for col in resp['reportExtendedMetadata']['detailColumnInfo'].values():
            columns.append(col['label'])
            converters.append(sf_pandas_map.get(col['dataType'], get_label))
        data = [[conv(cell) for conv, cell in zip(converters, row['dataCells'])]
                for sect_key, section in resp['factMap'].items()
                    if sect_key != 'T!T'
                        for row in section['rows']
                ]
        df = pd.DataFrame(data, columns=columns)
        return df

    def _call_report(self, report_id, metadata=None, command=None):
        url = '{}analytics/reports/{}{}'.format(self.base_url, report_id, command or '')
        data = json.dumps({'reportMetadata': metadata}) if metadata else None
        resp = self._call_salesforce('POST' if metadata else 'GET', url, data=data)
        return resp.json(object_pairs_hook=OrderedDict)
report_id = '00O24000004qtI4EAI'
# set Salesforce session_id some way (by login or reused from other app)
sf = SfReportsApi(username='me@example.com', password='password', security_token='token')
# sf = SfReportsApi(instance_url='https://na1.salesforce.com', session_id='')
# get report metadata if useful
metadata = sf.describe_report(report_id)['reportMetadata']
# modify them or write only the modified keys, e.g. change filters or remove subtotals etc.
metadata = {
    'orderBy': ['ACCOUNT.NAME'],
    'reportFilters': [{'value': 'W', 'column': 'ACCOUNT.NAME', 'operator': greaterOrEqual'}]
}
df = sf.to_pandas_dataframe(report_id, metadata)
用法示例

from collections import OrderedDict
from simple_salesforce import Salesforce
import pandas as pd
import json

class SfReportsApi(Salesforce):
    def __init__(self, *args, **kwargs):
        super(SfReportsApi, self).__init__(*args, **kwargs)

    def describe_report(self, report_id):
        return self._call_report(report_id, command='/describe')

    def to_pandas_dataframe(self, report_id, metadata=None):
        """SF report details exported to DataFrame, can be modified by metadata"""
        resp = self._call_report(report_id, metadata=metadata)
        if not resp['allData']:
            print("Detailed data have been truncated to the usual report limit (2000).")
        columns = []
        converters = []
        get_label = lambda x: x['label']
        sf_pandas_map = {
            'boolean': lambda x: x['value'],
            'currency': lambda x: x['value']['amount'],
            'date': lambda x: pd.Timestamp(x['value']),
            'datetime': lambda x: pd.Timestamp(x['value']),
            'double': lambda x: x['value'],
            'picklist': get_label,
            'string': get_label,
            'textarea': get_label,
        }
        for col in resp['reportExtendedMetadata']['detailColumnInfo'].values():
            columns.append(col['label'])
            converters.append(sf_pandas_map.get(col['dataType'], get_label))
        data = [[conv(cell) for conv, cell in zip(converters, row['dataCells'])]
                for sect_key, section in resp['factMap'].items()
                    if sect_key != 'T!T'
                        for row in section['rows']
                ]
        df = pd.DataFrame(data, columns=columns)
        return df

    def _call_report(self, report_id, metadata=None, command=None):
        url = '{}analytics/reports/{}{}'.format(self.base_url, report_id, command or '')
        data = json.dumps({'reportMetadata': metadata}) if metadata else None
        resp = self._call_salesforce('POST' if metadata else 'GET', url, data=data)
        return resp.json(object_pairs_hook=OrderedDict)
report_id = '00O24000004qtI4EAI'
# set Salesforce session_id some way (by login or reused from other app)
sf = SfReportsApi(username='me@example.com', password='password', security_token='token')
# sf = SfReportsApi(instance_url='https://na1.salesforce.com', session_id='')
# get report metadata if useful
metadata = sf.describe_report(report_id)['reportMetadata']
# modify them or write only the modified keys, e.g. change filters or remove subtotals etc.
metadata = {
    'orderBy': ['ACCOUNT.NAME'],
    'reportFilters': [{'value': 'W', 'column': 'ACCOUNT.NAME', 'operator': greaterOrEqual'}]
}
df = sf.to_pandas_dataframe(report_id, metadata)
可以动态添加列、筛选器、排序等(文档关于)。数据框架的方法
适用于包含详细信息的常规表格报告,也可以是一个总计,但不超过一级小计。可以从更复杂的报告中检索数据(请参阅docs about或a),但它没有实现,因为在运行之前通过元数据参数动态删除它们更容易


只能报告2000个详细数据行。可以使用带有筛选器的多个请求查看所有数据。

我不熟悉BeatBox,但使用simple salesforce提取csv并将其转换为DataFrame非常简单

#-*-coding:utf-8-*-
import pandas as pd
import numpy as np
from simple_salesforce import Salesforce 
import requests
###login to SF
sf = Salesforce(username='xxxxx', 
                password='xxxx', 
                security_token='', 
                organizationId='xxxxxx')
def readReport(reportid):
    with requests.session() as s:
        d = s.get("https://ap1.salesforce.com/{}?export=1&enc=UTF-8&xf=csv".format(reportid), 
                  headers=sf.headers, 
                  cookies={'sid': sf.session_id})
    import sys
    if sys.version_info[0] < 3:
        from StringIO import StringIO
    else:
        from io import StringIO
    return pd.read_csv(StringIO(d.text), sep=",")

df = readReport('your report id')
#-*-编码:utf-8-*-
作为pd进口熊猫
将numpy作为np导入
从simple_salesforce导入salesforce
导入请求
###登录到SF
sf=Salesforce(用户名='xxxxx',
密码='xxxx',
安全令牌=“”,
organizationId='xxxxxx')
def readReport(报告ID):
将requests.session()作为s:
d=s.get(“https://ap1.salesforce.com/{}?export=1&enc=UTF-8&xf=csv“。格式(reportid),
headers=sf.headers,
cookies={'sid':sf.session_id})
导入系统
如果系统版本信息[0]<3:
从StringIO导入StringIO
其他:
从io导入StringIO
返回pd.read_csv(StringIO(d.text),sep=“,”)
df=readReport('您的报告id')
将此代码添加到gist中。