From dc68c79d93daec3a7c6e44b41f7210083bff93b6 Mon Sep 17 00:00:00 2001 From: Mathias Scheider Date: Sun, 6 Sep 2026 23:21:18 +0200 Subject: [PATCH] reader_comdirect (tbc) --- .gitignore | 2 ++ src/reader.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_reader.py | 10 ++++++++++ uv.lock | 6 +++--- 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/test_reader.py diff --git a/.gitignore b/.gitignore index 073e81e..2a04809 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ wheels/ # Database instances *.db + +.local diff --git a/src/reader.py b/src/reader.py index e305e87..a70c483 100644 --- a/src/reader.py +++ b/src/reader.py @@ -1,4 +1,6 @@ import csv +import re +from enum import Enum, auto from pathlib import Path from src.models import B4wExport @@ -12,3 +14,47 @@ def reader_raw(path: Path): def reader_b4w(path: Path): raw = reader_raw(path) return list(map(B4wExport.model_validate, raw)) + + +def reader_comdirect(path: Path): + PATTERN_ACCOUNT_NAME = r'^"Umsätze ([^"]+)";"Zeitraum: .*$' + PATTERN_HEADER = r'"([^"]+)"' + PATTERN_DATA = r'"([^"]+)"' + + class EXPECT_LINE(Enum): + ACCOUNT_NAME = auto() + BLANK = auto() + HEADER = auto() + DATA = auto() + + with path.open(encoding="latin1") as fid: + lines = fid.readlines() + + expect_next = EXPECT_LINE.ACCOUNT_NAME + account_name = None + result = {} + for line in lines: + if expect_next == EXPECT_LINE.ACCOUNT_NAME: + account_name = re.findall(PATTERN_ACCOUNT_NAME, line) + if account_name: + account_name = account_name[0] + result[account_name] = {} + expect_next = EXPECT_LINE.BLANK + elif expect_next == EXPECT_LINE.BLANK: + if line == "\n": + expect_next = EXPECT_LINE.HEADER + elif expect_next == EXPECT_LINE.HEADER: + header = re.findall(PATTERN_HEADER, line) + if header: + result[account_name]["data"] = [] + expect_next = EXPECT_LINE.DATA + elif expect_next == EXPECT_LINE.DATA: + data = re.findall(PATTERN_DATA, line) + if data: + result[account_name]["data"].append( + {column: value for column, value in zip(header, data)} + ) + expect_next = EXPECT_LINE.ACCOUNT_NAME + else: + expect_next = EXPECT_LINE.ACCOUNT_NAME + return result diff --git a/tests/test_reader.py b/tests/test_reader.py new file mode 100644 index 0000000..5b0727e --- /dev/null +++ b/tests/test_reader.py @@ -0,0 +1,10 @@ +from pathlib import Path + +from src.reader import reader_comdirect + + +def test_reader(): + test_path = Path(".local/data/comdirect/umsaetze_9773063005_20260906-2043.csv") + assert test_path.exists() + data = reader_comdirect(test_path) + assert data diff --git a/uv.lock b/uv.lock index a170f6f..7f52dba 100644 --- a/uv.lock +++ b/uv.lock @@ -4,16 +4,16 @@ requires-python = ">=3.14" [[package]] name = "alembic" -version = "1.19.1" +version = "1.19.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mako" }, { name = "sqlalchemy" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/16/2b/e4153978368de59918115c9e01d3ebf58a558a7285efa7e960c383c4b59a/alembic-1.19.1.tar.gz", hash = "sha256:e0fca0518118c78acc493e31bcb5402f190057aaf6df8b5b95ce94c4789cf648", size = 2070816, upload-time = "2026-08-08T16:32:01.565Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/10/181eecdd552217d0342492bd6f3b8a96e973083379aace3d3402830ddc03/alembic-1.19.2.tar.gz", hash = "sha256:297950a8a91f6770eb82bfbce9bea55c728b90a5386c6e81430191a319d138b0", size = 2082643, upload-time = "2026-09-04T17:10:11.212Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/89/e62cc37b69ad357cc8ecd6e7367f5245f523d3cbb338a66197212bdf6749/alembic-1.19.1-py3-none-any.whl", hash = "sha256:b39018cb3d9413a19cbd54cf3c02ad33998641f0538eb77413a488a21c3e14be", size = 265946, upload-time = "2026-08-08T16:32:03.153Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cb/9014784dcb0585977ae23b6f43331d0a33c51ac0d692506d12b4f5ee9f3b/alembic-1.19.2-py3-none-any.whl", hash = "sha256:32d553dcd577e6fe5c3c63e91468526d35e4dcecafe865d7db4e9b328fa93cb2", size = 267399, upload-time = "2026-09-04T17:10:12.796Z" }, ] [[package]]