Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Server maintenance on Tue 24.5. at 12:00.
Estimated downtime less than 30 minutes.
Open sidebar
Timo Heikkilä
PET-rating
Commits
951b39f3
Commit
951b39f3
authored
Jun 12, 2019
by
Ossi Laine
Browse files
Reordered statistics -page and shows now only values from done experiments
parent
59d2b64f
Changes
9
Hide whitespace changes
Inline
Side-by-side
app/experiment/templates/experiment_statistics.html
View file @
951b39f3
...
...
@@ -35,18 +35,16 @@
</tr>
<tr>
<td>
Number of finished ratings:
</td>
<td>
{{ finished_ratings }}
</td>
</tr>
<tr>
<td><a
class=
"btn btn-primary btn-info"
href=
"{{ url_for('download_csv', exp_id=exp.idexperiment) }}"
role=
"button"
>
Export results (csv)
</a></td>
<td></td>
<td>
{{ finished_ratings }}
<a
class=
"btn btn-primary btn-info float-right"
href=
"{{ url_for('download_csv', exp_id=exp.idexperiment) }}"
role=
"button"
>
Export results (csv)
</a>
</td>
</tr>
</tbody>
</table>
{% endfor %}
<h1
class=
"container mt-5 display-
4
text-left"
><br>
Rating task
question headers:
</h1>
<h1
class=
"container mt-5 display-
6
text-left"
><br>
Slider
question headers:
</h1>
<br>
<table
class=
"table"
>
<thead>
...
...
@@ -69,7 +67,40 @@
</tbody>
</table>
<h1
class=
"container mt-5 display-4 text-left"
><br>
Rating task stimulus headers:
</h1>
<h1
class=
"container mt-5 display-6 text-left"
><br>
Slider question answers: (Page ID/Question ID)
</h1>
<br>
<table
class=
"table"
>
<thead>
<tr>
<th
scope=
"col"
nowrap
>
Participant ID:
</th>
{% for page in pages_and_questions %}
{% for p in pages_and_questions[page] %}
<th
scope=
"col"
nowrap
>
{{ p[0]}}/{{ p[1]}}
</th>
{% endfor %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for participant in participants_and_answers %}
<tr>
<td>
{{ participant }}
</td>
{% for answer in participants_and_answers[participant] %}
<td>
{{ answer }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<h1
class=
"container mt-5 display-6 text-left"
><br>
Embody pictures and answers:
</h1>
<br>
<table
class=
"table"
>
<thead>
...
...
@@ -87,8 +118,16 @@
{% if s.type == 'text' %}
<td>
{{ s.text }}
</td>
{% elif s.type == 'picture' %}
<td><img
src=
"/{{ s.media }}"
class=
"thumbnail"
/></td>
{% elif s.type == 'video' %}
<td>
<div
class=
"embed-responsive embed-responsive-16by9 "
>
<iframe
class=
"embed-responsive-item thumbnail"
src=
"/{{ s.media }}"
allowFullScreen
></iframe>
</div>
</td>
{% else %}
<td>
{{ s.
media
}}
</td>
<td>
{{ s.
text
}}
</td>
{% endif %}
<td><img
src=
"{{ embody_picture.picture }}"
class=
"thumbnail"
/></td>
...
...
@@ -116,43 +155,14 @@
<img
class=
"embody-image-container"
>
<h1
class=
"container mt-5 display-4 text-left"
><br>
Rating task values: (Stimulus ID/Question ID)
</h1>
<br>
<table
class=
"table"
>
<thead>
<tr>
<th
scope=
"col"
nowrap
>
Participant ID:
</th>
{% for page in pages_and_questions %}
{% for p in pages_and_questions[page] %}
<th
scope=
"col"
nowrap
>
{{ p[0]}}/{{ p[1]}}
</th>
{% endfor %}
{% endfor %}
</tr>
</thead>
<tbody>
{% for participant in participants_and_answers %}
<tr>
<td>
{{ participant }}
</td>
{% for answer in participants_and_answers[participant] %}
<td>
{{ answer[3] }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<h1
class=
"container mt-5 display-4 text-left"
><br>
Background question answers:
</h1>
<h1
class=
"container mt-5 display-6 text-left"
><br>
Background question answers:
</h1>
<br>
<table
class=
"table"
>
<thead>
<tr>
<th
scope=
"col"
nowrap
>
Question:
</th>
<th
scope=
"col"
nowrap
>
Participant
</th>
{% for bg in bg_questions %}
<th
scope=
"col"
nowrap
>
{{ bg.background_question }}
</th>
...
...
@@ -168,6 +178,8 @@
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
...
...
app/experiment/views.py
View file @
951b39f3
...
...
@@ -893,6 +893,14 @@ def statistics():
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
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
()
#Rating task headers
question_headers
=
question
.
query
.
filter_by
(
experiment_idexperiment
=
exp_id
).
all
()
...
...
@@ -903,47 +911,56 @@ def statistics():
pages_and_questions
=
{}
for
p
in
pages
:
questions_list
=
[(
p
.
idpage
,
a
.
idquestion
)
for
a
in
questions
]
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
participants_and_answers
=
{}
slider_answers
=
{}
for
participant
in
participants
:
answers
=
answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
answers_list
=
[(
a
.
idanswer
,
a
.
question_idquestion
,
a
.
answer_set_idanswer_set
,
a
.
answer
,
a
.
page_idpage
)
for
a
in
answers
]
participants_and_answers
[
participant
.
session
]
=
answers_list
# list only finished answer sets
if
experiment_page_count
==
participant
.
answer_counter
:
answers
=
answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
slider_answers
[
participant
.
session
]
=
[
a
.
answer
for
a
in
answers
]
# map slider_answers from str to int and calculate mean
a
=
[
map
(
int
,
i
)
for
i
in
list
(
slider_answers
.
values
())]
slider_answers
[
'mean'
]
=
[
float
(
sum
(
l
))
/
len
(
l
)
for
l
in
zip
(
*
a
)]
#Background question answers
bg_questions
=
background_question
.
query
.
filter_by
(
experiment_idexperiment
=
exp_id
).
all
()
bg_answers_for_participants
=
{}
for
participant
in
participants
:
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
# list only finished answer sets
if
experiment_page_count
==
participant
.
answer_counter
:
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
#started and finished ratings counters
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
()
# embody questions
embody_questions
=
embody_question
.
query
.
filter_by
(
experiment_idexperiment
=
exp_id
).
all
()
return
render_template
(
'experiment_statistics.html'
,
experiment_info
=
experiment_info
,
participants_and_answers
=
participants_and_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
,
embody_questions
=
embody_questions
)
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
,
embody_questions
=
embody_questions
)
import
embody_plot
...
...
app/forms.py
View file @
951b39f3
...
...
@@ -81,9 +81,9 @@ class TestForm2(Form):
questions1
=
SelectField
()
#Forms for editing functions
class
CreateExperimentForm
(
Form
):
name
=
StringField
(
'Name'
,
[
validators
.
DataRequired
()])
...
...
@@ -147,6 +147,7 @@ class CreateEmbodyForm(Form):
picture
=
FileField
(
'Upload picture'
)
submit
=
SubmitField
(
'Send'
)
class
EditQuestionForm
(
Form
):
left
=
StringField
(
'left_scale'
,
[
validators
.
DataRequired
()])
...
...
@@ -168,7 +169,6 @@ class UploadResearchBulletinForm(Form):
file
=
FileField
(
'Upload file'
)
submit
=
SubmitField
(
'Send'
)
class
EditPageForm
(
Form
):
...
...
@@ -183,6 +183,7 @@ class RemoveExperimentForm(Form):
remove
=
TextAreaField
(
'Remove'
)
submit
=
SubmitField
(
'Send'
)
class
GenerateIdForm
(
Form
):
number
=
IntegerField
(
'number'
,
[
validators
.
DataRequired
()])
...
...
app/routes.py
View file @
951b39f3
...
...
@@ -382,47 +382,50 @@ def download_csv():
header
+=
';'
+
';'
.
join
([
'page'
+
str
(
idx
)
+
'_'
+
str
(
count
)
+
'. embody_question: '
+
question
.
picture
for
count
,
question
in
enumerate
(
embody_questions
,
1
)])
csv
+=
header
+
'
\r\n
'
answer_row
=
''
for
participant
in
participants
:
try
:
# list only finished answer sets
if
experiment_page_count
==
participant
.
answer_counter
:
# append user session id
answer_row
+=
participant
.
session
+
';'
try
:
# append background question answers
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
]
answer_row
+=
';'
.
join
(
bg_answers_list
)
+
';'
# append slider answers
slider_answers
=
answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
answers_list
=
[
a
.
answer
for
a
in
slider_answers
]
answer_row
+=
';'
.
join
(
answers_list
)
+
';'
if
slider_answers
else
''
# append embody answers (coordinates)
# save embody answers as bitmap images
embody_answers
=
embody_answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
answers_list
=
[]
for
embody_answer_data
in
embody_answers
:
embody_answer_data
=
json
.
loads
(
embody_answer_data
.
coordinates
)
coordinates_to_bitmap
=
[[
0
for
x
in
range
(
embody_answer_data
[
'height'
]
+
2
)]
for
y
in
range
(
embody_answer_data
[
'width'
]
+
2
)]
for
point
in
list
(
zip
(
embody_answer_data
[
'x'
],
embody_answer_data
[
'y'
])):
coordinates_to_bitmap
[
point
[
0
]][
point
[
1
]]
+=
0.1
answers_list
.
append
(
json
.
dumps
(
coordinates_to_bitmap
))
# old way to save only visited points:
# answers_list = [ json.dumps(list(zip( json.loads(a.coordinates)['x'], json.loads(a.coordinates)['y']))) for a in embody_answers]
answer_row
+=
';'
.
join
(
answers_list
)
if
embody_answers
else
''
except
TypeError
as
err
:
print
(
err
)
csv
+=
answer_row
+
'
\r\n
'
answer_row
=
''
# append user session id
answer_row
+=
participant
.
session
+
';'
# append background question answers
bg_answers
=
background_question_answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
bg_answers_list
=
[
str
(
a
.
answer
).
strip
()
for
a
in
bg_answers
]
answer_row
+=
';'
.
join
(
bg_answers_list
)
+
';'
# append slider answers
slider_answers
=
answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
answers_list
=
[
str
(
a
.
answer
).
strip
()
for
a
in
slider_answers
]
answer_row
+=
';'
.
join
(
answers_list
)
+
';'
if
slider_answers
else
len
(
questions
)
*
len
(
pages
)
*
';'
# append embody answers (coordinates)
# save embody answers as bitmap images
embody_answers
=
embody_answer
.
query
.
filter_by
(
answer_set_idanswer_set
=
participant
.
idanswer_set
).
all
()
answers_list
=
[]
for
embody_answer_data
in
embody_answers
:
embody_answer_data
=
json
.
loads
(
embody_answer_data
.
coordinates
)
coordinates_to_bitmap
=
[[
0
for
x
in
range
(
embody_answer_data
[
'height'
]
+
2
)]
for
y
in
range
(
embody_answer_data
[
'width'
]
+
2
)]
for
point
in
list
(
zip
(
embody_answer_data
[
'x'
],
embody_answer_data
[
'y'
])):
coordinates_to_bitmap
[
point
[
0
]][
point
[
1
]]
+=
0.1
answers_list
.
append
(
'hello'
)
#answers_list.append(json.dumps(coordinates_to_bitmap))
# old way to save only visited points:
# answers_list = [ json.dumps(list(zip( json.loads(a.coordinates)['x'], json.loads(a.coordinates)['y']))) for a in embody_answers]
answer_row
+=
';'
.
join
(
answers_list
)
if
embody_answers
else
len
(
embody_questions
)
*
len
(
pages
)
*
';'
except
TypeError
as
err
:
print
(
err
)
csv
+=
answer_row
+
'
\r\n
'
answer_row
=
''
try
:
fd
,
path
=
tempfile
.
mkstemp
()
...
...
app/static/js/canvas.js
View file @
951b39f3
...
...
@@ -6,16 +6,7 @@ const defaultEmbodySource = '/static/img/dummy_600.png';
$
(
document
).
ready
(
function
()
{
// Init draw variables
/*
OO-style coordinates:
var paint;
var point = {
x: null,
y: null,
r: 13
}
var points = new Array()
*/
var
clickX
=
new
Array
();
var
clickY
=
new
Array
();
var
clickRadius
=
new
Array
();
...
...
@@ -36,9 +27,9 @@ $(document).ready(function() {
//var oldimg = document.getElementById("baseImage");
var
img
=
$
(
'
.embody-image.selected-embody
'
)[
0
]
$
(
img
)
.
on
(
'
load
'
,
function
()
{
img
.
onload
=
function
()
{
drawImage
()
}
)
}
}
catch
(
e
)
{
console
.
log
(
e
)
...
...
@@ -63,6 +54,8 @@ $(document).ready(function() {
}
}
drawImage
()
// Click handlers
canvas
.
mousedown
(
function
(
e
){
var
mouseX
=
e
.
pageX
-
this
.
offsetLeft
;
...
...
@@ -85,6 +78,27 @@ $(document).ready(function() {
}
});
canvas
.
bind
(
'
touchmove
'
,
function
(
e
){
e
.
preventDefault
()
var
mouseX
=
e
.
touches
[
0
].
pageX
-
this
.
offsetLeft
;
var
mouseY
=
e
.
touches
[
0
].
pageY
-
this
.
offsetTop
;
if
(
paint
&&
pointInsideBaseImage
([
mouseX
,
mouseY
])){
addClick
(
mouseX
,
mouseY
,
true
);
redraw
();
}
});
canvas
.
bind
(
'
touchstart
'
,
function
(
e
){
e
.
preventDefault
()
var
mouseX
=
e
.
touches
[
0
].
pageX
-
this
.
offsetLeft
;
var
mouseY
=
e
.
touches
[
0
].
pageY
-
this
.
offsetTop
;
paint
=
true
;
if
(
pointInsideBaseImage
([
mouseX
,
mouseY
]))
{
addClick
(
mouseX
,
mouseY
);
redraw
();
}
});
canvas
.
mouseup
(
function
(
e
){
paint
=
false
;
});
...
...
@@ -96,7 +110,6 @@ $(document).ready(function() {
$
(
"
#embody-canvas
"
).
bind
(
'
DOMMouseScroll
'
,
changeBrushSize
)
// DOMMouseScroll is only for firefox
//$(".canvas-container").bind('wheel', changeBrushSize)
function
changeBrushSize
(
event
)
{
event
.
preventDefault
()
...
...
@@ -149,7 +162,6 @@ $(document).ready(function() {
if
(
$
(
img
).
hasClass
(
'
last-embody
'
))
{
// Send data to db
try
{
console
.
log
(
points
)
points
=
JSON
.
stringify
(
points
)
$
(
"
#canvas-data
"
).
val
(
points
);
$
(
"
#canvas-form
"
).
submit
();
...
...
@@ -259,9 +271,5 @@ $(document).ready(function() {
clickY
=
[]
clickDrag
=
[]
}
drawImage
()
});
\ No newline at end of file
app/task/templates/task.html
View file @
951b39f3
...
...
@@ -94,10 +94,6 @@
{% if form.__name__ == 'embody' %}
<div
class=
"canvas-container"
>
<span
class=
"canvas-info"
></span>
<canvas
id=
"embody-canvas"
class=
"crosshair"
width=
"20"
height=
"20"
style=
"border: 1px solid blue;"
></canvas>
...
...
create_rating_db.sql
View file @
951b39f3
...
...
@@ -187,7 +187,7 @@ 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
);
/* Set flag if embody tool is enabled -> this is not the most modular
t
solution, but works for now */
/* Set flag if embody tool is enabled -> this is not the most modular solution, but works for now */
ALTER
TABLE
experiment
ADD
COLUMN
(
embody_enabled
BOOLEAN
DEFAULT
0
);
/* Set current answer type (embody/slider/etc..) so returning users are routed to correct question */
...
...
embody_plot.py
View file @
951b39f3
...
...
@@ -38,6 +38,7 @@ from matplotlib.figure import Figure
from
flask_socketio
import
emit
from
app
import
socketio
# Hard coded image size for default embody image
WIDTH
=
207
HEIGHT
=
600
...
...
@@ -191,15 +192,16 @@ def plot_coordinates(coordinates, image_path=DEFAULT_IMAGE_PATH):
if
image_path
==
DEFAULT_IMAGE_PATH
:
for
idx
,
point
in
enumerate
(
coordinates
[
"coordinates"
]):
frame
[
point
[
1
],
point
[
0
]]
=
1
frame
[
int
(
point
[
1
]
)
,
int
(
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
})
socketio
.
sleep
(
0
)
except
RuntimeError
as
err
:
print
(
err
)
continue
image_mask
=
mpimg
.
imread
(
IMAGE_PATH_MASK
)
...
...
requirements.txt
View file @
951b39f3
alembic
==0.9.9
autopep8
==1.4.4
Babel
==2.6.0
click
==6.7
cycler
==0.10.0
decorator
==4.4.0
dnspython
==1.16.0
dominate
==2.3.1
Flask
==1.0.2
Flask-Babel
Flask-Babel
==0.12.2
Flask-Bootstrap
==3.3.7.1
Flask-Cors
==3.0.7
Flask-Login
==0.4.1
Flask-Migrate
==2.2.1
Flask-Session
==0.3.1
Flask-SocketIO
==3.3.2
Flask-SQLAlchemy
==2.3.2
Flask-Uploads
==0.2.1
Flask-WTF
==0.14.2
gevent
==1.4.0
greenlet
==0.4.15
gunicorn
==19.9.0
itsdangerous
==0.24
Jinja2
==2.10
kiwisolver
==1.0.1
lml
==0.0.4
Mako
==1.0.7
MarkupSafe
==1.0
matplotlib
==3.0.3
monotonic
==1.5
mysql-connector
==2.2.9
networkx
==2.2
numpy
==1.16.2
Pillow
==6.0.0
pkg-resources
==0.0.0
ply
==3.11
pycodestyle
==2.5.0
pyexcel
==0.5.9.1
pyexcel-io
==0.5.9.1
PyMySQL
==0.9.3
pyparsing
==2.3.1
python-dateutil
==2.7.3
python-editor
==1.0.3
python-engineio
==3.5.1
python-socketio
==3.1.2
pytz
==2018.7
rope
==0.12.0
scipy
==1.2.1
six
==1.11.0
smop
==0.41
SQLAlchemy
==1.2.8
texttable
==1.5.0
uuid
==1.30
visitor
==0.1.3
Werkzeug
==0.14.1
WTForms
==2.2.1
WTForms-SQLAlchemy
==0.1
pyexcel
gunicorn
==19.9.0
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment