OmniAICreator/Qiita-1.07M huggingface dataset:
SendGridを使ってメールの到達率を向上させる

# 概要

サービスを運営する際に、メールを使った仕組みを実装することがよくあると思います。
弊社ではこれまで自前でメールサーバを運用していましたが、ある日突然、特定のプロバイダからメールを拒否されるという事態が発生し、それを機にメールサーバを[SendGrid](https://sendgrid.kke.co.jp/)に移行しました。

しかし、SendGridに移行後も同じ事象が発生したため、メールを正しくユーザーに届けるためにどういった配慮が必要なのか、2019/02/13に開催された「SendGridメール送信ハンズオン」に参加し情報収集しましたので、備忘録も兼ねて投稿します。


# メールがInboxに届くまで

以下はSendGridのブログから引用した、メールが相手のInbox(受信ボックス)に届くまでのフローです。

1. メールをInboxに入れるかの判断はISP側に委ねられている。
2. 判断の材料には「認証（Authentication）」と「レピュテーション（Reputation）」が使われる。

ということが読み取れるかと思います。

SendGridは以下のフローにおける「Outbound Mail Server」にあたるサービスですが、「認証（Authentication）」や「レピュテーション（Reputation）」に関わる実装を肩代わりしてくれる部分があります。

![Image](https://sendgrid.kke.co.jp/blog/wp/wp-content/uploads/2015/09/email-flow.png)
> 引用元:[SendGrid メールがInboxに届くまで](https://sendgrid.kke.co.jp/blog/?p=4092)


# 認証（Authentication）とは

フロー図にもありますが、SPF、DKIM、DMARC等の、送信ドメイン認証のことを指します。

項目 | 説明
:--- | :---
SPF | IPアドレスを利用して受信したメールの送信元が詐称されていないかどうかを確認するための技術。
DKIM | メールに電子署名を付与することで、受信したメールの送信元が詐称されていないかどうかを確認するための技術。
DMARC | 詐称されたメールを受信側がどう扱うべきかの方針をドメインの管理者側が宣言するための仕組み。

SendGridに手順がありますので、それに従って設定することになります。（ドメインは自分で用意し管理する必要があります。）
> [参照：Domain Authentication(SPF/DKIM設定)の設定方法](https://sendgrid.kke.co.jp/docs/User_Manual_JP/Settings/Sender_authentication/How_to_set_up_domain_authentication.html)


# レピュテーション（Reputation）とは

メール送信者の信頼度で、高い人ほどたくさんのメールを送信してもInboxに届きやすくなります。
レピュテーションはISPによって判定の基準がそれぞれ異なるため、一概に、こうすれば上がる、ということは言えませんが、一般的には以下のような基準があるとされています。

項目 | 説明
:--- | :---
迷惑メール報告率 | メールを受け取った人が迷惑メールとして報告した割合。
不明な宛先の数 | 存在しないメールアドレスに対して送付し、バウンスした数。バウンスは、一般的にメールアドレスに誤りがあったり受信ボックスが一杯になっていたりすることが原因で発生します。
スパムトラップ | スパムトラップに引っかかってしまった数。ISPではある特定のアカウントや、古くなったアカウントを再利用し、スパムトラップとして利用します。

上記のことから高いレピュテーションを維持するためには、

- バウンス/苦情への対処。
    - バウンスが発生した宛先には、再送しない。
    - メールの配信停止（オプトアウト）ができるような仕組み。
- 送信先リストのクリーニング。
    - 古くなった送信リストは宛先が存在するのか送信前に確認する。
    - 未開封が続く宛先には送信しないようにリストから除外する。
- エンゲージメントを高くキープする。
    - メールの本文やタイトルを、その人に合わせた内容にする。

といった配慮や、まだメールの送信実績がないIPはレピュテーションがない状態なので、いきなり大量のメールを送信するとブロックされる確率が高くなりますので、徐々に配信数を増やす（ウォームアップする）必要があります。


# SendGridにはどんな機能があるか

**独自のブロックリストを保持し、バウンスした宛先に再送しない。**

バウンスが発生した宛先に再送を続けると、スパムとみなされてしまうリスクがあります。
SendGridはハードバウンスとソフトバウンスに分けて対処を切り分けています。

項目 | ハードバウンス | ソフトバウンス
:--- | :--- | :---
意味 | 恒久的な問題により届かない状況。（例：宛先が存在しないなど） | 一時的な問題により届かない状況。（例：サーバダウン、容量オーバーなど）
処理 | 宛先のアドレスを「バウンスリスト」に登録。 | 最長72時間、自動的に再送を試みる。最終的に届かなかった場合は「ブロックリスト」に登録。
再送のリクエストへの対処 | 同じ宛先に送信しようとしても、SendGrid側でDropする。 | 同じ宛先に送信できる。

**固定IPの設定と自動ウォームアップ**

Pro以上のプラン限定となりますが、固定IPを設定し自動的にメール配信通数を振り分ける「自動ウォームアップ」をしてくれます。

**オプトアウトリンクの自動生成**

配信停止グループを設定でき、グループごとにオプトアウトのリンクを自動的に生成してくれます。
リンクをクリックした宛先は配信停止リストに登録され、次回メール送信のリクエストがあってもDropされます。


# 自分でやらないといけないこと

SendGridが様々な機能を提供してくれる一方で、以下のようなことは自分たちでやらないといけません。

- 送信リストのクリーニング。
- メールのコンテンツの見直し・改善。
- メールを送っても開封されない場合は、もう送信しないような配慮（Sunset Policyと呼ぶ）。

SendGridが全てを解決してくれるわけではないのですが、自分でメールを運用することを考えると導入はかなり楽になるのではないでしょうか。
みなさんもぜひSendGridを使ってみてください。
numpyにおける tensordotとeinsumの関係

# はじめに

個人的な備忘録を兼ねて簡潔に書きます。
誤った理解をしている可能性もあります。
その際は、コメントで指摘して下さい。


einsumの方がtensordotより一般的にかけるものと理解しています。

詳しい内容は下記の公式リファレンスやブログを参考にして下さい。

https://docs.scipy.org/doc/numpy/reference/generated/numpy.tensordot.html
https://docs.scipy.org/doc/numpy/reference/generated/numpy.einsum.html
http://segafreder.hatenablog.com/entry/2017/01/09/203811

# tensordot

```python
numpy.tensordot(A, B, axes)
```

A、Bは多次元配列 axesは掛け合わす軸（整数型）を指定する。

個人的には、tensordotは何をやっているかがわかりづらかったです。
einsumとの関係を調べ、tensordotの処理を理解できたので、書き留めます。

# tensordot と einsumの関係

多くの tensordotは 以下のeinsumで同じような処理を実現できます

```python
numpy.einsum(subscripts, A, B)
```

subscripts は 文字列型で変換前の添字と変換後の添字を指定します。
'{Aの添字}, {Bの添字}->{最終的な添字}'といった具合に指定します。

例えば、

```python
C = numpy.einsum('ijk, ijc->kc', A, B)
```

とすれば
$C_{kc} = \sum_{i, j} A_{ijk} B_{ijc}$
の計算を行います。
簡単にいうと iとjについて総和をとり、kとbが残るという感じです。

例に挙げた einsumと同じ処理をするtensordotは

```python
C_hat = numpy.tensordot(A, B, ([0, 1], [0, 1]))
```
となります。

tensordotでは 総和をとる軸を指定するので、以上のような形になります。

最終的な次元（Cの次元）はAとBで残った次元を合わせたものになります。

A.shape = (2, 3, 3), B.shape = (2, 3, 4)
ならば
C.shape = (3, 4)
です。

一言でまとめると
**tensordot では消す（足し合わす）軸を axesで指定しているだけです。**
レビューの大切さが数値で分かる話。

#はじめに
弊社ではレビューをあまりしてくれない。オヨ～。
ここで「レビューやってやって～」と駄々をコネても良いのだけれど、せっかくの情報化社会。
きっと誰かが「レビューをいれることによって実際にこのくらいお得だ」という統計結果を出してくれているに違いなくて、、
導入の結果どれくらい経済効果が得られたかが分かれば、弊社でも受け入れてくれるのではないか？
そう思ってググって見つけたレポート「[レビューの質と価値の定量化の提案](https://www.juse.or.jp/sqip/workshop/report/attachs/2007/1_A_report.pdf) 」の一部を紹介したいと思います。2007年の記事ですけどね……。


#結論
まず、結論です。
レビューのコスト12.5万円で修正規模64万円分を事前に発見できました。
つまり、**51.5万円の経済効果が得られた**とのことです。
詳細は以下で説明します。

#レポートの内容
防いだ損失を金額に換算することで価値を図っています。
そのために、まず修正に必要な工数をポイントで換算しています。

##修正範囲のポイント化
レビューの指摘内容から予測される「修正範囲」をポイント化した。

```
システムの修正が必要（大）：５ポイント 
複数モジュールの修正が必要（中）：２ポイント
モジュールの修正が必要（小）：１ポイント
```
##発見工程のポイント化
レビューを実施する工程と修正作業量の相対関係をポイント化した。

```
要求定義： ５ポイント
詳細設計： ２ポイント
コーディング： １ポイント
```
ポイントの割合はこちらのグラフを参考に。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307024/83fb5c59-ff0a-3904-0dbf-b632179f5c41.png)



##金額効果
「複数モジュールの修正が必要」でそれが「要求定義」から必要だとすれば、
修正に必要なポイントは２ポイント×５ポイント＝１０ポイントということになります。
金額も含めると以下の式になります。

```
修正規模の金額効果（単位：円） ＝
           修正範囲(ポイント) × 発見工程 (ポイント) × 時間／ポイント × 時間単価 (円) 
```


##実施内容
計 138 件のレビュー指摘項目に対して実施。 
ポイントごとの作業時間と時間単価は以下の通りです。

```
１ポイントに相当する作業時間： 4H
時間単価： 5000 円
```
レビュー所要時間内訳は以下の通りです。

``` 
レビュー会議工数　：6人×3.0H …18.0H
事前レビュー工数　：5人×1.0H … 5.0H
レビューイ準備工数：1人×2.0H … 2.0H
計 25.0H 投資金額 ＝ 25.0H × 時間単価5000円 ＝ 125,000円
```
##集計結果
![image.png](https://qiita-image-store.s3.amazonaws.com/0/307024/f3ad7756-acdc-a0cb-df96-9700a34a0efb.png)

#感想
一応、レビューをしたほうが良いという結論になっているのは問題ないと思います。
レビューすることで手戻りの量を減らせて結果的にプラスになっているのは間違いないでしょう。

しかし、このレポートにはいくつか疑問点が残ります。

まず、**データの発見工程が全て詳細設計**なんですよね。
要求定義まで戻ることもコードを直すだけなのも0ってあり得るんでしょうか……。

あと、修正範囲のポイントや、１ポイントに相当する作業時間と時間単価は適当につけたように見えます。
発見工程のポイント化はグラフから納得はできるのですが、そのグラフは修正範囲には関係ないのではないでしょうか……。
１ポイントに相当する作業時間と時間単価は何を元に決めたのか教えてほしいところです。

また、本レポートからは**コードレビューの効果**が分からないのも悲しいところです。一番知りたかったところなのですが。

以上、ありがとうございました。

最新のデータでレビューの効果を定量化している記事があったらご共有をお願いします。
input[type="text"]の横幅を入力された文字に合わせて可変にする

追記（2024年12月19日）：
`field-sizing` というcssが出てきたので、これで良さそう。
ただし、まだこのタイミングではChromeとEdgeしか動かないらしいので、SafariとFireFoxが対応されれば…。
https://developer.mozilla.org/ja/docs/Web/CSS/field-sizing

---

UIの都合上、input[type="text"]を複数横に並べて、
かつスマートに横幅を変えようとしたら実装で案外苦労したのでメモ。

## つくったもの
![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/37883/0120ffec-23a6-599b-f5d2-05b855499276.gif)
Demo: https://jamog3.github.io/input-width-to-variable/
Repository: https://github.com/jamog3/input-width-to-variable

## やってること
全体的なコードについては、[こちら](https://github.com/jamog3/input-width-to-variable)をご参照ください。

以下、コードまわりの解説。

### html
```html
<div class="input-text__item">
  <div class="input-text__dummy js-dummy-input-text" data-placeholder="文字を入力してください"></div>
  <input type="text" class="input-text js-input-text" placeholder="文字を入力してください">
</div>
```

htmlの構造的には上記な感じで、

+ `input`を`position:absolute`で配置し、親の`div`に依存した横幅になるように指定
    + つまり、`.input-text__dummy`に依存した横幅になる
+ `input`に入力された文字列を`.input-text__dummy`にそのままコピー（js）
+ `input[placeholder]`と`.input-text__dummy[data-placeholder]`に同じ文字列を指定する

ググったら「文字数に応じて横幅を動的に変更していく」みたいな参考記事はいくつかヒットしたのですが、
2バイト文字をlengthでカウントするのも面倒だし、
そもそも等幅フォントじゃないと破綻するし。。

### css
```css
.input-text__item {
  position: relative;
  display: inline-block;
}
```
親の`div`が可変になるように`inline-block`指定し、
`input`を`absolute`にしたときの基点になるように`relative`に。


```css
.input-text__dummy {
  display: inline-block;
  overflow: hidden;
  min-width: 1em;
  padding: 3px 5px;
  white-space: nowrap;
  opacity: 0;
}
.input-text__dummy::before {
  content: '';
}
.input-text__dummy:empty::before {
  content: attr(data-placeholder);
}
```
続いてダミー用`div`について、`input`は必ず1行なので`nowrap`を指定。
表示上は見えてる必要がないので`opacity`で隠す。
`::before`の`content`は通常時は不要なので空だが、
`:empty`な時に`placeholder`と同じ文字列が入るように指定。

```css
.input-text {
  position: absolute;
  top: 0;
  left: 0;
  box-sizing: border-box;
  width: 100%;
  padding: 2px 4px;
}
```
最後に入力用`input`は親の`div`の幅に依存するように`absolute`を指定。
`width:100%`と`padding-left/padding-right`を共存させるために、`box-sizing:border-box`に。

### JavaScript (jQuery)
```js

$(document).ready(function() {
  var $inputText = $('.js-input-text');
  var $dummyInputText = $('.js-dummy-input-text');

  function updateValue(e) {
    var value = $(e.target).val();
    $(e.target).prev($dummyInputText).text(value);
  }

  $inputText.on('keyup', updateValue);
  $inputText.on('compositionend', updateValue);
});
```
> `input`に入力された文字列を`.input-text__dummy`にそのままコピー

ってのはhtml/cssだけじゃ無理くさかったのでjsでやってます。

`$(e.target).prev($dummyInputText)`と指定してるのでDOM構造が異なる場合はご注意ください。

## 最後に
文字入力まわりを除けば、htmlとcssだけでレンダリングできているので、
jsが読み込み完了した時点で表示が変わってガタッとなるとかそういうのはないかと。

サンプルはjQueryで書きましたが、
ReactやVueならデータバインドで`input`と同じものを`.input-text__dummy`にtextとして入れるだけかな。

## 参考
日本語変換中の処理まわりで参考にさせていただきました。
https://qiita.com/RikutoYamaguchi/items/f3cebd06d8904e085433
【osx】スクショ

メモ
しばらくするとすぐ忘れる…

<h2>画面全体</h2>
<h4>command + shift + 3</h4>

<h2>範囲指定マニュアル</h2>
<h4>command + shift + 4</h4>

<h2>ウィンドウ</h3>
<h4>command + shift + 4 のあと、space</h2>


<h2>保存場所の変更</h2>

```
// ピクチャに変更
$ defaults write com.apple.screencapture location ~/Pictures/
```
```
$ killall SystemUIServer
```

デスクトップに戻す

```
$ defaults delete com.apple.screencapture location
```
```
$ killall SystemUIServer
```
[Laravel] 5.4 => 5.5 のバージョンアップで気になった点を検証

# バージョンアップ
* `5.4` から `5.5` へのバージョンアップを実施
* その中で気になった変更について実際に動作させて検証

アップグレードガイド
> https://readouble.com/laravel/5.5/ja/upgrade.html

# リクエスト

## hasメソッド
> \$request->hasメソッドは、入力が空文字列やnullであってもtrueを返すようになりました。
> 以前のhasの振る舞いを提供するため、新しい$request->filledメソッドが追加されました。

### 検証
* `5.5` では「要素が存在すれば」 `true` となる

```php
// GETパラメータ
?foo=test&bar

// 検証コード
dd($request->has('foo'), $request->has('bar'), $request->has('baz'));

// 5.4
true
false
false

// 5.5
true
true
false
```

## onlyメソッド
> onlyメソッドはリクエストペイロードに実際に存在する属性のみの返すようになりました。
> onlyメソッドの古い振る舞いを使いたい場合は、allメソッドを代わりに使用してください。

### 検証
* `5.5` の `all()` は `5.4` の `only` と同じ挙動になった
* `5.5` の `only()` は存在しないパラメータ指定されても完全無視になった

```php
// GETパラメータ
?foo=test&bar

// 検証コード:5.4(all()は引数受け取れない)
dd($request->all(), $request->only(['foo', 'bar', 'baz']));
// 検証コード:5.5(all()は引数受け取るようになった)
dd($request->all(['foo', 'bar', 'baz']), $request->only(['foo', 'bar', 'baz']));

// 5.4
[
  "foo" => "test"
  "bar" => ""
]
[
  "foo" => "test"
  "bar" => ""
  "baz" => null
]

// 5.5
[
  "foo" => "test"
  "bar" => ""
  "baz" => null
]
[
  "foo" => "test"
  "bar" => ""
]
```

# Tinker

> Laravel Tinkerはアプリケーションクラスを参照する際の、省略した名前空間をサポートするようになりました。
> この機能にはComposerクラスマップの最適化が必要であるため、composer.jsonファイルのconfigセクションへ、optimize-autoloaderディレクティブを追加する必要があります。

## optimize-autoloader
* `comopser.json` へ追加する

```json
...
"autoload": {
    "psr-4": {
        "App\\": "app/",
        "Modules\\": "app/Modules"
    }
},
...
"config": {
    ...
    "optimize-autoloader": true
}
```

### 検証

```php
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.1.26 — cli) by Justin Hileman

// 5.4
>>> Modules\Models\BlogModel::all(); // OK
>>> BlogModel::all(); // NG

// 5.5
>>> Modules\Models\BlogModel::all(); // OK
>>> BlogModel::all(); // OK
```
VSCodeでFlowつかうとき

# 概要

新しい案件で型チェックにFlow使ってて、VSCodeで開いたらTypeScriptの気持ちでチェックされてるみたいでテンパったのでメモ

# 手順

1. VSCodeの `左下設定アイコン>Extensions`から`Flow Language Support`を検索してインストール
2. `左下設定アイコン>Settings>Extensions>Flow Configuratin>Edit in settings.json`をクリック
3. 下記を追加

```json
"flow.useNPMPackagedFlow": true,
"javascript.validate.enable": false
```

これでなんかうまくいきそう
redirect_to @userの意味がわからなかったので、調べてみた

##なんで？？
初めて下記をみたとき意味がわかりませんでした。
なのでどういうことか調べてまとめてみました。

```
redirect_to @user
```
調べると下のコードと同じ意味のようです。

```
redirect_to user_url(@user)
```
user_urlだと、users/:idのようなURLになっていることが多いですが、
redirect_to user_urlだと:idの部分に何が入るのかわからないからエラーになる。
その:idの部分に何が入るか指定するのが(@user)

これも同じ意味のよう、、、

```
redirect_to "/users/#{@user.id}"
redirect_to user_url(id: @user.id)
redirect_to user_url(id: @user.to_param)
```

idを省略できる理由は、メソッドの引数URLとしてモデルオブジェクトが渡された時に、Railsはそのモデルオブジェクトのidを自動で取得して、最終的なURLを生成することができるから。


##参考記事
[redirect_to @userが何を省略しているかわかりますか？〜挫折しないRailsチュートリアル７章〜](https://qiita.com/Kawanji01/items/96fff507ed2f75403ecb)
Raspberry PiからMindSphereへ (2)Assetを作る



# 本稿のゴール
最終ゴールとして、Raspberry Piから得た気温データを、MindSphere Assetに保存します。その後アプリケーションを通じてデータの取り出しなどもしてみます。
筆者も初心者のため、何か間違いなどありましたらご指摘していただけると助かります。

前章は[こちら](https://qiita.com/IMdev/items/f77f975876e8ac8eead8)。

今回はアセットを作ります。



# Assetとは？
>アセットとは、1つまたは複数の自動化ユニット(PLCなど)や、工場サイトなどを論理的に表したものです。アセットは、アセットタイプを使用して定義します。
> (MindConnectLib AssetManager FleetManagerより)

自分の理解では、シーメンスのイメージとしては「アセットとはデジタル化された工場設備」のようにニュアンスで使っているように思えます。
ただアセットとは、要するにデータ集積所のことという理解で問題ないと思います。


# Assetを作る
## 計画を立てる
今回、取得して保存したいデータは、「気温」だけです。
時系列データでは「時間」のデータはあらかじめ用意されてるいのでこちらで用意する必要はなく、今回は「気温」という変数を設定すればいいだけです。
また気温は「27.34℃」のような形式で保存したいので、浮動小数点数で登録しなければいけません。ということは変数の型はdouble辺りがいいとなります。

もしも湿度や照度も記録した場合も、単位やデータ型をちゃんと計画しておいたほうがいいでしょう。というのも、一度設定をしてしまうと、後から新たに再設定しなおすというのは基本的に難しいからです(不可能ではありませんが、とても面倒)。
INTでデータ型を登録してしまい、あとで実はもっと大きな数字が使いたいというとき、面倒なことになりかねません。

## Aspectを作る。
ランチパッドからAsset Managerを開いてください。
まずAspectを作るので、左のサイドバーからAspectsをクリックし、AspectsのペインからCreate aspectをクリックします
Nameは今回はrasPiDataとします。

Choose categoryは「Dynamic」。
元からそう設定されているとは思いますが、Dynamicに設定しないと時系列データは取得できないので、ほぼ常にこちらを選びます。

次にAdd Variableで変数を登録します。
このとき、自分がデータとして登録したい変数を登録します。
今回は気温で「27.45℃」のようなデータを登録したいので、
Nameはtemperature。
Data type(データ型)はDouble、
Unit(単位)は「℃」を意味する「deg C」と設定します。

![Asset Manager.png](https://qiita-image-store.s3.amazonaws.com/0/365619/ae3dac06-f465-6724-21f2-272f2007a12b.png)

今回はわかりやすさのため、変数は一つだけですが、複数の変数を入れたいときはここで変数を追加すればいいでしょう。


## Typeを作る
次にTypeを作ります。サイドバーからTypesを選択。Typesペインから「BasicAgent」を見つけて、その右についている「>」をクリック。
そこでCreate typeをクリックします。

NameはrasType。
Aspects欄のBrowse aspectsをクリックして開いたモーダルからrasPiDataのチェックボックスをチェックし、add。

設定はこれだけでOKです。Saveを押しましょう。

![Asset Manager (1).png](https://qiita-image-store.s3.amazonaws.com/0/365619/60658197-1228-5050-6385-8d5a4feaf0e2.png)



ここでは、「BasicAgentタイプの派生タイプとして、新しくrasTypeというタイプを作る」……という意味の作業をしています。
今回はラズパイでAPIを使ってAssetを利用するため、BasicAgentという基本的なエージェントのタイプを選びました。

一方ラズパイではなく、たとえば産業用IoTゲートウェイであるMind Connect Nanoを使う場合は、Nano用のタイプを使いますし、MindConnect IoT2040を使いたい場合は、それ専用のタイプを使います。
またラズパイには他にMindConnect Libを利用する方法もあり、いろいろなデバイスごとにタイプを使い分けることができます。


## Assetを作る
ようやくAssetを作ります。
サイドバーからAssetsを選択、Create assetを押下。
Select typeペインから先程作ったrasTypeを選択。

Nameに「RasAsset」と記入し、Saveします。


## 結局この作業はなに？
一つのAssetを作るためにAspect、Typeを作ってようやく作ることができました。
煩雑な作業と思う所ですが、おそらくは使い回しなどができたり、異なるデバイスを使用するときに必要になるのではないかと思います。

たとえば気温を複数の地点で測りたいとしましょう。「板橋気温アセット」と「新宿気温アセット」を作りたいとき、アスペクトだけは同じものを使いまわすことができます。
産業用の場合、同じ機種の機械Aと機械Bでそれぞれアセットを作りたい、ということがあるでしょうからアスペクトの使い回しは利点があるのではないかと思います。

またタイプについては、複数の異なるデバイスを使用したいとき、たとえば板橋の気温はMind Connect Nanoを使うけど、新宿と名古屋の気温はラズパイでアップロードしたい……というときに、タイプごとに分けて使い回しできるというメリットがあるのではないでしょうか。

更にタイプには複数のアスペクトを設定することもでき、ある場所では気温だけ測りたいけど、別のグループには気温+湿度を測りたいというとき、気温アスペクトに追加して、湿度アスペクトを追加すればいいことになります。
気温と湿度だけなら手作業でどうにかなるかもしれませんが、産業用機械から取得する大量のデータの場合は、使い回しが可能なほうが便利なのではないかと思います。

(少数の変数を扱うときは、あまりメリットが感じられないでしょうが)


# まとめ
今回はアセットの設定だけまとめました。ややっこしいですが、慣れればそれほど難しくありません。

次回からはAPIの使用法に進みます。
スムーズスクロール　メモ

#ヌルっとスクロールする「スムーススクロール」

```javascript:scritp.js
$(function() {
	// #で始まるhrefをクリックした場合に処理
	$('a[href^=#]').click(function() {
		// スクロールの速度
		var speed = 400; // ミリ秒
		// アンカーの値取得
		var href = $(this).attr("href");
		// 移動先を取得
		var target = $(href == "#" || href == "" ? 'html' : href);
		// 移動先を数値で取得
		var position = target.offset().top;
		// スムーススクロール
		$('body,html').animate({
			scrollTop: position
		}, speed, 'swing');
		return false;
	});
});
```

コピペです。
少ないユーザー数をバレないようにしよう

# はじめに
Railsは、デフォルトだとレコードに連番でidを付けます。

それだと、URLにその連番のidが出力されるため、自分が何番目のユーザーなのか、サービスで何本目の記事を書いたのかなどが露出してしまいます。

ユーザー数のサービスだと人気がないことがバレてしまうことも。。。

そこで、Railsで主キーをuuidにする方法をまとめました。

# PostgreSQLの拡張を有効化
以下のRailsコマンドを実行してマイグレーションファイルを作成します。


```
rails g migration enable_extension_for_uuid
```

マイグレーションファイルに以下のように記入してください。

```20190129024253_enable_extension_for_uuid.rb
class EnableExtensionForUuid < ActiveRecord::Migration[5.2]
  def change
    enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
  end
end
```

# 初期化処理を追加
アプリケーション起動時にロードされる初期化処理に以下の内容を加えます。

```config/initializers/generators.rb

Rails.application.config.generators do |g|
  g.orm :active_record, primary_key_type: :uuid
end
```

**設定内容を読みこませる必要がありますので、ここで必ずRailsを再起動して下さい。**

再起動後にマイグレーションコマンドを実行します。

```
rails db:migrate
```

以下のような出力があれば成功です。

```
== 20190129024253 EnableExtensionForUuid: migrating ===========================
-- extension_enabled?("pgcrypto")
   -> 0.0206s
-- enable_extension("pgcrypto")
   -> 0.0771s
== 20190129024253 EnableExtensionForUuid: migrated (0.0981s) ==================
```

# Userモデルを生成する

```
rails g model User name:string
```

```20190129043612_create_users.rb
class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users, id: :uuid do |t|
      t.string :name

      t.timestamps
    end
  end
end
```

# マイグレーション

コンソールからマイグレーションコマンドを実行します。

```
rails db:migrate
```

以下のような出力であれば成功です。


```
== 20190129041857 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0618s
== 20190129041857 CreateUsers: migrated (0.0620s) =============================
```

# 確認

Rails consoleで確認します。

```
Rails c
```

```
> User.create
   (0.2ms)  BEGIN
  User Create (7.2ms)  INSERT INTO "users" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2019-01-29 04:41:06.181904"], ["updated_at", "2019-01-29 04:41:06.181904"]]
   (2.7ms)  COMMIT
=> #<User id: "f15e0b60-d657-4b20-b5e7-2dacf99609dc", name: nil, created_at: "2019-01-29 04:41:06", updated_at: "2019-01-29 04:41:06">
```

*<User id: "f15e0b60-d657-4b20-b5e7-2dacf99609dc"*とあるように、user_idは1ではなく、uuidが付与されています。


他のModelもマイグレーションファイルで同じ記載をすれば実現出来ます。
【Python】リスト操作まとめ

# はじめに
Pythonのリストに関する扱いの自分用メモとしてまとめてみました。AtCoderのPython3.4.3と3.8で動作確認済みです。

# 宣言と初期化

```python
lst = []         #宣言
lst = [1, 2, 3]  #初期化
```

# 要素の追加・挿入
appendメソッドは `.append(n)` でリストの末尾にnを追加します。

```python
lst.append(4)  #lst=[1, 2, 3, 4]
```

insertメソッドは `.insert(n,要素)` でn番目に要素を追加します。

```python
lst.insert(1,4)  #lst=[1, 4, 2, 3]
```

extendメソッドは `.extend(lst)` で末尾にリストを連結します。

```python
lst.extend([4, 5])  #lst=[1, 2, 3, 4, 5]
```

+演算子を使うことでもリストを連結させることが出来ます。

```python
lst = lst + [4, 5, 6]  #lst=[1, 2, 3, 4, 5, 6]
```

#要素の削除
del文は `del lst[n]` で対象のオブジェクトを削除します。

```python
del lst[1]  #lst=[1, 3]
```

popメソッドは `.pop(n)` でリストの指定された位置の要素を削除します。引数を取らなければ末尾を削除します。

```python
lst.pop(1)  #lst=[1, 3]
```

```python
lst.pop()  #lst=[1, 2]
```

clearメソッドは `clear()` でリストの全要素を削除します。

```python
lst.clear()  #lst=[]
```

#要素の整列
sortメソッド `.sort()` は対象となるリストそのものを変更します。

```python
lst = [4, 2, 3, 1]
lst.sort()
lst          #lst = [1, 2, 3, 4]
```

逆にsorted関数は対象のリストは変更しません。

```python
lst = [4, 2, 3, 1]
sorted(lst)  #lst = [1, 2, 3, 4]
lst          #lst = [4, 2, 3, 1]
```


#順序の反転
同様に、reverseメソッド `.reverse()`は対象となるリストそのものを変更します。

```python
lst = [4, 2, 3, 1]
lst.reverse()
lst          #lst = [1, 3, 2, 4]
```

reversed関数も対象のリストは変更しません。ただし、reversed関数はリストではなくイテレータを返すので注意が必要です。

```python
lstA = [4, 2, 3, 1]
lstB=list(reversed(lstA))
print(lstB)  #lstB = [1, 3, 2, 4]
print(lstA)  #lstA = [4, 2, 3, 1] 
```

#リストの複製
Pythonではリストの複製が思ったように出来ないケースが散見されます。これはオブジェクトに固有の識別値が割り振られているためであり、元のリストと自分が複製した（と思っている）リストが同じIDかどうかに気を付けなければなりません。
つまり、下記のようなコードではうまくコピーできません。

```python
lstA = [4, 2, 3, 1]
lstB = lstA
lstB.sort()
print(lstB)  #lstB = [1, 3, 2, 4]
print(lstA)  #lstA = [1, 3, 2, 4]
```

元のリストと別のリストを用意するにはcopy関数を使用すると便利ですが、その場合はcopyモジュールをインストールする必要があります。

```python
import copy
lstA = [4, 2, 3, 1]
lstB = copy.copy(lstA)
lstB.sort()
print(lstB)  #lstB = [1, 3, 2, 4]
print(lstA)  #lstA = [4, 2, 3, 1] 
```

他にもスライシングを用いた複製方法もあります。

```python
lstA = [4, 2, 3, 1]
lstB = lstA[:]
lstB.sort()
print(lstB)  #lstB = [1, 3, 2, 4]
print(lstA)  #lstA = [4, 2, 3, 1] 
```

浅いcopy `copy.copy()` と深いcopy `copy.deepcopy()` の違いに関してですが、浅いコピーでは、オブジェクト内のオブジェクトまでコピーをしてくれず、深いコピーでは再帰的にコピーを行うようです。

#2次元配列を1次元配列にする（flatten）
numpyやitertoolsを使わずに2次元配列を1次元配列に変換するにはどうすればいいのかと試行錯誤した結果です。

```python
print(lstA)  #lstAを確認

lstB = []
for i in lstA:          #lstAのi番目の要素を選択
    for j in i:         #lstAのi番目の要素の中のj番目の要素を選択
        lstB.append(j)  #lstBに選択した要素を追加

print(lstB)
```

```:出力
[[1, 3], [1, 2, 3], [3, 2]]
[1, 3, 1, 2, 3, 3, 2]
```

##flattenの関数化
せっかくなので関数にしましょう。

```python
def flatten(lstA):
    lstB = []
    for i in lstA:  #lstAのi番目の要素を選択
        for j in i:  #lstAのi番目の要素の中のj番目の要素を選択
            lstB.append(j)  #listBに選択した要素を追加
    return lstB
```

内包表記によるflatten関数はこのようになります。

```python
def flatten(mat):
    return [x for row in mat for x in row]
```

２次元配列の場合、sum関数を利用する方法があるようです。非常に簡単ですね。

```python
lstA=[[1, 3], [1, 2, 3], [3, 2]]
sum(lstA, [])
```

```:出力
[1, 3, 1, 2, 3, 3, 2]
```

# おわりに
読んでいただきありがとうございました。指摘等ございましたらコメントお願いします。
PHP: PDOStatement::fetchは途中でMySQLサーバが落ちたとしても静かに処理を中断してしまう

PHPのPDOStatementは`fetch`しているループの最中に、MySQLサーバがシャットダウンしたりして結果セットがすべて取れなくなっても、例外を投げたり、エラーを出したりはせずに静かに処理を中断してしまうようだ。

検証コード:

```php
<?php

$pdo = new PDO(
	'mysql:dbname=testing;host=localhost',
	'root',
	'root',
	[
		PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
		PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false,
	]
);

// ダミーデータを1024件(64MBほど)作る
$pdo->query('CREATE TABLE IF NOT EXISTS data (data text)');
//$pdo->query('TRUNCATE TABLE data');
$stmt = $pdo->query('SELECT COUNT(*) AS total FROM data');
if ($stmt->fetch()['total'] == 0) {
	$stmt = $pdo->prepare('INSERT INTO data VALUES (?)');
	for ($i = 0; $i < 1024; $i++) {
		$stmt->execute([str_repeat('a', (1024 * 64) - 1)]); // 64KB - 1
	}
}

unset($stmt);

// メモリが少なくなってる状況を作り、すべての結果セットがメモリに乗らないようにする
ini_set('memory_limit', '2M');

$statement = $pdo->query('SELECT * FROM data');
$i = 1;
while ($row = $statement->fetch()) {
	echo $i++, PHP_EOL;
	usleep(100000); // このループ中にMySQLサーバを落としてみる
}
var_dump($row);
var_dump($statement->errorInfo());
```


`PDOStatement::fetch()`している最中に、MySQLサーバを落としてみる:

```bash
$ brew services start mysql
==> Successfully started `mysql` (label: homebrew.mxcl.mysql)
```

すると読み込めた行まででループが終了する。下の結果例では77行目まで読み込めたが、残りの947行は当然読み取られない。

出力結果:

```php
71
72
73
74
75
76
77
/private/var/folders/mf/cm46yj755lv9v_v4458953lc0000gn/T/CodeRunner/Untitled 6.php:35:
bool(false)
/private/var/folders/mf/cm46yj755lv9v_v4458953lc0000gn/T/CodeRunner/Untitled 6.php:36:
array(3) {
  [0] =>
  string(5) "00000"
  [1] =>
  NULL
  [2] =>
  NULL
}
```

PDOのエラーコード「00000」は成功を意味するので、本当に静かにループを終了して終わってしまう。

こういう静かなエラーに対応する方法はあるのだろうか？
ローカルにあるマージ済みブランチを一括削除メモ

プルリクで作成したもののリモートでマージしたので残っている、ローカルリポジトリのブランチを一括削除するコマンド。

```
> git branch --merged | xargs git br -d
```

たま～にしか使わないから忘れるのでメモ
GAE+flaskでセッションストアにマネージドMemcacheを使う

# 概要
GAE＋flaskでsessionストアをcookieからMemcacheに変更したので作業手順をメモ。
cookieでも初めは困りませんが、sessionに格納するデータが増えてくると、 サイズが足りなくなりエラーが発生する可能性があります。
※flaskでは4kbyteまでしかcookieに格納できません。

```
UserWarning: The "session" cookie is too large
```

GAEには[マネージドなMemcacheサービス](https://cloud.google.com/appengine/docs/standard/python/memcache/?hl=ja)が用意されているのでそちらに移行します。

# Flask-sessionをインストール
```bash
pip install -t lib Flask-Session
```

# flaskのセッションインターフェースを差し替え

```py
from flask import Flask
from flask_session.sessions import MemcachedSessionInterface
from google.appengine.api import memcache

app = Flask(__name__)
app.session_interface = MemcachedSessionInterface(client=memcache, key_prefix='memsession')
```
[google.appengine.api.memcache](https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.memcache?hl=ja)にmemcached互換のライブラリが用意されています。
importしたモジュールをそのままMemcachedSessionInterfaceのclientに指定すれば認証等は特に意識せず、GAEのマネージドMemcacheに接続できます。
dev_appserver.pyを使用したローカル環境での開発でも、上記だけでMemcacheのエミュレーターに接続しに行ってくれます。
(GAE便利ですね…！)

また、`memcache.Client()`として自分でクライアントを作成することもできます。

```py
app.session_interface = MemcachedSessionInterface(client=memcache.Client(), key_prefix='memsession')
```

ただし、完全なmemcached互換では無いため、いくつかの引数は設定しても無視されます。

```py
# こんなことをしたとしてもGAEのmemcachedに接続しに行く
app.session_interface = MemcachedSessionInterface(client=memcache.Client(servers=['10.0.0.0.0.0.0.1:11211']), key_prefix='memsession')
```

また、スレッドセーフでは無いAPIも存在するので、セッションストアの管理目的の場合、そのままモジュールを渡すのが良いでしょう。
JSで値変更を検知したい-2（配列・defineProperty編）

## はじめに
* 前回「[JSで値変更を検知したい-1（オブジェクト・defineProperty編）](https://qiita.com/ymgd-a/items/5a6bb79e90aa5633d026)」の続きです。
* 今回は オブジェクトのプロパティに配列が含まれていた場合の対応を検討してみます。
* 用意するもの、準備は前回の記事と同じです。

## 実装方針
* 配列監視のはじめのアプローチとしては「前回使った `Object.defineProperty` が使えないか」という方向から考えてみます。
* ただし、配列の全要素に対し、１つ１つ ・・・というのは件数が増えたらヤバそうです。。。
* ここは **配列を操作するメソッド** をカスタマイズできないか検討してみましょう。
* 参考
    * [javascript - How to watch for array changes? - Stack Overflow](https://stackoverflow.com/questions/5100376/how-to-watch-for-array-changes)

## 実装１：push() を自分で定義する
* おそらく最もよく使うであろう 値を末尾に追加する `push()` メソッドの動作をカスタマイズしてみます。

### コード
```javascript
/**
 * 配列の変更を監視します
 * @param {Array} arr 監視対象の配列
 * @param {String} propName 監視対象のプロパティ名
 * @param {function(Object, Object)} func 値が変更された際に実行する関数
 */
function watchArray(arr, func) {
    Object.defineProperty(arr, 'push', {
        value: function () {
            const oldArray = [...this];
            // push() の動作
            let n = this.length;
            for (let i = 0; i < arguments.length; i++ , n++) {
                this[n] = arguments[i];
            }
            // 追加後に任意の処理を呼ぶ
            func(oldArray, this);
            return n;
        }
    });
}

// 適当なオブジェクトを定義
const obj = {
    name: '太郎',
    pets: ['dog']
};

// 変更時に実行したい関数を定義
function onChange(v1, v2) {
    console.log(v1);
    console.log(' =>', v2);
    console.log('');
};

// 監視（今回は配列のみ監視対象とする）
watchArray(obj.pets, onChange);

// 実際に変更してみる（値を追加）
obj.pets.push('mouse');
obj.pets.push('rabbit');
```

### 実行結果

```bash
[ 'dog' ]
 => [ 'dog', 'mouse' ]

[ 'dog', 'mouse' ]
 => [ 'dog', 'mouse', 'rabbit' ]
```

* 実行すると配列に値が `push` された際の動作をカスタマイズでき、変更を検知できていることがわかります。

### ポイント
* `defineProperty` で 配列オブジェクトを指定、メソッド名 `'push'` を指定します
* `defineProperty` で指定しているデスクリプタ の `value` というプロパティで動作を定義します（`push` という動作の `value` … なんだか変な感じですが）
* `value`: アロー演算子でなく `function` キーワードを指定しています
    * これにより `this` のスコープが **与えられた 配列インスタンス** (`arr`)となります。
* `const oldArray = [...this];` としているのは、単に `const oldArray = this;` とすると配列への参照が `oldArray` 変数に格納されてしまいます。
    * 後の処理で配列に変更を加えると `oldArray` にも影響があります（単に参照しているだけなので）
    * スプレッド演算子（...）を使い、配列の中身を取り出した後、再度ブラケットで配列として再定義します。
    * ただしこの方法では、オブジェクト配列の場合には通用しません。（ディープコピーまではできません。本記事のサンプルではオブジェクト配列でないため、うまく動作します）
* `for`文 で自身(this = 配列インスタンス)について **配列を自力で操作しています（後述・問題点）**


## 実装２：他のメソッドをぜんぶカスタマイズする（絶句）
* 実装１で `push` による変更検知が無事にできました！
* 配列の監視を実現するためには **残り全部 の Arrayクラス の 副作用のありそうな メソッド** をカスタマイズすれば良さそうですね！！
* ・
* ・・
* ・・・
* 実装１では **既存のメソッドの動作を自分で実装** していました。
* Arrayクラスのメソッドぜんぶ・・・？
* 次世代の ES がやってきて、メソッド増えたり仕様変わったら・・・？
* そもそもこの自作の動作、本当に正しいの・・・？


## 問題点

### Arrayクラスメソッドの動作を自分で実装しなければならない
* 確かに期待した動作は得られましたが、リスクが大きいですね。。。
* `defineProperty` を使った方法ではなく、別のアプローチで考える必要があります。が、詳細は [次回](https://qiita.com/ymgd-a/items/f82a6708746949960fc4) へ・・・
Relay Cursor Connectionsの仕様概略

(なんかあった時に見れるように、見せれるようにメモとして残しています。間違ったことが書いてあったらすみません。指摘は大歓迎です)

# Relay Cursor Connectionsの仕様概略
https://facebook.github.io/relay/graphql/connections.htm
仕様に沿ったコネクションタイプを定義することで、カーソルを使ったrelayサポートのページングができるようになる

## Connectionタイプ
定義

- 名前がConnectionで終わる
- edges, pageInfoというフィールドを持つ

フィールド

- edges
  - Edgeタイプのリストが返る
- pageInfo
  - non-nullのPageInfoというオブジェクトが返る
 
## Edgeタイプ
定義

- Connectionタイプのedgesフィールドから返る
- node, cursorというフィールドをもつ

フィールド

- node
  - Scalar, Enum, Object, Interface, Union, もしくはNon‐Nullのカスタムタイプを返す
  - リスト型は返せない
- cursor
  - StringかStringにシリアライズできるタイプを返す
  - 不明瞭な文字列が入る(例 Mg==)

## 引数
カーソルの後ろからデータをフェッチするとき

- first ... フェッチするデータの数
- after ... カーソル

カーソルの前からデータをフェッチするとき

- last ... フェッチするデータの数
- before ... カーソル

firstとlastを同時に含むのは非推奨(僕もまだ理由をしっかり理解していない)
https://facebook.github.io/relay/graphql/connections.htm#sec-Pagination-algorithm

## PageInfoオブジェクト
定義

- hasNextPage, hasPreviousPageというboolean型のフィールドを持つ

フィールド

- hasNextPage
  - after, firstがクエリの引数としてセットされた場合hasNextPageを必ず持つ必要がある　　　　
  - hasPreviousPageは持たなくても良い

- hasPreviousPage
  - last, beforeがクエリの引数としてセットされた場合hasPreviousPageを必ず持つ必要がある
  - hasNextPageは持たなくても良い
sendmail で宛先にメールが届かないときはアドレスをコマンドライン上で指定してみる

# 環境

CentOS: 7.4

# sendmail でメールが届かない

先日、[こちら][link1]の記事を参考に、サーバー上のコマンドラインからメールを送ろうとしました。

そのときの手順としては以下のとおり。

① 適当なディレクトリに以下のようなテキストファイルを作成。

```text:~/test_mail
From: <FROM-EMAIL-ADDRESS>
To: <TO-EMAIL-ADDRESS>
Subject: タイトルだよ
Content-Type: text/plain;charset="UTF-8"
Content-Transfer-Encoding: base64

メール送信テストだよ
```

② `cat` と `sendmail` コマンドを使ってメールを送信。

```
# cat ~/test_mail | sendmail -i -t
```

本来はこれだけでメールが送られるはずですが、送れませんでした（自分のローカルで試すと成功する）。
また、`/var/log/maillog` を確認してもエラーは見受けられませんでした。

# 送信元と送信先をコマンドライン上で指定する

細かい原因は分かりませんでしたが、最終的に送信元と送信先のアドレスをコマンドライン上で指定することで解決しました。

`~/test_mail` 内の１～２行の `From: <FROM-EMAIL-ADDRESS>` と `To: <TO-EMAIL-ADDRESS>` を削除したうえで、以下のコマンドを実行します。

```
cat ~/test_mail | sendmail -i -f <FROM-EMAIL-ADDRESS> <TO-EMAIL-ADDRESS>
```

`-t` オプションは必要ないので取り除き、代わりに `-f` オプションを利用して送信元アドレスを指定しています。また、同じく送信先アドレスもコマンド上で指定しています。

環境によってはまったく別の要因も考えられるかと思いますので、参考程度に見ていただければと思います。

[link1]:https://qiita.com/richmikan@github/items/0aea653765e485e16945
サイトに張り付ける画像ってどこに収納してるの？GoogleDrive？的な話

サイト制作で使う画像（背景画像やアイキャッチ画像などなど）。

今までローカルで保存してたんだけど、画像がいっぱいでデスクトップと画像ファイルがゴチャゴチャしてきちゃった。

エンジニアの人たちは、こういう画像をどこに保存してるんだろ？

ちなみに、画像の探しどころは[Canva](https://www.canva.com/)ってサイト使っております。

GoogleDriveとかweb上に保存してるんかなーとggってみるとそういう人もいるみたい。
ほかにやり方ないかなーあると思う。ググったり人に聞いたり探してみるみる。
Pinegrowを使ってみて数ヶ月-Bootstrap/CSSの直感的に操作できるビジュアルエディタの最高峰

こんにちは。haniokasaiです。

わたくし、とてもWebデザインを苦手としています。美的センスもないし、コーディングスキルもないからです。
直感的に操作できるWebデザインソフトを探していたのですが、少なくとも日本語の情報だと役に立たないものばかり。
海外サイトをいくつか覗いて、最終的に「Pinegrow」が最高だぜ！と思い購入しました。

https://pinegrow.com

#公式のPV
[![公式サイトの動画](http://img.youtube.com/vi/ITSMOYJ6usA/0.jpg)](https://www.youtube.com/watch?time_continue=3&v=ITSMOYJ6usA
)

#特徴
##0.Win/Mac/Linuxに対応
OSに縛られる設計ではないので、開発環境のOSにしばられない。
##1.直感的
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/e02912e6-fea8-c2e1-d6be-e3be8f7f225f.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/2dd54e1e-f41a-3c15-2d32-88c4cc5ee352.png)
画面左にあるツールボックスからブロックを取り出して、プレビュー上に設置できます。bootstrap由来のブロックとhtml由来のブロックがどちらも設置可能です。

##2.コードエディタは比較的自由
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/ae5f38bd-f2ab-c536-47a2-1b1e9c237765.png)
ほかのビジュアルエディタを試用してみて、Pinegrowは比較的コードを編集しやすいと思います。ブロックでの操作だけではなく、コードを書き換えたいこともあると思いますので、、、


#ただし
##1.ブロックが望み通りに動かないことがある
たまにブロックを配置してもおかしなことになります。その際はコードを編集しないといけません。
##2.コードエディタが貧弱
予測変換やリアルタイムのコードチェックは通常のIDEに比べると弱いです。
入力時に自動でタブで揃えてくれる機能もありません。
##3.言語が切り替えられない

#価格(2019/02/25)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/7bcd1fa1-3736-cff0-e1fe-665c2bc6b143.png)
わたしはPinegrow Proを使っています。Standardですとプロジェクトが使えないようなので、それはちょっと困ります。

#作業風景
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/b43532f4-fc16-3a95-9fff-db0241ea5066.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/245977/52303618-244b-8cb5-97e9-9bb863335d56.png)

#競合
-Bootstrap Studio
https://bootstrapstudio.io
-Pingendo
https://pingendo.com

#まとめ
webデザインは敬遠していましたが、Pinegrowのおかげでだいぶやりやすくなりました。日本では使ってる人は多くないようですが、増えたらいいなぁと思います。
【SIGNATE】Kaggleの日本版のご紹介

##はじめに
こんにちは、皆さん！[@Raphael_de_murl](https://twitter.com/Raphael_de_murl)です。久々の投稿になります！今回の記事はKaggleの日本版って呼ばれているSIGNATEをご紹介させて頂きたいと思います。

##SIGNATEとは
ちらっと話してましたがKaggleの日本版って呼ばれるサイトです。筆者もつい最近知ってたサイトなどでまだそんなに詳しくはないですけれども日本のデータサイエンティスト又は機械学習エンジニアの方には役に立つサイトだと思ってます。
[SIGNATE - Data Science Competition](https://signate.jp/)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/309361/bc667ee8-bac5-a900-d630-4bf5ac9c2913.png)

こちらのサイトになります。

##コンペティションとは
![image.png](https://qiita-image-store.s3.amazonaws.com/0/309361/601e18df-2e1b-c470-2fe2-299069b68c1c.png)

じゃあ、実際コンペティションとは何なのかを見てみると名前通りを競争です。自分が作った何かのアルゴリズムを投稿して評価を受けることでスコアが分かります。そのスコアで実際参加者の中で自分がどれぐらいの技術力を持っているのかがわかると思います。ちなみに筆者も現在画像処理のコンペに挑戦しています。週末限定でやってるので時間がかかりますね。
[あなたの「技術」を試してみませんか？](https://signate.jp/howto#step2)👈クリック！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/309361/29cc7cb1-5d0b-561b-500f-9a347b60c5a1.png)


##メリット＆デメリット
ではコンペをすることによって何が良くて何が大変なのかを自分の観点でご紹介します。

<b>メリット</b>
 - 自分のスキルアップ
 - データサイエンスの新たな知識
 - データ前処理の大事さ
 - 機械学習への視野拡大
 - 自分の単価アップ:ok_hand:

<b>デメリット</b>
 - 頭が暑くなる
 - スコアが少ないと悲しい:scream:
 - 数学の勉強をちゃんとやらなかったことを後悔する

##まとめに
上で冗談っぽくデメリットって話しましたが、本当にデメリットはないと思います。週末だと休む時間が減ることもありますけれどもその分自分の価値が上がるのでぜひ皆さんもやって見て下さい。

##余談
凄くないですか？:sunglasses:今までAIの勉強とかはほとんどKaggleだったからこれからでも日本に合うデータが提供されるって:yum:機械学習エンジニアとして本当ワクワクします。:heart_eyes:
Mind Sphereによくあるエラー・トラブル解決まとめ

# MindSphere関連でよくあるトラブル集まとめ
この記事では、MindSphereでよくあるトラブル、エラーなどをまとめていこうかと思っています。
随時更新。

## アプリケーション登録時に、経路(route)が設定されていないというエラー
アプリケーション登録時に、以下のエラーが発生するときがあります。

```
The route entered https://***.apps.eu1.mindsphere.io for ferix is currently not available in your CF Org and therefore it cannot be used. Please setup the route before trying again.
```

これは、CF CLIから解決できます。
まずCF CLIからログインし、以下のコマンドを実行

```bash
cf create-route [スペース名] apps.eu1.mindsphere.io --hostname samplehost
```
これでsamplehostという名前のhost名が登録されました。
あとはアプリケーション作成時に、ComponentsのCloud Foundry Direct URLに、`samplehost.apps.eu1.mindsphere.io` というURLを記述してください。





## アプリケーションに接続できない
たまにランチパッドからアプリケーションにアクセスしようとすると、以下のようなエラーが出るときがある。

```
MindSphere Gateway error: Authentication Failed: Could not obtain access token
```
この場合は、一度ランチパッドの右上のログアウト・ボタンをクリックして、ちゃんとログアウトしてから、再度アクセスしてみると繋がるかもしれません。
短時間にログイン、ログアウトを繰り返すと、再度エラーが出たりします。

## pushしたのに404になる
```
MindSphere Gateway error: There is no registered endpoint: https://テナント-アプリ名-テナント.eu1.mindsphere.io/main.js
```
上記のようなエラーが出るときがある
### 解決法
Developer cockpitに入りそのアプリを選択し、Application detailsのComponentsの欄のEnd pointsの「+」をクリック、pathに「/**」を追加する。


## 顔のある雲が出てきてから403 Forbiddenになる
アプリを操作しているとき、顔のついて雲の絵が中央に出てきて、以下のようなエラーが出た。
それ以降何度繰り返しリロードしても403 Forbiddenしか出なくなった。

```bash
InResponseToField of the Response doesn't correspond to sent message a45xxxxxxjj5ddd48e75jg448a78b10
```

### 解決法
一度ログアウトを確実にして、もう一度ログインしてください。
Maya XGen Interactive Glooming export のメモ 

# 目的

XGen IG のデータのつき方を解析する.

XGen Interactive Glooming(spline hair) のデータにアクセス, データのエクスポートを行う.

## mel コマンド

`xgmsplinequery` を利用して調べることができる.

https://around-the-corner.typepad.com/adn/2016/11/support-xgen-interactive-grooming-feature-in-your-plugin.html

http://help.autodesk.com/view/MAYAUL/2017/JPN/?guid=GUID-21159091-7F4C-4FAF-BA67-325E15C7A7EE

strand の本数など確認できる.

## mel/pymel でのアクセス

initial length : `description_base.initLength` で取得できる.

### width parameter

`description_Shape` で取得できる.

## C++ でデータへのアクセス

mel/python だと本数が 1 万を超えるようなモデルだと遅くなりそうなので, C++ API を検討する.

## Attribute

以下の attribute を出したい.

* width
* UV(per vertex of spline curve)
* texture coordinate
* guide spline(XGen IG で作成できるかしら?)

### optional

XGen とは違い, XGen IG ではサポートしていない(or 自前で計算が必要か)

* T(tangent)
* Strand id
* N(normal)
* dpDu, dPdv

## XPD

ベイク(Alembic と同じ?)した curve データを XPD で保存し, API や devkit にある xpd2txt でいじることができる.

https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-43899CB9-CE0F-476E-9E94-591AE2F1F807-htm.html

devkit に xgSplineDataToXpd サンプルがあるが, 中身は xgSpline を Xpd に変換しているだけなので, xgSpline を直接いじるのを想定しているのであれば Xpd はあまり関係なさそう(他のツールと Xpd でデータをやりとりしたい場合用でしょうか)

## 参考文献

* XGen hair の width を Alembic でエクスポートして cyhair に変換する https://qiita.com/syoyo/items/1f9361a86a81d45b594f
* Maya XGen Interactive Grooming Spline を CyHair に変換する https://qiita.com/syoyo/items/c6ad533e955249dc5725



## TODO

* 優秀な Maya exporter plugin 若人さまが, **人類史上最速で優秀な Maya XGen データエクスポート若人さまへ昇華**なされるスキームを確立する旅に出たい
`npm install` はパッケージを使う環境でやろうねという話

React NativeのCI環境を一部Dockerにしようとしたところ盛大にハマったので学びを共有しておきます。

iMacをjenkins-slaveとしていた環境で、React nativeのCI環境を作っていました。

- xcodeはiMacホストのものをつかう
- jsのバンドルなどはdockerで行う

という環境にしようとしていました。

するとなぜか `Mapbox.framework` が無くてビルドできないというエラー。開発環境では問題ない。

`@mapbox/react-native-mapbox-gl/package.json` を見るとこんな記述。

```
  "scripts": {
    "fetch:ios:sdk": "node ./scripts/download-mapbox-gl-native-ios-if-on-mac.js 3.7.8",
    "fetch:style:spec": ". ./scripts/download-style-spec.sh",
    "format": "npm run lint -- --fix",
    "generate": "node ./scripts/autogenerate",
    "lint": "./node_modules/eslint/bin/eslint.js ./javascript/** ./example/src/** ./__tests__/**",
    "precommit": "lint-staged",
    "preinstall": "npm run fetch:ios:sdk",
    "test": "npm run lint && npm run unittest",
    "unittest": "jest --coverage --verbose",
    "unittest:single": "jest --testNamePattern"
  },
```

`preinstall` で fetch:ios:sdk やってる。 fetch:ios:sdkのコマンドは `node ./scripts/download-mapbox-gl-native-ios-if-on-mac.js 3.7.8` 。
おお、macで実行しないと `Mapbox.framework` どダウンロードしない作りになっている。

# まなび
ああ、そういえばネイティブコールするパッケージとかもnode_modulesのコピーだけじゃうまくいかないこともあったなぁ。
npm installはパッケージのダウンロード後に、一部環境依存のファイルを生成したり、ダウンロードしたりするんだなぁ。
実際にパッケージを使う環境で`npm install`しないとはまることがあるなぁ。
編集画面から投稿を編集する方法

フォームから情報を投稿する、ツイッターみたいなアプリを作っています。

その中で投稿したデータを編集する方法を書いていきます

postモデル(投稿)

ルーティングを確認

![boost ルーティング.png](https://qiita-image-store.s3.amazonaws.com/0/333735/fc25f93a-3009-42ad-35ed-7e492410a97c.png)


５行目が編集に当てられているルーティングです
posts/id/edit
id番号1のpostsを編集したいという意味です

editコントローラーを作成

![boost 編集コントローラー.png](https://qiita-image-store.s3.amazonaws.com/0/333735/d1b8abab-0f06-51be-952b-2bbde70745b9.png)

リクエストされたidをPostモデルを使ってDBから取ってきて、インスタンスに代入

編集画面を作成

![編集画面.png](https://qiita-image-store.s3.amazonaws.com/0/333735/e974598a-8b48-6950-ddfe-80a86c6a6a51.png)

編集画面は登録した情報を編集する画面ですが、今回は新規投稿画面とほぼほぼ同じ
レイアウトで作成しました

form_forは　＠モデル（＠post）が持つデータを入力欄に表示する

新規投稿画面は、空の＠インスタンスを用意しているため空の入力フォームを表示する

updateコントローラーを作成

![updateアクション.png](https://qiita-image-store.s3.amazonaws.com/0/333735/418d3362-ffe2-3604-7894-1b4ea01f14f8.png)

updateアクションで入力欄の情報を取りDBを書き換える
updateメソッドを使用

最後にredirect_toでshowアクションへ（投稿詳細）
【UiPath】Retry Scopeの使い方

だいたい正常終了するけど、たまにエラーが発生する。なんて時にRetry Scopeが便利。

![retryscopetryecathc.png](https://qiita-image-store.s3.amazonaws.com/0/365107/f789e624-c811-582f-57f5-e747f4c3fbbd.png)
テストのためにRetry Scopeの中にThrowを入れて強制的にExceptionを発生させてみる。

![catch.png](https://qiita-image-store.s3.amazonaws.com/0/365107/fbbd7212-6062-6a87-0615-8578c2c6f260.png)
Catchの中ではログを出してRethrow。

![property.png](https://qiita-image-store.s3.amazonaws.com/0/365107/a1fbc8d7-0d39-a507-6485-146baead270b.png)
Propertiesはデフォルトのまま。
- NumberOfRetries：ループ回数
- RetryInterval：ループ間の待ち時間

#### 実行結果
![log2.png](https://qiita-image-store.s3.amazonaws.com/0/365107/a0cc09f4-e045-3ed5-9a6c-fcefd87e3911.png)
ログを見ると「エラー発生」が5秒毎に3回出てますね。
Django Webアプリ作成(7) テンプレートの拡張

環境
OS : macOS Mojave
Anaconda : python3.6.7
Django==2.1.5

#ここまで
前回までで一通りの流れをやりました。
今回は、Djangoのテンプレート拡張機能を用いて新たなページを作成していきたいと思います。

#テンプレートの拡張
はじめにサイトの大枠であるbase.htmlを作成します。
templatesディレクトリにbase.htmlを作成

```:Terminal
touch templates/base.html
```

base.htmlを作成したら、post_list.htmlの中身をコピーして貼る。
その後、

```html:base.html
{% for post in posts %}
.
.
.
{% endfor %}
```

部分を


```html:base.html
{% block content %}
{% endblock %}
```
に書き換える。

これで拡張元のhtmlファイルが完成
次に、post_list.htmlを以下のように書き換える。

```html:post_list.html
{% extends 'base.html' %}

{% block content %}
    {% for post in posts %}
        <div class="post">
            <div class="date">
                {{ post.published_date }}
            </div>
            <h1><a href="">{{ post.title }}</a></h1>
            <p>{{ post.text|linebreaks }}</p>
        </div>
    {% endfor %}
{% endblock %}

```
今回は、post_list.htmlを作ってからbase.htmlを作ったが
実際、作る際は先に大枠となるbase.htmlを作成してからそれぞれのページ（htmlファイル)を作成していくと良い。

#新ページ(post_detail.html)の作成
これまでブログの一覧ページ(post_list.html)を作成したので、次にブログの詳細表示ページ(post_detail.html)を作成していく。

```:Terminal
touch templates/post_detail.html
```
post_detai.htmlの中身は以下のように

```html:post_detail.html
{% extends 'base.html' %}

{% block content %}
    <div class="post">
        {% if post.published_date %}
            <div class="date">
                {{ post.published_date }}
            </div>
        {% endif %}
        <h1>{{ post.title }}</h1>
        <p>{{ post.text|linebreaksbr }}</p>
    </div>
{% endblock %}
```

 {% if ... %} ... {% endif %}はpythonの基本構文でいうとif...elif...に相当し今回の場合はpublished_dataが存在するかをチェックしている。

ページ(.html)を作成したら次はビューを追加します。

```python:blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Post


def post_list(request):
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    return render(request, 'post_list.html', {'posts': posts})


def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'post_detail.html', {'post': post})
```
上の1行目のfrom文とdef post_detail部分が新たに追加したものです。
ビューが完成したらurls.pyのurlパターンを追加します。

```python:blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]
```
最後にpost_list.html内にpost_detail.htmlに飛ぶようにリンクを追加します。

```python:post_list.html
<h1><a href="">{{ post.title }}</a></h1>
```
部分を以下のように編集

```python:post_list.html
<h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>
```
これは、クリックするとurls.py内のname='post_detail'が呼び出されるという意味です。

#復習
ページ作成の手順としては
htmlファイルの作成　→　ビュー関数の作成　
→　urls.pyへの追加　→ リンクの埋め込み　
といった感じです。

次回はページの表部分からブログ記事を投稿できるようにしたいと思います。
Unreal Engine 4メモ：名前空間

#UE4で名前空間を使うときの注意
UE4のエンジンコードは特にnamespaceで括られていることはないのでゲーム側のコードで名前が被らないように注意は必要。

無名空間はビルド時にcppファイルを連結する関係（オプションです）で「翻訳単位」が変わってしまい、スコープが考えていたよりも広くなる。テキトーにつけた名前が連結具合によって被るなどの事故もある。無名空間は実質グローバル変数の乱立となるので避けるべき。名前空間には必ず名前つけましょう。

using namespaceは上記の翻訳単位の関係で同じファイル内での使用に限定される場合であっても定義したシンボルが曖昧になる可能性があるため使用は避ける。

UE4のBPは名前空間に対応していないので下記のフォーラムのようなことが起こる。
[Using namespaces with UCLASS](https://forums.unrealengine.com/development-discussion/c-gameplay-programming/4908-using-namespaces-with-uclass)

#参照
[UCLASSの継承における外部ライブラリのnamespace解決](http://usagi.hatenablog.jp/entry/2017/06/21/022024)


-> 追加予定：「ビルド時のソースコード連結」に関する說明、リンク。c の ファイルローカル static 変数についての說明。
【Java】文字列の空白を除去する


##trim()
文字列の前後の空白を除去したい時は、**trim**メソッドを使用します。
ただし、文字列途中にある空白は除去の対象外になります。

```java:trim.java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        String str = "   Hello, World   ";
        //文字列前後の空白を除去する
        System.out.println(str.trim());
    }
}
```

```:結果
Hello, World
```

##replaceAll()
文字列のすべての空白を除去したい時は、**replaceAll**メソッドを使用します。

```java:trim.java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        String str = "   Hello, World   ";
        //すべての空白を除去する
        System.out.println(str.replaceAll(" ",""));
    }
}
```

```:結果
Hello,World
```
REST APIを使って期間を絞ったTwilioの通話ログを取得する

Twilioの通話ログをREST APIを使って、期間を絞った通話ログを取得する方法を記載します。

```
// 以下パラメーターを自分のパラメーターに置き換えてください。
// your_account_sid, account SID, AuthToken
// URLエンコード前
curl 'https://api.twilio.com/2010-04-01/Accounts/[your_account_sid]/Calls.json?StartTime>=対象開始日&EndTime<=対象終了日' -u [account SID]:[AuthToken]

// URLエンコード後
curl 'https://api.twilio.com/2010-04-01/Accounts/[your_account_sid]/Calls.json?StartTime%3E=対象開始日&EndTime%3C=対象終了日' -u [account SID]:[AuthToken]
```

REST API使っててなんかうまくいかないなーと感じたら[API Explorer](https://jp.twilio.com/console/runtime/api-explorer/voice/call-create)を使ってみてください！

[公式リファレンスはこちら](https://jp.twilio.com/docs/voice/api/call)
Puppeteerスクレイピング〜任意のページから電話番号を取得する〜

#はじめに
 - 今回、Node.jsのライブラリである、Puppeteerを使って、スクレイピングツールを作ったので、共有しておきます。
 - Puppeteerのインストール方法については、情報がたくさんありますので、割愛します。
 - ※本記事に出てくる会社概要や電話番号は、知り合いの会社のもので、許可を得て掲載してます。

#今回のコード
```javascript:example.js
const puppeteer = require('puppeteer'); // ①puppeteerの読み込み
let url = 'https://recore-pos.com/contact/'; //②ここにURLを入力する


(async () => {
	// ③puppeteerからブラウザと新規タブを立ち上げる
	const browser = await puppeteer.launch();
	const page = await browser.newPage();

	// ④pageを目的のURLへ移動
	await page.goto(url); 

	// ⑤html全体を取得
	let html = await page.$eval('html', item => {
		return item.innerHTML;
	});

	// ⑥電話番号をゆるゆるな正規表現で抽出
	let res = await html.match(/0\d{1,4}-\d{1,4}-\d{3,4}/);

	// ⑦結果を出力
	if(res !== null){
		console.log('電話番号取得成功：' + res[0]);
	}else{
		console.log('電話番号を取得できませんでした。');
	}

// ⑧puppeteerを終了
await browser.close();
})();
```
```terminal:実行結果
電話番号取得成功：06-6767-8787
```

#ざっくり解説
①ライブラリの読み込みに必要なおまじないです。
②ここに好きなURLを入れます。今回は、知り合いの会社のものを使わせて頂いてます。
③「browser」定数と「page」定数を、定義してます。他の言語で言うところの、「final」と同じ動きをします。
④「.goto()」で、引数のURLに移動します。
⑤第一引数の「'html'」はセレクタです。今回は、html全体を選択してます。
⑥ゆるゆるですが、一応電話番号を狙った正規表現です。ここを変えれば、好きなものをスクレイピングするツールにカスタムできます。※正規表現については、[こちら](https://qiita.com/riotam/items/47a0155e9e9af7ffebb5)でも触れています。
⑦結果を成功失敗で、分岐させてます。
⑧Puppeteerで開いたブラウザを閉じるおまじないです。

#最後に
URLや、正規表現を調整すれば、住所やメールアドレスなど、必要なものをスクレイピングできるツールにカスタマイズできます。
##今回のコードの注意点
 - 今回のコードでは、インラインフレーム内の文字列は、取得できません。
 - 電話番号はゆるゆるな正規表現を使っております。これでは、「-」なしの電話番号はヒットしません。[正規表現についてはこちら](https://qiita.com/riotam/items/47a0155e9e9af7ffebb5)。

以上です。最後まで、ありがとうございました。
Python3で作られたpickleファイルをPython2で読み込みたい

# はじめに
Python3でプロトコルを指定せずに作られたpickleを、Python2で読み込もうとすると、以下のエラーが出ることがあります。
プロトコルについては、後述します。

```
ValueError: unsupported pickle protocol: 3
```

# 解決法
Python3で読み込んで、Python2で読み込める形式で保存します。`pickle.dump()`内で、`protocol=2`と指定します。

```Python
#!/usr/bin/env python3

import pickle

with open("sample_py3.pkl", "rb") as f:
    w = pickle.load(f)

pickle.dump(w, open("sample_py2.pkl","wb"), protocol=2)
```
この例ですと、`sample_py2.pkl` をPython2で読み込むことができます。


# 読み込める形式とは
Pickleは、データをバイナリストリームに変換するためにプロトコルを使用します。
0〜HIGHEST_PROTOCOL（執筆時、2019/02/25では4）まであります。

- Python2では、3つの異なるプロトコル（0, 1, 2）を使うことができ、デフォルトは0です。
- Python3では、5つの異なるプロトコル（0, 1, 2, 3, 4）を使うことができ、デフォルトは3です。

Python2ではプロトコル3以上を選択できないので、Python2で、プロトコルが3以上のpickleを読み込みたい場合は、Python3で一旦読み込んでから、プロトコル2以下を指定して保存する必要があるということです。
各プロトコルバージョンについては、以下をご覧ください。
https://docs.python.jp/3/library/pickle.html#pickle-protocols

# 参考URL
- https://docs.python.org/3/library/pickle.html#pickle.load
- https://docs.python.org/3.6/library/pickle.html#data-stream-format
クロスサイトリクエストフォージェリ（CSRF）とは？

## 「クロスサイトリクエストフォージェリ（CSRF）」とは
CSRF(クロスサイトリクエストフォージェリ)とは、Cross-Site Request Forgeriesの略で、別のサイトを通して(クロスサイト)、悪意のある要求(リクエスト)を正しい要求だと偽って(フォージェリ)を送信することです。
##CSRFの原理
![image.png](https://qiita-image-store.s3.amazonaws.com/0/310210/70a178bf-b72d-7a80-a59d-193dc8042684.png)
上の図から、1回CSRFの攻撃をするなら、
１、信用サイトAにログインして、cookieを生成します。
２、Aサイトを閉じてないまま、危険サイトBに訪問します。
二つ要素が必要です。
## 危険性
CSRF脆弱性を具体例を用いて説明する。 
今ある銀行のWebサイト（標的サイト）にログインしているユーザALICEが自身の口座からBOBという別のユーザの口座に100ドルを送金する際、ALICEのブラウザから
```GET http://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1
```というHTTPリクエストが銀行のWebサイトに向かって送信されるものとする。
攻撃者MARIAは
```http://bank.com/transfer.do?acct=MARIA&amount=10000 HTTP/1.1
```というURLを公開掲示板に張ったり、不特定多数にメールしたりする。
銀行のユーザALICEがこのURLをクリックしてしまうと、ALICEのブラウザから
```GET http://bank.com/transfer.do?acct=MARIA&amount=10000 HTTP/1.1
```というHTTPリクエストが銀行に向かって送信される[7]。この際たまたまALICEが銀行にログインしていたら、銀行のサーバはこのリクエストを送金要求だと解釈してしまい、ALICEの口座からMARIAの口座に一万ドルが不正送金されてしまう。


## 対策
***1、http referer***
Referer が正しいリンク元かチェックする
![image.png](https://qiita-image-store.s3.amazonaws.com/0/310210/f0fa6fb4-b9bb-bf02-98ef-98b800d0da9d.png)
リファラを参照することで、
どこからそのページに要求が来たのかを知ることができます。
ブラウザから送信されてくるリンク元の情報である Referer を確認することで、
正しい画面遷移を行なってきていることを判断することでも CSRF の対策になります。
しかし、Referer の情報は、ユーザーの設定やセキュリティソフトなどにより情報が送信されないこともあるので、社内環境など環境と特定できる場合に利用することがよいかと思います。

***2、検証コード***
重要な情報が入力する場合、検証コードとかpasswordの入力を求めます。

***3、Tokens***
重要な処理を行うページに、hidden 項目(見えない項目)でトークンという秘密情報をセットして表示します。このとき、セッション情報としてトークンを保存しておきます。

そして、ユーザーが画面に必要情報を入力後送信処理を行うと、トークンも一緒に Webアプリケーションに送信されます。この送信されたトークンとセッションに保存しておいたトークンが等しければ、正しいリクエストだと判断できます。トークンが空だったり、値が異なればエラーとして処理をします。

## Flask-WTFのテストのCSRFトークンの扱い
- 1.バックエンドでcsrf_tokenを生成して、フロントでログインとか登録のルクエスト送る時に、csrf_tokenをフロントに送ります。
      + ファムに隠して送る
      + cookieの方法で送る
- 2.フロントで、またリクエストを送る時、csrf_tokenをバックエンドに渡します。
- 3.バックエンドで、フロントで持ってきたcsrf-tokenを検証します。
- 4.csrf-tokenの値が同じあれば、正しいリクエスト、トークンが空だったり、値が異なればエラーとして処理をします。

***全部のviewを保護するため、CsrfProtectモジュールを導入します***

```
from flask_wtf.csrf import CsrfProtect
 
CsrfProtect(app)
```
***テンプレートのファームにcsrf_tokenを入れます***

```
<form method="post" action="/">
    {{ form.csrf_token }}
</form>
```
***テンプレートの中にファームがなくても、テンプレート内にhiddenでインプットに入れとかないと動かないです***

```
<form method="post" action="/">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
```
***csrf検証失敗のリクエストを処理します***

```
@csrf.error_handler
def csrf_error(reason):
    return render_template('csrf_error.html', reason=reason), 400
```
***csrf検証が要らない場合***

```
@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
    # ...
    return 'ok'
```
#### AJAX
***テンプレートの< meta >にcsrf_tokenを渡します***

```
<meta name="csrf-token" content="{{ csrf_token() }}">
```
***テンプレートの< script >でも使えます***

```
<script type="text/javascript">
    var csrftoken = "{{ csrf_token() }}"
</script>
```
***AJAX(ここでは< meta >の方法を使いました）***

```
var csrftoken = $('meta[name=csrf-token]').attr('content')

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken)
        }
    }
})
```
参考サイト:http://docs.jinkan.org/docs/flask-wtf/csrf.html