views.py 39.7 KB
Newer Older
1
2
3

import os
import secrets
Ossi Laine's avatar
Ossi Laine committed
4
from datetime import date
5
from tempfile import mkstemp
6

Ossi Laine's avatar
Ossi Laine committed
7
8
9
10
from flask_socketio import emit
from sqlalchemy import and_
from flask_login import login_required
from werkzeug import secure_filename
11
from flask import (
Ossi Laine's avatar
Ossi Laine committed
12
13
14
15
16
    render_template,
    request,
    flash,
    redirect,
    url_for,
17
18
    Blueprint,
    send_file
19
20
)

Ossi Laine's avatar
Ossi Laine committed
21
from app import app, db, socketio
22
23
24
25
26
27
from app.routes import APP_ROOT
from app.models import background_question, experiment
from app.models import background_question_answer
from app.models import page, question
from app.models import background_question_option
from app.models import answer_set, answer, forced_id
Ossi Laine's avatar
Ossi Laine committed
28
from app.models import trial_randomization
Ossi Laine's avatar
Ossi Laine committed
29
from app.models import embody_answer, embody_question
30
from app.forms import (
Ossi Laine's avatar
Ossi Laine committed
31
32
33
34
    CreateBackgroundQuestionForm,
    CreateQuestionForm, UploadStimuliForm, EditBackgroundQuestionForm,
    EditQuestionForm, EditExperimentForm, UploadResearchBulletinForm,
    EditPageForm, RemoveExperimentForm, GenerateIdForm, CreateEmbodyForm
35
)
36
from app.utils import get_mean_from_slider_answers, map_answers_to_questions, \
Ossi Laine's avatar
Ossi Laine committed
37
38
39
    generate_csv

import embody_plot
40

Ossi Laine's avatar
Ossi Laine committed
41
# Stimuli upload folder setting
42
43
#APP_ROOT = os.path.dirname(os.path.abspath(__file__))

Ossi Laine's avatar
Ossi Laine committed
44
45
46
47
experiment_blueprint = Blueprint("experiment", __name__,
                                 template_folder='templates',
                                 # static_folder='static',
                                 url_prefix='/experiment')
48

Ossi Laine's avatar
Ossi Laine committed
49
50
51
# Set sliders/embody:
DEFAULT_EMBODY_PICTURE = '/static/img/dummy_600.png'
DEFAULT_EMBODY_QUESTION = 'Color the regions whose activity you feel increasing or getting stronger'
52

Ossi Laine's avatar
Ossi Laine committed
53

54
55
56
@experiment_blueprint.route('/view')
@login_required
def view():
Ossi Laine's avatar
Ossi Laine committed
57
58

    # crap:3lines
59
60
61
    exp_id = request.args.get('exp_id', None)
    media = page.query.filter_by(experiment_idexperiment=exp_id).all()

Ossi Laine's avatar
Ossi Laine committed
62
    # stimulus type
63
    mtype = page.query.filter_by(experiment_idexperiment=exp_id).first()
Ossi Laine's avatar
Ossi Laine committed
64

Ossi Laine's avatar
Ossi Laine committed
65
66
67
68
    # experiment info
    experiment_info = experiment.query.filter_by(idexperiment=exp_id).all()

    # background questions
69
    questions_and_options = {}
Ossi Laine's avatar
Ossi Laine committed
70
71
72
73
74
75
76
77
78
79
80
    questions = background_question.query.filter_by(
        experiment_idexperiment=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
81
82

    questions1 = questions_and_options
Ossi Laine's avatar
Ossi Laine committed
83

Ossi Laine's avatar
Ossi Laine committed
84
    # sliderset
85
86
87
    categories_and_scales = {}
    categories = question.query.filter_by(experiment_idexperiment=exp_id).all()

Ossi Laine's avatar
Ossi Laine committed
88
89
    for cat in categories:

90
        scale_list = [(cat.left, cat.right)]
Ossi Laine's avatar
Ossi Laine committed
91
92
        categories_and_scales[cat.idquestion, cat.question] = scale_list

93
94
    categories1 = categories_and_scales

Ossi Laine's avatar
Ossi Laine committed
95
96
97
98
99
    # embody pictures
    embody_pictures = embody_question.query.filter_by(
        experiment_idexperiment=exp_id).all()

    return render_template('view_experiment.html', exp_id=exp_id, media=media, mtype=mtype, experiment_info=experiment_info, categories1=categories1, questions1=questions1, embody_pictures=embody_pictures)
100
101
102
103
104
105
106
107
108
109
110
111


# Experiment info:

@experiment_blueprint.route('/remove', methods=['GET', 'POST'])
@login_required
def remove():

    exp_id = request.args.get('exp_id', None)
    exp_status = experiment.query.filter_by(idexperiment=exp_id).first()

    if exp_status.status != 'Hidden':
Ossi Laine's avatar
Ossi Laine committed
112

113
114
        flash("Experiment is public. Cannot modify structure.")
        return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
115

116
    else:
Ossi Laine's avatar
Ossi Laine committed
117

118
        form = RemoveExperimentForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
119

120
        if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
121

122
            if form.remove.data == 'DELETE':
Ossi Laine's avatar
Ossi Laine committed
123
124
125
126
127
128
129

                # This removes all experiment data from the database!

                # Remove research bulletin if it exists
                empty_filevariable = experiment.query.filter_by(
                    idexperiment=exp_id).first()

130
                if empty_filevariable.research_notification_filename is not None:
Ossi Laine's avatar
Ossi Laine committed
131
132
133
134
                    target = os.path.join(
                        APP_ROOT, empty_filevariable.research_notification_filename)

                    if os.path.exists(target):
135
                        os.remove(target)
Ossi Laine's avatar
Ossi Laine committed
136
137
138
139

                # Tables
                remove_forced_id = forced_id.query.filter_by(
                    experiment_idexperiment=exp_id).all()
Ossi Laine's avatar
Ossi Laine committed
140
                remove_rows(remove_forced_id)
141

Ossi Laine's avatar
Ossi Laine committed
142
143
144
145
146
                # background_question_option & background_question & background question answers:
                remove_background_question = background_question.query.filter_by(
                    experiment_idexperiment=exp_id).all()

                # Remove all background questions and all answers given to each bg question
147
                for a in range(len(remove_background_question)):
Ossi Laine's avatar
Ossi Laine committed
148
149
                    remove_background_question_option = background_question_option.query.filter_by(
                        background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
Ossi Laine's avatar
Ossi Laine committed
150
151
                    remove_rows(remove_background_question_option)

Ossi Laine's avatar
Ossi Laine committed
152
153
                    remove_background_question_answers = background_question_answer.query.filter_by(
                        background_question_idbackground_question=remove_background_question[a].idbackground_question).all()
Ossi Laine's avatar
Ossi Laine committed
154
155
                    remove_rows(remove_background_question_answers)

156
157
                    db.session.delete(remove_background_question[a])
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
158
159
160
161

                # Remove all questions and answers
                remove_question = question.query.filter_by(
                    experiment_idexperiment=exp_id).all()
162
                for a in range(len(remove_question)):
Ossi Laine's avatar
Ossi Laine committed
163
164
                    remove_question_answers = answer.query.filter_by(
                        question_idquestion=remove_question[a].idquestion).all()
Ossi Laine's avatar
Ossi Laine committed
165
                    remove_rows(remove_question_answers)
166
167
                    db.session.delete(remove_question[a])
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
168
169
170
171
172

                # Remove experiment embody pictures, questions and answers
                remove_embody_answers = embody_answer.query.filter(embody_answer.page_idpage.in_(list(map(
                    lambda x: x[0], page.query.with_entities(page.idpage).filter_by(experiment_idexperiment=exp_id).all())))).all()
                remove_rows(remove_embody_answers)
Ossi Laine's avatar
Ossi Laine committed
173
174
                remove_embody_questions = embody_question.query.filter_by(
                    experiment_idexperiment=exp_id).all()
Ossi Laine's avatar
Ossi Laine committed
175
176
177

                for a in range(len(remove_embody_questions)):
                    target = APP_ROOT + remove_embody_questions[a].picture
Ossi Laine's avatar
Ossi Laine committed
178
                    if os.path.exists(target) and DEFAULT_EMBODY_PICTURE != remove_embody_questions[a].picture:
Ossi Laine's avatar
Ossi Laine committed
179
180
                        os.remove(target)

Ossi Laine's avatar
Ossi Laine committed
181
182
                remove_rows(remove_embody_questions)

Ossi Laine's avatar
Ossi Laine committed
183
184
185
186
                # Remove all pages and datafiles
                remove_pages = page.query.filter_by(
                    experiment_idexperiment=exp_id).all()

187
                for a in range(len(remove_pages)):
Ossi Laine's avatar
Ossi Laine committed
188
                    if remove_pages[a].type != 'text':
189
                        target = os.path.join(APP_ROOT, remove_pages[a].media)
Ossi Laine's avatar
Ossi Laine committed
190
                        if os.path.exists(target):
191
                            os.remove(target)
Ossi Laine's avatar
Ossi Laine committed
192
193

                    # Now that the files are removed we can delete the page
194
195
                    db.session.delete(remove_pages[a])
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
196

Ossi Laine's avatar
Ossi Laine committed
197
198
199
200
                # Remove all answer_sets and trial_randomization orders
                remove_answer_set = answer_set.query.filter_by(
                    experiment_idexperiment=exp_id).all()

201
                for a in range(len(remove_answer_set)):
Ossi Laine's avatar
Ossi Laine committed
202
203
                    remove_trial_randomizations = trial_randomization.query.filter_by(
                        answer_set_idanswer_set=remove_answer_set[a].idanswer_set).all()
Ossi Laine's avatar
Ossi Laine committed
204
                    remove_rows(remove_trial_randomizations)
205
206
                    db.session.delete(remove_answer_set[a])
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
207
208
209
210

                # Remove experiment table
                remove_experiment = experiment.query.filter_by(
                    idexperiment=exp_id).first()
211
212
                db.session.delete(remove_experiment)
                db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
213
214
215
216

                # Remove empty directories
                os.rmdir(APP_ROOT + '/static/embody_images/' + str(exp_id))
                os.rmdir(APP_ROOT + '/static/experiment_stimuli/' + str(exp_id))
Ossi Laine's avatar
Ossi Laine committed
217

218
219
                flash("Experiment was removed from database!")
                return redirect(url_for('index'))
Ossi Laine's avatar
Ossi Laine committed
220

221
222
223
            else:
                flash("Experiment was not removed!")
                return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
224

225
226
227
228
229
230
231
        return render_template('remove_experiment.html', form=form, exp_id=exp_id)


@experiment_blueprint.route('/publish')
@login_required
def publish():
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
232
233
    publish_experiment = experiment.query.filter_by(
        idexperiment=exp_id).first()
234
235
236
237
238
239
240
241
242
243
    publish_experiment.status = 'Public'
    flash("Changed status to Public")
    db.session.commit()
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/hide')
@login_required
def hide():
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
244
    hide_experiment = experiment.query.filter_by(idexperiment=exp_id).first()
245
246
247
248
249
250
251
252
253
254
    hide_experiment.status = 'Hidden'
    flash("Changed status to Hidden")
    db.session.commit()
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/private')
@login_required
def private():
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
255
256
    private_experiment = experiment.query.filter_by(
        idexperiment=exp_id).first()
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
    private_experiment.status = 'Private'
    flash("Changed status to Private")
    db.session.commit()
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/randomization')
@login_required
def randomization():
    exp_id = request.args.get('exp_id', None)
    status = request.args.get('set')

    if status == 'On':
        flash("Enabled trial randomization")
    elif status == 'Off':
        flash("Disabled trial randomization")

Ossi Laine's avatar
Ossi Laine committed
274
275
    experiment.query.filter_by(
        idexperiment=exp_id).first().randomization = status
276
    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
277

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/set_forced_id')
@login_required
def set_forced_id():
    '''By using forced ID login subjects can only participate to a rating task
    by logging in with a pregenerated ID.'''

    exp_id = request.args.get('exp_id', None)
    status = request.args.get('set')

    if status == 'On':
        flash("Enabled forced ID login")
    elif status == 'Off':
        flash("Disabled forced ID login")

Ossi Laine's avatar
Ossi Laine committed
295
296
    experiment.query.filter_by(
        idexperiment=exp_id).first().use_forced_id = status
297
    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
298

299
300
301
302
303
304
305
306
307
308
309
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/view_forced_id_list', methods=['GET', 'POST'])
@login_required
def view_forced_id_list():
    '''Forced ID login for participants'''

    exp_id = request.args.get('exp_id', None)
    id_list = forced_id.query.filter_by(experiment_idexperiment=exp_id).all()
    form = GenerateIdForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
310

311
312
    if request.method == 'POST' and form.validate():

Ossi Laine's avatar
Ossi Laine committed
313
        for i in range(int(request.form['number'])):
314
            random_id = str(request.form['string']) + str(secrets.token_hex(3))
Ossi Laine's avatar
Ossi Laine committed
315
316
317
318
319
320
321
            check_answer_set = answer_set.query.filter_by(
                session=random_id).first()
            check_forced_id = forced_id.query.filter_by(
                pregenerated_id=random_id).first()

            # here we check if the generated id is found from given answers from the whole database in answer_set table
            # or from forced_id table. If so another id is generated instead to avoid a duplicate
322
            if check_answer_set is not None or check_forced_id is not None:
Ossi Laine's avatar
Ossi Laine committed
323

324
325
                #flash("ID already existed; generated a new one")
                random_id = secrets.token_hex(3)
Ossi Laine's avatar
Ossi Laine committed
326
327
328
329
330
331
332
                check_answer_set = answer_set.query.filter_by(
                    session=random_id).first()
                check_forced_id = forced_id.query.filter_by(
                    pregenerated_id=random_id).first()

            input_id = forced_id(
                experiment_idexperiment=exp_id, pregenerated_id=random_id)
333
334
            db.session.add(input_id)
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
335

336
        return redirect(url_for('experiment.view_forced_id_list', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
337

338
339
340
341
342
343
344
345
346
347
    return render_template('view_forced_id_list.html', exp_id=exp_id, id_list=id_list)


@experiment_blueprint.route('/upload_research_notification', methods=['GET', 'POST'])
@login_required
def upload_research_notification():
    '''Upload research bulletin'''

    exp_id = request.args.get('exp_id', None)
    form = UploadResearchBulletinForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
348

349
350
351
    if request.method == 'POST':
        path = 'static/experiment_stimuli/' + str(exp_id)
        target = os.path.join(APP_ROOT, path)
Ossi Laine's avatar
Ossi Laine committed
352

353
354
        if not os.path.isdir(target):
            os.mkdir(target)
Ossi Laine's avatar
Ossi Laine committed
355
356

        # This returns a list of filenames: request.files.getlist("file")
357
        for file in request.files.getlist("file"):
Ossi Laine's avatar
Ossi Laine committed
358
            # save files in the correct folder
359
360
361
            filename = file.filename
            destination = "/".join([target, filename])
            file.save(destination)
Ossi Laine's avatar
Ossi Laine committed
362
363
364

            # add pages to the db
            db_path = path + str('/') + str(filename)
365
366
367
            bulletin = experiment.query.filter_by(idexperiment=exp_id).first()
            bulletin.research_notification_filename = db_path
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
368

369
370
371
372
373
374
375
376
377
        return redirect(url_for('experiment.view', exp_id=exp_id))

    return render_template('upload_research_notification.html', exp_id=exp_id, form=form)


@experiment_blueprint.route('/remove_research_notification')
@login_required
def remove_research_notification():
    '''Remove research bulletin'''
Ossi Laine's avatar
Ossi Laine committed
378

379
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
380
381
382
383
384
385
    empty_filevariable = experiment.query.filter_by(
        idexperiment=exp_id).first()
    target = os.path.join(
        APP_ROOT, empty_filevariable.research_notification_filename)

    if os.path.exists(target):
386
        os.remove(target)
Ossi Laine's avatar
Ossi Laine committed
387

388
389
    empty_filevariable.research_notification_filename = None
    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
390

391
392
393
394
395
396
397
398
399
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/edit', methods=['GET', 'POST'])
@login_required
def edit():
    '''Edit experiment details'''

    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
400
401
402
    current_experiment = experiment.query.filter_by(
        idexperiment=exp_id).first()

403
404
    form = EditExperimentForm(request.form, obj=current_experiment)
    form.language.default = current_experiment.language
Ossi Laine's avatar
Ossi Laine committed
405

406
    if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
407

408
409
410
        form.populate_obj(current_experiment)
        db.session.commit()

Ossi Laine's avatar
Ossi Laine committed
411
        return redirect(url_for('experiment.view', exp_id=exp_id))
412

Ossi Laine's avatar
Ossi Laine committed
413
    return render_template('edit_experiment.html', form=form, exp_id=exp_id)
414
415
416
417
418
419
420


# Background questions:

@experiment_blueprint.route('/add_bg_question', methods=['GET', 'POST'])
@login_required
def add_bg_question():
Ossi Laine's avatar
Ossi Laine committed
421

422
423
424
425
426
427
    exp_id = request.args.get('exp_id', None)
    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))
Ossi Laine's avatar
Ossi Laine committed
428

429
430
    else:
        form = CreateBackgroundQuestionForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
431

432
        if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
433
434

            # Split the form data into a list that separates questions followed by the corresponding options
435
            str = form.bg_questions_and_options.data
436
            str_list = str.split('/n')
Ossi Laine's avatar
Ossi Laine committed
437
438

            # Iterate through the questions and options list
439
            for a in range(len(str_list)):
Ossi Laine's avatar
Ossi Laine committed
440
                # Split the list cells further into questions and options
441
                list = str_list[a].split(';')
Ossi Laine's avatar
Ossi Laine committed
442
443

                # Input the first item of the list as a question in db and the items followed by that as options for that question
444
445
                for x in range(len(list)):
                    if x == 0:
Ossi Laine's avatar
Ossi Laine committed
446
447
                        add_bgquestion = background_question(
                            background_question=list[x], experiment_idexperiment=exp_id)
448
449
450
                        db.session.add(add_bgquestion)
                        db.session.commit()
                    else:
Ossi Laine's avatar
Ossi Laine committed
451
452
                        add_bgq_option = background_question_option(
                            background_question_idbackground_question=add_bgquestion.idbackground_question, option=list[x])
453
454
                        db.session.add(add_bgq_option)
                        db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
455
456
457

            return redirect(url_for('experiment.view', exp_id=exp_id))

458
459
460
461
462
463
        return render_template('add_bg_question.html', form=form)


@experiment_blueprint.route('/edit_bg_question', methods=['GET', 'POST'])
@login_required
def edit_bg_question():
Ossi Laine's avatar
Ossi Laine committed
464

465
466
    bg_question_id = request.args.get('idbackground_question', None)

Ossi Laine's avatar
Ossi Laine committed
467
468
469
470
471
    # Search for the right question and for the right options. Form a string of those separated with ";" and insert the
    # formed string into the edit form
    current_bg_question = background_question.query.filter_by(
        idbackground_question=bg_question_id).first()
    exp_id = current_bg_question.experiment_idexperiment
472
    question_string = current_bg_question.background_question
Ossi Laine's avatar
Ossi Laine committed
473
474
475
476
477
478
479
    options = background_question_option.query.filter_by(
        background_question_idbackground_question=bg_question_id).all()

    for o in range(len(options)):
        question_string = str(question_string) + \
            str("; ") + str(options[o].option)

480
481
482
    form = EditBackgroundQuestionForm(request.form)
    form.bg_questions_and_options.data = question_string

Ossi Laine's avatar
Ossi Laine committed
483
    # After user chooses to update the question and options lets replace the old question and options with the ones from the form
484
    if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
485
        # Explode the string with new values from the form
486
487
        form_values = form.new_values.data
        form_values_list = form_values.split(';')
Ossi Laine's avatar
Ossi Laine committed
488
489

        # Check and remove possible whitespaces from string beginnings with lstrip
490
491
        for x in range(len(form_values_list)):
            form_values_list[x] = form_values_list[x].lstrip()
Ossi Laine's avatar
Ossi Laine committed
492
493

        # Cycle through strings and update db
494
        for x in range(len(form_values_list)):
Ossi Laine's avatar
Ossi Laine committed
495
496

            # Replace question and update the object to database
497
            if x == 0:
Ossi Laine's avatar
Ossi Laine committed
498
                current_bg_question.background_question = form_values_list[x]
499
                db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
500
501

                # Delete old options from db
502
503
504
                for o in options:
                    db.session.delete(o)
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
505
506

            # Insert new options to db
507
            else:
Ossi Laine's avatar
Ossi Laine committed
508
509
                new_option = background_question_option(
                    background_question_idbackground_question=current_bg_question.idbackground_question, option=form_values_list[x])
510
511
                db.session.add(new_option)
                db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
512

513
        return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
514

515
516
517
518
519
520
521
522
523
524
525
526
527
    return render_template('edit_bg_question.html', form=form, exp_id=exp_id)


@experiment_blueprint.route('/remove_bg_question')
@login_required
def remove_bg_question():

    exp_id = request.args.get('exp_id', None)
    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))
Ossi Laine's avatar
Ossi Laine committed
528

529
    else:
530

531
        remove_id = request.args.get('idbackground_question', None)
Ossi Laine's avatar
Ossi Laine committed
532
533
534
535
        remove_options = background_question_option.query.filter_by(
            background_question_idbackground_question=remove_id).all()

        for a in range(len(remove_options)):
536
537
            db.session.delete(remove_options[a])
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
538
539
540
541

        remove_question = background_question.query.filter_by(
            idbackground_question=remove_id).first()

542
543
544
        db.session.delete(remove_question)
        db.session.commit()

Ossi Laine's avatar
Ossi Laine committed
545
    return redirect(url_for('experiment.view', exp_id=exp_id))
546
547


548
549
550
551
552
@experiment_blueprint.route('/set_embody')
@login_required
def set_embody():
    '''Enable/disable embody tool'''
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
553
    exp = experiment.query.filter_by(idexperiment=exp_id).first()
554
555
556
557
    exp.embody_enabled = (True if exp.embody_enabled == False else False)
    return redirect(url_for('experiment.view', exp_id=exp_id))


Ossi Laine's avatar
Ossi Laine committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
@experiment_blueprint.route('/add_embody', methods=['GET', 'POST'])
@login_required
def add_embody():

    exp_id = request.args.get('exp_id', None)
    default = request.args.get('default', None)
    exp_info = experiment.query.filter_by(idexperiment=exp_id).first()

    if exp_info.status != 'Hidden':
        flash("Experiment is public. Cannot modify structure.")
        return redirect(url_for('experiment.view', exp_id=exp_id))
    elif default:

        # TODO: check if default image already added

Ossi Laine's avatar
Ossi Laine committed
573
574
575
        default_embody = embody_question(
            experiment_idexperiment=exp_id, picture=DEFAULT_EMBODY_PICTURE, question=DEFAULT_EMBODY_QUESTION)
        db.session.add(default_embody)
Ossi Laine's avatar
Ossi Laine committed
576
        exp_info.embody_enabled = 1
Ossi Laine's avatar
Ossi Laine committed
577
        db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
578
579
580
581
582
583
584
585
586
        return redirect(url_for('experiment.view', exp_id=exp_id))

    else:
        form = CreateEmbodyForm(request.form)

        if request.method == 'POST' and form.validate():
            picture = request.files.get("picture")
            question = request.form.get("question")

Ossi Laine's avatar
Ossi Laine committed
587
            # get filename
Ossi Laine's avatar
Ossi Laine committed
588
589
            filename = secure_filename(picture.filename)
            path = 'static/embody_images/' + str(exp_id)
Ossi Laine's avatar
Ossi Laine committed
590
            db_path = '/' + path + str('/') + str(filename)
Ossi Laine's avatar
Ossi Laine committed
591
592
593
594
595
596
597
598
599
600
            target = os.path.join(APP_ROOT, path)

            # create folder with experiment id (if it does not exist)
            if not os.path.isdir(target):
                os.mkdir(target)

            # save file to server
            destination = "/".join([target, filename])
            picture.save(destination)

Ossi Laine's avatar
Ossi Laine committed
601
602
603
            # add pages to the db
            new_embody = embody_question(
                experiment_idexperiment=exp_id, question=question, picture=db_path)
Ossi Laine's avatar
Ossi Laine committed
604
605
606
            db.session.add(new_embody)
            exp_info.embody_enabled = 1
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
607
            return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
608
609
610
611

        return render_template('add_embody.html', form=form)


612
613
614
@experiment_blueprint.route('/add_questions', methods=['GET', 'POST'])
@login_required
def add_questions():
Ossi Laine's avatar
Ossi Laine committed
615

616
617
618
619
620
621
622
623
    exp_id = request.args.get('exp_id', None)
    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:
        form = CreateQuestionForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
624

625
        if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
626

627
628
            str = form.questions_and_options.data
            str_list = str.split('/n')
Ossi Laine's avatar
Ossi Laine committed
629
630

            for a in range(len(str_list)):
631
                list = str_list[a].split(';')
Ossi Laine's avatar
Ossi Laine committed
632
633

                # If there are the right amount of values for the slider input values
634
                if len(list) == 3:
Ossi Laine's avatar
Ossi Laine committed
635
636
                    add_question = question(
                        experiment_idexperiment=exp_id, question=list[0], left=list[1], right=list[2])
637
638
                    db.session.add(add_question)
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
639
640

                # If slider has too many or too litlle parameters give an error and redirect back to input form
641
                else:
Ossi Laine's avatar
Ossi Laine committed
642
643
                    flash(
                        "Error Each slider must have 3 parameters separated by ; Some slider has:")
644
645
                    flash(len(list))
                    return redirect(url_for('create.experiment_questions', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
646
647
648

            return redirect(url_for('experiment.view', exp_id=exp_id))

649
650
651
652
653
654
        return render_template('add_questions.html', form=form)


@experiment_blueprint.route('/edit_question', methods=['GET', 'POST'])
@login_required
def edit_question():
Ossi Laine's avatar
Ossi Laine committed
655

656
657
658
    question_id = request.args.get('idquestion', None)
    current_question = question.query.filter_by(idquestion=question_id).first()
    form = EditQuestionForm(request.form, obj=current_question)
Ossi Laine's avatar
Ossi Laine committed
659

660
    if request.method == 'POST' and form.validate():
Ossi Laine's avatar
Ossi Laine committed
661

662
663
        form.populate_obj(current_question)
        db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
664

665
        return redirect(url_for('experiment.view', exp_id=current_question.experiment_idexperiment))
Ossi Laine's avatar
Ossi Laine committed
666

667
668
669
670
671
672
673
674
675
676
677
678
679
    return render_template('edit_question.html', form=form)


@experiment_blueprint.route('/remove_question')
@login_required
def remove_question():

    exp_id = request.args.get('exp_id', None)
    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))
Ossi Laine's avatar
Ossi Laine committed
680

681
682
683
    else:

        remove_id = request.args.get('idquestion', None)
Ossi Laine's avatar
Ossi Laine committed
684
685
        remove_question = question.query.filter_by(
            idquestion=remove_id).first()
Ossi Laine's avatar
Ossi Laine committed
686
687

        # remove answers before removing questions
Ossi Laine's avatar
Ossi Laine committed
688
689
        remove_answers = answer.query.filter_by(
            question_idquestion=remove_question.idquestion).all()
Ossi Laine's avatar
Ossi Laine committed
690
691
692
        for a in range(len(remove_answers)):
            db.session.delete(remove_answers[a])
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
693

Ossi Laine's avatar
Ossi Laine committed
694
695
        db.session.delete(remove_question)
        db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
696

Ossi Laine's avatar
Ossi Laine committed
697
698
699
700
701
702
703
704
705
706
707
708
709
    return redirect(url_for('experiment.view', exp_id=exp_id))


@experiment_blueprint.route('/remove_embody')
@login_required
def remove_embody():

    exp_id = request.args.get('exp_id', None)
    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))
Ossi Laine's avatar
Ossi Laine committed
710

Ossi Laine's avatar
Ossi Laine committed
711
712
    else:
        remove_id = request.args.get('idembody', None)
Ossi Laine's avatar
Ossi Laine committed
713
714
        remove_question = embody_question.query.filter_by(
            idembody=remove_id).first()
Ossi Laine's avatar
Ossi Laine committed
715
716
717
718
719

        # remove embody image from server
        if DEFAULT_EMBODY_PICTURE != remove_question.picture:
            os.remove(APP_ROOT + remove_question.picture)

Ossi Laine's avatar
Ossi Laine committed
720
721
        remove_answers = embody_answer.query.filter_by(
            embody_question_idembody=remove_question.idembody).all()
Ossi Laine's avatar
Ossi Laine committed
722
        remove_rows(remove_answers)
723
724
        db.session.delete(remove_question)
        db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
725

Ossi Laine's avatar
Ossi Laine committed
726
727
    question_count = embody_question.query.filter_by(
        experiment_idexperiment=exp_id).count()
Ossi Laine's avatar
Ossi Laine committed
728
729
730
731

    if question_count == 0:
        exp_status.embody_enabled = 0
        db.session.commit()
732

Ossi Laine's avatar
Ossi Laine committed
733
    return redirect(url_for('experiment.view', exp_id=exp_id))
734
735
736
737
738
739
740
741
742
743


# Stimuli:

@experiment_blueprint.route('/add_stimuli', methods=['GET', 'POST'])
@login_required
def add_stimuli():

    exp_id = request.args.get('exp_id', None)
    exp_status = experiment.query.filter_by(idexperiment=exp_id).first()
Ossi Laine's avatar
Ossi Laine committed
744

745
746
747
748
    if exp_status.status != 'Hidden':
        flash("Experiment is public. Cannot modify structure.")
        return redirect(url_for('experiment.view', exp_id=exp_id))
    else:
Ossi Laine's avatar
Ossi Laine committed
749
750
751
752
        # If there are no pages set for the experiment lets reroute user to create experiment stimuli upload instead
        is_there_any_stimuli = page.query.filter_by(
            experiment_idexperiment=exp_id).first()

753
754
        if is_there_any_stimuli is None:
            return redirect(url_for('create.experiment_upload_stimuli', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
755

756
757
        stimulus_type = request.args.get('stimulus_type', None)
        form = UploadStimuliForm(request.form)
Ossi Laine's avatar
Ossi Laine committed
758

759
760
761
762
        if request.method == 'POST':
            if stimulus_type == 'text':
                string = form.text.data
                str_list = string.split('/n')
Ossi Laine's avatar
Ossi Laine committed
763

764
                for a in range(len(str_list)):
Ossi Laine's avatar
Ossi Laine committed
765
766
                    add_text_stimulus = page(
                        experiment_idexperiment=exp_id, type='text', text=str_list[a], media='none')
767
768
                    db.session.add(add_text_stimulus)
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
769
770
771

                return redirect(url_for('experiment.view', exp_id=exp_id))

772
            else:
Ossi Laine's avatar
Ossi Laine committed
773
774
                # Upload stimuli into /static/experiment_stimuli/exp_id folder
                # Create the pages for the stimuli by inserting experiment_id, stimulus type, text and names of the stimulus files (as a path to the folder)
775
776
                path = 'static/experiment_stimuli/' + str(exp_id)
                target = os.path.join(APP_ROOT, path)
Ossi Laine's avatar
Ossi Laine committed
777

778
779
                if not os.path.isdir(target):
                    os.mkdir(target)
Ossi Laine's avatar
Ossi Laine committed
780
781

                # This returns a list of filenames: request.files.getlist("file")
782
                for file in request.files.getlist("file"):
Ossi Laine's avatar
Ossi Laine committed
783
                    # save files in the correct folder
784
785
786
                    filename = file.filename
                    destination = "/".join([target, filename])
                    file.save(destination)
Ossi Laine's avatar
Ossi Laine committed
787

Ossi Laine's avatar
Ossi Laine committed
788
789
790
791
                    # add pages to the db
                    db_path = path + str('/') + str(filename)
                    new_page = page(experiment_idexperiment=exp_id,
                                    type=form.type.data, media=db_path)
792
793
                    db.session.add(new_page)
                    db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
794

795
                return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
796

797
798
799
800
801
802
803
804
805
806
807
808
            return redirect(url_for('experiment.view', exp_id=exp_id))

        return render_template('add_stimuli.html', form=form, stimulus_type=stimulus_type)


@experiment_blueprint.route('/edit_stimuli', methods=['GET', 'POST'])
@login_required
def edit_stimuli():

    exp_id = request.args.get('exp_id', None)
    page_id = request.args.get('idpage', None)
    edit_page = page.query.filter_by(idpage=page_id).first()
809
810
811
    form = EditPageForm(request.form)
    #form = EditPageForm(request.form, obj=edit_page)

Ossi Laine's avatar
Ossi Laine committed
812
    # TODO: replacing image not working -> form not getting validated for some reaseon !!
813

814
    if request.method == 'POST' and form.validate():
815
        print("POST IMAGE")
Ossi Laine's avatar
Ossi Laine committed
816
        # If the stimulus type is not text, then the old stimulus file is deleted from os and replaced
817
        if edit_page.type != 'text':
Ossi Laine's avatar
Ossi Laine committed
818
            # remove old file
819
820
821
            target = os.path.join(APP_ROOT, edit_page.media)
            os.remove(target)

Ossi Laine's avatar
Ossi Laine committed
822
            # upload new file
823
824
            path = 'static/experiment_stimuli/' + str(exp_id)
            target = os.path.join(APP_ROOT, path)
Ossi Laine's avatar
Ossi Laine committed
825

826
827
            if not os.path.isdir(target):
                os.mkdir(target)
Ossi Laine's avatar
Ossi Laine committed
828
829

            # This returns a list of filenames: request.files.getlist("file")
830
            for file in request.files.getlist("file"):
Ossi Laine's avatar
Ossi Laine committed
831
                # save files in the correct folder
832
833
834
                filename = file.filename
                destination = "/".join([target, filename])
                file.save(destination)
Ossi Laine's avatar
Ossi Laine committed
835
836
837
838

                # update db object
                db_path = path + str('/') + str(filename)
                edit_page.media = db_path
839
840
                db.session.commit()

Ossi Laine's avatar
Ossi Laine committed
841
        # If editing text stimulus no need for filehandling
842
        else:
Ossi Laine's avatar
Ossi Laine committed
843
844
845
            form.populate_obj(edit_page)
            db.session.commit()

846
        return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
847

848
849
850
851
852
853
854
855
856
857
858
    return render_template('edit_stimuli.html', form=form, edit_page=edit_page)


@experiment_blueprint.route('/remove_stimuli')
@login_required
def remove_stimuli():

    exp_id = request.args.get('exp_id', None)
    exp_status = experiment.query.filter_by(idexperiment=exp_id).first()

    if exp_status.status != 'Hidden':
Ossi Laine's avatar
Ossi Laine committed
859

860
861
862
        flash("Experiment is public. Cannot modify structure.")

        return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
863

864
    else:
Ossi Laine's avatar
Ossi Laine committed
865

866
867
        remove_id = request.args.get('idpage', None)
        remove_page = page.query.filter_by(idpage=remove_id).first()
Ossi Laine's avatar
Ossi Laine committed
868
869
870
871
872
        experiment_pages = page.query.filter_by(
            experiment_idexperiment=exp_id).all()

        # if stimulustype is text, the stimulus itself is text on the database, other stimulus types are real files
        # on the server and need to be deleted
873
        if remove_page.type != 'text':
Ossi Laine's avatar
Ossi Laine committed
874
875

            # helper variable
876
            do_not_delete_file = 'False'
Ossi Laine's avatar
Ossi Laine committed
877
878

            # if the file to be deleted is in duplicate use of another page then we won't delete the file
879
880
881
            for a in range(len(experiment_pages)):
                if experiment_pages[a].media == remove_page.media and experiment_pages[a].idpage != remove_page.idpage:
                    do_not_delete_file = 'True'
Ossi Laine's avatar
Ossi Laine committed
882
883

            # If no other page is using the file then lets remove it
884
885
886
            if do_not_delete_file == 'False':
                target = os.path.join(APP_ROOT, remove_page.media)
                os.remove(target)
Ossi Laine's avatar
Ossi Laine committed
887

Ossi Laine's avatar
Ossi Laine committed
888
            # remove slider answer from this page
Ossi Laine's avatar
Ossi Laine committed
889
890
            remove_question_answers = answer.query.filter_by(
                page_idpage=remove_page.idpage).all()
Ossi Laine's avatar
Ossi Laine committed
891
892
893
            remove_rows(remove_question_answers)

            # remove embody answer from this page
Ossi Laine's avatar
Ossi Laine committed
894
895
            remove_embody_answers = embody_answer.query.filter_by(
                page_idpage=remove_page.idpage).all()
Ossi Laine's avatar
Ossi Laine committed
896
897
            remove_rows(remove_embody_answers)

898
899
            db.session.delete(remove_page)
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
900

901
            return redirect(url_for('experiment.view', exp_id=exp_id))
Ossi Laine's avatar
Ossi Laine committed
902

903
        if remove_page.type == 'text':
Ossi Laine's avatar
Ossi Laine committed
904

Ossi Laine's avatar
Ossi Laine committed
905
            # remove slider answer from this page
Ossi Laine's avatar
Ossi Laine committed
906
907
            remove_question_answers = answer.query.filter_by(
                page_idpage=remove_page.idpage).all()
Ossi Laine's avatar
Ossi Laine committed
908
909
910
            remove_rows(remove_question_answers)

            # remove embody answer from this page
Ossi Laine's avatar
Ossi Laine committed
911
912
            remove_embody_answers = embody_answer.query.filter_by(
                page_idpage=remove_page.idpage).all()
Ossi Laine's avatar
Ossi Laine committed
913
914
            remove_rows(remove_embody_answers)

915
916
            db.session.delete(remove_page)
            db.session.commit()
Ossi Laine's avatar
Ossi Laine committed
917

918
919
            return redirect(url_for('experiment.view', exp_id=exp_id))

Ossi Laine's avatar
Ossi Laine committed
920
        return redirect(url_for('experiment.view', exp_id=exp_id))
921
922
923
924
925
926
927


# Misc:

@experiment_blueprint.route('/statistics')
@login_required
def statistics():
928
929

    # TODO: Answers are in normal order although questions might be in randomized order
Ossi Laine's avatar
Ossi Laine committed
930

931
    exp_id = request.args.get('exp_id', None)
Ossi Laine's avatar
Ossi Laine committed
932
933
934
935
936
937

    experiment_info = experiment.query.filter_by(idexperiment=exp_id).all()
    participants = answer_set.query.filter_by(
        experiment_idexperiment=exp_id).all()

    # started and finished ratings counters
938
939
940
941
942
943
944
    started_ratings = answer_set.query.filter_by(
        experiment_idexperiment=exp_id).count()
    experiment_page_count = page.query.filter_by(
        experiment_idexperiment=exp_id).count()
    finished_ratings = answer_set.query.filter(and_(
        answer_set.answer_counter == experiment_page_count, answer_set.experiment_idexperiment == exp_id)).count()

Ossi Laine's avatar
Ossi Laine committed
945
946
947
948
949
950
    # Rating task headers
    question_headers = question.query.filter_by(
        experiment_idexperiment=exp_id).all()
    stimulus_headers = page.query.filter_by(
        experiment_idexperiment=exp_id).all()

951
952
953
954
    pages = page.query.filter_by(experiment_idexperiment=exp_id).all()
    questions = question.query.filter_by(experiment_idexperiment=exp_id).all()
    pages_and_questions = {}

955
956
    '''

957
958
    for p in pages:
        questions_list = [(p.idpage, a.idquestion) for a in questions]
Ossi Laine's avatar
Ossi Laine committed
959
960
961
962
        pages_and_questions[p.idpage] = questions_list

    # List of answers per participant in format question Stimulus ID/Question ID
    # those are in answer table as page_idpage and question_idquestion respectively
963
    slider_answers = {}
964
    for participant in participants:
Ossi Laine's avatar
Ossi Laine committed
965

966
967
        if int(participant.answer_counter) == 0:
            continue
Ossi Laine's avatar
Ossi Laine committed
968

969
970
971
972
        answers = answer.query.filter_by(
            answer_set_idanswer_set=participant.idanswer_set)\
            .order_by(answer.page_idpage)\
            .all()
973

974
975
976
977
978
979
980
        # flatten pages and questions to list of tuples (page_id, question_id)
        _questions = [
            item for sublist in pages_and_questions.values() for item in sublist]


        slider_answers[participant.session] = map_answers_to_questions(
            answers, _questions)
981
982
983
984
985

    mean = get_mean_from_slider_answers(slider_answers)
    # slider_answers['mean'] = get_mean_from_slider_answers(slider_answers)

    slider_answers = {
986
        'mean': mean
987
    }
988

989
990
991
992
    '''

    slider_answers = {}

Ossi Laine's avatar
Ossi Laine committed
993
    # Background question answers
Ossi Laine's avatar
Ossi Laine committed
994
995
    bg_questions = background_question.query.filter_by(
        experiment_idexperiment=exp_id).all()
996
997
998
    bg_answers_for_participants = {}

    for participant in participants:
999
        if participant.answer_counter > 0:
1000
1001
1002
1003
            bg_answers = background_question_answer.query.filter_by(
                answer_set_idanswer_set=participant.idanswer_set).all()
            bg_answers_list = [(a.answer) for a in bg_answers]
            bg_answers_for_participants[participant.session] = bg_answers_list
Ossi Laine's avatar
Ossi Laine committed
1004
1005
1006
1007
1008

    # embody questions
    embody_questions = embody_question.query.filter_by(
        experiment_idexperiment=exp_id).all()

Ossi Laine's avatar
Ossi Laine committed
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
    return render_template('experiment_statistics.html',
                           experiment_info=experiment_info,
                           participants_and_answers=slider_answers,
                           pages_and_questions=pages_and_questions,
                           bg_questions=bg_questions,
                           bg_answers_for_participants=bg_answers_for_participants,
                           started_ratings=started_ratings,
                           finished_ratings=finished_ratings,
                           question_headers=question_headers,
                           stimulus_headers=stimulus_headers,
Ossi Laine's avatar
Ossi Laine committed
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
                           embody_questions=embody_questions)


@experiment_blueprint.route('/download_csv')
def download_csv():
    exp_id = request.args.get('exp_id', None)
    path = request.args.get('path', None)

    filename = "experiment_{}_{}.csv".format(
        exp_id, date.today().strftime("%Y-%m-%d"))

    path = '/tmp/' + path

    try:
        return send_file(path,
                         mimetype='text/csv',
                         as_attachment=True,
                         attachment_filename=filename)

    finally:
        os.remove(path)
1040
1041


root's avatar
root committed
1042
1043
1044
1045
1046
1047
1048
def remove_rows(rows):
    """Remove list of rows from database"""
    for a in range(len(rows)):
        db.session.delete(rows[a])
        db.session.commit()


1049
@socketio.on('connect', namespace="/create_embody")
1050
def start_create_embody():
1051
1052
    emit('success', {'connection': 'on'})

Ossi Laine's avatar
Ossi Laine committed
1053

1054
@socketio.on('draw', namespace="/create_embody")
1055
1056
1057
def create_embody(meta):
    page = meta["page"]
    embody = meta["embody"]
Ossi Laine's avatar
Ossi Laine committed
1058
    img_path = embody_plot.get_coordinates(page, embody)
root's avatar
root committed
1059
    app.logger.info(img_path)
Ossi Laine's avatar
Ossi Laine committed
1060
1061
    emit('end', {'path': img_path})

1062