- 投稿日:2020-10-27T23:36:59+09:00
Youtuberヒカキンさんのじゃんけんを統計したWebアプリを開発してみた!
なぜ作ったのか?
国民的アニメのじゃんけんを集めたデータで機械学習でそのじゃんけんに勝てるプログラムを作っていたのが始まりです。
で、プログラムを作って満足はしたんですが、今度は違う題材で機械学習プログラム作ってみようと思いました。
それでじゃんけんって言ったら他に誰が人気かって言うと真っ先に思い浮かんだのは子供達に大人気のYoutuberである「ヒカキンさん
」と考えました。
なので、HikakinTVでヒカキンさんがじゃんけんをし始めた2014年11月からの動画を視聴し続けて結果を記録していきました。現状、機械学習するまでは至ってはいませんが、他にヒカキンさんのじゃんけん記録している人って探した限りいませんでした。
それならば!自分がヒカキンさんのじゃんけんの第一人者になっても良いんじゃないか
って思ったので、突発的にフレームワークの勉強しながら統計したWebサイトを作成してみました
。またヒカキンさんを選んだ理由として他にはこんな理由があります。
テレビとは違ってYoutubeに投稿されているので、
投稿者が消去しなければ半永久的に全ての動画が存在する
ので記録しやすい
ヒカキンさんのじゃんけんはゲストと一緒にしたり変装や特殊な環境下が多かったりする
ので、じゃんけんでも様々なバリエーションがあって面白そうヒカキンさんは子供にも人気あるので、もしかしたら
子供達も大勢みてくれるサイトになるかも!?
という甘い願望環境・ツール
開発PC:MacBook Pro macOS High Sierra 10.13.6 フレームワーク:Laravel サーバサイドプログラム言語:PHP フロントエンドプログラム言語:Javascript,scss データベース:MySQL ライブラリ:Webpack 運用サーバ:さくらVPS(Webサーバ,DBサーバの2つ) CentOS7.8サイトマップやワイヤーフレームなどはなく作成しながらデザインを決め、テーブル設計もボロが出てしまっていると思います。
制作時間はわかりませんが、期間は大体3ヶ月近くかかりました。成果物
URL:https://hikakinjunken.tk/
PC向けとして公開しています。
スマホでもみれますが、やや文字が小さくタップしにくいかと思います。サイトについて紹介している動画
Youtube:https://www.youtube.com/watch?v=aOOWyjU3kLc
ニコニコ:https://www.nicovideo.jp/watch/sm37629624GitHub:後日公開予定...
トップページ
トップページにアクセスすると、
左側は累計のじゃんけん数とそのグー・パー・チョキの内訳、
右側は最近投稿した動画でじゃんけんした動画5選表示されます。
- 「結果↓」をクリックするとじゃんけんの結果が表示されます。
- サムネイルまたはタイトルをクリックすると動画視聴ページに移行します。
キーワード検索
動画のタイトルと参戦したゲストに一致するワードを検索します。
ゲスト検索
じゃんけんに一緒に行ったゲストをプルダウンメニューから検索します。
じゃんけん一覧
年月ごとにHikakinTV内でアップロードされた動画を検索します。
検索ページ
キーワード検索とゲスト検索で検索すると、以下のように条件にあった動画が検索されてじゃんけん結果も確認できます。
- 例として、ゲスト検索で「はじめしゃちょー」を選んだときの結果です。
- 動画の投稿順やじゃんけんで出した手を決めて詳細に検索することも可能です。動画視聴ページ
動画のサムネイルかタイトルをクリックすると、動画視聴ページに移行できます。
このページでYoutubeに行かなくてもHikakinTVに投稿された動画を視聴できます。(怒られるかもしれない)
- 「タグ一覧」では、最大5つ動画に関連したタグを登録することができ、「キーワード検索」で登録したタグのワードで検索するとその動画が出てきます。(ニコニコ動画のタグ機能に似ている)
じゃんけん結果管理ページ
これは、私(管理者)しかアクセスできないページですが、ヒカキンさんの投稿動画とじゃんけん結果を登録・管理するページを作成しています。
このページのフォームに動画情報とじゃんけん結果、参戦したゲストを記載して登録することでMySQL側のデータベースに情報がInsertされます。
反省点
- 最初にサイトマップや要件を洗い出して作った方が良かった
Laravelで作ろうとは考えていましたが、環境を整えていたりコーディングしている間にも必要なライブラリが欲しかったり、こういう機能も入れた方が良いみたいな考えが出てきて寄り道することが多かった
- テーブル設計も考えて作るべきであった
ER図は作成したが第3正規化までのことをしていなかったので、もっとデータベースのテーブルはよく考えて作るべきだった
- 現状は単にじゃんけん結果を見るだけのサイトになってしまっている
じゃんけんの結果見るだけの需要でしかなく、サイトに満足して?使ってもらえるようにはしていないです。
機械学習でじゃんけんの結果を予測するページを作ったり、動画視聴ページでコメントできる機能を盛り込んで見ようかなとは考えています。まとめ
フレームワークを使ってオリジナルのアプリ開発するのは初めてに近い感じでしたので、色々と不備はあるかもしれないがある程度形にできたサイトになったのは良かったです。
自分がサボらなければ、ヒカキンさんが動画投稿したらじゃんけん結果を確認して、サイトに記録する...みたいなことを続けていきたいとは思います。
- 投稿日:2020-10-27T22:15:59+09:00
Django+MySQL+nginx の開発環境を Docker Compose で構築する
動作環境とこれまでの経緯
- ホスト環境
- macOS Catalina 10.15.6
- VirtualBox 6.1.14
- ゲスト環境
- Ubuntu Server 20.04.1 LTS
- Docker 19.03.13
- Docker Compose 1.27.3
- これまでの経緯
というわけで、Web アプリケーションを開発するために、Django+MySQL+nginx というマッスルドッキングな開発環境を Docker で構築していきたいと思います。
ディレクトリ構成
当初のディレクトリ構成は次のとおり。
app ├── docker-compose.yml ├── mysql │ ├── Dockerfile │ └── init.d │ └── init.sql ├── nginx │ ├── conf │ │ └── app_nginx.conf │ └── uwsgi_params └── python ├── .dockerignore ├── Dockerfile └── requirements.txtこの状態から
- docker-compose.yml
- Dockerfile(python)
- .dockerignore
- requirements.txt
- Dockerfile(mysql)
- init.sql
- app_nginx.conf
- uwsgi_params
の8つのファイルを編集することになります。ちなみに、以下では Django で「app」というアプリケーションを作る例を説明しています。各自で作りたいアプリケーションの名前に置き換えてね。
docker-compose.yml は最終的にこうなった
最初に結果を書きましょう。
ググったり本を読んだりしながら試行錯誤し、無慈悲なエラーメッセージと戦いながら、最終的にこんな docker-compose.yml にたどり着きました。長い道中を振り返れば、思わず目頭が熱くなる。docker-compose.ymlversion: '3.7' services: python: build: context: ./python dockerfile: Dockerfile command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log restart: unless-stopped container_name: Django networks: - django_net volumes: - ./src:/code - ./static:/static expose: - "8001" depends_on: - db db: build: context: ./mysql dockerfile: Dockerfile restart: unless-stopped container_name: MySQL networks: - django_net ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "*******" TZ: "Asia/Tokyo" volumes: - app.db.volume:/var/lib/mysql - ./mysql/init.d:/docker-entrypont-initdb.d nginx: image: nginx:1.17 restart: unless-stopped container_name: nginx networks: - django_net ports: - "80:80" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static depends_on: - python networks: django_net: driver: bridge volumes: app.db.volume: name: app.db.volumeDjango の設定
なにはともあれ、Django の設定から。
「python」ディレクトリ直下のDockerfile
を、次のように編集します。./python/DockerfileFROM python:3.8.5 WORKDIR /code ADD requirements.txt /code/ RUN pip install --upgrade pip && pip install -r requirements.txt ADD . /code/まず、
code
という名前の作業ディレクトリを作成し、そこに移ります。そのディレクトリにrequirements.txt
を追加した後、pip
で必要なパッケージをインストールして、最後にcode
をコンテナに追加します。で、
pip
でインストールするパッケージは、次のようにrequirements.txt
に列挙しておきます。今回必要になるのは、Django 本体と、nginx とソケット通信するための uWSGI と、MySQL に接続するための mysqlclient の3つ。./python/requirements.txtDjango==2.2.12 uwsgi==2.0.18 mysqlclient==1.4.6ついでに、
.dockerignore
も編集しておきましょう。余計なファイルがコンテナに入ると困るからね。./python/.dockerignore**/__pycache__ **/.DS_Store **/.Thumbs.db **/.git **/.gitignoreここまでの設定を踏まえて
docker-compose.yml
で Django を設定すると、次のとおりになります。docker-compose.yml(抜粋)python: build: context: ./python # docker-compose.yml からの Dockerfile の相対パス dockerfile: Dockerfile # 「Dockerfile」 に設定を書いてますよ、という明示的な指定 command: uwsgi --socket :8001 --module app.wsgi --py-autoreload 1 --logto /tmp/uwsgi.log restart: unless-stopped # コンテナが異常停止した場合は再起動する container_name: Django # コンテナ名を定義 networks: - django_net # ネットワーク名「django_net」を指定 volumes: - ./src:/code # ./src に /code をマウントする - ./static:/static # ./static に /static をマウントする expose: - "8001" # uwsgi が nginx とソケット通信するために、8001 番のポートを開けておく depends_on: - dbuWSGI の設定がややこしいですが、
app.wsgi
の部分が[Django のプロジェクト名].wsgi
であることを守れば、あとはおまじないです。MySQL の設定
MySQL の設定は、正直よく分かりません。だいたい、私はクエリが満足に書けないレベルで RDB は嫌いなのです。
マニュアルなどをいろいろと調べた結果、どうやらこれがベストプラクティスらしい。しらんけど。
./mysql/DockerfileFROM mysql:5.7 COPY init.d/* /docker-entrypoint-initdb.d/./mysql/init.d/init.sqlCREATE DATABASE IF NOT EXISTS app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER IF NOT EXISTS 'app_user'@'%' IDENTIFIED BY '[パスワード]'; GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'%'; FLUSH PRIVILEGES;ちなみに、上記の
init.sql
で作成したapp.db
(データベースの名前)は、後述のsettings.py
の設定に必要になるので、忘れないように。この設定に基づいて、
docker-compose.yml
で MySQL を設定すると、次のとおりになります。docker-compose.yml(抜粋)db: build: context: ./mysql # docker-compose.yml からの Dockerfile の相対パス dockerfile: Dockerfile # 「Dockerfile」 に設定を書いてますよ、という明示的な指定 restart: unless-stopped # コンテナが異常停止した場合は再起動する container_name: MySQL # コンテナ名を定義 networks: - django_net # ネットワーク名「django_net」を指定 ports: - "3306:3306" # 通信するポート番号を指定 environment: MYSQL_ROOT_PASSWORD: "*******" # ルートパスワードを環境変数で設定 TZ: "Asia/Tokyo" # タイムゾーンを環境変数で設定 volumes: - app.db.volume:/var/lib/mysql # データベースをボリューム「app.db.volume」に保存 - ./mysql/init.d:/docker-entrypont-initdb.d「app.db.volume」という名前のボリュームにデータベースを保存することにしたので、「volumes」を設定します。
docker-compose.yml(抜粋)volumes: app.db.volume: name: app.db.volumenginx の設定
uWSGI の設定もよく分かりません。正直そこまで uWSGI のことをガッツリ調べる気にならんかった。すまん。
次のとおり、適当にパラメータを設定しておけばいいらしい。しらんけど。
./nginx/uwsgi_paramsuwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;で、nginx の設定なのですが、まず uWSGI でソケット通信するためのポート 8001 番を開けてやる必要があります。
また、nginx の待ち受けポートを設定します。私の設定は 80 番になっているのですが、これはコンテナを動かす仮想マシンの設定で、ゲスト(仮想マシン)の 80 番を流れるデータを、ホストの 8081 番に流すように設定しているからです。
これで、ホストのブラウザで「127.0.0.1:8081」にアクセスすれば、ゲストで動く Web アプリが見られるというわけ。
./nginx/conf/app_nginx.confupstream django { ip_hash; server python:8001; # uWSGI で Django と nginx とが通信するためのポート } server { listen 80; # 待ち受けポート server_name 127.0.0.1; charset utf-8; location /static { alias /static; } client_max_body_size 75M; location / { uwsgi_pass django; include /etc/nginx/uwsgi_params; } } server_tokens off;この設定に基づいて、
docker-compose.yml
で nginx を設定すると、次のとおりになります。Django の設定と同様に、./static
を/static
にマウントしているので、Django で処理しない静的コンテンツは nginx でレスポンスを返せます。docker-compose.yml(抜粋)nginx: image: nginx:1.17 # nginx のイメージを適当に取ってくる restart: unless-stopped # コンテナが異常停止した場合は再起動する container_name: nginx # コンテナ名を定義 networks: - django_net # ネットワーク名「django_net」を指定 ports: - "80:80" # 通信するポート番号を指定 volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static # ./static に /static をマウントする depends_on: - pythonあと、ここまで「django_net」を使うと言っておきながら全然設定していなかったので、忘れずに「networks」の項目も設定します。
docker-compose.yml(抜粋)networks: django_net: driver: bridgeDjango プロジェクトの設定
さて、ここまでで Docker の設定は全部終わりました。あとは Django 本体の設定を残すだけです。
まずはプロジェクトを立ち上げましょう。
$ docker-compose run python django-admin.py startproject app .このコマンドを叩いた後で
src
とstatic
の中を覗くと、いろいろと Django 用のファイルができているはず。次に、
./src/app/settings.py
を編集します。./src/app/settings.pyDATABASES = { 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # コメントアウト 'ENGINE': 'django.db.backends.mysql', # 追加 # 'NAME': os.path.join(BASE_DIR, 'app_db'), # コメントアウト 'NAME': 'app_db', # init.sql で CREATE したデータベース名と同じことを確認 'USER': 'app_user', # 追加 'PASSWORD': '*****', # 追加(環境変数で指定したパスワードと同じもの) 'PORT': '3306', # 追加 'HOST': 'db', # 追加 } } STATIC_URL = '/static/' # 追加 STATIC_ROOT = '/static/' # 追加 ...(中略) LANGUAGE_CODE = 'ja' # 編集 TIME_ZONE = 'Asia/Tokyo' # 編集あとは Django でおなじみのコマンドを docker-compose で叩くだけです。
$ docker-compose run python ./manage.py migrate # DBマイグレーション $ docker-compose run python ./manage.py createsuperuser # 管理者の設定 $ docker-compose run python ./manage.py collectstatic # 静的ファイルをコピーコンテナの起動と停止
さて、いよいよコンテナを動かします。
出でよ Django!$ docker-compose up -d --rmそして、ホストのブラウザから 127.0.0.1:8081 にアクセスすると…
やったぜ!!
ハロー!Docker!
ハロー!Django!コンテナを停止する時は、
stop
します。--rm
オプションを付けて起動したので、停止させるとコンテナは削除されます。$ docker-compose stop
無慈悲なエラーメッセージたち
さて、こんな感じで記事にまとめると、まるで私がサクサクと設定したように誤解されそうですので、どれほど七転八倒しながら紆余曲折を経たかを書き残しておこうと思います。
まずこのエラーメッセージ。
ERROR: The Compose file './docker-compose.yml' is invalid because: services.pithon.volumes contains an invalid type, it should be an arrayなんやねんこれ、何が悪いねん、と憤りながらググりまくったのですが、直接的な解決法は見つかりません。
ふと「it should be array」(そこは配列のはずなのに、そうなってないでしょ)がポイントであることに気づき、YAML の構文を調べたところ……「YAML では、行頭に『-』をつけることで配列を表現します。『-』のあとには半角スペースを入れてください」という記載が。
よく見ると、ここ、
volumes: - ./src:/codeが、
volumes: -./src:/code # 「-」 の後に半角スペースがないこうなってた。これに気づくのに2時間かかった。
このクソ計算機が!!お前は親戚のめんどくさいオッサンか!!
たった半角スペースが1個なかっただけでこの仕打ち。もうやってらんない。そもそも「ヤムル」とかいう名前が気に食わない。韓国料理かよ(それはナムル)。次に、このエラーメッセージ。
Unsupported config option for services.nginx: 'deponds_on'よく見たら分かるのですが、
deponds_on
ではなく、depends_on
が正しい。アホみたいなスペルミスだが、これに気づくのに2時間かかった。このクソ計算機が!!私の貴重な人生の時間を返せ!!
ディープラーニングで顔認識とか正直どうでもよくて、この手のヒューマンエラーをそっと修正してくれる気の利いた計算機が欲しい。はい、さらにこのエラーメッセージ。
ERROR: Service 'nginx' depends on service 'python' which is undefined.なんでこんなエラーが出ていたかというと、
python
をpithon
とミスタイプしていたから(最初のエラーメッセージ参照)。これに気づくのに1時間かかった。このクソ計算機が!!てめえの頭はハッピーセットかよ!!
2コマ漫画みたいな展開に我ながら愕然としますが、Dvorak 配列では、y と i が上下で隣り合わせであることが原因と思われます。次回をお楽しみに
さて、私がどれほど計算機を憎んでいるか、なぜ大学院で機械学習を専攻しようと考えたか、そしてなぜ研究者もエンジニアも辞めたか、お分かりいただけたでしょうか(本稿の趣旨とは全然違う話)。
オモチャとして遊ぶにはちょうどいいが、これを仕事にする気にはなれない。
要するに、どう見ても無才です。本当にありがとうございました。最後は大変ハートウォーミングなオチになってしまいましたが、次回は AWS に本番環境を作っていきたいと思います。乞うご期待。
- 投稿日:2020-10-27T17:40:40+09:00
sqlacodegenを使って既存のMySQLサーバからSQLAlchemyのテーブル定義を生成する
まっさらなAmazonLinux2でやるときの手順です
実行環境
OS: AmazonLinux2 (Linux version 4.14.193-149.317.amzn2.x86_64)
python: Python 2.7.18やること
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py python get-pip.py pip install sqlacodegen pymysql sqlacodegen mysql+pymysql://user:password@sqlhost/dbnameオプション:
--outfile
--tables
これで標準出力に全テーブルのSQLAlchemy定義が出力されます公式サイトには
oursql
というのを使うと書いてありますが、この環境では下記エラーが出るためpymysql
を使います
https://pypi.org/project/sqlacodegen/Traceback (most recent call last): File "/home/ec2-user/.local/bin/sqlacodegen", line 8, in <module> sys.exit(main()) File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlacodegen/main.py", line 44, in main engine = create_engine(args.url) File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/__init__.py", line 500, in create_engine return strategy.create(*args, **kwargs) File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/strategies.py", line 61, in create entrypoint = u._get_entrypoint() File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/engine/url.py", line 172, in _get_entrypoint cls = registry.load(name) File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 254, in load loader = self.auto_fn(name) File "/home/ec2-user/.local/lib/python2.7/site-packages/sqlalchemy/dialects/__init__.py", line 32, in _auto_fn dialect, driver = name.split(".") ValueError: too many values to unpack
- 投稿日:2020-10-27T17:20:37+09:00
【Laravel】初めてのCRUD機能
Laravelでの単純なCRUD機能作成時の備忘録。
最終的なブラウザ表示
最終的なコード
■ ルーティング
実装機能 HTTPメソッド メソッド 一覧表示 get index 新規作成 get → post create → store 詳細表示 get show 編集 get → post edit → update 削除 delete destroy web.phpRoute::get('/', function () { return redirect('/articles'); }); Route::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list'); // 一覧表示ページ Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new'); // 新規投稿ページ Route::post('/article', [App\Http\Controllers\ArticleController::class, 'store'])->name('article.store'); // 新規保存 Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show'); // 詳細表示ページ Route::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete'); // 削除 Route::get('/article/edit/{id}', [App\Http\Controllers\ArticleController::class, 'edit'])->name('article.edit'); // 編集ページ表示 Route::post('/article/update/{id}', [App\Http\Controllers\ArticleController::class, 'update'])->name('article.update'); // 更新■ コントローラー
Articleコントローラー<?php namespace App\Http\Controllers; use App\Models\Article; use Illuminate\Http\Request; class ArticleController extends Controller{ // 一覧表示 public function index(Request $request){ if ($request->filled('keyword')) { $keyword = $request->input('keyword'); $message = '検索キーワード: '.$keyword; $articles = Article::where('content', 'like', '%'.$keyword.'%')->get(); }else{ $message = "検索キーワードを入力してください。"; $articles = Article::all(); } return view('index', ['message' => $message, 'articles' => $articles]); } // 新規投稿ページ表示 public function create(Request $request){ $message = '投稿フォーム: '; return view('new', ['message'=>$message]); } // 新規保存 public function store(Request $request){ $article = new Article(); $article->content = $request->content; $article->user_name = $request->user_name; $article->save(); return redirect()->route('article.show', ['id' => $article->id]); } // 詳細表示 public function show(Request $request, $id, Article $article){ $message = '記事の内容: '; $article = Article::find($id); return view('show', ['message' => $message, 'article' => $article]); } // 編集ページ表示 public function edit(Request $request, $id, Article $article){ $message = '記事の編集: '.$id; $article = Article::find($id); return view('edit', ['message'=>$message, 'article'=>$article]); } // 更新 public function update(Request $request, $id, Article $article){ $article = Article::find($id); $article->content = $request->content; $article->user_name = $request->user_name; $article->save(); return redirect()->route('article.show', ['id' => $article->id]); } // 削除 public function destroy(Request $request, $id, Article $article){ $article = Article::find($id); $article -> delete(); return redirect("/articles"); } }■ ビュー
- Bootstrapを使用。
- 共通部分
layout.blade.php<!DOCTYPE html> <html lang="ja"> <head> <meta charset='utf-8'> <title>Laravel_sample</title> <style>body {padding: 10px;}</style> @include('style-sheet') <!-- Bootstrapの読み込み --> </head> <body> @include('nav') <!-- ナビゲーションバー --> <div class='container'> @yield('content') </div> </body> </html>
- ナビゲーションバー
nav.blade.php<nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'> <a class='navbar-brand' href='{{ route("article.list") }}'>My Articles?</a> </nav>style-sheet.blade.php<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous"> <style>body {padding-top: 80px;}</style> <!-- 設置位置の調節 -->- 一覧表示ページ(root)
一覧表示ページ(index.blade.php)@extends('layout') @section('content') <h1>一覧表示ページ</h1> <p>{{ $message }}</p> @include('search') <!-- 検索フォーム --> <table class="table table-striped table-hover"> @foreach ($articles as $article) <tr> <td> <a href='{{ route("article.show", ["id" => $article->id]) }}' > {{ $article -> content }} </a> </td> <td>{{ $article -> user_name }}</td> <td>{{ $article -> created_at }}</td> </tr> @endforeach </tabel> <div> <a href = '{{ route("article.new") }}' class="btn btn-primary">投稿</a> </div> @endsection- 新規投稿ページ
新規投稿ページ(new.blade.php)@extends('layout') @section('content') <h1>新規投稿ページ</h1> <p>{{ $message }}</p> {{ Form::open(['route'=>'article.store']) }} <div class='form-group'> {{ Form::label('content', '内容:') }} {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }} </div> <div class='form-group'> {{ Form::label('user_name', '投稿者名: ') }} {{ Form::text('user_name', null, ['placeholder'=>'投稿者名を入力']) }} </div> <div class='form-group'> {{ Form::submit('投稿', ['class' => 'btn btn-primary']) }} <!-- 投稿ボタン --> <a href='{{ route("article.list") }}'>もどる</a> <!-- indexページへのリンク --> </div> {{ Form::close() }} @endsection- 詳細表示ページ
詳細表示ページ(show.blade.php)@extends('layout') @section('content') <h1>詳細表示ページ</h1> <p>{{ $message }}</p> <p>{{ $article -> content }}</p> <p>{{ $article -> user_name }}</p> <p>{{ $article -> created_at }}</p> <p> <a href='{{ route("article.edit", ["id"=> $article->id]) }}' class="btn btn-secondary">編集</a> <a href = '{{ route("article.list") }}' class="btn btn-outline-secondary">戻る</a> </p> <div> {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article -> id]]) }} {{ form::submit("削除ボタン") }} {{ Form::close() }} </div> @endsection- 編集ページ
編集ページ(edit.blade.php)@extends('layout') @section('content') <h1>編集ページ</h1> <p>{{ $message }}</p> {{ Form::model($article, ['route'=> ['article.update', $article->id] ]) }} <div class='form-group'> {{ Form::label('content', '内容:') }} {{ Form::text('content', null) }} </div> <div class='form-group'> {{ Form::label('user_name', '投稿者名: ') }} {{ Form::text('user_name', null) }} </div> <div class='form-group'> {{ Form::submit('保存', ['class' => 'btn btn-primary']) }} <a href='{{ route("article.show", ["id"=> $article->id]) }}'>もどる</a> </div> {{ Form::close() }} @endsection- 検索フォーム
- 部分テンプレートで作成し、indexページに設置。
検索フォーム(search.blade.php){{ Form::open(['method' => 'get']) }} {{ csrf_field() }} <div class='form-group'> {{ Form::label('keyword', 'キーワード:') }} {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }} </div> <div class='form-group'> {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }} <a href='{{ route("article.list") }}'>クリア</a> </div> {{ Form::close() }}各論
前提
- MySQLに、Articlesテーブル(content、user_nameカラム)作成済みの状態とする。
- 動作確認のために、rootページにコントローラーからデータを送れるか?、表示できるか? 程度は確認しとく。
参)コントローラー作成コマンド% php artisan make:controller articleController動作確認(ルーティング→コントローラー→indexビュー)// コントローラー public function index(){ $message = '動作確認用'; return view('index', ['message' => $message]); // indexビューに、message を渡してみる } // ルーティング Route::get('/', function () { return redirect('/articles'); }); // ビュー(index.brade.php で、messageを取得してみる) <!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>Laravel_sample</title> <style>body {padding: 10px;}</style> </head> <body> <p>{{ $message }}</p> </body> </html>CRUD機能 の実装
■ 一覧表示(index)
- コントローラー(モデルから全データを取得し、ビューに送る) → ビュー(一覧表示) の流れ。
/app/Http/Controllers/ArticleController.phppublic function index(){ $message = 'Hello world'; $articles = Article::all(); // モデルから全データ取得 return view('index', ['message' => $message, 'articles' => $articles]); // ビューに渡す }/resources/views/index.blade.php@extends('layout') // 部分テンプレートを呼び出す @section('content') <h1>一覧表示ページ</h1> <p>{{ $message }}</p> // コントローラーから $message 取得 @foreach ($articles as $article) // コントローラーから $article をループで取得 <p>{{ $article->content }}</p> @endforeach @endsection■ 詳細表示(show)
- コントローラー(モデルから1つの指定データ取得し、ビューに送る) → ビュー(詳細表示) の流れ。
/app/Http/Controllers/ArticleController.phppublic function show(Request $request, $id, Article $article){ // id、記事情報が必要 $message = '詳細表示: '.$id; $article = Article::find($id); // モデルから指定データ取得 return view('show', ['message' => $message, 'article' => $article]); // ビューに渡す }
- 詳細表示ページを作成。
/resources/views/show.blade.php<h1>詳細表示ページ</h1> <p>{{ $message }}</p> <p>{{ $article->content }}</p> <p> <a href='{{ route("article.list") }}'>戻る</a> // 一覧表示ページへのリンク (例:ルーティング名「article.list」と名付けてる) </p>
- 一覧表示ページに、詳細表示ページへのリンクを貼る。
/resources/views/index.blade.php@foreach ($articles as $article) <p> <a href='{{ route("article.show", ["id" => $article->id]) }}'> {{ $article->content }} {{ $article->user_name }} {{ $article->created_at }} </a> </p> @endforeach■ 新規投稿(create、store)
- 投稿ページへのルーティングを設定。
- 記述順序に注意!! newよりshowを先に記述するとエラー!
/article/{id}
を探しに行っちゃうから。/routes/web.phpRoute::get('/articles', [App\Http\Controllers\ArticleController::class, 'index'])->name('article.list'); // 一覧表示ページ Route::get('/article/new', [App\Http\Controllers\ArticleController::class, 'create'])->name('article.new'); // 新規投稿ページ Route::get('/article/{id}', [App\Http\Controllers\ArticleController::class, 'show'])->name('article.show'); // 詳細表示ページ
- コントローラーに、投稿ページ表示のためのメソッド(create)を追加。
/app/Http/Controllers/ArticleController.phppublic function create(Request $request){ // createでは、取得したい情報はない // 確認用:フォームを使わずに、固定テキストや日時を格納する場合 (※ storeメソッドは不要) $article = new Article(); // インスタンス作成 $article->content = '投稿内容'; $article->user_name = '投稿者'; $article->save(); return redirect('/articles'); // 保存したら、indexページにリダイレクト // フォームから、投稿する場合 $message = '投稿フォーム: '; return view('new', ['message'=>$message]); // newページへ送る }
- 続いて、保存のためのメソッド(store)も追加。
app/Http/Controllers/ArticleController.phppublic function store(Request $request){ $article = new Article(); // インスタンス作成 $article->content = $request->content; // 投稿内容 $article->user_name = $request->user_name; // 投稿者名 $article->save(); // 保存 // レコード保存後に、showページへデータを渡してリダイレクト return redirect('article.show', ['id' => $article->id]); }
- indexページに投稿ページへのリンクを設置。
- 上で、新規投稿ページへのルーティング名は、「article.new」と名付けたので、それを使用。
/resources/views/index.blade.php<div> <a href='{{ route("article.new") }}'>新規投稿</a> // 新規投稿ページへのリンク </div>
- 最後に、投稿フォームを作成する。
- 投稿フォームを部分テンプレートとして作成。
- Laravelでのフォーム設置には、laravelcollective/html ライブラリ のインストールが必要。
注意)インストールを忘れると、Class 'Form' not found
エラー!!ターミナルでフォーム使用時に必要なライブラリをインストール% composer require laravelcollective/html // メモリ制限エラー( Allowed memory size of 1610612736 bytes exhausted)の場合は、メモリを上げてインストールしてみる % php -d memory_limit=-1 /usr/local/bin/composer require laravelcollective/html % composer info // インストール済みか確認resources/views/new.blade.php@extends('layout') @section('content') <h1>投稿フォーム</h1> <p>{{ $message }}</p> {{ Form::open(['route' => 'article.store']) }} <!-- 投稿フォーム --> <div class='form-group'> <!-- 投稿内容の記述エリア --> {{ Form::label('content', '内容:') }} {{ Form::text('content', null, ['placeholder'=>'内容を入力']) }} </div> <div class='form-group'> <!-- 投稿者名の記述エリア --> {{ Form::label('user_name', '投稿者:') }} {{ Form::text('user_name', null, ['placeholder'=>'投稿者を入力']) }} </div> <div class="form-group"> <!-- ボタン設置 --> {{ Form::submit('投稿ボタン', ['class' => 'btn btn-primary']) }} <a href='{{ route("article.list") }}'>一覧に戻る</a> </div> {{ Form::close() }} @endsection■ 編集 (edit、 update)
- 編集ページ表示機能を実装(editメソッド)。
app/Http/Controllers/ArticleController.phppublic function edit(Request $request, $id, Article $article){ // 編集には、id情報 と 記事データが必要 $message = '記事の編集: '.$id; // 表示用 $article = Article::find($id); // 編集するレコードをid情報から取得 return view('edit', ['message'=>$message, 'article'=>$article]); // 編集ページに渡す }
- 編集フォームを作成する。
resources/views/edit.blade.php@extends('layout') @section('content') <h1>編集フォーム</h1> <p>{{ $message }}</p> <!-- Form::model : 編集内容を表示してくれる --> {{ Form::model($article, ['route' => ['article.update', $article->id]]) }} <div class='form-group'> {{ Form::label('content', '内容:') }} {{ Form::text('content', null) }} </div> <div class='form-group'> {{ Form::label('user_name', '投稿者:') }} {{ Form::text('user_name', null) }} </div> <div class="form-group"> {{ Form::submit('保存ボタン', ['class' => 'btn btn-primary']) }} <a href='{{ route("article.show", ["id" => $article->id]) }}'>もどる</a> </div> {{ Form::close() }} @endsection
- showページに、編集ボタンを設置。
resources/views/show.blade.php<a href='{{ route("article.edit", ["id" => $article->id]) }}' class='btn btn-outline-primary'>編集ボタン</a>
- 編集内容を保存する更新機能を実装(updateメソッド)。
app/Http/Controllers/ArticleController.phppublic function update(Request $request, $id, Article $article){ $article = Article::find($id); $article->content = $request->content; $article->user_name = $request->user_name; $article->save(); return redirect()->route('article.show', ['id' => $article->id]); }■ 削除(delete)
Laravelでは、ブラウザとの通信にHTTPメソッドを指定するが、ブラウザが deleteメソッドをサポートしてないので、POSTで隠しメソッドを送って、コントローラーメソッドのdestroyを実行してもらうイメージ。
まず、削除用のルーティングを設定。
/routes/web.phpRoute::delete('/article/{id}', [App\Http\Controllers\ArticleController::class, 'destroy'])->name('article.delete');
- コントローラーにアクション(destroy)を定義。
/app/Http/Controllers/ArticleController.phppublic function destroy(Request $request, $id, Article $article){ // 削除するレコードのid、記事データ が必要 $article = Article::find($id); // id情報から、レコードを取得 $article->delete(); // 削除 return redirect('/articles'); // 削除後に、一覧表示ページへリダイレクト }
- 詳細表示ページに、削除ボタンを設置。
/resources/views/show.blade.php<div> {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article->id]]) }} {{ Form::submit('削除ボタン') }} {{ Form::close() }} </div>■ 検索
- 削除と同じく、Laravelでフォームを使うには、「laravelcollective/html」ライブラリが必要。
参)laravelcollective/htmlのインストール% composer require laravelcollective/html % composer info // インストール済みかの確認
- Laravelでのフォームは、
{{ Form:: ... }}
に記述する(Formファサードという)。
(※ ファサード(facade)とは、建物の入り口という意味。)- 1. まず、検索フォームの部分テンプレートを作成する。
- GETメソッドによって、text型で、キー:「keyword」に値を格納してコントローラーにデータを送る。
resources/views/search.blade.php{{ Form::open(['method' => 'get']) }} {{ csrf_field() }} <!-- CSRFトークン --> <div class='form-group'> {{ Form::label('keyword', 'キーワード:') }} {{ Form::text('keyword', null, ['class' => 'form-control'], ['placeholder'=>'キーワードから記事を検索できます']) }} </div> <div class='form-group'> {{ Form::submit('検索', ['class' => 'btn btn-outline-primary']) }} <!-- 検索ボタン --> <a href='{{ route("article.list") }}'>クリア</a> <!-- indexページへのリンク --> </div> {{ Form::close() }} <!-- フォーム終了タグ -->
- 一覧表示ページに、部分テンプレートを読み込む。
resources/views/index.blade.php<p>{{ $message }}</p> @section('content') @include('search') <!-- ココで、検索フォームを読み込み --> @endsection
- 2. 次に、検索機能の実装。
- コントローラーで、フォームから送られたデータを取得し、該当する投稿をビューに渡して表示させる。
app/Http/Controllers/ArticleController.phppublic function index(Request $request){ // GETメソッドで受け取ったデータは、引数に指定した変数$requestで取得できる if ($request->filled('keyword')) { // データの有無で条件分岐 $keyword = $request->input('keyword'); // $request(フォームで送られたデータ) の中から、「keyword」を取得して、変数に定義 $message = '検索結果: '.$keyword; // ビュー表示のため、変数に格納 $articles = モデル名::where('検索するカラム名', 'like', '%'.$keyword.'%')->get(); }else{ // 未入力の場合 $message = "検索キーワードを入力してください。"; $articles = モデル名::all(); // 未入力なら、全データ表示 } return view('検索結果を送りたいビュー名', ['message' => $message, 'articles' => $articles]); // ビューに渡す }✔️ モデル名::where()
- 指定条件で、配列を絞り込む。
検索条件の指定(where)use Illuminate\Support\Arr; $array = [100, '200', 300, '400', 500]; $filtered = Arr::where($array, function($value, $key) { return is_string($value); // string型のみ取得。 [1=>'200', 3=>'400'] });✔️ filled( )
- 値が空でないか?の判定。
- blank メソッドの逆の動作。
空でないか?の判定(filled関数)hoge1 = 'value'; $request->filled(hoge1); // true : キーが存在、値が空でない $request->filled(hoge2); // false : キーが存在しない or 空参) artisan tinker (対話型コンソール)
- artisan tinker:対話型コンソール
- 手っ取り早く、コンソールからDBにデータを登録したい時などに便利。
対話型コンソールの使い方% php artisan tinker // 対話型コンソールの起動 >>> Article::all() // 全データ取得 >>> $article = Article::find(1) // 指定idのレコードを取得 >>> $article->content // カラムを出力 >>> $article // レコード出力 >>> $article->delete() // レコード削除 // レコード追加 >>> $article = new Article() // インスタンス作成 >>> $article->content = 'テスト' // 変数に格納 >>> $article->save() // データ保存 >>> exit // CTRL + C でもok(おまけ) 関連づけたテーブルからのデータ取得
例)articlesテーブル、categoriesテーブルのデータを表示したい// モデル にリレーションを定義 (例: Articleモデルは、Categoryモデルに対して、1対多の場合) public function category() { return $this->belongsTo('App\Models\Category'); } // ビュー( ※変数はコントローラーで定義 ) {{ $article -> name }} // articlesテーブルのnameカラムを表示 {{ $article -> category -> name }} // articlesテーブルに関連付けたcategoriesテーブルのnameカラムを表示(おまけ) 関連づけたテーブルへのデータ保存
例)articlesコントローラーから、categoriesテーブルにデータ保存したい// コントローラー use App\Models\Category; // Categoryモデルの使用を宣言 : public function create(){ $categories = Category::all()->pluck('name', 'id'); // Categoryモデルから、nameとidカラムだけ取得 return view('new', ['article' => $article, 'categories'=> $categories] ); } public function store(Request $request){ $article = new Article(); $article -> content = request('content'); $article -> user_name = request('user_name'); $article -> category_id = request('category_id'); $article -> save(); return redirect() -> route('article.detail', ['article' => $article->id] ); } // ビュー {{ Form::open(['route' => 'shop.store']) }} {{ Form::text('content', null) }} // articlesテーブルへのデータ保存 {{ Form::select('category_id', $categories) }} // categoriesテーブルのデータ選択 {{ Form::submit('登録') }} {{ Form::close() }}
- 投稿日:2020-10-27T16:11:31+09:00
ISUCON10 Online event
こんにちは, This is Santosh.I participated in ISUCON2020 Online event, It was held on September 12,2020.
Rules and regulations are mentioned on the official sites. please go through once(~5 minutes)
http://isucon.net/archives/54704557.html
Around 500~ teams participated on that day, It was fun as my first isucon! let's move to the technical side,
There was an application given「isummo」、It was about finding chair(isu in japanese) by different conditions. The goal was to improve the search time and processing speed of isummo application.
lightly just go through the following;
・http://sinatrarb.com/
・new relic
ruby version check-install,start mysql(may be it will helpful next time too)
If any login issue with mysql just try this or follow stackoverflow↓
mysql -u root -p
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_passwAbout coding language, you can choose any language Rust, Python, Go, Php or whatever you are comfortable with,I did with ruby as other team member were very familiar with it.Now the question is how to optimize our isummo app?
① Check all SQL queries -> Add index, as no index key was there
eg. SQL -> select * from isuumo.chair where kind = 'some value'
check kind is indexed or not ,if not -> KEYkind
(kind
)
adding index is easy as soon as you find the query, and it's fasten our app to some level.(rank was going up & team was enjoying)② Index based on order BY ASC OR DESC
eg; select * from issumo.desk where some_condition order by popularity DESC;
Please not here, order by have column by DESC, and the default index we add earlier will be ASC .To speedup execution of query make it, DESC INDEX, -> KEYpopularity
(popularity
DESC)
https://dev.mysql.com/doc/refman/8.0/en/descending-indexes.html
After this our app performance increased a bit.③ Next, find n+1,source code and try to fix that,
this was a bit tough ,I tried but there was no such improvement in speed.
Other things can be done on infrastructure side,as other member was doing that part I don't have much idea about it. Next isucon will try to cover up infra as well.
Good luck everyone for next event!
- 投稿日:2020-10-27T08:02:31+09:00
bash+WSL+MySQLを使用したTXT形式ファイルのDB IMPORT
1.実行前
==USER IMPORTからEND==迄にUSERテーブルにINSERTしたいデータが格納されている
DATA_SAMPLE1.TXT... ==USER IMPORT ckhaorus ckhaorus pawpOMFPAOKWDDK41po2kldaodkPAOWK matchan matchan HcrUC/zoaeQjF4XLVQbYk1TkHjhHkEkD+z8DFtc9SrY= END== ==SCHEDULE IMPORT 2019/10/27 01:42:32 2019/10/27 02:15:16 ...2.処理内容
text_import_env.sh#!/bin/bash ### ディレクトリ名 INPUT_DIR="/home/ckhaorus/csv-import/INPUT_DIR" WORK_DIR="/home/ckhaorus/csv-import/WORK_DIR" ### MYSQL MYSQL_HOST="127.0.0.1" MYSQL_USER="root" MYSQL_PWD="root" MYSQL_DB="sample" ###返却値 END_STATUS=0 # 正常終了 INVALID_DIRECTORY_STATUS=10 # INPUT_DIR不正 function log(){ echo -e "$(date '+%Y-%m-%dT%H:%M:%S') $1" } function infStr(){ log "INFO SH10001[]: $1: START[]" } function infEnd(){ log "INFO SH10002[]: $1: END[] STATUS CODE: $2" exit "$2" } function inputFileIsNotFoundErr(){ log "ERROR SH20001[]: $1: INPUT FILE IS NOT FOUND." }text_import.sh#!/bin/bash ### ファイル名 CONFIGURATION_FILE="text_import_env.sh" BASEFILE_NAME="text_import.sh" ### ファイルパターン INPUT_FILE_PATTERN=".*DATA_[A-Z]+[1-9].TXT.*" DATA_START_PATTERN="==USER IMPORT" DATA_END_PATTERN="END==" LINE_NO_PATTERN="s/:.*//g" # 設定ファイルの読み込み source "${CONFIGURATION_FILE}" # 処理の開始 infStr "${BASEFILE_NAME}" # INPUT_DIR内にファイルが配置されているか確認 if [ ! "$(ls "${INPUT_DIR}")" ]; then # ファイルが配置されていない場合、ERRORログ「SH20001」: INPUT_DIR内にファイルが存在しないを表示し、シェルを終了する。 inputFileIsNotFoundErr "${BASEFILE_NAME}" infEnd "${BASEFILE_NAME}" "${INVALID_DIRECTORY_STATUS}" fi # WORK_DIR内の全ディレクトリ・ファイルに対して削除を行い、WORK_DIRを初期化する。 rm -rf "${WORK_DIR:?}/"* # INPUT_DIRからFILE_PATTERNにマッチする全ファイルに対して、WORK_DIRに移動する。 find "${INPUT_DIR}" -type f -regextype posix-egrep -regex "${INPUT_FILE_PATTERN}" | xargs -I% mv % "${WORK_DIR}" # WORK_DIR内のFILE_PATTERNにマッチする全ファイルに対してループ for filepath in $(find "${WORK_DIR}" -type f -regextype posix-egrep -regex "${INPUT_FILE_PATTERN}"); do # ファイル名取得 base_filename=$(basename "${filepath}") # 出力ファイル名取得 data_filename=$(echo "${base_filename}" | cut -d "." -f 1)"_USER.TXT" # データの開始~終了行取得 start_line=$(grep -no "${DATA_START_PATTERN}" "$filepath" | sed -e "${LINE_NO_PATTERN}") end_line=$(grep -no "${DATA_END_PATTERN}" "$filepath" | sed -e "${LINE_NO_PATTERN}") # 行番号指定でデータを抜き出し、<元ファイル名_USER.TXT>に出力する。 sed -n "${start_line},${end_line}p" "$filepath" > "${WORK_DIR}/${data_filename}" done # user_import.shを起動 bash user_import.sh # WORK_DIR内の全ディレクトリ・ファイルに対して削除を行い、WORK_DIRを初期化する。 rm -rf "${WORK_DIR:?}/"* # 処理の終了 infEnd "${BASEFILE_NAME}" "${END_STATUS}"user_import.sh#!/bin/bash ### ファイル名 CONFIGURATION_FILE="text_import_env.sh" BASEFILE_NAME="user_import.sh" ### ファイルパターン USER_FILE_PATTERN=".*DATA_[A-Z]+[1-9]_USER.TXT.*" ### テーブル名 TEMP_TABLENAME="temp_user" # 環境設定ファイルの読み込み source "${CONFIGURATION_FILE}" # WORK_DIR内のFILE_PATTERNにマッチする全ファイルに対してループ for filepath in $(find "${WORK_DIR}" -type f -regextype posix-egrep -regex "${USER_FILE_PATTERN}"); do while read line; do if [[ ${line:0:1} =~ [a-z] ]]; then # ファイル名取得 base_filename=$(basename "$filepath") # 出力ファイル名取得 csv_filename=$(echo "${base_filename}" | cut -d "." -f 1)".CSV" # 各カラムの取得 login_id=$(echo "${line}" | awk '{print $1}') name=$(echo "${line}" | awk '{print $2}') password=$(echo "${line}" | awk '{print $3}') # TODO:LOAD DATA LOCAL INFILE文が使用不可 # echo "\"${login_id}\",\"${name}\",\"${password}\"" >> "${WORK_DIR}/${csv_filename}" # temp_userテーブルに対してINSERTを行う。 mysql -h "${MYSQL_HOST}" -u"${MYSQL_USER}" -p"${MYSQL_PWD}" --database="${MYSQL_DB}" -e "INSERT INTO ${TEMP_TABLENAME} (login_id, name, password) VALUES ('${login_id}', '${name}', '${password}');" fi done < "${filepath}" done3.出力
正常系.sh2020-10-27T07:56:46 INFO SH10001[]: text_import.sh: START[] 2020-10-27T07:56:47 INFO SH10002[]: text_import.sh: END[] STATUS CODE: 0異常系.sh2020-10-27T07:57:26 INFO SH10001[]: text_import.sh: START[] 2020-10-27T07:57:26 ERROR SH20001[]: text_import.sh: INPUT FILE IS NOT FOUND. 2020-10-27T07:57:26 INFO SH10002[]: text_import.sh: END[] STATUS CODE: 10
- 投稿日:2020-10-27T00:11:34+09:00
冴えない彼氏のKH-coderの使い方(Mac編)
はじめに
KH-coderをMacで使おうと思いたち、インストールしようとしたのである。その際、参考となるサイトが少ないことに辟易し、ここにその方法を記そうと思い立った次第である。
(決してcpanmをclean upしようとして、sudo rm -rfして 3ヶ月間のデータを失ったからでない←聞かれてない)後、タイトルに関しては察してくれ。
実行環境
MacBook Pro(15-inch,2018)
プロセッサ 2.9 Ghz Intel Core i9
メモリ 32GB 2400 MHz DDR4
OS:macOS Mojave(ver.10.14.6)前準備
X-code,Homebrewをインストールできていない場合
X-codeのインストール
xcode-select --install
Homebrewをインストール
Homebrewの公式サイト(https://brew.sh/index_ja.html) にインストールコマンドが掲載されているので、それに載っているスクリプトをターミナルを開き実行。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"インストール後の確認
brew doctor #インストール完了後の確認 brew --versionHomebrewでGitをインストール
brew install git
準備
XQuartzをインストール
このサイト(https://www.xquartz.org/index.html )のdmgをインストール
Stanford-Posttaggerをインストール
Stanford Log-linear Part-Of-Speech Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)の公式サイトからインストールする。
openjdkのインストール
brew install openjdk13
mecabのインストール
brew install mecab brew install mecab-ipandicmysqlのインストール
brew install mysql
インストール終了後
brew services start mysql ==> Successfully started `mysql` (label: homebrew.mxcl.mysql) #成功すると上記のように表示される起動後、次のコマンドを実行しパスワードを設定する。
コマンドを実行すると、最初にVALIDATE PASSWORD PLUGINという強固なパスワード設定を助けるプラグインを使用するかどうかを質問される。
(今回私はローカルでの使用しか想定していため、何も入力しなかった。)次にrootユーザーのパスワードを設定する
mysql_secure_installation(今回はパスワードを設定せず、次に進んだ)
mysql -u root
これで
(base) user:~ user$ mysql -u root Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 8.0.22 Homebrew Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>となれば成功。
mysqlの起動,終了
起動
brew services start mysqlもしくは
mysql.server start ####終了 ```bash brew services stop mysqlもしくは
mysql.server stopRのインストール
brew cask install R
R単体と同時にRstudio(https://rstudio.com/products/rstudio/ )も一緒にインストールした
以下KH-coderを動かすために必要なパッケージ
install.packages("ade4", dependencies=TRUE) install.packages("amap", dependencies=TRUE) install.packages("Cairo", dependencies=TRUE) install.packages("cluster", dependencies=TRUE) install.packages("codetools", dependencies=TRUE) install.packages("colorspace", dependencies=TRUE) install.packages("dichromat", dependencies=TRUE) install.packages("foreign", dependencies=TRUE) install.packages("ggdendro", dependencies=TRUE) install.packages("ggplot2", dependencies=TRUE) install.packages("ggnetwork", dependencies=TRUE) install.packages("ggsci", dependencies=TRUE) install.packages("gtable", dependencies=TRUE) install.packages("igraph", dependencies=TRUE) install.packages("KernSmooth", dependencies=TRUE) install.packages("lattice", dependencies=TRUE) install.packages("maptools", dependencies=TRUE) install.packages("MASS", dependencies=TRUE) install.packages("Matrix", dependencies=TRUE) install.packages("mgcv", dependencies=TRUE) install.packages("munsell", dependencies=TRUE) install.packages("nlme", dependencies=TRUE) install.packages("nnet", dependencies=TRUE) install.packages("permute", dependencies=TRUE) install.packages("pheatmap", dependencies=TRUE) install.packages("plyr", dependencies=TRUE) install.packages("proto", dependencies=TRUE) install.packages("RcolorBrewer", dependencies=TRUE) install.packages("Rcpp", dependencies=TRUE) install.packages("reshape2", dependencies=TRUE) install.packages("rgl", dependencies=TRUE) install.packages("rpart", dependencies=TRUE) install.packages("scales", dependencies=TRUE) install.packages("scatterplot3d", dependencies=TRUE) install.packages("slam", dependencies=TRUE) install.packages("som", dependencies=TRUE) install.packages("sp", dependencies=TRUE) install.packages("spatial", dependencies=TRUE) install.packages("stringr", dependencies=TRUE) install.packages("survival", dependencies=TRUE) install.packages("vegan", dependencies=TRUE) install.packages("wordcloud", dependencies=TRUE)perlのインストール
anyenvのインストール
brew install anyenv echo 'eval "$(anyenv init -)"' >> ~/.bash_profile exec $SHELL -l anyenv install --initanyenvのPluginのインストール
mkdir -p ~/.anyenv/plugins #anyenvのプラグインをインストールする「plugins」ディレクトリの作成 git clone https://github.com/znz/anyenv-update.git ~/.anyenv/plugins/anyenv-update #anyenv updateコマンドでanyenvで入れた**env系の全てをアップデートしてくれるプラグイン git clone https://github.com/znz/anyenv-git.git ~/.anyenv/plugins/anyenv-git #anyenv gitコマンドでanyenvで入れた**env系の全てのgitコマンドを実行するプラグインplenv
anyone install plenv exec $SHELL -lperlのインストール
plenv install (インストールしたいperl-version) exec $SHELL -l plenv global (インストールしたperl-version)cpanmをインストール
plenv install-cpanm exec $SHELL -lcpanモジュールをアンインストール
cpanm Unicode::Japanese exec $SHELL -l後はKH-coderに必要なもの(https://github.com/ko-ichi-h/khcoder/issues/91 )をただインストールするだけ
cpanm Jcode* cpanm Tk* cpanm DBI cpanm DBD::CSV (allow testing, say “y”) cpanm File::BOM* cpanm Lingua::JA::Regular::Unicode (needs sudo)* cpanm Net::Telnet cpanm Excel::Writer::XLSX cpanm DBD::mysql cpanm Spreadsheet::ParseExcel::FmtJapan cpanm Spreadsheet::ParseXLSX cpanm Statistics::ChisqIndep* cpanm Statistics::Lite* cpanm Unicode::Escape* cpanm Algorithm::NaiveBayes* cpanm Lingua::Sentence* cpanm Proc::Backgroundこのperlパッケージをインストールするところが準備段階で一番ハマる(私はPCのデータを全て吹き飛ばしたw)。
以下ハマったこと(cpanm)
cpanm DBD::mysqlとした時に以下のエラーメッセージが出た
Can't link/include C library 'ssl', 'crypto', aborting.このサイト( https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql )を参考にして、以下のように対応した
cpanm DBD::mysql --configure-args="--libs='-L/usr/local/opt/openssl/lib -lssl -lcrypto -L/usr/local/lib -lmysqlclient'"KH-coderのインストール
git clone https://github.com/ko-ichi-h/khcoder.gitKH-coder起動
mysql起動
ターミナルからmysqlを起動する
mysql.server start mysql mysql -u root Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 8.0.22 Homebrew Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SET GLOBAL local_infile=on; #これがないとmysqlに接続できないエラーが起きる Query OK, 0 rows affected (0.00 sec) SET GLOBAL local_infile=on;KH-coder 起動
configにあるconfig.iniを以下のように書き換える
sql_username root sql_password #今回は設定しなかったXQuartzを起動し、KH-coderのkh_coder.plのある場所に移動する。
cd (kh_coder.plのある場所) perl kh_coder.pl日本語がMeCabしか使えない上(少なくとも茶筌が見つからなかった)、辞書コードはutf-8であるため
utf-8
と設定するStanford POS Taggerの設定を開き
.jarファイル、.taggerファイルを設定する後はKH-coderの公式のチュートリアルデータ(https://khcoder.net/tutorial.html )でどのエラー画面が起きるかテストする。
日本語入力について
XQuartzは日本語を入力できないため、適当なテキストエディタに日本語を書き、ctr+vで貼り付ける。
最後に
KH-coderを使うときはWindowsでやろう。絶対それが楽。
参考にしたもの
KH-coder公式
KH-coder GitHub (https://github.com/ko-ichi-h/khcoder)
MacでKH-coderインストールした方々の備忘録
池田大輔氏のMacでのKH Coderのインストール(https://researchmap.jp/blogs/blog_entries/view/75043/1665e5f4b34a161b470604b59bf39e12?frame_id=616670)
@y-urakawa 氏の【Mac】KH Coder導入手順(https://qiita.com/y-urakawa/items/0423bf6775de8e6305f3)mysql関連
progarteのMySQLの開発環境を用意しよう(macOS)(https://prog-8.com/docs/mysql-env)
mita2db氏のMySQL 8.0 の LOAD DATA で The used command is not allowed with this MySQL version エラー(https://mita2db.hateblo.jp/entry/2020/01/13/163218)
perl関連
@rinpa 氏のanyenv + macOS環境構築(https://qiita.com/rinpa/items/81766cd6a7b23dea9f3c)
@laikuaut 氏のplenvの基本メモ書き(https://qiita.com/laikuaut/items/82c9b402920e30c2c4eb)
みつと氏のmacOS(mojave)にDBD::mysqlをインストールしたいっ!(https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql)
KH-coder module関連
Stanford Pos Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)
- 投稿日:2020-10-27T00:11:34+09:00
冴えない彼氏のKH-coderの使い方(Mac導入編)
はじめに
KH-coderをMacで使おうと思いたち、インストールしようとしたのである。その際、参考となるサイトが少ないことに辟易し、ここにその方法を記そうと思い立った次第である。
(決してcpanmをclean upしようとして、sudo rm -rfして 3ヶ月間のデータを失ったからでない←聞かれてない)
今回は導入編であるので、具体的な解析については他のサイトを読んでほしい。後、タイトルに関しては察してくれ。
実行環境
MacBook Pro(15-inch,2018)
プロセッサ 2.9 Ghz Intel Core i9
メモリ 32GB 2400 MHz DDR4
OS:macOS Mojave(ver.10.14.6)前準備
X-code,Homebrewをインストールできていない場合
X-codeのインストール
xcode-select --install
Homebrewをインストール
Homebrewの公式サイト(https://brew.sh/index_ja.html) にインストールコマンドが掲載されているので、それに載っているスクリプトをターミナルを開き実行。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"インストール後の確認
brew doctor #インストール完了後の確認 brew --versionHomebrewでGitをインストール
brew install git
準備
XQuartzをインストール
このサイト(https://www.xquartz.org/index.html )のdmgをインストール
Stanford-Posttaggerをインストール
Stanford Log-linear Part-Of-Speech Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)の公式サイトからインストールする。
openjdkのインストール
brew install openjdk13
mecabのインストール
brew install mecab brew install mecab-ipandicmysqlのインストール
brew install mysql
インストール終了後
brew services start mysql ==> Successfully started `mysql` (label: homebrew.mxcl.mysql) #成功すると上記のように表示される起動後、次のコマンドを実行しパスワードを設定する。
コマンドを実行すると、最初にVALIDATE PASSWORD PLUGINという強固なパスワード設定を助けるプラグインを使用するかどうかを質問される。
(今回私はローカルでの使用しか想定していため、何も入力しなかった。)次にrootユーザーのパスワードを設定する
mysql_secure_installation(今回はパスワードを設定せず、次に進んだ)
mysql -u root
これで
(base) user:~ user$ mysql -u root Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 8.0.22 Homebrew Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>となれば成功。
mysqlの起動,終了
起動
brew services start mysqlもしくは
mysql.server start ####終了 ```bash brew services stop mysqlもしくは
mysql.server stopRのインストール
brew cask install R
R単体と同時にRstudio(https://rstudio.com/products/rstudio/ )も一緒にインストールした
以下KH-coderを動かすために必要なパッケージ
install.packages("ade4", dependencies=TRUE) install.packages("amap", dependencies=TRUE) install.packages("Cairo", dependencies=TRUE) install.packages("cluster", dependencies=TRUE) install.packages("codetools", dependencies=TRUE) install.packages("colorspace", dependencies=TRUE) install.packages("dichromat", dependencies=TRUE) install.packages("foreign", dependencies=TRUE) install.packages("ggdendro", dependencies=TRUE) install.packages("ggplot2", dependencies=TRUE) install.packages("ggnetwork", dependencies=TRUE) install.packages("ggsci", dependencies=TRUE) install.packages("gtable", dependencies=TRUE) install.packages("igraph", dependencies=TRUE) install.packages("KernSmooth", dependencies=TRUE) install.packages("lattice", dependencies=TRUE) install.packages("maptools", dependencies=TRUE) install.packages("MASS", dependencies=TRUE) install.packages("Matrix", dependencies=TRUE) install.packages("mgcv", dependencies=TRUE) install.packages("munsell", dependencies=TRUE) install.packages("nlme", dependencies=TRUE) install.packages("nnet", dependencies=TRUE) install.packages("permute", dependencies=TRUE) install.packages("pheatmap", dependencies=TRUE) install.packages("plyr", dependencies=TRUE) install.packages("proto", dependencies=TRUE) install.packages("RcolorBrewer", dependencies=TRUE) install.packages("Rcpp", dependencies=TRUE) install.packages("reshape2", dependencies=TRUE) install.packages("rgl", dependencies=TRUE) install.packages("rpart", dependencies=TRUE) install.packages("scales", dependencies=TRUE) install.packages("scatterplot3d", dependencies=TRUE) install.packages("slam", dependencies=TRUE) install.packages("som", dependencies=TRUE) install.packages("sp", dependencies=TRUE) install.packages("spatial", dependencies=TRUE) install.packages("stringr", dependencies=TRUE) install.packages("survival", dependencies=TRUE) install.packages("vegan", dependencies=TRUE) install.packages("wordcloud", dependencies=TRUE)perlのインストール
anyenvのインストール
brew install anyenv echo 'eval "$(anyenv init -)"' >> ~/.bash_profile exec $SHELL -l anyenv install --initanyenvのPluginのインストール
mkdir -p ~/.anyenv/plugins #anyenvのプラグインをインストールする「plugins」ディレクトリの作成 git clone https://github.com/znz/anyenv-update.git ~/.anyenv/plugins/anyenv-update #anyenv updateコマンドでanyenvで入れた**env系の全てをアップデートしてくれるプラグイン git clone https://github.com/znz/anyenv-git.git ~/.anyenv/plugins/anyenv-git #anyenv gitコマンドでanyenvで入れた**env系の全てのgitコマンドを実行するプラグインplenv
anyone install plenv exec $SHELL -lperlのインストール
plenv install (インストールしたいperl-version) exec $SHELL -l plenv global (インストールしたperl-version)cpanmをインストール
plenv install-cpanm exec $SHELL -lcpanモジュールをアンインストール
cpanm Unicode::Japanese exec $SHELL -l後はKH-coderに必要なもの(https://github.com/ko-ichi-h/khcoder/issues/91 )をただインストールするだけ
cpanm Jcode* cpanm Tk* cpanm DBI cpanm DBD::CSV (allow testing, say “y”) cpanm File::BOM* cpanm Lingua::JA::Regular::Unicode (needs sudo)* cpanm Net::Telnet cpanm Excel::Writer::XLSX cpanm DBD::mysql cpanm Spreadsheet::ParseExcel::FmtJapan cpanm Spreadsheet::ParseXLSX cpanm Statistics::ChisqIndep* cpanm Statistics::Lite* cpanm Unicode::Escape* cpanm Algorithm::NaiveBayes* cpanm Lingua::Sentence* cpanm Proc::Backgroundこのperlパッケージをインストールするところが準備段階で一番ハマる(私はPCのデータを全て吹き飛ばしたw)。
以下ハマったこと(cpanm)
cpanm DBD::mysqlとした時に以下のエラーメッセージが出た
Can't link/include C library 'ssl', 'crypto', aborting.このサイト( https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql )を参考にして、以下のように対応した
cpanm DBD::mysql --configure-args="--libs='-L/usr/local/opt/openssl/lib -lssl -lcrypto -L/usr/local/lib -lmysqlclient'"KH-coderのインストール
git clone https://github.com/ko-ichi-h/khcoder.gitKH-coder起動
mysql起動
ターミナルからmysqlを起動する
mysql.server start mysql mysql -u root Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 11 Server version: 8.0.22 Homebrew Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SET GLOBAL local_infile=on; #これがないとmysqlに接続できないエラーが起きる Query OK, 0 rows affected (0.00 sec) SET GLOBAL local_infile=on;KH-coder 起動
configにあるconfig.iniを以下のように書き換える
sql_username root sql_password #今回は設定しなかったXQuartzを起動し、KH-coderのkh_coder.plのある場所に移動する。
cd (kh_coder.plのある場所) perl kh_coder.pl日本語がMeCabしか使えない上(少なくとも茶筌が見つからなかった)、辞書コードはutf-8であるため
utf-8
と設定するStanford POS Taggerの設定を開き
.jarファイル、.taggerファイルを設定する後はKH-coderの公式のチュートリアルデータ(https://khcoder.net/tutorial.html )でどのエラー画面が起きるかテストする。
日本語入力について
XQuartzは日本語を入力できないため、適当なテキストエディタに日本語を書き、ctr+vで貼り付ける。
最後に
KH-coderを使うときはWindowsでやろう。絶対それが楽。
参考にしたもの
KH-coder公式
KH-coder GitHub (https://github.com/ko-ichi-h/khcoder)
MacでKH-coderインストールした方々の備忘録
池田大輔氏のMacでのKH Coderのインストール(https://researchmap.jp/blogs/blog_entries/view/75043/1665e5f4b34a161b470604b59bf39e12?frame_id=616670)
@y-urakawa 氏の【Mac】KH Coder導入手順(https://qiita.com/y-urakawa/items/0423bf6775de8e6305f3)mysql関連
progarteのMySQLの開発環境を用意しよう(macOS)(https://prog-8.com/docs/mysql-env)
mita2db氏のMySQL 8.0 の LOAD DATA で The used command is not allowed with this MySQL version エラー(https://mita2db.hateblo.jp/entry/2020/01/13/163218)
perl関連
@rinpa 氏のanyenv + macOS環境構築(https://qiita.com/rinpa/items/81766cd6a7b23dea9f3c)
@laikuaut 氏のplenvの基本メモ書き(https://qiita.com/laikuaut/items/82c9b402920e30c2c4eb)
みつと氏のmacOS(mojave)にDBD::mysqlをインストールしたいっ!(https://blog.mitsuto.com/macos-mojave-perl-dbd-mysql)
KH-coder module関連
Stanford Pos Tagger(https://nlp.stanford.edu/software/tagger.shtml#About)
- 投稿日:2020-10-27T00:08:12+09:00
【AWS入門】RDSによるMySQL環境の構築
【AWS入門】EC2インスタンス上に.NET Core + nginx環境を構築の続きです。
今回はRDSを使用したMySQLインスタンスをAWS上に構築し、EC2インスタンス上に配置されたWebアプリから接続するところまでをご紹介しようと思います。
環境
MySQL 8.0.20
RDSインスタンスの作成
まずはRDSインスタンスを作成していきましょう。
以下手順でRDSのコンソールを開き、データベースの作成メニューを開いてください。
- AWSマネジメントコンソールにログイン
- 「サービス」の「すべてのサービス」に
RDS
を入力- RDSへのリンクが表示されるので押下
- RDSコンソールが表示されるので、「データベースの作成」を押下
以降各セクションごとに説明します。
データベースの作成方法
簡単作成でも問題ありませんが、今回は練習の意味で標準作成を選択します。
エンジンのオプション
MySQLを選択します。
バージョンは8.0.20にしました。
テンプレート
設定
MySQLに接続するマスターユーザの情報を入力します。
今回はインスタンス名をasp-net-app-db
、ユーザ名はデフォルトのadmin
に設定しました。
パスワードは適宜設定してください。また、ユーザ名とパスワードは後ほど接続文字列を設定する際に利用するので、どこかに書き留めておいてください。
DB インスタンスサイズ
無料枠なのでdb.t2.microのみ有効です。
そのまま進みましょう。
ストレージ
ストレージタイプ、ストレージ割り当てはそれぞれデフォルト値でOKです。
ストレージの自動スケーリングについては、万が一なにか起こった場合にスケールされるのを防ぐため、一応チェックを外しておきました。
可用性と耐久性
このセクションは無料枠の場合特に設定できる項目がないので次に進みます。
接続
RDSのインスタンスを配置するVPCを選択してください。
要件として、VPC内に別々のアベイラビリティゾーンに属するサブネットが最低2つ必要です。
次に、追加の接続設定を編集していきます。
設定名 設定値 サブネットグループ デフォルト値 パブリックアクセス可能 なし VPCセキュリティグループ 事前に作成したセキュリティグループ アベイラビリティゾーン 指定なし データベースポート 3306 ※VPCセキュリティグループについてはポート3306が開いている必要があります。
データベース認証
追加設定
最初のデータベース名に
asp_net_sample
を入力し、それ以外は全て無効に設定します。
設定後、データベースの作成を押下するとインスタンスの作成が開始されます。
接続文字列をServiceの環境変数に記述
最後に前回作成したアプリケーションからRDSインスタンスを参照できるように、Serviceの環境変数を編集します。
EC2インスタンスにSSH後、以下コマンドを実行します。
$ sudo vim /etc/systemd/system/kestrel-asp-net-app.service #Environment=ASPNETCORE_ENVIRONMENT=Production以下に追加 Environment=DB_CONNECTION_STRING=server=サーバのエンドポイント;uid=admin;pwd=パスワード;database=asp_net_sample Environment=ELASTIC_SEARCH_SERVER=http://localhost:9200設定後、以下コマンドを実行します。
$ sudo service nginx restart確認
ローカルマシンにて
http://サーバIP/
をブラウザで開いてください。
RDSインスタンスが作成されたばかりでテーブルが存在していないので、環境の初期化
を押下します。
以下のような画面が表示されれば正常に動作しています。
次回
Elasticsearchと連携 -> 作成中...