# Path: lib/fpos/core.py
# def money(value):
# def lcs(a, b):
# 
# Path: lib/fpos/core.py
# def money(value):
#     return "{:.2f}".format(value)
# 
# Path: lib/fpos/groups.py
# class DynamicGroups(GroupProtocol):
#     def __init__(self, threshold=0.85, size=4, backend=None):
#         if backend is None:
#             backend = SqlGroupCollection()
#         self.backend = backend
#         self._strgrp = Strgrp(threshold=threshold, size=size)
#         self.size = size
#         self.threshold = threshold
#         self.map = dict()
# 
#     def __iter__(self):
#         return iter(self._strgrp)
# 
#     def __enter__(self):
#         self.backend.__enter__()
#         return self
# 
#     def __exit__(self, exc_type, exc_value, traceback):
#         self.backend.__exit__(exc_type, exc_value, traceback)
# 
#     def _split_heap(self, heap):
#         i = None
# 
#         for i, grpbin in enumerate(heap):
#             acceptable = grpbin.is_acceptible(self._strgrp)
#             if not acceptable:
#                 r = (heap[:i], heap[i:])
#                 break
#         else:
#             r = (heap, [])
# 
#         return r
# 
#     def find_group(self, description):
#         grpbin = self._strgrp.grp_exact(description)
#         if grpbin is not None:
#             return grpbin
# 
#         did = gen_id(description, salt)
#         if self.backend.have_association(did):
#             cid = self.backend.get_canonical(did)
#             if cid in self.map:
#                 return self._strgrp.grp_exact(self.map[cid])
#             return None
# 
#         heap = self._strgrp.grps_for(description)
# 
#         if self.size == 0:
#             return heap[0] if len(heap) else None
# 
#         needles, haystack = self._split_heap(heap)
#         if len(needles) == 0:
#             return None
# 
#         dynamic = [n.is_dynamic(self._strgrp) for n in needles]
#         if sum(dynamic) == 1:
#             return needles[dynamic.index(True)]
# 
#         # Otherwise get user input
#         return self._request_match(description, needles)
# 
#     def insert(self, description, value, group=None):
#         did = gen_id(description, salt)
#         if group:
#             group.add(self._strgrp, description, value)
#             gid = gen_id(group.key(), salt)
#             cid = self.backend.get_canonical(gid);
#             if not self.backend.have_association(did):
#                 self.backend.associate(cid, did)
#         else:
#             group = self._strgrp.add(description, value)
#             if self.backend.have_association(did):
#                 cid = self.backend.get_canonical(did)
#                 self.map[cid] = description
#             else:
#                 self.map[did] = description
#                 self.backend.associate(did, did)
# 
#         return group
# 
#     def add(self, description, value):
#         return self.insert(description, value, self.find_group(description))


import argparse
import csv
import collections
import math
import re
from .core import categories
from .core import money
from .groups import DynamicGroups
def name():
    return __name__.split(".")[-1]

def parse_args(subparser=None):
    parser_init = subparser.add_parser if subparser else argparse.ArgumentParser
    parser = parser_init(name(), description=cmd_description, help=cmd_help)
    parser.add_argument('infile', metavar="INPUT", type=argparse.FileType('r'),
            help="The IR document containing un-categorised transactions")
    parser.add_argument('outfile', metavar="OUTPUT", type=argparse.FileType('w'),
            help="The IR document to which to write annotated transactions")
    parser.add_argument('--confirm', default=False, action="store_true",
            help="Prompt for confirmation after each entry has been annotated with a category")
    return [ parser ] if subparser else parser.parse_args()


def annotate(src, confirm=False, tagger=None):
    annotated = []
    if tagger is None:
        tagger = _Tagger()
    with tagger:
        try:
            for row in src:
                if 0 == len(row):
                    # Skip empty lines
                    continue
                cooked = Entry(row[0], row[1], row[2])
                category = None
                if 4 == len(row):
                    # Fourth column is category, check that it's known
                    try:

# Path: lib/fpos/core.py
# def money(value):
# def lcs(a, b):
# 
# Path: lib/fpos/core.py
# def money(value):
#     return "{:.2f}".format(value)
# 
# Path: lib/fpos/groups.py
# class DynamicGroups(GroupProtocol):
#     def __init__(self, threshold=0.85, size=4, backend=None):
#         if backend is None:
#             backend = SqlGroupCollection()
#         self.backend = backend
#         self._strgrp = Strgrp(threshold=threshold, size=size)
#         self.size = size
#         self.threshold = threshold
#         self.map = dict()
# 
#     def __iter__(self):
#         return iter(self._strgrp)
# 
#     def __enter__(self):
#         self.backend.__enter__()
#         return self
# 
#     def __exit__(self, exc_type, exc_value, traceback):
#         self.backend.__exit__(exc_type, exc_value, traceback)
# 
#     def _split_heap(self, heap):
#         i = None
# 
#         for i, grpbin in enumerate(heap):
#             acceptable = grpbin.is_acceptible(self._strgrp)
#             if not acceptable:
#                 r = (heap[:i], heap[i:])
#                 break
#         else:
#             r = (heap, [])
# 
#         return r
# 
#     def find_group(self, description):
#         grpbin = self._strgrp.grp_exact(description)
#         if grpbin is not None:
#             return grpbin
# 
#         did = gen_id(description, salt)
#         if self.backend.have_association(did):
#             cid = self.backend.get_canonical(did)
#             if cid in self.map:
#                 return self._strgrp.grp_exact(self.map[cid])
#             return None
# 
#         heap = self._strgrp.grps_for(description)
# 
#         if self.size == 0:
#             return heap[0] if len(heap) else None
# 
#         needles, haystack = self._split_heap(heap)
#         if len(needles) == 0:
#             return None
# 
#         dynamic = [n.is_dynamic(self._strgrp) for n in needles]
#         if sum(dynamic) == 1:
#             return needles[dynamic.index(True)]
# 
#         # Otherwise get user input
#         return self._request_match(description, needles)
# 
#     def insert(self, description, value, group=None):
#         did = gen_id(description, salt)
#         if group:
#             group.add(self._strgrp, description, value)
#             gid = gen_id(group.key(), salt)
#             cid = self.backend.get_canonical(gid);
#             if not self.backend.have_association(did):
#                 self.backend.associate(cid, did)
#         else:
#             group = self._strgrp.add(description, value)
#             if self.backend.have_association(did):
#                 cid = self.backend.get_canonical(did)
#                 self.map[cid] = description
#             else:
#                 self.map[did] = description
#                 self.backend.associate(did, did)
# 
#         return group
# 
#     def add(self, description, value):
#         return self.insert(description, value, self.find_group(description))


# Path: lib/fpos/annotate.py
import argparse
import csv
import collections
import math
import re
from .core import categories
from .core import money
from .groups import DynamicGroups

def name():
    return __name__.split(".")[-1]

def parse_args(subparser=None):
    parser_init = subparser.add_parser if subparser else argparse.ArgumentParser
    parser = parser_init(name(), description=cmd_description, help=cmd_help)
    parser.add_argument('infile', metavar="INPUT", type=argparse.FileType('r'),
            help="The IR document containing un-categorised transactions")
    parser.add_argument('outfile', metavar="OUTPUT", type=argparse.FileType('w'),
            help="The IR document to which to write annotated transactions")
    parser.add_argument('--confirm', default=False, action="store_true",
            help="Prompt for confirmation after each entry has been annotated with a category")
    return [ parser ] if subparser else parser.parse_args()


def annotate(src, confirm=False, tagger=None):
    annotated = []
    if tagger is None:
        tagger = _Tagger()
    with tagger:
        try:
            for row in src:
                if 0 == len(row):
                    # Skip empty lines
                    continue
                cooked = Entry(row[0], row[1], row[2])
                category = None
                if 4 == len(row):
                    # Fourth column is category, check that it's known
                    try:

                        group = tagger.find_group(cooked.description)