From 565274fce1691e8788f140017eb03a1664c09f49 Mon Sep 17 00:00:00 2001 From: Patrick Jentsch Date: Mon, 27 Apr 2020 13:50:54 +0200 Subject: [PATCH 1/4] A lot of database changes and add options to change notification level --- app/auth/views.py | 11 +++++--- app/models.py | 11 +++++++- app/profile/forms.py | 18 ++++++++++-- app/profile/views.py | 14 +++++++-- app/static/js/nopaque.js | 22 ++++++--------- app/templates/admin/user.html.j2 | 3 +- app/templates/jobs/job.html.j2 | 1 - app/templates/macros/materialize.html.j2 | 12 ++++---- app/templates/nopaque.html.j2 | 12 ++++++-- app/templates/profile/settings.html.j2 | 21 ++++++++------ migrations/versions/099037c4aa06_.py | 30 ++++++++++++++++++++ migrations/versions/49a42c69e523_.py | 36 ++++++++++++++++++++++++ 12 files changed, 149 insertions(+), 42 deletions(-) create mode 100644 migrations/versions/099037c4aa06_.py create mode 100644 migrations/versions/49a42c69e523_.py diff --git a/app/auth/views.py b/app/auth/views.py index 9470480f..6b87f744 100644 --- a/app/auth/views.py +++ b/app/auth/views.py @@ -17,10 +17,13 @@ def before_request(): Checks if a user is unconfirmed when visiting specific sites. Redirects to unconfirmed view if user is unconfirmed. """ - if (current_user.is_authenticated and not current_user.confirmed - and request.blueprint != 'auth' - and request.endpoint != 'static'): - return redirect(url_for('auth.unconfirmed')) + if current_user.is_authenticated: + current_user.ping() + if not current_user.confirmed \ + and request.endpoint \ + and request.blueprint != 'auth' \ + and request.endpoint != 'static': + return redirect(url_for('auth.unconfirmed')) @auth.route('/login', methods=['GET', 'POST']) diff --git a/app/models.py b/app/models.py index e5c29953..7866ef3c 100644 --- a/app/models.py +++ b/app/models.py @@ -106,13 +106,18 @@ class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) # Fields confirmed = db.Column(db.Boolean, default=False) + last_seen = db.Column(db.DateTime(), default=datetime.utcnow) email = db.Column(db.String(254), unique=True, index=True) password_hash = db.Column(db.String(128)) - registration_date = db.Column(db.DateTime(), default=datetime.utcnow) + member_since = db.Column(db.DateTime(), default=datetime.utcnow) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) username = db.Column(db.String(64), unique=True, index=True) # Setting Fields setting_dark_mode = db.Column(db.Boolean, default=False) + setting_job_status_mail_notifications = db.Column(db.String(16), + default='end') + setting_job_status_site_notifications = db.Column(db.String(16), + default='all') # Relationships corpora = db.relationship('Corpus', backref='creator', lazy='dynamic', cascade='save-update, merge, delete') @@ -205,6 +210,10 @@ class User(UserMixin, db.Model): """ return self.can(Permission.ADMIN) + def ping(self): + self.last_seen = datetime.utcnow() + db.session.add(self) + def delete(self): """ Delete the user and its corpora and jobs from database and filesystem. diff --git a/app/profile/forms.py b/app/profile/forms.py index 60257805..14517e29 100644 --- a/app/profile/forms.py +++ b/app/profile/forms.py @@ -1,6 +1,6 @@ from flask_wtf import FlaskForm -from wtforms import (BooleanField, PasswordField, StringField, SubmitField, - ValidationError) +from wtforms import (BooleanField, PasswordField, SelectField, StringField, + SubmitField, ValidationError) from wtforms.validators import DataRequired, Email, EqualTo @@ -11,6 +11,20 @@ class EditEmailForm(FlaskForm): class EditGeneralSettingsForm(FlaskForm): dark_mode = BooleanField('Dark mode') + job_status_mail_notifications = SelectField( + 'Job status mail notifications', + choices=[('', 'Choose your option'), + ('all', 'Notify on all status changes'), + ('end', 'Notify only when a job ended'), + ('none', 'No status update notifications')], + validators=[DataRequired()]) + job_status_site_notifications = SelectField( + 'Job status site notifications', + choices=[('', 'Choose your option'), + ('all', 'Notify on all status changes'), + ('end', 'Notify only when a job ended'), + ('none', 'No status update notifications')], + validators=[DataRequired()]) save_settings = SubmitField('Save settings') diff --git a/app/profile/views.py b/app/profile/views.py index 89d127df..f156715f 100644 --- a/app/profile/views.py +++ b/app/profile/views.py @@ -11,8 +11,7 @@ from .. import db def settings(): edit_email_form = EditEmailForm(prefix='edit-email-form') edit_general_settings_form = EditGeneralSettingsForm( - prefix='edit-general-settings-form' - ) + prefix='edit-general-settings-form') edit_password_form = EditPasswordForm(prefix='edit-password-form', user=current_user) # Check if edit_email_form is submitted and valid @@ -25,7 +24,12 @@ def settings(): # Check if edit_settings_form is submitted and valid if (edit_general_settings_form.save_settings.data and edit_general_settings_form.validate_on_submit()): - current_user.setting_dark_mode = edit_general_settings_form.dark_mode.data + current_user.setting_dark_mode = \ + edit_general_settings_form.dark_mode.data + current_user.setting_job_status_mail_notifications = \ + edit_general_settings_form.job_status_mail_notifications.data + current_user.setting_job_status_site_notifications = \ + edit_general_settings_form.job_status_site_notifications.data db.session.add(current_user) db.session.commit() flash('Your settings have been updated.') @@ -41,6 +45,10 @@ def settings(): # If no form is submitted or valid, fill out fields with current values edit_email_form.email.data = current_user.email edit_general_settings_form.dark_mode.data = current_user.setting_dark_mode + edit_general_settings_form.job_status_site_notifications.data = \ + current_user.setting_job_status_site_notifications + edit_general_settings_form.job_status_mail_notifications.data = \ + current_user.setting_job_status_mail_notifications return render_template( 'profile/settings.html.j2', edit_email_form=edit_email_form, diff --git a/app/static/js/nopaque.js b/app/static/js/nopaque.js index 2cba2c1b..d2c6ddbf 100644 --- a/app/static/js/nopaque.js +++ b/app/static/js/nopaque.js @@ -43,13 +43,6 @@ nopaque.socket.init = function() { var patch; patch = JSON.parse(msg); - for (operation of patch) { - /* "/corpusId/valueName" -> ["corpusId", "valueName"] */ - pathArray = operation.path.split("/").slice(1); - if (operation.op === "replace" && pathArray[1] === "status") { - nopaque.flash(`book[${nopaque.corpora[pathArray[0]].title}] New status: ${operation.value}`); - } - } nopaque.corpora = jsonpatch.apply_patch(nopaque.corpora, patch); for (let subscriber of nopaque.corporaSubscribers) {subscriber._update(patch);} }); @@ -58,14 +51,17 @@ nopaque.socket.init = function() { var patch; patch = JSON.parse(msg); - for (operation of patch) { - /* "/jobId/valueName" -> ["jobId", "valueName"] */ - pathArray = operation.path.split("/").slice(1); - if (operation.op === "replace" && pathArray[1] === "status") { - nopaque.flash(`work[${nopaque.jobs[pathArray[0]].title}] New status: ${operation.value}`); + nopaque.jobs = jsonpatch.apply_patch(nopaque.jobs, patch); + if (["all", "end"].includes(nopaque.user.settings.jobStatusSiteNotifications)) { + for (operation of patch) { + /* "/jobId/valueName" -> ["jobId", "valueName"] */ + pathArray = operation.path.split("/").slice(1); + if (operation.op === "replace" && pathArray[1] === "status") { + if (nopaque.user.settings.jobStatusSiteNotifications === "end" && !["complete", "failed"].includes(operation.value)) {continue;} + nopaque.flash(`work[${nopaque.jobs[pathArray[0]].title}] New status: ${operation.value}`); + } } } - nopaque.jobs = jsonpatch.apply_patch(nopaque.jobs, patch); for (let subscriber of nopaque.jobsSubscribers) {subscriber._update(patch);} }); diff --git a/app/templates/admin/user.html.j2 b/app/templates/admin/user.html.j2 index 15de85da..f6825976 100644 --- a/app/templates/admin/user.html.j2 +++ b/app/templates/admin/user.html.j2 @@ -15,8 +15,9 @@
  • Username: {{ user.username }}
  • Email: {{ user.email }}
  • ID: {{ user.id }}
  • -
  • Registration date: {{ user.registration_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}
  • +
  • Member sinse: {{ user.member_since.strftime('%m/%d/%Y, %H:%M:%S %p') }}
  • Confirmed status: {{ user.confirmed }}
  • +
  • Last seen: {{ user.last_seen.strftime('%m/%d/%Y, %H:%M:%S %p') }}
  • Role ID: {{ user.role_id }}
  • Permissions as Int: {{ user.role.permissions }}
  • Role name: {{ user.role.name }}
  • diff --git a/app/templates/jobs/job.html.j2 b/app/templates/jobs/job.html.j2 index 5b45d19a..5be1ed99 100644 --- a/app/templates/jobs/job.html.j2 +++ b/app/templates/jobs/job.html.j2 @@ -175,7 +175,6 @@ for (let operation of patch) { /* "/jobId/valueName" -> ["jobId", "valueName"] */ - console.log(operation.value); pathArray = operation.path.split("/").slice(1); if (pathArray[0] != this.jobId) {continue;} switch(operation.op) { diff --git a/app/templates/macros/materialize.html.j2 b/app/templates/macros/materialize.html.j2 index 40785de6..bf14c6e7 100644 --- a/app/templates/macros/materialize.html.j2 +++ b/app/templates/macros/materialize.html.j2 @@ -29,12 +29,15 @@ {% endmacro %} {% macro render_boolean_field(field) %} + {% set label = kwargs.pop('label', True) %}
    {% if 'material_icon' in kwargs %} {{ kwargs.pop('material_icon') }} {% endif %} @@ -44,12 +47,6 @@
    {% endmacro %} -{% macro render_decimal_range_field(field) %} -

    - {{ field(*args, **kwargs) }} -

    -{% endmacro %} - {% macro render_file_field(field) %} {% set placeholder = kwargs.pop('placeholder', '') %}
    @@ -64,12 +61,15 @@ {% endmacro %} {% macro render_generic_field(field) %} + {% set label = kwargs.pop('label', True) %}
    {% if 'material_icon' in kwargs %} {{ kwargs.pop('material_icon') }} {% endif %} {{ field(*args, **kwargs) }} + {% if label %} {{ field.label }} + {% endif %} {% for error in field.errors %} {{ error }} {% endfor %} diff --git a/app/templates/nopaque.html.j2 b/app/templates/nopaque.html.j2 index 8c305ad6..f608ac18 100644 --- a/app/templates/nopaque.html.j2 +++ b/app/templates/nopaque.html.j2 @@ -49,9 +49,15 @@ diff --git a/app/templates/profile/settings.html.j2 b/app/templates/profile/settings.html.j2 index 3ab20a0d..b5897ebb 100644 --- a/app/templates/profile/settings.html.j2 +++ b/app/templates/profile/settings.html.j2 @@ -27,16 +27,21 @@

     

    -

    notificationsEmail notifications

    -

    Receive emails when a job completes.

    +

    notificationsJob status site notifications

    +

    Receive site notifications about job status changes.

    -
    - -
    + {{ M.render_field(edit_general_settings_form.job_status_site_notifications, label=False) }} +
    +

     

    +
    +

     

    +
    +

    notificationsJob status mail notifications

    +

    Receive mail notifications about job status changes.

    +
    +
    + {{ M.render_field(edit_general_settings_form.job_status_mail_notifications, label=False) }}