budgettracker.py
author Fabien Ninoles <fabien@tzone.org>
Sun, 03 Jan 2016 20:24:28 -0500
changeset 2 64f48a8c758c
parent 0 811cd790a493
permissions -rw-r--r--
New portfolio report compatible with the merge.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     1
#!/usr/bin/env python3
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     2
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     3
import json
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     4
from decimal import Decimal
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     5
from datetime import datetime
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     6
from collections import OrderedDict, defaultdict
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     7
from operator import itemgetter
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     8
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
     9
class Transaction:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    10
    description = ""
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    11
    amount = Decimal(0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    12
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    13
    def __init__(self, **args):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    14
        self.budget = defaultdict(Decimal)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    15
        self.expenses = defaultdict(Decimal)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    16
        self.date = datetime.utcnow().replace(microsecond = 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    17
        balance = None
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    18
        for arg, val in args.items():
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    19
            if arg == "splits":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    20
                self.add_splits(val)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    21
            elif arg == "budget":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    22
                for env, amount in val: 
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    23
                    self.add_budget(env, amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    24
            elif arg == "expenses":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    25
                for env, amount in val:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    26
                    self.add_expense(env, amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    27
            elif arg in ("balance", "envelop"):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    28
                balance = val
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    29
            else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    30
                setattr(self, arg, val)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    31
        if balance is not None:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    32
            self.rebalance(balance)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    33
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    34
    def add_budget(self, envelop, amount):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    35
        assert(amount > 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    36
        self.budget[envelop] += amount
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    37
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    38
    def add_expense(self, envelop, amount):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    39
        assert(amount > 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    40
        self.expenses[envelop] += amount
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    41
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    42
    def add_splits(self, *splits):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    43
        for env, amount in splits:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    44
            if amount < 0:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    45
                self.add_expense(env, -amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    46
            else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    47
                self.add_budget(env, amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    48
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    49
    def rebalance(self, envelop):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    50
        b = self.balance
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    51
        if b < 0:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    52
            self.add_expense(envelop, -b)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    53
        else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    54
            self.add_budget(envelop, b)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    55
        assert(self.balance == 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    56
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    57
    @property
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    58
    def balance(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    59
        return self.amount \
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    60
            - sum(v for v in self.budget.values()) \
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    61
            + sum(v for v in self.expenses.values())
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    62
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    63
    @property
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    64
    def splits(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    65
        for env, amount in self.budget.items():
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    66
            yield (env, amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    67
        for env, amount in self.expenses.items():
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    68
            yield (env, -amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    69
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    70
    def rename_envelop(self, oldname, newname):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    71
        if oldname in self.budget:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    72
            self.budget[newname] += self.budget[oldname]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    73
            del self.budget[oldname]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    74
        if oldname in self.expenses:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    75
            self.expenses[newname] += self.expenses[oldname]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    76
            del self.expenses[oldname]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    77
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    78
    def as_dict(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    79
        def serialize(values):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    80
            for e, v in sorted(values.items(), key = itemgetter(0)):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    81
                d = OrderedDict.fromkeys(["envelop", "value"])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    82
                d.update(envelop = e, value = float(v))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    83
                yield d
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    84
        d = OrderedDict.fromkeys(["description", "date", "amount", "budget", "expenses"])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    85
        d.update(
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    86
            description = self.description,
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    87
            date = self.date.isoformat(),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    88
            amount = float(self.amount),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    89
            budget = list(serialize(self.budget)),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    90
            expenses = list(serialize(self.expenses)))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    91
        return d
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    92
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    93
    @classmethod
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    94
    def from_dict(cls, d):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    95
        try:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    96
            date = datetime.strptime("%Y-%m-%dT%H:%M:%S", d["date"])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    97
        except:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    98
            date = datetime.utcnow().replace(microsecond = 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
    99
        return Transaction(
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   100
            description = d.get("description", ""),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   101
            date = date,
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   102
            amount = Decimal(d.get("amount", Decimal(0))),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   103
            budget = ((v["envelop"], v["value"]) for v in d.get("budget", [])),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   104
            expenses = ((v["envelop"], v["value"]) for v in d.get("expenses", [])))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   105
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   106
class Envelop:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   107
    name = ""
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   108
    description = ""
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   109
    budget = Decimal(0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   110
    expenses = Decimal(0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   111
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   112
    def __init__(self, name = "", description = ""):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   113
        self.name = name
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   114
        self.description = description
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   115
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   116
    @property
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   117
    def balance(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   118
        return self.budget - self.expenses
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   119
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   120
    def update(self, amount):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   121
        if (amount < 0):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   122
            self.expenses -= amount
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   123
        else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   124
            self.budget += amount
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   125
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   126
class Budget:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   127
    name = "Budget"
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   128
    date = datetime.utcnow().replace(microsecond = 0)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   129
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   130
    def __init__(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   131
        self.metaenvelops = defaultdict(str)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   132
        self.transactions = []
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   133
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   134
    def load(self, fp):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   135
        d = json.load(fp, parse_float = Decimal)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   136
        self.metaenvelops = dict((e["name"], e["description"]) for e in d.get("envelops",[]))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   137
        self.transactions = []
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   138
        add = self.add_transaction
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   139
        for t in d.get("transactions",[]): add(Transaction.from_dict(t))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   140
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   141
    def save(self, fp, **args):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   142
        d = OrderedDict.fromkeys(["name", "date", "envelops", "transactions"])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   143
        envelops = list()
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   144
        for name, desc in sorted(self.metaenvelops.items(), key = itemgetter(0)):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   145
            if name != "":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   146
                env = OrderedDict.fromkeys(["name", "description"])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   147
                env.update(name = name, description = desc)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   148
                envelops.append(env)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   149
        d.update(name = self.name,
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   150
                 date = self.date.isoformat(),
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   151
                 envelops = envelops,
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   152
                 transactions = list(t.as_dict() for t in self.transactions))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   153
        json.dump(d, fp, allow_nan=False, **args)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   154
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   155
    def add_transaction(self, t):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   156
        for e in (s[0] for s in t.splits):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   157
            if e not in self.metaenvelops:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   158
                self.metaenvelops[e] = ""
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   159
        self.transactions.append(t)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   160
        return t
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   161
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   162
    def set_envelop_description(self, name, description):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   163
        self.metaenvelops[name] = description
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   164
    
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   165
    def rename_envelop(self, oldname, newname):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   166
        for t in self.transactions:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   167
            t.rename_envelop(oldname, newname)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   168
        if oldname in self.metaenvelops:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   169
            self.metaenvelops.setdefault(newname, self.metaenvelops[oldname])
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   170
            del self.metaenvelops[oldname]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   171
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   172
    @property
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   173
    def envelops(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   174
        total = Envelop("Total")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   175
        unassigned = Envelop("Unassigned", "Unassigned balance")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   176
        envelops = defaultdict(Envelop)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   177
        for t in self.transactions:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   178
            for name, value in t.splits:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   179
                assert(name)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   180
                envelops[name].update(value)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   181
            unassigned.update(t.balance)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   182
        for name, env in envelops.items():
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   183
            env.name = name
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   184
            env.description = self.metaenvelops.get(name,"")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   185
            total.budget += env.budget
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   186
            total.expenses += env.expenses
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   187
            yield env
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   188
        yield total
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   189
        yield unassigned
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   190
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   191
    def report(self):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   192
        for env in self.envelops:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   193
            yield "| {:<20} | {:<50} | {:> 10.2f} | {:> 10.2f} | {:> 10.2f} |".format(env.name, env.description, env.budget, env.expenses, env.balance)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   194
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   195
    def read_transaction(self, reader = input):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   196
        name = reader("Description: ")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   197
        amount = Decimal(reader("Amount: "))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   198
        t = Transaction(name = name, amount = amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   199
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   200
        while t.balance != 0:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   201
            envelop = reader("Balance: {:> 10.2f}\nEnvelop: ".format(t.balance))
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   202
            if not envelop:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   203
                break
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   204
            amount = reader("Amount:" )
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   205
            if amount == "":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   206
                t.rebalance(envelop)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   207
            else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   208
                amount = Decimal(amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   209
                t.add_expense(envelop, -amount) if amount < 0 else t.add_budget(envelop, amount)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   210
        keep = reader("Do you want to keep the transaction [y/N]? ")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   211
        if keep and keep[0].upper() == 'Y':
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   212
            return t
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   213
        return None
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   214
    
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   215
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   216
def main(filename, action):
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   217
    import os
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   218
    import sys
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   219
    b = Budget()
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   220
    if action == "init":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   221
        with open(filename, "w") as fp:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   222
            b.save(fp)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   223
    elif action == "report":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   224
        with open(filename, "r") as fp:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   225
            b.load(fp)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   226
        for l in b.report():
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   227
            print(l)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   228
    elif action == "add":
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   229
        with open(filename, "r") as fp:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   230
            b.load(fp)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   231
        t = b.read_transaction()
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   232
        if t:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   233
            b.add_transaction(t)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   234
            os.rename(os.path.realpath(filename), os.path.realpath(filename) + ".bak")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   235
            with open(filename, "w") as fp:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   236
                b.save(fp)
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   237
    else:
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   238
        print("bad command line option")
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   239
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   240
if __name__ == '__main__':
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   241
    import sys
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   242
    import os
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   243
    filename = sys.argv[1]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   244
    action = sys.argv[2]
811cd790a493 First version of BudgetTracker
Fabien Ninoles <fabien@tzone.org>
parents:
diff changeset
   245
    main(filename, action)