Implemented participant management

This commit is contained in:
2018-11-24 16:12:06 +00:00
parent f15370a098
commit 03c3e20855
4 changed files with 205 additions and 42 deletions
+50 -26
View File
@@ -1,39 +1,44 @@
from pathops import dir_must_exist
import os
import dill
class Participant:
def __init__(self, participant_dir=None, number=None, age=None, gender=None, general_notes=None):
def __init__(self, participant_dir=None, number=None, age=None, gender=None, handedness=None, general_notes=None):
'''
'''
dir_must_exist(participant_dir)
self.participant_dir = participant_dir
self.data_paths = {}
self.generate_folder_hierachy()
self.info = {
"number": number,
"age": age,
"gender": gender,
"general_notes": general_notes
}
self.data = {
"info": {
"number": number,
"age": age,
"gender": gender,
"handedness": handedness,
"general_notes": general_notes
},
self.adaptive_matrix_data = {
"notes": ''
}
"adaptive_matrix_data": {
"notes": ''
},
self.set_matrix_data = {
"notes": ''
}
"set_matrix_data": {
"notes": ''
},
self.da_data = {
"notes": ''
}
"da_data": {
"notes": ''
},
self.click_data = {
"notes": ''
}
"click_data": {
"notes": ''
},
self.pta_data = {
"notes": ''
"pta_data": {
"notes": ''
}
}
def generate_folder_hierachy(self):
@@ -41,13 +46,32 @@ class Participant:
'''
sub_dirs = ["adaptive_matrix_data", "da_data", "pta_data", "click_data", "info", "set_matrix_data"]
for dir_name in sub_dirs:
dir_must_exist(os.path.join(self.participant_dir, dir_name))
path = os.path.join(self.participant_dir, dir_name)
dir_must_exist(path)
self.data_paths[dir_name] = path
def __setitem__(self, key, item):
self.data[key] = item
def __getitem__(self, key):
return self.data[key]
def set_info(self, info):
self.data['info'] = info
def save(self, data_key):
'''
'''
directory = self.data_paths[data_key]
with open(os.path.join(directory, '{}.pkl'.format(data_key)), 'wb') as f:
dill.dump(self.data[data_key], f)
def save(self, folder):
def load(self, data_key):
'''
'''
folder = os.path.join(self.participant_dir, data_key)
with open(os.path.join(folder, "{}.pkl".format(data_key)), 'rb') as f:
self.data[data_key].update(dill.load(f))
def load(self, folder):
'''
'''
+43 -16
View File
@@ -14,7 +14,7 @@ import csv
import io
import re
import base64
import dill
import shutil
import sounddevice as sd
import webview
@@ -38,7 +38,7 @@ import config
server = config.server
socketio = config.socketio
participants = []
participants = {}
class StimGenThread(Thread):
@@ -116,11 +116,34 @@ def fullscreen():
webview.toggle_fullscreen()
return jsonify({})
@server.route('/participant/manage')
def manage_participant_page():
# Find all pre-existing participants
participants = find_participants()
part_num = gen_participant_num(participants)
return render_template("manage_participants.html", part_keys=participants.keys())
@socketio.on('delete_participant', namespace='/main')
def manage_participant_delete(participant_str):
shutil.rmtree(participants[participant_str].participant_dir)
del participants[participant_str]
return render_template("manage_participants.html", part_keys=participants.keys())
@socketio.on('update_participant', namespace='/main')
def manage_participant_save(data):
key = "participant_{}".format(data['number'])
participants[key].set_info(data)
participants[key].save("info")
@socketio.on('get_part_info', namespace='/main')
def get_participant_info(key):
socketio.emit('part_info', participants[key]['info'], namespace='/main')
@server.route('/participant/create')
def create_participant_page():
# Find all pre-existing participants
participant_locs = find_participants()
part_num = gen_participant_num(participant_locs)
participants = find_participants()
part_num = gen_participant_num(participants)
return render_template("create_participant.html", num=part_num)
def find_participants(folder='./participant_data/'):
@@ -130,26 +153,28 @@ def find_participants(folder='./participant_data/'):
'''
part_folder = [os.path.join(folder, o) for o in os.listdir(folder)
if os.path.isdir(os.path.join(folder,o))]
part_nums = []
for path in part_folder:
bn = os.path.basename(path)
m = re.search(r'\d+$', bn)
num = int(m.group())
part_nums.append(num)
return list(zip(part_nums, part_folder))
part_key = os.path.basename(path)
participants[part_key] = Participant(participant_dir=path)
participants[part_key].load('info')
return participants
def gen_participant_num(participant_locs, N = 100):
def gen_participant_num(participants, N = 100):
# generate array of numbers that haven't been taken between 0-100
# if list is empty increment until list isnt empty
# Choose a number
taken_nums = []
for num, loc in participant_locs:
taken_nums.append(num)
for part_key in participants.keys():
participant = participants[part_key]
taken_nums.append(participant['info']['number'])
n = 0
num_found = False
while not num_found:
potential_nums = np.arange(N)+n+1
valid_nums = np.setdiff1d(potential_nums, taken_nums)
try:
valid_nums = np.setdiff1d(potential_nums, taken_nums)
except:
set_trace()
if valid_nums.size:
num_found = True
else:
@@ -159,8 +184,10 @@ def gen_participant_num(participant_locs, N = 100):
@server.route('/participant/create/submit', methods=["POST"])
def create_participant_submit():
data = request.form
participants.append(Participant(participant_dir="./participant_data/participant_{}".format(data['number']), **data))
return render_template("manage_participants.html")
key = "participant_{}".format(data['number'])
participants[key] = Participant(participant_dir="./participant_data/{}".format(key), **data)
participants[key].save("info")
return render_template("manage_participants.html", part_keys = participants.keys())
@server.route('/participant_home')
def participant_homepage():
+8
View File
@@ -27,6 +27,14 @@
<option>Prefer not to say</option>
</select>
</div>
<div class="form-group">
<label>Handedness:</label>
<br>
<select class="form-control" name="handedness" id="handedness">
<option>Right handed</option>
<option>Left handed</option>
</select>
</div>
<div class="form-group">
<label for="comment">Notes:</label>
<textarea class="form-control" rows="6" id="notes" name="general_notes"></textarea>
+104
View File
@@ -0,0 +1,104 @@
{% extends 'index.html' %}
{% block content %}
<div class="card">
<div class="card-body">
<form id='manage_form' method="POST">
<div class="form-group">
<select class="form-control" name="participant" id="participant">
<option>--</option>
{% for p in part_keys %}
<option>{{ p }}</option>
{% endfor %}
</select>
</div>
<div id="main-div">
<div class="form-group">
<label for="number">Number:</label>
<br>
<input id="number" name="number" type="number" value="1" min="1" class="form-control">
</div>
<div class="form-group">
<label for="age">Age:</label>
<br>
<input id="age" name="age" type="number" value="18" max="99" min="18" class="form-control">
</div>
<div class="form-group">
<label>Gender:</label>
<br>
<select class="form-control" name="gender" id="gender">
<option>Male</option>
<option>Female</option>
<option>Other</option>
<option>Prefer not to say</option>
</select>
</div>
<div class="form-group">
<label>Handedness:</label>
<br>
<select class="form-control" name="handedness" id="handedness">
<option>Right handed</option>
<option>Left handed</option>
</select>
</div>
<div class="form-group">
<label for="comment">Notes:</label>
<textarea class="form-control" rows="6" id="general_notes" name="general_notes"></textarea>
</div>
<div class="form-group d-flex justify-content-center">
<button id="save" type="button" disabled class="btn btn-primary mx-3">Save</button>
<button id="delete" type="button" disabled class="btn btn-danger mx-3">Delete</button>
</div>
</div>
</form>
</div>
</div>
<script>
$(document).ready(function(){
// Initialise socketio with a namespace called "main"
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
$('#main-div').find('input, textarea, button, select').attr('disabled','disabled');
$('#participant').on('change', function() {
if(this.value != '--') {
$('#save').removeAttr('disabled')
$('#delete').removeAttr('disabled')
$('#main-div').find('input, textarea, button, select').removeAttr('disabled');
socket.emit("get_part_info", this.value);
}
else {
$('#save').attr('disabled','disabled');
$('#delete').attr('disabled','disabled');
$('#main-div').find('input, textarea, button, select').attr('disabled','disabled');
}
});
$('#save').click(function(event) {
var form_data = {
number: $('#number').val(),
age: $('#age').val(),
gender: $('#gender').val(),
handedness: $('#handedness').val(),
general_notes: $('#general_notes').val()
}
socket.emit('update_participant', form_data);
});
$('#delete').click(function(event) {
var r = confirm("Are you sure you want to delete the participant permenantly?");
if (r == true) {
socket.emit("delete_participant", $('#participant').val());
$("#participant option:selected").remove();
$('#main-div').find('input, textarea, button, select').attr('disabled','disabled');
}
});
socket.on('part_info', function(msg) {
for(var key in msg) {
$('#'+key).val(msg[key]);
}
});
});
</script>
{% endblock %}