Holger Frey
5 years ago
9 changed files with 277 additions and 43 deletions
@ -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) |
@ -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 |
Loading…
Reference in new issue