From mboxrd@z Thu Jan 1 00:00:00 1970 From: Gennady Trafimenkov Subject: Visual representation of clocked time Date: Tue, 3 Nov 2009 00:16:28 +0300 Message-ID: Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N54GN-0001rw-3Q for emacs-orgmode@gnu.org; Mon, 02 Nov 2009 16:16:31 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N54GM-0001rc-Ey for emacs-orgmode@gnu.org; Mon, 02 Nov 2009 16:16:30 -0500 Received: from [199.232.76.173] (port=52306 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N54GM-0001rZ-BK for emacs-orgmode@gnu.org; Mon, 02 Nov 2009 16:16:30 -0500 Received: from fg-out-1718.google.com ([72.14.220.155]:33258) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N54GL-0000xv-Ru for emacs-orgmode@gnu.org; Mon, 02 Nov 2009 16:16:30 -0500 Received: by fg-out-1718.google.com with SMTP id l26so76270fgb.12 for ; Mon, 02 Nov 2009 13:16:28 -0800 (PST) List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org Hello everyone, I was thinking that it would be nice to have a visual representation of clocked time. It could give you a quick overview of when and how long you had been working on a particular task. For example, you have a subtree like this: ** very important task CLOCK: [2009-10-03 Sat 22:02]--[2009-10-03 Sat 23:21] => 1:19 CLOCK: [2009-10-04 Sun 23:53]--[2009-10-05 Mon 02:10] => 2:17 CLOCK: [2009-10-06 Tue 14:10]--[2009-10-06 Tue 14:50] => 0:40 CLOCK: [2009-10-07 Wed 21:25]--[2009-10-08 Thu 00:21] => 2:56 CLOCK: [2009-10-13 Tue 01:52]--[2009-10-13 Tue 02:52] => 1:00 CLOCK: [2009-10-13 Tue 20:58]--[2009-10-13 Tue 23:32] => 2:34 CLOCK: [2009-10-14 Wed 23:20]--[2009-10-15 Thu 00:55] => 1:35 CLOCK: [2009-10-16 Fri 14:14]--[2009-10-16 Fri 14:53] => 0:39 some text here You press a combination of keys and get a table like this: |---------------+-----+-----+-----+-----+-----+-----+-----+------| | Week Starting | Mon | Tue | Wed | Thu | Fri | Sat | Sun | tt,h | |---------------+-----+-----+-----+-----+-----+-----+-----+------| | 2009-09-28 | | | | | | 79 | 137 | 3.6 | | 2009-10-05 | | 40 | 176 | | | | | 3.6 | | 2009-10-12 | | 214 | 95 | | 39 | | | 5.8 | |---------------+-----+-----+-----+-----+-----+-----+-----+------| The table contains total number of clocked minutes for every day and total number of hours for every week. If I could write such a thing in Emacs lisp, I would do that. But unfortunately I can't. So, I wrote a python script and small function in lisp to call it. I am posting them here in hope that it might be useful for someone. Org-mode is an excellent thing. Thank you! Best regards, Gennady Trafimenkov ============================================================ === === ============================================================ (defun gt/org-get-clocked-time-stat-on-subtree () "Calculate nice table with statistics of clocked time. It puts results into buffer '*clocked-time-stat*'. When called with C-u, it copies results into the kill ring. GNU General Public License version 3 or later." (interactive) (let ((outputbuffer "*clocked-time-stat*")) (save-excursion (outline-mark-subtree) (shell-command-on-region (region-beginning) (region-end) "python ~/bin/create-clock-table.py" outputbuffer) (if (equal current-prefix-arg '(4)) (if (get-buffer outputbuffer) (progn (with-current-buffer outputbuffer (mark-whole-buffer) (kill-new (filter-buffer-substring (region-beginning) (region-end)))))))))) ============================================================ === create-clock-table.py ================================== ============================================================ #/usr/bin/env python # # GNU General Public License version 3 or later # # This script extracts all record of this kind from the standart input: # # CLOCK: [2009-10-17 Sat 21:47]--[2009-10-17 Sat 23:10] => 1:23 # # then aggregates data and build table with statistics like this: # |---------------+-----+-----+-----+-----+-----+-----+-----+------| # | Week Starting | Mon | Tue | Wed | Thu | Fri | Sat | Sun | Tot. | # |---------------+-----+-----+-----+-----+-----+-----+-----+------| # | 2009-08-03 | | | | | | | | 0 | # | 2009-08-10 | | | | | | | | 0 | # | 2009-08-17 | | | | 75 | 60 | 60 | 15 | 210 | # | 2009-08-24 | 75 | 60 | 60 | 70 | | | | 265 | # | 2009-08-31 | | 10 | | | | | | 10 | # |---------------+-----+-----+-----+-----+-----+-----+-----+------| import sys import re import datetime if __name__ == '__main__': dailyStat = {} # we are going to extract records like: # CLOCK: [2009-10-17 Sat 21:47]--[2009-10-17 Sat 23:10] => 1:23 timeExtractor = re.compile('^\s*CLOCK: \[(\d{4})-(\d\d)-(\d\d).*\]--\[.*\] =>\s+(\d+):(\d\d)') for line in sys.stdin.readlines(): match = timeExtractor.match(line) if match: year, month, day = int(match.group(1)), int(match.group(2)), int(match.group(3)) hours, minutes = int(match.group(4)), int(match.group(5)) date = datetime.date(year, month, day) minutes += hours * 60 # print date, minutes dailyStat.setdefault(date, 0) dailyStat[date] += minutes if len(dailyStat) == 0: sys.exit(0) minDate = min(dailyStat.keys()) maxDate = max(dailyStat.keys()) firstWeek = minDate - datetime.timedelta(minDate.weekday()) lastWeek = maxDate - datetime.timedelta(maxDate.weekday()) # calculate weekly stat ############################################################ weeklyStat = {} week = firstWeek while week <= lastWeek: weeklyStat[week] = [0, 0, 0, 0, 0, 0, 0, 0] week += datetime.timedelta(7) biggestNumber = 0 for day in dailyStat.keys(): weekday = day.weekday() week = day - datetime.timedelta(weekday) weeklyStat[week][weekday] = dailyStat[day] weeklyStat[week][7] += dailyStat[day] biggestNumber = max(biggestNumber, dailyStat[day]) def PrintTableLine(firstColumn, otherColumns, otherColumnsWidth): cellTemplate = " %%%ds |" % otherColumnsWidth line = "| %13s |" % firstColumn for i in otherColumns: if i == 0: line += cellTemplate % "" else: line += cellTemplate % i print line def PrintTableDelimiter(numOfColumns, otherColumnsWidth): column = "-" * (otherColumnsWidth + 2) line = "|" + "-" * (13 + 2) for i in xrange(1, numOfColumns+1): line += "+" line += column line += "|" print line # printing the weekly stat table ############################################################ columnWidth = max(4, len("%d" % biggestNumber) + 1) PrintTableDelimiter(8, columnWidth) PrintTableLine("Week Starting", ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "tt,h"], columnWidth) PrintTableDelimiter(8, columnWidth) cellTemplate = " %%%ds |" % columnWidth week = firstWeek while week <= lastWeek: # convert total number of minutes in number of hours weeklyStat[week][7] = "%0.1f" % (float(weeklyStat[week][7]) / 60) PrintTableLine(week.isoformat(), weeklyStat[week], columnWidth) week += datetime.timedelta(7) PrintTableDelimiter(8, columnWidth) ============================================================