summaryrefslogtreecommitdiff
path: root/timeclock
diff options
context:
space:
mode:
Diffstat (limited to 'timeclock')
-rwxr-xr-xtimeclock461
1 files changed, 0 insertions, 461 deletions
diff --git a/timeclock b/timeclock
deleted file mode 100755
index 496c3471..00000000
--- a/timeclock
+++ /dev/null
@@ -1,461 +0,0 @@
-#!/usr/bin/env python
-
-# timeclock, a time-keeping program based on the Ledger library
-#
-# Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
-#
-# This program is made available under the terms of the BSD Public
-# License. See the LICENSE file included with the distribution for
-# details and disclaimer.
-#
-# This program implements a simple timeclock, using the identical
-# format as my timeclock.el module for Emacs (which is part of the
-# Emacs 21 distribution). This allows you to use either this script
-# or that module for creating time events.
-#
-# Usage is very simple: Set the environment variable TIMELOG to the
-# path to your timelog file (if this variable is unset, any events
-# created will simply be printed to stdout). Once this variable is
-# set:
-#
-# timeclock in "project" what aspect of the project will I do today
-# timeclock out what did I accomplish
-#
-# The description text is optional, but the project is required when
-# clocking in. This project should be a account name, which means it
-# can use ":" to separate the project from the task, for example:
-#
-# timeclock in Client:Meetings at the code review meeting
-#
-# To generate a balance report of time spent, use "timeclock" with no
-# arguments, or "timeclock balance". The options available are the
-# same as those used for ledger.
-
-import os
-import sys
-import string
-import time
-
-true, false = 1, 0
-
-from ledger import *
-
-home = os.getenv ("HOME")
-config.init_file = home + "/.timeclockrc";
-config.cache_file = home + "/.timeclock-cache";
-
-# Define some functions for reporting time quantities
-
-workday = 8 * 60 * 60 # length of a nominal workday
-
-def secstr (secs):
- return "%d:%02d" % (abs (secs) / 60 / 60, (abs (secs) / 60) % 60)
-
-def daystr (amt):
- dy = int (amt) / int (workday)
- amt = amt - (dy * workday)
- if dy >= 5:
- wk = dy / 5
- dy = dy % 5
- if dy: amt = "%sw %sd %s" % (wk, dy, secstr (amt))
- else: amt = "%sw %s" % (wk, secstr (amt))
- else:
- if dy: amt = "%sd %s" % (dy, secstr (amt))
- else: amt = secstr (amt)
- return amt
-
-def adaystr (details):
- result = ""
- for amt in account_xdata(details.account).total:
- if amt.commodity ().symbol == "s":
- result = daystr (float (amt))
- break
- return result
-
-config.amount_expr = "{1.00h}*(a/{3600.00h})"
-config.total_expr = "{1.00h}*(O/{3600.00h})"
-config.balance_format = "%20@adaystr() %8T %2_%-a\n";
-
-# Help text specific to timeclock
-
-def show_version (arg):
- print """Timeclock, a command-line timekeeping tool
-
-Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
-
-This program is made available under the terms of the BSD Public
-License. See the LICENSE file included with the distribution for
-details and disclaimer."""
- sys.exit (0)
-
-def option_help (arg):
- print """usage: timeclock [options] COMMAND [ACCT REGEX]...
-
-Basic options:
- -h, --help display this help text
- -v, --version show version information
- -i, --init FILE initialize ledger by loading FILE (def: ~/.ledgerrc)
- --cache FILE use FILE as a binary cache when --file is not used
- -f, --file FILE read ledger data from FILE
- -o, --output FILE write output to FILE
-
-Report filtering:
- -b, --begin DATE set report begin date
- -e, --end DATE set report end date
- -c, --current show only current and past entries (not future)
- -C, --cleared consider only cleared transactions
- -U, --uncleared consider only uncleared transactions
- -R, --real consider only real (non-virtual) transactions
- -Z, --actual consider only actual (non-automated) transactions
- -r, --related calculate report using related transactions
-
-Output customization:
- -F, --format STR use STR as the format; for each report type, use:
- --balance-format --register-format
- --plot-amount-format --plot-total-format
- -y, --date-format STR use STR as the date format (def: %Y/%m/%d)
- --wide for the default register report, use 132 columns
- -E, --empty balance: show accounts with zero balance
- -n, --collapse register: collapse entries with multiple transactions
- -s, --subtotal balance: show sub-accounts; register: show subtotals
- -S, --sort EXPR sort report according to the value expression EXPR
- -p, --period STR report using the given period
- --period-sort EXPR sort each report period's entries by EXPR
- --dow show a days-of-the-week report
- -W, --weekly show weekly sub-totals
- -M, --monthly show monthly sub-totals
- -Y, --yearly show yearly sub-totals
- -l, --limit EXPR calculate only transactions matching EXPR
- -d, --display EXPR display only transactions matching EXPR
- -t, --amount EXPR use EXPR to calculate the displayed amount
- -T, --total EXPR use EXPR to calculate the displayed total
- -j, --amount-data print only raw amount data (useful for scripting)
- -J, --total-data print only raw total data
-
-Commodity reporting:
- -A, --average report average transaction amount
- -D, --deviation report deviation from the average
-
-Commands:
- balance [REGEXP]... show balance totals for matching accounts
- register [REGEXP]... show register of matching events"""
- sys.exit (0)
-
-# This call registers all of the default command-line options that
-# Ledger supports into the option handling mechanism. Skip this call
-# if you wish to do all of your own processing -- in which case simply
-# modify the 'config' object however you like.
-
-add_config_option_handlers ()
-
-add_option_handler ("help", "h", option_help)
-add_option_handler ("version", "v", show_version)
-
-# Process the command-line arguments, test whether caching should be
-# enabled, and then process any option settings from the execution
-# environment. Some historical environment variable names are also
-# supported.
-
-args = process_arguments (sys.argv[1:])
-config.use_cache = not config.data_file
-process_environment (os.environ, "TIMECLOCK_")
-
-if os.environ.has_key ("TIMELOG"):
- process_option ("file", os.getenv ("TIMELOG"))
-
-# The command word is in the first argument. Canonicalize it to a
-# unique, simple form that the remaining code can use to find out
-# which command was specified.
-
-if len (args) == 0:
- args = ["balance"]
-
-command = args.pop (0);
-
-if command == "balance" or command == "bal" or command == "b":
- command = "b"
-elif command == "register" or command == "reg" or command == "r":
- command = "r"
-elif command == "entry":
- command = "e"
-elif command == "in" or command == "out":
- if config.data_file:
- log = open (config.data_file, "a")
- else:
- log = sys.stdout
-
- if command == "in":
- if len (args) == 0:
- print "A project name is required when clocking in."
- sys.exit (1)
- log.write ("i %s %s" % (time.strftime ("%Y/%m/%d %H:%M:%S"),
- args.pop (0)))
- if len (args) > 0:
- log.write (" %s\n" % string.join (args, " "))
- else:
- log.write ("o %s" % time.strftime ("%Y/%m/%d %H:%M:%S"))
- if len (args) > 0:
- log.write (" %s" % string.join (args, " "))
-
- log.write ("\n")
- log.close ()
- sys.exit (0)
-else:
- print "Unrecognized command:", command
- sys.exit (1)
-
-# Create the main journal object, into which all entries will be
-# recorded. Once done, the 'journal' may be iterated to yield those
-# entries, in the same order as which they appeared in the journal
-# file.
-
-journal = Journal ()
-
-# This parser is intended only for timelog files.
-
-class Event:
- def __init__(self, kind, when, desc):
- self.kind = kind
- self.when = when
- self.desc = desc
-
-class Interval:
- def __init__(self, begin, end):
- self.begin = begin
- self.end = end
-
- def length(self):
- "Return the length of the interval in seconds."
- return self.end.when - self.begin.when
-
-def parse_timelog(path, journal):
- import re
- if not os.path.exists (path):
- print "Cannot read timelog file '%s'" % path
- sys.exit (1)
- file = open(path)
- history = []
- begin = None
- linenum = 0
- for line in file:
- linenum += 1
- match = re.match("([iIoO])\s+([0-9/]+\s+[0-9:]+)\s*(.+)", line)
- if match:
- (kind, when, desc) = match.groups()
- when = time.strptime(when, "%Y/%m/%d %H:%M:%S")
- when = time.mktime(when)
- event = Event(kind, when, desc)
- if kind == "i" or kind == "I":
- begin = event
- else:
- if begin.desc:
- match = re.match ("(.+?) (.+)", begin.desc)
- if match:
- acct = match.group (1)
- desc = match.group (2)
- else:
- acct = begin.desc
- desc = ""
- else:
- acct = "Misc"
- desc = event.desc
-
- l = Interval(begin, event).length ()
- e = Entry ()
- e.date = int (begin.when)
- e.payee = desc
-
- x = Transaction (journal.find_account (acct),
- Amount ("%ss" % l), TRANSACTION_VIRTUAL)
- e.add_transaction (x)
-
- if not journal.add_entry (e):
- print "%s, %d: Failed to entry" % (path, linenum)
- sys.exit (1)
-
-parse_timelog (config.data_file, journal)
-
-# Now that everything has been correctly parsed (parse_ledger_data
-# would have thrown an exception if not), we can take time to further
-# process the configuration options. This changes the configuration a
-# bit based on previous option settings, the command word, and the
-# remaining arguments.
-
-if command == "b" and \
- config.amount_expr == "{1.00h}*(a/{3600.00h})":
- config.amount_expr = "a"
-
-config.process_options (command, args);
-
-# Determine the format string to used, based on the command.
-
-if config.format_string:
- format = config.format_string
-elif command == "b":
- format = config.balance_format
-elif command == "r":
- format = config.register_format
-else:
- format = config.print_format
-
-# The following two classes are responsible for outputing transactions
-# and accounts to the user. There are corresponding C++ versions to
-# these, but they rely on I/O streams, which Boost.Python does not
-# provide a conversion layer for.
-
-class FormatTransactions (TransactionHandler):
- last_entry = None
- output = None
-
- def __init__ (self, fmt):
- try:
- i = string.index (fmt, '%/')
- self.formatter = Format (fmt[: i])
- self.nformatter = Format (fmt[i + 2 :])
- except ValueError:
- self.formatter = Format (fmt)
- self.nformatter = None
-
- self.last_entry = None
-
- if config.output_file:
- self.output = open (config.output_file, "w")
- else:
- self.output = sys.stdout
-
- TransactionHandler.__init__ (self)
-
- def __del__ (self):
- if config.output_file:
- self.output.close ()
-
- def flush (self):
- self.output.flush ()
-
- def __call__ (self, xact):
- if not transaction_has_xdata (xact) or \
- not transaction_xdata (xact).dflags & TRANSACTION_DISPLAYED:
- if self.nformatter is not None and \
- self.last_entry is not None and \
- xact.entry == self.last_entry:
- self.output.write (self.nformatter.format (xact))
- else:
- self.output.write (self.formatter.format (xact))
- self.last_entry = xact.entry
- transaction_xdata (xact).dflags |= TRANSACTION_DISPLAYED
-
-class FormatAccounts (AccountHandler):
- output = None
-
- def __init__ (self, fmt, pred):
- self.formatter = Format (fmt)
- self.predicate = AccountPredicate (pred)
-
- if config.output_file:
- self.output = open (config.output_file, "w")
- else:
- self.output = sys.stdout
-
- AccountHandler.__init__ (self)
-
- def __del__ (self):
- if config.output_file:
- self.output.close ()
-
- def final (self, account):
- if account_has_xdata (account):
- xdata = account_xdata (account)
- if xdata.dflags & ACCOUNT_TO_DISPLAY:
- print "-------------------- ---------"
- xdata.value = xdata.total
- self.output.write (self.formatter.format (account))
-
- def flush (self):
- self.output.flush ()
-
- def __call__ (self, account):
- if display_account (account, self.predicate):
- if not account.parent:
- account_xdata (account).dflags |= ACCOUNT_TO_DISPLAY
- else:
- self.output.write (self.formatter.format (account))
- account_xdata (account).dflags |= ACCOUNT_DISPLAYED
-
-# Set the final transaction handler: for balances and equity reports,
-# it will simply add the value of the transaction to the account's
-# xdata, which is used a bit later to report those totals. For all
-# other reports, the transaction data is sent to the configured output
-# location (default is sys.stdout).
-
-if command == "b":
- handler = SetAccountValue ()
-else:
- handler = FormatTransactions (format)
-
-# Chain transaction filters on top of the base handler. Most of these
-# filters customize the output for reporting. None of this is done
-# for balance or equity reports, which don't need it.
-
-if command != "b":
- if config.display_predicate:
- handler = FilterTransactions (handler, config.display_predicate)
-
- handler = CalcTransactions (handler)
-
- if config.sort_string:
- handler = SortTransactions (handler, config.sort_string)
-
- if config.show_revalued:
- handler = ChangedValueTransactions (handler, config.show_revalued_only)
-
- if config.show_collapsed:
- handler = CollapseTransactions (handler);
-
-if config.show_subtotal and not (command == "b" or command == "E"):
- handler = SubtotalTransactions (handler)
-
-if config.days_of_the_week:
- handler = DowTransactions (handler)
-elif config.by_payee:
- handler = ByPayeeTransactions (handler)
-
-if config.report_period:
- handler = IntervalTransactions (handler, config.report_period,
- config.report_period_sort)
- handler = SortTransactions (handler, "d")
-
-# The next two transaction filters are used by all reports.
-
-if config.show_inverted:
- handler = InvertTransactions (handler)
-
-if config.show_related:
- handler = RelatedTransactions (handler, config.show_all_related)
-
-if config.predicate:
- handler = FilterTransactions (handler, config.predicate)
-
-if config.comm_as_payee:
- handler = SetCommAsPayee (handler)
-
-# Walk the journal's entries, and pass each entry's transaction to the
-# handler chain established above.
-
-walk_entries (journal, handler)
-
-# Flush the handlers, causing them to output whatever data is still
-# pending.
-
-handler.flush ()
-
-# For the balance and equity reports, the account totals now need to
-# be displayed. This is different from outputting transactions, in
-# that we are now outputting account totals to display a summary of
-# the transactions that were just walked.
-
-if command == "b":
- acct_formatter = FormatAccounts (format, config.display_predicate)
- sum_accounts (journal.master)
- walk_accounts (journal.master, acct_formatter, config.sort_string)
- acct_formatter.final (journal.master)
- acct_formatter.flush ()