Commit 3940ede0 authored by Ossi Laine's avatar Ossi Laine
Browse files

Merge branch 'bug-handle-randomization-when-exporting-results' into 'dev'

Bug handle randomization when exporting results

See merge request tithei/pet-rating!3
parents bcb340f1 45404c92
......@@ -921,7 +921,9 @@ def statistics():
slider_answers = {}
for participant in participants:
if participant.answer_counter > 0:
answers = answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set).all()
answers = answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set)\
.order_by(answer.page_idpage)\
.all()
slider_answers[participant.session] = [ a.answer for a in answers]
slider_answers['mean'] = get_mean_from_slider_answers(slider_answers)
......
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,8 +146,10 @@ 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'))
......@@ -145,9 +160,11 @@ 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 __repr__(self):
......@@ -161,17 +178,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 +208,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
import csv
import os
import random
import secrets
from datetime import datetime
import tempfile
from datetime import datetime, date
import json
from flask import (
Flask,
render_template,
request,
session,
flash,
redirect,
url_for,
Blueprint,
send_file
)
from flask import (render_template,
request,
session,
flash,
redirect,
url_for)
from sqlalchemy import and_
from flask_login import current_user, login_user, logout_user, login_required
from flask_babel import Babel, _, lazy_gettext as _l
from app import app, db, babel
from app import app, db
from app.models import background_question, experiment
from app.models import background_question_answer
from app.models import page, question, embody_question, embody_answer
......@@ -32,21 +21,23 @@ 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.forms import LoginForm, RegisterForm, StartWithIdForm
from app.utils import saved_data_as_file
#Stimuli upload folder setting
# Stimuli upload folder setting
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
@app.route('/')
@app.route('/index')
def index():
experiments = experiment.query.all()
if session:
flash("")
else:
#flash("sessio ei voimassa")
session['language'] = "English"
return render_template('index.html', title='Home', experiments=experiments)
......@@ -54,21 +45,21 @@ def index():
def consent():
exp_id = request.args.get('exp_id', None)
experiment_info = experiment.query.filter_by(idexperiment=exp_id).first()
instruction_paragraphs = str(experiment_info.short_instruction)
instruction_paragraphs = instruction_paragraphs.split('<br>')
consent_paragraphs = str(experiment_info.consent_text)
consent_paragraphs = consent_paragraphs.split('<br>')
if experiment_info.use_forced_id == 'On':
return redirect(url_for('begin_with_id', exp_id=exp_id))
return render_template('consent.html',
exp_id=exp_id,
experiment_info=experiment_info,
instruction_paragraphs=instruction_paragraphs,
consent_paragraphs=consent_paragraphs)
return render_template('consent.html',
exp_id=exp_id,
experiment_info=experiment_info,
instruction_paragraphs=instruction_paragraphs,
consent_paragraphs=consent_paragraphs)
@app.route('/set_language')
......@@ -88,28 +79,28 @@ def remove_language():
def participant_session():
'''Set up session variables and create answer_set (database level sessions)'''
#start session
# start session
session['exp_id'] = request.args.get('exp_id', None)
session['agree'] = request.args.get('agree', None)
#If user came via the route for "I have already a participant ID that I wish to use, Use that ID, otherwise generate a random ID
# If user came via the route for "I have already a participant ID that I wish to use, Use that ID, otherwise generate a random ID
if 'begin_with_id' in session:
session['user'] = session['begin_with_id']
session.pop('begin_with_id', None)
else:
#lets generate a random id. If the same id is allready in db, lets generate a new one and finally use that in session['user']
# lets generate a random id. If the same id is allready in db, lets generate a new one and finally use that in session['user']
random_id = secrets.token_hex(3)
check_id = answer_set.query.filter_by(session=random_id).first()
while check_id is not None:
random_id = secrets.token_hex(3)
check_id = answer_set.query.filter_by(session=random_id).first()
session['user'] = random_id
# Set session status variables
exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first()
exp_status = experiment.query.filter_by(
idexperiment=session['exp_id']).first()
# Create answer set for the participant in the database
the_time = datetime.now()
......@@ -121,55 +112,57 @@ def participant_session():
answer_set_type = 'embody'
participant_answer_set = answer_set(experiment_idexperiment=session['exp_id'],
session=session['user'],
agreement = session['agree'],
answer_counter = '0',
answer_type = answer_set_type,
registration_time=the_time,
session=session['user'],
agreement=session['agree'],
answer_counter='0',
answer_type=answer_set_type,
registration_time=the_time,
last_answer_time=the_time)
db.session.add(participant_answer_set)
db.session.commit()
#If trial randomization is set to 'On' for the experiment, create a randomized trial order for this participant
#identification is based on the uniquie answer set id
# If trial randomization is set to 'On' for the experiment, create a randomized trial order for this participant
# identification is based on the uniquie answer set id
if exp_status.randomization == 'On':
session['randomization'] = 'On'
#create a list of page id:s for the experiment
experiment_pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).all()
# create a list of page id:s for the experiment
experiment_pages = page.query.filter_by(
experiment_idexperiment=session['exp_id']).all()
original_id_order_list = [(int(o.idpage)) for o in experiment_pages]
#create a randomized page id list
helper_list = original_id_order_list
# create a randomized page id list
helper_list = original_id_order_list
randomized_order_list = []
for i in range(len(helper_list)):
element = random.choice(helper_list)
helper_list.remove(element)
randomized_order_list.append(element)
#Input values into trial_randomization table where the original page_ids have a corresponding randomized counterpart
experiment_pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).all()
# Input values into trial_randomization table where the original page_ids have a corresponding randomized counterpart
experiment_pages = page.query.filter_by(
experiment_idexperiment=session['exp_id']).all()
original_id_order_list = [(int(o.idpage)) for o in experiment_pages]
for c in range(len(original_id_order_list)):
random_page = trial_randomization(page_idpage=original_id_order_list[c], randomized_idpage=randomized_order_list[c], answer_set_idanswer_set = participant_answer_set.idanswer_set, experiment_idexperiment = session['exp_id'])
random_page = trial_randomization(page_idpage=original_id_order_list[c], randomized_idpage=randomized_order_list[
c], answer_set_idanswer_set=participant_answer_set.idanswer_set, experiment_idexperiment=session['exp_id'])
db.session.add(random_page)
db.session.commit()
if exp_status.randomization == "Off":
session['randomization'] = "Off"
#store participants session id in session list as answer_set, based on experiment id and session id
session_id_for_participant = answer_set.query.filter(and_(answer_set.session==session['user'], answer_set.experiment_idexperiment==session['exp_id'])).first()
# store participants session id in session list as answer_set, based on experiment id and session id
session_id_for_participant = answer_set.query.filter(and_(
answer_set.session == session['user'], answer_set.experiment_idexperiment == session['exp_id'])).first()
session['answer_set'] = session_id_for_participant.idanswer_set
#collect experiments mediatype from db to session['type'].
#This is later used in task.html to determine page layout based on stimulus type
mediatype = page.query.filter_by(experiment_idexperiment=session['exp_id']).first()
# collect experiments mediatype from db to session['type'].
# This is later used in task.html to determine page layout based on stimulus type
mediatype = page.query.filter_by(
experiment_idexperiment=session['exp_id']).first()
if mediatype:
session['type'] = mediatype.type
else:
......@@ -180,124 +173,129 @@ def participant_session():
if 'user' in session:
user = session['user']
return redirect('/register')
return "Session start failed return <a href = '/login'></b>" + "Home</b></a>"
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
questions_and_options = {}
questions = background_question.query.filter_by(experiment_idexperiment=session['exp_id']).all()
for q in questions:
options = background_question_option.query.filter_by(background_question_idbackground_question=q.idbackground_question).all()
options_list = [(o.option, o.idbackground_question_option) for o in options]
questions_and_options[q.idbackground_question, q.background_question] = options_list
questions = background_question.query.filter_by(
experiment_idexperiment=session['exp_id']).all()
for q in questions:
options = background_question_option.query.filter_by(
background_question_idbackground_question=q.idbackground_question).all()
options_list = [(o.option, o.idbackground_question_option)
for o in options]
questions_and_options[q.idbackground_question,
q.background_question] = options_list
form.questions1 = questions_and_options
if request.method == 'POST'and form.validate():
if request.method == 'POST' and form.validate():
data = request.form.to_dict()
for key, value in data.items():
#tähän db insertit
# tähän db insertit
#flash(key)
#flash(value)
#Input registration page answers to database
participant_background_question_answers = background_question_answer(answer_set_idanswer_set=session['answer_set'], answer=value, background_question_idbackground_question=key)
# flash(key)
# flash(value)
# Input registration page answers to database
participant_background_question_answers = background_question_answer(
answer_set_idanswer_set=session['answer_set'], answer=value, background_question_idbackground_question=key)
db.session.add(participant_background_question_answers)
db.session.commit()
return redirect('/instructions')
return render_template('register.html', form=form)
return render_template('register.html', form=form)
@app.route('/begin_with_id', methods=['GET', 'POST'])
def begin_with_id():
'''Begin experiment with experiment ID. GET -method returns login page for starting the
experiment and POST -method verifys users ID before starting new experiment'''
exp_id = request.args.get('exp_id', None)
form = StartWithIdForm()
experiment_info = experiment.query.filter_by(idexperiment=exp_id).first()
instruction_paragraphs = str(experiment_info.short_instruction)
instruction_paragraphs = instruction_paragraphs.split('<br>')
consent_paragraphs = str(experiment_info.consent_text)
consent_paragraphs = consent_paragraphs.split('<br>')
if form.validate_on_submit():
variable = form.participant_id.data
#check if participant ID is found from db with this particular ID. If a match is found inform about error
participant = answer_set.query.filter(and_(answer_set.session==variable, answer_set.experiment_idexperiment==exp_id)).first()
is_id_valid = forced_id.query.filter(and_(forced_id.pregenerated_id==variable, forced_id.experiment_idexperiment==exp_id)).first()
# check if participant ID is found from db with this particular ID. If a match is found inform about error
participant = answer_set.query.filter(and_(
answer_set.session == variable, answer_set.experiment_idexperiment == exp_id)).first()
is_id_valid = forced_id.query.filter(and_(
forced_id.pregenerated_id == variable, forced_id.experiment_idexperiment == exp_id)).first()
if participant is not None:
flash(_('ID already in use'))
return redirect(url_for('begin_with_id', exp_id=exp_id))
#if there was not a participant already in DB:
return redirect(url_for('begin_with_id', exp_id=exp_id))
# if there was not a participant already in DB:
if participant is None:
if is_id_valid is None:
flash(_('No such ID set for this experiment'))
return redirect(url_for('begin_with_id', exp_id=exp_id))
return redirect(url_for('begin_with_id', exp_id=exp_id))
else:
#save the participant ID in session list for now, this is deleted after the session has been started in participant_session-view
# save the participant ID in session list for now, this is deleted after the session has been started in participant_session-view
session['begin_with_id'] = form.participant_id.data
return render_template('consent.html', exp_id=exp_id, experiment_info=experiment_info, instruction_paragraphs=instruction_paragraphs, consent_paragraphs=consent_paragraphs)
return render_template('begin_with_id.html', exp_id=exp_id, form=form)
@app.route('/admin_dryrun', methods=['GET', 'POST'])
@login_required
def admin_dryrun():
exp_id = request.args.get('exp_id', None)
form = StartWithIdForm()
experiment_info = experiment.query.filter_by(idexperiment=exp_id).first()
if form.validate_on_submit():
#check if participant ID is found from db with this particular ID. If a match is found inform about error
participant = answer_set.query.filter(and_(answer_set.session==form.participant_id.data, answer_set.experiment_idexperiment==exp_id)).first()
# check if participant ID is found from db with this particular ID. If a match is found inform about error
participant = answer_set.query.filter(and_(
answer_set.session == form.participant_id.data, answer_set.experiment_idexperiment == exp_id)).first()
if participant is not None:
flash('ID already in use')
return redirect(url_for('admin_dryrun', exp_id=exp_id))
#if there was not a participant already in DB:
return redirect(url_for('admin_dryrun', exp_id=exp_id))
# if there was not a participant already in DB:
if participant is None:
#save the participant ID in session list for now, this is deleted after the session has been started in participant_session-view
# save the participant ID in session list for now, this is deleted after the session has been started in participant_session-view
session['begin_with_id'] = form.participant_id.data
return render_template('consent.html', exp_id=exp_id, experiment_info=experiment_info)
return render_template('admin_dryrun.html', exp_id=exp_id, form=form)
@app.route('/instructions')
def instructions():
participant_id = session['user']
instructions = experiment.query.filter_by(idexperiment = session['exp_id']).first()
instructions = experiment.query.filter_by(
idexperiment=session['exp_id']).first()
instruction_paragraphs = str(instructions.instruction)
instruction_paragraphs = instruction_paragraphs.split('<br>')
return render_template('instructions.html', instruction_paragraphs=instruction_paragraphs, participant_id=participant_id)