OmniAICreator/Qiita-1.07M huggingface dataset:
【Laravel】検索機能の実装③　~multipleを使って複数選択可能に~

以前⬇️の記事で、プルダウンから選択して検索できる機能を実装しました。

https://qiita.com/hinako_n/items/96584b4a641097c753c7

ここで作成した検索機能を、プルダウンから**複数選択**して検索できるようにアップデートしてみます。

##作成物
「媒体」を複数選択して検索できるようになっています。
![スクリーンショット 2021-09-16 21.45.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/e93e6356-292b-cc6b-3506-159880d285e6.png)

##ビューの編集

今回編集したのは、「媒体」検索機能のselect要素のみです。

select要素のname属性を配列にして、選択されたoption要素を配列で受け取れるようにします。
またselect要素にmultiple属性を加えます。

```php:index.blade.php
<form action="{{ route('index') }}" method="GET">

//略

<label for="">媒体
  //元のコード　　<select name="medium">
  <select name="medium[]" multiple>
    <option value="">全て</option>
    @foreach ($media_list as $media_item)
      <option value="{{ $media_item->getMedium() }}">{{ $media_item->getMedium() }}</option>
    @endforeach
  </select>
</label>

//略

</form>
```

##ルートの確認
ルートは以前作成した通りです。

```web.php
Route::get('/', [TeachingMaterialController::class, 'index'])->name('index');
```

##コントローラーの編集
今回編集したのは「媒体」の検索部分のみです。

```TeachingMaterialController.php
class TeachingMaterialController extends Controller
{
    public function index(Request $request)
    {
        //略
        
        //元のコード
        if(!empty($media)) {
            $query->where('medium', '=', $medium);
        }

        //編集後のコード
        if(!$media[0]==null) {
            foreach($media as $medium) {
                $query->orwhere('medium', '=', $medium);
            }
        }

       //略

```

###解説
* 元々のフォームでは、`option要素の「全て」`を選択した場合は`value=""`の通り空のデータとなり、$mediaの中身は空でした。
* 今回フォームのselect要素のname属性を配列にしたことで、「全て」を選択した場合の`$media`の中身は以下のようになります。よってif文の`!empty($media)`を`!$media[0]==null`という条件に変更しました。

```
array (
  0 => NULL,
)  
```

* if文の中で、$mediaで受け取った配列の中身ひとつずつに対して、データの絞り込みを行います。

<br>
##参考記事
http://html5.cyberlab.info/elements/forms/select-multiple.html

https://www.pnkts.net/2020/07/09/php-select-multiple
【TypeScript】type派？interface派？僕はもちろんtype派

# TypeScript？
--- 

## 型付けできるJavaScript

```typescript
let age: number = 0;

age = 28;

age = "ぞんぞん"; //エラー
```

--- 

## いろんな型

|  型  |  意味  |  例  |
| ---- | ---- | ---- |
|  string  |  文字列  |  'ぞんぞん'  |
|  number  | 数値  | 28 |
|  boolean  | 真偽値  | true |
|  []  | 配列  | string[] → ['aaa', 'bbb', 'ccc'] |
|  any  | 制約なし |  |

---
# オブジェクトの型は？
---
## 普通に書くと読みづらい、管理しづらい🥺(ぱおん)
```typescript
const miyazon: {name: string, age: number, department: string} = {
  name: "みやぞん",
  age: 28,
  department: "product"
}
```
---
# interface vs type(型エイリアス)
---
## オブジェクトの型指定にはinterfaceまたはtype(型エイリアス)を使う

### interface

```typescript
interface Employee {
  name: string;
  age: number;
  department: string;
}

const miyazon: Employee = {
  name: "みやぞん",
  age: 28,
  department: "product"
}
```
---
## オブジェクトの型指定にはinterfaceまたはtype(型エイリアス)を使う

### type(型エイリアス)

```typescript
type Employee = {
  name: string;
  age: number;
  department: string;
}

const miyazon: Employee = {
  name: "みやぞん",
  age: 28,
  department: "product"
}
```
---
## interfaceとtypeの違い
|   |  interface  |  type  |
| ---- | ---- | ---- |
| 用途  |  クラス、オブジェクトの型定義  |  型に別名をつける  |
| 交差型, 共用体型, タプル型, マップ型  | 非対応 | 対応 |
|  同じ要素名の再宣言  | 拡張される  | できない |
---
## 「用途の違い」を感じる(interface)
VS Codeなどで名前をホバーしたとき、型本来の情報を出してくれますが、ここで違いが出ます。
interfaceは型そのものなので名前が出ます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386799/6728209e-a43a-4c17-7a0b-541617b00559.png" width="550">

---
## 「用途の違い」を感じる(type)
無名の型に別名を付けただけなので、本来の「無名の型」が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/386799/396b6dd6-2d78-7723-68d1-06aae333adde.png" width="550">

---
## 「同じ要素名の再宣言」を感じる
typeでは再宣言した時点でエラーとなるがinterfaceではどうなる...？

```typescript
interface Employee {
  name: string;
}

interface Employee {
  age: number;
}

interface Employee {
  department: string;
}
```
---
## 「同じ要素名の再宣言」を感じる
エラーにならない！

```typescript
interface Employee {
  name: string;
}

interface Employee {
  age: number;
}

interface Employee {
  department: string;
}

const miyzon: Employee = {
  name: "ぞんぞん",
  age: 28,
  department: "product"
}
```
---

## どっちを使う？

- できることがtypeのほうが多い
- interfaceの再宣言による拡張でバグが発生しうる

---

# 型にはまらない人間でありたいが、私はtypeを選びます。

---

## 「複業クラウド」について
弊社Another worksでは複業マッチングプラットフォーム「複業クラウド」を開発しています！

▼複業でスキルを活かしてみませんか？複業クラウドの登録はこちら！
https://talent.aw-anotherworks.com/?login_type=none
備忘録【現場で役立つシステム設計の原則】③

##第3章　業務ロジックをわかりやすく整理する
###業務ロジックをわかりやすく整理する基本のアプローチ
☆データとロジックを一体にして業務ロジックを整理する
☆三層構造のそれぞれの関心事と業務ロジックの分離を徹底する

ダメなケースは、
・データクラスと機能クラスを分けると変更が大変になる
・データクラスを使うと業務ロジックの重複が増える
・共通機能ライブラリで汎用化、細分化してもほとんど失敗する

###データとロジックを一体にして業務ロジック分離を整理する

####業務ロジックを重複させないための設計
使う側のクラスのコードがシンプルになるように設計すること
☆メソッドをロジックの置き場所にする
　インスタンス変数を渡すだけのgetterメソッドは書かない
☆業務ロジックをデータを持つクラスに移動する
☆使う側のクラスにロジックを書き始めたら設計を見直す
　オブジェクト指向設計は、改善の繰り返し。
☆メソッドを短くして、ロジックの移動をやりやすくする
☆メソッドでは必ずインスタンス変数を使う
☆クラスが肥大化したら小さく分ける
☆パッケージを使ってクラスを整理する

###三層のそれぞれの関心事と業務ロジックの分離を徹底する
業務データと業務ロジックを一つにまとめたオブジェクトをドメインオブジェクトという。
ドメインオブジェクトとして小さい単位に分けて整理すると、クラス数が膨大になる。その場合は、パッケージ化を進める。
CCNA　ネットワーク初歩

ネットワークに関する基本的な用語を復習がてらまとめようと思います。

まず、人と人の繋がりを人脈というのに対して、コンピュータ同士の繋がりを
コンピュータネットワークと言います。
これにはメリットが３点あります。1点目はデータを共有できることです。
2点目はリソースを共有できることです。
3点目は情報の送受信ができることです。

繋ぎ方は2つありまして、比較的狭い範囲でのネットワークを
LAN(Local Area NewWork)と言い、
LANとLANを繋いだものをWAN(Wide Area Network)と言います。
これは英語も踏まえて覚えましょう。
また、WANは提供しているのが電気通信事業者でキャリアと
呼ばれています。
このキャリアのおかげでLANとLANが繋がっています。

WANの一種で、皆さんが普段使っているのが、インターネットです。
これは世界規模のネットワークで、プロバイダ(ISP:Internet Service Provider)に
よって提供されています。後々学ぶTCP/IPという技術を使っています。
インターネットは社内用ネットワークのイントラネットと
イントラネットとイントラネットを繋いだエクストラネットに分かれます。
これはLANとWANの関係に似ていますので、ここで抑えましょう。
イントラネットにより、使用を限定されたアプリケーションやデータを共有することができます。

ある機能を持ったソフトウェアをアプリケーションと言います。
このアプリを提供する側をサーバーと言い、この提供を受ける側を
クライアントと言います。この関係も抑えましょう。

そのアプリの中でもインターネットを利用したものを
ネットワークユーザアプリケーションと言います。
大きく分けて3つに分かれます。

ユーザが予め実行したタイミングで実行&終了できるものを
バッチアプリケーションと言います。バッチ処理はまとめてすることを意味します。
サーバーとクライアントでデータ処理が発生するのをインタラクティブアプリケーションと言います。
これはゲームなどを表します。

ラインなどのようなクライアント同士で使うアプリを
リアルタイムアプリケーションと言います。

コンピュータはどんな情報も2進数に直して送ります。
このデータをバイナリーデータと言います。
しかし、データはケーブルを通るので、電気信号に直す必要があります。
人が分かる情報をバイナリーデータ、これを電気信号、
これをバイナリーデータ、これを人に分かる情報に直します。

人に分かる情報をバイナリーデータに直すことを符号化(エンコード)と言います。
その反対を複合化(デコード)と言います。
また、世に存在する文字に数字を付けます。これを文字コードと言いまして、
これが違うと文字化けが起きてしまいます。
バイナリーデータを送信する際には0がOFFで、1がONとして送信します。

このデータを1秒間に送ることができる量を帯域幅と言います。
これは通信速度ということになります。
単位はbps(bits per second)で表します。

通信障害は代表的な2つを挙げます。
データを大量に送りすぎてデータ通信が困難になることを
輻輳と言います。
もう一つはデータが早くor遅く着いたことが原因で、映像の乱れに
繋がったりすることをジッタと言います。

ネットワークにおける機器をノード、ノードとノードの間の
通り道のことをリンクと言います。
このノードの繋ぎ方をトポロジと言って、
4つあります。

1つ目がバス型トポロジです。
これは同軸ケーブル(バス)にノードを繋ぎます。
この終端に終端抵抗(ターミネータ)がありまして、電気信号を遮断します。
しかし、バスに障害が起きると全てがダメになるので、あまり使われていません。

2つ目はスター型トポロジです。
これは車輪のように付けることからハブアンドスポークと言われます。
これは集線装置のハブを使い、これがLANで最も多く使われています。

3つ目はリング型トポロジです。
これは周りをトークンと言われる信号が回っています。
信号がキャッチしたら送受信ができます。

4つ目はメッシュ型トポロジです。
これには2種類ありまして、全てのノードを繋げる
フルメッシュ型トポロジと代表的なノードだけを全てのノードに
繋げるメッシュ型トポロジに分かれます。
コストがかかるが、どちらも冗長性に優れています。

最後に通信の種類に関してです。
1:1の通信をユニキャストです。
1:多をマルチキャストで、1:全はブロードキャストと呼ばれています。

#最後に

自分のアウトプットとして知識を出していますので、分かりにくい点等があると
思いますが、良ければ見て頂けると幸いです。
勉強中の身ですので、もし不明点や間違った点がありましたら是非コメント下さい。
【Rails】同名のパスのviewで表示するインスタンスを使い分ける

rails初学者によるポートフォリオ作成中の学びのアウトプットです。

#実現したい事
クイズアプリを作成中に複数のモードを全てquestionモデルとanswerモデルで賄いたい（ジャンルAのクイズ集とジャンルBのクイズ集を実装する）。下記の二つのページのようにnew_answer_pathからアクセスした時に異なるジャンルの問題が表示されるようにしたい。

（モードA）

ジャンルAの問題を表示

回答欄（answerモデルに保存される）


（モードB）

ジャンルBの問題を表示

回答欄

#実装の流れ
①questionモデルにmode_numカラム（integer)を追加して問題のジャンルを番号で識別するようにする。

②new_answer_path(mode_num: <識別番号>)とし、newアクションにmode_numを渡すようにする。

③newアクションで渡された識別番号からquestionモデルを検索するよう実装する。

#実際のコード

(②のステップから)
問題ジャンルAはquestion.mode_numを1に設定。
問題ジャンルBはquestion.mode_numを2に設定。

```home.html.erb
 <%= link_to "問題を解く", new_answer_path(mode_num: 1) %>
.
.
.
 <%= link_to "問題を解く", new_answer_path(mode_num: 2) %>
```


```answer_controller.rb
def new
    @questions = Question.where(mode_num: params[:mode_num])
    @question = @questions.find(@questions.pluck(:id).sample )
    @answer = @question.answers.new
end
```
 @questions = Question.where(mode_num: params[:mode_num])で受け取ったmode_numから該当するジャンルのquestionを配列で取得。

@question = @questions.find(@questions.pluck(:id).sample )で
取得した配列の中からランダムで一つのquestionを取得してそれをviewに表示する。

```new.html.erb

   <%= @question.content %>

   <%= form_with(model: @answer, local: true) do |f| %>
 

     <%= f.text_area :content %>
    
     <%= f.submit "次の問題へ進む" %>
   <% end %>

```
これでhome.html.erbの上のリンクと下のリンクからそれぞれアクセスした際、
   <%= @question.content %>に違う内容が表示される。
Linuxコマンド 基本



基本的なコマンド
コマンドを実際に試していくわけですが、普段ターミナルに打ち込むかと思います。
そのときにユーザーの入力したコマンドを解釈するソフトウェアのことをシェルといいます。

```

ls
lsはlistの略です。
lsコマンドでは、カレントディレクトリにあるファイルやフォルダの名前一覧を出してくれます。

Pro:~ username$ ls

Applications        Google ドライブ     Public          pitchshiftLogFile.txt   workspace
BackgroundMusic     Library         SHOW            projects
Desktop         Movies          bash_profile        rbenv
Documents       Music           dump.sql        reset.sql
Downloads       Pictures        evalonly.txt        ruby
cp
cpコマンドはcopyの略で、そのままファイルをコピーするコマンドです。

下記コマンドで同じディレクトリにコピーファイルを作成します。

Pro:blog username$ 
cp scope.rb scope1.rb
                          <!--cp [コピー元] [コピー先] で表します-->
mv
ファイルの移動、およびリネームを行います。

mv scope.rb /Users/kouyamaakira/projects/ruby
<!--mv [移動させたいもの] [移動先]-->

 mv scope.rb rename.rb
 <!--mv [元のファイル] [新しいファイル名]-->

rm
ファイルを削除するコマンドです

rm kesu.rb
 <!--rm [消したいファイル名]-->

mkdir
新規にディレクトリ（フォルダ）を作成するコマンドです。

mkdir make

cat
ファイルの内容を表示できるコマンドです。
サーバーをつなげたときに、エラーを見たいときなんかによく使います。
cat cat1.rb

こんなかんじ

2つのファイルを結合して中身を見ることもできます。

cat cat1.rb cat2.rb


tail
ファイルの終わり部分を表示するコマンドです。

オプションをつけずに実行すると終わりから10行表示されます。

これもエラーデバッグなんかで使われます。

tail scope.rb
エクスプローラからWindows Terminalのvimでファイルを開く

1. 任意のフォルダにvi.batを作成する

```vi.bat
@echo off

start "" "wt.exe" "wsl.exe" "vim" "`wslpath -a -u \"%1\"`"
```
2. vi.batのショートカットをshell:sendtoに入れる

3. エクスプローラの送る＞vi.batショートカットへファイルを送るとWindowsTerminalのWSLのvimでファイルが開く。

以上。
Gitコマンドを補完したり便利に使えるようにする

コマンドの補完と現在いるブランチが一眼でわかるようにするターミナルの設定方法忘備録です。
※すでにインストールしてあったりするものに関しては手順を飛ばしてください。
※この記事ではbashを利用しています。zsh愛用者の方は適宜置き換えてください。

# 手順
## home brewでGitをインストールしているかどうかを確認する
前提条件として __home brew__ でgitがインストールされている必要があります。

下記のコマンドで対象のファイルが見つかればhome brewでGitがインストールされています。

```
$ ls /usr/local/etc/bash_completion.d/
git-completion.bash                         git-prompt.sh 
```

## gitをインストールする
まさか __homebrew__ をインストールしてない人なんていませんよね？
もしまだインストールしていないようであれば、導入してください。

```
$ brew install git
```

## pathを通す

```
# インストールしたgitのバージョンを確認
$ brew info git | grep stable

# 下記コマンドを実行してHomebrewのgitのパスを通す。(X.XX.Xは先ほど確認したバージョンを入力)
$ echo 'export PATH=/usr/local/Cellar/git/X.XX.X/bin:$PATH' >> ~/.bash_profile

# .bash_profileを再読み込みする。
$ source ~/.bash_profile

# バージョン確認
$ git --version
> git version X.XX.X
```

## .bashrcに追加する
「vim ~/.bashrc」コマンド等で追加してください。

```
source /usr/local/etc/bash_completion.d/git-prompt.sh 
source /usr/local/etc/bash_completion.d/git-completion.bash 
GIT_PS1_SHOWDIRTYSTATE=true 
export PS1='\h\[\033[00m\]:\W\[\033[31m\]$(__git_ps1 [%s])\[\033[00m\]\$ ' 
```

## .bash_profileに追加する
「vim ~/.bash_profile」コマンド等で追加してください。

```
if [ -f ~/.bashrc ]; then 
    . ~/.bashrc 
fi 
```

## ターミナルを再起動する
ターミナルを再起動してください。

あなたの画面には便利なターミナルがきっと表示されているはずです。
補完もきいてるし現在いるブランチもわかるようになりました。
`.git`フォルダがなければブランチは表示されません。

# 最後に
参考にしたサイトのまとめみたいになりましたが、Gitを便利に使いたいなって思う人が増えてくれたら幸いです。

## 参考記事
- https://qiita.com/miriwo/items/68a372ef7bc69d120444
- https://hirooooo-lab.com/development/git-terminal-customize-bash/
「今日の積分」を参考にWolframAlphaとSymPy Liveでやってみたい。

オリジナル

https://qiita.com/fred55/items/04d2cf68407693a3dca2

#wolframalphaで計算

#### 積分より

https://ja.wolframalpha.com/input/?i=%E5%AE%9A%E7%A9%8D%E5%88%86&assumption=%7B%22F%22%2C+%22Integral%22%2C+%22rangeend%22%7D+-%3E%22pi%2F4%22&assumption=%7B%22F%22%2C+%22Integral%22%2C+%22integrand%22%7D+-%3E%22sqrt%28tan%28x%29%29%22&assumption=%7B%22C%22%2C+%22%E5%AE%9A%E7%A9%8D%E5%88%86%22%7D+-%3E+%7B%22Calculator%22%7D&assumption=%7B%22F%22%2C+%22Integral%22%2C+%22rangestart%22%7D+-%3E%220%22

定積分

https://ja.wolframalpha.com/input/?i=+%28%CF%80+%2B+log%283+-+2+sqrt%282%29%29%29%2F%282+sqrt%282%29%29

近似値
0.4874954943993610483599501672646054220029601099973553028726341256...

#### Qiitaの解答例を使って、wolframalphaで計算

https://ja.wolframalpha.com/input/?i=+%28%CF%80+-+2*log%281%2B+sqrt%282%29%29%29%2F%282+sqrt%282%29%29

近似値
0.4874954943993610483599501672646054220029601099973553028726341256...

#### 差を、wolframalphaで計算

https://ja.wolframalpha.com/input/?i=+log%283+-+2+sqrt%282%29%29-%28-+2*log%281%2B+sqrt%282%29%29%29

結果
0

#SymPy Liveで

以下サイトに、ソースコードを貼り付けて、Evaluateです。

https://live.sympy.org/


#### 定積分の計算
計算できませんでした。

```python
from sympy import *
var('x')
print("#",integrate(sqrt(tan(x)), (x, 0, pi/4)))
# Integral(sqrt(tan(x)), (x, 0, pi/4))
```


#### 差を、SymPy Liveで計算

```python
from sympy import *
eq1= log(3 - 2*sqrt(2))
eq2= -2*log(1+ sqrt(2))
print("#",eq1-eq2)
print("#",float(eq1-eq2))
# log(3 - 2*sqrt(2)) + 2*log(1 + sqrt(2))
# 2.3636425261531484e-125
```

('#', log(3 - 2*sqrt(2)) + 2*log(1 + sqrt(2)))
('#', 2.3636425261531484e-125)
CCNA　ACL

## Part 1　ACLの概要

ルータを通過するパケットをチェックし、その通信の制御をするために
ACL(Access Control List)が使用されます。
許可する通信、拒否する通信に分けることをパケットフィルタリングと言います。

番号付き標準ACLは番号は1~99,1300~1999
名前付き標準ACLは送信元IPアドレスでフィルタリングします。
番号付き拡張ACLは番号は100~199,2000~2699です。
名前付き拡張ACLはより詳細な設定ができます。

## Part 2　ACLフィルタリングのルール

ACLはリストの上の行から検索される。
該当する行があった場合、それ以降の行は検索しない。
最後の行には暗黙の「全て拒否」が入るので、
許可するか、しないかの2択になる。

## Part 3　標準ACLの作成方法

RT(config)# access-list [ACL番号][permit|deny][送信元IPアドレス]
[ワイルドカードマスク]で表します。

ただし、ワイルドカードで255.255.255.255はanyにしても同じです。
同様に0.0.0.0のワイルドカードだとhostにしても同じです。

名前付き標準ACLを作成するときは
コマンドでRT(config)# ip access-list standard[ACL名]を入力します。
すると、ACLコンフィギュレーションモードに移行します。
その状態で[permit|deny][送信元IPアドレス][ワイルドカードマスク]を入力します。

また、ワイルドカードマスクの求め方は
255.255.255.255からサブネットマスクを引くと分かります。

##Part 4 標準ACLの適応方法

インバウンドで適応した場合、ルータのインターフェイスにパケットが届いた段階で
ACLによるチェックが行われる。
アウトバウンドで適用した場合、ルータからパケットが出ていく段階で
ACLによるチェックが行われます。
また、標準ACLは拒否するトラフィックの宛先近くに設置することが
望ましいです。
しかし、拡張ACLは拒否するトラフィックの送信元近くに設置することが
望ましいです。なぜかと言うと、これは詳細の設定ができるからです。

##Part 5 拡張ACL

RT(config)# access-list[ACL番号][permit|deny][プロトコル]
[送信元IPアドレス][ワイルドカードマスク](送信元ポート番号)
[宛先IPアドレス][ワイルドカードマスク](宛先ポート番号)
[オプション]

特にプロトコルとポート番号は注意して確認するようにしてください。
【Python Django】マイグレーション(DB)をロールバック、元に戻す

Djangoはマイグレーションファイルからテーブルの定義変更や追加する機能がDjango ORマッパーにあります。さらに「ロールバック」つまり、マイグレーションファイルをもとにデータベースを前の定義に戻すこともできます。

用途としては、

- チームで開発してて、migrateした後に「こっちに修正した方が良くない？」と言われたので修正必要
- migrateしたけど、定義抜けてたからロールバック（戻して）定義追加し直したい

などに使えます。
ある程度Djangoで開発していくなら必須の知識ですね！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260345/e01c93ce-0bc6-1984-da1d-354054ad3484.png)

DjangoのDB・モデルの基礎は以下のように書いてますので良ければ

https://qiita.com/Bashi50/items/280bc341eeec5d7f8797

今回以下の記事のmodel定義を使用して記事作成しました。

https://qiita.com/Bashi50/items/9e1d62c4159f065b662b




# マイグレーション履歴を確認

```
python manage.py showmigrations
```
マイグレーション履歴を確認できる。
どこに戻せばいいか、「アプリ名」、「マイグレーション名」を確認。

```
accounts
 [X] 0001_initial
 [X] 0002_order_product_tag
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
```

今回は`accounts`アプリの`0002_order_product_tag`をロールバックにして元に戻すことにします。
# ロールバックのコマンドはmigrate
rollbackみたいなコマンドはなくて、migrateコマンドにどこまで戻るかをマイグレーション名を入力することで行うことができます。

書き方は
`python manage.py migrate <アプリ名> <マイグレーション名>`

例：

```
python manage.py migrate accounts 0002_order_product_tag
```

で指定したマイグレーションまで戻る

# マイグレーションファイルの修正や削除
先ほどのコマンドでロールバックすることができました。変更をなかったことにするなら、該当のマイグレーションファイルを削除すれば完了です。
もし定義を変更するなら

## パターン１: modelを編集してmakemigrations
できればこれやった方が安全。
`models.py`を編集してマイグレーションファイルを削除
今回は私の場合は`accounts/migrations/0002_order_product_tag.py`を削除ですね。

```
python manage.py makemigrations
```

でもう一度作成、

```
python manage.py migrate
```

これで完了。

## パターン2: マイグレーションファイルを直接編集
知識ある場合に使ってください。
マイグレーションした時に、新規データを作成したり、既存のデータを変更・削除したいなどなど、特別な処理を記述する場合は該当ファイルを編集してください。

あとは、同じく

```
python manage.py makemigrations
```

でもう一度作成、

```
python manage.py migrate
```

# 全てを初期の状態に戻したい
たまに、まっさらな状態に戻したいということもあると思います。そんな場合は以下のコマンドを使用。

`python manage.py migrate <アプリ名> zero`

例：

```
python manage.py migrate apps zero
```
行単位でRubocopを無効にする方法

##行ごとに無効にする場合
行末にコメントアウトで`# rubocop:disable ルール名`を記述する。

例.

```Ruby
~処理~　# rubocop:disable Rails/OutputSafety
```

## 複数行にまたがって無効にする場合
ルールを無効にしたい処理の前後を以下の様に囲む。

例.

```Ruby
# rubocop:disable Rails/OutputSafety
~処理~
# rubocop:enable Rails/OutputSafety

```
コードプロファイラによる処理時間増加問題

# はじめに
実装機能の処理時間を調査しているときに、実装箇所とは別のところで大幅に時間がかかっている問題に遭遇しました。
その時の原因と、おまけで計測処理について自分用に記録します。

# ざっくり言語・フレームワーク
言語:PHP
フレームワーク:FuelPHP

# 原因
***結論:コードプロファイラがオンになっていたため***

より細かく処理時間をログ出力して、原因を調査してみたところ最後のレスポンスを返すところまでは問題ありませんでした。

お手上げ状態の時にたまたま、コードプロファイラの存在を思い出し、オフにしてみたら解決しました。
プロファイラでは、プログラム実行時の様々な情報が確認できその中で実行されたSQLも確認できる。今回、かなり多くループ処理でSQL文（4~5万）が実行されていたた。そのすべての情報をプロファイラで出力されていたためプロファイラの処理に時間がかかったと考えられる。


### （コード）プロファイラとは？
> プロファイラとは、コンピュータプログラムが実行される様子を監視・記録し、プログラム中の各箇所の動作順や実行時間などを集計・解析するプログラム。ソフトウェアの開発環境や実行環境の機能の一部として提供され、プログラムの性能測定・解析を行うことができる。
引用元：[プロファイラ（profiler）とは - IT用語辞典 e-Words](https://e-words.jp/w/%E3%83%97%E3%83%AD%E3%83%95%E3%82%A1%E3%82%A4%E3%83%A9.html)

FuelPHPに同梱のアプリケーションプロファイラは、PHPQuickProfilerをベースにしています。
以下の画像がプロファイラの画像です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381376/0f30d816-4bf6-358d-d6b4-23a7bea13ff3.png)

プロファイラでは以下のような情報がわかる。

> - Console. デフォルトのタブ。エラー、ログ、メモリ使用量や実行タイミングに関する情報を提供します。
- Load time. リクエストのロード時間。タブ内で詳細を表示します。
- Database. 実行されたクエリ数や実行時間および、もしサポートされていればクエリの解析。
- Memory. リクエストの処理に要したメモリ使用量。
- Files. 読み込まれた PHP ファイルのフルパス名と、それらの容量。
- Config. 処理の終了時点における、設定値の内容。
- Session. 処理の終了時点における、セッション値の内容。
- GET. $_GET 配列の内容。
- POST. $_POST 配列の内容。

使用するには設定ファイルで、以下のように設定すればよい。

```php
'profiling'  => true,
```

# まとめ
今回は、コードプロファイラによって処理時間が大幅に増えたときのことをまとめた。
プロファイラの使用中に処理時間が増えた場合は、まず今回の件を思い出したい。
ユーザーごとの投稿一覧を表示させたい

<h2>患者(user)ごとの記録一覧ostomy(index)を医療者(admin)が見れるようにしたいが、nullデータが持ってこれない</h2>
```ruby:Staff/Ostomies.controller.rb
class Staff::OstomiesController < ApplicationController
  before_action :authenticate_staff!
  def index #患者さんごとのindex
    @ostomies = Ostomy.where(patient_id: params[:patient_id])
  end
```

```ruby:routes.rb
 namespace :staff do  #医療者のコントローラー内
    :
     resources :ostomies,:only => [:show],:index] do 　#患者さんが書いた記録
       resources :comments, only: [:create, :destroy] #記録へのコメント
       resource :favorites, only: [:create, :destroy]　　#記録へのいいね
     end
    resources :patients,:except => [:destroy,:new,:create] do
       resources :records,:except => [:destroy]   #患者ごとに記録するためネストさせる
    end
  end
```
<h2>idをurlにぶち込むことでid指定して持ってこれるようになる</h2>

https://railsguides.jp/routing.html

　
参照

```ruby:routes.rb
 namespace :staff do
    get '/ostomies/index/:patient_id',         to: 'ostomies#index', as: 'ostomy_index'
    get '/ostomies/show/:ostomy_id/:patient_id',to: 'ostomies#show', as: 'ostomy_show'
   :
  end
```
```
staff_ostomy_index GET  /staff/ostomies/index/:patient_id(.:format) staff/ostomies#index                                             
staff_ostomy_show GET  /staff/ostomies/show/:ostomy_id/:patient_id(.:format)  staff/ostomies#show                                                  

```
LinqPad: PostgreSQL のデータ操作を EF Driver で

# やりたかったこと
- LinqPad から PostgreSQL に、Insert/Update/Deleteといった操作を行いたい

# 背景
Power BI のデータソースで PostgreSQL を指定された。Sample Data は無く Create用 SQL だけは更新されて渡される。
LinqPad でSampleDataでも作るかと思ったところ・・

- Linq to DB だとClassとか諸々用意必要で、Schema変更の度に修正が必要で面倒・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/3918e2c5-33bf-0bda-ffa6-a867d319a01a.png)

で、Entity Framework Core Driver を使うことにしたというお話
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/9340ced2-8b16-564a-82ac-2f165f8f5ec3.png)

# 環境
- [PostgreSQL 13.4](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads)
- Visual Studio 2019
- .Net 5<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/81445b50-79df-8619-6901-b71bb9dfe6af.png)
- LinqPad 6

# 概要
1. PostgreSQL から DB First で DbContext を生成し、適当にBuildして Exe/Dll を用意しておく
1. Linq Pad で 作成した Exe を取り込んで接続

## PostgreSQL から DB First で DbContext を生成し、適当にBuildして Exe/Dll を用意しておく

適当なProjectで、以下二つを nuget から取得

- Microsoft.EntityFrameworkCore.Tools
- Npgsql.EntityFrameworkCore.PostgreSQL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/f516ac79-ed76-575c-cc7b-0cfcba75abb3.png)

対象となる Postgres の設定を使って、Scaffoldを実施

- "Force" の理由
    - Create.sql が更新されてくる為、Schema 毎削除して実施（コマンドは後述）する為、強制上書きとしている

```postscript:Scaffoldコマンドの例
Scaffold-DbContext "Server=localhost;Port=5432;User Id=postgres;Password=postgres;Database=postgres;" Npgsql.EntityFrameworkCore.PostgreSQL -o DbModels -Force
```

### 複数形にしたくない場合
> -NoPluralize

詳細は以下で

https://docs.microsoft.com/en-us/ef/core/cli/powershell#scaffold-dbcontext

## Linq Pad で 作成した Exe を取り込んで接続

Entity Framework Core で接続する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/86daaded-8b53-3c6b-18ce-a24f07e64bc0.png)

接続設定は生成した Dll を参照すると、内部の DbContext を選択出来るので、それを選択
※Connection String を別途ここで指定することも可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/e9bb50e5-2f25-9862-0ff8-4536fced68a3.png)

# 実際の Add/Remove

こんな感じで Add/Remove が出来るので、Sample Data をC#で生成して Power BIで分析がしやすくなりますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/76521748-cc09-5380-c9d0-4baeecef90dd.png)

テストTableは以下で紹介されている Create を利用

https://www.postgresql.jp/document/7.2/reference/sql-createtable.html

# あとがき
.Net 4の頃のように、ADO.Net Entity Data Model の追加でいけるかと思ったけど、以下エラー。Core 3ではそもそもメニューにはなかった記憶なので、そのうち対応されると期待　→　コマンド入力不要になる？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/99a138af-a126-33b3-515f-c0316043bdf6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/b89d2019-67cc-783a-56ee-de0855b646f8.png)

# PostgreSQL を Power BI のデータソースとした際に利用した SQL
## 全Table削除 SQL
Create 用のSQLが更新して渡されてくるので、SQL実行前に Schema 毎削除する為のコマンド
> DROP SCHEMA public CASCADE;
> CREATE SCHEMA public;

# keyword
"Add" and "Remove" commands in LinqPad with PostgreSQL
不要なbranchをまとめて削除する方法



masterブランチにmerge済みのブランチを、
一気にまとめて消す手順。
(masterを他のブランチ名に読み替えても可能)
####手順 1. masterブランチに移動

```
git checkout master
```


####手順 2. masterブランチ以外のmerge済みのブランチを確認
`git branch --merged`でmerge済みのブランチを出力。
`grep -v master`で"master"というブランチ名以外のブランチを出力している。

```
git branch --merged | grep -v master
```

####手順 3. masterブランチ以外のmerge済みのブランチを削除
`xargs`コマンドで手順 2.の出力結果を`git branch -d`に引数として渡す。

```
git branch --merged | grep -v master | xargs git branch -d
```
クラッキング入門(2) IDA Freewareを使用したcrackmeの解説、keygenの作成

# 注意事項
この記事ではクラッキングを助長する意図はありません。
また、この記事で紹介する手法はあくまでも一例です。小学生でも「オレ強い」できるレベルしか紹介する気はありません。(CTF入門以下のレベルです...)
あくまで自分の作ったソフトウェアのセキュリティ向上にお役立てください。
商用ソフトウェアではこの手法はほぼ通用しませんし、改変したソフトウェアを使用することは訴訟リスクにもなりえます。

## はじめに
こんにちは。
前回、[リバースエンジニアリングって何？ハッキング入門してみる](https://qiita.com/drafts/c7b1010c62e729026ed8/)という記事を書いたのですが、案外LGTMが多かったので続きを書いてみようかと思います。
今回は、簡単なcrackmeのリバースエンジニアリング、及びkeygenの作成です。
crackmeとはその名の通り、バイナリのクラッキング練習用に作成されたプログラムのことです。

## 必要なもの
- GhidraやIDA Free (もちろんProでも同じです)、HopperなどのDisassembler
- [crackme by LiveOverflow](https://github.com/LiveOverflow/liveoverflow_youtube)
- 前準備として、*0x07_0x08_uncrackable_crackme*にある*license_2.c*をコンパイルしておいてください。

```
gcc license_2.c -o license_2
```

## 手順
### 解析
**筆者が使い慣れていることもあり、今回はIDA Freewareを使用します。
Ghidraなどでも手順はほとんど同じです。(IDA Freewareは商用利用が禁止されている点やx86/64にしか対応していない点にご注意ください)**

まず、先程コンパイルした*license_2*を読み込み、Graph Viewを表示してみます。
![Screenshot 2021-09-16 at 20.08.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185096/48620e72-0832-bfdd-d904-ee87406afdd9.png)

分かりやすいようにラベルをつけておきました。これだけでも十分分かりやすいですが、main関数の擬似コードを表示してみましょう。(IDAの場合F5)
![Screenshot 2021-09-16 at 20.22.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185096/8bc0bef2-2de4-a094-5a44-b27a3dbd509e.png)

今回は単純なプログラムなので、ほぼソースコードそのままの擬似コードが生成されました。(コメント部分は私が書いたものです)
コードから、このプログラムのライセンスチェックは以下のフローで行われると推測されます。

0. 変数v5を用意
1. 引数(以下key)の存在チェック(ない場合Usageを表示)
2. strlenでkeyの長さを取得し、その回数だけforループ
3. forループ内で、key[i]をintに変換、変数に加算
4. ループ終了後、v5が916なら*Access Granted!*を表示、違うのであれば*WRONG!*を表示

今回は、これにあったKeygenを、擬似コードから作成してみます。

### Keygenの作成
今回のKeygenは、それぞれの文字のUnicode値の合計が916になるようなKeyを生成するプログラムです。
私の場合、以下のようなコードになりました。

```C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    printf("Generating License...\n");

    char key[512];
    int charSum;

    while (1) {
        char randomLetter = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"[arc4random() % 65];
        charSum += (int)randomLetter;
        strncat(key, &randomLetter, 1);
        if (charSum == 916) {
            printf("Key: %s", key);
            break;
        } else if (charSum > 916) {
            charSum = 0;
            memset(key, 0, sizeof(key));
        }
    }

    return 0;
}

```

まず、whileループの中で、randomLetterにランダムな文字列を代入します。
(ここでは `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_[arc4random() % 文字数+ヌル終端文字列]`で生成しています)

次に、randomLetterをUnicode値に変換し、charSumに加算します。
また、`strncat(key, &randomLetter, 1)`で、keyにrandomLetterをappendします。

もしcharSumが916なら、そのkeyを表示、それより小さければループを継続、それより大きければcharSumとkeyを初期化しループを継続します。

今回私はC言語でKeygenを書きましたが、フォルダ内にはpythonで書かれたものもあります。ぜひ参考にしていただければと思います。

![Screenshot 2021-09-16 at 20.59.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185096/26ecd3ff-4ac5-cdf9-a4c9-f3dbcaeeb9d2.png)


## おわりに
今回は単純なcrackmeの解説とkeygenの作成方法について説明しました。
このcrackmeはかなり簡単なものですが、crackmeへのイントロダクションになれば幸いです。

また、今回のcrackmeはLiveOverflowが作成したものです。
[Youtubeチャンネル](https://www.youtube.com/c/LiveOverflow/)ではかなり有益なセキュリティ関連の知識(CTFの解き方など)の動画をアップロードされているので、ぜひ御覧ください。
VM テンプレートの作成

## 1. はじめに
VMware vSphere 6.5でWindows Server 2019のVM テンプレートがなかったので作成したので、
その際の手順をまとめました。


## 2. 環境
Hyptervisor:VMware ESXi 6.5 / vCenter Server 6.5
OS:Windows Server 2019

## 3. テンプレート用のWindows Server 2019の構築
### 3.1 OSのインストール
まずはWindows Server 2019のインストールから。これはダイアログに従って次へ次へで。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/26efb281-8d29-f8b8-9cbe-dd76947821b7.png)

基本はデスクトップ エクスペリエンス（通常のWindows Server 2019）で。というかServer Coreの環境って作ったことない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/6f1f2e2c-878a-bd86-c4c7-6c13a62b97a1.png)

再起動！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/91260989-9cae-61d8-63b6-8032472180aa.png)

再起動後はAdministratorのパスワードを入力して
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/6a8171df-35ac-291d-d5c2-09361e22c5b2.png)

完了！インストールしたのは5月、記事を書いているのは9月…。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/782bb6bc-e26c-7715-ae76-82ff4fa55423.png)

### 3.2 テンプレートの前の作業
VMtoolsのインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/bfe71926-5f95-fd50-ef0c-ac1b5d822af4.png)

WindowsUpdate適用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/aef41246-9bb7-555c-66e8-55c2d1ff7174.png)

リモートデスクトップの許可
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/ec43911a-8dae-d49e-6b44-0b7c0c21082f.png)

Windows Defender ファイアウォールの無効化
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/e0f9d632-b60e-fe20-98b8-097b5af21201.png)

あと、VMのDVDドライブの設定でCD/DVD ドライブをクライアント デバイスに切り替えて、さらに切断しておくこと！localdatastoreにマウントした状態だとテンプレートからVMを作成するときに、接続しているdatastoreにしかVMがデプロイできないので注意！

### 3.3 テンプレートへ変換
手順は簡単、変換したいVMを選択して右クリック→テンプレート→テンプレートに変換
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693852/c52727c3-420e-bf00-aa48-d9175280f6f4.png)

以上、次回はテンプレートからVMを作ります。
オブジェクトに乗っても落下しないようにする

###１．はじめに

スーパーマリオ・ロックマン等のアクションゲームにありがちな動く床。はじめての制作だとプレイヤー物に乗っても動く床に沿って動かなかったり、下のGif動画のように落ちることがある。
解決策はとても簡単なものだが、ネタとしてはアリだと思いアウトプットします。
ここだと床の上昇のみでの紹介になるが、この方法はそれ以外の動きにも対応が可能です。

なおここでは接触判定等の紹介はしません。

![qiitaparents1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/4bcaab8a-f6ac-7e55-175c-dd4be65a7263.gif)


###２．スクリプト

スクリプトは単純明快で動く床の子オブジェクトになればいいです。注意点は動く床から離れた場合、親子関係を解除しないと動く床の挙動に影響され続けてしまいます。

 ~~~C#
using UnityEngine;

public class QiitaObject : MonoBehaviour
{

    //物体と接触したら物体の子オブジェクトとなる
    private void OnCollisionEnter(Collision collision)
    {
        transform.parent = collision.gameObject.transform;
    }

    //物体から離れたら物体の子オブジェクトを解除する。
    private void OnCollisionExit(Collision collision)
    {
        transform.parent = null;
    }
}
~~~


![qiitaparents2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307841/e578c3cf-93b1-341b-25c9-39ee234074a1.gif)

###３．最後に

親子関係の扱いはＶＲ制作だと物を掴む等で使っていましたが、それ以外の用途では使うことがなかったので、とても勉強になった。
これ以外にももっと良い方法があるかもしれませんが、その時は加筆をしたいと思います。
[初学者]スコープとvarの巻き上げ

#（初学者）javascriptの理解してなかった所
#はじめに
本記事はネット教材でサラッと勉強したけど後に深く理解してなくて、なんだこれ？となって調べたものです。
[参考文献](https://jsprimer.net/intro/) javascript primer

#目次
- javascriptのスコープの性質とはなんですか？
- varの巻き上げってなんですか？
- おわり

#javascriptのスコープの性質とはなんですか？
スコープとは変数や関数の引数をとって来れる範囲です。
そしてもう１つ重要なのが｛　｝で囲っている所をブロックと呼びます。ブロックの外から中は参照出来ません。
＊逆にブロック内のものはブロック外を参照出来ます。
例を下に書くと

```
const A = 10;　←グローバルスコープで定義されたグローバル変数
{
  const B = 20;
  {
    console.log(A);　 //10　←内から外へ参照を探しに行くスコープチェーン
  }
}
console.log(B);　←ブロック内の変数Bを参照出来なかった
//Uncaught ReferenceError: B is not defined
```

#varの巻き上げってなんですか？
＊そもそもなんでconstとletが追加されたのか
先ず基礎知識でconstとlet、varの違いについて
const　再宣言、再代入できない。
let　　  再宣言は出来ないが、再代入は出来る。
var 　　どっちも出来る。

再宣言が出来るとはなんなのか、コードで書いてみるとvarは初期20から再宣言により200になってます。
短いコードならまだ良いですが、長いと意図せずに同じ変数を宣言する可能性があるので危険です。varは非推奨

```
let A = 10;
var B = 20;

let A = 100;
var B = 200;

console.log(A);　//エラー　Uncaught SyntaxError: Identifier 'A' has already been declared
console.log(B);　//200

```

次にletとvarの他の違いについて、

```
let C = 30;
console.log(C); //30
console.log(D); //Uncaught ReferenceError: Cannot access 'D' before initialization
let D = 40;

var E = 50;
console.log(E); //50
console.log(F); //undefined
var F = 60;

```
コードは基本的に上から下へ行くものですが何故がconsole.log(F);にはundefinedが表示されます。
undefinedは未定義の意味なので、何かわからないものがある判定になってます。怖いですね。
引用すると
>
変数宣言が宣言と代入の2つの部分から構成されていると考えてみましょう。 
varによる変数宣言は、
宣言部分が暗黙的にもっとも近い関数またはグローバルスコープの先頭に巻き上げられ、
代入部分はそのままの位置に残るという特殊な動作をします
>

さっきのコードで改めて書くと

```
人から見たコード
console.log(F);
var F = 60;

実際の動き
var F
console.log(F);
var F = 60;
```

この人に見えない特殊な動きを巻き上げと言うそうです。

#おわり
初めて書いて見ましたが3時間ぐらいかかりました。
参照を元にアウトプットしたつもりがだたの自己流の書き直し感があり、すみません。
GTMを用いてURLパラメータを抽出してHTML要素に付け加える

URLにGETで渡されているパラメータ値をHTMLの要素で出力する際に、どうしてもGTMを利用したい場合の方法解説です。
通常はファイルに直接記述するパターンが多いかと思いますが、マーケティング担当でファイルを直接編集する権限がないが、広告計測等のためにパラメータをピクセルタグ等につける場合に活用できるかと思います。

データレイヤ変数としてGTMにパラメータ値を渡す方法もありますが、今回はHTMLファイルを一切編集せずGTMのみで実装する方法となります。

# 実装したいことの確認
(例)

```http
https://www.exapmle.com?param1=aaa&param2=bbb
```

このURLの状態でparam2(bbb)をimgタグのsrc属性の一部として以下のようなタグを発火させたい時

```html
<img src="https://conversion.com?p=bbb" width=1 height=1>
```

param2についているパラメータを、imgタグ(ピクセルタグ)のsrc属性のパラメータとして付与したい、
このような需要がHTMLファイルの編集権限は無いが、GTMは操作できる広告担当の方からあるかと思います。
この場合、GTMで以下のような設定をします。

# GTMの設定
タグ>カスタムHTMLを選択します。
![スクリーンショット 2021-09-16 19.53.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459539/e7104281-5fef-e1c3-10d4-4994e8bd86b2.png)
HTML入力ボックスに以下コードを追加します。

```html:HTML
<script type="text/javascript">
      var url = new URL(window.location.href);
      var params = url.searchParams;
      var value = params.get('param2');
      var cvTag = document.createElement('img');
      cvTag.src = "https://conversion.com?p="+value;
      cvTag.width = 1;
      cvTag.height = 1;
      document.body.appendChild(cvTag);
 </script>
```

実際に使用する際は以下の要素を適宜変更してご利用ください。

| 例での記載 | 設定項目 |
|:-:|:-:|
| param2  | URLに付与されているパラメータ名  |
|  cvTag.src | imgタグに設定したいアドレス  |
|  document.body.appendChild | 出力したい箇所をDOMで指定  |
※広告タグの場合は多くの場合はbody直下で問題ないため、document.body.appendChildはそのままで大丈夫なことが多いです。

## 注意
GTMは基本的にはES6に対応していないため、constやletおよびテンプレートリテラルは利用できません。
また、id等を指定して、document.getElementById等をする方法も考えられますが、広告事業者側がタグの改変を認めていない場合があるため、id等の属性を付加しない方が無難です。（idくらいなら付けても問題はないのですが、たまに厳しいところがあります。）

## 参考
https://gray-code.com/javascript/get-parameter-of-url/
DynamoDBに保存されているデータを一括でCSVに出力したい

## はじめに
DynamoDBでテーブルをまるっと取得するってなかなか難しくないですか？
作成された順にitem(RDBでいうレコードみたいなもの)を全件並べて解析したい！みたいなことも難しい🤔

一回のScanで取得できるデータが最大1MBという制約がなかなか厄介で、
DynamoDB コンソール上からは簡単に全件データのエクスポートができないようになっています。
ページごとの表示数をMAXの300件にしても、全件を取得するために「次のページを取得」を連打して、CSVをエクスポートする必要がありました。

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

どうにか簡単にできる方法はないか色々調べてたところ、
以下の方法でコマンド一発でテーブルの内容をまるっとにCSVにエクスポートできるようになりました。

## 一括でCSVへ出力する方法

### 前提条件
* AWS Config にAccessKeyやらのprofileを設定しておく
* AWS CLI を使用できるようにしておく

### 手順
1. jqコマンドを使用するのでインストールする
(Mac環境なのでbrewでインストールしてます🍎 )
1. AWS CLI と jqコマンドを組み合わせたコマンドを発行する

```bash:terminal
# jqコマンドをインストール
$ brew install jq

# インストールされたことを確認
$ jq --version
jq-1.6

# 全件取得とCSV出力のコマンド発行
$ aws dynamodb scan --table-name HogeTable --profile hoge-profile | jq -r '["ID","ユーザー名","作成日"], (.Items[] | [.ID.N, .Name.S, .createdAt.S]) | @csv' > sample.csv
```

### コマンド詳細

#### AWS CLIでDynamoDBから値をJSONで取得
```bash:terminal
aws dynamodb scan --table-name HogeTable --profile hoge-profile
```

このコマンドで、DynamoDBの指定したテーブル内容をJSON形式で取得しています。
```HogeTable```を対象のテーブル名、```hoge-profile```を```~/.aws/config```に設定しているprofileにしてください。
**なんか上手くいかない！**という場合はまずこのコマンドでJSONを取得できているか確認してみてください。

#### jqコマンドでJSONをCSVに整形
```bash:terminal
jq -r '["ID","ユーザー名","作成日"], (.Items[] | [.ID.N, .Name.S, .createdAt.S]) | @csv'
```

ここの箇所でJSONをCSV形式に整形しています。
```jq -r 'ヘッダー, (項目) | @csv'```というイメージです。
```aws dynamodb scan ~ ```コマンドで取得されるJSONは以下のような形式かと思うので、それにあわせて項目の部分を設定します。

```bash:terminal
# aws dynamodb scan ~で取得できるJSON例
{
    "Items": [
        {
            "ID": {
                "N": "1"
            },
            "Name": {
                "S": "清水真太郎"
            },
            "createdAt": {
                "S": "2021-09-13T02:33:07.443Z"
            }
        },
        {
            "ID": {
                "N": "2"
            },
            "Name": {
                "S": "柴田大知"
            },
            "createdAt": {
                "S": "2021-09-13T02:3７:２7.542Z"
            }
        }
    ]
}
```

#### 整形されたCSVデータをCSVファイルに出力
```bash:terminal
> sample.csv
```
最後にCSVデータをCSVファイルとして出力させています。

## まとめ
DynamoDBのテーブルに合わせてコマンドを作成するのに最初やや時間がかかりますが、
一度作成してしまえばいつでもすぐ全件をCSVにエクスポートできるようになりました！😇

DynamoDBのTipsあればぜひ教えてください！
SentryでのReleaseの導入方法


エラーのトラッキングソフトウェアにSentryを利用する機会があり、運用する中でSentryのRelease機能が便利だなと思ったので導入方法を執筆していきます。
この記事では`sentry-cli`を利用した設定方法を記述していきます。`sentry-webpack-plugin`を使用した設定方法は扱いません。
SentryはAngularアプリケーションで利用していました。
#### Sentry Releasesとは
Sentry Releasesを利用するとどのバージョンでエラーが発生したのか特定できるようになり、バージョン別のエラーモニタリングが可能になります。
Sentryにリリースのバージョンとリリースのアーティファクトをアップロードすることで、利用できるようになります。
また、ソースマップをアップロードすることでstack traceがソースマップと関連付けられ、Minifyされてないコードで表示されるようになります。そうすることで、エラー原因となるコードが特定しやすくなります。
※ソースマップとは、デプロイされたコードを元のソースコードにマッピングする方法を含むファイルです。ソースマップによりstack traceから得たコードを、変換されてない元の形で見ることが可能になります。
#### SentryへのReleasesの登録方法
Sentryにリリースを登録したい場合、sentry-cliコマンドを利用し、下記を実行するだけです。

```
sentry-cli releases -o "$ORGANIZATION" -p "$PROJECT" new "$VERSION"
```
上記コマンドを実行してSentryにリリースを作成した後、アプリケーションで`Sentry.init()`を呼び出している箇所に`release`を追加し、作成したリリースと同じバージョンを記述します。
一貫性を保証するために、リリースバージョンをプロジェクト内で環境変数として設定するのが良いかと思います。

```typescript:app.module.ts
import * as Sentry from "@sentry/angular";
...
Sentry.init({
    dsn: ${YOUR_SENTRY_DSN},
    release: ${VERSION}
});

```
#### ソースマップのアップロード方法
Sentryへのソースマップのアップロードには`sentry-cli releases files VERSION upload-sourcemaps`を使用します。
上記のアップロードコマンドをリリースフローに組み込むのが運用的にラクかと思います。
ソースマップのアップロードコマンドを実行した時点で、Sentryにリリースが作成されます。（`sentry-cli releases new`と同等の働き）
下記コマンド例になります。

```
sentry-cli releases -o "$ORGANIZATION" -p "$PROJECT" files "$VERSION" upload-sourcemaps ${path_to_sourcemaps} --rewrite --url-prefix ~/static/js
```
`--rewrite`オプションを付与すると対象のリリースに既にアーティファクトがアップロードされていても、上書きされます。
`--url-prefix`オプションを付与するとアップロードする全てのファイルの前にプレフィックスが設定されます。デフォルトでは`~/`が設定されているので、変更したい場合に使用します。stack trace上でのパスとアップロードするファイルのパスは一致している必要があるので、プレフィックスが異なる場合は`--url-prefix`の設定が必須になります。


以上、SentryのReleaseの導入方法に関して簡単に紹介しました。
他にもリリースにデプロイやコミットを関連づけることも可能なので、エラーモニタリングをより効果的に運用できそうです。

参考
https://docs.sentry.io/product/cli/releases/
https://docs.sentry.io/platforms/javascript/sourcemaps/
JUnit5の@ParameterizedTestがAndroidStudioで認識されない

複数の引数を入れて期待値が得られるかをテストするには、
JUnit5のParameterizedTestが便利らしいので導入しようとしました。

## gradleに導入

```gradle
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
```

## Testは認識されるがParameterizedTestが認識されない

unresolvedになってしまう
![Screenshot 2021-09-16 194455.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/295a8c8a-4adc-dbfb-3162-81c991342ca3.jpeg)

option + Enterも試しましたが候補が出ず、導入に問題があるのかと思い悩んでました。。

## 結論、importに手動で書く

自動補完になれすぎた弊害ですね。。これで動きました。

```kotlin
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
```
Powershellでcsvの特定の月を出力する

# csvで特定の月だけ抜き出すPowershellの作成
Powershellを使ってみたい ＆ 仕事につかえそうだと思ったので作成にチャレンジしてみました。

## 設計
初期イメージ
1. csvファイルの読み込み
2. 特定の行、カラムの判定(今回は日付)
3. 行の削除 or ファイルへの出力

## 実際のソース

```
# 処理対象のフォルダは同一の場所にあるものとする
​
# $csvファイルのリストを取得する。
$csvFileList = Get-ChildItem "*.csv";
​
# 先月以外の行を削除するため先月の文字列を取得
$now = Get-Date;
$prevMonth = $now.AddMonths(-1);
$formatDate = $prevMonth.ToString("yyyy/MM/");
$formatDate = '*' + $formatDate + '*';
​
# ファイル名用の先月の文字列を取得
$fileNameDate = $prevMonth.ToString("yyyyMM");
​
foreach($csvFile in $csvFileList)
{
​
    # csvを読み込む カラム名が無い or 特殊な場合は指定が必要
　  # このコードでは3カラムのみ指定しているため3カラムしか出力されない
    $item = Import-Csv $csvFile -Encoding default -Header "Id","Number","Date";
​
    #3行目から開始する特殊なcsvなのでfor
    # lt は < の意味を持つ
    for($i=3; $i -lt $item.Count;$i++)
    {
        $columnValue = $item[$i].Date;
        
        # 先月の内容でない場合は削除する
        if ( $columnValue -notlike $formatDate)
        {
            $item[$i] = $null;
        }
    }
​
    # 先月 + ファイル名で新しいファイル名の作成をする
    $fileName = $fileNameDate + $csvFile.Name;
    # 先月の内容を新しいファイルに出力する
    $item | Out-File "$fileName" -Encoding default;
}
```

## 作成時に躓いた箇所
1. 文字コード
ファイル出力時に文字化けしていた。  
→ Encodeを入れることで解決 ShiftJisの時はdefault,UTF8の時はUTF-8を入れる
2. 含むの判定  
今回は特定の月の文字列が含まれていたら～という処理にしようと考えていた。  
最初に調べたときにcontainsが出てきたのでcontainsを使用したが上手くいかなかった。  
調査の結果object、配列等の時にcontainsを使用することが分かり、likeに変更することで想定通りの処理ができた。
3. ループ処理  
読み込むcsvの仕様上特定の行から始めるループになっているが、
本来であれば全ての行を読み込むまで処理を行うforeachが使用できるので、そちらを使用したほうが分かりやすい。

## まとめ
始めてPowershellを使用してみたが、非常に色々なことができそうに感じた。
繰り返しになる作業を楽にできる様にVBAも触ってみたい。
簡単な Dockerfile を作成してコンテナを立ち上げる

## はじめに
簡単な Dockerfile を作ってコンテナを立ち上げる をします。
## 準備

```shell:shell
# Dockerfile 作成
$ touch Dockerfile

# Dockerfile 編集
$ vi Dockerfile

# vi 操作
# i を押して インサートモード
# 下記 Dockerfile の内容をコピーペースト
# ペーストしたら esc でコマンドモード
# :wq と入力しエンター　で保存して終了

$ touch index.php
$ vi index.php
# Dockerfileの時と同様に、 vi 操作でコピーペースト
```

下記2つのファイルが作成できればOK


```Dockerfile:Dockerfile
# FROM で元となるイメージの指定
FROM php:apache
# Dockerfileが存在するディレクトリの内容を、 イメージのファイルシステム上のパス /var/www/html/ にコピー
COPY . /var/www/html/
```

```php:index.php
<?php
// phpinfo — PHP の設定情報を出力する
phpinfo();
?>
```

## イメージのビルド
先程作成した Dockerfile が存在するディレクトリで実行

```shell:shell
# -t で ビルドするイメージに php-test という名前をつける
# . は 元になるDockerfileの位置を指定
$ docker build -t php-test .

# イメージ一覧表示
$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED              SIZE
php-test     latest    9261467f1c80   About a minute ago   473MB

# ↑php-test という名前のイメージがあればOK！

```

## コンテナ作成

```shell:shell
# -d (デタッチド) コンテナをバックグラウンドで起動
# -p 80:80 (ポートフォワード) localhost の80番ポート にアクセスが来たら コンテナ の80番ポートに繋ぐ
# --name (コンテナ名) コンテナ名を php-test-container にする
# php-test イメージを元にコンテナを作成する
$ docker run -d -p 80:80 --name php-test-container php-test

# 起動しているコンテナを一覧表示
$ docker container ls
# 一部項目省略 
CONTAINER ID   IMAGE      CREATED         STATUS         PORTS                NAMES
08020b5a1b50   php-test   6 seconds ago   Up 5 seconds   0.0.0.0:80->80/tcp   php-test-container

# コンテナが立ち上がったので localhost を開く
$ open http://localhost 
```
下記画像のような画面が表示されれば成功！

![スクリーンショット 2021-09-16 19.30.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/470307/e2b6a800-bfd5-61aa-e40d-20a1a7c20591.png)

### Dockerfile内の COPY について補足
```shell:shell
# コンテナ内のshell(bash)を起動し操作できるようにする
$ docker exec -it php-test-container bash

# root@[コンテナIDの一部]:[イメージのファイルシステム上のパス] 
root@b41c68711750:/var/www/html# ls
# Dockerfileに記載した COPY . /var/www/html/ でcopyされた Dokerfile と index.php が表示される 
Dockerfile  index.php 

# コンテナ内のshellから抜ける
root@b41c68711750:/var/www/html# exit

```


## 片付け
```shell:shell
# コンテナ停止
$ docker stop php-test-container
php-test-container # コンテナ名表示されれば成功

# コンテナ削除
$ docker rm php-test-container                             
php-test-container # コンテナ名表示されれば成功

# イメージ削除
$ docker rmi php-test       
Untagged: php-test:latest
Deleted: sha256:7103bbe5f77b40234a9b8791b1ab3383c8e0fc1a9fc8489bb40922177753c4a6 # deleted 表示で成功
```

## まとめ
簡単なDockerfileを作成してコンテナを立ち上げる をしました！
Windows10でtensorflow-gpuを動作させよう

# 読者のターゲット
Win10でdeeplearningしたい
Anacondaの仮想環境内で深層学習の環境を作りたい
GPU持ってる(NVIDIA製)
Python3.7, Tensorflow 2.6.0

# 環境構築の前に
Anacondaは入っている -> まだの場合は[こちら](https://www.anaconda.com/products/individual-d#Downloads)
NVIDIA デベロッパーアカウントを作っている -> まだの場合は[こちら](https://developer.nvidia.com/login)
GeForce のドライバをダウンロードしている -> まだの場合は[こちら](https://www.nvidia.co.jp/Download/index.aspx?lang=jp)
cuda ツールキットもダウンロードしている -> まだの場合は[こちら](https://developer.nvidia.com/cuda-toolkit)

## PythonでGPUを使うために
### cuDNNのダウンロード
cuDNNダウンロードの前にcudaのバージョンを確認する

```bash
nvcc -V
```
バージョンを確認したら[cuDNNサイト](https://developer.nvidia.com/cudnn-download-survey)で対応するバージョンの```cuDNN Library for Windows (x64)```をダウンロード![cuDNN Download _ NVIDIA Developer - Google Chrome 2021_09_16 19_21_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465042/ec3de355-95d1-a2cf-b7c2-49692f997505.png)

zipファイルがダウンロードされるので解凍すると以下のようなディレクトリの構成になっている。(今回のcudaのバージョンは11.4)

```bash
cuda
├── NVIDIA_SLA_cuDNN_Support.txt
├── bin
├── include
└── lib
    └── x64
```
このうち、```bin```, ```include```, ```lib``` を使用する。
cudaのインストール先はデフォルトだと```C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.4```なので、ここに解凍後の```bin```, ```include```, ```lib```をコピー&ペースト(すべて上書き)しましょう。

### PATHの追加
```Windowsコントロールパネル``` -> ```システムとセキュリティ``` -> ```システム``` -> 右側の関連設定 から```システムの詳細設定``` -> ```環境変数(N)```を選択
ユーザー環境変数, システム環境変数どちらでもいいので、```Path```の項目を探し、```編集(I)``` -> ```新規(N)```を選択。
```C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.4\bin```を貼り付け(cudaのインストールでフォルダを指定している場合は環境に合わせて設定してください。)

### 再起動
環境変数を変更したので再起動します。

## やっとPython
### conda仮想環境の作成と有効化
Anaconda Promptを開いて以下を実行

```bash
conda env create --name tf36 python=3.6
conda activate tf36
pip install --upgrade pip
```

プロンプトの先頭に```(tf36)```と表示が出ていることを確認。(出ていない場合はAnaconda Promptではないので環境内に入れていないです)

### tensorflowのインストール

```bash
conda install tensorflow-gpu
pip install tensorflow-gpu
```

#### condaとpip....??
condaだけだと、GPUを認識してくれないのでpipにも入れます。

## GPUが動作しているか確認をしましょう。
```bash
python -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())"
```

GPUの項目が表示されていたら成功です。
お疲れ様でした。
Bitnami とは

## 勉強前イメージ

見たことあるけど、わからん・・・・
tomcat的なやつ？

## 調査

### Bitnami とは

WordPressやRedmineなど
webアプリケーションに必要なphpやmysqlなどのミドルウェアと一緒にパッケージ化してくれるもので、
一式インストールするとすぐにwebアプリケーションが使えるようになるものである。
よく使われるのは、wordpressやredmineで250種類以上あります。

以下が [bitnamiのwebサイト](https://bitnami.com/) で
こちらから気に入ったものをインストールします。

![1Bitnami Application Catalog - Google Chrome 2021-0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/3c6f9ce4-24da-ec60-0a04-faa025630df7.png)

例としてwordpressを見ると
クラウド用だったり、コンテナ用、PCにインストールする用など様々あります。

![2WordPress Cloud Hosting, WordPress Installer, Dock.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/e20436fa-c527-2742-09c5-ab96be8eb8ac.png)


## 勉強後イメージ

httpとかアプリケーション、DBとかがパッケージされてすぐ使えるようになってるやつ
AWSのLightsailも似たような感じ。
そんなに種類ないけど、wordpressとredmineはある。

## 参考

- [BitnamiでローカルにWordPressのテスト環境を作る](https://keisukekuribara.com/bitnami-wordpress-local-test-dev/)
- [WordPress初心者でも簡単にローカル環境を構築できるbitnamiの使い方](https://naifix.com/bitnami/)
[備忘録]@nestjs/serve-staticで静的ファイルへのルーティングを行う際にトレイリングスラッシュをつけないようにする

公式のリファレンス通りに@nestjs/serve-staticを使ってフロント側のファイルへのルーティングが簡単に設定できます。
https://docs.nestjs.com/recipes/serve-static

```app.module.ts
@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),
    }),
  ],
})
export class AppModule implements NestModule {
```

ただ、この通りに実装すると必ずトレイリングスラッシュ付きのURLにリダイレクトされます。


# 解決

GitHub見てようやく分かりました。。。
https://github.com/nestjs/serve-static/blob/master/lib/interfaces/serve-static-options.interface.ts#L87-L90

`serveStaticOptions.redirect`に`false`を指定します。

```app.module.ts
@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),
      serveStaticOptions: {
        redirect: false,
      },
    }),
  ],
})
export class AppModule implements NestModule {
```
【個人開発】Chrome拡張機能「大谷メーター」を作りました

# はじめに
こんにちは、[席替えメーカー](https://qiita.com/krpk1900/items/22963432b62a9004717c)開発者の[@krpk1900_dev](https://twitter.com/krpk1900_dev)です。

2021年のメジャーリーグもシーズン終盤に差しかかり、HR王争いの激戦が繰り広げられていますね。「今日はHRを打ったのかな」と毎日ワクワクしながら大谷選手の活躍を確認しています。

今回は、今年の夏にリリースして現在も運用を続けているサービス**「大谷メーター」**について書いていこうと思います。大谷メーターは、大谷選手の**最新HR数と最新HRの動画リンクを表示してくれるChrome拡張機能**です。

[インストールはこちらから](https://chrome.google.com/webstore/detail/ohtanimeter/pbipgkaadgdbakhonngkopmnofndhdem)
![promotion(1400*560).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693123/63deafe9-8648-154f-331a-548716395cd6.png)

# Chrome拡張機能にした理由

大谷選手がHRを量産している今シーズン、「今日はHR打ったのかな？」と気になって私は一日に何回かTwitter検索やメジャーリーグ速報を見に行っていました。
しかし、そもそもその日に試合が行われていなかったり出場機会がなかったりして、**「大谷選手がHRを打った瞬間分かればな〜」**と感じていました。

**今回解決したかった課題はまさにこの課題です。**

メール通知や音声通知だと逆に鬱陶しく、専用のWebアプリケーションだとわざわざアクセスしに行かないといけません。

- 最小限の手間で確認できる
- 常駐していても邪魔にならない

この二点を同時に満たした上で今回の課題を解決できる方法を探し、**Chrome拡張機能**として開発することを決めました。

# 機能紹介
### 1. 最新HR数を表示するバッジを自動で更新
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693123/332f9d40-1fd7-1eb7-54a4-2775c396c612.gif" width=20%>
大谷選手がHRを打つと**青色**である既読状態の通知バッジが未読状態の**赤色**になり、同時に**最新HR数**が自動で更新されます。
バッジをクリックすることでポップアップが開かれると既読状態になり、通知バッジが**青色**に戻ります。

### 2.ポップアップに最新HRの動画リンクを表示
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693123/97cf2a47-ec64-43dc-a12d-d443c0e6619d.gif" width=50%>
クリックすることで開くことができるポップアップには**最新HRを閲覧することができるYouTubeやTwitterの動画へのリンク**が表示されます。このポップアップ内で動画の再生は行われません。

# インフラ構成
大谷選手のHR数は、MLB公式データを提供している[MLB Data API](https://api.rakuten.net/theapiguy/api/mlb-data/details)を定期的に叩くことによって取得しています。

ただし、各ユーザーがインストールしたChrome拡張機能からMLB Data APIを直接叩く構成にした場合、ユーザー数に比例してリクエスト数が増加し、それにともなってコストも跳ね上がることが想定されました。

![OhtaniMeter_infra.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693123/3749074f-3b1f-810c-283c-9b8397ad5317.png)

そこで、今回は上記のインフラ構成を採用することでコストを無料に抑えつつ機能を提供しています。

### ① Cloud FunctionsからMLB Data APIを叩く
MLB Data APIの無料枠である5,000リクエスト/月を超えないように注意して、Cloud Functionsによる定期実行で10分ごとにMLB Data APIを叩き、大谷選手のHR数を取得しています。

### ② Cloud FunctionsからCloud Firestoreの読み取り&書き込みを行う
大谷選手のHR数をMLB Data APIから取得した後は、Cloud Firestoreに保存されているHR数を読み取りに行きます。①でAPIから取得したHR数と②で読み取ったCloud Firestore内のHR数が異なっている場合（=大谷選手がHRを打った直後）Cloud FirestoreのHR数を更新します。

### ③ リアルタイムリスナーによってChrome拡張の表示を更新する
各ユーザーがインストールしたChrome拡張機能でCloud Firestoreのリアルタイムリスナーを起動しているため、Cloud Firestoreに保存されたHR数が更新された瞬間にブラウザ上の表示を更新することができます。今回はバッジの色やポップアップの動画リンクを更新しています。

大谷メーターでAWSのLambdaやDynamoDBではなく**FirebaseのCloud FunctionsとCloud Firestoreを採用した一番の理由**は、このリアルタイムリスナーを使用したかったためです。

# おわりに
Chrome拡張機能の開発方法については知識がなかったため開発前は難しいイメージを持っていました。しかし、サービスの形こそ異なりますが終わってみるとこれまで開発してきたWebサービスと根本的な部分はほとんど共通していました。開発期間は機能実装や素材作成を含めて約1週間です。

また、大谷メーターは日本の大谷ファンだけではなくアメリカのエンゼルスファンの方にも使ってもらいたいと考えていたので**i18nによって英語**にも対応させました。英語で海外の人にマーケティングまで行ったのはこのサービスが初めてですが、住んでいる国や使っている言語が異なっていたとしても喜んでくれる人が世界中にいることを体感できてとても感慨深い経験ができました。

シーズン終了まで残り一ヶ月を切っていますが、大谷選手のHR王争いを最後まで追い続けたい方はぜひ一度使ってみてください。

[大谷メーター](https://chrome.google.com/webstore/detail/ohtanimeter/pbipgkaadgdbakhonngkopmnofndhdem)
HTTPステータスコード422とは

# HTTPステータスコードとは
***HTTPリクエスト(GET POSTなど)***をした時HTTPレスポンスと一緒に返ってきて今の処理の結果を***人に３桁の番号で伝えてくれる***ものです。

ex)
```
200 正常
400　リクエストのミス
404 そういうものはない
```
などが代表的です。

# 422とは
```422 Unprocessable Entity(処理ができないもの)```のことで、***コードや文法、リクエストは間違っていないが、意味が間違っているため、うまく処理ができないもの***のことです。

自分の場合はバリデーションは関係ありませんでしたが、バリデーションが原因の場合がよくあるみたいです。

# 参考資料

https://www.itmanage.co.jp/column/http-www-request-response-statuscode/