GTG

Merge lp:~bertrand-rousseau/gtg/bugfix-1038662-dont-touch-due-date into lp:~gtg/gtg/old-trunk

Proposed by Bertrand Rousseau
Status: Merged
Merged at revision: 1237
Proposed branch: lp:~bertrand-rousseau/gtg/bugfix-1038662-dont-touch-due-date
Merge into: lp:~gtg/gtg/old-trunk
Diff against target: 307 lines (+191/-51)
3 files modified
CHANGELOG (+2/-0)
GTG/core/task.py (+179/-50)
GTG/gtk/browser/treeview_factory.py (+10/-1)
To merge this branch: bzr merge lp:~bertrand-rousseau/gtg/bugfix-1038662-dont-touch-due-date
Reviewer Review Type Date Requested Status
Izidor Matušov Approve
Bertrand Rousseau (community) Needs Resubmitting
Lionel Dricot (community) Needs Information
Review via email: mp+121929@code.launchpad.net

Description of the change

I changed the due date inheritance mechanism so that due date of children is not altered if it's not set.

Since I can't come up with a good UI to display the constraining date in the task editor, I think the best solution is to leave the due date selector widget blank and not display anything about a constraining date.

To post a comment you must log in.
1233. By Bertrand Rousseau

Update CHANGELOG

Revision history for this message
Lionel Dricot (ploum-deactivatedaccount) wrote :

I've tested and I see a problem :

1. Set the parent due date to "soon"

2. Open the children and set due date to "now".

Everything is fine.

3. Now set child due date to "later": immediately, the parent due date is changed too.

Is that what we really want ?

review: Needs Information
Revision history for this message
Izidor Matušov (izidor) wrote :

I don't think the case Lionel talked about is solved correctly. Fuzzy dates are and should be fuzzy: you can put that something is due "now", "soon" or "someday" although the parent is due tomorrow. The user shouldn't care about what soon means - is it 7 or 14 days? Otherwise she might want to configure it => added complexity. Fuzzy dates should be transparent and not constrain any parent or child.

review: Needs Fixing (code)
1234. By Bertrand Rousseau

Merge trunk and update set_due_date/get_constrained_date to avoid applying constrains to fuzzy dates. Also fix a bug: constrained were not applied through undefined tasks.

Revision history for this message
Bertrand Rousseau (bertrand-rousseau) wrote :

Izidor> Constraints are now not applied anymore to tasks with fuzzy due dates. However, it's not clear for how the task list should interpret some constraints:

If have a task set to "Tomorrow", and a child set to "Soon", what should be displayed for the child task in the task list? "Soon" or "Tomorrow"? IMHO, "Tomorrow" is better since it's more precise. (It's currently implemented that way: I consider precise dates to have the priority on fuzzy dates.)

Another situation: if I have a task set to "Now", and a child set to "Soon", what should be shown for the child? "Now"? Currently, I don't consider constraints between fuzzy dates, but it might make sense. If so, then it would also make sense to update related task due dates even when they are fuzzy, but it's in opposition to Lionel's comment.

And finally, if I have a task with "Soon" and a child with an undefined due date, should I display "Soon" as well for the child? Currently, I don't do this, but it might make sense too since that's what's done for precise dates.

Revision history for this message
Izidor Matušov (izidor) wrote :

It is hard to say what is right, we should define it on our own. I am proposing the following semantics:

Fuzzy dates "now" and "soon" are fully transparent. It means that a task can have now or soon althoug the parent has a precise date like tomorrow. (They are fuzzy!!!) Children would heritate the fuzzy due date. However, when you put precise date into ancestors, it complains regular limitations. (Fuzzy dates are transparent). Look at the following (valid) structure:

a (tomorrow)
-b (soon)
--c (now)
----d (soon)
-----e (today)

I would say that someday is not valid for any task with parent because you already have deadline and it is something more precise than someday. It means you would always rewrite date.

What do you say?

Revision history for this message
Bertrand Rousseau (bertrand-rousseau) wrote :

I agree, it is quite hard. For the record, I don't clearly understand
the use case for fuzzy dates and if it were for me alone, I would
remove that feature (at least from core), since I don't really think it
is necessary to GTG. But that's another discussion.

I don't really like the idea of sticking too much to the semantics.
Indeed, since fuzzy dates are fuzzy, everyone might like to interpret
things his/her way. So I'd prefer to leave the complete liberty of
interpretation to the user here, even if it could appear incompatible
to have an ancestor due for "now", and a child due for "someday".

So, I suggest to make ALL fuzzy dates transparent (even "someday"), and
to NOT check for consistency among fuzzy dates (total transparency).
And finally, since fuzzy dates are used at the explicit request of a
user to make those tasks stand out among the others, I propose to keep
showing fuzzy dates instead of precise dates even when an ancestor has
a precise due date.

So all in all, it would correspond to your suggestion and example, with
the addition of "someday".

I think that would allow to give a clear-ish definition of fuzzy dates
to the user: "fuzzy dates are there to provide you with some liberty in
assigning due dates to tasks (until you can clearly decide, for
instance). They don't follow and don't impose any constraining rules
themselves, and they let constaints from parents and children go
through them untouched".

If people seems okay with that (Lionel? Any opinion on this?) That's
what I'll implement.

Bertrand

On Mon 22 Oct 2012 10:47:20 PM CEST, Izidor Matušov wrote:
> It is hard to say what is right, we should define it on our own. I am proposing the following semantics:
>
> Fuzzy dates "now" and "soon" are fully transparent. It means that a task can have now or soon althoug the parent has a precise date like tomorrow. (They are fuzzy!!!) Children would heritate the fuzzy due date. However, when you put precise date into ancestors, it complains regular limitations. (Fuzzy dates are transparent). Look at the following (valid) structure:
>
> a (tomorrow)
> -b (soon)
> --c (now)
> ----d (soon)
> -----e (today)
>
> I would say that someday is not valid for any task with parent because you already have deadline and it is something more precise than someday. It means you would always rewrite date.
>
> What do you say?

--
Bertrand Rousseau
<email address hidden>

Revision history for this message
Izidor Matušov (izidor) wrote :

I agree, let's go with the fully transparent fuzzy dates!

1235. By Bertrand Rousseau

Update due date update policy.

Updated the due date update policy as discussed here in launchpad [1]:
ALL fuzzy dates transparent (even "someday"), and we DONT check for consistency
among fuzzy dates (total transparency). And finally, since fuzzy dates are used
at the explicit request of a user to make those tasks stand out among the
others, we keep showing fuzzy dates instead of precise dates even when an
ancestor has a precise due date.

[1] https://code.launchpad.net/~bertrand-rousseau/gtg/bugfix-1038662-dont-touch-due-date/+merge/121929/comments/282054.

Revision history for this message
Bertrand Rousseau (bertrand-rousseau) wrote :

I just updated the code to reflect what we discussed here. Please test the branch and tell me if it is ok!

1236. By Bertrand Rousseau

Ooops: left out a small bug, task list was not displaying constraining due date for undefined but constrained child task

1237. By Bertrand Rousseau

Fix for bug #1036695: Date constraints after drag and drop not applied

Revision history for this message
Bertrand Rousseau (bertrand-rousseau) wrote :

I also inspected the code for bug #1036695: "Date constraints after drag and drop not applied" and found a fix. To reduce review work I integrate this fix directly in this merge review.

1238. By Bertrand Rousseau

Merge trunk

1239. By Bertrand Rousseau

Oops: accidently removed my fix for constraints update after DnD while merging with the trunk

Revision history for this message
Izidor Matušov (izidor) wrote :

When I drag a child task to make it a stand alone task, I get the following error:

Problem with dragging: 'NoneType' object has no attribute 'get_due_date_constraint'

review: Needs Fixing
Revision history for this message
Izidor Matušov (izidor) wrote :

Otherwise the patch seems good to me and it works good.

1240. By Bertrand Rousseau

Fix bug in set_parent

Revision history for this message
Bertrand Rousseau (bertrand-rousseau) wrote :

Fix the bug found by Izidor ('NoneType' object has no attribute 'get_due_date_constraint').

FYI, It seems that this patch has a negative impact on performances (since constraining due dates are computed dynamically for fuzzy/undefined due date). On "normal" dataset, it is about 3% slower (which represents only 30ms on my laptop), but on big datasets (Bryce-ish datasets), it can go up to 10% (=3s on my laptop). I tried to fix this by updating the value only when required and caching it, but I can't succeed with this approach since during boot time it seems that the method "set_parent" is not used to update the task hierarchy. Consequently, I can't catch all task relations updates and the due date constraints are not correctly set up. I have no idea how to catch task relations updates any other way than that, so I don't know how to fix this. Now, since the impact is quite limited, I guess we could still merge the patch.

review: Needs Resubmitting
Revision history for this message
Izidor Matušov (izidor) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CHANGELOG'
--- CHANGELOG 2012-11-01 10:09:06 +0000
+++ CHANGELOG 2012-11-01 11:54:31 +0000
@@ -54,6 +54,8 @@
54 * Fix for bug #1036955: Due date is not preselected when start date is filled, by Steve Scheel54 * Fix for bug #1036955: Due date is not preselected when start date is filled, by Steve Scheel
55 * Fix for bug #1045036: Slovak Translation Updated by Slavko55 * Fix for bug #1045036: Slovak Translation Updated by Slavko
56 * Remove use of liblarch's "transparent" concept (since it's been removed from liblarch), fixes bugs #1001962, #1001962, #1069257, #1069963: intermediary tags, counter initialization, and regressions caused by initial versions of the patch56 * Remove use of liblarch's "transparent" concept (since it's been removed from liblarch), fixes bugs #1001962, #1001962, #1069257, #1069963: intermediary tags, counter initialization, and regressions caused by initial versions of the patch
57 * Fix for bug #1038662: Undefined due dates in subtasks should always stay undefined and displayed as such in the editor
58 * Fix for bug #1036695: Date constraints after drag and drop not applied
5759
582012-02-13 Getting Things GNOME! 0.2.9602012-02-13 Getting Things GNOME! 0.2.9
59 * Big refractorization of code, now using liblarch61 * Big refractorization of code, now using liblarch
6062
=== modified file 'GTG/core/task.py'
--- GTG/core/task.py 2012-11-01 10:09:06 +0000
+++ GTG/core/task.py 2012-11-01 11:54:31 +0000
@@ -246,68 +246,177 @@
246 def set_modified(self, modified):246 def set_modified(self, modified):
247 self.last_modified = modified247 self.last_modified = modified
248248
249 def set_due_date(self, fulldate):249 def recursive_sync(self):
250 """Recursively sync the task and all task children. Defined"""
251 self.sync()
252 for sub_id in self.children:
253 sub = self.req.get_task(sub_id)
254 sub.recursive_sync()
255
256 # ABOUT DUE DATES
257 #
258 # PLEASE READ THIS: although simple in appearance, handling task dates can
259 # actually be subtle. Take the time to understand this if you plan to work
260 # on the methods below.
261 #
262 # Due date is the date at which a task must be accomplished. Constraints
263 # exist between a task's due date and its ancestor/children's due dates.
264 #
265 # Date constraints
266 #
267 # Those are the following:
268 # - children of a task cannot have a task due date that happens later than
269 # the task's due date
270 # - ancestors of a task cannot have a due that happens before the
271 # task's due date (this is the reverse constraint from the first one)
272 # - a task's start date cannot happen later than this task's due date
273 #
274 # Tasks with undefined or fuzzy due dates
275 #
276 # Task with no due date (="undefined" tasks) or tasks with fuzzy start/due
277 # dates are not subject to constraints. Furthermore, they are "transparent".
278 # Meaning that they let the constraints coming from their children/parents
279 # pass through them. So, for instance, a children of a task with an
280 # undefined or fuzzy task would be constrained by this latter task's
281 # ancestors. Equally, the an ancestor from the same undefined/fuzzy task
282 # would be constrained by the children due dates.
283 #
284 # Updating a task due date
285 #
286 # Whenever a task due date is changed, all ancestor/chldren of this task
287 # *must* be updated according to the constraining rules. As said above,
288 # constraints must go through tasks with undefined/fuzzy due dates too!
289 #
290 # Undefined/fuzzy task dates are NEVER to be updated. They are not sensitive
291 # to constraint. If you want to now what constraint there is on this task's
292 # due date though, you can obtain it by using the get_due_date_constraint
293 # method.
294
295 def set_due_date(self, new_duedate):
250 """Defines the task's due date."""296 """Defines the task's due date."""
251 def get_due_date_constraint():297
252 """ Returns the most urgent due date constraint, following298 def __get_defined_parent_list(task):
253 parents' due dates. Return Date.no_date() if no constraint299 """Recursively fetch a list of parents that have a defined due date
254 is applied. """300 which is not fuzzy"""
255 cur_date = Date.no_date()301 parent_list = []
256 for par_id in self.get_parents():302 for par_id in task.parents:
257 par = self.req.get_task(par_id)303 par = self.req.get_task(par_id)
258 if par.get_due_date() != Date.no_date() and \304 if par.get_due_date() == Date.no_date() or \
259 par.get_due_date() < cur_date:305 ( par.get_due_date() != Date.no_date() and \
260 cur_date = par.get_due_date()306 par.get_due_date().is_fuzzy() ):
261 return cur_date307 parent_list += __get_defined_parent_list(par)
262 fulldate_obj = Date(fulldate) # caching the conversion308 else:
263 self.due_date = fulldate_obj309 parent_list.append(par)
264 # if the task's start date happens later than the 310 return parent_list
265 # new due date, we update it311
266 # (except for fuzzy dates)312 def __get_defined_child_list(task):
267 if self.get_start_date() != Date.no_date() and \313 """Recursively fetch a list of children that have a defined due date
268 not fulldate_obj.is_fuzzy() and \314 which is not fuzzy"""
269 self.get_start_date() > fulldate_obj:315 child_list = []
270 self.set_start_date(fulldate)316 for child_id in task.children:
271 if fulldate_obj != Date.no_date():317 child = self.req.get_task(child_id)
272 # if the parent's due date happens before the task's new318 if child.get_due_date() == Date.no_date() or \
273 # due date, we update it319 ( child.get_due_date() != Date.no_date() and \
274 for par_id in self.parents:320 child.get_due_date().is_fuzzy() ):
275 par = self.req.get_task(par_id)321 child_list += __get_defined_child_list(child)
276 if par.get_due_date() != Date.no_date() and \322 else:
277 par.get_due_date() < fulldate_obj:323 child_list.append(child)
278 par.set_due_date(fulldate)324 return child_list
279 # the current task being one of its children's parents, we must325
280 # apply the constraints on their due/start dates as well326 old_due_date = self.due_date
281 for sub_id in self.children:327 new_duedate_obj = Date(new_duedate) # caching the conversion
282 sub = self.req.get_task(sub_id)328 self.due_date = new_duedate_obj
283 # child's due date is not set, we use the task's new 329 # If the new date is fuzzy or undefined, we don't update related tasks
284 # due date330 if not new_duedate_obj.is_fuzzy() and new_duedate_obj != Date.no_date():
285 if sub.get_due_date() == Date.no_date():331 # if the task's start date happens later than the
286 sub.set_due_date(fulldate)332 # new due date, we update it (except for fuzzy dates)
287 # child's due date happens later than the task's: we333 if self.get_start_date() != Date.no_date() and \
334 not self.get_start_date().is_fuzzy() and \
335 self.get_start_date() > new_duedate_obj:
336 self.set_start_date(new_duedate)
337 # if some ancestors' due dates happen before the task's new
338 # due date, we update them (except for fuzzy dates)
339 for par in __get_defined_parent_list(self):
340 if par.get_due_date() < new_duedate_obj:
341 par.set_due_date(new_duedate)
342 # we must apply the constraints to the defined & non-fuzzy children
343 # as well
344 for sub in __get_defined_child_list(self):
345 sub_duedate = sub.get_due_date()
346 # if the child's due date happens later than the task's: we
288 # update it to the task's new due date347 # update it to the task's new due date
289 # (= the new most restrictive)348 if sub_duedate > new_duedate_obj:
290 if sub.get_due_date() != Date.no_date() and \349 sub.set_due_date(new_duedate)
291 sub.get_due_date() > fulldate_obj:
292 sub.set_due_date(fulldate)
293 # if the child's start date happens later than350 # if the child's start date happens later than
294 # the task's new due date, we update it351 # the task's new due date, we update it
295 # (except for fuzzy dates)352 # (except for fuzzy start dates)
296 if sub.get_start_date() != Date.no_date() and \353 sub_startdate = sub.get_start_date()
297 not fulldate_obj.is_fuzzy() and \354 if sub_startdate != Date.no_date() and \
298 sub.get_start_date() > fulldate_obj:355 not sub_startdate.is_fuzzy() and \
299 sub.set_start_date(fulldate)356 sub_startdate > new_duedate_obj:
300 else:357 sub.set_start_date(new_duedate)
301 self.due_date = get_due_date_constraint()358 # If the date changed, we notify the change for the children since the
302 self.sync()359 # constraints might have changed
360 if old_due_date != new_duedate_obj:
361 self.recursive_sync()
303362
304 def get_due_date(self):363 def get_due_date(self):
305 """ Returns the due date, which always respects all constraints """364 """ Returns the due date, which always respects all constraints """
306 return self.due_date365 return self.due_date
307366
367 def get_due_date_constraint(self):
368 """ Returns the most urgent due date constraint, following
369 parents' due dates. Return Date.no_date() if no constraint
370 is applied. """
371 # Check out for constraints depending on date definition/fuzziness.
372 strongest_const_date = self.due_date
373 if strongest_const_date == Date.no_date() or \
374 ( strongest_const_date != Date.no_date() and \
375 strongest_const_date.is_fuzzy() ):
376 for par_id in self.parents:
377 par = self.req.get_task(par_id)
378 par_duedate = par.get_due_date()
379 # if parent date is undefined or fuzzy, look further up
380 if par_duedate == Date.no_date() or \
381 par_duedate.is_fuzzy():
382 par_duedate = par.get_due_date_constraint()
383 # if par_duedate is still undefined/fuzzy, all parents' due
384 # dates are undefined or fuzzy: strongest_const_date is then
385 # the best choice so far, we don't update it.
386 if par_duedate == Date.no_date() or \
387 par_duedate.is_fuzzy():
388 continue
389 # par_duedate is not undefined/fuzzy. If strongest_const_date is
390 # still undefined or fuzzy, parent_duedate is the best choice.
391 if strongest_const_date == Date.no_date() or \
392 ( strongest_const_date != Date.no_date() and \
393 strongest_const_date.is_fuzzy() ):
394 strongest_const_date = par_duedate
395 continue
396 # strongest_const_date and par_date are defined and not fuzzy:
397 # we compare the dates
398 if par_duedate < strongest_const_date:
399 strongest_const_date = par_duedate
400 return strongest_const_date
401
402 # ABOUT START DATE
403 #
404 # Start date is the date at which the user has decided to work or consider
405 # working on this task.
406 #
407 # The only constraint applied to start dates is that start dates cannot
408 # happen later than the task due date.
409 #
410 # The task due date (and any constrained relatives) is updated if a new
411 # task start date is chosen that does not respect this rule.
412 #
413 # Undefined/fizzy start dates don't constraint the task due date.
414
308 def set_start_date(self, fulldate):415 def set_start_date(self, fulldate):
309 self.start_date = Date(fulldate)416 self.start_date = Date(fulldate)
310 if Date(fulldate) != Date.no_date() and \417 if Date(fulldate) != Date.no_date() and \
418 not Date(fulldate).is_fuzzy() and \
419 self.due_date != Date.no_date() and \
311 not self.due_date.is_fuzzy() and \420 not self.due_date.is_fuzzy() and \
312 Date(fulldate) > self.due_date:421 Date(fulldate) > self.due_date:
313 self.set_due_date(fulldate)422 self.set_due_date(fulldate)
@@ -316,6 +425,12 @@
316 def get_start_date(self):425 def get_start_date(self):
317 return self.start_date426 return self.start_date
318427
428 # ABOUT CLOSED DATE
429 #
430 # Closed date is the date at which the task has been closed (done or
431 # dismissed). Closed date is not constrained and doesn't constrain other
432 # dates.
433
319 def set_closed_date(self, fulldate):434 def set_closed_date(self, fulldate):
320 self.closed_date = Date(fulldate)435 self.closed_date = Date(fulldate)
321 self.sync()436 self.sync()
@@ -476,6 +591,20 @@
476 """591 """
477 return self.req.get_task(tid)592 return self.req.get_task(tid)
478593
594 def set_parent(self, parent_id):
595 """Update the task's parent. Refresh due date constraints."""
596 TreeNode.set_parent(self, parent_id)
597 if parent_id is not None:
598 par = self.req.get_task(parent_id)
599 par_duedate = par.get_due_date_constraint()
600 if par_duedate != Date.no_date() and \
601 not par_duedate.is_fuzzy() and \
602 self.due_date != Date.no_date() and \
603 not self.due_date.is_fuzzy() and \
604 par_duedate < self.due_date:
605 self.set_due_date(par_duedate)
606 self.recursive_sync()
607
479 def set_attribute(self, att_name, att_value, namespace=""):608 def set_attribute(self, att_name, att_value, namespace=""):
480 """Set an arbitrary attribute.609 """Set an arbitrary attribute.
481610
482611
=== modified file 'GTG/gtk/browser/treeview_factory.py'
--- GTG/gtk/browser/treeview_factory.py 2012-10-31 15:43:32 +0000
+++ GTG/gtk/browser/treeview_factory.py 2012-11-01 11:54:31 +0000
@@ -122,7 +122,12 @@
122 return node.get_start_date().to_readable_string()122 return node.get_start_date().to_readable_string()
123 123
124 def task_duedate_column(self,node):124 def task_duedate_column(self,node):
125 return node.get_due_date().to_readable_string()125 # We show the most constraining due date for task with no due dates.
126 if node.get_due_date() == Date.no_date():
127 return node.get_due_date_constraint().to_readable_string()
128 else:
129 # Other tasks show their due date (which *can* be fuzzy)
130 return node.get_due_date().to_readable_string()
126 131
127 def task_cdate_column(self,node):132 def task_cdate_column(self,node):
128 return node.get_closed_date().to_readable_string()133 return node.get_closed_date().to_readable_string()
@@ -154,6 +159,10 @@
154 elif para == 'due':159 elif para == 'due':
155 t1 = task1.get_due_date()160 t1 = task1.get_due_date()
156 t2 = task2.get_due_date()161 t2 = task2.get_due_date()
162 if t1 == Date.no_date():
163 t1 = task1.get_due_date_constraint()
164 if t2 == Date.no_date():
165 t2 = task2.get_due_date_constraint()
157 elif para == 'closed':166 elif para == 'closed':
158 t1 = task1.get_closed_date()167 t1 = task1.get_closed_date()
159 t2 = task2.get_closed_date()168 t2 = task2.get_closed_date()

Subscribers

People subscribed via source and target branches

to status/vote changes: