def load_dyn_properties(): global dyn_properties logger.info('Loading location & weather properties') location = get_location() if location: dyn_properties.update(location) city = location.get('city') neighborhood = location.get('neighborhood') base_location = '' if city: base_location = city elif 'country_name' in location: base_location = location.get('country_name') elif 'country' in location: base_location = location.get('country') if base_location: if neighborhood: location_str = '%s, %s' % (neighborhood, base_location) else: location_str = base_location dyn_properties['location'] = location_str weather_prop = None if location: weather = get_weather('{city}'.format(city=location['city'])) weather_prop = parse_weather(weather) if weather_prop: dyn_properties.update(weather_prop) if dyn_properties: logger.info("Dynamic properties {}".format(dyn_properties)) else: logger.warn("No dynamic properties")
def load_dyn_properties(): global dyn_properties location = get_location() if location: if 'city' in location: dyn_properties['location'] = location.get('city') elif 'country_name' in location: dyn_properties['location'] = location.get('country_name') weather_prop = None if location: weather = get_weather('{city},{country}'.format( city=location['city'], country=location['country_code'])) weather_prop = parse_weather(weather) if weather_prop: dyn_properties.update(weather_prop) logger.info("Update dynamic properties {}".format(dyn_properties))
def load_dyn_properties(): global dyn_properties location = get_location() if location: if 'city' in location: dyn_properties['location'] = location.get('city') elif 'country_name' in location: dyn_properties['location'] = location.get('country_name') weather_prop = None if location: weather = get_weather( '{city},{country}'.format( city=location['city'], country=location['country_code'])) weather_prop = parse_weather(weather) if weather_prop: dyn_properties.update(weather_prop) logger.info("Update dynamic properties {}".format(dyn_properties))
def render_weather(t): if hasattr(t.module, 'location'): location = t.module.location else: location = get_location() if location: if 'city' in location: location = location.get('city') city = query_city_info(location) if 'id' in city: id = city['id'] weather = get_weather_by_id(id) weather_prop = parse_weather(weather) desc = weather_prop['weather'] temperature = '{} degrees'.format(weather_prop['temperature']) return t.render(weatherdesc=desc, temperature=temperature, location=city)
def _ask_characters(characters, question, lang, sid, query, request_id, **kwargs): sess = session_manager.get_session(sid) if sess is None: return used_charaters = [] data = sess.get_session_data() user = getattr(data, 'user') botname = getattr(data, 'botname') weights = get_weights(characters, sess) weighted_characters = zip(characters, weights) logger.info("Weights {}".format(weights)) _question = preprocessing(question, lang, sess) response = {} hit_character = None answer = None cross_trace = [] cached_responses = defaultdict(list) control = get_character('control') if control is not None: _response = control.respond(_question, lang, sess, query, request_id) _answer = _response.get('text') if _answer == '[tell me more]': cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) if sess.last_used_character: if sess.cache.that_question is None: sess.cache.that_question = sess.cache.last_question context = sess.last_used_character.get_context(sess) if 'continue' in context and context.get('continue'): _answer, res = shorten(context.get('continue'), 140) response['text'] = answer = _answer response['botid'] = sess.last_used_character.id response['botname'] = sess.last_used_character.name sess.last_used_character.set_context( sess, {'continue': res}) hit_character = sess.last_used_character cross_trace.append((sess.last_used_character.id, 'continuation', 'Non-empty')) else: _question = sess.cache.that_question.lower().strip() cross_trace.append( (sess.last_used_character.id, 'continuation', 'Empty')) elif _answer.startswith('[weather]'): template = _answer.replace('[weather]', '') cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: location = context.get('querylocation') prop = parse_weather(get_weather(location)) if prop: try: _answer = template.format(location=location, **prop) if _answer: answer = _answer response['text'] = _answer response['botid'] = control.id response['botname'] = control.name except Exception as ex: cross_trace.append( (control.id, 'control', 'No answer')) logger.error(ex) logger.error(traceback.format_exc()) else: cross_trace.append((control.id, 'control', 'No answer')) elif _answer in OPERATOR_MAP.keys(): opt = OPERATOR_MAP[_answer] cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: item1 = context.get('item1') item2 = context.get('item2') item1 = words2num(item1) item2 = words2num(item2) if item1 is not None and item2 is not None: try: result = opt(item1, item2) img = math.modf(result)[0] if img < 1e-6: result_str = '{:d}'.format(int(result)) else: result_str = 'about {:.4f}'.format(result) if result > 1e20: answer = "The number is too big. You should use a calculator." else: answer = "The answer is {result}".format( result=result_str) except ZeroDivisionError: answer = "Oh, the answer is not a number" except Exception as ex: logger.error(ex) logger.error(traceback.format_exc()) answer = "Sorry, something goes wrong. I can't calculate it." response['text'] = answer response['botid'] = control.id response['botname'] = control.name else: cross_trace.append((control.id, 'control', 'No answer')) else: if _answer and not re.findall(r'\[.*\].*', _answer): cross_trace.append( (control.id, 'control', _response.get('trace') or 'No trace')) hit_character = control answer = _answer response = _response else: cross_trace.append((control.id, 'control', 'No answer')) for c in characters: try: c.remove_context(sess, 'continue') except NotImplementedError: pass sess.cache.that_question = None def _ask_character(stage, character, weight, good_match=False, reuse=False): logger.info("Asking character {} \"{}\" in stage {}".format( character.id, _question, stage)) if not reuse and character.id in used_charaters: cross_trace.append((character.id, stage, 'Skip used tier')) return False, None, None if character.id in used_charaters and character.type == TYPE_CS: cross_trace.append((character.id, stage, 'Skip CS tier')) return False, None, None used_charaters.append(character.id) answer = None answered = False if weight == 0: cross_trace.append((character.id, stage, 'Disabled')) logger.warn("Character \"{}\" in stage {} is disabled".format( character.id, stage)) return False, None, None response = character.respond(_question, lang, sess, query, request_id) answer = str_cleanup(response.get('text', '')) trace = response.get('trace') if answer: if 'pickup' in character.id: cached_responses['pickup'].append( (response, answer, character)) return False, None, None if good_match: if response.get('exact_match') or response.get('ok_match'): logger.info("{} has good match".format(character.id)) answered = True else: if not response.get('bad'): logger.info("{} has no good match".format( character.id)) cross_trace.append( (character.id, stage, 'No good match. Answer: {}, Trace: {}'.format( answer, trace))) cached_responses['nogoodmatch'].append( (response, answer, character)) elif response.get('bad'): cross_trace.append( (character.id, stage, 'Bad answer. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['bad'].append((response, answer, character)) elif DISABLE_QUIBBLE and response.get('quibble'): cross_trace.append( (character.id, stage, 'Quibble answer. Answer: {}, Trace: {}'.format( answer, trace))) cached_responses['quibble'].append( (response, answer, character)) elif response.get('gambit'): if random.random() > 0.3: cross_trace.append( (character.id, stage, 'Ignore gambit answer. Answer: {}, Trace: {}'.format( answer, trace))) cached_responses['gambit'].append( (response, answer, character)) else: answered = True else: answered = True if answered: if random.random() < weight: cross_trace.append( (character.id, stage, 'Trace: {}'.format(trace))) else: answered = False cross_trace.append( (character.id, stage, 'Pass through. Answer: {}, Weight: {}, Trace: {}'. format(answer, weight, trace))) logger.info("{} has no answer".format(character.id)) if 'markov' not in character.id: cached_responses['pass'].append( (response, answer, character)) else: cached_responses['?'].append( (response, answer, character)) else: if response.get('repeat'): answer = response.get('repeat') cross_trace.append( (character.id, stage, 'Repetitive answer. Answer: {}, Trace: {}'.format( answer, trace))) cached_responses['repeat'].append( (response, answer, character)) else: logger.info("{} has no answer".format(character.id)) cross_trace.append((character.id, stage, 'No answer. Trace: {}'.format(trace))) return answered, answer, response # If the last input is a question, then try to use the same tier to # answer it. if not answer: if sess.open_character in characters: answered, _answer, _response = _ask_character('question', sess.open_character, 1, good_match=True) if answered: hit_character = sess.open_character answer = _answer response = _response # Try the first tier to see if there is good match if not answer: c, weight = weighted_characters[0] answered, _answer, _response = _ask_character('priority', c, weight, good_match=True) if answered: hit_character = c answer = _answer response = _response # Select tier that is designed to be proper to answer the question if not answer: for c, weight in weighted_characters: if c.is_favorite(_question): answered, _answer, _response = _ask_character('favorite', c, 1) if answered: hit_character = c answer = _answer response = _response # Check the last used character if not answer: if sess.last_used_character and sess.last_used_character.dynamic_level: for c, weight in weighted_characters: if sess.last_used_character.id == c.id: answered, _answer, _response = _ask_character( 'last used', c, weight) if answered: hit_character = c answer = _answer response = _response break # Check the loop if not answer: for c, weight in weighted_characters: answered, _answer, _response = _ask_character('loop', c, weight, reuse=True) if answered: hit_character = c answer = _answer response = _response break if not answer: for response_type in [ 'pass', 'nogoodmatch', 'quibble', 'repeat', 'gambit', 'pickup', '?' ]: if cached_responses.get(response_type): response, answer, hit_character = cached_responses.get( response_type)[0] if response_type == 'repeat': pass response['text'] = answer cross_trace.append( (hit_character.id, response_type, response.get('trace') or 'No trace')) break if answer and re.match('.*{.*}.*', answer): logger.info("Template answer {}".format(answer)) try: response['orig_text'] = answer answer = render(answer) response['text'] = answer except Exception as ex: answer = '' response['text'] = '' logger.error("Error in rendering template, {}".format(ex)) dummy_character = get_character('dummy', lang) if not answer and dummy_character: if response.get('repeat'): response = dummy_character.respond("REPEAT_ANSWER", lang, sid, query) else: response = dummy_character.respond("NO_ANSWER", lang, sid, query) hit_character = dummy_character answer = str_cleanup(response.get('text', '')) if not query and hit_character is not None: response['AnsweredBy'] = hit_character.id sess.last_used_character = hit_character if is_question(answer.lower().strip()): if hit_character.dynamic_level: sess.open_character = hit_character logger.info("Set open dialog character {}".format( hit_character.id)) else: sess.open_character = None response['ModQuestion'] = _question response['trace'] = cross_trace return response
def _ask_characters(characters, question, lang, sid, query): sess = session_manager.get_session(sid) if sess is None: return used_charaters = [] data = sess.get_session_data() user = getattr(data, 'user') botname = getattr(data, 'botname') weights = get_weights(characters, sess) weighted_characters = zip(characters, weights) logger.info("Weights {}".format(weighted_characters)) _question = preprocessing(question) response = {} hit_character = None answer = None cross_trace = [] cached_responses = defaultdict(list) reduction = get_character('reduction') if reduction is not None: _response = reduction.respond(_question, lang, sess, query=True) reducted_text = _response.get('text') if reducted_text: _question = reducted_text control = get_character('control') if control is not None: _response = control.respond(_question, lang, sess, query) _answer = _response.get('text') if _answer == '[tell me more]': cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) if sess.last_used_character: if sess.cache.that_question is None: sess.cache.that_question = sess.cache.last_question context = sess.last_used_character.get_context(sess) if 'continue' in context and context.get('continue'): _answer, res = shorten(context.get('continue'), 140) response['text'] = answer = _answer response['botid'] = sess.last_used_character.id response['botname'] = sess.last_used_character.name sess.last_used_character.set_context(sess, {'continue': res}) hit_character = sess.last_used_character cross_trace.append((sess.last_used_character.id, 'continuation', 'Non-empty')) else: _question = sess.cache.that_question.lower().strip() cross_trace.append((sess.last_used_character.id, 'continuation', 'Empty')) elif _answer.startswith('[weather]'): template = _answer.replace('[weather]', '') cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: location = context.get('querylocation') prop = parse_weather(get_weather(location)) if prop: try: _answer = template.format(location=location, **prop) if _answer: answer = _answer response['text'] = _answer response['botid'] = control.id response['botname'] = control.name except Exception as ex: cross_trace.append((control.id, 'control', 'No answer')) logger.error(ex) else: cross_trace.append((control.id, 'control', 'No answer')) elif _answer in OPERATOR_MAP.keys(): opt = OPERATOR_MAP[_answer] cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: item1 = context.get('item1') item2 = context.get('item2') item1 = words2num(item1) item2 = words2num(item2) if item1 is not None and item2 is not None: try: result = opt(item1, item2) img = math.modf(result)[0] if img < 1e-6: result_str = '{:d}'.format(int(result)) else: result_str = 'about {:.4f}'.format(result) if result > 1e20: answer = "The number is too big. You should use a calculator." else: answer = "The answer is {result}".format(result=result_str) except ZeroDivisionError: answer = "Oh, the answer is not a number" except Exception as ex: logger.error(ex) answer = "Sorry, something goes wrong. I can't calculate it." response['text'] = answer response['botid'] = control.id response['botname'] = control.name else: cross_trace.append((control.id, 'control', 'No answer')) else: if _answer and not re.findall(r'\[.*\].*', _answer): cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) hit_character = control answer = _answer response = _response else: cross_trace.append((control.id, 'control', 'No answer')) for c in characters: try: c.remove_context(sess, 'continue') except NotImplementedError: pass sess.cache.that_question = None # If the last input is a question, then try to use the same tier to # answer it. if not answer: if sess.open_character and sess.open_character in characters \ and sess.open_character.weight != 0: logger.info("Using open dialog character {}".format(sess.open_character.id)) response = sess.open_character.respond(_question, lang, sess, query) used_charaters.append(sess.open_character.id) _answer = str_cleanup(response.get('text', '')) if _answer: if response.get('repeat'): cross_trace.append((sess.open_character.id, 'question', 'Repetitive answer')) cached_responses['repeat'].append((response, response.get('repeat'), sess.open_character)) elif response.get('bad'): cross_trace.append((sess.open_character.id, 'question', 'Bad answer: {}'.format(response.get('trace')))) cached_responses['bad'].append((response, _answer, sess.open_character)) else: answer = _answer hit_character = sess.open_character cross_trace.append((sess.open_character.id, 'question', response.get('trace') or 'No trace')) else: cross_trace.append((sess.open_character.id, 'question', 'No answer')) # Try the first tier to see if there is good match if not answer: c, weight = weighted_characters[0] if c.id == botname and weight != 0: _response = c.respond(_question, lang, sess, query=True) _answer = str_cleanup(_response.get('text', '')) if _response.get('exact_match') or _response.get('ok_match'): logger.info("{} has good match".format(c.id)) response = c.respond(_question, lang, sess, query) _answer = str_cleanup(response.get('text', '')) used_charaters.append(c.id) if _answer: if random.random() < weight: hit_character = c answer = _answer cross_trace.append((c.id, 'priority', response.get('trace') or 'No trace')) else: cross_trace.append((c.id, 'priority', 'Pass through')) cached_responses['pass'].append((response, _answer, c)) elif response.get('repeat'): cross_trace.append((c.id, 'priority', 'Repetitive answer')) cached_responses['repeat'].append((response, response.get('repeat'), c)) else: cross_trace.append((c.id, 'priority', 'No answer')) elif _answer: logger.info("{} has no good match".format(c.id)) cross_trace.append((c.id, 'priority', 'No good match: {}'.format(_response.get('trace') or 'No trace'))) cached_responses['nogoodmatch'].append((_response, _answer, c)) else: cross_trace.append((c.id, 'priority', 'No answer')) # Select tier that is designed to be proper to answer the question if not answer: for c, weight in weighted_characters: if weight != 0 and c.is_favorite(_question): _response = c.respond(_question, lang, sess, query) _answer = str_cleanup(_response.get('text')) if _answer: hit_character = c cross_trace.append((c.id, 'favorite', response.get('trace') or 'No trace')) answer = _answer response = _response break else: if _response.get('repeat'): cross_trace.append((c.id, 'favorite', 'Repetitive answer')) cached_responses['repeat'].append((_response, _response.get('repeat'), c)) else: cross_trace.append((c.id, 'favorite', 'No answer')) # Check the last used character if not answer: if sess.last_used_character and sess.last_used_character.dynamic_level \ and sess.last_used_character.weight != 0: if sess.last_used_character.id in used_charaters: cross_trace.append((sess.last_used_character.id, 'last used', 'Skip used tier')) else: for c, weight in weighted_characters: if sess.last_used_character.id == c.id: _response = c.respond(_question, lang, sess, query=True) _answer = str_cleanup(_response.get('text', '')) if _response.get('exact_match') or _response.get('ok_match'): logger.info("Last used tier {} has good match".format(c.id)) if sess.last_used_character.type == TYPE_CS: response = _response else: response = c.respond(_question, lang, sess, query) _answer = str_cleanup(response.get('text', '')) used_charaters.append(c.id) if _answer: if random.random() < weight: hit_character = c answer = _answer cross_trace.append((c.id, 'last used', response.get('trace') or 'No trace')) else: cross_trace.append((c.id, 'last used', 'Pass through')) cached_responses['pass'].append((response, _answer, c)) else: if response.get('repeat'): cross_trace.append((c.id, 'last used', 'Repetitive answer')) cached_responses['repeat'].append((response, response.get('repeat'), c)) else: cross_trace.append((c.id, 'last used', 'No answer')) elif _answer: logger.info("{} has no good match".format(c.id)) cross_trace.append((c.id, 'last used', 'No good match: {}'.format(_response.get('trace') or 'No trace'))) cached_responses['nogoodmatch'].append((_response, _answer, c)) else: cross_trace.append((c.id, 'last used', 'No answer')) break # Check the loop if not answer: for c, weight in weighted_characters: if weight == 0: logger.info("Ignore zero weighted character {}".format(c.id)) continue if c.id in used_charaters: logger.info("Ignore used tiers {}".format(c.id)) cross_trace.append((c.id, 'loop', 'Skip used tier')) continue response = c.respond(_question, lang, sess, query) used_charaters.append(c.id) assert isinstance(response, dict), "Response must be a dict" _answer = str_cleanup(response.get('text', '')) if not _answer: if response.get('repeat'): cross_trace.append((c.id, 'loop', 'Repetitive answer')) cached_responses['repeat'].append((response, response.get('repeat'), c)) else: cross_trace.append((c.id, 'loop', 'No answer')) continue if response.get('bad'): cross_trace.append((c.id, 'loop', 'Bad answer: {}'.format(response.get('trace')))) cached_responses['bad'].append((response, _answer, c)) continue if DISABLE_QUIBBLE and response.get('quibble'): logger.info("Ignore quibbled answer by {}".format(c.id)) cross_trace.append((c.id, 'loop', 'Quibble answer')) cached_responses['quibble'].append((response, _answer, c)) continue if response.get('gambit'): if random.random() > 0.3: cached_responses['gambit'].append((response, _answer, c)) cross_trace.append((c.id, 'loop', 'Ignore gambit answer')) logger.info("Ignore gambit response") continue if 'pickup' in c.id: cached_responses['pickup'].append((response, _answer, c)) # Each tier has weight*100% chance to be selected. # If the chance goes to the last tier, it will be selected anyway. if random.random() < weight: answer = _answer hit_character = c cross_trace.append((c.id, 'loop', response.get('trace') or 'No trace')) break else: cross_trace.append((c.id, 'loop', 'Pass through')) if 'pickup' not in c.id and 'markov' not in c.id: cached_responses['pass'].append((response, _answer, c)) else: cached_responses['?'].append((response, _answer, c)) if not answer: for response_type in ['pass', 'nogoodmatch', 'quibble', 'repeat', 'gambit', 'pickup', 'bad', '?']: if cached_responses.get(response_type): response, answer, hit_character = cached_responses.get(response_type)[0] if response_type == 'repeat': if len(answer) < 80: answer = "Again. " + answer elif 80 < len(answer) < 200: answer = "Let me say again. " + answer else: continue response['text'] = answer cross_trace.append( (hit_character.id, response_type, response.get('trace') or 'No trace')) break dummy_character = get_character('dummy', lang) if not answer and dummy_character: if response.get('repeat'): response = dummy_character.respond("REPEAT_ANSWER", lang, sid, query) else: response = dummy_character.respond("NO_ANSWER", lang, sid, query) hit_character = dummy_character answer = str_cleanup(response.get('text', '')) if not query and hit_character is not None: sess.add(question, answer, AnsweredBy=hit_character.id, User=user, BotName=botname, Trace=cross_trace, Revision=REVISION, Lang=lang, ModQuestion=_question) sess.last_used_character = hit_character if answer.lower().strip().endswith('?'): if hit_character.dynamic_level: sess.open_character = hit_character logger.info("Set open dialog character {}".format( hit_character.id)) else: sess.open_character = None response['trace'] = cross_trace return response
def _ask_characters(characters, question, lang, sid, query, request_id, **kwargs): sess = session_manager.get_session(sid) if sess is None: return used_charaters = [] data = sess.get_session_data() user = getattr(data, 'user') botname = getattr(data, 'botname') weights = get_weights(characters, sess) weighted_characters = zip(characters, weights) logger.info("Weights {}".format(weighted_characters)) _question = preprocessing(question, lang, sess) response = {} hit_character = None answer = None cross_trace = [] cached_responses = defaultdict(list) control = get_character('control') if control is not None: _response = control.respond(_question, lang, sess, query, request_id) _answer = _response.get('text') if _answer == '[tell me more]': cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) if sess.last_used_character: if sess.cache.that_question is None: sess.cache.that_question = sess.cache.last_question context = sess.last_used_character.get_context(sess) if 'continue' in context and context.get('continue'): _answer, res = shorten(context.get('continue'), 140) response['text'] = answer = _answer response['botid'] = sess.last_used_character.id response['botname'] = sess.last_used_character.name sess.last_used_character.set_context(sess, {'continue': res}) hit_character = sess.last_used_character cross_trace.append((sess.last_used_character.id, 'continuation', 'Non-empty')) else: _question = sess.cache.that_question.lower().strip() cross_trace.append((sess.last_used_character.id, 'continuation', 'Empty')) elif _answer.startswith('[weather]'): template = _answer.replace('[weather]', '') cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: location = context.get('querylocation') prop = parse_weather(get_weather(location)) if prop: try: _answer = template.format(location=location, **prop) if _answer: answer = _answer response['text'] = _answer response['botid'] = control.id response['botname'] = control.name except Exception as ex: cross_trace.append((control.id, 'control', 'No answer')) logger.error(ex) else: cross_trace.append((control.id, 'control', 'No answer')) elif _answer in OPERATOR_MAP.keys(): opt = OPERATOR_MAP[_answer] cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) context = control.get_context(sess) if context: item1 = context.get('item1') item2 = context.get('item2') item1 = words2num(item1) item2 = words2num(item2) if item1 is not None and item2 is not None: try: result = opt(item1, item2) img = math.modf(result)[0] if img < 1e-6: result_str = '{:d}'.format(int(result)) else: result_str = 'about {:.4f}'.format(result) if result > 1e20: answer = "The number is too big. You should use a calculator." else: answer = "The answer is {result}".format(result=result_str) except ZeroDivisionError: answer = "Oh, the answer is not a number" except Exception as ex: logger.error(ex) answer = "Sorry, something goes wrong. I can't calculate it." response['text'] = answer response['botid'] = control.id response['botname'] = control.name else: cross_trace.append((control.id, 'control', 'No answer')) else: if _answer and not re.findall(r'\[.*\].*', _answer): cross_trace.append((control.id, 'control', _response.get('trace') or 'No trace')) hit_character = control answer = _answer response = _response else: cross_trace.append((control.id, 'control', 'No answer')) for c in characters: try: c.remove_context(sess, 'continue') except NotImplementedError: pass sess.cache.that_question = None def _ask_character(stage, character, weight, good_match=False, reuse=False): logger.info("Asking character {} in stage {}".format(character.id, stage)) if not reuse and character.id in used_charaters: cross_trace.append((character.id, stage, 'Skip used tier')) return False, None, None if character.id in used_charaters and character.type == TYPE_CS: cross_trace.append((character.id, stage, 'Skip CS tier')) return False, None, None used_charaters.append(character.id) answer = None answered = False if weight == 0: cross_trace.append((character.id, stage, 'Disabled')) return False, None, None response = character.respond(_question, lang, sess, query, request_id) answer = str_cleanup(response.get('text', '')) trace = response.get('trace') if answer: if 'pickup' in character.id: cached_responses['pickup'].append((response, answer, character)) return False, None, None if good_match: if response.get('exact_match') or response.get('ok_match'): logger.info("{} has good match".format(character.id)) answered = True else: logger.info("{} has no good match".format(character.id)) cross_trace.append((character.id, stage, 'No good match. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['nogoodmatch'].append((response, answer, character)) elif response.get('bad'): cross_trace.append((character.id, stage, 'Bad answer. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['bad'].append((response, answer, character)) elif DISABLE_QUIBBLE and response.get('quibble'): cross_trace.append((character.id, stage, 'Quibble answer. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['quibble'].append((response, answer, character)) elif response.get('gambit'): if random.random() > 0.3: cross_trace.append((character.id, stage, 'Ignore gambit answer. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['gambit'].append((response, answer, character)) else: answered = True else: answered = True if answered: if random.random() < weight: cross_trace.append((character.id, stage, 'Trace: {}'.format(trace))) else: answered = False cross_trace.append((character.id, stage, 'Pass through. Answer: {}, Weight: {}, Trace: {}'.format(answer, weight, trace))) logger.info("{} has no answer".format(character.id)) if 'markov' not in character.id: cached_responses['pass'].append((response, answer, character)) else: cached_responses['?'].append((response, answer, character)) else: if response.get('repeat'): answer = response.get('repeat') cross_trace.append((character.id, stage, 'Repetitive answer. Answer: {}, Trace: {}'.format(answer, trace))) cached_responses['repeat'].append((response, answer, character)) else: logger.info("{} has no answer".format(character.id)) cross_trace.append((character.id, stage, 'No answer. Trace: {}'.format(trace))) return answered, answer, response # If the last input is a question, then try to use the same tier to # answer it. if not answer: if sess.open_character in characters: answered, _answer, _response = _ask_character( 'question', sess.open_character, 1, good_match=True) if answered: hit_character = sess.open_character answer = _answer response = _response # Try the first tier to see if there is good match if not answer: c, weight = weighted_characters[0] answered, _answer, _response = _ask_character( 'priority', c, weight, good_match=True) if answered: hit_character = c answer = _answer response = _response # Select tier that is designed to be proper to answer the question if not answer: for c, weight in weighted_characters: if c.is_favorite(_question): answered, _answer, _response = _ask_character( 'favorite', c, 1) if answered: hit_character = c answer = _answer response = _response # Check the last used character if not answer: if sess.last_used_character and sess.last_used_character.dynamic_level: for c, weight in weighted_characters: if sess.last_used_character.id == c.id: answered, _answer, _response = _ask_character( 'last used', c, weight) if answered: hit_character = c answer = _answer response = _response break # Check the loop if not answer: for c, weight in weighted_characters: answered, _answer, _response = _ask_character( 'loop', c, weight, reuse=True) if answered: hit_character = c answer = _answer response = _response break if not answer: for response_type in ['pass', 'nogoodmatch', 'quibble', 'repeat', 'gambit', 'pickup', 'bad', '?']: if cached_responses.get(response_type): response, answer, hit_character = cached_responses.get(response_type)[0] if response_type == 'repeat': if len(answer) < 80: answer = "Again. " + answer elif 80 < len(answer) < 200: answer = "Let me say again. " + answer else: continue response['text'] = answer cross_trace.append( (hit_character.id, response_type, response.get('trace') or 'No trace')) break if answer and re.match('.*{.*}.*', answer): logger.info("Template answer {}".format(answer)) try: response['orig_text'] = answer answer = render(answer) response['text'] = answer except Exception as ex: answer = '' response['text'] = '' logger.error("Error in rendering template, {}".format(ex)) dummy_character = get_character('dummy', lang) if not answer and dummy_character: if response.get('repeat'): response = dummy_character.respond("REPEAT_ANSWER", lang, sid, query) else: response = dummy_character.respond("NO_ANSWER", lang, sid, query) hit_character = dummy_character answer = str_cleanup(response.get('text', '')) if not query and hit_character is not None: sess.add(question, answer, AnsweredBy=hit_character.id, User=user, BotName=botname, Trace=cross_trace, Revision=REVISION, Lang=lang, ModQuestion=_question, RequestId=request_id,Marker=kwargs.get('marker')) sess.last_used_character = hit_character if is_question(answer.lower().strip()): if hit_character.dynamic_level: sess.open_character = hit_character logger.info("Set open dialog character {}".format( hit_character.id)) else: sess.open_character = None response['trace'] = cross_trace return response