#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import pickle
import tempfile
import ConfigParser
import shutil
import time
import functions
from model import Model
from minimclass import Minim
from minimpypreferences import PrefWin
from export import Export
from editvariable import EditVariable
try:
import pysvn
pysvn_available = True
except:
pysvn_available = False
try:
import pygtk
pygtk.require("2.0")
import gobject
except:
print("pygtk Not Availible")
sys.exit(1)
try:
import gtk
except:
print("GTK Not Availible")
sys.exit(1)
class ModelInteface(object):
valid_chars = ''.join([chr(x) for x in range(ord('A'), ord('Z')+1)]) + ''.join([chr(x) for x in range(ord('a'), ord('z')+1)]) + ' -_'
def close_window(self, widget, data=None):
self.window.destroy()
if __name__ == "__main__":
sys.exit(1)
def query_trial_dismis(self):
# this method checks whether the trial has unsaved data.
# if there are unsaved data, prompt a warning.
# output: True: means the dismis current state (e.g. start a new trial)
if self.trial_file_name:
return True
else:
if len(self.group_liststore) or len(self.variable_liststore):
msg = "Trial is not saved!\nAre you sure you want to continue?"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Trial data will be lost!")
if dialog.run() == gtk.RESPONSE_NO:
dialog.destroy()
return False
dialog.destroy()
return True
def delete_event(self, widget, event):
if not self.query_trial_dismis():
# do not exit
return True
self.window.destroy()
if __name__ == "__main__":
sys.exit(0)
def group_cell_edited(self, cell, row, new_text, col):
if col == 0:
# names must not be repeated
for r in range(len(self.group_liststore)):
if r != row and new_text == self.group_liststore[r][0]:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Group name already in use! Choose another name!")
return False
if len(new_text) == 0:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Group name can not be empty!")
return False
if col == 1:
if not new_text.isdigit():
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Allocation ratio must be an integer!")
return False
new_text = int(new_text)
if not new_text:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Allocation ratio must be zero!")
return False
self.group_liststore[row][col] = new_text
self.update_interface()
def group_add_button(self, widget, data=None):
self.group_liststore.append(('Group {0}'.format(len(self.group_liststore)+1), 1))
self.update_interface()
def group_delete_button(self, widget, data=None):
if self.group_treeview.get_cursor()[0]:
row = self.group_treeview.get_cursor()[0][0]
self.group_liststore.remove(self.group_liststore.get_iter(row))
for idx, record in enumerate(self.allocations):
if record['allocation'] == row:
self.ui_pool.append(self.allocations[idx]['UI'])
self.allocations[idx] = False
elif record['allocation'] > row:
record['allocation'] -= 1
self.allocations = filter(None, self.allocations)
self.update_interface()
else:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Please select a group first!")
return False
def variable_cell_edited(self, cell, row, new_text, col):
if col == 0:
for r in range(len(self.variable_liststore)):
if r != row and new_text == self.variable_liststore[r][0]:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: This variable name already in use! Choose another name!")
return False
if len(new_text) == 0:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Variable names can not be empty!")
return False
if col == 1:
new_text = float(new_text)
if not new_text:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Variable weight can not be zero!")
return False
if col == 2:
# check for deleting a level
new_levels = map(str.strip, new_text.split(','))
new_text = ','.join(new_levels)
if len(new_levels) < 2:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: At least two variable levels should be entered! Separate levels with commas")
return False
self.variable_liststore[row][col] = new_text
self.update_interface()
def variable_add_button(self, widget, data=None):
self.variable_liststore.append(('Variable {0}'.format(len(self.variable_liststore)+1), 1.0, 'Level 0,Level 1'))
self.update_interface()
def variable_delete_button(self, widget, data=None):
if self.variable_treeview.get_cursor()[0]:
row = self.variable_treeview.get_cursor()[0][0]
self.variable_liststore.remove(self.variable_liststore.get_iter(row))
for idx in range(len(self.allocations)):
self.allocations[idx]['levels'].pop(row)
self.update_interface()
else:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Please select a variable first!")
return False
def update_interface(self):
n = len(self.allocations_treeview.get_columns())
while n:
n = self.allocations_treeview.remove_column(self.allocations_treeview.get_column(n-1))
widgets = self.combo_vbox.get_children()
for widget in widgets:
widget.destroy()
table = gtk.Table(2, len(self.variable_liststore)+1)
table.show()
min_group_old_index = self.min_group_combo.get_active()
if min_group_old_index < 0:
min_group_old_index = 0
if (min_group_old_index + 1) > len(self.group_liststore):
min_group_old_index = 0
if self.min_group_combo.get_model():
self.min_group_combo.get_model().clear()
for group in self.group_liststore:
self.min_group_combo.append_text(group[0])
if len(self.group_liststore):
self.min_group_combo.set_active(min_group_old_index)
self.variable_combos = []
n = 0
for variable in self.variable_liststore:
lbl = gtk.Label(variable[0])
lbl.show()
table.attach(lbl, 0, 2, n, n+1)
n += 1
cbo = gtk.combo_box_new_text()
cbo.show()
for level in map(str.strip, variable[2].split(',')):
cbo.append_text(level)
self.variable_combos.append(cbo)
table.attach(cbo, 0, 2, n, n+1)
n += 1
self.combo_vbox.pack_start(table, False, False)
types = ((str,) * (len(self.variable_liststore) + 3))
liststore = gtk.ListStore(*types)
tvcolumn = gtk.TreeViewColumn('#')
self.allocations_treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 0)
tvcolumn.set_resizable(True)
tvcolumn.set_expand(True)
tvcolumn = gtk.TreeViewColumn('UI')
self.allocations_treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 1)
tvcolumn.set_resizable(True)
tvcolumn.set_expand(True)
tvcolumn = gtk.TreeViewColumn('Group')
self.allocations_treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', 2)
tvcolumn.set_resizable(True)
tvcolumn.set_expand(True)
n = 2
for variable in self.variable_liststore:
n += 1
tvcolumn = gtk.TreeViewColumn(variable[0])
self.allocations_treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', n)
tvcolumn.set_resizable(True)
tvcolumn.set_expand(True)
self.allocations_treeview.set_model(liststore)
self.allocations_treeview.show()
variable_models = [cbo.get_model() for cbo in self.variable_combos]
for row, record in enumerate(self.allocations):
liststore.append()
liststore[row][0] = row
liststore[row][1] = record['UI']
liststore[row][2] = self.group_liststore[record['allocation']][0]
for col, variable_model in enumerate(variable_models):
liststore[row][col+3] = variable_model[record['levels'][col]][0]
def get_minimize_case(self, new_case):
model = Model()
model.groups = range(len(self.group_liststore))
model.variables = [range(len(self.variable_liststore[row][2].split(','))) for row in range(len(self.variable_liststore))]
model.variables_weight = [self.variable_liststore[row][1] for row in range(len(self.variable_liststore))]
model.allocation_ratio = [self.group_liststore[row][1] for row in range(len(self.group_liststore))]
model.allocations = self.allocations
model.prob_method = self.get_prob_method()
model.distance_measure = self.get_distance_measure()
model.high_prob = self.high_prob_spin.get_value()
model.min_group = self.min_group_combo.get_active()
m = Minim(random=self.random, model=model, precision=int(self.config.getfloat('operations', 'precision')))
m.build_probs(model.high_prob, model.min_group)
m.build_freq_table()
if self.initial_freq_table:
self.add_to_initial(m.freq_table)
return m.enroll(new_case, m.freq_table)
def add_to_initial(self, freq_table):
for row, group in enumerate(freq_table):
for v, variable in enumerate(group):
for l, level in enumerate(variable):
freq_table[row][v][l] += self.initial_freq_table[row][v][l]
def remove_last_allocation(self, keep_last=False):
model = self.allocations_treeview.get_model()
if model == None or len(model) == 0:
if self.trial_file_name:
if keep_last:
return False
self.want_trial_unlock(prompt)
return False
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: No subject allocated!")
return False
row = len(model)-1
self.ui_pool.append(model[row][1])
path = (row,)
iter = model.get_iter(path)
model.remove(iter)
self.allocations.pop()
return True
def allocations_undo(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
model = self.allocations_treeview.get_model()
if model == None or len(model) == 0:
if self.trial_file_name:
self.want_trial_unlock(True)
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: No subject allocated!")
return False
msg = "Deallocation may introduce errors in randomization, specially in network trials!\nAre you sure you want to undo the last allocation?"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Undo Allocation!")
if dialog.run() == gtk.RESPONSE_NO:
dialog.destroy()
return False
dialog.destroy()
self.remove_last_allocation()
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
def allocations_undo_button(self, widget, data=None):
self.waiting_function(self.allocations_undo, ())
def allocations_clear(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
model = self.allocations_treeview.get_model()
if model == None or len(model) == 0:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Allocation table is empy!")
return
msg = "Are you sure you want to remove all allocations!\nData will be lost permanently, and the trial will be unlocked?"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Clear Allocations!")
if dialog.run() == gtk.RESPONSE_NO:
dialog.destroy()
return False
dialog.destroy()
while self.remove_last_allocation(keep_last=True):
pass
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
def allocations_clear_button(self, widget, data=None):
self.waiting_function(self.allocations_clear, ())
def allocations_add_all(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
model = self.allocations_treeview.get_model()
if not model:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Groups and prognostic factors must be defined first!")
return False
if not self.ui_pool:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, 'Error: Either this trial is finished or has not been saved!')
return False
while self.ui_pool:
for cbo in self.variable_combos:
item = self.random.choice(range(len(cbo.get_model())))
cbo.set_active(item)
model.append()
ui = self.ui_pool.pop()
new_case = {'levels': [], 'allocation': -1, 'UI': ui}
row = len(model)-1
model[row][0] = row
model[row][1] = ui
for col, cbo in enumerate(self.variable_combos):
model[row][col+3] = cbo.get_active_text()
new_case['levels'].append(cbo.get_active())
try:
m_group = self.get_minimize_case(new_case)
if m_group == -1:
self.remove_last_allocation(keep_last=True)
functions.error_dialog(self.window, 'Allocation error while minimizing the subject!\nCheck precision in "Preferences"', 'Minimization failed!')
return False
except Exception as ex:
self.remove_last_allocation(keep_last=True)
functions.error_dialog(self.window, 'Allocation error while minimizing the subject!\nPlease reload the trial', 'Minimization failed!')
return False
new_case['allocation'] = m_group
model[row][2] = self.group_liststore[m_group][0]
self.allocations.append(new_case)
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
def allocations_add_all_button(self, widget, data=None):
self.waiting_function(self.allocations_add_all, ())
def random_case_button(self, button, data=None):
for cbo in self.variable_combos:
item = self.random.choice(range(len(cbo.get_model())))
cbo.set_active(item)
def allocations_add(self):
cbo_selects = []
for cbo in self.variable_combos:
if cbo.get_active() == -1:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, 'Error: Some factor levels have not been selected for this case!')
return False
cbo_selects.append(cbo.get_active())
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
for i, cbo in enumerate(self.variable_combos):
cbo.set_active(cbo_selects[i])
model = self.allocations_treeview.get_model()
if not model:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Groups and prognostic factors must be defined first!")
return False
if not self.ui_pool:
if len(model):
if not self.set_extra_sample_size():
return False
else:
model = self.allocations_treeview.get_model()
else:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, 'Error: This trial has not been saved!')
return False
model.append()
ui = self.ui_pool.pop()
new_case = {'levels': [], 'allocation': -1, 'UI': ui}
row = len(model)-1
model[row][0] = row
model[row][1] = ui
for col, cbo in enumerate(self.variable_combos):
model[row][col+3] = cbo.get_active_text()
new_case['levels'].append(cbo.get_active())
try:
m_group = self.get_minimize_case(new_case)
if m_group == -1:
self.remove_last_allocation(keep_last=True)
functions.error_dialog(self.window, 'Allocation error while minimizing the subject!\nCheck precision in "Preferences"', 'Minimization failed!')
return False
except Exception as ex:
self.remove_last_allocation(keep_last=True)
functions.error_dialog(self.window, 'Allocation error while minimizing the subject!\nPlease reload the trial', 'Minimization failed!')
return False
new_case['allocation'] = m_group
model[row][2] = self.group_liststore[m_group][0]
self.allocations.append(new_case)
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
self.allocations_treeview.scroll_to_cell((row,))
self.allocations_treeview.set_cursor((row,),)
self.window.set_focus(self.allocations_treeview)
self.report_allocation(model)
if len(self.ui_pool) == 0:
self.end_trial()
def allocations_add_button(self, widget, data=None):
self.waiting_function(self.allocations_add, ())
def report_allocation(self, model):
column = self.allocations_treeview.get_columns()
row = len(model)-1
ret = []
for col, column in enumerate(column):
ret.append(column.get_title() + ': ' + model[row][col])
return
# if dialog is desired to show the allocation result
msg = "New case allocated\n" + '\n'.join(ret)
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format = msg)
dialog.set_title("New Allocation!")
dialog.run()
dialog.destroy()
def end_trial(self):
msg = "{0} cases have been minimized!\nDo you like to extend the sample size".format(len(self.allocations))
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Trial finished!")
if dialog.run() == gtk.RESPONSE_YES:
dialog.destroy()
return self.set_extra_sample_size()
dialog.destroy()
def set_extra_sample_size(self):
extra_sample = 0
extra_sample = functions.simple_spin_dialog("We have reached to the end of sample size\nExtra Sample", self.window)
if extra_sample == 0: return False
ss = len(self.allocations)+int(extra_sample)
self.trial_sample_size_spin.set_value(ss)
self.build_ui_pool(range(len(self.allocations), ss))
self.save_trial_changes()
model = self.allocations_treeview.get_model()
for row in range(len(model)):
model[row][1] = model[row][1].zfill(len(str(ss)))
return True
def refresh_freq_table(self):
model = Model()
model.groups = range(len(self.group_liststore))
model.variables = [range(len(self.variable_liststore[row][2].split(','))) for row in range(len(self.variable_liststore))]
model.variables_weight = [self.variable_liststore[row][1] for row in range(len(self.variable_liststore))]
model.allocation_ratio = [self.group_liststore[row][1] for row in range(len(self.group_liststore))]
model.allocations = self.allocations
m = Minim(random=self.random, model=model, precision=int(self.config.getfloat('operations', 'precision')))
m.build_freq_table()
if self.initial_freq_table:
self.add_to_initial(m.freq_table)
if not len(m.freq_table):
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: No allocation!")
return False
n = len(self.freq_table_treeview.get_columns())
while n:
n = self.freq_table_treeview.remove_column(self.freq_table_treeview.get_column(n-1))
#self.initial_table_hbox.hide()
# following for test only
# m.freq_table = [[[18, 25], [6, 3, 3, 4, 3, 6, 4, 5, 5, 4]], [[39, 45], [12, 8, 7, 7, 7, 9, 6, 12, 11, 5]], [[55, 68], [17, 12, 10, 14, 10, 13, 8, 16, 15, 8]]]
cols = 0
column_names = []
for variable in self.variable_liststore:
column_names.extend([level for level in map(str.strip, variable[2].split(','))])
column_names = ['Group'] + column_names + ['Total']
for var in m.freq_table[0]:
cols += len(var)
col_types = [str] + [int] * (cols + 1)
liststore = gtk.ListStore(*col_types)
for n in range(len(column_names)):
tvcolumn = gtk.TreeViewColumn(column_names[n])
self.freq_table_treeview.append_column(tvcolumn)
cell = gtk.CellRendererText()
#cell.set_property('editable', True)
#cell.connect("edited", edit_handler, n)
tvcolumn.pack_start(cell, True)
tvcolumn.add_attribute(cell, 'text', n)
tvcolumn.set_resizable(True)
tvcolumn.set_expand(True)
self.freq_table_treeview.set_model(liststore)
for row, group in enumerate(m.freq_table):
liststore.append()
liststore[row][0] = self.group_liststore[row][0]
col = 1
for variable in group:
for level in variable:
liststore[row][col] = level
col += 1
liststore[row][col] = sum(variable)
liststore.append()
liststore[len(m.freq_table)][0] = "Total"
total = 0
for c in range(1, col):
col_total = sum([liststore[row][c] for row in range(len(m.freq_table))])
total += col_total
liststore[len(m.freq_table)][c] = col_total
liststore[len(m.freq_table)][c+1] = total / len(self.variable_liststore)
if self.freq_table_enable_edit_button.get_label() == "Save as Initial Table":
self.freq_table_enable_edit()
def notebook_switch_page(self, page_num):
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, self.notbook_tabs[page_num])
if page_num < 4: return
if not self.trial_is_valid(show_msg=False, table_edit=True): return
if self.freq_table_enable_edit_button.get_label() == "Save as Initial Table": return
self.refresh_freq_table()
self.balance_table_refresh()
def notebook_switch_page_event(self, notebook, page, page_num, data=None):
if not page_num in (4, 5): return
self.waiting_function(self.notebook_switch_page, (page_num,))
def license_button_clicked(self, widget, data=None):
self.display_about_dialog()
def display_about_dialog(self):
dialog = gtk.AboutDialog()
dialog.set_name("MinimPy Program")
dialog.set_version("0.1")
dialog.set_copyright("Copyright (c) 2010-2011 Mahmoud Saghaei")
dialog.set_license("Distributed under the GNU GPL v3.\nFor full terms refer to http://www.gnu.org/copyleft/gpl.html.")
dialog.set_website("http://minimpy.sourceforge.net")
dialog.set_authors(["Mahmoud Saghaei"])
dialog.set_logo(self.pixbuf)
dialog.run()
dialog.destroy()
return False
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type =
gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format = "XKnight game\nCopyright (c) 2008-2009 Mahmoud Saghaei\nDistributed under the GNU GPL v3.\nFor full terms refer to http://www.gnu.org/copyleft/gpl.html.")
dialog.set_title("About XKnight Game!")
dialog.run()
dialog.destroy()
def trial_properties_cell_edited(self, cell, row, new_text, col):
self.trial_properties_liststore[row][col] = new_text
def trial_properties_add_button(self, widget, data=None):
self.trial_properties_liststore.append(('Property Name', 'Property Value'))
def trial_properties_delete_button(self, widget, data=None):
if self.network_sync_dict['sync'] and self.trial_file_name:
# with network trials, deleting trial properties after saving the trials may be too complicated.
return False
if self.trial_properties_treeview.get_cursor()[0]:
row = self.trial_properties_treeview.get_cursor()[0][0]
self.trial_properties_liststore.remove(self.trial_properties_liststore.get_iter(row))
def show_info_message(self, widget, event, message, tooltip):
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format = message)
dialog.set_title(tooltip)
dialog.run()
dialog.destroy()
def create_image_info(self, parent, tooltip, message=""):
eb = gtk.EventBox()
img = gtk.Image()
image_file = os.path.join(os.path.dirname(sys.argv[0]), 'images', 'b_info.png')
img.set_from_file(image_file)
eb.add(img)
if len(message):
eb.connect("button-press-event", self.show_info_message, message, tooltip)
tooltip += "\nClick to see more"
img.set_tooltip_text(tooltip)
img.set_has_tooltip(True)
parent.pack_start(eb, False, False)
def network_sync_ssl_server_trust_prompt(self, trust_dict):
return True, 3, True
def network_sync_get_login(self, realm, username, may_save):
self.get_repo_login()
return self.network_sync_dict['auth'], self.network_sync_user, self.network_sync_pw, True
def network_sync_chk_toggled(self, chk, data=None):
if not chk.get_active():
self.network_sync_dict['sync'] = False
return
dlg = gtk.Dialog(title='Network Sync',
parent=self.window,
flags=gtk.DIALOG_MODAL,
buttons=(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dlg.set_default_response(gtk.RESPONSE_APPLY)
dlg.set_has_separator(True)
lbl = gtk.Label('Warning: "Network Sync" currently is experimental\nUse it at your own risk!')
dlg.vbox.pack_start(lbl)
sep = gtk.HSeparator()
dlg.vbox.pack_start(sep)
hb = gtk.HBox(False)
lbl = gtk.Label('Repository URL')
hb.pack_start(lbl, False, False)
url = gtk.Entry()
if self.network_sync_dict.has_key('url'):
url.set_text(self.network_sync_dict['url'])
hb.pack_start(url, True, True)
dlg.vbox.pack_start(hb)
auth = gtk.CheckButton('Requires Authentication')
auth.set_active(self.network_sync_dict['auth'])
dlg.vbox.pack_start(auth)
dlg.vbox.show_all()
controls = url, auth, chk
dlg.connect('response', self.network_sync_dialog_respnse, controls)
dlg.run()
def network_sync_dialog_respnse(self, dialog, response_id, controls):
if response_id in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT):
controls[-1].set_active(False)
dialog.hide()
return
url, auth, chk = controls
if url.get_text() == '':
dialog.set_focus(url)
return
self.network_sync_dict['sync'] = True
self.network_sync_dict['url'] = url.get_text()
self.network_sync_dict['auth'] = auth.get_active()
dialog.hide()
def get_repo_login(self):
if not self.network_sync_dict['auth']: return False
dlg = gtk.Dialog(title='Network Login',
parent=self.window,
flags=gtk.DIALOG_MODAL,
buttons=(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
dlg.set_default_response(gtk.RESPONSE_OK)
dlg.set_has_separator(True)
lbl = gtk.Label('Please enter the username and password for:\n{0}'.format(self.network_sync_dict['url']))
dlg.vbox.pack_start(lbl)
hb = gtk.HBox(False)
lbl = gtk.Label('Username: ')
hb.pack_start(lbl, False, False)
user = gtk.Entry()
user.set_text(self.network_sync_user)
hb.pack_start(user, True, True)
dlg.vbox.pack_start(hb)
hb = gtk.HBox(False)
lbl = gtk.Label('Password: ')
hb.pack_start(lbl, False, False)
pw = gtk.Entry()
pw.set_visibility(False)
pw.set_text(self.network_sync_pw)
hb.pack_start(pw, True, True)
dlg.vbox.pack_start(hb)
dlg.vbox.show_all()
controls = user, pw
dlg.connect('response', self.get_repo_login_respnse, controls)
dlg.run()
def get_repo_login_respnse(self, dialog, response_id, controls):
if response_id in (gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT):
self.network_sync_user = ''
self.network_sync_pw = ''
dialog.hide()
self.network_sync_cancel_command = True
return
user, pw = controls
if user.get_text() == '':
dialog.set_focus(user)
return
if pw.get_text() == '':
dialog.set_focus(pw)
return
self.network_sync_user = user.get_text()
self.network_sync_pw = pw.get_text()
dialog.hide()
def variable_treeview_row_activated(self, treeview, path, view_column, data=None):
EditVariable(self, path[0])
def __init__(self):
self.file_chooser_current_folder = os.getcwd()
self.network_sync_cancel_command = False
self.config_file = os.path.join(os.path.dirname(sys.argv[0]), 'config.cfg')
self.config = ConfigParser.RawConfigParser()
self.config.read(self.config_file)
self.random = functions.get_app_random(self.config)
if self.config.getboolean('project', 'load_recent'):
self.trial_file_name = self.config.get('project', 'recent_trial')
else:
self.trial_file_name = None
self.notbook_tabs = []
self.ui_pool = []
self.initial_freq_table = False
self.allocations = []
self.network_sync_dict = dict(sync=False, url='', auth=False)
self.network_sync_user = ''
self.network_sync_pw = ''
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Minimization Program")
self.window.connect("delete_event", self.delete_event)
self.window.set_border_width(2)
self.window.set_size_request(750, 400)
module_list = ['os', 'sys', 'pickle', 'tempfile', 'ConfigParser', 'shutil', 'time', 'math', 'random', 'pygtk', 'gtk', 'gobject', 'pickle', 'pysvn']
ret = functions.check_dependencies(module_list)
if ret:
msg = 'Following necessary modules are not available on your system\n' + '\t'.join(ret) + '\nPlease read the installation help to install them'
functions.error_dialog(self.window, msg, 'module not found!')
sys.exit(1)
#label = gtk.Label("Network Sync! Please wait!")
label = gtk.Label("")
label.show()
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
#dialog.set_title("Network Sync!")
dialog.set_title("")
dialog.vbox.pack_end(label)
self.network_sync_dialog = dialog
self.network_sync_dialog_label = label
self.notebook = gtk.Notebook()
self.notebook.set_tab_pos(self.config.getint('interface', 'tab_pos'))
self.notebook.popup_enable()
self.notebook.connect("switch-page", self.notebook_switch_page_event)
img = gtk.Image()
image_file = os.path.join(os.path.dirname(sys.argv[0]), 'images', 'logo.png')
img.set_from_file(image_file)
img.show()
self.pixbuf = img.get_pixbuf()
win_vbox = gtk.VBox(False)
win_vbox.pack_start(self.notebook, True, True)
hb = gtk.HBox(False)
self.statusbar = gtk.Statusbar()
hb.pack_start(self.statusbar, True, True)
icon_box = gtk.HBox(False)
self.network_sync_icon = gtk.Image()
self.network_sync_icon.set_from_stock(gtk.STOCK_CONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.network_sync_icon.set_tooltip_text('Network Sync is enabled!')
icon_box.pack_start(self.network_sync_icon, False, False)
hb.pack_start(icon_box, False, False)
win_vbox.pack_start(hb, False, False, 0)
self.sb_context_id = self.statusbar.get_context_id("Statusbar")
nb_vbx = gtk.VBox(False)
hbx1 = gtk.HBox(False)
label = gtk.Label("Trial Title: ")
hbx1.pack_start(label, False, False)
self.create_image_info(hbx1, "Trial title!", "Please enter a valid title for your trial!\nTrial title is mandatory!")
self.trial_title_entry = gtk.Entry()
hbx1.pack_start(self.trial_title_entry, True, True)
label = gtk.Label("Sample Size: ")
hbx1.pack_start(label, False, False)
self.create_image_info(hbx1, "Trial sample size!", "Please enter the trial sample size!\nTrial sample size is mandatory!")
adj = gtk.Adjustment(value=30, lower=5, upper=1000000, step_incr=1, page_incr=10)
self.trial_sample_size_spin = gtk.SpinButton(adj)
hbx1.pack_start(self.trial_sample_size_spin, False, False)
nb_vbx.pack_start(hbx1, False, False)
hbx2 = gtk.HBox(False)
label = gtk.Label("Description: ")
hbx2.pack_start(label, False, False)
self.create_image_info(hbx2, "Trial description!", "You may enter a description for your trial!\nTrial description is optional!")
nb_vbx.pack_start(hbx2, False, False)
sep = gtk.HSeparator()
nb_vbx.pack_start(sep, False, False)
hbx3 = gtk.HBox(False)
sep = gtk.VSeparator()
hbx3.pack_start(sep, False, False)
self.trial_description_text = gtk.TextView()
hbx3.pack_start(self.trial_description_text, True, True)
sep = gtk.VSeparator()
hbx3.pack_start(sep, False, False)
nb_vbx.pack_start(hbx3, True, True)
sep = gtk.HSeparator()
nb_vbx.pack_start(sep, False, False)
hbx4 = gtk.HBox(False)
nb_vbx.pack_start(hbx4, True, True)
label = gtk.Label("Probability Method: ")
hb = gtk.HBox(False)
hb.pack_start(label, False, False)
self.create_image_info(hb, "Probability Method", """Probability of assignment subjects to trial groups!\n
The subject is allocated to the preferred treatment with a higher probability and to other groups with lower probabilities. In the simple form the probabilities are not affected by allocation ratios and usually the same high probability are used for all treatment groups when they are selected as preferred treatments (base probability). This is denoted by Naive Minimization. In this method, probabilities for non-preferred treatments are distributed equally. If treatments with higher allocations ratios are assigned more probabilities this will be biased-coin minimization. In this method a base probability is used for group with the lowest allocation ratio every time that group is selected as the preferred treatment. Probabilities for other groups (PHi) when they selected as preferred treatments are calculated as a function of base probability and allocation ratios
""")
vbox_prob = gtk.VBox(False)
vbox_prob.pack_start(hb, False, False)
radio = gtk.RadioButton(None, label="Biased Coin Minimization")
vbox_prob.pack_start(radio, False, False)
radio = gtk.RadioButton(radio, label="Naive Minimization")
vbox_prob.pack_start(radio, False, False)
# this method returns the radios in revese order
self.prob_method_radios = radio.get_group()
# so we need to invert the returned list
self.prob_method_radios.reverse()
hb = gtk.HBox(False)
lbl = gtk.Label("Min Group: ")
hb.pack_start(lbl, False, False)
self.create_image_info(hb, "Group with the lowest allocation ratio", "In the biased-coin minimization it is the index of the group with the lowest allocation ratio. Select the group index from the menu. It has no effect in naive minimization")
self.min_group_combo = gtk.combo_box_new_text()
hb.pack_start(self.min_group_combo, False, False)
vbox_prob.pack_start(hb, False, False)
hb = gtk.HBox(False)
lbl = gtk.Label("High Probability: ")
hb.pack_start(lbl, False, False)
self.create_image_info(hb, "Probability for the Min Group", """The amount of probability given to the group with the lowest allocation ratio in biased-coin minimization. In naive minimization it is the probability used for all treatment groups when they are selected as preferred treatments. Use the spin button to specify the value. In naive minimization this will be the base probability""")
adj = gtk.Adjustment(value=0.7, lower=0.1, upper=1.0, step_incr=0.01, page_incr=0.1)
self.high_prob_spin = gtk.SpinButton(adj, digits=2)
hb.pack_start(self.high_prob_spin, False, False)
vbox_prob.pack_start(hb, False, False)
hbx4.pack_start(vbox_prob, False, False)
sep = gtk.VSeparator()
hbx4.pack_start(sep, False, False)
label = gtk.Label("Distance Measure: ")
hb = gtk.HBox(False)
hb.pack_start(label, False, False)
self.create_image_info(hb, "Distanc measure used to calculate the imbalance score!")
vbox = gtk.VBox(False)
vbox.pack_start(hb, False, False)
radio = gtk.RadioButton(None, label="Marginal Balance: ")
hb = gtk.HBox(False)
hb.pack_start(radio, False, False)
self.create_image_info(hb, "Marginal balance", "Marginal balance proposed by Han and co-workers which tend to minimize more accurately when treatment groups are not equal in size. For each factor level marginal balance is calculated as a function of adjusted number of patients present in a factor level for each treatment group.")
vbox.pack_start(hb, False, False)
radio = gtk.RadioButton(radio, label="Range: ")
hb = gtk.HBox(False)
hb.pack_start(radio, False, False)
vbox.pack_start(hb, False, False)
self.create_image_info(hb, "Range", "For each factor, range is calculated as the difference between adjusted maximum and minimum counts of subject accross all factor levels for each group.")
radio = gtk.RadioButton(radio, label="Standard Deviation: ")
hb = gtk.HBox(False)
hb.pack_start(radio, False, False)
vbox.pack_start(hb, False, False)
self.create_image_info(hb, "Standard Deviation", "For each factor, SD is calculated as the SD of adjusted count values of subject accross all factor levels for each group.")
radio = gtk.RadioButton(radio, label="Variance: ")
hb = gtk.HBox(False)
hb.pack_start(radio, False, False)
vbox.pack_start(hb, False, False)
self.create_image_info(hb, "Variance", "For each factor, variance is calculated as the variance of adjusted count values of subject accross all factor levels for each group.")
hbx4.pack_start(vbox, False, False)
self.distance_measure_radios = radio.get_group()
self.distance_measure_radios.reverse()
sep = gtk.VSeparator()
hbx4.pack_start(sep, False, False)
vbox = gtk.VBox(False)
lbl = gtk.Label("Trial Properties: ")
hb = gtk.HBox(False)
hb.pack_start(lbl, False, False)
vbox.pack_start(hb, False, False)
self.create_image_info(hb, "Trial Properties", "Miscelaneous trial properties such as co-ordinator, moderator, investigator, data analyser, etc. Each property must have a name and value (description)")
hb = gtk.HBox(False)
self.trial_properties_liststore, self.trial_properties_treeview = functions.make_treeview(
(str, str), ['Name', 'Value'],
editable=True, edit_handler=self.trial_properties_cell_edited)
hb.pack_start(self.trial_properties_treeview, True, True)
vbuttonbox = gtk.VButtonBox()
vbuttonbox.set_layout(gtk.BUTTONBOX_START)
button = gtk.Button(None, gtk.STOCK_ADD)
button.connect("clicked", self.trial_properties_add_button)
vbuttonbox.pack_start(button, False, False)
button = gtk.Button(None, gtk.STOCK_DELETE)
button.connect("clicked", self.trial_properties_delete_button)
vbuttonbox.pack_start(button, False, False)
hb.pack_start(vbuttonbox, False, False)
vbox.pack_start(hb, False, False)
hbx4.pack_start(vbox, True, True)
sep = gtk.VSeparator()
hbx4.pack_start(sep, False, False)
sep = gtk.HSeparator()
nb_vbx.pack_start(sep, False, False)
hbuttonbox = gtk.HButtonBox()
hbuttonbox.set_layout(gtk.BUTTONBOX_START)
self.trial_new_button = gtk.Button(None, gtk.STOCK_NEW)
self.trial_new_button.connect("clicked", self.new_trial_clicked)
hbuttonbox.pack_start(self.trial_new_button, False, False)
self.trial_load_button = gtk.Button(None, gtk.STOCK_OPEN)
self.trial_load_button.connect("clicked", self.load_trial_clicked)
hbuttonbox.pack_start(self.trial_load_button, False, False)
self.trial_save_button = gtk.Button(None, gtk.STOCK_SAVE)
self.trial_save_button.connect("clicked", self.save_trial_clicked)
hbuttonbox.pack_start(self.trial_save_button, False, False)
self.network_sync_chk = gtk.CheckButton('Network Sync')
self.network_sync_chk.set_sensitive(False)
self.network_sync_chk_event_id = self.network_sync_chk.connect("toggled", self.network_sync_chk_toggled)
hbuttonbox.pack_start(self.network_sync_chk, False, False)
if pysvn_available:
self.network_sync_chk.set_sensitive(True)
self.pref_button = gtk.Button(None, gtk.STOCK_PREFERENCES)
self.pref_button.connect("clicked", self.pref_clicked)
hbuttonbox.pack_start(self.pref_button, False, False)
button = gtk.Button(None, gtk.STOCK_INFO)
button.connect("clicked", self.trial_info_button_clicked)
hbuttonbox.pack_start(button, False, False)
nb_vbx.pack_start(hbuttonbox, False, False)
lbl = gtk.Label("Settings")
self.notbook_tabs.append("Settings: Define and save a new trial or load a previous trial")
self.notebook.append_page(nb_vbx, lbl)
hbox = gtk.HBox(False)
self.group_liststore, self.group_treeview = functions.make_treeview(
(str, int), ['Group Name', 'Allocation Ratio'],
editable=True, edit_handler=self.group_cell_edited)
vb = gtk.VBox(False)
self.create_image_info(vb, "Groups Table", "Click add to add a new group. Then click on the group name or allocation ratio to edit the value. After finishing the edit, hit enter key to fix the edited value. Allocation ratios are integer numeric values. Click delete to delete the selected group.")
vb.pack_start(self.group_treeview, True, True)
hbox.pack_start(vb, True, True)
vbuttonbox = gtk.VButtonBox()
vbuttonbox.set_layout(gtk.BUTTONBOX_START)
button = gtk.Button(None, gtk.STOCK_ADD)
button.connect("clicked", self.group_add_button)
vbuttonbox.pack_start(button, False, False)
button = gtk.Button(None, gtk.STOCK_DELETE)
button.connect("clicked", self.group_delete_button)
vbuttonbox.pack_start(button, False, False)
hbox.pack_start(vbuttonbox, False, False)
lbl = gtk.Label('Groups')
self.notbook_tabs.append("Groups: Define trial groups and their allocation ratios")
self.group_hbox = hbox
self.notebook.append_page(hbox, lbl)
hbox = gtk.HBox(False)
self.variable_liststore, self.variable_treeview = functions.make_treeview(
(str, float, str), ['Variable Name', 'Weight', 'Levels'],
editable=True, edit_handler=self.variable_cell_edited)
tvcolumn = gtk.TreeViewColumn('Doubl click to edit')
self.variable_treeview.append_column(tvcolumn)
cell = gtk.CellRendererPixbuf()
tvcolumn.pack_start(cell, True)
cell.set_property('stock-id', gtk.STOCK_EDIT)
tvcolumn.set_resizable(False)
tvcolumn.set_expand(False)
self.variable_treeview.connect('row-activated', self.variable_treeview_row_activated)
vb = gtk.VBox(False)
self.create_image_info(vb, "Variable Table", "Click add to add a new variable. Then click on the variable name, weight or levels to edit the value. After finishing the edit, hit enter key to fix the edited value. Variable weights are decimal fraction values denoting the relative weight (importance) of that variable in minimization. Variable levels are the labels of levels of each variable separated by a comma. Click delete to delete the selected variable.")
vb.pack_start(self.variable_treeview, True, True)
hbox.pack_start(vb, True, True)
vbuttonbox = gtk.VButtonBox()
vbuttonbox.set_layout(gtk.BUTTONBOX_START)
button = gtk.Button(None, gtk.STOCK_ADD)
button.connect("clicked", self.variable_add_button)
vbuttonbox.pack_start(button, False, False)
button = gtk.Button(None, gtk.STOCK_DELETE)
button.connect("clicked", self.variable_delete_button)
vbuttonbox.pack_start(button, False, False)
hbox.pack_start(vbuttonbox, False, False)
lbl = gtk.Label('Variables')
self.notbook_tabs.append("Variables: Define trial prognostic factors, their weights and levels")
self.variable_hbox = hbox
self.notebook.append_page(hbox, lbl)
hbox = gtk.HBox(False)
vbox = gtk.VBox(False)
vb = gtk.VBox(False)
self.create_image_info(vb, "Allocations Table", "After saving the trial settings, defining groups and variables, now you can allocate incoming subjects to your trial. If the variables are defined you will see drop-down menus, one for each variable. Each menu contains lables, one for each levels within that group. For incominig subjects you have to select appropriate variable levels from these menus and then click add to minimize that subject into your trial. The the program will estimagte the best group for the specified subject based on the minimization settings, and allocate it to the trial. The subject will be added to the allocation table and a dialog will show the minimization result. In the minimization table for each subject you will see the UI (the unique identifier), group and the values level for each variable. Click 'Undo' to remove the last enrolled subject from the table. After removing all subjects, if you hit 'Undo' button for another time, a dialogue will appear and will ask you if you want to unlock the trial. Clicking the 'Clear' button will clear the table and unlock the trial instantly.")
hb_button_box = gtk.HBox()
self.hbuttonbox_batch_mode = gtk.HButtonBox()
self.hbuttonbox_batch_mode.set_layout(gtk.BUTTONBOX_START)
button = gtk.Button("Add All")
button.connect("clicked", self.allocations_add_all_button)
self.hbuttonbox_batch_mode.pack_start(button, False, False)
button = gtk.Button("Random Case")
button.connect("clicked", self.random_case_button)
self.hbuttonbox_batch_mode.pack_start(button, False, False)
hb_button_box.pack_start(self.hbuttonbox_batch_mode, True, True)
hbuttonbox = gtk.HButtonBox()
hbuttonbox.set_layout(gtk.BUTTONBOX_START)
button = gtk.Button(None, gtk.STOCK_ADD)
button.connect("clicked", self.allocations_add_button)
hbuttonbox.pack_start(button, False, False)
button = gtk.Button(None, gtk.STOCK_UNDO)
button.connect("clicked", self.allocations_undo_button)
hbuttonbox.pack_start(button, False, False)
button = gtk.Button(None, gtk.STOCK_CLEAR)
button.connect("clicked", self.allocations_clear_button)
hbuttonbox.pack_start(button, False, False)
button = gtk.Button('Import')
button.connect("clicked", self.allocations_import_button)
hbuttonbox.pack_start(button, False, False)
button = gtk.Button('Export')
button.connect("clicked", self.allocations_export_button)
hbuttonbox.pack_start(button, False, False)
hb_button_box.pack_start(hbuttonbox, True, True)
#self.total_allocation_label = gtk.Label('Total cases: ')
#hb_button_box.pack_start(self.total_allocation_label, True, True)
vb.pack_start(hb_button_box, False, False)
self.allocations_treeview = gtk.TreeView()
vb.pack_start(self.allocations_treeview, True, True)
hbox.pack_start(vb, True, True)
self.combo_vbox = gtk.VBox(False)
vbox.pack_start(self.combo_vbox, False, False)
hbox.pack_start(vbox, False, False)
lbl = gtk.Label('Allocations')
self.notbook_tabs.append('Allocations: Allocate subjects to treatments. To use minimization select "Minimize" as group name')
self.notebook.append_page(hbox, lbl)
vbox = gtk.VBox(False)
self.create_image_info(vbox, "Frequency table!", "This is the frequency table, showing the counts of different factor levels in each group for subjects already allocated! You can change this table to set an initial allocation load (initial frequency table). This is usually need when you have a number of subject already allocated by any method, and you want to use minimpy for allocation of remaining subjects. This table is editable only at the statrt of the trial, when you are configuring setting. After setting trial title, description, group, variables, etc. you can specify an initial frequency table. Then click on the 'Start Edit; button to make the table editable. The button changes to 'Save as Initial Table'. To change values click on the value, edit it and hit the enter key. After entering the initial frequency table hit the 'Save as Initial Table' button to save this table as the initial frequency table. To clear the initial frequency table and refreshing rows and columns labels click 'Clear / Refresh'.")
self.freq_table_treeview = gtk.TreeView()
vbox.pack_start(self.freq_table_treeview, False, False)
self.initial_table_hbox = gtk.HBox(False)
self.freq_table_enable_edit_button = gtk.Button("Start Edit")
self.freq_table_enable_edit_button.connect("clicked", self.freq_table_start_edit_clicked)
self.initial_table_hbox.pack_start(self.freq_table_enable_edit_button, False, False)
button = gtk.Button("Clear / Refresh")
button.connect("clicked", self.freq_table_delete_initial_clicked)
self.initial_table_hbox.pack_start(button, False, False)
vbox.pack_start(self.initial_table_hbox, False, False)
lbl = gtk.Label('Table')
self.notbook_tabs.append("Table: Displays frequency table for different levels of prognostic factors")
self.notebook.append_page(vbox, lbl)
hbox = gtk.HBox(False)
vb = gtk.VBox(False)
self.create_image_info(vb, "Balance Table", "This table shows the overall balance of different factors levels amoung trial groups. Four different imbalance scores are displayed, and are grouped by each variable, levels within each variable. Overall trial balance is displayed as mean and maximum values observed in each column")
treestore = gtk.TreeStore(str, float, float, float, float)
self.balance_table_treeview = gtk.TreeView(treestore)
col_names = ['Variable/Level', 'Marginal Balance', 'Range', 'Variance', 'SD']
for i in range(5):
cell = gtk.CellRendererText()
column = gtk.TreeViewColumn(col_names[i], cell, text=i)
self.balance_table_treeview.append_column(column)
vb.pack_start(self.balance_table_treeview, True, True)
hbox.pack_start(vb, True, True)
lbl = gtk.Label('Balance')
self.notbook_tabs.append("Balance: Displays current trial balance as four different balance measures")
self.notebook.append_page(hbox, lbl)
hbox = gtk.HBox(True)
self.about_text = gtk.TextView()
self.about_text.set_wrap_mode(gtk.WRAP_WORD)
self.about_text.set_editable(False)
self.about_text.set_cursor_visible(False)
self.about_text.set_left_margin(20)
self.about_text.set_right_margin(20)
about_text = """
*************************** MinimPy 0.1 ***********************************
Copyright (c) 2010 Mahmoud Saghaei
http://www.saghaei.com
MinimPy is a desktop minimization program for sequential randomization of subject to treatment groups in a clinical trial.
With this program you can define a new minimization model specifying the probability method used for assigning subject to treatment groups, distance measure which is the imbalance score at a the time of allocation considering the levels of prognostic factor for the a new subject which going to be allocated. Main features of MinimPy include total control over choice of probability method, distance measure, defining an initial minimization preload, adding a new method of probability assignment as biased coin minimization and another as marginal balance which has been shown to be superior to other distance measures.
"""
functions.set_text_buffer(self.about_text, about_text)
hbox.pack_start(self.about_text)
button = gtk.Button(None, gtk.STOCK_ABOUT)
button.connect("clicked", self.license_button_clicked)
functions.textview_add_widget(self.about_text, 0, button)
uri = 'file://' + os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), 'doc', 'index.html')
button = gtk.LinkButton(uri, "Quick Start Guide")
functions.textview_add_widget(self.about_text, 0, button)
self.notbook_tabs.append("About: Displays information about MinimPy and the developers")
lbl = gtk.Label('Help')
self.notebook.append_page(hbox, lbl)
self.notebook.set_show_tabs(True)
self.window.add(win_vbox)
self.window.show_all()
if self.trial_file_name:
self.waiting_function(self.load_trial, (self.trial_file_name,))
if not self.trial_file_name:
functions.error_dialog(self.window, 'Critical error while loading the last trial!', 'Trial load failed!')
self.set_new_trial()
else:
self.set_new_trial()
if not self.config.getboolean('interface', 'batch_allocation'):
self.hbuttonbox_batch_mode.hide()
def allocations_import(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
model = self.allocations_treeview.get_model()
if not model:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Groups and prognostic factors must be defined first!")
return False
if not self.ui_pool:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, 'Error: Either this trial is finished or has not been saved!')
return False
file_name = self.select_file("Select file", gtk.FILE_CHOOSER_ACTION_OPEN)
if not file_name:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Import canceled!")
return False
rows = open(file_name).readlines()
if not rows:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Empty file!")
return False
for cnt, row in enumerate(rows):
if len(self.ui_pool) == 0:
msg = '{0} record{1} imported'.format(cnt, ('', 's')[cnt>0])
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_OK, message_format = msg)
dialog.set_title("Finished importing!")
dialog.run()
dialog.destroy()
break
try:
row = map(str.strip, row.split(','))[2:]
group = row[0]
if not group.isdigit():
raise Exception, 'Bad file format'
group = int(group)
row = row[1:]
if len(row) != len(self.variable_combos):
raise Exception, 'Bad file format'
if not all(map(str.isdigit, row)):
raise Exception, 'Bad file format'
for idx, level in enumerate(row):
cbo = self.variable_combos[idx]
cbo.set_active(int(level))
model.append()
ui = self.ui_pool.pop()
new_case = {'levels': [], 'allocation': -1, 'UI': ui}
row = len(model)-1
model[row][0] = row
model[row][1] = ui
for col, cbo in enumerate(self.variable_combos):
model[row][col+3] = cbo.get_active_text()
new_case['levels'].append(cbo.get_active())
new_case['allocation'] = group
model[row][2] = self.group_liststore[group][0]
self.allocations.append(new_case)
except Exception as err:
msg = 'Error in import. Pehaps bad file format\n' + str(err) + '\nNow your original data will be restored'
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO,
buttons=gtk.BUTTONS_OK, message_format = msg)
dialog.set_title("Import failed!")
dialog.run()
dialog.destroy()
if self.trial_file_name:
self.load_trial(self.trial_file_name)
if not self.trial_file_name:
functions.error_dialog(self.window, 'Critical error while restoring trial!', 'Trial restore failed!')
self.set_new_trial()
else:
self.set_new_trial()
return
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
def allocations_import_button(self, widget, data=None):
self.waiting_function(self.allocations_import, ())
def allocations_export(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
self.remove_folder(repo_path)
if not self.trial_file_name:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Please save the trial first!")
return False
Export(self)
def allocations_export_button(self, widget, data=None):
self.waiting_function(self.allocations_export, ())
def trial_info_button_clicked(self, widget, data=None):
det_file_name = self.save_trial_details_temp()
uri = 'file://' + det_file_name
button = gtk.LinkButton(uri, 'More ...')
button.show()
info = self.get_trial_info()
info = info[:80] + ' ...'
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_OK, message_format = info)
dialog.vbox.pack_start(button)
dialog.set_title("Trial Information!")
dialog.run()
self.remove_temp_file(det_file_name)
dialog.destroy()
def pref_clicked(self, widget, data=None):
PrefWin(self)
def new_trial_clicked(self, widget, data=None):
if not self.query_trial_dismis():
return False
self.set_new_trial()
def set_new_trial(self):
self.trial_file_name = None
self.trial_title_entry.set_text('')
self.trial_sample_size_spin.set_value(30)
functions.set_text_buffer(self.trial_description_text, '')
self.trial_properties_liststore.clear()
self.high_prob_spin.set_value(0.7)
self.prob_method_radios[0].set_active(True)
self.distance_measure_radios[0].set_active(True)
self.group_liststore.clear()
self.variable_liststore.clear()
self.allocations_treeview.set_model()
self.allocations = []
self.freq_table_treeview.set_model()
self.initial_freq_table = False
self.freq_table_treeview.set_model()
self.update_interface()
self.min_group_combo.get_model().clear
self.lock_trial(unlock=True)
self.ui_pool = []
self.network_sync_chk.set_active(False)
self.network_sync_dict = dict(sync=False, url='', auth=False)
self.window.set_title('Minimization Program [untitled]')
self.notebook.set_current_page(0)
def balance_table_refresh(self):
if self.network_sync_dict['sync']:
repo_path = self.update_network_trial()
self.remove_folder(repo_path)
liststore = self.freq_table_treeview.get_model()
if not liststore:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: No allocation!")
return False
table = []
for row in range(len(liststore)-1):
table.append([])
for col in range(1, len(liststore[0])-1):
table[row].append(liststore[row][col])
balance = self.get_trial_balances(table)
treestore = self.balance_table_treeview.get_model()
treestore.clear()
variables = [range(len(self.variable_liststore[row][2].split(','))) for row in range(len(self.variable_liststore))]
row = 0
for idx, variable in enumerate(variables):
var_total = [0] * 4
level_rows = []
for level in variable:
for i in range(4):
var_total[i] += balance[row][i]
level_rows.append(balance[row])
row += 1
var_total = [self.variable_liststore[idx][0]] + [var_total[i] / len(variable) for i in range(4)]
variable_node = treestore.append(None, var_total)
level_names = map(str.strip, self.variable_liststore[idx][2].split(','))
for level, level_row in enumerate(level_rows):
treestore.append(variable_node, [level_names[level]] + level_row)
treestore.append(None, ['Mean'] + balance[row])
treestore.append(None, ['Max'] + balance[row+1])
def get_trial_balances(self, table):
model = Model()
m = Minim(random=self.random, model=model, precision=int(self.config.getfloat('operations', 'precision')))
levels = [[] for col in table[0]]
balances = [[] for col in table[0]] + [[], []]
for row in table:
for col, value in enumerate(row):
levels[col].append(value)
for row, level_count in enumerate(levels):
allocation_ratio = [self.group_liststore[r][1] for r in range(len(self.group_liststore))]
adj_count = [(1.0 * level_count[i]) / allocation_ratio[i] for i in range(len(level_count))]
balances[row].append(m.get_marginal_balance(adj_count))
balances[row].append(max(adj_count) - min(adj_count))
balances[row].append(m.get_variance(adj_count))
balances[row].append(m.get_standard_deviation(adj_count))
for col in range(4):
balances[len(levels)].append(1.0 * sum([balances[row][col] for row in range(len(levels))]) / len(levels))
balances[len(levels)+1].append(max([balances[row][col] for row in range(len(levels))]))
return balances
def freq_table_disable_edit(self):
functions.treeview_disable_edit(self.freq_table_treeview)
def freq_table_enable_edit(self):
functions.treeview_enable_edit(self.freq_table_treeview, self.freq_table_treeview_edit_handler)
def freq_table_start_edit_clicked(self, button, data=False):
initial_freq_model = self.freq_table_treeview.get_model()
if not initial_freq_model:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Trial is not initialized!")
return False
if not len(initial_freq_model):
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Trial is not initialized!")
return False
if button.get_label() == "Start Edit":
self.freq_table_enable_edit()
button.set_label("Save as Initial Table")
else:
if initial_freq_model[-1][-1] == -1:
functions.error_dialog(self.window, 'Error in initial frequency table!\nSome discrepancy between rows and colums sums', 'Frequency table error!')
return False
groups = range(len(self.group_liststore))
variables = [range(len(self.variable_liststore[row][2].split(','))) for row in range(len(self.variable_liststore))]
table = [[[0 for l in v] for v in variables] for g in groups]
for row, group in enumerate(table):
col = 1
for v, variable in enumerate(group):
for l, level in enumerate(variable):
table[row][v][l] = initial_freq_model[row][col]
col += 1
self.initial_freq_table = table
button.set_label("Start Edit")
self.freq_table_disable_edit()
def freq_table_treeview_edit_handler(self, cell, row, new_text, col):
model = self.freq_table_treeview.get_model()
if (col == 0) or (int(row) == (len(model) - 1)) or (col == (len(model[0]) - 1)):
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: This cell can not be changed!")
return False
if not new_text.isdigit():
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Only interger values allowed!")
return False
model[row][col] = int(new_text)
col_sum = 0
for r in range(len(model)-1):
col_sum += model[r][col]
model[-1][col] = col_sum
variables = [range(len(self.variable_liststore[r][2].split(','))) for r in range(len(self.variable_liststore) )]
table_row = [[0 for L in v] for v in variables]
cum_cols = 0
for v, variable in enumerate(table_row):
cum_cols += len(variable)
if cum_cols >= col:
sel_cols = range(cum_cols - len(variable) + 1, cum_cols + 1)
break
row_sum = 0
for c in sel_cols:
row_sum += model[row][c]
err_sum = False
cum_cols = 0
for v, variable in enumerate(table_row):
cum_cols += len(variable)
sel_cols = range(cum_cols - len(variable) + 1, cum_cols + 1)
row_sum_ext = 0
for c in sel_cols:
row_sum_ext += model[row][c]
if row_sum_ext != row_sum: err_sum = True
if not err_sum:
model[row][-1] = row_sum
else:
model[row][-1] = -1
total = 0
for r in range(len(model)-1):
total += model[r][-1]
if not err_sum:
model[-1][-1] = total
else:
model[-1][-1] = -1
def freq_table_delete_initial_clicked(self, button, data=None):
initial_freq_model = self.freq_table_treeview.get_model()
if not initial_freq_model:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Trial is not initialized!")
return False
msg = "Are you sure you want to delete the initial table?\nAll entered values into the table will be lost!\nGroup and factor level labels will be refreshed!"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Initial Table Delete / Refresh!")
if dialog.run() == gtk.RESPONSE_NO:
dialog.destroy()
return False
dialog.destroy()
self.refresh_freq_table()
self.initial_freq_table = False
self.freq_table_disable_edit()
self.freq_table_enable_edit_button.set_label("Start Edit")
def load_trial_clicked(self, button, data=None):
if not self.query_trial_dismis():
return False
file_name = self.select_file("Enter name of the file", gtk.FILE_CHOOSER_ACTION_OPEN)
if not file_name:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Trial save canceled!")
return False
self.waiting_function(self.load_trial, (file_name,))
def try_network_info_file(self, file_name):
try:
fp = open(file_name, 'rb')
data = pickle.load(fp)
fp.close()
except:
# file is invalid
return -1
if data.has_key('network_sync') and data.has_key('trial_name'):
self.trial_file_name = file_name
if self.update_network_trial():
self.lock_trial()
# file is valid and trial loaded
return 1
else:
self.set_new_trial()
# file is valid but trial not loaded
return 0
else:
self.set_new_trial()
# file is invalid
return -1
def save_trial_changes(self):
trial_title = self.trial_title_entry.get_text()
sample_size = self.trial_sample_size_spin.get_value()
ui_pool = self.ui_pool
trial_description = functions.get_text_buffer(self.trial_description_text)
cbo_selects = []
for cbo in self.variable_combos:
cbo_selects.append(cbo.get_active())
trial_properties = self.get_trial_properties()
if self.network_sync_dict['sync'] and self.trial_file_name:
repo_path = self.update_network_trial()
self.trial_title_entry.set_text(trial_title)
self.trial_sample_size_spin.set_value(sample_size)
self.ui_pool = ui_pool
functions.set_text_buffer(self.trial_description_text, trial_description)
for i, cbo in enumerate(self.variable_combos):
cbo.set_active(cbo_selects[i])
self.set_trial_properties(trial_properties)
if self.network_sync_dict['sync']:
path = repo_path
else:
path = self.trial_file_name
self.save_trial(path)
def save_trial_event(self):
# is this an update or save
if self.trial_file_name:
self.save_trial_changes()
return False
if not self.trial_is_valid():
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: This trial is not ready to save! Please check the requirements")
return False
file_name = self.select_file("Enter name of the file", gtk.FILE_CHOOSER_ACTION_SAVE)
if not file_name:
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, "Error: Trial save canceled!")
return False
self.build_ui_pool()
self.trial_file_name = self.save_trial(file_name, initial=True)
def save_trial_clicked(self, button, data=None):
self.waiting_function(self.save_trial_event, ())
def set_groups_data(self, groups):
self.group_liststore.clear()
for group in groups:
self.group_liststore.append((group['name'], group['allocation_ratio']))
def set_variables_data(self, variables):
self.variable_liststore.clear()
for variable in variables:
self.variable_liststore.append((variable['name'], variable['weight'], variable['levels']))
def build_ui_pool(self, lst=None):
if not lst:
ss = self.trial_sample_size_spin.get_value_as_int()
lst = range(ss)
self.random.shuffle(lst)
self.ui_pool = map(lambda x: x.zfill(len(str(max(lst)))), map(str, lst))
def get_trial_properties(self):
table = [['', ''] for row in range(len(self.trial_properties_liststore))]
for row in range(len(table)):
for col in range(len(table[0])):
table[row][col] = self.trial_properties_liststore[row][col]
return table
def set_trial_properties(self, table):
self.trial_properties_liststore.clear()
for row in range(len(table)):
self.trial_properties_liststore.append(table[row])
def trial_is_valid(self, show_msg=True, ret_code=None, table_edit=False):
if not self.trial_title_entry.get_text() and not table_edit:
if show_msg:
functions.error_dialog(self.window, "Please specify the trial title first", "Trial title")
self.notebook.set_current_page(0)
self.window.set_focus(self.trial_title_entry)
return False
if self.trial_title_entry.get_text():
for char in self.trial_title_entry.get_text():
if char not in self.valid_chars:
if show_msg:
functions.error_dialog(self.window, "Invalid character '{0}' in trial title".format(char), "Trial title")
return False
if not functions.get_text_buffer(self.trial_description_text) and not table_edit:
if show_msg:
functions.error_dialog(self.window, "Please enter a description for this trial first", "Trial description")
self.notebook.set_current_page(0)
self.window.set_focus(self.trial_description_text)
return False
if len(self.group_liststore) < 2:
if show_msg:
functions.error_dialog(self.window, "At least two groups must be defined for the trial", "Trial groups")
self.notebook.set_current_page(1)
self.window.set_focus(self.group_treeview)
return False
if len(self.variable_liststore) < 1:
if show_msg:
functions.error_dialog(self.window, "At least one prognostic factor (variable) must be defined for the trial", "Trial variables")
self.notebook.set_current_page(2)
self.window.set_focus(self.variable_treeview)
return False
if not show_msg: return True
return self.display_trial_details_response()
def save_trial_details_temp(self):
details = '<html><head><title>Trial Details</title></head><body>' + self.get_trial_info().replace('\n', '<br />') + '</body></html>'
det_file_name = tempfile.mkstemp(suffix='.html')[1]
fp = open(det_file_name, 'w')
fp.write(details)
fp.flush()
fp.close()
return det_file_name
def display_trial_details_response(self):
msg = "Are you sure you want to save the trial?\nOnce you saved a trial no change in trial settings are allowed!"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Trial Save!")
det_file_name = self.save_trial_details_temp()
uri = 'file://' + det_file_name
button = gtk.LinkButton(uri, "Trial Details")
button.show()
dialog.vbox.pack_start(button)
if dialog.run() == gtk.RESPONSE_YES:
self.remove_temp_file(det_file_name)
dialog.destroy()
return True
dialog.destroy()
self.remove_temp_file(det_file_name)
return False
def remove_temp_file(self, file_name):
try:
os.remove(file_name)
except:
pass
def want_trial_unlock(self, prompt=True):
if prompt:
msg = "Do you want to unlock the trial!\nUnlocking the trial make changes of trial setting possible!\nTrial file will be released.\nYou have to save the trial again!"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Unlock Trial!")
if dialog.run() == gtk.RESPONSE_NO:
dialog.destroy()
return False
dialog.destroy()
self.lock_trial(unlock=True)
self.ui_pool = []
self.trial_file_name = None
def lock_trial(self, unlock=False):
if pysvn_available:
self.network_sync_chk.set_property("sensitive", unlock)
# self.trial_save_button.set_property("sensitive", unlock)
self.trial_sample_size_spin.set_property("sensitive", unlock)
for radio in self.prob_method_radios:
radio.set_property("sensitive", unlock)
self.min_group_combo.set_property("sensitive", unlock)
self.high_prob_spin.set_property("sensitive", unlock)
for radio in self.distance_measure_radios:
radio.set_property("sensitive", unlock)
for child in self.group_hbox.get_children():
child.set_property("sensitive", unlock)
for child in self.variable_hbox.get_children():
child.set_property("sensitive", unlock)
for child in self.initial_table_hbox.get_children():
child.set_property("sensitive", unlock)
self.statusbar.pop(self.sb_context_id)
self.statusbar.push(self.sb_context_id, ("Trial lock!", "Trial unlocked!")[unlock])
def get_trial_info(self):
ret = []
ret.append("Title: " + self.trial_title_entry.get_text())
ret.append("Sample Size: " + str(self.trial_sample_size_spin.get_value_as_int()))
ret.append("Description: " + functions.get_text_buffer(self.trial_description_text))
ret.append("Probability Method: " + self.get_prob_method_name())
ret.append("Distance Measure: " + self.get_distance_measure_name())
ret.append("High Probability: " + str(self.high_prob_spin.get_value()))
ret.append("Groups:\n" + str(self.get_groups_info()))
ret.append("Variables:\n" + str(self.get_variables_info()))
return '\n'.join(ret)
def load_trial_from_file(self, file_name):
fp = open(file_name, 'rb')
data = pickle.load(fp)
fp.close()
self.ui_pool = data['ui_pool']
self.trial_title_entry.set_text(data['trial_title'])
if self.network_sync_dict['url']:
self.window.set_title('Minimization Program [{0}/{1}]'.format(self.network_sync_dict['url'], data['trial_title']))
else:
self.window.set_title('Minimization Program [{0}]'.format(data['trial_title']))
functions.set_text_buffer(self.trial_description_text, data['trial_description'])
self.set_trial_properties(data['trial_properties'])
self.allocations = data['allocations']
self.trial_sample_size_spin.set_value(data['sample_size'])
self.high_prob_spin.set_value(data['high_prob'])
self.initial_freq_table = data['initial_freq_table']
self.prob_method_radios[data['prob_method']].set_active(True)
self.distance_measure_radios[data['distance_measure']].set_active(True)
self.set_groups_data(data['groups'])
self.set_variables_data(data['variables'])
self.update_interface()
if len(self.group_liststore):
self.min_group_combo.set_active(data['min_group'])
if pysvn_available:
if data.has_key('network_sync'):
# previous versions of data files may not have this field
self.network_sync_dict = data['network_sync']
self.network_sync_chk.handler_block(self.network_sync_chk_event_id)
self.network_sync_chk.set_active(self.network_sync_dict['sync'])
self.network_sync_chk.handler_unblock(self.network_sync_chk_event_id)
def waiting_function(self, func, args):
gdk_cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
win = gtk.gdk.get_default_root_window()
win.set_cursor(gdk_cursor)
gobject.idle_add(func, *args)
gobject.idle_add(self.exit_waiting, win)
self.network_sync_dialog.show()
def exit_waiting(self, win):
gdk_cursor = gtk.gdk.Cursor(gtk.gdk.ARROW)
win.set_cursor(gdk_cursor)
self.network_sync_dialog.hide()
def load_trial(self, file_name):
# checking the file if this is a network sync info file. If yes, then we checkout and
# load the trial from the checkout
self.network_sync_dialog_label.set_text('Please wait!\n{0}'.format(self.network_sync_dict['url']))
ret = self.try_network_info_file(file_name)
if ret > 0:
self.network_sync_dialog_label.set_text('Network syncing! Please wait!\n{0}'.format(self.network_sync_dict['url']))
self.config.set('project', 'recent_trial', file_name)
self.notebook.set_current_page(3)
with open(self.config_file, 'wb') as config_file:
self.config.write(config_file)
config_file.flush()
config_file.close()
self.config.read(self.config_file)
return
if ret == 0: return
try:
self.load_trial_from_file(file_name)
self.lock_trial()
self.trial_file_name = file_name
self.notebook.set_current_page(3)
except Exception as ex:
functions.error_dialog(self.window, "Trial load failed. Selected file '{0}' may not be valid!".format(file_name), "Error loading file!")
self.lock_trial(unlock=True)
self.set_new_trial()
self.trial_file_name = False
def get_svn_client(self):
self.network_sync_cancel_command = False
client = pysvn.Client()
client.callback_cancel = self.network_sync_cancel
client.callback_get_login = self.network_sync_get_login
client.callback_ssl_server_trust_prompt = self.network_sync_ssl_server_trust_prompt
client.callback_notify = self.network_sync_notify
client.set_auth_cache(self.config.getboolean('operations', 'cach_cred'))
client.set_store_passwords(self.config.getboolean('operations', 'cach_cred'))
client.exception_style = 1
return client
def network_sync_notify(self, event_dict):
return
def save_trial_to_file(self, file_name):
data = {}
data['ui_pool'] = self.ui_pool
data['trial_title'] = self.trial_title_entry.get_text()
data['sample_size'] = self.trial_sample_size_spin.get_value_as_int()
data['trial_description'] = functions.get_text_buffer(self.trial_description_text)
data['trial_properties'] = self.get_trial_properties()
data['high_prob'] = self.high_prob_spin.get_value()
data['min_group'] = self.min_group_combo.get_active()
data['initial_freq_table'] = self.initial_freq_table
data['prob_method'] = self.get_prob_method()
data['distance_measure'] = self.get_distance_measure()
data['allocations'] = self.allocations
data['groups'] = self.get_groups_data()
data['variables'] = self.get_variables_data()
data['network_sync'] = self.network_sync_dict
fp = open(file_name, 'wb')
pickle.dump(data, fp)
fp.close()
if self.network_sync_dict['sync']:
if self.config.getboolean('operations', 'backup_network'):
backup_folder_name = self.config.get('operations', 'backup_network_path')
if backup_folder_name:
back_file_name = os.path.join(backup_folder_name, os.path.basename(file_name))
shutil.copyfile(file_name, back_file_name)
def save_trial_local(self, file_name):
self.save_trial_to_file(file_name)
self.window.set_title('Minimization Program [{0}]'.format(file_name.split(os.sep)[-1]))
self.config.set('project', 'recent_trial', file_name)
with open(self.config_file, 'wb') as config_file:
self.config.write(config_file)
config_file.flush()
config_file.close()
self.config.read(self.config_file)
return file_name
def load_network_trial_info(self):
fp = open(self.trial_file_name, 'rb')
data = pickle.load(fp)
fp.close()
try:
if data.has_key('network_sync'):
self.network_sync_dict = data['network_sync']
else:
raise Exception, "Trial info file is not valid!"
if data.has_key('trial_name'):
trial_name = data['trial_name']
else:
raise Exception, "Trial info file is not valid!"
return trial_name
except Exception as ex:
functions.error_dialog(self.window, "Problem with network trial info file!\n" + repr(ex), "Trial info file!")
return False
def update_network_trial(self):
# this method checkout the repo, load the trial from it, and then return the local path
if not self.trial_file_name:
self.set_new_trial()
return False
trial_name = self.load_network_trial_info()
if not trial_name:
self.set_new_trial()
return False
try:
client = self.get_svn_client()
url = self.network_sync_dict['url'] + '/' + trial_name
tempfolder = tempfile.mkdtemp()
rev = False
while True:
rev = client.checkout(url, tempfolder)
if rev:
if type(rev) == type(pysvn.Revision(pysvn.opt_revision_kind.head)): break
temp_file_name = os.path.join(tempfolder, trial_name)
self.load_trial_from_file(temp_file_name)
return tempfolder
except Exception as ex:
functions.error_dialog(self.window, "An unknown error occured during network sync (update)!\n" + repr(ex), "Network sync error!")
return False
def checkin_trial(self, path):
if not self.trial_file_name:
self.set_new_trial()
return False
trial_name = self.load_network_trial_info()
if not trial_name:
self.set_new_trial()
return False
url = self.network_sync_dict['url'] + '/' + trial_name
file_name = os.path.join(path, trial_name)
self.save_trial_to_file(file_name)
client = self.get_svn_client()
try:
rev = False
while True:
rev = client.checkin(path, time.ctime())
if rev:
if type(rev) == type(pysvn.Revision(pysvn.opt_revision_kind.head)): break
stats = client.status(path)
for stat in stats:
if stat.repos_text_status == pysvn.wc_status_kind.modified:
break
else:
break
self.config.set('project', 'recent_trial', self.trial_file_name)
with open(self.config_file, 'wb') as config_file:
self.config.write(config_file)
config_file.flush()
config_file.close()
self.remove_folder(path)
except pysvn.ClientError, e:
for message, code in e.args[1]:
if code == 160024 or 'Conflict' in message:
msg = "A conflict detected!\nMay be another user is working on the trial\nYou have to reload trial and try again\nDo you want to reload the trial?"
dialog = gtk.MessageDialog(self.window, flags = gtk.DIALOG_MODAL, type = gtk.MESSAGE_QUESTION, buttons=gtk.BUTTONS_YES_NO, message_format = msg)
dialog.set_title("Trial conflict!")
if dialog.run() == gtk.RESPONSE_YES:
dialog.destroy()
self.update_network_trial()
dialog.destroy()
break
else:
functions.error_dialog(self.window, "An unknown error occured during network sync!\nIt is very probable the changes has not been saved\nPlease restart the program\n" + str(e.args[0]), "Network sync error!")
if os.path.exists(path):
self.remove_folder(path)
return False
except Exception as ex:
functions.error_dialog(self.window, "An unknown error occured during network sync!\nIt is very probable the changes has not been saved\nPlease restart the program\n" + repr(ex), "Network sync error!")
if os.path.exists(path):
self.remove_folder(path)
return False
def import_trial(self, file_name):
trial_name = '_'.join(self.trial_title_entry.get_text().split())[:10] + str.partition(str(time.time()), '.')[0]
temp_dir = tempfile.mkdtemp(prefix=trial_name)
temp_file = os.path.join(temp_dir, trial_name)
self.save_trial_to_file(temp_file)
try:
client = self.get_svn_client()
url = self.network_sync_dict['url'] + '/' + trial_name
rev = False
while True:
rev = client.import_(temp_dir, url, 'Minimization initialized!')
if rev:
if type(rev) == type(pysvn.Revision(pysvn.opt_revision_kind.head)): break
data = {}
data['network_sync'] = self.network_sync_dict
data['trial_name'] = trial_name
fp = open(file_name, 'wb')
pickle.dump(data, fp)
fp.close()
self.config.set('project', 'recent_trial', file_name)
with open(self.config_file, 'wb') as config_file:
self.config.write(config_file)
config_file.flush()
config_file.close()
self.remove_folder(temp_dir)
self.lock_trial()
return file_name
except Exception as ex:
functions.error_dialog(self.window, "An unknown error occured during trial save!\nIt is very probable the changes has not been saved\nPlease restart the program\n" + repr(ex), "Trial save error!")
if os.path.exists(temp_dir):
self.remove_folder(temp_dir)
return False
def remove_folder(self, folder):
try:
shutil.rmtree(folder)
except:
pass
def save_trial(self, file_name, initial=False):
if self.network_sync_dict['sync']:
if initial:
# here file_name is the file containing trial info: rep url etc
self.trial_file_name = self.import_trial(file_name)
else:
# here file name is the path. A temp folder for checkin into the repo.
self.checkin_trial(file_name)
self.network_sync_icon.set_from_stock(gtk.STOCK_CONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.network_sync_icon.set_tooltip_text('Network Sync is enabled!')
else:
self.trial_file_name = self.save_trial_local(file_name)
self.network_sync_icon.set_from_stock(gtk.STOCK_DISCONNECT, gtk.ICON_SIZE_SMALL_TOOLBAR)
self.network_sync_icon.set_tooltip_text('Network Sync is disabled!')
if self.trial_file_name: self.lock_trial()
return file_name
def network_sync_cancel(self):
return self.network_sync_cancel_command
def get_distance_measure_name(self):
for distance_measure_radio in self.distance_measure_radios:
if distance_measure_radio.get_active():
return distance_measure_radio.get_label()
return "Unknown"
def get_distance_measure(self):
for idx, distance_measure_radio in enumerate(self.distance_measure_radios):
if distance_measure_radio.get_active():
return idx
return 0
def get_prob_method_name(self):
for prob_method_radio in self.prob_method_radios:
if prob_method_radio.get_active():
return prob_method_radio.get_label()
return "Unknown"
def get_prob_method(self):
for idx, prob_method_radio in enumerate(self.prob_method_radios):
if prob_method_radio.get_active():
return idx
return 0
def get_variables_info(self):
variables = []
n = 0
for row in self.variable_liststore:
n += 1
variable = "{0}) {1}, weight = {2}, levels = {3}".format(n, *row)
variables.append(variable)
return '\n'.join(variables)
def get_variables_data(self):
variables = []
for row in self.variable_liststore:
variable = {}
variable['name'] = row[0]
variable['weight'] = row[1]
variable['levels'] = row[2]
variables.append(variable)
return variables
def get_groups_info(self):
groups = []
n = 0
for row in self.group_liststore:
n += 1
group = "{0}) {1}, Allocation Ratio = {2}".format(n, *list(row))
groups.append(group)
return '\n'.join(groups)
def get_groups_data(self):
groups = []
for row in self.group_liststore:
group = {}
group['name'] = row[0]
group['allocation_ratio'] = row[1]
groups.append(group)
return groups
def select_file(self, title, action):
"""
A generic method returning file name
"""
stock = {
gtk.FILE_CHOOSER_ACTION_OPEN: gtk.STOCK_OPEN,
gtk.FILE_CHOOSER_ACTION_SAVE: gtk.STOCK_SAVE}
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, stock[action], gtk.RESPONSE_OK)
fdialog = gtk.FileChooserDialog(title, self.window, action, buttons)
if action == gtk.FILE_CHOOSER_ACTION_SAVE:
fdialog.set_do_overwrite_confirmation(True)
fdialog.set_default_response(gtk.RESPONSE_OK)
fdialog.set_current_folder(self.file_chooser_current_folder)
response = fdialog.run()
if response == gtk.RESPONSE_OK:
file_name = fdialog.get_filename()
self.file_chooser_current_folder = os.path.dirname(file_name)
else:
file_name = None
fdialog.destroy()
return file_name
def main():
gtk.main()
return 0
if __name__ == "__main__":
ModelInteface()
main()