OmniAICreator/Qiita-1.07M huggingface dataset:
【Laravel】Amazon S3へのアップロードが「403 Access Denied」で失敗する原因と対処方法

# はじめに
LaravelでAmazon S3へ画像アップロードする処理を実装し、いざアップロードしようとしたところ、以下のエラーが出て失敗しました。

```
Error executing "PutObject" on "https://minna-no-houteishiki.s3.ap-northeast-1.amazonaws.com/r1oJ01mYWVeol0m7jUTji7uef5lyG7MATNOqzl1A.png"; AWS HTTP error: Client error: `PUT https://minna-no-houteishiki.s3.ap-northeast-1.amazonaws.com/r1oJ01mYWVeol0m7jUTji7uef5lyG7MATNOqzl1A.png` resulted in a `403 Forbidden` response:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>ABBC2D (truncated...)
AccessDenied (client): Access Denied - <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>ABBC2D1C8E07A319</RequestId><HostId>ZvHZ5rneZGsSstZLX2jIoixQFaaFnRxFdtoiNE1Hd+0n17a09y4JKDckdlYsZOC/nMhiQuBb3fA=</HostId></Error>
```

`PutObject`というコマンドを実行したところ、アクセスが拒否されてしまったようです。

# 原因
S3のバケットにパブリックアクセス設定機能が設定されているためです。パブリックアクセス設定機能とは、バケット内のファイルをパブリックアクセスできないようにする設定のことです。

Amazon S3のセキュリティについては、以下の記事が大変よくまとまっているため参照してください。

[S3で誤ったデータの公開を防ぐパブリックアクセス設定機能が追加されました ｜ Developers\.IO](https://dev.classmethod.jp/cloud/aws/s3-block-public-access/)

つまり、Amazon S3にパブリックアクセス設定機能が追加されたことにより、デフォルトでパブリックアクセスができなくなったということです。

ちなみに、Laravelの実装は以下のようになっています。

```php
$image = $request->file('image');
$path = Storage::disk('s3')->put('/', $image, 'public');
$post->image = Storage::disk('s3')->url($path);
```

`put`メソッドの第3引数に`public`を指定しており、ファイルをパブリックファイルとしてアップロードしようとしています。`public`を指定せずにアップロードしても、アップロードはできても参照ができないため、意味がありません。

# 対処方法
AWS管理コンソールにサインインし、Amazon S3のバケット管理画面にアクセスします。

パブリックアクセス設定機能を以下のように変更します。

![public_access](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417570/412f7c93-6492-a774-a20d-220305b56b66.png)

- パブリックアクセスをすべてブロック：**オフ**
 - 新しいアクセスコントロールリスト (ACL) を介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする：**オフ**
 - 任意のアクセスコントロールリスト (ACL) を介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする：**オフ**
 - 新規のパブリックバケットポリシーまたはアクセスポイントポリシーを介して付与されたバケットとオブジェクトへのパブリックアクセスをブロックする：**オン**
 - 任意のパブリックバケットポリシーまたはアクセスポイントポリシーを介したバケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックする：**オン**
 
 正直、日本語がわかりにくいのでどれをオフにしたらいいかよくわかりません…😅
 
 パブリックアクセス設定機能はセキュリティを高める設定のため、オフにするのは最小限にとどめたほうがいいです。
 
 私の場合は上記のような設定でアップロードできるようになりました。
 
# まとめ
LaravelでAmazon S3へアップロードする方法を解説している記事はたくさんありますが、いずれもAWSのセキュリティ設定が変更される前の記事のため、手順通り設定しているのにアップロードできないという事態に陥ることが多いと思います。

Amazon S3へのアップロードができないという人は参考にしていただければと思います。
QGISでベクタータイルの生成・スタイリング「MBStyleGenerator」

## はじめに
以前にQiitaで読んだ、ベクタータイルのGitHubでのホスティングに関する記事がきっかけでした（URLは見失いました）。内容は、タイル化されたベクターデータはひとつのファイルのサイズが非常に小さく、GitHubによるホスティングに適している、との事。確かになぁと思いました。これはつまり、誰しもがベクタータイルを配信出来るという事です。それぞれが持っている大小問わないデータを、タイル化さえ出来ればリポジトリに置いておくだけで、誰しもが活用出来るわけです。実現すればとても素晴らしいなと思いました。
一方ベクタータイルの作成はハードルが高いとも感じており（難しいというよりは環境構築などの手間が障壁になる）、ベクタータイルの作成・配信が簡単に出来たらその状況が実現するかもなぁ、というぼんやりした考えがありました。
そんななか、以前紹介しましたがVectorTilesReaderというプラグインを知りました（以下VTReader）。これは、既にタイル化されホスティングされているベクターデータをQGISで読み込むプラグインです。じゃあ逆に、QGISのレイヤーをベクタータイルとして手軽にエクスポート出来れば便利では、と考え作成したのが「MBStyleGenerator」です。

## MBStyleGenerator
https://github.com/Kanahiro/MBStyleGenerator
https://plugins.qgis.org/plugins/MBStyleGenerator-master/

#### Sample
<img width="1435" alt="sample.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/534156e9-29a2-529d-37f1-2c2da52604a5.png">

このスクリーンショットは、QGIS上でVTReaderで読み込んだ地理院ベクトルタイルのいくつかのレイヤーをスタイリングしたものと、そのレイヤーを用いてMBStyleGeneratorで作成したstyle.jsonをMapbox GL JSで読み込んでいるものを並べた画像です（OSMのラスタータイルを背景にOpacity=0.5で表示しています）。QGISとMapboxで、地理院ベクトルタイルが同じようにスタイリングされている事がわかります。

#### 機能
- style.jsonの生成
- バイナリベクタータイル（.pbfファイル群のこと）とstyle.jsonの同時生成

つまり、style.jsonのみを生成するか、バイナリデータも併せて生成するか、どちらかです。いずれのケースでも基本的に、style.jsonをMapboxで読み込むだけで表示出来るようになっています。

####現状「Experimental」です。以下の既知の問題があります。

- Textの表示には対応していない
- 
QGISのサイズ単位はMMにのみ対応
→PointとPixelにも対応しました
- ベクタータイル生成時、maxzoomとminzoomの指定が出来ない（常にmax22, min0）

また現状、QGISのスタイル表現をMapboxに完璧に翻訳出来ている訳ではありません。

#### 仕様
- プロジェクト内で「表示されているすべてのレイヤー」をstyle.json（とバイナリデータ）に書き込みます
- プロジェクト内の各「レイヤー名」が、style.json内layersにおける"source-layer"になります
- プロジェクト内の各「レイヤーID」が、style.json内layersにおける”id”になります
- 幅などのサイズは、ポイントに整数値切り上げで変換されます
- ラスターレイヤーは、xyzタイルのみ対応しています（.tifなどには未対応）
- ラスターレイヤーのスタイリングは透過度（Opacity）のみ対応しています
- MVT Source URLはOptionalですが、空欄の場合はダミーテキストが挿入されるので使用前に手動で入力する必要があります
- バイナリベクタータイルは、表示されているすべてのベクターレイヤーから生成されます
- バイナリベクタータイルは、ベクターレイヤーをすべてEPSG:3857に変換したうえで、生成されます
- バイナリベクタータイルは、tippecanoeとmbutilにより生成され、データの量に応じて処理時間がかかります
- バイナリベクタータイルは、style.jsonと同じディレクトリに生成される「pbf」というディレクトリ内に書き出されます

#### 使い方
1. メニューから起動
<img width="563" alt="スクリーンショット 2020-02-28 16.11.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/5922a4ee-fe31-ecd1-4c9c-e10f50ecee46.png">

2. ダイアログに入力・実行
Runでファイルが出力されます。Make MVT Sourceにチェックを入れると、バイナリベクタータイルを生成します。
<img width="699" alt="スクリーンショット 2020-02-28 16.11.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/d018ff42-3c0b-c7d5-e019-e4eb879cb5e9.png">

3. 指定ディレクトリにstyle.json（とバイナリベクタータイル）が出力される
<img width="652" alt="スクリーンショット 2020-03-03 18.51.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/8ae36791-a9a2-031e-c438-d5907876c17a.png">


## 終わりに
思ったよりもちゃんと形になりました。もう少しブラッシュアップが必要ですが、まず公開してフィードバックを受けつつ修正した方がよいなと考えリリースしました。ちょうど[QGISでベクタータイルをサポートするクラウドファンディングが開始](https://www.lutraconsulting.co.uk/blog/2020/02/24/vectortiles-qgis/)されたところだったので、タイミングの良し悪しは置いといて、ベクタータイルが普及期にある事は間違いありません。今後の動向にも目が離せません。
ブラウザからの先読みリクエスト（Prefetch）はどうやって判定すべきか

# TL;DR
先読みリクエスト(プリフェッチ, Prefetch)はcssだけじゃなくて他のURLにも飛ぶケースがある。
特定のエンドポイントに対して、

 - ユーザーのアクションに起因するGETリクエストの件数をより正確に知りたい
 - 割と重い処理なので余計なリクエストはなるべく処理したくない

上記ニーズがある場合プリフェッチ対策をした方が良い。

# 経緯
あるWebシステムを開発中、テストサーバーに対してどう考えても意図しないGETリクエストがログに入ってきたため、原因について考えた。
調べたところ、ブラウザバーに何かを入力したりした時にブラウザが先読み（プリフェッチ）処理としてGETリクエストを飛ばすことがあるらしく、そのリクエストに関してはヘッダーに固有の値が設定されるため、それぞれのブラウザの仕様に対して検知処理を実装して対応した。

# 各ブラウザごとのプリフェッチを示すヘッダー

## Chrome(Chromium)
`Purpose: prefetch` または `X-Purpose：prefetch`
https://www.chromestatus.com/feature/6247959677108224
https://bugs.chromium.org/p/chromium/issues/detail?id=86175#c65

## Firefox
`X-Moz: prefetch`
https://developer.mozilla.org/ja/docs/Web/HTTP/Link_prefetching_FAQ

## Safari
`X-Purpose: preview`
※ 使ってないという情報もあり

# クローラーやネイティブアプリからのプリフェッチに関して
アトリビューション分析にBotやクローラー対策を行うのはもちろんのことですが、その他にもSNSの広告フィードに対して掲載されたページが事前にBotがキャッシュする場合に、プリフェッチ機能が使われるようです。(thank you @fujitatakesh )
[プリフェッチのしくみとFacebookでの利用方法を理解する | Facebook Businessヘルプセンター](https://www.facebook.com/business/help/1514372351922333/)
> 広告がクリックされると、最初のページはFacebookのキャッシュから読み込まれます。

この場合を考えると、プリフェッチされたくないURLをSNS上での操作で制御できるケースもありますが、Facebook以外でも同様の仕様が取られている場合に、広告のアトリビューション分析をサーバーサイドのHTMLへのGETリクエストを基に計測するのでは不十分なケースがあると考えられるので、javascriptなり何なりでユーザーの起点で計測できるように考慮された設計になっている方が良いと思います。

# 実装例

```java
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;

public static boolean isPrefetchRequest(HttpServletRequest request) {
    return StringUtils.equalsAny(request.getHeader("purpose"), "Prefetch", "prefetch") ||
            StringUtils.equalsAny(request.getHeader("X-moz"), "Prefetch", "prefetch") ||
            StringUtils.equalsAny(request.getHeader("X-Purpose"), "Preview", "preview") ||
            StringUtils.equalsAny(request.getHeader("X-Purpose"), "Prefetch", "prefetch");
}
```

※ Javaの場合ヘッダーのキーを `case insensitive` に評価することと、値は小文字で来ているらしいけど保険を取る目的で大文字小文字を含めて評価しているため上記のような実装になっている→[Link prefetching FAQ - HTTP | MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ)

# 補足
 - 軽く調べた感じではブラウザごとにフワフワしてる仕様なので、変更が入る前提で経過観察する。
    - [Mozilla曰く](https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ) 、

```
もちろん、このリクエストヘッダーはまったく標準化されておらず、将来のMozillaリリースで変更される可能性があります。
Chromeは「X-Purpose：prefetch」または「Purpose：prefetch」ヘッダーを使用します。
```
 - もちろんプリフェッチしたデータが実際にユーザーに表示される可能性もあるので、 `プリフェッチなら200の空ページを返す` みたいな雑な実装は思わぬ副作用となる可能性に注意した方が良い。
    - コンバージョン計測に使うロジックを抜くだけとか、複雑な処理で困ってる時はリロードして下さいって表示するViewを返すようにする（ひどい）とか。

 - ユーザーのプリフェッチによるアクセス負荷の問題は2020/6/11付で慶應義塾大学メディアセンターが以下のようなリリースを出しています。
    - [【注意】ブラウザの先読み機能による大量アクセスについて - 電子ジャーナル・電子ブックを使う - Keio University LibGuides at Keio University Media Center / 慶應義塾大学メディアセンター](https://libguides.lib.keio.ac.jp/e-resource/link-prefetching)
    - この事例に関しては現在、ユーザーに対して「ブラウザのプリフェッチ機能を無効にする」案内を出している状態のようですが、何か進展があればプリフェッチ対策のヒントになるかもしれません。
【Nuxt.js】faviconを設定する（nuxt.config.jsでmetaタグ情報を設定）

- メモ
  - metaタグの情報は、`nuxt.config.js`で設定する
  - favicon画像は`static`フォルダへ配置する
  - `href: "/favicon.ico"`とした場合、`static/favicon.ico`のファイルが呼び出される

```javascript:nuxt.config.js
export default {

  （省略）

  head: {
    title: process.env.npm_package_name || "",
    meta: [
      { charset: "utf-8" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
      {
        hid: "description",
        name: "description",
        content: process.env.npm_package_description || ""
      }
    ],
    link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }]
  },

  （省略）

}
```
AWS MediaLiveチャンネルの停止忘れを防ぐための自動通知設定

# はじめに
AWS MediaLiveを利用していると、
チャンネルを停止するのを忘れてしまうことがあるかと思います。

起動中のままにしておくと、請求が高額になる可能性があります。
今回はそれを防ぐために、RUNNING状態のチャンネルをslackに自動通知させる仕組みを考えました。

#やりたいこと
AWS MediaLiveで、ステータスが「RUNNING」となっているチャンネル情報を
自動的にSlackに通知させる。（どのタイミングで通知するかは任意で設定可能）
#構成図
![ScreenShot 2020-03-04 12.20.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277934/05c766df-d5e0-25c3-e1a9-3f032bd45145.png)
#使用リソース
- CloudWatch Events
- Lambda
- IAM
- SNS

#手順

##SNS
1.　SNSトピックを作成する。
2.　サブスクリプションを作成する。

>・プロトコル：Eメール
>・エンドポイント：任意のメールアドレス

3.　指定したメールアドレスにAWSからメールが届くので、本文中のリンクをクリックして承認する。
>・件名：Subscription Confirmation

4.　ステータスが「確認済み」となっていることを確認する。
![test-topic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277934/1b9d9498-f994-6797-5e17-2f3977abd5d7.png)

##IAM
Lambdaに設定するIAMロールを作成する。

1.　下記のJSONをコピーして、新規にポリシーを作成する。
2.　Lambda用のIAMロールを作成する。

```
{
    "Version": "2012-10-17",
    "Statement": [
    {
        "Effect": "Allow",
        "Action": "medialive:ListChannels",
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": "sns:Publish",
        "Resource": "*"
    },
    {
        "Effect": "Allow",
        "Action": [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
        ],
        "Resource": "*"
    }
    ]
}
```

##Lambda
Lambda関数を作成する。
>・ランタイム：Python3.8
>・タイムアウト：30秒
>・実行ロール：先ほどのIAMロールを設定する

```python:lambda_function.py
import boto3

medialive = boto3.client('medialive')
sns = boto3.client('sns')

def lambda_handler(event, context):
    channels = medialive.list_channels()
    channel_list = []
    
    for Channels in channels['Channels'] :
        if Channels['State'] == 'RUNNING':
            name = Channels['Name']
            id = Channels['Id']
            state = Channels['State']
            channel_list.append("| " + name + " | " + id + " | " + state + " |")

    print('\n'.join(channel_list))

    if channel_list == []:
        pass

    else:
        request = {
            'TopicArn': "<SNSトピックのARN>",
            'Message': ('\n'.join(channel_list)),
            'Subject': "Running MediaLive Channels"
            }
        sns.publish(**request)
```

##CloudWatch Events
CloudWatch Eventsでルールを作成する。
>・イベントソース：スケジュール（タイミングはお好みで）
>・ターゲット：先ほどのLambdaを設定する

##Slack
ここでは説明しませんので、下記のサイト等をご参照ください。

- [Gmailに受信した特定メールのみをSlackに通知する方法](https://reworkstyle.com/it-tool/gmail-slack-notification/)

#出力例
- Slack通知
![slack-sns.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277934/162b8b38-9047-937c-2832-1d50aa94085a.png)

※「RUNNING」状態のMediaLiveチャンネルがない場合、通知は飛びません。
#参考

- [Boto 3 Docs - MediaLive.Client.list_channels](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/medialive.html#MediaLive.Client.list_channels)
- [Boto 3 Docs - SNS.Client.publish](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html#SNS.Client.publish)
実践データ分析　ノック1~10

**ダウンロードファイルはこちらから**
http://www3.shuwasystem.co.jp/support/7980html/5875.html

**環境:Google Colab**
#ノック1：データの読み込み
```ノック1.py
import pandas as pd
customer_master=pd.read_csv("custmor_master.csv")
item_master=pd.read_csv("item_master.csv")
transaction_1=pd.read_csv("transaction_1.csv")
transaction_detail_1=pd.read_csv("transaction_detail_1.csv")
```

#ノック2:データを結合（インデックス数を増やす）
```ノック2.py
transaction_2=pd.read_csv("transaction_2.csv")
transaction=pd.concat([transaction_1,transaction_2],ignore_index=True)
transaction_detail_2=pd.read_csv("transaction_detail_2.csv")
transaction_detail=pd.concat([transaction_detail_1,transaction_detail_2],ignore_index=True)
```

#ノック3,4:データを結合（カラム数を増やす）
```ノック3,4.py
join_data=pd.merge(transaction_detail,transaction[["transaction_id","payment_date","customer_id"]],on="transaction_id",how="left")
join_data=pd.merge(join_data,customer_master,on="customer_id",how="left")
join_data=pd.merge(join_data,item_master,on="item_id",how="left")
```
***howの使い方***
https://www.sejuku.net/blog/76325

#ノック5:必要なデータ列を作る
```ノック5.py
join_data["price"]=join_data["quantity"]*join_data["item_price"]#列の追加
```

#ノック6:データの検算
```ノック6.py
print(join_data["price"].sum())
print(transaction["price"].sum())
#又は
join_data["price"].sum() == transaction["price"].sum()#TRUE/FALSEでもok
```

#ノック7:各種統計量を把握
```ノック7.py
join_data.isnull().sum()#欠損値の数を表示
join_data.describe()#データ件数(count),平均値(mean),標準偏差(std),最小値(min),四分位数(25%,75%),中央値(50%),最大値(max)を表示

#別個で確認すべき期間範囲データ
print(join_data["payment_date"].min())
print(join_data["payment_date"].max())
```

#ノック8:月別でデータ集計
```ノック8.py
join_data["payment_date"]=pd.to_datetime(join_data["payment_date"])#object型→datetime型
join_date["payment_month"]=join_data["payment_date"].dt.strftime("%Y%m")#dtを使って年,月のみを抽出
join_data.groupby("payment_month").sum()["price"]
```

***%Y年%m月%d日%H時%M分***で他の情報も得られる

#ノック9:月別,商品別でデータ集計
```ノック9.py
join_data.groupby(["payment_month","item_name"]).sum()[["price","quantity"]]
pd.pivot_table(join_data,index="item_name",columns="payment_month",values=["price","quantity"],aggfunc="sum")
```
pivot_tableの**indexとcoulumnsは行と列**を指定できる！
**valuesは集計したい数値列**を指定できる！その後、**aggfuncで集計方法をsum**に設定している！
```

#ノック10:商品別の売上推移を可視化
```ノック10.py
graph_data=pd.pivot_table("join_data",index="payment_month",columns="item_name",values="price",aggfunc="sum")
```
```ノック10.py
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(list(graph_data.index),graph_data["PC_A"],label="PC_A")
plt.plot(list(graph_data.index),graph_data["PC_B"],label="PC_B")
plt.plot(list(graph_data.index),graph_data["PC_C"],label="PC_C")
plt.plot(list(graph_data.index),graph_data["PC_D"],label="PC_D")
plt.plot(list(graph_data.index),graph_data["PC_E"],label="PC_E")
# 凡例の表示
plt.legend()
```
***plt.plot(x,y)は折れ線グラフ***
**plt.plotの解説URL**
https://own-search-and-study.xyz/2016/08/08/matplotlib-pyplot%E3%81%AEplot%E3%81%AE%E5%85%A8%E5%BC%95%E6%95%B0%E3%82%92%E4%BD%BF%E3%81%84%E3%81%93%E3%81%AA%E3%81%99/
【Nuxt.js】フォームをコンポーネント化した時のメモ（子Componentで変更されたv-modelの値を親に反映する）

### 概要
- フォームの入力欄をコンポーネント化した時のメモ
- 今回は例として、`components`に作成した`text_box.vue`というファイルを`pages`配下の`index.vue`に呼び出すことを想定したもの
- `text_box.vue`にはラベルと入力欄をセットで登録

### 実際のコード
- 入力値`input_text`を`export`する

```vuejs:components/TextBox.vue
<template>
  <div>
    <p>ラベル名</p>
    <input type="text" v-model="input_text" />
  </div>
</template>

<script>
export default {
  model: {
    prop: "input",  // 親側で"input"のbindをv-model`input_text`から行えるようになる
    event: "change"  // 変更というイベント
  },
  props: {
    input: String,  // input_textのデータ型を設定
  },
  // 子から親への変更を反映させるため算出プロパティを定義
  computed: {
    input_text: {
      // input_textの値を取得
      get() {
        return this.input  // ゲッターには戻り値が必要 modelプロパティで指定した"input"を戻り値とする
      },
      // input_textが変更されると以下が動作
      set(value) {
        this.$emit("change", value)  // modelプロパティのeventで指定されたイベント"change"を$emitする（親にデータを渡す）
      }
    }
  }
}
</script>
```

- `text_box.vue`コンポーネントを`import`して`input_text`を呼び出す

```pages/index.vue
<template>
  <section>
    <div class="input_form">
      <InputText v-model="input_text" />
      <button @click="submit">送信</button>
    </div>
  </section>
</template>

<script>
import InputText from "~/components/TextBox.vue"

export default {
  components: {
    InputText
  },
  data() {
    return {
      input_text: null  // 初期値を設定
    }
  },
  methods: {
    submit() {
      axios
        .post(url, input_text)  // urlにはAPIのパスを指定 入力値`input_text`をPOST
        .then(res => {
          // 処理
        })
        .catch(e => {
          // 処理
        })
    }
  }
}
</script>
```

### 参照
- [Vue.js v-modelなComponent](https://waino.hateblo.jp/entry/2018/10/22/115853)
グローバルIPがないLANでOpenVPNを使い外部から接続する

タイトルが分かりにくいですがつまりこういうことです。
![nw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155364/145078c5-2535-7a72-69a5-5119c855f244.png)

グローバルIPが付与されていないルーターの配下にあるネットワークに(RaspberryPiなどを使い)OpenVPNのクライアントを置くことでOpenVPNサーバー経由でインターネットからLAN内の端末にアクセスできるようになります。

(ということなのでインターネット上にOpenVPNのサーバを用意できることが前提となります。)

# TL;DR
OpenVPNの基本的な設定は終わっているものとします。

``` config:/etc/openvpn/server.conf
# 下記を追記する
# "192.168.1.0"と"255.255.255.0"は適当なものに変更する
push "route 192.168.1.0 255.255.255.0"

client-config-dir ccd
route 192.168.1.0 255.255.255.0
```
raspiはクライアント名にあわせる

/etc/openvpn/ccd/raspi

``` config:/etc/openvpn/ccd/raspi
iroute 192.168.1.0 255.255.255.0
```

# 解説
実際にやっていることはそんなに難しいことではありません。
ルーティング設定を追加しているだけです。

``` config:/etc/openvpn/server.conf
push "route 192.168.1.0 255.255.255.0"

client-config-dir ccd
route 192.168.1.0 255.255.255.0
```
`push "route 192.168.1.0 255.255.255.0"`はクライアントに設定を追加するように通知するものです。
192.168.1.0/24のネットワークがVPNにあることを教えています。

`client-config-dir ccd`はクライアントごとの設定を適用する際に使用します。クライアントが接続してきた時にccdというディレクトリ内にクライアントと同じ名前のファイルがあれば追加でそれも読み込みます。

`route 192.168.1.0 255.255.255.0`はVPNに192.168.1.0/24のネットワークがあることを教えています。

``` config:/etc/openvpn/ccd/raspi
iroute 192.168.1.0 255.255.255.0
```
`iroute 192.168.1.0 255.255.255.0`は192.168.1.0/24のトラフィックをクライアントに流す設定です。

`route`はクライアントからサーバーへ、`iroute`はサーバーからクライアントへのルーティングを設定します。

# あとがき
今回は自分が使用している設定を参考にしました。
設定したときの記憶がもうないのですがおそらくうまくいくと思います。
もし間違いなどがあればご指摘いただければ幸いです。

# Reference
- https://www.openvpn.jp/document/how-to/
gitコマンドをカスタマイズ。


とりあえず、gitの設定を見てみましょう。

```
$ git config -l
~
alias.s=status
alias.a=!git add . && git status
alias.au=!git add -u . && git status
alias.aa=!git add . && git add -u . && git status
alias.c=commit
alias.cm=commit -m
alias.ca=commit --amend
alias.ac=!git add . && git commit
alias.acm=!git add . && git commit -m
~
```
　
alias：エイリアスとは、置き換え、別名という意味。
例えば、一番下 "alias.acm=!git add . && git commit -m" の設定により、

```
$git add . && git commit -m
$git acm // 同義
```

で呼び出せるようになります。これは楽チンだ。

##自分で設定したい
新しいパラメータを設定・既存のパラメータを修正するには

```
$git config [パラメータ] [値]
```
が使えます。例えば、checkoutって打ちづらいなと最近よく思うので、

```
$git config alias.ch checkout // -> alias.ch=checkout　を追加

//これにより以下でチェックアウトできる
$git ch [ブランチ名]
```
いいぞ、alias。
CentOS7/ LetsEncrypt更新エラー / certbot renew でpyOpenSSLのインポートエラー "Try upgrading to v0.14 or newer"

# 背景

`#yum update`した時にPythonパッケージでエラーが出たのでごちゃごちゃしていたらいつの間にか`LetsEncrypt`が使えなくなりました。具体的には`certbot renew`で以下のエラー

```
ImportError: 'pyOpenSSL' module missing required functionality. Try upgrading to v0.14 or newer.
```

`pyOpenSSL`はyumで`certbot`をインストールした時に自動的にインストールされていてバージョンは`0.13.1`
これをアップデートをすればいいのですが、、、てこづりました。

# 対処方法/NG版

まずpipコマンドを使ってアップグレードしようと思いましたが、「削除できない」エラーが発生します。
削除してからインストールしようとしても削除できません。

試したコマンド

```
# pip uninstall pyOpenSSL
# pip install --upgrade --force-reinstall pyOpenSSL
```

エラー内容

```bash
ERROR: Cannot uninstall 'pyOpenSSL'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
```

次に`yum remove pyOpenSSL`を試しますが依存関係のあるライブラリ全て(certbot等)も削除されてしまいます・・・。


# 対処方法/OK版

yumでインストールした`pyOpenSSL`だけを削除するには`rpm`コマンドを使います。

```
rpm -e --nodeps pyOpenSSL
```

この後に`pip install pyOpenSSL`でインストール。解決しました。
Rust + Entity Component System で仕様変更に強いゲーム設計 その５－２～ やられアニメーション

## 目次

[その１ 〜 序文](https://qiita.com/mas-yo/items/e7d1e247e5d4a9f47e92)
[その２ 〜 キャラの移動](https://qiita.com/mas-yo/items/207244f04dd442cb567c)
[その３−１ 〜 コンポーネントの設計](https://qiita.com/mas-yo/items/e1f89bea5f84b7041462)
[その３−２ 〜 システムの設計](https://qiita.com/mas-yo/items/ca97fe6ed961d77be715)
[その３−３ 〜 メイン部分](https://qiita.com/mas-yo/items/9f0459dd942a874c6273)
[その４−１ 〜 剣を表示](https://qiita.com/mas-yo/items/daae411935d138d85c95)
[その４−２ 〜 アニメーションコンポーネント](https://qiita.com/mas-yo/items/678d775e4caf5e14abe7)
[その４－３ 〜 アニメーションを動かす](https://qiita.com/mas-yo/items/3db0a920cb76c75bca51)
[その５－１～ あたり判定](https://qiita.com/mas-yo/items/598ed52f5c08a0375ead)
【イマココ】[その５－２～ やられアニメーション](https://qiita.com/mas-yo/items/cf017ac1ebdd7ce25d86)
[その６ 〜 これまでの振り返り](https://qiita.com/mas-yo/items/22ead1dc226da4df54e2)

## やられアニメーション

それでは、攻撃を受けたときに再生される、やられアニメーションを実装しています。
今回のやられアニメーションは、一定距離、後ろに下がる、というものです。

やられアニメーションの特殊な点は、「アニメーションに伴う移動」を実装する必要がある、ということです。
なぜなら、アニメーションデータ自体はデザイナーが作成するものですが、それをそのまま再生するだけでは、ゲームロジック内で管理されているキャラの位置情報が変化しないからです。
アニメーションに仕込まれた「移動量」にしたがって、ゲームロジック上の位置も変化させなければなりません。


そのために、`CharacterAnimFrame`に、`move_forward`というフィールドを追加しました。
そのフレームでの移動量（前方がプラス方向）を表します。

```Rust
#[derive(Default, Clone)]
pub(crate) struct CharacterAnimFrame {
    pub radius_scale: f32,
    pub weapon_direction: f32,
    pub move_forward: f32,  //追加
}
```

システム側では、アニメーション中にこの値があったら、キャラの`Velocity`を上書きします。


```Rust
impl SystemProcess
    for System<CContainer<Velocity>, (&CContainer<CharacterView>, &CContainer<CharacterAnimator>)>
{
    fn process(velocities: &mut Self::Update, (views, animators): &Self::Refer) {
        velocities
            .iter_mut()
            .zip_entity2(views, animators)
            .for_each(|(_, velocity, view, animator)| {
                if let Some(val) = animator.value() {
                    if val.move_forward != 0f32 {
                        velocity.x = view.direction.cos() * val.move_forward;
                        velocity.y = view.direction.sin() * val.move_forward;
                    }
                }
            });
    }
}
```

さて、これまでで、`Velocity`を更新するシステムは、３つ作られました。

1. キー入力`Input`に従って`Velocity`を更新するシステム
1. AIによって決められた`MoveTarget`に従って`Velocity`を更新するシステム
1. アニメーションに従って`Velocity`を更新するシステム

1はプレイヤーキャラ用、2は敵用、3はプレイヤー、敵どちらも使います。
アニメーションによる移動は、キー入力や敵AIによる移動より、優先されなければなりません。
（やられ中は操作できない、という仕様にしたいので）
よって、これらを呼ぶ順番は

```Rust
        System::process(&mut self.velocities, &self.inputs);
        System::process(&mut self.velocities, &(&self.positions, &self.move_targets));
        System::process(
            &mut self.velocities,
            &(&self.character_views, &self.character_animators),
        );

```

この様に、最後にアニメーションによる更新を呼ぶようにします。

次回は、これまで作ったコンポーネントやシステムを、一度振り返って、オブジェクト指向による設計とどう違うか、見てみたいと思います。
Lambda から S3 を使う (Python3)

実行ロールには、S3 へのアクセス権がついている必要があります。

バケットの一覧

```py:list_buckets.py
# ------------------------------------------------------------------
import json
import boto3
print('Loading function')
s3 = boto3.resource('s3')

def lambda_handler(event, context):
    print("*** start ***")
#
    it = 1
 #
    for bucket in s3.buckets.all():
        print(str(it) + ':' + bucket.name)
        it +=  1
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

# ------------------------------------------------------------------
```

バケット内のファイルの一覧

```py:list_files.py
import json
import boto3
print('Loading function')
s3 = boto3.resource('s3')

def lambda_handler(event, context):
    print("*** start ***")
#
    bucket_name = "bucket01"

    my_bucket = s3.Bucket(bucket_name)
    for object in my_bucket.objects.all():
       print(object)
       print(object.key)
#
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

ファイルの作成

```py:s3_put.py

import json
import boto3
print('Loading function')
s3 = boto3.resource('s3')

def lambda_handler(event, context):
    print("*** start ***")

    bucket_name = "bucket01"

    json_key = "sample8.json"
    obj = s3.Object(bucket_name,json_key)

    test_json = {'key': 'valueaaa',
        'key2': 'Hello'}

    data = json.dumps(test_json).encode('utf-8')
    r = obj.put(Body = data)
    
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```
ウィザード形式でのユーザー登録の仕方 

この記事は自分用のアウトプットになります。
プログラミングスクールの最終課題でフリマサイトを作成した際にユーザー登録の時にユーザー情報と住所の登録を同時にする方法になります。

初めにユーザー管理のためにdeviseを持ちます。Gemfileに記述しましょう。

```ruby:Gemfile
gem 'devise'
```
記述をしたらターミナルでrails g devise:install
```ターミナル
rails g devise:install
```
コマンドを実行します。

次にdeviseのデフォルト状態でのログイン機能を実装していきます。

```:taminaru
rails g devise user
```
コマンドを入力するとUserモデルとマイグレーションファイルが作成されます。
作成されたマイグレーションファイルを修正し必要なカラムなどを入力してください
下記のカラムとタイプは参考にしてください　

|Column|Type|Options|
|------|----|-------|
|nickname|string|null:false|
|email|string|null: false、unique:ture|
|password|string|null:false|
|password_confiramation|string|null:false|
|family_name|string|null:false|
|first_name|string|null:false|
|hurigana_family_name|string|null:false|
|hurigana_first_name|string|null:false|
|birth_year|integer|null:false|
|birth_month|integer|null:false|
|birth_date|integer|null:false|
マイグレーションファイルを修正したらrails db:migrateをしましょう
次に、Appilication Controllerを修正します。カラムを追加したためです。

```
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :age])
  end
end
```
コントローラーに記載ができたらモデルに必要なバリデーションを設定しておきましょう

```：User.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  validates :nickname, presence: true
  validates :email, presence: true
  validates :password, presence: true
  validates :family_name, presence: true,format: { with: /\A[一-龥]+\z/, message: 'は漢字を入力してください'}
  validates :first_name, presence: true,format:{ with: /\A[一-龥]+\z/, message: 'は漢字を入力してください'}
  validates :hurigana_family_name, presence: true,format:{ with: /\A[ぁ-んー－]+\z/, message: 'は全角平仮名を入力してください'}
  validates :hurigana_first_name, presence: true,format:{ with: /\A[ぁ-んー－]+\z/, message: 'は全角平仮名を入力してください'}
  validates :birth_year, presence: true
  validates :birth_month, presence: true
  validates :birth_date, presence: true
```

モデルにバリデーションを記載したら次はビューを作成していきます。

```
rails g devise:views
```
コマンドを入力をすることでdeviseに対応したviewsファイルが作成されます。
作成されたviewsファイルからビューを作成しましょう

#こちらはビューファイルと対応するページ

| ファイルパス | 機能 |
|:--|:--|
| sessions/new.html.erb | ログイン画面 |
| registrations/new.html.erb | ユーザ登録画面 |
| registrations/edit.html.erb | プロフィール情報変更画面 |
| passwords/new.html.erb | メール送信画面(パスワード変更用) |
| passwords/edit.html.erb | パスワード変更画面 |
| confirmations/new.html.erb | メールによる認証を行う画面 |
| unlocks/new.html.erb | アカウントのアンロック画面 |

ひとまずここまでで終わります。
続きは次の記事に記載します。
Kustomizeでyamlを個別に出力する


  
Kustomizeで生成するyamlを個別に確認したかったので、試してみました。

### ディレクトリ構造

```shell
├── base
│   ├── deployment.yaml
│   ├── ingress.yaml
│   ├── kustomization.yaml
└── overlays
    ├── develop
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    └── production
        ├── deployment.yaml
        └── kustomization.yaml
```

### 通常

以下のように感じで生成するとyamlが全て結合された状態（サンプルだとdeploymentとingress）で表示されると思います。

```shell
kubectl kustomize overlays/develop
```

### 個別に出力

kubectlに統合されていない、kustomizeを使って以下のように-oオプションにて出力ディレクトリを指定するとyamlが個別にファイル出力されます。

```shell
mkdir
kustomize build overlays/develop -o build

$ ls -1 build
apps_v1_deployment_xxxx.yaml
extensions_v1beta1_ingress_xxxx.yaml
```

目的は達成しましたが、素のkustomizeを使用するのが面倒ですね‥
kubectlに統合されたコマンドにて同様の方法をご存知の方がいれば教えて頂きたいです ！！

## さいごに

ZEROBILLBANKでは一緒に働く仲間を募集中です。
[ZEROBILLBANK JAPAN Inc.](https://zerobillbank.com/%e3%81%8a%e5%95%8f%e3%81%84%e5%90%88%e3%82%8f%e3%81%9b/)
gitは環境によってリポジトリのファイル名の大文字小文字の検出オプション(core.ignorecase)をtrueで初期化することがある

タイトルの通りなのですが、gitは環境によって、`git init`や`git clone`時に、リポジトリのファイル名の大文字小文字の検出オプション(`core.ignorecase`)を`true`にすることがあります。

おそらく大文字小文字を区別しないファイルシステムだと`core.ignorecase=true`にするようです。

さっそく、本当なのか見てみましょう。

## macOS の場合 (APFS 大文字小文字区別なし)

```sh
# 環境作成
$ mkdir gittest
$ cd !$
$ git init .

# テスト。たしかに true になっている
$ git config -l --local | grep ignorecase
core.ignorecase=true

# 設定ファイルも見てみる。たしかに設定されている。
$ grep -Hn ignorecase .git/config
.git/config:6:	ignorecase = true
```

## Ubuntuの場合 (ext4 大文字小文字区別あり)

```sh
# Vagratで環境セットアップ
$ vagrant init ubuntu/trusty64
$ vagrant up 
$ vagrant ssh

$ sudo -i
$ apt-get update && apt-get install -y git
$ mkdir gittest
$ cd !$
$ git init .

# テスト。ignorecaseの記述はない。
$ git config -l --local | grep ignorecase

# 設定ファイルにもない
$ cat ~/.git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
```

(Linuxでケースインセンシティブなファイルシステムの場合は未検証)

## Windowsの場合 (NTFS 大文字小文字区別なし)

```bat
REM 環境作成
$ mkdir gittest
$ git init .

REM テスト。たしかに true になっている。
$ git config -l --local | find "ignorecase"
core.ignorecase=true

REM （設定ファイルは省略）
```

## Special Thanks


- https://qiita.com/sawadashota/items/aa312a3b7e2403448efe
- <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これのデフォルトってファイルシステム（OS）依存だったりしませんか？</p>&mdash; はぇ～☆ (@haxe) <a href="https://twitter.com/haxe/status/1235034446455177216?ref_src=twsrc%5Etfw">March 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
javascriptからURLスキームを利用してAndroidアプリを起動し、パラメータを与える方法

##書いた経緯
javascriptからURLスキームを利用して、パラメータを与えつつAndroidアプリを開く方法が割と見つからなかったため

##はじめに
単純にAndroidアプリをJSから開く方法は他で解説が沢山あったので、詳しくは書かない。

##普通に開く（パラメータなし）

```
intent://' + [ホスト名] + '/#Intent;scheme=' + [スキーム名] + ';package=' + [アンドロイドパッケージ名] + ';category=android.intent.category.BROWSABLE;action=android.intent.action.VIEW;end'
```
※上記[]の箇所を変数化して、そのまま代入すれば動くはず。

##パラメータを与えつつ開く
```
intent://' + [ホスト名] + '/#Intent;scheme=' + [スキーム名] + ';package=' + [アンドロイドパッケージ名] + ';category=android.intent.category.BROWSABLE;action=android.intent.action.VIEW;S.url=https://google.com;S.hoge=hogehoge;end'
```
※上記のパラメータは、String型での受け渡しとなるので、'S.'と指定している。
　'型'.'受け渡し変数名'='パラメータ'で受け渡し可能となる。

| 型 | 記述方法 |
|:------------|:----|
| String | 'S' |
| Boolean | 'B' |
| Byte | 'b' |
| Character | 'C' |
| Duyble | 'd' |
| Float | 'f' |
| Integer | 'i' |
| Long | 'l' |
| Short | 's' |

私は'S'しか試していないですが、多分他も動くんじゃないですかね？（他人事）
Flutter admob_flutter iOS実機で、errorCode 1 が表示される


Flutterにて、admob_flutterを使用し、バナーをiOS実機にて表示した際、このようなエラーが出力された。

```
Admob banner failed to load. Error code: 1
```

Xcodeで **Bundle Identifier** を、AdMob正しいものに設定することで解決。

ずっとシュミレータでテストしていたため、適当なBundle Identifierになっていたのを
正しいものに変更した。

```
com.example.abc → jp．quq.abc
```

AdmobInterstitial の方は、エラーが無く表示されたためモヤっとはするのだが解決。

コードは、[wiki](https://github.com/kmcgill88/admob_flutter/wiki) で紹介されている バナー（Banner Ads）用のコードをそのまま使用していた。

```dart
AdmobBanner(
  adUnitId: getBannerAdUnitId(),
  adSize: AdmobBannerSize.BANNER,
  listener: (AdmobAdEvent event, Map<String, dynamic> args) {
    switch (event) {
      case AdmobAdEvent.loaded:
        print('Admob banner loaded!');
        break;

      case AdmobAdEvent.opened:
        print('Admob banner opened!');
        break;

      case AdmobAdEvent.closed:
        print('Admob banner closed!');
        break;

      case AdmobAdEvent.failedToLoad:
        print('Admob banner failed to load. Error code: ${args['errorCode']}');
        break;
    }
  }
)
```
YOLO Pythonラッパークラス

# yolo34py

YOLOのPythonラッパークラスを見つけたため、備忘録。
以下に要件を転記する。

- Python3.5(3.7で動作を確認）
- Python3-Dev(sudo apt-get install python3-dev）
- Numpy pip3 install numpy
- Cython pip3 install cython
- Optionally, OpenCV 3.x with Python bindings. (Tested on OpenCV 3.4.0)


python3 setup.py build_ext --inplace

# keras-yolo3

kerasおよびtensorflowベースのyolo実装。Windowsでも動作する。
Lambda から S3 を使う (Node.js)

バケットの一覧

```js:list_buckets.js
'use strict'

const AWS = require('aws-sdk')
var s3 = new AWS.S3()

exports.handler = async (event) => {

console.log("*** start ***")

try {
       var data = await s3.listBuckets().promise()
       console.log(data.Buckets)
    } catch (ee) {
       console.log(ee)
    }

    const response = {
        statusCode: 200,
    }
    
    return response
}
```

バケット内のファイルの一覧

```js:list_files.js
// ---------------------------------------------------------------

var AWS = require('aws-sdk')
var s3 = new AWS.S3()

exports.handler = async (event) => {
	console.log("*** start ***")

	const bucket = 'bucket01'
	var params = { Bucket: bucket }

try {
       var s3Objects = await s3.listObjectsV2(params).promise();
       console.log(s3Objects)
    } catch (ee) {
       console.log(ee)
    }

    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    }

	return response
}

// ---------------------------------------------------------------
```

ファイルの作成

```js:s3_put.js
console.log('Loading function');

const aws = require('aws-sdk');


const s3 = new aws.S3();

exports.handler = async (event, context) => {

console.log ("*** start *** ")

    const bucket = 'bucket01'
  
    const key = 'Hello6.txt'

    
    var str_data = 'Good Morning\n'
str_data += 'Good Afternoon\n'
str_data += 'Good Evening\n'
str_data += 'Good Night\n'

console.log(str_data)
console.log ("REGION: " + process.env.AWS_REGION)

    const params = {
        Bucket: bucket,
        Key: key,
        Body: str_data,
    }
    
    try
    {   
   var data = await s3.putObject (params).promise()
   console.log(data)
       console.log("*** s3.putObject ***")
 
}
catch (error)
		{
		console.log ("*** error *** s3.putObject ***")
		console.log (error)
		}

    
 console.log ("*** end *** ")  
}
```
SQLトレースと拡張イベントのイベントの対応を取得する

# 動機
SQL Server で拡張イベントのセッションを設定中に「SQLトレースでいうあのイベントがないな…」という場面が稀によくあります。
具体的には `SP:Completed` に該当するイベントが見つからなくて困っていました。

# 調査方法
そんなときは下記記事を参考にイベントの対応を確認するクエリを実行しましょう。
[SQL トレースのイベント クラスと等価な拡張イベントを確認する](https://docs.microsoft.com/ja-jp/sql/relational-databases/extended-events/view-the-extended-events-equivalents-to-sql-trace-event-classes)

## クエリ
```sql
USE MASTER;
GO
SELECT DISTINCT
    tb.trace_event_id,
    te.name            AS 'Event Class',
    em.package_name    AS 'Package',
    em.xe_event_name   AS 'XEvent Name',
    tb.trace_column_id,
    tc.name            AS 'SQL Trace Column',
    am.xe_action_name  AS 'Extended Events action'
FROM
              sys.trace_events         te
    LEFT JOIN sys.trace_xe_event_map   em ON te.trace_event_id  = em.trace_event_id
    LEFT JOIN sys.trace_event_bindings tb ON em.trace_event_id  = tb.trace_event_id
    LEFT JOIN sys.trace_columns        tc ON tb.trace_column_id = tc.trace_column_id
    LEFT JOIN sys.trace_xe_action_map  am ON tc.trace_column_id = am.trace_column_id
ORDER BY te.name, tc.name
```

## 結果(一部抜粋)

| trace_event_id | Event Class | Package | XEvent Name | trace_column_id | SQL Trace Column | Extended Events action |
|----------------|--------------|-----------|-------------|-----------------|------------------|------------------------|
| 43 | SP:Completed | sqlserver | module_end | 1 | TextData | NULL |
| 43 | SP:Completed | sqlserver | module_end | 2 | BinaryData | NULL |
| 43 | SP:Completed | sqlserver | module_end | 3 | DatabaseID | database_id |
| 43 | SP:Completed | sqlserver | module_end | 4 | TransactionID | transaction_id |
| 43 | SP:Completed | sqlserver | module_end | 5 | LineNumber | tsql_frame |
| 43 | SP:Completed | sqlserver | module_end | 6 | NTUserName | nt_username |
| 43 | SP:Completed | sqlserver | module_end | 7 | NTDomainName | nt_username |
| 43 | SP:Completed | sqlserver | module_end | 8 | HostName | client_hostname |
| 43 | SP:Completed | sqlserver | module_end | 9 | ClientProcessID | client_pid |
| 43 | SP:Completed | sqlserver | module_end | 10 | ApplicationName | client_app_name |
| 43 | SP:Completed | sqlserver | module_end | 11 | LoginName | server_principal_name |
| 43 | SP:Completed | sqlserver | module_end | 12 | SPID | session_id |

SQLトレースでいう `SP:Completed` を拡張イベントで取得するには、`module_end` で取得すればいいことが分かりました。

# 余談
イベントを探す目的では、セッションを作成する際にフィルターをかけても探せます。
`RPC:Completed` は `rpc_completed` としてあるし、
`SP:StmtCompleted` は `sp_statement_completed` としてあるし、
`SP:Completed` は　`sp_completed` だよねーと思ったらまさかの `module_end` だったのでないものと思い込んでました、反省。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41133/57be2570-428d-af43-1318-815b2a693aaa.png)
System.FSNotifyでファイルイベントが一切検出されない時



解決策: `withManager`を実行しているスレッド__自体を__起動させ続ける

haddockの[例プログラム](https://hackage.haskell.org/package/fsnotify-0.3.0.1/docs/System-FSNotify.html)のコメントに

> -- start a watching job (in the background)

と書かれているので「これ自身で`forkIO`してくれている」と思ってしまいがちだ(思ってしまった)が、
__それを実行している側のスレッドも生かしておく必要がある__らしい。
自分の場合、メインスレッドでは他のプログラムを動かしていたので、`forever $ threadDelay 10000`を抜かしてしまいこれにはまった。
なので、同じように__メインスレッドは別でやることがある場合__以下のように`forkIO`してあげるといい。

```haskell
watchUpdate ... = forkIO . withManager . \mng -> do
    -- `watchDir`等で監視する
    forever $ threadDelay 10000
```

ちなみに、自分が使った例は[こちら(github: Cj-bc/brick-shgif)](https://github.com/Cj-bc/brick-shgif/blob/d4b2606ca9a7b185eb792b1095b7e500c2ccf504/app/shgifViewer.hs#L103-L121)


```haskell
-- | Watch specified file and send FileUpdated event
-- when it's updated.
watchUpdate :: BChan CustomEvent -> FilePath -> IO ()
watchUpdate ch fn = void . forkIO . withManager $ \mng -> do
    watchDir
        mng
        (takeDirectory fn)
        isWatchedFile
        sendEvent
    forever $ threadDelay 10000
    where
        isWatchedFile :: FSN.Event -> Bool
        isWatchedFile e = case e of
                 Modified fn _ _ -> True
                 otherwise       -> False

        sendEvent :: FSN.Event -> IO ()
        sendEvent = \_ -> writeBChan ch FileUpdated
```
Databricks Named a Leader in Gartner Magic Quadrant for Data Science and Machine Learning Platforms　翻訳してみた

##はじめに
今年の2月17日に Business Wire に掲載されました「Databricks Named a Leader in Gartner Magic Quadrant for Data Science and Machine Learning Platforms」の記事を翻訳してまとめたものを、紹介したいと思います。

本記事のリンクと、Gartner Magic Quadrant for Data Science and Machine Learning Platforms に関するリンクは下記参照でお願いします。

■リンク

- [Databricks Named a Leader in Gartner Magic Quadrant for Data Science and Machine Learning Platforms](https://www.businesswire.com/news/home/20200217005397/en/Databricks-Named-Leader-Gartner-Magic-Quadrant-Data)

- [Gartner Magic Quadrant for Data Science and Machine Learning Platforms](https://databricks.com/p/whitepaper/gartner-magic-quadrant-2020-data-science-machine-learning)

##記事概要
この度、Databricks は Gartner Magic Quadrant for Data Science and Machine Learning Platforms におけるリーダに指名されました。Databricks の Unified Data Analytics Platform は、組織がビジネス・インテリジェンスと機械学習の為に用意した自分たちの全ビッグデータ・従来のデータに対して、1つのプラットフォームからアクセスすることを可能とします。

共同創設者であり、Databricks の CEO である Ali Ghods 氏曰く、
「データサイエンスと機械学習はどの組織においても一番の課題でしょう。創業100年の伝統的な企業も、データドリブンなスタートアップ企業に対して優位性を保つために、ハイテク企業に変わろうとしています。私達は Gartner Magic Quadrant のリーダとして、この動きを今年のその他の認識に加えて、統合データ解析の重要拡大の成長を示す明らかなサインと見ております。」


Databricks はオープンソースコミュニティにおいて、目覚ましい貢献を果たしてきました。Apache Spark の開発の他にも、Delta Lake、MLflow、Koalas などの発展にも貢献したことで、Databricks は、オープンソースのコミュニティにおけるイノベータと評されるようになりました。

DatabrickｓはSeries F の資金調達を終えたことで勢いを維持し、自社の資金調達額は9億ドルにも昇りました。この資本金は世界規模でイノベーションの加速と拡大を目的としており、Databricks の価値を62億ドルと評しております。Nielsen、Hotels.com、Overstock、Bechtel、Shell、HP など、多くのグローバル企業がDatabricks を活用してデータサイエンスとデータエンジニアのチームの統合を実施しております。

Gartner のレポートでは、16のベンダーに対して Magic Quadrant for Data Science and Machine Learning Platforms のビジョンにおける実行力と完遂能力を評価しました。

■Gartner のビジョン完遂の軸となる8つの評価基準

- 市場の理解
- マーケティング戦略
- 販売戦略
- 商品の提供戦略
- ビジネスモデル
- 垂直/業界戦略
- イノベーション
- 地理的戦略

■Gartner のビジョン実行の軸となる7つの評価基準

- 商品/サービス
- 実行可能性
- 販売実行/販売価格
- 市場の対応/記録
- 市場の実効性
- 顧客経験
- オペレーション


##おわりに
Databricks Named a Leader in Gartner Magic Quadrant for Data Science and Machine Learning Platforms の翻訳については以上となります。
原文の記事の方も、ぜひ参照してみてください。
【vagrant】共有フォルダの設定

railsでアプリを作るためにドットインストールのvagran動画を参考に仮想環境を作成している最中です。
vagrantで共有フォルダがないと仮想環境とローカルでディレクトリが同期されず、どこに何のファイルが
あるのか分からなくなるので、その設定方法をメモしました。
（※ドットインストールでは特に共有フォルダを手動で設定するような動画は見つからなかったため、
　理由は分かりませんが、共有フォルダが無いまたは機能していないのはイレギュラーな現象のようです。）

version
・ubuntu - 12.04.5 LTS


vagrant initの後、vagrantfileをエディタで開くと、以下の１行があります。

```
# config.vm.synced_folder ".", "/vagrant"
```

「”.”」がホストOSで共有フォルダとして指定されているディレクトリで、
「”/vagrant”」がゲストOSで共有フォルダとして指定されているディレクトリを示しています。


ホストOSとゲストOSの共有フォルダを以下のディレクトに指定し直して、行の頭にある「＃」を外します。

```
config.vm.synced_folder "/Users/*****/Desktop/Myubuntu/rails_app", "/vagrant/rails_app"
```
上書き保存をして、

```
vagrant reload
```
以上で、共有フォルダを設定することができました。
Pythonでgoogle spreadsheetを取得するためにやること

下のリンクに従って、デモ動かしえ、spreadsheetの値を取得します。
https://developers.google.com/sheets/api/quickstart/python

spreadsheetファイル固有のIDはこちらを参考にして、適当なファイルを設定してください。
https://developers.google.com/sheets/api/guides/concepts

SAMPLE_RANGE_NAME は以下のように設定します。

```py
SAMPLE_RANGE_NAME = 'A2:E'
```
onlineTool > dbdiagram.io > テキストによるDatabaseのテーブル設計

```txt:動作環境
Windows 10 Pro (v1909)
```

### 背景

- Databaseまではいかないが、複数ファイルの関係を整理したい
- そういえば20年前にはDatabaseソフト作っていたなあ。。。
- Tableをオンラインで生成する何かないだろうか


### dbdiagram.io

https://dbdiagram.io

- テキストで編集可能
- コメントも残せる
- Export機能が利用可能
    - PDF Exportは動かない？
- 10ダイアグラムまで (無料版)
    - テキストは別途自分のツールで保存すればいいか。。。
- テキストペインでは入力補完が聞くので使いやすい

### 例

```
Table Crew {
  id int [pk]
  name varchar
  affil_id int [ref: > Affiliation.id]
  shift_id int [ref: > WorkingSchedule.id] 
  friend_id int 
  family_id int [ref: > Family.id]
}

Table Affiliation {
  id int [pk]
  name varchar
}

Table WorkingSchedule {
  id int [pk]
  startHour int // inclusive
  endHour int // exclusive
}

Table Family {
  id int [pK]
  name varcahr
}
```

![{ACDB5AE6-726B-415E-B4C9-0863B6519FD5}.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/d1dcbd45-1134-069b-1b92-9e034fde0d3e.png)
新機能SDFフォントを使ってみる

OpenSiv3D **0.4.2** の新機能、SDFフォントの使用

拡大縮小しても文字がシャープなままなので、それは有効活用できる。

カメラ機能での拡大縮小文字で動作確認

#実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76087/fe98797d-420d-1f60-e624-47cf19a9c97f.png)
#操作して拡大表示
カメラ機能で、マウスの**ホイール**で**拡大**して、**右ドラッグ**で位置調整で、SDFフォントと通常フォントを縦に並べて見てみる
上2行が**SDFフォント**、ぼやけているのが**通常フォント**　SDFフォントは**拡大してもシャープなまま**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76087/649318aa-520b-e97e-4dc6-576633ea3b39.png)




#ソースコード
```Cpp
# include <Siv3D.hpp> // OpenSiv3D v0.4.2

void Main()
{
    Font font(120);
    // ウィンドウ設定
    Window::Resize(1280, 720);
    Scene::Resize(1280, 720);
    System::Update();
    // グリフごとの Distance field のサイズ
    const int32 distanceFieldSize = 60;

    // SDFFont
    const SDFFont sdfFont(distanceFieldSize, Typeface::Bold);

    // 2D カメラ　比率1倍、すなわちピクセルと同じ座標系にする
    Camera2D camera(1);
    const String text = U"いろはにほへとちりぬるを";

    while (System::Update())
    {
        // 2D カメラを更新
        camera.update();
        {
            // 2D カメラの設定から Transformer2D を作成・適用
            const auto t = camera.createTransformer();

            // SDF パラメータの設定
            Graphics2D::SetSDFParameters(sdfFont.pixelRange());
 //           sdfFont(text).draw(40, Vec2(20, 20));
            sdfFont(text).draw(80, Vec2(20, 80));
            sdfFont(text).draw(120, Vec2(20, 180));

            // SDF パラメータの設定、太らせを 0.2 に
            Graphics2D::SetSDFParameters(sdfFont.pixelRange(), 0.2);
            sdfFont(text).draw(120, Vec2(20, 320), Palette::Black);
            // SDF パラメータの設定、太らせを 0.0 に
            Graphics2D::SetSDFParameters(sdfFont.pixelRange(), 0.0);
            sdfFont(text).draw(120, Vec2(20, 320));

            font(text).draw(0,500);
        }
        // 2D カメラ操作の UI を表示
        camera.draw(Palette::Orange);
    }
}
```
Chrome DevToolsのNetworkから特定パスのリクエストを除外したい

昨今のフロントエンドのツールを使っていると開発中はpingを送るためネットワークログが埋まるし流れる。開発中にはほぼ意味のないログなので除外したい。

DevToolsのFilterを使う。`-hoge`などのようにハイフンをつけることで特定の文字つを含んだものを除外できる。また`-domain:example.com`のように指定のプロパティで特定ドメインだけを除外できる。これはよくあるチャットサポートのツールなどを使っているときに使える。

なおハイフンなしの`domain:example.com`にすれば除外ではなく絞り込みになる。

![https://developers.google.com/web/tools/chrome-devtools/network/imgs/filter-text-box.svg](https://developers.google.com/web/tools/chrome-devtools/network/imgs/filter-text-box.svg)

参考: https://developers.google.com/web/tools/chrome-devtools/network/reference#filter

next.jsの場合は`-on-demand-entries-ping`としておけば快適になった
Rails（基礎）

# 最初に

自身で学習してきた内容を書き出していきます。
基本的には自分自身でわかるような内容になりますので、ご容赦ください。
また、誤っている点がありましたら、コメントにてご指摘ください。

（要確認）マークがあるものは、「実行前に必ずググる」べき内容。



# index
- Railsについて
- MVC
  - M(model)
  - V(view)
  - C(controller)
- 設計理念
  - DRY
  - CoC



# Railsについて
Webアプリケーションを開発するためのフレームワーク。
Rubyで作成されている。
今回はRuby on Railsとして話を進める。


# MVC
MVC（Model View Controller：モデルビューコントローラ）という考え方で構成。

### M(model)
アプリケーション固有のデータを扱う部分であり、データベースに対して、データの登録や取得、更新、削除などの処理を行う。

### V(view)
PCの画面（ユーザインタフェース）に関わる部分。ブラウザに表示させるHTMLを実際に組み立てる。

### C(controller)
モデルやビューを統合的に制御する部分。ユーザーからのリクエストを受けて、Modelと連携したり、どの画面（View）を表示するのかといったことを制御する管制塔。

<img width="803" alt="MVC.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591388/39cff966-6faa-b5da-a6d4-78d216b83fd1.png">



# 設計理念
### DRY（Don't Repeat Yourself）
**「Don't Repeat Yourself（同じ記述を繰り返さない）」**

### CoC（Convention over Configuration）
**「Convention over Configuration（設定より規約）」**

# 追加あれば、どんどん更新していきます。
駆け出しとして頑張ります。
Prometheusの設定変更・チェック・ReloadまでAnsibleで一発でやる

Prometheusの設定変更、Alertルールの変更した際にそれ専用のPlaybookを実行するとか面倒すぎる
同じplaybookで一発で反映・チェック・Reloadまでできるようにしてみた

まず、systemdのprometheus.serviceでReloadできるようにしておく

```
[Unit]
Description=Prometheus - Monitoring system and time series database
Documentation=https://prometheus.io/docs/introduction/overview/
After=network-online.target
After=network-online.target

[Service]
User=prometheus
ExecStart=/opt/prometheus/prometheus --config.file=/opt/prometheus/prometheus.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=always

[Install]
WantedBy=multi-user.target
```


Ansibleは

- validateでpromtoolを使ってcheckする
- changedならnotifyでreloadを実行
- tagを追加して単発実行できるようにしておく
- handlerにreloadを作成

と修正

```yaml:task
- name: rule files of Prometheus
  copy:
    src:  "{{ rule }}"
    dest: "{{ root_path }}/prometheus/rules.yml"
    validate: "{{ root_path }}/prometheus/promtool check rules %s"
  notify: Reload Prometheus
  with_items:
    - files/rules.yml
  loop_control:
    loop_var: rule
  tags: rules

- name: Configure Prometheus
  template:
    src:  templates/prometheus.yml.j2
    dest: "{{ root_path }}/prometheus/prometheus.yml"
    validate: "{{ root_path }}/prometheus/promtool check config %s"
  notify: Reload Prometheus
  tags: config
```

```yaml:handler
- name: Reload Prometheus
  service:
    name: prometheus
    state: reloaded
```

ruleを反映させるには `--tag rules` 、config fileの場合は `--tag config` を指定するだけ
playbookも1つで済むし、rulesやconfigが間違っててPrometheus起動できないっていう事故も無くせるので
AWS認定 ソリューションアーキテクト - アソシエイトの旧版と新板を比較してみた。

AWS認定でおそらく最も人気のある「ソリューションアーキテクト - アソシエイト」ですが、2020年3月23日以降より新バージョン(SAA-C02)となります。
https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/

旧バージョン(SAA-C01)を利用できるのは2020年3月22日までなので、どちらを受験するか迷っている人や、すでに認定取得済みの人の為に、どれくらい違うのかを比較してみようと思います。

## 試験範囲の概要
### SAA-C01

| 分野 | 試験における比重 |
| :--| :--: |
| 回復性の高いアーキテクチャを設計する | 34% |
| パフォーマンスに優れたアーキテクチャを定義する | 24% |
| セキュアなアプリケーションおよびアーキテクチャを規定する | 26% |
| コスト最適化アーキテクチャを設計する | 10% |
| オペレーショナルエクセレンスを備えたアーキテクチャを定義する | 6% |
| 合計 | 100% |

### SAA-C02

| 分野 | 試験における比重 |
| :--| :--: |
| レジリエントアーキテクチャの設計 | 30% |
| 高パフォーマンスアーキテクチャの設計 | 28% |
| セキュアなアプリケーションとアーキテクチャの設計 | 24% |
| コスト最適化アーキテクチャの設計 | 18% |
| 合計 | 100% |

分野としては1つ減り、その分各分野の比率が上がった感じになっています。
名前こそ大きく変わったものもありますが、問われる知識は変わりません。

## 比較

| 分野 | SAA-C01の比率 | SAA-C02の比率 | 差 |
| :--| :--: | :--: | :--: |
| 回復性（レジリエント）の高いアーキテクチャを設計する | 34% | 30% | -4% |
| パフォーマンスに優れたアーキテクチャを定義する | 24% | 28% | +4% |
| セキュアなアプリケーションおよびアーキテクチャを規定する | 26% | 24% | -2%
| コスト最適化アーキテクチャを設計する | 10% | 18% | +8% |
| オペレーショナルエクセレンスを備えたアーキテクチャを定義する | 6% | 0% | -6% |

特にコスト最適化の部分に重みが寄った感じになったようです。
この辺りを意識するとよいかもしれません。

### 参考資料
[AWS 認定ソリューションアーキテクト - アソシエイト](https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/)
[SAA-C01試験ガイド](https://d1.awsstatic.com/training-and-certification/docs-sa-assoc/AWS%20Certified%20Solutions%20Architect%20-%20Associate%20Exam%20Guide%20001%20v1.5%20JPN.pdf)
[SAA-C02試験ガイド](https://d1.awsstatic.com/ja_JP/training-and-certification/docs-sa-assoc/AWS-Certified-Solutions-Architect-Associate-Exam-Guide_v1.1_2019_08_27_FINAL.pdf)