オススメユーザー表示処理
ここまで、Twitter APIにアクセスしユーザデータを収集する処理について説明してきましたが、次はいよいよオススメユーザーを画面に表示する処理のについて説明していきます。
オススメユーザーデータ作成処理
オススメユーザー表示処理は、ユーザデータからオススメユーザデータを抽出する「オススメユーザーデータ作成処理」とオススメユーザを画面に表示する「オススメユーザ表示処理」の大きく2つに分かれます。まずは「オススメユーザデータ作成処理」のソースから説明します。処理概要は中編の図10などを参照してください。
リクエスト受付画面
オススメユーザ作成処理は、図1のようなリクエスト受付画面(index.html)からPOSTでリクエストを受け付けます。

オススメユーザ作成リクエスト受付ハンドラ
リクエスト受付画面では、オススメユーザ抽出対象のscreen_nameを入力します。オススメユーザデータを新たに抽出したい場合は、チェックボックスにチェックをいれます。「送信」ボタンが押下された場合に起動されるリクエストハンドラを以下に示します。
class DispRecommendData(webapp.RequestHandler):
def post(self):
# screen_nameを取得
screen_name = self.request.get('sn')
# ~中略
# 以下の場合、オススメユーザデータ作成処理要求をキューに格納する
# ・RecommendUserエンティティが存在しない
# ・データクリア指定のチェックボックスがチェックされた
rec_user = RecommendUser.get_by_key_name(screen_name)
clear_data = self.request.get('clear_data')
if rec_user == None or clear_data == '1':
# Task Queue にエンキューする
_enqueue_make_recommend_user(screen_name)
# メッセージを表示して終了する
self.response.out.write(screen_name + ' enque task for ' + screen_name + '.please back to top page and wait a minute.')
else:
# RecommendUserに格納された情報をHTMLに表示する
_disp_recommend(self,tw_user,rec_user)
RecommendUserエンティティ
TaskQueueで抽出したオススメユーザのデータは、RecommendUserというエンティティに保存します。RecommendUserはscreen_nameをkey_nameとしてkey_nameのオススメユーザのscreen_nameの集合をscreen_name_listに格納します。screen_nameは文字列なので、db.StringListPropertyを使用します。
class RecommendUser(db.Model):
key = db.Key()
screen_name_list = db.StringListProperty()
TaskQueue格納処理
DispRecommendDataのpostメソッドでは、RecommendUserが存在しない場合は、_enqueue_make_recommend_userメソッドを呼び、画面で入力されたscreen_nameのユーザにオススメのデータを作成するためのTaskを生成しTaskQueueに加えます。ソースを以下に示します。
def _enqueue_make_recommend_user(screen_name):
# 処理対象seqの上限seq値
tw_user_cur_seq = Counter.current_id('IdGetTwitterUserSeq')
threshold = 10 # オススメと判定する一致数の閾値
proc_unit_num = 20 # Task処理1回のデータ抽出数
lower_seq = 1 # TwitterUser.seqの抽出下限値
upper_seq = proc_unit_num # TwitterUser.seqの抽出上限値
while 1:
taskqueue.add(url='/mrd',
params={
'screen_name':screen_name,
'lower_seq':str(lower_seq),
'upper_seq':str(upper_seq),
'threshold':str(threshold),
}
)
# 処理対象seqの上限値を超えた場合、ループを抜ける
if tw_user_cur_seq < upper_seq: break
lower_seq += proc_unit_num
upper_seq += proc_unit_num
TaskQueueにTaskを追加するtaskqueue.addメソッドの引数には、さまざまな引数を指定することができます。urlパラメータには、Taskとして起動されるリクエストハンドラのパスを指定し、paramsパラメータには、Taskに渡すパラメータを辞書形式で指定しています。
Task処理
TaskQueueのタスクは、リクエストハンドラのpostメソッドが起動されます。Taskとして起動されるMakeRecommendDataクラスのpostメソッドを以下に示します。
class MakeRecommendData(webapp.RequestHandler):
# TQとして起動される処理
def post(self):
screen_name = self.request.get('screen_name') # 抽出処理対象scree_name
lower_seq = int(self.request.get('lower_seq')) # 下限seq
upper_seq = int(self.request.get('upper_seq')) # 上限seq
threshold = int(self.request.get('threshold')) # オススメと判定する一致数の閾値
# 処理対象ユーザデータ取得
target_user = TwitterUser.get_by_key_name(screen_name)
# --- 中略 ---
# Queueで指定されたseqの範囲でTwitterUserエンティティを取得する
q = db.GqlQuery("SELECT * FROM TwitterUser WHERE seq>=:1 AND seq<=:2",lower_seq,upper_seq)
tw_users = q.fetch(upper_seq - lower_seq + 1)
target_friends_set = set(target_user.friend_ids)
match_screen_name_list = []
for tw_user in tw_users:
# 以下の場合は処理対象外
# ・フォローしているユーザがいない場合
# ・本人の場合
# ・既にフォローしている場合
if (len(tw_user.friend_ids) == 0 or tw_user.twid == target_user.twid) or tw_user.twid in target_user.friend_ids:
continue
friends = tw_user.friend_ids
# フォローしているユーザが一致した数
match_cnt = len(target_user.friend_ids) - len(target_friends_set-set(friends))
# 閾値以上の場合「オススメ」とみなす
if threshold <= match_cnt:
match_screen_name_list.append(tw_user.screen_name)
_put_rec_user(target_user.screen_name,match_screen_name_list)
RecommendUserエンティティをDatastoreに保存する処理は_put_rec_userメソッドに実装されています。この処理は複数のTaskから並行で呼ばれる可能性がある処理のため、Counterクラスでも説明したdb.run_in_transactionの仕組みを使って、RecommendUserエンティティのトランザクションを保証しています。ソースを以下に示します。
def _put_rec_user(target_screen_name,screen_name_list):
def txn():
# 既存エンティティの取得
user = RecommendUserData.get_by_key_name(target_screen_name)
if user == None:
user = RecommendUserData(key_name=target_screen_name)
# リストを連結する
sn_list = user.screen_name_list + screen_name_list
# 重複を省いてからエンティティにセットする
s_list = list(set(sn_list))
user.screen_name_list = list(set(sn_list))
user.put()
return db.run_in_transaction(txn)
