Merge lp:~ijk/gtimelog/nutmeg into lp:~gtimelog-dev/gtimelog/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
Reviewer Review Type Date Requested Status
Marius Gedminas Needs Fixing
Review via email: mp+142036@code.launchpad.net
To post a comment you must log in.
lp:~ijk/gtimelog/nutmeg updated
246. By ijk

Use XDG Base Directory Spec

247. By ijk

Fix gtimelog.desktop validation. Use gtimelog icon instead of gnome-week.png.

Revision history for this message
Marius Gedminas (mgedmin) wrote :

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?)

review: Approve
Revision history for this message
Marius Gedminas (mgedmin) wrote :

Actually, wait a second:

        xdg = os.environ.get('XDG_CONFIG_HOME')
        if xdg is not None:
            return os.path.expanduser(xdg)
        else:
            return os.path.expanduser("~/.config/gtimelog")

so if I set XDG_CONFIG_HOME=~/.config, then gtimelog will use ~/.config/gtimelogrc?

Shouldn't that be

        xdg = os.environ.get('XDG_CONFIG_HOME')
        if xdg is not None:
            return os.path.join(os.path.expanduser(xdg), 'gtimelog')
        else:
            return os.path.expanduser("~/.config/gtimelog")

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.expanduser()?

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:

Subscribers

People subscribed via source and target branches