class Node: def __init__(self, node_name, classifier=None, msg_path=None, canJump=False): self.name = node_name self._load_message(msg_path) self.canJump = canJump self.sentiment = 1 self.sentiment_audit = [self.sentiment] self.model_name = classifier self.log = Logger(self.__class__.__name__, level=ENV.NODE_LOG_LEVEL.value).logger def summary(self): return { 'node_name': self.name, 'description': self.describe, 'class_name': self.__class__.__name__, 'model': self.model_name } def _triger_jump(self): if self.canJump is True: # jump trigger if self.output_label == 1 and self.sentiment >= 3: self.output_label = 1001 else: return None def process(self, sentence, model_dict, lower_bound=None, upper_bound=None): model = model_dict[self.model_name] clf = model.classify(sentence, lower_bound, upper_bound) self.output_label = clf['label'] # jump trigger self._triger_jump() self.detail = clf return self.output_label, self.detail def _load_message(self, msg_path): self.messages = pd.read_csv(msg_path, encoding='utf8') self.messages = self.messages[self.messages['node_name'] == self.name] self.messages.label = self.messages.label.astype('int') self.messages.sentiment = self.messages.sentiment.astype('int') def get_response(self, label): """ return response by label """ df = self.messages[self.messages.label == label] max_sentiment = np.max(df.sentiment.values) if self.sentiment > max_sentiment: sentiment = max_sentiment else: sentiment = self.sentiment df = df[df.sentiment == sentiment] self.log.debug( 'Current sentiment is {}, node sentiment is: {}, max message sentiment is: {}' .format(sentiment, self.sentiment, max_sentiment)) self.log.debug('Available number of message is {}'.format(len(df))) # enable random extract try: df = df.sample(frac=1) except ValueError: response = 'current node name is{},ouput label is{},sentiment is{}, no message has been set'.format( self.name, label, sentiment) self.log.error(response) return response try: response = df.message.values[0] add_sentiment = df.add_sentiment.values[0] except IndexError: response = 'current node name is{},ouput label is{},sentiment is{}, no message has been set'.format( self.name, label, sentiment) self.log.error(response) return response self.sentiment += add_sentiment self.sentiment_audit.append(self.sentiment) return response
class Cache: def __init__(self, graph_path, msg_path, model_dict, max_session=1000, debug=False): self.max_session = 1000 self.inform_interval = 60 self.inactive_maxlength = 150 #{'uid': {'strategy': Tree(), 'time_response': <time>, 'time_inform': <>} self.active_session = {} self.model_dict = model_dict self.graph_path = graph_path self.msg_path = msg_path self.debug = debug self.log = Logger(self.__class__.__name__, level=ENV.NODE_LOG_LEVEL.value).logger self._print() self.mongo_db = MongoClient('localhost', 27017).chatbot def _print(self): self.log.info('Max num of session is: {}'.format(self.max_session)) self.log.info('inform inacitve interval is {} seconds'.format( self.inform_interval)) self.log.info('inactive max length is {} seconds'.format( self.inactive_maxlength)) if self.debug: self.log.info('DEBUG is enabled') def create_session(self, uid, profile=None): if len(self.active_session) < self.max_session: self.active_session[uid] = {} try: self.active_session[uid].update({ 'strategy': TreeStage1(debug=self.debug, profile=profile) }) except KeyError as e: self.log.error('Key {} does not exist in profile'.format(e)) self.log.error('create session for user {} failed'.format(uid)) return False self.active_session[uid].update({'time_response': time.time()}) self.active_session[uid].update({'time_inform': time.time()}) self.active_session[uid].update({'chatting': []}) self.log.info('New session was created: {}'.format(uid)) self.log.info('Remaining session number is: {}'.format( self.max_session - len(self.active_session))) return True else: return False def remove_session(self, uid): response = '您当前的会话超过 {} 秒没有响应,系统将关闭当前会话!如有需求,请开始新的对话!'.format( self.inactive_maxlength) try: socketio.emit('my_response', {'data': response}, room=uid, namespace=name_space) except: pass try: history = self.active_session[uid]['strategy'].cache.copy() print('history', history) print('''history['chat']''', history['chat']) # self.mongo_db.query.insert(history) #对话结束, 已经到了删除会话的时候了,删除之前存数据,(出现end之后才存储) except: self.log.error('Fail to save cache for uid: ') try: disconnect_frontend(uid) except: pass try: del self.active_session[uid] self.log.info( '{} session is inactive, it has been removed!'.format(uid)) except KeyError: pass gc.collect() def chat(self, uid, sentence): if self.active_session.get(uid) is not None: self.log.info( 'receive message from user: {} ===================='.format( uid)) response = self.active_session[uid]['strategy'].process( sentence, self.model_dict) self.active_session[uid]['time_response'] = time.time() self.active_session[uid]['time_inform'] = time.time() self.active_session[uid]['chatting'].append(response) self.log.info( 'processing messages for user {} has been done!----------------' .format(uid)) else: response = None return response def _bulk_deletes(self): current = time.time() remove_list = [] try: for uid in self.active_session: try: if current - self.active_session[uid][ 'time_response'] > self.inactive_maxlength: remove_list.append(uid) except KeyError: pass except RuntimeError as e: self.log.error(e) return False finally: # delete for uid in remove_list: self.remove_session(uid) return True def _bulk_inform(self): current = time.time() inform_list = [] try: for uid in self.active_session: try: if current - self.active_session[uid][ 'time_inform'] > self.inform_interval: inform_list.append(uid) except KeyError as e: self.log.error(e) pass for each in inform_list: self.inform_inactive(each) except RuntimeError: return False return True def purge_inactive(self): current = time.time() while True: if self._bulk_deletes(): break while True: if self._bulk_inform(): break def inform_inactive(self, uid): self.active_session[uid]['time_inform'] = time.time() response = '您有在听我说吗?请回答我刚才的问题!' socketio.emit('my_response', {'data': response}, room=uid, namespace=name_space) self.log.info('{} is inactive. Just inform that user'.format(uid))
class PF: def __init__(self, profile=None): """ profile should be None or dictionary: fields: 1. name: lastName + firstName, eg "Li Ming" if name is None, the constructor will try to load "lastName" and "firstName" 2. principal: the money borrowed, eg:'10,000' 3. contractStartDate, the date when money was borrowed. eg:"2018年5月2日", format"dddd年dd月dd日" 4. contractEndDate, the date before when total amount should be paid. eg:"2018年5月2日", format"dddd年dd月dd日" 5. apr: yearly/monthly, no calculation will be involved. type: string. eg, '9%' 6. fee: late payment fee. string, eg "500" 7. lendingCompany: the money originally borrowed from type, string, eg "平安E贷" 8. collectionCompany type, string, eg "江苏逸能" 9. customerID string or int "100000" 10. gender string, "男/女" 11. collector: the agent who makes the call string : "李明" 12. totalAmount: the total amount owed by debotor string: “50,000” 13. informDeadline: the deadline to collect money 相对时间 string: “明天下午2点” 14. splitDebtMaxTolerance: the max tolerance of split debt time 相对时间: string: 1个月以后 15. splitDebtFirstPay: the first payment amount after set up split debt string: '10,000' *16. deltaTime: the time diff between now and contract end Date. This will be calcualted """ self.log = Logger(self.__class__.__name__, level=ENV.PROFILE_LOG_LEVEL.value).logger self.dt = LocalDateTime() if profile is None: self._load_default() else: self._load_profile(profile) self.re_time = TimePattern() self._loadUpLowBound() def _load_default(self): self.log.debug( 'profile is None. The default demo profile will be loaded!') self.name = PROFILE.lastName.value + PROFILE.firstName.value self.principal = PROFILE.principal.value self.contractStartDate = PROFILE.contractStartDate.value self.contractEndDate = PROFILE.contractEndDate.value self.apr = PROFILE.apr.value self.interest = PROFILE.interest.value self.fee = PROFILE.fee.value self.lendingCompany = PROFILE.lendingCompany.value self.collectionCompany = PROFILE.collectionCompany.value self.customerID = PROFILE.customerID.value self.gender = PROFILE.gender.value self.collector = PROFILE.collector.value self.totalAmount = PROFILE.totalAmount.value self.informDeadline = PROFILE.informDeadline.value self.splitDebtMaxTolerance = PROFILE.splitDebtMaxTolerance.value self.splitDebtFirstPay = PROFILE.splitDebtFirstPay.value self.deltaTime = (self.dt.getLocalNow() - self.create_from_D(self.contractEndDate)).days self._get_prefix() self.log.info('Customer ID is {}, principal is {}, apr is {}'.format( self.customerID, self.principal, self.apr)) def _load_profile(self, profile): self.log.debug('Loading From Profile') self.name = profile.get('name') if self.name is None: self.name = profile['lastName'] + profile['firstName'] self.principal = profile['principal'] self.contractStartDate = profile['contractStartDate'] self.contractEndDate = profile['contractEndDate'] self.apr = profile['apr'] self.interest = profile['interest'] self.fee = profile['fee'] self.lendingCompany = profile['lendingCompany'] self.collectionCompany = profile['collectionCompany'] self.customerID = profile.get('customerID') self.gender = profile['gender'] self.collector = PROFILE.collector.value self.totalAmount = profile['totalAmount'] self.informDeadline = profile['informDeadline'] self.splitDebtMaxTolerance = profile['splitDebtMaxTolerance'] self.splitDebtFirstPay = profile['splitDebtFirstPay'] self.deltaTime = (self.dt.getLocalNow() - self.create_from_D(self.contractEndDate)).days self._get_prefix() self.log.info('Customer ID is {}, principal is {}, apr is {}'.format( self.customerID, self.principal, self.apr)) def _loadUpLowBound(self): upper = self.re_time.process(self.splitDebtMaxTolerance) lower = self.re_time.process(self.informDeadline) try: self.upper = upper[0]['gapH'] self.upperDateTime = upper[0]['time'] self.log.info('Load profile Upper bound successfully!') except: self.log.error('Loading Upper error! Set to default') self._loadDefaultUpBound() try: self.lower = lower[0]['gapH'] self.lowerDateTime = lower[0]['time'] self.log.info('Load profile Lower bound successfully!') except KeyError: self.log.error('Loading lower error! Set to default') self._loadDefaultLowBound() def _loadDefaultUpBound(self): upper = self.re_time.process('1个月') self.upper = upper[0]['gapH'] self.upperDateTime = upper[0]['time'] def _loadDefaultLowBound(self): lower = self.re_time.process('明天下午3点') self.lower = lower[0]['gapH'] self.lowerDateTime = lower[0]['time'] def _get_prefix(self): if self.gender == '男': self.prefix = '先生' elif self.gender == '女': self.prefix = '女士' else: self.prefix = '先生/女士' def create_from_D(self, date): year = int(re.findall('\d{4}年', date)[0][:-1]) month = int(re.findall('\d{1,2}月', date)[0][:-1]) day = int(re.findall('\d{1,2}日', date)[0][:-1]) return self.dt.createLocalTime(year=year, month=month, day=day)