6
6

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 5 years have passed since last update.

パワーワードが作れる?ランダム単語合成アプリを作ってみた

Posted at

はじめに

クリックするだけ!ランダムに2つの単語を「反応」させて、パワーワードを作りましょう!

rand.gif

モードは

  • 形容詞+名詞
  • 副詞+動詞
  • 名詞+助詞+動詞

の3種類です!

phrases.gif

ソースコード

GitHub - Syuparn/LiteralReaction: ランダムに単語を「反応」させて、パワーワードを作ろう

(DockerやVue.jsをはじめて使ったので、汚いところもあると思います…ツッコミ、修正歓迎です)

Webアプリの形で作りましたが、web上では公開していません。
(単語が本当にランダムなので、暴言や不謹慎な表現も生成されるかもしれないからです…)

試したい方は上記リンクからダウンロードしてください。docker-composeを使用しているので、

$ docker-compose -f docker-compose.prod.yml up -d

でコンテナを起動すればlocalhost:8080からアクセスできます。

webアプリケーションの構成

WebサーバとAPIサーバの2段構成になっています。

network.png

WebサーバはUI(html)、APIサーバは単語情報(JSON)をやりとりします。

コンテナもWebサーバとAPIサーバの2つに分けています。
(UIと単語生成を疎結合にして修正しやすくするためです)

containers.png
docker-compose.prod.yml
version: '3'
services:
  apiserver:
    build:
      context: ./apiserver/
      dockerfile: Dockerfile
    ports:
      - "127.0.0.1:5050:5050" # host:container
    expose:
      - "5050" # port for other containers (not host)
  vue-frontend:
    build:
      dockerfile: Dockerfile
      context: ./vue-frontend
    ports:
      - "127.0.0.1:8080:80" # host:container

ただし、ユーザーから見ても別々のURLになってしまうと不格好なので、Webサーバの/api/ではじまるURLにリクエストされた場合はAPIサーバに転送するようにしています。

vue-frontend/nginx_config/default.conf
server {
    listen 80;
    server_name localhost;
    server_tokens off;

    location / {
    	# デフォルトではhtmlを返す
        root /usr/share/nginx/html;
        try_files $uri $uri/ @dynamic;
    }

    location /api/ {
    	# URLが"/api/"ではじまる場合のみ、APIサーバに転送
        # URLのうち"/api/"に続く部分のみを取り出す
        rewrite /api/(.*) /$1 break;
        # docker-composeの機能によって、exposeされた他コンテナのポートはアプリケーション名でアクセス可能
        proxy_pass http://apiserver:5050;
        # redirectを無効化(既にrewriteでリダイレクトを設定しているため)
        proxy_redirect off;
        proxy_set_header Host $host;
    }
}

単語情報GETの流れは以下の通りです。(例は「形容詞+名詞」の場合)

  1. クライアントは、単語生成(「反応」)ページを開いたときに、形容詞をlocalhost:8080/api/rand/adjectiveへGETリクエスト
  2. WebサーバがリクエストをAPIサーバhttp://apiserver:5050/rand/adjectiveへ転送
  3. APIサーバがリクエストに基づきランダムな形容詞1つをJSON形式で(Webサーバに)返す
  4. Webサーバはレスポンスをクライアントへ転送
  5. クライアントに形容詞データのJSONが届く
  6. 名詞についても同様

単語データ生成

「MeCab IPADIC」を使用しました。

MeCabは、文章を単語(正確には形態素、ことばの最小単位)ごとに分かち書きするソフトウェアです。

そして、MeCabが参照する形態素の辞書の1つがIPADICです。

品詞ごとにcsvファイルで書かれていて、各行に各形態素の表記、読み、品詞、活用などが格納されています。

Adj.csv
あらっぽい,19,19,6956,形容詞,自立,*,*,形容詞・アウオ段,基本形,あらっぽい,アラッポイ,アラッポイ
あらっぽし,23,23,6956,形容詞,自立,*,*,形容詞・アウオ段,文語基本形,あらっぽい,アラッポシ,アラッポシ
あらっぽから,27,27,6956,形容詞,自立,*,*,形容詞・アウオ段,未然ヌ接続,あらっぽい,アラッポカラ,アラッポカラ
...

このアプリの単語合成モードは

  • 形容詞(終止形)+名詞
  • 副詞+動詞(終止形)
  • 名詞+(助詞)+動詞(終止形)

の3種類です。
この形式で最大限単語を利用するため、(文法的用語には少し不正確ですが)以下のルールで単語を抽出しました。

apiserver/db/format-ipadic-csv.bash
# 形容詞として利用

# 形容詞終止形
cat $PATH_FROM/Adj.csv       | awk -F"," '$10~/^基本形$/ {print $1}' >  $PATH_TO/adj.txt
# (「な」を付けると形容詞になる名詞)+「な」
cat $PATH_FROM/Noun.adjv.csv | awk -F"," '{print $1 "な"}'         >> $PATH_TO/adj.txt
# (「ない」を付けると形容詞になる名詞)+「ない」
cat $PATH_FROM/Noun.nai.csv  | awk -F"," '{print $1 "ない"}'       >> $PATH_TO/adj.txt

# 名詞として利用
# 「ナンセンスさ」を出したいので人名、地名、専門用語は除外し、一般名詞だけ使用

# 名詞
cat $PATH_FROM/Noun.csv          | awk -F"," '{print $1}' >  $PATH_TO/noun.txt
# 「する」を付けると動詞になる名詞
cat $PATH_FROM/Noun.verbal.csv   | awk -F"," '{print $1}' >> $PATH_TO/noun.txt
# 「な」を付けると形容詞になる名詞
cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}' >> $PATH_TO/noun.txt
# 「ない」を付けると形容詞になる名詞は単体で使うと不自然なので未使用)

# 副詞として使用

# 副詞
cat $PATH_FROM/Adverb.csv        | awk -F"," '{print $1}'                    >  $PATH_TO/adverb.txt
# 副詞としても使える名詞(「毎日」等)
cat $PATH_FROM/Noun.adverbal.csv | awk -F"," '{print $1}'                    >> $PATH_TO/adverb.txt
## 形容詞連用形(小さい「っ」で終わるものは動詞が後続できないので削除)
cat $PATH_FROM/Adj.csv           | awk -F"," '$10~/^連用テ接続$/ {print $1}' \
                                 | awk       '!/っ$/'                        >> $PATH_TO/adverb.txt

# 動詞として利用

# 動詞
cat $PATH_FROM/Verb.csv        | awk -F"," '$10~/^基本形$/ {print $1}'        >  $PATH_TO/verb.txt
# (「する」を付けると動詞になる名詞)+「する」
cat $PATH_FROM/Noun.verbal.csv | awk -F"," '{print $1 "する"}'                >> $PATH_TO/verb.txt

上記で生成したcsvをSQLiteに流し込んだものを、単語データベースとして使用しました。

apiserver/db/csv2sqlite.bash
sqlite3 $SQL_PATH/$SQL_NAME << EOS

/* create tables */
.read ./db/init.sql

/* let col separator "," */
.separator ','

/* import word data csvs */
.mode csv
.import $CSV_PATH/adj.csv adjectives
.import $CSV_PATH/adverb.csv adverbs
.import $CSV_PATH/noun.csv nouns
.import $CSV_PATH/verb.csv verbs
EOS
apiserver/db/init.sql
drop table if exists adjectives;
create table adjectives (
    id integer primary key,
    word text
);

drop table if exists adverbs;
create table adverbs (
    id integer primary key,
    word text
);

drop table if exists nouns;
create table nouns (
    id integer primary key,
    word text
);

drop table if exists verbs;
create table verbs (
    id integer primary key,
    word text
);

(個人的)面白かった生成結果

Screenshot from 2020-03-14 23-17-22.png 私たちがコードを書けるのは、数学様のおかげでございます。 Screenshot from 2020-03-14 22-43-34.png ウケ狙いちゃうわ! Screenshot from 2020-03-14 22-08-33.png こんなアプリ作っちゃうくらいだからしょうがないね

Screenshot from 2020-03-14 22-42-12.png
文章が成立している!

意外とフレーズが成立する割合が高く(体感100回に1回くらい?)、他にも以下のような「まともな」フレーズが生成されました。

居場所を失う
酷い蛇行
自我を失う
泥棒を訴える
移り気なロマンチスト
やさしいインターフェース

はまったところ

docker-compose buildできない

docker-composeapt installでインストールすると古いバージョンが入ってしまいます…

そして、古いバージョンのdocker-composeでバージョン3のdocker-compose.ymlを使うとビルドに失敗します。

apt installではなくcurlで最新バージョンをダウンロードしましょう

Install Docker Compose | Docker Documentation

(公式ドキュメントはちゃんと読まねば…反省)

go buildできない

Go1.13からはライブラリ管理の方法が変わったため、最初にgo mod initをする必要があります。

このコマンドを打つと、モジュールの依存関係ファイルgo.modやモジュールが本物か確かめるためのチェックサムgo.sumが生成されます。

Go Modules - Qiita

Go 1.13 に向けて知っておきたい Go Modules とそれを取り巻くエコシステム - blog.syfm

その代わり、go.modのおかげでライブラリを手動でgo getする必要がなくなりました!
go build時に自動でダウンロードされる)

コンテナ作成中に生成した実行ファイルが消える

volumesCOPYを混同していました。

Dockerfile中でCOPYすると、コンテナのビルド時にホストのディレクトリをコピーします。

docker-compose.yml中でvolumesを指定すると、コンテナの起動時にコンテナのディレクトリにホストのディレクトリをマウントします。

そのため、ホストのソースをCOPYしてせっかく実行ファイルをビルドしても、同じディレクトリをvolumesに指定するとホストのディレクトリ(もちろん実行ファイルは無い)に隠されてしまいます…

docker-composeのvolumesで指定したホストのディレクトリがマウントされずハマった

conic-gradientが使えない

単語生成画面の集中線はCSSのconic-gradient関数を使用する予定でした

…が、この関数はFirefoxでは非対応です。

conic-gradient() - CSS: カスケーディングスタイルシート | MDN

そこで、同名のnpmパッケージを使ってjs側で模様を生成しました。

conic-gradient - npm

しかし、このパッケージはES5で書かれているのでVue CLI内部でimportできません。

結局、

  1. index.htmlで直接グローバルに読み込む
  2. Vue側ではwindow.ConicGradientの形で呼び出す
  3. npm run buildではビルドされないので、別途モジュールをstaticにコピーする

という手順を取りました。

vue-frontend/index.html
<!DOCTYPE html>
<html>
...
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <!-- ここでモジュールを読み込む(prefixfreeはconic-gradientの依存モジュール) -->
    <script src="./static/prefixfree.min.js"></script>
    <script src="./static/conic-gradient.js"></script>
  </body>
</html>
vue-frontend/src/components/bangBackground.vue
<script>
export default {
  name: 'bangBackground',
  data: function () {
    return {
      bangSVG: new window.ConicGradient({
        repeating: true,
        stops: `#ffffff 0,
                #ffffff 2.0%,
                #AAAAAA 2.125%,
                #AAAAAA 2.375%,
                #ffffff 2.5%`
      })
    }
  }
}
</script>
vue-frontend/Dockerfile
FROM node:lts-alpine as builder

RUN mkdir -p /app
WORKDIR /app

RUN npm install -g http-server

COPY ./package*.json ./
RUN npm install \
    # conic-gradientモジュールをstaticにコピー
    && mkdir -p static/ \
    && cp node_modules/conic-gradient/conic-gradient.js static/ \
    && cp node_modules/prefixfree/prefixfree.min.js static/

COPY . .
RUN npm run build

FROM nginx:stable-alpine as product

COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx_config/default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]

参考文献

Go言語

APIサーバ
golangでREST APIをやってみた① - Qiita

SQLiteとの接続
golangでSQLite3を使ってデータベースを操作する方法まとめ | Black Everyday Company

MeCab

IPADICダウンロード方法
Alpine LinuxでMeCab with NEologd - Qiita

Vue.js

SPA(シングルページアプリケーション)のつくり方
【Vue.js】爆速でSPAを作る - Qiita

axiosを使ったJSONのGET/POST
Vue-CLIのプロジェクトでaxiosを使ってAPIや外部リソースからのデータを取得する | 大阪市天王寺区SOHOホームページ制作 | デザインサプライ-DesignSupply.-

コンポーネント内部に要素を入れる(公式)
スロット — Vue.js

nginx

URLの一部分だけ取り出してポートフォワーディング(/api/hoge -> apiserver:5050/hoge)
Nginx reverse proxy + URL rewrite - Server Fault

Docker,docker-compose

docker-compose基本操作
docker-compose コマンドまとめ - Qiita

複数サーバ(複数コンテナ)を協調させる方法
マイクロサービスほどじゃないけどウェブサービスを分割開発したい人向けDocker設定を集めるスレ - Qiita

vue-cliビルド方法(公式)
Vue.js アプリケーションを Docker 化する — Vue.js

Golangマルチステージビルド方法

(マルチステージビルドのメリット)
Dockerのマルチステージビルドを使う - Qiita
(Go1.7の記事ですが、Go1.13以上の場合Dockerfileでgo getをする必要はありません)

(go-sqlite3ライブラリを使う場合)
go-sqlite3 が入った状態での Docker のマルチステージビルドを行う - Pistatium note

6
6
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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?