diff options
author | John Wiegley <johnw@newartisans.com> | 2006-02-15 20:10:49 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-04-13 02:41:21 -0400 |
commit | ce3491c99f089874725999ca6d8b1fb6a15c9e5e (patch) | |
tree | c48a2bf19a954c8103a02e26f9a1a490b8095268 /timeclock | |
parent | 2eafddc91b8d7f0b7bdfd991acdc9e0b2295e304 (diff) | |
download | fork-ledger-ce3491c99f089874725999ca6d8b1fb6a15c9e5e.tar.gz fork-ledger-ce3491c99f089874725999ca6d8b1fb6a15c9e5e.tar.bz2 fork-ledger-ce3491c99f089874725999ca6d8b1fb6a15c9e5e.zip |
Removed Python integration support.
Diffstat (limited to 'timeclock')
-rwxr-xr-x | timeclock | 461 |
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 () |