Holger Frey
5 years ago
8 changed files with 178 additions and 20 deletions
@ -0,0 +1,59 @@ |
|||||||
|
""" Budget Parser """ |
||||||
|
|
||||||
|
from collections import namedtuple |
||||||
|
|
||||||
|
from .exceptions import BudgetParserError |
||||||
|
|
||||||
|
EXPECTED_TABLE_HEADERS = [ |
||||||
|
"Nr.", |
||||||
|
"Projekt", |
||||||
|
"Laufzeit", |
||||||
|
"BA", |
||||||
|
"Budget", |
||||||
|
"Ausgaben", |
||||||
|
"Rest", |
||||||
|
] |
||||||
|
|
||||||
|
BudgetData = namedtuple( |
||||||
|
"BudgetData", |
||||||
|
[ |
||||||
|
"row", |
||||||
|
"number", |
||||||
|
"project_name", |
||||||
|
"period", |
||||||
|
"project", |
||||||
|
"budget", |
||||||
|
"expenses", |
||||||
|
"rest", |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
def _check_table_header(row): |
||||||
|
fields = [c.strip() for c in row[:7]] |
||||||
|
print(fields) |
||||||
|
print(EXPECTED_TABLE_HEADERS) |
||||||
|
if fields != EXPECTED_TABLE_HEADERS: |
||||||
|
raise BudgetParserError(f"unexpected headers: '{row}'") |
||||||
|
|
||||||
|
|
||||||
|
def _skip_empty_lines(rows, start=0): |
||||||
|
for i, row in enumerate(rows, start=start): |
||||||
|
first_cell = row[0] |
||||||
|
if first_cell is None: |
||||||
|
continue |
||||||
|
if isinstance(first_cell, str) and first_cell.strip() == "": |
||||||
|
continue |
||||||
|
yield i, row |
||||||
|
|
||||||
|
|
||||||
|
def _parse_data_table(rows, start=2): |
||||||
|
for i, data in _skip_empty_lines(rows, start): |
||||||
|
yield BudgetData(i, *data[:7]) |
||||||
|
|
||||||
|
|
||||||
|
def parse_budget_data(xls_sheet): |
||||||
|
""" parses the budget data """ |
||||||
|
rows = xls_sheet.values |
||||||
|
_check_table_header(next(rows)) |
||||||
|
return list(_parse_data_table(rows, start=2)) |
@ -0,0 +1,13 @@ |
|||||||
|
""" Exceptions used in the Project """ |
||||||
|
|
||||||
|
|
||||||
|
class SuperXBudgetError(ValueError): |
||||||
|
""" Base class for project errors """ |
||||||
|
|
||||||
|
|
||||||
|
class SuperXParserError(SuperXBudgetError): |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class BudgetParserError(SuperXBudgetError): |
||||||
|
pass |
Binary file not shown.
@ -0,0 +1,77 @@ |
|||||||
|
import pytest |
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture |
||||||
|
def example_file(example_root): |
||||||
|
return example_root / "Verbrauchsmittel-Toto-2020.xlsx" |
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture |
||||||
|
def example_workbook(example_file): |
||||||
|
import openpyxl |
||||||
|
|
||||||
|
yield openpyxl.open(example_file) |
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture |
||||||
|
def example_sheet(example_workbook): |
||||||
|
sheets = example_workbook.sheetnames |
||||||
|
first = sheets[0] |
||||||
|
yield example_workbook[first] |
||||||
|
|
||||||
|
|
||||||
|
def test_check_table_header_raises_error(): |
||||||
|
from superx_budget.budget import _check_table_header |
||||||
|
from superx_budget.exceptions import BudgetParserError |
||||||
|
|
||||||
|
row = ["not", "the", "expected", "row"] |
||||||
|
|
||||||
|
with pytest.raises(BudgetParserError): |
||||||
|
_check_table_header(row) |
||||||
|
|
||||||
|
|
||||||
|
def test_skip_empty_lines(): |
||||||
|
from superx_budget.budget import _skip_empty_lines |
||||||
|
|
||||||
|
rows = [[""], ["one"], [None], [""], ["two"], [""]] |
||||||
|
|
||||||
|
result = _skip_empty_lines(rows) |
||||||
|
|
||||||
|
assert list(result) == [(1, ["one"]), (4, ["two"])] |
||||||
|
|
||||||
|
|
||||||
|
def test_parse_data_table(): |
||||||
|
from superx_budget.budget import _parse_data_table |
||||||
|
|
||||||
|
rows = [ |
||||||
|
list("ABCDEFG"), |
||||||
|
[None for i in range(7)], |
||||||
|
list("tuvwxyzX"), # one item more |
||||||
|
] |
||||||
|
|
||||||
|
result = _parse_data_table(rows) |
||||||
|
first, last = list(result) |
||||||
|
|
||||||
|
assert first.row == 2 |
||||||
|
assert first.number == "A" |
||||||
|
assert first.rest == "G" |
||||||
|
assert last.row == 4 |
||||||
|
assert last.number == "t" |
||||||
|
assert last.rest == "z" |
||||||
|
|
||||||
|
|
||||||
|
def test_parse_budget_data(example_sheet): |
||||||
|
from superx_budget.budget import parse_budget_data |
||||||
|
|
||||||
|
result = parse_budget_data(example_sheet) |
||||||
|
first, last = result[0], result[-1] |
||||||
|
|
||||||
|
assert len(result) == 18 |
||||||
|
assert first.row == 3 |
||||||
|
assert first.number == 1 |
||||||
|
assert first.project_name == "Safegurard I (neu)" |
||||||
|
assert first.rest == "=E3-F3" |
||||||
|
assert last.row == 54 |
||||||
|
assert last.number == "=A51+1" |
||||||
|
assert last.project_name == "ZIM Microcoat II" |
||||||
|
assert last.rest == "=E54-F54" |
Loading…
Reference in new issue