From cef811fc607ca5c9ee996061e0fb14eac5ffef40 Mon Sep 17 00:00:00 2001
From: Robin Sonnabend <robin@fsmpi.rwth-aachen.de>
Date: Sat, 29 Oct 2022 19:39:53 +0200
Subject: [PATCH] Add default protocol recurrence
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

If a meeting has a regular interval (e.g. weekly or biweekly), setting
the protocoltype recurrence interval simplifies generating regular
protocols.
If recurrence is set and no [sitzung;…]-tag is given in the protocol, a
new protocol will be created on date+recurrence days on protocol upload,
provided this date is still in the future.
---
 migrations/versions/da846db78ff9_.py | 28 ++++++++++++++++++++++++++++
 models/database.py                   |  1 +
 tasks.py                             |  7 ++++++-
 views/forms.py                       |  1 +
 views/tables.py                      | 10 ++++++----
 5 files changed, 42 insertions(+), 5 deletions(-)
 create mode 100644 migrations/versions/da846db78ff9_.py

diff --git a/migrations/versions/da846db78ff9_.py b/migrations/versions/da846db78ff9_.py
new file mode 100644
index 0000000..a569337
--- /dev/null
+++ b/migrations/versions/da846db78ff9_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: da846db78ff9
+Revises: 7834767242e8
+Create Date: 2022-10-29 19:24:42.934124
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'da846db78ff9'
+down_revision = '7834767242e8'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('protocoltypes', sa.Column('recurrence', sa.Integer(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('protocoltypes', 'recurrence')
+    # ### end Alembic commands ###
diff --git a/models/database.py b/models/database.py
index a7c5ebf..6d98e43 100644
--- a/models/database.py
+++ b/models/database.py
@@ -78,6 +78,7 @@ class ProtocolType(DatabaseModel):
     restrict_networks = db.Column(db.Boolean)
     allowed_networks = db.Column(db.Text)
     latex_template = db.Column(db.Text)
+    recurrence = db.Column(db.Integer)
 
     protocols = relationship(
         "Protocol", backref=backref("protocoltype"),
diff --git a/tasks.py b/tasks.py
index 5976b98..d894769 100644
--- a/tasks.py
+++ b/tasks.py
@@ -4,7 +4,7 @@ import os
 import subprocess
 import shutil
 import tempfile
-from datetime import datetime
+from datetime import datetime, timedelta
 import time
 import traceback
 from copy import copy
@@ -500,6 +500,11 @@ def parse_protocol_async_inner(protocol, ignore_old_date=False):
         if not protocol.protocoltype.get_protocols_on_date(new_protocol_date):
             Protocol.create_new_protocol(
                 protocol.protocoltype, new_protocol_date, new_protocol_time)
+    if not protocol_tags and protocol.protocoltype.recurrence:
+        new_protocol_date = protocol.date + timedelta(
+            days=protocol.protocoltype.recurrence)
+        if new_protocol_date > datetime.now().date():
+            Protocol.create_new_protocol(protocol.protocoltype, new_protocol_date)
 
     # TOPs
     old_tops = list(protocol.tops)
diff --git a/views/forms.py b/views/forms.py
index b5b85b2..5517633 100644
--- a/views/forms.py
+++ b/views/forms.py
@@ -174,6 +174,7 @@ class ProtocolTypeForm(FlaskForm):
     wiki_only_public = BooleanField("Wiki ist öffentlich")
     printer = SelectField("Drucker", choices=[])
     calendar = SelectField("Kalender", choices=[])
+    recurrence = IntegerField("Turnus (in Tagen)", validators=[Optional()])
     restrict_networks = BooleanField("Netzwerke einschränken")
     allowed_networks = IPNetworkField("Erlaubte Netzwerke")
     latex_template = SelectField("LaTeX Vorlage", choices=[])
diff --git a/views/tables.py b/views/tables.py
index 1009957..9db9c09 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -237,6 +237,7 @@ class ProtocolTypeTable(SingleValueTable):
         calendar_headers = ["Kalender"]
         if not config.CALENDAR_ACTIVE:
             calendar_headers = []
+        recurrence_headers = ["Turnus"]
         network_headers = ["Netzwerke einschränken", "Erlaubte Netzwerke"]
         action_headers = ["Aktion"]
         feed_headers = []
@@ -249,8 +250,8 @@ class ProtocolTypeTable(SingleValueTable):
         return (
             general_headers + etherpad_headers + mail_headers
             + printing_headers + wiki_headers + calendar_headers
-            + network_headers + latex_template_headers + feed_headers
-            + action_headers)
+            + recurrence_headers + network_headers + latex_template_headers
+            + feed_headers + action_headers)
 
     def row(self):
         user = current_user()
@@ -297,6 +298,7 @@ class ProtocolTypeTable(SingleValueTable):
             if self.value.calendar is not None else ""]
         if not config.CALENDAR_ACTIVE:
             calendar_part = []
+        recurrence_part = [f"{self.value.recurrence} Tage" if self.value.recurrence is not None else ""]
         network_part = [Table.bool(self.value.restrict_networks)]
         if self.value.allowed_networks is not None:
             network_part.append(
@@ -344,8 +346,8 @@ class ProtocolTypeTable(SingleValueTable):
             action_part = [""]
         return (
             general_part + etherpad_part + mail_part + printing_part
-            + wiki_part + calendar_part + network_part + latex_template_part
-            + feed_part + action_part)
+            + wiki_part + calendar_part + recurrence_part + network_part
+            + latex_template_part + feed_part + action_part)
 
 
 class DefaultTOPsTable(Table):
-- 
GitLab