Skip to content
Snippets Groups Projects
Commit 18116235 authored by Stephan Porada's avatar Stephan Porada :speech_balloon:
Browse files

Add function to import exported results and view them after the import

parent a997fbe0
No related branches found
No related tags found
No related merge requests found
Showing
with 668 additions and 30 deletions
...@@ -55,4 +55,7 @@ def create_app(config_name): ...@@ -55,4 +55,7 @@ def create_app(config_name):
from .services import services as services_blueprint from .services import services as services_blueprint
app.register_blueprint(services_blueprint, url_prefix='/services') 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 return app
...@@ -2,9 +2,9 @@ from flask_table import Table, Col, LinkCol ...@@ -2,9 +2,9 @@ from flask_table import Table, Col, LinkCol
class AdminUserTable(Table): class AdminUserTable(Table):
""" '''
Declares the table describing colum by column. Declares the table describing colum by column.
""" '''
classes = ['highlight', 'responsive-table'] classes = ['highlight', 'responsive-table']
username = Col('Username', column_html_attrs={'class': 'username'}, username = Col('Username', column_html_attrs={'class': 'username'},
th_html_attrs={'class': 'sort', th_html_attrs={'class': 'sort',
...@@ -28,9 +28,9 @@ class AdminUserTable(Table): ...@@ -28,9 +28,9 @@ class AdminUserTable(Table):
class AdminUserItem(object): class AdminUserItem(object):
""" '''
Describes one item like one row per table. Describes one item like one row per table.
""" '''
def __init__(self, username, email, role_id, confirmed, id): def __init__(self, username, email, role_id, confirmed, id):
self.username = username self.username = username
......
...@@ -129,6 +129,8 @@ class User(UserMixin, db.Model): ...@@ -129,6 +129,8 @@ class User(UserMixin, db.Model):
cascade='save-update, merge, delete') cascade='save-update, merge, delete')
jobs = db.relationship('Job', backref='creator', lazy='dynamic', jobs = db.relationship('Job', backref='creator', lazy='dynamic',
cascade='save-update, merge, delete') cascade='save-update, merge, delete')
results = db.relationship('Result', backref='creator', lazy='dynamic',
cascade='save-update, merge, delete')
def to_dict(self): def to_dict(self):
return {'id': self.id, return {'id': self.id,
...@@ -532,6 +534,34 @@ class Corpus(db.Model): ...@@ -532,6 +534,34 @@ class Corpus(db.Model):
return '<Corpus {corpus_title}>'.format(corpus_title=self.title) return '<Corpus {corpus_title}>'.format(corpus_title=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')
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))
''' '''
' Flask-Login is told to use the application’s custom anonymous user by setting ' Flask-Login is told to use the application’s custom anonymous user by setting
' its class in the login_manager.anonymous_user attribute. ' its class in the login_manager.anonymous_user attribute.
......
from flask import Blueprint
results = Blueprint('results', __name__)
from . import views # noqa
\ No newline at end of file
from . import results
from ..models import Result
from flask import abort, render_template, current_app, request
from flask_login import current_user, login_required
from ..corpora.forms import DisplayOptionsForm
import json
import os
@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 importe 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))
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,
result=result,
result_json=result_json,
title='Result Insepct')
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)
from flask_table import Table, Col, DatetimeCol, LinkCol
class ResultTable(Table):
'''
Declares the Table showing results. Declaration is column by column.
'''
classes = ['highlight', 'responsive-table']
query = Col('Query', column_html_attrs={'class': 'query'},
th_html_attrs={'class': 'sort',
'data-sort': 'query'})
match_count = Col('Match count', column_html_attrs={'class':
'match-count'},
th_html_attrs={'class': 'sort',
'data-sort': 'match-count'})
corpus_name = Col('Corpus name', column_html_attrs={'class':
'corpus-name'},
th_html_attrs={'class': 'sort',
'data-sort': 'corpus-name'})
corpus_creation_date = DatetimeCol('Corpus creation date',
column_html_attrs={'class':
'corpus-creation- date'}, # noqa
th_html_attrs={'class': 'sort',
'data-sort':
'corpus-creation-date'},
datetime_format='dd/MM/yyyy, HH:mm:ss a') # noqa
corpus_analysis_date = DatetimeCol('Date of result creation',
column_html_attrs={'class':
'corpus-analysis-data'}, # noqa
th_html_attrs={'class': 'sort',
'data-sort':
'corpus-analysis-data'},
datetime_format='dd/MM/yyyy, HH:mm:ss a') # noqa
corpus_type = Col('Result Type',
column_html_attrs={'class':
'corpus-type'},
th_html_attrs={'class': 'sort',
'data-sort':
'corpus-type'})
details = LinkCol('Details', 'results.result_details',
url_kwargs=dict(result_id='id'),
anchor_attrs={'class': 'waves-effect waves-light btn-floating'}, # noqa
text_fallback='<i class="material-icons">info_outline</i>') # noqa
inspect = LinkCol('Inspect', 'results.result_inspect',
url_kwargs=dict(result_id='id'),
anchor_attrs={'class': 'waves-effect waves-light btn-floating'}, # noqa
text_fallback='<i class="material-icons">search</i>') # noqa
# TODO: Maybe somehow fix taht there are two columns fpr two action buttons
# Or maybe just get rid of flask tables?
class ResultItem(object):
'''
Describes one result item row.
'''
def __init__(self, query, match_count, corpus_name, corpus_creation_date,
corpus_analysis_date, corpus_type, id):
self.query = query
self.match_count = match_count
self.corpus_name = corpus_name
self.corpus_creation_date = corpus_creation_date
self.corpus_analysis_date = corpus_analysis_date
self.corpus_type = corpus_type
self.id = id
from flask import (abort, current_app, flash, make_response, render_template, from flask import (abort, current_app, flash, make_response, render_template,
url_for) url_for)
from flask_login import current_user, login_required from flask_login import current_user, login_required
from .forms import ImportResultsForm
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from . import services from . import services
from .. import db from .. import db
from ..jobs.forms import AddFileSetupJobForm, AddNLPJobForm, AddOCRJobForm from ..jobs.forms import AddFileSetupJobForm, AddNLPJobForm, AddOCRJobForm
from ..models import Job, JobInput from ..models import Job, JobInput, Result, ResultFile, User
from .tables import ResultTable, ResultItem
import json import json
import os import os
import html
from datetime import datetime
SERVICES = {'corpus_analysis': {'name': 'Corpus analysis'}, SERVICES = {'corpus_analysis': {'name': 'Corpus analysis'},
...@@ -81,3 +85,84 @@ def service(service): ...@@ -81,3 +85,84 @@ def service(service):
return render_template('services/{}.html.j2'.format(service), return render_template('services/{}.html.j2'.format(service),
title=SERVICES[service]['name'], title=SERVICES[service]['name'],
add_job_form=add_job_form) add_job_form=add_job_form)
@services.route('/import_results', methods=['GET', 'POST'])
@login_required
def import_results():
'''
View to import one json result file. Uses the ImportReultFileForm.
'''
# TODO: Build in a check if uploaded json is actually a result file and
# not something different
# Add the possibility to add several result files at once.
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)
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)
import_results_form.file.data.save(abs_file_path)
# Saves all needed metadata entries in one json field
with open(abs_file_path, 'r') as f:
corpus_metadata = json.load(f)
del corpus_metadata['matches']
del corpus_metadata['cpos_lookup']
result_file = ResultFile(
result_id=result.id,
dir=dir,
filename=import_results_form.file.data.filename)
result.corpus_metadata = corpus_metadata
db.session.add(result_file)
db.session.commit()
flash('Result file added!', 'result')
return make_response(
{'redirect_url': url_for('services.results')},
201)
return render_template('services/import_results.html.j2',
import_results_form=import_results_form,
title='Add corpus file')
@services.route('/results')
@login_required
def results():
'''
Shows an overview of imported results.
'''
# get all results of current user
results = User.query.get(current_user.id).results
# create table row for every result#
def __p_time(time_str):
return datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S.%f')
items = [ResultItem(r.corpus_metadata['query'],
r.corpus_metadata['match_count'],
r.corpus_metadata['corpus_name'],
__p_time(r.corpus_metadata['corpus_creation_date']),
__p_time(r.corpus_metadata['corpus_analysis_date']),
r.corpus_metadata['corpus_type'],
r.id) for r in results]
# create table with items and save it as html
table = html.unescape(ResultTable(items).__html__())
# add class=list to table body with string replacement
table = table.replace('tbody', 'tbody class=list', 1)
return render_template('services/results.html.j2',
title='Imported Results',
table=table)
...@@ -29,4 +29,32 @@ class InteractionElement { ...@@ -29,4 +29,32 @@ class InteractionElement {
let boundedCallback = callback["function"].bind(callback.bindThis); let boundedCallback = callback["function"].bind(callback.bindThis);
return boundedCallback; return boundedCallback;
} }
}
\ No newline at end of file static onChangeExecute(interactionElements) {
// checks if a change for every interactionElement happens and executes
// the callbacks accordingly
// TODO: This function scould be a static function of the Class InteractionElements
// This class does not exist yet. The Class InteractionElements should hold
// a list of InteractionElement objects. onChangeExecute loops over InteractionElements
// and executes the callbacks as mentioned accordingly. An additional
// InteractionElements Class is logically right but also makes things a little more
// complex. It is not yet decided.
for (let interaction of interactionElements) {
if (interaction.checkStatus) {
interaction.element.addEventListener("change", (event) => {
if (event.target.checked) {
let f_on = interaction.bindThisToCallback("on");
let args_on = interaction.callbacks.on.args;
f_on(...args_on);
} else if (!event.target.checked){
let f_off = interaction.bindThisToCallback("off");
let args_off = interaction.callbacks.off.args;
f_off(...args_off);
}
});
} else {
continue
}
};
}
}
...@@ -63,7 +63,7 @@ function querySetup(payload) { ...@@ -63,7 +63,7 @@ function querySetup(payload) {
// This callback is called on socket.on "query_results" // This callback is called on socket.on "query_results"
// this handels the incoming result chunks // this handels the incoming result chunks
function queryRenderResults(payload) { function queryRenderResults(payload, imported=false) {
let resultItems; // array of built html result items row element let resultItems; // array of built html result items row element
// This is called when results are transmitted and being recieved // This is called when results are transmitted and being recieved
console.log("Current recieved chunk:", payload.chunk); console.log("Current recieved chunk:", payload.chunk);
...@@ -102,18 +102,23 @@ function queryRenderResults(payload) { ...@@ -102,18 +102,23 @@ function queryRenderResults(payload) {
console.log("Results recieved:", results.data); console.log("Results recieved:", results.data);
// upate progress status // upate progress status
progress = payload.progress; // global declaration progress = payload.progress; // global declaration
if (progress === 100) { if (progress === 100 && !imported) {
queryResultsProgressElement.classList.add("hide"); queryResultsProgressElement.classList.add("hide");
queryResultsUserFeedbackElement.classList.add("hide"); queryResultsUserFeedbackElement.classList.add("hide");
queryResultsExportElement.classList.remove("disabled"); queryResultsExportElement.classList.remove("disabled");
addToSubResultsElement.removeAttribute("disabled"); addToSubResultsElement.removeAttribute("disabled");
results.jsList.activateInspect();
// inital expert mode check and sub results activation // inital expert mode check and sub results activation
results.jsList.activateInspect();
if (addToSubResultsElement.checked) { if (addToSubResultsElement.checked) {
results.jsList.activateAddToSubResults(); results.jsList.activateAddToSubResults();
} }
if (expertModeSwitchElement.checked) { if (expertModeSwitchElement.checked) {
results.jsList.expertModeOn("query-display"); results.jsList.expertModeOn("query-display");
} }
} else if (imported) {
results.jsList.activateInspect();
if (expertModeSwitchElement.checked) {
results.jsList.expertModeOn("query-display");
}
} }
} }
\ No newline at end of file
...@@ -200,7 +200,6 @@ ...@@ -200,7 +200,6 @@
</tbody> </tbody>
</table> </table>
<ul class="pagination paginationBottom"></ul> <ul class="pagination paginationBottom"></ul>
</div> </div>
</div> </div>
</div> </div>
...@@ -567,23 +566,7 @@ ...@@ -567,23 +566,7 @@
// checks if a change for every interactionElement happens and executes // checks if a change for every interactionElement happens and executes
// the callbacks accordingly // the callbacks accordingly
for (let interaction of interactionElements) { InteractionElement.onChangeExecute(interactionElements);
if (interaction.checkStatus) {
interaction.element.addEventListener("change", (event) => {
if (event.target.checked) {
let f_on = interaction.bindThisToCallback("on");
let args_on = interaction.callbacks.on.args;
f_on(...args_on);
} else if (!event.target.checked){
let f_off = interaction.bindThisToCallback("off");
let args_off = interaction.callbacks.off.args;
f_off(...args_off);
}
});
} else {
continue
}
};
// eventListener if pagination is used to apply new context size to new page // eventListener if pagination is used to apply new context size to new page
// and also activate inspect match if progress is 100 // and also activate inspect match if progress is 100
......
...@@ -27,13 +27,13 @@ ...@@ -27,13 +27,13 @@
<div class="row"> <div class="row">
<div class="col s12 m6"> <div class="col s12 m6">
<div class="input-field"> <div class="input-field">
<input disabled value="{{ corpus.creation_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}" id="creation-date" type="text" class="validate"> <input disabled value="{{ corpus.creation_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}" id="creation-date" type="text" class="validate">
<label for="creation-date">Creation date</label> <label for="creation-date">Creation date</label>
</div> </div>
</div> </div>
<div class="col s12 m6"> <div class="col s12 m6">
<div class="input-field"> <div class="input-field">
<input disabled value="{{ corpus.last_edited_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}" id="last_edited_date" type="text" class="validate"> <input disabled value="{{ corpus.last_edited_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}" id="last_edited_date" type="text" class="validate">
<label for="creation-date">Last edited</label> <label for="creation-date">Last edited</label>
</div> </div>
</div> </div>
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
<div class="col s12 m6"> <div class="col s12 m6">
<div class="input-field"> <div class="input-field">
<input disabled id="creation-date" type="text" value="{{ job.creation_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}"> <input disabled id="creation-date" type="text" value="{{ job.creation_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}">
<label for="creation-date">Creation date</label> <label for="creation-date">Creation date</label>
</div> </div>
</div> </div>
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
<ul class="pagination"></ul> <ul class="pagination"></ul>
</div> </div>
<div class="card-action right-align"> <div class="card-action right-align">
<a class="waves-effect waves-light btn" href="{{ url_for('services.results') }}">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> <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>
</div> </div>
......
{% 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" href="{{ url_for('services.import_results') }}">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 %}
{% 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>
<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
// ###### 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;
// ###### 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");
// 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", () => {
// ###### 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 %}
\ No newline at end of file
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
<ul class="pagination"></ul> <ul class="pagination"></ul>
</div> </div>
<div class="card-action right-align"> <div class="card-action right-align">
<a class="waves-effect waves-light btn" href="{{ url_for('services.results') }}">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> <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>
</div> </div>
......
{% 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 %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment