Implemented participant management
This commit is contained in:
+50
-26
@@ -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):
|
||||
'''
|
||||
'''
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user