mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	Remove old result package
This commit is contained in:
		@@ -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,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}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user