You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.8 KiB
96 lines
2.8 KiB
"""SuperX Parser""" |
|
|
|
from collections import namedtuple |
|
from datetime import datetime |
|
|
|
from .exceptions import SuperXParserError |
|
from .helpers import get_sheet_of_file, strip_excel_value |
|
|
|
EXPECTED_HEADLINE = "Verwendungsnachweis und Kassenstand SAP" |
|
EXPECTED_METADATA_KEYS = {"Haushaltsjahr", "Stand", "Gruppierung"} |
|
EXPECTED_EXPORT_GROUPING = "automatisch" |
|
EXPECTED_DATA_TABLE_HEADER = "Kostenstelle" |
|
|
|
|
|
SuperXResult = namedtuple( |
|
"SuperXResult", ["account_year", "export_date", "data"] |
|
) |
|
SuperXData = namedtuple( |
|
"SuperXData", |
|
[ |
|
"cost_center", |
|
"fonds", |
|
"project", |
|
"kind", |
|
"budget_year", |
|
"obligo", |
|
"expenses", |
|
"revenue_actual", |
|
"revenue_target", |
|
"actual_value", |
|
], |
|
) |
|
|
|
|
|
def _check_export_headline(row): |
|
"""checks the first line of the excel data if it's what we'd expect""" |
|
headline = row[0] |
|
if headline != EXPECTED_HEADLINE: |
|
msg = f"unexpected headline: '{headline}'" |
|
raise SuperXParserError(msg) |
|
|
|
|
|
def _get_export_metadata(row): |
|
"""extracts the metadata from the second row of the excel sheet""" |
|
data = row[0] |
|
entries = data.split(";") |
|
parts = [entry.split(":", 1) for entry in entries] |
|
metadata = {key.strip(): value.strip() for key, value in parts} |
|
if EXPECTED_METADATA_KEYS - set(metadata.keys()): |
|
msg = f"unexpected metadata: '{data}'" |
|
raise SuperXParserError(msg) |
|
if metadata["Gruppierung"] != EXPECTED_EXPORT_GROUPING: |
|
msg = f"unexpected grouping: {metadata['Gruppierung']}" |
|
raise SuperXParserError(msg) |
|
date_part, *rest = metadata["Stand"].split(",") |
|
return SuperXResult( |
|
metadata["Haushaltsjahr"], |
|
datetime.strptime(date_part.strip(), "%d.%m.%Y"), |
|
None, |
|
) |
|
|
|
|
|
def _skip_export_data_until_table_header(rows): |
|
"""skip rows until data table headers""" |
|
for line in rows: |
|
first_cell = line[0] |
|
if first_cell == EXPECTED_DATA_TABLE_HEADER: |
|
break |
|
else: |
|
msg = "could not find table header" |
|
raise SuperXParserError(msg) |
|
|
|
|
|
def _parse_data_table(rows): |
|
"""parses non-empty lines of the data table""" |
|
for line in rows: |
|
if not line[0]: |
|
continue |
|
data = [strip_excel_value(value) for value in line[:10]] |
|
yield SuperXData(*data) |
|
|
|
|
|
def parse_export_data(xls_sheet): |
|
"""parses the exported superx data""" |
|
rows = xls_sheet.values # noqa: PD011 |
|
_check_export_headline(next(rows)) |
|
metadata = _get_export_metadata(next(rows)) |
|
_skip_export_data_until_table_header(rows) |
|
data = list(_parse_data_table(rows)) |
|
return SuperXResult(metadata.account_year, metadata.export_date, data) |
|
|
|
|
|
def parse_exported_file(file_path): |
|
"""parses the budget file""" |
|
sheet = get_sheet_of_file(file_path, sheet=None) |
|
return parse_export_data(sheet)
|
|
|