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>
......
This diff is collapsed.
from sqlalchemy import and_
from flask import session
from app import db
from sqlalchemy import Column, Integer, String, Text, Boolean
from flask_wtf import FlaskForm
from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from app import login
......@@ -15,9 +17,10 @@ class background_question(db.Model):
__tablename__ = "background_question"
idbackground_question = db.Column(db.Integer, primary_key=True)
background_question = db.Column(db.String(120))
answers = db.relationship('background_question_answer', backref='question', lazy='dynamic')
answers = db.relationship(
'background_question_answer', backref='question', lazy='dynamic')
experiment_idexperiment = db.Column(db.Integer)
def __repr__(self):
return "<idbackground_question = '%s', background_question = '%s'>" % (self.idbackground_question, self.background_question)
......@@ -25,11 +28,12 @@ class background_question(db.Model):
class background_question_option(db.Model):
__tablename__ = "background_question_option"
idbackground_question_option = db.Column(db.Integer, primary_key=True)
background_question_idbackground_question = db.Column(db.Integer, db.ForeignKey('background_question.idbackground_question'))
background_question_idbackground_question = db.Column(
db.Integer, db.ForeignKey('background_question.idbackground_question'))
option = db.Column(db.String(120))
def __repr__(self):
return "<idbackground_question_option = '%s', background_question_idbackground_question = '%s', option = '%s'>" % (self.idbackground_question_option, self.background_question_idbackground_question, self.option)
return "<idbackground_question_option = '%s', background_question_idbackground_question = '%s', option = '%s'>" % (self.idbackground_question_option, self.background_question_idbackground_question, self.option)
class experiment (db.Model):
......@@ -51,7 +55,7 @@ class experiment (db.Model):
consent_text = db.Column(db.Text, index=True)
use_forced_id = db.Column(db.String(120))
embody_enabled = db.Column(db.Boolean, unique=False, default=False)
def __repr__(self):
return "<idexperiment = '%s', name='%s', instruction='%s', directoryname='%s', language='%s', status='%s', randomization='%s', short_instruction='%s', single_sentence_instruction='%s', is_archived='%s', creator_name='%s', research_notification_filename='%s', creation_time='%s', stimulus_size='%s', consent_text='%s', use_forced_id='%s', embody_enabled='%s'>" % (self.idexperiment, self.name, self.instruction, self.directoryname, self.language, self.status, self.randomization, self.short_instruction, self.single_sentence_instruction, self.is_archived, self.creator_name, self.research_notification_filename, self.creation_time, self.stimulus_size, self.consent_text, self.use_forced_id, self.embody_enabled)
......@@ -59,13 +63,16 @@ class experiment (db.Model):
class answer_set (db.Model):
__tablename__ = "answer_set"
idanswer_set = db.Column(db.Integer, primary_key=True)
experiment_idexperiment = db.Column(db.Integer, db.ForeignKey('experiment.idexperiment'))
experiment_idexperiment = db.Column(
db.Integer, db.ForeignKey('experiment.idexperiment'))
session = db.Column(db.String(120))
agreement = db.Column(db.String(120))
answer_counter = db.Column(db.Integer)
answer_type = db.Column(db.String(120))
registration_time = db.Column(db.DateTime, index=True, default=datetime.utcnow)
last_answer_time = db.Column(db.DateTime, index=True, default=datetime.utcnow)
registration_time = db.Column(
db.DateTime, index=True, default=datetime.utcnow)
last_answer_time = db.Column(
db.DateTime, index=True, default=datetime.utcnow)
def __repr__(self):
return "<idanswer_set = '%s', experiment_idexperiment = '%s', session = '%s', agreement = '%s', answer_counter = '%s', registration_time = '%s', last_answer_time = '%s'>" % (self.idanswer_set, self.experiment_idexperiment, self.session, self.agreement, self.answer_counter, self.registration_time, self.last_answer_time)
......@@ -74,17 +81,20 @@ class answer_set (db.Model):
class background_question_answer(db.Model):
__tablename__ = "background_question_answer"
idbackground_question_answer = db.Column(db.Integer, primary_key=True)
answer_set_idanswer_set = db.Column(db.Integer, db.ForeignKey('answer_set.idanswer_set'))
answer_set_idanswer_set = db.Column(
db.Integer, db.ForeignKey('answer_set.idanswer_set'))
answer = db.Column(db.String(120))
background_question_idbackground_question = db.Column(db.Integer, db.ForeignKey('background_question.idbackground_question'))
background_question_idbackground_question = db.Column(
db.Integer, db.ForeignKey('background_question.idbackground_question'))
def __repr__(self):
return "<idbackground_question_answer = '%s', answer_set_idanswer_set = '%s', answer = '%s', background_question_idbackground_question = '%s'>" % (self.idbackground_question_answer, self.answer_set_idanswer_set, self.answer, self.background_question_idbackground_question)
return "<idbackground_question_answer = '%s', answer_set_idanswer_set = '%s', answer = '%s', background_question_idbackground_question = '%s'>" % (self.idbackground_question_answer, self.answer_set_idanswer_set, self.answer, self.background_question_idbackground_question)
def background_question_answer_query():
return background_question_answer.query
"""
class ChoiceForm(FlaskForm):
opts = QuerySelectField(query_factory=background_question_answer_query, allow_blank=True)
......@@ -98,30 +108,33 @@ vastaukset = u.answers.all()
class question (db.Model):
__tablename__ = "question"
idquestion = db.Column(db.Integer, primary_key=True)
experiment_idexperiment = db.Column(db.Integer, db.ForeignKey('experiment.idexperiment'))
experiment_idexperiment = db.Column(
db.Integer, db.ForeignKey('experiment.idexperiment'))
question = db.Column(db.String(120))
left = db.Column(db.String(120))
right = db.Column(db.String(120))
def __repr__(self):
return "<idquestion = '%s', experiment_idexperiment = '%s', question = '%s', left = '%s', right = '%s'>" % (self.idquestion, self.experiment_idexperiment, self.question, self.left, self.right)
return "<idquestion = '%s', experiment_idexperiment = '%s', question = '%s', left = '%s', right = '%s'>" % (self.idquestion, self.experiment_idexperiment, self.question, self.left, self.right)
class embody_question (db.Model):
__tablename__ = "embody_question"
idembody = db.Column(db.Integer, primary_key=True)
experiment_idexperiment = db.Column(db.Integer, db.ForeignKey('experiment.idexperiment'))
experiment_idexperiment = db.Column(
db.Integer, db.ForeignKey('experiment.idexperiment'))
picture = db.Column(db.Text)
question = db.Column(db.Text)
def __repr__(self):
return "<idembody = '%s', experiment_idexperiment = '%s', picture = '%s', question = '%s'>" % (self.idembody, self.experiment_idexperiment, self.picture, self.question )
return "<idembody = '%s', experiment_idexperiment = '%s', picture = '%s', question = '%s'>" % (self.idembody, self.experiment_idexperiment, self.picture, self.question)
class page (db.Model):
__tablename__ = "page"
idpage = db.Column(db.Integer, primary_key=True)
experiment_idexperiment = db.Column(db.Integer, db.ForeignKey('experiment.idexperiment'))
experiment_idexperiment = db.Column(
db.Integer, db.ForeignKey('experiment.idexperiment'))
type = db.Column(db.String(120), index=True)
text = db.Column(db.Text)
media = db.Column(db.String(120), index=True)
......@@ -133,11 +146,19 @@ class page (db.Model):
class answer (db.Model):
__tablename__ = "answer"
idanswer = db.Column(db.Integer, primary_key=True)
question_idquestion = db.Column(db.Integer, db.ForeignKey('question.idquestion'))
answer_set_idanswer_set = db.Column(db.Integer, db.ForeignKey('answer_set.idanswer_set'))
question_idquestion = db.Column(
db.Integer, db.ForeignKey('question.idquestion'))
answer_set_idanswer_set = db.Column(
db.Integer, db.ForeignKey('answer_set.idanswer_set'))
answer = db.Column(db.String(120))
page_idpage = db.Column(db.Integer, db.ForeignKey('page.idpage'))
def question(self):
return int(self.question_idquestion)
def result(self):
return int(self.answer)
def __repr__(self):
return "<idanswer = '%s', question_idquestion = '%s', answer_set_idanswer_set = '%s', answer = '%s', page_idpage = '%s'>" % (self.idanswer, self.question_idquestion, self.answer_set_idanswer_set, self.answer, self.page_idpage)
......@@ -145,11 +166,19 @@ class answer (db.Model):
class embody_answer (db.Model):
__tablename__ = "embody_answer"
idanswer = db.Column(db.Integer, primary_key=True)
answer_set_idanswer_set = db.Column(db.Integer, db.ForeignKey('answer_set.idanswer_set'))
answer_set_idanswer_set = db.Column(
db.Integer, db.ForeignKey('answer_set.idanswer_set'))
page_idpage = db.Column(db.Integer, db.ForeignKey('page.idpage'))
embody_question_idembody = db.Column(db.Integer, db.ForeignKey('embody_question.idembody'))
embody_question_idembody = db.Column(
db.Integer, db.ForeignKey('embody_question.idembody'))
coordinates = db.Column(db.Text)
def question(self):
return self.embody_question_idembody
def result(self):
return self.coordinates
def __repr__(self):
return "<idanswer = '%s', answer_set_idanswer_set = '%s', coordinates = '%s', page_idpage = '%s', embody_question_idembody='%s' >" % (self.idanswer, self.answer_set_idanswer_set, self.coordinates, self.page_idpage, self.embody_question_idembody)
......@@ -161,17 +190,24 @@ class trial_randomization (db.Model):
randomized_idpage = db.Column(db.Integer)
answer_set_idanswer_set = db.Column(db.Integer)
experiment_idexperiment = db.Column(db.Integer)
def __repr__(self):
return "<idtrial_randomization = '%s', page_idpage = '%s', randomized_idpage = '%s', answer_set_idanswer_set = '%s', experiment_idexperiment = '%s'>" % (self.idtrial_randomization, self.page_idpage, self.randomized_idpage, self.answer_set_idanswer_set, self.experiment_idexperiment)
@classmethod
def get_randomized_page(cls, page_id):
return cls.query.filter(and_(
cls.answer_set_idanswer_set == session['answer_set'],
cls.page_idpage == page_id)).first()
class forced_id (db.Model):
__tablename__ = "forced_id"
idforced_id = db.Column(db.Integer, primary_key=True)
experiment_idexperiment = db.Column(db.Integer, db.ForeignKey('experiment.idexperiment'))
pregenerated_id = db.Column(db.String(120))
experiment_idexperiment = db.Column(
db.Integer, db.ForeignKey('experiment.idexperiment'))
pregenerated_id = db.Column(db.String(120))
def __repr__(self):
return "<idforced_id = '%s', experiment_idexperiment = '%s', pregenerated_id = '%s'>" % (self.idforced_id, self.experiment_idexperiment, self.pregenerated_id)
......@@ -184,16 +220,15 @@ class user(UserMixin, db.Model):
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<user {}>'.format(self.username)
return '<user {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@login.user_loader
def load_user(id):
return user.query.get(int(id))
\ No newline at end of file
This diff is collapsed.
body{
background-color: #000000;
}
\ No newline at end of file
......@@ -75,4 +75,20 @@ body {
max-width: 90%;
}
}
\ No newline at end of file
}
.stimulus img {
height: 100%;
object-fit: contain;
}
#export-link-container {
margin-top: 20px;
padding: 10px;
}
#export-error {
float:right;
color:red;
}
......@@ -59,8 +59,14 @@ $(document).ready(function() {
// Click handlers
canvas.mousedown(function(e){
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;