mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
A lot of database changes and add options to change notification level
This commit is contained in:
parent
76e7d65017
commit
565274fce1
@ -17,10 +17,13 @@ def before_request():
|
|||||||
Checks if a user is unconfirmed when visiting specific sites. Redirects to
|
Checks if a user is unconfirmed when visiting specific sites. Redirects to
|
||||||
unconfirmed view if user is unconfirmed.
|
unconfirmed view if user is unconfirmed.
|
||||||
"""
|
"""
|
||||||
if (current_user.is_authenticated and not current_user.confirmed
|
if current_user.is_authenticated:
|
||||||
and request.blueprint != 'auth'
|
current_user.ping()
|
||||||
and request.endpoint != 'static'):
|
if not current_user.confirmed \
|
||||||
return redirect(url_for('auth.unconfirmed'))
|
and request.endpoint \
|
||||||
|
and request.blueprint != 'auth' \
|
||||||
|
and request.endpoint != 'static':
|
||||||
|
return redirect(url_for('auth.unconfirmed'))
|
||||||
|
|
||||||
|
|
||||||
@auth.route('/login', methods=['GET', 'POST'])
|
@auth.route('/login', methods=['GET', 'POST'])
|
||||||
|
@ -106,13 +106,18 @@ class User(UserMixin, db.Model):
|
|||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
# Fields
|
# Fields
|
||||||
confirmed = db.Column(db.Boolean, default=False)
|
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)
|
email = db.Column(db.String(254), unique=True, index=True)
|
||||||
password_hash = db.Column(db.String(128))
|
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'))
|
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
|
||||||
username = db.Column(db.String(64), unique=True, index=True)
|
username = db.Column(db.String(64), unique=True, index=True)
|
||||||
# Setting Fields
|
# Setting Fields
|
||||||
setting_dark_mode = db.Column(db.Boolean, default=False)
|
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
|
# Relationships
|
||||||
corpora = db.relationship('Corpus', backref='creator', lazy='dynamic',
|
corpora = db.relationship('Corpus', backref='creator', lazy='dynamic',
|
||||||
cascade='save-update, merge, delete')
|
cascade='save-update, merge, delete')
|
||||||
@ -205,6 +210,10 @@ class User(UserMixin, db.Model):
|
|||||||
"""
|
"""
|
||||||
return self.can(Permission.ADMIN)
|
return self.can(Permission.ADMIN)
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
self.last_seen = datetime.utcnow()
|
||||||
|
db.session.add(self)
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"""
|
"""
|
||||||
Delete the user and its corpora and jobs from database and filesystem.
|
Delete the user and its corpora and jobs from database and filesystem.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (BooleanField, PasswordField, StringField, SubmitField,
|
from wtforms import (BooleanField, PasswordField, SelectField, StringField,
|
||||||
ValidationError)
|
SubmitField, ValidationError)
|
||||||
from wtforms.validators import DataRequired, Email, EqualTo
|
from wtforms.validators import DataRequired, Email, EqualTo
|
||||||
|
|
||||||
|
|
||||||
@ -11,6 +11,20 @@ class EditEmailForm(FlaskForm):
|
|||||||
|
|
||||||
class EditGeneralSettingsForm(FlaskForm):
|
class EditGeneralSettingsForm(FlaskForm):
|
||||||
dark_mode = BooleanField('Dark mode')
|
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')
|
save_settings = SubmitField('Save settings')
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,8 +11,7 @@ from .. import db
|
|||||||
def settings():
|
def settings():
|
||||||
edit_email_form = EditEmailForm(prefix='edit-email-form')
|
edit_email_form = EditEmailForm(prefix='edit-email-form')
|
||||||
edit_general_settings_form = EditGeneralSettingsForm(
|
edit_general_settings_form = EditGeneralSettingsForm(
|
||||||
prefix='edit-general-settings-form'
|
prefix='edit-general-settings-form')
|
||||||
)
|
|
||||||
edit_password_form = EditPasswordForm(prefix='edit-password-form',
|
edit_password_form = EditPasswordForm(prefix='edit-password-form',
|
||||||
user=current_user)
|
user=current_user)
|
||||||
# Check if edit_email_form is submitted and valid
|
# Check if edit_email_form is submitted and valid
|
||||||
@ -25,7 +24,12 @@ def settings():
|
|||||||
# Check if edit_settings_form is submitted and valid
|
# Check if edit_settings_form is submitted and valid
|
||||||
if (edit_general_settings_form.save_settings.data
|
if (edit_general_settings_form.save_settings.data
|
||||||
and edit_general_settings_form.validate_on_submit()):
|
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.add(current_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('Your settings have been updated.')
|
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
|
# If no form is submitted or valid, fill out fields with current values
|
||||||
edit_email_form.email.data = current_user.email
|
edit_email_form.email.data = current_user.email
|
||||||
edit_general_settings_form.dark_mode.data = current_user.setting_dark_mode
|
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(
|
return render_template(
|
||||||
'profile/settings.html.j2',
|
'profile/settings.html.j2',
|
||||||
edit_email_form=edit_email_form,
|
edit_email_form=edit_email_form,
|
||||||
|
@ -43,13 +43,6 @@ nopaque.socket.init = function() {
|
|||||||
var patch;
|
var patch;
|
||||||
|
|
||||||
patch = JSON.parse(msg);
|
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(`<i class="left material-icons">book</i>[<a href="/jobs/${pathArray[0]}">${nopaque.corpora[pathArray[0]].title}</a>] New status: ${operation.value}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nopaque.corpora = jsonpatch.apply_patch(nopaque.corpora, patch);
|
nopaque.corpora = jsonpatch.apply_patch(nopaque.corpora, patch);
|
||||||
for (let subscriber of nopaque.corporaSubscribers) {subscriber._update(patch);}
|
for (let subscriber of nopaque.corporaSubscribers) {subscriber._update(patch);}
|
||||||
});
|
});
|
||||||
@ -58,14 +51,17 @@ nopaque.socket.init = function() {
|
|||||||
var patch;
|
var patch;
|
||||||
|
|
||||||
patch = JSON.parse(msg);
|
patch = JSON.parse(msg);
|
||||||
for (operation of patch) {
|
nopaque.jobs = jsonpatch.apply_patch(nopaque.jobs, patch);
|
||||||
/* "/jobId/valueName" -> ["jobId", "valueName"] */
|
if (["all", "end"].includes(nopaque.user.settings.jobStatusSiteNotifications)) {
|
||||||
pathArray = operation.path.split("/").slice(1);
|
for (operation of patch) {
|
||||||
if (operation.op === "replace" && pathArray[1] === "status") {
|
/* "/jobId/valueName" -> ["jobId", "valueName"] */
|
||||||
nopaque.flash(`<i class="left material-icons">work</i>[<a href="/jobs/${pathArray[0]}">${nopaque.jobs[pathArray[0]].title}</a>] New status: ${operation.value}`);
|
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(`<i class="left material-icons">work</i>[<a href="/jobs/${pathArray[0]}">${nopaque.jobs[pathArray[0]].title}</a>] New status: ${operation.value}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nopaque.jobs = jsonpatch.apply_patch(nopaque.jobs, patch);
|
|
||||||
for (let subscriber of nopaque.jobsSubscribers) {subscriber._update(patch);}
|
for (let subscriber of nopaque.jobsSubscribers) {subscriber._update(patch);}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
<li>Username: {{ user.username }}</li>
|
<li>Username: {{ user.username }}</li>
|
||||||
<li>Email: {{ user.email }}</li>
|
<li>Email: {{ user.email }}</li>
|
||||||
<li>ID: {{ user.id }}</li>
|
<li>ID: {{ user.id }}</li>
|
||||||
<li>Registration date: {{ user.registration_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
<li>Member sinse: {{ user.member_since.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||||
<li>Confirmed status: {{ user.confirmed }}</li>
|
<li>Confirmed status: {{ user.confirmed }}</li>
|
||||||
|
<li>Last seen: {{ user.last_seen.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||||
<li>Role ID: {{ user.role_id }}</li>
|
<li>Role ID: {{ user.role_id }}</li>
|
||||||
<li>Permissions as Int: {{ user.role.permissions }}</li>
|
<li>Permissions as Int: {{ user.role.permissions }}</li>
|
||||||
<li>Role name: {{ user.role.name }}</li>
|
<li>Role name: {{ user.role.name }}</li>
|
||||||
|
@ -175,7 +175,6 @@
|
|||||||
|
|
||||||
for (let operation of patch) {
|
for (let operation of patch) {
|
||||||
/* "/jobId/valueName" -> ["jobId", "valueName"] */
|
/* "/jobId/valueName" -> ["jobId", "valueName"] */
|
||||||
console.log(operation.value);
|
|
||||||
pathArray = operation.path.split("/").slice(1);
|
pathArray = operation.path.split("/").slice(1);
|
||||||
if (pathArray[0] != this.jobId) {continue;}
|
if (pathArray[0] != this.jobId) {continue;}
|
||||||
switch(operation.op) {
|
switch(operation.op) {
|
||||||
|
@ -29,12 +29,15 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_boolean_field(field) %}
|
{% macro render_boolean_field(field) %}
|
||||||
|
{% set label = kwargs.pop('label', True) %}
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
{% if 'material_icon' in kwargs %}
|
{% if 'material_icon' in kwargs %}
|
||||||
<i class="material-icons prefix">{{ kwargs.pop('material_icon') }}</i>
|
<i class="material-icons prefix">{{ kwargs.pop('material_icon') }}</i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<label>
|
<label>
|
||||||
|
{% if label %}
|
||||||
{{ field.label.text }}
|
{{ field.label.text }}
|
||||||
|
{% endif %}
|
||||||
{{ field(*args, **kwargs) }}
|
{{ field(*args, **kwargs) }}
|
||||||
<span class="lever"></span>
|
<span class="lever"></span>
|
||||||
</label>
|
</label>
|
||||||
@ -44,12 +47,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_decimal_range_field(field) %}
|
|
||||||
<p class="range-field">
|
|
||||||
{{ field(*args, **kwargs) }}
|
|
||||||
</p>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro render_file_field(field) %}
|
{% macro render_file_field(field) %}
|
||||||
{% set placeholder = kwargs.pop('placeholder', '') %}
|
{% set placeholder = kwargs.pop('placeholder', '') %}
|
||||||
<div class="file-field input-field">
|
<div class="file-field input-field">
|
||||||
@ -64,12 +61,15 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_generic_field(field) %}
|
{% macro render_generic_field(field) %}
|
||||||
|
{% set label = kwargs.pop('label', True) %}
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
{% if 'material_icon' in kwargs %}
|
{% if 'material_icon' in kwargs %}
|
||||||
<i class="material-icons prefix">{{ kwargs.pop('material_icon') }}</i>
|
<i class="material-icons prefix">{{ kwargs.pop('material_icon') }}</i>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ field(*args, **kwargs) }}
|
{{ field(*args, **kwargs) }}
|
||||||
|
{% if label %}
|
||||||
{{ field.label }}
|
{{ field.label }}
|
||||||
|
{% endif %}
|
||||||
{% for error in field.errors %}
|
{% for error in field.errors %}
|
||||||
<span class="helper-text red-text">{{ error }}</span>
|
<span class="helper-text red-text">{{ error }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -49,9 +49,15 @@
|
|||||||
<script src="{{ url_for('static', filename='js/nopaque.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/nopaque.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/nopaque.lists.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/nopaque.lists.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
nopaque.user.isAuthenticated = {{ current_user.is_authenticated|tojson }};
|
{% if current_user.is_authenticated %}
|
||||||
nopaque.user.settings.darkMode = {{ (current_user.is_authenticated and current_user.setting_dark_mode)|tojson }};
|
nopaque.user.isAuthenticated = true;
|
||||||
nopaque.flashedMessages = {{ get_flashed_messages(with_categories=true)|tojson }};
|
nopaque.user.settings.darkMode = {{ current_user.setting_dark_mode|tojson }};
|
||||||
|
nopaque.user.settings.jobStatusMailNotifications = {{ current_user.setting_job_status_mail_notifications|tojson }};
|
||||||
|
nopaque.user.settings.jobStatusSiteNotifications = {{ current_user.setting_job_status_site_notifications|tojson }};
|
||||||
|
{% else %}
|
||||||
|
nopaque.user.isAuthenticated = false;
|
||||||
|
{% endif %}
|
||||||
|
nopaque.flashedMessages = {{ get_flashed_messages(with_categories=True)|tojson }};
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -27,16 +27,21 @@
|
|||||||
<div class="col s12 divider"></div>
|
<div class="col s12 divider"></div>
|
||||||
<div class="col s12"><p> </p></div>
|
<div class="col s12"><p> </p></div>
|
||||||
<div class="col s9">
|
<div class="col s9">
|
||||||
<p><i class="material-icons left">notifications</i>Email notifications</p>
|
<p><i class="material-icons left">notifications</i>Job status site notifications</p>
|
||||||
<p class="light">Receive emails when a job completes.</p>
|
<p class="light">Receive site notifications about job status changes.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s3 right-align">
|
<div class="col s3 right-align">
|
||||||
<div class="switch">
|
{{ M.render_field(edit_general_settings_form.job_status_site_notifications, label=False) }}
|
||||||
<label>
|
</div>
|
||||||
<input disabled type="checkbox">
|
<div class="col s12"><p> </p></div>
|
||||||
<span class="lever"></span>
|
<div class="col s12 divider"></div>
|
||||||
</label>
|
<div class="col s12"><p> </p></div>
|
||||||
</div>
|
<div class="col s9">
|
||||||
|
<p><i class="material-icons left">notifications</i>Job status mail notifications</p>
|
||||||
|
<p class="light">Receive mail notifications about job status changes.</p>
|
||||||
|
</div>
|
||||||
|
<div class="col s3 right-align">
|
||||||
|
{{ M.render_field(edit_general_settings_form.job_status_mail_notifications, label=False) }}
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
Seperate each setting with the following two elements
|
Seperate each setting with the following two elements
|
||||||
|
30
migrations/versions/099037c4aa06_.py
Normal file
30
migrations/versions/099037c4aa06_.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 099037c4aa06
|
||||||
|
Revises: 66253783654f
|
||||||
|
Create Date: 2020-04-27 09:17:15.039728
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '099037c4aa06'
|
||||||
|
down_revision = '66253783654f'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('users', sa.Column('last_seen', sa.DateTime(), nullable=True))
|
||||||
|
op.add_column('users', sa.Column('setting_site_job_status_notifications', sa.String(length=16), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('users', 'setting_site_job_status_notifications')
|
||||||
|
op.drop_column('users', 'last_seen')
|
||||||
|
# ### end Alembic commands ###
|
36
migrations/versions/49a42c69e523_.py
Normal file
36
migrations/versions/49a42c69e523_.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 49a42c69e523
|
||||||
|
Revises: 099037c4aa06
|
||||||
|
Create Date: 2020-04-27 11:18:32.999099
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '49a42c69e523'
|
||||||
|
down_revision = '099037c4aa06'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('users', sa.Column('member_since', sa.DateTime(), nullable=True))
|
||||||
|
op.add_column('users', sa.Column('setting_job_status_mail_notifications', sa.String(length=16), nullable=True))
|
||||||
|
op.add_column('users', sa.Column('setting_job_status_site_notifications', sa.String(length=16), nullable=True))
|
||||||
|
op.drop_column('users', 'setting_site_job_status_notifications')
|
||||||
|
op.drop_column('users', 'registration_date')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('users', sa.Column('registration_date', postgresql.TIMESTAMP(), autoincrement=False, nullable=True))
|
||||||
|
op.add_column('users', sa.Column('setting_site_job_status_notifications', sa.VARCHAR(length=16), autoincrement=False, nullable=True))
|
||||||
|
op.drop_column('users', 'setting_job_status_site_notifications')
|
||||||
|
op.drop_column('users', 'setting_job_status_mail_notifications')
|
||||||
|
op.drop_column('users', 'member_since')
|
||||||
|
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user