diff --git a/app/__init__.py b/app/__init__.py index aefa4b3e1a5dc62ae1e1d6b53b0b8232d56ee8bc..c0403b5b49d168a66632020067f0ee3af6a6a526 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -84,5 +84,6 @@ app.register_blueprint(create_blueprint) app.secret_key = 'random string' """app.secret_key = os.urandom(24)""" +app.jinja_env.auto_reload = True from app import routes, models diff --git a/app/experiment/templates/view_experiment.html b/app/experiment/templates/view_experiment.html index 15744719537fb6828b582d9152da51819c300bf3..472a0760e755418186db9260264e4051135e665e 100644 --- a/app/experiment/templates/view_experiment.html +++ b/app/experiment/templates/view_experiment.html @@ -9,9 +9,8 @@ <table class="table"> <tbody> - {% for exp in experiment_info %} - +<!-- Why there is a for loop her ?! --> <tr> <td nowrap>Name:</td> @@ -215,53 +214,70 @@ <h1 class="container mt-5 display-4 text-left"><br>Rating set:</h1> -<hr> -TODO: add embody tool +<table class="table"> + <colgroup> + <col style="width:33%;%"> + <col style="width:33%;%"> + <col style="width:33%;%"> + </colgroup> + <tr> + <td>Add embody tool:</td> + <td> + {% if experiment_info[0].embody_enabled %} + Enabled + {% else %} + Disabled + {% endif %} + </td> + <td><a class="btn btn-primary btn-block btn-sm btn-info" href="{{ url_for('experiment.set_embody', exp_id=exp_id) }}" role="button">Enable embody</a></td> + </tr> +</table> + <hr> <br> - {% for category in categories1 %} - {% for scale in categories1[category] %} - <div class="row form-group"> - <div class="col-2 text-center"> - <p>{{ scale[0] }}</p> - </div> - <div class="col text-center"> - <label for="customRange">{{ category[1] }}</label> - <input type="range" class="custom-range" id="customRange" name={{ category[0] }}> - </div> - <div class="col-2 text-center"> - <p>{{ scale[1] }}</p> - </div> - <div class="col-2 text-center"> - - - <button type="button" class="btn btn-primary btn-sm btn-dark" data-toggle="modal" data-target="#mymodal{{category[0]}}">Remove</button> - <!-- Modal --> - <div class="modal fade" id="mymodal{{category[0]}}" role="dialog"> - <div class="modal-dialog modal-dialog-centered" id="{{category[0]}}"> - <!-- Modal content--> - <div class="modal-content modal-dialog-centered"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal">×</button> - <h4 class="modal-title">Notice!</h4> - </div> - <div class="modal-body"> - <p>Are you sure you want to remove this?</p> - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> - <a class="btn btn-primary" href="{{ url_for('experiment.remove_question', idquestion=category[0], exp_id=exp_id) }}" role="button">Yes, remove</a> - </div> - </div> +{% for category in categories1 %} + {% for scale in categories1[category] %} + <div class="row form-group"> + <div class="col-2 text-center"> + <p>{{ scale[0] }}</p> + </div> + <div class="col text-center"> + <label for="customRange">{{ category[1] }}</label> + <input type="range" class="custom-range" id="customRange" name={{ category[0] }}> + </div> + <div class="col-2 text-center"> + <p>{{ scale[1] }}</p> + </div> + <div class="col-2 text-center"> + + + <button type="button" class="btn btn-primary btn-sm btn-dark" data-toggle="modal" data-target="#mymodal{{category[0]}}">Remove</button> + <!-- Modal --> + <div class="modal fade" id="mymodal{{category[0]}}" role="dialog"> + <div class="modal-dialog modal-dialog-centered" id="{{category[0]}}"> + <!-- Modal content--> + <div class="modal-content modal-dialog-centered"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal">×</button> + <h4 class="modal-title">Notice!</h4> + </div> + <div class="modal-body"> + <p>Are you sure you want to remove this?</p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> + <a class="btn btn-primary" href="{{ url_for('experiment.remove_question', idquestion=category[0], exp_id=exp_id) }}" role="button">Yes, remove</a> </div> </div> - <a class="btn btn-primary btn-sm btn-info" href="{{ url_for('experiment.edit_question', idquestion=category[0]) }}" role="button">Edit</a> - - </div> - + </div> + </div> + <a class="btn btn-primary btn-sm btn-info" href="{{ url_for('experiment.edit_question', idquestion=category[0]) }}" role="button">Edit</a> + </div> - {% endfor %} - {% endfor %} + + </div> + {% endfor %} +{% endfor %} </table> <table class="table"> diff --git a/app/experiment/views.py b/app/experiment/views.py index 360f7ec0b48c874045da12fa42b8d265d16734e5..d297abf805d79b5339cfde9f04cab73017747517 100644 --- a/app/experiment/views.py +++ b/app/experiment/views.py @@ -79,15 +79,8 @@ def view(): categories_and_scales[cat.idquestion, cat.question] = scale_list categories1 = categories_and_scales - - return render_template('view_experiment.html', exp_id=exp_id, media=media, mtype=mtype, experiment_info=experiment_info, categories1=categories1, questions1=questions1) - - - - - - + return render_template('view_experiment.html', exp_id=exp_id, media=media, mtype=mtype, experiment_info=experiment_info, categories1=categories1, questions1=questions1) # Experiment info: @@ -556,6 +549,20 @@ def remove_bg_question(): # Rating set: +@experiment_blueprint.route('/set_embody') +@login_required +def set_embody(): + '''Enable/disable embody tool''' + + exp_id = request.args.get('exp_id', None) + + exp = experiment.query.filter_by(idexperiment = exp_id).first() + exp.embody_enabled = (True if exp.embody_enabled == False else False) + db.session.commit() + + return redirect(url_for('experiment.view', exp_id=exp_id)) + + @experiment_blueprint.route('/add_questions', methods=['GET', 'POST']) @login_required def add_questions(): @@ -856,6 +863,8 @@ def remove_stimuli(): @experiment_blueprint.route('/statistics') @login_required def statistics(): + + # TODO: Answers are in normal order although questions might be in randomized order exp_id = request.args.get('exp_id', None) @@ -879,51 +888,6 @@ def statistics(): #List of answers per participant in format question Stimulus ID/Question ID #those are in answer table as page_idpage and question_idquestion respectively - - - """ - pages = page.query.filter_by(experiment_idexperiment=exp_id).all() - - participants_and_answers = {} - - #participants on kaikki expin osallistujat - for participant in participants: - - #kaikki yhden khn vastaukset ko experimentille koska answer_setin id matchaa answereiden kanssa - flash(participant.session) - for p in pages: - - answers = answer.query.filter_by(answer_set_idanswer_set=participant.idanswer_set).all() - #kaikki yhden participantin vastaukset pagelle - answers_for_page = answer.query.filter(and_(answer.answer_set_idanswer_set==participant.idanswer_set, answer.page_idpage==p.idpage)).all() - - for ans in answers: - if ans.page_idpage == p.idpage: - #flash(ans.page_idpage) - flash("X") - else: - flash("NA") - - #pages on kaikki experimentin paget - - for a in answers: - if p.idpage == a.page_idpage: - flash("match") - else: - flash("no match") - flash("participant:") - flash(participant.session) - flash("stimulus:") - flash(a.page_idpage) - flash("Kysymys") - flash(a.question_idquestion) - flash("vastaus:") - flash(a.answer) - - #answers_list = (a.idanswer, a.question_idquestion, a.answer_set_idanswer_set, a.answer, a.page_idpage) - #participants_and_answers[participant.session] = answers_list - """ - participants_and_answers = {} for participant in participants: diff --git a/app/forms.py b/app/forms.py index 4cf81189def89729ae45a56479eef1423a5baad6..ff4bbb91ae6a4ad663ba3e7e496436ea2f8bab5a 100644 --- a/app/forms.py +++ b/app/forms.py @@ -25,11 +25,17 @@ class RegisterForm(Form): class TaskForm(Form): - + __name__= 'slider' categories1 = FieldList(SelectField([validators.InputRequired()])) submit = SubmitField("Send") +class StringForm(Form): + __name__= 'embody' + text = StringField() + submit = SubmitField("Send") + + class ContinueTaskForm(FlaskForm): participant_id = StringField('participant_id', validators=[DataRequired()]) diff --git a/app/models.py b/app/models.py index 85ab39a9d32167449b7662c9ffab678419d5aed2..158039315c35d926866e803903260d4ae2f22290 100644 --- a/app/models.py +++ b/app/models.py @@ -1,9 +1,7 @@ from app import db -from sqlalchemy import Column, Integer, String, Text -from flask_sqlalchemy import SQLAlchemy +from sqlalchemy import Column, Integer, String, Text, Boolean from flask_wtf import FlaskForm from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField -from flask_bootstrap import Bootstrap from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin from app import login @@ -53,9 +51,10 @@ class experiment (db.Model): stimulus_size = db.Column(db.String(120)) 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=True) 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'>" % (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) + 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) class answer_set (db.Model): diff --git a/app/routes.py b/app/routes.py index faec69b7a0452a30ddc09707b54de03d711b9e76..30da6bcf5759bf7fe3e8f168922aa48b49b035d2 100644 --- a/app/routes.py +++ b/app/routes.py @@ -83,8 +83,7 @@ def remove_language(): @app.route('/session') def participant_session(): - - # TODO: too long method? + '''Set up session variables''' #start session session['exp_id'] = request.args.get('exp_id', None) @@ -100,12 +99,11 @@ def participant_session(): check_id = answer_set.query.filter_by(session=random_id).first() while check_id is not None: - #flash("ID already existed; generated a new one") random_id = secrets.token_hex(3) check_id = answer_set.query.filter_by(session=random_id).first() session['user'] = random_id - + #create answer set for the participant in the database the_time = datetime.now() the_time = the_time.replace(microsecond=0) @@ -115,30 +113,22 @@ def participant_session(): answer_counter = '0', registration_time=the_time, last_answer_time=the_time) - db.session.add(participant_answer_set) db.session.commit() + + # Set session status variables + exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first() #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 - - exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first() - if exp_status.randomization == 'On': session['randomization'] = 'On' - #flash("answer_set_id") - #flash(participant_answer_set.idanswer_set) - #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] - #flash("original Page id order:") - #for a in range(len(original_id_order_list)): - #flash(original_id_order_list[a]) - #create a randomized page id list helper_list = original_id_order_list randomized_order_list = [] @@ -159,29 +149,33 @@ def participant_session(): if exp_status.randomization == "Off": session['randomization'] = "Off" - - #store participants session id in session list as answer_set - #old: was missing experiment id so made duplicates - #session_id_for_participant = answer_set.query.filter_by(session=session['user']).first() + if exp_status.embody_enabled: + session['embody'] = True + else: + session['embody'] = False + #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 - + + + + # TODO: this is unnecessary if experiment contains multiple stimulus types #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: flash('No pages or mediatype set for experiment') return redirect('/') + + # Redirect user to register page if 'user' in session: user = session['user'] - #flash('Session started for user {}'.format(user)) return redirect('/register') return "Session start failed return <a href = '/login'></b>" + "Home</b></a>" diff --git a/app/static/css/main.css b/app/static/css/main.css index 73f519a91fc6317dce08e1f6192b860251b16f23..efc6260167dc9116c0c496392121b096c60de2b6 100644 --- a/app/static/css/main.css +++ b/app/static/css/main.css @@ -1,17 +1,7 @@ body { - background-color: darkgrey; font-family: Helvetica, Arial, sans-serif; } - -container { - - text-decoration-color: #4EB1BA; - margin-left: auto; - margin-right: auto; - -} - #header{ background-color: darkslategrey; color: white; @@ -19,10 +9,13 @@ container { } -#nav{ - +.hidden { + display: none; } -#main{ +#embody-canvas { + display: block; + margin: 0 auto; + margin-bottom: 30px; } \ No newline at end of file diff --git a/app/static/img/dummy_600.png b/app/static/img/dummy_600.png new file mode 100644 index 0000000000000000000000000000000000000000..7a5363d79486b525f41bf3b142117b8d509005dd Binary files /dev/null and b/app/static/img/dummy_600.png differ diff --git a/app/static/js/canvas.js b/app/static/js/canvas.js new file mode 100644 index 0000000000000000000000000000000000000000..a8363ac16ca0bd49c24d5cbd74b87d6f82ff468f --- /dev/null +++ b/app/static/js/canvas.js @@ -0,0 +1,86 @@ + + +$(document).ready(function() { + + + $("#canvas-data").val("hello world"); + var canvas = $("#embody-canvas") + var context = document.getElementById("embody-canvas").getContext("2d"); + + + canvas.mousedown(function(e){ + var mouseX = e.pageX - this.offsetLeft; + var mouseY = e.pageY - this.offsetTop; + + paint = true; + addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop); + redraw(); + }); + + canvas.mousemove(function(e){ + if(paint){ + addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true); + redraw(); + } + }); + + canvas.mouseup(function(e){ + paint = false; + }); + + canvas.mouseleave(function(e){ + paint = false; + }); + + var clickX = new Array(); + var clickY = new Array(); + var clickDrag = new Array(); + var paint; + + function addClick(x, y, dragging) + { + clickX.push(x); + clickY.push(y); + clickDrag.push(dragging); + } + + + function redraw(){ + context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas + + context.strokeStyle = "#df4b26"; + context.lineJoin = "round"; + context.lineWidth = 5; + + for(var i=0; i < clickX.length; i++) { + context.beginPath(); + + if (clickDrag[i] && i) { + context.moveTo(clickX[i-1], clickY[i-1]); + } else { + context.moveTo(clickX[i]-1, clickY[i]); + } + + context.lineTo(clickX[i], clickY[i]); + context.closePath(); + context.stroke(); + } + + } + + + function drawBaseImage() + { + var img = document.getElementById("baseImage"); + var width = img.clientWidth; + var height = img.clientHeight; + + context.canvas.height = height + context.canvas.width = width + context.drawImage(img, 0, 0); + + img.classList.add("hidden") + } + + drawBaseImage() +}); \ No newline at end of file diff --git a/app/task/templates/task.html b/app/task/templates/task.html index 77cdddd664a346cd54be103f19866c2c0bb7cf15..49d185e5941ac12d7141ed35271d2f5df988b6c1 100644 --- a/app/task/templates/task.html +++ b/app/task/templates/task.html @@ -8,6 +8,7 @@ <!-- TODO change session['type'] TO stimulus['type'] because session should have multiple type of stimuli!!! --> + {% if session['type']=='text' %} <div class="container text-center mt-5 pt-5"> @@ -89,12 +90,36 @@ <br> - - - <h4 class="text-center">{{ rating_instruction }}</h4> - <form class="form-group mt-5" action="" method="post"> + <!-- Select form type --> + + {{page_num}} + + {% if form.__name__ == 'embody' %} + + <canvas id="embody-canvas" width="200" height="100" style="border: 1px solid blue;" ></canvas> + + <img id="baseImage" class="" src={{ url_for('static', filename='img/dummy_600.png') }} /> + <form class="form-group mt-5" action="/task/embody/{{ page_num }}" method="post"> + + <input id="canvas-data" type="hidden" value="" name="text"> + + <div class="form-row text-center"> + <div class="col-12"> + <a class="btn btn-primary" href={{ url_for('task.quit') }} role="button">{{ _('Quit task') }}</a> + <button type="submit" class="btn btn-primary">{{ _('Next page') }}</button> + </div> + <div class="col-12"> + <br> + <p>{{ _('You can zoom in/out the page view by pressing ctrl+/ctrl- (Windows) or ⌘+/⌘- (Mac)') }} </p> + </div> + </div> + </form> + +{% elif form.__name__ == 'slider' %} + + <form class="form-group mt-5" action="/task/question/{{ page_num }}" method="post"> {% for category in form.categories1 %} {% for scale in form.categories1[category] %} <div class="row form-group mt-0 mb-0"> @@ -104,7 +129,6 @@ <h6 class="col text-center mt-0 mb-0"> <label for="customRange">{{ category[1] }}</label> <input type="range" class="custom-range" id="customRange" name={{ category[0] }}> - </h6> <h6 class="col-3 text-left mt-0 mb-0"> {{ scale[1] }} @@ -112,16 +136,20 @@ </div> {% endfor %} {% endfor %} - <div class="form-row text-center"> - <div class="col-12"> - <a class="btn btn-primary" href={{ url_for('task.quit') }} role="button">{{ _('Quit task') }}</a> - <button type="submit" class="btn btn-primary">{{ _('Next page') }}</button> - </div> - <div class="col-12"> - <br> - <p>{{ _('You can zoom in/out the page view by pressing ctrl+/ctrl- (Windows) or ⌘+/⌘- (Mac)') }} </p> - </div> - </div> + <div class="form-row text-center"> + <div class="col-12"> + <a class="btn btn-primary" href={{ url_for('task.quit') }} role="button">{{ _('Quit task') }}</a> + <button type="submit" class="btn btn-primary">{{ _('Next page') }}</button> + </div> + <div class="col-12"> + <br> + <p>{{ _('You can zoom in/out the page view by pressing ctrl+/ctrl- (Windows) or ⌘+/⌘- (Mac)') }} </p> + </div> + </div> </form> + +{% endif %} + +<script src="{{ url_for('static', filename='js/canvas.js') }}" ></script> {% endblock %} \ No newline at end of file diff --git a/app/task/views.py b/app/task/views.py index c92ee9d915f8991fe05c1201ac8085b897f0e990..9b779454d8ff669ba26d615907780ce0eb3bc686 100644 --- a/app/task/views.py +++ b/app/task/views.py @@ -23,7 +23,7 @@ from app.models import experiment from app.models import page, question from app.models import answer_set, answer from app.models import user, trial_randomization -from app.forms import Answers, TaskForm, ContinueTaskForm +from app.forms import Answers, TaskForm, ContinueTaskForm, StringForm task_blueprint = Blueprint("task", __name__, template_folder='templates', @@ -31,19 +31,20 @@ task_blueprint = Blueprint("task", __name__, url_prefix='/task') -def get_randomized_page(pages): +def get_randomized_page(page_id): #this variable is feeded to the template as empty if trial randomization is set to "off" randomized_stimulus = "" #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 - randomized_page_id = trial_randomization.query.filter(and_( + randomized_page = trial_randomization.query.filter(and_( trial_randomization.answer_set_idanswer_set==session['answer_set'], - trial_randomization.page_idpage==pages.items[0].idpage + trial_randomization.page_idpage==page_id + #trial_randomization.page_idpage==pages.items[0].idpage )).first() - return randomized_page_id + return randomized_page def add_slider_answer(key, value, randomized_page_id): @@ -51,19 +52,71 @@ def add_slider_answer(key, value, randomized_page_id): the values are inputted for session['current_idpage']. Otherwise the values are set for the corresponding id found in the trial randomization table''' - page_idpage = session['current_idpage'] if session['randomization'] == 'Off' else randomized_page_id.randomized_idpage + page_idpage = session['current_idpage'] if session['randomization'] == 'Off' else randomized_page_id participant_answer = answer(question_idquestion=key, answer_set_idanswer_set=session['answer_set'], answer=value, page_idpage=page_idpage) db.session.add(participant_answer) db.session.commit() -@task_blueprint.route('/<int:page_num>', methods=['POST']) +def update_answer_set(): + the_time = datetime.now() + the_time = the_time.replace(microsecond=0) + + update_answer_counter = answer_set.query.filter_by(idanswer_set=session['answer_set']).first() + update_answer_counter.answer_counter = int(update_answer_counter.answer_counter) + 1 + update_answer_counter.last_answer_time = the_time + db.session.commit() + + +def slider_question_has_answers(user, page_id): + '''This should return true IF there are questions from certain page and no answers''' + + answer_set_id = answer_set.query.filter_by(session=user).first().idanswer_set + experiment_id = answer_set.query.filter_by(session=user).first().experiment_idexperiment + + if session['randomization'] == 'On': + randomized_page_id = get_randomized_page(page_id).randomized_idpage + answers = answer.query.filter_by(answer_set_idanswer_set=answer_set_id, page_idpage=randomized_page_id).all() + else: + answers = answer.query.filter_by(answer_set_idanswer_set=answer_set_id, page_idpage=page_id).all() + + questions = question.query.filter_by(experiment_idexperiment=experiment_id).all() + + return (True if (len(answers) == 0 and len(questions) > 0) else False) + + +@task_blueprint.route('/embody/<int:page_num>', methods=['POST']) +def task_embody(page_num): + '''Save embody drawing to database''' + + form = StringForm(request.form) + pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).paginate(per_page=1, page=page_num, error_out=True) + page_id = pages.items[0].idpage + + if form.validate(): + data = request.form.to_dict() + for key, value in data.items(): + print(key) + print(value) + + # Check if there are unanswered slider questions + if slider_question_has_answers(session['user'], page_id): + return redirect( url_for('task.task', page_num=page_num, show_sliders=True)) + + if not pages.has_next: + return redirect ( url_for('task.completed')) + + # If user has answered to all questions, then move to next page + return redirect( url_for('task.task', page_num=pages.next_num)) + + +@task_blueprint.route('/question/<int:page_num>', methods=['POST']) def task_answer(page_num): + '''Save slider answers to database''' form = TaskForm(request.form) pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).paginate(per_page=1, page=page_num, error_out=True) - - # TODO: determine wheter handling POST data from slider or embody!!! + page_id = pages.items[0].idpage if form.validate(): #Lets check if there are answers in database already for this page_id (eg. if user returned to previous page and tried to answer again) @@ -71,19 +124,13 @@ def task_answer(page_num): #this has to be done separately for trial randomization "on" and "off" situations if session['randomization'] == 'On': - randomized_page_id = get_randomized_page(pages) - check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==randomized_page_id.randomized_idpage)).first() + randomized_page_id = get_randomized_page(page_id).randomized_idpage + check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==randomized_page_id)).first() else: check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==session['current_idpage'])).first() if check_answer is None: - the_time = datetime.now() - the_time = the_time.replace(microsecond=0) - - update_answer_counter = answer_set.query.filter_by(idanswer_set=session['answer_set']).first() - update_answer_counter.answer_counter = int(update_answer_counter.answer_counter) + 1 - update_answer_counter.last_answer_time = the_time - db.session.commit() + update_answer_set() data = request.form.to_dict() for key, value in data.items(): @@ -103,7 +150,14 @@ def task_answer(page_num): @task_blueprint.route('/<int:page_num>', methods=['GET']) def task(page_num): - experiment_info = experiment.query.filter_by(idexperiment=session['exp_id']).first() + try: + experiment_info = experiment.query.filter_by(idexperiment=session['exp_id']).first() + except KeyError as err: + print(err) + flash("No valid session found") + return redirect('/') + + rating_instruction = experiment_info.single_sentence_instruction stimulus_size = experiment_info.stimulus_size @@ -111,9 +165,8 @@ def task(page_num): #A value of stimulus size 12 gives h1 and value of 1 gives h6 stimulus_size_text = 7-math.ceil((int(stimulus_size)/2)) - print(stimulus_size_text) - pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).paginate(per_page=1, page=page_num, error_out=True) + page_id = pages.items[0].idpage progress_bar_percentage = round((pages.page/pages.pages)*100) #this variable is feeded to the template as empty if trial randomization is set to "off" @@ -122,35 +175,45 @@ def task(page_num): # 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 if session['randomization'] == 'On': - - #set the stimulus to be shown if randomization is on - randomized_page_id = get_randomized_page(pages) - randomized_stimulus = page.query.filter_by(idpage=randomized_page_id.randomized_idpage).first() + randomized_page_id = get_randomized_page(page_id).randomized_idpage + randomized_stimulus = page.query.filter_by(idpage=randomized_page_id).first() for p in pages.items: session['current_idpage'] = p.idpage - - #slider set - form = TaskForm(request.form) - categories_and_scales = {} - categories = question.query.filter_by(experiment_idexperiment=session['exp_id']).all() - for cat in categories: - scale_list = [(cat.left, cat.right)] - categories_and_scales[cat.idquestion, cat.question] = scale_list - - form.categories1 = categories_and_scales + print(session) + + # Select form type (TODO: question order is now harcoded to EMBODY -> SLIDERS + # there should be more flexible solution if more question types are added...) + if request.args.get('show_sliders', False) or not session['embody']: + # Init slider form + form = TaskForm() + + # Get sliders from this experiment + categories = question.query.filter_by(experiment_idexperiment=session['exp_id']).all() + + categories_and_scales = {} + for cat in categories: + scale_list = [(cat.left, cat.right)] + categories_and_scales[cat.idquestion, cat.question] = scale_list + + form.categories1 = categories_and_scales + + else: + form = StringForm() return render_template( - 'task.html', - pages=pages, - progress_bar_percentage=progress_bar_percentage, - form=form, - randomized_stimulus=randomized_stimulus, - rating_instruction=rating_instruction, - stimulus_size=stimulus_size, - stimulus_size_text=stimulus_size_text - ) + 'task.html', + pages=pages, + page_num=page_num, + progress_bar_percentage=progress_bar_percentage, + form=form, + randomized_stimulus=randomized_stimulus, + rating_instruction=rating_instruction, + stimulus_size=stimulus_size, + stimulus_size_text=stimulus_size_text, + experiment_info=experiment_info + ) @task_blueprint.route('/completed') @@ -188,9 +251,10 @@ def continue_task(): session['answer_set'] = participant.idanswer_set mediatype = page.query.filter_by(experiment_idexperiment=session['exp_id']).first() - rand = experiment.query.filter_by(idexperiment=session['exp_id']).first() + exp = experiment.query.filter_by(idexperiment=session['exp_id']).first() - session['randomization'] = rand.randomization + session['randomization'] = exp.randomization + session['embody'] = exp.embody_enabled if mediatype: session['type'] = mediatype.type diff --git a/app/templates/base.html b/app/templates/base.html index 89bb79317d5adf0ddeab1a7f67a8796b8e832ec4..e566944f253e9c1cd6d0f21c56fdf4354f763195 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -9,6 +9,7 @@ <meta name="description" content=""> <meta name="author" content=""> <link rel="stylesheet" href="{{ url_for('static', filename='lib/css/bootstrap.min.css') }}" ></link> + <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}" ></link> <script src="{{ url_for('static', filename='lib/js/jquery-3.3.1.slim.min.js') }}" ></script> <script src="{{ url_for('static', filename='lib/js/popper.min.js') }}" ></script> <script src="{{ url_for('static', filename='lib/js/bootstrap.min.js') }}" ></script> diff --git a/config.py b/config.py index 542e7012df0c8d6d898d08651706580cdd7cf2c5..b945f2f5fc08ca3dd740a2782cab4891ad3f1401 100644 --- a/config.py +++ b/config.py @@ -28,3 +28,5 @@ class Config(object): SQLALCHEMY_TRACK_MODIFICATIONS = False + TEMPLATES_AUTO_RELOAD = True + DEBUG = True