見出し画像

Django×Tweepy_テンプレートに渡すコンテキスト変数はリスト形式でなければならない #124日目

tweepyを活用したアプリ作りをしていて、以下のようなグラフを作成しました。

グラフの作成ついてはこちらをご参照ください。


このグラフを見ると次に気になるのが、3/12の週はいいね&リツイートがとても多かったですが、これはどういう内容のツイートだったのか、という点です。

これを解決するために以下の機能を実装しました。

  • モデルから特定条件のレコードのみ取得

  • 対象期間のツイートを「いいね数」でランキング化

順番に解説させていただきます。

ただビュー全体は少し長いので該当部分だけ抜粋し、最後に全体を記載させていただきます。


モデルから特定条件のレコードのみ取得

前提として、グラフと一緒にその時にバズッたツイートを表示したいので、グラフを作成するための「TweetAnalysis(FormView)」の中に定義しています。

そしてこのビューでは「AnalysisForm」から分析用のキーワードと対象期間(開始日と終了日)を受け取っています。この条件に合致するようにモデルからレコードを抽出しているのが以下の部分です。

[views.py]
 
class TweetAnalysis(FormView):
    form_class    = AnalysisForm
    template_name = 'tweepy_test/twitter_analysis.html'

    def form_valid(self, form):
        criteria  = form.cleaned_data
        keyword   = criteria['keyword']
        start_day = criteria['start_day']
        end_day   = criteria['end_day']
        # キーワードに完全一致して、かつツイート日時が指定期間のツイートだけを抽出してくる
        qs = Tweets.objects.filter(keyword=keyword, created_at__range=[start_day, end_day + timedelta(days=1)])

「モデル名.object.filter(フィールド名='条件')」といった形でフィルタリングできます。日付の範囲指定は「フィールド名__range=[開始日, 終了日]」で可能です。ただ、この終了日の1日前までしか抽出されないので、例えば3/14までのデータが欲しいのであれば3/15と入力する必要があります。Pythonではよくある設定ですね。ここではtimedeltaを使って1日足しています。


対象期間のツイートを「いいね数」でランキング化

ちなみに、時短のため変数名は超適当なのでご容赦ください。。笑

抽出してきたレコードのうち、いいね数、ユーザー名、ツイート内容だけを使います。そしてランキング作成用に空のリスト(good_ranking)を作り、そこに順次インサートしていく形です。

ここで各ツイートの「いいね数」を1番目に格納しています。これはランキング化のためにsortするとき、1番目の要素が最初のキーになるのでこの順番にしました。2番目にはテンプレートに表示するための「いいね数&ユーザー名」を、3番目には今回の本命であるツイート内容を格納しています。

そしてtop_tweetという名前でテンプレートに渡す用のリストを作っているところがポイントです。リスト型にしておかないとテンプレート側で扱うことができませんでした。

[views.py]
 
class TweetAnalysis(FormView):
    form_class    = AnalysisForm
    template_name = 'tweepy_test/twitter_analysis.html'

    def form_valid(self, form):

        ~ 中略 ~

        qs = Tweets.objects.filter(keyword=keyword, created_at__range=[start_day, end_day + timedelta(1)])

        ~ 中略 ~

        # いいね数でランキングして上位5つのテキストを表示する
        good_ranking = []
        for i in range(len(qs)):
            tweet_data2 = qs[i]
            good_ranking.append([tweet_data2.favorite, "【{0}いいね】 / {1} / {2}".format(tweet_data2.favorite, tweet_data2.user_name, localtime(tweet_data2.created_at).strftime('%Y-%m-%d %H:%M')), tweet_data2.text])
        
        # リストの1番目にある「いいね数」で降順に並び替える=ランキング化
        good_ranking.sort(reverse=True)
        # テンプレートに渡すのは5つだけにするため新たにリストに格納する(リスト形式にしないとテンプレート側で扱えない)
        top_tweet = []
        for i in range(5):
            top_tweet.append([good_ranking[i][1], good_ranking[i][2]]))

        ctxt  = self.get_context_data(chart=chart, form=form, top_tweet=top_tweet)
        return self.render_to_response(ctxt)


これでテンプレート側で以下のように表示すれば、いいね数トップ5のツイートが表示されます。

[twitter_anaylsis.html]

~ 中略(グラフ表示部分) ~ 

{% for tweet in top_tweet %}
<div>
    {% for ele in tweet %}
        <p>
        {{ ele }}
        </p>
    {% endfor %}
    -----------------
</div>
{% endfor %}

↓こんな感じです。全然整えていないですが、表示したいものは出てきました。


もっといいやり方はある気がしますが。。
一先ずパッと思いついたのがこの方法でした。


↓最後に、分析ページのビューのコード全体です

[views.py]
 
from django.views.generic import FormView
from .models import Tweets
from .forms import AnalysisForm
from datetime import timedelta
from django.utils.timezone import localtime
import pandas as pd
from .graphs import Plot_Graph
from datetime import timedelta


class TweetAnalysis(FormView):
    form_class    = AnalysisForm
    template_name = 'tweepy_test/twitter_analysis.html'

    def form_valid(self, form):
        criteria  = form.cleaned_data
        keyword   = criteria['keyword']
        start_day = criteria['start_day']
        end_day   = criteria['end_day']
        # キーワードに完全一致したツイートだけを抽出してくる
        qs = Tweets.objects.filter(keyword=keyword, created_at__range=[start_day, end_day + timedelta(1)])

        # 該当キーワードのツイート情報を一時的に格納し、日付情報などを整理する
        dataf = pd.DataFrame({
            'tweet_time':[],
            'tweet_count':[],
            'favorite':[],
            'retweet':[],
        })
        # データフレームに1行ずつインサートしていく(1はツイート数。1行1ツイートのため1としている)
        for i in range(len(qs)):
            tweet_data   = qs[i] 
            dataf.loc[i] = [localtime(tweet_data.created_at), 1, tweet_data.favorite, tweet_data.retweet]
        # ツイート日時の情報を日付に直す
        dataf['tweet_time'] = pd.to_datetime(dataf['tweet_time']).dt.date

        # グラフに表示する「対象期間の日数」を指定する
        N = (end_day - start_day).days + 1
        # 指定期間の日付をキーに辞書を作り、ツイート数といいね&リツイート数をリストで格納する → ('日付':(ツイート数, いいね&リツイート数))
        tweet_days = {}
        for i in range(N):
            date = start_day + timedelta(i)
            tweet_days[date] = [0, 0]
        
        # tweet_daysの日付に合致するところの[ツイート数, いいね&リツイート数を更新する]
        # iterrowsにするとデータフレームを1行ずつ繰り返し処理できるようになる
        # (インデックス番号, (tweet_time, tweet_count, favorite, retweet))という構造になる
        for data in dataf.iterrows():
            if data[1][0] in tweet_days:    # 上記のtweet_timeがtweet_daysの中にあるかで条件分岐
                # data[1][0]でようやくtweet_timeに行きつく→リストになっているため、そのうち1番目と2番目を更新する
                tweet_days[data[1][0]][0] += data[1][1]
                tweet_days[data[1][0]][1] += (data[1][2] + data[1][3])
        
        # グラフのx軸とy軸を作る
        tweet_date       = []
        tweet_count      = []
        favorite_retweet = []
        for key, value in tweet_days.items():
            value = list(value)
            tweet_date.append(key)
            tweet_count.append(value[0])
            favorite_retweet.append(value[1])
        # グラフを作成する関数を呼び出す
        chart = Plot_Graph(tweet_date, tweet_count, favorite_retweet)

        # いいね数でランキングして上位5つのテキストを表示する
        good_ranking = []
        for i in range(len(qs)):
            tweet_data2 = qs[i]
            good_ranking.append([tweet_data2.favorite, "【{0}いいね】 / {1} / {2}".format(tweet_data2.favorite, tweet_data2.user_name, localtime(tweet_data2.created_at).strftime('%Y-%m-%d %H:%M')), tweet_data2.text])
        
        # リストの1番目にある「いいね数」で降順に並び替える=ランキング化
        good_ranking.sort(reverse=True)
        # テンプレートに渡すのは5つだけにするため新たにリストに格納する(リスト形式にしないとテンプレート側で扱えない)
        top_tweet = []
        for i in range(5):
            top_tweet.append([good_ranking[i][1], good_ranking[i][2]]))

        ctxt  = self.get_context_data(chart=chart, form=form, top_tweet=top_tweet)
        return self.render_to_response(ctxt)


ここまでお読みいただきありがとうございました!!


参考


この記事が気に入ったらサポートをしてみませんか?