Commit d5efed83 authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Metadata before compiling and its handling

This adds default values to default metadata and the option to set it
before compiling the protocol (i.e. while planning to TO).
Additionally, the (dynamic) metadata fields are handled correctly in the
ProtocolForm and metadata is shown while still planning.
Additionally, protocol.start_time is used instead of
protocoltype.usual_time.

/close #47
/close #65
/close #106
parent 173da7c9
......@@ -131,9 +131,8 @@ class ADManager:
for group_dn in result.memberOf:
group_dn_parts = parse_dn(group_dn)
if len(group_dn_parts) >= 1:
for group_dn in group_dn_parts:
key, group, next_char = group_dn
yield group
key, group, next_char = group_dn_parts[0]
yield group
def all_groups(self):
connection = self.prepare_connection()
......
"""empty message
Revision ID: 70547c924023
Revises: 4e472894cc70
Create Date: 2017-04-17 23:31:58.499305
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '70547c924023'
down_revision = '4e472894cc70'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('defaultmetas', sa.Column('prior', sa.Boolean(), nullable=False))
op.add_column('defaultmetas', sa.Column('value', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('defaultmetas', 'value')
op.drop_column('defaultmetas', 'prior')
# ### end Alembic commands ###
......@@ -249,8 +249,14 @@ class Protocol(DatabaseModel):
db.session.commit()
return get_etherpad_url(self.pad_identifier)
def get_time(self):
if self.start_time is not None:
return self.start_time
return self.protocoltype.usual_time
def get_datetime(self):
return datetime(self.date.year, self.date.month, self.date.day, self.protocoltype.usual_time.hour, self.protocoltype.usual_time.minute)
time = self.get_time()
return datetime(self.date.year, self.date.month, self.date.day, time.usual_time.hour, time.usual_time.minute)
def has_nonplanned_tops(self):
return len([top for top in self.tops if not top.planned]) > 0
......@@ -670,7 +676,9 @@ class DefaultMeta(DatabaseModel):
protocoltype_id = db.Column(db.Integer, db.ForeignKey("protocoltypes.id"))
key = db.Column(db.String)
name = db.Column(db.String)
value = db.Column(db.String)
internal = db.Column(db.Boolean)
prior = db.Column(db.Boolean, default=False, nullable=False)
def get_parent(self):
return self.protocoltype
......
......@@ -24,7 +24,7 @@ from shared import db, date_filter, datetime_filter, date_filter_long, date_filt
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 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, 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, ProtocolForm, TopForm, LocalTopForm, SearchForm, DecisionSearchForm, ProtocolSearchForm, TodoSearchForm, NewProtocolFileUploadForm, NewTodoForm, TodoForm, TodoMailForm, DefaultMetaForm, MetaForm, MergeTodosForm, DecisionCategoryForm
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
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
......@@ -165,9 +165,13 @@ def index():
key=_protocol_sort_key,
reverse=True
)
protocol = finished_protocols[0] if len(finished_protocols) > 0 else None
show_private = protocol.has_private_view_right(user)
has_public_view_right = protocol.protocoltype.has_public_view_right(user)
protocol = None
show_private = False
has_public_view_right = False
if len(finished_protocols) > 0:
protocol = finished_protocols[0]
show_private = protocol.has_private_view_right(user)
has_public_view_right = protocol.protocoltype.has_public_view_right(user)
todos = None
if check_login():
todos = [
......@@ -480,11 +484,18 @@ def new_protocol():
flash("Dir fehlen die nötigen Zugriffsrechte.", "alert-error")
return redirect(request.args.get("next") or url_for("index"))
protocol = Protocol(protocoltype_id=protocoltype.id)
print(form.start_time.data)
form.populate_obj(protocol)
if form.start_time.data is None:
protocol.start_time = protocoltype.usual_time
db.session.add(protocol)
db.session.commit()
for local_top in protocol.create_localtops():
db.session.add(local_top)
for default_meta in protocoltype.metas:
if default_meta.prior:
meta = Meta(protocol_id=protocol.id, name=default_meta.name, internal=default_meta.internal, value=default_meta.value)
db.session.add(meta)
db.session.commit()
tasks.push_tops_to_calendar(protocol)
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
......@@ -672,12 +683,16 @@ def etherpush_protocol(protocol):
@require_modify_right()
def update_protocol(protocol):
upload_form = KnownProtocolSourceUploadForm()
edit_form = ProtocolForm(obj=protocol)
edit_form = generate_protocol_form(protocol)(obj=protocol)
if edit_form.validate_on_submit():
edit_form.populate_obj(protocol)
for meta in protocol.metas:
meta.value = getattr(edit_form.metas, meta.name).data
db.session.commit()
tasks.push_tops_to_calendar(protocol)
return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
for meta in protocol.metas:
getattr(edit_form.metas, meta.name).data = meta.value
return render_template("protocol-update.html", upload_form=upload_form, edit_form=edit_form, protocol=protocol)
@app.route("/protocol/publish/<int:protocol_id>")
......@@ -1351,7 +1366,7 @@ def check_and_send_reminders():
print("regular action for reminders")
for protocol in Protocol.query.filter(Protocol.done != True).all():
day_difference = (protocol.date - current_day).days
usual_time = protocol.protocoltype.usual_time
usual_time = protocol.get_time()
protocol_time = datetime(1, 1, 1, usual_time.hour, usual_time.minute)
hour_difference = (protocol_time - current_time).seconds // 3600
print("diff: {} days, {} hours".format(day_difference, hour_difference))
......
......@@ -71,6 +71,22 @@ to not render a label for the CRSFTokenField -->
{% endfor %}
{%- endmacro %}
{% macro render_form_inner(field, labels_visible) -%}
{% if field.type == 'BooleanField' %}
{{ render_checkbox_field(field) }}
{% elif field.type == 'RadioField' %}
{{ render_radio_field(field) }}
{% elif field.type == 'TextAreaField' %}
{{ render_field(field, label_visible=labels_visible, **kwargs) }}
{% elif field.type == 'FormField' %}
{% for f in field %}
{{render_form_inner(f, labels_visible=labels_visible)}}
{% endfor %}
{% else %}
{{ render_field(field, label_visible=labels_visible) }}
{% endif %}
{%- endmacro %}
{# Renders WTForm in bootstrap way. There are two ways to call function:
- as macros: it will render all field forms using cycle to iterate over them
- as call: it will insert form fields as you specify:
......@@ -95,15 +111,7 @@ to not render a label for the CRSFTokenField -->
{{ caller() }}
{% else %}
{% for f in form %}
{% if f.type == 'BooleanField' %}
{{ render_checkbox_field(f) }}
{% elif f.type == 'RadioField' %}
{{ render_radio_field(f) }}
{% elif f.type == 'TextAreaField' %}
{{ render_field(f, label_visible=labels_visible, rows=textarea_rows) }}
{% else %}
{{ render_field(f, label_visible=labels_visible) }}
{% endif %}
{{render_form_inner(f, labels_visible=labels_visible, textarea_rows=textarea_rows, **kwargs)}}
{% endfor %}
{% endif %}
<button type="submit" class="{{btn_class}}">{{action_text}}</button>
......
......@@ -79,9 +79,14 @@
<p><strong>{{meta.name}}:</strong> {{meta.value}}</p>
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% else %}
{% if protocol.date is not none %}<p><strong>Geplant:</strong> {{protocol.date|datify_long}}{% endif %}</p>
{% if protocol.date is not none %}
<p><strong>Geplant:</strong> {{protocol.date|datify_long}}, {{protocol.start_time|timify}}</p>
{% endif %}
{% for meta in protocol.metas %}
<p><strong>{{meta.name}}:</strong> {{meta.value}}</p>
{% endfor %}
{% endif %}
<h3>Tagesordnung{% if has_modify_right and not protocol.has_nonplanned_tops() %} <a href="{{url_for("new_top", protocol_id=protocol.id)}}">Top hinzufügen</a>{% endif %}</h3>
......
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.get_time()|timify}} statt.
{% for meta in protocol.metas %}
{{meta.name}}: {{meta.value}}
{% endfor %}
Die vorläufige Tagesordnung ist:
{% for top in protocol.get_tops() %}
......
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField, DateTimeField, TextAreaField, Field, widgets
from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField, DateTimeField, TextAreaField, Field, widgets, FormField
from wtforms.validators import InputRequired, Optional
import ipaddress
......@@ -146,6 +146,7 @@ class MeetingReminderForm(FlaskForm):
class NewProtocolForm(FlaskForm):
protocoltype_id = SelectField("Typ", choices=[], coerce=int)
date = DateField("Datum (dd.mm.yyyy)", validators=[InputRequired("Du musst ein Datum angeben.")], format="%d.%m.%Y")
start_time = DateTimeField("Uhrzeit (HH:MM, optional)", validators=[Optional()], format="%H:%M")
def __init__(self, protocoltypes, **kwargs):
super().__init__(**kwargs)
......@@ -175,15 +176,20 @@ class NewProtocolFileUploadForm(FlaskForm):
super().__init__(**kwargs)
self.protocoltype_id.choices = get_protocoltype_choices(protocoltypes, add_all=False)
class ProtocolForm(FlaskForm):
date = DateField("Datum (dd.mm.yyyy)", validators=[InputRequired("Bitte gib das Datum des Protkolls an.")], format="%d.%m.%Y")
start_time = DateTimeField("Beginn (%H:%M)", format="%H:%M", validators=[Optional()])
end_time = DateTimeField("Ende (%H:%M)", format="%H:%M", validators=[Optional()])
location = StringField("Ort")
author = StringField("Protokoll")
participants = StringField("Anwesende")
done = BooleanField("Fertig")
public = BooleanField("Veröffentlicht")
def generate_protocol_form(protocol):
class ProtocolMetasForm(FlaskForm):
pass
for meta in protocol.metas:
setattr(ProtocolMetasForm, meta.name, StringField(meta.name))
class ProtocolForm(FlaskForm):
date = DateField("Datum (dd.mm.yyyy)", validators=[InputRequired("Bitte gib das Datum des Protkolls an.")], format="%d.%m.%Y")
start_time = DateTimeField("Beginn (%H:%M)", format="%H:%M", validators=[Optional()])
end_time = DateTimeField("Ende (%H:%M)", format="%H:%M", validators=[Optional()])
metas = FormField(ProtocolMetasForm)
done = BooleanField("Fertig")
public = BooleanField("Veröffentlicht")
return ProtocolForm
class TopForm(FlaskForm):
name = StringField("TOP", validators=[InputRequired("Du musst den Namen des TOPs angeben.")])
......@@ -248,7 +254,9 @@ class MetaForm(FlaskForm):
class DefaultMetaForm(FlaskForm):
key = StringField("Key", validators=[InputRequired("Bitte gib den Protokoll-Syntax-Schlüssel der Metadaten an.")])
name = StringField("Name", validators=[InputRequired("Bitte gib den Namen der Metadaten an.")])
value = StringField("Standardwert")
internal = BooleanField("Intern")
prior = BooleanField("Planungsrelevant")
class DecisionCategoryForm(FlaskForm):
name = StringField("Name", validators=[InputRequired("Bitte gib den Namen der Kategorie an.")])
......
......@@ -479,14 +479,16 @@ class DefaultMetasTable(Table):
)
def headers(self):
return ["Name", "Key", "Intern", ""]
return ["Name", "Key", "Standardwert", "Intern", "Vorher", ""]
def row(self, meta):
user = current_user()
general_part = [
meta.name,
meta.key,
Table.bool(meta.internal)
meta.value,
Table.bool(meta.internal),
Table.bool(meta.prior)
]
links = [
Table.link(url_for("edit_defaultmeta", defaultmeta_id=meta.id), "Ändern"),
......
Markdown is supported
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