OmniAICreator/Qiita-1.07M huggingface dataset:
rakeタスクのrspec記述方

## ``rake_helper.rb``の設定
``rake_helper.rb``にテスト全体に及ぼす設定を記入する。

```spec/rake_helper.rb
require 'rails_helper'
require 'rake'
RSpec.configure do |config|
  config.before(:suite) do
    Rails.application.load_tasks #全てのrakeタスクを読み込む
  end

  config.before(:each) do
    Rake.application.tasks.each(&:reenable) # Remove persistency between examples
  end
end
```
#### ``before(:suite)``
実行のタイミングを表している。英語の意味通り、``before``なら前、``after``なら後にタイミングを指定できる。

```
before :suite
after  :suite
before :context
after  :context
before :example(it)
after  :example(it)

suite : テスト(RSpecコマンド)の実行前(後)に呼ばれる。1回のみだけ呼ばれる。
all : context単位で実行される。
each : example(it)単位で実行される。
```
#### ``Rails.application.load_tasks``
これを記入することで以下のメリットがある。

・正規の手順で、``Rakeタスク``を読み込み出来る

・``Rails``が内部的に定義しているようなタスクも全て読み込み出来る

・``before(:suite)``時のオーバーヘッドはあるが、一度読み込んだものを再度読み込む必要が無いので全般的なパフォーマンスが向上する

・``Rake.application.rake_require``を手動で実行する場合には、テスト間で読込済判定を避ける為の技を駆使しないといけないが、そのような心配事から解放される

・``Rake::Task.define_task(:environment)``のようなモックを作る作業から解放される

#### ``Rake.application.tasks.each(&:reenable)``
``Rakeタスク``には複数の実行手法があり、``invoke``と``execute``である。

実行順序や依存関係を扱いやすくするために、``invoke``で呼ばれたタスクは、同一プロセス内においては、2回目実行されることはない。

テストにおいては、テスト間の結合性をなくすということは非常に重要なので、全てのタスクが呼ばれた履歴を抹消する。

その後、``reenableメソッド``で自身をもう一度実行できるようにする。

## specファイルに記述
ある記事が、公開日時になっている公開待ちの記事を、公開に変更するタスクのテスト。
(0 :draft[下書き], 1 :published[公開])

```spec/lib/tasks/change_to_published_spec.rb
require 'rake_helper'

describe 'article_state:change_to_published' do
  subject(:task) { Rake.application['article_state:change_to_published'] }

  before do
    create(:article, state: :publish_wait, published_at: Time.current - 1.day)
    create(:article, state: :draft)
  end

  it 'change_to_published' do
    expect { task.invoke }.to change { Article.published.size }.from(0).to(1)
  end
end
```

#### ``subject``
``let``と比較するとわかりやすい。

| 区分名 | 使い方 |
|:-:|:-:|
| let  | expect（it）の引数になり得ないもの  |
| subject  |  テスト対象 = expect(it)の引数になるもの |

#### ``{ Rake.application['article_state:change_to_published'] }``
``article_state:change_to_published``はタスク名なので、ここで``rakeタスク　``を呼び出している。

#### ``{ Article.published.size }.from(0).to(1)``
``published.size``はレコードの数を指している。（``0 :draft[下書き]``の``0``の部分。）

``from(0).to(1)``これは、レコードを``0``から``1``にすることで下書きから公開にステータスを変更している。

## 参考記事
[RailsでRakeタスクをシンプルかつ効果的にテストする手法](https://qiita.com/aeroastro/items/c97bd26ce8b8818b6bed)
[rspecのdescribe, context, subject, letなどの書き分け](https://kei-p3.hatenablog.com/entry/2016/05/25/235124)
pycharmでdjangoのアプリを新規作成時に起こるエラー対処

#pycharmでdjangが作成できなくなった
pycharmでnew projectからDjangoを選択し、app nameに記入してcreatすると
```
Error creating Django application: Error on Python side. Exit code: 1, err: Traceback (most recent call last):
  File "manage.py", line 21, in <module>
    main()
  File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
  File "C:\Users\user\anaconda3\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "C:\Users\user\anaconda3\lib\site-packages\django\core\management\__init__.py", line 357, in execute
    django.setup()
  File "C:\Users\user\anaconda3\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "C:\Users\user\anaconda3\lib\site-packages\django\apps\registry.py", line 114, in populate
```
のようにエラーが出てしまい、terminalでrunserverを実行してもうまくいかない。
正直上記のエラーが何によるものかが未だよくわかっていないが、大変参考になるサイトから引用させてもらうと、
>settings.pyのSTATIC_URLの下に以下の設定を追加。
>
```
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
```

この一文を加えることで、runserverコマンドも通るようになった
#参考サイト
https://www.simpletraveler.jp/2021/06/20/python-django-troubleshooting-collectstaatic-exited-with-exitcode-1/
RubyのGETリクエストを送る際にヘッダを指定する方法

## 背景
WEBサービスにLINE Loginを実装する際に、ヘッダを指定してGETリクエストを送るのにハマってしまったので、記載。httpartyを使えば、あっさりと書くことができた。

## 環境
- Ruby 2.6.2

## 実装
```
# httparty.rb
require 'httparty'


@res = HTTParty.get("https://api.line.me/v2/profile", :headers => {"Authorization" => "Bearer #{@response_body['access_token']}"})
```
## 参考
https://developers.line.biz/ja/docs/line-login/managing-users/#get-profile
https://gist.github.com/jeffdonthemic/99c4da9daba9a4ee36ba
多変量解析における変数選択とその数

# はじめに

多変量解析では、共変量と呼ばれる変数を決定する必要があります。
この変数の決定方法とその数についてまとめます。

# 変数選択について

結論から述べると、変数選択は「**データを見ずに、アウトカムが同じ先行文献や医療・福祉などデータに関する専門家の意見を元に決定する**」が最も良い方法となります。
これは、データの p 値などに引きずられずに、かつ必要な変数を除いてしまうことなく因果関係のあるデータを抽出できるためです。

一部の文献などで検定結果の比較を元に p 値の小さい変数を決定したり、ステップワイズ法で p 値の小さい順に決定するということを述べているものがありますが、この方法には問題があります。
p 値だけで決定してしまった場合、決定する変数と目的変数間の因果関係などは全く考慮されません。
そのため、特に医療・福祉統計において多くある、絶対に除いてはいけない変数を除いてしまうことがあります。
また、特定の変数の p 値を小さくなるように変数を選択することは、「恣意的な選択」になってしまいます。

# 変数の数について

多変量解析では多くの変数を扱うことができますが、極力少ない方が良いと言われています。
これは、変数が増えるほど解析の信頼性が下がること、また結果の解釈が難しくなるためです。
また、変数に対してデータが少なすぎると統計的に正しい結果を得るのが難しくなります。
ではいくつが良いのかというと、一般に解析方法ごと以下のように言われています。

* 重回帰分析：総データ数を15で割った数まで
* ロジスティック回帰分析：イベントありとなしのうち、少ない方を10で割った数まで
* Cox 比例ハザードモデル：イベントありの数を10で割った数まで

これより変数が増えてしまった場合、上記した方法で変数選択をして数を絞ったり、似た変数を合成したり、片方を削ったりする必要があります。
特に似た変数を合成・削除するのは、多重共線性を防ぐ観点からも有効です。

# まとめ

今回記載した方法で変数を選択した上で、数を絞ることで多変量解析に最適な変数を決定することが出来ると思います。
もし解釈に怪しい部分などありましたら、ご指摘いただけると幸いです。

# 参考文献

* [多変量解析―説明変数の選び方（新谷歩）](https://www.igaku-shoin.co.jp/paper/archive/y2011/PA02949_04)
* [多変量解析における独立（説明）変数の選び方](https://haru-reha.com/multivariate-analysis-independent-variable-choice/)
* [多変量解析の説明変数は何個が適切？変数の正しい選び方とは？](https://best-biostatistics.com/correlation_regression/multi-setumeihennsuu.html)
SNSのアカウント名を勝手に作ってくれるアプリを作ってみた

##作ってみた経緯
SNSなどでアカウント名をつけるのがめんどくさいなあと思う時があったので、Javaの復習とHerokuへのデプロイ練習をしたかったので、取り組んでみました。

##作ってみたものでできること
入力した内容から、ランダムでアカウント名を作成できます。
記入する内容は、姓、名、生年月日（姓名はローマ字であればなんでも、生年月日は未来日でなければどの日付を指定しても大丈夫です。）

####＜トップページ＞
[![Image from Gyazo](https://i.gyazo.com/161beabe779d024a59f47de911cb15ae.png)](https://gyazo.com/161beabe779d024a59f47de911cb15ae)

####＜実行結果＞
[![Image from Gyazo](https://i.gyazo.com/2cb82dabe28e2ebcef4d76f530a622f7.png)](https://gyazo.com/2cb82dabe28e2ebcef4d76f530a622f7)

##作ってみた感想
プログラム自体はシンプルなので苦戦はしなかったが、Mavenの使い方、Webアプリケーションのビルドの仕方、Herokuへのデプロイの仕方にかなり手こずりました。Qiitaで質問した際に回答していただいた方々、有り難うございました！
本当は凝ったwebデザインにしたいなと思っていたが、いいデザインが思いつかなかったのと、調整の沼にハマりかけたので諦めました...
下記にアプリとGithubのURLを記載するので、お試しいただけますと幸いです。
改善案や修正すべき点、バグ、悪口などなど、絶賛受け付けております！
宜しくお願いします。

##URLと使用した画像リファレンス
アカウント名つけるくん
https://accountnamerandomizer.herokuapp.com/
ソースコード
https://github.com/mrmapleleaf/ranc
使用した画像リファレンス
https://www.linustock.com/

##次にやってみたいこと
DBとSpring bootを使用したwebアプリケーション開発
Kintone JavaScriptカスタマイズVol.3

#jQueryを使用したカスタマイズ
3回目となる今回は、[jQuery](https://www.pasonatech.co.jp/workstyle/column/detail.html?p=2570)を用いた簡単なカスタマイズを行なっていきます。
レコード一覧画面にボタンを配置し、それを押すと任意の場所に動きをつけたメッセージを表示させます。
#jQueryを使用するメリット
jQueryというライブラリを用いる事で、通常のJavascriptのコードよりも簡単に記述する事ができます。
また、HTMLタグの各属性をまとめて指定できたり、CSSのプロパティ名もそのまま使用する事ができます。
#環境と準備
JavaScriptの記述にはプラグインの「JSEdit for kintone」を使用していきます。
また今回は、jQueryを利用していくので[Cybozu CDN](https://developer.cybozu.io/hc/ja/articles/202960194)のライブラリからjQueryのURLをコピーし使用します。
#jQueryの呼び出し方
通常の記述方法だと、以下のように記述しますが

```javascript:sample.js
(function() {
    "use strict";
    //処理内容
    });;
})();
```

jQueryを用いる場合は、以下のように記述します。

```javascript:sample.js
(function($) {
    "use strict";
    //処理内容
 });
})(jQuery);
```

先頭行と最終行の違いだけですね。
#カスタマイズ
準備ができて、jQueryの呼び出し方と記述方法が分かったところでカスタマイズを行なっていきます。
今回のカスタマイズ手順を確認します。
1. レコード一覧画面にボタンを配置
2. ボタンを押した際の内容を記述
3. 出現アニメーション（動き）を記述
 
コードの記述内容は以下です。

```javascript:sample.js
(function($) {
    "use strict";
    kintone.events.on('app.record.index.show', function(event) {
        // 1.レコード一覧画面にボタンを配置
        if ($('#sample_button')[0]) {
            return;
        }
        var $sample_button = $("<button>", {
            id: 'sample_button',
            text: 'こちらを押してください！'
        }).click(function() {
            var myHeaderSpace = kintone.app.getHeaderSpaceElement();
            // 2.ボタンを押したときに表示される内容
            var $myListHeaderDiv = $("<div>", { text: '今日も一日頑張りましょう'}).css({
                "width": '100%',
                "height": '30px',
                "text-align": 'center',
                "font-family": 'ヒラギノ角ゴ Pro W3',
                "font-size": '16px',
                "font-weight": 'bold',
                "background-color": 'rgb(255, 204, 0)'
            });

            // 3.出現時のアニメーション内容
           $(myHeaderSpace).hide().html($myListHeaderDiv).fadeIn("slow");
        });

        $(kintone.app.getHeaderMenuSpaceElement()).append($sample_button);
    });
})(jQuery);
```
 
 

実際にアプリを確認してみると..
![スクリーンショット 2022-02-18 20.42.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1954118/833f90ec-217d-1bbf-c5ee-4944527e4b2e.png)


ボタンができています。
こちらを押してみると..


![スクリーンショット 2022-02-18 20.42.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1954118/8e65c071-7238-5190-1cec-35d346321386.png)

動きがわかりませんが、指定した内容で表示させる事ができました。
実際には　.fadeIn("slow")　を指定したので、フワッと表示されます。

#まとめ
簡単ではありますが、今回はjQueryを用いたKintoneカスタマイズを行いました。
このように、jQueryというライブラリを使用することで、JavaScriptの記述も容易に行う事ができ、アニメーション表示なども非常に簡単に行う事ができました。
備忘録（gitクローンからプッシュまで）

参考になる記事たち
https://qiita.com/nt-7/items/c5ea999a2638e03ee418

1.まず最初にターミナルを開いてgit add . をしよう
2.ブランチを分けよう
git checkout -b "develop"
3.変更をステージしよう
4.変更をコミットしよう
git commit -m "ほげほげ"
5.プッシュするよ
git push -u origin master


git init
→.git ファイルをディレクトリ内に作るよ。もうあるなら（クローンしたなら）いらないよ
Canvg v4を使ってブラウザ上でSVGをCanvasに変換する

# はじめに

2022年2月17日にCanvgのメジャーアップデートであるv4.0.0がリリースされました。

https://github.com/canvg/canvg/releases/tag/v4.0.0

[前回の記事](https://qiita.com/wm-ytakano/items/d33c3822aa84d695f8e6)では、Canvg v3を使ったSVGからCanvasへの変換方法に説明しましたが、今回はCanvg v4を使った方法について説明します。

https://qiita.com/wm-ytakano/items/d33c3822aa84d695f8e6

とはいえ、破壊的な変更は`Canvg`がデフォルトエクスポートから、名前付きエクスポートに変更されたことが主で、v2からv3への移行時ほどの違いはありません。以下のようにインポート部分を書き換えるだけでよさそうです。

```javascript:canvg_v3.js
import Canvg from "canvg";
```

```javascript:canvg_v4.js
import { Canvg } from "canvg";
```


# サンプルコード

詳細な説明は、[前回の記事](https://qiita.com/wm-ytakano/items/d33c3822aa84d695f8e6)を参照してください。

```javascript:canvg_v4.0.0.js
import { Canvg } from "canvg";

function render() {
    const svgRoot = document.querySelector("#svg_root");
    const svgString = svgRoot.outerHTML.replace(/NS[0-9]+:href/g, "xlink:href");
    // .replace()はiOS Safari等でnamespaceが"xlink"ではなく"NSなんとか"になるのに対応するため

    const canvas = document.createElement("canvas");
    const canvas.width = 1920; // canvasの幅を指定
    const canvas.height = 1080; // canvasの高さを指定
    const ctx = canvas.getContext('2d');

    const v = Canvg.fromString(ctx, svgString);
    return v.render(); // Promise
}
```
【初心者向け】配列の値をループ内で操作するには「アレイ(配列)のフィルター処理」が便利！

ある条件でのループ内で、JSONで受け取った配列の値を1レコードずつ見ていくときに便利だったアクションについての覚書です。

# 検証のきっかけ
なんとなくこんな感じのフローを想定していました。
![スライド1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/6402bf03-cd6a-8392-77d0-d7dcbc2dd431.png)
ちなみに配列Nの中身はこんなイメージです。↓
![配列N.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/33244b81-dea4-477e-4f9d-48c853ec0953.png)

要は**とあるWeb APIから複数のレコードを取得してきた値を取り出すためのループの外で更に別のループを回したかった**のですが、上記のフローには以下の問題点があります。

## 自動生成されるApply to Eachがループの回数をおかしくする…！
それは「配列Nから日付列を取り出そうとするときに**自動生成されるApply to Eachアクション**を考慮していない」という点です。

Apply to Eachアクションが自動生成された場合、「以前の手順から出力を選択」というプロパティでは自動で配列Nの名前がセットされます。
これは<b>配列Nの要素の数だけループ処理を行う、</b>という意味になります。

👇Apply to Eachアクションについての詳細👇

https://blog-tips.sekenkodqx.jp/2020/08/31/power-automate-action-apply-to-each-control/
 
つまり、上記のフローだと意図していない子ループが発生してしまうせいで**指定日数×配列Nの要素の数**ループが発生してしまうのです。
![スライド2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/4b79a6c9-a40a-ff0b-ffb5-7549294b1c71.png)

なので、何とかしてApply to Eachが意図していない箇所で自動生成されないように工夫をする必要があります。
![スライド3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/e138dcf2-7a36-e271-1535-e547a8832677.png)

# :bulb:「アレイ(配列)のフィルター処理」
ここで使用すると便利なアクションが「アレイ(配列)のフィルター処理」です。

https://docs.microsoft.com/ja-jp/power-automate/data-operations#use-the-filter-array-action

アクション「アレイのフィルター処理」を追加すると下記のような設定画面が追加されるので、**配列Nから取り出されるデータが1件になるように**設定します。
![アレイのフィルター処理.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/7dbb6ace-a65c-be08-dced-b1af18dac5f8.png)

「アレイのフィルター処理」を使用した結果のフローは下図のようになります。
![スライド4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/a9b0128a-6b66-5054-0a4b-9cf32c6ab759.png)


## 不要なApply to eachの回避
ここで生成された値を更にfirst関数をかけるとフローの可読性が高まることもあると思います。

https://artisan.jp.net/blog/excel-to-sharepoint-6/
>形式は配列でも取得する値が1つであることが明確である場合、わざわざ「Apply to each」アクションを使用すると- フローの保守性・可読性が低くなるので不便です。

# :warning:その他の繰り返し処理作成時の注意点について
以下記事で[ループ処理のDo~Untilのループを抜ける条件について](https://qiita.com/fsgwmhr/items/d3cd5c7cef94a1b4d713#%E3%83%AB%E3%83%BC%E3%83%97%E5%87%A6%E7%90%86%E3%81%AEdountil%E3%81%AE%E3%83%AB%E3%83%BC%E3%83%97%E3%82%92%E6%8A%9C%E3%81%91%E3%82%8B%E6%9D%A1%E4%BB%B6%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)紹介しています。



https://qiita.com/fsgwmhr/items/d3cd5c7cef94a1b4d713
SaaS用語集的な

個人的なメモで随時加筆していこうかな

* Horizontal SaaS
    * 業界問わず特定の部門や機能に特化したSaaS
    * コラボレーション、マーケティング、人事、法務などなど
    * 例：Marketo, Sansan, Bizreach, Chatwork, Zoom, Bizreach, freee
* Vertical SaaS
    * 特定の業界に特化したSaaS
    * 不動産、医療、教育、製造、教育などなど
    * 例：Veeva, カイポケ, スマレジ
* MRR (Monthly Recurring Revenue):月間の経常収益
* ARR (Annual Recurring Revenue):年間の経常収益
* LTV (Life Time Value):顧客が中長期で利用した場合の合計の売上。
  * MRR/CR ※CRはChurn Lateで解約率
* Churn:解約のこと
* 40%ルール:年間の売上成長率+利益率が40%以上であれば健全な状態とする考え方。
* PMF (Product Market Fit):一定数の顧客が抱えている課題に対して適切なソリューションが提供出来ており、新規獲得、解約防止により売上拡大が見込める状態
* T2D3 (Triple-Triple-Double-Double-Double):ARR1億円をスタートとして、年次で3倍->3倍->2倍->2倍->2倍のスピードでARR72億円へと成長すること。ARR2億円をスタートとする場合もある。
* PMFとT2D3:SaaSビジネスではPFMを目指す。PMFした後はマーケティング・セールスへと投資し、T2D3の成長を描くのが理想とされている。
* 書籍
  * The Model:xx
*
CF-1.9がリリースされました

今更ながら、 2021-09-10 に CF-1.9 がリリースされました。
https://github.com/cf-convention/cf-conventions/releases/tag/1.9.0

変更点概要は、GitHub マイルストーンのプルリクエスト一覧として提示されています。合理的な作り方ですね。
https://github.com/cf-convention/cf-conventions/pulls?q=is%3Apr+milestone%3A%221.9%22+
［AWS］今週の問題3週目

# 問題
問1. あなたはroute53で「hogehoge.com」ドメインを取得し、ACMでSSL証明書を発行した。この証明書をALBに設定することでインターネットからVPC内に来る通信に対して暗号化することができる。ではVPC内からVPC内への通信に対して暗号化さらにはどうすれば良いか答えよ。

問2. AWSはログイン後何時間で自動的にサインアウトされるか、またIAMロールのスイッチロールは最大何時間まだ認証情報が有効活か答えよ。

問3. アカウントAからアカウントBへAMIを共有したい。アカウントAのAMIを削除してもアカウントBに残す方法を答えよ。

問4. AMIの保存にいくら料金が答えよ。

問5. 以下の選択肢からルートテーブルの設定変更が必要なものを答えよ。
・インターフェイス型エンドポイント
・ゲートウェイ型エンドポイント
・NATゲートウェイ
・インターネットゲートウェイ

# 解答
問1. パブリックな通信と同じ証明書を利用すれば良い。証明書にはパブリックな通信かプライベートな通信かの情報は持っていない。

問2. 12時間後に自動的にサインアウトされる。スイッチロールは15分から12時間まで設定可能。どちらもタイミングが悪いと途中までの操作が無くなるので充分に気をつけること。

問3. アカウントBに共有したAMIからインスタンスを立て、即AMI化する。

問4. AMIの実態はEBSのスナップショットである。この情報がS3に保存される。月額5〜6円／GBほどで安価。

問5. インターフェイス型エンドポイント以外ルートテーブルの設定が必要である。基本的にゲートウェイ型のサービスは設定を行う。
【G検定対策】個人情報保護法2022年4月改正

次回2022年3月に行われるG検定で出題されそうだと思ったのでまとめます。

---
改正ポイントは大きく6つ

1. 個人の権利
1. 事業者の守るべき責務
1. 事業者による自主的な取組を促す仕組み
1. データ利活用に関する施策
1. ペナルティ
1. 法の域外適用・越境移転

---

## 個人の権利
* 自分の個人情報が他の事業者へどのように提供されているのか、**記録の開示**を事業者に対して求めることが可能
* 個人による自身の**個人情報の利用停止・消去等の請求権が拡大**され、事業者に対応が求められる範囲が拡大

## 事業者の守るべき責務
* 違法または**不当な行為を助長・誘発**する可能性がある個人情報の利用が禁止
* 重大な漏洩等が生じた場合には国と個人に報告が求められる
* 外国の事業者に個人データを提供する場合には、**本人からの同意**を取得し、**移転先の事業者の所在国の名称や、当該外国における個人情報の保護に関する制度などの情報提供**が必要

## 事業者による自主的な取組を促す仕組み
* 事業者は個人データの扱いに関する**自主ルール策定**が期待され、安全管理のために講じた措置を公表する必要がある
* 提供先で個人データになることが想定される情報の提供には**本人の関与**が求められる

## データ利活用に関する施策

:::note info
この部分はわかりませんでした。
どなたかわかる方がいらっしゃいましたら、教えていただきたいです:bow_tone1:
:::

## ペナルティ
* 法人・個人に関わらず命令違反や虚偽申告をした場合の法定刑が引き上げられ、法人には**最高1億円**の罰金が課せられる
* 一方、事業者のイノベーションを促進するため、**「仮名加工情報」制度**が創設され、利用を内部分析に限定するなどを条件に事業者の義務が**緩和**される

## 法の域外適用・越境移転
* 外国にある第三者への個⼈データの提供時に、移転先事業者における個⼈情報の取扱いに関する**本⼈への情報提供の充実**等を求める

---

## 参照

https://j-net21.smrj.go.jp/news/tsdlje0000012r9h.html

https://j-net21.smrj.go.jp/news/tsdlje0000012r9h-att/211109_reiwa2kaiseikojinjouhouhogohou.pdf

https://j-net21.smrj.go.jp/news/tsdlje0000012r9h-att/211109_hajimetenokojinjouhouhogohou.pdf
python ひな形


スレッド、引数、キー入力(opencv2)

```

import threading
import time
import sys
import cv2

class MyThread(threading.Thread):
  def __init__(self, val): #インスタンス化メソッド
    self.mode = val #名前をつける
    
  def run(self):
    img1 = cv2.imread("img.jpg")

    # サイズをそろえる
    window_size = (400,400)
    img = cv2.resize(img1, window_size)

    # 画像を表示
    while True:
        cv2.imshow("img",img) # ウィンドウ開かないとキー入力できない
        if cv2.waitKey(1) == ord('q'):
            break
        time.sleep(1)
    
    cv2.destroyAllWindows()

def mainloop():
    t = MyThread(1)
    t.start() 
    
if __name__ == "__main__":

    args = sys.argv
    if 2 > len(args):
        print("usage : python3 ***.py ")
        exit(1)
            
    dummy = args[1]
    mainloop()
    exit(1)

```

# global 
https://qiita.com/sk161717/items/a5fafd7af108b628a5b0

>調べてみたら、pythonのglobal宣言は、そのスコープの中においてaをglobal変数として扱う、ということらしい。つまり、fooのローカルスコープから参照したときにaはローカルスコープから探し出される。
よって、foo内でaを参照したければ、foo内でもglobal宣言してあげる必要があるってこと。

# pynput
X がいるので　SSHとかターミナルでは使えない。

```

# sample2.py
from pynput import keyboard  # ターミナルでは使えない
import time
import sys
def press(key):
    global exitcnt 
    try:
        print('アルファベット {0} が押されました'.format(key.char))
        exitcnt = exitcnt + 1
    except AttributeError:
        exitcnt = exitcnt + 1
        print('スペシャルキー {0} が押されました'.format(key))

# def release(key):
#     print('{0} が離されました'.format(key))
#     if key == keyboard.Key.esc:     # escが押された場合
#         return False    # listenerを止める

if __name__ == "__main__":

    # listener = keyboard.Listener(
    #     on_press=press,
    #     on_release=release)

    listener = keyboard.Listener(on_press=press)
    listener.start()
    global exitcnt 
    exitcnt = 0
    for i in range(30):
        time.sleep(1)
        if(exitcnt >= 1):
            exit(1)
        print("." + str(exitcnt))
    exit(1)

```

# ターミナルでも有効なキー判定方法

https://www.ishikawasekkei.com/index.php/2019/10/21/python-programing-inkey/
pip install blessed

```

# https://www.ishikawasekkei.com/index.php/2019/10/21/python-programing-inkey/
#pip install blessed

from blessed import Terminal

t = Terminal()
with t.cbreak():
    while True:
        k = t.inkey(timeout=0.001)
        if not k :
            pass
        elif k.is_sequence:
            if k.name == 'KEY_ESCAPE':
                break
            print(f'"{k.name}"が押されました。終了するには「ESC」キーを押してください。')
        else:
            print(f'"{k}"が押されました。終了するには「ESC」キーを押してください。')

```
asyncで取得したデータがpromiseから取り出せない時。

vue.jsで以下のようにしてLaravelが発行するJsonと連携を試みていて、どハマりしました。

```
import axios, {AxiosResponse} from "axios";

export default {

~~~ 中略 ~~~

  data() {
    return {

~~~ 中略 ~~~
  , mounted: function () { // ここが間違ってた
    let items;

      items = await this.fetchEvents();
      console.log(items); // 中身が欲しいのにPromise<pending>が返ってくる。

  }
  , methods: {
    fetchEvents: async function () {

      const response = await axios.get('http://localhost:3000/api/get-events-from-google-calendar');
      return response.data.items;
    }
  },
}
</script>

```

awaitはasyncの中に置かなければいけない、というところまでは把握していましたが、その関数を呼んでいる側も asyncでないといけない事に気づいていませんでした。

なので、上のコードのmountedのところは下のように宣言しないとasyncで囲われていることになりません。

```
, mounted: async function () { // ここが間違ってた
```

もしくはmountedないで、即時関数使って

```
async () => ({この中で await this.fetchEvents(); }()) 
```

みたいにして、asyncにする。処理の初めから非同期を止めたいところまで囲い切らないと効かない。
考えてみれば当たり前ですが日頃コピペでPromiseを乗り切っていると、ちょっと関数に切り出すだけでもハマります。

# 参考

https://teratail.com/questions/325503
【随時更新】クレしんチャレンジまとめ

# クレしんチャレンジとは

クレしんチャレンジとは、以下のようなクレヨンしんちゃんのタイトルコールの画面を作成するチャレンジのことです。
画面作成する練習には良いのではないかと思い、まとめ記事作成してみました。

![スクリーンショット 2022-02-18 19.12.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6280/98ff6809-2d2e-5a76-03e9-3e5bb00cc945.png)

※ 本画像は[クレヨンしんちゃん (HTML+CSS) - Qiita](https://qiita.com/yhatt/items/4cd0c21b6bf5cf8af456)より引用させて頂きました。

本チャレンジをすることで

- キャンバスサイズの設定方法
- 色の定義方法（ピンクから黄色のグラデーション）
- タイトル文字の設定方法
- ルビをふる方法

を学ぶことができます。


# 言語毎のクレしんチャレン

## HTML/CSS

https://qiita.com/yhatt/items/4cd0c21b6bf5cf8af456

## Swift（SwiftUI）

https://qiita.com/noppefoxwolf/items/b4376392d98cea2446dd

https://qiita.com/nullpo33/items/f3d31b567ab8280ef763

## Vue.js + PHP

https://qiita.com/ichii731/items/10eb5ea91991b508ac65

## Flutter

https://qiita.com/RyuNen344/items/272762d0ada00ea49078

## Python

https://qiita.com/tt_and_tk/items/585e5c1184c19827a1a1
makemigrationsコマンド⇛No installed app with labelエラーの対処

# 現象
makemigrationsコマンドをすると
No installed app with label
のエラーが出る

```
(myvenv) PS C:\Users\name\djangogirls> python manage.py makemigrations blog
No installed app with label 'blog'.
```

## 対処①
setting.pyの
installed appに追加する

```
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog.apps.BlogConfig', 
    #blogフォルダのapps.pyファイルに書かれているBlogConfigクラス
]
```

## 対処②
setting.pyをセーブ（Ctrl+S）してから実行
Navigator.popで前の画面に遷移した時に、SnackBarなどで完了を表示する方法

※完全に備忘録です。記事のクオリティには期待しないでください。笑

#Navigator.popで画面遷移した後に、SnackBarで完了を表示するには

![Screen Recording 2022-02-18 at 18.49.44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/782392/9a4bf845-25d3-1aa0-a198-93ecc09b9e99.gif)

これを作るときにには`SnackBar`のコードは`Navigator.push`のコード内に記述します。
「なんで前の画面に記述するの？」「`Navigator.pop`の中じゃないの？」
って自分で思い、自分なりの理解をしたので備忘録として残しておきます。

# なぜ`Navigator.push`の中に記載するのか
今回参考にさせて頂いたのはYouTubeチャンネル「Flutter大学」の中のコードです。

### Navigator.pushが含まれているコード

    return FloatingActionButton(
              onPressed: () async {
                final bool? added = await Navigator.push(
                  //画面遷移させる
                  context,
                  MaterialPageRoute(
                    builder: (context) => AddBookPage(),
                    //遷移先を指定
                  ),
                );
                //▲ここで一旦処理が止まる

                //▼Navigator.popで帰ってきた時に残りのコードが呼び出される
                if(added != null && added){
                  final snackBar = SnackBar(
                    content: Text('本の入力を完了しました'),
                    backgroundColor: Colors.green,
                  );
                  ScaffoldMessenger.of(context).showSnackBar(snackBar);
                }
                model.fetchBookList();
              },
              tooltip: 'Increment',
              child: Icon(Icons.add),
              );

## Navigator.pop（今回はNavigator.of(context).pop();のコード）
     onPressed: () async {
        try {
        await model.addBook();
        Navigator.of(context).pop(true);
        } catch(e){
        final snackBar = SnackBar(
             content: Text(e.toString())
        );
        ScaffoldMessenger.of(context).showSnackBar(snackBar);
        }
        },
        child: Text('追加する')

#なんでpushの中じゃないの？
※あくまで個人的な捉え方です。

Navigator.pushで画面遷移した時に、遷移する前のコードの処理はその場で1時的にストップします。なので、popで戻ってきた時に遷移前に処理した部分からまた処理が再開されるので、`if(added != null, added)`のコードから再開されるというわけです。

# popの中じゃダメなの？
1つの画面には1つのdartファイルが当てはめられているため、遷移前の画面のプログラムには書いても遷移後の画面に反映されないので、遷移後のDartファイルに記載することになります。

# 初投稿なので、文章の適当さ加減は勘弁してください。汗
これからもアウトプットとして書いていきたいと思います！！
シェルスクリプトでanacondaを起動する方法


# 背景
手動で `conda activate [仮想環境名]` とコマンドを打てば環境が切り替わるのですが、 バッチ定期実行をしたいと思ってシェルスクリプトに書き込むとできなかったので、解決方法を記述します。

# 解決方法
`conda activate [仮想環境名]`の前にanacondaのパッケージにある `conda.sh` を使います。

```sh
source 【workspace】/anaconda3/etc/profile.d/conda.sh
conda activate 【仮想環境名】

```

# PostScript
おそらく`.bash_profile`を使って、環境パスを追加する方法でもできる、ハズ。

# 参考にしたリンク
[シェルスクリプト内でのconda環境のアクティブ化](https://ubuntuaa.com/ja/q/126009)
blockMeshDictをどこに置くか問題

# 昔はconstant/polyMeshだった
OpenFOAMのメッシュ作成ユーティリティのblockMeshを使ってメッシュをきるときにはblockMeshDictを作成する必要がありました。古いバージョン（4.0とかより前）だとconstant/polyMeshに置いてました。

# 今はsystemの中に置くことになっている
いつの頃からかsystemに入れておくことになりました。

# OpenFOAM 8だと
昔つくったblockMeshDictのチェックをOpenFOAMのバージョンを変えながらやったので，systemとconstant/polyMeshの両方にblockMeshDictを入れてたら，constant/polyMeshのなかのblockMeshDictを優先的に読みに行ってるのに気づきました。

いつの頃からか，どっちに置いてもいいことになってたみたいです。これに気づかず，メッシュが更新されないのにハマりました。

新しいOpenFOAMだと，systemとconstant/polyMeshのどっちにblockMeshDictがあってもいいけど，両方におくと，constantの方を優先的に見に行く仕様になっていました。
CP4DのWatsonStudioでメモリやCPUを増やしてJupyterNotebookを起動する

CP4DのWatson StudioでJupyter Notebookではデフォルトは1VCPU,2GB メモリで起動します（CP4D 4.0.5で確認）。大きなデータを扱う場合にメモリやCPUを増やしたいという場合には環境定義を行うことで可能です。

* テスト環境
    * CP4D 4.0.5

#1. 新規環境定義

「プロジェクト」を開き、「環境」タブで「新規環境定義」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d798ea6a-270d-84b4-f5de-6163f045c064.png)

名前を付け、VCPUとRAMを指定します。ここでは2VCPU,4GBのメモリーを指定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/faef85f6-8d7f-43e5-9be4-93cdfd0e3350.png)

環境定義が作成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/39a494af-fbf1-6ad8-e9ac-0ed0fe2a5231.png)



環境定義の一覧にも追加されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/fa4d4820-d755-33f5-2f6b-8bf2dc862bf8.png)

#2. Jupyter Notebookの起動
Jupyter Notebookを起動します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f57183e2-939f-2279-b18d-87a745f56e9d.png)

ランタイムの環境として先ほど作成した2VCPU,4GBの環境を選択してNotebookを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/285f85b1-49a5-1d99-e9b8-70318e644194.png)

４GBで起動していることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/597866b1-7ba7-5cc2-4501-0f868a0e82d6.png)

既存のNotebookを先ほどの2VCPU,4GBの環境で起動する場合は、環境の変更を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6a82ec95-3154-f840-2947-201e80b85657.png)

ランタイムとして2VCPU,4GBの環境を関連付けします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/59bbe49e-88f1-a4ca-3ea4-2a2531e04a10.png)
chakra uiのボタン内テキストが`textAlign="left"` で左寄せにならない

さっさと結論知りたい方は[解決策](#解決策)へ

ある日開発中ボタンの中のテキストを左寄せにしたかったわいはこのように書きました。


```html
<Button textAlign="left">ほげほげ</Button>
```

なんということでしょう

テキストが中央寄せのままではありませんか

ここでGoogle先生にhelpを求めます。GitHubにissueがありました。

[[Button] Text alignment does not work #44](https://github.com/chakra-ui/chakra-ui/issues/44)

## 解決策
```html
<Button justifyContent="flex-start">ほげほげ</Button>
```

`textAlign`じゃなくて`justifyContent`でやるそうです。

issueにも書いてはありますがどうやらButtonに`inline-flex`が使われているとのこと。

だから`textAlign`が効かないのね。。。

些細なことですがこれみんな引っかからないの。。？
TaskChute2の補助アドイン 未完のタスクだけ表示する【ExcelVBA】

## 概要
TaskChute2そのものはVBEにロックがかかっていて編集できないのとモジュール追加するのもどうかと思ったのでアドインで機能を追加。

`Ctrl`+`Shift`+`x`で未完のタスクだけ表示/非表示を切り替えるだけですがタスク一覧画面がスッキリします。
（確か標準ではこの機能なかったですよね）

## 補足
### メインプロシージャについて
実行するプロシージャが`TC2_Filter_終了_未`になってますが、
`TC2_Filter_開始_未`にすると開始していないものでフィルタすることもできます。

### TaskChute2のブック名について
TaskChuteのブック名が下記にヒットしない場合は適宜変更してください。
`Const TargetWbName = "*taskchute2*"`

### 行や列を追加されている方は起点を変更してください
`Const Adr起点 = "A7"`
としていますが
購入時のヘッダ左端セルにしています。
起点セルがここでない方は適宜変更してください。

同様に列構成を変更されている方は
`Enum`内を変更してください。

## コード
以下を各モジュールにコピペしてください

### ThisWorkbook

``` vb
Option Explicit


Private Sub Workbook_BeforeClose(Cancel As Boolean)
  Application.OnKey "^+x"

End Sub

Private Sub Workbook_Open()
  Application.OnKey "^+x", "TC2_Filter_終了_未"
End Sub

```

### 標準モジュール
``` vb
Option Explicit

Const TargetWbName = "*taskchute2*"

Private Enum c
  □ = 1
  月日
  曜
  節
  No
  Sort
  ★
  Project
  Mode
  作業内容
  見積H単
  見積M単
  実績
  開始
  終了
  節内S
  タスク実行前のヒント
  タスク実行後のコメント
  評価
End Enum

Const Adr起点 = "A7"

Sub TC2_Filter_開始_未()
  Call FilterOnOff(c.開始, "")
End Sub

Sub TC2_Filter_終了_未()
  Call FilterOnOff(c.終了, "")
End Sub

Sub FilterOnOff(列No As Long, KW)
 'ActiveブックがTaskChute2以外なら何もしない
  If IsTC2 = False Then: Exit Sub
  
  If IsFilterOn = True Then
    Call ShowAllData
  Else
    Call 指定列フィルタ(列No, KW)
  End If
End Sub

Sub 指定列フィルタ(列No As Long, KW)
  Dim Area As Range: Set Area = Range(Adr起点).CurrentRegion
  
  Area.AutoFilter Field:=列No, Criteria1:="=" & KW
  
End Sub

Sub ShowAllData()
  ActiveSheet.ShowAllData
End Sub

Private Function IsFilterOn() As Boolean
    IsFilterOn = ActiveSheet.FilterMode
End Function

Private Function IsTC2() As Boolean
  If ActiveWorkbook.Name Like TargetWbName Then
    IsTC2 = True
  Else
    IsTC2 = False
  End If
End Function
```


## TaskChute2はこちらから

https://cyblog.biz/pro/taskchute2/index2.php
Ubuntuでddrescueを使って、SSDを丸ごとコピーする

WindowsがインストールされているSSDが劣化によって不安定になったので、新しいSSDに丸ごとコピーしました。その際の手順についてまとめます。

Ubuntuの起動に関する詳細な手順は、ここでは割愛します。

# 1. UbuntuのLiveUSBを作成する

公式ページからUbuntuをインストールするためのISOイメージをダウンロードします。
https://ubuntu.com/download/desktop

16GB程度のUSBにISOイメージを焼きこみます。おすすめは**Rufus**です。
https://rufus.ie/ja/

# 2. Ubuntuを起動して、ddrescueをインストールする

インストールしたUSBからUbuntuを起動します。BIOSの設定画面で、起動ディスクをインストールしたUSBに設定することで、Ubuntuを起動することができます。

次のコマンドを使って、パーティションを確認できるツール「gparted」とSSDをクローンできるツールの「ddrescue」をインストールします。

```shell
$ sudo apt update
$ sudo apt install -y gparted ddrescue
```

私の環境では、ddrescueがaptで見つからなかったので、Ubuntu Package Repositoryから直接ダウンロードしてインストールしました。

```shell
# 北アメリカのミラーサーバーからダウンロード
$ wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gddrescue/gddrescue_1.23-2build1_amd64.deb
$ sudo apt install ./gddrescue_1.23-2build1_amd64.deb
```

# 3. GPartedを使って、対象のドライブを確認する

GPartedは、GUIのツールなのでアプリの一覧から起動できます。
Ubuntuを起動したPCにドライブを接続し、GPartedからコピー元、コピー先のドライブのパスを確認します。

例えば、

- /dev/sda
- /dev/sdb
- /dev/sdc

というように表現されています。パスをメモっておきましょう。

# 4. ddrescueを使って、ドライブをコピーする

いよいよ、ドライブをコピーします。ターミナルから先ほどメモしたパスを確認しながら、次のコマンドを実行しましょう。コピー先のドライブを間違えると取り返しがつきません。

```shell
# ddrescue [options] [コピー元] [コピー先]
$ sudo ddrescue -f -v /dev/sdb /dev/sdc
```

オプションの`-f`は、コピー先のドライブを強制的に上書きします。`-v`は、コピー中の進捗状況を表示してくれます。
コピー中は大人しく寝ていましょう。

# まとめ

手順は以上です。コピーが完了した後は、PCに新しいSSDを接続して起動してみましょう。
512GBのSSDのコピーをUSB3.0経由で行ったところ、だいたい4時間くらいかかりました。コピーは、時間に余裕があるときに行いましょう。

また、SSDが損傷したり、パーティションテーブルが吹っ飛んでいたりするとコピーすること自体できません。
日ごろから常にバックアップを心がけましょう。
Spotifyのプレイリストをスマホから見やすくするアプリを作ってみた

graphfyというアプリを開発したので、開発におけるメモ等を残そうと思います。

# graphfyとは
https://graphfy-daiki-kon.vercel.app

Spotifyのプレイリストを時系列順のグラフで表示できるようにしたアプリです。
基本的にはスマホからのアクセスを想定してます。そのためPCからアクセスするとレイアウトが崩れています。
<img width="200px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598845/c72e5713-7a51-5a65-9edb-8b3c2d7e83d6.png">

Gitのグラフ表示をイメージしました。
まだまだ追加したい機能はあるので絶賛開発中です(track検索機能など)

以下のリポジトリで管理してます。(Readmeがデフォルトのまま。。。)

https://github.com/daiki-kon/graphfy

## モチベーション
私は普段iPhoneからSpotifyのアプリを使用して音楽を聞いています。
他の音楽アプリでも同様にプレイリストが作成できると思いますが、SpotifyのプレイリストをiPhoneアプリで見ると
その曲をいつ追加したのかがわかりにくいです。(単純に表示の仕方がわかってないだけかもですが。。。)

<div>
<img width="200px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598845/f47de695-6a66-1019-aa74-b06308d931fe.jpeg">

<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598845/9aad349d-0149-eb18-5990-e8d622198558.png">
</div>
左: iPhone、右: Desktop

個人的にSpotifyのUIはとても見やすく、使いやすいのですがPlaylistのこの件に関しては不満があったので、せっかくなら自分で見やすいアプリを作ろうと考えました。

# 開発に関して

## 環境、ライブラリ等
- Next.js: 12.0.10
- Typescript: 4.5.5
- NextAuth.js: 4.2.1
- Chakra UI: 1.8.3
- SWR: 1.2.1
- Storybook: 6.4.19

## 参考にした記事🙇‍♂️
https://qiita.com/Yuki_Oshima/items/82116e4044687b16ef60

https://qiita.com/ohanawb/items/cfad096cf34a124fcf1f

## 開発で詰まった箇所
主にNextAuthとSpotify周りの部分です...

### NextAuthで取得したアクセストークンに型を付ける方法

`useSession`から取得したsessionの中身をそのまま触ろうとすると、型が`unknown`となっていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/598845/6389474b-9ff2-e13c-637e-93cdb91715c1.png)

これに関しては公式のドキュメントに記載がありました。

https://next-auth.js.org/getting-started/typescript#:~:text=To%20extend/augment%20this%20type%2C%20create%20a%20types/next%2Dauth.d.ts%20file%20in%20your%20project%3A

### Vercelにデプロイする際のエラー

NextAuthを使用したアプリをそのままVercelへデプロイしても403エラーが発生し、認証を通ることができませんでした。

調べたところ、環境変数等の設定が必要でした。

詳細に関しては以下の記事が助けになりました。

https://zenn.dev/tkengineer/articles/5eb78800e9cd5f

https://zenn.dev/seiwa/articles/bab4b36966a391
【Android】キーボード関連の処理

# はじめに
キーボード関連の処理をメソッド形式でメモ。

# コード
キーボード表示状態の確認

```Kotlin
fun keyboardVisibility(view: View) =
	view.height > Rect().apply { view.getWindowVisibleDisplayFrame(this) }.height()
```

---
キーボードの表示

```Kotlin
fun showKeyboard(editText: EditText) {
	editText.requestFocus()
	val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
	imm.showSoftInput(editText, 0)
}
```

---
キーボードの非表示

```Kotlin
fun hideKeyboard() {
	val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
	imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0)
	currentFocus?.clearFocus()
}
```

---
キーボード表示状態変更リスナー

```Kotlin
fun View.setKeyboardListener() {
	this.viewTreeObserver.addOnGlobalLayoutListener {
		val keyboardVisibility = keyboardVisibility(this)
		if (keyboardVisibility) {
			Toast.makeText(applicationContext, "open", Toast.LENGTH_SHORT).show()
		} else {
			Toast.makeText(applicationContext, "close", Toast.LENGTH_SHORT).show()
		}
	}
}
```

---
キーボード表示状態変更リスナー
※リスナー実行後、リスナーを削除する場合

```Kotlin
fun View.setKeyboardListener() {
	this.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
		override fun onGlobalLayout() {
			val keyboardVisibility = keyboardVisibility(this@setKeyboardListener)
			if (keyboardVisibility) {
				Toast.makeText(applicationContext, "open", Toast.LENGTH_SHORT).show()
			} else {
				Toast.makeText(applicationContext, "close", Toast.LENGTH_SHORT).show()
			}
			//処理実行後、リスナーを無効にする
			this@setKeyboardListener.viewTreeObserver.removeOnGlobalLayoutListener(this)
		}
	})
}
```
Laravelでの2段階認証

#はじめに#
セキュリティ面での不安からポートフォリオの管理者ログインに2段階認証をつけようと思ってやってみたところ、結構詰まったので備忘録として残します。記事の内容がセキュリティ的に明らかにまずかったりした場合は、ご指摘頂けると幸いです。

#記事の対象者#
アプリにLaravelを使用していて、管理者アカウントにワンタイムパスワードによる2段階認証を検討している方。

#環境#
Laravel 8.76.2
Google2FA-Laravel v2.0.1 リンクは[こちら](https://github.com/antonioribeiro/google2fa-laravel)

#参考#
https://github.com/antonioribeiro/google2fa-laravel

#Tinkerで管理者インスタンスを作成・鍵発行#
LaravelのコマンドラインツールであるTinkerを用いて、2段階認証を付与するインスタンスを作成します。インスタンス作成時に、Googleの認証アプリであるAuthenticatorに入力するための16桁の鍵を発行します。この鍵は後ほど認証アプリに入力するので、コマンドラインを閉じない or 控えておくかしておいてください。

```ruby:tinker
>>> php artisan tinker
//使用するクラスを指定
>>> use Google2FA;
>>> use App\Models\Admin;

//認証アプリに入力するための鍵を発行
//**後で使用するので控えておく**
>>> $secret_key = Google2FA::generateSecretKey();
//管理者のインスタンスを作成
>>> $admin = new Admin();
//$adminの各種プロパティに値をセット
>>> $admin->hoge = hoge;
//先ほど作成した鍵をセット
>>> $admin->google2fa_secret = $secret_key;
//インスタンスの保存
>>> $admin->save();
//Tinkerの終了
>>> quit
```

#認証アプリに発行した鍵をセット#
[認証アプリ](https://apps.apple.com/jp/app/google-authenticator/id388497605)に先程発行した16桁の鍵をセットします。

#ログイン画面でワンタイムパスワードを入力#
ログイン処理時に正しいワンタイムパスワードか判定するためのinputを設置します。

```php:login.blade.php
//前後は省略します
<input type="number" name="one_time_password">
```

#ログイン処理#

```php:AuthenticatedSessionController.php
public function store(LoginRequest $request)
    {
        $request->authenticate();

        $request->session()->regenerate();

        $g2fa = app('pragmarx.google2fa');
        $g2fa_key = Auth::guard('admin')->user()->google2fa_secret;
        $one_time_password = $request->input('one_time_password');
        if (!$g2fa->verifyGoogle2FA($g2fa_key, $one_time_password)) {
            Auth::guard('admin')->logout();
            return redirect()->route('admin.login')->with('alert', 'ワンタイムパスワードが間違っています。');
        }

        $message = "ログインが完了しました。";
        
        return view('admin.dashboard', compact('message'));
    }
```

通常認証を行った後、ログイン画面にて入力されたワンタイムパスワードが正しいワンタイムパスワードか判定し、誤っていた場合はログイン画面にリダイレクトするように設定しています。

#認証アプリで発行したワンタイムパスワードを入力#
認証アプリで発行したワンタイムパスワード以外の値を入力してもログイン画面にリダイレクトします。ログインしたい場合は、認証アプリで発行したワンタイムパスワードを入力してください。

#おわりに#
以上になります。セキュリティ的にまずかったり、コードにおかしな部分がありましたらご指摘いただけるとありがたいです。
AWS セッションマネージャー アクセスユーザーとログインについて

sshではなくセッションマネージャー（ssm)でのログインに切り替えてしばらく経過。
もはや慣れたと思っていたら、突然思わぬ落とし穴があったので備忘録。

#ssmのログインユーザーは
デフォルトでssm-userというユーザーになっています。
このユーザーは実際はログインしている訳ではなく、シェルアクセスしているだけのようです。

ログイン直後の様子

```shell
Starting session with SessionId: testuser-xxxxxxx
sh-4.2$
```

https://dev.classmethod.jp/articles/mitigation-idea-for-session-manager/

なので`~/.bash_profile`に記載した内容なども実行されません。

使い勝手が悪い為、ec2-userで自動でログインするようにしたいです。
そのためには下記のような方法が考えられます。

①　ssmコンソールのLinux shell profileに `sudo su - ec2-user`を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469267/a0a9c457-0a73-3823-9add-41a9702ea26b.png)

②ssmコンソールのGeneral　preferencesのRun　Asサポートを有効にし、ユーザー名をec2-userにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469267/d0ae16ad-1d8e-20e6-6bc2-23810ad6df35.png)

②の方法がスマートそうだけど、やっぱりこちらも「ログイン」ではなさそう。

https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/session-preferences-run-as.html

#ログインできないと困ること
慣例的に`~/.bash_profile`に色々書く運用をしているので、ログインできないとこれが実行されないという状況。
`~/.bash_profile`ではなくLinux shell profileに記載すれば良いのかもしれないのだけれども。
AWS使わない人でもわかるようにしたいから、ということもある。

#対処方法
何かのタイミングで`sudo su - ec2-user`でログインするしかなさそう。
そうなるとやっぱりssmコンソールのLinux shell profileに `sudo su - ec2-user`を追加する方法が現状のベストになるのか。
ssmコンソールのGeneral　preferencesのRun　Asサポートを有効にし、ユーザー名をec2-userにする方法で既に運用している場合は、仕方ないのでssmコンソールのLinux shell profileを下記のようにする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469267/8ce713a8-e598-49e2-c8d4-33d277efdef7.png)
freezed (json_serializable) でカスタムマッピング

# やりたいこと
toJson, fromJson 時に、そのままの値でマッピングするのでなく特殊な処理を行い変換したい。

# 基本

`@JsonKey` 内に `fromJson`, `toJson` に変換用の関数を渡してあげればそれ使ってくれる。
static な関数しか渡せない。
以下は timestamp を datetime にマッピングする例

```dart
@JsonKey(
  fromJson: timestampToDatetimeNullable,
  toJson: datetimeTotimestampNullable,
)
    DateTime? createdAt,

// 以下は変換用の関数たち
/// TimestampをDateTimeに変換する(nullable)
DateTime? timestampToDatetimeNullable(Timestamp? timestamp) {
  return timestamp?.toDate();
}

/// DateTimeをTimestampに変換する(nullable)
Timestamp? datetimeTotimestampNullable(DateTime? dateTime) {
  if (dateTime == null) return null;
  return Timestamp.fromDate(dateTime);
}
```

# 使いまわす
JsonConverter を継承すればアノテーションとして使いまわせる
```dart
class TimestampField
    implements JsonConverter<DateTime?, Timestamp?> {
  const TimestampField();

  @override
  DateTime? fromJson(Timestamp? timestamp) {
    return timestamp?.toDate();
  }

  @override
  Timestamp? toJson(DateTime? dateTime) {
    if (dateTime == null) return null;
    return Timestamp.fromDate(dateTime);
  }
}
```
```dart
@TimestampField() DateTime? createdAt,
```

# よくあるエラー

``fromJson`` の変換用関数で `List<特定の型>` を引数にすると死ぬ。
特定の型があっていたとしても死ぬ。

例えば以下を読み込んで、userID のリストから User のリストにデコードしようとする。

```json
{
  "id": 0,
  "name": "ほげチーム",
  "userIDs": [1, 2]
}
```

```dart
// 省略

@JsonKey(
  name: 'userIDs',
  fromJson: userDecode,
  toJson: userEncode,
)
    List<User> users,

// 省略

// デコード用
List<User> _userDecode(List<int> userIDs) {
	return userIDs.map((id) => User.fromID(id));
}
```

すると ``List<dynamic>`` は ``List<int>`` に変換できないと言われて死ぬ。

さて、変換してる様子は g.dart に書いてある。変換してからデコード関数に渡すようだ。

```json
users: _userDecode(json['userIDs'] as List<int>),
```

で、これはだめらしい。json.decode とか、firebase からのデータは ``dynamic`` であり、`dynamic` は `List<dynamic>`, `Map<String, dynamic>` までしかダウンキャストできない。そして中身が正しくても`List<dynamic> -> List<int>` とかはできない。ただし、`dynamic -> int` はできる。

なのでこうする。``dynamic`` を ``map`` で一つずつ ``int`` に変換してあげる。

```dart
// 省略

@JsonKey(
  fromJson: userDecode,
  toJson: userEncode,
)
    List<User> users,

// 省略

// デコード用
List<User> _userDecode(List<dynamic> userIDs) {
	return userIDs.map((dynamic id) => id as int).map((id) => User.fromID(id));
}
```
AIOps 発見記（OpsRamp編）

# 概要
この記事では、AIOps の動向と学習のため、各社の AIOps について調べたことを記録しています。
今回は OpsRamp社の AIOps について調べてみました。

# OpsRamp を一言でいうと
SaaS 型 IT 運用管理プラットフォーム

# できること一覧
1. 異常検知
1. 予測検知
1. アラートの相関
1. 定期的なアラートの抑制
1. 自動修復
1. インシデントの自動ルーティング

# 1. 異常検知
最近の傾向から突然の変化を検出した場合にアラートを生成します

# 2. 予測検知
しきい値に違反するまでの予測時間に基づいてアラートを生成します

# 3. アラートの相関
機械学習により、過去のアラートの発生パターンから相関関係にあるアラートをグループ化します。
その結果、アラートの数を削減して、根本原因を特定します。

# 4. 定期的なアラートの抑制
定期的に発生して自然解消するアラートを自動的に抑制します。例えば、毎週土曜の夜にジョブを実行するので一時的に負荷が上がるといったケース。
その結果、メール通知やインシデントの数を削減します。

# 5. 自動修復
アラートをトリガーにしてスクリプトを呼び出し、自動修復アクションを実行します。

# 6. インシデントの自動ルーティング
アラートをトリガーにインシデントを自動作成する際に、過去のアラート、インシデント、および通知データに基づいた学習パターン（担当者グループ、問題のカテゴリ、優先度、ナレッジベースなど）を使用して、さまざまなタイプのアラートのインシデント割り当てをルーティングします。


# 参考資料
https://www.opsramp.com/solutions/service-centric-aiops/?lang=ja
https://info.opsramp.com/service-centric-aiops-white-paper
電電系高専生の一年を振り返る

# 一年、いろいろありましたね
電気電子工学科の高専一年生、Nekoshark02です。Twitterでからんでる人は鍋王せさみジャーキーの方が馴染みがあるかもしれないです。
高専に入って、前年とは考えられないくらい技術力が上がりました。というのも、高専に入ってからというもの、勉強よりパソコン触ってた時間の方が多かったからですかね。（よくない）
しかし今年は一年だからということなのか、専門教科も少なかったすねー。ほんとはパソコンでプログラムばっかり組んでいるんじゃなくて、ちゃんと電気電子工学を勉強したかったんですが、ハード買いに行くめんどくささや勉強のめんどくささの方が買っちゃって、プログラミングばっかりになっちゃいました。
ということで、そんな一年を振り返っていこうかなと思います。
# ３月　父よありがとう、パソコンを買う
このころにパソコンを買ってもらいました。買ってもらって最初にやった言語はPythonだった覚えがあります。

ちなみに自分がプログラムしだしたのって、せいぜいが中３ぐらいなんですよね。だから今はプログラミング始めて１年ちょい位なのか？
まあよくわかんないですけど、まだ全然よわよわなんですよね...でも、最初のころのVScodeのインストールでさえ手間取っていたころと比べると、だいぶ技術力上がったんだなと実感しますね。
# ４月　高専入学二日目からロボコンに凸りに行く
何故か自分は新入生歓迎会をすっ飛ばして、いきなりロボコンに凸りに行きましたね。（強行）
そんなおかげなのか知りませんが、いろんな先輩としゃべったりすることが出来て、めちゃめちゃ楽しいです。いいメンバーにも恵まれて、ロボコン入ってよかったなーと思います。
# ５月　C++進める
ロボコンの制御はだいたいC++でやっているので、それを勉強していました。一回C++を勉強すると、もう戻れなくなるくらいに馴染みます。自分が一番好きな言語がC系になりましたね。
# ６月　マイコンとかいじりだす
始めてマイコンを制御しました。mbedが一番最初のマイコンになりました。抽象的なライブラリのおかげか、さくさく勉強できました。いまはPICでレジスタを叩いたり出来るようになって、ようやくマイコンと0と1で会話できるようになったのかと感慨深いです。パソコンの気持ちを少しでも理解できるように、もっと頑張って勉強したいですね。
# ７月　Web開発にも手を出す
多分この時期だったんじゃないかと思いますが、Webアプリケーション製作にちょっと手を出すになりました。Flaskのチュートリアルでブログアプリケーションを作りました。今も趣味の範囲でちょこちょこ作ったりします。Webアプリケーション作りって、現実逃避したいときにちょうどいいんですよね。
# ８月　Arduinoを買って遊ぶ
自分でArduinoを買って遊んだりしました。７セグとか超音波センサーとか面白いものがいっぱいあったので、ちょこちょこやってましたね。しかもこの時期に夏休みが始まって、ロボコンでも一年生でいろいろやろうかということになりました。
# ９月（１０月？）　４輪メカナムの制御に成功！
この時期ついに足回りの制御に成功しました。というのもありがたいライブラリが存在しているおかげで、メカナムを計算なしで動かせるんですよね。そのありがたいライブラリの作者の神のような先輩が、直接メカナムの制御を教えてくれました。その時期はライブラリもそこまでよく使えなくて、悪戦苦闘しましたが、なんとか制御することが出来ました。今のところ、ロボコンでの一年の思い出の１、２を競うくらい、いい思い出です。
# １１月　超（スーパー）雑用マンになり、雑用をめっちゃやる
この時期はロボコンの地区大会に向けてお手伝いをすることが多くなりました。制御とかはロボコンではあまりやらず、家でぼちぼち色々やってました。雑用で体力がつきました。
# １２月　高専ロボコン！
凄いことに、地区大会で優勝しました。雑用もちょっとは報われたのかなと思いました。優勝が決まったときは凄い盛り上がりでした。いい思い出です。
全国大会にも応援で行かせてもらえることになって、感謝しかないですね。
# １月　明けまして何もしなかった
なんか一回燃え尽きたのか知りませんが、ずっと布団で寝てたり、Twitterしかしなかったり、Youtubeで動画あさったりしてました。休憩は必要...
たぶんSTM32の開発はしてたかな。
# ２月（Now！）　PICやSTM32の開発、Node.jsに触れるなど
今のことだからいくらでも詳しく書けますねｗ
一回プロコンのLT会を見に行った時に、Node.jsをしてるみたいだったので、「そんなにええのか～」と思ってやってます。expressとejsでまたまたWebアプリケーション作ってます。出来たらAzureにデプロイしたいです（あまりデプロイが得意じゃない...）
デスクトップ開発も興味ないわけではないですが、今はロボコンに関係ない新しいことは、あんまり覚えたくないですね...
あとPICでレジスタを叩く感触を覚えました。意外と楽しくて、ドキュメントやリファレンスに書いてあることがより理解できるようになったし、リファレンス見ながらスイッチみたいに0と1でプログラム書いてるのが楽しいです。
STMの開発自体はわりと前々からやってますね。

# 電電高専生の一年
どうでしたか！？やっぱりパソコン一日中触ってると得られるものが大きいですね！失うものも大きいですが！
こうしてみると、たいして電電やってないし、なんか情報科っぽいですね...
今年一年もいろいろ経験つみたいですね。
javascript69_GC

GC(garbage collection)
一つのオブジェクトについて、変数や属性から使われない場合は、
Garbageとなります。

例：
var obj1 = new Object();
obj1 = null;
ここでobj1はgarbageです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1067004/13be74ef-1791-634c-af6c-a66dede0ade2.png)

このようなGarbageオブジェクトに対しては、整理しないとPCの運用スピードが落ちます。

ただし、JSは使わないオブジェクトを自動的に回収してくれるシステムがあるため、人間は何も操作しなくても楽チンです！

そして、var obj= new Object();の中に、変数に使われいているオブジェクトについてJSが自動的に整理してくれなく、人間がもう使わないオブジェクトをnullに設定する必要があります。
そうしたらPCがGarbageを気づいてくれます。
３０代初心者インフラエンジニアが知っておくべきPOWERSHELL

<H1>PowerShellでブール値【$TRUE/$FALSE】</H1>

```powershell
PS > $a = $TRUE
#変数aにブール値を代入
PS > $a.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Boolean                                  System.ValueType
#変数aのタイプを確認
```

<H1>PowerShellでスリープ【Start-Sleep -s 秒数】</H1>

```powershell
Start-Sleep -s 10
#10秒待つ
```

<H1>PowerShellでOutlookからメールを送る</H1>

```powershell
$outlook = New-Object -comObject Outlook.Application

$mail = $outlook.CreateItem(0)

#CC送信先を指定、Outlookアドレス帳に存在する場合は自動的に Outlookユーザに変換される
$mail.CC = "******@*****.jp" + ";" + "******@*****.jp"

$mail.Subject = "【件名】"

$mail.Body = "【本文】" 

#送信先を指定、Outlookアドレス帳に存在する場合は自動的に Outlookユーザに変換される
$mail.Recipients.Add("******@*****.jp") 
$mail.Recipients.Add("******@*****.jp") 

$mail.save()

$inspector = $mail.GetInspector

$inspector.Display()

```
windows上でのPython(Django)環境構築①

win環境でPythonを使ってみようと思ったのでその際に実施した内容です。

今回は以下をインストールしました
・Python3.10.2 (公式サイト：https://www.python.org/downloads/)
・Django (公式サイト：https://docs.djangoproject.com/ja/4.0/topics/install/#installing-official-release)
・VSCode (公式サイト：https://azure.microsoft.com/ja-jp/products/visual-studio-code/)

## Pythonインストール 

今回は公式サイトから取得したインストーラをそのままの設定で実行、インストールしました。
コマンドプロンプト上で下記コマンドを打ってバージョンが返ってくればとりあえずOK

```
py --version
```

## Djangoインストール

こちらも同様に公式サイトからコマンドを引用。
下記のインストールコマンドをコマンドプロンプト上で実行。


```
#インストールコマンド
python -m pip install Django

#インストール後確認用コマンド
python -m django --version
```


## VSCodeインストール

今回はVSCodeをIDEとして使用。
一応eclipseでも開発出来るようだが、今回は勉強用なので使用したことないツールで試す。

これも公式サイトから拾ってきたインストーラをそのまま実行。



## 動作確認

各ツールのインストール後、VSCodeのタブから File > Open Folder で適当に作業用のフォルダを指定。
その後、○○.pyというファイルを作成して適当にPythonのコーディングを行う。

作成した○○.pyファイルを右クリックすると"ターミナルで実行"というものがあるらしいので実行。
するとターミナルタブ上に実行結果が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1802586/509d9a66-c2a4-2930-76d4-f92cb2415a60.png)



ターミナル上で以下のようにコマンド実行しても問題なかったのでおそらくコッチが主流かもしれない。

```
py ○○.py
```
Docker Composeを使う際のコマンドrunとexecの違い

## 概要
`docker compose`のコマンドで`run`と`exec`の違いが曖昧だったためメモ用に残しておきます。
`docker compose`について詳しく知りたい場合は下記公式ドキュメント参照。
[Docker Compose公式ドキュメント](https://docs.docker.jp/compose/toc.html)

## docker composeのrun、 execコマンドとは
`run`、`exec`コマンドの用途はどちらも同じです。
コンテナに対してコマンドを実行したいときに使用するコマンドです。
下記のように実行します。

```terminal
docker-compose run サービス名 実行したいコマンド
or
docker-compose exec サービス名 実行したいコマンド
```
ちなみにコマンドで出てくるサービス名は、`docker-compose.yml`で指定したサービス名です。
`docker-compose.yml`の記載方法はここでは割愛します。

### コマンド入力例
例えば、`backend`というサービス名の`Ruby on Rails`のコンテナに対して下記のようにコマンドを実行できます。

```terminal
docker-compose run backend rails db:migrate
or
docker-compose exec backend rails db:migrate
```

## run、execコマンドの違い
`run` → __新たにコンテナを立ち上げて__入力したコマンドを実行。
`exec` → __起動中のコンテナに対して__入力したコマンドを実行。

### runコマンドを使う際のTips
`run`コマンドは新たにコンテナを立ち上げるので、その都度`rm`コマンドや`prune`コマンドでコンテナを削除しないと容量を圧迫することになります。
いちいちコマンド実行するのが面倒なので、`run`コマンドを実行する際にオプションで`--rm`を渡してあげるとコマンド実行後はそのままコンテナを削除してくれます。

```terminal
docker-compose run --rm サービス名 実行したいコマンド
```

## まとめ
`run`と`exec`のどちらを使用するかですが、個人的には`run`を使用します。
なぜなら`exec`はコンテナを起動しておかないといけないからです。
Rails 記事検索機能の実装

#結論
下記のコードを書けば大丈夫です

```posts/search.html.erb
<h1>記事を検索する</h1>
<%= form_with url: search_posts_path, method: :get, local: true do |f| %>
<%= text_field_tag :content %>
<%= f.submit :検索 %>
<% end %>
<% @posts.each do |post| %>
<img src="<%= "/user_images/#{post.user.image_name}" %>">
<%= link_to(post.user.name, "/users/#{post.user.id}") %>
<%= link_to(post.content, "/posts/#{post.id}") %>
<% end %>
```

```posts_controller.rb
def search
@posts = if params[:content].present?
Post.where('content LIKE ?', "%#{params[:content]}%")
else
Post.none
end
end
```

##上のコードの解説（posts#seaechに検索機能を実装しています。）
###1行目の解説
form_withを使います。(form_tag と form_for は Rails5.1で soft deprecated (非推奨) となっているそうです。)
入力された情報をデータベースに保存しないのでmodelに関しては記述しません。
url: search_posts_pathと指定することで、ユーザーが入力した文字列をposts_controllerのsearchアクションに送ります。
データを取得したいのでmethod: :getを指定します。
また、form_withはlocal: trueを入力していない場合、非同期通信(Ajaxによる通信)が行われます。
そのためlocal: trueを入力すれば、非同期通信をキャンセルし、HTMLとしてフォームの送信が可能になる。
###2行目の解説
text_field_tagは、文字列入力用のスペースを実現することができるViewヘルパーです。
###3行目の解説
サブミットボタンを生成します。
###参考記事
[【Rails 5】(新) form_with と (旧) form_tag, form_for の違い](https://qiita.com/hmmrjn/items/24f3b8eade206ace17e2)
[サブミットボタンを生成](https://railsdoc.com/page/submit)
[[Rails] form_with](https://qiita.com/yait/items/f81dbcd54ce15437008e)
[Railsのform_withを使ってシンプルな検索機能を実装する](https://qiita.com/harashoo/items/6c880f8f99e13ed4878f)

##下のコードの解説
###1行目の解説
送られてきた内容が存在するか
###２行目の解説
where メソッドは、返されるレコードを制限するための条件を指定します
第一引数（content LIKE ?）の?の部分が、第二引数（%#{params[:content]}%）と置き換えられます。
%検索文字列%と表記することによって条件を満たすものを部分一致で返すようになってます.
例えば肉と入力すれば焼肉も肉食も出るということです。
###参考記事
[where メソッド](https://railsguides.jp/active_record_querying.html#%E9%85%8D%E5%88%97%E3%81%A7%E8%A1%A8%E3%81%95%E3%82%8C%E3%81%9F%E6%9D%A1%E4%BB%B6)
[Railsの検索メソッドの基礎（whereでLIKE句を使う）](https://qiita.com/tanutanu/items/74b0760137fe7d6e134a)