It is impossible to create a cumulative Team workload in Portfolio

I have a new task assignement:

Collect all working hours of team members and sum them up so they can be displayed as cumulative team workload in the Portfolio view. Product manager just wants to see how occupied a team is compared to its maximum worload. He doesn’t want to see the individual team members workload. He just wants to see one object (“backend team”) in the portfolio view.

Let’s assume the team has 5 members which have a capacity of 40 weekly hrs each. The team object in portfolio view than need to have an upper threshold of 200 and the graph shall show how many hours in total for all team members are already used.

The three key points i am trying to figure out:

AFAIK the portfolio view can only display the workload of an individual person. How to circumvent that?

How to sum up the individuals workload to generate a cumulative team workload?

Will i have to use webhooks?

Best regards,

John

1 Like

Bump

@lpb @Bastien_Siebman @LEGGO You guys have more Portfolio experience than I do - anybody have some input for @anon1824926?

1 Like

You should use Universal Reporting for this :slight_smile: sum all values from a specific custom field from several projects.

2 Likes

@anon1824926,

If @Bastien_Siebman’s solution is enough, that’s certainly easy. You could communicate the upper threshold simply in the title of the chart since it’s fixed.

Alternately, you could code a solution, perhaps, that accumulates the totals into a dummy user (takes up a seat) representing the team and then perhaps be able to see that in Workload itself, if desired. Much harder!!

Larry

3 Likes

The reporting doesn’t give u enough details, so we will go with a solution where we copy all tasks of a project into a shadow project and create a dummy user which gets all those tasks assigned.

I am getting an error with thte duplicate task api call tough:

result = client.tasks.duplicate_task(task1_gid, {'data': {'include': ['notes','assignee'],'name': 'mynewtask'}}, opt_pretty=True)

Traceback (most recent call last):
File “duplicate_task.py”, line 48, in
result = client.tasks.duplicate_task(task1_gid, {‘data’: {‘include’: [‘notes’,‘assignee’],‘name’: ‘mynewtask’}}, opt_pretty=True)
File “C:\Users\John\AppData\Roaming\Python\Python39\site-packages\asana\resources\tasks.py”, line 111, in duplicate_task
return self.client.post(path, params, **options)
File “C:\Users\John\AppData\Roaming\Python\Python39\site-packages\asana\client.py”, line 202, in post
return self.request(‘post’, path, data=body, headers=headers, **options)
File “C:\Users\John\AppData\Roaming\Python\Python39\site-packages\asana\client.py”, line 91, in request
raise STATUS_MAPresponse.status_code
asana.error.InvalidRequestError: Invalid Request: name: Missing input

This bothers me, because i specified the ‘name’ parameter with ‘mynewtask’ but the API is still not accepting it. Can you point me to a working python example of this function?

Bump

Did you try to not wrap everything inside a data object? I can’t think of anything else… @Phil_Seeman ?

1 Like

@anon1824926,

I don’t use python but it looks like @Bastien_Siebman is exactly correct; you don’t want to be wrapping it in data. Here’s a working syntax:

2 Likes

Alright, thanks. Beginners mistake.

1 Like

Not to worry, we’ve all been there. (And sometimes we still make beginners mistakes!)

1 Like

I am coding a custom solution and made some progress but now ran into an issue. My current approach is this:

For each real project, i created a “shadow” project. If a task is created, modified or deleted in the real project, a respective “shadow task” in the “shadow project” is synced. All tasks in this “shadow project” are automatically assigned to a dummy user which is then used for the reporting.

I succeed in making an initial sync from the real project to the shadow project by getting all tasks in the real project, duplicating them, renaming them with a _shadow suffix, moving them to the shadow project and assigning the dummy user. So far so good.

Now i need to keep the shadow project in sync with the real project if a user changes something there. For example if the user creates a task in the real project, i listen to the events and then if i catch the create event, i also create a matching task in the shadow project.

I ran into a problem when a user deletes a task in the real project. I catch the delete event with the event handler and extract the gid of the deleted task. I then need to find the matching task in the shadow project to also delete it.

My first approach was that every real task had the shadow listed as a dependent so i could now which shadow task belongs to which real task. I used the get dependent function:

GET /tasks/{task_gid}/dependents

The issue is that when i call that function upon receiving the delete event for that task, this task has obviously already been deleted and the call fails.

I need a way to find/identify always the corresponding shadow task of a real task if some action is performed on the real task. I need a link from the real task to it’s shadow to keep the shadow in sync.

I haven’t found a satisfying solution and want to ask if you know any good approach?

I thought about using the “Search tasks in a workspace function” and tried it but couldn’t get it to work as i want. I had two ideas on how to use it:

  1. I created a section in the shadow project for each duplicated task which had the original task’s gid as a name string. I would then use the search function to get this section in the shadow project and have access to it’s tasks. But this didnt work, i suspect the search function can only find sections by their gid and not by their displayed name string?

  2. I could create a custom field in each shadow task which has the gid of the original task in it and then use the search function to get the shadow task with the right gid string value in this custom field. I am still thinking about this solution but i feel there must be an easier way.

So in a nutshell, how can i have this lookup function between the real task and its separate shadow task that i can always find the shadow task when something in the real task?

Thankful for any ideas and possible approaches. I can also post code more snippets if anything is unclear.

My small event handler looks like this:

print("starting streaming events for project \r\n")
for event in client.events.get_iterator({'resource': project_gids['proj_wow_sc']}):
	#print("event", event, end='\r\n\n')
	event_type = event['type']
	action = event['action']
	print("Action:", action, "Event type:", event_type, end='\r\n')
	# if the event has the task type, we need to change something in the shadow project
	if event_type == 'task':
		# if delete task, delete dependents from shadow
		if action == 'deleted':
			try:
				# Try to find the task
				search_task_result = client.tasks.search_tasks_for_workspace(GID_WORKSPACE, {'?sections.any': event['resource']['gid'], '?projects.any': project_gids['proj_wow_sc_shadow']}, opt_pretty=USE_PRETTY_RESPONSE)
				print(next(search_task_result))
				'''
				#get section of task, delete tasks in that section and delete section
				print("Task",event['resource']['name'],"was", action, end='\r\n')
				shadow_section_result = client.sections.get_section(event['resource']['gid'], {'param': 'value', 'param': 'value'}, opt_pretty=USE_PRETTY_RESPONSE)
				print(shadow_section_result)
				#shadow_section_result = client.tasks.get_tasks_for_section(event['resource']['gid'], {'param': 'value', 'param': 'value'}, opt_pretty=USE_PRETTY_RESPONSE)
				'''
			except KeyError:
				print("Key not found", end='\r\n')

	# if create task, create new task in shadow too

	# if change task, change task in shadow too, or if change, delete shadow, create new shadow?

I’m afraid I’ll have to defer to Bastien and Phil here.

@anon1824926,

I think I would go with your #2 idea, but instead of putting the gid in a custom field, maybe use the external field which has the advantage that it will never show in the UI and thus never possibly get deleted or modified by a user. If it helps in your logic you might even make a two-way link; that is, also put the gid of the duplicate in the external field of the real task. Because users will never see that data, you can comfortably put it in the real task.

Hi,

thanks for your suggestion! Would it be possible to locate the shadow task with the search function by searching for the value of the external field? Could you point me to a python example since i couldnt figure out how to use the search function properly yet? (I have read the API doc but a real world example would be nice…maybe one of your developers has a snippet ready?

Again, thanks for the great support here!

One more thing: do you think its better to use the duplicate task function to create the shadow task or a combination of get task and create task? It would be nice to create / duplicate the shadow_task DIRECTLY in the shadow project, because otherwise it will briefly show up in the original project and create a create event on my event handler…

Sorry, I don’t code in python, can’t help you there. But I don’t think you can use the Search API for external data and you don’t need to - store the gid of the shadow task in the external field of the real task and then you can access it directly via the gid. See here for more on external data:

Definitely agree, you don’t want it showing briefly in the real project and then disappearing.

I read all the documentation already, but i cannot get it to work in python. IMO there are not enough detailed code examples. Could you reach out to a developer?

@AndrewWong are you able to help out? I’m not familiar with the Asana python client…