Commit 42e8970d authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Add descriptions for default tops (per protocol)

/close #64
parent a85e279e
"""empty message
Revision ID: a06cc03bdef4
Revises: 4b813bbbd8ef
Create Date: 2017-03-15 22:47:07.462793
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a06cc03bdef4'
down_revision = '4b813bbbd8ef'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('localtops',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('protocol_id', sa.Integer(), nullable=True),
sa.Column('defaulttop_id', sa.Integer(), nullable=True),
sa.Column('description', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['defaulttop_id'], ['defaulttops.id'], ),
sa.ForeignKeyConstraint(['protocol_id'], ['protocols.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('localtops')
# ### end Alembic commands ###
...@@ -99,7 +99,6 @@ class ProtocolType(DatabaseModel): ...@@ -99,7 +99,6 @@ class ProtocolType(DatabaseModel):
and (self.private_group != "" and self.private_group in user.groups)) and (self.private_group != "" and self.private_group in user.groups))
or self.has_admin_right(user)) or self.has_admin_right(user))
def has_modify_right(self, user): def has_modify_right(self, user):
return ((user is not None return ((user is not None
and (self.modify_group != "" and self.modify_group in user.groups)) and (self.modify_group != "" and self.modify_group in user.groups))
...@@ -135,6 +134,7 @@ class ProtocolType(DatabaseModel): ...@@ -135,6 +134,7 @@ class ProtocolType(DatabaseModel):
def get_wiki_infobox_title(self): def get_wiki_infobox_title(self):
return "Vorlage:{}".format(self.get_wiki_infobox()) return "Vorlage:{}".format(self.get_wiki_infobox())
class Protocol(DatabaseModel): class Protocol(DatabaseModel):
__tablename__ = "protocols" __tablename__ = "protocols"
__model_name__ = "protocol" __model_name__ = "protocol"
...@@ -155,6 +155,7 @@ class Protocol(DatabaseModel): ...@@ -155,6 +155,7 @@ class Protocol(DatabaseModel):
documents = relationship("Document", backref=backref("protocol"), cascade="all, delete-orphan", order_by="Document.is_compiled") documents = relationship("Document", backref=backref("protocol"), cascade="all, delete-orphan", order_by="Document.is_compiled")
errors = relationship("Error", backref=backref("protocol"), cascade="all, delete-orphan", order_by="Error.id") errors = relationship("Error", backref=backref("protocol"), cascade="all, delete-orphan", order_by="Error.id")
metas = relationship("Meta", backref=backref("protocol"), cascade="all, delete-orphan") metas = relationship("Meta", backref=backref("protocol"), cascade="all, delete-orphan")
localtops = relationship("LocalTOP", backref=backref("protocol"), cascade="all, delete-orphan")
def get_parent(self): def get_parent(self):
return self.protocoltype return self.protocoltype
...@@ -282,6 +283,17 @@ class Protocol(DatabaseModel): ...@@ -282,6 +283,17 @@ class Protocol(DatabaseModel):
self.todos.remove(todo) self.todos.remove(todo)
db.session.delete(todo) db.session.delete(todo)
def get_tops(self):
tops_before, tops_after = [], []
if not self.has_nonplanned_tops():
for default_top in self.protocoltype.default_tops:
top = default_top.get_top(self)
if default_top.is_at_end():
tops_after.append(top)
else:
tops_before.append(top)
return tops_before + self.tops + tops_after
@event.listens_for(Protocol, "before_delete") @event.listens_for(Protocol, "before_delete")
def on_protocol_delete(mapper, connection, protocol): def on_protocol_delete(mapper, connection, protocol):
protocol.delete_orphan_todos() protocol.delete_orphan_todos()
...@@ -295,12 +307,30 @@ class DefaultTOP(DatabaseModel): ...@@ -295,12 +307,30 @@ class DefaultTOP(DatabaseModel):
name = db.Column(db.String) name = db.Column(db.String)
number = db.Column(db.Integer) number = db.Column(db.Integer)
localtops = relationship("LocalTOP", backref=backref("defaulttop"), cascade="all, delete-orphan")
def get_parent(self): def get_parent(self):
return self.protocoltype return self.protocoltype
def is_at_end(self): def is_at_end(self):
return self.number > 0 return self.number > 0
def get_localtop(self, protocol):
localtop = LocalTOP.query.filter_by(defaulttop_id=self.id,
protocol_id=protocol.id).first()
if localtop is None:
localtop = LocalTOP(protocol_id=protocol.id, defaulttop_id=self.id,
description="")
db.session.add(localtop)
db.session.commit()
return localtop
def get_top(self, protocol):
localtop = self.get_localtop(protocol)
top = TOP(protocol_id=protocol.id, name=self.name,
description=localtop.description)
return top
class TOP(DatabaseModel): class TOP(DatabaseModel):
__tablename__ = "tops" __tablename__ = "tops"
__model_name__ = "top" __model_name__ = "top"
...@@ -314,6 +344,17 @@ class TOP(DatabaseModel): ...@@ -314,6 +344,17 @@ class TOP(DatabaseModel):
def get_parent(self): def get_parent(self):
return self.protocol return self.protocol
class LocalTOP(DatabaseModel):
__tablename__ = "localtops"
__model_name__ = "localtop"
id = db.Column(db.Integer, primary_key=True)
protocol_id = db.Column(db.Integer, db.ForeignKey("protocols.id"))
defaulttop_id = db.Column(db.Integer, db.ForeignKey("defaulttops.id"))
description = db.Column(db.String)
def get_parent(self):
return self.protocol
class Document(DatabaseModel): class Document(DatabaseModel):
__tablename__ = "documents" __tablename__ = "documents"
__model_name__ = "document" __model_name__ = "document"
......
...@@ -23,8 +23,8 @@ import config ...@@ -23,8 +23,8 @@ import config
from shared import db, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, time_filter_short, ldap_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 shared import db, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, time_filter_short, ldap_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 from utils import is_past, mail_manager, url_manager, get_first_unused_int, set_etherpad_text, get_etherpad_text, split_terms, optional_int_arg
from decorators import db_lookup, require_public_view_right, require_private_view_right, require_modify_right, require_admin_right from decorators import db_lookup, require_public_view_right, require_private_view_right, require_modify_right, require_admin_right
from models.database import ProtocolType, Protocol, DefaultTOP, TOP, Document, Todo, Decision, MeetingReminder, Error, TodoMail, DecisionDocument, TodoState, Meta, DefaultMeta, DecisionCategory from models.database import ProtocolType, Protocol, DefaultTOP, TOP, LocalTOP, Document, Todo, Decision, MeetingReminder, Error, TodoMail, DecisionDocument, TodoState, Meta, DefaultMeta, DecisionCategory
from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm, KnownProtocolSourceUploadForm, NewProtocolSourceUploadForm, ProtocolForm, TopForm, SearchForm, DecisionSearchForm, ProtocolSearchForm, TodoSearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm, DecisionCategoryForm from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm, KnownProtocolSourceUploadForm, NewProtocolSourceUploadForm, ProtocolForm, TopForm, LocalTopForm, SearchForm, DecisionSearchForm, ProtocolSearchForm, TodoSearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm, DecisionCategoryForm
from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable, DocumentsTable, DecisionsTable, TodoTable, ErrorTable, TodoMailsTable, DefaultMetasTable, DecisionCategoriesTable from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable, DocumentsTable, DecisionsTable, TodoTable, ErrorTable, TodoMailsTable, DefaultMetasTable, DecisionCategoriesTable
from legacy import import_old_todos, import_old_protocols, import_old_todomails from legacy import import_old_todos, import_old_protocols, import_old_todomails
...@@ -760,6 +760,18 @@ def move_top(top, diff): ...@@ -760,6 +760,18 @@ def move_top(top, diff):
flash("Die angegebene Differenz ist keine Zahl.", "alert-error") flash("Die angegebene Differenz ist keine Zahl.", "alert-error")
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=top.protocol.id)) return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=top.protocol.id))
@app.route("/protocol/localtop/edit/<int:localtop_id>", methods=["GET", "POST"])
@login_required
@db_lookup(LocalTOP)
@require_modify_right()
def edit_localtop(localtop):
form = LocalTopForm(obj=localtop)
if form.validate_on_submit():
form.populate_obj(localtop)
db.session.commit()
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=localtop.protocol.id))
return render_template("localtop-edit.html", form=form, localtop=localtop)
def _get_page(): def _get_page():
try: try:
page = request.args.get("page") page = request.args.get("page")
......
{% if not protocol.has_nonplanned_tops() %} {% for top in protocol.get_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if not default_top.is_at_end() %}
{{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% for top in protocol.tops %}
{{top.name}} {{top.name}}
{% endfor %} {% endfor %}
{% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %}
{{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% extends "layout.html" %}
{% from "macros.html" import render_form %}
{% block title %}TOP-Beschreibung bearbeiten{% endblock %}
{% block content %}
<div class="container">
<h3>{{localtop.defaulttop.name}}</h3>
{{render_form(form, action_url=url_for("edit_localtop", localtop_id=localtop.id, next=request.args.get("next") or url_for("show_protocol", protocol_id=localtop.protocol.id)), action_text="Ändern", textarea_rows=5)}}
</div>
{% endblock %}
...@@ -9,23 +9,9 @@ Zeit: von {{protocol.start_time|timify}} bis {{protocol.end_time|timify}} ...@@ -9,23 +9,9 @@ Zeit: von {{protocol.start_time|timify}} bis {{protocol.end_time|timify}}
{% endfor %} {% endfor %}
Die Tagesordnung ist: Die Tagesordnung ist:
{% if not protocol.has_nonplanned_tops() %} {% for top in protocol.get_tops() %}
{% for default_top in protocol.protocoltype.default_tops %} * {{top.name}}
{% if not default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% for top in protocol.tops %}
* {{top.name }}
{% endfor %} {% endfor %}
{% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
Beschlüsse: Beschlüsse:
{% if protocol.decisions|length > 0 %} {% if protocol.decisions|length > 0 %}
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
{% if not protocol.has_nonplanned_tops() %} {% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %} {% for default_top in protocol.protocoltype.default_tops %}
{% if not default_top.is_at_end() %} {% if not default_top.is_at_end() %}
{{-render_top(default_top)}} {{-render_top(default_top.get_top(protocol), use_description=True)}}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
{% if not protocol.has_nonplanned_tops() %} {% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %} {% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %} {% if default_top.is_at_end() %}
{{render_top(default_top)}} {{-render_top(default_top.get_top(protocol), use_description=True)}}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
...@@ -2,7 +2,19 @@ ...@@ -2,7 +2,19 @@
{% if not protocol.has_nonplanned_tops() %} {% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %} {% for default_top in protocol.protocoltype.default_tops %}
{% if not default_top.is_at_end() %} {% if not default_top.is_at_end() %}
<li>{{default_top.name}}</li> {% set localtop = default_top.get_localtop(protocol) %}
<li{% if has_private_view_right and localtop.description is not none and localtop.description|length > 0 %} class="expansion-button" id="localtop-{{localtop.id}}" title="{{localtop.description}}"{% endif %}>
{{default_top.name}}
{% if not protocol.is_done() and has_modify_right %}
<a href="{{url_for('edit_localtop', localtop_id=localtop.id)}}">Ändern</a>
{% endif %}
{% if has_private_view_right and localtop.description is not none and localtop.description|length > 0 %}
<span class="glyphicon glyphicon-info-sign"></span>
<pre id="localtop-{{localtop.id}}-description" class="expansion-text">
{{-localtop.description-}}
</pre>
{% endif %}
</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
...@@ -29,7 +41,19 @@ ...@@ -29,7 +41,19 @@
{% if not protocol.has_nonplanned_tops() %} {% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %} {% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %} {% if default_top.is_at_end() %}
<li>{{default_top.name}}</li> {% set localtop = default_top.get_localtop(protocol) %}
<li{% if has_private_view_right and localtop.description is not none and localtop.description|length > 0 %} class="expansion-button" id="localtop-{{localtop.id}}" title="{{localtop.description}}"{% endif %}>
{{default_top.name}}
{% if not protocol.is_done() and has_modify_right %}
<a href="{{url_for('edit_localtop', localtop_id=localtop.id)}}">Ändern</a>
{% endif %}
{% if has_private_view_right and localtop.description is not none and localtop.description|length > 0 %}
<span class="glyphicon glyphicon-info-sign"></span>
<pre id="localtop-{{localtop.id}}-description" class="expansion-text">
{{-localtop.description-}}
</pre>
{% endif %}
</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
......
Die nächste {{protocol.protocoltype.name}} findet am {{protocol.date|datify}} um {{protocol.protocoltype.usual_time|timify}} statt. Die nächste {{protocol.protocoltype.name}} findet am {{protocol.date|datify}} um {{protocol.protocoltype.usual_time|timify}} statt.
Die vorläufige Tagesordnung ist: Die vorläufige Tagesordnung ist:
{% if not protocol.has_nonplanned_tops() %} {% for top in protocol.get_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if not default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% for top in protocol.tops %}
* {{top.name }} * {{top.name }}
{% endfor %} {% endfor %}
{% if not protocol.has_nonplanned_tops() %}
{% for default_top in protocol.protocoltype.default_tops %}
{% if default_top.is_at_end() %}
* {{default_top.name}}
{% endif %}
{% endfor %}
{% endif %}
{% if reminder.additional_text is not none %} {% if reminder.additional_text is not none %}
{{reminder.additional_text}} {{reminder.additional_text}}
{% endif %} {% endif %}
...@@ -190,6 +190,9 @@ class TopForm(FlaskForm): ...@@ -190,6 +190,9 @@ class TopForm(FlaskForm):
number = IntegerField("Sortierung", validators=[InputRequired("Du musst eine Sortierung in der Reihenfolge angebene.")]) number = IntegerField("Sortierung", validators=[InputRequired("Du musst eine Sortierung in der Reihenfolge angebene.")])
description = TextAreaField("Beschreibung") description = TextAreaField("Beschreibung")
class LocalTopForm(FlaskForm):
description = TextAreaField("Beschreibung")
class SearchForm(FlaskForm): class SearchForm(FlaskForm):
search = StringField("Suchbegriff") search = StringField("Suchbegriff")
protocoltype_id = SelectField("Typ", choices=[], coerce=int) protocoltype_id = SelectField("Typ", choices=[], coerce=int)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment