There will be a short maintenance break on Wed 27.10. at 12:00. Estimated time 30 minutes.

Commit 4958ffad authored by Ossi Laine's avatar Ossi Laine
Browse files

Merge branch 'dev' into 'master'

Dev

See merge request tithei/pet-rating!6
parents dcfe881e e96f5aed
......@@ -13,4 +13,9 @@ config.py
*.db
/embody
/app/static/lib
.env
/app/static/*.png
/app/static/embody_images
.vscode/
documentation
......@@ -15,7 +15,7 @@ from flask_cors import CORS, cross_origin
app = Flask(__name__)
CORS(app)
#CORS(app, resources={r"/*": {"cors_allowed_origins":"*"} } )
# CORS(app, resources={r"/*": {"cors_allowed_origins":"*"} } )
#app.config['BABEL_DEFAULT_LOCALE'] = 'fin'
#app.config['BABEL_TRANSLATION_DIRECTORIES'] ='C:/Users/Timo/git/pet-rating/app/translations'
......@@ -68,9 +68,6 @@ def get_locale():
"""
# Run flask app with socketIO
socketio = SocketIO()
socketio.init_app(app)
#mariabd mysql portti 3306 tarkista?
......@@ -81,6 +78,11 @@ migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
# Run flask app with socketIO
socketio = SocketIO(app, cors_allowed_origins="*")
# socketio = SocketIO()
socketio.init_app(app)
# Register blueprints
from .task.views import task_blueprint
from .experiment.views import experiment_blueprint
......
......@@ -3,8 +3,7 @@
<h1 class="container mt-5 display-4 text-center"><br>Add new embody picture:</h1>
<br>
<p class="lead">
Upload new embody image... instructions here for admins..
<p class="lead"> Upload new embody image. Submit without choosing file if you want to use default embody picture.
</p>
{% from "_formhelpers.html" import render_field %}
......@@ -12,8 +11,8 @@ Upload new embody image... instructions here for admins..
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="Background questions">image question/explanation:</label>
<textarea class="form-control" rows="5" id="embody_picture_text" name="question"></textarea>
<label for="Background questions">Image question/explanation:</label>
<textarea class="form-control" rows="5" id="embody_picture_text" name="question">Color the regions whose activity you feel increasing or getting stronger</textarea>
</div>
<div class="custom-file">
......@@ -23,7 +22,7 @@ Upload new embody image... instructions here for admins..
<hr>
<button type="submit" class="btn btn-primary submit-file" disabled>Submit</button>
<button type="submit" class="btn btn-primary">Submit</button>
<a class="btn btn-primary" href="{{ request.referrer }}" role="button">Cancel</a>
</form>
......
......@@ -36,7 +36,27 @@
<tr>
<td>Number of finished ratings:</td>
<td>{{ finished_ratings }}
<a class="btn btn-primary btn-info float-right" href="{{ url_for('download_csv', exp_id=exp.idexperiment) }}" role="button">Export results (csv)</a>
<button data-value="{{ exp.idexperiment }}" class="btn btn-primary float-right get-csv-results">
export results
</button>
<div id="export-link-container" class="hidden">
<a id="export-link" class="float-right"
href="{{ url_for('experiment.download_csv', exp_id=exp.idexperiment) }}" role="button"></a>
<p id="export-error"></p>
</div>
<div class="progress hidden">
<div id="export-results-bar" class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
</div>
</div>
</td>
</tr>
</tbody>
......@@ -49,21 +69,21 @@
<table class="table">
<thead>
<tr>
<th scope="col" nowrap>Question ID:</td>
<th scope="col" nowrap>Question:</th>
<th scope="col" nowrap>Left scale</th>
<th scope="col" nowrap>Right scale</th>
<th scope="col" nowrap>Question ID:</td>
<th scope="col" nowrap>Question:</th>
<th scope="col" nowrap>Left scale</th>
<th scope="col" nowrap>Right scale</th>
</tr>
</thead>
<tbody>
{% for q in question_headers %}
{% for q in question_headers %}
<tr>
<td>{{ q.idquestion }}</td>
<td>{{ q.question }}</td>
<td>{{ q.left }}</td>
<td>{{ q.right }}</td>
<td>{{ q.idquestion }}</td>
<td>{{ q.question }}</td>
<td>{{ q.left }}</td>
<td>{{ q.right }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
......@@ -74,29 +94,29 @@
<table class="table">
<thead>
<tr>
<th scope="col" nowrap>Participant ID:</th>
{% for page in pages_and_questions %}
{% for p in pages_and_questions[page] %}
<th scope="col" nowrap>{{ p[0]}}/{{ p[1]}}</th>
{% endfor %}
{% endfor %}
<th scope="col" nowrap>Participant ID:</th>
{% for page in pages_and_questions %}
{% for p in pages_and_questions[page] %}
<th scope="col" nowrap>{{ p[0]}}/{{ p[1]}}</th>
{% endfor %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for participant in participants_and_answers %}
{% for participant in participants_and_answers %}
<tr>
{% if participant == 'mean' %}
<td><b>{{ participant }}</b></td>
{% else %}
<td>{{ participant }}</td>
{% endif %}
{% for answer in participants_and_answers[participant] %}
<td>{{ answer }}</td>
{% endfor %}
{% if participant == 'mean' %}
<td><b>{{ participant }}</b></td>
{% else %}
<td>{{ participant }}</td>
{% endif %}
{% for answer in participants_and_answers[participant] %}
<td>{{ answer }}</td>
{% endfor %}
</tr>
{% endfor %}
......@@ -109,56 +129,60 @@
<table class="table">
<thead>
<tr>
<th style="width:25%;%" scope="col" nowrap>Stimulus:</td>
<th style="width:25%;%" scope="col" nowrap>Picture:</th>
<th style="width:25%;%" scope="col" nowrap>Description:</th>
<th style="width:25%;%" scope="col" nowrap></th>
<th style="width:25%;%" scope="col" nowrap>Stimulus:</td>
<th style="width:25%;%" scope="col" nowrap>Picture:</th>
<th style="width:25%;%" scope="col" nowrap>Description:</th>
<th style="width:25%;%" scope="col" nowrap></th>
</tr>
</thead>
<tbody>
{% for s in stimulus_headers %}
{% for embody_picture in embody_questions %}
<tr>
{% if s.type == 'text' %}
<td>{{ s.text }}</td>
{% elif s.type == 'picture' %}
<td><img src="/{{ s.media }}" class="thumbnail" /></td>
{% elif s.type == 'video' %}
<td>
<div class="embed-responsive embed-responsive-16by9 ">
<iframe class="embed-responsive-item thumbnail" src="/{{ s.media }}" allowFullScreen></iframe>
</div>
</td>
{% elif s.type == 'audio' %}
<td>
<div class="embed-responsive embed-responsive-16by9 ">
<iframe class="embed-responsive-item thumbnail" src="/{{ s.media }}" allowFullScreen></iframe>
</div>
</td>
{% else %}
<td>{{ s.text }}</td>
{% endif %}
<td><img src="{{ embody_picture.picture }}" class="thumbnail" /></td>
<td>{{ embody_picture.question }}</td>
<td>
<button data-value="{{ s.idpage }}-{{ embody_picture.idembody }}" class="btn btn-primary embody-get-drawing">
<span class="spinner-border spinner-border-sm hidden"></span>
Draw
</button>
</td>
</tr>
{% endfor %}
{% endfor %}
{% for s in stimulus_headers %}
{% for embody_picture in embody_questions %}
<tr>
{% if s.type == 'text' %}
<td>{{ s.text }}</td>
{% elif s.type == 'picture' %}
<td><img src="/{{ s.media }}" class="thumbnail" /></td>
{% elif s.type == 'video' %}
<td>
<div class="embed-responsive embed-responsive-16by9 ">
<iframe class="embed-responsive-item thumbnail" src="/{{ s.media }}" allowFullScreen></iframe>
</div>
</td>
{% elif s.type == 'audio' %}
<td>
<div class="embed-responsive embed-responsive-16by9 ">
<audio class="embed-responsive-item thumbnail" controls>
<source src="/{{ s.media }}">
Your browser does not support the audio element.
</audio>
</div>
</td>
{% else %}
<td>{{ s.text }}</td>
{% endif %}
<td><img src="{{ embody_picture.picture }}" class="thumbnail" /></td>
<td>{{ embody_picture.question }}</td>
<td>
<button data-value="{{ s.idpage }}-{{ embody_picture.idembody }}" class="btn btn-primary embody-get-drawing">
<span class="spinner-border spinner-border-sm hidden"></span>
Draw
</button>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<div class="progress hidden" id="plotted-image">
<div id="image-loading-progress" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<div id="image-loading-progress" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar"
aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" style="width: 0%">
<!-- Creating image... -->
</div>
</div>
......@@ -172,20 +196,20 @@
<table class="table">
<thead>
<tr>
<th scope="col" nowrap>Participant</th>
{% for bg in bg_questions %}
<th scope="col" nowrap>{{ bg.background_question }}</th>
{% endfor %}
<th scope="col" nowrap>Participant</th>
{% for bg in bg_questions %}
<th scope="col" nowrap>{{ bg.background_question }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for p in bg_answers_for_participants %}
{% for p in bg_answers_for_participants %}
<tr>
<td>{{ p }}</td>
{% for bg_answer in bg_answers_for_participants[p] %}
<td align="center">{{ bg_answer }}</td>
{% endfor %}
<td>{{ p }}</td>
{% for bg_answer in bg_answers_for_participants[p] %}
<td align="center">{{ bg_answer }}</td>
{% endfor %}
</tr>
{% endfor %}
......@@ -193,7 +217,9 @@
</tbody>
</table>
<script src="{{ url_for('static', filename='lib/js/socket.io.js') }}" ></script>
<script src="{{ url_for('static', filename='js/getDrawing.js') }}" ></script>
<script src="{{ url_for('static', filename='lib/js/socket.io.js') }}"></script>
<script src="{{ url_for('static', filename='js/urls.js') }}"></script>
<script src="{{ url_for('static', filename='js/getDrawing.js') }}"></script>
<script src="{{ url_for('static', filename='js/getCSV.js') }}"></script>
{% endblock %}
{% endblock %}
\ No newline at end of file
......@@ -211,7 +211,6 @@
</table>
<h1 class="container mt-5 display-4 text-left"><br>Add embody tool:</h1>
<table class="table">
......@@ -231,15 +230,13 @@
{% endif %}
</td>
<td>
<a class="btn btn-primary btn-block btn-sm btn-info" href="{{ url_for('experiment.add_embody', exp_id=exp_id, default=true) }}" role="button">Add default</a>
<a class="btn btn-primary btn-block btn-sm btn-info" href="{{ url_for('experiment.add_embody', exp_id=exp_id) }}" role="button">Add new picture</a>
</td>
</tr>
{% for embody_picture in embody_pictures %}
<tr>
<td>ID: {{ embody_picture.idembody }} <br> {{ embody_picture.question }}</td>
<td>ID: {{ embody_picture.idembody }} <br> {{ embody_picture.question }}</td>
<td><img src="{{ embody_picture.picture }}" class="thumbnail" /></td>
<td><a class="btn btn-primary btn-block btn-sm btn-dark" href="{{ url_for('experiment.remove_embody', exp_id=exp_id, idembody=embody_picture.idembody) }}" role="button">Remove</a></td>
</tr>
......
import os
import secrets
import json
from datetime import date
from tempfile import mkstemp
from flask_socketio import emit
from sqlalchemy import and_
from flask_login import login_required
from werkzeug import secure_filename
from flask import (
Flask,
render_template,
request,
session,
flash,
redirect,
url_for,
render_template,
request,
flash,
redirect,
url_for,
Blueprint,
jsonify
send_file
)
from wtforms import Form
from sqlalchemy import and_, update
from flask_login import login_required
from app import app, db
from app import app, db, socketio
from app.routes import APP_ROOT
from app.models import background_question, experiment
from app.models import background_question_answer
from app.models import page, question
from app.models import background_question_option
from app.models import answer_set, answer, forced_id
from app.models import user, trial_randomization
from app.models import trial_randomization
from app.models import embody_answer, embody_question
from app.forms import (
CreateBackgroundQuestionForm,
CreateQuestionForm, UploadStimuliForm, EditBackgroundQuestionForm,
EditQuestionForm, EditExperimentForm, UploadResearchBulletinForm,
EditPageForm, RemoveExperimentForm, GenerateIdForm,CreateEmbodyForm
CreateBackgroundQuestionForm,
CreateQuestionForm, UploadStimuliForm, EditBackgroundQuestionForm,
EditQuestionForm, EditExperimentForm, UploadResearchBulletinForm,
EditPageForm, RemoveExperimentForm, GenerateIdForm, CreateEmbodyForm
)
from werkzeug import secure_filename
from app.utils import get_mean_from_slider_answers, map_answers_to_questions, \
generate_csv
import embody_plot
#Stimuli upload folder setting
# Stimuli upload folder setting
#APP_ROOT = os.path.dirname(os.path.abspath(__file__))
experiment_blueprint = Blueprint("experiment", __name__,
template_folder='templates',
#static_folder='static',
url_prefix='/experiment')
experiment_blueprint = Blueprint("experiment", __name__,
template_folder='templates',
# static_folder='static',
url_prefix='/experiment')
# Set sliders/embody:
DEFAULT_EMBODY_PICTURE = '/static/img/dummy_600.png'
DEFAULT_EMBODY_QUESTION = 'Color the regions whose activity you feel increasing or getting stronger'
@experiment_blueprint.route('/view')
@login_required
def view():
#crap:3lines
# crap:3lines
exp_id = request.args.get('exp_id', None)
media = page.query.filter_by(experiment_idexperiment=exp_id).all()
# stimulus type
# stimulus type
mtype = page.query.filter_by(experiment_idexperiment=exp_id).first()
#experiment info
experiment_info = experiment.query.filter_by(idexperiment = exp_id).all()
#background questions
# experiment info
experiment_info = experiment.query.filter_by(idexperiment=exp_id).all()
# background questions
questions_and_options = {}
questions = background_question.query.filter_by(
experiment_idexperiment=exp_id).all()
......@@ -79,7 +81,7 @@ def view():
questions1 = questions_and_options
#sliderset
# sliderset
categories_and_scales = {}
categories = question.query.filter_by(experiment_idexperiment=exp_id).all()
......@@ -107,51 +109,59 @@ def remove():
exp_status = experiment.query.filter_by(idexperiment=exp_id).first()
if exp_status.status != 'Hidden':
flash("Experiment is public. Cannot modify structure.")
return redirect(url_for('experiment.view', exp_id=exp_id))
else:
form = RemoveExperimentForm(request.form)
if request.method == 'POST' and form.validate():
if form.remove.data == 'DELETE':
#This removes all experiment data from the database!
#Remove research bulletin if it exists
empty_filevariable = experiment.query.filter_by(idexperiment=exp_id).first()
# This removes all experiment data from the database!
# Remove research bulletin if it exists
empty_filevariable = experiment.query.filter_by(
idexperiment=exp_id).first()
if empty_filevariable.research_notification_filename is not None:
target = os.path.join(APP_ROOT, empty_filevariable.research_notification_filename)
if os.path.exists(target):
target = os.path.join(
APP_ROOT, empty_filevariable.research_notification_filename)
if os.path.exists(target):
os.remove(target)
#Tables
remove_forced_id = forced_id.query.filter_by(experiment_idexperiment=exp_id).all()
# Tables
remove_forced_id = forced_id.query.filter_by(
experiment_idexperiment=exp_id).all()
remove_rows(remove_forced_id)
#background_question_option & background_question & background question answers:
remove_background_question = background_question.query.filter_by(experiment_idexperiment=exp_id).all()
#Remove all background questions and all answers given to each bg question
# background_question_option & background_question & background question answers:
remove_background_question = background_question.query.filter_by(
experiment_idexperiment=exp_id).all()
# Remove all background questions and all answers given to each bg question
for a in range(len(remove_background_question)):
remove_background_question_option = background_question_option.query.filter_by(background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
remove_background_question_option = background_question_option.query.filter_by(
background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
remove_rows(remove_background_question_option)
remove_background_question_answers = background_question_answer.query.filter_by(background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
remove_background_question_answers = background_question_answer.query.filter_by(
background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
remove_rows(remove_background_question_answers)
db.session.delete(remove_background_question[a])
db.session.commit()
#Remove all questions and answers
remove_question = question.query.filter_by(experiment_idexperiment=exp_id).all()
# Remove all questions and answers
remove_question = question.query.filter_by(
experiment_idexperiment=exp_id).all()
for a in range(len(remove_question)):
remove_question_answers = answer.query.filter_by(question_idquestion=remove_question[a].idquestion).all()
remove_question_answers = answer.query.filter_by(
question_idquestion=remove_question[a].idquestion).all()
remove_rows(remove_question_answers)
db.session.delete(remove_question[a])
db.session.commit()
......@@ -160,54 +170,58 @@ def remove():
remove_embody_answers = embody_answer.query.filter(embody_answer.page_idpage.in_(list(map(
lambda x: x[0], page.query.with_entities(page.idpage).filter_by(experiment_idexperiment=exp_id).all())))).all()
remove_rows(remove_embody_answers)
remove_embody_questions = embody_question.query.filter_by(experiment_idexperiment=exp_id).all()
remove_embody_questions = embody_question.query.filter_by(
experiment_idexperiment=exp_id).all()
for a in range(len(remove_embody_questions)):
target = APP_ROOT + remove_embody_questions[a].picture
if os.path.exists(target) and DEFAULT_EMBODY_PICTURE != remove_embody_questions[a].picture:
if os.path.exists(target) and DEFAULT_EMBODY_PICTURE != remove_embody_questions[a].picture:
os.remove(target)
remove_rows(remove_embody_questions)
#Remove all pages and datafiles
remove_pages = page.query.filter_by(experiment_idexperiment=exp_id).all()
# Remove all pages and datafiles
remove_pages = page.query.filter_by(
experiment_idexperiment=exp_id).all()
for a in range(len(remove_pages)):
if remove_pages[a].type != 'text':
target = os.path.join(APP_ROOT, remove_pages[a].media)
if os.path.exists(target):
if os.path.exists(target):
os.remove(target)
#Now that the files are removed we can delete the page
# Now that the files are removed we can delete the page
db.session.delete(remove_pages[a])
db.session.commit()
#Remove all answer_sets and trial_randomization orders
remove_answer_set = answer_set.query.filter_by(experiment_idexperiment=exp_id).all()
# Remove all answer_sets and trial_randomization orders
remove_answer_set = answer_set.query.filter_by(
experiment_idexperiment=exp_id).all()
for a in range(len(remove_answer_set)):
remove_trial_randomizations = trial_randomization.query.filter_by(answer_set_idanswer_set=remove_answer_set[a].idanswer_set).all()
remove_trial_randomizations = trial_randomization.query.filter_by(
answer_set_idanswer_set=remove_answer_set[a].idanswer_set).all()
remove_rows(remove_trial_randomizations)
db.session.delete(remove_answer_set[a])
db.session.commit()
#Remove experiment table
remove_experiment = experiment.query.filter_by(idexperiment=exp_id).first()
# Remove experiment table
remove_experiment = experiment.query.filter_by(
idexperiment=exp_id).first()
db.session.delete(remove_experiment)
db.session.commit()
# Remove empty directories
os.rmdir(APP_ROOT + '/static/embody_images/' + str(exp_id))
os.rmdir(APP_ROOT + '/static/experiment_stimuli/' + str(exp_id))