From 023b130cde91f4e38c719cb20828cceda7460597 Mon Sep 17 00:00:00 2001 From: Robin Sonnabend <robin@fsmpi.rwth-aachen.de> Date: Tue, 13 Jun 2017 19:34:16 +0200 Subject: [PATCH] Footnotes and small fixes /close #132 /close #130 --- parser.py | 22 ++++++++++++++- server.py | 3 ++- tasks.py | 59 ++++++++++++++++++++++++++++++++--------- templates/protocol.html | 7 +++++ templates/protocol.wiki | 4 +++ utils.py | 3 +++ 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/parser.py b/parser.py index 9e7285a..12703d1 100644 --- a/parser.py +++ b/parser.py @@ -4,6 +4,7 @@ from collections import OrderedDict from enum import Enum from shared import escape_tex +from utils import footnote_hash import config @@ -223,6 +224,8 @@ class Tag: r"\textit{{({})}}".format(self.decision.get_categories_str()) ) return " ".join(parts) + elif self.name == "footnote": + return r"\footnote{{{}}}".format(self.values[0]) return r"\textbf{{{}:}} {}".format(escape_tex(self.name.capitalize()), escape_tex(";".join(self.values))) elif render_type == RenderType.plaintext: if self.name == "url": @@ -231,6 +234,8 @@ class Tag: if not show_private: return "" return self.values[0] + elif self.name == "footnote": + return "[^]({})".format(self.values[0]) return "{}: {}".format(self.name.capitalize(), ";".join(self.values)) elif render_type == RenderType.wikitext: if self.name == "url": @@ -239,6 +244,8 @@ class Tag: if not show_private: return "" return self.todo.render_wikitext(current_protocol=protocol) + elif self.name == "footnote": + return "<ref>{}</ref>".format(self.values[0]) return "'''{}:''' {}".format(self.name.capitalize(), ";".join(self.values)) elif render_type == RenderType.html: if self.name == "url": @@ -259,6 +266,9 @@ class Tag: return " ".join(parts) else: return "<b>Beschluss:</b> {}".format(self.values[0]) + elif self.name == "footnote": + return '<sup id="#fnref{0}"><a href="#fn{0}">Fn</a></sup>'.format( + footnote_hash(self.values[0])) else: raise _not_implemented(self, render_type) @@ -284,7 +294,7 @@ class Tag: # v3: also match [] without semicolons inbetween, as there is not other use for that PATTERN = r"\[(?<content>[^\]]*)\]" - KNOWN_TAGS = ["todo", "url", "beschluss"] + KNOWN_TAGS = ["todo", "url", "beschluss", "footnote"] class Empty(Element): @@ -495,6 +505,16 @@ class Fork(Element): else: return 1 + def get_visible_elements(self, show_private, elements=None): + if elements is None: + elements = set() + if show_private or not self.test_private(self.name): + for child in self.children: + elements.add(child) + if isinstance(child, Fork): + child.get_visible_elements(show_private, elements) + return elements + @staticmethod def create_root(): return Fork(None, None, None, 0) diff --git a/server.py b/server.py index 0ca5a9a..cfb636c 100755 --- a/server.py +++ b/server.py @@ -21,7 +21,7 @@ import mimetypes import config from shared import db, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, time_filter_short, user_manager, security_manager, current_user, check_login, login_required, group_required, class_filter, needs_date_test, todostate_name_filter, code_filter, indent_tab_filter -from utils import is_past, mail_manager, url_manager, get_first_unused_int, set_etherpad_text, get_etherpad_text, split_terms, optional_int_arg, fancy_join +from utils import is_past, mail_manager, url_manager, get_first_unused_int, set_etherpad_text, get_etherpad_text, split_terms, optional_int_arg, fancy_join, footnote_hash from decorators import db_lookup, require_public_view_right, require_private_view_right, require_modify_right, require_publish_right, require_admin_right from models.database import ProtocolType, Protocol, DefaultTOP, TOP, LocalTOP, Document, Todo, Decision, MeetingReminder, Error, TodoMail, DecisionDocument, TodoState, Meta, DefaultMeta, DecisionCategory, Like from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm, KnownProtocolSourceUploadForm, NewProtocolSourceUploadForm, generate_protocol_form, TopForm, LocalTopForm, SearchForm, DecisionSearchForm, ProtocolSearchForm, TodoSearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm, DecisionCategoryForm, DocumentEditForm @@ -60,6 +60,7 @@ app.jinja_env.filters["todo_get_name"] = todostate_name_filter app.jinja_env.filters["code"] = code_filter app.jinja_env.filters["indent_tab"] = indent_tab_filter app.jinja_env.filters["fancy_join"] = fancy_join +app.jinja_env.filters["footnote_hash"] = footnote_hash app.jinja_env.tests["auth_valid"] = security_manager.check_user app.jinja_env.tests["needs_date"] = needs_date_test diff --git a/tasks.py b/tasks.py index ef4d1ba..f393309 100644 --- a/tasks.py +++ b/tasks.py @@ -12,7 +12,7 @@ from models.database import Document, Protocol, Error, Todo, Decision, TOP, Defa 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 -from utils import mail_manager, url_manager, encode_kwargs, decode_kwargs, add_line_numbers, set_etherpad_text, get_etherpad_text +from utils import mail_manager, url_manager, encode_kwargs, decode_kwargs, add_line_numbers, set_etherpad_text, get_etherpad_text, footnote_hash from parser import parse, ParserException, Element, Content, Text, Tag, Remark, Fork, RenderType from wiki import WikiClient, WikiException from calendarpush import Client as CalendarClient, CalendarException @@ -168,6 +168,8 @@ def parse_protocol_async_inner(protocol, encoded_kwargs): return # 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", @@ -304,11 +306,20 @@ def parse_protocol_async_inner(protocol, encoded_kwargs): db.session.commit() todo_tag.todo = todo # Decisions + 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( + 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) db.session.commit() - decision_tags = [tag for tag in tags if tag.name == "beschluss"] decisions_to_render = [] for decision_tag in decision_tags: if len(decision_tag.values) == 0: @@ -352,9 +363,15 @@ def parse_protocol_async_inner(protocol, encoded_kwargs): decision_top = decision_tag.fork.get_top() decision_content = texenv.get_template("decision.tex").render( render_type=RenderType.latex, decision=decision, - protocol=protocol, top=decision_top, show_private=False) + 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] + + # TOPs old_tops = list(protocol.tops) for top in old_tops: protocol.tops.remove(top) @@ -365,31 +382,47 @@ def parse_protocol_async_inner(protocol, encoded_kwargs): db.session.add(top) db.session.commit() - render_kwargs = { + # render + private_render_kwargs = { "protocol": protocol, - "tree": tree + "tree": tree, + "footnotes": footnote_tags, } + public_render_kwargs = copy(private_render_kwargs) + public_render_kwargs["footnotes"] = public_footnote_tags + render_kwargs = {True: private_render_kwargs, False: public_render_kwargs} + maxdepth = tree.get_maxdepth() privacy_states = [False] - content_private = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=True, **render_kwargs) - content_public = render_template("protocol.txt", render_type=RenderType.plaintext, show_private=False, **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, **render_kwargs) + 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, **render_kwargs) + render_type=RenderType.html, show_private=False, **public_render_kwargs) for show_private in privacy_states: - latex_source = texenv.get_template("protocol.tex").render(render_type=RenderType.latex, show_private=show_private, **render_kwargs) + latex_source = texenv.get_template("protocol.tex").render( + render_type=RenderType.latex, + show_private=show_private, + **render_kwargs[show_private]) compile(latex_source, protocol, show_private=show_private, maxdepth=maxdepth) if protocol.protocoltype.use_wiki: - wiki_source = wikienv.get_template("protocol.wiki").render(render_type=RenderType.wikitext, show_private=not protocol.protocoltype.wiki_only_public, **render_kwargs).replace("\n\n\n", "\n\n") - wiki_infobox_source = wikienv.get_template("infobox.wiki").render(protocoltype=protocol.protocoltype) - push_to_wiki(protocol, wiki_source, wiki_infobox_source, "Automatisch generiert vom Protokollsystem 3.0") + show_private = not protocol.protocoltype.wiki_only_public + wiki_source = wikienv.get_template("protocol.wiki").render( + render_type=RenderType.wikitext, + show_private=show_private, + **render_kwargs[show_private] + ).replace("\n\n\n", "\n\n") + wiki_infobox_source = wikienv.get_template("infobox.wiki").render( + protocoltype=protocol.protocoltype) + push_to_wiki(protocol, wiki_source, wiki_infobox_source, + "Automatisch generiert vom Protokollsystem 3.0") protocol.done = True db.session.commit() diff --git a/templates/protocol.html b/templates/protocol.html index 08a54d2..391beba 100644 --- a/templates/protocol.html +++ b/templates/protocol.html @@ -4,3 +4,10 @@ {% endif %} {% endfor %} + +{% if footnotes|length > 0 %} + <hr /> + {% for footnote in footnotes %} + <p><sup id="fn{{footnote.values[0]|footnote_hash}}">{{loop.index}}. {{footnote.values[0]}} <a href="#fnref{{footnote.values[0]|footnote_hash}}">↩</a></sup></p> + {% endfor %} +{% endif %} diff --git a/templates/protocol.wiki b/templates/protocol.wiki index bc5e061..2ab67c9 100644 --- a/templates/protocol.wiki +++ b/templates/protocol.wiki @@ -28,4 +28,8 @@ <env> endif </env> <env> endfor </env> +<env> if footnotes|length > 0 </env> + <references /> +<env> endif </env> + [[Kategorie:<var>protocol.protocoltype.wiki_category</var>]] diff --git a/utils.py b/utils.py index 1ac0c4d..dfc3fa1 100644 --- a/utils.py +++ b/utils.py @@ -200,3 +200,6 @@ def fancy_join(values, sep1=" und ", sep2=", "): last = values[-1] start = values[:-1] return "{}{}{}".format(sep2.join(start), sep1, last) + +def footnote_hash(text, length=5): + return str(sum(ord(c) * i for i, c in enumerate(text)) % 10**length) -- GitLab