Creating a budget overview from a SuperX export.
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.

175 lines
5.1 KiB

2 months ago
"""Views for the create overview part"""
from tempfile import NamedTemporaryFile
from pyramid.httpexceptions import HTTPFound
2 months ago
from pyramid.view import view_config
from pyramid_mailer.message import Attachment, Message
from ..budget import parse_budget_file
2 months ago
from ..exceptions import BudgetParserError, SuperXParserError
from ..helpers import find_budget_file, find_recipients, get_sheet_of_file
from ..overview import create_overview
from ..superx import parse_exported_file
2 months ago
from . import XLSX_CONTENT_TYPE, Root
MAIL_MESSAGE_BODY = """Hallo an Alle,
anbei ist eine aktuelle Übersicht unserer Verbrauchsmittel-Budgets.
Frohes Bestellen!
--
Dies ist eine automatisch generierte Email,
bei Fragen bitte an Holgi wenden
"""
def _map_from_form_data(request, prefix):
if not prefix.endswith("-"):
prefix = f"{prefix}-"
result = {}
for key, value in request.POST.items():
if key.startswith(prefix):
row_str = key.split("-")[-1]
row = int(row_str)
try:
2 months ago
as_number = float(value)
except ValueError:
2 months ago
as_number = 0
result[row] = as_number
return result
@view_config(
context=Root,
request_method="GET",
renderer="superx_budget:pyramid/templates/start.jinja2",
permission="view",
)
2 months ago
def index(context, request): # noqa: ARG001
return {}
@view_config(
context=Root,
request_method="POST",
renderer="superx_budget:pyramid/templates/overview.jinja2",
permission="view",
)
2 months ago
def superx_upload(context, request): # noqa: ARG001
upload = request.POST.get("superx")
if upload == b"" or not upload.filename.endswith(".xlsx"):
request.session.flash("No Excel file uploaded.", "error")
return HTTPFound("/")
try:
superx_export = parse_exported_file(upload.file)
except SuperXParserError:
request.session.flash(
"File does not appear to be the required SuperX export.", "error"
)
return HTTPFound("/")
budget_file = find_budget_file(
request.budgets_dir, superx_export.account_year
)
if budget_file is None:
request.session.flash(
f"No budget file for year {superx_export.account_year} found.",
"error",
)
return HTTPFound("/")
try:
budget_data = parse_budget_file(budget_file)
except BudgetParserError:
request.session.flash(
"Budget File does not appear to be in the required format.",
"error",
)
return HTTPFound("/")
overview_map = create_overview(budget_data, superx_export)
overview = sorted(overview_map.values(), key=lambda i: i.row)
if any(not (item.found) for item in overview):
request.session.flash(
(
"Some projects in the budget template were not in the SuperX "
"export. Please adjust their expenses manually."
),
"info",
)
recipients = find_recipients(request.budgets_dir)
return {
"account_year": superx_export.account_year,
"export_date": superx_export.export_date.strftime("%Y-%m-%d"),
"overview": overview,
"template": budget_file.name,
"recipients": recipients,
}
@view_config(
context=Root,
name="send",
request_method="POST",
renderer="superx_budget:pyramid/templates/sent.jinja2",
permission="view",
)
2 months ago
def send_overview(context, request): # noqa: ARG001
export_date = request.POST.get("export_date").strip()
tmp_recipients = request.POST.get("recipients").strip()
recipients = tmp_recipients.splitlines()
budget_template = request.POST.get("template")
budget_file = request.budgets_dir / budget_template
budgets = _map_from_form_data(request, "budget-")
expenses = _map_from_form_data(request, "expense-")
available = {k: (v - expenses[k]) for k, v in budgets.items()}
# sanity check
if (
not export_date
or not recipients
or not expenses
or not available
or not budget_file.is_file()
):
request.session.flash(
"There was an error with your submisssion, please try again.",
"error",
)
return HTTPFound("/")
sheet = get_sheet_of_file(budget_file)
for row, value in expenses.items():
cell = f"F{row}"
sheet[cell] = value
for row, value in available.items():
cell = f"G{row}"
sheet[cell] = value
budget_year = budget_file.stem.split("-")[-1]
message = Message(
subject=f"Budget Übersicht {budget_year}, Stand {export_date}",
sender="cpiserver@imtek.uni-freiburg.de",
recipients=recipients,
body=MAIL_MESSAGE_BODY,
)
xls_name = f"{export_date}-Budget-Overview-{budget_year}.xlsx"
with NamedTemporaryFile() as tmp:
2 months ago
sheet._parent.save(tmp.name) # noqa: SLF001
tmp.seek(0)
attachment = Attachment(xls_name, XLSX_CONTENT_TYPE, tmp)
message.attach(attachment)
request.mailer.send_immediately(message, fail_silently=False)
return {"recipients": recipients, "xls_name": xls_name}