class ExperimentSettings(Singleton): global_settings = {} timeline = Timeline('TEST_STOCK') subscribers = {} def __init__(self): pass def set_field(self, tag, value, notify_subscribers=True): self.global_settings[tag] = value if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'SET FIELD: %s = %s' % (tag, value) def get_field(self, tag, default=None): return self.global_settings.get(tag, default) def remove_field(self, tag, notify_subscribers=True): '''completely removes the specified tag from the metadata (if it exists) ''' #if self.get_field(tag) is not None: self.global_settings.pop(tag) if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'DEL FIELD: %s' % (tag) def get_action_tags(self): '''returns all existing TEMPORAL tags as list''' return [ tag for tag in self.global_settings if tag.split('|')[0] in ('CellTransfer', 'Perturbation', 'Labeling', 'AddProcess', 'DataAcquis') ] def get_field_instances(self, tag_prefix): '''returns a list of unique instance ids for each tag beginning with tag_prefix''' ids = set([ get_tag_instance(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ get_tag_attribute(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_dict(self, protocol): '''returns a dict mapping attribute names to their values for a given protocol. eg: get_attribute_dict('CellTransfer|Seed|1') --> {'SeedingDensity': 12, 'MediumUsed': 'agar', 'MediumAddatives': 'None', 'Trypsinization': True} ''' d = {} for tag in self.get_matching_tags('|*|'.join(protocol.rsplit('|', 1))): if (get_tag_attribute(tag) not in ('Wells', 'EventTimepoint', 'Images', 'OriginWells')): d[get_tag_attribute(tag)] = self.global_settings[tag] return d def get_eventtype_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[1] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_eventclass_list(self, tag_prefix): '''returns a list of event class name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[0] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_field_tags(self, tag_prefix=None, instance=None): '''returns a list of all tags beginning with tag_prefix. If instance is passed in, only tags of the given instance will be returned''' tags = [] for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): tags += [tag] return tags def get_matching_tags(self, matchstring): '''returns a list of all tags matching matchstring matchstring -- a string that matches the tags you want eg: CellTransfer|* ''' tags = [] for tag in self.global_settings: match = True for m, subtag in map(None, matchstring.split('|'), tag.split('|')): if m != subtag and m not in ('*', None): match = False break if match: tags += [tag] return tags def get_protocol_instances(self, prefix): '''returns a list of protocol instance names for tags matching the given prefix. ''' return list( set([get_tag_instance(tag) for tag in self.get_field_tags(prefix)])) def get_new_protocol_id(self, prefix): '''returns an id string that hasn't been used for the given tag prefix prefix -- eg: CellTransfer|Seed ''' instances = self.get_protocol_instances(prefix) for i in xrange(1, 100000): if str(i) not in instances: return str(i) def clear(self): self.global_settings = {} PlateDesign.clear() # # TODO: # self.timeline = Timeline('TEST_STOCK') for matchstring, callbacks in self.subscribers.items(): for callback in callbacks: callback(None) def get_timeline(self): return self.timeline def update_timeline(self, welltag): '''Updates the experiment metadata timeline event associated with the action and wells in welltag (eg: 'ExpNum|AddProcess|Spin|Wells|1|1') ''' platewell_ids = self.get_field(welltag, []) if platewell_ids == []: self.timeline.delete_event(welltag) else: event = self.timeline.get_event(welltag) if event is not None: event.set_well_ids(platewell_ids) else: self.timeline.add_event(welltag, platewell_ids) def save_to_file(self, file): f = open(file, 'w') for field, value in sorted(self.global_settings.items()): f.write('%s = %s\n' % (field, repr(value))) f.close() def load_from_file(self, file): # Populate the tag structure self.clear() f = open(file, 'r') for line in f: tag, value = line.split('=') tag = tag.strip() self.set_field(tag, eval(value), notify_subscribers=False) f.close() # Populate PlateDesign PlateDesign.clear() for vessel_type in ('Plate', 'Flask', 'Dish', 'Coverslip'): prefix = 'ExptVessel|%s' % (vessel_type) for inst in self.get_field_instances(prefix): d = self.get_attribute_dict(prefix + '|' + inst) shape = d.get('Design', None) if shape is None: shape = (1, 1) group = d.get('GroupName', None) PlateDesign.add_plate(vessel_type, inst, shape, group) # Update everything for tag in self.global_settings: self.notify_subscribers(tag) # Update the bench time-slider # TODO: this is crappy try: import wx bench = wx.GetApp().get_bench() bench.set_time_interval(0, self.get_timeline().get_max_timepoint()) except: return def add_subscriber(self, callback, match_string): '''callback -- the function to be called match_string -- a regular expression string matching the tags you want to be notified of changes to ''' self.subscribers[match_string] = self.subscribers.get( match_string, []) + [callback] def remove_subscriber(self, callback): '''unsubscribe the given callback function. This MUST be called before a callback function is deleted. ''' for k, v in self.subscribers: if v == callback: self.subscribers.pop(k) def notify_subscribers(self, tag): for matchstring, callbacks in self.subscribers.items(): if re.match(matchstring, tag): for callback in callbacks: callback(tag)
class ExperimentSettings(Singleton): global_settings = {} timeline = Timeline('TEST_STOCK') subscribers = {} def __init__(self): pass def set_field(self, tag, value, notify_subscribers=True): self.global_settings[tag] = value if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'SET FIELD: %s = %s'%(tag, value) def get_field(self, tag, default=None): return self.global_settings.get(tag, default) def remove_field(self, tag, notify_subscribers=True): '''completely removes the specified tag from the metadata (if it exists) ''' #if self.get_field(tag) is not None: self.global_settings.pop(tag) if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'DEL FIELD: %s'%(tag) def get_action_tags(self): '''returns all existing TEMPORAL tags as list''' return [tag for tag in self.global_settings if tag.split('|')[0] in ('CellTransfer', 'Perturbation', 'Staining', 'AddProcess', 'DataAcquis', 'Notes')] def get_field_instances(self, tag_prefix): '''returns a list of unique instance ids for each tag beginning with tag_prefix''' ids = set([get_tag_instance(tag) for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_attribute_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([get_tag_attribute(tag) for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_attribute_list_by_instance(self, tag_prefix, instance=None): '''returns a list of all attributes beginning with tag_prefix. If instance is passed in, only attributes of the given instance will be returned''' ids = set([get_tag_attribute(tag) for tag in self.global_settings if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance))]) return list(ids) #tags = [] #for tag in self.global_settings: #if ((tag_prefix is None or tag.startswith(tag_prefix)) and #(instance is None or get_tag_instance(tag) == instance)): #tag += [tag] #ids= set([get_tag_attribute(tag)]) #return list(ids) def get_attribute_dict(self, protocol): '''returns a dict mapping attribute names to their values for a given protocol. eg: get_attribute_dict('CellTransfer|Seed|1') --> {'SeedingDensity': 12, 'MediumUsed': 'agar', 'MediumAddatives': 'None', 'Trypsinization': True} ''' d = {} for tag in self.get_matching_tags('|*|'.join(protocol.rsplit('|',1))): if (get_tag_attribute(tag) not in ('Wells', 'EventTimepoint', 'Images', 'OriginWells')): d[get_tag_attribute(tag)] = self.global_settings[tag] return d def get_eventtype_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([tag.split('|')[1] for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_eventclass_list(self, tag_prefix): '''returns a list of event class name for each tag beginning with tag_prefix''' ids = set([tag.split('|')[0] for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_field_tags(self, tag_prefix=None, instance=None): '''returns a list of all tags beginning with tag_prefix. If instance is passed in, only tags of the given instance will be returned''' tags = [] for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): tags += [tag] return tags def get_matching_tags(self, matchstring): '''returns a list of all tags matching matchstring matchstring -- a string that matches the tags you want eg: CellTransfer|* ''' tags = [] for tag in self.global_settings: match = True for m, subtag in map(None, matchstring.split('|'), tag.split('|')): if m != subtag and m not in ('*', None): match = False break if match: tags += [tag] return tags def get_protocol_instances(self, prefix): '''returns a list of protocol instance names for tags matching the given prefix. ''' return list(set([get_tag_instance(tag) for tag in self.get_field_tags(prefix)])) def get_new_protocol_id(self, prefix): '''returns an id string that hasn't been used for the given tag prefix prefix -- eg: CellTransfer|Seed ''' instances = self.get_protocol_instances(prefix) for i in xrange(1, 100000): if str(i) not in instances: return str(i) def get_instance_by_field_value(self, prefix, field_value): '''returns instance number given tag prefix and field value ''' return get_tag_instance([tag for tag, value in self.global_settings.iteritems() if value == field_value][0]) def get_stack_ids(self, prefix): '''returns the stack ids for a given type of vessels prefix e.g. ExptVessel|Tube''' return set([self.get_field(prefix+'|StackNo|%s'%instance) for instance in sorted(self.get_field_instances(prefix))]) def get_rep_vessel_instance(self, prefix, stack_id): ''' returns instance number of the vessel for this stack, since all vessels of a given stack has identical settings one instance represents others''' for instance in sorted(self.get_field_instances(prefix)): if self.get_field(prefix+'|StackNo|%s'%(instance)) == stack_id: return instance def clear(self): self.global_settings = {} PlateDesign.clear() # # TODO: # self.timeline = Timeline('TEST_STOCK') for matchstring, callbacks in self.subscribers.items(): for callback in callbacks: callback(None) def onTabClosing(self, event): event.Veto() dlg = wx.MessageDialog(None, 'Can not delete instance', 'Deleting..', wx.OK| wx.ICON_STOP) dlg.ShowModal() return def get_timeline(self): return self.timeline def does_tag_exists(self, tag_prefix, instnace=None): for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instnace is None or get_tag_instance(tag) == instnace)): return True else: return False def is_supp_protocol_filled(self, tag_prefix, instance=None): '''tag_prefix is always type|event e.g. AddProcess|Wash ''' for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): if (self.get_field(tag_prefix+'|ProtocolName|%s'%instance) is None) or (self.get_field(tag_prefix+'|Step1|%s'%instance) == ['', '', '']): return False else: return True def update_timeline(self, welltag): '''Updates the experiment metadata timeline event associated with the action and wells in welltag (eg: 'ExpNum|AddProcess|Spin|Wells|1|1') ''' platewell_ids = self.get_field(welltag, []) if platewell_ids == []: self.timeline.delete_event(welltag) else: event = self.timeline.get_event(welltag) if event is not None: event.set_well_ids(platewell_ids) else: self.timeline.add_event(welltag, platewell_ids) def save_to_file(self, file): f = open(file, 'w') for field, value in sorted(self.global_settings.items()): f.write('%s = %s\n'%(field, repr(value))) f.close() def save_settings(self, file, protocol): ''' saves settings in text file. the settings may include instrument settings, supp protocol, stock flask Format: attr = value where value may be text, int, list, etc. ''' instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) setting_type = get_tag_event(protocol) f = open(file,'w') f.write(setting_type+'\n') attributes = self.get_attribute_list_by_instance(tag_stump, instance) for attr in attributes: info = self.get_field(tag_stump+'|%s|%s' %(attr, instance)) print info f.write('%s = %s\n'%(attr, repr(info))) f.close() def save_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file,'w') attributes = self.get_attribute_list_by_instance(tag_stump, instance) for attr in attributes: info = self.get_field(tag_stump+'|%s|%s' %(attr, instance)) if isinstance(info, list): f.write('%s|'%attr) for i, val in enumerate(info): if isinstance(val, tuple): print val elif i == len(info)-1: f.write('%s'%val) else: f.write('%s|'%val) f.write('\n') else: f.write('%s|%s\n'%(attr, info)) f.close() def load_from_file(self, file): # Populate the tag structure self.clear() f = open(file, 'r') for line in f: tag, value = line.split('=', 1) tag = tag.strip() self.set_field(tag, eval(value), notify_subscribers=False) f.close() # Populate PlateDesign PlateDesign.clear() for vessel_type in ('Plate', 'Flask', 'Dish', 'Coverslip', 'Tube'): prefix = 'ExptVessel|%s'%(vessel_type) for inst in self.get_field_instances(prefix): d = self.get_attribute_dict(prefix+'|'+inst) shape = d.get('Design', None) if shape is None: shape = (1,1) group = d.get('StackName', None) PlateDesign.add_plate(vessel_type, inst, shape, group) # Update everything for tag in self.global_settings: self.notify_subscribers(tag) # Update the bench time-slider # TODO: this is crappy try: import wx bench = wx.GetApp().get_bench() bench.set_time_interval(0, self.get_timeline().get_max_timepoint()) except:return def load_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) lines = [line.strip() for line in open(file)] if not lines: import wx dial = wx.MessageDialog(None, 'Supplementary protocol file is empty!!', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: #line.rstrip('\n') line_info = line.split('|') attr = line_info.pop(0) if len(line_info)>1: self.set_field(tag_stump+'|%s|%s'%(attr, instance), line_info) else: self.set_field(tag_stump+'|%s|%s'%(attr, instance), line_info[0]) def load_settings(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file, 'r') lines = [line.strip() for line in f] lines.pop(0) # takes out the first line or the header where all setting type microscope/spin etc are written for line in lines: attr, value = line.split('=', 1) attr = attr.strip() tag = tag_stump+'|%s|%s'%(attr, instance) self.set_field(tag, eval(value), notify_subscribers=False) self.notify_subscribers(tag) f.close() def add_subscriber(self, callback, match_string): '''callback -- the function to be called match_string -- a regular expression string matching the tags you want to be notified of changes to ''' self.subscribers[match_string] = self.subscribers.get(match_string, []) + [callback] def remove_subscriber(self, callback): '''unsubscribe the given callback function. This MUST be called before a callback function is deleted. ''' for k, v in self.subscribers: if v == callback: self.subscribers.pop(k) def notify_subscribers(self, tag): for matchstring, callbacks in self.subscribers.items(): if re.match(matchstring, tag): for callback in callbacks: callback(tag) def getNM(self, nm): return int(nm.split('-')[0]), int(nm.split('-')[1]) def belongsTo(self, value, rangeStart, rangeEnd): if value >= rangeStart and value <= rangeEnd: return True return False def partition(self, lst, n): division = len(lst) / float(n) rlist = [lst[int(round(division * i)): int(round(division * (i + 1)))] [-1] for i in xrange(n) ] rlist.insert(0, lst[0]) return rlist def stringSplitByNumbers(self, x): r = re.compile('(\d+)') l = r.split(x) return [int(y) if y.isdigit() else y for y in l] def nmToRGB(self, w): # colour if w >= 380 and w < 440: R = -(w - 440.) / (440. - 350.) G = 0.0 B = 1.0 elif w >= 440 and w < 490: R = 0.0 G = (w - 440.) / (490. - 440.) B = 1.0 elif w >= 490 and w < 510: R = 0.0 G = 1.0 B = -(w - 510.) / (510. - 490.) elif w >= 510 and w < 580: R = (w - 510.) / (580. - 510.) G = 1.0 B = 0.0 elif w >= 580 and w < 645: R = 1.0 G = -(w - 645.) / (645. - 580.) B = 0.0 elif w >= 645 and w <= 780: R = 1.0 G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 # intensity correction if w >= 380 and w < 420: SSS = 0.3 + 0.7*(w - 350) / (420 - 350) elif w >= 420 and w <= 700: SSS = 1.0 elif w > 700 and w <= 780: SSS = 0.3 + 0.7*(780 - w) / (780 - 700) else: SSS = 0.0 SSS *= 255 return [int(SSS*R), int(SSS*G), int(SSS*B)] def decode_ch_component(self, component): '''this method decofify the components of the light path for a given channel 'LSR488' --> Laser 488nm, 'DMR567LP' ---> Long pass Dichroic Mirror 567nm etc.. ''' description = '' if component.startswith('LSR'): nm = re.sub('\D', '', component) description = nm+' nm Excitation laser ' if component.startswith('DMR'): nm = re.sub('\D', '', component) if component.endswith('LP'): description = nm+' nm Long Pass Dichroic Mirror' if component.endswith('SP'): description = nm+' nm Short Pass Dichroic Mirror' if component.startswith('FLT'): if component.endswith('LP'): description = re.sub('\D', '', component)+' nm Long Pass Filter' if component.endswith('SP'): description = re.sub('\D', '', component)+' nm Short Pass Filter' if component.endswith('BP'): description = (component.split('FLT')[1]).split('BP')[0]+' nm Band pass Filter' # this needs to be adjusted if component.startswith('DYE'): dye = component.split('_')[1] description = 'Dye used: %s' %dye if component.startswith('DTC'): volt = re.sub('\D', '', component) description = 'PMT voltage %s volts' %volt return description def setDyeList(self, emLow, emHgh): '''This method sets the list of dye for a given spectrum range''' dyeList = [] for dye in FLUOR_SPECTRUM: dyeLowNM, dyeHghNM = self.getNM(FLUOR_SPECTRUM[dye][1]) for wl in range(emLow, emHgh+1): if wl in range(dyeLowNM, dyeHghNM+1): dyeList.append(dye) #self.dyeListBox.Clear() return sorted(list(set(dyeList))) def saveData(self, ctrl, tag, settings_controls): if isinstance(ctrl, wx.ListBox) and ctrl.GetStringSelection() == 'Other': other = wx.GetTextFromUser('Insert Other', 'Other') ctrl.Append(other) ctrl.SetStringSelection(other) if len(tag.split('|'))>4: # get the relevant controls for this tag eg, duration, temp controls for this step subtags = [] info = [] for subtag in [t for t, c in settings_controls.items()]: if get_tag_stump(tag, 4) == get_tag_stump(subtag, 4): subtags.append(subtag) for i in range(0, len(subtags)): info.append('') for st in subtags: if isinstance(settings_controls[st], wx.Choice) or isinstance(settings_controls[st], wx.ListBox): info[int(st.split('|')[4])]=settings_controls[st].GetStringSelection() else: info[int(st.split('|')[4])]=settings_controls[st].GetValue() self.set_field(get_tag_stump(tag, 4), info) # get the core tag like AddProcess|Spin|Step|<instance> = [duration, description, temp] else: if isinstance(ctrl, wx.Choice) or isinstance(ctrl, wx.ListBox): self.set_field(tag, ctrl.GetStringSelection()) elif isinstance(ctrl, wx.DatePickerCtrl): date = ctrl.GetValue() self.set_field(tag, '%02d/%02d/%4d'%(date.Day, date.Month+1, date.Year)) else: user_input = ctrl.GetValue() self.set_field(tag, user_input) def get_seeded_sample(self, platewell_id): '''this method returns sample or cell line information for the selected well ''' timeline = self.get_timeline() timepoints = timeline.get_unique_timepoints() events_by_timepoint = timeline.get_events_by_timepoint() seeding_instances = [] for i, timepoint in enumerate(timepoints): for ev in events_by_timepoint[timepoint]: for well_id in ev.get_well_ids(): if well_id == platewell_id and ev.get_welltag().startswith('CellTransfer|Seed'): seeding_instances.append(ev.get_welltag()) return seeding_instances def get_sampleInstance(self, seed_tag): '''This method returns the stock culutre or sample instance for a given seeding tag CellTransfer|Seed|Wells|<instance> ''' instance = get_tag_instance(seed_tag) # if seed from stock culture if self.global_settings.has_key('CellTransfer|Seed|StockInstance|%s'%instance): return self.get_field('CellTransfer|Seed|StockInstance|%s'%instance) elif self.global_settings.has_key('CellTransfer|Seed|HarvestInstance|%s'%instance): return self.get_field('CellTransfer|Harvest|StockInstance|%s' %str(self.get_field('CellTransfer|Seed|HarvestInstance|%s'%instance))) #---------------------------------------------------------------------- def getStateRGB(self, trackTags): """This method returns the colour of the node given the history of the ancestor nodes events""" currRGB = (255, 255, 255, 100) for tag in trackTags: event = get_tag_event(tag) if event.startswith('Notes') or event.startswith('DataAcquis'): # since these are measurements or notes continue currRGB = (int((currRGB[0]+EVENT_RGB[event][0])/2), int((currRGB[1]+EVENT_RGB[event][1])/2), int((currRGB[2]+EVENT_RGB[event][2])/2), 100) return currRGB #---------------------------------------------------------------------- def getEventRGB(self, tag): """get all event tags for the passed node and returns the colour associated with the last event** Need to change**""" #currRGB = (255, 255, 255, 100) #if nodeTags: #tag = nodeTags.pop() event = get_tag_event(tag) if not event.startswith('Notes') or not event.startswith('DataAcquis'): # since these are measurements or notes return (EVENT_RGB[event][0], EVENT_RGB[event][1], EVENT_RGB[event][2], 100) else: return (255, 255, 255, 100) #---------------------------------------------------------------------- def getEventIcon(self, icon_size, act): """get the associated icon for the given action/event""" if act == 'Seed': icon = icons.seed.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Stock': icon = icons.stock.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Harvest': icon = icons.harvest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Chem': icon = icons.treat.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Bio': icon = icons.dna.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Dye': icon = icons.stain.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Immuno': icon = icons.antibody.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Genetic': icon = icons.primer.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Spin': icon = icons.spin.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Wash': icon = icons.wash.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Dry': icon = icons.dry.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Medium': icon = icons.medium.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Incubator': icon = icons.incubator.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='HCS': icon = icons.staticimage.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='FCS': icon = icons.arrow_up.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='TLM': icon = icons.tlm.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Hint': #icon = icons.hint.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='CriticalPoint': #icon = icons.critical.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Rest': #icon = icons.rest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='URL': #icon = icons.url.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Video': #icon = icons.video.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() return icon
class ExperimentSettings(Singleton): global_settings = {} timeline = Timeline('TEST_STOCK') subscribers = {} #curr_dir = os.path.dirname(os.path.abspath(__file__)) #save_file_path = curr_dir+'\\temporary_experiment.txt' # first assign it to the temp directory save_file_path = None def __init__(self): pass def set_field(self, tag, value, notify_subscribers=True): self.global_settings[tag] = value if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'SET FIELD: %s = %s' % (tag, value) def get_field(self, tag, default=None): return self.global_settings.get(tag, default) def remove_field(self, tag, notify_subscribers=True): '''completely removes the specified tag from the metadata (if it exists) ''' #if self.get_field(tag) is not None: self.global_settings.pop(tag) if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'DEL FIELD: %s' % (tag) def remove_harvest_seed_tags(self, h_inst, s_inst): '''removes all associated tags with coupled harvest seed tags e.g density, steps etc''' h_attrs = self.get_attribute_list_by_instance('Transfer|Harvest', h_inst) s_attrs = self.get_attribute_list_by_instance('Transfer|Seed', s_inst) if h_attrs: for h_attr in h_attrs: self.remove_field('Transfer|Harvest|%s|%s' % (h_attr, h_inst), notify_subscribers=False) if s_attrs: for s_attr in s_attrs: self.remove_field('Transfer|Seed|%s|%s' % (s_attr, s_inst), notify_subscribers=False) def remove_associated_dataacquis_tag(self, wells): '''removes all image tags associated with this timepoint and wells e.g. DataAcquis|*|Images|*|timepoint|[(Plate_Well), ()]''' image_tags = self.get_matching_tags('DataAcquis|*|Images|*|*|%s' % str(wells)) if image_tags: for i_tag in image_tags: self.remove_field(i_tag) def remove_timeline_attachments(self, timepoint): '''removes all notes and attachment from the timeline at the given timepoint''' note_tags = self.get_matching_tags('Notes|*|%s|*' % timepoint) attach_tags = self.get_matching_tags('Attachments|*|%s|*' % timepoint) if note_tags: for n_tag in note_tags: self.remove_field(n_tag) if attach_tags: for a_tag in attach_tags: self.remove_field(a_tag) def get_action_tags(self): '''returns all existing TEMPORAL tags as list''' return [ tag for tag in self.global_settings if tag.split('|')[0] in ('Transfer', 'Perturbation', 'Labeling', 'AddProcess', 'DataAcquis', 'InstProcess', 'Notes') ] def get_field_instances(self, tag_prefix): '''returns a list of unique instance ids for each tag beginning with tag_prefix''' ids = set([ get_tag_instance(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ get_tag_attribute(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_list_by_instance(self, tag_prefix, instance=None): '''returns a list of all attributes beginning with tag_prefix. If instance is passed in, only attributes of the given instance will be returned''' ids = set([ get_tag_attribute(tag) for tag in self.global_settings if ((tag_prefix is None or tag.startswith(tag_prefix)) and ( instance is None or get_tag_instance(tag) == instance)) ]) return list(ids) #tags = [] #for tag in self.global_settings: #if ((tag_prefix is None or tag.startswith(tag_prefix)) and #(instance is None or get_tag_instance(tag) == instance)): #tag += [tag] #ids= set([get_tag_attribute(tag)]) #return list(ids) def get_attribute_dict(self, protocol): '''returns a dict mapping attribute names to their values for a given protocol. eg: get_attribute_dict('Transfer|Seed|1') --> {'SeedingDensity': 12, 'MediumUsed': 'agar', 'MediumAddatives': 'None', 'Trypsinization': True} ''' d = {} for tag in self.get_matching_tags('|*|'.join(protocol.rsplit('|', 1))): if (get_tag_attribute(tag) not in ('Wells', 'EventTimepoint', 'Images', 'OriginWells')): d[get_tag_attribute(tag)] = self.global_settings[tag] return d def get_eventtype_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[1] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_eventclass_list(self, tag_prefix): '''returns a list of event class name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[0] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_field_tags(self, tag_prefix=None, instance=None): '''returns a list of all tags beginning with tag_prefix. If instance is passed in, only tags of the given instance will be returned''' tags = [] for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): tags += [tag] return tags def get_matching_tags(self, matchstring): '''returns a list of all tags matching matchstring matchstring -- a string that matches the tags you want eg: Transfer|* ''' tags = [] for tag in self.global_settings: match = True for m, subtag in map(None, matchstring.split('|'), tag.split('|')): if m != subtag and m not in ('*', None): match = False break if match: tags += [tag] return tags def get_protocol_instances(self, prefix): '''returns a list of protocol instance names for tags matching the given prefix. ''' return list( set([get_tag_instance(tag) for tag in self.get_field_tags(prefix)])) def get_new_protocol_id(self, prefix): '''returns an id string that hasn't been used for the given tag prefix prefix -- eg: Transfer|Seed ''' instances = self.get_protocol_instances(prefix) for i in xrange(1, 100000): if str(i) not in instances: return str(i) def get_instance_by_field_value(self, prefix, field_value): '''returns instance number given tag prefix and field value ''' return get_tag_instance([ tag for tag, value in self.global_settings.iteritems() if value == field_value ][0]) def get_stack_ids(self, prefix): '''returns the stack ids for a given type of vessels prefix e.g. ExptVessel|Tube''' return set([ self.get_field(prefix + '|StackNo|%s' % instance) for instance in sorted(self.get_field_instances(prefix)) ]) def get_rep_vessel_instance(self, prefix, stack_id): ''' returns instance number of the vessel for this stack, since all vessels of a given stack has identical settings one instance represents others''' for instance in sorted(self.get_field_instances(prefix)): if self.get_field(prefix + '|StackNo|%s' % (instance)) == stack_id: return instance def clear(self): self.global_settings = {} PlateDesign.clear() # # TODO: # self.timeline = Timeline('TEST_STOCK') for matchstring, callbacks in self.subscribers.items(): for callback in callbacks: callback(None) def onTabClosing(self, event): event.Veto() dlg = wx.MessageDialog(None, 'Can not delete instance', 'Deleting..', wx.OK | wx.ICON_STOP) dlg.ShowModal() return def get_timeline(self): return self.timeline def does_tag_exists(self, tag_prefix, instnace=None): for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instnace is None or get_tag_instance(tag) == instnace)): return True else: return False def is_supp_protocol_filled(self, tag_prefix, instance=None): '''tag_prefix is always type|event e.g. AddProcess|Wash ''' for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): if (self.get_field(tag_prefix + '|ProtocolName|%s' % instance) is None) or (self.get_field(tag_prefix + '|Step1|%s' % instance) == ['', '', '']): return False else: return True def update_timeline(self, welltag): '''Updates the experiment metadata timeline event associated with the action and wells in welltag (eg: 'ExpNum|AddProcess|Spin|Wells|1|1') ''' platewell_ids = self.get_field(welltag, []) if platewell_ids == []: self.timeline.delete_event(welltag) else: event = self.timeline.get_event(welltag) if event is not None: event.set_well_ids(platewell_ids) else: self.timeline.add_event(welltag, platewell_ids) def save_file_dialogue(self): exp_date = self.get_field('Overview|Project|ExptDate') exp_num = self.get_field('Overview|Project|ExptNum') exp_title = self.get_field('Overview|Project|Title') if self.save_file_path: self.save_to_file() #import ntpath #filename = os.path.splitext(ntpath.basename(self.save_file_path))[0] else: filename = 'new_experiment.txt' if None not in [exp_date, exp_num, exp_title]: day, month, year = exp_date.split('/') filename = '%s%s%s_%s_%s.txt' % (year, month, day, exp_num, exp_title) dlg = wx.FileDialog(None, message='Saving experimental protocol...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: self.save_file_path = dlg.GetPath() self.save_to_file() def save_as_file_dialogue(self): exp_date = self.get_field('Overview|Project|ExptDate') exp_num = self.get_field('Overview|Project|ExptNum') exp_title = self.get_field('Overview|Project|Title') if self.save_file_path: import ntpath filename = os.path.splitext(ntpath.basename( self.save_file_path))[0] else: filename = 'new_experiment.txt' if None not in [exp_date, exp_num, exp_title]: day, month, year = exp_date.split('/') filename = '%s%s%s_%s_%s.txt' % (year, month, day, exp_num, exp_title) dlg = wx.FileDialog(None, message='Saving experimental protocol...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: self.save_file_path = dlg.GetPath() self.save_to_file() def save_to_file(self): if self.save_file_path: try: f = open(self.save_file_path, 'w') f.write(VERSION + '\n') for field, value in sorted(self.global_settings.items()): f.write('%s = %s\n' % (field, repr(value))) f.close() except IOError: import wx dial = wx.MessageDialog( None, 'No permission to create temporary experimental file in current directory\nPlease save file in separate directory.', 'Error', wx.OK | wx.ICON_ERROR) if dial.ShowModal() == wx.ID_OK: self.save_as_file_dialogue() def saveData(self, ctrl, tag, settings_controls): if isinstance(ctrl, wx.ListBox) and ctrl.GetStringSelection() == 'Other': other = wx.GetTextFromUser('Insert Other', 'Other') ctrl.Append(other) ctrl.SetStringSelection(other) if len(tag.split('|')) > 4: # get the relevant controls for this tag eg, duration, temp controls for this step subtags = [] info = [] for subtag in [t for t, c in settings_controls.items()]: if get_tag_stump(tag, 4) == get_tag_stump(subtag, 4): subtags.append(subtag) for i in range(0, len(subtags)): info.append('') for st in subtags: if isinstance(settings_controls[st], wx.Choice) or isinstance( settings_controls[st], wx.ListBox): info[int(st.split( '|')[4])] = settings_controls[st].GetStringSelection() #settings_controls[st].SetToolTipString(settings_controls[st].GetStringSelection()) else: info[int( st.split('|')[4])] = settings_controls[st].GetValue() #settings_controls[st].SetToolTipString(settings_controls[st].GetValue()) self.set_field( get_tag_stump(tag, 4), info ) # get the core tag like AddProcess|Spin|Step|<instance> = [duration, description, temp] else: if isinstance(ctrl, wx.Choice) or isinstance(ctrl, wx.ListBox): self.set_field(tag, ctrl.GetStringSelection()) #ctrl.SetToolTipString(ctrl.GetStringSelection()) elif isinstance(ctrl, wx.DatePickerCtrl): date = ctrl.GetValue() self.set_field( tag, '%02d/%02d/%4d' % (date.Day, date.Month + 1, date.Year)) else: user_input = ctrl.GetValue() self.set_field(tag, user_input) #ctrl.SetToolTipString(ctrl.GetValue()) def saving_settings(self, protocol, tag, m_tags): import wx if not self.get_field(tag): dial = wx.MessageDialog(None, 'Please provide a settings/protocol name', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return if self.checkMandatoryTags(m_tags): filename = self.get_field(tag) + '.txt' dlg = wx.FileDialog(None, message='Saving ...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: dirname = dlg.GetDirectory() filename = dlg.GetFilename() file_path = os.path.join(dirname, filename) self.save_settings(file_path, protocol) def save_settings(self, file, protocol): ''' saves settings in text file. the settings may include instrument settings, supp protocol, stock flask Format: attr = value where value may be text, int, list, etc. ''' instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) setting_type = get_tag_event(protocol) f = open(file, 'w') f.write(setting_type + '\n') attributes = list( set(self.get_attribute_list_by_instance(tag_stump, instance))) if 'Wells' in attributes: attributes.remove('Wells') for attr in attributes: value = self.get_field(tag_stump + '|%s|%s' % (attr, instance)) if attr.endswith('Instance'): f.write('Dependency%s = %s\n' % (attr, repr(value)) ) # to get the ...Instance which will be overwritten for d_attr, d_val in self.get_attribute_dict( '%s|%s' % (DEPENDENCY[get_tag_event(protocol)], value)).iteritems(): f.write('Dependency%s = %s\n' % (d_attr, repr(d_val))) else: f.write('%s = %s\n' % (attr, repr(value))) f.close() def load_from_file(self, file, menuitem): # Populate the tag structure self.clear() f = open(file, 'r') lines = [line.strip() for line in f] if not lines.pop(0).startswith('ProtocolNavigator'): import wx dial = wx.MessageDialog( None, 'Selected file is not a ProtocolNavigator file', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: tag, value = line.split('=', 1) tag = tag.strip() self.set_field(tag, eval(value), notify_subscribers=False) f.close() # Disable the open file menu menuitem.Enable(False) # index the file path self.save_file_path = file #import ntpath #self.temp_file_path = os.path.dirname(os.path.abspath(file))+os.path.splitext(ntpath.basename(self.save_file_path))[0]+'_temp.txt' # Populate PlateDesign PlateDesign.clear() for vessel_type in ('Plate', 'Flask', 'Dish', 'Coverslip', 'Tube'): prefix = 'ExptVessel|%s' % (vessel_type) for inst in self.get_field_instances(prefix): d = self.get_attribute_dict(prefix + '|' + inst) shape = d.get('Design', None) if shape is None: shape = (1, 1) group = d.get('StackName', None) PlateDesign.add_plate(vessel_type, inst, shape, group) # Update everything for tag in self.global_settings: self.notify_subscribers(tag) # Update the bench time-slider # TODO: this is crappy try: import wx bench = wx.GetApp().get_bench() bench.set_time_interval(0, self.get_timeline().get_max_timepoint()) except: return def load_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) lines = [line.strip() for line in open(file)] if not lines: import wx dial = wx.MessageDialog(None, 'Sub-process file is empty!!', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: #line.rstrip('\n') line_info = line.split('|') attr = line_info.pop(0) if len(line_info) > 1: self.set_field(tag_stump + '|%s|%s' % (attr, instance), line_info) else: self.set_field(tag_stump + '|%s|%s' % (attr, instance), line_info[0]) def load_settings(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file, 'r') lines = [line.strip() for line in f] event = lines.pop( 0 ) # takes out the first line or the header where all setting type microscope/spin etc are written attributes = {} for line in lines: attr, value = line.split('=', 1) attributes[attr.strip()] = value # set all dependency attributes e.g. Centifugation depends on Centrifuge dependecny_attrs = [s for s in attributes if 'Dependency' in s] if dependecny_attrs: d_inst = self.get_new_protocol_id(DEPENDENCY[event]) for d_attr in dependecny_attrs: if d_attr.endswith('Instance'): tag = tag_stump + '|%s|%s' % ( d_attr.split('Dependency')[1], instance) self.set_field(tag, d_inst, notify_subscribers=False) self.notify_subscribers(tag) del attributes[d_attr] else: tag = DEPENDENCY[event] + '|%s|%s' % ( d_attr.split('Dependency')[1], d_inst) self.set_field(tag, eval(attributes[d_attr]), notify_subscribers=False) self.notify_subscribers(tag) del attributes[d_attr] # set rest of the attributes for attr in attributes: tag = tag_stump + '|%s|%s' % (attr, instance) self.set_field(tag, eval(attributes[attr]), notify_subscribers=False) self.notify_subscribers(tag) f.close() #for line in lines: #attr, value = line.split('=', 1) #attr = attr.strip() #tag = tag_stump+'|%s|%s'%(attr, instance) #self.set_field(tag, eval(value), notify_subscribers=False) #self.notify_subscribers(tag) #f.close() def add_subscriber(self, callback, match_string): '''callback -- the function to be called match_string -- a regular expression string matching the tags you want to be notified of changes to ''' self.subscribers[match_string] = self.subscribers.get( match_string, []) + [callback] def remove_subscriber(self, callback): '''unsubscribe the given callback function. This MUST be called before a callback function is deleted. ''' for k, v in self.subscribers: if v == callback: self.subscribers.pop(k) def notify_subscribers(self, tag): for matchstring, callbacks in self.subscribers.items(): if re.match(matchstring, tag): #self.save_to_temp_file() # update info to the temp file for callback in callbacks: callback(tag) def getNM(self, nm): return int(nm.split('-')[0]), int(nm.split('-')[1]) def belongsTo(self, value, rangeStart, rangeEnd): if value >= rangeStart and value <= rangeEnd: return True return False def partition(self, lst, n): division = len(lst) / float(n) rlist = [ lst[int(round(division * i)):int(round(division * (i + 1)))][-1] for i in xrange(n) ] rlist.insert(0, lst[0]) return rlist def stringSplitByNumbers(self, x): r = re.compile('(\d+)') l = r.split(x) return [int(y) if y.isdigit() else y for y in l] def nmToRGB(self, w): # colour if w >= 380 and w < 440: R = -(w - 440.) / (440. - 350.) G = 0.0 B = 1.0 elif w >= 440 and w < 490: R = 0.0 G = (w - 440.) / (490. - 440.) B = 1.0 elif w >= 490 and w < 510: R = 0.0 G = 1.0 B = -(w - 510.) / (510. - 490.) elif w >= 510 and w < 580: R = (w - 510.) / (580. - 510.) G = 1.0 B = 0.0 elif w >= 580 and w < 645: R = 1.0 G = -(w - 645.) / (645. - 580.) B = 0.0 elif w >= 645 and w <= 780: R = 1.0 G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 # intensity correction if w >= 380 and w < 420: SSS = 0.3 + 0.7 * (w - 350) / (420 - 350) elif w >= 420 and w <= 700: SSS = 1.0 elif w > 700 and w <= 780: SSS = 0.3 + 0.7 * (780 - w) / (780 - 700) else: SSS = 0.0 SSS *= 255 return [int(SSS * R), int(SSS * G), int(SSS * B)] def decode_ch_component(self, component): '''this method decofify the components of the light path for a given channel 'LSR488' --> Laser 488nm, 'DMR567LP' ---> Long pass Dichroic Mirror 567nm etc.. ''' description = '' if component.startswith('LSR'): nm = re.sub('\D', '', component) description = nm + ' nm Excitation laser ' if component.startswith('DMR'): nm = re.sub('\D', '', component) if component.endswith('LP'): description = nm + ' nm Long Pass Dichroic Mirror' if component.endswith('SP'): description = nm + ' nm Short Pass Dichroic Mirror' if component.startswith('FLT'): if component.endswith('LP'): description = re.sub('\D', '', component) + ' nm Long Pass Filter' if component.endswith('SP'): description = re.sub('\D', '', component) + ' nm Short Pass Filter' if component.endswith('BP'): description = (component.split('FLT')[1]).split('BP')[ 0] + ' nm Band pass Filter' # this needs to be adjusted if component.startswith('SLT'): ratio = component.split('SLT')[1] description = re.sub('/', ':', ratio) + ' Beam Splitter' if component.startswith('DYE'): dye = component.split('_')[1] description = 'Dye used: %s' % dye if component.startswith('DTC'): volt = re.sub('\D', '', component) description = 'PMT voltage %s volts' % volt return description def setDyeList(self, emLow, emHgh): '''This method sets the list of dye for a given spectrum range''' dyeList = [] for dye in FLUOR_SPECTRUM: dyeLowNM, dyeHghNM = self.getNM(FLUOR_SPECTRUM[dye][1]) for wl in range(emLow, emHgh + 1): if wl in range(dyeLowNM, dyeHghNM + 1): dyeList.append(dye) #self.dyeListBox.Clear() return sorted(list(set(dyeList))) def get_seeded_sample(self, platewell_id): '''this method returns sample or cell line information for the selected well ''' timeline = self.get_timeline() timepoints = timeline.get_unique_timepoints() events_by_timepoint = timeline.get_events_by_timepoint() seeding_instances = [] for i, timepoint in enumerate(timepoints): for ev in events_by_timepoint[timepoint]: for well_id in ev.get_well_ids(): if well_id == platewell_id and ev.get_welltag().startswith( 'Transfer|Seed'): seeding_instances.append(ev.get_welltag()) return seeding_instances #---------------------------------------------------------------------- def get_sampleInstance(self, seed_tag): '''This method returns the stock culutre or sample instance for a given seeding tag Transfer|Seed|Wells|<instance> ''' instance = get_tag_instance(seed_tag) # if seed from stock culture if self.global_settings.has_key('Transfer|Seed|CellLineInstance|%s' % instance): return self.get_field('Transfer|Seed|CellLineInstance|%s' % instance) elif self.global_settings.has_key('Transfer|Seed|HarvestInstance|%s' % instance): return self.get_field('Transfer|Harvest|CellLineInstance|%s' % str( self.get_field('Transfer|Seed|HarvestInstance|%s' % instance))) #---------------------------------------------------------------------- def getStateRGB(self, trackTags): """This method returns the colour of the node given the history of the ancestor nodes events""" currRGB = (255, 255, 255, 100) for tag in trackTags: event = get_tag_event(tag) if event.startswith('Notes') or event.startswith( 'DataAcquis'): # since these are measurements or notes continue currRGB = (int((currRGB[0] + EVENT_RGB[event][0]) / 2), int((currRGB[1] + EVENT_RGB[event][1]) / 2), int((currRGB[2] + EVENT_RGB[event][2]) / 2), 100) return currRGB #---------------------------------------------------------------------- def getEventRGB(self, tag): """get all event tags for the passed node and returns the colour associated with the last event** Need to change**""" #currRGB = (255, 255, 255, 100) #if nodeTags: #tag = nodeTags.pop() event = get_tag_event(tag) if not event.startswith('Notes') or not event.startswith( 'DataAcquis'): # since these are measurements or notes return (EVENT_RGB[event][0], EVENT_RGB[event][1], EVENT_RGB[event][2], 100) else: return (255, 255, 255, 100) #---------------------------------------------------------------------- def getEventIcon(self, icon_size, act): """get the associated icon for the given action/event""" if act == 'Seed': icon = icons.seed.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'CellLine': icon = icons.stock.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Harvest': icon = icons.harvest.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Chemical': icon = icons.treat.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Biological': icon = icons.dna.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Physical': icon = icons.physical.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Dye': icon = icons.stain.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Immuno': icon = icons.antibody.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Genetic': icon = icons.primer.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Centrifugation': icon = icons.spin.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Wash': icon = icons.wash.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Drying': icon = icons.drying.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Medium': icon = icons.medium.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Initiation': icon = icons.initiation.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Storage': icon = icons.storage.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Incubation': icon = icons.incubator.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'RheoManipulation': icon = icons.rheometer.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'HCS': icon = icons.hcs.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'FCS': icon = icons.fcs.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'TLM': icon = icons.tlm.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'RHE': icon = icons.rhe.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Hint': #icon = icons.hint.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Text': #icon = icons.critical.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Rest': #icon = icons.rest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='URL': #icon = icons.url.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='MultiMedia': #icon = icons.video.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() return icon #---------------------------------------------------------------------- def get_Row_Numbers(self, protocol, token): """This method returs TAGS with similar elements eg. 'AddProcess|Rheology|Gas1|1' 'AddProcess|Rheology|Gas2|1' etc""" tag_stump = get_tag_stump(protocol, 2) instance = get_tag_attribute(protocol) return sorted(self.get_attribute_list_by_instance( tag_stump + '|%s' % token, instance), key=self.stringSplitByNumbers) #---------------------------------------------------------------------- def setLabelColour(self, tags, labels): """Change mandatory label colour Red-->Green when filled""" for tag in tags: if self.get_field(tag): labels[tag].SetForegroundColour(('#006600')) labels[tag].Refresh() else: labels[tag].SetForegroundColour(wx.RED) labels[tag].Refresh() #---------------------------------------------------------------------- def checkMandatoryTags(self, tags): """Checks whether the mandatory fields/tags being filled""" for tag in tags: if not self.get_field(tag): import wx dial = wx.MessageDialog( None, 'Please fill %s mandatory field' % get_tag_attribute(tag), 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return return True #---------------------------------------------------------------------- def alphanumeric_sort(self, lst): """sort alphanumeric strings in a list e.g. [Plate1, Plate11, Tube2]""" re_natural = re.compile('[0-9]+|[^0-9]+') return [(1, int(c)) if c.isdigit() else (0, c.lower()) for c in re_natural.findall(lst)] + [lst] #---------------------------------------------------------------------- def last_seed_instance(self, instance): """finds the last seeding instance that is associated with a harvest instance""" for inst in list(reversed(range(int(instance)))): if 'Transfer|Seed|HarvestInstance|%s' % str(inst) is not None: return str(inst) #---------------------------------------------------------------------- def get_cellLine_Name(self, inst, mode): """returns cell Line name given the seeding/harvesting (mode) instance that is realted with a harvest instance if mode is None, it refers to Harvest_Seed type of seeding instance""" if mode is 'H': return self.get_field('Sample|CellLine|Name|%s' % str( self.get_field( 'Transfer|Harvest|CellLineInstance|%s' % str(inst)))) if mode is 'S': return self.get_field('Sample|CellLine|Name|%s' % str( self.get_field( 'Transfer|Seed|CellLineInstance|%s' % str(inst)))) if mode is 'HS': return self.get_field('Sample|CellLine|Name|%s' % str( self.get_field('Transfer|Harvest|CellLineInstance|%s' % str( self.get_field( 'Transfer|Seed|HarvestInstance|%s' % str(inst))))))
class ExperimentSettings(Singleton): global_settings = {} timeline = Timeline('TEST_STOCK') subscribers = {} def __init__(self): pass def set_field(self, tag, value, notify_subscribers=True): self.global_settings[tag] = value if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'SET FIELD: %s = %s' % (tag, value) def get_field(self, tag, default=None): return self.global_settings.get(tag, default) def remove_field(self, tag, notify_subscribers=True): '''completely removes the specified tag from the metadata (if it exists) ''' #if self.get_field(tag) is not None: self.global_settings.pop(tag) if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'DEL FIELD: %s' % (tag) def get_action_tags(self): '''returns all existing TEMPORAL tags as list''' return [ tag for tag in self.global_settings if tag.split('|')[0] in ('CellTransfer', 'Perturbation', 'Staining', 'AddProcess', 'DataAcquis', 'Notes') ] def get_field_instances(self, tag_prefix): '''returns a list of unique instance ids for each tag beginning with tag_prefix''' ids = set([ get_tag_instance(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ get_tag_attribute(tag) for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_attribute_list_by_instance(self, tag_prefix, instance=None): '''returns a list of all attributes beginning with tag_prefix. If instance is passed in, only attributes of the given instance will be returned''' ids = set([ get_tag_attribute(tag) for tag in self.global_settings if ((tag_prefix is None or tag.startswith(tag_prefix)) and ( instance is None or get_tag_instance(tag) == instance)) ]) return list(ids) #tags = [] #for tag in self.global_settings: #if ((tag_prefix is None or tag.startswith(tag_prefix)) and #(instance is None or get_tag_instance(tag) == instance)): #tag += [tag] #ids= set([get_tag_attribute(tag)]) #return list(ids) def get_attribute_dict(self, protocol): '''returns a dict mapping attribute names to their values for a given protocol. eg: get_attribute_dict('CellTransfer|Seed|1') --> {'SeedingDensity': 12, 'MediumUsed': 'agar', 'MediumAddatives': 'None', 'Trypsinization': True} ''' d = {} for tag in self.get_matching_tags('|*|'.join(protocol.rsplit('|', 1))): if (get_tag_attribute(tag) not in ('Wells', 'EventTimepoint', 'Images', 'OriginWells')): d[get_tag_attribute(tag)] = self.global_settings[tag] return d def get_eventtype_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[1] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_eventclass_list(self, tag_prefix): '''returns a list of event class name for each tag beginning with tag_prefix''' ids = set([ tag.split('|')[0] for tag in self.global_settings if tag.startswith(tag_prefix) ]) return list(ids) def get_field_tags(self, tag_prefix=None, instance=None): '''returns a list of all tags beginning with tag_prefix. If instance is passed in, only tags of the given instance will be returned''' tags = [] for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): tags += [tag] return tags def get_matching_tags(self, matchstring): '''returns a list of all tags matching matchstring matchstring -- a string that matches the tags you want eg: CellTransfer|* ''' tags = [] for tag in self.global_settings: match = True for m, subtag in map(None, matchstring.split('|'), tag.split('|')): if m != subtag and m not in ('*', None): match = False break if match: tags += [tag] return tags def get_protocol_instances(self, prefix): '''returns a list of protocol instance names for tags matching the given prefix. ''' return list( set([get_tag_instance(tag) for tag in self.get_field_tags(prefix)])) def get_new_protocol_id(self, prefix): '''returns an id string that hasn't been used for the given tag prefix prefix -- eg: CellTransfer|Seed ''' instances = self.get_protocol_instances(prefix) for i in xrange(1, 100000): if str(i) not in instances: return str(i) def get_instance_by_field_value(self, prefix, field_value): '''returns instance number given tag prefix and field value ''' return get_tag_instance([ tag for tag, value in self.global_settings.iteritems() if value == field_value ][0]) def get_stack_ids(self, prefix): '''returns the stack ids for a given type of vessels prefix e.g. ExptVessel|Tube''' return set([ self.get_field(prefix + '|StackNo|%s' % instance) for instance in sorted(self.get_field_instances(prefix)) ]) def get_rep_vessel_instance(self, prefix, stack_id): ''' returns instance number of the vessel for this stack, since all vessels of a given stack has identical settings one instance represents others''' for instance in sorted(self.get_field_instances(prefix)): if self.get_field(prefix + '|StackNo|%s' % (instance)) == stack_id: return instance def clear(self): self.global_settings = {} PlateDesign.clear() # # TODO: # self.timeline = Timeline('TEST_STOCK') for matchstring, callbacks in self.subscribers.items(): for callback in callbacks: callback(None) def onTabClosing(self, event): event.Veto() dlg = wx.MessageDialog(None, 'Can not delete instance', 'Deleting..', wx.OK | wx.ICON_STOP) dlg.ShowModal() return def get_timeline(self): return self.timeline def does_tag_exists(self, tag_prefix, instnace=None): for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instnace is None or get_tag_instance(tag) == instnace)): return True else: return False def is_supp_protocol_filled(self, tag_prefix, instance=None): '''tag_prefix is always type|event e.g. AddProcess|Wash ''' for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): if (self.get_field(tag_prefix + '|ProtocolName|%s' % instance) is None) or (self.get_field(tag_prefix + '|Step1|%s' % instance) == ['', '', '']): return False else: return True def update_timeline(self, welltag): '''Updates the experiment metadata timeline event associated with the action and wells in welltag (eg: 'ExpNum|AddProcess|Spin|Wells|1|1') ''' platewell_ids = self.get_field(welltag, []) if platewell_ids == []: self.timeline.delete_event(welltag) else: event = self.timeline.get_event(welltag) if event is not None: event.set_well_ids(platewell_ids) else: self.timeline.add_event(welltag, platewell_ids) def save_to_file(self, file): f = open(file, 'w') for field, value in sorted(self.global_settings.items()): f.write('%s = %s\n' % (field, repr(value))) f.close() def save_settings(self, file, protocol): ''' saves settings in text file. the settings may include instrument settings, supp protocol, stock flask Format: attr = value where value may be text, int, list, etc. ''' instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) setting_type = get_tag_event(protocol) f = open(file, 'w') f.write(setting_type + '\n') attributes = self.get_attribute_list_by_instance(tag_stump, instance) for attr in attributes: info = self.get_field(tag_stump + '|%s|%s' % (attr, instance)) print info f.write('%s = %s\n' % (attr, repr(info))) f.close() def save_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file, 'w') attributes = self.get_attribute_list_by_instance(tag_stump, instance) for attr in attributes: info = self.get_field(tag_stump + '|%s|%s' % (attr, instance)) if isinstance(info, list): f.write('%s|' % attr) for i, val in enumerate(info): if isinstance(val, tuple): print val elif i == len(info) - 1: f.write('%s' % val) else: f.write('%s|' % val) f.write('\n') else: f.write('%s|%s\n' % (attr, info)) f.close() def load_from_file(self, file): # Populate the tag structure self.clear() f = open(file, 'r') for line in f: tag, value = line.split('=', 1) tag = tag.strip() self.set_field(tag, eval(value), notify_subscribers=False) f.close() # Populate PlateDesign PlateDesign.clear() for vessel_type in ('Plate', 'Flask', 'Dish', 'Coverslip', 'Tube'): prefix = 'ExptVessel|%s' % (vessel_type) for inst in self.get_field_instances(prefix): d = self.get_attribute_dict(prefix + '|' + inst) shape = d.get('Design', None) if shape is None: shape = (1, 1) group = d.get('StackName', None) PlateDesign.add_plate(vessel_type, inst, shape, group) # Update everything for tag in self.global_settings: self.notify_subscribers(tag) # Update the bench time-slider # TODO: this is crappy try: import wx bench = wx.GetApp().get_bench() bench.set_time_interval(0, self.get_timeline().get_max_timepoint()) except: return def load_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) lines = [line.strip() for line in open(file)] if not lines: import wx dial = wx.MessageDialog(None, 'Supplementary protocol file is empty!!', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: #line.rstrip('\n') line_info = line.split('|') attr = line_info.pop(0) if len(line_info) > 1: self.set_field(tag_stump + '|%s|%s' % (attr, instance), line_info) else: self.set_field(tag_stump + '|%s|%s' % (attr, instance), line_info[0]) def load_settings(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file, 'r') lines = [line.strip() for line in f] lines.pop( 0 ) # takes out the first line or the header where all setting type microscope/spin etc are written for line in lines: attr, value = line.split('=', 1) attr = attr.strip() tag = tag_stump + '|%s|%s' % (attr, instance) self.set_field(tag, eval(value), notify_subscribers=False) self.notify_subscribers(tag) f.close() def add_subscriber(self, callback, match_string): '''callback -- the function to be called match_string -- a regular expression string matching the tags you want to be notified of changes to ''' self.subscribers[match_string] = self.subscribers.get( match_string, []) + [callback] def remove_subscriber(self, callback): '''unsubscribe the given callback function. This MUST be called before a callback function is deleted. ''' for k, v in self.subscribers: if v == callback: self.subscribers.pop(k) def notify_subscribers(self, tag): for matchstring, callbacks in self.subscribers.items(): if re.match(matchstring, tag): for callback in callbacks: callback(tag) def getNM(self, nm): return int(nm.split('-')[0]), int(nm.split('-')[1]) def belongsTo(self, value, rangeStart, rangeEnd): if value >= rangeStart and value <= rangeEnd: return True return False def partition(self, lst, n): division = len(lst) / float(n) rlist = [ lst[int(round(division * i)):int(round(division * (i + 1)))][-1] for i in xrange(n) ] rlist.insert(0, lst[0]) return rlist def stringSplitByNumbers(self, x): r = re.compile('(\d+)') l = r.split(x) return [int(y) if y.isdigit() else y for y in l] def nmToRGB(self, w): # colour if w >= 380 and w < 440: R = -(w - 440.) / (440. - 350.) G = 0.0 B = 1.0 elif w >= 440 and w < 490: R = 0.0 G = (w - 440.) / (490. - 440.) B = 1.0 elif w >= 490 and w < 510: R = 0.0 G = 1.0 B = -(w - 510.) / (510. - 490.) elif w >= 510 and w < 580: R = (w - 510.) / (580. - 510.) G = 1.0 B = 0.0 elif w >= 580 and w < 645: R = 1.0 G = -(w - 645.) / (645. - 580.) B = 0.0 elif w >= 645 and w <= 780: R = 1.0 G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 # intensity correction if w >= 380 and w < 420: SSS = 0.3 + 0.7 * (w - 350) / (420 - 350) elif w >= 420 and w <= 700: SSS = 1.0 elif w > 700 and w <= 780: SSS = 0.3 + 0.7 * (780 - w) / (780 - 700) else: SSS = 0.0 SSS *= 255 return [int(SSS * R), int(SSS * G), int(SSS * B)] def decode_ch_component(self, component): '''this method decofify the components of the light path for a given channel 'LSR488' --> Laser 488nm, 'DMR567LP' ---> Long pass Dichroic Mirror 567nm etc.. ''' description = '' if component.startswith('LSR'): nm = re.sub('\D', '', component) description = nm + ' nm Excitation laser ' if component.startswith('DMR'): nm = re.sub('\D', '', component) if component.endswith('LP'): description = nm + ' nm Long Pass Dichroic Mirror' if component.endswith('SP'): description = nm + ' nm Short Pass Dichroic Mirror' if component.startswith('FLT'): if component.endswith('LP'): description = re.sub('\D', '', component) + ' nm Long Pass Filter' if component.endswith('SP'): description = re.sub('\D', '', component) + ' nm Short Pass Filter' if component.endswith('BP'): description = (component.split('FLT')[1]).split('BP')[ 0] + ' nm Band pass Filter' # this needs to be adjusted if component.startswith('DYE'): dye = component.split('_')[1] description = 'Dye used: %s' % dye if component.startswith('DTC'): volt = re.sub('\D', '', component) description = 'PMT voltage %s volts' % volt return description def setDyeList(self, emLow, emHgh): '''This method sets the list of dye for a given spectrum range''' dyeList = [] for dye in FLUOR_SPECTRUM: dyeLowNM, dyeHghNM = self.getNM(FLUOR_SPECTRUM[dye][1]) for wl in range(emLow, emHgh + 1): if wl in range(dyeLowNM, dyeHghNM + 1): dyeList.append(dye) #self.dyeListBox.Clear() return sorted(list(set(dyeList))) def saveData(self, ctrl, tag, settings_controls): if isinstance(ctrl, wx.ListBox) and ctrl.GetStringSelection() == 'Other': other = wx.GetTextFromUser('Insert Other', 'Other') ctrl.Append(other) ctrl.SetStringSelection(other) if len(tag.split('|')) > 4: # get the relevant controls for this tag eg, duration, temp controls for this step subtags = [] info = [] for subtag in [t for t, c in settings_controls.items()]: if get_tag_stump(tag, 4) == get_tag_stump(subtag, 4): subtags.append(subtag) for i in range(0, len(subtags)): info.append('') for st in subtags: if isinstance(settings_controls[st], wx.Choice) or isinstance( settings_controls[st], wx.ListBox): info[int(st.split( '|')[4])] = settings_controls[st].GetStringSelection() else: info[int( st.split('|')[4])] = settings_controls[st].GetValue() self.set_field( get_tag_stump(tag, 4), info ) # get the core tag like AddProcess|Spin|Step|<instance> = [duration, description, temp] else: if isinstance(ctrl, wx.Choice) or isinstance(ctrl, wx.ListBox): self.set_field(tag, ctrl.GetStringSelection()) elif isinstance(ctrl, wx.DatePickerCtrl): date = ctrl.GetValue() self.set_field( tag, '%02d/%02d/%4d' % (date.Day, date.Month + 1, date.Year)) else: user_input = ctrl.GetValue() self.set_field(tag, user_input) def get_seeded_sample(self, platewell_id): '''this method returns sample or cell line information for the selected well ''' timeline = self.get_timeline() timepoints = timeline.get_unique_timepoints() events_by_timepoint = timeline.get_events_by_timepoint() seeding_instances = [] for i, timepoint in enumerate(timepoints): for ev in events_by_timepoint[timepoint]: for well_id in ev.get_well_ids(): if well_id == platewell_id and ev.get_welltag().startswith( 'CellTransfer|Seed'): seeding_instances.append(ev.get_welltag()) return seeding_instances def get_sampleInstance(self, seed_tag): '''This method returns the stock culutre or sample instance for a given seeding tag CellTransfer|Seed|Wells|<instance> ''' instance = get_tag_instance(seed_tag) # if seed from stock culture if self.global_settings.has_key('CellTransfer|Seed|StockInstance|%s' % instance): return self.get_field('CellTransfer|Seed|StockInstance|%s' % instance) elif self.global_settings.has_key( 'CellTransfer|Seed|HarvestInstance|%s' % instance): return self.get_field( 'CellTransfer|Harvest|StockInstance|%s' % str( self.get_field( 'CellTransfer|Seed|HarvestInstance|%s' % instance))) #---------------------------------------------------------------------- def getStateRGB(self, trackTags): """This method returns the colour of the node given the history of the ancestor nodes events""" currRGB = (255, 255, 255, 100) for tag in trackTags: event = get_tag_event(tag) if event.startswith('Notes') or event.startswith( 'DataAcquis'): # since these are measurements or notes continue currRGB = (int((currRGB[0] + EVENT_RGB[event][0]) / 2), int((currRGB[1] + EVENT_RGB[event][1]) / 2), int((currRGB[2] + EVENT_RGB[event][2]) / 2), 100) return currRGB #---------------------------------------------------------------------- def getEventRGB(self, tag): """get all event tags for the passed node and returns the colour associated with the last event** Need to change**""" #currRGB = (255, 255, 255, 100) #if nodeTags: #tag = nodeTags.pop() event = get_tag_event(tag) if not event.startswith('Notes') or not event.startswith( 'DataAcquis'): # since these are measurements or notes return (EVENT_RGB[event][0], EVENT_RGB[event][1], EVENT_RGB[event][2], 100) else: return (255, 255, 255, 100) #---------------------------------------------------------------------- def getEventIcon(self, icon_size, act): """get the associated icon for the given action/event""" if act == 'Seed': icon = icons.seed.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Stock': icon = icons.stock.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Harvest': icon = icons.harvest.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Chem': icon = icons.treat.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Bio': icon = icons.dna.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Dye': icon = icons.stain.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Immuno': icon = icons.antibody.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Genetic': icon = icons.primer.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Spin': icon = icons.spin.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Wash': icon = icons.wash.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Dry': icon = icons.dry.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Medium': icon = icons.medium.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'Incubator': icon = icons.incubator.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'HCS': icon = icons.staticimage.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'FCS': icon = icons.arrow_up.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'TLM': icon = icons.tlm.Scale( icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Hint': #icon = icons.hint.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='CriticalPoint': #icon = icons.critical.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Rest': #icon = icons.rest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='URL': #icon = icons.url.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Video': #icon = icons.video.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() return icon
class Workplace: # ---------- INITIALISATION ---------- def __init__(self, file=None, verbose=False): # Create an empty workplace self.agents = [] self.completed_tasks = [] self.current_task = None self.tasks_todo = [] self.time = 0 self.timeline = Timeline() # list of TimePoints self.coordination_times = {} self.Tperf = {} self.verbose = verbose if file: print("Reading from input file " + file + "...\n") self.parse_json(file, verbose) def parse_json(self, filename, verbose=False): ''' reads json file and loads agents, tasks and parameters''' with open(filename) as f: data = json.load(f) for idx, agent in enumerate(data['agents'], verbose): self.add_agent(idx, agent, verbose=verbose) for idx, task in enumerate(data['tasks']): self.add_task(idx, task) self.import_parameters(data['parameters']) def add_agent(self, idx, agent, verbose=False): skills = [ Skill(_id=skill['id'], exp=skill['exp'], mot=skill['mot']) for skill in agent['skillset'] ] mbti = agent['mbti'] if 'mbti' in agent else None initial_frustration = agent[ 'initial_frustration'] if 'initial_frustration' in agent else None self.agents.append( Agent(_id=idx, mbti=mbti, initial_frustration=initial_frustration, skillset=skills, verbose=verbose)) def add_task(self, idx, task): self.tasks_todo.append(Task(_id=idx, json_task=task)) def import_parameters(self, params): ''' Loads all parameters from dictionary that comes from json file''' if 'task_unit_duration' in params: P.TASK_UNIT_DURATION = params['task_unit_duration'] if 'alpha_e' in params: P.ALPHA_E = params['alpha_e'] if 'alpha_m' in params: P.ALPHA_M = params['alpha_m'] if 'alpha_f' in params: P.ALPHA_F = params['alpha_f'] if 'beta' in params: P.BETA = params['beta'] if 'lam_learn' in params: P.LAM_LEARN = params['lam_learn'] if 'lam_motiv' in params: P.LAM_MOTIV = params['lam_motiv'] if 'mu_learn' in params: P.MU_LEARN = params['mu_learn'] if 'mu_motiv' in params: P.MU_MOTIV = params['mu_motiv'] if 'th_e' in params: P.TH_E = params['th_e'] if 'th_m' in params: P.TH_M = params['th_m'] if 'max_e' in params: P.MAX_E = params['max_e'] if 'max_m' in params: P.MAX_M = params['max_m'] if 'max_h' in params: P.MAX_H = params['max_h'] if 'excite' in params: P.EXCITE = params['excite'] if 'inhibit' in params: P.INHIBIT = params['inhibit'] # Normalise alphas if P.ALPHA_E + P.ALPHA_F + P.ALPHA_M != 1: factor = 1 / (P.ALPHA_E + P.ALPHA_F + P.ALPHA_M) P.ALPHA_E *= factor P.ALPHA_F *= factor P.ALPHA_M *= factor # ---------- TASK PROCESSING ---------- def process_tasks(self): ''' Just a while loop that processes all the tasks in another function''' # While there is work to do... while len(self.tasks_todo) > 0: # Tasks are handled one at a time self.current_task = self.tasks_todo.pop(0) self.process_current_task() if self.verbose: print('Processed task:\n' + str(self.current_task) + '\n') self.completed_tasks.append(self.current_task) self.current_task = None # Output frustration values, for later plot media_path = '../media/' if os.path.isdir('../media') \ else '../../media/' with open(media_path + 'moods.data', 'w') as f: for i in range(len(self.agents[0].frustration)): f.write(str(self.agents[0].frustration[i]) + ', ' + \ str(self.agents[0].frustration[i]) + '\n') def process_current_task(self): ''' Processes current tasks one by one. Called by process_tasks() ''' # Repeat action assignment until all actions have been completed while True: # Assign agents to each of the actions actions_to_process = [choose_agent(self, action) for action in self.current_task.actions \ if action.completion < action.duration] if len(actions_to_process) == 0: break assignments, allocation_times, skill_ids, action_ids = zip( *actions_to_process) self.coordination_times[self.time] = sum(allocation_times) t_perfs = [ agent.calculate_performance_time(skill_ids, assignments, self.time) for agent in self.agents ] self.Tperf[ self.time] = max(t_perfs) + self.coordination_times[self.time] # ~ HOUSEKEEPING ~ for agent in self.agents: agent.flush_prev_act( assignments, skill_ids ) # Clear internal variables related to previous task agent.update_memory() # Update expertise and motivation # Update current actions for all agents for i, assignment in enumerate(assignments): self.agents[assignment].current_action.append({ 'task': self.current_task._id, 'action': action_ids[i], 'start_time': self.time }) self.timeline.add_event(Event(start_time = self.time, \ duration = 1, # Constant, for now task_id = self.current_task._id, \ action_id = action_ids[i], \ agent_id = assignment, \ )) # ~ END HOUSEKEEPING ~ self.time += 1 # ---------- GETTERS ---------- def get_sum_perf_time(self): return int(np.round(sum(self.Tperf.values()))) # ---------- PRINTING ---------- def plot_skills(self, agent): ''' Plots the expertise of two agents as a function of number of cycles''' y1 = np.round(np.array(agent.skillset[0].expertise)) y2 = np.round(np.array(agent.skillset[1].expertise)) x = np.round(np.array(list(range(len(y1))))) y1 = [y if y > 0 else 0 for y in y1] y2 = [y if y > 0 else 0 for y in y2] trace1 = go.Scatter(x=x, y=y1, mode='lines+markers', name='Skill 1') trace2 = go.Scatter(x=x, y=y2, mode='lines+markers', name='Skill 2') layout = go.Layout( title='Expertise', xaxis=dict(title='Cycles', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), yaxis=dict(title='Expertise', titlefont=dict(family='Arial, sans-serif', size=24, color='black'))) data = [trace1, trace2] fig = go.Figure(data=data, layout=layout) iplot(fig) def plot_skills_matplotlib(self, agent): ''' Plots the expertise of two agents as a function of number of cycles''' y1 = np.round(np.array(agent.skillset[0].expertise)) y2 = np.round(np.array(agent.skillset[1].expertise)) x = np.round(np.array(list(range(len(y1))))) y1 = [y if y > 0 else 0 for y in y1] y2 = [y if y > 0 else 0 for y in y2] fig = plt.figure() plt.plot(x, y1, '.-', x, y2, '.-') plt.xlabel('Cycles') plt.ylabel('Expertise') plt.title('Evolution of expertise: Agent ' + str(agent._id)) plt.legend(['Skill 1', 'Skill 2']) plt.draw() return fig def plot_motivation(self, agent): ''' Plots the motivation of two agents as a function of #cycles''' y1 = np.round(np.array(agent.skillset[0].motivation)) y2 = np.round(np.array(agent.skillset[1].motivation)) x = np.round(np.array(list(range(len(y1))))) y1 = [y if y > 0 else 0 for y in y1] y2 = [y if y > 0 else 0 for y in y2] trace1 = go.Scatter(x=x, y=y1, mode='lines+markers', name='Skill 1') trace2 = go.Scatter(x=x, y=y2, mode='lines+markers', name='Skill 2') layout = go.Layout( title='Motivation', xaxis=dict(title='Cycles', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), yaxis=dict(title='Motivation', titlefont=dict(family='Arial, sans-serif', size=24, color='black'))) data = [trace1, trace2] fig = go.Figure(data=data, layout=layout) iplot(fig) def plot_frustration(self): ''' Plots frustration of two agents as a function of #cycles''' y0 = np.array(self.agents[0].frustration) y1 = np.array(self.agents[1].frustration) x = np.array(list(range(len(y1)))) trace1 = go.Scatter(x=x, y=y0, mode='lines+markers', name='Agent 1') trace2 = go.Scatter(x=x, y=y1, mode='lines+markers', name='Agent 2') layout = go.Layout( title='Frustration', xaxis=dict(title='Cycles', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), yaxis=dict(title='Frustration', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), showlegend=True) data = [trace1, trace2] fig = go.Figure(data=data, layout=layout) iplot(fig) def plot_frustration_matplotlib(self): ''' Plots frustration of two agents as a function of #cycles''' y0 = np.array(self.agents[0].frustration) y1 = np.array(self.agents[1].frustration) x = np.array(list(range(len(y1)))) fig = plt.figure() plt.plot(x, y0, '.-', x, y1, '.-') plt.xlabel('Cycles') plt.ylabel('Frustration') plt.title('Frustration') plt.legend(['Agent 1', 'Agent 2']) plt.draw() return fig def plot_allocations(self): ''' Plots allocation time it took for every cycle''' y0 = np.array(self.agents[0].allocation_times) y1 = np.array(self.agents[1].allocation_times) x = np.array(list(range(len(y1)))) # y1 = [y if y > 0 else 0 for y in y1] # y2 = [y if y > 0 else 0 for y in y2] trace1 = go.Scatter(x=x, y=y0, mode='lines+markers', name='Agent 1') trace2 = go.Scatter(x=x, y=y1, mode='lines+markers', name='Agent 2') layout = go.Layout( title='Allocations', xaxis=dict(title='Cycles', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), yaxis=dict(title='Allocation time', titlefont=dict(family='Arial, sans-serif', size=24, color='black'))) data = [trace1, trace2] fig = go.Figure(data=data, layout=layout) iplot(fig) def plot_performance(self): ''' Plots performance times of the agents and of the whole system''' y = [] y.append(np.round(np.array(list(self.Tperf.values())))) y.append(np.round(np.array(list(self.coordination_times.values())))) y.append( np.round(np.array(list( self.agents[0].performance_times.values())))) y.append( np.round(np.array(list( self.agents[1].performance_times.values())))) for _y in y: _y = [y if y > 0 else 0 for y in _y] x = np.array(list(range(len(y[0])))) names = ['System', 'Coordination Time', 'Agent 1', 'Agent 2'] data = [ go.Scatter(x=x, y=_y, mode='lines+markers', name=names[i]) for i, _y in enumerate(y) ] layout = go.Layout( title='Performance', xaxis=dict(title='Cycles', titlefont=dict(family='Arial, sans-serif', size=24, color='black')), yaxis=dict(title='Performance Time', titlefont=dict(family='Arial, sans-serif', size=24, color='black'))) fig = go.Figure(data=data, layout=layout) iplot(fig) def plot_performance_matplotlib(self): ''' Plots performance times of the agents and of the whole system''' y = [] y.append(np.round(np.array(list(self.Tperf.values())))) y.append(np.round(np.array(list(self.coordination_times.values())))) y.append( np.round(np.array(list( self.agents[0].performance_times.values())))) y.append( np.round(np.array(list( self.agents[1].performance_times.values())))) for _y in y: _y = [y if y > 0 else 0 for y in _y] x = np.array(list(range(len(y[0])))) fig = plt.figure() plt.plot(x, y[0], '.-', x, y[1], '.-', x, y[2], '.-', x, y[3], '.-') plt.xlabel('Cycles') plt.ylabel('Time') plt.title('Performance') plt.legend(['System', 'Coordination Time', 'Agent 1', 'Agent 2']) plt.draw() return fig def print_parameters(self): print('task_unit_duration: ' + str(P.TASK_UNIT_DURATION)) print('alpha_e: ' + str(P.ALPHA_E)) print('alpha_m: ' + str(P.ALPHA_M)) print('alpha_f: ' + str(P.ALPHA_F)) print('beta: ' + str(P.BETA)) print('lam_learn: ' + str(P.LAM_LEARN)) print('lam_motiv: ' + str(P.LAM_MOTIV)) print('mu_learn: ' + str(P.MU_LEARN)) print('mu_motiv: ' + str(P.MU_MOTIV)) print('th_e: ' + str(P.TH_E)) print('th_m: ' + str(P.TH_M)) print('max_e: ' + str(P.MAX_E)) print('max_m: ' + str(P.MAX_M)) print('max_h: ' + str(P.MAX_H)) print('excite: ' + str(P.EXCITE)) print('inhibit: ' + str(P.INHIBIT)) print('\n') print('Maximum number of steps in coordination:' + str(P.MAX_COORD_STEPS)) print('\n') mbti_types = [ 'ESTJ', 'ESTP', 'ESFJ', 'ESFP', 'ENTJ', 'ENTP', 'ENFJ', 'ENFP', 'ISTJ', 'ISTP', 'ISFJ', 'ISFP', 'INTJ', 'INTP', 'INFJ', 'INFP' ] print('MBTI Matrix:') print('\t'.join(mbti_types)) for i in range(len(mbti_types)): print(mbti_types[i] + '\t') for j in range(len(mbti_types)): print(P.MBTI[i][j], end='\t') def print_history(self): ''' Debugging information: same as Gantt diagram''' for i in range(len(self.timeline)): print('--- Time Point ' + str(i) + ' ---') print(self.timeline.events[i]) def agents_string(self): return 'Agents:\n' + '\n'.join(list(map(str, self.agents))) def tasks_string(self): return 'TASKS:\n\n' + '\n'.join(list(map(str, self.tasks_todo))) def print_current_state(self): ''' Printing for Debugging purposes ''' # Print time stamp print("Time elapsed:") print(self.time, end='') print(" time units.\n") # Print Tasks: Completed, Current, To-Do print("Completed tasks:") for task in self.completed_tasks: print(task) print("Current task:") print(self.current_task) print("Future tasks:") for task in self.tasks_todo: print(task) # Print Agents: List, Current Engagement... print("Currently employed agents:") for agent in self.agents: print(agent) # Print history of completed actions print("History:") self.timeline.plot_gantt()
class ExperimentSettings(Singleton): global_settings = {} timeline = Timeline('TEST_STOCK') subscribers = {} #curr_dir = os.path.dirname(os.path.abspath(__file__)) #save_file_path = curr_dir+'\\temporary_experiment.txt' # first assign it to the temp directory save_file_path = None def __init__(self): pass def set_field(self, tag, value, notify_subscribers=True): self.global_settings[tag] = value if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'SET FIELD: %s = %s'%(tag, value) def get_field(self, tag, default=None): return self.global_settings.get(tag, default) def remove_field(self, tag, notify_subscribers=True): '''completely removes the specified tag from the metadata (if it exists) ''' #if self.get_field(tag) is not None: self.global_settings.pop(tag) if re.match(get_matchstring_for_subtag(2, 'Well'), tag): self.update_timeline(tag) if notify_subscribers: self.notify_subscribers(tag) print 'DEL FIELD: %s'%(tag) def remove_harvest_seed_tags(self, h_inst, s_inst): '''removes all associated tags with coupled harvest seed tags e.g density, steps etc''' h_attrs = self.get_attribute_list_by_instance('Transfer|Harvest', h_inst) s_attrs = self.get_attribute_list_by_instance('Transfer|Seed', s_inst) if h_attrs: for h_attr in h_attrs: self.remove_field('Transfer|Harvest|%s|%s'%(h_attr, h_inst), notify_subscribers =False) if s_attrs: for s_attr in s_attrs: self.remove_field('Transfer|Seed|%s|%s'%(s_attr, s_inst), notify_subscribers =False) def remove_associated_dataacquis_tag(self, wells): '''removes all image tags associated with this timepoint and wells e.g. DataAcquis|*|Images|*|timepoint|[(Plate_Well), ()]''' image_tags = self.get_matching_tags('DataAcquis|*|Images|*|*|%s'%str(wells)) if image_tags: for i_tag in image_tags: self.remove_field(i_tag) def remove_timeline_attachments(self, timepoint): '''removes all notes and attachment from the timeline at the given timepoint''' note_tags = self.get_matching_tags('Notes|*|%s|*'%timepoint) attach_tags = self.get_matching_tags('Attachments|*|%s|*'%timepoint) if note_tags: for n_tag in note_tags: self.remove_field(n_tag) if attach_tags: for a_tag in attach_tags: self.remove_field(a_tag) def get_action_tags(self): '''returns all existing TEMPORAL tags as list''' return [tag for tag in self.global_settings if tag.split('|')[0] in ('Transfer', 'Perturbation', 'Labeling', 'AddProcess', 'DataAcquis', 'InstProcess', 'Notes')] def get_field_instances(self, tag_prefix): '''returns a list of unique instance ids for each tag beginning with tag_prefix''' ids = set([get_tag_instance(tag) for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_attribute_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([get_tag_attribute(tag) for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_attribute_list_by_instance(self, tag_prefix, instance=None): '''returns a list of all attributes beginning with tag_prefix. If instance is passed in, only attributes of the given instance will be returned''' ids = set([get_tag_attribute(tag) for tag in self.global_settings if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance))]) return list(ids) #tags = [] #for tag in self.global_settings: #if ((tag_prefix is None or tag.startswith(tag_prefix)) and #(instance is None or get_tag_instance(tag) == instance)): #tag += [tag] #ids= set([get_tag_attribute(tag)]) #return list(ids) def get_attribute_dict(self, protocol): '''returns a dict mapping attribute names to their values for a given protocol. eg: get_attribute_dict('Transfer|Seed|1') --> {'SeedingDensity': 12, 'MediumUsed': 'agar', 'MediumAddatives': 'None', 'Trypsinization': True} ''' d = {} for tag in self.get_matching_tags('|*|'.join(protocol.rsplit('|',1))): if (get_tag_attribute(tag) not in ('Wells', 'EventTimepoint', 'Images', 'OriginWells')): d[get_tag_attribute(tag)] = self.global_settings[tag] return d def get_eventtype_list(self, tag_prefix): '''returns a list of attributes name for each tag beginning with tag_prefix''' ids = set([tag.split('|')[1] for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_eventclass_list(self, tag_prefix): '''returns a list of event class name for each tag beginning with tag_prefix''' ids = set([tag.split('|')[0] for tag in self.global_settings if tag.startswith(tag_prefix)]) return list(ids) def get_field_tags(self, tag_prefix=None, instance=None): '''returns a list of all tags beginning with tag_prefix. If instance is passed in, only tags of the given instance will be returned''' tags = [] for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): tags += [tag] return tags def get_matching_tags(self, matchstring): '''returns a list of all tags matching matchstring matchstring -- a string that matches the tags you want eg: Transfer|* ''' tags = [] for tag in self.global_settings: match = True for m, subtag in map(None, matchstring.split('|'), tag.split('|')): if m != subtag and m not in ('*', None): match = False break if match: tags += [tag] return tags def get_protocol_instances(self, prefix): '''returns a list of protocol instance names for tags matching the given prefix. ''' return list(set([get_tag_instance(tag) for tag in self.get_field_tags(prefix)])) def get_new_protocol_id(self, prefix): '''returns an id string that hasn't been used for the given tag prefix prefix -- eg: Transfer|Seed ''' instances = self.get_protocol_instances(prefix) for i in xrange(1, 100000): if str(i) not in instances: return str(i) def get_instance_by_field_value(self, prefix, field_value): '''returns instance number given tag prefix and field value ''' return get_tag_instance([tag for tag, value in self.global_settings.iteritems() if value == field_value][0]) def get_stack_ids(self, prefix): '''returns the stack ids for a given type of vessels prefix e.g. ExptVessel|Tube''' return set([self.get_field(prefix+'|StackNo|%s'%instance) for instance in sorted(self.get_field_instances(prefix))]) def get_rep_vessel_instance(self, prefix, stack_id): ''' returns instance number of the vessel for this stack, since all vessels of a given stack has identical settings one instance represents others''' for instance in sorted(self.get_field_instances(prefix)): if self.get_field(prefix+'|StackNo|%s'%(instance)) == stack_id: return instance def clear(self): self.global_settings = {} PlateDesign.clear() # # TODO: # self.timeline = Timeline('TEST_STOCK') for matchstring, callbacks in self.subscribers.items(): for callback in callbacks: callback(None) def onTabClosing(self, event): event.Veto() dlg = wx.MessageDialog(None, 'Can not delete instance', 'Deleting..', wx.OK| wx.ICON_STOP) dlg.ShowModal() return def get_timeline(self): return self.timeline def does_tag_exists(self, tag_prefix, instnace=None): for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instnace is None or get_tag_instance(tag) == instnace)): return True else: return False def is_supp_protocol_filled(self, tag_prefix, instance=None): '''tag_prefix is always type|event e.g. AddProcess|Wash ''' for tag in self.global_settings: if ((tag_prefix is None or tag.startswith(tag_prefix)) and (instance is None or get_tag_instance(tag) == instance)): if (self.get_field(tag_prefix+'|ProtocolName|%s'%instance) is None) or (self.get_field(tag_prefix+'|Step1|%s'%instance) == ['', '', '']): return False else: return True def update_timeline(self, welltag): '''Updates the experiment metadata timeline event associated with the action and wells in welltag (eg: 'ExpNum|AddProcess|Spin|Wells|1|1') ''' platewell_ids = self.get_field(welltag, []) if platewell_ids == []: self.timeline.delete_event(welltag) else: event = self.timeline.get_event(welltag) if event is not None: event.set_well_ids(platewell_ids) else: self.timeline.add_event(welltag, platewell_ids) def save_file_dialogue(self): exp_date = self.get_field('Overview|Project|ExptDate') exp_num = self.get_field('Overview|Project|ExptNum') exp_title = self.get_field('Overview|Project|Title') if self.save_file_path: self.save_to_file() #import ntpath #filename = os.path.splitext(ntpath.basename(self.save_file_path))[0] else: filename = 'new_experiment.txt' if None not in [exp_date, exp_num, exp_title]: day, month, year = exp_date.split('/') filename = '%s%s%s_%s_%s.txt'%(year, month, day , exp_num, exp_title) dlg = wx.FileDialog(None, message='Saving experimental protocol...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE|wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: self.save_file_path = dlg.GetPath() self.save_to_file() def save_as_file_dialogue(self): exp_date = self.get_field('Overview|Project|ExptDate') exp_num = self.get_field('Overview|Project|ExptNum') exp_title = self.get_field('Overview|Project|Title') if self.save_file_path: import ntpath filename = os.path.splitext(ntpath.basename(self.save_file_path))[0] else: filename = 'new_experiment.txt' if None not in [exp_date, exp_num, exp_title]: day, month, year = exp_date.split('/') filename = '%s%s%s_%s_%s.txt'%(year, month, day , exp_num, exp_title) dlg = wx.FileDialog(None, message='Saving experimental protocol...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE|wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: self.save_file_path = dlg.GetPath() self.save_to_file() def save_to_file(self): if self.save_file_path: try: f = open(self.save_file_path, 'w') f.write(VERSION+'\n') for field, value in sorted(self.global_settings.items()): f.write('%s = %s\n'%(field, repr(value))) f.close() except IOError: import wx dial = wx.MessageDialog(None, 'No permission to create temporary experimental file in current directory\nPlease save file in separate directory.', 'Error', wx.OK | wx.ICON_ERROR) if dial.ShowModal() == wx.ID_OK: self.save_as_file_dialogue() def saveData(self, ctrl, tag, settings_controls): if isinstance(ctrl, wx.ListBox) and ctrl.GetStringSelection() == 'Other': other = wx.GetTextFromUser('Insert Other', 'Other') ctrl.Append(other) ctrl.SetStringSelection(other) if len(tag.split('|'))>4: # get the relevant controls for this tag eg, duration, temp controls for this step subtags = [] info = [] for subtag in [t for t, c in settings_controls.items()]: if get_tag_stump(tag, 4) == get_tag_stump(subtag, 4): subtags.append(subtag) for i in range(0, len(subtags)): info.append('') for st in subtags: if isinstance(settings_controls[st], wx.Choice) or isinstance(settings_controls[st], wx.ListBox): info[int(st.split('|')[4])]=settings_controls[st].GetStringSelection() #settings_controls[st].SetToolTipString(settings_controls[st].GetStringSelection()) else: info[int(st.split('|')[4])]=settings_controls[st].GetValue() #settings_controls[st].SetToolTipString(settings_controls[st].GetValue()) self.set_field(get_tag_stump(tag, 4), info) # get the core tag like AddProcess|Spin|Step|<instance> = [duration, description, temp] else: if isinstance(ctrl, wx.Choice) or isinstance(ctrl, wx.ListBox): self.set_field(tag, ctrl.GetStringSelection()) #ctrl.SetToolTipString(ctrl.GetStringSelection()) elif isinstance(ctrl, wx.DatePickerCtrl): date = ctrl.GetValue() self.set_field(tag, '%02d/%02d/%4d'%(date.Day, date.Month+1, date.Year)) else: user_input = ctrl.GetValue() self.set_field(tag, user_input) #ctrl.SetToolTipString(ctrl.GetValue()) def saving_settings(self, protocol, tag, m_tags): import wx if not self.get_field(tag): dial = wx.MessageDialog(None, 'Please provide a settings/protocol name', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return if self.checkMandatoryTags(m_tags): filename = self.get_field(tag)+'.txt' dlg = wx.FileDialog(None, message='Saving ...', defaultDir=os.getcwd(), defaultFile=filename, wildcard='.txt', style=wx.SAVE|wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() == wx.ID_OK: dirname=dlg.GetDirectory() filename=dlg.GetFilename() file_path = os.path.join(dirname, filename) self.save_settings(file_path, protocol) def save_settings(self, file, protocol): ''' saves settings in text file. the settings may include instrument settings, supp protocol, stock flask Format: attr = value where value may be text, int, list, etc. ''' instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) setting_type = get_tag_event(protocol) f = open(file,'w') f.write(setting_type+'\n') attributes = list(set(self.get_attribute_list_by_instance(tag_stump, instance))) if 'Wells' in attributes: attributes.remove('Wells') for attr in attributes: value = self.get_field(tag_stump+'|%s|%s' %(attr, instance)) if attr.endswith('Instance'): f.write('Dependency%s = %s\n'%(attr, repr(value))) # to get the ...Instance which will be overwritten for d_attr, d_val in self.get_attribute_dict('%s|%s' %(DEPENDENCY[get_tag_event(protocol)], value)).iteritems(): f.write('Dependency%s = %s\n'%(d_attr, repr(d_val))) else: f.write('%s = %s\n'%(attr, repr(value))) f.close() def load_from_file(self, file, menuitem): # Populate the tag structure self.clear() f = open(file, 'r') lines = [line.strip() for line in f] if not lines.pop(0).startswith('ProtocolNavigator'): import wx dial = wx.MessageDialog(None, 'Selected file is not a ProtocolNavigator file', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: tag, value = line.split('=', 1) tag = tag.strip() self.set_field(tag, eval(value), notify_subscribers=False) f.close() # Disable the open file menu menuitem.Enable(False) # index the file path self.save_file_path = file #import ntpath #self.temp_file_path = os.path.dirname(os.path.abspath(file))+os.path.splitext(ntpath.basename(self.save_file_path))[0]+'_temp.txt' # Populate PlateDesign PlateDesign.clear() for vessel_type in ('Plate', 'Flask', 'Dish', 'Coverslip', 'Tube'): prefix = 'ExptVessel|%s'%(vessel_type) for inst in self.get_field_instances(prefix): d = self.get_attribute_dict(prefix+'|'+inst) shape = d.get('Design', None) if shape is None: shape = (1,1) group = d.get('StackName', None) PlateDesign.add_plate(vessel_type, inst, shape, group) # Update everything for tag in self.global_settings: self.notify_subscribers(tag) # Update the bench time-slider # TODO: this is crappy try: import wx bench = wx.GetApp().get_bench() bench.set_time_interval(0, self.get_timeline().get_max_timepoint()) except:return def load_supp_protocol_file(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) lines = [line.strip() for line in open(file)] if not lines: import wx dial = wx.MessageDialog(None, 'Sub-process file is empty!!', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return for line in lines: #line.rstrip('\n') line_info = line.split('|') attr = line_info.pop(0) if len(line_info)>1: self.set_field(tag_stump+'|%s|%s'%(attr, instance), line_info) else: self.set_field(tag_stump+'|%s|%s'%(attr, instance), line_info[0]) def load_settings(self, file, protocol): instance = get_tag_attribute(protocol) tag_stump = get_tag_stump(protocol, 2) f = open(file, 'r') lines = [line.strip() for line in f] event = lines.pop(0) # takes out the first line or the header where all setting type microscope/spin etc are written attributes = {} for line in lines: attr, value = line.split('=', 1) attributes[attr.strip()] = value # set all dependency attributes e.g. Centifugation depends on Centrifuge dependecny_attrs = [s for s in attributes if 'Dependency' in s] if dependecny_attrs: d_inst = self.get_new_protocol_id(DEPENDENCY[event]) for d_attr in dependecny_attrs: if d_attr.endswith('Instance'): tag = tag_stump+'|%s|%s'%(d_attr.split('Dependency')[1], instance) self.set_field(tag, d_inst, notify_subscribers=False) self.notify_subscribers(tag) del attributes[d_attr] else: tag = DEPENDENCY[event]+'|%s|%s'%(d_attr.split('Dependency')[1], d_inst) self.set_field(tag, eval(attributes[d_attr]), notify_subscribers=False) self.notify_subscribers(tag) del attributes[d_attr] # set rest of the attributes for attr in attributes: tag = tag_stump+'|%s|%s'%(attr, instance) self.set_field(tag, eval(attributes[attr]), notify_subscribers=False) self.notify_subscribers(tag) f.close() #for line in lines: #attr, value = line.split('=', 1) #attr = attr.strip() #tag = tag_stump+'|%s|%s'%(attr, instance) #self.set_field(tag, eval(value), notify_subscribers=False) #self.notify_subscribers(tag) #f.close() def add_subscriber(self, callback, match_string): '''callback -- the function to be called match_string -- a regular expression string matching the tags you want to be notified of changes to ''' self.subscribers[match_string] = self.subscribers.get(match_string, []) + [callback] def remove_subscriber(self, callback): '''unsubscribe the given callback function. This MUST be called before a callback function is deleted. ''' for k, v in self.subscribers: if v == callback: self.subscribers.pop(k) def notify_subscribers(self, tag): for matchstring, callbacks in self.subscribers.items(): if re.match(matchstring, tag): #self.save_to_temp_file() # update info to the temp file for callback in callbacks: callback(tag) def getNM(self, nm): return int(nm.split('-')[0]), int(nm.split('-')[1]) def belongsTo(self, value, rangeStart, rangeEnd): if value >= rangeStart and value <= rangeEnd: return True return False def partition(self, lst, n): division = len(lst) / float(n) rlist = [lst[int(round(division * i)): int(round(division * (i + 1)))] [-1] for i in xrange(n) ] rlist.insert(0, lst[0]) return rlist def stringSplitByNumbers(self, x): r = re.compile('(\d+)') l = r.split(x) return [int(y) if y.isdigit() else y for y in l] def nmToRGB(self, w): # colour if w >= 380 and w < 440: R = -(w - 440.) / (440. - 350.) G = 0.0 B = 1.0 elif w >= 440 and w < 490: R = 0.0 G = (w - 440.) / (490. - 440.) B = 1.0 elif w >= 490 and w < 510: R = 0.0 G = 1.0 B = -(w - 510.) / (510. - 490.) elif w >= 510 and w < 580: R = (w - 510.) / (580. - 510.) G = 1.0 B = 0.0 elif w >= 580 and w < 645: R = 1.0 G = -(w - 645.) / (645. - 580.) B = 0.0 elif w >= 645 and w <= 780: R = 1.0 G = 0.0 B = 0.0 else: R = 0.0 G = 0.0 B = 0.0 # intensity correction if w >= 380 and w < 420: SSS = 0.3 + 0.7*(w - 350) / (420 - 350) elif w >= 420 and w <= 700: SSS = 1.0 elif w > 700 and w <= 780: SSS = 0.3 + 0.7*(780 - w) / (780 - 700) else: SSS = 0.0 SSS *= 255 return [int(SSS*R), int(SSS*G), int(SSS*B)] def decode_ch_component(self, component): '''this method decofify the components of the light path for a given channel 'LSR488' --> Laser 488nm, 'DMR567LP' ---> Long pass Dichroic Mirror 567nm etc.. ''' description = '' if component.startswith('LSR'): nm = re.sub('\D', '', component) description = nm+' nm Excitation laser ' if component.startswith('DMR'): nm = re.sub('\D', '', component) if component.endswith('LP'): description = nm+' nm Long Pass Dichroic Mirror' if component.endswith('SP'): description = nm+' nm Short Pass Dichroic Mirror' if component.startswith('FLT'): if component.endswith('LP'): description = re.sub('\D', '', component)+' nm Long Pass Filter' if component.endswith('SP'): description = re.sub('\D', '', component)+' nm Short Pass Filter' if component.endswith('BP'): description = (component.split('FLT')[1]).split('BP')[0]+' nm Band pass Filter' # this needs to be adjusted if component.startswith('SLT'): ratio = component.split('SLT')[1] description = re.sub('/', ':', ratio)+' Beam Splitter' if component.startswith('DYE'): dye = component.split('_')[1] description = 'Dye used: %s' %dye if component.startswith('DTC'): volt = re.sub('\D', '', component) description = 'PMT voltage %s volts' %volt return description def setDyeList(self, emLow, emHgh): '''This method sets the list of dye for a given spectrum range''' dyeList = [] for dye in FLUOR_SPECTRUM: dyeLowNM, dyeHghNM = self.getNM(FLUOR_SPECTRUM[dye][1]) for wl in range(emLow, emHgh+1): if wl in range(dyeLowNM, dyeHghNM+1): dyeList.append(dye) #self.dyeListBox.Clear() return sorted(list(set(dyeList))) def get_seeded_sample(self, platewell_id): '''this method returns sample or cell line information for the selected well ''' timeline = self.get_timeline() timepoints = timeline.get_unique_timepoints() events_by_timepoint = timeline.get_events_by_timepoint() seeding_instances = [] for i, timepoint in enumerate(timepoints): for ev in events_by_timepoint[timepoint]: for well_id in ev.get_well_ids(): if well_id == platewell_id and ev.get_welltag().startswith('Transfer|Seed'): seeding_instances.append(ev.get_welltag()) return seeding_instances #---------------------------------------------------------------------- def get_sampleInstance(self, seed_tag): '''This method returns the stock culutre or sample instance for a given seeding tag Transfer|Seed|Wells|<instance> ''' instance = get_tag_instance(seed_tag) # if seed from stock culture if self.global_settings.has_key('Transfer|Seed|CellLineInstance|%s'%instance): return self.get_field('Transfer|Seed|CellLineInstance|%s'%instance) elif self.global_settings.has_key('Transfer|Seed|HarvestInstance|%s'%instance): return self.get_field('Transfer|Harvest|CellLineInstance|%s' %str(self.get_field('Transfer|Seed|HarvestInstance|%s'%instance))) #---------------------------------------------------------------------- def getStateRGB(self, trackTags): """This method returns the colour of the node given the history of the ancestor nodes events""" currRGB = (255, 255, 255, 100) for tag in trackTags: event = get_tag_event(tag) if event.startswith('Notes') or event.startswith('DataAcquis'): # since these are measurements or notes continue currRGB = (int((currRGB[0]+EVENT_RGB[event][0])/2), int((currRGB[1]+EVENT_RGB[event][1])/2), int((currRGB[2]+EVENT_RGB[event][2])/2), 100) return currRGB #---------------------------------------------------------------------- def getEventRGB(self, tag): """get all event tags for the passed node and returns the colour associated with the last event** Need to change**""" #currRGB = (255, 255, 255, 100) #if nodeTags: #tag = nodeTags.pop() event = get_tag_event(tag) if not event.startswith('Notes') or not event.startswith('DataAcquis'): # since these are measurements or notes return (EVENT_RGB[event][0], EVENT_RGB[event][1], EVENT_RGB[event][2], 100) else: return (255, 255, 255, 100) #---------------------------------------------------------------------- def getEventIcon(self, icon_size, act): """get the associated icon for the given action/event""" if act == 'Seed': icon = icons.seed.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act == 'CellLine': icon = icons.stock.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Harvest': icon = icons.harvest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Chemical': icon = icons.treat.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Biological': icon = icons.dna.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Physical': icon = icons.physical.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Dye': icon = icons.stain.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Immuno': icon = icons.antibody.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Genetic': icon = icons.primer.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Centrifugation': icon = icons.spin.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Wash': icon = icons.wash.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Drying': icon = icons.drying.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Medium': icon = icons.medium.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Initiation': icon = icons.initiation.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Storage': icon = icons.storage.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='Incubation': icon = icons.incubator.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='RheoManipulation': icon = icons.rheometer.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='HCS': icon = icons.hcs.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='FCS': icon = icons.fcs.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='TLM': icon = icons.tlm.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() elif act =='RHE': icon = icons.rhe.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Hint': #icon = icons.hint.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Text': #icon = icons.critical.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='Rest': #icon = icons.rest.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='URL': #icon = icons.url.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() #elif act =='MultiMedia': #icon = icons.video.Scale(icon_size, icon_size, quality=wx.IMAGE_QUALITY_HIGH).ConvertToBitmap() return icon #---------------------------------------------------------------------- def get_Row_Numbers(self, protocol, token): """This method returs TAGS with similar elements eg. 'AddProcess|Rheology|Gas1|1' 'AddProcess|Rheology|Gas2|1' etc""" tag_stump = get_tag_stump(protocol, 2) instance = get_tag_attribute(protocol) return sorted(self.get_attribute_list_by_instance(tag_stump+'|%s'%token, instance), key = self.stringSplitByNumbers) #---------------------------------------------------------------------- def setLabelColour(self, tags, labels): """Change mandatory label colour Red-->Green when filled""" for tag in tags: if self.get_field(tag): labels[tag].SetForegroundColour(('#006600')) labels[tag].Refresh() else: labels[tag].SetForegroundColour(wx.RED) labels[tag].Refresh() #---------------------------------------------------------------------- def checkMandatoryTags(self, tags): """Checks whether the mandatory fields/tags being filled""" for tag in tags: if not self.get_field(tag): import wx dial = wx.MessageDialog(None, 'Please fill %s mandatory field' %get_tag_attribute(tag), 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() return return True #---------------------------------------------------------------------- def alphanumeric_sort(self, lst): """sort alphanumeric strings in a list e.g. [Plate1, Plate11, Tube2]""" re_natural = re.compile('[0-9]+|[^0-9]+') return [(1, int(c)) if c.isdigit() else (0, c.lower()) for c in re_natural.findall(lst)] + [lst] #---------------------------------------------------------------------- def last_seed_instance(self, instance): """finds the last seeding instance that is associated with a harvest instance""" for inst in list(reversed(range(int(instance)))): if 'Transfer|Seed|HarvestInstance|%s'%str(inst) is not None: return str(inst) #---------------------------------------------------------------------- def get_cellLine_Name(self, inst, mode): """returns cell Line name given the seeding/harvesting (mode) instance that is realted with a harvest instance if mode is None, it refers to Harvest_Seed type of seeding instance""" if mode is 'H': return self.get_field('Sample|CellLine|Name|%s' %str(self.get_field('Transfer|Harvest|CellLineInstance|%s'%str(inst)))) if mode is 'S': return self.get_field('Sample|CellLine|Name|%s' %str(self.get_field('Transfer|Seed|CellLineInstance|%s'%str(inst)))) if mode is 'HS': return self.get_field('Sample|CellLine|Name|%s' %str(self.get_field('Transfer|Harvest|CellLineInstance|%s' %str(self.get_field('Transfer|Seed|HarvestInstance|%s' %str(inst))))))