OmniAICreator/Qiita-1.07M huggingface dataset:
[laravel]個人的バリデーションの書き方

こんにちは！

この間laravelでファイルアップロード処理を含む保存処理のバリデーションの実装をしていたときに、バリデーションエラー時にアップロードしたファイルのデータがなくなってしまうので、ファイルアップロード以外のバリデーションエラー時にアップロードしたファイルを仮アップロードしてからリダイレクトという実装をしました。


まぁカスタムバリデートを実装すればいいことですが、フォームリクエストバリデーションの中でアップロードの処理をするのも変な感じがするし、かと言ってコントローラにValidatorのロジックを書くとコントローラ内が冗長になるし、アクションごとにフォームリクエストバリデーションとコントローラでのバリデーションを切り替えるのも気持ちが悪い

なんかなるべく共通した書き方できないかなーと感じて個人的にしっくりきた書き方を紹介したいと思います！

結論を先に書くとバリデーションクラスを作ってValidatorインスタンスを返す処理を実装します


コードはユーザーの名前とアイコン画像の編集を実装すると仮定します
# route

```web.php
Route::post('user/update', 'UserController@update')->name('user.update');
```

# view

```html
<form action="{{route('user.update')}}" method="post" enctype="multipart/form-data">
    @csrf
    <input type="text" name="name">
    <input type="file" name="file">
</form>
```

# Validator
```UserValidator.php
<?php

namespace App\Http\Validator;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class UserValidator
{
    public function createValidation(Request $request): \Illuminate\Contracts\Validation\Validator
    {
        $params = $request->all();
        return Validator::make($params, [// <- バリデーターインスタンスを返す
            'name' => [
                'required',
            ],
            'file' => [
                'file',
                'image',
                'mimes:jpeg,jpg,png,gif',
                'max:5120',
            ],
        ], [
            'name' => '入力されていません。',
            'file.file' => '不正なファイルです。',
            'file.image' => '画像以外のデータが送信されました。',
            'file.mimes' => '登録できる拡張子はjpeg,jpg,png,gifのいずれかになります。',
            'file.max' => '画像サイズが大きすぎます。',
        ])->after(function ($validator) use ($params) {
            //...カスタムバリデーション
            $validator->errors()->add('name', 'エラー！');
        });
    }
}

```

# controller
```UserController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Validator\UserValidator;

class UserController extends Controller
{
    privare $userValidator;

    public function __construct(UserValidator $userValidator)
    {
        $this->userValidator = $userValidator;
    }
    public function update(Request $request)
    {
        $validator = $this->userValidator->updateValidation($request);
        $validator->validate();//@todo バリデーションエラー時に即リダイレクト
        //or
        if ($validator->fails()) {
            //@todo バリデーションに引っかかったフィールド名を取得(ここもっと分かり易いやり方方法ないのかな？)
            $messageBag = $validator->messages();
            $error_messages = $messageBag->getMessages();
            if (!array_key_exists('file', $error_messages)) {
                //@todo nameフィールドでバリデート引っかかったけどfileフィールドは通過した時の処理(仮ファイルアップロードの処理など)
                //...
            }
            //@todo バリデーションエラーと入力したデータを持ってリダイレクト
　　　　　　　return redirect()
                ->route('user.edit')
                ->withErrors($validator)
                ->withInput();
        }
        //@todo バリデーション通過時の処理
        ...
        return redirect()->route('user.edit');
    }
}
```

こうすれば即リダイレクトと処理を挟んでリダイレクトが簡単に切り替えられるし、そこまで冗長じゃないし、あと他の人がコントローラを見たとき何がしたいのかよくわかるのかなーと

まぁsymfonyのhandleRequestに近づけた感じです

参考にしてください！
【Python】REST API でファイルをアップロードする (FastAPI)


# はじめに

### FastAPIで

これまで WEB APIは Flaskで作ってきましたが、調べているとfastAPIを使う方が、便利なようです。非同期処理がどうたらとかいろいろあるようですが、個人的に一番響いたのは、http://localhost:8080/docs と開くと、自作のWEB APIのマニュアルやテストができてしまうことです。これはもうチュートリアルをたどるとすぐできるので、書くのは省略するか後回し。これに感動しました。

今回、FastAPIでファイルをアップロードして保存する方法を少し調べたのでメモしておきます。

# ファイルのアップロード

### Request File (tutorial)

Tutorial に書いてあることのコピーなのですが、まぁ自分のメモです。
FastAPI Tutorial-User Guide の [Request File]
(https://fastapi.tiangolo.com/tutorial/request-files/) のページです。


```python:main.py

from fastapi import FastAPI, File, UploadFile, Form

app = FastAPI()

@app.post("/files/")
async def create_file(file: bytes = File(...)):
    return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    return {"filename": file.filename}
```

下記のコードはtutorial にあるものです。私の環境では下記で動きました。bytes や UploadFileを引数にすれば良さそうです。


```
python3 -m pip install uvicorn, fastap
python3 -m uvicorn main:app --reload
```

上記のサーバを起動中に、適当に同じディレクトリにある16M bytes 位のファイルを指定してみると、

```
$ curl -X POST "http://127.0.0.1:8000/files/" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -F "file=@archive.zip;type=application/x-zip-compressed"
{"file_size":16957046}
```

と返ってきました。

### 一時ファイルの保存

```UploadFile``` の file を```shutil.copyfileobj()```すれば良いようです。
ここに一時ファイルに保存する例があったので、動かしてみたら動きました。
https://github.com/tiangolo/fastapi/issues/426

```python:main2.py
from fastapi import FastAPI, File, UploadFile, Form
import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile

@app.post("/saveuploadfile/")
async def save_upload_file_tmp(fileb: UploadFile=File(...), token:str=Form(...)):
    tmp_path:Path = ""
    try:
        print(type(fileb))# <class 'starlette.datastructures.UploadFile'>
        print(type(fileb.file)) #<class 'tempfile.SpooledTemporaryFile'>
        suffix = Path(fileb.filename).suffix
        with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
            shutil.copyfileobj(fileb.file, tmp)
            tmp_path = Path(tmp.name)
            print(tmp_path)
    finally:
        fileb.file.close()
    return {
        "filename": fileb.filename,
        "temporary_filepath": tmp_path,
        "token": token,
        "fileb_content_type": fileb.content_type,
    }
```


別ターミナルで動かすと、

```
$ curl -X POST "http://127.0.0.1:8000/saveuploadfile/" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -F "token=agd" -F "fileb=@archive.zip;type=application/x-zip-compressed"
```

responce body が

```
{
  "filename": "archive.zip",
  "temporary_filepath": "/tmp/tmp2ki9kvdp.zip",
  "token": "agd",
  "fileb_content_type": "application/x-zip-compressed"
}
```

と返ってきます。

# まとめ

とりあえずfastAPIでファイルのアップロードはできた。明日の作業も何とか進められそうだ。

まだまだ試せていないのは以下の通り。

- fileのサイズの取得方法が分からない。一応、tempfile(https://docs.python.org/3/library/tempfile.html)
を読んではみたのだが。
- 複数ファイルの取り扱い。これはTutorial にあったので、試せばよいだけだと思うが。
- ダウンロードもしたい。

(2021/02/08)
PyTorchのUserWarning: Using a target size ... that is different to the input size ...の警告を侮ってはいけない

#はじめに
掲題の警告を無視して、3時間を浪費してしまった話。

# 環境
- PyTorch 1.7.0


#PyTorchの学習コード

以下のようなコードで回帰の予測モデルを作成。

```python

for epoch in range(max_epoch):

    losses = []
    for batch in train_loader:

        # バッチサイズ分のサンプル抽出
        x, t = batch
        
        # パラメータの勾配を初期化
        optimizer.zero_grad()

        # 予測値の算出
        y = net(x)

        # 目的関数の値を算出
        #print(y)
        #print(t)
        loss = F.mse_loss(y, t)

        # 目的関数の値を表示
        #print('loss:', loss.item())
        losses.append(loss.item())
        # 各パラメータの値を算出
        loss.backward()

        # 勾配の情報を用いたパラメータの更新
        optimizer.step()

    avg_loss = torch.tensor(losses).mean()
    print('TRAIN_LOSS={0}, VAL_LOSS={1}'.format(avg_loss, calc_mse(val_loader)))
```

学習を進めたところ、``` loss = F.mse_loss(y, t) ```　の箇所で以下の警告が出ていたが、学習は進むので無視して学習を進めたところ、全く精度があがらず、最終的にはこの警告で記載されていることが原因であった。


```
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:12: UserWarning: Using a target size (torch.Size([12])) that is different to the input size (torch.Size([12, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.
  if sys.path[0] == '':
```

#精度が上がらなかった原因

以下のように成果値と予測値のTensorの形状が異なっていたため、mse_lossが正しく計算されていなかった。

## 正解値

形状：バッチサイズ x 1の行列
 
```python
tensor([[27.0766],
        [19.2778],
        [27.9340],
        [21.0067],
        [15.3397],
        [20.8324],
        [16.6614],
        ...
        [15.0981],
        [22.3690],
        [12.4326]], grad_fn=<AddmmBackward>)
```

## 予測値

形状：ベクトル

```python
tensor([21.2000, 22.7000, 19.9000, 21.8000, 18.5000, 16.1000, 10.9000, 21.0000,
        13.0000, 19.5000, 20.9000, 19.5000, 17.1000, 21.5000, 36.2000, 17.8000,
        ...
        22.9000, 14.5000, 14.9000, 26.4000, 24.4000, 14.1000, 34.9000, 11.8000,
        24.4000, 20.9000,  8.8000, 22.6000, 11.8000, 37.0000, 21.7000, 17.6000,
        11.7000, 19.6000, 19.4000, 36.0000])
```


#対策

以下のように unsqueezeを使って予測値の形状を、N x 1 の形に変更する。

```
y = y.unsqueeze(1)
```

#おわりに
警告は無視せずにしっかり読もう！という話。
Tensorflow/Kerasで学習済み「モデル/重み」を保存/復元する方法

# モデル/重みの保存
機械学習を行っている際に毎回同じ学習を行うのは時間の無駄なので、`学習済みモデル/重み`として`保存`しておきましょう。
保存した学習済みモデル/重みを`復元(load)`して認識などを行う方法も紹介します。
`MNIST`を例に紹介します。
公式日本語解説:https://www.tensorflow.org/tutorials/keras/save_and_load?hl=ja

## 保存の方法の種類
ファイルとして保存/フォルダー形式で保存の２つのパターンがあります。
ファイルは`.h5`という拡張子の`HDF5ファイル`で保存します。
フォルダー形式だとややこしいので、１つのファイル(HDF5)で保存する方法を紹介します。

## 重みだけ保存
モデルは保存しないで重みだけ保存する方法は別記事で紹介します。
こちらは復元の際は先にモデルを別で定義する必要があります。
[Qiita: Tensorflow/Kerasで学習済み「重み」を保存/復元する方法](https://qiita.com/zunda_pixel/items/6178855310302efabfcc)

## モデル/重みの保存
`model.save()`でモデルと重みを保存します。
これは`model.fit()`で学習した後に実行します。

```python
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras.optimizers import RMSprop

(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.reshape(x_train.shape[0], x_train.shape[1] * x_train.shape[2])
x_test  = x_test.reshape(x_test.shape[0],   x_test.shape[1]  * x_test.shape[2])

x_train = x_train.astype('float32')
x_test  = x_test.astype('float32')

x_train /= 255
x_test  /= 255

num_classes = len(list(set(y_train)))

y_train = to_categorical(y_train, num_classes)
y_test = to_categorical(y_test, num_classes)

input_shape = x_train[0].shape

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=input_shape))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])
model.summary()

batch_size = 16
epochs = 3

history  = model.fit(x_train, y_train, 
                     batch_size=batch_size,
                     epochs=epochs,
                     verbose=1,
                     validation_data=(x_test, y_test))

model.save('mnist_widgets.h5') #モデルと重みの保存

score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
```
##モデル/重みの復元

```python
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import load_model


(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_test  = x_test.reshape(x_test.shape[0],   x_test.shape[1]  * x_test.shape[2])

x_test  = x_test.astype('float32')

x_test  /= 255

num_classes = len(list(set(y_test)))

y_test = to_categorical(y_test, num_classes)

model = load_model('mnist_widgets.h5') #モデルと重みを復元

score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
```
SSL設定をする

###事前準備

ドメインを取得しておく。
（今回はRoute53でドメインを購入
　　⇨レコード追加でipアドレスと結びつける）

###設定を行っている

サーバーへの設定はCertbotというツールを利用します。
Amazon Linux2ではEPEL7という拡張パッケージをインストールすることで利用できるようになります。

以下作業はホームディレクトリで行いました

```
$ cd
```

####EPEL7をインストール

```
$ sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/
```

```
$ sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm
```

```
$ sudo yum-config-manager --enable epel*
```
最後に下記コマンドで正常に準備できているか確認します。

```
$ sudo yum repolist all
```
問題なければepel関連がenabledとして表示されます。

####Certbotをインストール

```
$ sudo yum install -y certbot python2-certbot-apache
```

####証明書を作成
次に実際にCertbotを実行して証明書を作成します。


```
$ sudo certbot
```
最初にファイルが存在しないというエラーが出力されるが問題なし。
最初にメールアドレスを入力する

```
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): 
```
規約の同意を求められるのでyesをおす

```
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server. Do you agree?
```

Apache設定の際に入力したバーチャルホストの設定を元に、SSL化するホスト名の確認が行われます。
問題なければEnterを入力する。

```

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: yourhost.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
```

作成して問題なければ下記のメッセージを含む表示が出て完了となります。

```
Congratulations! You have successfully enabled https://yourhost.example.com
```

Apacheを再起動して反映されます。

```
$ sudo service httpd restart
```

ブラウザでSSL化したものを確認。

####証明書を自動で更新させる

Let's Encryptの証明書は３ヶ月しか有効期限がありません。
そのためCrontabを利用します。

```
$ sudo vi /etc/crontab
```
下記の行を追記します。

```
39 1,13 * * * root sudo certbot renew --no-self-upgrade --post-hook "/bin/systemctl reload httpd"
```
最後にcrondを再起動して設定を反映させます。

```
$ sudo systemctl restart crond
```
下記コマンドで正常に動作するか確認

```
$ sudo certbot renew --no-self-upgrade --post-hook "/bin/systemctl reload httpd" --force-renewal
```
GitHub のフロー


#GitHub Flowモデル
メインブランチはmasterの１つだけ、常にデプロイ可能な状態に維持するというシンプルなブランチモデルです。プルリクエストを作って、レビューしてもらって、マージします。
これは頻繁にリリース（デプロイ）する開発に適したブランチモデルです。

masterブランチ
現在の製品のメインブランチです。常にデプロイ可能な状態です。

featureブランチ

１　新しい作業を開始するときは、わかりやすい名前のブランチをmasterから分岐します。
２　ローカルでコミットして、同じ名前でリモートにもpushします。
３　フィードバックやヘルプが必要なとき、
　　またはマージの準備ができたらプルリクエストを作成します。
４　他のメンバーがレビューして確認したら、masterへマージできます。
５　masterにマージしたらデプロイできます。

「わかりやすい名前」としか説明されていませんが、それが難しいんですよね。
筆者はIssue番号と合わせて、feature/123 のようにつけています。


#git-flowモデル
masterブランチ
現在の製品のメインブランチです。originに常に存在します。

developブランチ
次回リリース用のメインブランチです。originに常に存在します。リリース時にmasterへマージします。

featureブランチ
新しい機能を開発するための開発者用のブランチです。Issueの作業を開始するときに、developから分岐して、Issueの作業が完了したら、developへマージします。developへマージしたら削除します。

例：feature/123　（123はIssue番号）

releaseブランチ
次回リリースが近くなったら、developから分岐して、developとmasterへマージします。リリース担当者用のブランチです。リリースしたら、削除します。

例：release/1.2

hotfixブランチ
現在の製品バージョンのバグフィックス用で、開発者ローカルブランチです。masterから分岐して、masterとdevelopへマージします。マージしたら削除します。

例：hotfix/123（123はIssue番号）


#GitLab Flowモデル
productionブランチモデル
featureブランチ
GitHub Flowと同じ使い方です。masterから分岐して、プルリクエストして、masterへマージします。

masterブランチ
製品のメインブランチです。masterにマージするタイミングで、本番環境へデプロイできるとはかぎりません。本番環境へデプロイする準備ができたら、productionブランチへマージします。

productionブランチ
本番ブランチです。本番環境にデプロイされます。

環境ブランチモデル
featureブランチ
GitHub Flowと同じ使い方です。masterから分岐して、プルリクエストして、masterへマージします。

masterブランチ
製品のメインブランチです。ステージング環境へデプロイされます。ステージング環境でのテストに合格したら、pre-productionへマージします。

pre-productionブランチ
本番直前環境ブランチです。本番直前環境にデプロイされます。本番直前環境でのテストに合格したら、productionブランチへマージします。

productionブランチ
本番環境ブランチです。本番環境にデプロイされます。

リリースモデル
ソフトウェアを外部にリリースする場合、バージョンごとのリリースブランチで作業する必要があります。

featureブランチ
GitHub Flowと同じ使い方です。masterから分岐して、プルリクエストして、masterへマージします。

masterブランチ
製品のメインブランチです。ステージング環境へデプロイされます。ステージング環境でテストします。

各種のバグ修正は、まずmasterへマージされて、各リリースブランチへcherry-pickされます。

リリースブランチ
各バージョンごとのリリースブランチです。

例：2-3-stable、2-4-stable
Apache ログ解析 URLの出現回数を数える

## やりたいこと
Apacheのログから各URLの出現回数を数えたい
ログが分析環境などに入ってなく、サーバから3ヶ月分のログを取得して実施
割と地道にやっていくしかないが、同じような作業が発生した際の参考にしてもらえれば。

・アクセスログ(1ファイル およそ100万行 x 90)
IPアドレス、日付、リクエスト、リファラーがある
この中のURLリクエストの数を数えたい (以下の `/TEST/xxx/xxxxxx/`)

```apache:access.log
111.11.1.111 - - [25/Dec/2020:00:00:00 +0900] "GET /TEST/xxx/xxxxxx/ HTTP/1.1" 200 12364 "https://xxxx" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" 54089
```

## 方法

調べると、apachelogを読み込むツールなどもあるみたいだが、今回はシンプルにコマンドで頑張ってみる。
sedコマンドを利用して不要な部分を取り除いていくだけ。

#### 1.URLだけ出力できるようにsedコマンドの置換で頑張る

```terminal
cat access.log | sed -e "s/.*\] \"//" | sed -e "s/ HTTP.*$//" | sed -e "s/POST //" | sed -e "s/GET //"
```
(accesslogがgz形式などで圧縮されているときは、解凍せずに`gzcat`で出力するとローカルのディスク容量も安心。)

#### 2.1.が目的の形でできるようになったらそれをファイルに出力する

```terminal
cat access.log | sed -e "s/.*\] \"//" | sed -e "s/ HTTP.*$//" | sed -e "s/POST //" | sed -e "s/GET //" > out.txt
```

#### 3.件数を数える

```terminal
cat out.txt | awk '{count[$0]++}END{for(i in count)print count[i], i}'
```

awkを利用してgroup byのような感じで検索できる
https://qiita.com/oshiro/items/11b6833aa41627723059

上記はコマンドの例だが、90日分のファイルでまぁまぁでかいので、その後さらに踏み込んだ調査などをしたいケースを踏まえるとHIVEやRedshiftなどのデータウェアハウスの環境があるのであればそこにImportすると良い。
Dockerfileがいかに便利か体験してみた


自身のアプリケーションにdockerの導入をしようと勉強をしていたところ
とりあえずdockerfileに記述して、docker-composeに記述すると・・・、ハイ、できた・・・
けどよくわからないという状態になってしまったので
dockerfileで行われていることをdockerfileを使わないで実装してみる。

目標
centOSにApacheとPHPをインストールしてphpアプリをコピーしてApacheを起動する


centosのイメージからコンテナを立ち上げる（test）　
```
% docker run -d -it -p 8080:80 --name test centos:centos7
```

コンテナにログイン

```
# docker exec -it test /bin/bash
```

Apacheをインストール

```
# yum install httpd
```

phpをインストール

```
# yum install httpd
```

Apacheを起動

```
# /usr/sbin/httpd
```

コンテナからログアウト

```
# exit
```

phpファイル作成

```test.php
<?php
  echo "hoge";
?>
```

コンテナにコピー

```
% docker cp test.php test:/var/www/html
```

http://localhost/test.phpにアクセス

[![Image from Gyazo](https://i.gyazo.com/da2a7a9adfeab4014ac2a5993b5236b0.png)](https://gyazo.com/da2a7a9adfeab4014ac2a5993b5236b0)

と表示される。

## わかったこと
centosのイメージから作ったコンテナに
apacheをインストール、ｐｈｐをインストールと順番に追加していく。
大変。。。

## Dockerfileを使うと

以下の同じフォルダにDockerfileを作成

```dockerfile.
FROM centos:centos7

RUN yum -y install httpd php

COPY test.php /var/www/html/

CMD ["/usr/sbin/httpd","-DFOREGROUND"]
```

dockerfileのディレクトリで

```
% docker build -t test .
% docker run -d -p 8080:80 --name testweb test:latest  
```

同じ結果となる。

## わかったこと
はじめのやり方をDockerfileに記述して
PCにやってもらう。
簡単。

## PHPのdockerイメージを使ってコンテナを作る

Dockerfileを以下に修正

```dockerfile.
FROM php:7.2-apache
COPY test.php /var/www/html/
```

dockerfileのディレクトリで

```
% docker build -t test .
% docker run -d -p 8080:80 --name testweb test:latest  
```

同じ結果となる

## わかったこと
「osを何にするか、その後何をインストールするか」を
他の人が指定してくれたもの（dockerfile）をdockerイメージとして
DockerHubからPULLしてくる
もっと簡単。


## まとめ

導入の方法ばかり検索してしまい、どうしてこのコードを書いているのかという
根本を忘れてしまう。
手間ではあるが、一度その技術が出来る前の方法を理解すると
なぜその技術が生まれたのか、どれくらい便利なものなのかが実感できることがわかった。

参考にしたページ
世界一わかりみが深いコンテナ & Docker入門 〜 その3:Dockerfileってなに？ 〜
https://tech-lab.sios.jp/archives/19191
XserverにSSH接続をしてローカルにファイルをコピーする(Mac)

自身のMacのターミナルからSSH通信でサーバーに接続し、
公開されているファイルをローカルにコピーする手順を書きます。

#手順


##１、サーバー側のSSH設定
Xserverのサーバーパネルにログイン
→SSH設定→変更→ONにする→公開鍵と認証用鍵ペアの生成→パスフレーズを設定（メモしておく）

秘密鍵のダウンロードが始まる。
ファイル名：[サーバーID].key



---
公開鍵と認証用鍵ペアの生成がどういうものかについては、

簡単にまとめると、パスフレーズをつくった時にローカルに秘密鍵のファイルがダウンロードされて、SSH接続するときにパスフレーズを入力することで、サーバー側の公開鍵とローカルの秘密鍵が合致してログイン認証に成功する。そんな感じだったと思います。

なので、秘密鍵のファイルを持っているかつパスフレーズを知らないとログインできないのでセキュリティがかなり強固！というイメージでOK

---
##２、SSH通信の準備
ホームディレクトリに.sshディレクトリを作成する

```
$ mkdir ~/.ssh
```
先ほどダウンロードした秘密鍵を.sshのディレクトリへ移動する

```
$ mv {サーバーID}.key ~/.ssh
```
その際にキーの権限を変更しておく

```
$ chmod 600 ~/.ssh/{サーバーID}.key
```
秘密鍵の名前も変更しておく

```
$ mv {サーバーID}.key id_xserver_{任意の名前}
```

##３、サーバーにログイン
この時のSSH接続のポート番号は[10022]
たぶんXserverであればポート番号は統一されてるんじゃないかと思います。

```
$ ssh -i ~/.ssh/{サーバーID}.key -p 10022 {サーバーID}@{サーバー番号}.xserver.jp
```
この時にパスワードの入力を求められるので、さっきXserverで設定したパスフレーズを入力する

ログインに成功したらコピーしたいディレクトリorファイルのルートパスを確認しておきます。

##４、サーバーのファイルをローカルのホームディレクトリにｓｃｐコマンドでコピーする
ここで私が一番つまづいたのは、ｓｃｐコマンドはローカル側で使用しなければいけなかったことです。
つまり何回もサーバーに接続した状態でコピーしようとしてました。
その結果、ディレクトリがありません、だったり認証が間違っていますというエラーに頭悩まされていた。

```
scp -i ~/.ssh/{サーバーID}.key -P 10022 -p {サーバーID}@{サーバー番号}.xserver.jp:/{コピーしたいファイルorディレクトリ} .
```
ここで気をつけたいのがｓｃｐコマンドでのオプション、-Pがポート番号の指定にあたる。
-pオプションは更新日やパーミッション（権限）を保持しつつコピーする。

※-pオプションを指定しないと、中身を編集してないのに更新日が更新されたり、管理者権限がローカルにコピーした段階で変わったりするので、実際に開発する際はほとんど必須でつける

##５、ローカルのファイルをサーバーのホームディレクトリにコピーする
```
scp -i ~/.ssh/{サーバーID}.key -P 10022 -p {ファイル} {サーバーID}@{サーバー番号}.xserver.jp:~/
```
-rとオプションをつけるとディレクトリをコピーすることができる。

#まとめ

sshコマンドとscpコマンドのポート番号の指定は小文字と大文字で違うので注意すべき！！

ポート指定
scpでは-P
sshでは-p

##参考文献
scpコマンド
https://qiita.com/chihiro/items/142ebe6980a498b5d4a7
エックスサーバーにssh接続する方法（ターミナル利用）
https://qiita.com/ryo2132/items/38b5a93b3df476dd2b44
MacでターミナルからXServerへSSH接続するまで
https://qiita.com/s2ysk/items/367b5a0c027411e7626d
よく分かる公開鍵認証」～初心者でもよくわかる！VPSによるWebサーバー運用講座(2)
https://knowledge.sakura.ad.jp/3543/
Excelのシートを壊す方法(VBAマクロ)

みなさんこんにちは。
SESで派遣されていることを理由に技術的負債のメンテナンスを拒否し始めた私です。
あまりの環境の酷さに、派遣先の社員が数ヶ月先の展望すら見ない作業をし始めていて、色々不安に思っています。
(まあ、ブラックボックス化の原因が社員だったらまだマシだろうと、見なかったことにしています。)

今日もそんな職場のバッドノウハウを広め、Excelマクロの普及を妨害していきます。

# 要点
APIとして外部から提供されているプロパティの名称は無視してはいけません。
無駄に柔軟性の高いシステムをしているので、予想外の部分に影響が出ます。

# 問題
特定のマクロを実行し、保存した後にそのファイルを開こうとすると「～～の一部の内容に問題が見つかりました。可能な限り内容を回復しますか？ブックの発行元が信頼できる場合は、[はい]をクリックして下さい。」との表記が出る。
データに破損した様子はなかったが、書式やフォームオブジェクト(ボタン)などが破損していた。

# 原因
自分の環境では再現性が取れなかったが、シートに止めを刺したのは下記のようなマクロが組まれていたことだったようだ。
職場では下記の部分を修正したところ破損箇所が条件付き書式関係だけになった。
(まだ条件付き書式まわりにこんな感じのバグがあるんだろうなぁ……)

```vb

Sub BreakMacro()
    Dim formula As String
    
    formula = "=VLOOKUP(A" & "TAG_ROW" & ",データ置き場!A:B,2,FALSE)"
    
    Sheet1.Cells(2, 2).Value = Replace(formula, "TAG_ROW", "2")
    Sheet1.Cells(2, 3).Value = Replace(formula, "TAG_ROW", "3")
    Sheet1.Cells(2, 4).Value = Replace(formula, "TAG_ROW", "4")

End Sub

```

今回の場合は、Value(値)プロパティに、関数(Formula)を入れていることがバグの温床になっている。
これが、```Sheet1.Cells(2, 2).Formula = Replace(formula, "TAG_ROW", "2")```や、```Sheet1.Cells(2, 2) = Replace(formula, "TAG_ROW", "2")```だったら問題は無い。

**そもそも、「値」のプロパティに「関数」を入れず、APIの指示にきちんと従いましょう。**
そうだ、SQLを勉強しよう③

#はじめに
徐々に複雑になってきた模様。
違いや似ているところを意識して覚えていきたい。

##WHERE句の色々

SELECT文、DELETE文、UPDATE文に使うことができます。

###比較演算子

Rubyと違って間違えそうなところ。

|SQLでの比較演算子|意味|Rubyでの比較演算子
|--------|----|---|
|=|左右の値が等しい|==|
|<>|左右の値が等しくない|!=|
その他の使い方は同じ。違いを間違わずに覚えよう。

###論理演算子
優先順位が存在します。
1. NOT
2. AND
3. OR

ただし、()をつけることによって、優先順位を引き上げることができます。
…算数の計算の順序みたい。

###NULLは=で判定できない

|||
|---|---|
|式　IS NULL|NULLであることを判定|
|式　IS NOT NULL|NULLでなないことを判定|

…こっちは中１英語みたい。

###感想
SQLを学んでいると、RailsのActiveRecodeへの理解も深まっていく気がします。
欲しいSQL文から逆算してActiveRecodeを考えることができました。
pychart日本語化

<H1>pychart日本語化の手順

1:https://mergedoc.osdn.jp/
2:下の方のOS選択で自分のパソコンOSを選んでダウンロード
![スクリーンショット 2021-02-07 23.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1087184/3a3d35cc-4ba2-8197-a175-a310d22e0d08.png)
3:インストールまでできたら、Macの場合、右クリックで開くをクリックする。
![スクリーンショット 2021-02-07 23.35.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1087184/2626b445-6226-8be0-e6a6-3bb2f0fb4636.png)

4:開けたら、選択のところから、finderのアプリケーションにアクセスし、pychartを選択。
![スクリーンショット 2021-02-07 23.36.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1087184/a5d0c68f-5ed5-490e-b9ba-425bc6d6ee1c.png)

5:pychartを再起動すると日本語化完了。
ls + awk + xargs + mvの複合コマンド

業務で覚えた知識を備忘録として記載していきます。

#利用シーン
- |(パイプ)を使って処理を繋げてみたい場合
- find -name `条件` | xargs mv　で処理ができない場合

先日、あるファイルをディレクトリに移動させるshellを書くことがあったのですが該当ファイルを特定する方法としてFind -name"検索条件" で検索したファイルを|でつないでmvコマンドで移動させていました。
処理記述自体はあっていたのですが、いざ動かすタイミングで該当ディレクトリの配下に同様の条件に合致するファイルが複数存在することがわかりました。

```ts
#こんな感じの環境を想定
/home
   └sample #処理対象
      hogehoge.txt
      hogehoge.csv
      └targetdirectory
      └dummydirectory #処理対象にしたくない
              hogehoge.txt
              hogehoge.csv

pwd
/home/
find -name "hoge*"
/sample/hogehoge.txt
/sample/hogehoge.csv
/sample/dummydirectory/hogehoge.txt
/sample/dummydirectory/hogehoge.csv
```


そのままの処理では、検索対象のファイルがすべて該当ディレクトリに移動してしまうため修正を余儀なくされました。

最初はFindのオプションか別のコマンドですぐに解決すると思ったのですが思いのほか該当する記事が見つからず困り果てたところ、人に聞いたりしてようやく解決することが出来ました。
今回は検索対象となるファイルがすべて同じディレクトリにある事前提で、同じような事で困っている人がいたときの為と自分の復習用に記事にしました。



#やりたいこと
+ ある条件のファイルを移動(コピー、削除)したいが、Findコマンドではファイル特定ができない場合に処理する


#使用コマンド ls + awk + xargs + mv コマンドの複合
```ts
ls -l /検索先 | awk '{print$9}' | xargs -p mv 検索先{} 移動先
```

#解説(という体の確認)
※誤っている個所があればご指摘頂けると幸いです。

```ts 
pwd
/home/sample/

ls -l #言わずと知れたファイルやディレクトリを表示するコマンド
      #今回は -l オプションを使いlongフォーマットを表示させる
ls -l
drwxrwxrwx 1 user user 0  MM月 DD 10:00 targetdirectory
drwxrwxrwx 1 user user 0  MM月 DD 10:00 dummydirectory
-rwxrwxrwx 1 user user 0  MM月 DD 00:00 hogehoge.txt
-rwxrwxrwx 1 user user 0  MM月 DD 00:01 hogehoge.csv

awk {print$9} #longフォーマットの9つ目のカラムがファイル名の為$9を選択

ls -l | awk '{print$9}'
hogehoge.txt
hogehoge.csv

xargs -p  #←の処理結果を引数として→の処理に渡せる便利なコマンド
          #hogehoge.txtとhogehoge.csvを次のコマンドに引数として渡してくれる
          #引数として使ってほしいところに{}を書く

mv #ファイル移動コマンド

#上記を複合させると

ls -l | awk '{print$9}' | xargs -p mv /home/sample/{} /home/sample/targetdirectory
echo $?
0
#結果確認
ls -l /home/sample
drwxrwxrwx 1 user user 0  MM月 DD 10:00 targetdirectory
drwxrwxrwx 1 user user 0  MM月 DD 10:00 dummydirectory
ls -l /home/sample/targetdirectory
-rwxrwxrwx 1 user user 0 MM月 DD 00:00 hogehoge.txt
-rwxrwxrwx 1 user user 0 MM月 DD 00:01 hogehoge.csv
ls -l /home/sample/dummydirectory
-rwxrwxrwx 1 user user 0 MM月 DD 00:00 hogehoge.txt
-rwxrwxrwx 1 user user 0 MM月 DD 00:01 hogehoge.csv

```

やったね。
Ubuntu 20.04にSSHで接続できるようにする

#目的
メインPCからSSH接続でUbuntuに接続できるようにする。
基本的にCUIでの操作になれているので、更新等をできるようにしたい。

#環境
- Windows10(接続元OS)
- Ubuntu 20.04.2 LTS Desktop（接続先OS）

#SSHのインストール
Ubuntuの端末にSSHをインストールする。

```
sudo apt install ssh
```
接続するためにUbuntuのIPアドレスを調べる。

```
hostname -I
```
例：192.168.1.32

表示されたIPアドレスを使いUbuntuに接続する。
継続的にSSHを使用する場合はIPアドレスと固定したほうがよい。

#Windows10から接続

今回はTeraTermを使用して接続を行う。

上記で調べたIPアドレスを入力しOKを選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1090510/352e60e3-37c8-f013-a32a-c590ef8c307c.png)

ユーザ名とパスフレーズを要求されるので接続先のUbuntuに設定したものを入力してOKを選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1090510/b90d2e2c-b21d-e2d4-3a4f-493d266a79ab.png)

接続ができれば、以下の画面が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1090510/3d3349ba-c426-5a62-d47e-ca5d4957a438.png)

これで、Ubuntuの端末と同じようにコマンドを使用することができるようになった。

#最後に
SSHの接続をrootでの接続を禁止したり、特定のIPアドレスからのみを許可したりできるので一応、設定しておくとよいと思う。
fail2banの使い方


最新記事は以下に記載しました。
[fail2banの使い方](https://hana-shin.hatenablog.com/entry/2023/05/24/210949)

#1 fail2banとは?
不正アクセスからサーバを守るツールです。
具体的には、ログファイルに記録される内容を監視して、何度も認証に失敗しているログや、
連続アクセスしているログを見つけると、ファイアーウォールを自動的に調整して、
接続元からの不正アクセスを禁止にします。
たとえば、sshのログイン失敗すると、`/var/log/secure`に以下のログが記録されます。
fail2banは、このログを監視してsshログインに何度も失敗している接続元をアクセス禁止にします。


```console:/var/log/secureの抜粋
Jan 31 20:58:45 server unix_chkpwd[127841]: password check failed for user (root)
Jan 31 20:58:45 server sshd[127839]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.2.105  user=root
Jan 31 20:58:47 server sshd[127839]: Failed password for root from 192.168.2.105 port 55770 ssh2
```


#2 環境
VMware Workstation 15 Player上の仮想マシン(2台)を使いました。
2台とも仮想マシンの版数は以下のとりです。

```console:CentOSの版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
```

```console:カーネル版数
[root@server ~]# uname -r
4.18.0-240.el8.x86_64
```

検証環境は以下のとおりです。
サーバで、fail2banを実行します。
クライアントでは、サーバに対してsshログインやcurlコマンドを実行します。

```console:環境
              192.168.2.0/24
client --------------------------- server
    .120                        .115
```


#3 インストール方法

##3.1 fail2banのインストール
fail2banはepelリポジトリにあるので、まず、epel-releaseパッケージをインストールします。

```console:epel-releaseのインストール
[root@server ~]# dnf -y install epel-release
```

次に、fail2banパッケージをインストールします。

```console:fail2banのインストール
[root@server ~]# dnf -y install fail2ban
```

インストールしたfail2banの版数を確認します。

```console:版数の確認
[root@server ~]# fail2ban-server --version
Fail2Ban v0.11.2
[root@server ~]# fail2ban-client --version
Fail2Ban v0.11.2
[root@server ~]# fail2ban-regex --version
fail2ban-regex 0.11.2
[root@server ~]# fail2ban-python --version
Python 3.6.8
```

##3.2 Apacheのインストール
サーバにApacheをインストールします。

```console:インストール
[root@server ~]# dnf -y install httpd
```
【Java】正規表現　matchesメソッド使い方

Javaを勉強していく中のでメモ代わりです。

matchesメソッドの使い方についてメモします。
文字列のチェックなどに使えるメソッドとのこと。

```Main.java
public class Main {
    public static void main(String[] args){
        String name = "Abcd1234";
        CheckrName checkName = new CheckrName ();

        if(checkName.isValidName(name)){
            System.out.println("Success");
        }else{
            System.out.println("Failed");
        }
    }
}
```
```CheckName.java
public class CheckrName {
    public boolean isValidName(String name){
        //1文字目が大文字で文字数が8文字ならtrue
        return name.matches("[A-Z][A-Za-z0-9]{7}");
    }
}
```
この例では、isValidNameに渡された引数が
1文字目が大文字かつ文字数が8文字であればtrueを返すプログラムとなっています。
今回は、変数`name`に`Abcd1234`が代入されており、1文字目が大文字かつ文字数が8文字であるので、
`true`が返却されます。
Tensorflowをソースからビルドするも、tensorflow.keras使えなくて萎える件

#はじめに
みなさんの使っているPCのCPUは、AVX命令に対応していますか？ここ10年以内にIntelCPUのPCを新規で買った人には関係ないとは思います。それが、普通なのかもしれません。しかし、私のGPUを使えるマシンではCPUがAVX命令に対応していません。AVX命令がないとなんの不自由があるのかって？Ver.1.6以降のTensorflowが、AVXがないCPUだと動かんのですよ。
昨年、Githubから先達のPythonソースをもらってきて動かそうとすると・・・そいつがTesorflow2.2以降を要求するのです。あー、はいはい、Tensorflowね、っと Pip インストールとかして、まぁ文句も言われずインストールできたのですが、いざ、動かそうとすると「無言で終了する」現象が発生していました。そういうものか、と思っていたのですが、そうではなくて、先達のPythonスクリプトが呼び出したTensorflowが動作せずにエラーが出ているのを、エラーメッセージの出力なしにストップしていただけということがわかり・・・。理由をしらべてみると、[Tensorflow2.xは古いCPUでは動かないという情報](https://www.tensorflow.org/install/source?hl=ja#configuration_options)を発見。いったんこのPCの利用をやめてしまったのですが・・・

こいつがうちで唯一のWindowsOSマシンであり、GPUを積んでメモリが64GBも積んであるXeonマシン。[WindowsOSのデベロッパープレビューではWSLからGPUを使える](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started)ようになったと情報（日本語での方法案内は、例えば、[こちら](https://qiita.com/ksasaki/items/ee864abd74f95fea1efa)や[こちら](https://www.kkaneko.jp/tools/wsl/wsl_tensorflow2.html)）を受け、その[パフォーマンスが悪くないとのうわさ](https://www.tegakari.net/2020/11/windows-subsystem-for-linux-2wsl2_vol2/
)も聞いて何とか動かせないかと・・・PCの買い替えは今使ってるPCの処分方法にも困る（購入資金の問題、処分どうする？問題という多重の問題を発生させる）ので、避けたいところです。

# Tensorflowをソースからビルドする
当該AVX非対応PCにて、WSL2上でTensowflowをソースしてはどうか、というのが私の考え。そこで、[公式](https://www.tensorflow.org/install/source?hl=ja)に従ってビルドするのですが・・・なんですか、このビルドに要する時間の長さ！12時間ほどかかりましたよ。ビルド時間すら時間かかりすぎじゃね？ビルド中のCPU使用率をみても20%前後の低空飛行。全力で2時間で終わらせろよ！とか思ってしまいました・・・
![スクリーンショット 2021-02-07 221615.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699890/54009707-1cd4-47c6-b412-9376b46398d6.png)

先の参考記事の参考でWSL2でのパフォーマンスは素のWindows利用時よりも落ちることとその理由がCPUの使い方に制限があるかも、とか、ビルド中にWindowsのファイルシステムにアクセスしてるのかも・・とも推察できるのですが、その解決方法もわからず耐え忍ぶことにしました。

耐え忍びながら、調べてみた参考情報。

[WSL2でつくる快適なUbuntu環境](https://blog.mamansoft.net/2020/07/02/efficient-wsl2-with-ubuntu/#path%E3%81%8B%E3%82%89windows%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%82%92%E9%99%A4%E5%A4%96%E3%81%99%E3%82%8B)
[wsl.conf と .wslconfig  --  roy-n-roy メモ](https://roy-n-roy.github.io/Windows/WSL%EF%BC%86%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A/wslconfig/)

とりあえず、WSL2 linux 側に /etc/wsl.conf を作って次の書き込みを行っておきました。

```
[interop]
enabled=true                # WSL内からWindowsプログラムの起動をサポートする (ver1809以降)
appendWindowsPath=true      # WSL内のPATH環境変数に、WindowsのPATH環境変数を追加する (ver1809以降)
```


# いざ、実行・・・だが・・・
さて、ビルドのできたTensorflowを（これも公式の手順にしたがって）Python pip でインストール。意気揚々と、元来の目的の先達のPythonスクリプトを実行したのでありますが・・・

だめです。

tensorflow.keras が見つかりません、というようなエラーがでます。うーんまぁ、kerasが見つかればいいんだよね、ということで、pip でkerasをインストールして、ソースも書き換えてから実行すると・・・今度は、kerasがTensorflowが見つかりません、というようなエラーを出し・・・あ、そうか自前でビルドしたTensorflowをpyplのkerasは見つけてくれないのか・・・と。

じゃぁ、tensorflowをソースからビルドしたひとはどうやってkerasを使うのか。わからん。

そんなの解説したマニアックな先達がGoogleでは見つからない。2020年中ごろから、kerasってtensorflowの一部に統合されているのでtensorflowをビルドすれば一緒にできあがるのでは、と勝手に期待して・・・。公式に加えて、以下を実行。

```
python3 -m pip install keras-applications
```

意味があるのかどうか、わかりませんが・・・

再度、ビルド命令・・・。また、12時間。

# だめかも
まだ、ビルドが終わりませんけど、あんまり期待しておりません。WindowsではDocker-composeが使いづらい（-Vパスの指定の方法が理解できない）ので、今回だめだったら、WSL2もあきらめて、OSをUbuntuに変えようと思っています。・・・というか、PCを買い替えるほうがいいのだろうか・・・。
AtCoder 生活108日目

[前回](https://qiita.com/flandrescarlet237/items/c7e102541b7b7908dc20)

#108日目
今日は、灰色を2問と茶色2問を解きました。

|問題|難易度|自力で解けた|かかった時間|
|:-:|:-:|:-:|---|---|
|AGC046|A|◯|3分2秒|
|ABC179|C|◯|5分35秒|
|ARC093|C|◯|25分20秒|
|ARC080|C|✕|83分42秒|


##[AGC046_A](https://atcoder.jp/contests/agc046/tasks/agc046_a)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    int x;
    cin >> x;
    int s = lcm(x,360);
    cout << s/x << endl;
}

```

解くのにかかった時間：3分2秒


##[ABC179_C](https://atcoder.jp/contests/abc179/tasks/abc179_c)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    int n;
    cin >> n;
    int ans = 0;
    for(int i = 1;i < n;i++){
        for(int j = 1;i*j < n;j++)ans++;
    }
    cout << ans << endl;
}

```

解くのにかかった時間：5分35秒


##[ARC093_C](https://atcoder.jp/contests/arc093/tasks/arc093_a)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    int n;
    cin >> n;
    int now = 0;
    int ans = 0;
    vector<int> a(n+2);
    a[0] = 0;
    for(int i = 1;i <= n;i++){
        cin >> a[i];
        ans += abs(now - a[i]);
        now = a[i];
    }
    a[n+1] = 0;
    ans += abs(a[n] - a[n+1]);
    for(int i = 1;i <= n;i++){
        int x = abs(a[i] - a[i-1]);
        int y = abs(a[i] - a[i+1]);
        int z = abs(a[i-1] - a[i+1]);
        cout << ans - (x+y) + z << endl;
    }
}

```

解くのにかかった時間：25分20秒


##[ARC080_C](https://atcoder.jp/contests/arc080/tasks/arc080_a)

```c++

#include <bits/stdc++.h>
#define rep(i,n) for(int i = 0;i < (n);i++)
using namespace std;
using ll = long long;
using pii = pair<int,int>;

const int INF = 2e9;

int main(){
    ll n;
    cin >> n;
    ll odd = 0,q = 0;
    rep(i,n){
        int a;
        cin >> a;
        if(a%2)odd++;
        else if(a%4 == 0)q++;
    }
    bool ok = false;
    if(odd == 0)ok = true;
    else if(odd <= q)ok = true;
    else if(odd + q == n && odd = q+1)ok = true;
    if(ok) cout << "Yes" << endl;
    else cout << "No" << endl;
}

```

解くのにかかった時間：83分42秒
感想：あと一歩が足りなかった。


###最後に
茶色もなかなか解けるようになってきたような気がする。目標としては30分で解けるようにしていきたい。
Djangoのアプリケーションをローカル環境に、MySQLをDockerで構成しようしたらエラーがでた

# エラーの内容
```console:
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
Did you install mysqlclient?
```

`mysqlclient`をインストールしましたか？？なんだろう

## mysqlclientとは？
PythonからMySQLに接続するためのパッケージとのこと。
Djangoの公式サイトでは「mysqlclient」の方を推奨しているとか。

`pymysql`って奴もあるけど、公式推奨を使おう。

確かに入れてないのでインストール。

```
pip install mysqlclient
```

## まだエラーが出る。。

```
 OSError: mysql_config not found
```

上記のようなエラーが。。

[Pypiのmysqlclientのインストール方法](https://pypi.org/project/mysqlclient/)を見てみた。

mysqlclientをインストールする前に、brewでMySQLか、サーバー不要な人はmysql-clientをインストールしろと。

今回は、DockerのMySQLを使用するためサーバー不要の手順で実施。

```
$ brew install mysql-client
$ echo 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile
$ export PATH="/usr/local/opt/mysql-client/bin:$PATH"
$ pip install mysqlclient
```

成功！！！

## Djangoのsetting.pyの設定

```
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '',     # コンテナ作成した時に指定したDBの名前
        'USER': 'root',
        'PASSWORD': '', # rootのパスワード
        'HOST': '127.0.0.1',
        'PORT': '3306'  # コンテナ作成した時に、特にポート指定してなければこのまま
    }
}
```

`python manage.py runserver`を実行。。。。

うまく動きました！！
Raspberry PIにてtmux使用について

#tmuxとは
ターミナルマルチプレクサ(Terminal Multiplexer) の略でLinux 系のターミナル画面を複数のセッション、ウィンドウ、ペインに分割して利用することができます。

１）１つのターミナルは複数のセッションを持つことができます。
２）１つのセッションは複数のウィンドウを持つことができます。
３）１つのウィンドウは複数のペインを持つことができます。
４）ターミナルを終了してもセッションは維持されます。コマンドを実行して翌日の朝結果を確認しようとしたら SSH ログアウトしてしまっていたという悲劇を避けることができます。

#tmuxをインストール
下記のコマンドでインストールします。

~~~bash
sudo apt install tmux
~~~

#tmuxを起動
①tmuxコマンドを実行します。
コマンド）tmux
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/8049e95b-a16d-b14e-28aa-22cd8a51422c.png)

②画面を左右にペインを分割します。
キー）Ctrl+b,%：ctrl+bを押した後、%記号を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/41cb87f4-9291-acac-cd51-e1c8e33eeae6.png)

③ペインを移動は次のキーでできます。
キー）ctrl+b,←→：ctrl+bを押した後、矢印（←、→）を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/4005a875-eb62-2d1f-aaf5-e57ee0fa038f.png)

④次は右ペインで上下にペインを分割します。
キー）ctrl+b,"；ctrl+bを押した後、記号「"」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/18f40859-f1a8-7d6f-c8ce-66f986123e36.png)

⑤セッションを中断する場合は、次のキーを押します。
キー）ctrl+b,d：ctrl+bを押した後、dを押します。

~~~bash
pi@RPI-DOCKER:~ $ tmux
[detached (from session 0)]
~~~

⑥セッションの一覧を確認は次のコマンドを入れます。
コマンド）tmux ls

~~~bash
pi@RPI-DOCKER:~ $ tmux ls
0: 1 windows (created Sun Feb  7 21:56:31 2021) [189x44]
pi@RPI-DOCKER:~ $
~~~

⑦セッションを再度開始する場合は、次のコマンドを入れます。
コマンド)tmux a -t 0
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/96273f42-6da1-8c34-4b5c-1dd2f92417fa.png)

⑧セッションを終了したい場合は、次のコマンドを入れます。
コマンド）exit
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/93c7dfec-2735-243e-f8db-b467dbfc37b2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/076000ac-aff1-d598-4ed0-39b9de8016f6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162336/fe98d4bd-fdf4-ebbc-fbc7-9d97df990bd5.png)

~~~bash
pi@RPI-DOCKER:~ $ tmux
[detached (from session 0)]
pi@RPI-DOCKER:~ $ tmux ls
0: 1 windows (created Sun Feb  7 21:56:31 2021) [189x44]
pi@RPI-DOCKER:~ $ tmux a -t 0
[exited]
pi@RPI-DOCKER:~ $
~~~

#その他
今は１つのセッションでの画面でしたが、複数のセッションを使いたい場合は
次のキーを使います。
キー）ctrl+b,c：ctrl+bを押した後、cを押します。
分割されている画面が消えて新しい画面が表示されます。
この場合、分割されている画面に戻りたい場合は「ctrl+b,0」を押します。
新しく生成したセッションに戻りたい場合は、「ctrl+b,1」を押します。
結局新しく作成したセッションに移動は数字キー「0-9」まで使用が可能です。
※画面の線が綺麗に表示されないですが、たぶん言語を変更すれば表示できると思います。
★Teratermの言語を日本語にすれば文字化けにならないです。英語だと今の表示と同じです。★

#終わりに
画面を分割してよいことは左側のペインはソースコードを編集に使います。
右側の上下はソースコードの実行及びコードチェックなどに使うのが便利だと思います。
後は同じユーザーで複数アクセスする場合は、Aさんがtmux上で作業している内容をBさんは見れます。
Bさんからコマンドを入力することもできます。
つまり、環境を共有して使うことができることです。
誰と画面を共有しながらやりたい場合は、おすすめです。

※tmuxについて詳しく知りたい方はネット上で検索してみてください。
m1 macの設定


こちらが纏まってる。自分用に随時メモする予定。

[M1 Macの開発環境](https://qiita.com/shibukawa/items/797b7cbb7e530842e6f7)


# brew

brewは公式の通りではダメなので、直でインストールした

https://qiita.com/Buzzword111/items/c2357d5d74a664346c48


# docker

自分の時は特別対応が必要だった。

# xcode

自分の時は特別対応が必要だった。
Rosettaでの起動を指定すること。
[M1 Macでの iOSアプリの開発環境構築 解説
](https://zenn.dev/osuzuki/articles/dd26742f989e54)

# cocoapod

アーキテクチャ指定で動かすこと


```
arch -x86_64 pod install
```


[Mac M1での pod install エラーの簡単解決方法](https://qiita.com/wwalpha/items/f02a96204fce4bfedad4)
リーダブルコード 6章 コメントは正確で簡潔に

本書の第１部「表面上の改善」にある6章のサマリーをまとめる。
（編集途中です）

**コメントは領域に対する情報の比率が高くなければいけない**

# 6.1 コメントを簡潔にしておく

1行で説明できることを、3行も使わない。

# 6.2 あいまいな代名詞を避ける

代名詞は物事を複雑にする。
読み手は代名詞を「還元」しなければならず、場合によって「それ」や「これ」が何を指すのかよくわからないこともある。

最も簡単な改善は、名詞を代名詞に代入すること。
または文章全体を書き換えて代名詞を明確化できる。

# 6.3 歯切れの悪い文章を磨く

コメントを正確にすることと簡潔にすることは両立する。
単純で短くて直接的なコメントがいい。

# 6.4 関数の動作を正確に記述する

実装に適したコメントは伝わる情報を格段に増やす。

# 6.5 入出力のコーナーケースに実例を使う

慎重に選んだ入出力の実例をコメントに書いておけば、千の言葉に等しい。

# 6.6 コードの意図を書く

コメントはコードを書いているときに考えていたことを読み手に伝えるためのもの。
でも、コードの動作をそのまま書いているだけで何も情報を追加していないコメントが多い。

プログラムの動作を高レベルから説明するといい。
すると、コメントが冗長検査の役割を果たしうる。

# 6.7 「名前付き引数」コメント

インラインコメントを使って引数を名前付きで渡せば、引数をわかりやすくできる。

# 6.8 情報密度の高い言葉を使う

コメントが長くてくどいと感じたら、問題や解決策のパターンやイディオムを説明するための言葉や表現を使えないか確かめる。

# 6.9 まとめ

* 複数のものを指す可能性のある「それ」や「これ」などの代名詞を避ける
* 関数の動作はできるだけ正確に説明する
* コメントに含める入出力の実例を慎重に選ぶ
* コードの意図は、詳細レベルではなく高レベルで記述する
* よくわからない引数にはインラインコメントを使う（`Function(/* arg = */ ... )`など）
* 多くの意味が詰め込まれた言葉や表現を使ってコメントを簡潔に保つ
Python 自作パッケージ 作成からインストールまで

# はじめに
この記事は、社会人２年目 AWS、python初心者が勉強したことをまとめたものです。
間違っている点、改善点等ございましたご指摘いただけると大変ありがたいです。

# この記事について
自身の備忘録です。
Pythonで自作パッケージを作成してインストールするまでの手順を記します。

# 作成

## ディレクトリ構成


```
.
├── sushi_library
│   ├── __init__.py # パッケージimport時最初に呼び出される。
│   └── base.py
├── setup.py # パッケージ作成に必須のファイル
```

## 必要モジュール

### setup.py

```python:setup.py
from setuptools import setup, find_packages

setup(
    name = 'sushi_library', # パッケージ名
    version='0.0.1', # バージョン
    description='Package for data analysis', # パッケージの説明
    author='Sushi Master', # 作成者
    author_email='sushi.master@sushi.com', # 作成者のメールアドレス
    install_requires=['numpy', 'pandas', 'pymysql'], # 依存ライブラリ
    packages = find_packages(),
)

```


```python:__init__.py
from base import *
```

`sushi_library`がimport されると`__init__.py`が最初に読み込まれます。
`__init__.py`は無くてもパッケージの作成は可能です。

ではなぜ`__init__.py`を作成しておくかというと**サブモジュールの名前をインポートするときに少し楽になるからです。**

`__init__.py`がない場合、サブモジュール`base.py`の`db_connection`関数をインポートするには
**`import sushi_library.base.db_connection`**を実行することとなる。

上の`__init__.py`が存在する場合、
**`import sushi_library.db_connection`**を実行すればよく、**サブモジュール名を省略することができるようになります。**



# インストール

パッケージ一式が存在するディレクトリに移動し下記コマンドを実行するとインストールされます。

```console
pip install -e .
```

インストール後はどのディレクトリからもパッケージを呼び出すことができます。
【Shopify】日本語で検索しても、まともなアプリがヒットしない件

#はじめに
今回の記事は、自分の主観が多く含まれます。

しかし、Shopifyのアプリを触っていく上で、非常に不満に思うことが多くあったので、今回は記事としてまとめさせて頂きました。

多くのアプリに対して、**使いにくい**や**高価である**という書き方をしていますが、それらのアプリを開発している開発者の方々は非常にリスペクトしていますし、お金をとることを悪いことだとは考えていません。

しかし、あまり良くないアプリばかりが紹介されているShopifyアプリ業界の現状は良い状態ではないと思うので、注意喚起として今回の記事を執筆させて頂きます。

今回は、Shopifyのアプリに関する問題点について紹介します。

その問題点とは、**日本語でShopifyアプリを調べても、まともなアプリがヒットしない**ということです。

**まともなアプリがヒットしない**は言い過ぎですが、少なくとも、Shopifyの良いアプリには巡り会えません。

Shopifyには有名なアプリは多くありますが、その多くが**高価**で**使いにくい**です。

もっと良い**無料**で**使いやすい**アプリが非常に多く有るんですが、その事実を多くの人が知りません。

今回はそのことについて詳しく考察していきます。

#本当にまともなアプリはないの？
結論から書くと、有名なアプリで良いアプリは殆どありません。

例えばShopifyとインスタを連携させることで有名なアプリとして`Instafeed`と`Instagram shop by Snapppt`ですが、これよりも機能が豊富で使いやすく、かつ無料のアプリがいくかあります。

というか上記の2つは有名なだけで、使いやすくもなければ無料というわけでもありません。

上記の2つより良いアプリとして、`Instafeed, Instagram Feed Shop`や`Section Feed`があります。

どちらも完全無料で使用できる上に、機能が豊富でしかもUXが高いです。

他の分野、例えば**ポイントアプリ**や**予約販売**、**言語変換**や**通貨変換**、**レポート**や**サブスクリプション**、**予約機能**、**テイクアウト**や**デリバリー**に至るまで、有名なアプリで良いアプリは殆どありません。

というか、極論を言えば、**紹介されているアプリで良いアプリはない**ぐらいに考えた方がよいです。

根気よく探せば、殆ど確実に無料・もしくは安価でUXが高いアプリが見つかります。

#どうして有名なアプリで良いものはないの？
ここからは自分の考察がはいります。

##殆どの人が一次情報に触れない

日本語で調べても良いアプリが出てこない理由としては、**殆どの人が一次情報に触れない**からだと思います。

つまり、日本語でアプリを紹介している人も、日本語で調べて出てきたアプリをまとめて記事にしているだけだからです。

Shopifyアプリにおける一次情報とは、つまりはShopifyアプリストアです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878708/4580ffe8-be8c-e69a-e0c6-b938fe2f2741.png)

アプリを調べたり紹介したりする際に、このアプリストアから実際に調べる人が殆どいないように思われます。

多くのプログラミング言語で**公式ドキュメント**が神格化されているように、Shopifyアプリ業界においてもこちらの**公式Shopify app store**から地道にアプリを探すという行為が非常に重要です。

日本語でアプリを調べるのではなく、こちらの公式ストアから地道に探しましょう。

##アプリを紹介するとアフィリエイトが入る
Shopifyアプリの中には、紹介するとアフィリエイトが入るものがあります。

つまり、無料のアプリを紹介しても旨味が無いので殆どの人が有料のアプリを紹介しているのではないかと思います。

#結論　公式ストアから探そう
結論として、Shopifyアプリは公式ストアから探すことを本当にオススメします。

プログラミング学習において公式ドキュメントを読むように、Shopifyのアプリを探すときは必ず公式ストアから探しましょう。

#終わりに
ここまで読んで下さりありがとうございます。

お疲れさまでした。
【自分用メモ】何でか分からないけどアプリが実行できない時にまずすべき事まとめ

#本記事について
「あれれ？うまくアプリが実行されないぞ？」
　---LogCatを見る---
「いや、特に気になるエラーはでてないなぁ…。」

上記のように、何の取っ掛かりもない時にまずすべきことをまとめていきます。
多分、随時更新していきます。

#PC再起動/ キャッシュクリア
何においても「今まで上手くいっていたのに突然上手くいかなくなった！💦」
の場合は、一に再起動、二にキャッシュクリアです。
それでも上手くいかない場合は以下をお試しください。

#$ adb kill-serverする
ターミナルで`$ ads kill-server`を叩いてから、エミュレーターを再起動してください。

```
$ adb start-server
* daemon not running; starting now at tcp:5037
* daemon started successfully
```
となったらokです。

#エミュレータが接続状態か確認する
画像の※１と※２を確認します。
![スクリーンショット 2021-02-07 212728.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/1793c51c-500b-2230-4de3-65b5d9d1ecb1.jpeg)
※１に関して、プルダウンをクリックし、エミュレータがDEADまたはOFFLINEになっていないか確認。
※２に関して、プルダウンをクリックし、実行したいアプリのプロジェクトになっているか確認。

**※１がDEADまたはOFFLINEになっている場合**
プルダウンをクリックして、DEADまたはOFFLINEではないエミュレータが存在した場合はそっちをクリックしましょう。そっちのエミュレータは生きてるかもしれません。

また、そのやり方で上手くいかなかった場合やプルダウンの中に生きてるエミュレータが存在しなかった場合は`Tools->AVD Manager`でAVD Managerを表示し、`Actions`の一番右端のプルダウンから`Delete`を選択して一度エミュレータを削除した後に新しくエミュレータを作成してください。

新規のエミュレータ作成はAVD Manager画面の一番左下隅にある`Create Virtual Device...`というボタンから簡単に作り直せます。同じエミュレータを作りたければ削除する前にメモを取っておくのをオススメします。

**※２が実行したいアプリのプロジェクトになっていない場合**
まず、エミュレータを開発者モードに設定します。
すでに開発者モードになっている場合は必要ないです。

エミュレータの方で`Settings->System->Advanced->About emulated device->Build number`で、Build numberを７回タップすると開発者モードになります。

では、もう一度`System`に戻ってみてください。`Developer options`が追加されているはずです。これが開発者モードになっている証です。

次に、`Developer options`をタップして`Select debug app`をタップし、あなたが実行したいアプリを選択して下さい。**選択されていても、もう一度選択してください。**

これでもう一度アプリを実行してみましょう。大概上手くいきます。

#キャッシュを消す
何度も実行を繰り返したり、デバッグばかりして久々に通常の実行をした時などに残っているキャッシュが悪さをするときがあります。

`Build->Clean Project`でキャッシュをクリーンできます。キャッシュをクリーンしてからもう一度ビルド/デバッグを実行しましょう。大概上手くいきます。

※注意事項※
ちなみにですが、アプリをビルドしないと生成されない情報とかもあり、そういう情報はキャッシュをクリーンしてしまうと一緒に削除されるため、クリーン後に急にコンパイルエラーが生じることもあります。しかし、問題なくビルドできますので、ビルドしてください。ビルド時に再度情報が生成されてコンパイルが通るようになります。

#buildするとSigningReportが下に表示される時
証明書作成をエミュレータ上でやっている状態で、アプリをエミュレータで起動している状態ではありません。以下の画像の`app` を選択して、再びbuildしてみてください。
![スクリーンショット 2021-03-07 12.55.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/906559/ba5b83f4-9f35-beca-0599-87dc86280b2e.png)
大概上手くいきます。

#BPを全部消す
ずっとデバッグしているとブレークポイント(BP)が消えずに残ってしまい、私達からはちゃんとした位置にBPが貼られているように見えるけれどAndroidStudioの裏側では違う行にBPが貼られてしまっている...という怪奇現象もたまに起こりえます。そういう時はBPを全てクリアにしてデバッガを起動しましょう。

`Run -> View BreakPoints -> BPを「−」ボタンで全部消す`この流れでBPを全て消せます。

この状態でアプリをデバッグしてみましょう。大概上手くいきます。

#gradle書いてるのに、gradleの内容が読み込まれてないのかも
例えば、firebaseとかgoogle cloudとかimplementationをgradleに書いていますが、もしかしたらそれが読み込まれていないというか、なんかしらの不具合で整合性が取れてない可能性があります。そういう時は右端のGradle->untitled->build->buildでビルドしてみて下さい。上手くいくかもしれません...！！

#認証系の画面に何故かいかない/思う通りに実行されない
認証系の画面(ログインや登録系)はエミュレータのStorageに情報が残っているとそれが邪魔して期待通りの実行がされない場合があります。

エミュレータの`Settings->Apps&notifications->実行したいアプリを選択->Storage->CLEAR STORAGE`でストレージを削除します。

この方法で実行してうまくいかない場合はFirebaseなどのデータベースに情報が残っていてそちらが悪さをしている場合も考えられますので、そっちも削除してから実行してみましょう。

大概上手くいきます。
windows10でtensorflow

こいついっつもインストールしてんな・・・

#前提条件
windows10 64bit
GPU版
GPUはgtx1070を使用

# CUDAとcuDNNをインストール
以下サイトが詳しかったです。
https://www.kkaneko.jp/tools/win/cuda.html

# pythonをインストール。必ず3.8。
3.9入れても動きません。
「python」で検索してwindows版をインストール
「Add Python3.x to PATH」にはチェックを入れる

# tensorflow-gpuは2.4.1
最新版。

```python
pip install tensorflow-gpu
```

# keras　2.4.3
最新版。

```python
pip install keras
```

# anacondaとかminicondaとかjupiterとか
仮想環境のためのアプリケーション。あると便利だが覚えるのが大変なので、とりあえずは無くても大丈夫。
昔はライブラリごとの依存関係が複雑で、色々なライブラリを動かすための環境を個別に作らないと大変だったが、
最近は大分解消された。
仮想環境作り自体も結構手間なので、後回しでもよいと思う。


# バージョンの確認
コマンドラインで…
py -m pip show tensorflow-gpu -V

以下、意味

- py　pythonをインストールすると、py.exeというランチャーも同時にインストールされる。pythonにはpathが通ってないのでpyでok
- -m　[module-name]pip使う時はこのオプションが必要
- pip　インストーラー
- show　中身教えて
- tensorflow-gpu　「-gpu」をつけないとnot foundと言われる
- -V　バージョン

# カレントフォルダの確認

```python
import os
os.getcwd()
>'C:\\Users\\81909'
```

※ユーザー名は「test_man」です

なぜかwindowsではユーザー名が表示されず仮想環境っぽくなるようですが、
カレントフォルダは
C:\\Users\\test_man
になっていてそのまま作業できます。フルパスで処理する際は気をつける必要があるかもしれません

# tensorflow_hub
あったらいいなが、モデル集みたいなもん。
py -m pip install tensorflow_hub

# gitのインストール
多分必要になるのでついでに
「git　windows」で検索してインストール

# 他

## グラフが表示されない
plt～のコード付近でグラフが表示されない時は、
plt.show()
を入れる
pltはmatplotlibの一部
LaravelのwhereInを使って、配列の並び順に並び替える方法。


##WhereInは勝手に昇順で並び替えられてしまう。

例えば下記を見てください。

```php
$ids = array(1,5,2,4,3);
$users = User::whereIn('id', $ids)->get();

dd($users);
```

上記のようにすれば、`user`は`1,2,3,4,5` の順番で並び替えられてしまいます。

これだと色々と不都合な時がある。
そのため、ちょっと工夫する必要があります。

##WhereInを使って、配列順にソートしたい時

結論からいうと、下記のようにすればOK。

```php
$ids = array(1,5,2,4,3);
$placeholder = '';

foreach ($ids as $key => $value) {
  $placeholder .= ($key == 0) ? '?' : ',?';
}

$users = User::whereIn('id', $ids)->orderByRaw("FIELD(id, $placeholder)", $ids)->get();

dd($users);
```

こんな感じにすれば、きちんと`1,5,2,4,3`の順番で`user`を取得できます。

少し解説していきます。

###foreachの中身

まずは`foreach`の中から。

`$placeholder .= ($key == 0) ? '?' : ',?';`は文字列を作成しています。

`$placeholder`の中身は`"?,?,?,?,?"`こんな感じになっており、次で使います。

###orderByRawメソッド

`orderByRawメソッド`は要するに好きなようにSQL文を書いて、並び替えができるよってメソッドです。

第一引数にはSQL文、第二引数にはバインドしたい値を書けばOK。

###FIELDメソッド

`FIELDメソッド`はSQL側のメソッドで、第一引数に並び替え対象のカラム、第二引数に並び替えたい順番を書けばOK。

今回の場合は`"FIELD(id, $placeholder)"`とすることで、`idカラム`で対象としています。

そして、`$placeholder`は現在`"?,?,?,?,?"`こんな文字列になっています。
そのため、第二引数に`$ids`を指定することで、Laravel側がバインドしてくれて、いい感じにしてくれます。

そして、最後は`get`してあげれば完成。
EBS、S3、EFS三者の違い

<H1>EBS、S3、EFSとは</h1>

EBS : <strong>AWS Elastic Block Store</strong>
S3 : <strong>Simple Storage Service</strong>
EFS : <strong>Elastic File System</strong>

三者ともAWSのストレージサービス。

⭐️　EBSはデータベースやアプリケーション向けのストレージサービス。高速なI/O Throughput。
⭐️　S3は静的webpage、マルチメディアの分配、バージョン管理、ビッグデータ分析に適用。AWS CloudFrontと合わせて使えば、アップロード＆ダウンロードの速度が早くなる。
⭐️　EFSは複数のインスタンスが同一のファイルをシェアできるサービスを提供している。ビッグデータ分析、マルチメディア処理、内容管理に適用。

<H1>EBS、S3、EFSの比較</h1>

<table>
<thead>
<tr>
<td>属性</td>
<td>Amazon S3</td>
<td>EBS</td>
<td>EFS</td>
</tr>
</thead>
<tbody>
<tr>
<td>ストレージ種類</td>
<td>Object storage</td>
<td>Block storage</td>
<td>File storage</td>
</tr>
<tr>
<td>ストレージサイズ</td>
<td>制限なし</td>
<td>最大16TB</td>
<td>制限なし</td>
</tr>
<tr>
<td>I/O Throughput</td>
<td>Single Object Uploadなら<br>一つのファイルサイズは5GBまで</td>
<td>HDD or SSDを選べること<br>によって提供のIOが異なる</td>
<td>Default ３GB</td>
</tr>
<tr>
<td>アクセス</td>
<td>インタネット経由</td>
<td>単一のEC2 instance</td>
<td>複数のEC2 instanceに同時に訪問される</td>
</tr>
<td>スピード</td>
<td>一番遅い</td>
<td>一番早い</td>
<td>普通</td>
</tr>
<tr>
<td>料金</td>
<td>一番安い</td>
<td>普通</td>
<td>一番高い</td>
</tr>
</tbody>
</table>

<h1>終わりに</h1>
各ストレージサービスの特徴が違い。自分（or お客様）のニーズ、シチュエーションに合わせて選べていくのが大事でしょう。

ご覧いただき、ありがとうございます。
お疲れ様でした〜