From mboxrd@z Thu Jan 1 00:00:00 1970 From: Carsten Dominik Subject: Re: Visual representation of clocked time Date: Tue, 3 Nov 2009 18:03:43 +0100 Message-ID: <14A2BF1A-0FD8-4E26-8C95-6A3F1AA481E4@gmail.com> References: Mime-Version: 1.0 (Apple Message framework v936) Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes Content-Transfer-Encoding: 7bit Return-path: Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N5NT4-0004FY-RS for emacs-orgmode@gnu.org; Tue, 03 Nov 2009 12:46:54 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N5NSz-00049w-3Y for emacs-orgmode@gnu.org; Tue, 03 Nov 2009 12:46:53 -0500 Received: from [199.232.76.173] (port=45096 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N5NSy-00049U-QR for emacs-orgmode@gnu.org; Tue, 03 Nov 2009 12:46:48 -0500 Received: from mail-ew0-f228.google.com ([209.85.219.228]:64135) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N5NSx-0000iu-W0 for emacs-orgmode@gnu.org; Tue, 03 Nov 2009 12:46:48 -0500 Received: by mail-ew0-f228.google.com with SMTP id 28so3815036ewy.42 for ; Tue, 03 Nov 2009 09:46:47 -0800 (PST) In-Reply-To: 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: Gennady Trafimenkov Cc: emacs-orgmode@gnu.org Hi Gennady, thanks for sharing! - Carsten On Nov 2, 2009, at 10:16 PM, Gennady Trafimenkov wrote: > 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) > > ============================================================ > > > _______________________________________________ > Emacs-orgmode mailing list > Remember: use `Reply All' to send replies to the list. > Emacs-orgmode@gnu.org > http://lists.gnu.org/mailman/listinfo/emacs-orgmode - Carsten