diff --git a/.flaskenv b/.flaskenv index 557b84410e90c35e4130f92151117f37dbf8678a..0544671576aca4281abc3bba9a35eaacc3e4330f 100644 --- a/.flaskenv +++ b/.flaskenv @@ -1,2 +1,2 @@ FLASK_APP=server.py:app -FLASK_ENV=development +FLASK_DEBUG=True diff --git a/migrations/versions/da846db78ff9_.py b/migrations/versions/da846db78ff9_.py new file mode 100644 index 0000000000000000000000000000000000000000..a569337c48e509669c15edb7f012d44008f4cd4d --- /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 a7c5ebf7e249c382ad01ed00555d25d896d7e893..6d98e438eb843b9d6e789706878938375b6e32d4 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/requirements.txt b/requirements.txt index 8979d5ca0f479ee0270fdcde1a7092db44656b40..57271353d070318274e6d64ff41ea36b5012be17 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,91 +1,99 @@ -alembic==1.6.5 -amqp==5.0.6 +alembic==1.8.1 +amqp==5.1.1 appdirs==1.4.4 -APScheduler==3.7.0 +APScheduler==3.9.1 argh==0.26.2 -bandit==1.7.0 +async-timeout==4.0.2 +bandit==1.7.4 billiard==3.6.4.0 +blessed==1.19.1 blessings==1.7 -blinker==1.4 -bpython==0.21 -caldav==0.8.0 -celery==5.1.2 -certifi==2021.5.30 -chardet==4.0.0 -charset-normalizer==2.0.3 -click==7.1.2 -click-didyoumean==0.0.3 +blinker==1.5 +bpython==0.23 +caldav==0.10.0 +celery==5.2.7 +certifi==2022.9.24 +chardet==5.0.0 +charset-normalizer==2.1.1 +click==8.1.3 +click-didyoumean==0.3.0 click-plugins==1.1.1 click-repl==0.2.0 -colorama==0.4.4 -coverage==5.5 -curtsies==0.3.5 -cwcwidth==0.1.4 -dnspython==1.16.0 +colorama==0.4.6 +coverage==6.5.0 +curtsies==0.4.1 +cwcwidth==0.1.8 +Deprecated==1.2.13 +dnspython==2.2.1 enum-compat==0.0.3 etherpad-lite==0.5 -eventlet==0.31.1 +eventlet==0.33.1 feedgen==0.9.0 -Flask==2.0.1 -Flask-Migrate==3.0.1 -Flask-SQLAlchemy==2.5.1 -Flask-WTF==0.15.1 +Flask==2.2.2 +Flask-Migrate==3.1.0 +Flask-SQLAlchemy==3.0.2 +Flask-WTF==1.0.1 fuzzywuzzy==0.18.0 -gitdb==4.0.7 +gitdb==4.0.9 gitdb2==4.0.2 -GitPython==3.1.18 -greenlet==1.1.0 -icalendar==4.0.7 -idna==3.2 -itsdangerous==2.0.1 -Jinja2==3.0.1 -kombu==5.1.0 +GitPython==3.1.29 +greenlet==1.1.3.post0 +icalendar==5.0.1 +idna==3.4 +itsdangerous==2.1.2 +Jinja2==3.1.2 +kombu==5.2.4 ldap3==2.9.1 -lxml==4.6.3 -Mako==1.1.4 -mando==0.7.0 -MarkupSafe==2.0.1 -mccabe==0.6.1 +Levenshtein==0.20.7 +lxml==4.9.1 +Mako==1.2.3 +mando==0.7.1 +MarkupSafe==2.1.1 +mccabe==0.7.0 monotonic==1.6 nose==1.3.7 -packaging==21.0 +packaging==21.3 pathtools==0.1.2 -pbr==5.6.0 -prompt-toolkit==3.0.19 -psycopg2-binary==2.9.1 +pbr==5.11.0 +prompt-toolkit==3.0.31 +psycopg2-binary==2.9.5 pyasn1==0.4.8 pyasn1-modules==0.2.8 -pycodestyle==2.7.0 -Pygments==2.9.0 +pycodestyle==2.9.1 +Pygments==2.13.0 pyldap==3.0.0.post1 -pyparsing==2.4.7 +pyparsing==3.0.9 python-dateutil==2.8.2 -python-dotenv==0.19.0 +python-dotenv==0.21.0 python-editor==1.0.4 -python-engineio==4.2.0 -python-ldap==3.3.1 -python-Levenshtein==0.12.2 -python-pam==1.8.4 -pytz==2021.1 -pyxdg==0.27 -PyYAML==5.4.1 +python-engineio==4.3.4 +python-ldap==3.4.3 +python-Levenshtein==0.20.7 +python-pam==2.0.2 +pytz==2022.5 +pytz-deprecation-shim==0.1.0.post0 +pyxdg==0.28 +PyYAML==6.0 +rapidfuzz==2.12.0 raven==6.10.0 -redis==3.5.3 -regex==2021.7.6 -requests==2.26.0 +redis==4.3.4 +regex==2022.9.13 +requests==2.28.1 six==1.16.0 -smmap==4.0.0 +smmap==5.0.0 smmap2==3.0.1 -SQLAlchemy==1.4.22 -SQLAlchemy-Utils==0.37.8 -stevedore==3.3.0 +SQLAlchemy==1.4.42 +SQLAlchemy-Utils==0.38.3 +stevedore==4.1.0 typing==3.7.4.3 -tzlocal==2.1 -urllib3==1.26.6 +tzdata==2022.5 +tzlocal==4.2 +urllib3==1.26.12 uwsgidecorators==1.1.0 vine==5.0.0 vobject==0.9.6.1 -watchdog==2.1.3 +watchdog==2.1.9 wcwidth==0.2.5 -Werkzeug==2.0.1 -WTForms==2.3.3 +Werkzeug==2.2.2 +wrapt==1.14.1 +WTForms==3.0.1 diff --git a/tasks.py b/tasks.py index 5976b989bd2dd2dab85dc33e2e8f1d9ead76d407..d894769faa617cef4fe44395e9aaadc48960ec90 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 77308a73c0dd27907139adcd074db434cc7bc1eb..551763351d47a69676f8140da6f7b72b64bc8fa3 100644 --- a/views/forms.py +++ b/views/forms.py @@ -1,8 +1,8 @@ from flask_wtf import FlaskForm from wtforms import ( StringField, PasswordField, BooleanField, IntegerField, SelectField, - FileField, DateTimeField, TextAreaField, Field, FormField, widgets) -from wtforms.fields.html5 import DateField + FileField, DateTimeField, TextAreaField, Field, FormField, DateField, + widgets) from wtforms.validators import InputRequired, Optional import ipaddress @@ -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 1009957ad1dd7eed3367af1a123453385378ada3..9db9c096196a4a44bfa230bc4e05d2e4dcbef47d 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):