Merge lp:~ijk/gtimelog/nutmeg into lp:~gtimelog-dev/gtimelog/trunk
- nutmeg
- Merge into trunk
Proposed by
ijk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merge reported by: | Marius Gedminas | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | lp:~ijk/gtimelog/nutmeg | ||||||||
Merge into: | lp:~gtimelog-dev/gtimelog/trunk | ||||||||
Diff against target: |
267 lines (+78/-39) 5 files modified
NEWS.txt (+4/-2) README.txt (+12/-11) gtimelog.desktop (+2/-2) scripts/export-my-calendar.py (+4/-3) src/gtimelog/main.py (+56/-21) |
||||||||
To merge this branch: | bzr merge lp:~ijk/gtimelog/nutmeg | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marius Gedminas | Needs Fixing | ||
Review via email: mp+142036@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
lp:~ijk/gtimelog/nutmeg
updated
Revision history for this message
Marius Gedminas (mgedmin) wrote : | # |
Actually, wait a second:
xdg = os.environ.
if xdg is not None:
return os.path.
else:
return os.path.
so if I set XDG_CONFIG_
Shouldn't that be
xdg = os.environ.
if xdg is not None:
return os.path.
else:
return os.path.
and likewise for the data dir?
review:
Needs Fixing
Revision history for this message
Marius Gedminas (mgedmin) wrote : | # |
Also, should it really pass the value of the environment variable through os.path.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS.txt' |
2 | --- NEWS.txt 2012-08-24 13:53:30 +0000 |
3 | +++ NEWS.txt 2013-01-09 18:20:25 +0000 |
4 | @@ -1,7 +1,9 @@ |
5 | 0.8.1 (unreleased) |
6 | ================== |
7 | -* No changes yet. |
8 | - |
9 | +* Fix STRF formatting problem on windows (LP: #1096489). |
10 | +* Use XDG Base Directory Specification for config and data files. |
11 | +* Fix gtimelog.desktop validation. (LP: #1051226). |
12 | + Use gtimelog icon instead of gnome-week.png. |
13 | |
14 | 0.8.0 (2012-08-24) |
15 | ================== |
16 | |
17 | === modified file 'README.txt' |
18 | --- README.txt 2012-08-21 15:04:04 +0000 |
19 | +++ README.txt 2013-01-09 18:20:25 +0000 |
20 | @@ -13,15 +13,16 @@ |
21 | the activity name upfront, and just delay pressing the Enter key until you're |
22 | done. |
23 | |
24 | -There's also a Tasks pane that lists tasks found in ~/.gtimelog/tasks.txt. |
25 | +There's also a Tasks pane that lists tasks found in ~/.local/share/gtimelog/tasks.txt. |
26 | You can click on those to save typing. Or you can specify a URL in |
27 | -~/.gtimelog/gtimelogrc and download the task list from a wiki or wherever. |
28 | +~/.config/gtimelog/gtimelogrc and download the task list from a wiki or wherever. |
29 | |
30 | -There are two broad categories of activities: ones that count as work (coding, |
31 | -planning, writing proposals or reports, answering work-related email), and ones |
32 | -that don't (browsing the web for fun, reading personal email, chatting with |
33 | -a friend on the phone for two hours, going out for a lunch break). To indicate |
34 | -which activities are not work related add two asterisks to the activity name: |
35 | +There are two broad categories of activities: ones that count as work |
36 | +(coding, planning, writing proposals or reports, answering |
37 | +work-related email), and ones that don't (browsing the web for fun, |
38 | +reading personal email, chatting with a friend on the phone for two |
39 | +hours, going out for a lunch break). To indicate which activities are |
40 | +not work related add two asterisks to the activity name: |
41 | |
42 | lunch ** |
43 | browsing slashdot ** |
44 | @@ -44,7 +45,7 @@ |
45 | for convenience. It also advises you how much time you still have to work today |
46 | to get 8 hours of work done, and how much time is left just to have spent a |
47 | workday at the office (the number of hours in a day is configurable in |
48 | -~/.gtimelog/gtimelogrc). There are three basic views: one shows all the |
49 | +~/.config/gtimelog/gtimelogrc). There are three basic views: one shows all the |
50 | activities in chronological order, with starting and ending times; another |
51 | groups all entries with the same title into one activity and just shows |
52 | the total duration; and a third one groups all entries from the same categories |
53 | @@ -52,7 +53,7 @@ |
54 | |
55 | At the end of the day you can send off a daily report by choosing File -> Daily |
56 | Report. A mail program (Mutt in a terminal, unless you have changed it in |
57 | -~/.gtimelog/gtimelogrc) will be started with all the activities listed in it. |
58 | +~/.config/gtimelog/gtimelogrc) will be started with all the activities listed in it. |
59 | My Mutt configuration lets me edit the report before sending it. |
60 | |
61 | If you forget to enter an activity, you can start your activity with a 24h |
62 | @@ -63,7 +64,7 @@ |
63 | |
64 | If you make a mistake and type in the wrong activity name, don't worry. |
65 | GTimeLog stores the time log in a simple plain text file |
66 | -~/.gtimelog/timelog.txt. Every line contains a timestamp and the name of the |
67 | +~/.config/gtimelog/timelog.txt. Every line contains a timestamp and the name of the |
68 | activity that was finished at the time. All other lines are ignored, so you |
69 | can add comments if you want to -- just make sure no comment begins with a |
70 | timestamp. You do not have to worry about GTimeLog overwriting your changes |
71 | @@ -79,7 +80,7 @@ |
72 | from midnight makes it easy to read the graph scale as the time of day. Try |
73 | a bargraph with stacked values and both first row and column as labels. |
74 | |
75 | -You can change the configuration directory (by default, ~/.gtimelog) by |
76 | +You can change the configuration directory (by default, ~/.config/gtimelog) by |
77 | setting the environment variable $GTIMELOG_HOME to a non-empty string naming |
78 | the directory you want to use. |
79 | |
80 | |
81 | === modified file 'gtimelog.desktop' |
82 | --- gtimelog.desktop 2010-09-02 21:11:41 +0000 |
83 | +++ gtimelog.desktop 2013-01-09 18:20:25 +0000 |
84 | @@ -5,5 +5,5 @@ |
85 | Terminal=false |
86 | Type=Application |
87 | StartupNotify=true |
88 | -Icon=gnome-week.png |
89 | -Categories=Application;Utility |
90 | +Icon=gtimelog |
91 | +Categories=Application;Utility; |
92 | |
93 | === modified file 'scripts/export-my-calendar.py' |
94 | --- scripts/export-my-calendar.py 2012-11-28 17:49:14 +0000 |
95 | +++ scripts/export-my-calendar.py 2013-01-09 18:20:25 +0000 |
96 | @@ -12,11 +12,12 @@ |
97 | outputfile = 'calendar.ics' |
98 | |
99 | settings = gtimelog.Settings() |
100 | -configdir = os.path.expanduser('~/.gtimelog') |
101 | -settings_file = os.path.join(configdir, 'gtimelogrc') |
102 | +configdir = settings.get_config_dir() |
103 | +datadir = settings.get_data_dir() |
104 | +settings_file = settings.get_config_file() |
105 | if os.path.exists(settings_file): |
106 | settings.load(settings_file) |
107 | -timelog = gtimelog.TimeLog(os.path.join(configdir, 'timelog.txt'), |
108 | +timelog = gtimelog.TimeLog(settings.get_timelog_file(), |
109 | settings.virtual_midnight) |
110 | window = timelog.window_for(d1, d2) |
111 | window.icalendar(open(outputfile, 'w')) |
112 | |
113 | === modified file 'src/gtimelog/main.py' |
114 | --- src/gtimelog/main.py 2012-11-28 17:49:14 +0000 |
115 | +++ src/gtimelog/main.py 2013-01-09 18:20:25 +0000 |
116 | @@ -113,7 +113,7 @@ |
117 | ui_file = os.path.join(resource_dir, "gtimelog.ui") |
118 | icon_file_bright = os.path.join(resource_dir, "gtimelog-small-bright.png") |
119 | icon_file_dark = os.path.join(resource_dir, "gtimelog-small.png") |
120 | -default_home = '~/.gtimelog' |
121 | +legacy_default_home = '~/.gtimelog' |
122 | |
123 | # This is for distribution packages |
124 | if not os.path.exists(ui_file): |
125 | @@ -704,8 +704,8 @@ |
126 | def weekly_report_categorized(self, output, email, who, |
127 | estimated_column=False): |
128 | """Format a weekly report with entries displayed under categories.""" |
129 | - week = self.window.min_timestamp.strftime('%V') |
130 | - subject = 'Weekly report for %s (week %s)' % (who, week) |
131 | + week = self.window.min_timestamp.isocalendar()[1] |
132 | + subject = 'Weekly report for {} (week {:0>2})'.format(who, week) |
133 | return self._categorizing_report(output, email, who, subject, |
134 | period_name='week', |
135 | estimated_column=estimated_column) |
136 | @@ -721,8 +721,8 @@ |
137 | |
138 | def weekly_report_plain(self, output, email, who, estimated_column=False): |
139 | """Format a weekly report .""" |
140 | - week = self.window.min_timestamp.strftime('%V') |
141 | - subject = 'Weekly report for %s (week %s)' % (who, week) |
142 | + week = self.window.min_timestamp.isocalendar()[1] |
143 | + subject = 'Weekly report for {} (week {:0>2})'.format(who, week) |
144 | return self._plain_report(output, email, who, subject, |
145 | period_name='week', |
146 | estimated_column=estimated_column) |
147 | @@ -746,12 +746,12 @@ |
148 | # would give us translated names |
149 | weekday_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] |
150 | weekday = weekday_names[window.min_timestamp.weekday()] |
151 | - week = window.min_timestamp.strftime('%V') |
152 | - print >> output, "To: %(email)s" % {'email': email} |
153 | - print >> output, ("Subject: %(date)s report for %(who)s" |
154 | - " (%(weekday)s, week %(week)s)" |
155 | - % {'date': window.min_timestamp.strftime('%Y-%m-%d'), |
156 | - 'weekday': weekday, 'week': week, 'who': who}) |
157 | + week = window.min_timestamp.isocalendar()[1] |
158 | + print >> output, "To: {email}".format(email=email) |
159 | + print >> output, ("Subject: {:%Y-%m-%d} report for {who}" |
160 | + " ({weekday}, week {week:0>2})".format( |
161 | + window.min_timestamp, who=who, |
162 | + weekday=weekday, week=week)) |
163 | print >> output |
164 | items = list(window.all_entries()) |
165 | if not items: |
166 | @@ -1066,13 +1066,39 @@ |
167 | |
168 | report_style = 'plain' |
169 | |
170 | + def check_legacy_config(self): |
171 | + envar_home = os.environ.get('GTIMELOG_HOME') |
172 | + if envar_home is not None: |
173 | + return os.path.expanduser(envar_home) |
174 | + if os.path.isdir(os.path.expanduser(legacy_default_home)): |
175 | + return os.path.expanduser(legacy_default_home) |
176 | + |
177 | def get_config_dir(self): |
178 | - envar_home = os.environ.get('GTIMELOG_HOME') |
179 | - return os.path.expanduser(envar_home if envar_home else default_home) |
180 | + legacy = self.check_legacy_config() |
181 | + if legacy is not None: return legacy |
182 | + #http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html |
183 | + xdg = os.environ.get('XDG_CONFIG_HOME') |
184 | + if xdg is not None: |
185 | + return os.path.expanduser(xdg) |
186 | + else: |
187 | + return os.path.expanduser("~/.config/gtimelog") |
188 | + |
189 | + def get_data_dir(self): |
190 | + legacy = self.check_legacy_config() |
191 | + if legacy is not None: return legacy |
192 | + |
193 | + xdg = os.environ.get('XDG_DATA_HOME') |
194 | + if xdg is not None: |
195 | + return os.path.expanduser(xdg) |
196 | + else: |
197 | + return os.path.expanduser("~/.local/share/gtimelog") |
198 | |
199 | def get_config_file(self): |
200 | return os.path.join(self.get_config_dir(), 'gtimelogrc') |
201 | |
202 | + def get_timelog_file(self): |
203 | + return os.path.join(self.get_data_dir(), 'timelog.txt') |
204 | + |
205 | def _config(self): |
206 | config = ConfigParser.RawConfigParser() |
207 | config.add_section('gtimelog') |
208 | @@ -1527,7 +1553,8 @@ |
209 | else: |
210 | today = self.looking_at_date |
211 | window = self.timelog.window_for_day(today) |
212 | - today = today.strftime('%A, %Y-%m-%d (week %V)') |
213 | + today = ("{:%A, %Y-%m-%d} (week {:0>2})" |
214 | + .format(today, today.isocalendar()[1])) |
215 | self.current_view_label.set_text(today) |
216 | if self.chronological: |
217 | for item in window.all_entries(): |
218 | @@ -2286,13 +2313,21 @@ |
219 | |
220 | settings = Settings() |
221 | configdir = settings.get_config_dir() |
222 | + datadir = settings.get_data_dir() |
223 | try: |
224 | - # Create it if it doesn't exist. |
225 | + # Create configdir if it doesn't exist. |
226 | os.makedirs(configdir) |
227 | except OSError as error: |
228 | if error.errno != errno.EEXIST: |
229 | # XXX: not the most friendly way of error reporting for a GUI app |
230 | raise |
231 | + try: |
232 | + # Create datadir if it doesn't exist. |
233 | + os.makedirs(datadir) |
234 | + except OSError as error: |
235 | + if error.errno != errno.EEXIST: |
236 | + raise |
237 | + |
238 | settings_file = settings.get_config_file() |
239 | if not os.path.exists(settings_file): |
240 | if opts.debug: |
241 | @@ -2303,20 +2338,20 @@ |
242 | print 'Loading settings from %s' % settings_file |
243 | settings.load(settings_file) |
244 | if opts.debug: |
245 | - print 'Loading time log from %s' % os.path.join(configdir, 'timelog.txt') |
246 | + print 'Loading time log from %s' % settings.get_timelog_file() |
247 | print 'Assuming date changes at %s' % settings.virtual_midnight |
248 | - timelog = TimeLog(os.path.join(configdir, 'timelog.txt'), |
249 | + timelog = TimeLog(settings.get_timelog_file(), |
250 | settings.virtual_midnight) |
251 | if settings.task_list_url: |
252 | if opts.debug: |
253 | print 'Loading cached remote tasks from %s' % ( |
254 | - os.path.join(configdir, 'remote-tasks.txt')) |
255 | + os.path.join(datadir, 'remote-tasks.txt')) |
256 | tasks = RemoteTaskList(settings.task_list_url, |
257 | - os.path.join(configdir, 'remote-tasks.txt')) |
258 | + os.path.join(datadir, 'remote-tasks.txt')) |
259 | else: |
260 | if opts.debug: |
261 | - print 'Loading tasks from %s' % os.path.join(configdir, 'tasks.txt') |
262 | - tasks = TaskList(os.path.join(configdir, 'tasks.txt')) |
263 | + print 'Loading tasks from %s' % os.path.join(datadir, 'tasks.txt') |
264 | + tasks = TaskList(os.path.join(datadir, 'tasks.txt')) |
265 | main_window = MainWindow(timelog, settings, tasks) |
266 | start_in_tray = False |
267 | if settings.show_tray_icon: |
This:
+ subject = 'Weekly report for {} (week {:0>2}) '.format( who, week)
means a UnicodeEncodeError if who is a unicode string with non-ASCII characters.
Although testing reveals that currently `who` is a byte string, which causes bug 1117109, so this doesn't make anything worse.
(It also drops support for Python 2.6 or older, which is probably fine. We'll see if anyone complains about that.)
There are some minor PEP-8 transgressions that I'll fix up after merging.
(Ick, launchpad's review UI sucks. Why haven't I moved to github already?)