0
0

自分の好きなアーティストと似ているアーティストを出力するWebアプリケーションの開発 part2

Last updated at Posted at 2024-05-26

はじめに

大学の授業で、入力されたアーティストと近しいアーティストのネットワーク図を作成するシステムを作ったので、今回は個人的な趣味で、そのシステムを発展させてWebアプリケーションを開発しました。その際の備忘録を残します。
part2では、フロントエンド側の実装についてまとめます。

Webアプリケーションの大まかな流れ

  1. 初期画面で入力フォームを表示し、ユーザにアーティスト名を入力させる
  2. 入力されたアーティストに対して、Spotipyのartist_related_artists()を使用して、そのアーティストと関連のあるアーティスト情報を取得する
  3. networkxライブラリを使用して、関連のあるアーティストを芋づる式にノードで結んでネットワーク図を作り、ページランクの値が高い順に取り出す(この時、人気曲の情報も取得する)
  4. リザルト画面で関連のあるアーティスト10人と取得した人気曲の情報を表示する

今回の備忘録では、1.と4.のフロントエンド側の実装についてまとめます。

ディレクトリ構造

前回記事で紹介したapp.pyと今回記述するindes.htmlresult.htmlerror.htmlstyle.cssのディレクトリ構造は以下の通りです。

ディレクトリ構造
{プロジェクト名}/
├── app.py
├── static/
│   └── style.css
└── templates/
    └── index.html
    └── result.html
    └── error.html

app.pyはルートディレクトリ直下に、indes.htmlresult.htmlerror.htmlの3つはtemplatesディレクトリに、style.cssstaticディレクトリに格納しています。

フロントエンド側の実装

初期画面の実装

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
    <title>Artists Recommender</title>
</head>
<body>
    <div class="container">
        <h1>Welcome to Artists Recommender</h1>

        <form action="/" method="POST">
            <label for="artist_name">
                あなたの好きなアーティストと関連度(*)の高いアーティストを10人推薦します。<br>
                アーティスト名を<strong>ローマ字</strong>で入力して下さい。<br><br>
                <div class="upper-note">
                    (例) 嵐 >> ARASHI <br>
                         平井堅 >> Ken Hirai<br>
                </div>
            </label>

            <div class="input-wrapper">
                <input type="text" id="artist_name" name="artist_name">
            </div>
            <div class="button-wrapper">
                <input type="submit" value="OK" class="button1">
            </div>
            <br>
            <div class="under-note">
                *「関連度」は、Spotifyコミュニティの視聴履歴の分析に基づいています。
            </div>
        </form>
    </div>
</body>
</html>

この画面は、ユーザーが初めてページにアクセスしたときや、フォームを送信せずにページをリロードしたときに表示するHTMLテンプレートです。

<input type="text" id="artist_name" name="artist_name">は、ユーザの入力フォームとなっていて、文字列を入力することができます。

<input type="submit" value="OK" class="button1">は、送信ボタンとなっていて、サーバにPOSTリクエストを送ります。

リザルト画面(正常画面)

result.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
    <title>Artist Network Result</title>
</head>
<body>
    <div class="container">
        <h1>以下が「{{ artist_name }}」と関連度の高いアーティストです</h1>
        <div class="note">※注 記載のリリース年はアルバム収録年です。</div>
        <ul>
            {% for artist in top_artists_list %}
                {% if loop.index == 1 %}
                    <li><div class="first_order">{{ loop.index }}st. {{ artist }} </div><br><br> 
                        {% for track_info in top_tracks_dict[artist] %}
                            {{ track_info['name'] }}({{ track_info['release_date'] }})<br>
                            {% if track_info['image_url'] %}
                                    <img src="{{ track_info['image_url'] }}" alt="{{ track_info['name'] }}のアルバムアート" width="100" height="100"><br>
                            {% else %}
                                <p>画像なし</p><br>
                            {% endif %}  
                        {% endfor %}</li>
                {% elif loop.index == 2 %}
                    <li><div class="second_order">{{ loop.index }}nd. {{ artist }} </div><br><br> 
                        {% for track_info in top_tracks_dict[artist] %}
                            {{ track_info['name'] }}({{ track_info['release_date'] }})<br>
                            {% if track_info['image_url'] %}
                                    <img src="{{ track_info['image_url'] }}" alt="{{ track_info['name'] }}のアルバムアート" width="100" height="100"><br>
                            {% else %}
                                <p>画像なし</p><br>
                            {% endif %}  
                        {% endfor %}</li>
                {% elif loop.index == 3 %}
                    <li><div class="third_order">{{ loop.index }}rd. {{ artist }} </div><br><br> 
                        {% for track_info in top_tracks_dict[artist] %}
                            {{ track_info['name'] }}({{ track_info['release_date'] }})<br>
                            {% if track_info['image_url'] %}
                                    <img src="{{ track_info['image_url'] }}" alt="{{ track_info['name'] }}のアルバムアート" width="100" height="100"><br>
                            {% else %}
                                <p>画像なし</p><br>
                            {% endif %}   
                        {% endfor %}</li>
                {% else %}
                    <li><div class="other_order">{{ loop.index }}th. {{ artist }} </div><br><br> 
                        {% for track_info in top_tracks_dict[artist] %}
                            {{ track_info['name'] }}({{ track_info['release_date'] }})<br>
                            {% if track_info['image_url'] %}
                                    <img src="{{ track_info['image_url'] }}" alt="{{ track_info['name'] }}のアルバムアート" width="100" height="100"><br>
                            {% else %}
                                <p>画像なし</p><br>
                            {% endif %}   
                        {% endfor %}</li>
                {% endif %}
            {% endfor %}
        </ul>
        <a href="/" class="button2">戻る</a>
    </div>
</body>
</html>

この画面は、初期画面でユーザがSpotifyデータベースに存在するアーティスト名を入力し、前回記事で紹介したapp.pyが正常に動作した時(top_artists_listが空でない場合)に表示するHTMLテンプレートです。

順位の表示の部分で、1位は1の後ろに「.st」、2位は「.nd」、3位は「.rd」、4位から10位は「.th」となるように記述しました。

top_tracks_dict辞書は、キーにartistを入れると、値としてそのアーティストの人気曲の3つの情報(「曲名」・「アルバム収録年」・「アルバム画像」)を返す辞書なので、それらを表示するように記述しています。

<a href="/" class="button2">戻る</a>は、初期画面に戻るボタンです。

リザルト画面(エラー画面)

error.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
    <title>Artist Not Found</title>
</head>
<body>
    <div class="container">
        <h1>入力されたアーティスト名は対応しておりません</h1>
        <p>「{{ artist_name }}」はSpotifyのデータベースに存在しないか、対応していません。</p>
        <p>ローマ字で入力するか、他のアーティスト名を入力してください。</p>
        <a href="/" class="button2">戻る</a>
    </div>
</body>
</html>

この画面は、初期画面でユーザがSpotifyデータベースに存在しないアーティスト名を入力し、前回記事で紹介したapp.pytop_artists_listが空(つまり、関連アーティストが見つからない)場合に表示するHTMLテンプレートです。

CSSの実装

style.css
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

body {
    font-family: 'Roboto', sans-serif;
    background-color: #f0f0f0;
    margin: 0;
    padding: 0;
}

.container {
    max-width: 800px;
    margin: auto;
    padding: 20px;
    padding-right: 20px;
    text-align: center;
}

.upper-note {
    margin-right: 80px;
}

.under-note {
    text-align: left;
    display: inline-block;
    margin-top: 30px;
}

input[type="text"] {
    width: 300px;
    padding: 10px;
    margin-top: 10px;
    margin-right: -200px;
}

input[type="submit"] {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    cursor: pointer;
    margin-top: 10px;
    margin-left: -100px;
}

input[type="submit"]:hover {
    background-color: #0056b3;
}

.input-wrapper {
    display: inline-block;
    width: 50%; 
    margin-right: 50px;
}

.button-wrapper {
    display: inline-block;
    width: 28%;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    margin-bottom: 10px;
    background-color: #ffffff;
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.button2 {
    padding: 10px 20px;
    background-color: #007bff;
    color: white;
    border: none;
    cursor: pointer;
}

.button2:hover {
    background-color: #0056b3;
}

.first_order{
    color:#dbb400;
    font-size: 35px;
    font-family: 'Roboto', sans-serif;
}

.second_order{
    color:#9fa0a0;
    font-size: 30px;
    font-family: 'Roboto', sans-serif;
}

.third_order{
    color:#c47022;
    font-size: 25px;
    font-family: 'Roboto', sans-serif;
}

.other_order{
    font-size: 20px;
    font-family: 'Roboto', sans-serif;
}

/* スマホ向けのスタイル */
@media (max-width: 768px) {
    .upper-note {
        margin-right: 70px;
    }
    .input-wrapper, .button-wrapper {
        display: block;
        width: 100%;
        text-align: center;
    }

    input[type="text"] {
        width: calc(100% - 20px);
        margin: 10px auto;
    }

    input[type="submit"] {
        width: calc(100% - 20px);
        margin: 10px auto;
    }
}

初期画面とリザルト画面の装飾部分の実装です。フォントはGoogleapiからRobotoをインポートして使用しています。

中央揃えで表示させたかったので、基本的にtext-align: center;で記述しています。(一部見た目上の都合で、text-align: left; display: inline-block;として、左揃えになるように記述しているところもあります。)

PCだけでなく、スマホにも対応したWebアプリケーションを開発したかったので、後半部分ではスマホに対応したCSSを実装しています。

marginの設定方法が分からなかったので、マイナスの値になってしまっています。

実際の画面

以下のように表示されます。

初期画面(PC版)

スクリーンショット 2024-05-26 151531.png

初期画面(スマホ版)

スクリーンショット 2024-05-26 152837.png

リザルト画面(正常)

「ARASHI」と入力した場合
artistsrecommender-1.onrender.com_result_ARASHI.png

リザルト画面(エラー)

「いきものがかり」と入力した場合
エラー原因:ローマ字で入力しなければならない

スクリーンショット 2024-05-26 151934.png

最後に

今回は、Webアプリケーションのフロントエンド側の実装についてまとめました。次回以降では、.envファイルを用いた環境変数の管理方法やGit、Dockerの操作方法などを書こうと思います。

参考文献

以下の記事を参考にしました。ありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0