Refactored routing and socket logic. Implementing eeg train/test
This commit is contained in:
@@ -10,3 +10,5 @@ server = Flask(__name__)
|
||||
server.config["SEND_FILE_MAX_AGE_DEFAULT"] = 1 # disable caching
|
||||
socketio = SocketIO(async_mode='threading')
|
||||
socketio.init_app(server)
|
||||
|
||||
participants = {}
|
||||
|
||||
@@ -180,6 +180,8 @@ class MatTestThread(Thread):
|
||||
copy2(self.backupFilepath, backup_path)
|
||||
else:
|
||||
copy2(self.backupFilepath, './finalised_backup.pkl')
|
||||
with open('./Matrix_test_results.pkl', 'wb') as f:
|
||||
dill.dump(saveDict, f)
|
||||
self.finalised = True
|
||||
|
||||
|
||||
|
||||
+44
-1
@@ -1,6 +1,45 @@
|
||||
from pathops import dir_must_exist
|
||||
import os
|
||||
import dill
|
||||
import numpy as np
|
||||
|
||||
from config import server, socketio, participants
|
||||
|
||||
def find_participants(folder='./participant_data/'):
|
||||
'''
|
||||
Returns a tuple of (participant number, participant filepath) for every
|
||||
participant folder found in directory provided
|
||||
'''
|
||||
part_folder = [os.path.join(folder, o) for o in os.listdir(folder)
|
||||
if os.path.isdir(os.path.join(folder,o))]
|
||||
for path in 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(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 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
|
||||
try:
|
||||
valid_nums = np.setdiff1d(potential_nums, taken_nums)
|
||||
except:
|
||||
set_trace()
|
||||
if valid_nums.size:
|
||||
num_found = True
|
||||
else:
|
||||
n += N
|
||||
return np.random.choice(valid_nums)
|
||||
|
||||
|
||||
class Participant:
|
||||
def __init__(self, participant_dir=None, number=None, age=None, gender=None, handedness=None, general_notes=None):
|
||||
@@ -24,7 +63,11 @@ class Participant:
|
||||
"notes": ''
|
||||
},
|
||||
|
||||
"set_matrix_data": {
|
||||
"eeg_train_data": {
|
||||
"notes": ''
|
||||
},
|
||||
|
||||
"eeg_test": {
|
||||
"notes": ''
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from flask import Flask, url_for, render_template, jsonify, request, make_response, g
|
||||
from flask_socketio import emit
|
||||
import pdb
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import base64
|
||||
import shutil
|
||||
|
||||
import webview
|
||||
import webbrowser
|
||||
import app
|
||||
import time
|
||||
from threading import Thread, Event
|
||||
import numpy as np
|
||||
import random
|
||||
from pysndfile import sndio
|
||||
from scipy.optimize import minimize
|
||||
|
||||
from app import generate_matrix_stimulus
|
||||
from matrix_test.filesystem import globDir, organiseWavs, prepareOutDir
|
||||
from matrix_test_thread import MatTestThread
|
||||
from pathops import dir_must_exist
|
||||
from participant import Participant, find_participants, gen_participant_num
|
||||
|
||||
from config import server, socketio, participants
|
||||
|
||||
|
||||
'''
|
||||
General routing
|
||||
'''
|
||||
@server.route("/")
|
||||
def landing():
|
||||
"""
|
||||
Render index.html
|
||||
"""
|
||||
return render_template("index.html")
|
||||
|
||||
@server.route('/participant_home')
|
||||
def participant_homepage():
|
||||
title = "Welcome"
|
||||
paragraph = [
|
||||
"Please wait while the experimenter sets up the test..."
|
||||
]
|
||||
|
||||
try:
|
||||
return render_template("participant_home.html", title = title, paragraph=paragraph)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@server.route('/home')
|
||||
def homepage():
|
||||
title = "Welcome"
|
||||
paragraph = [
|
||||
"This is the clinician view. Use the dropdown menus to access controls "
|
||||
"and feedback for the various tests available.",
|
||||
"This web app was developed for the generation of stimulus and "
|
||||
"running of experiments for the PhD project \"Predicting speech "
|
||||
"in noise performance using evoked responses\"."
|
||||
]
|
||||
|
||||
try:
|
||||
return render_template("home.html", title = title, paragraph=paragraph)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@server.route("/choose/path")
|
||||
def choose_path():
|
||||
"""
|
||||
Invokes a folder selection dialog
|
||||
"""
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
|
||||
response = {"status": "ok", "directory": directory}
|
||||
else:
|
||||
response = {"status": "cancel"}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@server.route("/fullscreen")
|
||||
def fullscreen():
|
||||
webview.toggle_fullscreen()
|
||||
return jsonify({})
|
||||
|
||||
'''
|
||||
Participant routing
|
||||
'''
|
||||
@server.route('/participant/manage')
|
||||
def manage_participant_page():
|
||||
# Find all pre-existing participants
|
||||
participants = find_participants()
|
||||
return render_template("manage_participants.html", part_keys=participants.keys())
|
||||
|
||||
@server.route('/participant/create')
|
||||
def create_participant_page():
|
||||
# Find all pre-existing participants
|
||||
participants = find_participants()
|
||||
part_num = gen_participant_num(participants)
|
||||
return render_template("create_participant.html", num=part_num)
|
||||
|
||||
@server.route('/participant/create/submit', methods=["POST"])
|
||||
def create_participant_submit():
|
||||
data = request.form
|
||||
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())
|
||||
|
||||
'''
|
||||
EEG routing
|
||||
'''
|
||||
@server.route('/eeg')
|
||||
def eeg_setup():
|
||||
participants = find_participants()
|
||||
return render_template("eeg_setup.html", part_keys=participants.keys())
|
||||
|
||||
'''
|
||||
Matrix behavioral test routing
|
||||
'''
|
||||
@server.route('/matrix_test')
|
||||
def matrix_test_setup():
|
||||
participants = find_participants()
|
||||
return render_template("matrix_test_setup.html", part_keys=participants.keys())
|
||||
|
||||
@server.route('/matrix_test/run')
|
||||
def run_matrix_test():
|
||||
return render_template("mat_test_run.html")
|
||||
|
||||
@server.route('/matrix_test/complete')
|
||||
def mat_end():
|
||||
return render_template("mat_test_end.html")
|
||||
|
||||
@server.route('/matrix_test/clinician/control')
|
||||
def clinician_control_mat():
|
||||
return render_template("mat_test_clinician_view.html")
|
||||
|
||||
@server.route('/matrix_test/clinician/complete')
|
||||
def clinician_mat_end():
|
||||
return render_template("mat_test_clinician_end.html")
|
||||
|
||||
@server.route('/matrix_test/stimulus_generation')
|
||||
def matDecStim():
|
||||
return render_template("matrix_decode_stim.html")
|
||||
|
||||
'''
|
||||
Click stimulus routing
|
||||
'''
|
||||
@server.route('/click_stim')
|
||||
def clickStim():
|
||||
return render_template("click_stim.html")
|
||||
|
||||
|
||||
'''
|
||||
/da/ stimulus routing
|
||||
'''
|
||||
@server.route('/da_stim')
|
||||
def daStim():
|
||||
return render_template("da_stim.html")
|
||||
|
||||
|
||||
@@ -32,13 +32,8 @@ from matrix_test_thread import MatTestThread
|
||||
from pathops import dir_must_exist
|
||||
from participant import Participant
|
||||
|
||||
import config
|
||||
|
||||
server = config.server
|
||||
socketio = config.socketio
|
||||
|
||||
participants = {}
|
||||
|
||||
from config import server, socketio, participants
|
||||
from route import *
|
||||
|
||||
class StimGenThread(Thread):
|
||||
'''
|
||||
@@ -78,6 +73,26 @@ def run_matrix_thread(listN=None, sessionFilepath=None, participant=None):
|
||||
participant=participant)
|
||||
matThread.start()
|
||||
|
||||
def run_eeg_train(sessionFilepath=None, participant=None):
|
||||
global eegTrainThread
|
||||
if 'matThread' in globals():
|
||||
if matThread.isAlive() and isinstance(matThread, MatTestThread):
|
||||
matThread.join()
|
||||
matThread = MatTestThread(socketio=socketio, listN=listN,
|
||||
sessionFilepath=sessionFilepath,
|
||||
participant=participant)
|
||||
matThread.start()
|
||||
|
||||
def run_eeg_test(sessionFilepath=None, participant=None):
|
||||
global eegTestThread
|
||||
if 'eegTestThread' in globals():
|
||||
if eegTestThread.isAlive() and isinstance(eegTestThread, EEGTestThread):
|
||||
eegTestThread.join()
|
||||
eegTestThread = EEGTestThread(socketio=socketio, listN=listN,
|
||||
sessionFilepath=sessionFilepath,
|
||||
participant=participant)
|
||||
eegTestThread.start()
|
||||
|
||||
|
||||
@server.after_request
|
||||
def add_header(response):
|
||||
@@ -86,315 +101,6 @@ def add_header(response):
|
||||
return response
|
||||
|
||||
|
||||
@server.route("/")
|
||||
def landing():
|
||||
"""
|
||||
Render index.html
|
||||
"""
|
||||
return render_template("index.html")
|
||||
|
||||
|
||||
@server.route("/choose/path")
|
||||
def choose_path():
|
||||
"""
|
||||
Invokes a folder selection dialog
|
||||
"""
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
|
||||
response = {"status": "ok", "directory": directory}
|
||||
else:
|
||||
response = {"status": "cancel"}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@server.route("/fullscreen")
|
||||
def fullscreen():
|
||||
webview.toggle_fullscreen()
|
||||
return jsonify({})
|
||||
|
||||
@server.route('/participant/manage')
|
||||
def manage_participant_page():
|
||||
# Find all pre-existing participants
|
||||
participants = find_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_info', 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
|
||||
participants = find_participants()
|
||||
part_num = gen_participant_num(participants)
|
||||
return render_template("create_participant.html", num=part_num)
|
||||
|
||||
def find_participants(folder='./participant_data/'):
|
||||
'''
|
||||
Returns a tuple of (participant number, participant filepath) for every
|
||||
participant folder found in directory provided
|
||||
'''
|
||||
part_folder = [os.path.join(folder, o) for o in os.listdir(folder)
|
||||
if os.path.isdir(os.path.join(folder,o))]
|
||||
for path in 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(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 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
|
||||
try:
|
||||
valid_nums = np.setdiff1d(potential_nums, taken_nums)
|
||||
except:
|
||||
set_trace()
|
||||
if valid_nums.size:
|
||||
num_found = True
|
||||
else:
|
||||
n += N
|
||||
return np.random.choice(valid_nums)
|
||||
|
||||
@server.route('/participant/create/submit', methods=["POST"])
|
||||
def create_participant_submit():
|
||||
data = request.form
|
||||
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():
|
||||
title = "Welcome"
|
||||
paragraph = [
|
||||
"Please wait while the experimenter sets up the test..."
|
||||
]
|
||||
|
||||
try:
|
||||
return render_template("participant_home.html", title = title, paragraph=paragraph)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@server.route('/home')
|
||||
def homepage():
|
||||
title = "Welcome"
|
||||
paragraph = [
|
||||
"This is the clinician view. Use the dropdown menus to access controls "
|
||||
"and feedback for the various tests available.",
|
||||
"This web app was developed for the generation of stimulus and "
|
||||
"running of experiments for the PhD project \"Predicting speech "
|
||||
"in noise performance using evoked responses\"."
|
||||
]
|
||||
|
||||
try:
|
||||
return render_template("home.html", title = title, paragraph=paragraph)
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
@server.route('/matrix_test')
|
||||
def matrix_test_setup():
|
||||
participants = find_participants()
|
||||
return render_template("matrix_test_setup.html", part_keys=participants.keys())
|
||||
|
||||
@server.route('/matrix_test/run')
|
||||
def run_matrix_test():
|
||||
return render_template("mat_test_run.html")
|
||||
|
||||
@server.route('/matrix_test/complete')
|
||||
def mat_end():
|
||||
return render_template("mat_test_end.html")
|
||||
|
||||
@server.route('/matrix_test/clinician/control')
|
||||
def clinician_control_mat():
|
||||
return render_template("mat_test_clinician_view.html")
|
||||
|
||||
@server.route('/matrix_test/clinician/complete')
|
||||
def clinician_mat_end():
|
||||
return render_template("mat_test_clinician_end.html")
|
||||
|
||||
@server.route('/matrix_test/stimulus_generation')
|
||||
def matDecStim():
|
||||
return render_template("matrix_decode_stim.html")
|
||||
|
||||
@socketio.on('start_mat_test', namespace='/main')
|
||||
def start_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
listN = int(msg['listN'])
|
||||
part_key = msg['part_key']
|
||||
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
|
||||
run_matrix_thread(listN=listN, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_mat_backup', namespace='/main')
|
||||
def start_backup_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
folder = participant.data_paths["adaptive_matrix_data"]
|
||||
backupPath = os.path.join(folder, "mat_state.pkl")
|
||||
else:
|
||||
participant = None
|
||||
backupPath = './mat_state.pkl'
|
||||
|
||||
run_matrix_thread(sessionFilepath=backupPath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_mat_session', namespace='/main')
|
||||
def start_saved_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
else:
|
||||
return None
|
||||
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
run_matrix_thread(sessionFilepath=filepath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('run_mat_stim_gen', namespace='/main')
|
||||
def generateStim(msg):
|
||||
'''
|
||||
When process buton is clicked in GUI, start an asynchronous thread to run
|
||||
process
|
||||
'''
|
||||
global thread
|
||||
n_part = int(msg['n_part'])
|
||||
snr_len = float(msg['snr_len'])
|
||||
snr_num = int(msg['snr_num'])
|
||||
mat_dir = msg['mat_dir']
|
||||
save_dir = msg['save_dir']
|
||||
thread = StimGenThread(n_part, snr_len, snr_num, mat_dir, save_dir, socketio=socketio)
|
||||
thread.start()
|
||||
|
||||
|
||||
@socketio.on('check-mat-processing-status', namespace='/main')
|
||||
def checkMatProcessingStatus():
|
||||
global thread
|
||||
if thread.is_alive():
|
||||
socketio.emit('mat-processing-status', {'data': True}, namespace='/main')
|
||||
else:
|
||||
socketio.emit('mat-processing-status', {'data': False}, namespace='/main')
|
||||
|
||||
|
||||
@socketio.on('open_save_file_dialog', namespace='/main')
|
||||
def openSaveFileDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.SAVE_DIALOG,
|
||||
file_types=("Python Pickle (*.pkl)",),
|
||||
save_filename="test_session.pkl")
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
# Make sure file ends with pickle file extension
|
||||
head, tail = os.path.splitext(filepath)
|
||||
filepath = head + ".pkl"
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('save_file_dialog_resp', {'data': filepath}, namespace='/main', broadcast=True, include_self=True)
|
||||
|
||||
|
||||
@socketio.on('open_load_file_dialog', namespace='/main')
|
||||
def openLoadFileDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
if not os.path.isfile(filepath):
|
||||
socketio.emit('main-notification', {'data': "\'{}\' is not a valid file".format(directory)}, namespace='/main')
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('load_file_dialog_resp', {'data': filepath}, namespace='/main', broadcast=True, include_self=True)
|
||||
|
||||
|
||||
@socketio.on('open_save_dialog', namespace='/main')
|
||||
def openSaveDirDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
if not os.path.isdir(directory):
|
||||
socketio.emit('main-notification', {'data': "\'{}\' is not a valid directory".format(directory)}, namespace='/main')
|
||||
return None
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('save-file-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
|
||||
@socketio.on('open_mat_dialog', namespace='/main')
|
||||
def openMatDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
# TODO: Add filepath checking here...
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('mat-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
|
||||
@server.route('/click_stim')
|
||||
def clickStim():
|
||||
return render_template("click_stim.html")
|
||||
|
||||
|
||||
@server.route('/da_stim')
|
||||
def daStim():
|
||||
return render_template("da_stim.html")
|
||||
|
||||
|
||||
def set_trace():
|
||||
import logging
|
||||
log = logging.getLogger('werkzeug')
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
import pandas as pd
|
||||
import os
|
||||
from flask import Flask, url_for, render_template, jsonify, request, make_response, g
|
||||
from flask_socketio import emit
|
||||
import pdb
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import base64
|
||||
import shutil
|
||||
|
||||
import webview
|
||||
import webbrowser
|
||||
import app
|
||||
import time
|
||||
from threading import Thread, Event
|
||||
import numpy as np
|
||||
import random
|
||||
from pysndfile import sndio
|
||||
from scipy.optimize import minimize
|
||||
|
||||
from app import generate_matrix_stimulus
|
||||
from matrix_test.filesystem import globDir, organiseWavs, prepareOutDir
|
||||
from matrix_test_thread import MatTestThread
|
||||
from pathops import dir_must_exist
|
||||
from participant import Participant
|
||||
|
||||
from config import server, socketio, participants
|
||||
|
||||
'''
|
||||
Generic socket handlers
|
||||
'''
|
||||
@socketio.on('open_save_file_dialog', namespace='/main')
|
||||
def openSaveFileDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.SAVE_DIALOG,
|
||||
file_types=("Python Pickle (*.pkl)",),
|
||||
save_filename="test_session.pkl")
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
# Make sure file ends with pickle file extension
|
||||
head, tail = os.path.splitext(filepath)
|
||||
filepath = head + ".pkl"
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('save_file_dialog_resp', {'data': filepath}, namespace='/main', broadcast=True, include_self=True)
|
||||
|
||||
|
||||
@socketio.on('open_load_file_dialog', namespace='/main')
|
||||
def openLoadFileDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
if not os.path.isfile(filepath):
|
||||
socketio.emit('main-notification', {'data': "\'{}\' is not a valid file".format(directory)}, namespace='/main')
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('load_file_dialog_resp', {'data': filepath}, namespace='/main', broadcast=True, include_self=True)
|
||||
|
||||
|
||||
@socketio.on('open_save_dialog', namespace='/main')
|
||||
def openSaveDirDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
if not os.path.isdir(directory):
|
||||
socketio.emit('main-notification', {'data': "\'{}\' is not a valid directory".format(directory)}, namespace='/main')
|
||||
return None
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('save-file-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
|
||||
|
||||
'''
|
||||
Participant socket handlers
|
||||
'''
|
||||
@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_info', 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')
|
||||
|
||||
'''
|
||||
EEG training socket handlers
|
||||
'''
|
||||
@socketio.on('start_eeg_train', namespace='/main')
|
||||
def start_eeg_train(msg):
|
||||
'''
|
||||
Relay train start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_eeg', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
|
||||
run_eeg_train_thread(listN=listN, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_eeg_train_backup', namespace='/main')
|
||||
def start_backup_eeg_train(msg):
|
||||
'''
|
||||
Relay train start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_eeg_train', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
folder = participant.data_paths["eeg_train_data"]
|
||||
backupPath = os.path.join(folder, "eeg_train_state.pkl")
|
||||
else:
|
||||
participant = None
|
||||
backupPath = './eeg_train_state.pkl'
|
||||
|
||||
run_eeg_train_thread(sessionFilepath=backupPath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_eeg_train_session', namespace='/main')
|
||||
def start_saved_eeg_train(msg):
|
||||
'''
|
||||
Relay train start message to participant view
|
||||
'''
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
else:
|
||||
return None
|
||||
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
socketio.emit('participant_start_eeg_train', {'data': ''}, namespace='/main', broadcast=True)
|
||||
run_eeg_train_thread(sessionFilepath=filepath, participant=participant)
|
||||
|
||||
'''
|
||||
EEG test socket handlers
|
||||
'''
|
||||
@socketio.on('start_eeg_test', namespace='/main')
|
||||
def start_eeg_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_eeg', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
|
||||
run_eeg_test_thread(listN=listN, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_eeg_test_backup', namespace='/main')
|
||||
def start_backup_eeg_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_eeg_test', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
folder = participant.data_paths["eeg_test_data"]
|
||||
backupPath = os.path.join(folder, "eeg_test_state.pkl")
|
||||
else:
|
||||
participant = None
|
||||
backupPath = './eeg_test_state.pkl'
|
||||
|
||||
run_eeg_test_thread(sessionFilepath=backupPath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_eeg_test_session', namespace='/main')
|
||||
def start_saved_eeg_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
else:
|
||||
return None
|
||||
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
socketio.emit('participant_start_eeg_test', {'data': ''}, namespace='/main', broadcast=True)
|
||||
run_eeg_test_thread(sessionFilepath=filepath, participant=participant)
|
||||
|
||||
'''
|
||||
Behavioral matrix test socket handlers
|
||||
'''
|
||||
@socketio.on('start_mat_test', namespace='/main')
|
||||
def start_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
listN = int(msg['listN'])
|
||||
part_key = msg['part_key']
|
||||
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
|
||||
run_matrix_thread(listN=listN, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_mat_backup', namespace='/main')
|
||||
def start_backup_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
folder = participant.data_paths["adaptive_matrix_data"]
|
||||
backupPath = os.path.join(folder, "mat_state.pkl")
|
||||
else:
|
||||
participant = None
|
||||
backupPath = './mat_state.pkl'
|
||||
|
||||
run_matrix_thread(sessionFilepath=backupPath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('load_mat_session', namespace='/main')
|
||||
def start_saved_mat_test(msg):
|
||||
'''
|
||||
Relay test start message to participant view
|
||||
'''
|
||||
filepath = webview.create_file_dialog(dialog_type=webview.OPEN_DIALOG, file_types=("Python Pickle (*.pkl)",))
|
||||
if filepath and len(filepath) > 0:
|
||||
filepath = filepath[0]
|
||||
if isinstance(filepath, bytes):
|
||||
filepath = filepath.decode("utf-8")
|
||||
else:
|
||||
return None
|
||||
|
||||
part_key = msg['part_key']
|
||||
if part_key != "--":
|
||||
participant = participants[part_key]
|
||||
else:
|
||||
participant = None
|
||||
socketio.emit('participant_start_mat', {'data': ''}, namespace='/main', broadcast=True)
|
||||
run_matrix_thread(sessionFilepath=filepath, participant=participant)
|
||||
|
||||
|
||||
@socketio.on('open_mat_dialog', namespace='/main')
|
||||
def openMatDialog():
|
||||
# Open a file dialog interface for selecting a directory
|
||||
dirs = webview.create_file_dialog(webview.FOLDER_DIALOG)
|
||||
if dirs and len(dirs) > 0:
|
||||
directory = dirs[0]
|
||||
if isinstance(directory, bytes):
|
||||
directory = directory.decode("utf-8")
|
||||
# TODO: Add filepath checking here...
|
||||
# Send message with selected directory to the GUI
|
||||
socketio.emit('mat-dialog-resp', {'data': directory}, namespace='/main')
|
||||
|
||||
'''
|
||||
Matrix test stimulus generation socket handlers
|
||||
'''
|
||||
@socketio.on('run_mat_stim_gen', namespace='/main')
|
||||
def generateStim(msg):
|
||||
'''
|
||||
When process buton is clicked in GUI, start an asynchronous thread to run
|
||||
process
|
||||
'''
|
||||
global thread
|
||||
n_part = int(msg['n_part'])
|
||||
snr_len = float(msg['snr_len'])
|
||||
snr_num = int(msg['snr_num'])
|
||||
mat_dir = msg['mat_dir']
|
||||
save_dir = msg['save_dir']
|
||||
thread = StimGenThread(n_part, snr_len, snr_num, mat_dir, save_dir, socketio=socketio)
|
||||
thread.start()
|
||||
|
||||
|
||||
@socketio.on('check-mat-processing-status', namespace='/main')
|
||||
def checkMatProcessingStatus():
|
||||
global thread
|
||||
if thread.is_alive():
|
||||
socketio.emit('mat-processing-status', {'data': True}, namespace='/main')
|
||||
else:
|
||||
socketio.emit('mat-processing-status', {'data': False}, namespace='/main')
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
{% extends 'index.html' %}
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div id="main-div" class="card-body">
|
||||
<form action="{{ url_for('matDecStim') }}" 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 class="form-group">
|
||||
<label for="open-mat-folder-container">Behavioral test data:</label>
|
||||
<br>
|
||||
<input type="text" id="behav_res" name='behav_res' value="./Matrix_test_results.pkl" style="width:85%"></input>
|
||||
<button type="button" id="behav_res_button" class="btn btn-primary">Browse...</button>
|
||||
</div>
|
||||
<div class="form-group d-flex justify-content-center">
|
||||
<button type="button" id="start_eeg_train" class="btn btn-primary mx-3">Start training data collection</button>
|
||||
<button type="button" id="start_eeg_test" class="btn btn-primary mx-3">Start test data collection</button>
|
||||
<button type="button" id="load-saved" class="btn btn-primary mx-3">Load saved session</button>
|
||||
<button type="button" id="load-backup" class="btn btn-primary mx-3">Load previous automatic backup</button>
|
||||
</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');
|
||||
|
||||
$('#load-backup').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('load_eeg_backup', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
$('#load-saved').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('load_eeg_session', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
$('#start_eeg_train').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('start_eeg_train', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
$('#start_eeg_test').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('start_eeg_test', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
|
||||
socket.on('participant_start_mat', function(msg) {
|
||||
window.location.href = '/eeg/clinician/control';
|
||||
});
|
||||
|
||||
|
||||
socket.on('save-dialog-resp', function(msg) {
|
||||
// Set form test to filepath returned by dialog
|
||||
document.getElementById("save-dir").value = msg.data
|
||||
});
|
||||
|
||||
$('#mat-dir-button').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('open_eeg_dialog');
|
||||
return false;
|
||||
})
|
||||
|
||||
socket.on('eeg-dialog-resp', function(msg) {
|
||||
// Set form test to filepath returned by dialog
|
||||
document.getElementById("eeg-dir").value = msg.data
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -83,8 +83,8 @@
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<a class="dropdown-item" href="#">PTA</a>
|
||||
<a class="dropdown-item" href="#">Tympanometry</a>
|
||||
<a class="dropdown-item" href="/matrix_test">Matrix Test</a>
|
||||
<a class="dropdown-item" href="#">EEG recording</a>
|
||||
<a class="dropdown-item" href="/matrix_test">Behavioral Matrix Test</a>
|
||||
<a class="dropdown-item" href="/eeg">EEG recording</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -36,17 +36,14 @@
|
||||
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/main');
|
||||
|
||||
$('#load-backup').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('load_mat_backup', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
$('#load-saved').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('load_mat_session', {part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
$('#submit').click(function(event) {
|
||||
// Send message to call stimulus generation function in Python
|
||||
socket.emit('start_mat_test', {listN: $("#listN").val(), part_key: $("#participant").val()});
|
||||
return false;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user