Skip to content
Snippets Groups Projects
Commit 09bcfb5d authored by Michael DM Dryden's avatar Michael DM Dryden
Browse files

Add database code

parent eb8866f5
Branches
Tags
1 merge request!10Move to gtk3 and new command protocol
import logging
from time import sleep, time
from os.path import expanduser
from uuid import uuid4
from BTrees.OOBTree import OOBTree
import mr_db.mr_db as db
logger = logging.getLogger("dstat.db")
current_db = None
def start_db(path=None):
global current_db
current_db = Database(data_dir=path)
def restart_db(object, path):
logger.info("Restarting database")
global current_db
if current_db is None:
logger.info("No database running")
start_db(path=path)
else:
stop_db()
start_db(path=path)
def stop_db():
global current_db
if not current_db is None:
logger.info("Stopping ZEO")
current_db.disconnect()
current_db = None
db.stop_server()
else:
logger.warning("Tried to disconnect ZEO when not connected")
class Database(object):
def __init__(self, name='dstat', data_dir=None):
if data_dir is None:
data_dir = expanduser('~/.mr_db')
self.connected = False
self.name = name
self.db_connect(data_dir)
self.db = self.connection.databases
# Make sure database exists
if not self.db.has_key(name):
self.db[name] = OOBTree()
db.transaction.commit()
def disconnect(self):
if self.connected is True:
self.connection.db.close()
else:
logger.war("Tried to disconnect ZEO when not connected")
def db_connect(self, root_dir):
"""Connects to ZEO process. Starts ZEO if not running. Returns
connection object.
"""
if root_dir == '':
root_dir = None
while self.connected is False:
try:
self.connection = db.DbConnection(root_dir=root_dir)
self.connected = True
logger.info("Connected to ZEO server")
except db.ClientStorage.ClientDisconnected:
db.stop_server()
logger.info("Starting ZEO server -- root_dir = %s", root_dir)
db_proc = db.start_server(root_dir=root_dir)
sleep(3)
def add_results(self, measurement_uuid=None, measurement_name=None,
experiment_uuid=None, experiment_metadata=None,
patient_id=None,
timestamp=None,
data=None):
"""Add a measurement"""
if experiment_metadata is None:
experiment_metadata = {}
try:
logger.info("Starting DB transaction")
db.transaction.begin()
logger.info("Creating Experiment with id: %s", experiment_uuid)
exp_db, exp_id = self.add_experiment(
experiment_uuid=experiment_uuid,
timestamp=timestamp,
**experiment_metadata)
logger.info("Adding Measurement with id: %s", measurement_uuid)
name = self.add_dstat_measurement(experiment=exp_db[exp_id],
measurement_uuid=measurement_uuid,
name=measurement_name,
timestamp=timestamp,
data=data)
if patient_id is not None:
if not patient_id in self.db['patients']:
logger.info("Creating patient with id: %s", patient_id)
patient = db.Patient(pid=patient_id)
self.db['patients'][patient_id] = patient
if not exp_id in self.db['patients'][patient_id].experiments:
logger.info("Linking experiment into patient with id: %s",
patient_id)
self.db['patients'][patient_id].link_experiment(exp_db,
exp_id)
logger.info("Committing DB transaction")
db.transaction.commit()
return name
except:
logger.error("Aborting DB transaction")
db.transaction.abort()
raise
def add_experiment(self, experiment_uuid=None, timestamp=None, **kwargs):
"""Add a new experiment. Will raise KeyExistsError if id is already
in db to avoid unintended collisions.
----
Arguments:
id: experiment id---UUID will be generated if not supplied
timestamp: current time---Will be generated if not supplied
kwargs: additional keyword arguments that will be saved in experiment.
"""
if experiment_uuid is None:
experiment_uuid = uuid4().hex
if timestamp is None:
timestamp = time()
kwargs.update({'id':experiment_uuid, 'timestamp':timestamp})
if not experiment_uuid in self.db[self.name]:
self.db[self.name][experiment_uuid] = db.PersistentMapping()
else:
logger.info("Experiment already exists, appending")
self.db[self.name][experiment_uuid].update(kwargs)
return (self.db[self.name], experiment_uuid)
def add_dstat_measurement(self, experiment, measurement_uuid=None,
data=None, timestamp=None, name=None):
if measurement_uuid is None:
measurement_uuid = uuid4().hex
if timestamp is None:
timestamp = time()
if data is None:
data = {}
if not 'measurements' in experiment:
experiment['measurements'] = db.PersistentMapping()
if not 'measurements_by_name' in experiment:
experiment['measurements_by_name'] = db.PersistentMapping()
if measurement_uuid in experiment['measurements']:
raise db.KeyExistsError(measurement_uuid,
"Measurement ID already exists. Access directly to update")
data['timestamp'] = timestamp
data['id'] = measurement_uuid
if name is not None:
data['name'] = name
experiment['measurements'][measurement_uuid] = data
if name is not None:
while name in experiment['measurements_by_name']:
first, sep, last = name.rpartition('-')
if last.isdigit():
index = int(last)
index += 1
name = sep.join((first,str(index)))
else:
name += '-1'
experiment['measurements_by_name'][name] = data
return name
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.24"/>
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="db_window">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Database</property>
<property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="db_enable_checkbutton">
<property name="label" translatable="yes">Enable Database saving</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_db_enable_checkbutton_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="db_control_table">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="n_rows">5</property>
<property name="n_columns">3</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement ID</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Experiment ID</property>
</object>
</child>
<child>
<object class="GtkEntry" id="exp_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="measure_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="patient_id_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Patient ID</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="exp_id_autogen_button">
<property name="label" translatable="yes">Generate New</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_exp_id_autogen_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">DB Path</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="db_path_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<object class="GtkButton" id="db_apply_button">
<property name="label" translatable="yes">Apply</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_db_apply_button_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Measurement Name</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="measure_name_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char"></property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="primary_icon_sensitive">True</property>
<property name="secondary_icon_sensitive">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
#!/usr/bin/env python
# DStat Interface - An interface for the open hardware DStat potentiostat
# Copyright (C) 2014 Michael D. M. Dryden -
# Wheeler Microfluidics Laboratory <http://microfluidics.utoronto.ca>
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import logging
from uuid import uuid4
import gtk
import gobject
logger = logging.getLogger('dstat.interface.db')
class DB_Window(gobject.GObject):
__gsignals__ = {
'db-reset' : (gobject.SIGNAL_RUN_LAST,
gobject.TYPE_NONE,
(gobject.TYPE_STRING,)
)
}
def __init__(self):
gobject.GObject.__init__(self)
self.builder = gtk.Builder()
self.builder.add_from_file('interface/db.glade')
self.builder.connect_signals(self)
self.window = self.builder.get_object('db_window')
# Make sure window isn't destroyed if checkbox hit.
self.window.connect('delete-event', self.on_delete_event)
self.db_control_table = self.builder.get_object('db_control_table')
ui_keys = ['exp_id_entry',
'measure_id_entry',
'patient_id_entry',
'measure_name_entry',
'db_path_entry',
'db_apply_button',
'exp_id_autogen_button',
'db_enable_checkbutton'
]
self.persistent_keys = ['db_path_entry','db_enable_checkbutton']
self.ui = {}
for i in ui_keys:
self.ui[i] = self.builder.get_object(i)
self.metadata_map = [('experiment_uuid', 'exp_id_entry'),
('patient_id', 'patient_id_entry'),
('name', 'measure_name_entry')]
self._params = {}
@property
def persistent_params(self):
"""Dict of parameters that should be saved."""
self._get_params()
return {k:self._params[k] for k in self.persistent_keys}
@property
def params(self):
"""Dict of parameters."""
logger.info("getter")
self._get_params()
logger.info("params: %s", self._params)
return self._params
def _get_params(self):
"""Updates self._params from UI."""
for i in self.ui:
if i.endswith('checkbutton'):
self._params[i] = self.ui[i].get_active()
elif i.endswith('entry'):
text = self.ui[i].get_text()
logger.info("%s=%s",i,text)
if text == "":
text = None
self._params[i] = text
@params.setter
def params(self, params):
logger.info("Setter Params: %s", params)
if self._params is {}:
self._params = dict.fromkeys(self.ui.keys())
for i in self._params:
if i in params:
self._params[i] = params[i]
self._set_params()
logger.info("params: %s", self._params)
def _set_params(self):
"""Updates UI with new parameters."""
for i in self.ui:
if i.endswith('checkbutton'):
self.ui[i].set_active(self._params[i])
elif i.endswith('entry'):
if self._params[i] is not None:
self.ui[i].set_text(self._params[i])
else:
self.ui[i].set_text("")
def update_from_metadata(self, metadata):
params = {k : metadata[j] for j, k in self.metadata_map}
# if j in metadata}
self.params = params
def show(self):
self.window.show_all()
self.on_db_enable_checkbutton_toggled()
def on_db_enable_checkbutton_toggled(self, *args):
if self.ui['db_enable_checkbutton'].get_active():
self.db_control_table.show()
else:
self.db_control_table.hide()
def on_exp_id_autogen_button_clicked(self, *args):
self.ui['exp_id_entry'].set_text(uuid4().hex)
def on_db_apply_button_clicked(self, widget=None, *data):
self.emit('db-reset', self.params['db_path_entry'])
def on_delete_event(self, widget=None, *data):
widget.hide()
return True
gobject.type_register(DB_Window)
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment