Update custom_fields with Python in asana API - python

I've been trying update custom_fields per the latest version of Asana's API, very similarly to this post but with a later version of the API (e.g. I need to use update_task method). I can update fields at the top level of a task, but the custom_fields object is proving much more challenging to update. For example, I have many custom fields, and am trying to update a test field called "Update" and just set the text_value to "Hello"...
import asana
asanaPAT = 'myToken'
client = asana.Client.access_token(asanaPAT)
result = client.tasks.get_tasks({'project': 'myProjectID'}, opt_pretty=True)#, iterator_type=None)
for index, result in enumerate(result):
complete_task = client.tasks.find_by_id(result["gid"])
task_name = complete_task['name']
task_id = complete_task['gid']
custom_fields = complete_task['custom_fields']
#I can easily update top-level fields like 'name' and 'completed'...
#result = client.tasks.update_task(task_id, {'name': task_name + '(new)'}, opt_pretty=True)
#result = client.tasks.update_task(task_id, {'completed': False}, opt_pretty=True)
for custom_fieldsRow in custom_fields:
if custom_fieldsRow['name'] == "Updated":
#custom_fieldsRow['text_value'] = 'Hello'
#finished loop through individual custom fields, so update on the level of the task...
#client.tasks.update_task(task_id, {custom_fields}, opt_pretty=True)
manualCustomField = {'data': { 'custom_fields': {'gid': 'theGIDOfCustomField', 'text_value': 'Hello'} }}
resultFromUpdate = client.tasks.update_task(task_id, manualCustomField, opt_pretty=True)
As you can see above, I started off trying to loop through the custom_fields and make changes to the specific field before updating later. But now I'm even trying to manually set the custom_field data (last line of my code), but it does nothing (no error, but doesn't change my task). I'm completely out of ideas to troubleshoot this so appreciate any feedback on where I'm going wrong.

Apologies, I figured out my mistake, I just needed my penultimate line to read...
manualCustomField = { 'custom_fields': {'theGIDOfCustomField':'Hello'} }
Kinda a strange way to do that in the API (not specifically stating which field you'll update or which id you're using) if you ask me, but now it finally works.

Related

Trouble assigning a contact to a task - HubSpot

I am creating a task with the Hubspot Python package, and am trying to assign it a contact.
However, I am having trouble assigning the contact and am getting the error:
{"status":"error","message":"One or more associations are invalid","correlationId":"74f11757-cf08-40e0-8ea4-b476fa7c7f32","context":{"INVALID_OBJECT_IDS":["CONTACT=15447877061 is not valid"]},"category":"VALIDATION_ERROR"}
Here is my code:
from hubspot import HubSpot
from hubspot.crm.tickets import SimplePublicObjectInput
# Create a task.
properties = {
"hs_timestamp": current_utc_time,
"hs_task_body": body,
"hubspot_owner_id": secrets["hubspot_owner_id"],
"hs_task_subject": subject,
"hs_task_status": "WAITING",
"hs_task_priority": "HIGH",
}
simple_public_object_input = SimplePublicObjectInput(properties=properties)
task = hubspot.crm.objects.tasks.basic_api.create(
simple_public_object_input=simple_public_object_input
)
# Assign it a contact.
hubspot.crm.objects.tasks.associations_api.create(
task_id=task.id,
to_object_type="Contact",
to_object_id=101, # example contact ID. The contact 101 exists in my env.
association_spec=[
{
"associationCategory": "HUBSPOT_DEFINED",
"associationTypeId": 1, # what is this supposed to be?
}
],
)
It seems to think my task ID is my contact ID..? I am also confused by the associationTypeId. What is it supposed to be? In the docs it is always 0 with no explanation of what it is (as far as I could see). It seems I am missing/not understanding something here. Thank you.

I am trying to update data but it doesn't get updated in the database

I am new to python and Mongo db. What am I trying to do is that I want to update data in my database and code seems to be working fine. But, still the data doesn't get updated in the database.
I have tried functions like update and update_one etc. But still no luck so far.
#app.route("/users/update_remedy", methods = ['POST'])
def update_remedy():
try:
remedy = mongo.db.Home_Remedies
name = request.get_json()['name']
desc = request.get_json()['desc']
print("S")
status = remedy.update_one({"name" : name}, {"$set": {"desc" : desc}})
print("h")
return jsonify({"result" : "Remedy Updated Successfully"})
except Exception:
return 'error'
It's likely that your update_one call is looking for a document that doesn't exist. If the query on a vanilla update doesn't return any documents then no update operation will be performed. Make sure that a doc with the field {"name" : name}
actually exists. You could also check the return value from the update_one to ensure an update happened. See UpdateResult for details.

How to Replicate Multidict for Flask Post Unit Test Python

So in my flask app, I have a form on the frontend that gets populated with several users. Each user is associated with a checkbox with the name 'selected_user'. On submit, the form is posted through standard HTML form controls (no javascript or manual ajax of any kind).
In the backend, I can parse this using
flask.request.form.getlist('selected_user')
and it returns a list of users as I expect (a user here is itself a dictionary of unique keys and associated values).
Printing out flask.request.form looks like so for example:
ImmutableMultiDict([
('_xsrf_token', u'an_xsrf_token_would_go_here'),
('selected_user', u'{u\'primaryEmail\': u\'some_value\'}'...),
('selected_user', u'{u\'primaryEmail\': u\'some_value\'}'...)])
My problem is, I cannot seem to replicate this format in my unit tests for the life of me. Obviously, I could use some javascript to bundle the checked users on the frontend into an array or whatever and then duplicate that area much easier on the backend, and that may very well be what I end up doing, but that seems like an unnecessary hassle just to make this function testable when it already behaves perfectly in my application.
Here is what I currently have tried in my test, which seems like it should be the correct answer, but it does not work:
mock_users = []
for x in range(0, len(FAKE_EMAILS_AND_NAMES)):
mock_user = {}
mock_user['primaryEmail'] = FAKE_EMAILS_AND_NAMES[x]['email']
mock_user['name'] = {}
mock_user['name']['fullName'] = FAKE_EMAILS_AND_NAMES[x]['name']
mock_users.append(mock_user)
data = {}
data['selected_user'] = mock_users
response = self.client.post(flask.url_for('add_user'), data=data,
follow_redirects=False)
This gives me an error as follows:
add_file() got an unexpected keyword argument 'primaryEmail'
I've also attempted sending these as query strings, sending data as json.dumps(data), encoding each mock_user as a tuple like this:
data = []
for x in range(0, 3):
my_tuple = ('selected_user', mock_users[x])
data.append(my_tuple)
None of these approaches have worked for various other errors. What am I missing here? Thanks ahead of time for any help! Also, sorry if there are an obvious syntax errors as I rewrote some of this for SO instead of copy pasting.
You can create a MultiDict, then make it Immutable:
from werkzeug.datastructures import MultiDict, ImmutableMultiDict
FAKE_EMAILS_AND_NAMES = [
{'email': 'a#a.com',
'name': 'a'},
{'email': 'b#b.com',
'name': 'b'},
]
data = MultiDict()
for x in range(0, len(FAKE_EMAILS_AND_NAMES)):
mock_user = {}
mock_user['primaryEmail'] = FAKE_EMAILS_AND_NAMES[x]['email']
mock_user['name'] = {}
mock_user['name']['fullName'] = FAKE_EMAILS_AND_NAMES[x]['name']
data.add('select_user', mock_user)
data = ImmutableMultiDict(data)
print data
This prints:
ImmutableMultiDict([
('select_user', {'primaryEmail': 'a#a.com', 'name': {'fullName': 'a'}}),
('select_user', {'primaryEmail': 'b#b.com', 'name': {'fullName': 'b'}})
])
EDIT:
The line data.add... should probably be data.add('selected_user', json.dumps(mock_user)) since it looks like the output you posted is a JSON encoded string.

Updating DataStore JSON values using endpoints (Python)

I am trying to use endpoints to update some JSON values in my datastore. I have the following Datastore in GAE...
class UsersList(ndb.Model):
UserID = ndb.StringProperty(required=True)
ArticlesRead = ndb.JsonProperty()
ArticlesPush = ndb.JsonProperty()
In general what I am trying to do with the API is have the method take in a UserID and a list of articles read (with an article being represented by a dictionary holding an ID and a boolean field saying whether or not the user liked the article). My messages (centered on this logic) are the following...
class UserID(messages.Message):
id = messages.StringField(1, required=True)
class Articles(messages.Message):
id = messages.StringField(1, required=True)
userLiked = messages.BooleanField(2, required=True)
class UserIDAndArticles(messages.Message):
id = messages.StringField(1, required=True)
items = messages.MessageField(Articles, 2, repeated=True)
class ArticleList(messages.Message):
items = messages.MessageField(Articles, 1, repeated=True)
And my API/Endpoint method that is trying to do this update is the following...
#endpoints.method(UserIDAndArticles, ArticleList,
name='user.update',
path='update',
http_method='GET')
def get_update(self, request):
userID = request.id
articleList = request.items
queryResult = UsersList.query(UsersList.UserID == userID)
currentList = []
#This query always returns only one result back, and this for loop is the only way
# I could figure out how to access the query results.
for thing in queryResult:
currentList = json.loads(thing.ArticlesRead)
for item in articleList:
currentList.append(item)
for blah in queryResult:
blah.ArticlesRead = json.dumps(currentList)
blah.put()
for thisThing in queryResult:
pushList = json.loads(thisThing.ArticlesPush)
return ArticleList(items = pushList)
I am having two problems with this code. The first is that I can't seem to figure out (using the localhost Google APIs Explorer) how to send a list of articles to the endpoints method using my UserIDAndArticles class. Is it possible to have a messages.MessageField() as an input to an endpoint method?
The other problem is that I am getting an error on the 'blah.ArticlesRead = json.dumps(currentList)' line. When I try to run this method with some random inputs, I get the following error...
TypeError: <Articles
id: u'hi'
userLiked: False> is not JSON serializable
I know that I have to make my own JSON encoder to get around this, but I'm not sure what the format of the incoming request.items is like and how I should encode it.
I am new to GAE and endpoints (as well as this kind of server side programming in general), so please bear with me. And thanks so much in advance for the help.
A couple things:
http_method should definitely be POST, or better yet PATCH because you're not overwriting all existing values but only modifying a list, i.e. patching.
you don't need json.loads and json.dumps, NDB does it automatically for you.
you're mixing Endpoints messages and NDB model properties.
Here's the method body I came up with:
# get UsersList entity and raise an exception if none found.
uid = request.id
userlist = UsersList.query(UsersList.UserID == uid).get()
if userlist is None:
raise endpoints.NotFoundException('List for user ID %s not found' % uid)
# update user's read articles list, which is actually a dict.
for item in request.items:
userslist.ArticlesRead[item.id] = item.userLiked
userslist.put()
# assuming userlist.ArticlesPush is actually a list of article IDs.
pushItems = [Article(id=id) for id in userlist.ArticlesPush]
return ArticleList(items=pushItems)
Also, you should probably wrap this method in a transaction.

Python beatbox salesforce external Id upsert

I'm trying to upsert a record via the salesforce Beatbox python client the upsert operation seems to work fine but I can't quite work out how to specify an externalid as a foreign key:
Attempting to upsert with:
consolidatedToInsert = []
for id,ce in ConsolidatedEbills.items():
consolidatedToInsert.append(
{
'type':'consolidated_ebill__c',
'Account__r':{'type':'Account','ETL_Natural_Key__c':ce['CLASS_REFERENCE']},
'ETL_Natural_Key__c':ce['ISSUE_UNIQUE_ID']
}
)
print consolidatedToInsert[0]
pc.login('USERNAME', 'TOTALLYREALPASSWORD')
ret = pc.upsert('ETL_Natural_Key__c',consolidatedToInsert[0])
print ret
gives the error:
'The external foreign key reference does not reference a valid entity: Account__r'
[{'isCreated': False, 'errors': [{'fields': [], 'message': 'The external foreign key reference does not reference a valid entity: Account__r', 'statusCode': 'INVALID_FIEL
D'}], 'id': '', 'success': False, 'created': False}]
The soap examples and the specificity of the error text seem to indicate that it's possible but I can find little in the documentation about inserting with external ids.
On a closer look I'm not sure if this is possible at all, a totally mangled key to Account__r seems to pass silently as if it's not even being targeted for XML translation, I'd love to be wrong though.
A quick change to pythonclient.py 422:0:
for k,v in field_dict.items():
if v is None:
fieldsToNull.append(k)
field_dict[k] = []
if k.endswith('__r') and isinstance(v,dict):
pass
elif hasattr(v,'__iter__'):
if len(v) == 0:
fieldsToNull.append(k)
else:
field_dict[k] = ";".join(v)
and another to __beatbox.py 375:0
for fn in sObjects.keys():
if (fn != 'type'):
if (isinstance(sObjects[fn],dict)):
self.writeSObjects(s, sObjects[fn], fn)
else:
s.writeStringElement(_sobjectNs, fn, sObjects[fn])
and it works like some dark magic.
Currently Beatbox doesn't support serializing nested dictionaries like this, which is needed for the externalId resolution you're trying to do. (If you look at the generated request, you can see that the nested dictionary is just serialized as a string)).

Categories