OmniAICreator/Qiita-1.07M huggingface dataset:
wsl2でdockerが占有する容量の開放

## docker on wsl2 が容量を開放してくれない
wsl2上でdockerを動かしていると、イメージが肥大して容量を食っていきますが、`docker system prune`でも容量を減らない時は以下を試す。
以下リンク先記事を参考にさせて頂き実行。自分用の備忘録として投稿。

https://7me.nobiki.com/2020/10/12/wsl2-docker-re-use-disk-space/

## 容量開放手順
1. docker終了
2. wsl用いているVScodeなりTerminalなりがあれば終了
3. 以下コマンドをpowershellで順に実行

```ps1
# wslを終了させる呪文。不具合等起きた時も基本これ
> wsl --shutdown

# diskpartを起動する（diskpartのウィンドウが開きます）
> diskpart

# 対象のvhdxファイルを指定（PATHは各自確認）
DISKPART> select vdisk file="C:\Users\username\AppData\Local\Docker\wsl\data\ext4.vhdx"
DISKPART> attach vdisk readonly

# 私の環境の場合はイメージファイルが以下にばらけてました
# C:\Users\username\AppData\Local\Docker\wsl\distro\ext4.vhdx
# C:\Users\username\AppData\Local\Docker\wsl\data\ext4.vhdx
# C:\Users\username\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
# 3つ目のものはdockerでなくubuntuそのものっぽい

# 最適化する
DISKPART> compact vdisk

# 終了
DISKPART> detach vdisk
DISKPART> exit
```
HMSとAndroid OSバージョンについての考察

# 背景
Androidのbuild.gradleのminSdkVersionを16以下に設定し、HMSライブラリを導入すると、ビルド時に次のようなエラーが出てきます。

```text
Manifest merger failed : uses-sdk:minSdkVersion 16 cannot be smaller than version 17 declared in library [com.huawei.hms:hwid:5.2.0.300] xxxxxxxxxxxxxxxxxxxxxxx\AndroidManifest.xml as the library might be using APIs not available in 16
	Suggestion: use a compatible library with a minSdk of at most 16,
		or increase this project's minSdk version to at least 17,
		or use tools:overrideLibrary="com.huawei.android.hms.hwid" to force usage (may lead to runtime failures)
```

minSdkVersionを17以上に引き上げるか、tools:overrideLibraryで強制ビルドするか、どれかにしろと怒られました。

一番簡単な解決策は当然minSdkVersionを17以上に引き上げることですが、サービスの事情でminSdkVersionを変えられない場合、tools:overrideLibraryで対応するしかありません。しかし、"may lead to runtime failures"という警告文で、もしユーザーのAndroid API レベルが16以下だったら、動作中にクラッシュが起きるのではないかと心配するエンジニアが多くいるのではありませんか。

そこで、tools:overrideLibraryという解決策について考察してみました。

# 考察
### HMSアプリの動作条件
当たり前ですが、HMSアプリはHMSが入っている端末でしか動作しません。もっと正しい言い方ですと、<b>HMS Core</b>アプリが入っている端末でしか動作しないということです。

<img width="300" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704548/2a4d7b49-f750-f9c0-54d8-b3bbe49496a8.png">

### HMS Coreアプリのインストール条件
HMS Coreは[AppGallery](https://consumer.huawei.com/jp/mobileservices/appgallery/)からダウンロードできます。[AppGallery](https://consumer.huawei.com/jp/mobileservices/appgallery/)で提供されているバージョンは常に最新版になっています。

ちなみに、現時点（2021-3-24）の最新バージョンは5.2.0.303です。

また、オフィシャルサイトでは、HMS Core 2.xが非推奨となっており、2021年12月31日をもってサポートが終了すると発表されました。

https://developer.huawei.com/consumer/jp/doc/development/HMS-2-Guides/removal_notice

つまり、HMS対応はHMS Core 3.0以上のみ考えればよいということです。
そこで、HMS Core アプリのバージョン3.0～5.0のminSdkVersionをまとめてみました。

|HMS Core アプリのバージョン|minSdkVersion|対応キット数|
|:---:|:---:|:---:|
|3.0|19|[9個](https://developer.huawei.com/consumer/jp/doc/overview/HMS-3-0)|
|4.0|19|[22個](https://developer.huawei.com/consumer/jp/doc/overview/HMS-4-0)|
|5.0|19|[33個](https://developer.huawei.com/consumer/jp/doc/overview/HMS)|

HMS Core 3.0～5.0のminSdkVersionは19になっています。
つまり<b><font color="red">HMSが入っている端末のAndroidのAPIレベルが必ず17以上である</font></b>ことが保証されています。

### 結論
"runtime failures"の条件はユーザーのAndroid APIレベルが16以下であることです。ところが、HMS Coreが入っている端末のAndroid APIレベルが必ず19以上になるので、tools:overrideLibraryで対応しても、"runtime failures"が起きるはずがないという結論が導けます。
[AWS][nodejs] S3にある JPEG ファイルの EXIF 情報を削除して書き戻す

必要な npm パッケージは `aws-sdk`, `file-type`, `piexif`

`S3.getObject` でデータ取得して、`file-type` で JPEG 画像かチェックして、`piexif` で EXIF を削除して、`S3.putObject` で書き戻す感じです。

```javascript
const AWS = require('aws-sdk');
const FyleType = require('file-type');
const piexif = require('piexif');

const removeExif = (region, bucket, key) => {
    const S3 = new AWS.S3({region: region});

    return Promise.resolve({Bucket: bucket, Key: key})
    .then(params => S3.getObject(params).promise())
    .then(data => new Promise(async (resolve, reject) => {
        // mime type のチェック
        const fileInfo = await FyleType.fromBuffer(data.Body);
        if (fileInfo.mime === 'image/jpeg') {
            data.ContentType = fileInfo.mime;
            resolve(data);
        } else {
            reject(Error("It's not jpeg format."));
        }
    }))
    .then(data => new Promise((resolve, reject) => {
        // EXIF 情報の削除
        try {
            const newBody = piexif.remove(data.Body.toString('binary'));
            resolve({
                Bucket: bucket,
                Key: key,
                ContentType: data.ContentType,
                Body: Buffer.from(newBody, 'binary')
            });
        }
        catch (err) {
            reject(err);
        }
    }))
    .then(params => S3.putObject(params).promise())
    .then(data => Promise.resolve(data))
    .catch(err => Promise.reject(err));
}
```

Lambda ファンクションとして動くようにして、PUT のタイミングで EXIF 情報を削除するとか、Lambda@Edge で動くようにして EXIF 情報を秘匿するとかのサンプルにしてください。

Lambda@Edge で画像ファイルに対するレスポンスを編集するためのサンプルとしては HM さんのも参考になるかも
https://github.com/humanmade/tachyon
ssh経由で、画像データなどのバイナリをサーバーに送る

sshからウェブコンテンツなどを編集しているとき、ちょっとした画像をサーバー上に送りたいときがある。もうひとつコンソールを開いてそこからscpしたり、転送用のアプリケーションを起動したりしてもよいが、おっくうだ。数台のサーバーを踏み台にして目的のサーバー上で作業しているときなどは、特にそう。ポートフォワードを使った転送はもちろん知っているけど、やりかたを思い出したりメモを探ったりする手間がかかってしまい、ストレスだ。

というわけで、まず手元に下のようなpython3スクリプトを準備する。ファイル名は、仮に b64ify.py とした。環境はwindows。

```python:b64ify.py
#!py -3
import sys
import base64
fname = sys.argv[1]
c = open(fname, mode='rb').read()
print(base64.encodebytes(c).decode())
input()
```

このスクリプトのアイコンの上に、送りたい画像ファイルをドラッグ＆ドロップする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114736/262420ee-4049-a235-514b-4d1497d965b7.png)

するとBASE64エンコードされたものが表示されるので、これをクリップボード上にコピー。（pythonスクリプトをもうちょっといじって、クリップボードへの転送を勝手にやるようにしてもいいかもね。）

サーバーの上では、

```
$ base64 -d > image.jpg
```
とコマンドをたたいて、それに続き、今のBASE64データをコンソール上にペーストする。ペーストが終わったら [ctrl]+[D]。これで、サーバー上には、BASE64状態から復元されたデータが格納されているというわけ。

あくまで数KBのファイルを送るときしか実用的でないと思うけど、どうでしょう。面倒具合はそんなに変わらないかな。
gpg コマンド備忘録

## 概要
全く覚えられないコマンドをここに備忘録として残しておきます。

## コマンド

```
gpg --version
```


```
gpg --expert --full-generate-key
```

```
gpg --output abc.asc --armor  --export "tag"
```

```
gpg --import myname.asc
```


```
gpg -o myfiles.tar.lz --decrypt myfiles.tar.lz.asc
```
”npm run dev”のエラー 「There are multiple modules with names that only differ in casing.」 解決法

## 環境
* Windows10
* GitBash
* Vscode

## 状況

Next.jsを学習がてら触ろうと思い、デスクトップにデモアプリを作成し`npm run dev`したところ、以下のようなエラーが出てしまい、解決しましたので記しておこうと思います。

```MINGW64　~/desktop/my-app
 $ npm run dev
  ~略~
　There are multiple modules with names that only differ in casing.
　This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
　Use equal casing. Compare these module identifiers:
　~略~　　
```
## 解決策
エラー文の通り、小文字と大文字の識別に関するエラーのようなので検索したところ、[こちら](https://fantashit.com/there-are-multiple-modules-with-names-that-only-differ-in-casing/)の記事から解決することができました。

簡単に記すと、原因としては、プロジェクトの作成場所に移動する際に小文字で移動することで起きてしまったエラーのようです。そのため、プロジェクトの作成場所に移動する際に
`cd desktop`  →　`cd Desktop`
とすることで解決することができました。

エラー文を読むことで解決できるので、エラー文を読むのも大切だと感じました。

それにしても、小文字と大文字の識別やタイプエラーが多くて大変です・・・
huskyを使ってgit commit前にstylelintを動かす

huskyを使えば簡単にpre-commitにstylelintが組み込めます。
チーム開発では非常に便利なので是非取り入れてみてください！

# 目次
1. [huskyとは](#Chapter1)
2. [インストール](#Chapter2)
3. [husky準備](#Chapter3)
4. [package.jsonに追加](#Chapter4)
5. [動作確認](#Chapter5)

### バージョン
npm: 6.14.8
husky: 5.2.0
lint-staged: 10.5.4

<a id="#Chapter1"></a>
# huskyとは

git hookを簡単に使えるようにするツールです。
git hookを使うとgit commitやpushをする前にlintやテストを走らせることが出来ます。

# インストール
stylelintはインストール済みとします。 stylelintの説明は[こちら](https://qiita.com/DesignChips/items/cd5282dba553026757c8)をご覧ください。
まずhusky、lint-stagedをインストールします。

lint-stagedとは、gitのステージングに上がったファイルにのみlintやテストをかけることが出来るツールです。

```shell
$ npm install husky lint-staged
```
lint-stagedはステージングされた(git addされた)ファイルのみを対象にすることが出来ます。

# husky準備

続いてhuskyを使えるようにします。

```shell
$ npx husky init
```

ルートディレクトリに.huskyが自動で作成されます。

その中のpre-commitファイルを以下のように変更します。

```shell:./.husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run lint-staged  // ここを変更
```

# package.jsonに追加
続いてpackage.jsonを確認してみるとscripts何か追加されています。

```json:package.json
{
	"scripts": {
	  // 省略
      "prepare": "husky install",  // 自動で追加される
  }
}
```



stylelintのコマンドとlint-stagedコマンドをscriptsに追加します。
lint-stagedの設定もついでにやりましょう。
lintの対象ディレクトリと、行いたいlintコマンドを設定します。

```json:package.json
{
	"scripts": {
	  // 省略
      "prepare": "husky install",
      "stylelint": "stylelint resources/sass/**",
      "lint-staged": "lint-staged"
  },
  "lint-staged": {
      "resources/sass/**": [
          "npm run stylelint"
      ]
  },
}
```

これで完成です。

### 動作確認

試しにこのようなファイルをコミットしてみましょう。

```css
p {
	color: #ffffffffff  // fが多いのでエラーになる
}
```

エラーを感知してくれました！
![スクリーンショット 2021-03-23 11.35.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540584/878d43df-985e-867e-05c4-5d736125f2de.png)
ゼロ幅を利用した難読化

# はじめに
この記事は前回書いた記事[「ゼロ幅を利用した隠し文字埋め込み」](https://qiita.com/ikesou/items/9d0929145f4cf863f8c4)の発展です。ゼロ幅についてはそちらをご覧ください。
また、この記事は紹介だけのため難読化プログラムはありません。

# 仕組み
まずは以下のソースコードの出力結果がどうなるかわかりますか？

```js
var a = 1;
var a‌‍‍‌‌ = 2;
var a‌‍‍‌‌‌‌‍ = 3;

if (a == 1 && a‌‍‍‌‌ == 2 && a‌‍‍‌‌‌‌‍ == 3) {
  console.log("ok");
} else {
  console.log("no");
}
```

割と有名なのでご存じの方も多いかもしれませんが、初めて見た方は no じゃないの？って思われるかもしれません。

しかし結果は
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305681/38815c30-e14d-f80a-42dc-a62eac523e6a.png)
ok と出力されます。

F12でコンソールを開いて上のコードを張り付ければわかると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305681/da9db782-8406-7a6d-4879-7f8623288146.png)


ゼロ幅は表示されないだけで文字としては存在しています。
つまり、a = 1 と a = 2 と a = 3 は別の変数として認識されます。

これを利用してソースコードの難読化が可能です。
# 終わりに
難読化のコードは時間がないので省略しました。時間ができれば追加したいと思います。


# 小ネタ
上記のソースコードの (a == 1 && a == 2 && a == 3) はゼロ幅を用いなくても true にすることができます。
良ければこちらもどうぞ
[a == 1 && a == 2 && a == 3を成立させる](https://qiita.com/spinach/items/9b6d20f5d148e5984ac1)
澪田唯吹にあずにゃんがいないのが納得できないため、いい感じの名前に改名させる

ダンガンロンパ2の「澪田唯吹」
彼女は、けいおんの
  
* 秋山**澪**
* **田**井中律
* 平沢**唯**
* 琴**吹**紬
  
から名前が作られているのは有名な話
(澪田唯吹は超高校級の軽音楽部)
　　
しかし、彼女の名前に「中野梓」がいない
たしかに、4人とは学年が違うが、主要キャラ中の主要キャラをハブるのは良くない。
という事であずにゃんを織り交ぜていい感じの名前が作れるか試行錯誤してみる
  
```sample.py
import random

names_list = ["平沢唯",
              "秋山澪",
              "田井中律",
              "琴吹紬",
              "中野梓",]

def shuffle_names(names):
    result = ""
    for name in random.sample(names,len(names)):
        result += random.choice(name)
    return result

if __name__ == "__main__":
    for i in range(1,101):
        print(f"{i} : {shuffle_names(names_list)}")
```  
  
とりあえず100回
  
```result
1 : 律平秋中吹
2 : 琴中律澪唯
3 : 琴律秋梓平
4 : 紬中平秋野
5 : 中律山吹平
6 : 秋平律琴野
7 : 中中沢琴山
8 : 唯吹梓中秋
9 : 井琴澪平中
10 : 澪吹平梓井
11 : 沢井紬秋中
12 : 中唯中秋琴
13 : 井野紬山沢
14 : 梓澪琴平律
15 : 山律梓吹平
16 : 唯山琴中中
17 : 吹野沢山田
18 : 吹澪梓律平
19 : 律梓吹唯澪
20 : 沢琴中澪野
21 : 琴平梓秋井
22 : 井秋琴沢中
23 : 唯吹田野澪
24 : 吹中沢澪梓
25 : 沢野秋律吹
26 : 紬唯律野山
27 : 琴沢澪田梓
28 : 吹秋中中沢
29 : 吹秋梓田沢
30 : 平琴中山中
31 : 吹秋沢田中
32 : 井梓秋沢琴
33 : 井野吹澪唯
34 : 沢梓吹律山
35 : 琴井野澪沢
36 : 琴田唯梓澪
37 : 吹澪中沢中
38 : 中野平吹秋
39 : 沢中律吹山
40 : 中秋沢梓琴
41 : 中律山平吹
42 : 梓吹井秋沢
43 : 紬律澪梓平
44 : 野沢山中紬
45 : 田紬平澪梓
46 : 律琴野平澪
47 : 中井澪琴平
48 : 吹律澪中唯
49 : 律梓琴唯澪
50 : 唯紬秋田梓
51 : 琴中沢律秋
52 : 井中吹唯澪
53 : 沢山吹梓律
54 : 律野山琴沢
55 : 野平井秋琴
56 : 山吹田平梓
57 : 梓琴唯秋中
58 : 山井琴唯梓
59 : 中中琴山平
60 : 井山野琴沢
61 : 紬沢井山中
62 : 井沢吹梓秋
63 : 野平琴中秋
64 : 紬沢田野山
65 : 澪野唯律吹
66 : 中中唯吹山
67 : 唯山中田紬
68 : 澪律琴唯中
69 : 中唯中吹山
70 : 唯琴野律澪
71 : 紬井平野澪
72 : 中琴梓澪沢
73 : 琴山野唯中
74 : 田澪野紬平
75 : 律唯秋吹梓
76 : 琴井山沢梓
77 : 野平井琴山
78 : 秋中紬野唯
79 : 沢井秋野紬
80 : 中琴中沢山
81 : 秋平井吹梓
82 : 秋琴梓律平
83 : 紬野中沢澪
84 : 紬平井梓山
85 : 澪沢琴野中
86 : 唯山田中紬
87 : 紬田沢澪野
88 : 中山紬平田
89 : 紬井梓秋沢
90 : 野井紬唯山
91 : 中吹中秋沢
92 : 井中吹唯澪
93 : 中梓秋唯吹
94 : 琴沢中澪井
95 : 山吹唯井梓
96 : 平吹中山野
97 : 律紬秋梓沢
98 : 梓澪琴律唯
99 : 井澪唯吹中
100 : 紬澪井唯野
```  
  
5文字の名前って難しいな...

何となく、
33 : 井野吹澪唯
が名前として成立してそう
読み方は「いのふき みゆ」かな

ちなみに、これで生成できる名前の総数は  
1. 各名前の文字数の積($3\times 3\times 4\times 3\times 3 = 324$通り)
2. 各人の順列($\begin{eqnarray*}&& {}_5 P _5 \end{eqnarray*} = 120$)
3. 1,2の積($324\times 120 = 38880$通り)

38880通りか...

現状「井野吹澪唯」が一番名前っぽい
もうちょっと試そう...
jQuery セレクタに複数の要素を指定する方法

# jQuery セレクタに複数の要素を指定したい時
例えば、clickイベント内の処理を複数の要素（今回はdata属性）で使用したい時、以下のように書くだろう。

```index.js
  $("[data-aaa], [data-bbb], [data-ccc], ...).click(function () {
　  ...
  });
```

このままだと、指定したい要素がもっと増えた時は、横に長くなり可読性が落ちる。
そこで、セレクタに変数を代入し複数の要素を縦書き、可読性をよくする方法を共有します。

##方法
```index.js

  // 指定したい要素を配列に定義しておく
  let targetAttrArr = [
    '[data-aaa]',
    '[data-bbb]',
    '[data-ccc]',
     ...
  ];

  // 配列内の文字列を結合してtargetに代入
  let target = '';
  targetAttrArr.forEach(function (element, index) {
    target += element;
    if ( index != (targetAttrArr.length - 1)) {
      target += ', ';
    }
  });

  // target変数をセレクタで代入
  $(target).click(function () {
    ...
  });

```

OK
Vue.js から Google OAuthでaccess_token,refresh_tokenを取得する

## 当ドキュメントが出来た理由
-フロントエンド：Vue.js
-バックエンド：Flask
の環境でアプリケーション構築を行っていた所、GoogleのOAuthの処理をバックエンド側から投げるとCORSエラーが出ます。CORSで検索すると色々な情報が出てくるのですが、最終的にそもそもそのような実装は出来ないという記載を見つけたため(どこだったか失念してしまったのですが)、今回はフロントエンドでOAuthをする実装を目指しました。
*もし同様の構成でバックエンドから投げても通る場合は教えて下さい
Vue.jsからGoogleのOAuthを行う方法を検索すると色々と出てくるのですが、最終的にrefresh_tokenが取得できずに数時間を溶かしたのでここに記しておきます。

## vue.jsで使うライブラリはvue-google-oauth2
https://github.com/guruahn/vue-google-oauth2
こちらです。
インストール方法は記載の通りです。

```main.js
Vue.use(GAuth, { 
  clientId: process.env.VUE_APP_GOOGLE_OAUTH_CLIENT_ID
  , scope: 'email profile openid https://www.googleapis.com/auth/calendar.readonly'
  , prompt: 'consent'
  , fetch_basic_profile: false
  }
)
```
こんな感じでVueに追加してください。


```oauth.vue
        try {
          const authcode = await this.$gAuth.getAuthCode()
          if (!googleUser) {
            return null
          }else{
          this.$store
            .dispatch('getRefreshToken', {token: authcode})
            .then((data) => {
              this.loading = false
              //console.log('after getRefreshToken')
              //console.log(data)
            })
            .catch(() => {
              this.loading = false
            })
          }
        } catch (error) {
          console.error(error)
          return null
        }
      }
```
getAuthCode()でauthcodeが取得できます。
これを、バックエンドから投げることによって
access_token
refresh_token
token_expiry
を取得することが出来ます。

```backend.py
@auth.route('/getRefreshToken', methods=['POST'])
def getRefreshToken():
    token = request.json.get("token", None)
    if token:
        try:
            credentials = client.credentials_from_code(os.environ.get("GOOGLE_OAUTH_CLIENT_ID"), os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET"), scope='', code=token)
        except client.FlowExchangeError as e:
            print('outh error')
            print(e)
        else:
            return jsonify(access_token=credentials.access_token, refresh_token=credentials.refresh_token, token_expiry=credentials.token_expiry), 200
    else:
        return jsonify(google_token='false'), 200
```
コードはこちらを参照してます
https://github.com/guruahn/vue-google-oauth2/blob/master/backend-samples/python/main.py

credentialsの中に
access_token
refresh_token
token_expiry
の3つが入っていますので、それをよしなに使ってください。

## refresh_tokenが取れなかった実装パターン
const googleUser = await this.$gAuth.getAuthCode()
これを
const googleUser = await this.$gAuth.signIn()
これで実装すると取得できませんでした。
特に必要としない場合はsignIn()で実装してしまえば、バックエンドから投げる必要が無いので簡単な実装ができます。
Lambda関数から別アカウントのDynamoDBに接続

## 記事を書いた経緯
- 初めてAWSを触った！記録取っておこう！
- Lamdba関数から他アカウントのDynamoDBにアクセスを経験！個人的に詰まったところもあったので、同じミスはしないように記録として残そう！
- ナレッジ化して共有できればいいなー

って感じです。初投稿なので、「ん？」って思うところがあるかもしれないですが、参考になれば幸いです。

## 環境
- Typescript
- DynamoDB
- Lambda

## まず驚いたこと
Lamdba関数からただ単に`aws-sdk`の`DynamoDB Client`を作ってアクセスをしたら`ResourceNotFoundException`になった。。。
なぜだ。。。

### NGコード
```typescript
import {DynamoDB} from "aws-sdk"

const client = new DynamoDB.DocumentClient()
const param = {
  TableName: "sample",
  Key: {
    'id': 1
  },
}
const result = docClient.get(param)
```

## 結論
他アカウントにアクセスする際にAssume Roleを行い、権限を付与しなければいけなかった。
（今思えば、それはそうだよなーってなった。。endpointがわかれば誰でもアクセスできるなんてセキュリティアウトだろ！）

色々試行錯誤して、以下のコードで正常にレスポンスが返ってきた！
ヨシッ！

### OKコード
```typescript
import {DynamoDB, STS, Credentials} from "aws-sdk"

const sts = new STS()

// Lambda実行アカウント情報
const account = sts.getCallerIdentity((error: any, data: any) => {
  if (error) return error
  return data
}).promise()

// assume role
const role = sts.assumeRole({
  RoleArn:[IAM], //権限を付与するIAMのARN
  RoleSessionName: 'sample', // ここはなんでもいい
  ExternalId: account['Account'] // 外部キーにアカウントIDを指定したケースなので、ここは任意
}).promise()


// クレデンシャル
const credentials = new Credentials(
  role.Credentials.AccessKeyId,
  role.Credentials.SecretAccessKey,
  role.Credentials.SessionToken
)

// clientインスタンス作成
const client = new DynamoDB.DocumentClient({credentials})
const param = {
  TableName: "sample",
  Key: {
    'id': 1
  },
}
const result = docClient.get(param)
```

## 今回省略した内容
- Lamdba関数にAssume Roleの権限を与える
- Assume Roleについて

## 参考資料
- DynamoDB操作
   - https://xp-cloud.jp/blog/2019/10/24/5698/
- Assume Roleについて
   - https://dev.classmethod.jp/articles/iam-role-passrole-assumerole/
pythonでyoutube動画をダウンロード

この記事は著作権法違反を助長するものではありません(実際にダウンロードする際は自己責任でお願いします)

##pytube
pythonでyoutube動画をダウンロードするには、pytubeというモジュールを使います
pytubeはpipコマンドで入ります

```console
$ python -m pip install pytube
```

エラーが出た場合はこちらのコマンドを試してみてください

```bash
$ python -m pip3 install pytube3
```

##使い方

```python
from pytube import YouTube

url = #YouTube動画のURL
for y in YouTube(url).streams.all():
    print(y)
```
YouTube(url).streams.all()だけだとリストが返ってきて見ずらいのでfor文で見やすくします。
これを実行するとitagと動画の保存形式が出てくると思います
pytubeではこのitagで保存形式を指定します

```python
from pytube import YouTube

url = #YouTube動画のURL
YouTube(url).streams.get_by_itag("itag").download("保存先ディレクトリ(フルパス)"):
```
上記のプログラムでダウンロードできます
また、download()は何も指定しないと自動的にカレントディレクトリに保存されます。

##ソースコード

```python
from pytube import YouTube

url = input("urlを指定してください")
for y in YouTube(url).streams.all():
    print(y)
itag = input("itagを入力してください")
YouTube(url).streams.get_by_itag(int(itag)).download()
print("ダウンロードが終了しました")
```
itagは数値でないといけないのですが、input()で代入すると文字として扱われてしまうので、int()で数値に変換します

```python:タイムアウト付き
from pytube import YouTube
from timeout_decorator import timeout, TimeoutError

@timeout(300)
def download(url, itag):
    YouTube(url).streams.get_by_itag(int(itag)).download()

def set():
    url = input("urlを指定してください")
    for y in YouTube(url).streams.all():
        print(y)
    itag = input("itagを入力してください")
    return url, itag

def startdownload(url, itag):
    try:
        download(url, itag)
        print("ダウンロードが終了しました")
    except TimeoutError:
        print("タイムアウトしました")

def main():
    url, itag = set()
    startdownload(url, itag)

if __name__ == "__main__":
    main()
```
pytubeにはタイムアウトがない(多分)ので、timeout_decoratorでタイムアウトを設定してあげます(上記コードでは5分)
タイムアウトが付いてると処理が少し遅くなるそうなので、もうちょっと長めに設定してもいいかもしれません



timeout_decoratorもpipで入ります

```console
$ python -m pip install timeout_decorator
```

def文の前に付くアットマーク(@)の文はデコレーターという意味を持ちます
まあデコレーターのことは下記サイトを見てもらえれば分かりやすいと思います
- [アットマーク演算子の使い方](https://techacademy.jp/magazine/38457)

##テキストファイルからURLを取得する場合

```python
from pytube import YouTube

with open("ファイルパス", "r") as file:
    url = file.read().split("\n")
for d in url:
    for y in YouTube(d).streams.all():
        print(y)
    itag = input("itagを入力してください")
    YouTube(d).streams.get_by_itag(int(itag)).download()
print("ダウンロードが終了しました")
```

read()でテキストファイルを読み込み、split("\n")で改行で区切りリスト化、それをfor文で回すとできます。

##終わり
いかがでしたでしょうか？
pytubeは便利なモジュールですが、冒頭でも述べたように、実際に使う際は自己責任でお願いします。


##参考

  https://pypi.org/project/timeout-decorator/

https://techacademy.jp/magazine/38457
Pipenvでシステムに直接インストールしたり、requirements.txtを出力したり

Pythonのパッケージ管理は何を利用していらっしゃいますか？
私はPipenvです。Pipenvの操作はわかりやすく、とても助かります。

Pipenvの解説は@y-tsutsuさんの「[Pipenvを使ったPython開発まとめ](https://qiita.com/y-tsutsu/items/54c10e0b2c6b565c887a)」が大変参考になります

上記記事で取り上げていない、Pipenvの便利な機能の紹介を本記事でします。

取り上げた内容は以下の2本です。

- Pipenvを使って、システムに直接インストールする
- Pipenvで使っているパッケージを`requirements.txt`に出力する

### Pipenvでシステムに直接インストールする - コンテナで利用するときなど

コンテナ環境を用意してPythonを実行する場合、すでにコンテナによって隔離されているのでPythonの仮想環境を作成する必要はありません。`--system`オプションで直接システムに直接インストールしましょう。

```
$ pipenv install --system
```

`Pipfile.lock`に指定されたバージョンをインストールする`sync`でも利用できます。

```
$ pipenv sync --system
```

Pipfile.lockで指定されたバージョンが古く、`pipenv install`でインストールしたバージョンと違った時、インストールをやめることもできます。

```
$ pipenv install --system --deploy
```

こうすることで古いパッケージを利用したままデプロイしてしまうのを回避できます。ただこれは良い点悪い点があると思うので、各自で判断してください。

#### Dockerfile サンプル

```Dockerfile
FROM python:3.8

WORKDIR /app
COPY . /app
RUN pip install pipenv && \
    pipenv sync --system && \
    pip uninstall --yes pipenv
```

### requirements.txt を出力する

互換性のために`requirements.txt`を出力する必要があるときもあります。

`lock -r`で`requirements.txt`を作成できます。

```
$ pipenv requirements > requirements.txt
```

開発環境を含めて出力する場合は以下になります。

```
$ pipenv requirements --dev > requirements.txt
```

本番用とdev用の`requirements.txt`を分割する方法もあります。`--dev-only`オプションを利用します。

```
$ pipenv requirements > requirements.txt
$ pipenv requirements --dev-only > dev-requirements.txt
```

以上です。みなさんの参考になれば嬉しいです。

## 参考

- [Pipenvを使ったPython開発まとめ](https://qiita.com/y-tsutsu/items/54c10e0b2c6b565c887a)
- [Advanced Usage of Pipenv¶](https://pipenv.pypa.io/en/latest/advanced)
[JS] ループ

for文
---

```
const arry = [1,2,3,4,5];

for(let i = 0; i < 5; i = i + 1) {
  console.log(i);
}
```

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

let i = 0;　　　変数を定義
i < 5;　　　 　ループを継続する条件を指定
i = i + 1　　　ブロックが一回終了したときに実行される式

---
arry[ i ]とすれば、配列の要素を回すことができます


```
const arry = [1,2,3,4,5];

for(let i = 0; i < arry.length; i ++) {
  console.log(arry[i]);
}
```

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

i < arry.length;
i ++

for in
---
in arryとすると配列のインデックスが渡ってきます

```
const arry = [1,2,3,4,5];

for(let i in arry) {
  console.log(i);
}
```

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

---
i, arry[ i ]とすると値を一括で見ることもできます

```
const arry = [1,2,3,4,5];

for(let i in arry) {
  console.log(i, arry[i]);
}
```

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

for of
---
for ofは値がそのまま渡ってきます
現在対応していないブラウザがありますのでご注意ください

```
const arry = [1,2,3,4,5];

for(let v of arry) {
  console.log(v);
}
```

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

オブジェクトのループ
---

```
const todos = [
  {
    id: 1,
    title: 'Go to school',
    completed: true
  },
  {
    id: 2,
    title: 'Go to museum',
    completed: true
  },
  {
    id: 3,
    title: 'Go shopping',
    completed: false
  }
]

for(let i = 0; i < todos.length; i++) {
  console.log(todos[i]);
}
```

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

---
if文を使って、ToDoの中で、タスクが完了しているもののタイトルだけ出力する

```
const todos = [
  {
    id: 1,
    title: 'Go to school',
    completed: true
  },
  {
    id: 2,
    title: 'Go to museum',
    completed: true
  },
  {
    id: 3,
    title: 'Go shopping',
    completed: false
  }
]

for(let i = 0; i < todos.length; i++) {
  let todo = todos[i];
  if (todo.completed === true) {
    console.log(todo.title);
  }
}
```

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

---
inとofも使えます

```
for(let i in todos) {
  let todo = todos[i];
  if (todo.completed === true) {
    console.log(todo.title);
  }
}
```

```
for(let i of todos) {
  let todo = todos[i];
  if (todo.completed === true) {
    console.log(todo.title);
  }
}
```
初心者の初心者による初心者のための配列プログラム課題のソースコード例

初心者の初心者による初心者のための配列プログラム課題のソースコード例です。

課題は下記のURLの問題です。

https://qiita.com/nogizakapython/items/dd0bb83e729b9a7ffafe


私が書いたソースコード例です。（Java）


```java
//
//  配列課題1模範解答(Java)でかきました
//  Author  坂道好きのITエンジニア
//　Date    2021/3/23　新規作成
//  修正する場合は下に修正日と修正者名を書く
//



package paiza;

public class hairetsu2 {
	public static void main(String[] args) {
		//配列の宣言
		int[] data = {7,4,5,8,2,6};
		//配列の長さを取得する
		int nagasa = data.length;
		//配列の長さを出力する
		System.out.println("配列の長さは" + nagasa + "です");

	}

}
GitLabのプロジェクト名・グループ名をURLごと変更し、localとも連携する

プロトタイプで作っていたサービスで、正式にサービス名とドメインが決定したのでプロジェクト名とグループ名を変更し、URLも合わせて変更したい。
新たにチームに加わったり保守してくれる人が違和感の無いように。
新たにリポジトリを作り直してもいいんだけども。

プロジェクト名を変更すると（ディレクトリ名かもしれない）、docker volume name も変わるので注意。
volumeしたデータが消えると困る場合は、volumeの移行もしておく。
難しくないので。

### GitLabのプロジェクト名を変更する。
プロジェクトTOP > Settings > General で Project name を変更する。
これだけではURLは変わらないので、
プロジェクトTOP > Settings > General > Advanced で Change path を変更。

ここで、グループ名も変えねばと気づく。

### GitLabのグループ名を変更する。

グループTOPから、プロジェクト名と同様に変更する。

### ローカルと連携

これで、新たにcloneすると新しいプロジェクト名、URLが反映される。
既存のディレクトリとgitのローカルリポジトリがある場合は、ディレクトリ名とremoteのURLを変えておいたほうが良い。


```
# 既存のリモートリポジトリを確認。
$ cd [service directory]
$ git remote -v
origin	[old project url] (fetch)
origin	[old project url] (push)

# URL変更
$ git remote set-url origin [new project url]

# 確認
$ git remote -v
origin	[new project url] (fetch)
origin	[new project url] (push)
```

ちなみに、URL変更しなくてもbranchをpushできる。
その際に警告を出してくれるので、言われたとおりにすると良い。

```
$ git push origin feature/test
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: To create a merge request for feature/test, visit:
remote:   https://[gitlab domain]/gitlab/[new group name]/[new project name]/merge_requests/new?merge_request%5Bsource_branch%5D=feature%2Ftest
remote:
remote: Project '[old group name]/[old project name]' was moved to '[new group name]/[new project name]'.
remote:
remote: Please update your Git remote:
remote:
remote:   git remote set-url origin git@git.orso.jp:[new group name]/[new project name].git
```

必要であれば、Dockerfileのディレクトリ名やdocker-compose.ymlやconfigファイルのサービス名を変更する。
個人的には共通して使えるように汎用的な名前にして、プロジェクトごとに変えないような運用にしている。

### docker volume nameを変更する

ここで docker-compose up すると、docker image name が変わっているのでbuildが走る。
そして、DBにつながらないエラーが発生する。
（DBがない場合createする処理を入れている場合はサービスが立ち上がるが、データがまっさらになっていることに気づくはず。）
docker volume nameが変わり、新たなvolumeが作られたため。
以前のvolumeを使うために、volume name の変更を行う。
もう少し正確に言うと、volume name の変更はできないようなので、「[新しい Volume に全てコピーして古い方を消す](https://qiita.com/hoto17296/items/d2f7f63c529fae276f8c)」操作を行う。

```
$ docker volume ls
......
local     ............
local     [old project name]_data
local     [new project name]_data
......

# 一応新しくできたvolumeを消しておく。（必要ないかも）
$ docker volume rm [new project name]

$ docker volume create --name [new project name]
$ docker run --rm -it -v [old project name]:/from -v [new project name]:/to alpine sh -c "cp -av /from/* /to"
$ docker volume rm [old project name]

```
ちなみに、私のdocker-compose.ymlのvolume部分はこんな感じ。

```yml:docker-compose.yml
version: '3'
services:
  db:
    volumes:
      - data:/var/lib/mysql

volumes:
  data:
    driver: local
```

これで元のDBに接続するようになった。
色々やっているといつの間にか docker-compose.override.yml が失くなっていた。
いつ消えたんだろう。。
fetchしたときかな。。

参考
- [リモートの URL の変更 \- GitHub Docs](https://docs.github.com/ja/github/using-git/changing-a-remotes-url)
- [Docker で Volume 名を変更する \- Qiita](https://qiita.com/hoto17296/items/d2f7f63c529fae276f8c)
- [Docker の Volume がよくわからないから調べた \- Qiita](https://qiita.com/aki_55p/items/63c47214cab7bcb027e0)
- [Docker におけるデータ管理 \| Docker ドキュメント](https://matsuand.github.io/docs.docker.jp.onthefly/storage/)
- [\[Docker for Mac\] Docker Volumeの実態がどこにあるのか、探してみた｜w0o0ps｜note](https://note.com/w0o0ps/n/n9bc1bcd9fa59)
【TypeScript】色々な型定義についてpart2　〜備忘録〜

まだまだ型定義について学んでいきます！

頑張っていきましょう!

#readonlyとは
readonlyがついたプロパティは上書きすることができません！

```object.ts
export const human = () => {
    let person : {
        age : number,
        lastName : string,
        readonly firstName : string,
      
    }
    person = {
        age : 43,
        lastName : 'kenshi',
        firstName : 'takada',
    }
    console.log(person);

    person.firstName = 'yoshida'
   
    

console.log(person);
}
```

`person.firstName = 'yoshida'`と入力すると下記エラーが発生します！
「読み取り専用プロパティであるため、'firstName' に代入することはできません。」
これは　firstNameにreadonlyをつけてる為、上書きができませんよ！と教えてくれてます！
それがreadonlyの挙動になります

#型エイリアス
よく見る構文ですよね！

事前にobjectにどんな型なのかを定義し、それを関数にあてることで型を定義する形です。
これをすることで、同じ型をいちいち定義することがなくなり、再利用することができます！

```alies.ts
export const aliesSample = () => {
   type Country ={
        name : string,
        language : string,
        location : string,
        age: number
   }

   const japan: Country={
    name : '東京',
    language : '日本語',
    location : 'アジア',
    age : 100
   }

   console.log(japan);
```

#合併型と交差型について
合併型・・・どちらかの型を持つ
交差型・・・両方の型を持つ

意味わからん。。。
実際にコードを書いていきます！

####合併型

```alies.ts
type hero = {
    hp:number,
    attack:number,
    swordSkill:string,
}

type badGuy = {
    hp:number,
    damage:number,
    attack:number,
    magicSkill:string,
}

type Adventurers = hero | badGuy

const adventurer4:Adventurers = {
    hp:100,
    attack:200,
    swordSkill:'鬼斬'
}

const adventurer5:Adventurers = {
    hp:600,
    damage:192,
    attack:100,
    magicSkill:'サーペンソーティア',
}



```
`type Adventurers = hero | badGuy`とすることで合併型を宣言します→「｜」がまたはという意味。

`console.log(adventurer4)`とすると
「{hp: 100, attack: 200, swordSkill: "鬼斬"}」が表示されます！
また`console.log(adventurer5)`とすると
「{hp: 600, damage: 192, attack: 100, magicSkill: "サーペンソーティア"}」と表示されます！

**合併型はhero, badGuy型両方のプロパティが使える！！**

####交差型

```alies.ts
type hero = {
    hp:number,
    attack:number,
    swordSkill:string,
}

type badGuy = {
    hp:number,
    damage:number,
    attack:number,
    magicSkill:string,
}
type crossing = hero & badGuy

const adventurer6:crossing = {
    hp:100,
    attack:200,
    swordSkill:'鬼斬',
    damage:192,
    magicSkill:'サーペンソーティア',
}
const adventurer7:crossing = {
    hp:100,
    attack:200,
    swordSkill:'鬼斬',
    damage:192,
}

```

adventurer6は合併型同様hero, badGuy型両方のプロパティが使えます！
しかし、adventurer7では、`magicSkill:'サーペンソーティア'`を指定してない為、エラーが発生します。
**交差型は全てのプロパティを使う必要がある為です！**

これが合併型と交差型の違いです！

#最後に
自分も合併型と交差型を使いこなせる自信がないので、色々いじっていきたいと思います！
SQLを学んでみた⑥(ネスト)

実務でSQLやDBについて理解していないといけないと思い勉強を始めました。
その内容をまとめます。

## ネスト

SELECT文の中にSELECT文を入れることで本来2回必要な処理が1回でできます。

例えば、「売上管理簿の中で最も大きい売上金額の内容と金額を求めたい」となった場合

2回で行う場合は以下のように書きます。

```
//まず最も大きい売上金額を求める
SELECT MAX(売上金額) FROM 売上管理簿

//上記の結果を書き留めておき以下の文を実行

SELECT 内容, 売上金額 FROM 売上管理簿
WHERE 売上金額 = <上記の結果>
```

上記のSQL文はネスト構造にすることで1回で行うことができます。
ネストしたSQL文は以下のように書きます。

```
SELECT 内容, 売上金額 FROM 売上管理簿
WHERE 売上金額 = (SELECT MAX(売上金額) FROM 売上管理簿)
```

処理の流れを説明します。

①ネストされている( )の中を処理
②( )の外の処理

というようにネストされている部分から処理されるので( )から先に考えればどんな処理がされているか分かりやすいのかなと思いました。


以上です。
よく使うBQコマンド

BQコマンド

--copy cmd
bq --project_id=hoge_user cp test_project:test_dataset.table_a test_project:test_dataset.table_b
//既に存在しているテーブルでも上書き可能

--drop cmd
bq --project_id=hoge_user rm test_project:test_dataset.table_a
【phpunit】テストを実行するたびにローカルDBがリセットされる


#はじめに
phpunitを使ってテストを書いた際に、`テストを実行するたびにローカルDBがリセットされる`というバグにハマったので、解決方法を記録しておきます。



#開発環境

PHP 7.4.16 
Laravel  6.20.17
mysql   8.0.23
nginx 1.18.0

#DB設定方法
ControllerTest.php に下記を追加

```
use Illuminate\Foundation\Testing\DatabaseTransactions;


class ControllerTest extends TestCase
{
    use DatabaseTransactions;
}
```



phpunit.xmlを下記にする

```
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="MAIL_DRIVER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
    </php>
</phpunit>
```
この時のポイントは、テスト用のデータベース（ローカルと同じ）を見るように設定すること！

下記の様な記載がphpunit.xmlにある時は、削除する！！

```
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/> 
```
Pandasデータフレームから特定の列を省いたデータフレームの作り方（備忘録）

Pandasで、データ(csvなど）を取り込んだ後、特定の列を省いたデータフレームの作り方について、よく忘れがちなので・・・備忘！

## データフレームの作成
sklearnに付属のデータセットを使う。

```
from sklearn.datasets import load_boston
import pandas as pd

# ボストンデータの読み込み
boston_data = load_boston()
# データフレームの作成
boston_df = pd.DataFrame(boston_data["data"], columns=boston_data["feature_names"])
```

#### boston_dfの中身

```
CRIM	ZN	INDUS	CHAS	NOX	RM	AGE	DIS	RAD	TAX	PTRATIO	B	LSTAT
0	0.00632	18.0	2.31	0.0	0.538	6.575	65.2	4.0900	1.0	296.0	15.3	396.90	4.98
1	0.02731	0.0	7.07	0.0	0.469	6.421	78.9	4.9671	2.0	242.0	17.8	396.90	9.14
2	0.02729	0.0	7.07	0.0	0.469	7.185	61.1	4.9671	2.0	242.0	17.8	392.83	4.03
3	0.03237	0.0	2.18	0.0	0.458	6.998	45.8	6.0622	3.0	222.0	18.7	394.63	2.94
4	0.06905	0.0	2.18	0.0	0.458	7.147	54.2	6.0622	3.0	222.0	18.7	396.90	5.33
...	...	...	...	...	...	...	...	...	...	...	...	...	...
501	0.06263	0.0	11.93	0.0	0.573	6.593	69.1	2.4786	1.0	273.0	21.0	391.99	9.67
502	0.04527	0.0	11.93	0.0	0.573	6.120	76.7	2.2875	1.0	273.0	21.0	396.90	9.08
503	0.06076	0.0	11.93	0.0	0.573	6.976	91.0	2.1675	1.0	273.0	21.0	396.90	5.64
504	0.10959	0.0	11.93	0.0	0.573	6.794	89.3	2.3889	1.0	273.0	21.0	393.45	6.48
505	0.04741	0.0	11.93	0.0	0.573	6.030	80.8	2.5050	1.0	273.0	21.0	396.90	7.88
```

ここから、INDUS列とLSTAT列 を除いたデータフレームを作る。

```
# 削除したい列をリストで保存
drop_col = ["INDUS","LSTAT"]
# 削除したい列リストに含まれない列のデータを取得
boston_df_drop_col = boston_df[ [ i for i in boston_df.columns if (i not in drop_col) ] ]   # ←ここが備忘ポイント
```

`[ i for i in boston_df.columns if (i not in drop_col) ]` が備忘ポイント
※if文の中のカッコ( ) は、無くてもOK！

#### boston_df_drop_colの中身

```
	CRIM	ZN	CHAS	NOX	RM	AGE	DIS	RAD	TAX	PTRATIO	B
0	0.00632	18.0	0.0	0.538	6.575	65.2	4.0900	1.0	296.0	15.3	396.90
1	0.02731	0.0	0.0	0.469	6.421	78.9	4.9671	2.0	242.0	17.8	396.90
2	0.02729	0.0	0.0	0.469	7.185	61.1	4.9671	2.0	242.0	17.8	392.83
3	0.03237	0.0	0.0	0.458	6.998	45.8	6.0622	3.0	222.0	18.7	394.63
4	0.06905	0.0	0.0	0.458	7.147	54.2	6.0622	3.0	222.0	18.7	396.90
...	...	...	...	...	...	...	...	...	...	...	...
501	0.06263	0.0	0.0	0.573	6.593	69.1	2.4786	1.0	273.0	21.0	391.99
502	0.04527	0.0	0.0	0.573	6.120	76.7	2.2875	1.0	273.0	21.0	396.90
503	0.06076	0.0	0.0	0.573	6.976	91.0	2.1675	1.0	273.0	21.0	396.90
504	0.10959	0.0	0.0	0.573	6.794	89.3	2.3889	1.0	273.0	21.0	393.45
505	0.04741	0.0	0.0	0.573	6.030	80.8	2.5050	1.0	273.0	21.0	396.90
```

INDUS列とLSTAT列 を除いたデータフレームになっている。

おしまい
2021/3/23 Android System WebViewは何が修正されたのか？

2021/3/23に世間を騒がせた、Android System WebView (Google Playだと 'AndroidシステムのWebView' って表記のやつ)、結局、何を修正したら直ったのか気になったので、Gitログを調べた。

* おかしいと報告のあったバージョン: 89.0.4389.90
* 直ったと報告のあったバージョン: 89.0.4389.105

https://github.com/chromium/chromium/compare/89.0.4389.90...89.0.4389.105

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74571/5393e685-c1f2-0f2a-81a1-820968157d39.png)

なるほどわからん。
【Thunderbird】サンダーバードで署名を設定する方法

メーラーソフトのThunderbird(サンダーバード)で署名を設定する方法のメモ

例

```

ーーーーーーーーーー+
 株式会社〇〇〇〇
 〇〇〇〇部　山田　太郎
 TEL：03-0000-0000
 mail：hoge@hoge.jp
+ーーーーーーーーーー 
```



①ツール(T)
→
②アカウント設定(S)
→
③署名編集(X):←ココ
click で group を分割 + pass_context  で共通化オプションを設定する

# Overview

python の cli コマンドツールの [click](https://click.palletsprojects.com/en/7.x) で、

* サブコマンド用にファイルを分割する方法
* 分割したサブコマンドに共通オプションを渡す方法

のやり方。


# コード

```bash:files
mycommand/
├── cli1.py
├── cli2.py
└── cmd.py
```

```python:mycommand/cmd.py
"""
エントリポイント
"""
import click

# サブコマンド ファイル分割
from . import cli1  # サブコマンド 1
from . import cli2  # サブコマンド 2

@click.command(cls=click.CommandCollection, sources=[cli1.cli1, cli2.cli2])
@click.option('--debug/--no-debug', default=False)  # 全サブコマンドに共通のオプション
@click.pass_context
def cli(ctx, debug):
    """エントリポイント"""
    ctx.obj = {'debug': debug}

if __name__ == '__main__':
    cli()
```

```python:mycommand/cli1.py
"""
サブコマンド 1
"""
import click

cli1 = click.Group()

@cli1.group()
@click.option('--verbose/--no-verbose', default=False, envvar="VERBOSE")  # cli1_grp1_cmd1 / cli1_grp1_cmd2 に共通のオプション
@click.pass_obj
def cli1_grp1(obj, verbose):
    """サブコマンド1"""
    obj['verbose'] = verbose

@cli1_grp1.command()
@click.option('-o', '--opt')  # cli1_grp1_cmd1 のオプション
@click.pass_obj
def cli1_grp1_cmd1(obj, opt):
    """サブコマンド1-コマンド1"""
    print(locals())

@cli1_grp1.command()
@click.option('-o', '--opt')  # cli1_grp1_cmd2 のオプション
@click.pass_obj
def cli1_grp1_cmd2(obj, opt):
    """サブコマンド1-コマンド2"""
    print(locals())
```

```python:mycommand/cli2.py
"""
サブコマンド 2
"""
import click

cli2 = click.Group()

@cli2.command()
@click.option('-v', '--value')  # cli2_cmd1 のオプション
@click.pass_obj
def cli2_cmd1(obj, value):
    """サブコマンド2-コマンド1"""
    print(locals())
```

# 実行

```bash:エントリポイント
$ python -m mycommand.cmd
Usage: cmd.py [OPTIONS] COMMAND [ARGS]...

  エントリポイント

Options:
  --debug / --no-debug
  --help                Show this message and exit.

Commands:
  cli1-grp1  サブコマンド1
  cli2-cmd1  サブコマンド2-コマンド1
```

```bash:サブコマンド1
$ python -m mycommand.cmd cli1-grp1
Usage: cmd.py cli1-grp1 [OPTIONS] COMMAND [ARGS]...

  サブコマンド1

Options:
  --verbose / --no-verbose
  --help                    Show this message and exit.

Commands:
  cli1-grp1-cmd1  サブコマンド1-コマンド1
  cli1-grp1-cmd2  サブコマンド1-コマンド2
```

```bash:サブコマンド1-コマンド1
$ VERBOSE=1 python -m mycommand.cmd --debug cli1-grp1 cli1-grp1-cmd1 --opt 123
{'obj': {'debug': True, 'verbose': True}, 'opt': '123'}
```
#!/bin/sh じゃないよ！ #!/usr/bin/env bash だよ！！ Mac/Linuxの海を泳ぎ切る話

#tl;dr
クロスプラットフォームするなら
スクリプトの一行目はこれで書こうね、の話

```
#!/usr/bin/env bash

# 以下だと困るケースがある：
#!/bin/sh
#!/bin/bash
#!/opt/homebrew/bin/bash
```

# 経緯
アプリ用データ/ePub作成からML用まで、「画像の出し入れ」用の各種スクリプトを書いているのですが、
以下の遍歴で継ぎ足してきたスパゲッティになっています：

    1. ローカルMac用 (自分だけ使ってた頃)
    2. Linuxサーバ用 (みんなで使おうね版)
    3. クラウド(Docker)用 （お客さんも使ってね版）

なんですが、数年間継ぎ足しを続けてる間に保守が大変になってきました。

# Docker で全部やっちゃえばよくね？
一時期、「Docker用をローカル」でも使ってたんですが、
そこに出てきた<b>Apple Silicon</b>。
フルに性能を活かすなら今はまだローカルで動かすのが最善なようで。

中身は imagemagick と poppler を諸々オプション付けて GNU Parallel で実行するだけのスクリプトなので
移植性は本来そんなに悪く無いはずなのです。

そんなわけで基本のキ、「shebangの書き方」からちゃんと整理する事にしました。


# 正解はこれ
```
#!/usr/bin/env bash
```
ほぼどのOSでも、確実にbashを呼び出してくれます

## 僕の環境で正解じゃなかったけどよく見る書き方 1
```
#!/bin/sh
```
元祖にして至高、これ以外だとぶん殴るって方もいるのですが
これだと下記のように「どのシェルが呼ばれるかわからない」問題が発生します。
対策は「どのシェルでも動くように書け」なんですが、すでにあるコード資産(bash専用/zshはNG)があるのを宗教上の理由だけで書き直すのは何かの哲学に反する気がするので今回はNG

/bin/sh で呼ばれるシェル一覧：
Ubuntu : dash 又は bash 
Mac (10.13以前) : bash 3
Mac (10.14以降) : zsh 5.8 又は bash 3
Mac (Homebrew で bash 入れて chsh した) : bash 5.1

## 僕の環境で正解じゃなかったけどよく見る書き方 2
```
#!/bin/bash
```
今試したらOSX内蔵の bash3.2 が開きました。
現行のBash5系に統一したいのでNG。

(MacOS 10.14後期以降、zsh へのエイリアスに変更されたとか目にした気がしたんだけど勘違いだったのかな)

## 僕の環境で正解じゃなかったけどよく見る書き方 3
```
#!/opt/homebrew/bin/bash
```
これはレアケースかと思いますが、MacのHomebrewの標準インストールパスです。
Bash5を明示して使いたい場合にこう書くマカーの方も居たんですが、
当然Linuxではエラーで動かなくなります。


## 「DockerがM1対応するの待てば良いのに」
RC版が出ましたね。
なんだけどそこにM1があったので。
M1マヂはええっすわ。すんごい快適
期間の検索条件についてのお話。

# 問題

突然ですが、問題です。

とあるシステムの利用期間を管理しているユーザマスタが存在するとします。

2021/4/1 ～ 2021/10/1 の期間で

このシステムを1日でも利用できるユーザを検索したい場合
※下の図で言うと、ユーザA ～ ユーザG までを検索結果として出力したい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146632/f8866ac7-642a-57b4-4d8f-a0fb6e874d0a.png)
どのように検索条件を書くのが最適でしょうか？

# アプローチ

このような場合、図にして考えると割と考えやすいですが
面倒臭がって頭の中だけで考えて結論を出すと、結構ゴチャゴチャした条件を書きがちな気がします。(私だけか？)

地道にアプローチしていくと、
ユーザAが該当するにはユーザAの利用期間が2021/4/1 ～ 2021/10/1 内にあればいい。
でもこの条件だとユーザAとユーザDしか該当しないな。。。
じゃあユーザ利用期間(From)が2021/4/1 ～ 2021/10/1、またはユーザ利用期間(To)が2021/4/1 ～ 2021/10/1 にすれば。。。ユーザEが該当しない！
といった具合になります。
(私だけか？)

# 答え

実はユーザF と ユーザG を検索する条件だけを書けば、ユーザA ～ ユーザG が検索条件に該当します。

よって、
**2021/4/1 <= ユーザ.利用期間(To)  かつ  ユーザ.利用期間(From) <= 2021/10/1**

が答えになります。

# おわりに

備忘録代わりです。
現在の状況

<h1>プログラムに慣れる</h1>
　今自分は「すっきりわかるサーブレット&JSP入門」という本を読んで
プログラムを書いています。この本は「すっきりわかるJava入門」という
本を経た人が次に読む本で、実際にＷＥＢサイトがどのように動いている
かが分かってくる内容になってます。

<h2>プログラムの練習</h2>
　この本の10章では簡易的な掲示板を作って開発を体験することができます。
最終的にこの掲示板が見なくても作れるようになることを目標にプログラム
のコーディングをしています。
内容としてはユーザーが書いた内容をほかのユーザーが視認できるものを
作っています。


<h2>最後に</h2>
しばらくSpringBootを使って開発をしていてJavaのコード内容を
忘れてしまったのでこれを機会にまたJavaの再復習を行いたいと思う。
Firestore: Reference型をJSONにするときの挙動を変える

#前置き

例えば以下のようなデータを**Functionsで**Firestoreのドキュメント`users/12345`に入れたとする．

```json
{
    "display_name": String,
    ...
    "userDetail": Reference
}
```

Functionsでこのデータを取ってきてJSONでクライアントに返すAPIを作る(`error()`とか`success()`は別で作ってあるって言う体で...)．

```JavaScript
app.get("/users/:id", async (req, res, next) => {
    const users = await db.doc(`accounts/${req.params.id}`).get();
    if (!users.exists) {
        error(res, 404, "users_created", "You've not created first user.");
        return;
    }
    success(res, users.data());
    return;
});
```

するとこんな感じのデータが返ってくる

```json
{
   "display_name": "Bony_Chops",
   ....
   "userDetail": {
       "_firestore": {
           "_settings": {
               "projectId": "nicha-nnct",
               "firebaseVersion": "9.5.0",
               "libName": "gccl",
               "libVersion": "4.9.4 fire/9.5.0",
               "ssl": false,
               ....
           }
           ....
       }
   ...
   }
}
```
`Reference型`がめっちゃでかい上ヤバそうな値を返してくる．これはまずい．

# 失敗例

Reference型の正体である`firebase.firestore.DocumentReference`クラスの`toJSON`を`prototype`で上書きします(何故か補完で`firebase.default.firestore.DocumentReference`だったのでそちらで...)．とりあえず，そのReferenceが指すpathを返却するようにしてみます．

```javascript
const firebase = require("firebase");

firebase.default.firestore.DocumentReference.prototype.toJSON = function () {
    return this.path;
}
```
でもさっきの結果は変わりませんでした．なんで？？？

# 結論

Firestoreへのアクセスを`db = admin.firestore();`で行っていたので`admin.firestore.DocumentReference`を使うのが適切だったみたいです．

```javascript
const admin = require('firebase-admin');

admin.firestore.DocumentReference.prototype.toJSON = function () {
    return this.path;
}
```
## 結果
```json
{
    "display_name": "Bony_Chops",
    ...
    "userDetail": "users_detail/12345"
}
```
これでいい感じ．
TS + react hooks のstateを親コンポーネントから子コンポーネントへどうやって渡すか

set~ のほうがうまく送れなかった
関数にしてpropsとして送る

親
hooks
const [show, setShow] = useState(false);
･･･

return(
<子コンポーネント show={show} off={(): void => setShow(false)}/>
)

子
interface props {
  show: boolean,
  off: () => void
}

const NewModal: React.FC<props> = ({ show, off }) => {
~


//show, off が使える~
}



参考
https://qiita.com/akifumiyoshimu/items/ec9fdb9dd7d649c2f3dc
PowerAutomateDesktopとは

#プログラミング勉強日記
2021年3月24日
最近、PowerAutomateDesktopという単語を見かけたので気になって調べてみた。

#PowerAutomateDesktopとは
　2021年3月2日にMicrosoftが出した公式のロボティクス・プロセス・オートメーション(RPA)ツール。
　RPAは、企業の変革に欠かせないツールとされていて、事業作業を担うホワイトワーカーがパソコンなどを用いて行う一連の作業を自動化できるソフトウェアロボットのことである。(参考：[「RPA」（ロボティックプロセスオートメーション）の定義は？](https://www.sbbit.jp/article/cont1/37189))
　Power Automateの機能は大きく分けて、UIの自動化(RPA)、APIによる自動化(DPA)、AIを活用(AI)の3つある。Power Automate Desktopの一般提供か開始され、デスクトップフローの強化が行われている。

#PowerAutomateDesktopのインストール方法
　Microsoftのアカウントがあれば、すぐに導入することができる。[PowerAutomateの公式サイト](https://flow.microsoft.com/ja-jp/)にアクセスして、すぐにインストールすることができる。
　インストール方法は[こちらのサイト](https://gigazine.net/news/20210303-power-automate-desktop-for-windows10/)に詳しく乗ってたで、気になる方は見てください。

#参考文献
[10分で理解する「RPA」、今求められるRPA人材の教科書](https://www.sbbit.jp/article/cont1/37189)
[PowerAutomateDesktopがスゴ過ぎたので紹介しておく。](https://yizm.work/powerautomate/powerautomatedesktop%E3%81%8C%E3%82%B9%E3%82%B4%E9%81%8E%E3%81%8E%E3%81%9F%E3%81%AE%E3%81%A7%E7%B4%B9%E4%BB%8B%E3%81%97%E3%81%A6%E3%81%8A%E3%81%8F%E3%80%82/)
[話題のPower Automate Desktop版を学ぼう！RPALT！](https://rpa-bank.com/column/51801/)
【TypeScript】色々な型定義について　〜備忘録〜

今回は関数のパラメータに型定義する方法をについてざっくり学んでいきます！
間違い等あればご指摘して頂ければと思います！

#オプションパラメータについて
オプションパラメータは「？」をつけることでパラメータを使っても使わなくていいと宣言？することができます！
ただし、**オプションパラメータは最後に宣言する必要があります！**

実際にコードを書いてみます

```prameters.ts
export const isUserSignedIn = (userId:number,name?:string):boolean => {
    if(userId === 1){
        console.log(name)
        return true
    }else{
        console.log('noName')
        return false
    }
}

```
`isUserSignedIn(1,'takeda');`とすることでconsoleはtakedaを表示します！
`isUserSignedIn(2);`とするとelseが走るので、noNameが表示されます！
isUserSignedIn(2)はnameを引数に指定してませんがオプションパラメータがあることで指定しなくてもOKです！

#デフォルトパラメータについて
引数がなかった場合は初めに指定した値が代入される

```prameters.ts
export const isUserSignedIn2 = (userId:number,name="suzuki"):boolean => {
    if(userId === 1){
        console.log(name)
        return true
    }else{
        console.log(name)
        return false
    }
}

```
`isUserSignedIn2(1)`をパラメータに渡すとnameの指定がないのでsuzukiが表示される。
`isUserSignedIn2(1,'yamada')`を引数に指定するとyamadaが表示される
これがデフォルトパラメータです！


#レストパラメータについて
 不特定多数の引数を受け取りたい時に使用します！

```prameters.ts
export const value = (...num:number[]) => {
    console.log(num)
}
```
...と記載することで不特定多数のnumber型を配列に格納してくれます！

`value(1,2,3,4,5)`で呼び出すことでconsoleは[1, 2, 3, 4, 5]と表示されます！

#呼び出しシグネチャ
今まではパラメータに型定義してましたが、関数に型定義する方法です！
書き方が２種類あるので紹介していきます！

####正式な記法

```prameters.ts
type messageSignenecher = {
    (message :string):void
}

export const logMeesage :messageSignenecher = (message) => {
    console.log(message);
}
```
オブジェクトに似た形式で記載することができます！
voidはreturnを返さない時に記載する特別な指定方法です！

####省略記法
```prameters.ts
type messageSignenecher = (message:string) =>  void

export const logMeesage :messageSignenecher = (message) => {
    console.log(message);
}
```
アロー関数に似た形式で記述することができます！

#最後に
指定の方法がたくさんあって迷うなー。
とりあえずわからなくなったら都度調べればいいか・・・
【Qliksense】フォルダにある複数のエクセルファイルをfor文で簡単に読み込む

#これは何か
毎月蓄積しているエクセルファイルがあるとします。そのすべて読み込むのは、ワイルドカードを使ってロードすれば良いですが、例えば、直近半年だけロードする場合、for eachを使うと楽になるという備忘録です。

■フォルダ例
Sample
└201901_売上.xlsx
└201902_売上.xlsx
└201903_売上.xlsx
・
・
・
└202103_売上.xlsx

#結論
for each 変数 in を使う。変数に該当年月を指定し、nextで繰り返す。

#スクリプト
```sql

for each month in 202010,202011,202012,202101,202102,202103

LOAD
    年月,
    売上,
    顧客
FROM [lib://Sample/$(month)_売上.xlsx]
(ooxml, embedded labels, table is Sheet1);

next
```
セッションを用いたセキュリティ攻撃の知識

#本記事について
プログラミング初心者がセキュリティ攻撃に関する知識とその対策について学んだので、その備忘録として載せております。

どうかお手柔らかにお願いいたしますm (- -)(_ _)ペコリ

##セッション
webサービスにおいて情報を一時的に保存しておく仕組み（概念）
利用者の状態を保持するために使用
保存先はクッキー

##クッキー
ブラウザにある一時的に保存できる保存場所

##セッションハイジャック
XSSなどを用いて、正規利用者ではない者が正規利用者のセッションを取得する手法
セッションが保存されているクッキーを狙った攻撃
セッションIDを盗み出す、または乗っ取る攻撃

クッキーにあるセッションIDという識別番号を盗み出すとセッションハイジャックが成立
クッキーにはセッションIDという識別番号を用いて保存している。

##セッションID
通信中の正規利用者（クラインアント）へ付与される、固有の識別情報
正規利用者を識別するために付与される固有の番号
→ログイン情報などが含まれている

セッションハイジャックをされると正規利用者できる大概のことができてしまう。
→送金や物品購入などができてしまう。

##セッションIDの推測
セッションIDの生成規則に問題があると第三者に予測される。
外部から参照可能な値を用いてセッションIDを推測する。

対策として独自でセッションIDの生成を行わなわず、フレームワークなどの開発ツールを利用する。脆弱性が発見された場合も素早く指摘改善が見込まれるため。

##セッションIDの盗み出し
JavaScriptでは<script>document.cookie</script>というスクリプトでクッキーの情報が表示でき、このスクリプトに変更を加える。攻撃者側にユーザーの情報を送るようにする。

対策①
特殊文字等によるXSSの対策を講じる

対策②
SSLを用いる

##SSL（Secure Socket Layer）
インターネット環境の安全性向上のため、インターネット上の通信を暗号化してくれる技術
→第３者の情報の盗聴や改ざんを防げる

デプロイするHerokeではhttps://＜アプリの名前＞.heroku.com/となっており、SSLに対応している。

SSLに対応したwebアプリケーションなどのURLはhttps://から始まる

対策はXSSを防ぐことや安全なネットワーク環境を用いることが有効

##セッションIDの強制
セッションIDの固定化
セッションIDを攻撃者が外部から強制的に固定する

①攻撃者は普通の利用者としてセッションIDを取得
②ユーザー（正規利用者）に対しての①で取得したIDを強制
③ユーザーは標的アプリケーションにログイン
④攻撃者はユーザーに強制したセッションIDを使って標的アプリケーションにログイン


対策としてはログイン後にセッションIDを変更する。固定化されないように常に変更し続けることが重要。認証を都度変更することが大事。


deviseでも固定化への対策等が行われている。

##CSRF（クロスサイトリクエストフォージェリ）
webアプリケーションなどへ不正なリクエストを行う攻撃手法
攻撃者が正規利用者になりすましてリクエストを送る攻撃
→ログイン情報などを盗み出されアカウントが不正利用される。
　利用者の所持している金銭が利用される

別のサイトを経由して不正なリクエストを行うことからクロスサイト（サイトをまたいだ）リクエストフォージェリ（リクエストの偽装）と言われる。
→サイトをまたいだリクエストの偽装

XSS クライアント側の脆弱性
CSRF サーバサイド側の脆弱性
→総合的な対処が必要

CSRFはすべてのリクエストに行う必要はなく、どのリクエスト対して脆弱性対策が必要かを考えることが重要
他ユーザーから勝手に実行されては困るようなリクエスト（物品購入処理やパスワード変更等）

##HTTPメソッドのデータベースの情報が書き換えられるためのリクエスト
リクエストが不正か否かを判別する手法
対策①パスワードの再入力
CSRF対策以外にも物品の購入などにおいてノイ利用者の意思の念押し、共用のPCにおいて正規利用者以外の利用者が重要な処理を実行するのを防ぐ、パスワード変変更等において効果あり

対策②リクエストへのトークン（秘密情報）の埋め込み
攻撃者が知り得ないトークン（秘密情報を）を要求するようにする。

##トークン
１度だけ使用可能なパスワード
ユーザーの認証に用いられれる秘密情報
ユーザーのブラウザ上のみに保管されるもの


Ruby on Railsのフォームのヘルパーメソッドはトークンをおいている。トークンはログイン情報と照らし合わせることができるため、不正なリクエストかどうか判別できる。送信者のブラウザで保存されたトークンなので、固有のものとなるため。

トークンが存在するのはPOST、DELETE、PATCH、PUTなどのHTTPメソッドの時のみ、重要な処理についてはこれらのHTTPリクエストのメソッドになるようにする、

CRSFを防ぐにはトークンを埋め込んだりパスワードを再入力させたりするなどして、リクエストの際正規利用者しか知り得ない情報を用いることが有効。

以上です