def getConfig(section, key):
    config = ConfigParser()
    path = os.path.split(os.path.realpath(__file__))[0] + '/conf/self.conf'
    config.read(path, encoding='UTF-8')
    return config.get(section, key)
 def nlu_tuling(question, loc="上海"):
    """图灵 API 
    """
    url = 'http://www.tuling123.com/openapi/api'
    data = {
        'key': "fd2a2710a7e01001f97dc3a663603fa1",
        'info': question,
        "loc": loc,
        'userid': mac_address
    }
    try:
        r = json.loads(requests.post(url=url, data=data).text)
    except:
        return
    if not r['code'] in (100000, 200000, 302000, 308000, 313000, 314000): return
    if r['code'] == 100000: # 文本类
        return '\n'.join([r['text'].replace('<br>','\n')])
    elif r['code'] == 200000: # 链接类
        return '\n'.join([r['text'].replace('<br>','\n'), r['url']])
    elif r['code'] == 302000: # 新闻类
        l = [r['text'].replace('<br>','\n')]
        for n in r['list']: l.append('%s - %s'%(n['article'], n['detailurl']))
        return '\n'.join(l)
    elif r['code'] == 308000: # 菜谱类
        l = [r['text'].replace('<br>','\n')]
        for n in r['list']: l.append('%s - %s'%(n['name'], n['detailurl']))
        return '\n'.join(l)
    elif r['code'] == 313000: # 儿歌类
        return '\n'.join([r['text'].replace('<br>','\n')])
    elif r['code'] == 314000: # 诗词类
        return '\n'.join([r['text'].replace('<br>','\n')]) def get_location_by_ip(city="上海市"):
    """根据IP获取当前地址
    """
    url = "http://api.map.baidu.com/location/ip"
    data = {
        "ak": "wllxHD5CmWv8qX6CN2lyY73a",
        "coor": "bd09ll"
    }
    try:
        result = requests.post(url, data, timeout=20).text
        location = json.loads(result)["content"]["address"]
        print("当前所在城市：", location)
    except:
        location = city
        print("采用默认城市：", location)
    return location def synonym_cut(sentence, pattern="wf"):
    """Cut the sentence into a synonym vector tag.
    将句子切分为同义词向量标签。

    If a word in this sentence was not found in the synonym dictionary,
    it will be marked with default value of the word segmentation tool.
    如果同义词词典中没有则标注为切词工具默认的词性。

    Args:
        pattern: 'w'-分词, 'k'-唯一关键词，'t'-关键词列表, 'wf'-分词标签, 'tf-关键词标签'。
    """
    # 句尾标点符号过滤
    sentence = sentence.rstrip(''.join(punctuation_all))
    # 句尾语气词过滤
    sentence = sentence.rstrip(tone_words)
    synonym_vector = []
    if pattern == "w":
        synonym_vector = [item for item in jieba.cut(sentence) if item not in filter_characters]
    elif pattern == "k":
        synonym_vector = analyse.extract_tags(sentence, topK=1)
    elif pattern == "t":
        synonym_vector = analyse.extract_tags(sentence, topK=10)
    elif pattern == "wf":
        result = posseg.cut(sentence)
        # synonym_vector = [(item.word, item.flag) for item in result \
        # if item.word not in filter_characters]
        # Modify in 2017.4.27 
        for item in result:
            if item.word not in filter_characters:
                if len(item.flag) < 4:
                    item.flag = list(posseg.cut(item.word))[0].flag
                synonym_vector.append((item.word, item.flag))
    elif pattern == "tf":
        result = posseg.cut(sentence)
        tags = analyse.extract_tags(sentence, topK=10)
        for item in result:
            if item.word in tags:
                synonym_vector.append((item.word, item.flag))
    return synonym_vector
 def segment(sentence):
    """过滤分词。
    """
    # 句尾标点符号过滤
    s = sentence.rstrip(''.join(punctuation_all))
    # 句尾语气词过滤
    s = s.rstrip(tone_words)
    # 句中标点符号过滤
    sv = [word for word in jieba.cut(s) if word not in filter_characters]
    return sv
 def get_tag(sentence, config):
    """Get semantic tag of sentence. 获取句子语义标签。
    """
    iquestion = sentence.format(**config)
    try:
        keywords = analyse.extract_tags(iquestion, topK=1)
        keyword = keywords[0]
    except IndexError:
        keyword = iquestion
    tags = synonym_cut(keyword, 'wf') # tuple list
    if tags:
        tag = tags[0][1]
        if not tag:
            tag = keyword
    else:
        tag = keyword
    return tag
 def similarity(synonym_vector1, synonym_vector2, pattern='j'):
    """Similarity score between two sentences.
    两个向量的相似度得分。

    Args:
        pattern: Similarity computing model. 相似度计算模式。
            Defaults to 'j' represents 'jaccard'.
    """
    assert synonym_vector1 != [], "synonym_vector1 can not be empty"
    assert synonym_vector2 != [], "synonym_vector2 can not be empty"
    if synonym_vector1 == synonym_vector2:
        return 1.0
    if pattern == 'jb':
        sim = jaccard_basic(synonym_vector1, synonym_vector2)
    elif pattern == 'j':
        sim = jaccard(synonym_vector1, synonym_vector2)
    elif pattern == 'j2':
        sim = jaccard2(synonym_vector1, synonym_vector2)
    elif pattern == 'e':
        sim = edit_distance(synonym_vector1, synonym_vector2)
    return sim
 def check_swords(sentence):
    """检测是否包含敏感词
    """
    for word in sensitive_words:
        if word in sentence:
            return True
    return False
    # words = synonym_cut(sentence, pattern="w")
    # swords = set(sensitive_words).intersection(words)
    # return bool(swords)
 def get_location(sentence):
    """Get location in sentence. 获取句子中的地址。
    """
    location = []
    sv_sentence = synonym_cut(sentence, 'wf')
    for word, tag in sv_sentence:
        if tag.startswith("Di02") or tag.startswith("Di03") or tag == "Cb25A11#":
            location.append(word)
    return location
 def time_me(info="used", format_string="ms"):
    """Performance analysis - time

    Decorator of time performance analysis.
    性能分析——计时统计
    系统时间(wall clock time, elapsed time)是指一段程序从运行到终止，系统时钟走过的时间。
    一般系统时间都是要大于CPU时间的。通常可以由系统提供，在C++/Windows中，可以由<time.h>提供。
    注意得到的时间精度是和系统有关系的。
    1.time.clock()以浮点数计算的秒数返回当前的CPU时间。用来衡量不同程序的耗时，比time.time()更有用。
    time.clock()在不同的系统上含义不同。在UNIX系统上，它返回的是"进程时间"，它是用秒表示的浮点数（时间戳）。
    而在WINDOWS中，第一次调用，返回的是进程运行的实际时间。而第二次之后的调用是自第一次
    调用以后到现在的运行时间。（实际上是以WIN32上QueryPerformanceCounter()为基础，它比毫秒表示更为精确）
    2.time.perf_counter()能够提供给定平台上精度最高的计时器。计算的仍然是系统时间，
    这会受到许多不同因素的影响，例如机器当前负载。
    3.time.process_time()提供进程时间。

    Args:
        info: Customize print info. 自定义提示信息。
        format_string: Specifies the timing unit. 指定计时单位，例如's': 秒，'ms': 毫秒。
            Defaults to 's'.
    """
    def _time_me(func):
        @wraps(func)
        def _wrapper(*args, **kwargs):
            start = time.clock()
            # start = time.perf_counter()
            # start = time.process_time()
            result = func(*args, **kwargs)
            end = time.clock()
            if format_string == "s":
                print("%s %s %s"%(func.__name__, info, end - start), "s")
            elif format_string == "ms":
                print("%s %s %s" % (func.__name__, info, 1000*(end - start)), "ms")
            return result
        return _wrapper
    return _time_me
 def get_current_time(format_string="%Y-%m-%d-%H-%M-%S", info=None):
    """Get current time with specific format_string.
    获取指定日期表示方式的当前时间。

    Args:
        format_string: Specifies the format of time. 指定日期表示方式。
            Defaults to '%Y-%m-%d-%H-%M-%S'.
    """
    assert isinstance(format_string, str), "The format_string must be a string."
    # Python3
    # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError.
    # http://bugs.python.org/issue8304
    try:
        current_time = time.strftime(format_string, time.localtime())
    except UnicodeEncodeError:
        result = time.strftime(format_string.encode('unicode-escape').decode(), time.localtime())
        current_time = result.encode().decode('unicode-escape')
    return current_time
 def random_item(mylist):
    """Get random item of data.
    从数据中获取随机元素。

    Returns:
        item: Random item of data. 数据随机项。
    """
    assert mylist is not None, "The list can not be None."
    if isinstance(mylist, list):
        item = mylist[random.randint(0, len(mylist)-1)]
    elif isinstance(mylist, str):
        item = mylist
    return item
 def get_age(format_string="%s年%s个月%s天", info="2016-7-25"):
    """Get age with specific format_string.
    获取指定日期表示方式的年龄。

    Args:
        format_string: Specifies the format of time. 指定日期表示方式。
            Defaults to '{y}年{m}个月{d}天'.
    """
    assert isinstance(format_string, str), "The format_string must be a string."
    assert isinstance(info, str), "The birthday must be a string."
    
    # 方案1：根据日期字面差计算具体时长
    mdays = [31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30] # 从12（0）月到11月
    ct = get_current_time(format_string="%Y-%m-%d")
    st = [int(i) for i in info.split('-')]
    et = [int(i) for i in ct.split('-')]
    if st[1] < et[1]:
        year = et[0] - st[0]
        if st[2] < et[2]:
            month = et[1] - st[1]
            day = et[2] - st[2]
        else:
            month = et[1] - st[1] - 1
            day = et[2] + mdays[(et[1] - 1) % 12] - st[2]
    else:
        year = et[0] - st[0] - 1
        if st[2] < et[2]:
            month = et[1] + 12 - st[1]
            day = et[2] - st[2]
        else:
            month = et[1] + 12 - st[1] - 1
            day = et[2] + mdays[(et[1] - 1) % 12] - st[2]

    # 方案2：根据日期天数差计算具体时长
    # start_time= datetime.datetime.strptime(info, "%Y-%m-%d")
    # end_time= datetime.datetime.strptime(ct, "%Y-%m-%d")

    # seconds = (end_time - start_time).seconds  
    # hours = (end_time - start_time).hours
    # days = (end_time - start_time).days
    # year = int(days / 365)
    # month = int(days % 365 / 30)
    # day = int(days % 365 % 30)

    age = format_string % (str(year), str(month), str(day))
    return age
 def pinyin_cut(sentence, pattern=None):
    """Cut the sentence into phonetic vectors.
    将句子切分为拼音向量。
    """
    return lazy_pinyin(sentence)
 def jaccard_pinyin(pv1, pv2, threshold=0.7):
    """Similarity score between two pinyin vectors with jaccard.
    计算两个拼音向量的语义 jaccard 相似度得分。

    According to the semantic jaccard model to calculate the similarity.
    The similarity score interval for each two pinyin sentences was [0, 1].
    根据语义jaccard模型来计算相似度。每两个拼音向量的相似度得分区间为为[0, 1]。
    """
    sv_matrix = []
    sv_rows = []
    for pinyin1 in pv1:
        for pinyin2 in pv2:
            score = match_pinyin(pinyin1, pinyin2)
            sv_rows.append(score)
        sv_matrix.append(sv_rows)
        sv_rows = []
    matrix = mat(sv_matrix)
    result = sum_cosine(matrix, threshold)
    total = result["total"]
    total_dif = result["total_dif"]
    num = result["num_not_match"]
    sim = total/(total + num*(1-total_dif))
    return sim

import copy
import json
import sqlite3
from collections import deque
from py2neo import Graph, Node, Relationship, NodeSelector
from .config import getConfig
from .apilib import nlu_tuling, get_location_by_ip
from .semantic import synonym_cut, segment, get_tag, similarity, check_swords, get_location
from .mytools import time_me, get_current_time, random_item, get_age
from .word2pinyin import pinyin_cut, jaccard_pinyin
        if not sv1:
            return self.update_result(question)
        for node in subgraph:
            iquestion = self.iformat(node["name"])
            if question == iquestion:
                print("Similarity Score: Original sentence")
                return self.update_result(question, node)
            sv2 = synonym_cut(iquestion, 'wf') # 基于 semantic.jaccard
            # sv2 = segment(iquestion) # 基于 semantic.jaccard2
            if sv2:
                temp_sim = similarity(sv1, sv2, 'j') # 基于 semantic.jaccard
                # temp_sim = similarity(sv1, sv2, 'j2') # 基于 semantic.jaccard2
            # 匹配加速，不必选取最高相似度，只要达到阈值就终止匹配
            if temp_sim > athreshold:
                print("Q: " + iquestion + " Similarity Score: " + str(temp_sim))
                return self.update_result(question, node)
        # ===========================================================
            ss.append(temp_sim)
        max_score = max(ss)
        if max_score > threshold:
            node = subgraph[ss.index(max_score)]
            iquestion = self.iformat(node["name"])
            print("Q: " + iquestion + " Similarity Score: " + str(max_score))
            return self.update_result(question, node)
        # ===========================================================
        return self.update_result(question)

    def extract_synonym_first(self, question, subgraph, threshold=0.60):
        """Extract synonymous QA in NLU database。
        QA匹配模式：从知识库选取匹配度最高的问答对。

        Args:
            question: User question. 用户问题。
            subgraph: Sub graphs corresponding to the current dialogue. 当前对话领域对应的子图。
        """
        temp_sim = 0
        ss = []
        max_score = 0
        sv1 = synonym_cut(question, 'wf') # 基于 semantic.jaccard
        # sv1 = segment(question) # 基于 semantic.jaccard2
        if not sv1:
            return self.update_result(question)
        for node in subgraph:
            iquestion = self.iformat(node["name"])
            if question == iquestion:
                print("Similarity Score: Original sentence")
                return self.update_result(question, node)
            sv2 = synonym_cut(iquestion, 'wf') # 基于 semantic.jaccard
            # sv2 = segment(iquestion) # 基于 semantic.jaccard2
            if sv2:
                temp_sim = similarity(sv1, sv2, 'j') # 基于 semantic.jaccard
                # temp_sim = similarity(sv1, sv2, 'j2') # 基于 semantic.jaccard2
            ss.append(temp_sim)
        max_score = max(ss)
        if max_score > threshold:
            node = subgraph[ss.index(max_score)]
            iquestion = self.iformat(node["name"])
            print("Q: " + iquestion + " Similarity Score: " + str(max_score))
            return self.update_result(question, node)
        return self.update_result(question)

    def extract_keysentence(self, question, data=None, threshold=0.40):
        """Extract keysentence QA in NLU database。
        QA匹配模式：从知识库选取包含关键句的问答对。

        Args:
            question: User question. 用户问题。
        """
        if data:
            subgraph = [node for node in data if node["name"] in question]
        else:
            usertopics = ' '.join(self.usertopics)
            # 只从目前挂接的知识库中匹配
            match_string = "MATCH (n:NluCell) WHERE '" + question + \
                "' CONTAINS n.name and '" + usertopics +  \
                "' CONTAINS n.topic RETURN n LIMIT 1"
            subgraph = [item['n'] for item in self.graph.run(match_string).data()]
        if subgraph:
            # 选取第一个匹配节点
            print("Similarity Score: Key sentence")
            # return self.extract_synonym(question, subgraph, threshold=threshold)
            node = subgraph[0]
            return self.update_result(question, node)
        return self.update_result(question)

    def extract_keysentence_first(self, question, data=None, threshold=0.40):
        """Extract keysentence QA in NLU database。
        QA匹配模式：从知识库选取包含关键句且匹配度最高的问答对。

        Args:
            question: User question. 用户问题。
        """
        if data:
            subgraph = [node for node in data if node["name"] in question]
        else:
            usertopics = ' '.join(self.usertopics)
            # 只从目前挂接的知识库中匹配
            match_string = "MATCH (n:NluCell) WHERE '" + question + \
                "' CONTAINS n.name and '" + usertopics +  \
                "' CONTAINS n.topic RETURN n"
            subdata = self.graph.run(match_string).data()
            subgraph = [item['n'] for item in subdata]
        if subgraph:
            # 选取得分最高的
            print("Similarity Score: Key sentence")
            return self.extract_synonym_first(question, subgraph, threshold=threshold)
        return self.update_result(question)

    def remove_name(self, question):
        # 姓氏误匹配重定义
        if question.startswith("小") and len(question) == 2:
            question = self.user['robotname']
        # 称呼过滤
        for robotname in ["小民", "小明", "小名", "晓明"]:
            if question.startswith(robotname) and len(question) >= 4 and "在线" not in question:
                question = question.lstrip(robotname)
        if not question:
            question = self.user['robotname']
        return question
    

    @time_me()