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
1=== modified file 'CHANGELOG'
2--- CHANGELOG 2012-11-01 10:09:06 +0000
3+++ CHANGELOG 2012-11-01 11:54:31 +0000
4@@ -54,6 +54,8 @@
5 * Fix for bug #1036955: Due date is not preselected when start date is filled, by Steve Scheel
6 * Fix for bug #1045036: Slovak Translation Updated by Slavko
7 * 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
8+ * Fix for bug #1038662: Undefined due dates in subtasks should always stay undefined and displayed as such in the editor
9+ * Fix for bug #1036695: Date constraints after drag and drop not applied
10
11 2012-02-13 Getting Things GNOME! 0.2.9
12 * Big refractorization of code, now using liblarch
13
14=== modified file 'GTG/core/task.py'
15--- GTG/core/task.py 2012-11-01 10:09:06 +0000
16+++ GTG/core/task.py 2012-11-01 11:54:31 +0000
17@@ -246,68 +246,177 @@
18 def set_modified(self, modified):
19 self.last_modified = modified
20
21- def set_due_date(self, fulldate):
22+ def recursive_sync(self):
23+ """Recursively sync the task and all task children. Defined"""
24+ self.sync()
25+ for sub_id in self.children:
26+ sub = self.req.get_task(sub_id)
27+ sub.recursive_sync()
28+
29+ # ABOUT DUE DATES
30+ #
31+ # PLEASE READ THIS: although simple in appearance, handling task dates can
32+ # actually be subtle. Take the time to understand this if you plan to work
33+ # on the methods below.
34+ #
35+ # Due date is the date at which a task must be accomplished. Constraints
36+ # exist between a task's due date and its ancestor/children's due dates.
37+ #
38+ # Date constraints
39+ #
40+ # Those are the following:
41+ # - children of a task cannot have a task due date that happens later than
42+ # the task's due date
43+ # - ancestors of a task cannot have a due that happens before the
44+ # task's due date (this is the reverse constraint from the first one)
45+ # - a task's start date cannot happen later than this task's due date
46+ #
47+ # Tasks with undefined or fuzzy due dates
48+ #
49+ # Task with no due date (="undefined" tasks) or tasks with fuzzy start/due
50+ # dates are not subject to constraints. Furthermore, they are "transparent".
51+ # Meaning that they let the constraints coming from their children/parents
52+ # pass through them. So, for instance, a children of a task with an
53+ # undefined or fuzzy task would be constrained by this latter task's
54+ # ancestors. Equally, the an ancestor from the same undefined/fuzzy task
55+ # would be constrained by the children due dates.
56+ #
57+ # Updating a task due date
58+ #
59+ # Whenever a task due date is changed, all ancestor/chldren of this task
60+ # *must* be updated according to the constraining rules. As said above,
61+ # constraints must go through tasks with undefined/fuzzy due dates too!
62+ #
63+ # Undefined/fuzzy task dates are NEVER to be updated. They are not sensitive
64+ # to constraint. If you want to now what constraint there is on this task's
65+ # due date though, you can obtain it by using the get_due_date_constraint
66+ # method.
67+
68+ def set_due_date(self, new_duedate):
69 """Defines the task's due date."""
70- def get_due_date_constraint():
71- """ Returns the most urgent due date constraint, following
72- parents' due dates. Return Date.no_date() if no constraint
73- is applied. """
74- cur_date = Date.no_date()
75- for par_id in self.get_parents():
76- par = self.req.get_task(par_id)
77- if par.get_due_date() != Date.no_date() and \
78- par.get_due_date() < cur_date:
79- cur_date = par.get_due_date()
80- return cur_date
81- fulldate_obj = Date(fulldate) # caching the conversion
82- self.due_date = fulldate_obj
83- # if the task's start date happens later than the
84- # new due date, we update it
85- # (except for fuzzy dates)
86- if self.get_start_date() != Date.no_date() and \
87- not fulldate_obj.is_fuzzy() and \
88- self.get_start_date() > fulldate_obj:
89- self.set_start_date(fulldate)
90- if fulldate_obj != Date.no_date():
91- # if the parent's due date happens before the task's new
92- # due date, we update it
93- for par_id in self.parents:
94- par = self.req.get_task(par_id)
95- if par.get_due_date() != Date.no_date() and \
96- par.get_due_date() < fulldate_obj:
97- par.set_due_date(fulldate)
98- # the current task being one of its children's parents, we must
99- # apply the constraints on their due/start dates as well
100- for sub_id in self.children:
101- sub = self.req.get_task(sub_id)
102- # child's due date is not set, we use the task's new
103- # due date
104- if sub.get_due_date() == Date.no_date():
105- sub.set_due_date(fulldate)
106- # child's due date happens later than the task's: we
107+
108+ def __get_defined_parent_list(task):
109+ """Recursively fetch a list of parents that have a defined due date
110+ which is not fuzzy"""
111+ parent_list = []
112+ for par_id in task.parents:
113+ par = self.req.get_task(par_id)
114+ if par.get_due_date() == Date.no_date() or \
115+ ( par.get_due_date() != Date.no_date() and \
116+ par.get_due_date().is_fuzzy() ):
117+ parent_list += __get_defined_parent_list(par)
118+ else:
119+ parent_list.append(par)
120+ return parent_list
121+
122+ def __get_defined_child_list(task):
123+ """Recursively fetch a list of children that have a defined due date
124+ which is not fuzzy"""
125+ child_list = []
126+ for child_id in task.children:
127+ child = self.req.get_task(child_id)
128+ if child.get_due_date() == Date.no_date() or \
129+ ( child.get_due_date() != Date.no_date() and \
130+ child.get_due_date().is_fuzzy() ):
131+ child_list += __get_defined_child_list(child)
132+ else:
133+ child_list.append(child)
134+ return child_list
135+
136+ old_due_date = self.due_date
137+ new_duedate_obj = Date(new_duedate) # caching the conversion
138+ self.due_date = new_duedate_obj
139+ # If the new date is fuzzy or undefined, we don't update related tasks
140+ if not new_duedate_obj.is_fuzzy() and new_duedate_obj != Date.no_date():
141+ # if the task's start date happens later than the
142+ # new due date, we update it (except for fuzzy dates)
143+ if self.get_start_date() != Date.no_date() and \
144+ not self.get_start_date().is_fuzzy() and \
145+ self.get_start_date() > new_duedate_obj:
146+ self.set_start_date(new_duedate)
147+ # if some ancestors' due dates happen before the task's new
148+ # due date, we update them (except for fuzzy dates)
149+ for par in __get_defined_parent_list(self):
150+ if par.get_due_date() < new_duedate_obj:
151+ par.set_due_date(new_duedate)
152+ # we must apply the constraints to the defined & non-fuzzy children
153+ # as well
154+ for sub in __get_defined_child_list(self):
155+ sub_duedate = sub.get_due_date()
156+ # if the child's due date happens later than the task's: we
157 # update it to the task's new due date
158- # (= the new most restrictive)
159- if sub.get_due_date() != Date.no_date() and \
160- sub.get_due_date() > fulldate_obj:
161- sub.set_due_date(fulldate)
162+ if sub_duedate > new_duedate_obj:
163+ sub.set_due_date(new_duedate)
164 # if the child's start date happens later than
165 # the task's new due date, we update it
166- # (except for fuzzy dates)
167- if sub.get_start_date() != Date.no_date() and \
168- not fulldate_obj.is_fuzzy() and \
169- sub.get_start_date() > fulldate_obj:
170- sub.set_start_date(fulldate)
171- else:
172- self.due_date = get_due_date_constraint()
173- self.sync()
174+ # (except for fuzzy start dates)
175+ sub_startdate = sub.get_start_date()
176+ if sub_startdate != Date.no_date() and \
177+ not sub_startdate.is_fuzzy() and \
178+ sub_startdate > new_duedate_obj:
179+ sub.set_start_date(new_duedate)
180+ # If the date changed, we notify the change for the children since the
181+ # constraints might have changed
182+ if old_due_date != new_duedate_obj:
183+ self.recursive_sync()
184
185 def get_due_date(self):
186 """ Returns the due date, which always respects all constraints """
187 return self.due_date
188
189+ def get_due_date_constraint(self):
190+ """ Returns the most urgent due date constraint, following
191+ parents' due dates. Return Date.no_date() if no constraint
192+ is applied. """
193+ # Check out for constraints depending on date definition/fuzziness.
194+ strongest_const_date = self.due_date
195+ if strongest_const_date == Date.no_date() or \
196+ ( strongest_const_date != Date.no_date() and \
197+ strongest_const_date.is_fuzzy() ):
198+ for par_id in self.parents:
199+ par = self.req.get_task(par_id)
200+ par_duedate = par.get_due_date()
201+ # if parent date is undefined or fuzzy, look further up
202+ if par_duedate == Date.no_date() or \
203+ par_duedate.is_fuzzy():
204+ par_duedate = par.get_due_date_constraint()
205+ # if par_duedate is still undefined/fuzzy, all parents' due
206+ # dates are undefined or fuzzy: strongest_const_date is then
207+ # the best choice so far, we don't update it.
208+ if par_duedate == Date.no_date() or \
209+ par_duedate.is_fuzzy():
210+ continue
211+ # par_duedate is not undefined/fuzzy. If strongest_const_date is
212+ # still undefined or fuzzy, parent_duedate is the best choice.
213+ if strongest_const_date == Date.no_date() or \
214+ ( strongest_const_date != Date.no_date() and \
215+ strongest_const_date.is_fuzzy() ):
216+ strongest_const_date = par_duedate
217+ continue
218+ # strongest_const_date and par_date are defined and not fuzzy:
219+ # we compare the dates
220+ if par_duedate < strongest_const_date:
221+ strongest_const_date = par_duedate
222+ return strongest_const_date
223+
224+ # ABOUT START DATE
225+ #
226+ # Start date is the date at which the user has decided to work or consider
227+ # working on this task.
228+ #
229+ # The only constraint applied to start dates is that start dates cannot
230+ # happen later than the task due date.
231+ #
232+ # The task due date (and any constrained relatives) is updated if a new
233+ # task start date is chosen that does not respect this rule.
234+ #
235+ # Undefined/fizzy start dates don't constraint the task due date.
236+
237 def set_start_date(self, fulldate):
238 self.start_date = Date(fulldate)
239 if Date(fulldate) != Date.no_date() and \
240+ not Date(fulldate).is_fuzzy() and \
241+ self.due_date != Date.no_date() and \
242 not self.due_date.is_fuzzy() and \
243 Date(fulldate) > self.due_date:
244 self.set_due_date(fulldate)
245@@ -316,6 +425,12 @@
246 def get_start_date(self):
247 return self.start_date
248
249+ # ABOUT CLOSED DATE
250+ #
251+ # Closed date is the date at which the task has been closed (done or
252+ # dismissed). Closed date is not constrained and doesn't constrain other
253+ # dates.
254+
255 def set_closed_date(self, fulldate):
256 self.closed_date = Date(fulldate)
257 self.sync()
258@@ -476,6 +591,20 @@
259 """
260 return self.req.get_task(tid)
261
262+ def set_parent(self, parent_id):
263+ """Update the task's parent. Refresh due date constraints."""
264+ TreeNode.set_parent(self, parent_id)
265+ if parent_id is not None:
266+ par = self.req.get_task(parent_id)
267+ par_duedate = par.get_due_date_constraint()
268+ if par_duedate != Date.no_date() and \
269+ not par_duedate.is_fuzzy() and \
270+ self.due_date != Date.no_date() and \
271+ not self.due_date.is_fuzzy() and \
272+ par_duedate < self.due_date:
273+ self.set_due_date(par_duedate)
274+ self.recursive_sync()
275+
276 def set_attribute(self, att_name, att_value, namespace=""):
277 """Set an arbitrary attribute.
278
279
280=== modified file 'GTG/gtk/browser/treeview_factory.py'
281--- GTG/gtk/browser/treeview_factory.py 2012-10-31 15:43:32 +0000
282+++ GTG/gtk/browser/treeview_factory.py 2012-11-01 11:54:31 +0000
283@@ -122,7 +122,12 @@
284 return node.get_start_date().to_readable_string()
285
286 def task_duedate_column(self,node):
287- return node.get_due_date().to_readable_string()
288+ # We show the most constraining due date for task with no due dates.
289+ if node.get_due_date() == Date.no_date():
290+ return node.get_due_date_constraint().to_readable_string()
291+ else:
292+ # Other tasks show their due date (which *can* be fuzzy)
293+ return node.get_due_date().to_readable_string()
294
295 def task_cdate_column(self,node):
296 return node.get_closed_date().to_readable_string()
297@@ -154,6 +159,10 @@
298 elif para == 'due':
299 t1 = task1.get_due_date()
300 t2 = task2.get_due_date()
301+ if t1 == Date.no_date():
302+ t1 = task1.get_due_date_constraint()
303+ if t2 == Date.no_date():
304+ t2 = task2.get_due_date_constraint()
305 elif para == 'closed':
306 t1 = task1.get_closed_date()
307 t2 = task2.get_closed_date()

Subscribers

People subscribed via source and target branches

to status/vote changes: