Merge branch 'development' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into development

This commit is contained in:
Patrick Jentsch 2020-07-09 09:42:34 +02:00
commit e20a6a31d9
7 changed files with 160 additions and 51 deletions

View File

@ -44,7 +44,17 @@ def corpus(corpus_id):
corpus = Corpus.query.get_or_404(corpus_id)
if not (corpus.creator == current_user or current_user.is_administrator()):
abort(403)
return render_template('corpora/corpus.html.j2', corpus=corpus,
corpus_files = [dict(filename=corpus_file.filename,
author=corpus_file.author,
title=corpus_file.title,
publishing_year=corpus_file.publishing_year,
corpus_id=corpus.id,
id=corpus_file.id
)
for corpus_file in corpus.files]
return render_template('corpora/corpus.html.j2',
corpus=corpus,
corpus_files=corpus_files,
title='Corpus')
@ -216,7 +226,7 @@ def prepare_corpus(corpus_id):
abort(403)
if corpus.files.all():
tasks.build_corpus(corpus_id)
flash('Corpus gets build now.', 'corpus')
flash('Building Corpus...', 'corpus')
else:
flash('Can not build corpus, please add corpus file(s).', 'corpus')
return redirect(url_for('corpora.corpus', corpus_id=corpus_id))

View File

@ -6,10 +6,11 @@ from ..models import Result, ResultFile, User
from .forms import ImportResultsForm
from datetime import datetime
from flask import (abort, render_template, current_app, request, redirect,
flash, url_for, make_response)
flash, url_for, make_response, send_from_directory)
from flask_login import current_user, login_required
import json
import os
from .. import logger
@results.route('/import_results', methods=['GET', 'POST'])
@ -69,10 +70,12 @@ def results_overview():
'''
# get all results of current user
results = User.query.get(current_user.id).results
logger.warning(results)
def __p_time(time_str):
# helper to convert the datetime into a nice readable string
return datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S.%f')
# convert results into a list of dicts to add the measier to list.js in
# the template
results = [dict(query=r.corpus_metadata['query'],
@ -81,6 +84,7 @@ def results_overview():
corpus_creation_date=__p_time(r.corpus_metadata['corpus_creation_date']), # noqa
corpus_analysis_date=__p_time(r.corpus_metadata['corpus_analysis_date']), # noqa
corpus_type=r.corpus_metadata['corpus_type'],
file_id=r.file[0].id,
id=r.id)
for r in results]
return render_template('results/results.html.j2',
@ -140,3 +144,19 @@ def result_delete(result_id):
tasks.delete_result(result_id)
flash('Result deleted!')
return redirect(url_for('results.results_overview'))
@results.route('/<int:result_id>/file/<int:result_file_id>/download')
@login_required
def result_download(result_id, result_file_id):
result_file = ResultFile.query.get_or_404(result_file_id)
if not result_file.result_id == result_id:
abort(404)
if not (result_file.result.creator == current_user
or current_user.is_administrator()):
abort(403)
dir = os.path.join(current_app.config['NOPAQUE_STORAGE'],
result_file.dir)
return send_from_directory(as_attachment=True,
directory=dir,
filename=result_file.filename)

View File

@ -1,6 +1,7 @@
class RessourceList extends List {
constructor(idOrElement, subscriberList, type, options={}) {
if (!["corpus", "job", "result", "user", "job_input"].includes(type)) {
if (!["corpus", "job", "result", "user", "job_input",
"corpus_file"].includes(type)) {
console.error("Unknown Type!");
return;
}
@ -78,7 +79,17 @@ RessourceList.dataMapper = {
"analyse-link": ["analysing", "prepared", "start analysis"].includes(corpus.status) ? `/corpora/${corpus.id}/analyse` : "",
"edit-link": `/corpora/${corpus.id}`,
status: corpus.status,
title: corpus.title}),
title: corpus.title
}),
// Mapping for corpus file entities shown in the corpus overview
corpus_file: corpus_file => ({filename: corpus_file.filename,
author: corpus_file.author,
title: corpus_file.title,
publishing_year: corpus_file.publishing_year,
"edit-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/edit`,
"download-link": `${corpus_file.corpus_id}/files/${corpus_file.id}/download`,
"delete-modal": `delete-corpus-file-${corpus_file.id}-modal`
}),
// Mapping for job entities shown in the dashboard table.
job: job => ({creation_date: job.creation_date,
description: job.description,
@ -86,11 +97,13 @@ RessourceList.dataMapper = {
link: `/jobs/${job.id}`,
service: job.service,
status: job.status,
title: job.title}),
title: job.title
}),
// Mapping for job input files shown in table on every job page
job_input: job_input => ({filename: job_input.filename,
id: job_input.job_id,
"download-link": `${job_input.job_id}/inputs/${job_input.id}/download`}),
"download-link": `${job_input.job_id}/inputs/${job_input.id}/download`
}),
// Mapping for imported result entities from corpus analysis.
// Shown in imported results table
result: result => ({ query: result.query,
@ -101,14 +114,17 @@ RessourceList.dataMapper = {
corpus_type : result.corpus_type,
"details-link": `${result.id}/details`,
"inspect-link": `${result.id}/inspect`,
"delete-modal": `delete-result-${result.id}-modal`}),
"download-link": `${result.id}/file/${result.file_id}/download`,
"delete-modal": `delete-result-${result.id}-modal`
}),
// Mapping for user entities shown in admin table
user: user => ({username: user.username,
email: user.email,
role_id: user.role_id,
confirmed: user.confirmed,
id: user.id,
"profile-link": `user/${user.id}`})
"profile-link": `user/${user.id}`
})
};
@ -146,12 +162,12 @@ RessourceList.options = {
<span class="badge new status" data-badge-caption="">
</span>
</td>
<td class="right-align">
<a class="btn-floating edit-link waves-effect waves-light">
<td class="actions center-align">
<a class="btn-floating edit-link waves-effect waves-light" data-tooltip="Edit">
<i class="material-icons">edit</i>
</a>
<a class="btn-floating analyse-link waves-effect waves-light">
<i class="material-icons right">search</i>
<i class="material-icons">search</i>
</a>
</td>
</tr>`,
@ -165,6 +181,42 @@ RessourceList.options = {
{name: "edit-link", attr: "href"},
{name: "status", attr: "data-status"}]
},
// Corpus file entity blueprint setting html strucuture per entity per row
// Link classes have to correspond with Links defined in the Mapping process
corpus_file: {item: `<tr>
<td class="filename" style="word-break: break-word;"></td>
<td class="author" style="word-break: break-word;"></td>
<td class="title" style="word-break: break-word;"></td>
<td class="publishing_year" style="word-break: break-word;"></td>
<td class="actions center-align">
<a class="btn-floating tooltipped edit-link
waves-effect waves-light"
data-position="top"
data-tooltip="Edit">
<i class="material-icons">edit</i>
</a>
<a class="btn-floating tooltipped download-link
waves-effect waves-light"
data-position="top"
data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
<a class="btn-floating tooltipped modal-trigger red
waves-effect waves-light delete-modal"
data-position="top"
data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
</td>
</tr>`,
valueNames: ["filename",
"author",
"title",
"publishing_year",
{name: "edit-link", attr: "href"},
{name: "download-link", attr: "href"},
{name: "delete-modal", attr: "data-target"}]
},
// Job entity blueprint setting html strucuture per entity per row
// Link classes have to correspond with Links defined in the Mapping process
job: {item: `<tr>
@ -180,9 +232,9 @@ RessourceList.options = {
<td>
<span class="badge new status" data-badge-caption=""></span>
</td>
<td class="right-align">
<td class="actions center-align">
<a class="btn-floating link waves-effect waves-light">
<i class="material-icons right">send</i>
<i class="material-icons">send</i>
</a>
</td>
</tr>`,
@ -198,8 +250,12 @@ RessourceList.options = {
},
job_input: {item : `<tr>
<td class="filename"></td>
<td class="actions">
<a class="btn-floating download-link waves-effect waves-light"><i class="material-icons">file_download</i>
<td class="actions center-align">
<a class="btn-floating tooltipped download-link
waves-effect waves-light"
data-position="top"
data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
</td>
</tr>`,
@ -217,12 +273,30 @@ RessourceList.options = {
<td class="corpus_creation_date"></td>
<td class="corpus_analysis_date"></td>
<td class="corpus_type"></td>
<td class="actions right-align">
<a class="btn-floating details-link waves-effect waves-light"><i class="material-icons">info_outline</i>
<td class="actions center-align">
<a class="btn-floating tooltipped details-link
waves-effect waves-light"
data-position="top"
data-tooltip="Metadata Info">
<i class="material-icons">info_outline</i>
</a>
<a class="btn-floating inspect-link waves-effect waves-light"><i class="material-icons">search</i>
<a class="btn-floating tooltipped inspect-link
waves-effect waves-light"
data-position="top"
data-tooltip="View Results">
<i class="material-icons">search</i>
</a>
<a class="btn-floating red delete-modal waves-effect waves-light modal-trigger"><i class="material-icons">delete</i>
<a class="btn-floating tooltipped download-link
waves-effect waves-light"
data-position="top"
data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
<a class="btn-floating tooltipped red delete-modal
waves-effect waves-light modal-trigger"
data-position="top"
data-tooltip="Delete">
<i class="material-icons">delete</i>
</a>
</td>
</tr>`,
@ -236,6 +310,7 @@ RessourceList.options = {
"corpus_type",
{name: "details-link", attr: "href"},
{name: "inspect-link", attr: "href"},
{name: "download-link", attr: "href"},
{name: "delete-modal", attr: "data-target"}]
},
// User entity blueprint setting html strucuture per entity per row
@ -246,8 +321,12 @@ RessourceList.options = {
<td class="role_id"></td>
<td class="confirmed"></td>
<td class="id"></td>
<td class="actions">
<a class="btn-floating profile-link waves-effect waves-light"><i class="material-icons">edit</i>
<td class="actions center-align">
<a class="btn-floating tooltipped profile-link waves-effect
waves-light"
data-position="top"
data-tooltip="Edit User">
<i class="material-icons">edit</i>
</a>
</td>
</tr>`,

View File

@ -59,41 +59,34 @@
<div class="col s12">
<div class="card">
<div class="card-content" style="overflow: hidden;">
<div class="card-content" id="corpus-files" style="overflow: hidden;">
<span class="card-title">Files</span>
<div class="input-field">
<i class="material-icons prefix">search</i>
<input id="search-results" class="search" type="search"></input>
<label for="search-results">Search results</label>
</div>
<ul class="pagination paginationTop"></ul>
<table class="highlight responsive-table">
<thead>
<tr>
<th>Filename</th>
<th>Author</th>
<th>Title</th>
<th>Publishing year</th>
<th></th>
<th class="sort" data-sort="filename">Filename</th>
<th class="sort" data-sort="author">Author</th>
<th class="sort" data-sort="title">Title</th>
<th class="sort" data-sort="publishing_year">Publishing year</th>
<th>{# Actions #}</th>
</tr>
</thead>
<tbody>
<tbody class="list">
<tr class="show-if-only-child">
<td colspan="5">
<span class="card-title"><i class="material-icons left">book</i>Nothing here...</span>
<p>Corpus is empty. Add texts using the option below.</p>
</td>
</tr>
{% for file in corpus.files %}
<tr>
<td style="word-break: break-word;">{{ file.filename }}</td>
<td style="word-break: break-word;">{{ file.author }}</td>
<td style="word-break: break-word;">{{ file.title }}</td>
<td>{{ file.publishing_year }}</td>
<td class="right-align">
<a class="btn-floating waves-effect waves-light" href="{{ url_for('corpora.edit_corpus_file', corpus_file_id=file.id, corpus_id=corpus.id) }}"><i class="material-icons">edit</i></a>
<a class="btn-floating waves-effect waves-light" href="{{ url_for('corpora.download_corpus_file', corpus_file_id=file.id, corpus_id=corpus.id) }}"><i class="material-icons">file_download</i></a>
<a data-target="delete-corpus-file-{{ file.id }}-modal" class="btn-floating modal-trigger red waves-effect waves-light"><i class="material-icons">delete</i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<ul class="pagination paginationBottom"></ul>
</div>
<div class="card-action right-align">
<a href="{{ url_for('corpora.add_corpus_file', corpus_id=corpus.id) }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add corpus file</a>
@ -129,6 +122,11 @@
<script>
// create corpus file table
var ressources = {{ corpus_files|tojson|safe }};
var corpusFilesList = new RessourceList("corpus-files", null, "corpus_file");
corpusFilesList.addRessources(ressources);
class InformationUpdater {
constructor(corpusId, foreignCorpusFlag) {
this.corpusId = corpusId;

View File

@ -120,7 +120,7 @@
<tr>
<th>Result Type</th>
<th>Archive Name</th>
<th>Download</th>
<th>{# Actions #}</th>
</tr>
</thead>
<tbody class="results">
@ -159,7 +159,6 @@
<script>
// job_input_table code
var ressources = {{ job_inputs|tojson|safe }};
console.log(ressources);
var jobInputsList = new RessourceList("inputs", null, "job_input");
jobInputsList.addRessources(ressources);
@ -244,9 +243,12 @@
<tr>
<td>${resultType}</td>
<td>${result.filename}</td>
<td>
<a class="btn-floating waves-effect waves-light" download href="/jobs/${result.job_id}/results/${result.id}/download">
<i class="material-icons left">file_download</i>
<td class="center-align">
<a class="btn-floating tooltipped waves-effect waves-light"
download href="/jobs/${result.job_id}/results/${result.id}/download"
data-position="top"
data-tooltip="Download">
<i class="material-icons">file_download</i>
</a>
</td>
</tr>

View File

@ -85,7 +85,8 @@
</button>
{% endmacro %}
{% macro delete_modal_file(file) %}
{# maybe create a modal template later on for actions like delete etc #}
{# {% macro delete_modal_file(file) %}
<div id="delete-corpus-file-{{ file.id }}-modal" class="modal">
<div class="modal-content">
<h4>Confirm corpus file deletion</h4>
@ -96,4 +97,4 @@
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('corpora.delete_corpus_file', corpus_file_id=resource_id, corpus_id=corpus.id) }}"><i class="material-icons left">delete</i>Delete</a>
</div>
</div>
{% endmacro %}
{% endmacro %} #}

View File

@ -8,7 +8,6 @@ Flask-Migrate
Flask-Paranoid
Flask-SocketIO
Flask-SQLAlchemy
Flask-Table
Flask-WTF
jsonpatch
psycopg2