def testResult(self): test_id = 'test' watches = [] now_utc = utc_now() + timedelta(days=-1) for i in range(WATCH_COUNTS): watches.append( Watch(date=now_utc + timedelta(seconds=WATCH_INTERVAL * (i + 1)), sent_count=1, is_replied=True)) drinking = Drinking(id=test_id, mid=test_id, start_date=now_utc, is_done=True, watches=watches) drinking.put() check_result() (status, info) = get_status(test_id) self.assertEqual(status, User.STAT_WAIT_RESULT) result = u'二日酔い' msg = handle_result(result, info) drinking = Drinking.get_key(test_id).get() self.assertEqual(drinking.result, result, drinking.result) self.assertEqual(drinking.sentiment, 0.0) self.assertEqual(drinking.magnitude, 0.0) self.assertTrue(msg.find(u'次回も大人飲み') >= 0, msg)
def testWatchFinished(self): TEST_INTERVAL = 5 test_id = 'test' watches = [] now_utc = utc_now() + timedelta(seconds=TEST_INTERVAL) for i in range(WATCH_COUNTS): watches.append( Watch(date=now_utc + timedelta(seconds=TEST_INTERVAL * (i + 1)))) drinking = Drinking(id=test_id, mid=test_id, start_date=now_utc, watches=watches) drinking.put() sleep(TEST_INTERVAL * 2 + 2) watch_drinkings() (stat, info) = get_status(test_id, is_peek=True) msg = handle_reply(u'帰宅した', info) self.assertTrue(msg.startswith(u'お疲れさまでした'), msg) drinking = Drinking.get_key(test_id).get() self.assertEqual(drinking.finished_date.year, now_utc.year) self.assertEqual(drinking.finished_date.month, now_utc.month) self.assertEqual(drinking.finished_date.day, now_utc.day) self.assertEqual(drinking.finished_date.hour, now_utc.hour) for j, watch in enumerate(drinking.watches): self.assertEqual(watch.is_replied, True if j == 0 else False) self.assertEqual(watch.sent_count, 1 if j == 0 else 0) self.assertTrue(drinking.is_done)
def cancel_drinking(mid): query = Drinking.query(Drinking.mid == mid, Drinking.is_done == False) drinkings = query.fetch(keys_only=True) if len(drinkings) == 0: return u'予約された飲みはありません' # delete drinking entry Drinking.delete_drinkings(drinkings) return u'予約された飲みをキャンセルしました'
def handle_reply(text, watch_info): drinking = Drinking.get_key(watch_info['key']).get() (elms, drink_id, cancel_id, finished_id, history_id) = parse_message(text) if drinking: summary = parse_drinking_amount(text, drinking.summary) drinking.watches[watch_info['idx']].reply = text drinking.watches[watch_info['idx']].is_replied = True drinking.summary = summary if len(summary): msg = u'これまで合計\n' for kind in summary: msg += u'%s %d杯\n' % (kind, summary[kind]) msg += u'飲みました!\n' else: msg = u'' if finished_id >= 0: # it is finished msg += finish_the_drinkig(drinking) else: msg += u'引き続き大人飲みでいきましょう!' drinking.put() return msg else: return None
def testHistory(self): test_id = 'test' test_msgs = [u'過去の飲みは?', u'これまでの呑みを', u'今までの呑みは?', u'前の呑み'] for test_msg in test_msgs: msg = handle_message(test_id, test_msg) self.assertTrue(msg.startswith(u'まだ飲みの'), msg) user = User(id=test_id) user.put() for test_msg in test_msgs: msg = handle_message(test_id, test_msg) self.assertTrue(msg.startswith(u'過去の飲みは'), msg) user = User.get_key(test_id).get() self.assertTrue(user.history_url != None and len(user.history_url) > 0) self.assertTrue(user.history_expire < utc_now() + timedelta(minutes=HISTORY_DURATION)) sdt = utc_now() + timedelta(days=-2) check_sdt = [] for i in range(MAX_HISTORY + 1): watches = [] for j in range(WATCH_COUNTS): watches.append( Watch(date=sdt + timedelta(minutes=WATCH_INTERVAL * (i + 1)))) key = test_id + sdt.strftime('%Y%m%d%H%M') drinking = Drinking(id=key, mid=test_id, start_date=sdt, watches=watches) drinking.put() check_sdt.append( format_jdate(sdt.replace(tzinfo=tz_utc).astimezone(tz_jst))) sdt = sdt + timedelta(days=-1) self.assertEqual(get_drinking_history_content(user.history_url[:-1]), None) soup = BeautifulSoup(get_drinking_history_content(user.history_url), 'html.parser') trs = soup.body.div.table.tbody.findAll('tr') self.assertEqual(len(trs), MAX_HISTORY * 2) for i in range(MAX_HISTORY): self.assertEqual(trs[i * 2].td.text, check_sdt[i])
def finish_drinking(mid): query = Drinking.query(Drinking.mid == mid, Drinking.is_done == False) drinkings = query.fetch(1) if len(drinkings) == 0: return u'すでに帰宅されているようです' # finish the drinking return finish_the_drinkig(drinkings[0])
def testWatch(self): TEST_INTERVAL = 10 test_id = 'test' watches = [] now_utc = utc_now() + timedelta(seconds=TEST_INTERVAL) for i in range(WATCH_COUNTS): watches.append( Watch(date=now_utc + timedelta(seconds=TEST_INTERVAL * (i + 1)))) drinking = Drinking(id=test_id, mid=test_id, start_date=now_utc, watches=watches) drinking.put() sleep(TEST_INTERVAL + 2) for i in range(WATCH_COUNTS): sleep(TEST_INTERVAL) watch_drinkings() (status, info) = get_status(test_id, is_peek=True) self.assertEqual(status, User.STAT_WAIT_REPLY) self.assertTrue(info is not None) self.assertEqual(info['key'], test_id, info['key']) self.assertEqual(info['idx'], i, info['idx']) drinking = Drinking.get_key(test_id).get() for j, watch in enumerate(drinking.watches): self.assertEqual(watch.is_replied, True if j < i else False) self.assertEqual(watch.sent_count, 1 if j <= i else 0) self.assertFalse(drinking.is_done) content = { "events": [{ "replyToken": "xxxx", "type": "message", "source": { "type": "user", "userId": test_id }, "message": { "type": "text", "text": "OK" } }] } recv_req = WebhookRequest(json.dumps(content)) receive_message(recv_req) (status, info) = get_status(test_id) self.assertEqual(status, User.STAT_NONE) drinking = Drinking.get_key(test_id).get() for j, watch in enumerate(drinking.watches): self.assertEqual(watch.is_replied, True if j <= i else False) self.assertEqual(watch.sent_count, 1 if j <= i else 0) drinking = Drinking.get_key(test_id).get() self.assertFalse(drinking.is_done)
def testWatchDelayed(self): TEST_INTERVAL = 5 test_id = 'test' watches = [] now_utc = utc_now() + timedelta(seconds=TEST_INTERVAL) for i in range(WATCH_COUNTS): watches.append( Watch(date=now_utc + timedelta(seconds=TEST_INTERVAL * (i + 1)))) drinking = Drinking(id=test_id, mid=test_id, start_date=now_utc, watches=watches) drinking.put() sleep(TEST_INTERVAL * 2 + 2) for i in range(5): watch_drinkings() (status, info) = get_status(test_id, True) self.assertEqual(status, User.STAT_WAIT_REPLY) self.assertTrue(info is not None) self.assertEqual(info['key'], test_id, info['key']) self.assertEqual(info['idx'], 0) drinking = Drinking.get_key(test_id).get() for j, watch in enumerate(drinking.watches): self.assertEqual(watch.is_replied, False) self.assertEqual(watch.sent_count, i + 1 if j == 0 else 0) drinking = Drinking.get_key(test_id).get() self.assertFalse(drinking.is_done)
def handle_result(text, info): drinking = Drinking.get_key(info['key']).get() if drinking: (sentiment, magnitude) = call_google_sentiment_analytics(text) drinking.result = text drinking.sentiment = sentiment drinking.magnitude = magnitude drinking.put() msg = u'昨日は' + get_drinking_quality_word(sentiment, magnitude) + \ u'\n次回も大人飲みのお手伝いをします。またメッセージくださいね!' return msg else: return None
def check_result(): req_to_send = {} yesterday = (utc_now()+timedelta(days=-1)).replace(hour=0, minute=0, second=0, microsecond=0) query = Drinking.query(Drinking.start_date >= yesterday, Drinking.start_date < yesterday+timedelta(days=1), Drinking.result == None) drinkings_to_req = query.fetch() for drinking in drinkings_to_req: req_to_send[drinking.mid] = { 'key' : drinking.key.id(), 'result' : True } if drinking.is_done == False: drinking.is_done = True drinking.put() if len(req_to_send): send_request_message(req_to_send)
def watch_drinkings(): watches_to_send = {} now = utc_now() query = Drinking.query(Drinking.is_done == False, Drinking.watches.date <= now, Drinking.watches.is_replied == False) drinkings_to_watch = query.fetch() for drinking in drinkings_to_watch: for i, watch in enumerate(drinking.watches): if watch.is_replied == False and \ (watch.date <= now and now <= watch.date+timedelta(minutes=WATCH_TIMEOUT)): watches_to_send[drinking.mid] = { 'key' : drinking.key.id(), 'idx' : i } watch.sent_count += 1 drinking.put() break if len(watches_to_send): send_watch_message(watches_to_send)
def check_result(): req_to_send = {} yesterday = (utc_now() + timedelta(days=-1)).replace(hour=0, minute=0, second=0, microsecond=0) query = Drinking.query(Drinking.start_date >= yesterday, Drinking.start_date < yesterday + timedelta(days=1), Drinking.result == None) drinkings_to_req = query.fetch() for drinking in drinkings_to_req: req_to_send[drinking.mid] = {'key': drinking.key.id(), 'result': True} if drinking.is_done == False: drinking.is_done = True drinking.put() if len(req_to_send): send_request_message(req_to_send)
def watch_drinkings(): watches_to_send = {} now = utc_now() query = Drinking.query(Drinking.is_done == False, Drinking.watches.date <= now, Drinking.watches.is_replied == False) drinkings_to_watch = query.fetch() for drinking in drinkings_to_watch: for i, watch in enumerate(drinking.watches): if watch.is_replied == False and \ (watch.date <= now and now <= watch.date+timedelta(minutes=WATCH_TIMEOUT)): watches_to_send[drinking.mid] = { 'key': drinking.key.id(), 'idx': i } watch.sent_count += 1 drinking.put() break if len(watches_to_send): send_watch_message(watches_to_send)
def is_duplicated_drinking(key): drinking = Drinking.get_key(key).get() return drinking != None
def get_drinking_history(mid, num=MAX_HISTORY): dt = utc_now().replace(hour=0, minute=0, second=0, microsecond=0) query = Drinking.query( Drinking.mid == mid, Drinking.start_date < dt).order(-Drinking.start_date) return query.fetch(num)
def handle_message(user_id, msg): mid = user_id (elms, drink_id, cancel_id, finished_id, history_id) = parse_message(msg) if history_id >= 0 and drink_id >= 0 and depends_drink(history_id, elms, drink_id): # this is request for drinking history return history_drinking(mid) if finished_id >= 0: # this is finish message return finish_drinking(mid) if cancel_id >= 0: # this is cancel message return cancel_drinking(mid) if drink_id == -1: # this is not nomi message... return None # user can have only one drink if has_drinking(mid): return u'飲みは1つしか予約できません。予約した飲みをキャンセルするには「やめ」とメッセージしてください。' # 3rd, determine date and time now = utc_now().replace(second=0, microsecond=0) s_date = datetime.now(tz=tz_jst).replace(second=0, microsecond=0) for id, elm in elms.items(): if depends_drink(id, elms, drink_id): start_info = '' for morphem in elm['morphemlist']: start_info += morphem['surface'] # find time time_patterns = [ u'(\d\d)[時じ::](\d\d)\D', '(\d\d)(\d\d)', u'(\d+)[時じ::](\d+)', u'(\d+)[時じ]' ] for pattern in time_patterns: mo = re.search(pattern, start_info) if mo: hour = int(mo.group(1)) minute = int(mo.group(2)) if mo.lastindex == 2 else 0 if hour < 12 and \ (start_info.find('PM') >= 0 or start_info.find('pm') >= 0 or start_info.find(u'午後') >= 0 or start_info.find(u'ごご') >= 0): hour += 12 s_date = s_date.replace(hour=hour, minute=minute) break # find date if start_info.find(u'明日') >= 0 or \ start_info.find(u'あした') >= 0 or \ start_info.find(u'あす') >= 0: s_date = s_date + timedelta(days=1) elif start_info.find(u'明後日') >= 0 or \ start_info.find(u'あさって') >= 0: s_date = s_date + timedelta(days=2) else: date_patterns = [ u'(\d+)月(\d+)', '(\d+)/(\d+)' ] for pattern in date_patterns: mo = re.search(pattern, start_info) if mo: month = int(mo.group(1)) day = int(mo.group(2)) s_date = s_date.replace(month=month, day=day) break # store data watches = [] utc_s_date = s_date.astimezone(tz_utc).replace(tzinfo=None) s_date_str = u'%d月%d日%d時%d分' % (s_date.month, s_date.day, s_date.hour, s_date.minute) # check if s_date is valid if utc_s_date < now: # past date !! return s_date_str + u'は過去です。' # check duplicate key = mid + s_date.strftime('%Y%m%d%H%M') if is_duplicated_drinking(key): return s_date_str + u'からの飲みは登録済みです。' for i in range(WATCH_COUNTS): watches.append(Watch(date=utc_s_date+timedelta(minutes=WATCH_INTERVAL*(i+1)))) drinking = Drinking(id=key, mid=mid, start_date=utc_s_date, watches=watches) drinking.put() msg = s_date_str + u'から飲むのですね!\n約%d分毎に%d回メッセージを送信しますので、何を何杯飲んだかなど、状況を返信してくださいね。帰宅したら帰宅とメッセージしてください。' % (WATCH_INTERVAL, WATCH_COUNTS) # past drinkings query = Drinking.query(Drinking.mid==mid, Drinking.is_done==True).order(-Drinking.start_date) prev_drinkings = query.fetch(1) if len(prev_drinkings): prev_drinking = prev_drinkings[0] msg += u'\n\nちなみに前回の飲みは%sで、その時は%s\n' % (format_jdate(prev_drinking.start_date. replace(tzinfo=tz_utc).astimezone(tz_jst)), get_drinking_quality_word(prev_drinking.sentiment, prev_drinking.magnitude)) sep = u'' for kind in prev_drinking.summary: msg += sep + u' %s %d 杯' % (kind, prev_drinking.summary[kind]) sep = u'\n' return msg
def get_worst_dinking(mid): query = Drinking.query(Drinking.mid==mid, Drinking.is_done==True).order(Drinking.sentiment) drinkings = query.fetch(1) return drinkings[0] if len(drinkings) > 0 else None
def get_drinking_history(mid, num=MAX_HISTORY): dt = utc_now().replace(hour=0, minute=0, second=0, microsecond=0) query = Drinking.query(Drinking.mid==mid, Drinking.start_date<dt).order(-Drinking.start_date) return query.fetch(num)
def get_worst_dinking(mid): query = Drinking.query(Drinking.mid == mid, Drinking.is_done == True).order(Drinking.sentiment) drinkings = query.fetch(1) return drinkings[0] if len(drinkings) > 0 else None
def handle_message(user_id, msg): mid = user_id (elms, drink_id, cancel_id, finished_id, history_id) = parse_message(msg) if history_id >= 0 and drink_id >= 0 and depends_drink( history_id, elms, drink_id): # this is request for drinking history return history_drinking(mid) if finished_id >= 0: # this is finish message return finish_drinking(mid) if cancel_id >= 0: # this is cancel message return cancel_drinking(mid) if drink_id == -1: # this is not nomi message... return None # user can have only one drink if has_drinking(mid): return u'飲みは1つしか予約できません。予約した飲みをキャンセルするには「やめ」とメッセージしてください。' # 3rd, determine date and time now = utc_now().replace(second=0, microsecond=0) s_date = datetime.now(tz=tz_jst).replace(second=0, microsecond=0) for id, elm in elms.items(): if depends_drink(id, elms, drink_id): start_info = '' for morphem in elm['morphemlist']: start_info += morphem['surface'] # find time time_patterns = [ u'(\d\d)[時じ::](\d\d)\D', '(\d\d)(\d\d)', u'(\d+)[時じ::](\d+)', u'(\d+)[時じ]' ] for pattern in time_patterns: mo = re.search(pattern, start_info) if mo: hour = int(mo.group(1)) minute = int(mo.group(2)) if mo.lastindex == 2 else 0 if hour < 12 and \ (start_info.find('PM') >= 0 or start_info.find('pm') >= 0 or start_info.find(u'午後') >= 0 or start_info.find(u'ごご') >= 0): hour += 12 s_date = s_date.replace(hour=hour, minute=minute) break # find date if start_info.find(u'明日') >= 0 or \ start_info.find(u'あした') >= 0 or \ start_info.find(u'あす') >= 0: s_date = s_date + timedelta(days=1) elif start_info.find(u'明後日') >= 0 or \ start_info.find(u'あさって') >= 0: s_date = s_date + timedelta(days=2) else: date_patterns = [u'(\d+)月(\d+)', '(\d+)/(\d+)'] for pattern in date_patterns: mo = re.search(pattern, start_info) if mo: month = int(mo.group(1)) day = int(mo.group(2)) s_date = s_date.replace(month=month, day=day) break # store data watches = [] utc_s_date = s_date.astimezone(tz_utc).replace(tzinfo=None) s_date_str = u'%d月%d日%d時%d分' % (s_date.month, s_date.day, s_date.hour, s_date.minute) # check if s_date is valid if utc_s_date < now: # past date !! return s_date_str + u'は過去です。' # check duplicate key = mid + s_date.strftime('%Y%m%d%H%M') if is_duplicated_drinking(key): return s_date_str + u'からの飲みは登録済みです。' for i in range(WATCH_COUNTS): watches.append( Watch(date=utc_s_date + timedelta(minutes=WATCH_INTERVAL * (i + 1)))) drinking = Drinking(id=key, mid=mid, start_date=utc_s_date, watches=watches) drinking.put() msg = s_date_str + u'から飲むのですね!\n約%d分毎に%d回メッセージを送信しますので、何を何杯飲んだかなど、状況を返信してくださいね。帰宅したら帰宅とメッセージしてください。' % ( WATCH_INTERVAL, WATCH_COUNTS) # past drinkings query = Drinking.query( Drinking.mid == mid, Drinking.is_done == True).order(-Drinking.start_date) prev_drinkings = query.fetch(1) if len(prev_drinkings): prev_drinking = prev_drinkings[0] msg += u'\n\nちなみに前回の飲みは%sで、その時は%s\n' % (format_jdate( prev_drinking.start_date.replace( tzinfo=tz_utc).astimezone(tz_jst)), get_drinking_quality_word( prev_drinking.sentiment, prev_drinking.magnitude)) sep = u'' for kind in prev_drinking.summary: msg += sep + u' %s %d 杯' % (kind, prev_drinking.summary[kind]) sep = u'\n' return msg
def has_drinking(mid): query = Drinking.query(Drinking.mid == mid, Drinking.is_done == False) drinkings = query.fetch() return len(drinkings) > 0