diff --git a/app/create/templates/create_experiment_upload_stimuli.html b/app/create/templates/create_experiment_upload_stimuli.html index 9a7d9e3af8b02d44d7afc50c79784ca63bc6346b..4823fc3be7fca4d79c0311a41f34231182b6d6de 100644 --- a/app/create/templates/create_experiment_upload_stimuli.html +++ b/app/create/templates/create_experiment_upload_stimuli.html @@ -70,6 +70,6 @@ </form> - +<script src="{{ url_for('static', filename='js/enable_button.js') }}" ></script> {% endblock %} \ No newline at end of file diff --git a/app/create/views.py b/app/create/views.py index 3e3bb9c973af719a533c6b46e1da3d31d4ab398c..7f49fe85d9245cd4b644992c01b9f77dce955b78 100644 --- a/app/create/views.py +++ b/app/create/views.py @@ -106,17 +106,12 @@ def experiment_bgquestions(): @create_blueprint.route('/experiment_questions', methods=['GET', 'POST']) @login_required def experiment_questions(): - - # TODO: add embody -type here + """Add slider questions""" exp_id = request.args.get('exp_id', None) form = CreateQuestionForm(request.form) - print(form) - print(form.validate()) - if request.method == 'POST': - #if request.method == 'POST' and form.validate(): str = form.questions_and_options.data str_list = str.split('/n') @@ -144,15 +139,13 @@ 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 render_template('create_experiment_questions.html', form=form) - @create_blueprint.route('/experiment_upload_stimuli', methods=['GET', 'POST']) @login_required def experiment_upload_stimuli(): + """Upload stimuli""" exp_id = request.args.get('exp_id', None) form = UploadStimuliForm(request.form) diff --git a/app/experiment/templates/add_stimuli.html b/app/experiment/templates/add_stimuli.html index 113b8286f786b94250bd82da16f849130bbb3247..3c71eba08a07e0ae85890d61d105d4fc29369bea 100644 --- a/app/experiment/templates/add_stimuli.html +++ b/app/experiment/templates/add_stimuli.html @@ -18,20 +18,21 @@ <span class="input-group-text">Upload:</span> </div> <div class="custom-file"> - <input type="file" class="custom-file-input" id="file" name="file" accept="audio/*,image/*, .mp4" multiple> - - <!-- TODO add hidden type field { stimulus_type } else this will be None!! --> - - <label class="custom-file-label" for="upload_file">Choose file</label> + <input type="file" class="custom-file-input" id="file" name="file" accept="audio/*,image/*, .mp4" multiple> + <label class="custom-file-label" for="upload_file">Choose file</label> </div> </div> {% endif %} + <input style="display:none;" type="radio" name="type" value="{{ stimulus_type }}" checked > - <br> - <button type="submit" class="btn btn-primary">Submit</button> - <a class="btn btn-primary" href="{{ request.referrer }}" role="button">Cancel</a> + <br> + <button type="submit" class="btn btn-primary">Submit</button> + <a class="btn btn-primary" href="{{ request.referrer }}" role="button">Cancel</a> </form> + +<script src="{{ url_for('static', filename='js/enable_button.js') }}" ></script> + {% endblock %} diff --git a/app/experiment/templates/edit_stimuli.html b/app/experiment/templates/edit_stimuli.html index e234e0df3067b62dc9f6cabec7a35e06d35bfacc..5ce72c3368397ed271cbacbed305453dcf3862e6 100644 --- a/app/experiment/templates/edit_stimuli.html +++ b/app/experiment/templates/edit_stimuli.html @@ -24,8 +24,16 @@ </div> {% endif %} <br> - <button type="submit" class="btn btn-primary">Submit</button> - <a class="btn btn-primary" href="{{ request.referrer }}" role="button">Cancel</a> + + <!-- this should be added in order to get the right stimulus type to database.. stimulus_type is not defined yet --> + <input style="display:none;" type="radio" name="type" value="{{ stimulus_type }}" checked > + + <button type="submit" class="btn btn-primary">Submit</button> + <a class="btn btn-primary" href="{{ request.referrer }}" role="button">Cancel</a> + </form> + +<script src="{{ url_for('static', filename='js/enable_button.js') }}" ></script> + {% endblock %} diff --git a/app/experiment/templates/view_experiment.html b/app/experiment/templates/view_experiment.html index 41fec30f3171d8d896f458247d295a2699f75daf..b3ee1a19262734852c5b61c91df1b477fd603e86 100644 --- a/app/experiment/templates/view_experiment.html +++ b/app/experiment/templates/view_experiment.html @@ -398,7 +398,6 @@ <table class="table"> <tbody> <td class="text-nowrap align-bottom text-right col-8"> - {{ mtype }} <a class="btn btn-primary btn-sm btn-info" href="{{ url_for('experiment.add_stimuli', exp_id=exp_id, stimulus_type=mtype.type) }}" role="button">Add more</a> </td> </tbody> diff --git a/app/experiment/views.py b/app/experiment/views.py index d6a6344a4be24f93f8403e54f4f67756d51bb82f..40aaee1a78b7dc224b1a77f462f4e17c2864b4ea 100644 --- a/app/experiment/views.py +++ b/app/experiment/views.py @@ -161,24 +161,27 @@ def remove(): lambda x: x[0], page.query.with_entities(page.idpage).filter_by(experiment_idexperiment=exp_id).all())))).all() remove_rows(remove_embody_answers) remove_embody_questions = embody_question.query.filter_by(experiment_idexperiment=exp_id).all() + + for a in range(len(remove_embody_questions)): + target = APP_ROOT + remove_embody_questions[a].picture + if os.path.exists(target) and DEFAULT_EMBODY_PICTURE != remove_embody_questions[a].picture: + os.remove(target) + remove_rows(remove_embody_questions) #Remove all pages and datafiles remove_pages = page.query.filter_by(experiment_idexperiment=exp_id).all() for a in range(len(remove_pages)): - if remove_pages[a].type == 'text': - db.session.delete(remove_pages[a]) - db.session.commit() - else: + if remove_pages[a].type != 'text': target = os.path.join(APP_ROOT, remove_pages[a].media) - if os.path.exists(target): os.remove(target) #Now that the files are removed we can delete the page db.session.delete(remove_pages[a]) db.session.commit() + #Remove all answer_sets and trial_randomization orders remove_answer_set = answer_set.query.filter_by(experiment_idexperiment=exp_id).all() @@ -193,6 +196,10 @@ def remove(): remove_experiment = experiment.query.filter_by(idexperiment=exp_id).first() db.session.delete(remove_experiment) db.session.commit() + + # Remove empty directories + os.rmdir(APP_ROOT + '/static/embody_images/' + str(exp_id)) + os.rmdir(APP_ROOT + '/static/experiment_stimuli/' + str(exp_id)) flash("Experiment was removed from database!") return redirect(url_for('index')) @@ -661,9 +668,6 @@ def remove_question(): @login_required def remove_embody(): - # TODO: if len(embody_questions) == 0: - # set embody_enabled to False (in experiment) - exp_id = request.args.get('exp_id', None) exp_status = experiment.query.filter_by(idexperiment=exp_id).first() @@ -683,6 +687,13 @@ def remove_embody(): remove_rows(remove_answers) db.session.delete(remove_question) db.session.commit() + + + question_count = embody_question.query.filter_by(experiment_idexperiment=exp_id).count() + + if question_count == 0: + exp_status.embody_enabled = 0 + db.session.commit() return redirect(url_for('experiment.view', exp_id=exp_id)) @@ -737,7 +748,7 @@ def add_stimuli(): filename = file.filename destination = "/".join([target, filename]) file.save(destination) - + #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) @@ -761,16 +772,8 @@ def edit_stimuli(): form = EditPageForm(request.form) #form = EditPageForm(request.form, obj=edit_page) - # TODO: replacing image not working!! + # TODO: replacing image not working -> form not getting validated for some reaseon !! - print(request.files.getlist("file")) - print("errors:", form.errors) - print("form:", form.__dict__) - print("type:", form.type.data) - print("media:", form.media.data) - print("file:", form.file.data) - print("text:", form.text.data) - if request.method == 'POST' and form.validate(): print("POST IMAGE") #If the stimulus type is not text, then the old stimulus file is deleted from os and replaced diff --git a/app/routes.py b/app/routes.py index 0db3d788d5fd3fd4f9a6c58a20b5095f3d5b7203..c740da9fb597d7df9129c7dd12788268f195e4cb 100644 --- a/app/routes.py +++ b/app/routes.py @@ -111,11 +111,11 @@ def participant_session(): exp_status = experiment.query.filter_by(idexperiment=session['exp_id']).first() - #create answer set for the participant in the database + # 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 + # Check which question type is the first in answer_set (embody is first if enabled) answer_set_type = 'slider' if exp_status.embody_enabled: answer_set_type = 'embody' @@ -167,7 +167,6 @@ def participant_session(): 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() diff --git a/app/static/js/canvas.js b/app/static/js/canvas.js index 9f30042ff1ad6e8ff3b5a94c00484df7e1be7e3c..d98fb76f6010fcbe15eb69f4b1c0d11e702a0749 100644 --- a/app/static/js/canvas.js +++ b/app/static/js/canvas.js @@ -76,7 +76,6 @@ $(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; @@ -95,10 +94,6 @@ $(document).ready(function()Â { }); - // TODO: changing drawradius doesnt affect to the saved datapoints !!! - // Bigger brush should make more datapoints compared to smaller ones. - // add brush size to click arry -> {x:[...], y:[...], size:[...]} ?? - $("#embody-canvas").bind('DOMMouseScroll', changeBrushSize) // DOMMouseScroll is only for firefox //$(".canvas-container").bind('wheel', changeBrushSize) @@ -209,14 +204,6 @@ $(document).ready(function()Â { } function redraw() { - /* - Check: - https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation - - TODO: It's possible to use only one mask image propably - */ - //context.globalCompositeOperation='destination-over' // - //context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas lastX = clickX[clickX.length - 1] lastY = clickY[clickY.length - 1] diff --git a/app/static/js/enable_button.js b/app/static/js/enable_button.js index b5c1da789597aa8300b3dfe12e9ab278d456d158..3bd23427de2e72cdcb8650e0a733f19fb8b7c4a1 100644 --- a/app/static/js/enable_button.js +++ b/app/static/js/enable_button.js @@ -2,11 +2,18 @@ $( document ).ready( function() { - $('#embody-file-input').change(function(filename) { + $('#embody-file-input, .custom-file-input').change(function(filename) { + + var label = $(this).next('.custom-file-label'); - var fileName = $(this).val(); //replace the "Choose a file" label - $(this).next('.custom-file-label').html(fileName); + $.each($(this).prop("files") ,function (index, value) { + if (index === 0) { + label.html(value.name) + } else { + label.append(', ' + value.name) + } + }) $('.submit-file').prop("disabled", false); }) diff --git a/app/task/templates/task.html b/app/task/templates/task.html index 39fd4e9df4f4c5a2aa184a32bfdde60c49925eb9..d0619ba75cf5855f2ae20d6e4fc7a0edd3a441fe 100644 --- a/app/task/templates/task.html +++ b/app/task/templates/task.html @@ -5,11 +5,6 @@ {% if session['randomization']=='Off' %} -<!-- 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"> {% for page in pages.items %} diff --git a/app/task/views.py b/app/task/views.py index 9ed7b78a5352a558ab4775129633701cb61eef94..e72588f910ab45bce172f12aa3904bfcb8d7bfb8 100644 --- a/app/task/views.py +++ b/app/task/views.py @@ -249,49 +249,9 @@ def task(page_num): randomized_page_id = get_randomized_page(page_id).randomized_idpage randomized_stimulus = page.query.filter_by(idpage=randomized_page_id).first() - # get all embody questions - embody_questions = embody_question.query.filter_by(experiment_idexperiment=session['exp_id']).all() - - - print(embody_questions) - print(session) - - ''' - - [ - <idembody = '7', experiment_idexperiment = '5', picture = '/static/embody_images/5/madam-300x250.jpg', question = 'Color the ares where human emotions can be found'>, - <idembody = '14', experiment_idexperiment = '5', picture = '/static/embody_images/5/onni_LOGO-RGB-transparent_bg_cut.png', question = 'dfDFADFADSFDFAS'>, - <idembody = '16', experiment_idexperiment = '5', picture = '/static/img/dummy_600.png', question = 'Color the regions whose activity you feel increasing or getting stronger'>] - - <SecureCookieSession { - '_fresh': True, - '_id': '64d8cf95fb694f63a0b4ffdfbdfde4b608fdcabd374869510101345cf3ce527576ba7b2c4b6ed24315eeb23eac573309ab75f22121d5618d3048479552b948e4', - 'answer_set': 52, - 'csrf_token': 'ab1381281c440e95afc3ef6c0c69712c8c64173a', - 'current_idpage': 13, - 'exp_id': '5', - 'language': 'English', - 'randomization': 'Off', - 'type': 'text', - 'user': 'f693e4', - 'user_id': '1' - }> - - - from session['current_idpage'] system gets CURRENT stimulant (page) - - in select_from_type() the CURRENT question type is selected (embody/sliders) - - - - ''' - - # TODO: show embody tool when its disabled!!!! - - return render_template( 'task.html', pages=pages, @@ -381,9 +341,4 @@ def quit(): return render_template('quit_task.html', user_id=user_id) -# TODO: removable? -@task_blueprint.route('/create') -def create(): - return render_template('create_task.html') - - +#EOF diff --git a/create_rating_db.sql b/create_rating_db.sql index 0e34f82716d3f29393c545e43448f669b977e05c..c29b62e5ed8111dc85a397c9826a9c686d407e96 100644 --- a/create_rating_db.sql +++ b/create_rating_db.sql @@ -187,7 +187,8 @@ CREATE TABLE embody_question ( ALTER TABLE embody_answer ADD COLUMN (embody_question_idembody INTEGER DEFAULT 0); ALTER TABLE embody_answer ADD CONSTRAINT FOREIGN KEY (embody_question_idembody) REFERENCES embody_question (idembody); -ALTER TABLE experiment ADD COLUMN (embody_enabled BOOLEAN DEFAULT 0); /*this might be unnecessary*/ -ALTER TABLE answer_set ADD COLUMN (answer_type VARCHAR(120)); +/* Set flag if embody tool is enabled -> this is not the most modulart solution, but works for now */ +ALTER TABLE experiment ADD COLUMN (embody_enabled BOOLEAN DEFAULT 0); -/* TODO: Update answer_set so it knows which part of the page the user is doing (embody/sliders/something else) */ \ No newline at end of file +/* Set current answer type (embody/slider/etc..) so returning users are routed to correct question */ +ALTER TABLE answer_set ADD COLUMN (answer_type VARCHAR(120)); diff --git a/embody_plot.py b/embody_plot.py index ea1af5db160dcce0d458b67484cf8d73eae24ea5..1e27ae05fcca827d4d17faed48eb03c017e2f8b5 100644 --- a/embody_plot.py +++ b/embody_plot.py @@ -6,9 +6,6 @@ Visualize emBODY data This python script is based on matlab code found from: https://version.aalto.fi/gitlab/eglerean/embody/tree/master/matlab -Data is loaded from hardcoded experiment (exp_id) --> TODO: create argument parser where user determines which - experiment is used or if data is loaded from all answers Requirements: - python 3+ @@ -18,6 +15,7 @@ Requirements: Run: python embody_plot.py + """ import sys @@ -40,7 +38,7 @@ from matplotlib.figure import Figure from flask_socketio import emit from app import socketio -# Hard coded image size +# Hard coded image size for default embody image WIDTH = 207 HEIGHT = 600 @@ -190,19 +188,20 @@ def plot_coordinates(coordinates, image_path=DEFAULT_IMAGE_PATH): # set height/width from image frame = np.zeros((image_data[0] + 10,image_data[1] + 10)) - for idx, point in enumerate(coordinates["coordinates"]): - frame[point[1], point[0]] = 1 - point = ndimage.gaussian_filter(frame, sigma=5) - ax2.imshow(point, cmap='hot', interpolation='none') + if image_path == DEFAULT_IMAGE_PATH: - # Try to send progress information to socket.io - try: - socketio.sleep(0) - emit('progress', {'done':idx+1/points_count, 'from':points_count}) - except RuntimeError as err: - continue + for idx, point in enumerate(coordinates["coordinates"]): + frame[point[1], point[0]] = 1 + point = ndimage.gaussian_filter(frame, sigma=5) + ax2.imshow(point, cmap='hot', interpolation='none') + + # Try to send progress information to socket.io + try: + socketio.sleep(0) + emit('progress', {'done':idx+1/points_count, 'from':points_count}) + except RuntimeError as err: + continue - if image_path == DEFAULT_IMAGE_PATH: image_mask = mpimg.imread(IMAGE_PATH_MASK) ax2.imshow(image_mask) else: