Implement a basic way to see the project for a task in the workday
ClosedPublic

Authored by dfaure on Aug 4 2018, 8:33 AM.

Details

Summary

Currently shown as a tooltip, the main purpose was to implement the
backend for this (AkonadiTaskQueries::findProjectForTask).

Test Plan

Hovering over a task in the workday shows the project for this task in a tooltip.

Diff Detail

Repository
R4 Zanshin
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
dfaure requested review of this revision.Aug 4 2018, 8:33 AM
dfaure created this revision.
dfaure updated this revision to Diff 39049.Aug 4 2018, 8:45 AM

Improve GUI, showing "Inbox" for tasks not in a project + basic unittest for the tooltip role

ervin requested changes to this revision.Aug 5 2018, 9:22 AM

I think it should be async like the rest, I understand it makes some things more complicated in the case of tooltips, but if we use something else than tooltips to display that information we'll need to update when the backend signals a change anyway.

It's an excellent start though! Looking forward to it, since we also have the findContexts for tasks it'll "just" be about channeling that information in the various models. :-)

src/akonadi/akonaditaskqueries.cpp
76–77

Unrelated right?

src/akonadi/akonaditaskqueries.h
68

This makes the operation synchronous, couldn't it be made async like all the operations in TaskQueries? One quick way of doing it is switching to QueryResult for the return type (like the other ones), except that in this case there'd be either 0 or 1 item in the result.

src/domain/taskqueries.h
54

I'd say the return type needs to be changed (see above).

This revision now requires changes to proceed.Aug 5 2018, 9:22 AM
dfaure added a comment.Aug 6 2018, 8:08 AM

I think it should be async like the rest, I understand it makes some things more complicated in the case of tooltips, but if we use something else than tooltips to display that information we'll need to update when the backend signals a change anyway.

It's more complicated at two levels. In the implementation and in the usage ;)
Let's talk about implementation first.

I need to go up the tree (of tasks) until I get to the project. Right now I do that with the full collection cache at hand, no problem.
IIUC, the live query mechanism will call my predicate with one item at a time, and I get to say "yes, keep this one". But that can't work if the predicate is called for the project before it's called for an intermediate parent task. I won't be able to say "yes this is my project" until I get the intermediate task which is the missing link to my child task.
Unless I'm missing something, I'd say the current framework for live queries isn't adapted to those needs.
I could of course still use m_cache->items(item.parentCollection()) in the predicate but that's crazy, right? There's no point in looking in ALL items every time the predicate is called with ONE item, this would amount to doing N times what can be done a single time...

It seems to me that we need a live query that calls (just once, not once per item) a method (one that returns a Project::Ptr, not a bool) whenever the collection has changed.
Using the cache, that method can right away return the project (if found).

It's an excellent start though! Looking forward to it, since we also have the findContexts for tasks it'll "just" be about channeling that information in the various models. :-)

When you say "we also have", do you mean this?

TaskQueries::ContextResult::Ptr TaskQueries::findContexts(Domain::Task::Ptr task) const
{
    qFatal("Not implemented yet");
    Q_UNUSED(task);
    return ContextResult::Ptr();
}

That's an interesting concept of "having" :-)

src/akonadi/akonaditaskqueries.cpp
76–77

Yes, atomically separate if you want, but related because at some point this method was my model (until I switched away from a live query).

I think renaming the argument helps readability because otherwise it "clashes" (in a human brain at least) with the local variable of the same name.

ervin added a comment.Aug 6 2018, 9:23 PM

I think it should be async like the rest, I understand it makes some things more complicated in the case of tooltips, but if we use something else than tooltips to display that information we'll need to update when the backend signals a change anyway.

It's more complicated at two levels. In the implementation and in the usage ;)

Sure.

Let's talk about implementation first.

I need to go up the tree (of tasks) until I get to the project. Right now I do that with the full collection cache at hand, no problem.
IIUC, the live query mechanism will call my predicate with one item at a time, and I get to say "yes, keep this one".

Depends on the query part. It doesn't have to list the whole collection, it could just fetch the single item + ancestors and then return a list containing just the top most ancestor. Your predicate could then just return true if said parent was indeed a project. That fetch should not be expensive since in the current design it'll go through the cache anyway.

But that can't work if the predicate is called for the project before it's called for an intermediate parent task. I won't be able to say "yes this is my project" until I get the intermediate task which is the missing link to my child task.
Unless I'm missing something, I'd say the current framework for live queries isn't adapted to those needs.

I think it's because you're limiting yourself in the fetch part of the query, but that could be anything as long as it returns a list of items in the end.

Still in what I propose I'm not totally clear on change notifications... if an intermediate parent change project, there's currently nothing which would signal a change of project in the child. Note it's a problem already present in the current patch AFAICT.

I could of course still use m_cache->items(item.parentCollection()) in the predicate but that's crazy, right? There's no point in looking in ALL items every time the predicate is called with ONE item, this would amount to doing N times what can be done a single time...

It seems to me that we need a live query that calls (just once, not once per item) a method (one that returns a Project::Ptr, not a bool) whenever the collection has changed.
Using the cache, that method can right away return the project (if found).

That'd be an alternative indeed, maybe a better one even. Requires more work obviously.

It's an excellent start though! Looking forward to it, since we also have the findContexts for tasks it'll "just" be about channeling that information in the various models. :-)

When you say "we also have", do you mean this?

TaskQueries::ContextResult::Ptr TaskQueries::findContexts(Domain::Task::Ptr task) const
 {
     qFatal("Not implemented yet");
     Q_UNUSED(task);
     return ContextResult::Ptr();
 }

That's an interesting concept of "having" :-)

Damn, dunno why I was sure the implementation was already done... :-/

dfaure added a comment.Aug 9 2018, 9:28 AM

Depends on the query part. It doesn't have to list the whole collection, it could just fetch the single item + ancestors and then return a list containing just the top most ancestor. Your predicate could then just return true if said parent was indeed a project. That fetch should not be expensive since in the current design it'll go through the cache anyway.

Ah, I see what you mean. But I'm hitting a new issue, trying to implement this.
There is no clean way to convert a task to a project. I mean, your description above relies on the fact that a project is a specific kind of task, but that's a kcalcore implementation detail that you tried hard to hide in the Domain classes and in AkonadiSerializer. So if my fetch returns tasks, even if my predicate filters for projects, the result of that query is still tasks.
... which the end user code (WorkdayPageModel's tooltip code, for now) then has to convert to Domain::Project somehow. I can probably get there by passing a serializer to that class, and then going task => item => project, but that seems horrible... Do we bite the bullet and add task<->project conversion methods to the serializer (without going via an unnecessary Akonadi::Item)? Or do I give up trying to make this a generic "findTopMostParentForTask" (as you described) and instead make it a findProjectForTask again, only working for projects?

Still in what I propose I'm not totally clear on change notifications... if an intermediate parent change project, there's currently nothing which would signal a change of project in the child. Note it's a problem already present in the current patch AFAICT.

This opens up an even wider question: can a task be in a project that actually belongs to a different akonadi collection? The code currently only searches within the same collection (I *think* I saw that somewhere already, so I took it as a constraint I could rely on). I guess that's ok, given that projects are indeed under collections in the tree? So that means "changing project" potentially means "moving to a different akonadi collection"? Subtasks included? Is there code to handle that?

If such a constraint exists at the akonadi::collection level, then change notifications should work, via "collection modified"... no?

It seems to me that we need a live query that calls (just once, not once per item) a method (one that returns a Project::Ptr, not a bool) whenever the collection has changed.
Using the cache, that method can right away return the project (if found).

That'd be an alternative indeed, maybe a better one even. Requires more work obviously.

Yeah... I don't even see from which angle to attack that one...

dfaure updated this revision to Diff 39806.Aug 15 2018, 7:01 PM

Redesigned to use a live query as requested.

Code sharing between LiveQuery and LiveRelationshipQuery would be more difficult
than we discussed, it turns out that the two are quite different in the end. In fact
I don't see much that can really be shared. At most a provider->clear()...

dfaure updated this revision to Diff 39992.Aug 19 2018, 10:21 AM

Repair the actual tooltips.

The async API doesn't return anything immediately, and the reusing of queries failed
because they were deleted immediately. By keeping the last query around, tooltips work
again because - fortunately - the view doesn't cache anything and calls ToolTipRole
every time the mouse moves by one pixel over an item.

Longer term, we forgot to discuss what the proper GUI for this would be :-)

dfaure updated this revision to Diff 40277.Aug 23 2018, 7:05 AM

Fix crash on removing an item (wrong end condition)

ervin requested changes to this revision.Aug 23 2018, 12:12 PM

Still a few issues left, but it's really looking on the right track and promising. :-)

src/akonadi/akonadilivequeryhelpers.cpp
169

Can we find a better name that reflects that then?

src/akonadi/akonadilivequeryhelpers.h
56

I'd call that "Ancestors" I think. Maybe it's just me but "parent" I expect only direct parents.

src/akonadi/akonaditaskqueries.cpp
89

This is unused

src/domain/livequery.h
270

Should be tested then, right?

tests/units/presentation/workdaypagemodeltest.cpp
170

I guess needs more such things. Also I wonder if we shouldn't have a specific role right away instead of using ToolTipRole for that. I remember we discussed changing the delegate to make that appear in the list directly (doesn't have to be in that same commit of course).

This revision now requires changes to proceed.Aug 23 2018, 12:12 PM
dfaure marked 7 inline comments as done.Sep 14 2018, 7:06 AM
dfaure added inline comments.
src/akonadi/akonadilivequeryhelpers.cpp
169

OK, renamed to LiveQueryHelpers::fetchTaskAndAncestors.

src/domain/livequery.h
270

It's tested by akonaditaskqueriestest.cpp but OK, you like tests at all levels :-)

Done.

tests/units/presentation/workdaypagemodeltest.cpp
170

Done, but we might want to generalize the role name later, depending on what we want to do in other pages...

dfaure updated this revision to Diff 41599.Sep 14 2018, 7:07 AM
dfaure marked 3 inline comments as done.

Made all requested changes

ervin accepted this revision.Oct 18 2018, 2:20 PM
This revision is now accepted and ready to land.Oct 18 2018, 2:20 PM
This revision was automatically updated to reflect the committed changes.