mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2025-01-18 05:50:34 +00:00
Merge branch 'development' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into development
This commit is contained in:
commit
5e02f96c58
@ -59,7 +59,4 @@ def create_app(config_name):
|
||||
from .services import services as services_blueprint
|
||||
app.register_blueprint(services_blueprint, url_prefix='/services')
|
||||
|
||||
from .results import results as results_blueprint
|
||||
app.register_blueprint(results_blueprint, url_prefix='/results')
|
||||
|
||||
return app
|
||||
|
@ -133,8 +133,6 @@ class User(UserMixin, db.Model):
|
||||
cascade='save-update, merge, delete')
|
||||
jobs = db.relationship('Job', backref='creator', lazy='dynamic',
|
||||
cascade='save-update, merge, delete')
|
||||
results = db.relationship('Result', backref='creator', lazy='dynamic',
|
||||
cascade='save-update, merge, delete')
|
||||
query_results = db.relationship('QueryResult',
|
||||
backref='creator',
|
||||
cascade='save-update, merge, delete',
|
||||
@ -660,59 +658,6 @@ class QueryResult(db.Model):
|
||||
return '<QueryResult {}>'.format(self.title)
|
||||
|
||||
|
||||
class Result(db.Model):
|
||||
'''
|
||||
Class to define a result set of one query.
|
||||
'''
|
||||
__tablename__ = 'results'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
# Foreign keys
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||||
# Relationships'
|
||||
corpus_metadata = db.Column(db.JSON())
|
||||
file = db.relationship('ResultFile', backref='result', lazy='dynamic',
|
||||
cascade='save-update, merge, delete')
|
||||
|
||||
def delete(self):
|
||||
result_file_path = os.path.join(current_app.config['NOPAQUE_STORAGE'],
|
||||
self.file[0].dir)
|
||||
try:
|
||||
os.remove(result_file_path)
|
||||
except OSError:
|
||||
pass
|
||||
db.session.delete(self)
|
||||
|
||||
def __repr__(self):
|
||||
'''
|
||||
String representation of the Result. For human readability.
|
||||
'''
|
||||
return '<Result ID: {result_id}>'.format(result_id=self.id)
|
||||
|
||||
|
||||
class ResultFile(db.Model):
|
||||
'''
|
||||
Class to define a ResultFile
|
||||
'''
|
||||
__tablename__ = 'result_files'
|
||||
# Primary key
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
# Foreign keys
|
||||
result_id = db.Column(db.Integer, db.ForeignKey('results.id'))
|
||||
# Fields
|
||||
filename = db.Column(db.String(255))
|
||||
dir = db.Column(db.String(255))
|
||||
|
||||
def delete(self):
|
||||
db.session.delete(self)
|
||||
db.session.commit()
|
||||
|
||||
def __repr__(self):
|
||||
'''
|
||||
String representation of the ResultFile. For human readability.
|
||||
'''
|
||||
return '<ResultFile {result_file_name}>'.format(result_file_name=self.filename) # noqa
|
||||
|
||||
|
||||
'''
|
||||
' Flask-Login is told to use the application’s custom anonymous user by setting
|
||||
' its class in the login_manager.anonymous_user attribute.
|
||||
|
@ -1,5 +0,0 @@
|
||||
from flask import Blueprint
|
||||
|
||||
|
||||
results = Blueprint('results', __name__)
|
||||
from . import views # noqa
|
@ -1,18 +0,0 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from werkzeug.utils import secure_filename
|
||||
from wtforms import FileField, SubmitField, ValidationError
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class ImportResultsForm(FlaskForm):
|
||||
'''
|
||||
Form used to import one result json file.
|
||||
'''
|
||||
file = FileField('File', validators=[DataRequired()])
|
||||
submit = SubmitField()
|
||||
|
||||
def validate_file(self, field):
|
||||
if not field.data.filename.lower().endswith('.json'):
|
||||
raise ValidationError('File does not have an approved extension: '
|
||||
'.json')
|
||||
field.data.filename = secure_filename(field.data.filename)
|
@ -1,13 +0,0 @@
|
||||
from .. import db
|
||||
from ..decorators import background
|
||||
from ..models import Result
|
||||
|
||||
|
||||
@background
|
||||
def delete_result(result_id, *args, **kwargs):
|
||||
with kwargs['app'].app_context():
|
||||
result = Result.query.get(result_id)
|
||||
if result is None:
|
||||
raise Exception('Result {} not found'.format(result_id))
|
||||
result.delete() # cascades down and also deletes ResultFile
|
||||
db.session.commit()
|
@ -1,186 +0,0 @@
|
||||
from . import results
|
||||
from . import tasks
|
||||
from .. import db
|
||||
from ..corpora.forms import DisplayOptionsForm, InspectDisplayOptionsForm
|
||||
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, send_from_directory)
|
||||
from flask_login import current_user, login_required
|
||||
import json
|
||||
import os
|
||||
from .. import logger
|
||||
from jsonschema import validate
|
||||
|
||||
|
||||
@results.route('/import', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def result_import():
|
||||
'''
|
||||
View to import one json result file. Uses the ImportReultFileForm.
|
||||
'''
|
||||
import_results_form = ImportResultsForm(prefix='add-result-file-form')
|
||||
if import_results_form.is_submitted():
|
||||
if not import_results_form.validate():
|
||||
return make_response(import_results_form.errors, 400)
|
||||
# Save the file
|
||||
# result creation only happens on file save to avoid creating a result
|
||||
# object in the db everytime by just visiting the import_results page
|
||||
result = Result(user_id=current_user.id)
|
||||
db.session.add(result)
|
||||
db.session.commit()
|
||||
if not (result.creator == current_user
|
||||
or current_user.is_administrator()):
|
||||
abort(403)
|
||||
# create paths to save the uploaded json file
|
||||
dir = os.path.join(str(result.user_id),
|
||||
'results',
|
||||
'corpus_analysis_results',
|
||||
str(result.id))
|
||||
abs_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'], dir)
|
||||
abs_file_path = os.path.join(abs_dir,
|
||||
import_results_form.file.data.filename)
|
||||
os.makedirs(abs_dir)
|
||||
# save the json file
|
||||
import_results_form.file.data.save(abs_file_path)
|
||||
# Create ResultFile db entry
|
||||
result_file = ResultFile(result_id=result.id,
|
||||
dir=dir,
|
||||
filename=import_results_form.file.data.filename) # noqa
|
||||
db.session.add(result_file)
|
||||
db.session.commit()
|
||||
# reads uploaded json file
|
||||
with open(abs_file_path, 'r') as f:
|
||||
corpus_metadata = json.load(f)
|
||||
try:
|
||||
# open json schema to validate against it
|
||||
with open('app/static/json_schema/nopaque_cqi_py_results_schema.json', # noqa
|
||||
'r') as s:
|
||||
schema = json.load(s)
|
||||
# validate if imported json is actually a json result file
|
||||
validate(instance=corpus_metadata, schema=schema)
|
||||
# if validated continue
|
||||
# delete matches and cpos_lookup from read json file
|
||||
del corpus_metadata['matches']
|
||||
del corpus_metadata['cpos_lookup']
|
||||
# save metadate directly as json into one field
|
||||
result.corpus_metadata = corpus_metadata
|
||||
flash('Result file added!', 'result')
|
||||
db.session.commit()
|
||||
return make_response(
|
||||
{'redirect_url': url_for('results.results_overview')},
|
||||
201)
|
||||
except Exception as e:
|
||||
# this runs if validation fails
|
||||
flash('Uploaded file was not a valid result JSON!', 'result')
|
||||
# deletes before created Result and ResultFile db entries
|
||||
tasks.delete_result(result.id)
|
||||
return make_response(
|
||||
{'redirect_url': url_for('results.result_import')},
|
||||
201)
|
||||
return render_template('results/result_import.html.j2',
|
||||
import_results_form=import_results_form,
|
||||
title='Add corpus file')
|
||||
|
||||
|
||||
@results.route('/')
|
||||
@login_required
|
||||
def results_overview():
|
||||
'''
|
||||
Shows an overview of imported results.
|
||||
'''
|
||||
# get all results of current user
|
||||
results = User.query.get(current_user.id).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'],
|
||||
match_count=r.corpus_metadata['match_count'],
|
||||
corpus_name=r.corpus_metadata['corpus_name'],
|
||||
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',
|
||||
title='Imported Results',
|
||||
# table=table,
|
||||
results=results)
|
||||
|
||||
|
||||
@results.route('/<int:result_id>/details')
|
||||
@login_required
|
||||
def result_details(result_id):
|
||||
'''
|
||||
View to show metadate and details about on imported result file.
|
||||
'''
|
||||
result = Result.query.get_or_404(result_id)
|
||||
if not (result.creator == current_user or current_user.is_administrator()):
|
||||
abort(403)
|
||||
return render_template('results/result_details.html.j2',
|
||||
result=result,
|
||||
title='Result Details')
|
||||
|
||||
|
||||
@results.route('/<int:result_id>/inspect')
|
||||
@login_required
|
||||
def result_inspect(result_id):
|
||||
'''
|
||||
View to inspect one imported result file in a corpus analysis like interface
|
||||
'''
|
||||
display_options_form = DisplayOptionsForm(
|
||||
prefix='display-options-form',
|
||||
result_context=request.args.get('context', 20),
|
||||
results_per_page=request.args.get('results_per_page', 30))
|
||||
inspect_display_options_form = InspectDisplayOptionsForm(
|
||||
prefix='inspect-display-options-form')
|
||||
result = Result.query.get_or_404(result_id)
|
||||
result_file_path = os.path.join(current_app.config['NOPAQUE_STORAGE'],
|
||||
result.file[0].dir,
|
||||
result.file[0].filename)
|
||||
with open(result_file_path, 'r') as result_json:
|
||||
result_json = json.load(result_json)
|
||||
if not (result.creator == current_user or current_user.is_administrator()):
|
||||
abort(403)
|
||||
return render_template('results/result_inspect.html.j2',
|
||||
display_options_form=display_options_form,
|
||||
inspect_display_options_form=inspect_display_options_form,
|
||||
result=result,
|
||||
result_json=result_json,
|
||||
title='Result Insepct')
|
||||
|
||||
|
||||
@results.route('/<int:result_id>/delete')
|
||||
@login_required
|
||||
def result_delete(result_id):
|
||||
result = Result.query.get_or_404(result_id)
|
||||
if not result.id == result_id:
|
||||
abort(404)
|
||||
if not (result.creator == current_user
|
||||
or current_user.is_administrator()):
|
||||
abort(403)
|
||||
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)
|
@ -1,6 +1,6 @@
|
||||
class RessourceList extends List {
|
||||
constructor(idOrElement, subscriberList, type, options={}) {
|
||||
if (!["Corpus", "CorpusFile", "Job", "JobInput", "QueryResult", "User", "result"].includes(type)) {
|
||||
if (!["Corpus", "CorpusFile", "Job", "JobInput", "QueryResult", "User"].includes(type)) {
|
||||
console.error("Unknown Type!");
|
||||
return;
|
||||
}
|
||||
@ -124,16 +124,6 @@ RessourceList.dataMapper = {
|
||||
link: `/query_results/${query_result.id}`,
|
||||
query: query_result.query_metadata.query,
|
||||
title: query_result.title}),
|
||||
result: result => ({query: result.query,
|
||||
match_count: result.match_count,
|
||||
corpus_name: result.corpus_name,
|
||||
corpus_creation_date: result.corpus_creation_date,
|
||||
corpus_analysis_date: result.corpus_analysis_date,
|
||||
corpus_type : result.corpus_type,
|
||||
"details-link": `${result.id}/details`,
|
||||
"inspect-link": `${result.id}/inspect`,
|
||||
"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,
|
||||
@ -277,56 +267,6 @@ RessourceList.options = {
|
||||
{data: ["id"]},
|
||||
{name: "inspect-link", attr: "href"},
|
||||
{name: "link", attr: "href"}]},
|
||||
// Result (imported from corpus analysis) entity blueprint setting html
|
||||
// strucuture per entity per row
|
||||
// Link classes have to correspond with Links defined in the Mapping process
|
||||
result: {item: `<tr>
|
||||
<td class="query"></td>
|
||||
<td class="match_count"></td>
|
||||
<td class="corpus_name"></td>
|
||||
<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 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 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 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>`,
|
||||
// Result Value Names per column. Have to correspond with keys from the
|
||||
// Mapping step above.
|
||||
valueNames: ["query",
|
||||
"match_count",
|
||||
"corpus_name",
|
||||
"corpus_creation_date",
|
||||
"corpus_analysis_date",
|
||||
"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
|
||||
// Link classes have to correspond with Links defined in the Mapping process
|
||||
User: {item: `<tr>
|
||||
|
@ -28,8 +28,6 @@
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.result_import') }}">Import Results<i class="material-icons right">file_upload</i></a>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.results_overview') }}">Show Imported Results<i class="material-icons right">folder</i></a>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('corpora.add_corpus') }}">New corpus<i class="material-icons right">add</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,179 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% set headline = 'Workflow' %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s4">
|
||||
<h5>Your data</h5>
|
||||
</div>
|
||||
<div class="col s4">
|
||||
<div class="card hoverable">
|
||||
<div class="card-content center-align">
|
||||
<p>
|
||||
<i class="material-icons large">account_circle</i>
|
||||
</p>
|
||||
<span class="card-title">You</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s4 right-align">
|
||||
<h5>Your goals</h5>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||
<h3>Process your data with nopaque</h3>
|
||||
</div>
|
||||
|
||||
<div class="col s12"></div>
|
||||
|
||||
<div class="col s4">
|
||||
<div class="card hoverable" id="fs-trigger">
|
||||
<div class="card-content">
|
||||
<a class="btn-floating btn-large hoverable left" style="margin-right: 15px;">
|
||||
<i class="material-icons service" data-service="file-setup"></i>
|
||||
</a>
|
||||
<span class="card-title">File setup<br><br></span>
|
||||
<i>Prepare images for further processing</i>
|
||||
<i class="material-icons medium teal-text hoverable" style="position: absolute; top: 40px; right: -40px; z-index: 9;">trending_flat</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s4">
|
||||
<div class="card hoverable" id="ocr-trigger">
|
||||
<div class="card-content">
|
||||
<a class="btn-floating btn-large hoverable left" style="margin-right: 15px;">
|
||||
<i class="material-icons service" data-service="ocr"></i>
|
||||
</a>
|
||||
<span class="card-title">Optical Character Recognition</span>
|
||||
<i>Convert image data into machine readable text</i>
|
||||
<i class="material-icons medium teal-text hoverable" style="position: absolute; top: 40px; right: -40px; z-index: 9;">trending_flat</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s4">
|
||||
<div class="card hoverable" id="nlp-trigger">
|
||||
<div class="card-content">
|
||||
<a class="btn-floating btn-large left" style="margin-right: 15px;">
|
||||
<i class="material-icons service" data-service="nlp"></i>
|
||||
</a>
|
||||
<span class="card-title">Natural Language Processing</span>
|
||||
<i>Append linguistic informations to your text</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 hide" id="fs-info">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">More information</span>
|
||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
|
||||
<p>Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 hide" id="ocr-info">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">More information</span>
|
||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
|
||||
<p>Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 hide" id="nlp-info">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">More information</span>
|
||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
|
||||
<p>Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
fileSetupTriggerElement = document.getElementById("fs-trigger");
|
||||
fileSetupInfoElement = document.getElementById("fs-info");
|
||||
oCRTriggerElement = document.getElementById("ocr-trigger");
|
||||
oCRInfoElement = document.getElementById("ocr-info");
|
||||
nLPTriggerElement = document.getElementById("nlp-trigger");
|
||||
nLPInfoElement = document.getElementById("nlp-info");
|
||||
fileSetupTriggerElement.addEventListener("click", () => {
|
||||
fileSetupInfoElement.classList.remove("hide");
|
||||
oCRInfoElement.classList.add("hide");
|
||||
nLPInfoElement.classList.add("hide");
|
||||
});
|
||||
oCRTriggerElement.addEventListener("click", () => {
|
||||
fileSetupInfoElement.classList.add("hide");
|
||||
oCRInfoElement.classList.remove("hide");
|
||||
nLPInfoElement.classList.add("hide");
|
||||
});
|
||||
nLPTriggerElement.addEventListener("click", () => {
|
||||
fileSetupInfoElement.classList.add("hide");
|
||||
oCRInfoElement.classList.add("hide");
|
||||
nLPInfoElement.classList.remove("hide");
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="col s12">
|
||||
<h3>Research process</h3>
|
||||
</div>
|
||||
|
||||
<div class="col s2"></div>
|
||||
|
||||
<div class="col s8">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<a class="btn-floating btn-large hoverable left" style="margin-right: 15px;">
|
||||
<i class="material-icons service" data-service="corpus_analysis"></i>
|
||||
</a>
|
||||
<span class="card-title">Corpus analysis</span>
|
||||
<i>Create as many text corpora as you want. It makes use of CQP Query Language, which allows for complex search requests with the aid of metadata and NLP tags.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12"></div>
|
||||
|
||||
<div class="col s3">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Publish</span>
|
||||
<i>Get information from your texts and open up new perspectives, this may lead to further analysis ideas.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s3">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Enlightment</span>
|
||||
<i>Get information from your texts and open up new perspectives, this may lead to further analysis ideas.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s3">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Import/Export results</span>
|
||||
<i>You can export your query results and share it with others or view results from your research partners.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s3">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">Stuff</span>
|
||||
<i>You can export your query results and share it with others or view results from your research partners.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -1,119 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% block page_content %}
|
||||
|
||||
|
||||
<div class="col s12">
|
||||
<p>Below the metadata for the results from the Corpus
|
||||
<i>{{ result.corpus_metadata.corpus_name }}</i> generated with the query
|
||||
<i>{{ result.corpus_metadata.query }}</i> are shown.
|
||||
</p>
|
||||
<p>{{ texts_metadata }}</p>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content" id="results">
|
||||
<table class="responsive-table highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Metadata Description</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pair in result.corpus_metadata|dictsort %}
|
||||
<tr>
|
||||
<td>{{ pair[0] }}</td>
|
||||
{% if pair[0] == 'corpus_all_texts'
|
||||
or pair[0] == 'text_lookup' %}
|
||||
<td>
|
||||
<table>
|
||||
{% for key, value in pair[1].items() %}
|
||||
<tr style="border-bottom: none;">
|
||||
<td>
|
||||
<i>{{ value['title'] }}</i> written
|
||||
by <i>{{ value['author'] }}</i>
|
||||
in <i>{{ value['publishing_year'] }}</i>
|
||||
<a class="waves-effect
|
||||
waves-light
|
||||
btn
|
||||
right
|
||||
more-text-detials"
|
||||
data-metadata-key="{{ pair[0] }}"
|
||||
data-text-key="{{ key }}"
|
||||
href="#modal-text-details">More
|
||||
<i class="material-icons right"
|
||||
data-metadata-key="{{ pair[0] }}"
|
||||
data-text-key="{{ key }}">
|
||||
info_outline
|
||||
</i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>{{ pair[1] }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a class="waves-effect waves-light btn left-align" href="{{ url_for('results.results_overview') }}">Back To Overview<i class="material-icons right">arrow_back</i></a>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.result_inspect', result_id=result.id) }}">Inspect Results<i class="material-icons right">search</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Structure -->
|
||||
<div id="modal-text-details" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Bibliographic data</h4>
|
||||
<p id="bibliographic-data"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-green red btn">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var moreTextDetailsButtons;
|
||||
moreTextDetailsButtons = document.getElementsByClassName("more-text-detials");
|
||||
for (var btn of moreTextDetailsButtons) {
|
||||
btn.onclick = () => {
|
||||
let modal = document.getElementById("modal-text-details");
|
||||
modal = M.Modal.init(modal, {"dismissible": true});
|
||||
modal.open();
|
||||
let metadataKey = event.target.dataset.metadataKey;
|
||||
let textKey = event.target.dataset.textKey;
|
||||
let textData = {{ result.corpus_metadata|tojson|safe }}[metadataKey][textKey];
|
||||
console.log(textData);
|
||||
let bibliographicData = document.getElementById("bibliographic-data");
|
||||
bibliographicData.innerHTML = "";
|
||||
let table = document.createElement("table");
|
||||
for (let [key, value] of Object.entries(textData)) {
|
||||
table.insertAdjacentHTML("afterbegin",
|
||||
`
|
||||
<tr>
|
||||
<td>${key}</td>
|
||||
<td>${value}</td>
|
||||
</tr>
|
||||
`);
|
||||
}
|
||||
table.insertAdjacentHTML("afterbegin",
|
||||
`
|
||||
<thead>
|
||||
<th>Description</th>
|
||||
<th>Value</th>
|
||||
</thead>
|
||||
`)
|
||||
bibliographicData.appendChild(table);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -1,39 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12 m4">
|
||||
<p>Fill out the following form to upload and view Results and Sub Results
|
||||
exported from the Corpus analsis Tool.</p>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('main.dashboard') }}"><i class="material-icons left">arrow_back</i>Back to dashboard</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<form class="nopaque-submit-form" data-progress-modal="progress-modal">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
{{ import_results_form.hidden_tag() }}
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
{{ M.render_field(import_results_form.file, accept='.json', placeholder='Choose your .json file') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ M.render_field(import_results_form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="progress-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4>
|
||||
<div class="progress">
|
||||
<div class="determinate" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,281 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% set headline = ' ' %}
|
||||
|
||||
{% set full_width = True %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12" id="query-display">
|
||||
<div class="card">
|
||||
<div class="card-content" id="result-list" style="overflow: hidden;">
|
||||
<div class="row" style="margin-bottom: 0px;">
|
||||
<div class="col s12 m3 l3" id="results-info">
|
||||
<div class="row section">
|
||||
<h6 style="margin-top: 0px;">Infos</h6>
|
||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
||||
<div class="col" id="infos">
|
||||
<p>
|
||||
Displaying
|
||||
<span id="received-match-count">
|
||||
</span> of
|
||||
<span id="match-count"></span>
|
||||
matches.
|
||||
<br>
|
||||
Matches occured in
|
||||
<span id="text-lookup-count"></span>
|
||||
corpus files:
|
||||
<br>
|
||||
<span id=text-titles></span>
|
||||
</p>
|
||||
<div class="progress hide" id="query-results-progress">
|
||||
<div class="determinate" id="query-results-determinate"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12 m9 l9" id="actions-and-tools">
|
||||
<div class="row section">
|
||||
<div class="col s12 m3 l3" id="display">
|
||||
<h6 style="margin-top: 0px;">Display</h6>
|
||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<form id="display-options-form">
|
||||
{{ M.render_field(display_options_form.results_per_page,
|
||||
material_icon='format_list_numbered') }}
|
||||
{{ M.render_field(display_options_form.result_context,
|
||||
material_icon='short_text') }}
|
||||
{{ M.render_field(display_options_form.expert_mode) }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Table showing the query results -->
|
||||
<div class="col s12">
|
||||
<ul class="pagination paginationTop"></ul>
|
||||
<table class="responsive-table highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 2%">Nr.</th>
|
||||
<th style="width: 3%">Title</th>
|
||||
<th style="width: 25%">Left context</th>
|
||||
<th style="width: 35%">Match</th>
|
||||
<th style="width: 10%">Actions</th>
|
||||
<th style="width: 25%">Right Context</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" id="query-results">
|
||||
</tbody>
|
||||
</table>
|
||||
<ul class="pagination paginationBottom"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Context modal used for detailed information about one match -->
|
||||
<div id="context-modal" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<form>
|
||||
<div class="row" style="margin-bottom: 0px; margin-top: -20px;">
|
||||
<div class="col s12 m6 l6">
|
||||
<div class="section">
|
||||
<h6 style="margin-top: 0px;">Display</h6>
|
||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
||||
<div class="col s12" style="margin-bottom: 10px;" id="display-inspect">
|
||||
{{ inspect_display_options_form.expert_mode_inspect.label.text }}
|
||||
<div class="switch right">
|
||||
<label>
|
||||
{{ inspect_display_options_form.expert_mode_inspect() }}
|
||||
<span class="lever"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12" style="margin-bottom: 10px;" id="create-inspect">
|
||||
{{ inspect_display_options_form.highlight_sentences.label.text }}
|
||||
<div class="switch right">
|
||||
<label>
|
||||
{{ inspect_display_options_form.highlight_sentences() }}
|
||||
<span class="lever"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12" style="margin-bottom: 10px;">
|
||||
Sentences around match
|
||||
<div class="input-field right" style="margin-top: -2rem;
|
||||
margin-bottom: -2rem;
|
||||
height: 0px;">
|
||||
<p class="range-field">
|
||||
<input type="range"
|
||||
id="context-sentences"
|
||||
min="1"
|
||||
max="10"
|
||||
value="3" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="row section">
|
||||
<h5 style="margin-top: 0px;">Context for match:
|
||||
<span id="context-match-nr"></span></h5>
|
||||
<div class="divider" style="margin-bottom: 10px;"></div>
|
||||
<div class="col s12" >
|
||||
<div id="context-results">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{# <a id="inspect-download-context" class="left waves-effect waves-light btn">
|
||||
Export Single Context
|
||||
<i class="material-icons right">file_download</i>
|
||||
</a> #}
|
||||
<a href="#!" class="modal-close waves-effect waves-light red btn">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/nopaque.Results.js') }}">
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/nopaque.callbacks.js') }}">
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/nopaque.InteractionElement.js') }}">
|
||||
</script>
|
||||
<script>
|
||||
// ###### global variables ######
|
||||
var full_result_json;
|
||||
var result_json;
|
||||
var queryResultsDeterminateElement; // The progress bar for recieved results
|
||||
var receivedMatchCountElement; // Nr. of loaded matches will be displayed in this element
|
||||
var textLookupCountElement // Nr of texts the matches occured in will be shown in this element
|
||||
var textTitlesElement; // matched text titles
|
||||
var progress; // global progress value
|
||||
var queryResultsProgressElement; // Div element holding the progress bar
|
||||
var expertModeSwitchElement; // Expert mode switch Element
|
||||
var matchCountElement; // Total nr. of matches will be displayed in this element
|
||||
var interactionElements; // Interaction elements and their parameters
|
||||
var contextModal; // Modal to open on inspect for further match context
|
||||
|
||||
// ###### Defining local scope variables
|
||||
let displayOptionsFormElement; // Form holding the display informations
|
||||
let resultItems; // array of built html result items row element. This is called when results are transmitted and being recieved
|
||||
let hitsPerPageInputElement;let contextPerItemElement; // Form Element for display option
|
||||
let paginationElements;
|
||||
let inspectBtnElements;
|
||||
|
||||
// ###### Initializing variables ######
|
||||
displayOptionsFormElement = document.getElementById("display-options-form");
|
||||
resultItems = [];
|
||||
queryResultsDeterminateElement = document.getElementById("query-results-determinate");
|
||||
receivedMatchCountElement = document.getElementById("received-match-count");
|
||||
textLookupCountElement = document.getElementById("text-lookup-count");
|
||||
textTitlesElement = document.getElementById("text-titles");
|
||||
queryResultsProgressElement = document.getElementById("query-results-progress");
|
||||
expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
|
||||
matchCountElement = document.getElementById("match-count");
|
||||
hitsPerPageInputElement = document.getElementById("display-options-form-results_per_page");
|
||||
contextPerItemElement = document.getElementById("display-options-form-result_context");
|
||||
paginationElements = document.getElementsByClassName("pagination");
|
||||
contextModal = document.getElementById("context-modal");
|
||||
|
||||
// js list options
|
||||
displayOptionsData = ResultsList.getDisplayOptions(displayOptionsFormElement);
|
||||
resultsListOptions = {page: displayOptionsData["resultsPerPage"],
|
||||
pagination: [{
|
||||
name: "paginationTop",
|
||||
paginationClass: "paginationTop",
|
||||
innerWindow: 8,
|
||||
outerWindow: 1
|
||||
}, {
|
||||
paginationClass: "paginationBottom",
|
||||
innerWindow: 8,
|
||||
outerWindow: 1
|
||||
}],
|
||||
valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
|
||||
item: `<span></span>`
|
||||
};
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
// Initialize some Modals
|
||||
contextModal = M.Modal.init(contextModal, {"dismissible": true});
|
||||
|
||||
// ###### recreating chunk structure to reuse callback queryRenderResults()
|
||||
full_result_json = {{ result_json|tojson|safe }};
|
||||
result_json = {};
|
||||
result_json["chunk"] = {};
|
||||
result_json.chunk["cpos_lookup"] = full_result_json.cpos_lookup;
|
||||
result_json.chunk["cpos_ranges"] = full_result_json.cpos_ranges;
|
||||
result_json.chunk["matches"] = full_result_json.matches;
|
||||
result_json.chunk["text_lookup"] = full_result_json.text_lookup;
|
||||
|
||||
// Init corpus analysis components
|
||||
data = new Data();
|
||||
resultsList = new ResultsList("result-list", resultsListOptions);
|
||||
resultsMetaData = new MetaData();
|
||||
results = new Results(data, resultsList, resultsMetaData);
|
||||
results.clearAll(); // inits some object keys and values
|
||||
// TODO: save metadate into results.metaData
|
||||
|
||||
// setting some initial values for user feedback
|
||||
matchCountElement.innerText = full_result_json.match_count;
|
||||
|
||||
// Initialization of interactionElemnts
|
||||
// An interactionElement is an object identifing a switch or button via
|
||||
// htmlID. Callbacks are set for these elements which will be triggered on
|
||||
// a pagination interaction by the user or if the status of the element has
|
||||
// been altered. (Like the switche has ben turned on or off).
|
||||
interactionElements = new Array();
|
||||
let expertModeInteraction = new InteractionElement("display-options-form-expert_mode");
|
||||
expertModeInteraction.setCallback("on",
|
||||
results.jsList.expertModeOn,
|
||||
results.jsList,
|
||||
["query-display"])
|
||||
expertModeInteraction.setCallback("off",
|
||||
results.jsList.expertModeOff,
|
||||
results.jsList,
|
||||
["query-display"])
|
||||
|
||||
let activateInspectInteraction = new InteractionElement("inspect",
|
||||
false);
|
||||
activateInspectInteraction.setCallback("noCheck",
|
||||
results.jsList.activateInspect,
|
||||
results.jsList);
|
||||
|
||||
let changeContextInteraction = new InteractionElement("display-options-form-results_per_page",
|
||||
false);
|
||||
changeContextInteraction.setCallback("noCheck",
|
||||
results.jsList.changeContext,
|
||||
results.jsList)
|
||||
interactionElements.push(expertModeInteraction, activateInspectInteraction, changeContextInteraction);
|
||||
|
||||
// checks if a change for every interactionElement happens and executes
|
||||
// the callbacks accordingly
|
||||
InteractionElement.onChangeExecute(interactionElements);
|
||||
|
||||
// eventListener if pagination is used to apply new context size to new page
|
||||
// and also activate inspect match if progress is 100
|
||||
// also adds more interaction buttons like add to sub results
|
||||
for (let element of paginationElements) {
|
||||
element.addEventListener("click", (event) => {
|
||||
results.jsList.pageChangeEventInteractionHandler(interactionElements);
|
||||
});
|
||||
}
|
||||
|
||||
// render results in table imported parameter is true
|
||||
queryRenderResults(result_json, true);
|
||||
|
||||
// live update of hits per page if hits per page value is changed
|
||||
let changeHitsPerPageBind = results.jsList.changeHitsPerPage.bind(results.jsList);
|
||||
hitsPerPageInputElement.onchange = changeHitsPerPageBind;
|
||||
|
||||
// live update of lr context per item if context value is changed
|
||||
contextPerItemElement.onchange = results.jsList.changeContext;
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,71 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% set full_width = True %}
|
||||
|
||||
{% block page_content %}
|
||||
|
||||
<div class="col s12">
|
||||
<p>This is an overview of all your imported results.</p>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content" id="results">
|
||||
<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 class="sort" data-sort="query">Query</th>
|
||||
<th class="sort" data-sort="match_count">Match count</th>
|
||||
<th class="sort" data-sort="corpus_name">Corpus name</th>
|
||||
<th class="sort" data-sort="corpus_creation_date">Corpus creation date</th>
|
||||
<th class="sort" data-sort="corpus_analysis_date">Corpus analysis date</th>
|
||||
<th class="sort" data-sort="corpus_type">Corpus type</th>
|
||||
<th>{# Actions #}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
<tr class="show-if-only-child">
|
||||
<td colspan="5">
|
||||
<span class="card-title"><i class="material-icons left">folder</i>Nothing here...</span>
|
||||
<p>No results yet imported.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ul class="pagination paginationBottom"></ul>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.result_import') }}">Import Results<i class="material-icons right">file_upload</i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Delete modals #}
|
||||
{% for result in results %}
|
||||
<div id="delete-result-{{ result.id }}-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Confirm result file deletion</h4>
|
||||
<p>Do you really want to delete the result file created on <i>{{ result.corpus_analysis_date }}</i>?
|
||||
<p>The file holds results for the query <i>{{ result.query }}</i>.</p>
|
||||
<p>The file will be permanently deleted!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||
<a class="btn modal-close red waves-effect waves-light" href="{{ url_for('results.result_delete', result_id=result.id) }}"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<script>
|
||||
var ressources = {{ results|tojson|safe }};
|
||||
var importedResultsList = new RessourceList("results", null, "result");
|
||||
importedResultsList.addRessources(ressources);
|
||||
RessourceList.modifyTooltips();
|
||||
</script>
|
||||
{% endblock %}
|
@ -37,8 +37,6 @@
|
||||
<ul class="pagination"></ul>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.result_import') }}">Import Results<i class="material-icons right">file_upload</i></a>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('results.results_overview') }}">Show Imported Results<i class="material-icons right">folder</i></a>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('corpora.add_corpus') }}">New corpus<i class="material-icons right">add</i></a>
|
||||
</div>
|
||||
</div>
|
||||
|
43
web/migrations/versions/c3827cddea6e_.py
Normal file
43
web/migrations/versions/c3827cddea6e_.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: c3827cddea6e
|
||||
Revises: 9d21b228d353
|
||||
Create Date: 2020-07-15 12:33:24.574579
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c3827cddea6e'
|
||||
down_revision = '9d21b228d353'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('results')
|
||||
op.drop_table('result_files')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('result_files',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('result_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column('filename', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
|
||||
sa.Column('dir', sa.VARCHAR(length=255), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['result_id'], ['results.id'], name='result_files_result_id_fkey'),
|
||||
sa.PrimaryKeyConstraint('id', name='result_files_pkey')
|
||||
)
|
||||
op.create_table('results',
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column('corpus_metadata', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], name='results_user_id_fkey'),
|
||||
sa.PrimaryKeyConstraint('id', name='results_pkey')
|
||||
)
|
||||
# ### end Alembic commands ###
|
@ -3,7 +3,7 @@ eventlet.monkey_patch() # noqa
|
||||
from app import create_app, db, socketio
|
||||
from app.models import (Corpus, CorpusFile, Job, JobInput, JobResult,
|
||||
NotificationData, NotificationEmailData, QueryResult,
|
||||
Result, ResultFile, Role, User)
|
||||
Role, User)
|
||||
from flask_migrate import Migrate, upgrade
|
||||
import os
|
||||
|
||||
@ -22,8 +22,6 @@ def make_shell_context():
|
||||
'NotificationData': NotificationData,
|
||||
'NotificationEmailData': NotificationEmailData,
|
||||
'QueryResult': QueryResult,
|
||||
'Result': Result,
|
||||
'ResultFile': ResultFile,
|
||||
'Role': Role,
|
||||
'User': User}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user