views.py 11.7 KB
Newer Older
1
2
3
4



import math
5
import json
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from datetime import datetime

from flask import (
    Flask, 
    render_template, 
    request, 
    session, 
    flash, 
    redirect, 
    url_for, 
    Blueprint
)

from sqlalchemy import and_ 
20
from sqlalchemy import exc
21
22
23
24
25
from flask_babel import _, lazy_gettext as _l

from app import db
from app.models import experiment
from app.models import page, question
26
from app.models import answer_set, answer, embody_answer
27
from app.models import user, trial_randomization
28
from app.forms import Answers, TaskForm, ContinueTaskForm, StringForm
29
30
31
32
33
34
35

task_blueprint = Blueprint("task", __name__, 
                template_folder='templates',
                static_folder='static',
                url_prefix='/task')


36
def get_randomized_page(page_id):
37
38
    """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"""
39

40
    randomized_page = trial_randomization.query.filter(and_(
41
            trial_randomization.answer_set_idanswer_set==session['answer_set'], 
42
            trial_randomization.page_idpage==page_id
43
44
        )).first()

45
    return randomized_page
46
47


48
def add_slider_answer(key, value, page_id=None):
49
50
51
52
    '''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'''

53
    participant_answer = answer(question_idquestion=key, answer_set_idanswer_set=session['answer_set'], answer=value, page_idpage=page_id)
54
55
56
57
    db.session.add(participant_answer)
    db.session.commit()


58
59
def update_answer_set_page():
    """Increment the page number by one in answer_set when user goes to next page"""
60
61
62
63
64
65
66
67
68
    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()


69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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 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

102

103
104
105
106
107
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':
108
        check_answer = embody_answer.query.filter(and_(embody_answer.answer_set_idanswer_set==session['answer_set'], embody_answer.page_idpage==page_id)).first()
109
    elif answer_type == 'slider':
110
        check_answer = answer.query.filter(and_(answer.answer_set_idanswer_set==session['answer_set'], answer.page_idpage==page_id)).first()
111

112
    return check_answer
113
114


115
116
117
118
def next_page(pages):
    # If no more pages left -> complete task
    if not pages.has_next:
        return redirect ( url_for('task.completed'))
119

120
121
122
123
124
125
126
127
    # If user has answered to all questions, then move to next page
    return redirect( url_for('task.task', page_num=pages.next_num))


def get_experiment_info():
    """Return experiment information from current session"""
    try:
        return experiment.query.filter_by(idexperiment=session['exp_id']).first()
Ossi Laine's avatar
Ossi Laine committed
128
    except KeyError as err:
129
130
131
132
133
134
135
136
137
138
        flash("No valid session found")
        return redirect('/')

def embody_on():
    """Check from session[exp_id] if embody questions are enabled"""
    experiment_info = get_experiment_info()
    if experiment_info.embody_enabled:
        return True
    else:
        return False
139
140


141
142
143
144
145
146
147
148
149
def slider_on():
    """Check from session[exp_id] if there are slider questions in this session"""
    experiment_info = get_experiment_info()
    questions = question.query.filter_by(experiment_idexperiment=experiment_info.idexperiment).all()

    if len(questions) == 0:
        return False
    return True

150

151
152
153
154
155
156
157
158
159
160
@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()
161
162
        coordinates = json.loads(data['coordinates'])

163
164
        # Check if randomization ON and if user has already answered to embody question
        if session['randomization'] == 'On':
165
166
167
            page_id = get_randomized_page(page_id).randomized_idpage

        check_answer = check_if_answer_exists('embody',page_id)
168

169
170
171
        # Add answer to DB
        if check_answer is None:
            # Add new embody answer
172
            participant_answer = embody_answer(answer_set_idanswer_set=session['answer_set'], coordinates=data['coordinates'], page_idpage=page_id)
173
174
175
176
            db.session.add(participant_answer)
            db.session.commit()
        else:
            flash("Page has been answered already. Answers discarded")
177

178
179
    # Check if there are unanswered slider questions -> if true redirect to same page
    if slider_on():
180
181
        update_answer_set_type('slider')
        return redirect( url_for('task.task', page_num=page_num))
182

183
184
    update_answer_set_page()
    return next_page(pages)
185
186
187


@task_blueprint.route('/question/<int:page_num>', methods=['POST'])
188
def task_answer(page_num):
189
    '''Save slider answers to database'''
190

191
    form = TaskForm(request.form)
192
    pages = page.query.filter_by(experiment_idexperiment=session['exp_id']).paginate(per_page=1, page=page_num, error_out=True)
193
    page_id = pages.items[0].idpage
194

195
196
    if form.validate():

197
         # Check if randomization ON and if user has already answered to slider question
198
        if session['randomization'] == 'On':
199
200
201
            page_id = get_randomized_page(page_id).randomized_idpage
        
        check_answer = check_if_answer_exists('slider',page_id)
202
203
204
205

        if check_answer is None:
            data = request.form.to_dict()
            for key, value in data.items():
206
                add_slider_answer(key, value, page_id)
207
208
209
        else:
            flash("Page has been answered already. Answers discarded")
        
210
    if embody_on():
211
212
        update_answer_set_type('embody')

213
214
215
    # Always redirect to next page from sliders
    update_answer_set_page()
    return next_page(pages)
216
217
218
219


@task_blueprint.route('/<int:page_num>', methods=['GET'])
def task(page_num):
220
221
    """Get selected task page"""
    randomized_stimulus=""
222
223
    experiment_info = get_experiment_info()
 
224
225
    #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
226
    stimulus_size = experiment_info.stimulus_size
227
228
229
    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)
230
    page_id = pages.items[0].idpage
231
    progress_bar_percentage = round((pages.page/pages.pages)*100)
232
    session['current_idpage'] = page_id
233
234
235
236

    # 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':
237
238
        randomized_page_id = get_randomized_page(page_id).randomized_idpage
        randomized_stimulus = page.query.filter_by(idpage=randomized_page_id).first()
239

240
        
241
    print(session)
242
    return render_template(
243
244
245
246
        'task.html', 
        pages=pages, 
        page_num=page_num,
        progress_bar_percentage=progress_bar_percentage, 
247
        form=select_form_type(), 
248
        randomized_stimulus=randomized_stimulus, 
249
        rating_instruction=experiment_info.single_sentence_instruction, 
250
251
252
        stimulus_size=stimulus_size, 
        stimulus_size_text=stimulus_size_text,
        experiment_info=experiment_info
253
    )
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270


@task_blueprint.route('/completed')
def completed():
    
    session.pop('user', None)
    session.pop('exp_id', None)    
    session.pop('agree', None)
    session.pop('answer_set', None)
    session.pop('type', None)
    session.pop('randomization', None)

    return render_template('task_completed.html')


@task_blueprint.route('/continue', methods=['GET', 'POST'])
def continue_task():
271
272
273
    """Continue unfinished task"""

    # Set experience if to session
274
    exp_id = request.args.get('exp_id', None)
275
276
    session['exp_id'] = exp_id

277
278
279
280
281
282
283
    form = ContinueTaskForm()
    
    if form.validate_on_submit():
        #check if participant ID is found from db and that the answer set is linked to the correct experiment
        participant = answer_set.query.filter(and_(answer_set.session==form.participant_id.data, answer_set.experiment_idexperiment==exp_id)).first()
        if participant is None:
            flash('Invalid ID')
284
            return redirect(url_for('task.continue_task', exp_id=exp_id))        
285
286
        
        #if correct participant_id is found with the correct experiment ID; start session for that user
287
        exp = get_experiment_info()
288
289
        session['user'] = form.participant_id.data 
        session['answer_set'] = participant.idanswer_set
290
        session['randomization'] = exp.randomization
291
292

        update_answer_set_type(participant.answer_type)
293
    
294
        mediatype = page.query.filter_by(experiment_idexperiment=session['exp_id']).first()
295
296
297
298
299
300
301
302
        if mediatype:
            session['type'] = mediatype.type
        else:
            flash('No pages or mediatype set for experiment')
            return redirect('/')

        #If participant has done just the registration redirect to the first page of the experiment        
        if participant.answer_counter == 0:
303
            return redirect( url_for('task.task', page_num=1))
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        
        redirect_to_page = participant.answer_counter + 1
        experiment_page_count = db.session.query(page).filter_by(experiment_idexperiment=session['exp_id']).count()
        
        #If participant has ansvered all pages allready redirect to task completed page
        if experiment_page_count == participant.answer_counter:
            return redirect( url_for('task.completed'))
        
        return redirect( url_for('task.task', page_num=redirect_to_page))
        
    return render_template('continue_task.html', exp_id=exp_id, form=form)


@task_blueprint.route('/quit')
def quit():
    
    user_id = session['user']
    session.pop('user', None)
    session.pop('exp_id', None)    
    session.pop('agree', None)
    session.pop('answer_set', None)
    session.pop('type', None)
    
    return render_template('quit_task.html', user_id=user_id)


# TODO: removable?
@task_blueprint.route('/create')
def create():
    return render_template('create_task.html')