見出し画像

PYTHONではじめてのWEBサイトを作成 9

バインミー店のランキングの更新

全国のバインミー店をあらためてPlaces APIで取得する

毎月バインミー店の情報は更新されるのでデータベースの設計を変える

毎月ランキングも更新させたい

なのでまずはmodels.pyを更新する


modelsでmonthlydataモデルを作成

from banhmilove_app.models import Store, MonthlyData
from datetime import date
from django.core.management.base import BaseCommand
import requests
from banhmilove_app.models import Store, MonthlyData
from django.db import transaction
from datetime import date

class Command(BaseCommand):
    help = 'Fetches Banh Mi shop data from Japan'

    def handle(self, *args, **options):
        self.stdout.write("Fetching Banh Mi shops in Japan...")
        self.fetch_places()

    def fetch_places(self):
        locations = [
            "Hokkaido", "Aomori", "Iwate", "Miyagi", "Akita", "Yamagata", "Fukushima",
            "Ibaraki", "Tochigi", "Gunma", "Saitama", "Chiba", "Tokyo", "Kanagawa",
            "Niigata", "Toyama", "Ishikawa", "Fukui", "Yamanashi", "Nagano", "Gifu", 
            "Shizuoka", "Aichi", "Mie", "Shiga", "Kyoto", "Osaka", "Hyogo", "Nara", 
            "Wakayama", "Tottori", "Shimane", "Okayama", "Hiroshima", "Yamaguchi", 
            "Tokushima", "Kagawa", "Ehime", "Kochi", "Fukuoka", "Saga", "Nagasaki", 
            "Kumamoto", "Oita", "Miyazaki", "Kagoshima", "Okinawa"
        ]
        current_date = date.today()
        for location in locations:
            url = "https://maps.googleapis.com/maps/api/place/textsearch/json"
            params = {
                "query": f"Banh Mi shop in {location} Japan",
                "key": "YOUR APP KEY",  # 本番キーが公開されないように注意
                "language": "ja"
            }
            response = requests.get(url, params=params)
            self.stdout.write(f"Response from API: {response.text}")  # APIの応答内容をログに出力
            if response.status_code == 200:
                results = response.json().get('results', [])
                if not results:
                    self.stdout.write("No data received from API.")
                else:
                        self.save_data(results, location, current_date)
                        self.stdout.write(f"Stores in {location} fetched and saved successfully.")
            else:
                self.stdout.write(f"Failed to fetch data for {location}: {response.status_code} - {response.text}")

    def save_data(self, results, location, current_date):
        with transaction.atomic():
            for place in results:
                city = self.fetch_place_details(place['place_id'])
                store, created = Store.objects.get_or_create(
                    place_id=place.get('place_id'),
                    defaults={
                        'name': place.get('name'),
                        'prefecture': location,
                        'city': city if city else "N/A",
                        'url': place.get('website', ''),
                        'rating': place.get('rating', 0),
                        'review_count': place.get('user_ratings_total', 0),
                        'latitude': place.get('geometry', {}).get('location', {}).get('lat', 0),
                        'longitude': place.get('geometry', {}).get('location', {}).get('lng', 0)
                    }
                )
                if not created:
                    store.name = place.get('name')
                    store.prefecture = location
                    store.city = city if city else "N/A"
                    store.url = place.get('website', '')
                    store.rating = place.get('rating', 0)
                    store.review_count = place.get('user_ratings_total', 0)
                    store.latitude = place.get('geometry', {}).get('location', {}).get('lat', 0)
                    store.longitude = place.get('geometry', {}).get('location', {}).get('lng', 0)
                    store.save()

                MonthlyData.objects.update_or_create(
                    date=current_date,
                    store=store,
                    defaults={
                        'ranking': 0,  # ランキングは後で更新する
                        'weighted_score': store.weighted_score
                    }
                )
                print(f"Saved store: {store.name} with weighted score: {store.weighted_score}")
                
    def fetch_place_details(self, place_id):
        detail_url = "https://maps.googleapis.com/maps/api/place/details/json"
        params = {
            "place_id": place_id,
            "fields": "address_component,website",
            "key": "YOUR APP KEY",  # 同様にAPIキーを安全に保管
            "language": "ja"
        }
        response = requests.get(detail_url, params=params)
        if response.status_code == 200:
            details = response.json().get('result', {})
            city = None
            for component in details.get('address_components', []):
                if 'locality' in component['types']:
                    city = component['long_name']
            return city
        return None


データの取得

python manage.py fetch_banhmi

取得できた

ついでにショップリストも修正

450店舗取得できたので、
store_list.htmlに50ショップ毎のページネーションを追加
都道府県も表示
都道府県で絞り込みできるように変更

レビュー数5が上位になってしまったので100以上お店だけに表示を調整

views.py

def national_ranking(request):
    # レビュー数が100以上の店舗のみを取得し、weighted_scoreの降順で並び替え
    national_stores = Store.objects.filter(review_count__gte=100).order_by('-weighted_score', '-review_count')[:20]
    
    # ランキングの順位を付与
    for idx, store in enumerate(national_stores):
        store.rank = idx + 1

    return render(request, 'banhmilove_app/rank.html', {'stores': national_stores})


ランキングを20表示

Fetchで全国のバインミー店を再取得


6月のデータも取得

python manage.py fetch_banhmi

ついでにランキングページを修正


さらにVIEWSで毎月ランキング表示が変わるように修正
views.py

def national_ranking(request):
    year = int(request.GET.get('year', timezone.now().year))
    month = int(request.GET.get('month', timezone.now().month))

    # 指定された年と月のデータを取得
    monthly_data = MonthlyData.objects.filter(date__year=year, date__month=month, store__review_count__gte=100).order_by('-weighted_score', '-store__review_count')

    # ランキングの順位を付与
    for idx, data in enumerate(monthly_data):
        data.ranking = idx + 1

    # 1から12までの月のリストを生成
    months = list(range(1, 13))

    # データが存在するかをチェック
    data_exists = monthly_data.exists()

    return render(request, 'banhmilove_app/rank.html', {
        'stores': monthly_data,
        'year': year,
        'month': month,
        'months': months,
        'data_exists': data_exists,
    })


毎月表示が変わるようにテンプレートを修正

{% extends 'banhmilove_app/base.html' %}

{% block content %}
<div class="container">
    <h1>{{ year }}年{{ month }}月バインミー店ランキング</h1>
    <p>※スコアはレーティングとレビュー数から算出</p>

    <!-- 月ごとのタブ -->
    <ul class="nav nav-tabs" id="myTab" role="tablist">
        {% for m in range(1, 13) %}
        <li class="nav-item">
            <a class="nav-link {% if m == month|int %}active{% endif %}" href="?year={{ year }}&month={{ m }}">{{ m }}月</a>
        </li>
        {% endfor %}
    </ul>

    <table class="table table-bordered ranking-table">
        <thead class="thead-light">
            <tr>
                <th>順位</th>
                <th>店名</th>
                <th>所在地</th>
                <th>スコア※</th>
                <th>google<br>レーティング</th>
                <th>google<br>レビュー数</th>
            </tr>
        </thead>
        <tbody>
            {% for data in stores %}
            <tr>
                <td class="index">{{ data.ranking }}</td>
                <td>{{ data.store.name }}</td>
                <td>{{ data.store.prefecture }}</td>
                <td>{{ data.weighted_score|floatformat:2 }}</td>
                <td>{{ data.store.rating }}</td>
                <td>{{ data.store.review_count }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
{% endblock %}

ランキングが月ごとに変わるように修正完了


タブで月毎のランキングが見れる


今日はここまで

まとめ

5月目標を宣言して始めたけど6月の中盤になってしまった。

気にせずに前向きにいきましょう!

今回はランキングとDBを更新しました。

次はショップリストの更新と新ショップのページを作りましょう。



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