From f6b7a61960e4bee2329111325d5c9112df455daa Mon Sep 17 00:00:00 2001 From: Martijn Hemeryck Date: Fri, 15 Mar 2019 10:46:27 +0100 Subject: [PATCH] Major cleanup Changes done: - python3 compatible - black autoformat - isort import order - pipenv for dependency management - migrate from deprecated travis-ci.org to circleci --- .circleci/config.yml | 74 ++++++ .coveragerc | 5 + .flake8 | 4 + .isort.cfg | 6 + .travis.yml | 10 - LICENSE.txt | 2 +- Pipfile | 18 ++ Pipfile.lock | 288 +++++++++++++++++++++ README.md | 2 +- pycoda/__init__.py | 4 +- pycoda/codafile.py | 28 ++- pycoda/factories.py | 69 ++--- pycoda/fields.py | 112 +++++---- pycoda/records.py | 443 +++++++++++++++++---------------- pycoda/tests/__init__.py | 2 - pycoda/tests/test_codafile.py | 12 +- pycoda/tests/test_factories.py | 25 +- pycoda/tests/test_fields.py | 294 +++++++++++----------- pycoda/tests/test_records.py | 152 ++++++----- pytest.ini | 2 + requirements.txt | 22 -- setup.py | 22 +- 22 files changed, 1022 insertions(+), 574 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .isort.cfg delete mode 100644 .travis.yml create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 pytest.ini delete mode 100644 requirements.txt diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..08dc4d5 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,74 @@ +version: 2 +references: + restore_caches: &restore_caches + restore_cache: + keys: + - source-{{ .Branch }} + - deps-{{ .Branch }}-{{ checksum "Pipfile.lock" }} + install_deps: &install_deps + run: + name: Install dependencies + command: | + pipenv install --dev --ignore-pipfile --deploy + +jobs: + build: + docker: + - image: circleci/python:3.7.2 + + steps: + - checkout + - *restore_caches + - *install_deps + - save_cache: + key: source-{{ .Branch }} + paths: + - ".git" + - save_cache: + key: deps-{{ .Branch }}-{{ checksum "Pipfile.lock" }} + paths: + - "/home/circleci/.local/share/virtualenvs/" + lint: + docker: + - image: circleci/python:3.7.2 + + steps: + - checkout + - *restore_caches + - *install_deps + - run: + name: Black autoformat check + command: | + pipenv run black --check . + - run: + name: Isort import order check + command: | + pipenv run isort -c + test: + docker: + - image: circleci/python:3.7.2 + + steps: + - checkout + - *restore_caches + - *install_deps + - run: + name: Unit tests + command: | + pipenv run py.test + - run: + name: Coverage upload + command: | + pipenv run coveralls + +workflows: + version: 2 + build_test: + jobs: + - build + - lint: + requires: + - build + - test: + requires: + - build diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..841036d --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +branch = True + +[report] +fail_under = 100 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..607eade --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 88 +select = C,E,F,W,B,B950 +ignore = E501,E203 diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..669a4ee --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,6 @@ +[settings] +multi_line_output=3 +include_trailing_comma=True +force_grid_wrap=0 +combine_as_imports=True +line_length=88 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0d8ab3c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: python -python: - - "2.7" -install: "pip install -r requirements.txt" -script: - - flake8 . - - python -m unittest discover - - coverage run --source=pycoda/ -m unittest discover -after_success: - - coveralls diff --git a/LICENSE.txt b/LICENSE.txt index f0fc3c2..bf2fb17 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Martijn Hemeryck +Copyright (c) 2019 Martijn Hemeryck Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..59b713c --- /dev/null +++ b/Pipfile @@ -0,0 +1,18 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] +pytest = "*" +black = "==18.9b0" +pytest-cov = "*" +isort = "*" +coveralls = "*" + +[packages] +factory-boy = "*" +pytest-socket = "*" + +[requires] +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..8408570 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,288 @@ +{ + "_meta": { + "hash": { + "sha256": "2f76c6f97d5d84db40ac7680a94a74a98ff1efdb966a08a95e5a6451902db66c" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "factory-boy": { + "hashes": [ + "sha256:6f25cc4761ac109efd503f096e2ad99421b1159f01a29dbb917359dcd68e08ca", + "sha256:d552cb872b310ae78bd7429bf318e42e1e903b1a109e899a523293dfa762ea4f" + ], + "index": "pypi", + "version": "==2.11.1" + }, + "faker": { + "hashes": [ + "sha256:00b7011757c4907546f17d0e47df098b542ea2b04c966ee0e80a493aae2c13c8", + "sha256:745ac8b9c9526e338696e07b7f2e206e5e317e5744e22fdd7c2894bf19af41f1" + ], + "version": "==1.0.4" + }, + "more-itertools": { + "hashes": [ + "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", + "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + ], + "markers": "python_version > '2.7'", + "version": "==6.0.0" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pytest": { + "hashes": [ + "sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523", + "sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4" + ], + "version": "==4.3.1" + }, + "pytest-socket": { + "hashes": [ + "sha256:a90401b7a6db7091add1e33fc94a29dae6b449fdc469a725bd7cea303d12a7ed" + ], + "index": "pypi", + "version": "==0.3.3" + }, + "python-dateutil": { + "hashes": [ + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + ], + "version": "==2.8.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "text-unidecode": { + "hashes": [ + "sha256:5a1375bb2ba7968740508ae38d92e1f889a0832913cb1c447d5e2046061a396d", + "sha256:801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc" + ], + "version": "==1.2" + } + }, + "develop": { + "appdirs": { + "hashes": [ + "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", + "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" + ], + "version": "==1.4.3" + }, + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "black": { + "hashes": [ + "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", + "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5" + ], + "index": "pypi", + "version": "==18.9b0" + }, + "certifi": { + "hashes": [ + "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", + "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + ], + "version": "==2019.3.9" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "version": "==7.0" + }, + "coverage": { + "hashes": [ + "sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", + "sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", + "sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", + "sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", + "sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", + "sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", + "sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", + "sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", + "sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", + "sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", + "sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", + "sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", + "sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", + "sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", + "sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", + "sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", + "sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", + "sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", + "sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", + "sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", + "sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", + "sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", + "sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", + "sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", + "sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", + "sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", + "sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", + "sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", + "sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", + "sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", + "sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a" + ], + "version": "==4.5.3" + }, + "coveralls": { + "hashes": [ + "sha256:6f213e461390973f4a97fb9e9d4ebd4956af296ff0a4d868e622108145835cb7", + "sha256:a7d0078c9e9b5692c03dcd3884647e837836c265c01e98094632feadef767d36" + ], + "index": "pypi", + "version": "==1.6.0" + }, + "docopt": { + "hashes": [ + "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491" + ], + "version": "==0.6.2" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "isort": { + "hashes": [ + "sha256:18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b", + "sha256:96151fca2c6e736503981896495d344781b60d18bfda78dc11b290c6125ebdb6" + ], + "index": "pypi", + "version": "==4.3.15" + }, + "more-itertools": { + "hashes": [ + "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", + "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + ], + "markers": "python_version > '2.7'", + "version": "==6.0.0" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pytest": { + "hashes": [ + "sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523", + "sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4" + ], + "version": "==4.3.1" + }, + "pytest-cov": { + "hashes": [ + "sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33", + "sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f" + ], + "index": "pypi", + "version": "==2.6.1" + }, + "requests": { + "hashes": [ + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" + ], + "version": "==2.21.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "toml": { + "hashes": [ + "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", + "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + ], + "version": "==0.10.0" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "version": "==1.24.1" + } + } +} diff --git a/README.md b/README.md index b863d31..f1e8cbc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pycoda -[![Build Status](https://travis-ci.org/mhemeryck/pycoda.svg?branch=master)](https://travis-ci.org/mhemeryck/pycoda) +[![CircleCI](https://circleci.com/gh/mhemeryck/pycoda.svg?style=svg)](https://circleci.com/gh/mhemeryck/pycoda) [![Coverage Status](https://coveralls.io/repos/github/mhemeryck/pycoda/badge.svg)](https://coveralls.io/github/mhemeryck/pycoda) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) diff --git a/pycoda/__init__.py b/pycoda/__init__.py index d0c1224..d9df779 100644 --- a/pycoda/__init__.py +++ b/pycoda/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals - -__version__ = '0.3.3' +__version__ = "0.3.4" diff --git a/pycoda/codafile.py b/pycoda/codafile.py index 9f77aeb..d085255 100644 --- a/pycoda/codafile.py +++ b/pycoda/codafile.py @@ -1,15 +1,21 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from os import linesep -from pycoda.records import (InitialRecord, OldBalanceRecord, TransactionRecord, - TransactionPurposeRecord, TransactionDetailRecord, - InformationRecord, - InformationPurposeRecord, InformationDetailRecord, - NewBalanceRecord, ExtraMessageRecord, FinalRecord, - RecordIdentification) - +from pycoda.records import ( + ExtraMessageRecord, + FinalRecord, + InformationDetailRecord, + InformationPurposeRecord, + InformationRecord, + InitialRecord, + NewBalanceRecord, + OldBalanceRecord, + RecordIdentification, + TransactionDetailRecord, + TransactionPurposeRecord, + TransactionRecord, +) RECORD_TYPES = ( InitialRecord, @@ -37,8 +43,10 @@ def __init__(self, records=None): def _record_from_header(self, line): """Builds record from type, read from first 2 entries on the line""" record_id = int(line[0]) - if record_id in (RecordIdentification.TRANSACTION, - RecordIdentification.INFORMATION): + if record_id in ( + RecordIdentification.TRANSACTION, + RecordIdentification.INFORMATION, + ): article_id = int(line[1]) else: article_id = None diff --git a/pycoda/factories.py b/pycoda/factories.py index 0b040db..5925be8 100644 --- a/pycoda/factories.py +++ b/pycoda/factories.py @@ -1,18 +1,25 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from datetime import date, datetime from decimal import Decimal -from factory import fuzzy, Factory, Trait, LazyFunction, LazyAttribute +from factory import Factory, LazyAttribute, LazyFunction, Trait, fuzzy from factory.faker import Faker from pycoda.codafile import CodaFile -from pycoda.records import (InitialRecord, OldBalanceRecord, TransactionRecord, - TransactionPurposeRecord, TransactionDetailRecord, - InformationRecord, - InformationPurposeRecord, InformationDetailRecord, - NewBalanceRecord, ExtraMessageRecord, FinalRecord) +from pycoda.records import ( + ExtraMessageRecord, + FinalRecord, + InformationDetailRecord, + InformationPurposeRecord, + InformationRecord, + InitialRecord, + NewBalanceRecord, + OldBalanceRecord, + TransactionDetailRecord, + TransactionPurposeRecord, + TransactionRecord, +) class InitialRecordFactory(Factory): @@ -21,10 +28,10 @@ class Meta: creation_date = fuzzy.FuzzyDate(date(2000, 1, 1)) bank_identification_number = fuzzy.FuzzyInteger(999) - is_duplicate = Faker(b'boolean', chance_of_getting_true=10) + is_duplicate = Faker("boolean", chance_of_getting_true=10) reference = fuzzy.FuzzyText(length=10) - addressee = Faker(b'name') - bic = fuzzy.FuzzyChoice(('KREDBEBB', 'GKCCBEBB', 'BBRUBEBB')) + addressee = Faker("name") + bic = fuzzy.FuzzyChoice(("KREDBEBB", "GKCCBEBB", "BBRUBEBB")) account_holder_reference = fuzzy.FuzzyInteger(99999999999) free = None transaction_reference = None @@ -33,11 +40,11 @@ class Meta: class Params: direct_debit = Trait( bank_identification_number=725, - reference='00417969', - addressee='UNLEASHED NV', - bic='KREDBEBB', + reference="00417969", + addressee="UNLEASHED NV", + bic="KREDBEBB", account_holder_reference=886946917, - free='00000' + free="00000", ) @@ -52,10 +59,10 @@ class Params: direct_debit = Trait( account_structure=2, serial_number=256, - account_number='BE02737026917240 EUR', + account_number="BE02737026917240 EUR", balance_sign=0, - account_holder_name='UNLEASHED NV', - account_description='Bedrijfsrekening', + account_holder_name="UNLEASHED NV", + account_description="Bedrijfsrekening", bank_statement_serial_number=119, ) @@ -72,9 +79,9 @@ class Meta: class Params: direct_debit = Trait( detail_number=1, - bank_reference_number='AQQE12627 BHKDGLGTESC', + bank_reference_number="AQQE12627 BHKDGLGTESC", balance_sign=1, - balance=Decimal('15.000'), + balance=Decimal("15.000"), balance_date=LazyFunction(datetime.now), transaction_code=350000, reference_type=0, @@ -83,7 +90,7 @@ class Params: bank_statement_serial_number=256, globalisation_code=1, transaction_sequence=True, - information_sequence=True + information_sequence=True, ) @@ -98,7 +105,8 @@ class Params: direct_debit = Trait( detail_number=2, client_reference=LazyAttribute( - lambda o: 'C20160903040112-{o.collect_file_id}F'.format(o=o)), + lambda o: "C20160903040112-{o.collect_file_id}F".format(o=o) + ), transaction_sequence=True, information_sequence=True, ) @@ -109,14 +117,13 @@ class Meta: model = TransactionDetailRecord serial_number = fuzzy.FuzzyInteger(1e3 - 1) - account_holder_name = Faker(b'name') + account_holder_name = Faker("name") class Params: payment_id = 1 direct_debit = Trait( - account_number='BE68539007547034', - description=LazyAttribute( - lambda o: '1-{o.payment_id}'.format(o=o)), + account_number="BE68539007547034", + description=LazyAttribute(lambda o: "1-{o.payment_id}".format(o=o)), serial_number=22, ) @@ -130,8 +137,8 @@ class Params: detail_number=4, information_sequence=False, transaction_sequence=False, - reference='001Martijn', - reference_number='CVYC01435BSCTOBAOVERS', + reference="001Martijn", + reference_number="CVYC01435BSCTOBAOVERS", reference_type=1, serial_number=22, transaction_code=150000, @@ -166,12 +173,10 @@ class Meta: credit = fuzzy.FuzzyDecimal(1e6, precision=3) debit = fuzzy.FuzzyDecimal(1e6, precision=3) - number_records = fuzzy.FuzzyInteger(1e6-1) + number_records = fuzzy.FuzzyInteger(1e6 - 1) class Params: - direct_debit = Trait( - has_sequence=False, - ) + direct_debit = Trait(has_sequence=False) class CodaFileFactory(Factory): @@ -196,5 +201,5 @@ class Params: TransactionDetailRecordFactory(direct_debit=True), InformationRecordFactory(direct_debit=True), FinalRecordFactory(direct_debit=True), - ], + ] ) diff --git a/pycoda/fields.py b/pycoda/fields.py index 2d8bed0..a421ee1 100644 --- a/pycoda/fields.py +++ b/pycoda/fields.py @@ -1,11 +1,10 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals import re from abc import ABCMeta, abstractmethod from datetime import datetime - from decimal import Decimal + from six import with_metaclass @@ -24,9 +23,10 @@ def _parse(self, string): """Find match with regex and return if found""" match = re.match(self._regex(), string) if match is None: - raise ValueError('Specified string {} ' - 'does not match field regex'.format(string)) - return match.group('value') + raise ValueError( + "Specified string {} does not match field regex".format(string) + ) + return match.group("value") @abstractmethod def dumps(self): @@ -38,24 +38,23 @@ def loads(self, string): class StringField(Field): - def __init__(self, position, length, value=None, tag=None, - pad='', align='<'): - super(StringField, self).__init__(position, length, - value=value, tag=tag) + def __init__(self, position, length, value=None, tag=None, pad="", align="<"): + super(StringField, self).__init__(position, length, value=value, tag=tag) self.pad = pad self.align = align def _regex(self): - return (r'^(?P[\w\s\-\&\.\/\(\)\'\,]' - r'{{{self.length}}})$').format(self=self) + return (r"^(?P[\w\s\-\&\.\/\(\)\'\,]{{{self.length}}})$").format( + self=self + ) def _parse(self, string): return super(StringField, self)._parse(string) def dumps(self): - value = self.value if self.value else '' - dump_format = '{value:{self.pad}{self.align}{self.length}s}' - return dump_format.format(self=self, value=value)[:self.length] + value = self.value if self.value else "" + dump_format = "{value:{self.pad}{self.align}{self.length}s}" + return dump_format.format(self=self, value=value)[: self.length] def loads(self, string): self.value = self._parse(string) @@ -80,7 +79,7 @@ def loads(self, string): class ZeroesField(StringField): def __init__(self, position, length, tag=None): - super(ZeroesField, self).__init__(position, length, tag=tag, pad='0') + super(ZeroesField, self).__init__(position, length, tag=tag, pad="0") def _regex(self): return super(ZeroesField, self)._regex() @@ -96,19 +95,20 @@ def loads(self, string): class NumericField(StringField): - def __init__(self, position, length, value=None, tag=None, - pad=0, align='>', head='', tail=''): - super(NumericField, self).__init__(position, length, - value=value, tag=tag, - pad=pad, align=align) + def __init__( + self, position, length, value=None, tag=None, pad=0, align=">", head="", tail="" + ): + super(NumericField, self).__init__( + position, length, value=value, tag=tag, pad=pad, align=align + ) self.head = head self.tail = tail def _regex(self): length = self.length - len(self.head) - len(self.tail) - return (r'^({self.head})?' - r'(?P[\d\s]{{{length}}})' - r'({self.tail})?$').format(self=self, length=length) + return (r"^({self.head})?(?P[\d\s]{{{length}}})({self.tail})?$").format( + self=self, length=length + ) def _parse(self, string): return super(NumericField, self)._parse(string) @@ -116,38 +116,45 @@ def _parse(self, string): def dumps(self): value = self.value if self.value else 0 length = self.length - len(self.head) - len(self.tail) - value_string = ('{value:' - '{self.pad}' - '{self.align}' - '{length}' - 'd}').format(self=self, value=value, length=length) + value_string = ("{value:{self.pad}{self.align}{length}d}").format( + self=self, value=value, length=length + ) truncated = value_string[:length] - return ('{self.head}' - '{truncated}' - '{self.tail}').format(self=self, truncated=truncated) + return ("{self.head}{truncated}{self.tail}").format( + self=self, truncated=truncated + ) def loads(self, string): self.value = int(self._parse(string)) class DateField(StringField): - def __init__(self, position, length=6, value=None, tag=None, - pad='', align='<', date_format='%d%m%y'): - super(DateField, self).__init__(position, length, value=value, tag=tag, - pad=pad, align=align) + def __init__( + self, + position, + length=6, + value=None, + tag=None, + pad="", + align="<", + date_format="%d%m%y", + ): + super(DateField, self).__init__( + position, length, value=value, tag=tag, pad=pad, align=align + ) self.date_format = date_format def _regex(self): - return r'^(?P\d{{{self.length}}})$'.format(self=self) + return r"^(?P\d{{{self.length}}})$".format(self=self) def _parse(self, string): return super(DateField, self)._parse(string) def dumps(self): if self.value is None: - raise ValueError('No valid date value available') - dump_format = '{self.value:{self.date_format}}' - return dump_format.format(self=self)[:self.length] + raise ValueError("No valid date value available") + dump_format = "{self.value:{self.date_format}}" + return dump_format.format(self=self)[: self.length] def loads(self, string): date_string = self._parse(string) @@ -158,9 +165,10 @@ class BalanceField(Field): LENGTH = 15 DECIMAL_PLACES = 3 - def __init__(self, position, value=None, tag=None, pad='0'): - super(BalanceField, self).__init__(position, BalanceField.LENGTH, - value=value, tag=tag) + def __init__(self, position, value=None, tag=None, pad="0"): + super(BalanceField, self).__init__( + position, BalanceField.LENGTH, value=value, tag=tag + ) self.pad = pad def _regex(self): @@ -175,27 +183,27 @@ def dumps(self): else: value_tuple = self.value.as_tuple() shifted = Decimal((value_tuple.sign, value_tuple.digits, 0)) - dump_format = '{shifted:{self.pad}{self.LENGTH}f}' + dump_format = "{shifted:{self.pad}{self.LENGTH}f}" return dump_format.format(self=self, shifted=shifted) def loads(self, string): parsed = Decimal(string) parsed_tuple = parsed.as_tuple() - self.value = Decimal((parsed_tuple.sign, - parsed_tuple.digits, - -BalanceField.DECIMAL_PLACES)) + self.value = Decimal( + (parsed_tuple.sign, parsed_tuple.digits, -BalanceField.DECIMAL_PLACES) + ) class BooleanField(StringField): - def __init__(self, position, length, value=False, tag=None, - true_value='1', false_value=' '): - super(BooleanField, self).__init__(position, length, - value=value, tag=tag) + def __init__( + self, position, length, value=False, tag=None, true_value="1", false_value=" " + ): + super(BooleanField, self).__init__(position, length, value=value, tag=tag) if len(true_value) != length: - raise ValueError('true_value has incorrect length') + raise ValueError("true_value has incorrect length") self.true_value = true_value if len(false_value) != length: - raise ValueError('false_value has incorrect length') + raise ValueError("false_value has incorrect length") self.false_value = false_value def _regex(self): diff --git a/pycoda/records.py b/pycoda/records.py index d490dd1..719c558 100644 --- a/pycoda/records.py +++ b/pycoda/records.py @@ -1,10 +1,14 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - import re -from pycoda.fields import (NumericField, StringField, ZeroesField, DateField, - EmptyField, BalanceField, BooleanField) +from pycoda.fields import ( + BalanceField, + BooleanField, + DateField, + EmptyField, + NumericField, + StringField, + ZeroesField, +) class RecordIdentification(object): @@ -32,82 +36,92 @@ def __init__(self): def __getattr__(self, item): """By implementing this, all the fields their values are accessible""" - field_name = '_{item}_field'.format(item=item) + field_name = "_{item}_field".format(item=item) if field_name not in self.__dict__.keys(): - raise AttributeError('Unknown value') + raise AttributeError("Unknown value") return self.__dict__[field_name].value def __setattr__(self, key, value): """By implementing this, all the fields their values are accessible""" - field_name = '_{key}_field'.format(key=key) + field_name = "_{key}_field".format(key=key) if field_name in self.__dict__.keys(): self.__dict__[field_name].value = value else: super(Record, self).__setattr__(key, value) def dumps(self): - return ''.join(field.dumps() for field in self._fields) + return "".join(field.dumps() for field in self._fields) def loads(self, string): for field in self._fields: - field.loads(string[field.position:field.position + field.length]) + field.loads(string[field.position : field.position + field.length]) def field_dict(self): """Dict-like representation of all field values""" - regex = re.compile(r'^_(?P((\w+)(_)?)+)_field') + regex = re.compile(r"^_(?P((\w+)(_)?)+)_field") dictionary = {} for key, field in self.__dict__.items(): if field in self._fields: match = regex.match(key) if match is not None: - dictionary[match.group('name')] = field.value + dictionary[match.group("name")] = field.value return dictionary class InitialRecord(Record): IDENTIFICATION = RecordIdentification.INITIAL ARTICLE = None - APPLICATION_CODE = '05' + APPLICATION_CODE = "05" VERSION_CODE = 2 - def __init__(self, - creation_date=None, - bank_identification_number=None, - is_duplicate=None, - reference=None, - addressee=None, - bic=None, - account_holder_reference=None, - free=None, - transaction_reference=None, - related_reference=None,): + def __init__( + self, + creation_date=None, + bank_identification_number=None, + is_duplicate=None, + reference=None, + addressee=None, + bic=None, + account_holder_reference=None, + free=None, + transaction_reference=None, + related_reference=None, + ): super(Record, self).__init__() self._identification_field = NumericField( - 0, 1, value=InitialRecord.IDENTIFICATION) + 0, 1, value=InitialRecord.IDENTIFICATION + ) self._zeroes_field = ZeroesField(1, 4) self._creation_date_field = DateField(5, 6, value=creation_date) self._bank_identification_number_field = NumericField( - 11, 3, value=bank_identification_number) + 11, 3, value=bank_identification_number + ) self._application_code_field = StringField( - 14, 2, value=InitialRecord.APPLICATION_CODE) + 14, 2, value=InitialRecord.APPLICATION_CODE + ) self._duplicate_field = BooleanField( - 16, 1, value=is_duplicate, true_value='D', false_value=' ') + 16, 1, value=is_duplicate, true_value="D", false_value=" " + ) self._empty_field0 = EmptyField(17, 7) self._reference_field = StringField(24, 10, value=reference) self._addressee_field = StringField(34, 26, value=addressee) self._bic_field = StringField(60, 11, value=bic) self._account_holder_reference_field = NumericField( - 71, 11, value=account_holder_reference, head='0') + 71, 11, value=account_holder_reference, head="0" + ) self._empty_field1 = EmptyField(82, 1) self._free_field = StringField(83, 5, value=free) self._transaction_reference_field = StringField( - 88, 16, value=transaction_reference, tag='20/1') + 88, 16, value=transaction_reference, tag="20/1" + ) self._related_reference_field = StringField( - 104, 16, value=related_reference, tag='21/1') + 104, 16, value=related_reference, tag="21/1" + ) self._empty_field2 = EmptyField(120, 7) self._version_code_field = NumericField( - 127, 1, value=InitialRecord.VERSION_CODE) + 127, 1, value=InitialRecord.VERSION_CODE + ) self._fields = ( self._identification_field, @@ -134,37 +148,34 @@ class OldBalanceRecord(Record): IDENTIFICATION = RecordIdentification.OLD_BALANCE ARTICLE = None - def __init__(self, - account_structure=None, - serial_number=None, - account_number=None, - balance_sign=None, - old_balance=None, - balance_date=None, - account_holder_name=None, - account_description=None, - bank_statement_serial_number=None): + def __init__( + self, + account_structure=None, + serial_number=None, + account_number=None, + balance_sign=None, + old_balance=None, + balance_date=None, + account_holder_name=None, + account_description=None, + bank_statement_serial_number=None, + ): super(OldBalanceRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=OldBalanceRecord.IDENTIFICATION) - self._account_structure_field = NumericField( - 1, 1, value=account_structure) - self._serial_number_field = NumericField( - 2, 3, value=serial_number, tag='28c/1') + 0, 1, value=OldBalanceRecord.IDENTIFICATION + ) + self._account_structure_field = NumericField(1, 1, value=account_structure) + self._serial_number_field = NumericField(2, 3, value=serial_number, tag="28c/1") self._account_number_field = StringField(5, 37, value=account_number) - self._balance_sign_field = NumericField( - 42, 1, value=balance_sign, tag='60F/1') - self._old_balance_field = BalanceField( - 43, value=old_balance, tag='60F/4') - self._balance_date_field = DateField( - 58, 6, value=balance_date, tag='60F/2') - self._account_holder_name_field = StringField( - 64, 26, value=account_holder_name) - self._account_description_field = StringField( - 90, 35, value=account_description) + self._balance_sign_field = NumericField(42, 1, value=balance_sign, tag="60F/1") + self._old_balance_field = BalanceField(43, value=old_balance, tag="60F/4") + self._balance_date_field = DateField(58, 6, value=balance_date, tag="60F/2") + self._account_holder_name_field = StringField(64, 26, value=account_holder_name) + self._account_description_field = StringField(90, 35, value=account_description) self._bank_statement_serial_number_field = NumericField( - 125, 3, value=bank_statement_serial_number) + 125, 3, value=bank_statement_serial_number + ) self._fields = ( self._identification_field, @@ -184,56 +195,54 @@ class TransactionRecord(Record): IDENTIFICATION = RecordIdentification.TRANSACTION ARTICLE = RecordArticle.DEFAULT - def __init__(self, - serial_number=None, - detail_number=None, - bank_reference_number=None, - balance_sign=None, - balance=None, - balance_date=None, - transaction_code=None, - reference_type=None, - reference=None, - booking_date=None, - bank_statement_serial_number=None, - globalisation_code=None, - transaction_sequence=None, - information_sequence=None): + def __init__( + self, + serial_number=None, + detail_number=None, + bank_reference_number=None, + balance_sign=None, + balance=None, + balance_date=None, + transaction_code=None, + reference_type=None, + reference=None, + booking_date=None, + bank_statement_serial_number=None, + globalisation_code=None, + transaction_sequence=None, + information_sequence=None, + ): super(TransactionRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=TransactionPurposeRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=TransactionRecord.ARTICLE) - self._serial_number_field = NumericField( - 2, 4, value=serial_number) - self._detail_number_field = NumericField( - 6, 4, value=detail_number) + 0, 1, value=TransactionPurposeRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=TransactionRecord.ARTICLE) + self._serial_number_field = NumericField(2, 4, value=serial_number) + self._detail_number_field = NumericField(6, 4, value=detail_number) self._bank_reference_number_field = StringField( - 10, 21, value=bank_reference_number, tag='61/8') - self._balance_sign_field = NumericField( - 31, 1, value=balance_sign, tag='61/3') - self._balance_field = BalanceField(32, value=balance, tag='61/5') - self._balance_date_field = DateField( - 47, 6, value=balance_date, tag='61/1') + 10, 21, value=bank_reference_number, tag="61/8" + ) + self._balance_sign_field = NumericField(31, 1, value=balance_sign, tag="61/3") + self._balance_field = BalanceField(32, value=balance, tag="61/5") + self._balance_date_field = DateField(47, 6, value=balance_date, tag="61/1") self._transaction_code_field = NumericField( - 53, 8, value=transaction_code, tag='61/6') + 53, 8, value=transaction_code, tag="61/6" + ) self._reference_type_field = NumericField(61, 1, value=reference_type) - self._reference_field = StringField( - 62, 53, value=reference, tag='61/9') - self._booking_date_field = DateField( - 115, 6, value=booking_date, tag='61/2') + self._reference_field = StringField(62, 53, value=reference, tag="61/9") + self._booking_date_field = DateField(115, 6, value=booking_date, tag="61/2") self._bank_statement_serial_number_field = NumericField( - 121, 3, value=bank_statement_serial_number, tag='28/c') - self._globalisation_code_field = NumericField( - 124, 1, value=globalisation_code) + 121, 3, value=bank_statement_serial_number, tag="28/c" + ) + self._globalisation_code_field = NumericField(124, 1, value=globalisation_code) self._transaction_sequence_field = BooleanField( - 125, 1, value=transaction_sequence, - true_value='1', false_value='0') + 125, 1, value=transaction_sequence, true_value="1", false_value="0" + ) self._empty_field = EmptyField(126, 1) self._information_sequence_field = BooleanField( - 127, 1, value=information_sequence, - true_value='1', false_value='0') + 127, 1, value=information_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -260,39 +269,39 @@ class TransactionPurposeRecord(Record): IDENTIFICATION = RecordIdentification.TRANSACTION ARTICLE = RecordArticle.PURPOSE - def __init__(self, - serial_number=None, - detail_number=None, - bank_statement=None, - client_reference=None, - bic=None, - purpose_category=None, - purpose=None, - transaction_sequence=None, - information_sequence=None): + def __init__( + self, + serial_number=None, + detail_number=None, + bank_statement=None, + client_reference=None, + bic=None, + purpose_category=None, + purpose=None, + transaction_sequence=None, + information_sequence=None, + ): super(Record, self).__init__() self._identification_field = NumericField( - 0, 1, value=TransactionPurposeRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=TransactionPurposeRecord.ARTICLE) + 0, 1, value=TransactionPurposeRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=TransactionPurposeRecord.ARTICLE) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) self._bank_statement_field = StringField(10, 53, value=bank_statement) - self._client_reference_field = StringField( - 63, 35, value=client_reference) + self._client_reference_field = StringField(63, 35, value=client_reference) self._bic_field = StringField(98, 11, value=bic) self._empty_field0 = EmptyField(109, 8) - self._purpose_category_field = StringField( - 117, 4, value=purpose_category) + self._purpose_category_field = StringField(117, 4, value=purpose_category) self._purpose_field = StringField(121, 4, value=purpose) self._transaction_sequence_field = BooleanField( - 125, 1, value=transaction_sequence, - true_value='1', false_value='0') + 125, 1, value=transaction_sequence, true_value="1", false_value="0" + ) self._empty_field1 = EmptyField(126, 1) self._information_sequence_field = BooleanField( - 127, 1, value=information_sequence, - true_value='1', false_value='0') + 127, 1, value=information_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -315,30 +324,31 @@ class TransactionDetailRecord(Record): IDENTIFICATION = RecordIdentification.TRANSACTION ARTICLE = RecordArticle.DETAIL - def __init__(self, - serial_number=None, - detail_number=None, - account_number=None, - account_holder_name=None, - description=None, - information_sequence=None): + def __init__( + self, + serial_number=None, + detail_number=None, + account_number=None, + account_holder_name=None, + description=None, + information_sequence=None, + ): super(TransactionDetailRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=TransactionDetailRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=TransactionDetailRecord.ARTICLE) + 0, 1, value=TransactionDetailRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=TransactionDetailRecord.ARTICLE) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) self._account_number_field = StringField(10, 37, value=account_number) - self._account_holder_name_field = StringField( - 47, 35, value=account_holder_name) + self._account_holder_name_field = StringField(47, 35, value=account_holder_name) self._description_field = StringField(82, 43, value=description) self._sequence_code_field = ZeroesField(125, 1) self._empty_field = EmptyField(126, 1) self._information_sequence_field = BooleanField( - 127, 1, value=information_sequence, - true_value='1', false_value='0') + 127, 1, value=information_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -358,37 +368,41 @@ class InformationRecord(Record): IDENTIFICATION = RecordIdentification.INFORMATION ARTICLE = RecordArticle.DEFAULT - def __init__(self, - serial_number=None, - detail_number=None, - reference_number=None, - transaction_code=None, - reference_type=None, - reference=None, - transaction_sequence=None, - information_sequence=None): + def __init__( + self, + serial_number=None, + detail_number=None, + reference_number=None, + transaction_code=None, + reference_type=None, + reference=None, + transaction_sequence=None, + information_sequence=None, + ): super(InformationRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=InformationRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=InformationRecord.ARTICLE) + 0, 1, value=InformationRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=InformationRecord.ARTICLE) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) self._reference_number_field = StringField( - 10, 21, value=reference_number, tag='61/8') + 10, 21, value=reference_number, tag="61/8" + ) self._transaction_code_field = NumericField( - 31, 8, value=transaction_code, tag='61/6') + 31, 8, value=transaction_code, tag="61/6" + ) self._reference_type_field = NumericField(39, 1, value=reference_type) - self._reference_field = StringField(40, 73, value=reference, tag='86') + self._reference_field = StringField(40, 73, value=reference, tag="86") self._empty_field0 = EmptyField(113, 12) self._transaction_sequence_field = BooleanField( - 125, 1, value=transaction_sequence, - true_value='1', false_value='0') + 125, 1, value=transaction_sequence, true_value="1", false_value="0" + ) self._empty_field1 = EmptyField(126, 1) self._information_sequence_field = BooleanField( - 127, 1, value=information_sequence, - true_value='1', false_value='0') + 127, 1, value=information_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -402,7 +416,7 @@ def __init__(self, self._empty_field0, self._transaction_sequence_field, self._empty_field1, - self._information_sequence_field + self._information_sequence_field, ) @@ -410,30 +424,33 @@ class InformationPurposeRecord(Record): IDENTIFICATION = RecordIdentification.INFORMATION ARTICLE = RecordArticle.PURPOSE - def __init__(self, - serial_number=None, - detail_number=None, - bank_reference_number=None, - information_sequence0=None, - information_sequence1=None): + def __init__( + self, + serial_number=None, + detail_number=None, + bank_reference_number=None, + information_sequence0=None, + information_sequence1=None, + ): super(InformationPurposeRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=InformationPurposeRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=InformationPurposeRecord.ARTICLE) + 0, 1, value=InformationPurposeRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=InformationPurposeRecord.ARTICLE) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) self._bank_reference_number_field = StringField( - 10, 105, value=bank_reference_number) + 10, 105, value=bank_reference_number + ) self._empty_field0 = EmptyField(115, 10) self._information_sequence_field0 = BooleanField( - 125, 1, value=information_sequence0, - true_value='1', false_value='0') + 125, 1, value=information_sequence0, true_value="1", false_value="0" + ) self._empty_field1 = EmptyField(126, 1) self._information_sequence_field1 = BooleanField( - 127, 1, value=information_sequence1, - true_value='1', false_value='0') + 127, 1, value=information_sequence1, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -452,27 +469,30 @@ class InformationDetailRecord(Record): IDENTIFICATION = RecordIdentification.INFORMATION ARTICLE = RecordArticle.DETAIL - def __init__(self, - serial_number=None, - detail_number=None, - bank_reference_number=None, - information_sequence=None): + def __init__( + self, + serial_number=None, + detail_number=None, + bank_reference_number=None, + information_sequence=None, + ): super(InformationDetailRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=InformationPurposeRecord.IDENTIFICATION) - self._article_field = NumericField( - 1, 1, value=InformationPurposeRecord.ARTICLE) + 0, 1, value=InformationPurposeRecord.IDENTIFICATION + ) + self._article_field = NumericField(1, 1, value=InformationPurposeRecord.ARTICLE) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) self._bank_reference_number_field = StringField( - 10, 90, value=bank_reference_number) + 10, 90, value=bank_reference_number + ) self._empty_field0 = EmptyField(100, 25) self._sequence_code_field = ZeroesField(125, 1) self._empty_field1 = EmptyField(126, 1) self._information_sequence_field = BooleanField( - 127, 1, value=information_sequence, - true_value='1', false_value='0') + 127, 1, value=information_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -491,30 +511,29 @@ class NewBalanceRecord(Record): IDENTIFICATION = RecordIdentification.NEW_BALANCE ARTICLE = None - def __init__(self, - serial_number=None, - account_number=None, - balance_sign=None, - new_balance=None, - balance_date=None, - sequence=None): + def __init__( + self, + serial_number=None, + account_number=None, + balance_sign=None, + new_balance=None, + balance_date=None, + sequence=None, + ): super(NewBalanceRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=NewBalanceRecord.IDENTIFICATION) - self._serial_number_field = NumericField( - 1, 3, value=serial_number, tag='28c/1') - self._account_number_field = StringField( - 4, 37, value=account_number) - self._balance_sign_field = NumericField( - 41, 1, value=balance_sign, tag='62F/1') - self._new_balance_field = BalanceField( - 42, value=new_balance, tag='62F/4') - self._balance_date_field = DateField( - 57, 6, value=balance_date, tag='62F/2') + 0, 1, value=NewBalanceRecord.IDENTIFICATION + ) + self._serial_number_field = NumericField(1, 3, value=serial_number, tag="28c/1") + self._account_number_field = StringField(4, 37, value=account_number) + self._balance_sign_field = NumericField(41, 1, value=balance_sign, tag="62F/1") + self._new_balance_field = BalanceField(42, value=new_balance, tag="62F/4") + self._balance_date_field = DateField(57, 6, value=balance_date, tag="62F/2") self._empty_field = EmptyField(63, 64) self._sequence_field = BooleanField( - 127, 1, value=sequence, true_value='1', false_value='0') + 127, 1, value=sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -532,15 +551,18 @@ class ExtraMessageRecord(Record): IDENTIFICATION = RecordIdentification.EXTRA_MESSAGE ARTICLE = None - def __init__(self, - serial_number=None, - detail_number=None, - extra_message=None, - has_sequence=None,): + def __init__( + self, + serial_number=None, + detail_number=None, + extra_message=None, + has_sequence=None, + ): super(ExtraMessageRecord, self).__init__() self._identification_field = NumericField( - 0, 1, value=ExtraMessageRecord.IDENTIFICATION) + 0, 1, value=ExtraMessageRecord.IDENTIFICATION + ) self._empty_field0 = EmptyField(1, 1) self._serial_number_field = NumericField(2, 4, value=serial_number) self._detail_number_field = NumericField(6, 4, value=detail_number) @@ -548,7 +570,8 @@ def __init__(self, self._extra_message_field = StringField(32, 80, value=extra_message) self._empty_field2 = EmptyField(112, 15) self._sequence_field = BooleanField( - 127, 1, value=has_sequence, true_value='1', false_value='0') + 127, 1, value=has_sequence, true_value="1", false_value="0" + ) self._fields = ( self._identification_field, @@ -566,22 +589,18 @@ class FinalRecord(Record): IDENTIFICATION = RecordIdentification.FINAL ARTICLE = None - def __init__(self, - number_records=None, - debit=None, - credit=None, - has_sequence=None): + def __init__(self, number_records=None, debit=None, credit=None, has_sequence=None): super(FinalRecord, self).__init__() - self._identification_field = NumericField( - 0, 1, FinalRecord.IDENTIFICATION) + self._identification_field = NumericField(0, 1, FinalRecord.IDENTIFICATION) self._empty_field0 = EmptyField(1, 15) self._number_records_field = NumericField(16, 6, value=number_records) self._debit_field = BalanceField(22, value=debit) self._credit_field = BalanceField(37, value=credit) self._empty_field1 = EmptyField(52, 75) self._sequence_field = BooleanField( - 127, 1, value=has_sequence, true_value='1', false_value='2') + 127, 1, value=has_sequence, true_value="1", false_value="2" + ) self._fields = ( self._identification_field, diff --git a/pycoda/tests/__init__.py b/pycoda/tests/__init__.py index a2b5079..e69de29 100644 --- a/pycoda/tests/__init__.py +++ b/pycoda/tests/__init__.py @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals diff --git a/pycoda/tests/test_codafile.py b/pycoda/tests/test_codafile.py index 8c9eac2..6499761 100644 --- a/pycoda/tests/test_codafile.py +++ b/pycoda/tests/test_codafile.py @@ -1,17 +1,21 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from os import linesep from unittest import TestCase from pycoda.codafile import CodaFile -from pycoda.tests.test_records import (InitialRecordTest, OldBalanceRecordTest, - TransactionRecordTest, FinalRecordTest, - NewBalanceRecordTest) +from pycoda.tests.test_records import ( + FinalRecordTest, + InitialRecordTest, + NewBalanceRecordTest, + OldBalanceRecordTest, + TransactionRecordTest, +) class CodaFileTest(TestCase): """Build a minimum test from previous record tests""" + def setUp(self): self.lines = ( InitialRecordTest.RAW, diff --git a/pycoda/tests/test_factories.py b/pycoda/tests/test_factories.py index eaeaab4..2da900b 100644 --- a/pycoda/tests/test_factories.py +++ b/pycoda/tests/test_factories.py @@ -1,18 +1,21 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from unittest import TestCase -from pycoda.factories import (InitialRecordFactory, OldBalanceRecordFactory, - TransactionRecordFactory, - TransactionPurposeRecordFactory, - TransactionDetailRecordFactory, - InformationRecordFactory, - InformationPurposeRecordFactory, - InformationDetailRecordFactory, - NewBalanceRecordFactory, - FinalRecordFactory, - ExtraMessageRecordFactory, CodaFileFactory) +from pycoda.factories import ( + CodaFileFactory, + ExtraMessageRecordFactory, + FinalRecordFactory, + InformationDetailRecordFactory, + InformationPurposeRecordFactory, + InformationRecordFactory, + InitialRecordFactory, + NewBalanceRecordFactory, + OldBalanceRecordFactory, + TransactionDetailRecordFactory, + TransactionPurposeRecordFactory, + TransactionRecordFactory, +) class InitialRecordFactoryTest(TestCase): diff --git a/pycoda/tests/test_fields.py b/pycoda/tests/test_fields.py index cb8a525..3c08cf0 100644 --- a/pycoda/tests/test_fields.py +++ b/pycoda/tests/test_fields.py @@ -1,175 +1,181 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from datetime import date from decimal import Decimal from unittest import TestCase -from pycoda.fields import (NumericField, StringField, ZeroesField, DateField, - EmptyField, BalanceField, BooleanField) +from pycoda.fields import ( + BalanceField, + BooleanField, + DateField, + EmptyField, + NumericField, + StringField, + ZeroesField, +) class StringFieldTest(TestCase): def test_dumps(self): - field = StringField(0, 3, value='foo') - assert field.dumps() == 'foo' + field = StringField(0, 3, value="foo") + assert field.dumps() == "foo" def test_loads(self): field = StringField(0, 3) - field.loads('foo') - assert field.value == 'foo' + field.loads("foo") + assert field.value == "foo" def test_loads_dumps(self): field = StringField(0, 3) - field.loads('foo') - assert field.dumps() == 'foo' + field.loads("foo") + assert field.dumps() == "foo" def test_loads_from_dumps(self): - field = StringField(0, 3, value='foo') + field = StringField(0, 3, value="foo") field.loads(field.dumps()) - assert field.value == 'foo' + assert field.value == "foo" def test_new_field_from_dumps(self): - field = StringField(0, 3, value='foo') + field = StringField(0, 3, value="foo") new_field = StringField(0, 3, field.dumps()) - assert new_field.value == 'foo' + assert new_field.value == "foo" def test_init_length_shorter_than_value(self): - field = StringField(0, 2, value='foo') - assert field.value == 'foo' + field = StringField(0, 2, value="foo") + assert field.value == "foo" def test_init_length_longer_than_value(self): - field = StringField(0, 4, value='foo') - assert field.value == 'foo' + field = StringField(0, 4, value="foo") + assert field.value == "foo" def test_init_empty_value(self): - field = StringField(0, 1, value=' ') - assert field.value == ' ' + field = StringField(0, 1, value=" ") + assert field.value == " " def test_init_leading_space(self): - field = StringField(0, 4, value=' foo') - assert field.value == ' foo' + field = StringField(0, 4, value=" foo") + assert field.value == " foo" def test_init_trailing_space(self): - field = StringField(0, 4, value='foo ') - assert field.value == 'foo ' + field = StringField(0, 4, value="foo ") + assert field.value == "foo " def test_loads_length_shorter_than_value(self): field = StringField(0, 2) with self.assertRaises(ValueError): - field.loads('foo') + field.loads("foo") def test_loads_length_longer_than_value(self): field = StringField(0, 4) with self.assertRaises(ValueError): - field.loads('foo') + field.loads("foo") def test_loads_empty_string(self): field = StringField(0, 1) - field.loads(' ') - assert field.value == ' ' + field.loads(" ") + assert field.value == " " def test_loads_leading_space(self): field = StringField(0, 4) - field.loads(' foo') - assert field.value == ' foo' + field.loads(" foo") + assert field.value == " foo" def test_loads_trailing_space(self): field = StringField(0, 4) - field.loads('foo ') - assert field.value == 'foo ' + field.loads("foo ") + assert field.value == "foo " def test_loads_with_spaces(self): field = StringField(0, 11) - field.loads('foo bar qux') - assert field.value == 'foo bar qux' + field.loads("foo bar qux") + assert field.value == "foo bar qux" def test_dumps_length_shorter_than_value(self): - field = StringField(0, 2, value='foo') - assert field.dumps() == 'fo' + field = StringField(0, 2, value="foo") + assert field.dumps() == "fo" def test_dumps_length_longer_than_value(self): - field = StringField(0, 4, value='foo') - assert field.dumps() == 'foo ' + field = StringField(0, 4, value="foo") + assert field.dumps() == "foo " def test_dumps_empty_string(self): - field = StringField(0, 1, value=' ') - assert field.dumps() == ' ' + field = StringField(0, 1, value=" ") + assert field.dumps() == " " def test_dumps_empty_value(self): field = StringField(0, 3) - assert field.dumps() == ' ' + assert field.dumps() == " " def test_dumps_leading_space(self): - field = StringField(0, 4, value=' foo') - assert field.dumps() == ' foo' + field = StringField(0, 4, value=" foo") + assert field.dumps() == " foo" def test_dumps_trailing_space(self): - field = StringField(0, 4, value='foo ') - assert field.dumps() == 'foo ' + field = StringField(0, 4, value="foo ") + assert field.dumps() == "foo " def test_dumps_init_none_value(self): field = StringField(0, 3) - assert field.dumps() == ' ' + assert field.dumps() == " " def test_dumps_align_left(self): - field = StringField(0, 4, value='foo', pad=' ', align='<') - assert field.dumps() == 'foo ' + field = StringField(0, 4, value="foo", pad=" ", align="<") + assert field.dumps() == "foo " def test_dumps_align_right(self): - field = StringField(0, 4, value='foo', pad=' ', align='>') - assert field.dumps() == ' foo' + field = StringField(0, 4, value="foo", pad=" ", align=">") + assert field.dumps() == " foo" def test_dumps_align_left_pad_zero(self): - field = StringField(0, 6, value='foo', pad='0', align='<') - assert field.dumps() == 'foo000' + field = StringField(0, 6, value="foo", pad="0", align="<") + assert field.dumps() == "foo000" def test_dumps_align_right_pad_zero(self): - field = StringField(0, 6, value='foo', pad='0', align='>') - assert field.dumps() == '000foo' + field = StringField(0, 6, value="foo", pad="0", align=">") + assert field.dumps() == "000foo" def test_dumps_pad_zero(self): field = StringField(0, 3, pad=0) - assert field.dumps() == '000' + assert field.dumps() == "000" def test_loads_hyphen(self): field = StringField(0, 11) - field.loads('some-string') - assert field.value == 'some-string' + field.loads("some-string") + assert field.value == "some-string" def test_dumps_hyphen(self): - field = StringField(0, 7, value='foo-bar') - assert field.dumps() == 'foo-bar' + field = StringField(0, 7, value="foo-bar") + assert field.dumps() == "foo-bar" def test_loads_from_dumps_hyphen(self): - field = StringField(0, 7, value='foo-bar') + field = StringField(0, 7, value="foo-bar") field.loads(field.dumps()) - assert field.value == 'foo-bar' + assert field.value == "foo-bar" def test_loads_extra_space_hyphen(self): field = StringField(0, 14) - field.loads('some-string ') - assert field.value == 'some-string ' + field.loads("some-string ") + assert field.value == "some-string " def test_loads_from_dumps_ampersand(self): field = StringField(0, 11) - field.loads('drum & bass') - assert field.value == 'drum & bass' + field.loads("drum & bass") + assert field.value == "drum & bass" def test_loads_from_dumps_dot(self): field = StringField(0, 7) - field.loads('web 2.0') - assert field.value == 'web 2.0' + field.loads("web 2.0") + assert field.value == "web 2.0" def test_loads_from_dumps_forward_slash(self): field = StringField(0, 5) - field.loads('A / B') - assert field.value == 'A / B' + field.loads("A / B") + assert field.value == "A / B" def test_loads_from_dumps_forward_braces(self): field = StringField(0, 13) - field.loads('(hello world)') - assert field.value == '(hello world)' + field.loads("(hello world)") + assert field.value == "(hello world)" def test_loads_from_dumps_forward_accent(self): field = StringField(0, 7) @@ -178,89 +184,89 @@ def test_loads_from_dumps_forward_accent(self): def test_loads_from_dumps_forward_comma(self): field = StringField(0, 12) - field.loads('ebony, ivory') - assert field.value == 'ebony, ivory' + field.loads("ebony, ivory") + assert field.value == "ebony, ivory" class EmptyFieldTest(TestCase): def test_loads(self): field = EmptyField(0, 4) - field.loads(' ') - assert field.value == ' ' + field.loads(" ") + assert field.value == " " def test_dumps(self): field = EmptyField(0, 4) - assert field.dumps() == ' ' + assert field.dumps() == " " def test_load_from_dumps(self): field = EmptyField(0, 4) field.loads(field.dumps()) - assert field.value == ' ' + assert field.value == " " class ZeroesFieldTest(TestCase): def test_loads(self): field = ZeroesField(0, 3) - field.loads('000') - assert field.value == '000' + field.loads("000") + assert field.value == "000" def test_dumps(self): field = ZeroesField(0, 10) - assert field.dumps() == '0' * 10 + assert field.dumps() == "0" * 10 def test_loads_from_dumps(self): field = StringField(0, 3) field.loads(field.dumps()) - assert field.value == ' ' + assert field.value == " " class NumericFieldTest(TestCase): def test_dumps_length_value_equal(self): field = NumericField(0, 1, value=1) - assert field.dumps() == '1' + assert field.dumps() == "1" def test_dumps_length_longer_than_value(self): field = NumericField(0, 2, value=1) - assert field.dumps() == '01' + assert field.dumps() == "01" def test_dumps_length_shorter_than_value(self): field = NumericField(0, 1, value=12) - assert field.dumps() == '1' + assert field.dumps() == "1" def test_loads_length_string_equal(self): field = NumericField(0, 10) - field.loads('1234567890') + field.loads("1234567890") assert field.value == 1234567890 def test_loads_length_longer_than_string(self): field = NumericField(0, 2) with self.assertRaises(ValueError): - field.loads('1') + field.loads("1") def test_loads_trailing_space(self): field = NumericField(0, 2) - field.loads('1 ') + field.loads("1 ") assert field.value == 1 def test_loads_leading_space(self): field = NumericField(0, 2) - field.loads(' 1') + field.loads(" 1") assert field.value == 1 def test_loads_length_shorter_than_string(self): field = NumericField(0, 1) with self.assertRaises(ValueError): - field.loads('11') + field.loads("11") def test_loads_dumps_equal_same_length(self): field = NumericField(0, 10) - field.loads('9876543210') - assert field.dumps() == '9876543210' + field.loads("9876543210") + assert field.dumps() == "9876543210" def test_loads_dumps_equal_shorter(self): field = NumericField(0, 10) - field.loads('12345 ') - assert field.dumps() == '0000012345' + field.loads("12345 ") + assert field.dumps() == "0000012345" def test_loads_from_dumps(self): field = NumericField(0, 6, value=123) @@ -268,41 +274,41 @@ def test_loads_from_dumps(self): assert field.value == 123 def test_loads_leading_head(self): - field = NumericField(0, 6, head='abc') - field.loads('abc' + '123') + field = NumericField(0, 6, head="abc") + field.loads("abc" + "123") assert field.value == 123 def test_loads_trailing_tail(self): - field = NumericField(0, 6, tail='abc') - field.loads('123' + 'abc') + field = NumericField(0, 6, tail="abc") + field.loads("123" + "abc") assert field.value == 123 def test_loads_leading_head_and_trailing_tail(self): - field = NumericField(0, 12, head='000', tail='QWERTY') - field.loads('000123QWERTY') + field = NumericField(0, 12, head="000", tail="QWERTY") + field.loads("000123QWERTY") assert field.value == 123 def test_dumps_leading_head(self): - field = NumericField(0, 6, value=456, head='lol') - assert field.dumps() == 'lol456' + field = NumericField(0, 6, value=456, head="lol") + assert field.dumps() == "lol456" def test_dumps_trailing_tail(self): - field = NumericField(0, 6, value=456, tail='lol') - assert field.dumps() == '456lol' + field = NumericField(0, 6, value=456, tail="lol") + assert field.dumps() == "456lol" def test_dumps_leading_head_trailing_tail(self): - field = NumericField(0, 16, value=12357, head='asddfg', tail='zxcvb') - assert field.dumps() == 'asddfg12357zxcvb' + field = NumericField(0, 16, value=12357, head="asddfg", tail="zxcvb") + assert field.dumps() == "asddfg12357zxcvb" def test_loads_head_align_pad(self): - field = NumericField(0, 11, head='0', pad='0', align='>') - field.loads('00886946917') + field = NumericField(0, 11, head="0", pad="0", align=">") + field.loads("00886946917") assert field.value == 886946917 def test_loads_dumps_head_align_pad(self): - field = NumericField(0, 11, head='0', pad='0', align='>') - field.loads('00886946917') - assert field.dumps() == '00886946917' + field = NumericField(0, 11, head="0", pad="0", align=">") + field.loads("00886946917") + assert field.dumps() == "00886946917" class DateFieldTest(TestCase): @@ -313,21 +319,21 @@ def test_dumps_empty(self): def test_loads(self): field = DateField(0) - field.loads('280386') + field.loads("280386") assert field.value == date(1986, 3, 28) def test_dumps(self): field = DateField(0, value=date(1986, 3, 28)) - assert field.dumps() == '280386' + assert field.dumps() == "280386" def test_loads_21st_century(self): field = DateField(0) - field.loads('050417') + field.loads("050417") assert field.value == date(2017, 4, 5) def test_dumps_21st_century(self): field = DateField(0, value=date(2017, 4, 5)) - assert field.dumps() == '050417' + assert field.dumps() == "050417" def test_loads_from_dumps(self): field = DateField(0, value=date(1986, 3, 28)) @@ -335,38 +341,38 @@ def test_loads_from_dumps(self): assert field.value == date(1986, 3, 28) def test_dumps_alternate_date_format(self): - field = DateField(0, 8, value=date(2017, 4, 5), date_format='%Y%m%d') - assert field.dumps() == '20170405' + field = DateField(0, 8, value=date(2017, 4, 5), date_format="%Y%m%d") + assert field.dumps() == "20170405" def test_loads_alternate_date_format(self): - field = DateField(0, 8, date_format='%Y%m%d') - field.loads('20170405') + field = DateField(0, 8, date_format="%Y%m%d") + field.loads("20170405") assert field.value == date(2017, 4, 5) class BalanceFieldTest(TestCase): def test_loads(self): field = BalanceField(0) - field.loads('000000034568797') - assert field.value == Decimal('34568.797') + field.loads("000000034568797") + assert field.value == Decimal("34568.797") def test_dumps_none(self): field = BalanceField(0) - assert field.dumps() == '000000000000000' + assert field.dumps() == "000000000000000" def test_dumps(self): - field = BalanceField(0, value=Decimal('65536.128')) - assert field.dumps() == '000000065536128' + field = BalanceField(0, value=Decimal("65536.128")) + assert field.dumps() == "000000065536128" def test_loads_from_dumps(self): - field = BalanceField(0, value=Decimal('65536.128')) + field = BalanceField(0, value=Decimal("65536.128")) field.loads(field.dumps()) - assert field.value == Decimal('65536.128') + assert field.value == Decimal("65536.128") def test_loads_from_dumps_higher_precision(self): - field = BalanceField(0, value=Decimal('65536.1024')) + field = BalanceField(0, value=Decimal("65536.1024")) field.loads(field.dumps()) - assert field.value == Decimal('655361.024') + assert field.value == Decimal("655361.024") def test_regex_not_implemented(self): field = BalanceField(0) @@ -376,33 +382,31 @@ def test_regex_not_implemented(self): def test_parse_not_implemented(self): field = BalanceField(0) with self.assertRaises(NotImplementedError): - field._parse('') + field._parse("") class BooleanFieldTest(TestCase): def test_default_value_empty(self): field = BooleanField(0, 1) - assert field.dumps() == ' ' + assert field.dumps() == " " def test_dumps_default_1(self): field = BooleanField(0, 1, value=True) - assert field.dumps() == '1' + assert field.dumps() == "1" def test_loads_default_1(self): field = BooleanField(0, 1) - field.loads('1') + field.loads("1") assert field.value def test_loads_default_empty(self): field = BooleanField(0, 1) - field.loads(' ') + field.loads(" ") assert not field.value def test_loads_default_random(self): - field = BooleanField(0, 8, - true_value='12345678', - false_value='qwertyui') - field.loads('fsdfghjk') + field = BooleanField(0, 8, true_value="12345678", false_value="qwertyui") + field.loads("fsdfghjk") assert not field.value def test_loads_from_dumps_true(self): @@ -416,27 +420,27 @@ def test_loads_from_dumps_false(self): assert not field.value def test_dumps_diferent_true_value(self): - field = BooleanField(0, 1, value=True, true_value='0') - assert field.dumps() == '0' + field = BooleanField(0, 1, value=True, true_value="0") + assert field.dumps() == "0" def test_dumps_diferent_false_value(self): - field = BooleanField(0, 1, value=False, false_value='x') - assert field.dumps() == 'x' + field = BooleanField(0, 1, value=False, false_value="x") + assert field.dumps() == "x" def test_loads_diferent_true_value(self): - field = BooleanField(0, 1, value=True, true_value='0') - field.loads('0') + field = BooleanField(0, 1, value=True, true_value="0") + field.loads("0") assert field.value def test_loads_diferent_false_value(self): - field = BooleanField(0, 1, value=False, false_value='x') - field.loads('x') + field = BooleanField(0, 1, value=False, false_value="x") + field.loads("x") assert not field.value def test_init_wrong_true_value_length(self): with self.assertRaises(ValueError): - BooleanField(0, 1, true_value='123') + BooleanField(0, 1, true_value="123") def test_init_wrong_false_value_length(self): with self.assertRaises(ValueError): - BooleanField(0, 1, false_value='123') + BooleanField(0, 1, false_value="123") diff --git a/pycoda/tests/test_records.py b/pycoda/tests/test_records.py index 2fbc3ea..d8aaacd 100644 --- a/pycoda/tests/test_records.py +++ b/pycoda/tests/test_records.py @@ -1,19 +1,45 @@ -# -*- coding: utf-8 -*- from __future__ import unicode_literals from datetime import date from unittest import TestCase -from pycoda.records import (InitialRecord, OldBalanceRecord, TransactionRecord, - TransactionPurposeRecord, TransactionDetailRecord, - InformationRecord, InformationPurposeRecord, - InformationDetailRecord, NewBalanceRecord, - ExtraMessageRecord, FinalRecord) +from pycoda.records import ( + ExtraMessageRecord, + FinalRecord, + InformationDetailRecord, + InformationPurposeRecord, + InformationRecord, + InitialRecord, + NewBalanceRecord, + OldBalanceRecord, + Record, + TransactionDetailRecord, + TransactionPurposeRecord, + TransactionRecord, +) + + +class DummyRecordTest(TestCase): + class DummyRecord(Record): + def __init__(self): + super(Record, self).__init__() + + self._dummy = None + self._fields = (self._dummy,) + + def setUp(self): + self.record = self.DummyRecord() + + def test_field_dict(self): + """Check that if a field is set without the suffix `_field`, it does not show up""" + assert self.record.field_dict() == {} class InitialRecordTest(TestCase): - RAW = ('0000019091672505 00417969 VIKINGCO NV KRED' - 'BEBB 00886946917 00000 2') + RAW = ( + "0000019091672505 00417969 VIKINGCO NV KRED" + "BEBB 00886946917 00000 2" + ) def setUp(self): self.record = InitialRecord() @@ -44,88 +70,90 @@ def test_duplicate_code_false(self): assert not self.record.is_duplicate def test_reference(self): - self.record.reference = '00417969' - assert self.record.reference == '00417969' + self.record.reference = "00417969" + assert self.record.reference == "00417969" def test_addressee(self): - self.record.addressee = 'Unleashed NV' - assert self.record.addressee == 'Unleashed NV' + self.record.addressee = "Unleashed NV" + assert self.record.addressee == "Unleashed NV" def test_bic(self): - self.record.bic = 'KREDBEBB' - assert self.record.bic == 'KREDBEBB' + self.record.bic = "KREDBEBB" + assert self.record.bic == "KREDBEBB" def test_account_holder_reference(self): - self.record.account_holder_reference = '00886946917' - assert self.record.account_holder_reference == '00886946917' + self.record.account_holder_reference = "00886946917" + assert self.record.account_holder_reference == "00886946917" def test_free(self): - self.record.free = '12345' - assert self.record.free == '12345' + self.record.free = "12345" + assert self.record.free == "12345" def test_transaction_reference(self): - self.record.transaction_reference = 'abc123xyz' - assert self.record.transaction_reference == 'abc123xyz' + self.record.transaction_reference = "abc123xyz" + assert self.record.transaction_reference == "abc123xyz" def test_related_reference(self): - self.record.related_reference = 'qwerty' - assert self.record.related_reference == 'qwerty' + self.record.related_reference = "qwerty" + assert self.record.related_reference == "qwerty" def test_get_unknown_field_value_raises(self): with self.assertRaises(AttributeError): x = self.record.some_value # noqa def test_set_unknown_field_value_passes(self): - self.record.some_value = 'something wicked' + self.record.some_value = "something wicked" def test_field_dict_creation_date(self): self.record.creation_date = date(1986, 3, 28) - assert self.record.field_dict()['creation_date'] == date(1986, 3, 28) + assert self.record.field_dict()["creation_date"] == date(1986, 3, 28) def test_field_dict_bank_identification_number(self): self.record.bank_identification_number = 123 - assert self.record.field_dict()['bank_identification_number'] == 123 + assert self.record.field_dict()["bank_identification_number"] == 123 def test_field_dict_application_code(self): - self.record.application_code = '05' - assert self.record.field_dict()['application_code'] == '05' + self.record.application_code = "05" + assert self.record.field_dict()["application_code"] == "05" def test_field_dict_duplicate(self): self.record.duplicate = True - assert self.record.field_dict()['duplicate'] + assert self.record.field_dict()["duplicate"] def test_field_dict_reference(self): - self.record.reference = 'QWERTY' - assert self.record.field_dict()['reference'] == 'QWERTY' + self.record.reference = "QWERTY" + assert self.record.field_dict()["reference"] == "QWERTY" def test_field_dict_addressee(self): - self.record.addressee = 'Some guy' - assert self.record.field_dict()['addressee'] == 'Some guy' + self.record.addressee = "Some guy" + assert self.record.field_dict()["addressee"] == "Some guy" def test_field_dict_bic(self): - self.record.bic = 'KREDBEBB' - assert self.record.field_dict()['bic'] == 'KREDBEBB' + self.record.bic = "KREDBEBB" + assert self.record.field_dict()["bic"] == "KREDBEBB" def test_field_dict_account_holder_reference(self): - self.record.account_holder_reference = 'QWERTY' - assert self.record.field_dict()['account_holder_reference'] == 'QWERTY' + self.record.account_holder_reference = "QWERTY" + assert self.record.field_dict()["account_holder_reference"] == "QWERTY" def test_field_dict_free(self): - self.record.free = 'WILLY' - assert self.record.field_dict()['free'] == 'WILLY' + self.record.free = "WILLY" + assert self.record.field_dict()["free"] == "WILLY" def test_field_dict_transaction_reference(self): - self.record.transaction_reference = 'AZERTY' - assert self.record.field_dict()['transaction_reference'] == 'AZERTY' + self.record.transaction_reference = "AZERTY" + assert self.record.field_dict()["transaction_reference"] == "AZERTY" def test_field_dict_related_reference(self): - self.record.related_reference = 'DVORAK' - assert self.record.field_dict()['related_reference'] == 'DVORAK' + self.record.related_reference = "DVORAK" + assert self.record.field_dict()["related_reference"] == "DVORAK" class OldBalanceRecordTest(TestCase): - RAW = ('12256BE02737026917240 EUR0000005020346650150916' - 'VIKINGCO NV KBC-Bedrijfsrekening 119') + RAW = ( + "12256BE02737026917240 EUR0000005020346650150916" + "VIKINGCO NV KBC-Bedrijfsrekening 119" + ) def setUp(self): self.record = OldBalanceRecord() @@ -141,8 +169,10 @@ def test_example_loads_dumps_raw_record(self): class TransactionRecordTest(TestCase): - RAW = ('2100010000OL1002OFFASCTOVSOVERS000000000001000018081600150000110' - '1048573874287 18081623001 0') + RAW = ( + "2100010000OL1002OFFASCTOVSOVERS000000000001000018081600150000110" + "1048573874287 18081623001 0" + ) def setUp(self): self.record = TransactionRecord() @@ -158,8 +188,10 @@ def test_example_loads_dumps_raw_record(self): class TransactionPurposeRecordTest(TestCase): - RAW = ('2200220000 C' - '20160903040112-0001F 0 0') + RAW = ( + "2200220000 C" + "20160903040112-0001F 0 0" + ) def setUp(self): self.record = TransactionPurposeRecord() @@ -175,8 +207,10 @@ def test_example_loads_dumps_raw_record(self): class TransactionDetailRecordTest(TestCase): - RAW = ('2300230105BEBEBEBEBEBEBEBE mvstagingpos20iii' - 'iiiiiiiii Test 1-trtrtrtr 0 0 1') + RAW = ( + "2300230105BEBEBEBEBEBEBEBE mvstagingpos20iii" + "iiiiiiiii Test 1-trtrtrtr 0 0 1" + ) def setUp(self): self.record = TransactionDetailRecord() @@ -192,8 +226,10 @@ def test_example_loads_dumps_raw_record(self): class InformationRecordTest(TestCase): - RAW = ('3100230106AQPJ06455 SDDBCDBCRFN505500001001mvstagingpos20iiiiiii' - 'iiiii Test 0 0') + RAW = ( + "3100230106AQPJ06455 SDDBCDBCRFN505500001001mvstagingpos20iiiiiii" + "iiiii Test 0 0" + ) def setUp(self): self.record = InformationRecord() @@ -229,8 +265,10 @@ def test_field_positions_are_consecutive(self): class NewBalanceRecordTest(TestCase): - RAW = ('8231BE02737026917240 EUR0000005973199110180816 ' - ' 0') + RAW = ( + "8231BE02737026917240 EUR0000005973199110180816 " + " 0" + ) def setUp(self): self.record = NewBalanceRecord() @@ -256,8 +294,10 @@ def test_field_positions_are_consecutive(self): class FinalRecordTest(TestCase): - RAW = ('9 027972000000000322000000000142995170 ' - ' 2') + RAW = ( + "9 027972000000000322000000000142995170 " + " 2" + ) def setUp(self): self.record = FinalRecord() diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..67d1ac6 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts=--disable-socket --cov=pycoda --cov-report term-missing:skip-covered diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9caae0a..0000000 --- a/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ --i https://pypi.org/simple -appdirs==1.4.3 -certifi==2018.10.15 -chardet==3.0.4 -configparser==3.5.0 -coverage==4.0.3 -enum34==1.1.6 -factory-boy==2.8.1 -faker==0.7.11 -flake8==3.3.0 -idna==2.7 -ipaddress==1.0.18 -mccabe==0.6.1 -pycodestyle==2.3.1 -pyflakes==1.5.0 -pyparsing==2.2.0 -python-coveralls==2.9.1 -python-dateutil==2.6.0 -pyyaml>=4.2b1 -requests==2.20.0 -six==1.10.0 -urllib3==1.24.1 diff --git a/setup.py b/setup.py index c675ef5..02ffb73 100644 --- a/setup.py +++ b/setup.py @@ -1,25 +1,21 @@ -# -*- encoding: utf-8 -*- from __future__ import unicode_literals from setuptools import setup import pycoda - -def requirements(filename='requirements.txt'): - with open(filename, b'r') as fh: - return fh.read() +install_requires = ("factory-boy",) setup( - name='pycoda', + name="pycoda", version=pycoda.__version__, - description='Coded statement of Account (CODA) python API', - url='https://github.com/mhemeryck/pycoda', - install_requires=requirements(), - author='Martijn Hemeryck', - author_email='martijn.hemeryck@gmail.com', - license='MIT', - packages=['pycoda'], + description="Coded statement of Account (CODA) python API", + url="https://github.com/mhemeryck/pycoda", + install_requires=install_requires, + author="Martijn Hemeryck", + author_email="martijn.hemeryck@gmail.com", + license="MIT", + packages=["pycoda"], zip_safe=True, )