Asana/python-asana

Custom fields on tasks are bulky and hard to use

BBI-YggyKing opened this issue · 2 comments

I hope I am missing something here, and apologies if this isn't appropriate to raise as a GitHub issue, feel free to send me elsewhere if there's a better forum for this.

I have just started to use the Asana API. I am retrieving tasks as follows, so far so good:

task_fields = ['name', 'completed', 'assignee', 'completed_at']
tasks = client.tasks.find_by_project(project_id, opt_fields=task_fields)

Now I want to retrieve a specific custom field, which leads to two questions:

  1. Is there a way to request which custom fields I want to retrieve?
  2. Is there a simple way to retrieve the value of a specific field from the returned object?

At present I have added 'custom_fields' to the list of task_fields, which retrieves ALL the custom fields. And not just the fields, but all their metadata as well. We have a number of enum fields, and so the specification of all the metadata ends up as 500 lines of pretty-printed json, most of which is duplicated for every task. This seems inefficient, though perhaps under the hood things are cached?

Now that I have the custom fields, I want the value of a specific field for a particular task. Now, task['custom_fields'] is an array, so I need to search the array by name until I find what I'm looking for, then retrieve the corresponding value. It feels like there should be a better way than this:

time_spent = 0
for field in task['custom_fields']:
  if field['name'] == "Time Spent (days)":
    time_spent = field['number_value']

Information and advice welcomed with thanks! 😅

When I wrote that I wasn't aware of using paths in opt_fields, but I don't think it helps, since Time Spent (days) isn't a key, and number_value isn't unique.

Hi @BBI-YggyKing, apologies for the late reply. To answer your questions:

1: Is there a way to request which custom fields I want to retrieve?

Unfortunately we do not have a way to request which custom fields you want to retrieve. The custom fields property on a task is an array of object so it will return an array of custom field objects (See task schema). You can however use paths (“Selecting fields”) to select what fields on the custom field objects you want to return. Note when using our path feature it will always return a gid of the selected resource.

So for example, if you specify assignee.name in the path it will return the gid of the assignee and their name and ignore the other properties like resource_type that would have been returned if the path feature was not used.

import os
import asana
from pprint import pprint

client = asana.Client.access_token(os.environ['ASANA_PERSONAL_ACCESS_TOKEN'])
tasks = client.tasks.get_tasks_for_project(
    '<YOUR_PROJECT_GID>',
    opt_fields=[
        'assignee.name'
    ]
)

This will return something like:

[{'assignee': {'gid': '1201938574638920', 'name': 'example@user.com'},'gid': '1203019405968385'}]

Notice that in the task schema if you expand the assignee property you see gid, resource_type, and name since we specified assignee.name we will get the name property back + the gid since gid will be returned by default.

2: Is there a simple way to retrieve the value of a specific field from the returned object?

As far as I know there is no efficient way to retrieve the value of a specific field from the returned object. Because custom fields on a task is a list of custom field objects you will have to loop through each custom field on the task response to find the particular custom field that you are looking for. I would recommend that you change your if condition check to look at the GID of the custom field rather than the name. This is because if someone changes the name of the custom field within Asana (UI) then this condition will fail.

Here’s some sample code that wraps this together:

import os
import asana
from pprint import pprint

client = asana.Client.access_token(os.environ['ASANA_PERSONAL_ACCESS_TOKEN'])
tasks = client.tasks.get_tasks_for_project(
    ‘<YOUR_PROJECT_GID>’,
    opt_fields=[
        'name',
        'custom_fields.name',
        'custom_fields.number_value',
        'custom_fields.type'
    ]
)

for task in tasks:
    time_spent = 0
    for field in task['custom_fields']:
        if field['gid'] == '<YOUR_CUSTOM_FIELD_GID>':
            time_spent = field['number_value']
            print(time_spent)

The returned tasks response will look like the following:

[{'custom_fields': [{
                     'gid': '1233333333333332',
                     'name': 'Priority',
                     'type': 'enum'
                    },
                    {'gid': '1233333333333333',
                     'name': 'Time Spent (days)',
                     'number_value': 1,
                     'type': 'number'
                    }],
  'gid': '1203000000000000',
  'name': 'task 1'},
 {'custom_fields': [{
                     'gid': '1233333333333332',
                     'name': 'Priority',
                     'type': 'enum'
                    },
                    {'gid': '1233333333333333',
                     'name': 'Time Spent (days)',
                     'number_value': None,
                     'type': 'number'
                    }],
  'gid': '1203000000000001',
  'name': 'task 2'}
]

Notice that custom fields that are not of type number does not return a number_value value

Also please switch from using client.tasks.find_by_project to client.tasks.get_tasks_for_project