如何使用Python/Django中的BonitaSoft REST API
我在网上找不到很多关于如何做这件事的信息,所以我决定发布我是如何做到的。请随时分享您的建议或经验 首先,在settings.py中,我设置了一些可以重用的变量。如果您在本地使用Bonita Studio,则这些是默认设置如何使用Python/Django中的BonitaSoft REST API,python,django,rest,Python,Django,Rest,我在网上找不到很多关于如何做这件事的信息,所以我决定发布我是如何做到的。请随时分享您的建议或经验 首先,在settings.py中,我设置了一些可以重用的变量。如果您在本地使用Bonita Studio,则这些是默认设置 BPM_HOST = 'http://localhost:9090/bonita-server-rest/' BPM_USERNAME = 'restuser' BPM_PASSWORD = 'restbpm' 在views.py中,我设置了一个函数,可以在需要调用时随时使用
BPM_HOST = 'http://localhost:9090/bonita-server-rest/'
BPM_USERNAME = 'restuser'
BPM_PASSWORD = 'restbpm'
在views.py中,我设置了一个函数,可以在需要调用时随时使用。它使用设置文件中的变量,并接受登录用户的参数、要调用的url和post_数据字典。它按照Bonitasoft期望的方式设置基本身份验证和内容类型头
from django.conf import settings
import urllib
import urllib2
import base64
def restcall(user,url,post_data={}):
#create bpm_request
bpm_request = urllib2.Request(settings.BPM_HOST + url)
#encode username and password and add to header
authKey = base64.b64encode(settings.BPM_USERNAME + ':' + settings.BPM_PASSWORD)
bpm_request.add_header("Authorization","Basic " + authKey)
#add content type to header
bpm_request.add_header("Content-Type","application/x-www-form-urlencoded")
#must send current user in options
current_user = 'user:' + 'user'
post_data['options'] = current_user
bpm_request.add_data(urllib.urlencode(post_data))
response = urllib2.urlopen(bpm_request)
try:
return response
except Exception, exception:
logging.info(str(exception))
#endtry
#end restcall
现在假设您想要构建一个包含所有流程实例的列表:
import xml.etree.ElementTree as ET
response = restcall(my_user,'API/queryRuntimeAPI/getLightProcessInstances')
root = ET.parse(response).getroot()
UUIDs=[]
for doc in root.findall('LightProcessInstance'):
UUIDs.append(doc.find('instanceUUID').find('value').text)
#endfor
或构建收件箱中的流程实例列表:
response = restcall(my_user,'API/queryRuntimeAPI/getLightParentProcessInstancesWithActiveUser/' + my_user +'?fromIndex=0&pageSize=200')
root = ET.parse(response).getroot()
UUIDs=[]
for doc in root.findall('LightProcessInstance'):
UUIDs.append(doc.find('instanceUUID').find('value').text)
#endfor
发布数据真的很痛苦。首先,我创建了一个函数来清除您可能发送的任何文本:
def super_clean(text):
"""This will make data safe to send by rest.
Escape for <> (so we don't screw up our XML).
Quote plus to handle + signs.
Encode for international chars and smart quoates.
Strip to take out extra blanks before and after"""
return urllib.quote_plus(escape(text.encode('utf-8','replace').strip()))
#end super_clean
第2部分-添加附件 附加文件需要调用/API/runtimeAPI/addAttachmentOctetStream/{instanceUUID}?name=�&文件名=� 此REST调用与常规Bonita REST调用稍有不同,因此我编写了以下函数以提供帮助:
from django.conf import settings
import urllib
import urllib2
import base64
def restAttachment(user,url,file):
#create bpm_request
bpm_request = urllib2.Request(settings.BPM_HOST + url,data=file.read())
#encode username and password and add to header
authKey = base64.b64encode(settings.BPM_USERNAME + ':' + settings.BPM_PASSWORD)
bpm_request.add_header("Authorization","Basic " + authKey)
#must send current user in options
current_user = 'user:' + 'user'
#add headers
bpm_request.add_header("Content-Type","application/octet-stream")
bpm_request.add_header('Content-Length', str(file.size))
bpm_request.add_header('Cache-Control', 'no-cache')
bpm_request.add_header('options', current_user)
bpm_request.add_header("Content-Disposition","attachment; filename="+urllib.quote(file.name))
try:
response = urllib2.urlopen(bpm_request)
except Exception, exception:
logging.info(str(exception))
#endtry
return response
#end restAttachment
在forms.py中,您需要这样一个表单:
class AddAttachmentForm(Form):
process_instance_uuid = CharField(widget=HiddenInput())
attachment = FileField()
#end AddAttachmentForm
在模板中,您需要具有以下内容(请注意enctype):
处理表格时,您应具备:
if 'attachment' not in request.FILES:
return render_to_response(...
form = AddAttachmentForm(request.POST, request.FILES) #note the request.FILES
response = restAttachment(user,'API/runtimeAPI/addAttachmentOctetStream/' + form.cleaned_data['process_instance_uuid'] + '?name=bonita_attachment_type_field_name) + '&fileName=' + urllib.quote(request.FILES['attachment'].name),request.FILES['attachment'])
第3部分-通过REST调用使用Search Bonitasoft的搜索功能 据我所知,关于这方面的文档很少甚至没有 让我们从基础开始。要进行搜索,您必须发布到API/queryRuntimeAPI/searchByMaxResult?firstResult=�&最大结果=�. 如果发布到API/queryRuntimeAPI/search,则只会获得记录数,而不会获得记录本身 您可以使用firstResult和maxResults进行分页 你的身体应该是这样的:
options=user:my_user&query=<query_xml>
您可能认为这将返回字段“first_name”设置为“Archibald”的任何记录。事实上,它返回任何包含字段“first_name”(应该是所有字段)的记录,并返回任何值为“Archibald”的用户定义字段。因此,如果某人的姓是“阿奇博尔德”,他们也会被退回。似乎没有办法解决这个问题。最好接受这是一个完整的搜索,并将其简化为:
query.criterion(ProcessInstanceIndex.VARIABLE_VALUE).equalsTo('Archibald');
这将生成以下XML:
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>
我不想把分页搞得一团糟,所以如果有超过100条记录,我会给它们一条消息,以缩小它们的标准。请注意,在xml中,我在搜索文本周围加了引号。这使得搜索匹配整个文本。如果文本中有空格,而您不使用引号,则搜索不会尝试匹配所有单词。以下是我处理表单和执行搜索的方式:
if request.method == 'POST':
search_form = SearchForm(request.POST)
if not search_form.is_valid():
return render_to_response(...
#format dates in manner expected by API
start = search_form.cleaned_data['start_date'].strftime('%Y%m%d') + "000000000"
end = search_form.cleaned_data['end_date'].strftime('%Y%m%d') + "999999999"
post_data={}
#the search query must be in this xml format
post_data['query'] = '''
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<string>(</string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>"%s"</value>
</org.ow2.bonita.search.Criterion>
<string> OR </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedBy</fieldName>
<value>"%s"</value>
</org.ow2.bonita.search.Criterion>
<string>)</string>
<string> AND </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedDate</fieldName>
<value>[%s TO %s]</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>''' \
% (super_clean(search_form.cleaned_data['search_text']),
super_clean(search_form.cleaned_data['search_text']),
start,
end)
#get number of records
response = restcall(request,'API/queryRuntimeAPI/search',post_data)
number_of_records = response.read()
if is_int(number_of_records):
number_of_records = int(number_of_records)
else:
return render_to_response(...
if number_of_records > 100:
return render_to_response(...
if number_of_records == 0:
return render_to_response(...
#now get the records
response = restcall(request,'API/queryRuntimeAPI/searchByMaxResult?firstResult=0&maxResults=100',post_data)
root = ET.parse(response).getroot()
#loop through requests
for doc in root.findall('LightProcessInstance'):
...
if request.method==“POST”:
search\u form=SearchForm(request.POST)
如果不是,则搜索表单。表单是否有效()
返回呈现到响应(。。。
#以API预期的方式格式化日期
开始=搜索表单。已清理的数据['start\u date'].strftime(“%Y%m%d”)+“000000000”
end=搜索\表单。已清理\数据['end\ u date'].strftime(“%Y%m%d”)+“99999999”
post_数据={}
#搜索查询必须采用此xml格式
post_数据['query']=''
(
变量_值
%s
或
开始
%s
)
及
开始日期
[%s到%s]
''' \
%(超级清理(搜索表单.清理的数据['search\u text']),
超级清理(搜索表单.清理的数据['search\u text']),
开始
(完)
#获取记录数
response=restcall(请求,'API/queryRuntimeAPI/search',post_数据)
记录的数量=响应。读取()
如果为int(记录数):
记录数=int(记录数)
其他:
返回呈现到响应(。。。
如果记录的数量大于100:
返回呈现到响应(。。。
如果记录的数量=0:
返回呈现到响应(。。。
#现在去拿记录
response=restcall(请求,'API/queryRuntimeAPI/searchByMaxResult?firstResult=0&maxResults=100',post_数据)
root=ET.parse(response).getroot()
#循环处理请求
对于root.findall('LightProcessInstance')中的文档:
...
options=user:my_user&query=<query_xml>
import org.ow2.bonita.search.SearchQueryBuilder;
import org.ow2.bonita.search.index.ProcessInstanceIndex;
import org.ow2.bonita.search.index.ActivityInstanceIndex;
SearchQueryBuilder query = new SearchQueryBuilder(new ProcessInstanceIndex());
query.criterion(ProcessInstanceIndex.VARIABLE_NAME).equalsTo("first_name").and().criterion(ProcessInstanceIndex.VARIABLE_VALUE).equalsTo('Archibald');
return query;
query.criterion(ProcessInstanceIndex.VARIABLE_VALUE).equalsTo('Archibald');
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
<string> OR </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedBy</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<string>(</string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
<string> OR </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedBy</fieldName>
<value>Archibald</value>
</org.ow2.bonita.search.Criterion>
<string>)</string>
<string> AND </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedDate</fieldName>
<value>[20130101000000000 TO 20131231999999999]</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>
class SearchForm(Form):
search_text = CharField(widget=TextInput(attrs={'size':'80'}))
start_date = DateField(widget=widgets.TextInput(attrs={"class":"calendar"}))
end_date = DateField(widget=widgets.TextInput(attrs={"class":"calendar"}))
if request.method == 'POST':
search_form = SearchForm(request.POST)
if not search_form.is_valid():
return render_to_response(...
#format dates in manner expected by API
start = search_form.cleaned_data['start_date'].strftime('%Y%m%d') + "000000000"
end = search_form.cleaned_data['end_date'].strftime('%Y%m%d') + "999999999"
post_data={}
#the search query must be in this xml format
post_data['query'] = '''
<SearchQueryBuilder>
<index class="org.ow2.bonita.search.index.ProcessInstanceIndex"/>
<query>
<string>(</string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>variable_value</fieldName>
<value>"%s"</value>
</org.ow2.bonita.search.Criterion>
<string> OR </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedBy</fieldName>
<value>"%s"</value>
</org.ow2.bonita.search.Criterion>
<string>)</string>
<string> AND </string>
<org.ow2.bonita.search.Criterion>
<builder reference="../../.."/>
<fieldName>startedDate</fieldName>
<value>[%s TO %s]</value>
</org.ow2.bonita.search.Criterion>
</query>
</SearchQueryBuilder>''' \
% (super_clean(search_form.cleaned_data['search_text']),
super_clean(search_form.cleaned_data['search_text']),
start,
end)
#get number of records
response = restcall(request,'API/queryRuntimeAPI/search',post_data)
number_of_records = response.read()
if is_int(number_of_records):
number_of_records = int(number_of_records)
else:
return render_to_response(...
if number_of_records > 100:
return render_to_response(...
if number_of_records == 0:
return render_to_response(...
#now get the records
response = restcall(request,'API/queryRuntimeAPI/searchByMaxResult?firstResult=0&maxResults=100',post_data)
root = ET.parse(response).getroot()
#loop through requests
for doc in root.findall('LightProcessInstance'):
...