dialogflow python SDK: How do I parse the DetectIntentResponse - python

I am moving from dialogflow V1 to V2.
Using the dialogflow python SDK I receive a DetectIntentResponse struct object that should have the information inside that I need.
After some time of trying to find documentation and trying to inspect this object I need your help. This object is so far out of my league ...
For documentation, thats how I get the response object:
import dialogflow_v2 as dialogflow
session_client = dialogflow.SessionsClient()
session = session_client.session_path(project_id, session_id)
text_input = dialogflow.types.TextInput(text=text, language_code=language_code)
query_input = dialogflow.types.QueryInput(text=text_input)
response = session_client.detect_intent(session=session, query_input=query_input)
How can I parse the response?
e.g. I get some parameter struct by using response.query_result.parameters But how do I get this list?
Maybe I can transform the response into json (that would make things quite easy)?
I need dicts, lists, strings ... :)

You can use MessageToJson in google.protobuf project. (Google's Protocol Buffer)
#import the function
from google.protobuf.json_format import MessageToJson
#...after getting the response = session_client.detect_intent(...)
json_response = MessageToJson(response)
You can convert various types included in the DetectIntentResponse such as QueryResult by MessageToJson(response.query_result) etc. to get a specific response.
My answer is specific to DialogFlow however I took the hint from this answer at SO which answers a much general question related to python.
Please note that I have used google.protobuf.json_format.MessageToJson specifically because the DialogFlow API V2 returns class objects defined by Google. I cannot guarantee that this will work with other Chatbot APIs (may be I need to explore it too).

The response we get after calling detect_intent() function is of type <class 'google.cloud.dialogflow_v2.types.DetectIntentResponse'>
Here is a sample response after calling detect_intent() function:
query_result {
query_text: "testing testing 123 abc#gmail.com"
action: "test"
parameters {
fields {
key: "email"
value {
string_value: "abc#gmail.com"
}
}
fields {
key: "number-integer"
value {
list_value {
values {
number_value: 123.0
}
}
}
}
}
all_required_params_present: true
fulfillment_text: "testing invoked"
fulfillment_messages {
text {
text: "testing invoked"
}
}
output_contexts {
name: "projects/*****/agent/sessions/session-test/contexts/testing-context"
lifespan_count: 5
parameters {
fields {
key: "email"
value {
string_value: "abc#gmail.com"
}
}
fields {
key: "email.original"
value {
string_value: "abc#gmail.com"
}
}
fields {
key: "number-integer"
value {
list_value {
values {
number_value: 123.0
}
}
}
}
fields {
key: "number-integer.original"
value {
string_value: "123"
}
}
}
}
intent {
name: "projects/*****/agent/intents/*****"
display_name: "test"
}
intent_detection_confidence: 1.0
language_code: "en"
}
You can easily parse values using below code:
Query Result Type : type(response) --> <class 'google.cloud.dialogflow_v2.types.DetectIntentResponse'>
Query text : response.query_result.query_text --> testing testing 123 abc#gmail.com
Detected intent : response.query_result.intent.display_name --> test
Fulfillment text : response.query_result.fulfillment_text --> testing invoked
Parameters : response.query_result.parameters --> fields {
key: "email"
value {
string_value: "abc#gmail.com"
}
}
fields {
key: "number-integer"
value {
list_value {
values {
number_value: 123.0
}
}
}
}
Hope it helps.

I solved my problem using the property ".pb". Here is my code:
...
json_response = MessageToJson(response._pb)
...

Related

How to ignore key in nested JSON (because it has variable name)

I have following JSON, where element "Key_with_variable_name" has variable name compared to other keys:
{
"Key1": {
"Key11": {
"Key111": "Value111",
"Key112": "Value112",
"Key113": "Value113",
"Key114": {
"Key1141": {
"Key_with_variable_name": {
"Key114111": {
"Key1141111": "Value1141111",
"Key1141112": "Value1141112",
"Key1141113": "Value1141113"
}
}
}
}
}
}
}
How can I access Key114111 (get its name) which is "under" Key_with_variable_name and then access nested Key1141111 and it's value Value1141111? And can I get value of Key_with_variable_name?
I used json_message = json.loads(jsonObject) and worked on JSON as nested dictionary, but struggled to get down deep past "Key_with_variable_name"
Or maybe it can be done easier working on JSON not on dictionary?

Adding multiple list of values to a dictionary doesn't create the same order of values

I am working with Clarifai's API to write the returned responses to a CSV. For each image I submit, I want to take the top 5 images and append their metadata to a row in a data frame with the file_id as the first column. So each image would have 5 rows with the metadata attached to it in each successive columns. I seem to be generating some dictionary entries where the order of the values in the list being appended isn't preserved and I am trying to figure out why. This is a sample response I am iterating over.
[score: 0.8935652375221252
input {
id: "1002648140"
data {
image {
url: "https://images.lowes.com/product/converted/192665/192665007798.jpg"
hosted {
prefix: "https://s3.amazonaws.com/clarifai-api/img3/prod"
suffix: "3617f25f9ee5463497e7abf350091b93/c1ee1a24260772d532f0d371ac08fb06"
sizes: "orig"
sizes: "tiny"
sizes: "small"
sizes: "large"
}
}
metadata {
fields {
key: "cat_entry_id"
value {
string_value: "1002648140"
}
}
fields {
key: "catalog_name"
value {
string_value: "Roses"
}
}
fields {
key: "catalog_path"
value {
string_value: "Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Roses"
}
}
fields {
key: "link_url"
value {
string_value: "https://www.lowes.com/pd/Spring-Hill-Nurseries-in-Bare-Root-Red-Flowering-Blaze-Climbing-Rose/1002648140"
}
}
fields {
key: "product_name"
value {
string_value: "Spring Hill Nurseries in Bare Root Red Flowering Blaze Climbing Rose"
}
}
}
clusters {
id: "985_203"
}
}
created_at {
seconds: 1599796898
nanos: 391916000
}
modified_at {
seconds: 1599798078
nanos: 291417000
}
status {
code: INPUT_DOWNLOAD_SUCCESS
description: "Download complete"
}
}
annotation {
id: "c588c2f17f01465f94bde2d2c2e5687f"
input_id: "1002648140"
data {
}
status {
code: ANNOTATION_SUCCESS
description: "Annotation success"
}
created_at {
seconds: 1599798076
nanos: 759651000
}
modified_at {
seconds: 1599798076
nanos: 759651000
}
model_version_id: "bb186755eda04f9cbb6fe32e816be104"
}
, score: 0.8807249665260315
input {
id: "1000383061"
data {
image {
url: "https://images.lowes.com/product/converted/743425/743425079649.jpg"
hosted {
prefix: "https://s3.amazonaws.com/clarifai-api/img3/prod"
suffix: "3617f25f9ee5463497e7abf350091b93/756b268566daea823f07248208c05e21"
sizes: "orig"
sizes: "tiny"
sizes: "small"
sizes: "large"
}
}
metadata {
fields {
key: "cat_entry_id"
value {
string_value: "1000383061"
}
}
fields {
key: "catalog_name"
value {
string_value: "Annuals"
}
}
fields {
key: "catalog_path"
value {
string_value: "Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Annuals"
}
}
fields {
key: "link_url"
value {
string_value: "https://www.lowes.com/pd/3-Quart-Purple-Purple-Garden-Mum-in-Pot/1000383061"
}
}
fields {
key: "product_name"
value {
string_value: " 3-Quart Purple Purple Garden Mum in Pot"
}
}
}
clusters {
id: "54_203"
}
}
created_at {
seconds: 1599795418
nanos: 883441000
}
modified_at {
seconds: 1599795429
nanos: 301624000
}
status {
code: INPUT_DOWNLOAD_SUCCESS
description: "Download complete"
}
}
annotation {
id: "91434754e4bd425ba3c528be9cd901e0"
input_id: "1000383061"
data {
}
status {
code: ANNOTATION_SUCCESS
description: "Annotation success"
}
created_at {
seconds: 1599795427
nanos: 683212000
}
modified_at {
seconds: 1599795427
nanos: 683212000
}
model_version_id: "bb186755eda04f9cbb6fe32e816be104"
}
I am using a dictionary and adding the value of metadata as a list and not getting the same order of the lists being appended. Please see the following sample dictionary for reference.
{'test_output/fe_IMG_0574.jpg_0': ['Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Roses',
'https://www.lowes.com/pd/Spring-Hill-Nurseries-in-Bare-Root-Red-Flowering-Blaze-Climbing-Rose/1002648140',
'Roses',
'Spring Hill Nurseries in Bare Root Red Flowering Blaze Climbing Rose',
'1002648140'],
'test_output/fe_IMG_0574.jpg_1': ['https://www.lowes.com/pd/3-Quart-Purple-Purple-Garden-Mum-in-Pot/1000383061',
'1000383061',
' 3-Quart Purple Purple Garden Mum in Pot',
'Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Annuals',
'Annuals'],
'test_output/fe_IMG_0574.jpg_2': ['https://www.lowes.com/pd/4-Count-Dahlia-Bulbs-L3480/1000151071',
'1000151071',
' 4 Count Dahlia Bulbs (L3480)',
'Outdoors$Plants & Planters$Plants, Bulbs & Seeds$Plant Bulbs',
'Plant Bulbs']}
which then creates a data frame that has different values for columns in what seems like sporadic rows. This is the code I am using to generate this.
final_dict = {}
for i in range(5):
response_object = response.hits[i].input.data.metadata.items()
final_list = []
url = response_object[0][1]
product_name = response_object[1][1]
catalog_entry_id = response_object[2][1]
catalog_path = response_object[3][1]
catalog_name = response_object[4][1]
final_list.append(url)
final_list.append(catalog_entry_id)
final_list.append(product_name)
final_list.append(catalog_path)
final_list.append(catalog_name)
final_dict[file_id+'_'+str(i)] = final_list
print(final_dict)
final_df = pd.DataFrame.from_dict(final_dict, orient='index')
Please see the following rows as an example of what I mean
This is for posterity. The new Clarifai GRPC client has different data structuring. So as a result, I had to parse them by first listing fields. This is what my final loop looked like. It had the ListFields added line
for i in range(5):
response_object = response.ListFields()[1]
test_meta = response_object[1][i].input.data.metadata
final_list = []
url = test_meta['link_url']
product_name = test_meta['product_name']
catalog_entry_id = test_meta['cat_entry_id']
catalog_path = test_meta['catalog_path']
catalog_name = test_meta['catalog_name']
final_list.append(i+1)
final_list.append(url)
final_list.append(catalog_entry_id)
final_list.append(product_name)
final_list.append(catalog_path)
final_list.append(catalog_name)
final_dict[file_id+'_'+str(i)] = final_list

Using the Firestore REST API to Update a Document Field

I've been searching for a pretty long time but I can't figure out how to update a field in a document using the Firestore REST API. I've looked on other questions but they haven't helped me since I'm getting a different error:
{'error': {'code': 400, 'message': 'Request contains an invalid argument.', 'status': 'INVALID_ARGUMENT', 'details': [{'#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'oil', 'description': "Error expanding 'fields' parameter. Cannot find matching fields for path 'oil'."}]}]}}
I'm getting this error even though I know that the "oil" field exists in the document. I'm writing this in Python.
My request body (field is the field in a document and value is the value to set that field to, both strings received from user input):
{
"fields": {
field: {
"integerValue": value
}
}
}
My request (authorizationToken is from a different request, dir is also a string from user input which controls the directory):
requests.patch("https://firestore.googleapis.com/v1beta1/projects/aethia-resource-management/databases/(default)/documents/" + dir + "?updateMask.fieldPaths=" + field, data = body, headers = {"Authorization": "Bearer " + authorizationToken}).json()
Based on the the official docs (1,2, and 3), GitHub and a nice article, for the example you have provided you should use the following:
requests.patch("https://firestore.googleapis.com/v1beta1/projects{projectId}/databases/{databaseId}/documents/{document_path}?updateMask.fieldPaths=field")
Your request body should be:
{
"fields": {
"field": {
"integerValue": Value
}
}
}
Also keep in mind that if you want to update multiple fields and values you should specify each one separately.
Example:
https://firestore.googleapis.com/v1beta1/projects/{projectId}/databases/{databaseId}/documents/{document_path}?updateMask.fieldPaths=[Field1]&updateMask.fieldPaths=[Field2]
and the request body would have been:
{
"fields": {
"field": {
"integerValue": Value
},
"Field2": {
"stringValue": "Value2"
}
}
}
EDIT:
Here is a way I have tested which allows you to update some fields of a document without affecting the rest.
This sample code creates a document under collection users with 4 fields, then tries to update 3 out of 4 fields (which leaves the one not mentioned unaffected)
from google.cloud import firestore
db = firestore.Client()
#Creating a sample new Document “aturing” under collection “users”
doc_ref = db.collection(u'users').document(u'aturing')
doc_ref.set({
u'first': u'Alan',
u'middle': u'Mathison',
u'last': u'Turing',
u'born': 1912
})
#updating 3 out of 4 fields (so the last should remain unaffected)
doc_ref = db.collection(u'users').document(u'aturing')
doc_ref.update({
u'first': u'Alan',
u'middle': u'Mathison',
u'born': 2000
})
#printing the content of all docs under users
users_ref = db.collection(u'users')
docs = users_ref.stream()
for doc in docs:
print(u'{} => {}'.format(doc.id, doc.to_dict()))
EDIT: 10/12/2019
PATCH with REST API
I have reproduced your issue and it seems like you are not converting your request body to a json format properly.
You need to use json.dumps() to convert your request body to a valid json format.
A working example is the following:
import requests
import json
endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"
body = {
"fields" : {
"[FIELD_1]" : {
"stringValue" : "random new value"
}
}
}
data = json.dumps(body)
headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data, headers=headers).json())
I found the official documentation to not to be of much use since there was no example mentioned. This is the API end-point for your firestore database
PATCH https://firestore.googleapis.com/v1beta1/projects/{YOUR_PROJECT_ID}/databases/(default)/documents/{COLLECTION_NAME}/{DOCUMENT_NAME}
the following code is the body of your API request
{
"fields": {
"first_name": {
"stringValue":"Kurt"
},
"name": {
"stringValue":"Cobain"
},
"band": {
"stringValue":"Nirvana"
}
}
}
The response you should get upon successful update of the database should look like
{
"name": "projects/{YOUR_PROJECT_ID}/databases/(default)/documents/{COLLECTION_ID/{DOC_ID}",
{
"fields": {
"first_name": {
"stringValue":"Kurt"
},
"name": {
"stringValue":"Cobain"
},
"band": {
"stringValue":"Nirvana"
}
}
"createTime": "{CREATE_TIME}",
"updateTime": "{UPDATE_TIME}"
Note that performing the above action would result in a new document being created, meaning that any fields that existed previously but have NOT been mentioned in the "fields" body will be deleted. In order to preserve fields, you'll have to add
?updateMask.fieldPaths={FIELD_NAME} --> to the end of your API call (for each individual field that you want to preserve).
For example:
PATCH https://firestore.googleapis.com/v1beta1/projects/{YOUR_PROJECT_ID}/databases/(default)/documents/{COLLECTION_NAME}/{DOCUMENT_NAME}?updateMask.fieldPaths=name&updateMask.fieldPaths=band&updateMask.fieldPaths=age. --> and so on

Post GraphQL mutation with Python Requests

I'm having trouble posting mutations with GraphQL and Python Requests.
My function looks like:
def create(request):
access_token = 'REDACTED'
headers = {
"X-Shopify-Storefront-Access-Token": access_token
}
mutation = """
{
checkoutCreate(input: {
lineItems: [{ variantId: "Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC80", quantity: 1 }]
}) {
checkout {
id
webUrl
lineItems(first: 5) {
edges {
node {
title
quantity
}
}
}
}
}
}
"""
data = (requests.post('https://catsinuniform.myshopify.com/api/graphql', json={'mutation': mutation}, headers=headers).json())
print(data)
return render(request, 'Stock/create.html', { 'create': data })
I'm getting errors saying I have a bad request "bad_request - Parameter Missing or Invalid" in my json response.
Even though you're sending a mutation, your request body should still include a query property, the value of which should be the string representing your operation. It's a bit confusing, but informally both queries and mutations are called "queries" (you're still "querying" the server either way). Change your request to:
requests.post('https://catsinuniform.myshopify.com/api/graphql', json={'query': mutation}, headers=headers)

Adding a Source to Librato Data When Sending through Segment

I am trying to figure out how to add a source to a metric in Librato when sending the information via Segment. I am using the python library and have tried creating a property for source (below) but it doesn't seem to be working properly.
Here's what I've got:
userID = '12345'
analytics.track(userID, 'event', {
'value': 1,
'integrations.Librato.source': userID
})
I've also tried 'source' and 'Librato.source' as properties, which were referenced in Segment's documentation. Any suggestions?
Similarly for ruby, using the segment gem you can specify a source like so:
require 'analytics-ruby'
segment_token = 'asdfasdf' # The secret write key for my project
Analytics.init({
secret: segment_token,
#Optional error handler
on_error: Proc.necd giw { |status, msg| print msg } })
Analytics.track(
user_id: 123,
writeKey: segment_token,
event: 'segment.librato',
properties: { value: 42 }, context: { source:'my.source.name' })
You can't set the source of the Librato metric in the properties when sending from Segment, you need to send it as part of the context meta data. Librato does not accept any properties other than 'value' so nothing else you send as a property will be recorded. To set the source using the python library, the code needs to be as follows:
userID = '12345'
analytics.track(userID, 'event', {
'value': 1
}, {
'Librato': {
'source': userID
}
})
If you are are using javascript, it would be:
analytics.track({
userId: '12345',
event: 'event'
properties: {
value: 1
},
context: {
'Librato': {
'source': userID
}
}
});

Categories