mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-11-15 01:05:42 +00:00
Remove old result package
This commit is contained in:
parent
0714070a9b
commit
ac3b20d304
@ -59,7 +59,4 @@ 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
|
||||||
|
@ -133,8 +133,6 @@ 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')
|
|
||||||
query_results = db.relationship('QueryResult',
|
query_results = db.relationship('QueryResult',
|
||||||
backref='creator',
|
backref='creator',
|
||||||
cascade='save-update, merge, delete',
|
cascade='save-update, merge, delete',
|
||||||
@ -660,59 +658,6 @@ class QueryResult(db.Model):
|
|||||||
return '<QueryResult {}>'.format(self.title)
|
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
|
' 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.
|
||||||
|
@ -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 {
|
class RessourceList extends List {
|
||||||
constructor(idOrElement, subscriberList, type, options={}) {
|
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!");
|
console.error("Unknown Type!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -124,16 +124,6 @@ RessourceList.dataMapper = {
|
|||||||
link: `/query_results/${query_result.id}`,
|
link: `/query_results/${query_result.id}`,
|
||||||
query: query_result.query_metadata.query,
|
query: query_result.query_metadata.query,
|
||||||
title: query_result.title}),
|
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
|
// Mapping for user entities shown in admin table
|
||||||
User: user => ({username: user.username,
|
User: user => ({username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
@ -277,56 +267,6 @@ RessourceList.options = {
|
|||||||
{data: ["id"]},
|
{data: ["id"]},
|
||||||
{name: "inspect-link", attr: "href"},
|
{name: "inspect-link", attr: "href"},
|
||||||
{name: "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
|
// User entity blueprint setting html strucuture per entity per row
|
||||||
// Link classes have to correspond with Links defined in the Mapping process
|
// Link classes have to correspond with Links defined in the Mapping process
|
||||||
User: {item: `<tr>
|
User: {item: `<tr>
|
||||||
|
@ -28,8 +28,6 @@
|
|||||||
<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('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>
|
<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>
|
||||||
|
@ -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>
|
<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('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>
|
<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>
|
||||||
|
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 import create_app, db, socketio
|
||||||
from app.models import (Corpus, CorpusFile, Job, JobInput, JobResult,
|
from app.models import (Corpus, CorpusFile, Job, JobInput, JobResult,
|
||||||
NotificationData, NotificationEmailData, QueryResult,
|
NotificationData, NotificationEmailData, QueryResult,
|
||||||
Result, ResultFile, Role, User)
|
Role, User)
|
||||||
from flask_migrate import Migrate, upgrade
|
from flask_migrate import Migrate, upgrade
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -22,8 +22,6 @@ def make_shell_context():
|
|||||||
'NotificationData': NotificationData,
|
'NotificationData': NotificationData,
|
||||||
'NotificationEmailData': NotificationEmailData,
|
'NotificationEmailData': NotificationEmailData,
|
||||||
'QueryResult': QueryResult,
|
'QueryResult': QueryResult,
|
||||||
'Result': Result,
|
|
||||||
'ResultFile': ResultFile,
|
|
||||||
'Role': Role,
|
'Role': Role,
|
||||||
'User': User}
|
'User': User}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user