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
Пример #3
0
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
Пример #4
0
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     = {}
    #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))))))