def _build_one_answer(self, da_metadata, answer, follow_order=False):
        '''Build a full answer to a system act.

        Args:
            da_metadata: a dict contains only description of system act such as slots-values.
            answer: a dic full description of answer act.
            follow_order: a boolean specifying the first return act need to be satisfied or cancel completely the answer.

        Returns:
            A list of DialogueActItem object.
        '''
        #print answer
        da_items = []
        new_items = []
        first_act = True
        for act_out in answer['return_acts']:#for reply without ordered answer
            #New for repeat
            if act_out == 'repeat':
                da_items.extend(self.last_user_da)#add previous act, may add more if we use inform in return_acts
                continue
            
            answer_types = get_dict_value(answer, act_out + '_answer_types')
            answer_type = None
            if answer_types is not None:
                answer_type = sample_from_dict(answer_types)
            overridden_properties = get_dict_value(answer, act_out + '_overridden_properties')
            #da_items.extend(self._build_dialogue_act_items(da_metadata, act_out, answer_type, overridden_properties))
            new_items = self._build_dialogue_act_items(da_metadata, act_out, answer_type, overridden_properties)
            da_items.extend(new_items)

            if follow_order and 'all_act_valid' in answer.keys() and answer['all_act_valid']==True:#this case of this answer requires all acts must appliabl
                if len(new_items)==0:
                    da_items = []#cancel all other da item already build, that will move to next case of anser
                    break 
            if first_act and follow_order:#if the first action in a return actions which follows the order not successful, that case not satisfy, give up
                first_act=False
                if len(da_items)==0:
                    break
        return da_items
    def _get_random_goal(self):
        '''Sample a final goal of user.

        Returns:
            A dict represent slots and their values sampled.
        '''
        self.goal_id = sample_from_dict(self._goal_dist)
        goal_des = self.metadata['goals'][self.goal_id]
        goal = {}
        self.goal = goal
        sampled_slots = []

        for s, v in goal_des['fixed_slots']:#fixed slots
            goal[s] = v
            sampled_slots.append(s)

        for key in goal_des['same_table_slot_keys']:#same table slots NOTE same_table_slot was coded without testing since data hadn't faked yet
            slots_values = self._get_random_same_table_slots_values(key)
            for slot in slots_values.keys():
                if slot in goal_des['changeable_slots']:
                    sampled_slots.append(slot)
                    goal[slot] = slots_values[slot]

        for one_set in goal_des['one_of_slot_set']:#get value for  one slot set and ignore other slots
            slot_set = sample_from_dict(one_set)
            for slot in slot_set:
                goal[slot] = self._get_random_slot_value(slot)
            for key in one_set.keys():
                sampled_slots.extend(key)

        sampled_slots.extend(goal_des['sys_unaskable_slots'])
        remain_slots = matlab.subtract(goal_des['changeable_slots'], sampled_slots)
        for slot in remain_slots:#changeable slots
            goal[slot] = self._get_random_slot_value(slot)

        '''Don't fill default slots, only return when system ask
        for slot, value in goal_des['default_slots_values']:#fill the default slot which was not being filled
            if slot not in goal.keys():
                goal[slot] = value
        '''
    
        fun = get_dict_value(goal_des,'goal_post_process_fun')
        if fun is not None:
            #goal = fun(self, goal)
            goal = fun(goal)

        #for debug:
        #self.goal_id = 0
        #goal = {'to_city': u'Allerton', 'task': 'find_connection', 'from_city': u'North Massapequa',}
        return goal
 def end_dialogue(self):
     '''end dialgoue and post-processing'''
     fun = get_dict_value(self.metadata['goals'][self.goal_id], 'end_dialogue_post_process_fun')
     if fun is not None:
         fun(self)
    def _get_combined_slots(self, act_in, act_out_des, answer_type, used_slots):
        '''Find combineable slots for 

        Args:
            act_in: A dict presenting the metadata of system act.
            act_out_des: A dict describing the setting of answer act.
            answer_type: A string showing the type of answer such as direct_answer, over_answer
            used_slots: A list of slots which the act already used in previous turns.
        Returns:
            A list of combineable slots.

        Raises:
            NotImplementedError:    The source providing slots for the action was not implemented.
                                    Or the answer_type used was not implemented.
        '''
        #print 'get_combined_slot, act_in=', act_in, ' act_out_des=', act_out_des
        lst = []

        remain_slots = self.goal.keys()

        if 'combineable_slots' in act_out_des.keys():#figured out list of combineable slot in the config file, but still have to filter at status slot
            lst.extend(act_out_des['combineable_slots'])
            #return act_out_des['combineable_slots']
            remain_slots = act_out_des['combineable_slots']

        #print '--combined slots1', lst
        if 'accept_used_slots' in act_out_des.keys() and act_out_des['accept_used_slots']==False:#filter used slot
            remain_slots = matlab.subtract(remain_slots, used_slots)

        #print '--combined slots2', lst
        if 'slot_from' in act_out_des.keys():#take all slot in the type figured in slot_from
            if act_out_des['slot_from']=='sys_da':
                lst.extend(act_in['slots'])
            elif act_out_des['slot_from']=='none':
                pass#dont take slot from sys_da
            elif act_out_des['slot_from']=='goal':
                lst.extend(self.goal.keys())
            else:
                raise NotImplementedError('slot_from=%s unhandled yet'%act_out_des['slot_from'])
            
        #print '--combined slots3', lst
        #process answer_type
        if answer_type=='direct_answer':
            pass#every slot in sys_da already included
        elif answer_type=='over_answer':
            #only over or complete answer for slot not mentioned
            remain_slots = matlab.subtract(remain_slots, lst)
            lst.extend(random_filter_list(remain_slots))
        elif answer_type=='complete_answer':
            remain_slots = matlab.subtract(remain_slots, lst)
            lst.extend(remain_slots)
        elif answer_type is None:
            pass
        else:
            raise NotImplementedError('answer_type=%s unhandled yet'%answer_type)            


        #print '--combined slots4', lst
        #process litmited slots
        if 'limited_slots' in act_out_des.keys():
            limits = act_out_des['limited_slots']
            limits = matlab.subtract(limits, act_in['slots'])#limited slots will be used when system ask
            #lst = matlab.subtract(lst, act_out_des['limited_slots'])
            lst = matlab.subtract(lst, limits)

        #process status included
        if 'status_included' in act_out_des.keys():
            status_included = act_out_des['status_included']
            lst = self._filter_slot_status(act_in, lst, status_included)
        
        #print '--combined slots5', lst
        # the act required all its slot need to be have the same status
        if 'status_in_all_slots' in act_out_des.keys() and act_out_des['status_in_all_slots']:
            if len(lst)!= len(act_in['slots']):#TODO this way of testing may give trouble in some cases in future, but currently it works.
                lst = []#this action require all of requested slot must satisfy the given status

        #add atlesat slots
        if 'atleast_slots' in act_out_des.keys():
            for slot in act_out_des['atleast_slots']:
                if slot not in lst:
                    lst.append(slot)

        #process slot_used_sequence
        goal_des = self.metadata['goals'][self.goal_id]
        if 'slot_used_sequence' in goal_des.keys():
            use_sequence = get_dict_value(act_out_des, 'use_slot_sequence')
            if use_sequence is not None and use_sequence:#default dont using slot sequence 
                lst = self._filter_slot_used_sequence(goal_des['slot_used_sequence'], lst)

        #print '--combined slots', lst
        return lst