From 7ab7e125189b5f9a167e51d10552eccf02bdfd95 Mon Sep 17 00:00:00 2001 From: osmala <ossi.laine@utu.fi> Date: Wed, 20 May 2020 18:46:30 +0300 Subject: [PATCH] Fixed randomization issues while exporting results --- app/experiment/views.py | 4 +- app/models.py | 83 +++++---- app/routes.py | 324 ++++++++++++++++++++---------------- app/static/js/getDrawing.js | 5 + app/task/views.py | 5 +- embody_plot.py | 4 - 6 files changed, 245 insertions(+), 180 deletions(-) diff --git a/app/experiment/views.py b/app/experiment/views.py index 1571725..7a08b88 100644 --- a/app/experiment/views.py +++ b/app/experiment/views.py @@ -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) diff --git a/app/models.py b/app/models.py index 6920ffa..ed4669c 100644 --- a/app/models.py +++ b/app/models.py @@ -1,7 +1,9 @@ +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 diff --git a/app/routes.py b/app/routes.py index 8324481..e3dfd9d 100644 --- a/app/routes.py +++ b/app/routes.py @@ -4,18 +4,18 @@ import csv import os import random import secrets -from datetime import datetime +from datetime import datetime, date import tempfile import json from flask import ( - Flask, - render_template, - request, - session, - flash, - redirect, - url_for, + Flask, + render_template, + request, + session, + flash, + redirect, + url_for, Blueprint, send_file ) @@ -33,20 +33,21 @@ from app.models import answer_set, answer, forced_id from app.models import user, trial_randomization from app.forms import LoginForm, RegisterForm, StartWithIdForm -#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 +55,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 +89,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 +122,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 +183,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) @@ -308,13 +316,14 @@ def login(): return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): - user_details = user.query.filter_by(username=form.username.data).first() + user_details = user.query.filter_by( + username=form.username.data).first() if user_details is None or not user_details.check_password(form.password.data): flash('Invalid username or password') return redirect(url_for('login')) - login_user(user_details, remember=form.remember_me.data) + login_user(user_details, remember=form.remember_me.data) return redirect(url_for('index')) - + # flash('Login requested for user {}, remember_me={}'.format( # form.username.data, form.remember_me.data)) # return redirect('/index') @@ -329,11 +338,11 @@ def logout(): @app.route('/view_research_notification') def view_research_notification(): - + exp_id = request.args.get('exp_id', None) image = experiment.query.filter_by(idexperiment=exp_id).first() research_notification_filename = image.research_notification_filename - + return render_template('view_research_notification.html', research_notification_filename=research_notification_filename) @@ -342,10 +351,13 @@ def view_research_notification(): def download_csv(): exp_id = request.args.get('exp_id', None) - experiment_info = experiment.query.filter_by(idexperiment = exp_id).all() + experiment_info = experiment.query.filter_by(idexperiment=exp_id).all() + + print(experiment_info) # answer sets with participant ids - participants = answer_set.query.filter_by(experiment_idexperiment= exp_id).all() + participants = answer_set.query.filter_by( + experiment_idexperiment=exp_id).all() # pages aka stimulants pages = page.query.filter_by(experiment_idexperiment=exp_id).all() @@ -358,21 +370,25 @@ def download_csv(): questions = question.query.filter_by(experiment_idexperiment=exp_id).all() # embody questions - embody_questions = embody_question.query.filter_by(experiment_idexperiment=exp_id).all() + embody_questions = embody_question.query.filter_by( + experiment_idexperiment=exp_id).all() csv = '' # create CSV-header header = 'participant id;' - header += ';'.join([str(count) +'. bg_question: '+ question.background_question.strip() for count,question in enumerate(bg_questions, 1)]) + header += ';'.join([str(count) + '. bg_question: ' + question.background_question.strip() + for count, question in enumerate(bg_questions, 1)]) - for idx in range(1,len(pages) + 1): + for idx in range(1, len(pages) + 1): if len(questions) > 0: - header += ';' + ';'.join(['page' + str(idx) + '_' + str(count) +'. slider_question: ' + question.question.strip() for count,question in enumerate(questions, 1)]) + header += ';' + ';'.join(['page' + str(idx) + '_' + str(count) + '. slider_question: ' + + question.question.strip() for count, question in enumerate(questions, 1)]) - for idx in range(1,len(pages) + 1): + for idx in range(1, len(pages) + 1): if len(embody_questions) > 0: - header += ';' + ';'.join(['page' + str(idx) + '_' + str(count) +'. embody_question: '+ question.picture.strip() for count,question in enumerate(embody_questions, 1)]) + header += ';' + ';'.join(['page' + str(idx) + '_' + str(count) + '. embody_question: ' + + question.picture.strip() for count, question in enumerate(embody_questions, 1)]) csv += header + '\r\n' answer_row = '' @@ -385,43 +401,63 @@ def download_csv(): answer_row += participant.session + ';' # append background question answers - bg_answers = background_question_answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set).all() - bg_answers_list = [ str(a.answer).strip() for a in bg_answers] + bg_answers = background_question_answer.query.filter_by( + answer_set_idanswer_set=participant.idanswer_set).all() + bg_answers_list = [str(a.answer).strip() for a in bg_answers] answer_row += ';'.join(bg_answers_list) + ';' - # append slider answers - slider_answers = answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set).all() - answers_list = [ str(a.answer).strip() for a in slider_answers] - answer_row += ';'.join(answers_list) + ';' if slider_answers else len(questions) * len(pages) * ';' + # append slider answers + slider_answers = answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set)\ + .order_by(answer.page_idpage)\ + .all() + + answers_list = [str(a.answer).strip() for a in slider_answers] + answer_row += ';'.join(answers_list) + \ + ';' if slider_answers else len( + questions) * len(pages) * ';' # append embody answers (coordinates) - # save embody answers as bitmap images - embody_answers = embody_answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set).all() - answers_list = [] + # save embody answers as bitmap images + embody_answers = embody_answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set)\ + .order_by(embody_answer.page_idpage)\ + .all() - # TODO: check randomization + answers_list = [] - for embody_answer_data in embody_answers: + for answer_data in embody_answers: try: - embody_answer_data = json.loads(embody_answer_data.coordinates) - coordinates_to_bitmap = [[0 for x in range(embody_answer_data['height'] + 2)] for y in range(embody_answer_data['width'] + 2)] + coordinates = json.loads(answer_data.coordinates) + em_height = coordinates.get('height', 600) + 2 + em_width = coordinates.get('width', 200) + 2 + + coordinates_to_bitmap = [ + [0 for x in range(em_height)] for y in range(em_width)] - for point in list(zip( embody_answer_data['x'], embody_answer_data['y'] )): + coordinates = list( + zip(coordinates.get('x'), coordinates.get('y'))) + + for point in coordinates: try: - coordinates_to_bitmap[point[0]][point[1]] += 0.1 + # for every brush stroke, increment the pixel + # value for every brush stroke + coordinates_to_bitmap[point[0]][point[1]] += 0.1 except IndexError: - continue + continue answers_list.append(json.dumps(coordinates_to_bitmap)) - + except ValueError as err: - app.logger.info(err) + app.logger(err) + + answer_row += ';'.join(answers_list) if embody_answers else \ + len(embody_questions) * len(pages) * ';' # old way to save only visited points: - # answers_list = [ json.dumps(list(zip( json.loads(a.coordinates)['x'], json.loads(a.coordinates)['y']))) for a in embody_answers] - answer_row += ';'.join(answers_list) if embody_answers else len(embody_questions) * len(pages) * ';' + # answers_list = [json.dumps( + # list(zip( json.loads(a.coordinates)['x'], + # json.loads(a.coordinates)['y']))) for a in embody_answers] except TypeError as err: print(err) @@ -431,15 +467,21 @@ def download_csv(): try: fd, path = tempfile.mkstemp() + with os.fdopen(fd, 'w') as tmp: tmp.write(csv) tmp.flush() - return send_file(path, mimetype='text/csv') + cur_date = date.today().strftime("%Y-%m-%d") + filename = "experiment_{}_{}.csv".format(exp_id, cur_date) + return send_file(path, + mimetype='text/csv', + as_attachment=True, + attachment_filename=filename) finally: os.remove(path) - + @app.route('/researcher_info') @login_required diff --git a/app/static/js/getDrawing.js b/app/static/js/getDrawing.js index 919c727..4504eb3 100644 --- a/app/static/js/getDrawing.js +++ b/app/static/js/getDrawing.js @@ -48,6 +48,11 @@ $(document).ready(function() { // var pageId = this.dataset.value.split('-')[0] var embodyId = this.dataset.value.split('-')[1] + + console.log(pageId) + console.log(embodyId) + + socket.emit('draw', {page:pageId, embody:embodyId}) progressBarContainer.removeClass("hidden") diff --git a/app/task/views.py b/app/task/views.py index c7815a3..7e559e7 100644 --- a/app/task/views.py +++ b/app/task/views.py @@ -37,10 +37,7 @@ task_blueprint = Blueprint("task", __name__, def get_randomized_page(page_id): """if trial randomization is on we will still use the same functionality that is used otherwise but we will pass the randomized pair of the page_id from trial randomization table to the task.html""" - return trial_randomization.query.filter(and_( - trial_randomization.answer_set_idanswer_set==session['answer_set'], - trial_randomization.page_idpage==page_id - )).first() + return trial_randomization.get_randomized_page(page_id) def add_slider_answer(key, value, page_id=None): diff --git a/embody_plot.py b/embody_plot.py index e72e065..0d61fad 100644 --- a/embody_plot.py +++ b/embody_plot.py @@ -119,13 +119,11 @@ def timeit(method): return timed -import sys @timeit def get_coordinates(idpage, idembody=None, select_clause=SELECT_BY_PAGE_AND_PICTURE): """Select all drawn points from certain stimulus and plot them onto the human body""" - db = MyDB() db.query(select_clause, (idpage,idembody)) @@ -138,13 +136,11 @@ def get_coordinates(idpage, idembody=None, select_clause=SELECT_BY_PAGE_AND_PICT image_path = db._db_cur.fetchone()[0] image_path = './app' + image_path - # Draw image plt = plot_coordinates(coordinates, image_path) else: plt = plot_coordinates(coordinates, DEFAULT_IMAGE_PATH) - # Save image to ./app/static/ img_filename = 'PAGE-' + str(idpage) + '-' + DATE_STRING + '.png' plt.savefig(STATIC_PATH + img_filename) -- GitLab