mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-25 19:04:18 +00:00
Merge branch 'development' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into development
This commit is contained in:
commit
817c9c376f
@ -1,6 +1,7 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_wtf.file import FileField, FileRequired
|
||||
from wtforms import (
|
||||
BooleanField,
|
||||
StringField,
|
||||
SubmitField,
|
||||
TextAreaField,
|
||||
@ -77,6 +78,9 @@ class UpdateCorpusFileForm(CorpusFileBaseForm):
|
||||
kwargs['prefix'] = 'update-corpus-file-form'
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
class ChangeCorpusSettingsForm(FlaskForm):
|
||||
is_public = BooleanField('Public Corpus')
|
||||
submit = SubmitField()
|
||||
|
||||
class ImportCorpusForm(FlaskForm):
|
||||
pass
|
||||
|
@ -5,15 +5,17 @@ from flask import (
|
||||
Markup,
|
||||
redirect,
|
||||
render_template,
|
||||
send_from_directory
|
||||
request,
|
||||
send_from_directory,
|
||||
url_for
|
||||
)
|
||||
from flask_login import current_user, login_required
|
||||
from threading import Thread
|
||||
import os
|
||||
from app import db
|
||||
from app.models import Corpus, CorpusFile, CorpusStatus
|
||||
from app import db, hashids
|
||||
from app.models import Corpus, CorpusFile, CorpusStatus, CorpusFollowerAssociation, User
|
||||
from . import bp
|
||||
from .forms import CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm
|
||||
from .forms import ChangeCorpusSettingsForm, CreateCorpusFileForm, CreateCorpusForm, UpdateCorpusFileForm
|
||||
|
||||
|
||||
def user_can_read_corpus(user, corpus):
|
||||
@ -64,19 +66,35 @@ def create_corpus():
|
||||
)
|
||||
|
||||
|
||||
@bp.route('/<hashid:corpus_id>')
|
||||
@bp.route('/<hashid:corpus_id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def corpus(corpus_id):
|
||||
corpus = Corpus.query.get_or_404(corpus_id)
|
||||
if not user_can_read_corpus(current_user, corpus):
|
||||
abort(403)
|
||||
corpus_settings_form = ChangeCorpusSettingsForm(
|
||||
data=corpus.to_json_serializeable(),
|
||||
prefix='corpus-settings-form'
|
||||
)
|
||||
if corpus_settings_form.validate_on_submit():
|
||||
corpus.is_public = corpus_settings_form.is_public.data
|
||||
db.session.commit()
|
||||
flash('Your changes have been saved')
|
||||
return redirect(url_for('.corpus', corpus_id=corpus.id))
|
||||
# following_users = [
|
||||
# u.to_json_serializeable() for u
|
||||
# in corpus.following_users
|
||||
# ]
|
||||
return render_template(
|
||||
'corpora/corpus.html.j2',
|
||||
corpus_settings_form=corpus_settings_form,
|
||||
corpus=corpus,
|
||||
# following_users=following_users,
|
||||
title='Corpus'
|
||||
)
|
||||
|
||||
|
||||
|
||||
# @bp.route('/<hashid:corpus_id>/update')
|
||||
# @login_required
|
||||
# def update_corpus(corpus_id):
|
||||
@ -263,3 +281,63 @@ def import_corpus():
|
||||
@login_required
|
||||
def export_corpus(corpus_id):
|
||||
abort(503)
|
||||
|
||||
@bp.route('/<hashid:corpus_id>/follow', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def follow_corpus(corpus_id):
|
||||
corpus = Corpus.query.get_or_404(corpus_id)
|
||||
user_hashid = request.args.get('user_id')
|
||||
if user_hashid is None:
|
||||
user = current_user
|
||||
else:
|
||||
if not current_user.is_administrator():
|
||||
abort(403)
|
||||
else:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
user = User.query.get_or_404(user_id)
|
||||
if not user.is_following_corpus(corpus):
|
||||
user.follow_corpus(corpus)
|
||||
db.session.commit()
|
||||
return {}, 202
|
||||
|
||||
@bp.route('/<hashid:corpus_id>/unfollow', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def unfollow_corpus(corpus_id):
|
||||
corpus = Corpus.query.get_or_404(corpus_id)
|
||||
user_hashid = request.args.get('user_id')
|
||||
if user_hashid is None:
|
||||
user = current_user
|
||||
else:
|
||||
if not current_user.is_administrator():
|
||||
abort(403)
|
||||
else:
|
||||
user_id = hashids.decode(user_hashid)
|
||||
user = User.query.get_or_404(user_id)
|
||||
if user.is_following_corpus(corpus):
|
||||
user.unfollow_corpus(corpus)
|
||||
db.session.commit()
|
||||
return {}, 202
|
||||
|
||||
@bp.route('/add_permission/<hashid:corpus_id>/<hashid:user_id>/<int:permission>')
|
||||
def add_permission(corpus_id, user_id, permission):
|
||||
a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404()
|
||||
a.add_permission(permission)
|
||||
db.session.commit()
|
||||
return 'ok'
|
||||
|
||||
|
||||
@bp.route('/remove_permission/<hashid:corpus_id>/<hashid:user_id>/<int:permission>')
|
||||
def remove_permission(corpus_id, user_id, permission):
|
||||
a = CorpusFollowerAssociation.query.filter_by(followed_corpus_id=corpus_id, following_user_id=user_id).first_or_404()
|
||||
a.remove_permission(permission)
|
||||
db.session.commit()
|
||||
return 'ok'
|
||||
|
||||
@bp.route('/public/<hashid:corpus_id>')
|
||||
def public_corpus(corpus_id):
|
||||
corpus = Corpus.query.get_or_404(corpus_id)
|
||||
return render_template(
|
||||
'corpora/public_corpus.html.j2',
|
||||
corpus=corpus,
|
||||
title=corpus.title
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
from flask import flash, redirect, render_template, url_for
|
||||
from flask_login import current_user, login_required, login_user
|
||||
from app.auth.forms import LoginForm
|
||||
from app.models import User
|
||||
from app.models import Corpus, User
|
||||
from . import bp
|
||||
|
||||
|
||||
@ -31,7 +31,11 @@ def dashboard():
|
||||
u.to_json_serializeable(filter_by_privacy_settings=True) for u
|
||||
in User.query.filter(User.is_public == True, User.id != current_user.id).all()
|
||||
]
|
||||
return render_template('main/dashboard.html.j2', title='Dashboard', users=users)
|
||||
corpora = [
|
||||
c.to_json_serializeable() for c
|
||||
in Corpus.query.filter(Corpus.is_public == True).all()
|
||||
]
|
||||
return render_template('main/dashboard.html.j2', title='Dashboard', users=users, corpora=corpora)
|
||||
|
||||
|
||||
@bp.route('/dashboard2')
|
||||
|
@ -68,6 +68,11 @@ class ProfilePrivacySettings(IntEnum):
|
||||
SHOW_EMAIL = 1
|
||||
SHOW_LAST_SEEN = 2
|
||||
SHOW_MEMBER_SINCE = 4
|
||||
|
||||
class CorpusFollowPermission(IntEnum):
|
||||
VIEW = 1
|
||||
CONTRIBUTE = 2
|
||||
ADMINISTRATE = 4
|
||||
# endregion enums
|
||||
|
||||
|
||||
@ -298,6 +303,16 @@ class CorpusFollowerAssociation(db.Model):
|
||||
def __repr__(self):
|
||||
return f'<CorpusFollowerAssociation {self.following_user.__repr__()} ~ {self.followed_corpus.__repr__()}>'
|
||||
|
||||
def has_permission(self, permission):
|
||||
return self.permissions & permission == permission
|
||||
|
||||
def add_permission(self, permission):
|
||||
if not self.has_permission(permission):
|
||||
self.permissions += permission
|
||||
|
||||
def remove_permission(self, permission):
|
||||
if self.has_permission(permission):
|
||||
self.permissions -= permission
|
||||
|
||||
class User(HashidMixin, UserMixin, db.Model):
|
||||
__tablename__ = 'users'
|
||||
@ -576,6 +591,18 @@ class User(HashidMixin, UserMixin, db.Model):
|
||||
self.profile_privacy_settings = 0
|
||||
#endregion Profile Privacy settings
|
||||
|
||||
def follow_corpus(self, corpus):
|
||||
if not self.is_following_corpus(corpus):
|
||||
self.followed_corpora.append(corpus)
|
||||
|
||||
def unfollow_corpus(self, corpus):
|
||||
if self.is_following_corpus(corpus):
|
||||
self.followed_corpora.remove(corpus)
|
||||
|
||||
def is_following_corpus(self, corpus):
|
||||
return corpus in self.followed_corpora
|
||||
|
||||
|
||||
def to_json_serializeable(self, backrefs=False, relationships=False, filter_by_privacy_settings=False):
|
||||
json_serializeable = {
|
||||
'id': self.hashid,
|
||||
@ -623,6 +650,10 @@ class User(HashidMixin, UserMixin, db.Model):
|
||||
x.hashid: x.to_json_serializeable(relationships=True)
|
||||
for x in self.spacy_nlp_pipeline_models
|
||||
}
|
||||
json_serializeable['followed_corpora'] = {
|
||||
x.hashid: x.to_json_serializeable(relationships=True)
|
||||
for x in self.followed_corpora
|
||||
}
|
||||
|
||||
if filter_by_privacy_settings:
|
||||
if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL):
|
||||
|
@ -17,7 +17,7 @@ class CorpusFileList extends ResourceList {
|
||||
});
|
||||
});
|
||||
app.getUser(this.userId).then((user) => {
|
||||
this.add(Object.values(user.corpora[this.corpusId].files));
|
||||
this.add(Object.values(user.corpora[this.corpusId].files || user.followed_corpora[this.corpusId].files));
|
||||
this.isInitialized = true;
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
class UserList extends ResourceList {
|
||||
static autoInit() {
|
||||
for (let publicUserListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) {
|
||||
new UserList(publicUserListElement);
|
||||
for (let userListElement of document.querySelectorAll('.user-list:not(.no-autoinit)')) {
|
||||
new UserList(userListElement);
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,14 +41,14 @@ class UserList extends ResourceList {
|
||||
|
||||
initListContainerElement() {
|
||||
if (!this.listContainerElement.hasAttribute('id')) {
|
||||
this.listContainerElement.id = Utils.generateElementId('public-user-list-');
|
||||
this.listContainerElement.id = Utils.generateElementId('user-list-');
|
||||
}
|
||||
let listSearchElementId = Utils.generateElementId(`${this.listContainerElement.id}-search-`);
|
||||
this.listContainerElement.innerHTML = `
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="${listSearchElementId}" class="search" type="text"></input>
|
||||
<label for="${listSearchElementId}">Search public user</label>
|
||||
<label for="${listSearchElementId}">Search user</label>
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
@ -77,7 +77,7 @@ class UserList extends ResourceList {
|
||||
'full-name': user.full_name ? user.full_name : '',
|
||||
'location': user.location ? user.location : '',
|
||||
'organization': user.organization ? user.organization : '',
|
||||
'corpora-online': '0'
|
||||
'corpora-online': '-'
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
<li><a href="{{ url_for('main.dashboard') }}"><i class="material-icons">dashboard</i>Dashboard</a></li>
|
||||
<li><a href="{{ url_for('main.dashboard', _anchor='corpora') }}" style="padding-left: 47px;"><i class="nopaque-icons">I</i>My Corpora</a></li>
|
||||
<li><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" style="padding-left: 47px;"><i class="nopaque-icons">J</i>My Jobs</a></li>
|
||||
<li><a href="{{ url_for('main.dashboard', _anchor='social') }}" style="padding-left: 47px;"><i class="material-icons">groups</i>Social</a></li>
|
||||
<li><a href="{{ url_for('contributions.contributions') }}"><i class="material-icons">new_label</i>Contribute</a></li>
|
||||
<li><div class="divider"></div></li>
|
||||
<li><a class="subheader">Processes & Services</a></li>
|
||||
|
@ -1,4 +1,5 @@
|
||||
{% extends "base.html.j2" %}
|
||||
{% import "materialize/wtf.html.j2" as wtf %}
|
||||
{% from "corpora/_breadcrumbs.html.j2" import breadcrumbs with context %}
|
||||
|
||||
{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %}
|
||||
@ -9,7 +10,15 @@
|
||||
<div class="col s12" data-corpus-id="{{ corpus.hashid }}" data-user-id="{{ corpus.user.hashid }}" id="corpus-display">
|
||||
<div class="row">
|
||||
<div class="col s8 m9 l10">
|
||||
<h1 id="title"><span class="corpus-title"></span></h1>
|
||||
{# <h1 id="title"><span class="corpus-title"></span></h1> #}
|
||||
<h1 id="title">{{ corpus.title }}</h1>
|
||||
{% if not corpus.user == current_user %}
|
||||
{% if current_user.is_following_corpus(corpus) %}
|
||||
<a class="btn waves-effect waves-light" id="follow-corpus-request"><i class="material-icons left">add</i>Unfollow Corpus</a>
|
||||
{% elif not current_user.is_following_corpus(corpus) %}
|
||||
<a class="btn waves-effect waves-light" id="follow-corpus-request"><i class="material-icons left">add</i>Follow Corpus</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col s4 m3 l2 right-align">
|
||||
<p> </p>
|
||||
@ -76,6 +85,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_user.can(Permission.ADMINISTRATE) or current_user.hashid == corpus.user.hashid %}
|
||||
<div class="col s12">
|
||||
<form method="POST">
|
||||
{{ corpus_settings_form.hidden_tag() }}
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title" id="files">Corpus settings</span>
|
||||
<br>
|
||||
<p></p>
|
||||
{{ wtf.render_field(corpus_settings_form.is_public) }}
|
||||
<br>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ wtf.render_field(corpus_settings_form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title" id="files">Corpus followers</span>
|
||||
<div class="user-list no-autoinit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
||||
|
86
app/templates/corpora/public_corpus.html.j2
Normal file
86
app/templates/corpora/public_corpus.html.j2
Normal file
@ -0,0 +1,86 @@
|
||||
{% extends "base.html.j2" %}
|
||||
{% import "materialize/wtf.html.j2" as wtf %}
|
||||
|
||||
{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }} </h1>
|
||||
<div class="card service-color-border border-darken" data-service="corpus-analysis" style="border-top: 10px solid">
|
||||
<div class="card-content">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<p><b>Description:</b> {{ corpus.description }}</p>
|
||||
<br>
|
||||
<p></p>
|
||||
</div>
|
||||
<div class="col s6">
|
||||
<p><b>Creation date:</b> {{ corpus.creation_date }}</p>
|
||||
</div>
|
||||
<div class="col s6">
|
||||
<p><b>Number of tokens used:</b> {{ corpus.num_tokens }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
let corpusFollowingRequest = document.querySelector('#follow-corpus-request');
|
||||
|
||||
{# let followingUserList = new UserList(document.querySelector('.user-list'));
|
||||
followingUserList.add({{ following_users|tojson }}); #}
|
||||
|
||||
corpusFollowingRequest.addEventListener('click', function() {
|
||||
corpusFollowingRequest.innerHTML = '<i class="material-icons left">add</i>Unfollow Corpus';
|
||||
if ("{{ current_user.is_following_corpus(corpus) }}" === "False") {
|
||||
corpusFollowingRequest.lastChild.textContent = 'Unfollow Corpus';
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`/corpora/{{ corpus.hashid }}/follow`, {method: 'POST', headers: {Accept: 'application/json'}})
|
||||
.then(
|
||||
(response) => {
|
||||
if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
|
||||
if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
|
||||
if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);}
|
||||
app.flash(`You follow "{{ corpus.title }}" now`, 'corpus');
|
||||
window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'
|
||||
resolve(response);
|
||||
},
|
||||
(response) => {
|
||||
app.flash('Something went wrong', 'error');
|
||||
reject(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
} else {
|
||||
corpusFollowingRequest.innerHTML = '<i class="material-icons left">add</i>Unfollow Corpus';
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(`/corpora/{{ corpus.hashid }}/unfollow`, {method: 'POST', headers: {Accept: 'application/json'}})
|
||||
.then(
|
||||
(response) => {
|
||||
if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
|
||||
if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
|
||||
if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);}
|
||||
app.flash(`You are not following "{{ corpus.title }}" anymore`, 'corpus');
|
||||
resolve(response);
|
||||
window.location.href = '{{ url_for("corpora.corpus", corpus_id=corpus.id) }}'
|
||||
},
|
||||
(response) => {
|
||||
app.flash('Something went wrong', 'error');
|
||||
reject(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock scripts %}
|
@ -42,6 +42,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12" id="social">
|
||||
<h3>Social</h3>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Other users</span>
|
||||
<p>Find other users and follow them to see their corpora.</p>
|
||||
<div class="user-list no-autoinit"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Public corpora</span>
|
||||
<p>Find public corpora</p>
|
||||
<div class="public-corpus-list no-autoinit"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
||||
@ -96,3 +113,14 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock modals %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
let userList = new UserList(document.querySelector('.user-list'));
|
||||
userList.add({{ users|tojson }});
|
||||
let publicCorpusList = new CorpusList(document.querySelector('.public-corpus-list'));
|
||||
publicCorpusList.add({{ corpora|tojson }});
|
||||
</script>
|
||||
{% endblock scripts %}
|
||||
|
||||
|
@ -89,6 +89,24 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col s6">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<h4>Groups</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s6">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<h4>Public corpora</h4>
|
||||
<div class="public-corpora-list" data-user-id="{{ user.hashid }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock page_content %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user