Skip to content
Snippets Groups Projects
Commit 663a9d49 authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Improve tasks.py code quality

parent 32bfe777
Branches
No related tags found
No related merge requests found
......@@ -196,7 +196,7 @@ LATEX_BULLETPOINTS = [
# "logo": "asta-logo.tex", # optional: replaces the general template to include at the top of protocol.tex set by LATEX_LOGO_TEMPLATE
# "geometry": "bottom=1.6cm,top=1.6cm,inner=2.5cm,outer=1.0cm,footskip=1.0cm,headsep=0.6cm", # optional: replaces the general protocol page geometry set by LATEX_GEOMETRY
# "pagestyle": "fancy", # optional: replaces the general protocol pagestyle set by LATEX_PAGESTYLE
# "additionalpackages": ["[absolute]{textpos}", "{fancyheadings}"], # optional: replaces the general latex packages set by LATEX_ADDITIONAL_PACKAGES
# "additional_packages": ["[absolute]{textpos}", "{fancyheadings}"], # optional: replaces the general latex packages set by LATEX_ADDITIONAL_PACKAGES
# "headerfooter": True # optional: replaces the general LATEX_HEADER_FOOTER option
# }
#}
......
......@@ -9,15 +9,21 @@ import traceback
from copy import copy
import xmlrpc.client
from models.database import Document, Protocol, Error, Todo, Decision, TOP, DefaultTOP, MeetingReminder, TodoMail, DecisionDocument, TodoState, OldTodo, DecisionCategory
from models.database import (
Document, Protocol, Todo, Decision, TOP, MeetingReminder,
TodoMail, DecisionDocument, TodoState, OldTodo, DecisionCategory)
from models.errors import DateNotMatchingException
from server import celery, app
from shared import db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, class_filter, KNOWN_KEYS, WikiType
from utils import mail_manager, encode_kwargs, decode_kwargs, add_line_numbers, set_etherpad_text, get_etherpad_text, footnote_hash, parse_datetime_from_string
from protoparser import parse, ParserException, Element, Content, Text, Tag, Remark, Fork, RenderType
from shared import (
db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long,
date_filter_short, time_filter, class_filter, KNOWN_KEYS, WikiType)
from utils import (
mail_manager, add_line_numbers,
set_etherpad_text, parse_datetime_from_string)
from protoparser import parse, ParserException, Tag, Remark, Fork, RenderType
from wiki import WikiClient, WikiException
from calendarpush import Client as CalendarClient, CalendarException
from legacy import lookup_todo_id, import_old_todos
from legacy import lookup_todo_id
import config
......@@ -32,7 +38,6 @@ texenv.filters["escape_tex"] = escape_tex
texenv.filters["unhyphen"] = unhyphen
texenv.trim_blocks = True
texenv.lstrip_blocks = True
#texenv.filters["url_complete"] = url_manager.complete
texenv.filters["datify"] = date_filter
texenv.filters["datify_long"] = date_filter_long
texenv.filters["datify_short"] = date_filter_short
......@@ -42,7 +47,9 @@ texenv.filters["class"] = class_filter
logo_template = getattr(config, "LATEX_LOGO_TEMPLATE", None)
if logo_template is not None:
texenv.globals["logo_template"] = logo_template
latex_geometry = getattr(config, "LATEX_GEOMETRY", "vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm")
latex_geometry = getattr(
config, "LATEX_GEOMETRY",
"vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm")
texenv.globals["latex_geometry"] = latex_geometry
raw_additional_packages = getattr(config, "LATEX_ADDITIONAL_PACKAGES", None)
additional_packages = []
......@@ -59,51 +66,67 @@ latex_header_footer = getattr(config, "LATEX_HEADER_FOOTER", False)
texenv.globals["latex_header_footer"] = latex_header_footer
latex_templates = getattr(config, "LATEX_TEMPLATES", None)
def provide_latex_template(template, documenttype):
_latex_template_documenttype_filename = {
_DOCUMENTTYPE_FILENAME_MAP = {
"class": "protokoll2.cls",
"protocol": "protocol.tex",
"decision": "decision.tex"
}
_latex_template_filename = _latex_template_documenttype_filename[documenttype]
_PROVIDES = "provides"
_LOGO_TEMPLATE = "logo_template"
_LOGO = "logo"
_LATEX_GEOMETRY = "latex_geometry"
_GEOMETRY = "geometry"
_ADDITIONAL_PACKAGES = "additional_packages"
_LATEX_PAGESTYLE = "latex_pagestyle"
_PAGESTYLE = "pagestyle"
_LATEX_HEADER_FOOTER = "latex_header_footer"
_HEADER_FOOTER = "headerfooter"
_latex_template_filename = _DOCUMENTTYPE_FILENAME_MAP[documenttype]
_latex_template_foldername = ""
if logo_template is not None:
texenv.globals["logo_template"] = logo_template
texenv.globals["latex_geometry"] = latex_geometry
texenv.globals["additional_packages"] = additional_packages
if latex_pagestyle is not None and latex_pagestyle:
texenv.globals["latex_pagestyle"] = latex_pagestyle
elif "latex_pagestyle" in texenv.globals:
del texenv.globals["latex_pagestyle"]
texenv.globals["latex_header_footer"] = latex_header_footer
if (latex_templates is not None) and (template is not ""):
texenv.globals[_LOGO_TEMPLATE] = logo_template
texenv.globals[_LATEX_GEOMETRY] = latex_geometry
texenv.globals[_ADDITIONAL_PACKAGES] = additional_packages
if latex_pagestyle:
texenv.globals[_LATEX_PAGESTYLE] = latex_pagestyle
elif _LATEX_PAGESTYLE in texenv.globals:
del texenv.globals[_LATEX_PAGESTYLE]
texenv.globals[_LATEX_HEADER_FOOTER] = latex_header_footer
if latex_templates is not None and template != "":
if template in latex_templates:
if "provides" in latex_templates[template]:
_latex_template_foldername = (template + "/") if documenttype in latex_templates[template]["provides"] else ""
if "logo" in latex_templates[template]:
texenv.globals["logo_template"] = template + "/" + latex_templates[template]["logo"]
if "geometry" in latex_templates[template]:
texenv.globals["latex_geometry"] = latex_templates[template]["geometry"]
if "pagestyle" in latex_templates[template]:
if latex_templates[template]["pagestyle"]:
texenv.globals["latex_pagestyle"] = latex_templates[template]["pagestyle"]
if "additionalpackages" in latex_templates[template]:
_raw_additional_packages = latex_templates[template]["additionalpackages"]
template_data = latex_templates[template]
if _PROVIDES in template_data:
if documenttype in template_data[_PROVIDES]:
_latex_template_foldername = template
if _LOGO in template_data:
texenv.globals[_LOGO_TEMPLATE] = os.path.join(
template, template_data[_LOGO])
if _GEOMETRY in template_data:
texenv.globals[_LATEX_GEOMETRY] = template_data[_GEOMETRY]
if _PAGESTYLE in template_data:
if template_data[_PAGESTYLE]:
texenv.globals[_LATEX_PAGESTYLE] = (
template_data[_PAGESTYLE])
if _ADDITIONAL_PACKAGES in template_data:
_raw_additional_packages = template_data[_ADDITIONAL_PACKAGES]
_additional_packages = []
if _raw_additional_packages is not None:
for _package in _raw_additional_packages:
if "{" not in _package:
_package = "{{{}}}".format(_package)
_additional_packages.append(_package)
texenv.globals["additional_packages"] = _additional_packages
if "headerfooter" in latex_templates[template]:
texenv.globals["latex_header_footer"] = latex_templates[template]["headerfooter"]
return _latex_template_foldername + _latex_template_filename
texenv.globals[_ADDITIONAL_PACKAGES] = _additional_packages
if _HEADER_FOOTER in latex_templates[template]:
texenv.globals[_LATEX_HEADER_FOOTER] = (
template_data[_HEADER_FOOTER])
return os.path.join(_latex_template_foldername, _latex_template_filename)
mailenv = app.create_jinja_environment()
mailenv.trim_blocks = True
mailenv.lstrip_blocks = True
#mailenv.filters["url_complete"] = url_manager.complete
mailenv.filters["datify"] = date_filter
mailenv.filters["datetimify"] = datetime_filter
......@@ -124,43 +147,46 @@ wikienv.filters["timify"] = time_filter
wikienv.filters["class"] = class_filter
def _make_error(protocol, *args):
error = protocol.create_error(*args)
db.session.add(error)
db.session.commit()
ID_FIELD_BEGINNING = "id "
def parse_protocol(protocol, **kwargs):
parse_protocol_async.delay(protocol.id, encode_kwargs(kwargs))
def parse_protocol(protocol):
parse_protocol_async.delay(protocol.id)
@celery.task
def parse_protocol_async(protocol_id, encoded_kwargs):
def parse_protocol_async(protocol_id):
with app.app_context():
with app.test_request_context("/"):
try:
kwargs = decode_kwargs(encoded_kwargs)
protocol = Protocol.query.filter_by(id=protocol_id).first()
protocol = Protocol.first_by_id(protocol_id)
if protocol is None:
raise Exception("No protocol given. Aborting parsing.")
parse_protocol_async_inner(protocol, encoded_kwargs)
parse_protocol_async_inner(protocol)
except Exception as exc:
stacktrace = traceback.format_exc()
error = protocol.create_error("Parsing", "Exception",
return _make_error(
protocol, "Parsing", "Exception",
"{}\n\n{}".format(str(exc), stacktrace))
db.session.add(error)
db.session.commit()
def parse_protocol_async_inner(protocol, encoded_kwargs):
def parse_protocol_async_inner(protocol):
old_errors = list(protocol.errors)
for error in old_errors:
protocol.errors.remove(error)
db.session.commit()
if protocol.source is None or len(protocol.source.strip()) == 0:
error = protocol.create_error("Parsing", "Protocol source is empty", "")
db.session.add(error)
db.session.commit()
return
return _make_error(protocol, "Parsing", "Protocol source is empty", "")
if protocol.source == config.EMPTY_ETHERPAD:
error = protocol.create_error("Parsing", "The etherpad is unmodified and does not contain a protocol.", protocol.source)
db.session.add(error)
db.session.commit()
return
return _make_error(
protocol, "Parsing", "The etherpad is unmodified and does not "
"contain a protocol.", protocol.source)
tree = None
try:
tree = parse(protocol.source)
......@@ -169,30 +195,36 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
if exc.linenumber is not None:
source_lines = protocol.source.splitlines()
start_index = max(0, exc.linenumber - config.ERROR_CONTEXT_LINES)
end_index = min(len(source_lines) - 1, exc.linenumber + config.ERROR_CONTEXT_LINES)
end_index = min(
len(source_lines) - 1,
exc.linenumber + config.ERROR_CONTEXT_LINES)
context = "\n".join(source_lines[start_index:end_index])
if exc.tree is not None:
context += "\n\nParsed syntax tree was:\n" + str(exc.tree.dump())
error = protocol.create_error("Parsing", str(exc), context)
db.session.add(error)
db.session.commit()
return
remarks = {element.name: element for element in tree.children if isinstance(element, Remark)}
return _make_error(protocol, "Parsing", str(exc), context)
remarks = {
element.name: element
for element in tree.children
if isinstance(element, Remark)
}
required_fields = copy(KNOWN_KEYS)
for default_meta in protocol.protocoltype.metas:
required_fields.append(default_meta.key)
if not config.PARSER_LAZY:
missing_fields = [field for field in required_fields if field not in remarks]
missing_fields = [
field
for field in required_fields
if field not in remarks
]
if len(missing_fields) > 0:
error = protocol.create_error("Parsing", "Du hast vergessen, Metadaten anzugeben.", ", ".join(missing_fields))
db.session.add(error)
db.session.commit()
return
return _make_error(
protocol, "Parsing", "Du hast vergessen, Metadaten anzugeben.",
", ".join(missing_fields))
try:
protocol.fill_from_remarks(remarks)
except ValueError:
error = protocol.create_error(
"Parsing", "Invalid fields",
return _make_error(
protocol, "Parsing", "Invalid fields",
"Date or time fields are not '%d.%m.%Y' respectively '%H:%M', "
"but rather {}".format(
", ".join([
......@@ -200,28 +232,27 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
remarks["Beginn"].value.strip(),
remarks["Ende"].value.strip()
])))
db.session.add(error)
db.session.commit()
return
except DateNotMatchingException as exc:
error = protocol.create_error("Parsing", "Date not matching",
"This protocol's date should be {}, but the protocol source says {}.".format(date_filter(exc.original_date) if exc.original_date is not None else "not present", date_filter(exc.protocol_date) if exc.protocol_date is not None else "not present"))
db.session.add(error)
db.session.commit()
return
return _make_error(
protocol, "Parsing", "Date not matching",
"This protocol's date should be {}, but the protocol source "
"says {}.".format(
date_filter(exc.original_date)
if exc.original_date is not None
else "not present",
date_filter(exc.protocol_date)
if exc.protocol_date is not None
else "not present"))
# tags
tags = tree.get_tags()
elements = tree.get_visible_elements(show_private=True)
public_elements = tree.get_visible_elements(show_private=False)
for tag in tags:
if tag.name not in Tag.KNOWN_TAGS:
error = protocol.create_error("Parsing", "Invalid tag",
return _make_error(
protocol, "Parsing", "Invalid tag",
"The tag in line {} has the kind '{}', which is "
"not defined. This is probably an error mit a missing "
"semicolon.".format(tag.linenumber, tag.name))
db.session.add(error)
db.session.commit()
return
# todos
old_todo_number_map = {}
for todo in protocol.todos:
......@@ -233,14 +264,12 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
raw_todos = []
for todo_tag in todo_tags:
if len(todo_tag.values) < 2:
error = protocol.create_error("Parsing", "Invalid todo-tag",
return _make_error(
protocol, "Parsing", "Invalid todo-tag",
"The todo tag in line {} needs at least "
"information on who and what, "
"but has less than that. This is probably "
"a missing semicolon.".format(todo_tag.linenumber))
db.session.add(error)
db.session.commit()
return
who = todo_tag.values[0]
what = todo_tag.values[1]
field_id = None
......@@ -254,39 +283,45 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
try:
field_id = int(other_field[len(ID_FIELD_BEGINNING):])
except ValueError:
error = protocol.create_error("Parsing", "Non-numerical todo ID",
return _make_error(
protocol, "Parsing", "Non-numerical todo ID",
"The todo in line {} has a nonnumerical ID, but needs "
"something like \"id 1234\"".format(todo_tag.linenumber))
db.session.add(error)
db.session.commit()
return
"something like \"id 1234\"".format(
todo_tag.linenumber))
else:
try:
field_state = TodoState.from_name(other_field)
continue
except ValueError:
pass
try:
field_date = datetime.strptime(other_field, "%d.%m.%Y")
continue
except ValueError:
pass
try:
field_state, field_date = TodoState.from_name_with_date(other_field.strip(), protocol=protocol)
field_state, field_date = TodoState.from_name_with_date(
other_field.strip(), protocol=protocol)
continue
except ValueError:
pass
try:
field_state = TodoState.from_name_lazy(other_field)
except ValueError:
error = protocol.create_error("Parsing",
"Invalid field",
return _make_error(
protocol, "Parsing", "Invalid field",
"The todo in line {} has the field '{}', but "
"this does neither match a date (\"%d.%m.%Y\") "
"nor a state.".format(
todo_tag.linenumber, other_field))
db.session.add(error)
db.session.commit()
return
raw_todos.append((who, what, field_id, field_state, field_date, todo_tag))
raw_todos.append(
(who, what, field_id, field_state, field_date, todo_tag))
for (_, _, field_id, _, _, _) in raw_todos:
if field_id is not None:
old_todos = [todo for todo in old_todos
if todo.id != field_id]
old_todos = [
todo for todo in old_todos
if todo.id != field_id
]
for todo in old_todos:
protocol.todos.remove(todo)
db.session.commit()
......@@ -294,28 +329,23 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
if field_state is None:
field_state = TodoState.open
if field_state.needs_date() and field_date is None:
error = protocol.create_error("Parsing",
"Todo missing date",
return _make_error(
protocol, "Parsing", "Todo missing date",
"The todo in line {} has a state that needs a date, "
"but the todo does not have one.".format(todo_tag.linenumber))
db.session.add(error)
db.session.commit()
return
who = who.strip()
what = what.strip()
todo = None
if field_id is not None:
todo = Todo.query.filter_by(number=field_id).first()
if todo is None and not config.PARSER_LAZY:
error = protocol.create_error("Parsing",
"Invalid Todo ID",
return _make_error(
protocol, "Parsing", "Invalid Todo ID",
"The todo in line {} has the ID {}, but there is no "
"Todo with that ID.".format(todo_tag.linenumber, field_id))
db.session.add(error)
db.session.commit()
return
if todo is None and field_id is None and what in old_todo_number_map:
todo = Todo(protocoltype_id=protocol.protocoltype.id,
todo = Todo(
protocoltype_id=protocol.protocoltype.id,
who=who, description=what, state=field_state,
date=field_date, number=old_todo_number_map[what])
db.session.add(todo)
......@@ -326,7 +356,8 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
OldTodo.protocol_key == protocol_key).all()
if len(old_candidates) == 0:
# new protocol
todo = Todo(protocoltype_id=protocol.protocoltype.id,
todo = Todo(
protocoltype_id=protocol.protocoltype.id,
who=who, description=what, state=field_state,
date=field_date)
db.session.add(todo)
......@@ -338,7 +369,8 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
number = field_id or lookup_todo_id(old_candidates, who, what)
todo = Todo.query.filter_by(number=number).first()
if todo is None:
todo = Todo(protocoltype_id=protocol.protocoltype.id,
todo = Todo(
protocoltype_id=protocol.protocoltype.id,
who=who, description=what, state=field_state,
date=field_date, number=number)
db.session.add(todo)
......@@ -360,13 +392,12 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
decision_tags = [tag for tag in tags if tag.name == "beschluss"]
for decision_tag in decision_tags:
if decision_tag not in public_elements:
error = protocol.create_error("Parsing", "Decision in private context.",
"The decision in line {} is in a private context, but decisions are "
"and have to be public. Please move it to a public spot.".format(
return _make_error(
protocol, "Parsing", "Decision in private context.",
"The decision in line {} is in a private context, but "
"decisions are and have to be public. "
"Please move it to a public spot.".format(
decision_tag.linenumber))
db.session.add(error)
db.session.commit()
return
old_decisions = list(protocol.decisions)
for decision in old_decisions:
protocol.decisions.remove(decision)
......@@ -374,36 +405,35 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
decisions_to_render = []
for decision_tag in decision_tags:
if len(decision_tag.values) == 0:
error = protocol.create_error("Parsing", "Empty decision found.",
"The decision in line {} is empty.".format(decision_tag.linenumber))
db.session.add(error)
db.session.commit()
return
return _make_error(
protocol, "Parsing", "Empty decision found.",
"The decision in line {} is empty.".format(
decision_tag.linenumber))
decision_content = decision_tag.values[0]
decision_categories = []
for decision_category_name in decision_tag.values[1:]:
decision_category = DecisionCategory.query.filter_by(protocoltype_id=protocol.protocoltype.id, name=decision_category_name).first()
decision_category = DecisionCategory.query.filter_by(
protocoltype_id=protocol.protocoltype.id,
name=decision_category_name).first()
if decision_category is None:
category_candidates = DecisionCategory.query.filter_by(protocoltype_id=protocol.protocoltype.id).all()
category_candidates = DecisionCategory.query.filter_by(
protocoltype_id=protocol.protocoltype.id).all()
category_names = [
"'{}'".format(category.name)
for category in category_candidates
]
error = protocol.create_error("Parsing",
"Unknown decision category",
return _make_error(
protocol, "Parsing", "Unknown decision category",
"The decision in line {} has the category {}, "
"but there is no such category. "
"Known categories are {}".format(
decision_tag.linenumber,
decision_category_name,
", ".join(category_names)))
db.session.add(error)
db.session.commit()
return
else:
decision_categories.append(decision_category)
decision = Decision(protocol_id=protocol.id,
content=decision_content)
decision = Decision(
protocol_id=protocol.id, content=decision_content)
db.session.add(decision)
db.session.commit()
for decision_category in decision_categories:
......@@ -412,66 +442,67 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
decisions_to_render.append((decision, decision_tag))
for decision, decision_tag in decisions_to_render:
decision_top = decision_tag.fork.get_top()
decision_content = texenv.get_template(provide_latex_template(protocol.protocoltype.latex_template, "decision")).render(
decision_content = texenv.get_template(provide_latex_template(
protocol.protocoltype.latex_template, "decision")).render(
render_type=RenderType.latex, decision=decision,
protocol=protocol, top=decision_top, show_private=True)
maxdepth = decision_top.get_maxdepth()
compile_decision(decision_content, decision, maxdepth=maxdepth)
# Footnotes
footnote_tags = [tag for tag in tags if tag.name == "footnote"]
public_footnote_tags = [tag for tag in footnote_tags if tag in public_elements]
footnote_tags = [
tag for tag in tags
if tag.name == "footnote"
]
public_footnote_tags = [
tag for tag in footnote_tags
if tag in public_elements
]
# new Protocols
protocol_tags = [tag for tag in tags if tag.name == "sitzung"]
for protocol_tag in protocol_tags:
if len(protocol_tag.values) not in {1, 2}:
error = protocol.create_error("Parsing",
"Falsche Verwendung von [sitzung;…].",
return _make_error(
protocol, "Parsing", "Falsche Verwendung von [sitzung;…].",
"Der Tag \"sitzung\" benötigt immer ein Datum "
"und optional eine Uhrzeit, also ein bis zwei Argumente. "
"Stattdessen wurden {} übergeben, nämlich {}".format(
len(protocol_tag.values),
protocol_tag.values))
db.session.add(error)
db.ession.commit()
return
else:
try:
protocol_date = parse_datetime_from_string(
protocol_tag.values[0])
parse_datetime_from_string(protocol_tag.values[0])
except ValueError as exc:
error = protocol.create_error("Parsing", "Invalides Datum",
return _make_error(
protocol, "Parsing", "Invalides Datum",
"'{}' ist kein valides Datum.".format(
protocol_tag.values[0]))
db.session.add(error)
db.session.commit()
return
if len(protocol_tag.values) > 1:
try:
protocol_time = datetime.strptime(protocol_tag.values[1], "%H:%M")
datetime.strptime(protocol_tag.values[1], "%H:%M")
except ValueError:
error = protocol.create_error("Parsing", "Invalide Uhrzeit",
return _make_error(
protocol, "Parsing", "Invalide Uhrzeit",
"'{}' ist keine valide Uhrzeit.".format(
protocol_tag.values[1]))
db.session.add(error)
db.session.commit()
return
for protocol_tag in protocol_tags:
new_protocol_date = parse_datetime_from_string(protocol_tag.values[0])
new_protocol_time = None
if len(protocol_tag.values) > 1:
new_protocol_time = datetime.strptime(protocol_tag.values[1], "%H:%M")
Protocol.create_new_protocol(protocol.protocoltype,
new_protocol_date, new_protocol_time)
new_protocol_time = datetime.strptime(
protocol_tag.values[1], "%H:%M")
Protocol.create_new_protocol(
protocol.protocoltype, new_protocol_date, new_protocol_time)
# TOPs
old_tops = list(protocol.tops)
for top in old_tops:
protocol.tops.remove(top)
tops = []
for index, fork in enumerate((child for child in tree.children if isinstance(child, Fork))):
top = TOP(protocol_id=protocol.id, name=fork.name, number=index,
for index, fork in enumerate(
(child for child in tree.children if isinstance(child, Fork))):
top = TOP(
protocol_id=protocol.id, name=fork.name, number=index,
planned=False)
db.session.add(top)
db.session.commit()
......@@ -488,23 +519,32 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
maxdepth = tree.get_maxdepth()
privacy_states = [False]
content_private = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=True, **private_render_kwargs)
content_public = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=False, **public_render_kwargs)
content_private = render_template(
"protocol.txt", render_type=RenderType.plaintext, show_private=True,
**private_render_kwargs)
content_public = render_template(
"protocol.txt", render_type=RenderType.plaintext, show_private=False,
**public_render_kwargs)
if content_private != content_public:
privacy_states.append(True)
protocol.content_private = content_private
protocol.content_public = content_public
protocol.content_html_private = render_template("protocol.html",
render_type=RenderType.html, show_private=True, **private_render_kwargs)
protocol.content_html_public = render_template("protocol.html",
render_type=RenderType.html, show_private=False, **public_render_kwargs)
protocol.content_html_private = render_template(
"protocol.html", render_type=RenderType.html, show_private=True,
**private_render_kwargs)
protocol.content_html_public = render_template(
"protocol.html", render_type=RenderType.html, show_private=False,
**public_render_kwargs)
for show_private in privacy_states:
latex_source = texenv.get_template(provide_latex_template(protocol.protocoltype.latex_template, "protocol")).render(
latex_source = texenv.get_template(provide_latex_template(
protocol.protocoltype.latex_template, "protocol")).render(
render_type=RenderType.latex,
show_private=show_private,
**render_kwargs[show_private])
compile(latex_source, protocol, show_private=show_private, maxdepth=maxdepth)
compile(
latex_source, protocol, show_private=show_private,
maxdepth=maxdepth)
if protocol.protocoltype.use_wiki:
wiki_type = WikiType[getattr(config, "WIKI_TYPE", "MEDIAWIKI")]
......@@ -525,17 +565,21 @@ def parse_protocol_async_inner(protocol, encoded_kwargs):
if wiki_type == WikiType.MEDIAWIKI:
wiki_infobox_source = wikienv.get_template("infobox.wiki").render(
protocoltype=protocol.protocoltype)
push_to_wiki(protocol, wiki_source, wiki_infobox_source,
push_to_wiki(
protocol, wiki_source, wiki_infobox_source,
"Automatisch generiert vom Protokollsystem 3.0")
elif wiki_type == WikiType.DOKUWIKI:
push_to_dokuwiki(protocol, wiki_source,
push_to_dokuwiki(
protocol, wiki_source,
"Automatisch generiert vom Protokollsystem 3.0")
protocol.done = True
db.session.commit()
def push_to_wiki(protocol, content, infobox_content, summary):
push_to_wiki_async.delay(protocol.id, content, infobox_content, summary)
@celery.task
def push_to_wiki_async(protocol_id, content, infobox_content, summary):
with WikiClient() as wiki_client, app.app_context():
......@@ -550,40 +594,48 @@ def push_to_wiki_async(protocol_id, content, infobox_content, summary):
content=content,
summary=summary)
except WikiException as exc:
error = protocol.create_error("Pushing to Wiki", "Pushing to Wiki failed.", str(exc))
db.session.add(error)
db.session.commit()
return _make_error(
protocol, "Pushing to Wiki", "Pushing to Wiki failed.",
str(exc))
def push_to_dokuwiki(protocol, content, summary):
push_to_dokuwiki_async.delay(protocol.id, content, summary)
@celery.task
def push_to_dokuwiki_async(protocol_id, content, summary):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
with xmlrpc.client.ServerProxy(config.WIKI_API_URL) as proxy:
try:
if not proxy.wiki.putPage(protocol.get_wiki_title(),
content, {"sum": "Automatisch generiert vom Protokollsystem 3."}):
error = protocol.create_error("Pushing to Wiki",
if not proxy.wiki.putPage(
protocol.get_wiki_title(), content,
{"sum":
"Automatisch generiert vom Protokollsystem 3."}):
return _make_error(
protocol, "Pushing to Wiki",
"Pushing to Wiki failed." "")
db.session.add(error)
db.session.commit()
except xmlrpc.client.Error as exception:
error = protocol.create_error("Pushing to Wiki",
"XML RPC Exception",
return _make_error(
protocol, "Pushing to Wiki", "XML RPC Exception",
str(exception))
db.session.add(error)
db.session.commit()
def compile(content, protocol, show_private, maxdepth):
compile_async.delay(content, protocol.id, show_private=show_private, maxdepth=maxdepth)
compile_async.delay(
content, protocol.id, show_private=show_private, maxdepth=maxdepth)
def compile_decision(content, decision, maxdepth):
compile_async.delay(content, decision.id, use_decision=True, maxdepth=maxdepth)
compile_async.delay(
content, decision.id, use_decision=True, maxdepth=maxdepth)
@celery.task
def compile_async(content, protocol_id, show_private=False, use_decision=False, maxdepth=5):
def compile_async(
content, protocol_id, show_private=False, use_decision=False,
maxdepth=5):
with tempfile.TemporaryDirectory() as compile_dir, app.app_context():
decision = None
protocol = None
......@@ -598,10 +650,19 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
protocol_target_filename = "protocol.pdf"
protocol_class_filename = "protokoll2.cls"
log_filename = "protocol.log"
with open(os.path.join(compile_dir, protocol_source_filename), "w") as source_file:
with open(
os.path.join(compile_dir, protocol_source_filename),
"w") as source_file:
source_file.write(content)
protocol2_class_source = texenv.get_template(provide_latex_template(protocol.protocoltype.latex_template, "class")).render(fonts=config.FONTS, maxdepth=maxdepth, bulletpoints=config.LATEX_BULLETPOINTS)
with open(os.path.join(compile_dir, protocol_class_filename), "w") as protocol2_class_file:
protocol2_class_source = texenv.get_template(
provide_latex_template(
protocol.protocoltype.latex_template,
"class")).render(
fonts=config.FONTS, maxdepth=maxdepth,
bulletpoints=config.LATEX_BULLETPOINTS)
with open(
os.path.join(compile_dir, protocol_class_filename),
"w") as protocol2_class_file:
protocol2_class_file.write(protocol2_class_source)
os.chdir(compile_dir)
command = [
......@@ -610,15 +671,23 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
"-file-line-error",
protocol_source_filename
]
subprocess.check_call(command, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.check_call(command, universal_newlines=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.check_call(
command, universal_newlines=True, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
subprocess.check_call(
command, universal_newlines=True, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
os.chdir(current)
document = None
if not use_decision:
for old_document in [document for document in protocol.documents if document.is_compiled and document.is_private == show_private]:
for old_document in [
document for document in protocol.documents
if document.is_compiled
and document.is_private == show_private]:
protocol.documents.remove(old_document)
db.session.commit()
document = Document(protocol_id=protocol.id,
document = Document(
protocol_id=protocol.id,
name="protokoll{}_{}_{}.pdf".format(
"_intern" if show_private else "",
protocol.protocoltype.short_name,
......@@ -627,7 +696,8 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
is_compiled=True,
is_private=show_private)
else:
document = DecisionDocument(decision_id=decision.id,
document = DecisionDocument(
decision_id=decision.id,
name="beschluss_{}_{}_{}.pdf".format(
protocol.protocoltype.short_name,
date_filter_short(protocol.date),
......@@ -635,48 +705,60 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False,
filename="")
db.session.add(document)
db.session.commit()
target_filename = "compiled-{}-{}.pdf".format(document.id, "internal" if show_private else "public")
target_filename = "compiled-{}-{}.pdf".format(
document.id, "internal" if show_private else "public")
if use_decision:
target_filename = "decision-{}-{}-{}.pdf".format(protocol.id, decision.id, document.id)
target_filename = "decision-{}-{}-{}.pdf".format(
protocol.id, decision.id, document.id)
document.filename = target_filename
shutil.copy(os.path.join(compile_dir, protocol_target_filename), os.path.join(config.DOCUMENTS_PATH, target_filename))
shutil.copy(
os.path.join(compile_dir, protocol_target_filename),
os.path.join(config.DOCUMENTS_PATH, target_filename))
db.session.commit()
shutil.copy(os.path.join(compile_dir, log_filename), "/tmp/proto-tex.log")
shutil.copy(
os.path.join(compile_dir, log_filename),
"/tmp/proto-tex.log")
except subprocess.SubprocessError:
log = ""
total_log_filename = os.path.join(compile_dir, log_filename)
total_source_filename = os.path.join(compile_dir, protocol_source_filename)
total_source_filename = os.path.join(
compile_dir, protocol_source_filename)
log = ""
if os.path.isfile(total_source_filename):
with open(total_source_filename, "r") as source_file:
log += "Source:\n\n" + add_line_numbers(source_file.read())
total_class_filename = os.path.join(compile_dir, protocol_class_filename)
total_class_filename = os.path.join(
compile_dir, protocol_class_filename)
if os.path.isfile(total_class_filename):
with open(total_class_filename, "r") as class_file:
log += "\n\nClass:\n\n" + add_line_numbers(class_file.read())
log += "\n\nClass:\n\n" + add_line_numbers(
class_file.read())
if os.path.isfile(total_log_filename):
with open(total_log_filename, "r") as log_file:
log += "\n\nLog:\n\n" + add_line_numbers(log_file.read())
else:
log += "\n\nLogfile not found."
error = protocol.create_error("Compiling", "Compiling LaTeX failed", log)
db.session.add(error)
db.session.commit()
_make_error(protocol, "Compiling", "Compiling LaTeX failed", log)
finally:
os.chdir(current)
def print_file(filename, protocol):
if config.PRINTING_ACTIVE:
print_file_async.delay(filename, protocol.id)
@celery.task
def print_file_async(filename, protocol_id):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
if protocol.protocoltype.printer is None:
error = protocol.create_error("Printing", "No printer configured.", "You don't have any printer configured for the protocoltype {}. Please do so before printing a protocol.".format(protocol.protocoltype.name))
db.session.add(error)
db.session.commit()
return _make_error(
protocol, "Printing", "No printer configured.",
"You don't have any printer configured for the "
"protocoltype {}. "
"Please do so before printing a protocol.".format(
protocol.protocoltype.name))
try:
command = [
"/usr/bin/lpr",
......@@ -685,70 +767,96 @@ def print_file_async(filename, protocol_id):
"-U", config.PRINTING_USER,
"-T", protocol.get_identifier(),
]
for option in config.PRINTING_PRINTERS[protocol.protocoltype.printer]:
command.extend(["-o", '"{}"'.format(option) if " " in option else option])
for option in config.PRINTING_PRINTERS[
protocol.protocoltype.printer]:
command.extend([
"-o", '"{}"'.format(option)
if " " in option else option])
command.append(filename)
subprocess.check_output(command, universal_newlines=True, stderr=subprocess.STDOUT)
subprocess.check_output(
command, universal_newlines=True, stderr=subprocess.STDOUT)
except subprocess.SubprocessError as exception:
error = protocol.create_error("Printing", "Printing {} failed.".format(protocol.get_identifier()), exception.stdout)
db.session.add(error)
db.session.commit()
return _make_error(
protocol, "Printing", "Printing {} failed.".format(
protocol.get_identifier()), exception.stdout)
def send_reminder(reminder, protocol):
send_reminder_async.delay(reminder.id, protocol.id)
@celery.task
def send_reminder_async(reminder_id, protocol_id):
with app.app_context():
reminder = MeetingReminder.query.filter_by(id=reminder_id).first()
protocol = Protocol.query.filter_by(id=protocol_id).first()
reminder_text = render_template("reminder-mail.txt", reminder=reminder, protocol=protocol)
reminder_text = render_template(
"reminder-mail.txt", reminder=reminder, protocol=protocol)
if reminder.send_public:
print("sending public reminder mail to {}".format(protocol.protocoltype.public_mail))
send_mail(protocol, protocol.protocoltype.public_mail,
send_mail(
protocol, protocol.protocoltype.public_mail,
"Tagesordnung der {}".format(protocol.protocoltype.name),
reminder_text, reply_to=protocol.protocoltype.public_mail)
if reminder.send_private:
print("sending private reminder mail to {}".format(protocol.protocoltype.private_mail))
send_mail(protocol, protocol.protocoltype.private_mail,
send_mail(
protocol, protocol.protocoltype.private_mail,
"Tagesordnung der {}".format(protocol.protocoltype.name),
reminder_text, reply_to=protocol.protocoltype.private_mail)
def remind_finishing(protocol, delay_days, min_delay_days):
remind_finishing_async.delay(protocol.id, delay_days, min_delay_days)
@celery.task
def remind_finishing_async(protocol_id, delay_days, min_delay_days):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
mail_text = render_template("remind-finishing-mail.txt",
protocol = Protocol.first_by_id(protocol_id)
mail_text = render_template(
"remind-finishing-mail.txt",
protocol=protocol, delay_days=delay_days,
min_delay_days=min_delay_days)
send_mail(protocol, protocol.protocoltype.private_mail,
send_mail(
protocol, protocol.protocoltype.private_mail,
"Unfertiges Protokoll der {}".format(protocol.protocoltype.name),
mail_text, reply_to=protocol.protocoltype.private_mail)
def send_protocol_private(protocol):
send_protocol_async.delay(protocol.id, show_private=True)
send_todomails_async.delay(protocol.id)
def send_protocol_public(protocol):
send_protocol_async.delay(protocol.id, show_private=False)
@celery.task
def send_protocol_async(protocol_id, show_private):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
next_protocol = Protocol.query.filter_by(protocoltype_id=protocol.protocoltype.id).filter_by(done=False).filter(Protocol.date > datetime.now()).order_by(Protocol.date).first()
to_addr = protocol.protocoltype.private_mail if show_private else protocol.protocoltype.public_mail
subject = "{}{}-Protokoll vom {}".format("Internes " if show_private else "", protocol.protocoltype.short_name, date_filter(protocol.date))
mail_content = render_template("protocol-mail.txt", protocol=protocol, show_private=show_private, next_protocol=next_protocol)
appendix = [(document.name, document.as_file_like())
next_protocol = Protocol.query.filter_by(
protocoltype_id=protocol.protocoltype.id).filter_by(
done=False).filter(
Protocol.date > datetime.now()).order_by(Protocol.date).first()
to_addr = (
protocol.protocoltype.private_mail
if show_private
else protocol.protocoltype.public_mail)
subject = "{}{}-Protokoll vom {}".format(
"Internes " if show_private else "",
protocol.protocoltype.short_name, date_filter(protocol.date))
mail_content = render_template(
"protocol-mail.txt", protocol=protocol, show_private=show_private,
next_protocol=next_protocol)
appendix = [
(document.name, document.as_file_like())
for document in protocol.documents
if show_private or not document.is_private
]
send_mail(protocol, to_addr, subject, mail_content, appendix)
@celery.task
def send_todomails_async(protocol_id):
with app.app_context():
......@@ -764,7 +872,8 @@ def send_todomails_async(protocol_id):
for user in users
}
subject = "Du hast noch was zu tun!"
todomail_providers = getattr(config, "ADDITIONAL_TODOMAIL_PROVIDERS", None)
todomail_providers = getattr(
config, "ADDITIONAL_TODOMAIL_PROVIDERS", None)
additional_todomails = {}
if todomail_providers:
for provider in todomail_providers:
......@@ -779,33 +888,42 @@ def send_todomails_async(protocol_id):
if user in additional_todomails:
todomail = additional_todomails[user]
if todomail is None:
error = protocol.create_error("Sending Todomail", "Sending Todomail failed.", "User {} has no Todo-Mail-Assignment.".format(user))
db.session.add(error)
db.session.commit()
_make_error(
protocol, "Sending Todomail", "Sending Todomail failed.",
"User {} has no Todo-Mail-Assignment.".format(user))
continue
to_addr = todomail.get_formatted_mail()
mail_content = render_template("todo-mail.txt", protocol=protocol, todomail=todomail, todos=grouped_todos[user])
send_mail(protocol, to_addr, subject, mail_content,
mail_content = render_template(
"todo-mail.txt", protocol=protocol, todomail=todomail,
todos=grouped_todos[user])
send_mail(
protocol, to_addr, subject, mail_content,
reply_to=protocol.protocoltype.private_mail)
def send_mail(protocol, to_addr, subject, content, appendix=None, reply_to=None):
def send_mail(protocol, to_addr, subject, content, appendix=None,
reply_to=None):
if to_addr is not None and len(to_addr.strip()) > 0:
send_mail_async.delay(protocol.id, to_addr, subject, content, appendix, reply_to)
send_mail_async.delay(
protocol.id, to_addr, subject, content, appendix, reply_to)
@celery.task
def send_mail_async(protocol_id, to_addr, subject, content, appendix, reply_to):
def send_mail_async(protocol_id, to_addr, subject, content, appendix,
reply_to):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
try:
mail_manager.send(to_addr, subject, content, appendix, reply_to)
except Exception as exc:
error = protocol.create_error("Sending Mail", "Sending mail failed", str(exc))
db.session.add(error)
db.session.commit()
return _make_error(
protocol, "Sending Mail", "Sending mail failed", str(exc))
def push_tops_to_calendar(protocol):
push_tops_to_calendar_async.delay(protocol.id)
@celery.task
def push_tops_to_calendar_async(protocol_id):
if not config.CALENDAR_ACTIVE:
......@@ -817,21 +935,22 @@ def push_tops_to_calendar_async(protocol_id):
description = render_template("calendar-tops.txt", protocol=protocol)
try:
client = CalendarClient(protocol.protocoltype.calendar)
client.set_event_at(begin=protocol.get_datetime(),
client.set_event_at(
begin=protocol.get_datetime(),
name=protocol.protocoltype.short_name, description=description)
except CalendarException as exc:
error = protocol.create_error("Calendar",
return _make_error(
protocol, "Calendar",
"Pushing TOPs to Calendar failed", str(exc))
db.session.add(error)
db.session.commit()
def set_etherpad_content(protocol):
set_etherpad_content_async.delay(protocol.id)
@celery.task
def set_etherpad_content_async(protocol_id):
with app.app_context():
protocol = Protocol.query.filter_by(id=protocol_id).first()
identifier = protocol.get_identifier()
return set_etherpad_text(identifier, protocol.get_template())
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment