Files
@ 50376b2b78f0
Branch filter:
Location: NPO-Accounting/import2ledger/import2ledger/__main__.py - annotation
50376b2b78f0
4.0 KiB
text/x-python
README: Fix typo in Benevity Ledger entry key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | cc8c03ab626a 5c73c40bccfe efe576894101 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe cc8c03ab626a cc8c03ab626a cc8c03ab626a 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 76f2707aacf7 5c73c40bccfe 5c73c40bccfe cc8c03ab626a cc8c03ab626a 07d22418d428 2c6211c9ea07 cc8c03ab626a cc8c03ab626a 07d22418d428 cc8c03ab626a 76f2707aacf7 db59d2fc8ceb db59d2fc8ceb 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 76f2707aacf7 ab9c65d20dc5 76f2707aacf7 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 9472be10f15d 5c73c40bccfe 5c73c40bccfe cc8c03ab626a 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 5c73c40bccfe 5c73c40bccfe d2f8772e08fb 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 efe576894101 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe 5c73c40bccfe | import collections
import contextlib
import decimal
import logging
import sys
from . import config, errors, hooks, importers
logger = logging.getLogger('import2ledger')
class FileImporter:
def __init__(self, config, stdout):
self.config = config
self.importers = list(importers.load_all())
self.hooks = [hook(config) for hook in hooks.load_all()]
self.stdout = stdout
def import_file(self, in_file, in_path=None):
if in_path is None:
in_path = pathlib.Path(in_file.name)
importers = []
for importer in self.importers:
in_file.seek(0)
if importer.can_import(in_file):
importers.append(importer)
if not importers:
raise errors.UserInputFileError("no importers available", in_file.name)
source_vars = {
'source_abspath': in_path.absolute().as_posix(),
'source_absdir': in_path.absolute().parent.as_posix(),
'source_dir': in_path.parent.as_posix(),
'source_name': in_path.name,
'source_path': in_path.as_posix(),
'source_stem': in_path.stem,
}
for importer in importers:
source_vars['importer_class'] = importer.__name__
source_vars['importer_module'] = importer.__module__
in_file.seek(0)
for entry_data in importer(in_file):
entry_data = collections.ChainMap(entry_data, source_vars)
for hook in self.hooks:
hook_retval = hook.run(entry_data)
if hook_retval is None:
pass
elif hook_retval is False:
break
else:
entry_data = hook_retval
def import_path(self, in_path):
if in_path is None:
raise errors.UserInputFileError("only seekable files are supported", '<stdin>')
with in_path.open(errors='replace') as in_file:
if not in_file.seekable():
raise errors.UserInputFileError("only seekable files are supported", in_path)
return self.import_file(in_file, in_path)
def import_paths(self, path_seq):
for in_path in path_seq:
try:
retval = self.import_path(in_path)
except (OSError, errors.UserInputError) as error:
yield in_path, error
else:
yield in_path, retval
def setup_logger(logger, main_config, stream):
formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
handler = logging.StreamHandler(stream)
handler.setFormatter(formatter)
logger.addHandler(handler)
def decimal_context(base=decimal.BasicContext):
context = base.copy()
context.rounding = decimal.ROUND_HALF_EVEN
context.traps = {
decimal.Clamped: True,
decimal.DivisionByZero: True,
decimal.FloatOperation: True,
decimal.Inexact: False,
decimal.InvalidOperation: True,
decimal.Overflow: True,
decimal.Rounded: False,
decimal.Subnormal: True,
decimal.Underflow: True,
}
return context
def main(arglist=None, stdout=sys.stdout, stderr=sys.stderr):
try:
my_config = config.Configuration(arglist, stdout, stderr)
except errors.UserInputError as error:
my_config.error("{}: {!r}".format(error.strerror, error.user_input))
return 3
setup_logger(logger, my_config, stderr)
with decimal.localcontext(decimal_context()):
importer = FileImporter(my_config, stdout)
failures = 0
for input_path, error in importer.import_paths(my_config.args.input_paths):
if error is None:
logger.info("%s: imported", input_path)
else:
logger.warning("%s: failed to import: %s", input_path or error.path, error)
failures += 1
if failures == 0:
return 0
else:
return min(10 + failures, 99)
if __name__ == '__main__':
exit(main())
|