1
1
import json
2
2
from datetime import date
3
3
from datetime import datetime
4
+ from datetime import timedelta
4
5
from time import mktime
5
6
from typing import Iterable
6
- from typing import List
7
7
from typing import Optional
8
- from typing import Tuple
9
8
from typing import Union
10
9
11
10
import click
12
11
import humanize
13
12
import parsedatetime
14
13
import pytz
15
14
from dateutil .tz import tzlocal
16
- from tabulate import tabulate
17
15
18
16
from todoman .model import Todo
19
17
from todoman .model import TodoList
@@ -63,18 +61,36 @@ def compact(self, todo: Todo) -> str:
63
61
return self .compact_multiple ([todo ])
64
62
65
63
def compact_multiple (self , todos : Iterable [Todo ], hide_list = False ) -> str :
64
+ # TODO: format lines fuidly and drop the table
65
+ # it can end up being more readable when too many columns are empty.
66
+ # show dates that are in the future in yellow (in 24hs) or grey (future)
66
67
table = []
67
68
for todo in todos :
68
69
completed = "X" if todo .is_completed else " "
69
70
percent = todo .percent_complete or ""
70
71
if percent :
71
72
percent = f" ({ percent } %)"
72
- priority = self .format_priority_compact (todo .priority )
73
+ priority = click .style (
74
+ self .format_priority_compact (todo .priority ),
75
+ fg = "magenta" ,
76
+ )
73
77
74
- due = self .format_datetime (todo .due )
78
+ due = self .format_datetime (todo .due ) or "(no due date)"
75
79
now = self .now if isinstance (todo .due , datetime ) else self .now .date ()
76
- if todo .due and todo .due <= now and not todo .is_completed :
77
- due = click .style (str (due ), fg = "red" )
80
+
81
+ due_colour = None
82
+ if todo .due :
83
+ if todo .due <= now and not todo .is_completed :
84
+ due_colour = "red"
85
+ elif todo .due >= now + timedelta (hours = 24 ):
86
+ due_colour = "white"
87
+ elif todo .due >= now :
88
+ due_colour = "yellow"
89
+ else :
90
+ due_colour = "white"
91
+
92
+ if due_colour :
93
+ due = click .style (str (due ), fg = due_colour )
78
94
79
95
recurring = "⟳" if todo .is_recurring else ""
80
96
@@ -93,64 +109,36 @@ def compact_multiple(self, todos: Iterable[Todo], hide_list=False) -> str:
93
109
percent ,
94
110
)
95
111
112
+ # TODO: add spaces on the left based on max todos"
113
+
114
+ # FIXME: double space when no priority
96
115
table .append (
97
- [
98
- todo .id ,
99
- f"[{ completed } ]" ,
100
- priority ,
101
- f"{ due } { recurring } " ,
102
- summary ,
103
- ]
116
+ f"[{ completed } ] { todo .id } { priority } { due } { recurring } { summary } "
104
117
)
105
118
106
- return tabulate ( table , tablefmt = "plain" )
119
+ return " \n " . join ( table )
107
120
108
- def _columnize_text (
109
- self ,
110
- label : str ,
111
- text : Optional [str ],
112
- ) -> List [Tuple [Optional [str ], str ]]:
113
- """Display text, split text by line-endings, on multiple colums.
114
-
115
- Do nothing if text is empty or None.
116
- """
117
- lines = text .splitlines () if text else None
121
+ def _format_multiline (self , title : str , value : str ) -> str :
122
+ formatted_title = click .style (title , fg = "white" )
118
123
119
- return self ._columnize_list (label , lines )
120
-
121
- def _columnize_list (
122
- self ,
123
- label : str ,
124
- lst : Optional [List [str ]],
125
- ) -> List [Tuple [Optional [str ], str ]]:
126
- """Display list on multiple columns.
127
-
128
- Do nothing if list is empty or None.
129
- """
130
-
131
- rows : List [Tuple [Optional [str ], str ]] = []
132
-
133
- if lst :
134
- rows .append ((label , lst [0 ]))
135
- for line in lst [1 :]:
136
- rows .append ((None , line ))
137
-
138
- return rows
124
+ if value .strip ().count ("\n " ) == 0 :
125
+ return f"\n \n { formatted_title } : { value } "
126
+ else :
127
+ return f"\n \n { formatted_title } :\n { value } "
139
128
140
129
def detailed (self , todo : Todo ) -> str :
141
130
"""Returns a detailed representation of a task.
142
131
143
132
:param todo: The todo component.
144
133
"""
145
- extra_rows = []
146
- extra_rows += self . _columnize_text ( "Description" , todo .description )
147
- extra_rows += self ._columnize_text ( "Location " , todo .location )
134
+ extra_lines = []
135
+ if todo .description :
136
+ extra_lines . append ( self ._format_multiline ( "Description " , todo .description ) )
148
137
149
- if extra_rows :
150
- return "{}\n \n {}" .format (
151
- self .compact (todo ), tabulate (extra_rows , tablefmt = "plain" )
152
- )
153
- return self .compact (todo )
138
+ if todo .location :
139
+ extra_lines .append (self ._format_multiline ("Location" , todo .location ))
140
+
141
+ return f"{ self .compact (todo )} { '' .join (extra_lines )} "
154
142
155
143
def format_datetime (self , dt : Optional [date ]) -> Union [str , int , None ]:
156
144
if not dt :
0 commit comments