From 4e38530eaeba845a7bb4f883c5f2197e1c53f056 Mon Sep 17 00:00:00 2001
From: Ossi Laine <ossi.laine@utu.fi>
Date: Tue, 2 Apr 2019 15:46:59 +0300
Subject: [PATCH] Refactoring task.html POST methods

---
 app/create/views.py     |  11 ++-
 app/experiment/views.py |   7 +-
 app/models.py           |  36 ++++-----
 app/routes.py           |  23 +++---
 app/static/js/canvas.js |   1 +
 app/task/views.py       | 165 ++++++++++++++++++++++++++--------------
 create_rating_db.sql    |   4 +
 7 files changed, 149 insertions(+), 98 deletions(-)

diff --git a/app/create/views.py b/app/create/views.py
index bca9240..3e3bb9c 100644
--- a/app/create/views.py
+++ b/app/create/views.py
@@ -111,8 +111,12 @@ def experiment_questions():
     
     exp_id = request.args.get('exp_id', None)
     form = CreateQuestionForm(request.form)
+
+    print(form)
+    print(form.validate())
     
-    if request.method == 'POST' and form.validate():
+    if request.method == 'POST':
+    #if request.method == 'POST' and form.validate():
 
         str = form.questions_and_options.data
         str_list = str.split('/n')
@@ -140,9 +144,10 @@ def experiment_questions():
             db.session.add(add_question)
             db.session.commit()
         
-        return redirect(url_for('create.experiment_upload_stimuli', exp_id=exp_id))    
+        #return redirect(url_for('create.experiment_upload_stimuli', exp_id=exp_id))    
+    return redirect(url_for('create.experiment_upload_stimuli', exp_id=exp_id))    
 
-    return render_template('create_experiment_questions.html', form=form)
+    #return render_template('create_experiment_questions.html', form=form)
 
 
 @create_blueprint.route('/experiment_upload_stimuli', methods=['GET', 'POST'])
diff --git a/app/experiment/views.py b/app/experiment/views.py
index c028388..55e7e98 100644
--- a/app/experiment/views.py
+++ b/app/experiment/views.py
@@ -517,13 +517,12 @@ def remove_bg_question():
     exp_status = experiment.query.filter_by(idexperiment=exp_id).first()
 
     if exp_status.status != 'Hidden':
-    
         flash("Experiment is public. Cannot modify structure.")
-
         return redirect(url_for('experiment.view', exp_id=exp_id))
         
     else:
-    
+
+        # TODO: cannot remove background question if there are answers
         remove_id = request.args.get('idbackground_question', None)
         remove_options = background_question_option.query.filter_by(background_question_idbackground_question=remove_id).all()
         
@@ -545,8 +544,6 @@ def remove_bg_question():
 
 
 
-
-
 # Rating set:
 
 @experiment_blueprint.route('/set_embody')
diff --git a/app/models.py b/app/models.py
index 1580393..9c3fe4d 100644
--- a/app/models.py
+++ b/app/models.py
@@ -28,7 +28,6 @@ class background_question_option(db.Model):
     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) 
 
@@ -64,6 +63,7 @@ class answer_set (db.Model):
     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)
 
@@ -78,15 +78,8 @@ class background_question_answer(db.Model):
     answer = db.Column(db.String(120))
     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) 
-    """
-
-    def __repr__(self):
-        return '<answer {}>'.format(self.answer) 
-
-    """
 
 
 def background_question_answer_query():
@@ -95,10 +88,7 @@ def background_question_answer_query():
 """
 class ChoiceForm(FlaskForm):
     opts = QuerySelectField(query_factory=background_question_answer_query, allow_blank=True)
-"""    
-
 
-"""
 u = background_question.query.get(1)
 vastaukset = u.answers.all()
 ## pitää sisällään kysymyksen 1 vastaukset
@@ -124,13 +114,7 @@ class page (db.Model):
     type = db.Column(db.String(120), index=True)
     text = db.Column(db.Text)
     media = db.Column(db.String(120), index=True)
-    """
-    def __repr__(self):
-        return "<idpage = '%s', experiment_idexperiment = '%s', type = '%s', text = '%s', media = '%s'>" % (self.idpage, self.experiment_idexperiment, self.type, self.text, self.media) 
-    
-    def __repr__(self):
-        return '{}'.format(self.text) 
-    """
+
     def __repr__(self):
         return "<idpage = '%s', experiment_idexperiment = '%s', type = '%s', text = '%s', media = '%s'>" % (self.idpage, self.experiment_idexperiment, self.type, self.text, self.media)
 
@@ -147,6 +131,17 @@ class answer (db.Model):
         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)
 
 
+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'))
+    page_idpage = db.Column(db.Integer, db.ForeignKey('page.idpage'))
+    coordinates = db.Column(db.Text)
+
+    def __repr__(self):
+        return "<idanswer = '%s', answer_set_idanswer_set = '%s', coordinates = '%s', page_idpage = '%s'>" % (self.idanswer, self.answer_set_idanswer_set, self.coordinates, self.page_idpage)
+
+
 class trial_randomization (db.Model):
     __tablename__ = "trial_randomization"
     idtrial_randomization = db.Column(db.Integer, primary_key=True)
@@ -169,11 +164,6 @@ class forced_id (db.Model):
         return "<idforced_id = '%s', experiment_idexperiment = '%s', pregenerated_id = '%s'>" % (self.idforced_id, self.experiment_idexperiment, self.pregenerated_id)
 
 
-
-
-
-
-
 class user(UserMixin, db.Model):
     __tablename__ = "user"
     id = db.Column(db.Integer, primary_key=True)
diff --git a/app/routes.py b/app/routes.py
index 30da6bc..67314d6 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -83,7 +83,7 @@ def remove_language():
 
 @app.route('/session')
 def participant_session():
-    '''Set up session variables'''
+    '''Set up session variables and create answer_set (database level sessions)'''
 
     #start session
     session['exp_id'] = request.args.get('exp_id', None)
@@ -104,20 +104,29 @@ def participant_session():
         
         session['user'] = random_id
 
+    # Set session status variables
+    exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first()
+
+
     #create answer set for the participant in the database
     the_time = datetime.now()
     the_time = the_time.replace(microsecond=0)
+
+    # TODO: Check which question type is the first in answer_set
+    answer_set_type = 'slider'
+    if exp_status.embody_enabled:
+        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, 
                                         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
@@ -150,18 +159,11 @@ def participant_session():
     if exp_status.randomization == "Off":
         session['randomization'] = "Off"
 
-    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
@@ -172,7 +174,6 @@ def participant_session():
         flash('No pages or mediatype set for experiment')
         return redirect('/')
 
-    
     # Redirect user to register page
     if 'user' in session:
         user = session['user']
diff --git a/app/static/js/canvas.js b/app/static/js/canvas.js
index 7b084dd..770d750 100644
--- a/app/static/js/canvas.js
+++ b/app/static/js/canvas.js
@@ -40,6 +40,7 @@ $(document).ready(function() {
     });
 
     canvas.mousemove(function(e){
+        // TODO: if mousedown -> can draw outside of image
         var mouseX = e.pageX - this.offsetLeft;
         var mouseY = e.pageY - this.offsetTop;
         if(paint && pointInsideBaseImage([mouseX, mouseY])){
diff --git a/app/task/views.py b/app/task/views.py
index d420441..5a5ccce 100644
--- a/app/task/views.py
+++ b/app/task/views.py
@@ -22,7 +22,7 @@ from flask_babel import _, lazy_gettext as _l
 from app import db
 from app.models import experiment
 from app.models import page, question
-from app.models import answer_set, answer 
+from app.models import answer_set, answer, embody_answer
 from app.models import user, trial_randomization
 from app.forms import Answers, TaskForm, ContinueTaskForm, StringForm
 
@@ -33,22 +33,18 @@ 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"""
 
-    #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 = trial_randomization.query.filter(and_(
             trial_randomization.answer_set_idanswer_set==session['answer_set'], 
             trial_randomization.page_idpage==page_id
-            #trial_randomization.page_idpage==pages.items[0].idpage
         )).first()
 
     return randomized_page
 
 
-def add_slider_answer(key, value, randomized_page_id):
+def add_slider_answer(key, value, randomized_page_id=None):
     '''Insert slider value to database. If trial randomization is set to 'Off' 
     the values are inputted for session['current_idpage']. Otherwise the values 
     are set for the corresponding id found in the trial randomization table'''
@@ -59,7 +55,8 @@ def add_slider_answer(key, value, randomized_page_id):
     db.session.commit()
 
 
-def update_answer_set():
+def update_answer_set_page():
+    """Increment the page number by one in answer_set when user goes to next page"""
     the_time = datetime.now()
     the_time = the_time.replace(microsecond=0)
 
@@ -69,6 +66,18 @@ def update_answer_set():
     db.session.commit()
 
 
+def update_answer_set_type(answer_type):
+    """If there are multiple question types(embody,slider,...) on one page, 
+    then update the current question type"""
+    the_time = datetime.now()
+    the_time = the_time.replace(microsecond=0)
+
+    updated_answer_set = answer_set.query.filter_by(idanswer_set=session['answer_set']).first()
+    updated_answer_set.answer_type = answer_type
+    updated_answer_set.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'''
 
@@ -83,9 +92,63 @@ def slider_question_has_answers(user, page_id):
 
     questions = question.query.filter_by(experiment_idexperiment=experiment_id).all()
 
+    # TODO: should return true if there are no slider questions!!!!
+
     return (True if (len(answers) == 0 and len(questions) > 0) else False)
 
 
+def select_form_type():
+    """Select form type based on the value in answer_set->answer_type"""
+
+    form = None
+    answer_set_type = answer_set.query.filter_by(idanswer_set=session['answer_set']).first().answer_type
+
+    if answer_set_type == 'slider':
+        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 form
+
+def check_if_answer_exists(answer_type, page_id):
+    """Check if there is already answer on certain experiment->page"""
+    check_answer = None
+
+    if answer_type == 'embody':
+        check_answer = embody_answer.query.filter(and_(embody_answer.answer_set_idanswer_set==session['answer_set'], embody_answer.page_idpage==session['current_idpage'])).first()
+    elif answer_type == 'slider':
+        check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==session['current_idpage'])).first()
+
+    return (check_answer, None)
+
+def check_if_randomized_answer_exists(answer_type, page_id):
+    """Check if there is already answer on certain experiment->page if the pages are in randomized order"""
+    check_answer = randomized_page_id = None
+
+    if answer_type == 'embody':
+        randomized_page_id = get_randomized_page(page_id).randomized_idpage
+        check_answer = embody_answer.query.filter(and_(embody_answer.answer_set_idanswer_set==session['answer_set'], embody_answer.page_idpage==randomized_page_id)).first()
+    elif answer_type == 'slider':
+        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()
+
+    return (check_answer, randomized_page_id)
+
+
+################################
+###                          ###
+###       ROUTES             ###
+###                          ###
+################################
+
 @task_blueprint.route('/embody/<int:page_num>', methods=['POST'])
 def task_embody(page_num):
     '''Save embody drawing to database'''
@@ -96,19 +159,34 @@ def task_embody(page_num):
 
     if form.validate():
         data = request.form.to_dict()
-
         coordinates = json.loads(data['coordinates'])
-        print("x:",coordinates['x'])
-        print("y:",coordinates['y'])
 
+        # Check if randomization ON and if user has already answered to embody question
+        if session['randomization'] == 'On':
+            check_answer, randomized_page_id = check_if_randomized_answer_exists('embody',page_id)
+        else:
+            check_answer, randomized_page_id = check_if_answer_exists('embody',page_id)
 
+        # Add answer to DB
+        if check_answer is None:
+            page_idpage = session['current_idpage'] if session['randomization'] == 'Off' else randomized_page_id
+
+            # Add new embody answer
+            participant_answer = embody_answer(answer_set_idanswer_set=session['answer_set'], coordinates=data['coordinates'], page_idpage=page_idpage)
+            db.session.add(participant_answer)
+
+            # Update answer set type
+            answer_set.query.filter_by(idanswer_set=session['answer_set']).first().answer_type = 'slider'
+
+            db.session.commit()
+        else:
+            flash("Page has been answered already. Answers discarded")
 
-    # Test that everything OK
-    return json.dumps(coordinates)
 
     # 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))
+        update_answer_set_type('slider')
+        return redirect( url_for('task.task', page_num=page_num))
 
     if not pages.has_next:
         return redirect ( url_for('task.completed'))
@@ -126,23 +204,17 @@ def task_answer(page_num):
     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)
-        #If so flash ("Page has been answered already. Answers discarded"), else insert values in to db
-        #this has to be done separately for trial randomization "on" and "off" situations        
 
+         # Check if randomization ON and if user has already answered to slider question
         if session['randomization'] == 'On':
-            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()
+            check_answer, randomized_page_id = check_if_randomized_answer_exists('slider',page_id)
         else:
-            check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==session['current_idpage'])).first()
+            check_answer, randomized_page_id = check_if_answer_exists('slider',page_id)
 
         if check_answer is None:
-            update_answer_set()
-        
             data = request.form.to_dict()
             for key, value in data.items():
                 add_slider_answer(key, value, randomized_page_id)
-
         else:
             flash("Page has been answered already. Answers discarded")
         
@@ -151,11 +223,20 @@ def task_answer(page_num):
         if not pages.has_next:
             return redirect ( url_for('task.completed'))
 
+    update_answer_set_page()
+
+    # If embody in use -> change the answer set type
+    exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first()
+    if exp_status.embody_enabled:
+        update_answer_set_type('embody')
+
     return redirect( url_for('task.task', page_num=pages.next_num))
 
 
 @task_blueprint.route('/<int:page_num>', methods=['GET'])
 def task(page_num):
+    """Get selected task page"""
+    randomized_stimulus=""
 
     try:
         experiment_info = experiment.query.filter_by(idexperiment=session['exp_id']).first()
@@ -163,22 +244,16 @@ def task(page_num):
         print(err)
         flash("No valid session found")
         return redirect('/')
-
-
-    rating_instruction = experiment_info.single_sentence_instruction
-    stimulus_size = experiment_info.stimulus_size
     
     #for text stimuli the size needs to be calculated since the template element utilises h1-h6 tags.
     #A value of stimulus size 12 gives h1 and value of 1 gives h6
+    stimulus_size = experiment_info.stimulus_size
     stimulus_size_text = 7-math.ceil((int(stimulus_size)/2))
 
     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"
-    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
     if session['randomization'] == 'On':
@@ -188,39 +263,18 @@ def task(page_num):
     for p in pages.items:
         session['current_idpage'] = p.idpage
 
-    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, 
         page_num=page_num,
         progress_bar_percentage=progress_bar_percentage, 
-        form=form, 
+        form=select_form_type(), 
         randomized_stimulus=randomized_stimulus, 
-        rating_instruction=rating_instruction, 
+        rating_instruction=experiment_info.single_sentence_instruction, 
         stimulus_size=stimulus_size, 
         stimulus_size_text=stimulus_size_text,
         experiment_info=experiment_info
-        )
+    )
 
 
 @task_blueprint.route('/completed')
@@ -261,7 +315,6 @@ def continue_task():
         exp = experiment.query.filter_by(idexperiment=session['exp_id']).first()
         
         session['randomization'] = exp.randomization
-        session['embody'] = exp.embody_enabled
     
         if mediatype:
             session['type'] = mediatype.type
diff --git a/create_rating_db.sql b/create_rating_db.sql
index 46ec627..b454ca3 100644
--- a/create_rating_db.sql
+++ b/create_rating_db.sql
@@ -55,6 +55,10 @@ CREATE TABLE answer_set (
 	FOREIGN KEY(experiment_idexperiment) REFERENCES experiment (idexperiment)
 );
 
+ALTER TABLE answer_set ADD COLUMN (answer_type VARCHAR(120));
+
+/* TODO: Update answer_set so it knows which part of the page the user is doing (embody/sliders/something else) */ 
+
 /* Background questions are asked before the experiment begins */ 
 CREATE TABLE background_question (
 	idbackground_question INTEGER NOT NULL AUTO_INCREMENT,
-- 
GitLab