Browse Source

added overview module

pull/1/head
Holger Frey 5 years ago
parent
commit
6a2cebacff
  1. 5
      superx_budget/__init__.py
  2. 2
      superx_budget/budget.py
  3. 11
      superx_budget/helpers.py
  4. 77
      superx_budget/overview.py
  5. 31
      tests/conftest.py
  6. 27
      tests/test_budget_parser.py
  7. 20
      tests/test_helpers.py
  8. 127
      tests/test_overview.py
  9. 20
      tests/test_superx_parser.py

5
superx_budget/__init__.py

@ -5,6 +5,7 @@ Creating a budget overview from a SuperX export
__version__ = "0.0.1" __version__ = "0.0.1"
from .budget import parse_budget_file # noqa: F401
from .superx import parse_export_data # noqa: F401 from .superx import parse_exported_file # noqa: F401
from .overview import create_overview # noqa: F401
from .exceptions import BudgetParserError, SuperXParserError # noqa: F401 from .exceptions import BudgetParserError, SuperXParserError # noqa: F401

2
superx_budget/budget.py

@ -35,8 +35,6 @@ BudgetData = namedtuple(
def _check_table_header(xl_row): def _check_table_header(xl_row):
fields = [c.strip() for c in xl_row.data[:7]] fields = [c.strip() for c in xl_row.data[:7]]
print(fields)
print(EXPECTED_TABLE_HEADERS)
if fields != EXPECTED_TABLE_HEADERS: if fields != EXPECTED_TABLE_HEADERS:
raise BudgetParserError(f"unexpected headers: '{xl_row.data}'") raise BudgetParserError(f"unexpected headers: '{xl_row.data}'")

11
superx_budget/helpers.py

@ -3,6 +3,17 @@
import openpyxl import openpyxl
def excel_value_as_number(value):
if value is None:
return 0
if isinstance(value, str):
try:
return float(value)
except ValueError:
return 0
return value
def get_sheet_of_file(excel_file, sheet=None): def get_sheet_of_file(excel_file, sheet=None):
""" returns a sheet from an excel FileCache """ returns a sheet from an excel FileCache

77
superx_budget/overview.py

@ -0,0 +1,77 @@
from collections import namedtuple
from .helpers import excel_value_as_number
KIND_OBLIGO = "obligo"
KIND_EXPENSES = "expenses"
VALID_MATERIAL_IDS = {
"0216 - Sachmittel",
"0227 - Verbrauchsmaterial",
"0843 - Verbrauch/Stoffe",
"2 - Sachmittel",
}
OverviewBudgetEntry = namedtuple(
"OverviewBudgetEntry", ["description", "kind", "amount"]
)
class ProjectOverview:
def __init__(self, budget_data):
""" initializes the class """
self.budget_data = budget_data
self.entries = []
@property
def project(self):
""" returns the project number """
return self.budget_data.project
@property
def expenses(self):
""" returns the accumulated expenses """
numbers = (excel_value_as_number(e.amount) for e in self.entries)
values = (abs(entry) for entry in numbers)
return sum(values)
@property
def available(self):
""" returns the still available budget """
return self.budget_data.budget - self.expenses
def add(self, description, kind, amount):
""" adds an entry that modifies the available budget """
entry = OverviewBudgetEntry(description, kind, amount)
self.entries.append(entry)
def _create_overview_map(budget_list):
""" returns a dictonary with project as key and overview as value """
map = {}
for budget_data in budget_list:
overview = ProjectOverview(budget_data)
map[str(overview.project)] = overview
return map
def _filter_superx_material_expenses(superx_export):
""" filters superx data to only contain material entries """
return (i for i in superx_export.data if i.kind in VALID_MATERIAL_IDS)
def _create_entries_from_superx(overview_map, superx_export_data):
""" adds overview entries from superx data """
for line in superx_export_data:
if line.project in overview_map:
overview = overview_map[line.project]
overview.add(line.kind, KIND_OBLIGO, line.obligo)
overview.add(line.kind, KIND_EXPENSES, line.expenses)
return overview_map
def create_overview(budget_list, superx_export):
""" create a overview map with budget entries from the parsed raw data """
overview_map = _create_overview_map(budget_list)
material_expenses = _filter_superx_material_expenses(superx_export)
return _create_entries_from_superx(overview_map, material_expenses)

31
tests/conftest.py

@ -18,3 +18,34 @@ class MockWorkbookSheet:
def example_root(request): def example_root(request):
root_dir = Path(request.config.rootdir) root_dir = Path(request.config.rootdir)
yield root_dir / "test data" yield root_dir / "test data"
@pytest.fixture
def budget_example_file(example_root):
return example_root / "Verbrauchsmittel-Toto-2020.xlsx"
@pytest.fixture
def budget_example_workbook(budget_example_file):
import openpyxl
yield openpyxl.open(budget_example_file)
@pytest.fixture
def budget_example_sheet(budget_example_workbook):
sheets = budget_example_workbook.sheetnames
first = sheets[0]
yield budget_example_workbook[first]
@pytest.fixture
def superx_example_file(example_root):
return example_root / "Verwendungsnachweis_und_Kassenstand_SAP_Zahlen.xlsx"
@pytest.fixture
def superx_example_workbook(superx_example_file):
import openpyxl
yield openpyxl.open(superx_example_file)

27
tests/test_budget_parser.py

@ -1,25 +1,6 @@
import pytest 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(): def test_check_table_header_raises_error():
from superx_budget.budget import _check_table_header, ExcelRow from superx_budget.budget import _check_table_header, ExcelRow
from superx_budget.exceptions import BudgetParserError from superx_budget.exceptions import BudgetParserError
@ -67,10 +48,10 @@ def test_parse_data_table():
assert last.rest == "z" assert last.rest == "z"
def test_parse_budget_data(example_sheet): def test_parse_budget_data(budget_example_sheet):
from superx_budget.budget import parse_budget_data from superx_budget.budget import parse_budget_data
result = parse_budget_data(example_sheet) result = parse_budget_data(budget_example_sheet)
first, last = result[0], result[-1] first, last = result[0], result[-1]
assert len(result) == 18 assert len(result) == 18
@ -84,10 +65,10 @@ def test_parse_budget_data(example_sheet):
assert last.rest == "=E54-F54" assert last.rest == "=E54-F54"
def test_parse_budget_file(example_file): def test_parse_budget_file(budget_example_file):
from superx_budget.budget import parse_budget_file from superx_budget.budget import parse_budget_file
result = parse_budget_file(example_file) result = parse_budget_file(budget_example_file)
first = result[0] first = result[0]
assert len(result) == 18 assert len(result) == 18

20
tests/test_helpers.py

@ -71,3 +71,23 @@ def test_get_sheet_of_file_named(example_file):
first_cell = first_row[0] first_cell = first_row[0]
assert first_cell == 1 assert first_cell == 1
@pytest.mark.parametrize(
"input,expected",
[
(None, 0),
("", 0),
("a", 0),
("1", 1.0),
("2.4", 2.4),
(1, 1),
(2.4, 2.4),
],
)
def test_excel_value_as_number(input, expected):
from superx_budget.helpers import excel_value_as_number
result = excel_value_as_number(input)
assert result == expected

127
tests/test_overview.py

@ -0,0 +1,127 @@
import pytest
@pytest.fixture
def example_budget_data():
from superx_budget.budget import BudgetData
example = BudgetData(
3,
"1.",
"Safegurard I",
"01.08.2019-31.07.2020",
"1100000102",
5000,
0,
0,
)
yield example
def test_project_overview_init(example_budget_data):
from superx_budget.overview import ProjectOverview
over = ProjectOverview(example_budget_data)
assert over.budget_data == example_budget_data
assert over.entries == []
def test_project_overview_project_property(example_budget_data):
from superx_budget.overview import ProjectOverview
over = ProjectOverview(example_budget_data)
assert over.project == example_budget_data.project
def test_project_overview_add(example_budget_data):
from superx_budget.overview import ProjectOverview, OverviewBudgetEntry
over = ProjectOverview(example_budget_data)
over.add("Sachmittel", "Obligo", -100)
over.add("Kleinzeugs", "Ausgaben", 200)
assert len(over.entries) == 2
assert isinstance(over.entries[0], OverviewBudgetEntry)
assert over.entries[0].description == "Sachmittel"
assert over.entries[0].kind == "Obligo"
assert over.entries[0].amount == -100
def test_project_overview_expenses_property(example_budget_data):
from superx_budget.overview import ProjectOverview
over = ProjectOverview(example_budget_data)
over.add("Sachmittel", "Obligo", -100)
over.add("Kleinzeugs", "Ausgaben", 200)
assert over.expenses == 300
def test_project_overview_available_property(example_budget_data):
from superx_budget.overview import ProjectOverview
over = ProjectOverview(example_budget_data)
over.add("Sachmittel", "Obligo", -100)
over.add("Kleinzeugs", "Ausgaben", 200)
assert over.available == 5000 - 300
def test_create_overview_map(budget_example_file):
from superx_budget.overview import _create_overview_map
from superx_budget.budget import parse_budget_file
budget_data = parse_budget_file(budget_example_file)
result = _create_overview_map(budget_data)
assert len(result) == 18
assert "2100276501" in result
for key, value in result.items():
assert key == str(value.project)
def test_create_entries_from_export(budget_example_file, superx_example_file):
from superx_budget.overview import (
_create_entries_from_superx,
_create_overview_map,
)
from superx_budget.budget import parse_budget_file
from superx_budget.superx import parse_exported_file
superx_data = parse_exported_file(superx_example_file)
budget_data = parse_budget_file(budget_example_file)
budget_map = _create_overview_map(budget_data)
_create_entries_from_superx(budget_map, superx_data.data)
assert budget_map["1100000102"].available == 5000 - 0.01 - 1000 - 1 - 1001
def test_filter_superx_material_expenses(superx_example_file):
from superx_budget.overview import (
_filter_superx_material_expenses,
VALID_MATERIAL_IDS,
)
from superx_budget.superx import parse_exported_file
superx_data = parse_exported_file(superx_example_file)
result = list(_filter_superx_material_expenses(superx_data))
assert {item.kind for item in result} == VALID_MATERIAL_IDS
assert result[0].obligo == 1
assert result[0].expenses == 1001
def test_create_overview(budget_example_file, superx_example_file):
from superx_budget.overview import create_overview
from superx_budget.budget import parse_budget_file
from superx_budget.superx import parse_exported_file
superx_data = parse_exported_file(superx_example_file)
budget_data = parse_budget_file(budget_example_file)
budget_map = create_overview(budget_data, superx_data)
assert budget_map["1100000102"].available == 5000 - 1 - 1001

20
tests/test_superx_parser.py

@ -4,18 +4,6 @@
import pytest import pytest
@pytest.fixture
def example_file(example_root):
return example_root / "Verwendungsnachweis_und_Kassenstand_SAP_Zahlen.xlsx"
@pytest.fixture
def example_workbook(example_file):
import openpyxl
yield openpyxl.open(example_file)
def test_check_export_headline(): def test_check_export_headline():
from superx_budget.superx import _check_export_headline from superx_budget.superx import _check_export_headline
from superx_budget.exceptions import SuperXParserError from superx_budget.exceptions import SuperXParserError
@ -109,11 +97,11 @@ def test_parse_data_table():
assert second_value.acutal_value == "z" assert second_value.acutal_value == "z"
def test_parse_export_data(example_workbook): def test_parse_export_data(superx_example_workbook):
from superx_budget.superx import parse_export_data from superx_budget.superx import parse_export_data
from datetime import datetime from datetime import datetime
result = parse_export_data(example_workbook.active) result = parse_export_data(superx_example_workbook.active)
assert result.account_year == "2020" assert result.account_year == "2020"
assert result.export_date == datetime(2020, 3, 18) assert result.export_date == datetime(2020, 3, 18)
@ -142,10 +130,10 @@ def test_parse_export_data(example_workbook):
assert last.acutal_value == 4236 assert last.acutal_value == 4236
def test_parse_exported_file(example_file): def test_parse_exported_file(superx_example_file):
from superx_budget.superx import parse_exported_file from superx_budget.superx import parse_exported_file
result = parse_exported_file(example_file) result = parse_exported_file(superx_example_file)
first = result.data[0] first = result.data[0]
assert len(result.data) == 212 assert len(result.data) == 212

Loading…
Cancel
Save