Commits

Andy Mikhailenko  committed 04286a6

Added CLI for financial tracker.

  • Participants
  • Parent commits c6d6c6d

Comments (0)

Files changed (3)

File orgtool/ext/finances/__init__.py

 from tool.ext.templating import register_templates
 from schema import *
 from views import *
+from commands import command_list
 import admin
 
 
-class WebMoneyTracker(BasePlugin):
+class CLIMoneyTracker(BasePlugin):
+    """Money tracker: CLI interface.
+    """
+    commands = command_list
+    features = 'money'
+
+    def make_env(self, default_currency='EUR'):
+        return {'default_currency': default_currency}
+
+
+class WebMoneyTracker(CLIMoneyTracker):
     """Money tracker: web interface.
     """
-    features = 'money'
     requires = ['{templating}', '{routing}']
 
     def make_env(self, default_currency='EUR'):

File orgtool/ext/finances/commands.py

+# -*- coding: utf-8 -*-
+"""
+Commands
+========
+"""
+import datetime
+from decimal import Decimal as D
+from argh import *
+from tool import app
+
+from .schema import Contract, Payment
+
+
+# FIXME refactor this function (it's almost everywhere!)
+def args_to_unicode(namespace, names):
+    for name in names:
+        value = getattr(namespace, name, None)
+        if value is not None:
+            setattr(namespace, name, value.decode('utf8'))
+
+
+def get_contracts(query):
+    db = app.get_feature('document_storage').default_db
+    contracts = Contract.objects(db)
+    if query:
+        return contracts.where(summary__matches_caseless=query)
+    else:
+        return contracts
+
+def get_payments(query=None, contract_query=None):
+    db = app.get_feature('document_storage').default_db
+    contracts = get_contracts(contract_query) if contract_query else None
+    payments = Payment.objects(db)
+    if query:
+        payments = payments.where(summary__matches_caseless=query)
+    if contract_query:
+        if not contracts:
+            return []
+        payments = payments.where(plan__in=[c.pk for c in contracts])
+    return payments
+
+@alias('lsc')
+@arg('-q', '--query')
+def list_contracts(args):
+    "Displays a list of matching contracts."
+    args_to_unicode(args, ['query'])
+    contracts = get_contracts(args.query)
+    for c in contracts:
+        yield unicode(c)
+
+@alias('lsp')
+@arg('-q', '--query')
+@arg('-c', '--contract')
+def list_payments(args):
+    "Displays a list of matching payments."
+    args_to_unicode(args, ['query', 'contract'])
+    payments = get_payments(args.query, args.contract)
+    for p in payments:
+        yield unicode(p)
+
+@alias('log')
+@arg('amount', help='e.g. "-9.99" (negative = expences, positive = income)')
+@arg('currency', help='e.g. "USD"')
+@arg('-d', '--date', help='e.g. 2010-12-27 (leave empty for today)')
+@arg('-t', '--time', help='e.g. 14:52 (leave empty for 00:00)')
+@arg('-c', '--contract', help='contract (query on summary)')
+@arg('--balance', help='balance after the payment')
+@arg('-s', '--summary', help='usually not needed when a contract is specified')
+@arg('--dry-run')
+def add_payment(args):
+    "Logs a payment with given properties."
+    args_to_unicode(args, ['currency', 'summary', 'contract'])
+    contract = None
+    if args.contract:
+        contracts = get_contracts(args.contract)
+        if not contracts:
+            raise COmmandError('no contracts matching query')
+        if 1 < len(contracts):
+            yield 'Mare than one contract matched query:'
+            for c in contracts:
+                yield unicode(c)
+            raise CommandError('Please use a more precise query')
+
+        contract = contracts[0]
+
+    date_time = datetime.datetime.utcnow()
+    if args.date:
+        year, month, day = [int(x) for x in args.date.split('-')]
+        h, m = [int(x) for x in args.time.split(':')] if args.time else (0, 0)
+        date_time = datetime.datetime(year, month, day, h, m)
+
+    amount = D(args.amount)
+    balance = D(args.balance) if args.balance else None
+
+    payment = Payment(
+        date_time = date_time,
+        amount = amount,
+        currency = args.currency,
+        plan = contract,
+        summary = args.summary,
+        balance = balance,
+    )
+
+    if not args.dry_run:
+        db = app.get_feature('document_storage').default_db
+        payment.save(db)
+
+    yield u'Added payment:'
+    yield ''
+    for k, v in payment.iteritems():
+        if v is not None:
+            yield u'  {0}: {1}'.format(k, v)
+    yield ''
+    yield u'assigned primary key {0}'.format(payment.pk)
+
+command_list = list_contracts, list_payments, add_payment

File orgtool/ext/finances/utils.py

         'years': '%Y',
     }
     for date, group in grouped:
-        print date, group
         fmt = scale_to_date_fmt.get(scale, '%d %b')
         label = date.strftime(fmt)
         group_labels.append(label)
     min_y, max_y = [_prep_boundary(x) for x in (safe_min_amount,
                                                 safe_max_amount)]
 
-    def prep(val):
-        #return '%.01f'%val if isinstance(val, float) else str(val)
-        return str(val.quantize(D('.1'))) if isinstance(val, D) else str(val)
-    def join(*vals):
-        return ','.join(prep(v) for v in vals)
-
     span = (max_amount + abs(min_amount)) if 0 < max_amount else max_amount + min_amount
     if max_amount <= 0:
         zero_border = 1
     # positive balance (green)
     marker_positive = ''
     if 0 < max_amount:
-        print 'MAX Y', max_amount
         marker_positive = 'r,EEFFEE,0,{start},{end},{z_index}'.format(
             start = 1,
             end = zero_border,
     # negative balance (red)
     marker_negative = ''
     if min_amount < 0:
-        print 'MIN Y', min_amount
         marker_negative = 'r,FFEEEE,0,{start},{end},{z_index}'.format(
             start = zero_border,
             end = 0,
 
     # grid made out of markers. Simple grid would get hidden behind
     # positive/negative area merkers. It also didn't follow data ticks.
-    marker_grid_v = 'V,cccccc,0,::2,0.5,-1'
+    marker_grid_v = 'V,cccccc,0,::1,0.5,-1'
     marker_grid_h = 'h,cccccc,0,0:2:.2,0.5,-1'
 
     markers = [
 
 #--- TODO: extract code below to Dark(?)
 
+def prep(val):
+    #return '%.01f'%val if isinstance(val, float) else str(val)
+    return str(val.quantize(D('.1'))) if isinstance(val, D) else str(val)
+
+def join(*vals):
+    return ','.join(prep(v) for v in vals)
+
+
 class Chart(object):
     def __init__(self, title, width, height, with_legend=True):
         self.title = title