示例#1
0
def user_connect():
    """user_connect The route to the user connect page of the flask application

    Renders the user connect page of the flask app. This page allows the user to 
    view the status of the pacemaker in different pacing modes. Pacing modes can 
    be changed via a selection box at the top of the page. NOTE: This page will 
    correctly change between different pacing mode states, though these states 
    and their corresponding rendered templates are black as no serial communication 
    with the pacemaker has been implemented. As a result changing modes will have little 
    visible effect on the rendered content of the application.

    :return: The render template used by the user connect page, in this case the 'user_connect.html' template
    :rtype: :class:`flask.render_tmeplate`
    """
    #sets the default pacemaker mode to 'None', forcing the user to select a mode manually
    #This is an attempt to avoid the user programming the pacemaker in a mode they did not intend
    mode = user.get_pacemaker_mode()
    global this_port
    if request.method == 'POST':
        if 'Program' in request.form:
            print('PROGRAM REQUEST ------ PROGRAM REQUEST')
            this_port = request.form['Comm Port']
            # programmable_parameters = []
            # for i in list(user.get_pacemaker_parameters().values()):
            #     if i == None or i == 'None':
            #         programmable_parameters.append(0)
            #     else:
            #         programmable_parameters.append(float(i))
            result = sendSerial(user.get_pacemaker_mode(), user.get_pacemaker_parameters(), request.form['Comm Port'])
            if result == False:
                flash('"None" values are not allowed!')
        elif 'Pacing Mode' in request.form:
            #print(request.form['Pacing Mode'])
            #Flashes the new pacing mode to the user to let them know the applications state has changed
            flash('Pacing Mode Changed To {0}'.format(request.form['Pacing Mode']))
            #changes the pacing mode (i.e. the application state)
            mode = request.form['Pacing Mode']
            user.update_pacemaker_mode(mode)
        else:
            values = dict(request.form)
            mode = values.pop('Mode', None)            
            #create a new parameters dictionary by starting with the original dictionary, and updating its values with the modified value from the post request
            required_values = Config.getInstance().get('Parameters', 'parameters.mode.' + str(mode)).split(',')
            for value in required_values:
                if value not in values:
                    values[value] = -1
            updates = {j: {k.replace(' ', ''): v for k, v in values.items() if v}.get(j, user.get_pacemaker_parameters()[j]) for j in user.get_pacemaker_parameters()}
            #updates all the pacemaker parameters with the newly generated list
            Logger.getInstance().log('DEBUG', 'Pacemaker Parameters Updated: {0}'.format(updates))
            user.update_all_pacemaker_parameters(updates.values())
    if mode == None:
        parameters = {}
    else:
        parameters = { re.sub(r"(\w)([A-Z])", r"\1 \2", k): user.get_pacemaker_parameters()[k] for k in [ value.replace(' ', '') for value in Config.getInstance().get('Parameters', 'parameters.mode.' + str(mode)).split(',') ] }
    modes = Config.getInstance().get('Parameters', 'parameters.modes').split(',')
    ports = serial.tools.list_ports.comports()
    port_list = []
    for port, desc, hwid in sorted(ports):
            port_list.append(port)
    return render_template('user_connect.html', port=this_port, ports=port_list, mode=mode, modes=modes, parameters=parameters, limits=user.get_limits())
示例#2
0
def open_browser():
    """open_browser Opens the flask app automatically in the web brower.

    So the user doesn't have to memorize the url and port the application 
    is being hosted on.
    """
    webbrowser.open_new('http://' + Config.getInstance().get('HostSelection', 'host.address') +
                        ':' + Config.getInstance().get('HostSelection', 'host.port') + "/")
示例#3
0
 def __init__(self):
     """Constructor Method
     """
     self.__parameters = { i.replace(' ', '') : None for i in Config.getInstance().get('Parameters', 'parameters.values').split(',') }
     self.__num_parameters = len(self.__parameters)
     self.__mode = None
     self.__id = ''
     self.__username = ''
     self.__password = ''
     self.__conn, self.__cursor = init_db(Config.getInstance().get('Database', 'db.local-uri'))
     self.__set_limits()
示例#4
0
def update_pacemaker_parameters(conn, cursor, id, values):
    """update_pacemaker_parameters Given a list of pacemaker parameters, updates the database values

    When given handler to the database and the unique ID of the user being affected, will update the
    users pacemaker parameters to match the input list.
    NOTE: No complete check is done to ensure the validity of the input, it is up 
    to the method user to ensure the lists correctness.

    :param conn: The connection handler for the database whos contents to change
    :type conn: :class:`sqlite3.Connection`
    :param cursor: The cursor handler for the database whos contents to change
    :type cursor: :class:`sqlite3.Cursor`
    :param id: The unique ID of the user whos parameters should be changed
    :type id: int
    :param values: A list of pacemaker parameters, whos order matches the databases contents
    :type values: list
    """
    cursor.execute(""" --begin-sql
        SELECT COUNT(*) FROM {0};
    """.format('user_' + str(id)))
    num_entries = cursor.fetchall()[0][0]

    parameter_list = [
        i.replace(' ', '') for i in Config.getInstance().get(
            'Parameters', 'parameters.values').split(',')
    ]

    insert_query = 'SELECT ?, mode'
    create_query = 'INSERT INTO user_{0} (timestamp, mode'.format(id)
    update_query = "UPDATE user_{0} SET mode = '{1}'".format(id, values[0])

    for index, parameter in enumerate(parameter_list, start=1):
        insert_query += ', {0}'.format(parameter)
        create_query += ', {0}'.format(parameter)
        update_query += ", {0} = '{1}'".format(parameter, values[index])

    insert_query = create_query + ') ' + insert_query + ' FROM user_{0} WHERE timestamp = ?;'.format(
        id)
    create_query += ') VALUES(' + '?' + (len(values)) * ',?' + ');'
    update_query += ' WHERE timestamp = ?;'

    if num_entries == 0:
        cursor.execute(create_query, ['current'] + values)
        conn.commit()

    cursor.execute(update_query, ['current'])
    conn.commit()

    timestamp = datetime.datetime.now().strftime(Config.getInstance().get(
        'Database', 'db.timestamp'))

    cursor.execute(insert_query, [timestamp, 'current'])
    conn.commit()
示例#5
0
def user_parameters():
    """user_parameters The route to the user parameters page of the flask application

    Renders the user parameters page of the flask app. This page allows the user to 
    view and modify their pacemaker parameters through a submittable form. Users can 
    modify some or all parameters and submit the changes through a post request. Changes 
    will be made immediately in the flask app and database, and the user parameters page 
    updated with these changed values.

    :return: The render template used by the user parameters page, in this case the 'user_parameters.html' template
    :rtype: :class:`flask.render_tmeplate`
    """
    #if a post request is made, a user is trying to change their parameters
    if request.method == 'POST':
        values = dict(request.form)
        required_values = Config.getInstance().get('Parameters', 'parameters.values').split(',')
        for value in required_values:
            if value not in values:
                values[value] = -1
        #create a new parameters dictionary by starting with the original dictionary, and updating its values with the modified value from the post request
        updates = {j: {k.replace(' ', ''): v for k, v in values.items() if v}.get(j, user.get_pacemaker_parameters()[j]) for j in user.get_pacemaker_parameters()}
        #updates all the pacemaker parameters with the newly generated list
        Logger.getInstance().log('DEBUG', 'Pacemaker Parameters Updated: {0}'.format(updates))
        user.update_all_pacemaker_parameters(updates.values())
    parameters = { re.sub(r"(\w)([A-Z])", r"\1 \2", k): user.get_pacemaker_parameters()[k] for k in user.get_pacemaker_parameters() }
    return render_template('user_parameters.html', username=user.get_username(), parameters=parameters, limits=user.get_limits())
示例#6
0
    def __set_limits(self):
        """__set_limits Sets the limits of user parameters

        A private function called by the contructor upon user creation.
        Will search the application configuration and implement any
        parameter limits defined there. Limits are enforced in the entry
        form on the DCM, not the database to ensure a more robust DCM.
        """
        raw_limits = dict(Config.getInstance().get_all('Limits'))
        limits = {}
        for key in self.__parameters.keys():
            query = 'limits.' + re.sub(r"(\w)([A-Z])", r"\1 \2", key).lower()
            disable_query = re.sub(r"(\w)([.])", r"\1.can-disable\2", query)
            limit_entry = {}
            should_add_entry = False
            if query in raw_limits.keys():
                limit_entry['min'] = float(raw_limits[query].split(',')[0])
                limit_entry['max'] = float(raw_limits[query].split(',')[1])
                limit_entry['inc'] = float(raw_limits[query].split(',')[2])
                should_add_entry = True
            if disable_query in raw_limits.keys():
                limit_entry['can_disable'] = bool(raw_limits[disable_query])
                should_add_entry = True
            if should_add_entry:
                limits[re.sub(r"(\w)([A-Z])", r"\1 \2", key)] = limit_entry
        self.__limits = limits
示例#7
0
def user_egram():
    """user_egram The route to the user history page of the flask application

    Renders the user egram page of the flask app. This page allows the user to 
    view a live graph (or egram) of the pacemakers atrium and ventrical chambers.
    NOTE: The graph displayed is an adaptable graph and will take up to one minute 
    to adjust both the graphs domain and range values.

    :return: The render template used by the user connect page, in this case the 'user_egram.html' template
    :rtype: :class:`flask.render_tmeplate`
    """
    if request.method == 'POST':
        publish_data(user.get_username())
    set_start_time()
    domain = int(Config.getInstance().get('Graphing', 'graph.domain'))
    period = int(Config.getInstance().get('Graphing', 'graph.update-period'))
    return render_template('user_egram.html', domain=domain, period=period)
示例#8
0
 def create_history_file(self):
     """create_history_file Creates a csv file containing the users parameter history
     """
     history = self.get_history()
     data_file = open(os.path.join(parentfolder, 'downloads', str(self.__username) + '-User-History-' + str(date.today()) + '.csv' ), 'w')
     csv_writer = csv.writer(data_file)
     test_headers = ['Date', 'Mode'] + Config.getInstance().get('Parameters', 'parameters.values').split(',')
     csv_writer.writerow(test_headers)
     for entry in history:
         csv_writer.writerow(entry[1:])
     data_file.close()
示例#9
0
def publish_data(username):
    """publish_data Publishes the current (or most recent) live graphs data to a csv file

    Will generate a csv file containing all the data from when the current (or most recent)
    graph was started.
    """
    x1 = []
    x2 = []
    y1 = []
    y2 = []

    for point_set in __data:
        x1.append(point_set[0][0])
        y1.append(point_set[0][1])

        x2.append(point_set[1][0])
        y2.append(point_set[1][1])

    figure = plt.figure()
    plt.plot(x1, y1, label='Atrium')
    plt.plot(x2, y2, label='Ventrical')
    plt.xlabel('Time (ms)')
    plt.ylabel('Voltage (V)')
    plt.title("'{0}' Live Egram Data".format(username))
    plt.legend()

    timestamp = datetime.datetime.now().strftime(Config.getInstance().get(
        'Database',
        'db.timestamp')).replace(' ', '_').replace('/', '-').replace(':', '-')
    graph_doc_name = "{0}_Live_Egram_Data_From_{1}.pdf".format(
        username, timestamp)
    pp = PdfPages(os.path.join(parentfolder, 'downloads', graph_doc_name))
    pp.savefig(figure)
    pp.close()

    csv_output = list(zip(x1, y1, x2, y2))

    csv_doc_name = "{0}_Live_Egram_Data_From_{1}.csv".format(
        username, timestamp)
    with open(os.path.join(parentfolder, 'downloads', csv_doc_name),
              'w') as file:
        writer = csv.writer(file)
        writer.writerow([
            'Atrium Timestamp', 'Atrium Value', 'Ventrical Timestamp',
            'Ventrical Value'
        ])
        for line in csv_output:
            writer.writerow(line)
示例#10
0
    def update_pacemaker_mode(self, mode):
        """update_pacemaker_mode Changes the pacemaker mode

        Given a mode that is contained in the application
        configurations list of allowed modes, will change 
        the current pacemaker mode to the new one. If no 
        valid mode is given will do nothing and return
        False.

        :param mode: The pacemaker mode to change to
        :type mode: str
        :return: True if the pacemaker mode was successfully changed, Fale otherwise
        :rtype: bool
        """
        allowed_modes = Config.getInstance().get('Parameters', 'parameters.modes').split(',')
        if not mode in allowed_modes:
            return False
        self.__mode = mode
        update_pacemaker_parameters(self.__conn, self.__cursor, self.__id, [self.__mode] + list(self.__parameters.values()))
        return True
示例#11
0
def insert_user(conn, cursor, username, password):
    """insert_user Given a username and password of a new user, will insert the user into the database

    This function will create a new entry in the database of a user with the given username and password. 
    Only the users username and password are initialized upon user creation, the pacemaker parameters will
    default to None, forcing the user to manually enter their parameters.
    NOTE: This function does no check for conflicting users in the database before inserting a new user. 
    It is up to the user of this function to check for conflicts (if they wish to do so) before calling
    this function.

    :param conn: The connection handler for the database to insert a new user into
    :type conn: :class:`sqlite3.Connection`
    :param cursor: The cursor handler for the database to insert a new user into
    :type cursor: :class:`sqlite3.Cursor`
    :param username: The username for the new user
    :type username: str
    :param password: The password for the new user
    :type password: str
    """
    cursor.execute('INSERT INTO users (username, password) VALUES(?,?)',
                   [username, password])
    conn.commit()

    user_id = find_user(cursor, username=username, password=password)[0][0]

    query_string = 'CREATE TABLE IF NOT EXISTS user_{0}( _entryid INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, mode TEXT'.format(
        str(user_id))
    parameter_list = [
        i.replace(' ', '') for i in Config.getInstance().get(
            'Parameters', 'parameters.values').split(',')
    ]

    for parameter in parameter_list:
        query_string += ', {0} INTEGER'.format(parameter)

    query_string += ');'

    cursor.execute(query_string)
    conn.commit()
示例#12
0
def get_user_parameters(cursor, id):
    """get_user_parameters Returns a complete list of the users most recent parameters

    Return type is a list of parameters. Since the search is done by unique ID,
    this list is garunteed to be of constant length, defined by the parameter list
    in the application configuration.

    :param cursor: The cursor handler for the database the user can be found in
    :type cursor: :class:`sqlite3.Cursor`
    :param id: The unique ID of the user to search for
    :type id: int
    :return: A list of the users current pacemaker parameters
    :rtype: list
    """
    cursor.execute(""" --begin-sql
        SELECT * FROM {0};
    """.format('user_' + str(id)))
    result = cursor.fetchall()
    if len(result) > 0:
        return result[0]
    return [-1, 'current', None] + [
        None for i in Config.getInstance().get('Parameters',
                                               'parameters.values').split(',')
    ]
示例#13
0
from threading import Timer
from time import time
from random import random
import webbrowser, sqlite3, re, json
import serial.tools.list_ports

from config.config_manager import Config, Logger
from config.decorators import login_required, logout_required
from data.user import User
from data.database import init_db
from graphs.graphing import update_data, publish_data, set_start_time
from serialcom.sendSerial import sendSerial


#initialize the config and logger before createing the app
Config()
if not Config.getInstance().is_config_read():
    Config.getInstance().read_config()
Logger()
if not Logger.getInstance().is_logger_started():
    Logger.getInstance().start_logger(Config.getInstance())
#create the user
user = User()
#create the flask app, passing this file as the main, and a random string as the secret key
app = Flask(__name__)
app.secret_key = Config.getInstance().get('Applictation', 'app.secret-key')

global this_port
this_port = None