44from math import ceil
55from datetime import datetime , timedelta
66
7+ from colorama import Style
8+
79from . import const
810
911def get_org_files (rcfile ):
@@ -110,27 +112,49 @@ def format_inline(str_, reset='normal'):
110112
111113 return str_
112114
115+ #===============================================================================
113116# Main function applying styles to a line's dict object
114- #---------------------------------------
115- def colorize (dict_ ):
117+ #===============================================================================
118+ def colorize (dict_ , ** kwargs ):
116119 """Apply ANSI sequences to specific dictionary entries.
117120
118- The input dictionary should be of the same format as those returned by
119- 'tree.OrgNode.parse()'.
121+ The input dictionary should have the following keys:
122+ - level (not affected)
123+ - todostate (TODO, DONE, etc.)
124+ - text
125+ - num_tasks
126+ - date_one ('%A %d %b' for "agenda" mode, '<%Y-%m-%d %a>' otherwise)
127+ - tag
128+ - date_two ('Scheduled:', 'Deadline:', or 'In X d.:' for "agenda")
129+ - category
130+ - days (int.; the # of days from today to duedate)
120131 """
121132 styles = const .styles
122133 tagtype = 'tag'
123134 if re .search ('urgent' , dict_ ['tag' ], re .IGNORECASE ):
124135 tagtype = 'urgent'
125136
126- # Different styles for different todo states
137+ # Update "todostate" (2) element
138+ #-------------------------------------------------------
127139 state = dict_ ['todostate' ].strip ().lower ()
128- dict_ .update (todostate = styles [state ] + dict_ ['todostate' ].strip () + styles ['normal' ])
140+ if state != '' :
141+ dict_ .update (todostate = styles [state ] + dict_ ['todostate' ].strip () + styles ['normal' ])
142+ if dict_ ['days' ] <= 0 :
143+ dict_ .update (todostate = Style .BRIGHT + dict_ ['todostate' ])
129144
145+ # Update "date_two" (7) element
146+ #-------------------------------------------------------
130147 # Scheduled vs Deadline
131148 dtype = dict_ ['date_two' ].strip (': ' ).lower ()
132- if dtype != '' :
149+ if dtype in [ 'scheduled' , 'deadline' ] :
133150 dict_ .update (date_two = styles [dtype ] + dict_ ['date_two' ] + styles ['normal' ])
151+ elif dtype != '' :
152+ # This branch only occurs with "agenda" mode
153+ dtype = 'deadline'
154+ if dict_ ['days' ] >= ceil (kwargs ['num_days' ] / 2 ):
155+ dict_ .update (date_two = styles ['deadline_two' ] + dict_ ['date_two' ] + styles ['normal' ])
156+ else :
157+ dict_ .update (date_two = styles ['deadline' ] + dict_ ['date_two' ] + styles ['normal' ])
134158 else :
135159 dtype = 'normal'
136160
@@ -141,73 +165,78 @@ def colorize(dict_):
141165 duedate = 'today'
142166 else :
143167 duedate = 'later'
168+
169+ # Update "num_tasks" (4), "date_one" (5), and "tag" (6) elements
170+ #-------------------------------------------------------
171+ # For blank dates (in agenda mode), date strings should be bold white
172+ if dict_ ['text' ] == '' :
173+ dict_ .update (date_one = styles ['bright' ] + dict_ ['date_one' ])
174+
144175 dict_ .update (tag = styles [tagtype ] + dict_ ['tag' ] + styles ['normal' ],
145176 num_tasks = styles ['checkbox' ] + dict_ ['num_tasks' ] + styles ['normal' ],
146177 date_one = styles [duedate ] + dict_ ['date_one' ] + styles ['normal' ])
147178
179+ # Update "category" (8) and "text" (3) elements
180+ #-------------------------------------------------------
148181 if dict_ ['days' ] > 0 :
149- dict_ .update (category = styles ['category' ] + dict_ ['category' ] + styles [ 'normal' ] ,
150- text = format_inline (dict_ ['text' ]) + styles [ 'normal' ] )
182+ dict_ .update (category = styles ['category' ] + dict_ ['category' ],
183+ text = format_inline (dict_ ['text' ]))
151184 else :
152- dict_ .update (text = styles [dtype ] + format_inline (dict_ ['text' ], dtype ) + styles [ 'normal' ] ,
185+ dict_ .update (text = styles [duedate ] + format_inline (dict_ ['text' ], dtype ),
153186 category = styles [duedate ] + dict_ ['category' ])
154187
155- return dict_
156-
188+ #===============================================================================
157189# Function to update the line's dict for 'agenda' mode
158- #---------------------------------------
190+ #===============================================================================
159191def update_agenda (list_ , ** kwargs ):
160- """Update entries if 'agenda' view is chosen."""
161- styles = const .styles
162- num_days = int (kwargs ['num_days' ])
192+ """Update entries if 'agenda' view is chosen.
193+
194+ For days in which no task is due, a blank entry is added to the list.
195+ Furthermore, dates with a future Deadline are repeated so that they
196+ appear with the current date's tasks (if any).
197+ """
198+ num_days = kwargs ['num_days' ]
163199 todolist = copy .deepcopy (list_ )
164- dates_agenda = []
165- for i in range (num_days ):
166- dates_agenda .append ((const .today + timedelta (i )).strftime ('<%Y-%m-%d %a>' ))
167-
168- repeat_tasks = []; deadline = []
169- for d in todolist :
170- if kwargs ['colors' ]:
171- if d ['days' ] > ceil (num_days / 2 ):
172- deadline .append (styles ['deadline_two' ])
173- else :
174- deadline .append (styles ['deadline' ])
175- else :
176- deadline .append ('' )
177200
178201 # Pad output if there are late tasks or larger 'num_days' is requested
202+ repeat_tasks = []
179203 max_days = max ([len (str (x ['days' ])) for x in todolist ])
180204 for i , d in enumerate (todolist ):
181205 if re .search ('Deadline' , d ['date_two' ]):
206+ day_str = str (d ['days' ]).rjust (max_days + 1 )
182207 if 0 < d ['days' ] < num_days :
183- day_str = str (d ['days' ]).rjust (max_days + 1 )
184208 d_copy = dict (d )
185209 d_copy ['date_one' ] = const .regex ['date' ].sub (const .today_date , d ['date_one' ])
186- d_copy ['date_two' ] = deadline [ i ] + ' In' + day_str + ' d.:' + styles [ 'normal' ]
210+ d_copy ['date_two' ] = ' In' + day_str + ' d.:'
187211 repeat_tasks .append (d_copy )
188212 elif d ['days' ] < 0 :
189- day_str = str (d ['days' ]).rjust (max_days )
190- todolist [i ].update (date_one = styles ['late' ] + const .today_date + '\n ' ,
191- date_two = deadline [i ] + ' In ' + day_str + ' d.:' + styles ['bright' ])
213+ todolist [i ].update (date_one = const .today_date + '\n ' ,
214+ date_two = ' In' + day_str + ' d.:' )
192215
193216 todolist = todolist + repeat_tasks
194217
195218 # Add a blank entry for dates with no active tasks
196- for d in dates_agenda :
219+ for n in range (num_days ):
220+ d = (const .today + timedelta (n )).strftime ('<%Y-%m-%d %a>' )
197221 if not any (re .search (d , item ) for item in [x ['date_one' ] for x in todolist ]):
198222 blank_dict = {
199- 'date_one' : styles [ 'bright' ] + d ,
223+ 'date_one' : d ,
200224 'date_two' : '' , 'category' : '' , 'text' : '' , 'level' : '' ,
201225 'num_tasks' : '' , 'tag' : '' , 'todostate' : '' , 'days' : days_until_due (d )
202226 }
203227 todolist .append (blank_dict )
204228
205- todolist = sorted (todolist , key = lambda d : (const . regex [ 'ansicolors' ]. sub ( '' , d ['date_one' ]) , d ['days' ]))
229+ todolist = sorted (todolist , key = lambda d : (d ['date_one' ], d ['days' ]))
206230
207231 # Change '<%Y-%m-%d %a>' to '%A %d %b' for all entries
208232 for i , d in enumerate (todolist ):
209233 todolist [i ]['date_one' ] = day_names (d ['date_one' ])
210234
235+ # Don't show a date for overdue tasks
236+ for i , d in enumerate (todolist ):
237+ if d ['date_two' ] != 'Scheduled:' and d ['days' ] < 0 :
238+ d .update (date_one = 'Overdue\n ' )
239+
211240 return todolist
212241
213242#-------------------------------------------------------------------------------
@@ -253,9 +282,9 @@ def get_parse_string(todostates):
253282
254283 return pattern_line
255284
256- #-------------------------------------------------------------------------------
285+ #===============================================================================
257286# Print functions
258- #-------------------------------------------------------------------------------
287+ #===============================================================================
259288def print_delim (n = 30 ):
260289 """Print a line of blue '#' symbols."""
261290 print ('\t \t ' + const .styles ['url' ] + n * '#' )
0 commit comments