|
|
|
""" Views for the create overview part """
|
|
|
|
|
|
|
|
from tempfile import NamedTemporaryFile
|
|
|
|
|
|
|
|
from pyramid.view import view_config
|
|
|
|
from pyramid.httpexceptions import HTTPFound
|
|
|
|
from pyramid_mailer.message import Message, Attachment
|
|
|
|
|
|
|
|
from . import XLSX_CONTENT_TYPE, Root
|
|
|
|
from ..budget import parse_budget_file
|
|
|
|
from ..superx import parse_exported_file
|
|
|
|
from ..helpers import find_recipients, find_budget_file, get_sheet_of_file
|
|
|
|
from ..overview import create_overview # noqa: F401
|
|
|
|
from ..exceptions import BudgetParserError, SuperXParserError # noqa: F401
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context=Root,
|
|
|
|
request_method="GET",
|
|
|
|
renderer="superx_budget:pyramid/templates/start.jinja2",
|
|
|
|
permission="view",
|
|
|
|
)
|
|
|
|
def index(context, request):
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
@view_config(
|
|
|
|
context=Root,
|
|
|
|
request_method="POST",
|
|
|
|
renderer="superx_budget:pyramid/templates/overview.jinja2",
|
|
|
|
permission="view",
|
|
|
|
)
|
|
|
|
def superx_upload(context, request):
|
|
|
|
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",
|
|
|
|
)
|
|
|
|
def send_overview(context, request):
|
|
|
|
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
|
|
|
|
expenses = {}
|
|
|
|
for key, value in request.POST.items():
|
|
|
|
if key.startswith("expense-"):
|
|
|
|
row_str = key.split("-")[-1]
|
|
|
|
row = int(row_str)
|
|
|
|
try:
|
|
|
|
value = float(value)
|
|
|
|
except ValueError:
|
|
|
|
value = 0
|
|
|
|
expenses[row] = value
|
|
|
|
|
|
|
|
# sanity check
|
|
|
|
if (
|
|
|
|
not export_date
|
|
|
|
or not recipients
|
|
|
|
or not expenses
|
|
|
|
or not budget_file.is_file()
|
|
|
|
):
|
|
|
|
request.session.flash(
|
|
|
|
f"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
|
|
|
|
|
|
|
|
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:
|
|
|
|
sheet._parent.save(tmp.name)
|
|
|
|
tmp.seek(0)
|
|
|
|
attachment = Attachment(xls_name, XLSX_CONTENT_TYPE, tmp)
|
|
|
|
message.attach(attachment)
|
|
|
|
request.mailer.send(message)
|
|
|
|
|
|
|
|
return {"recipients": recipients, "xls_name": xls_name}
|