0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

日本語諸方言コーパスをDB化して遊ぶ (7) 話者ごとの発話総覧を作る

Last updated at Posted at 2020-08-17

連載記事です。前回、談話ごとの発話総覧を作ったので、今回は話者ごとの発話総覧を作ります。やることはあまり変わりません。完全に自分用の作業メモで、説明もいろいろ足りていないと思いますが、ご容赦ください。

画面遷移図

画面遷移図を再掲します。前回よりずっとシンプルなつくりですが、発話単位で分割されているテキストを結合して、文単位に切りなおす操作を挟みます。

func_2.png

コンポーネントのルーティング

画面遷移図のとおりにルーティングします。今回作りこむ2画面を登録しましょう。

resources/js/app.js
+ import SpeakerIndexComponent from "./components/SpeakerIndexComponent";
+ import SpeakerShowComponent from "./components/SpeakerShowComponent";

+         {
+             path: "/speaker",
+             name: "speaker.index",
+             component: SpeakerIndexComponent,
+             props: true
+         },
+         {
+             path: "/speaker/:speakerid",
+             name: "speaker.show",
+             component: SpeakerShowComponent,
+             props: true
+         },

各コンポーネントの作成

話者一覧

特筆すべき箇所はありません。全話者の情報を取得して(getSpeakers 関数)、「話者ID」「話者生年」「話者性別」を表示するだけです。今回はページ幅に余裕があるので[閲覧]ボタンを別途用意しても大丈夫でしょう。

resources/js/components/SpeakerIndexComponent.vue
<template>
<table class="table table-sm table-hover">
    <thead class="thead-dark">
            <tr>
                <th>話者 ID</th>
                <th>話者生年</th>
                <th>話者性別</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="s in speakers" v-bind:key="s.speakerid">
                <td>{{ s.speakerid }}</td>
                <td>{{ s.speakerbirthyear }}</td>
                <td>{{ s.speakersex }}</td>
                <td>
                    <div class="text-right">
                        <router-link 
                            v-bind:to="{ name: 'speaker.show', params: { speakerid: s.speakerid } }"
                        >
                            <button class="btn btn-success btn-sm text-nowrap">
                                閲覧
                            </button>
                        </router-link>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
</template>

<script>
export default {
    data: function() {
        return {
            speakers: []
        };
    },
    methods: {
        getSpeakers() {
            spinner.style.opacity = 1;
            axios.get("/api/speaker").then(res => {
                this.speakers = res.data;
                spinner.style.opacity = 0;
            });
        }
    },
    mounted() {
        this.getSpeakers();
    }
};
</script>

話者の発話総覧

話者情報を表示する部分と発話総覧の部分に分かれています。処理もそれぞれに用意します。

前者では、パスで話者IDを渡して(props の部分)、データベースから当該話者の情報だけ取ってきて表示します(getSpeakerInfo 関数)。

後者では、データベースから発話の全レコードを取得してきて、それをフロントエンドで加工して文単位に切りなおしています(getUtterances 関数)。この処理はサーバでやってもいいかもしれません。

resources/js/components/SpeakerShowComponent.vue
<template>
    <div>
        <table class="table table-sm table-hover">
            <tbody>
                <tr class="table-success">
                    <td nowrap>話者ID</td>
                    <td>{{ speakerInfo.speakerid }}</td>
                </tr>
                <tr class="table-success">
                    <td nowrap>話者生年</td>
                    <td>{{ speakerInfo.speakerbirthyear }}</td>
                </tr>
                <tr class="table-success">
                    <td nowrap>話者性別</td>
                    <td>{{ speakerInfo.speakersex }}</td>
                </tr>
            </tbody>
        </table>
        <div class="pt-3">
            <table class="table table-sm table-striped table-hover">
                <thead>
                    <tr class="thead-dark">
                        <th><div class="text-center">発話</div></th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="sentence in sentences" v-bind:key="sentence.id">
                        <td>
                            <div class="px-4">{{ sentence.d }}</div>
                            <div class="px-4">{{ sentence.s }}</div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
export default {
    props: {
        speakerid: String
    },
    data: () => {
        return {
            sentences: [],
            speakerInfo: {}
        };
    },
    methods: {
        getUtterances() {
            axios
                .get("/api/speaker/" + this.speakerid + "/utterances")
                .then(res => {
                    // 取得した発話をいったん全て結合する
                    let dTextStr = "";
                    let sTextStr = "";
                    for (let i = 0, i_len = res.data.length; i < i_len; i++) {
                        // 文節区切りをつぶさないよう適宜スペースを入れる
                        dTextStr += " " + res.data[i].dialecttext;
                        sTextStr += " " + res.data[i].standardtext;
                    }
                    // 句点で切って this.sentences に入れていく
                    const dSentences = dTextStr.split("");
                    const sSentences = sTextStr.split("");
                    for (let i = 0, i_len = dSentences.length; i < i_len; i++) {
                        // sentences は id, d(ialect), s(tandard) の連想配列にする
                        if (dSentences[i] && sSentences[i]) {
                            this.sentences.push({
                                id: i,
                                d: dSentences[i].trim(),
                                s: sSentences[i].trim()
                            });
                        }
                    }
                });
        },
        getSpeakerInfo() {
            axios.get("/api/speaker/" + this.speakerid).then(res => {
                // correction で返ってくるので [0] で取得する
                this.speakerInfo = {
                    speakerid: res.data[0]["speakerid"],
                    speakerbirthyear: res.data[0]["speakerbirthyear"],
                    speakersex: res.data[0]["speakersex"]
                };
            });
        }
    },
    mounted() {
        this.getUtterances();
        this.getSpeakerInfo();
    }
};
</script>

コントローラへのルーティング

使用するコントローラはひとつ(SpeakerController)でよさそうなので、API のルーティングも悩むことはありません。関数の名前も適当に考えます。

routes/api.php
Route::get('/speaker', 'SpeakerController@index');
Route::get('/speaker/{speakerid}', 'SpeakerController@show');
Route::get('/speaker/{speakerid}/utterances', 'SpeakerController@getUtterances');

コントローラの作成

先ほど使うことにした3つの関数を実装していきます。前回と同様に、Controller で ->first() するのではなく、->get() してコレクションを返してフロントエンドで絞る感じにしています。

app/Http/Controllers/SpeakerController.php
<?php
namespace App\Http\Controllers;
use App\Models\Speaker;
use Illuminate\Support\Facades\DB;

class SpeakerController extends Controller{
    // 全レコード取得
    public function index(){
        $md = new Speaker();
        $data = $md->getData();
        return $data;
    }

    // 指定した話者 ID に一致する話者レコードを返す
    public function show(String $speakerid){
        $data = DB::table('speaker')
        ->select('speaker.*')
        ->where('speaker.speakerid', '=', $speakerid)
        ->get();
        return $data;
    }

    // 指定した話者 ID をもつ発話をすべて返す
    public function getAllUtterances(String $speakerid){
        $data = DB::table('utterance')
        ->select('utterance.*')
        ->where('utterance.speakerid', '=', $speakerid)
        ->get();
        return $data;
    }

}

完成形

うまくいけばこんな具合に表示されるはずです。

/speaker
speakerindex.png

/speaker/02_e_099::C
speakershow.png

次回

第3の機能「ファイルの変換」を作りこんでいきます。

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?