OmniAICreator/Qiita-1.07M huggingface dataset:
SagemakerでEndpoint configurationからもう一度Endpointを作る方法（pytorch）


# 背景

SagemakerのNotebookから学習を行い、そのままEndpointを作って使うことはあると思います。
使い終わったEndpointは削除すると思いますが、もう一度Endpointを作りたいと思った時に学習からやり直すのはつらいです。
そこで、 もう一度Endpointを作成する方法を模索しました。

# 前提

この記事ではpytorchを使って試しています。他のフレームワークは試していません。
既に一度Endpointの作成までは行っていることを前提としています。
実行は全てSageMakerのNotebookで行います。

# Endpointを作る方法


## 1. Endpoint configurationからEndpointを作成

下のコードを実行することでEndpointが作成されます。

```python
%%time

import time

#任意の名前
endpoint_name = 'test-endpoint'

#ここに学習から実行した時のEndpoint configurationのNameを指定します。
endpoint_config_name = 'sagemaker-pytorch-2019-08-06-12-14-22-209'

print(endpoint_name)

create_endpoint_response = sm.create_endpoint(
    EndpointName=endpoint_name,
    EndpointConfigName=endpoint_config_name)

print(create_endpoint_response['EndpointArn'])

resp = sm.describe_endpoint(EndpointName=endpoint_name)
status = resp['EndpointStatus']
print("Status: " + status)

while status=='Creating':
    time.sleep(60)
    resp = sm.describe_endpoint(EndpointName=endpoint_name)
    status = resp['EndpointStatus']
    print("Status: " + status)

print("Arn: " + resp['EndpointArn'])
print("Status: " + status)
```

## 2. 作成したEndpointをNotebookから実行できるように

作成したEndpointをNotebookから実行できるようにします。


```python
from sagemaker.predictor import RealTimePredictor, npy_serializer, numpy_deserializer

predictor = RealTimePredictor(endpoint_name,
                serializer=npy_serializer,
                deserializer=numpy_deserializer,
                content_type='application/x-npy',
                accept='application/x-npy'
)
```

作成したpredictorは、学習から行う時と同じように推論に使うことができます。

```python
y = predictor.predict(data)
```
Fail2BanでRainLoopへの不正アクセスをBANする

# はじめに
Webベースのシンプルなメールクライアント**[RainLoop](https://www.rainloop.net/)**をサーバーにインストールすることで、Webブラウザから自分のメールアカウントにアクセスすることができます。
本記事では、**[Fail2Ban](https://www.fail2ban.org)**を使ってRainLoopへの不正アクセスを対策し、不正なログインを試みたクライアントのIPアドレスをBANします。

# 対象読者
**RainLoop**を導入するサーバー管理者。

# 環境
- Debian 9.9
- Fail2Ban 0.9.6-2
- RainLoop v1.13.0

## Fail2Banのインストール
APTでインストール。

```bash
$ sudo apt install fail2ban   
$ sudo service fail2ban start
```

## RainLoopのインストール
RainLoopは[公式サイト](https://www.rainloop.net/downloads/)から「Community edition」をダウンロードして`/var/www/rainloop/`に展開します。
基本的な設定は他の記事で解説されているので割愛します。

# 不正アクセスの対策をする
RainLoopとFail2Banの設定を変更します。[^1]
[^1]: https://github.com/RainLoop/rainloop-webmail/issues/1580

## RainLoopのログ出力を設定
RainLoopの設定ファイルは`/var/www/rainloop/data/_data_/_default_/configs/application.ini`にあります。
[logs]セクションの以下の項目を変更し、ログ出力時のデフォルトのタイムゾーンがUTCになっているのを日本標準時(システムのタイムゾーン)に合わせます。
また、出力されるログのファイル名を`auth-fail.log`にしておきます。

```ini:application.ini
time_offset = 9

auth_logging = On
auth_logging_filename = "fail2ban/auth-fail.log"
auth_logging_format = "[{date:Y-m-d H:i:s}] Auth failed: ip={request:ip} user={imap:login} host={imap:host} port={imap:port}"
```

## Fail2Banに設定を追加
`/etc/fail2ban/filter.d/rainloop.conf`を作成して`auth-fail.log`の出力にマッチするフィルタを設定します。

```conf:rainloop.conf
[Definition]
failregex = Auth failed: ip= user=.* host=.* port=.*$
```

また、`/etc/fail2ban/jail.local`を作成し、RainLoop用の設定を追加します。ここでは、作成した`ffilter.d/rainloop.conf`をフィルタに設定し、対象ログに`auth-fail.log`を指定します。

```conf:jail.local
[rainloop]
enabled = true
filter  = rainloop
logpath = /var/www/rainloop/data/_data_/_default_/logs/fail2ban/auth-fail.log
```

設定ファイルを作成したら、`fail2ban-client`コマンドから設定を読み込みます。

```bash
$ sudo fail2ban-client reload
```

これにより、`jail.conf`の設定に基づき、600秒以内に5回ログインに失敗したクライアントのIPアドレスを600秒間BANし、サーバーへのアクセスを遮断するiptablesが設定されます。なお、この設定を変更する場合も`jail.conf`は変更せず、`jail.local`に新たな項目を追加して上書きします。

## 動作確認
`/var/log/fail2ban.log`から、ログインの失敗と対象のBANが確認できます。また、`iptables -L`コマンドより**Chain f2b-rainloop**が追加されていることが分かります。
なお、BANされている間はSSHでサーバーに接続することもできなくなってしまうため動作を確認する際は注意しましょう。
php-master-changes 2019-08-15

この日は 循環参照 GC の実装修正、typo の修正、ArrayObject::__unserialize() で SEGV が起きる問題の修正、SimpleXML::asXML() の引数処理の修正、arginfo の PHP スタブへの移行、sqlite3 のリファクタリング、ドキュメントの更新があった！

# 2019-08-15
## nikic: Fixed bug [#78412](https://bugs.php.net/bug.php?id=78412)
- https://github.com/php/php-src/commit/fcabe7e5e44067319f9c4123d71a7cea6a823af4
- [7.2~]
- 循環参照 GC の実装で、Generator の $this の参照カウントのアサーションでこける問題の修正

## nikic: Fix reference printing in GC tracing
- https://github.com/php/php-src/commit/c238b5bbefd9d3c012c587fc6e9fd8ce98546261
- [7.3~]
- 循環参照 GC の実装で、デバッグ用の GC トレース出力での参照の出力を修正

## cmb69: Update credits_ext.h
- https://github.com/php/php-src/commit/8fb297cd82328c765ab9a2fe12fb8770352b38d9
- credits_ext.h の不要なスペースの削除

## zhdaniel: Fix typo
- https://github.com/php/php-src/commit/844b261e3a19200e9deaa6368efa74d94db6434e
- sapi/fpm で、コメントの typo を修正

## nikic: Fixed bug [#78409](https://bugs.php.net/bug.php?id=78409)
- https://github.com/php/php-src/commit/34885408db9e16c48e8c25195c39e0ef758faaaa
- [7.4~]
- ext/spl で、ArrayObject::__unserialize() で SEGV が起きる問題の修正

## nikic: Normalize SimpleXML::asXML() argument handling
- https://github.com/php/php-src/commit/743d27631fa600f071d4b3b1cf061708a9866f5b
- ext/simplexml で、[SimpleXML::asXML()](https://www.php.net/manual/ja/simplexmlelement.asxml.php) の引数処理を通常の ZPP で行うよう修正

## duncan3dc: Convert sqlite3 functions arginfo to php stubs
- https://github.com/php/php-src/commit/107c1e0c1bc1cc2acdd904ffaff563ae4d021ef7
- ext/sqlite3 で、arginfo の PHP スタブへの移行

## cmb69: Don't repeat yourself
- https://github.com/php/php-src/commit/e3ebdb6be2adac499e5d801bda10294710f46d3d
- ext/sqlite3 で、[SQLite3Stmt::bindParam()](https://www.php.net/manual/ja/sqlite3stmt.bindparam.php) と [SQLite3Stmt::bindValue()](https://www.php.net/manual/ja/sqlite3stmt.bindvalue.php) の実装を共通化

## cmb69: Unify ZPP handling in ext/sqlite3
- https://github.com/php/php-src/commit/a3abbc092009fbc399c118a36d53549d18a6c08c
- ext/sqlite3 で、zend_parse_parameters_throw() をやめて zend_parse_parameters() に統一し、不要な引数エラー通知の RETURN_FALSE を削除

## Victor Gazotti: creating stubs for echant ext
- https://github.com/php/php-src/commit/75a3213d19c98039ffe9ef433e57ed84f419519b
- ext/enchant で、arginfo の PHP スタブへの移行

## cmb69: Update NEWS
- https://github.com/php/php-src/commit/49666474435ff0db2f96fcb00fb04ff7fcfcec57
- NEWS の更新
超シンプルなタイマーを作る-01　調査編

# なぜタイマーを作るのか

アナログ電子回路の学習のため、タイマーを作ります。
動機としては、初学者向けの題材を調べた中で、
比較的簡単そうであり、なおかつ実用性がありそうだったから。
あと、今後の制作に応用すると色々捗りそうというのも理由の一つ。

ちなみに、ルールとしてはマイコンは使用しない代わりに、LSI等の便利なICは使用します。
Arduino使ったら機能としてはすぐ出来るけどそれでは意味がないので。

ICを一切使用しないタイマーについてお調べの方はあまり参考にならないかと思いますので、
「ディスクリート」をキーワードに検索してみてください。
IC、LSI、ディスクリートについては以下を参照。

『FAQ 1009021 : ICとLSIの違いは？ - RENESAS』
https://ja-support.renesas.com/knowledgeBase/17793343

# 超定番IC「555」

まずはタイマーってどうやって作れば良いのか？
という所から調査しました。

電子工作でタイマーというと、
「555」というタイマーICがすぐに見つかります。
8ピンの比較的シンプルなICですが、
タイマーだけでなく発振回路など色々と応用が効くようで、
かなり色々な電子機器に使用されている超定番との事。

この「555」を使用したタイマーを作成する事にします。

# 「555」の動作の仕組み

わずか8ピンなのに、ここを理解するのになかなか苦労しました。
色々なサイトを調べ回った結果、以下のサイトが比較的わかりやすいかと。

『ＬＭ５５５　目次 - ブレッドボードラジオ』
http://bbradio.sakura.ne.jp/555.html

ここの説明を軸に、他のサイトで不足部分を補ったら大体の機能がわかりました。

まず前提として、

**みんな細かく説明しすぎ！！！！！**


抵抗値がどうとか何μFとか回路図とか、
全部細かく書いちゃうと私みたいな初学者は拒否反応を示しますよ！

このICも8ピンあるけど、実際は4・5ピンはあまり使用されないらしいし、
初学者からすると拡張機能的なものなので、最初は無視して良いと思います。

初学者には仕組みのイメージがわかりづらいので、
なんとなく自分なりに理解したイメージを書いてみます。

# 「555」とは、バケツである

よく電子回路は水で例えられたりしますが、
555は電流や電圧以上に水での例えがしっくりくるなと思いました。

タイマーというからには、
・自動で
・時間を計る
この2点は必須です。

では、555はどういう仕組みかというと、

**バケツです。**

どんなバケツかというと、
・合図があると水が注がれる
・一定量（2/3）になったら自動的に止まり、排水される

つまり、注ぐ水量とバケツの大きさで計る時間を調整する訳です。

基本的には以下のように動作します。

![単安定動作_20190816.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467163/a958b40e-71b6-4642-6eed-9355f121dc40.png)
『タイマーIC555を利用した回路の設計 - 山本ワールド』
http://yamatyuu.net/el/555/index.html

1.トリガが入ると、
2.充電が開始され、OUTが出力される
3.充電が2/3に達すると、
4.放電され、OUTがOFFになる

上記サイトの「単安定動作」の部分からイメージ画像をお借りしました。
非常にわかりやすい。
初学者にはこれぐらいシンプルで良いのです。

# 「555」のピン配列と役割

ここで各ピンの動作を説明。
![555ピン配列_20190816.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/467163/27f1e9da-3222-0d9f-7ba2-2ad19f73eb0c.png)

8：Vcc（電源）
1：GND
言うまでもないですね。
電子回路の基礎、このICの電源部分です。

2：トリガ
前述の「合図」です。Vccから電源が来るとスタンバイ状態になります。
このトリガをきっかけに以下が作動します。

3：出力
トリガから合図が来ると出力を開始します。
6：スレッショルド
「しきい」です。デフォルトでは、電源電圧の2/3になっています。
トリガから合図が来ると出力され、コンデンサに充電を開始します。

7：放電
スレッショルドが「しきい」に達すると出力がOFFになり、
コンデンサはIC内の回路を経由してGNDへ放電され0Vになります。

4：リセット
5：コントロール電圧
現時点ではオプションと考え、説明しません。
4は比較的使うみたいですが、5はほとんど使われないみたいです。

# 次回予告

ある程度調べたので、さっさと555を購入します。
実際に動作させてみて、壁に当たれば否が応でも座学する事になるので。
買ったらブレボで一番シンプルな回路を組んで動作テストしてみます。
購入→テスト→回路図作成→レイアウト作成→加工
って感じで進める予定。

ご意見、ご感想、ご指摘などなど、なんでもお待ちしてます。

# 参考URL
『FAQ 1009021 : ICとLSIの違いは？ - RENESAS』
https://ja-support.renesas.com/knowledgeBase/17793343
『ＬＭ５５５　目次 - ブレッドボードラジオ』
http://bbradio.sakura.ne.jp/555.html
『タイマーIC555を利用した回路の設計 - 山本ワールド』
http://yamatyuu.net/el/555/index.html
R_基本統計量を自作（平均値）


# 平均値
```R
#平均値
result_mean <- function(x)
{
  sum<-0
  n<-length(x)
  for(i in 1:n)
  {
    sum <- sum+x[i]
  }
  mean<-sum/n
  return(mean)
}
```
#Rのもともとある関数mean()で動作確認

```R
x<-c(10,20,30,40)
result_mean(x)
[1] 25
mean(x)
[1] 25
```
Ansible AWXでSSHのフィンガープリントを無視する設定

# 概要
AWX で SSH のフィンガープリントをチェックせずにplaybookを実行できるようにします。

# 問題
AWX でplaybookを実行したとき、対象のIPアドレスが同じだが ssh の
フィンガープリントが異なる場合に以下のエラーが発生し接続に失敗します。

![スクリーンショット (25).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162569/1299aa88-3c69-83b7-4035-c7fd1475a583.png)



```yml:実際のメッセージ

{
    "msg": "Failed to connect to the host via ssh: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @\r\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\nIT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\r\nSomeone could be eavesdropping on you right now (man-in-the-middle attack)!\r\nIt is also possible that a host key has just been changed.\r\nThe fingerprint for the ECDSA key sent by the remote host is\nSHA256:fZvUoJgp8hxdmqRe7W5z08Y0gbo1jw3V3M8pdBoSH9E.\r\nPlease contact your system administrator.\r\nAdd correct host key in /root/.ssh/known_hosts to get rid of this message.\r\nOffending ECDSA key in /root/.ssh/known_hosts:1\r\nPassword authentication is disabled to avoid man-in-the-middle attacks.\r\nKeyboard-interactive authentication is disabled to avoid man-in-the-middle attacks.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
    "unreachable": true,
    "changed": false
}

```

例えば、[前回の記事](https://qiita.com/solaris64sparc/items/4e777cbea983075175d2)で作成した環境を以下のようにリセットした場合、コンテナ起動時に SSH のフィンガープリントが再生成されるため、同様の現象が発生します。

```shell
dokcer-compose.yml があるディレクトリに移動
# cd centos7

コンテナをすべて停止、削除
# docker-compose down

コンテナを起動
# docker-compose up -d
```



# 問題を回避するための設定
AWX にログイン後、「設定」から「ジョブ」をクリックします。

![スクリーンショット (26).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162569/88e86537-b799-9c72-0cab-0d5b1ca7d499.png)


画面下の「追加の環境変数」を追加の「環境変数（変更後）」以下のように変更します。

```json:追加の環境変数（変更前）

{
 "HOME": "/var/lib/awx"
}

```


```json:追加の環境変数（変更後）

{
 "HOME": "/var/lib/awx",
 "ANSIBLE_SSH_ARGS": "-C -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
}

```

「保存」をクリックして設定を反映させます。

![スクリーンショット (28).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162569/28607a70-d203-cb2d-71eb-8fd7faaf32c6.png)

以上です。
javascriptでワンライナーの再帰関数

# javascriptでワンライナーの再帰関数
何とかワンライナーで再帰関数いけないかなと思って調べたところ
一旦、適当な変数に代入してそれを関数内から参照すればOKでした。

```javascript

 (f = (x) => x < 10 ? f(x+1) : x)(0); //fはアロー関数内からでも見える
```

実際のコードではまず使わないかと思いますけど、
コードゴルフの類なら使えるかも知れません。
Laravel collectionの比較（イコール）

##Collection関数を使って、比較を行う（イコール）

```
$oldCollection = collect([1]);
$newCollection = collect([1]);
$diffCollection = $oldCollection->diff($newCollection);

if($diffCollection->isEmpty() && $oldCollection->count() === $newCollection->count()){
　　　　dd('hello');
}
```

これでCollectionのイコール比較完了
Pythonで始めるwebスクレイピング_[1]導入編

## Pythonを導入から
過去に業務でPythonを使用したことはあったのですが、しばらくの間離れていたところ、ほとんど最初から学び直す羽目になりました。
会社の都合で使用する機会がありそうなので、触ってみることにしました。
ただ言語を学ぶのだと面白く無さそうなので、ついでにwebスクレイピングやクロールをしながら学んでいこうと思います。


### 実行環境
pythonは２系がサポート終了するので、３系でやることにします。
２系の経験がある方は２系と３系の違いは以下のようなまとめてくれている記事を参照するといいと思います。

* https://www.python-izm.com/tips/difference_in_version/
* https://qiita.com/cstoku/items/353fd4b0fd9ed17dc152

## スクレイピングのための道具を準備する
まず手始めに[こちら](https://qiita.com/Azunyan1111/items/9b3d16428d2bcc7c9406)を参考にスクレイピングをやってみます。
pythonとBeautiful Soupをインストールします。

内容はざっくり説明すると、

* urllibを使用し、HTTPリクエストする
* Beautiful soupを使い、取得したHTMLをパースする
* 内容を解析し、処理する

です。
参考記事ではurllib2を使用していますが、python3系ではurllibというライブラリを使用します。
共にpythonの組み込みライブラリです。

csvに出力しているところは、必要に応じてでいいと思うので、とりあえず今はやらないこととします。

#### ライブラリをインポートする
まず必要なライブラリをインポートし、コード中で使用できるようにします。
ちなみに、`# coding: UTF-8`の記述はpython3系では不要です。
先ほどのpython２系と３系の違いをまとめてくれている記事で、２系では文字列型としてunicode型とstr型が有ったが、３系は全てunicodeになり、str型オンリーになったと書いてありました。

##### モジュールのインポート
```py
import math
```
他の言語でも見るやつです。
> Pythonでは関数やクラスなどをまとめて書いたファイルをモジュールという。

このモジュールをimportする文です。

###### 複数インポートする
```py
import a, b, c
```
とできます。が・・・
PEP8というpythonの標準ライブラリなどに関するコーディング規約があります。
このPEP8によるとこのカンマ区切りのまとめてimportは推奨されないとか。なので、

```py
import a
import b
import ...
```
と書くことが推奨のようです。

##### オブジェクトのインポート
```py
from math import pi
```
モジュール`math`からオブジェクト`pi`をインポートする書き方です。
複数オブジェクトをインポートする時はカンマで区切って記載できます。

```py
from math import pi, radians
```
PEP8ではこちらはカンマ区切りで複数インポートしてOKとのこと。
もっと大量のオブジェクトをインポートしたい時は()で括って複数行にできます。

```py
import math import (
    pi,
    radians,
    sin,
    ....
)
```

さて、これで使用するライブラリをインポートする書き方が分かりました。
早速、`urllib`と`Beautiful Soup`をインポートしてみます。

```py
import urllib.request
from bs4 import BeautifulSoup
```

#### ファイルを読み込み
次に、早速どこかのURLにリクエストを飛ばし、HTMLを取得してみます。

```py
html = urllib2.urlopen('https://www.yahoo.co.jp')
```
これだけです。便利な世の中ですね。
次にこの取得したhtmlをパースします。

```py
html = urllib.request.urlopen('https://www.yahoo.co.jp')
soup = BeautifulSoup(html, 'html5lib')
print(soup)
```
これで取得したhtmlをパースできました。
30日でできる！自作OSのカウント処理で詰まった

30日でできる！自作OS本の中で、何度か『ある一定の時間で何回計算ができるか』という事をします。
しかし、これをするとプログラムが動かなくなってしまいました。
同じことで詰まるのも嫌なのでメモ代わりに書いています。
ただもしかしたら嘘の可能性もあるので、これが嘘だった場合教えてほしいです。Ubuntu18.04 ,qemu-system-i386で動かしています。

これはday15-7のbootpack.cにあるtask_b_main()内の無限ループのコードになります。

```c:bootpack.c
while(1){
    cnt++; 
    //割り込みの余裕を作る
    putfonts8_asc_sht(sht_back,0,300,COL8_FFFFFF,COL8_008484," ",1);
    io_cli();
    if(fifo32_statue(&fifo)==0){
      io_sti();
    }else{
      info = fifo32_get(&fifo);
      io_sti();
      if(info==1){
        sprintf(s,"%d",cnt);
        putfonts8_asc_sht(sht_back,0,144,COL8_FFFFFF,COL8_008484,s,10);
        timer_settime(timer_put,1);
      }else if(info==100){
        sprintf(s,"%d",cnt-cnt0);
        putfonts8_asc_sht(sht_back,0,128,COL8_FFFFFF,COL8_008484,s,11);
        cnt0 = cnt;
        timer_settime(timer_ls,100);
      }
    }
  }

```

変えたのは、cntを増やすところとio_cli()の間に、無を表示させるようにしたことです。
io_cli()は割り込み機能を禁止させる関数で、io_sti()は割り込みをまた可能にさせる関数です。
割り込みがあるか無いかを確認している最中に割り込みがあると、困ってしまうので禁止と許可を繰り返しているのですが、その間隔が短すぎてどうやら割り込みする時間が無くなっているようでした。
なのでほどよく時間を使ってくれる処理をはさみました。
ただこの処理のせいで、残念ながら本に書いてあるほどカウントは稼げません。
MATLABで株価データ自動取得　その3　データ可視化

# これまで
MATLABでYahoo Financeから自動データ収集をして、移動平均を求めるところまでできた。
timetableデータを可視化すると休日や祝日が歯抜けになってしまう問題が発覚。Yahoo Financeのように歯抜けにならないように表示する。

# 期待する表示
timetableをstackedplotやcandle関数で可視化すると以下のように歯抜けになってしまう。今年のGWは10日間も休みだったので、最初のほうに大きな歯抜けがある。
![candlePlot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/fa7007d0-3b9e-dc1e-fe62-5fa467641f9b.png)

上を下（Yahooファイナンスから抜粋） の様に歯抜け無く可視化できるようにしたい。
![Yplot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/48a65cd7-2fd0-adba-8aa3-dbb6f09422d5.png)

# 歯抜けの解消
timetableデータを可視化することで歯抜けが発生するので、時間軸のデータを使わずに表示を行うと歯抜けが解消。

```matlab:
candle(data{1:end, 1:4})    % 休場日が歯抜けになるので並べ替え
legend('Candle')

hold on
% 移動平均を表示
plot(data.AveFilt5,...
    'Color', [0.9 0.3 0.3], 'DisplayName','5day')
plot(data.AveFilt15,...
    'Color', [0.7 0.3 0.1], 'DisplayName','15day')
plot(data.AveFilt30,...
    'Color', [0.5 0.3 0.1], 'DisplayName','30day')
hold off
grid on
```

![candlePlot2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/fefefbb6-305f-43ec-6057-847f00dded66.png)

さらにX軸の目盛り設定を行って

```matlab:
ha = gca;   % 休場日が歯抜けになるので並べ替え
xticksloc = ceil(ha.XTick);
tempTime = data.Time;
for n = length(data.Time)+1:max(xticksloc)+1
    % Plot上のTickはあるが、データが無いところを補完
    tempTime(end+1) = tempTime(end)+1;
end
sz1 = size(tempTime, 1);

% データ長によって軸目盛を可変
if sz1 < 50
    tempTime.Format = 'M月d日';
    ha.XTickLabel = string(tempTime(xticksloc+1));
    xlabel(year(tempTime(end)))
elseif sz1 < 105    
    tempTime.Format = 'yy年M月';
    ha.XTickLabel = string(tempTime(xticksloc+1));
    tempTime.Format = 'yy年M月d日';
    ha.XTickLabel{1} = string(tempTime(xticksloc(1)+1));
    ha.XTickLabel{end} = string(tempTime(xticksloc(end)+1));
    xlabel(year(tempTime(end)))
else
    % 上と同じ。様子を見ながら調整する。
    tempTime.Format = 'yy年M月';
    ha.XTickLabel = string(tempTime(xticksloc+1));
    tempTime.Format = 'yy年M月d日';
    ha.XTickLabel{1} = string(tempTime(xticksloc(1)+1));
    ha.XTickLabel{end} = string(tempTime(xticksloc(end)+1));
%     xlabel(year(tempTime(end)))
end
title([data.Properties.Description '  ' num2str(data.Properties.UserData)])
```

# 色も変えてみた
ついでに・・・
candle関数の中を少しいじって株価が上がったときと下がった時で色を変えてみた。
![candlePlot3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/9bc18d4b-9f06-0ab2-a297-e36d154d9554.png)

変えたのはcandle関数のここ↓

```matlab:
    for i = 1 : numObs
        if op(i) < cl(i)
            color = 'r';        % red
        else
            color = 'b';
        end
        h(i+1) = fill(ax, ...
                [indexLeft(i); indexLeft(i); indexRight(i); indexRight(i)], ...
                [op(i); cl(i); cl(i); op(i)], color, 'Edgecolor',color, ...
                'LineStyle','-','Marker','none','AlignVertexCenters', 'on');
    end
```

だいぶ見やすくなった。

終わり

Thanks to iさん
Google Spreadsheetでチェックボックス一覧のクリア

備忘録
需要がないことはわかってる

```javascript
function myHogefugo() {
  var spreadsheet = SpreadsheetApp.getActive();
  for (  var iy = 2;  iy < 33;  iy++  ) { //行
    for (  var ix = 1;  ix < 17;  ix++  ) { //列
      var tarCell=String.fromCharCode(64+2+ix) + iy; //65=A
      spreadsheet.getRange(tarCell).activate();
      spreadsheet.getCurrentCell().setValue('FALSE');
    }
  }
};
```
QualityForwardのAPIを操作するNode.jsライブラリ

[QualityForward](http://bit.ly/32huG3K)はテスト管理クラウドサービスです。ExcelやGoogleスプレッドシートような一覧形式でテストを一括作成したり、APIから操作もできます。APIはRESTfulなものでシンプルな作りですが、直接URLを叩くような使い方は殆どしないでしょう。

そこで少しでも使いやすくするためにNode.jsライブラリを作り始めました。まだテストスイートの操作しかできませんが、紹介します。

## リポジトリ

リポジトリは以下のURLです。ライセンスはMIT Licenseとなっています。

[goofmint/qualityforward-node](https://github.com/goofmint/qualityforward-node)

## 使い方

まず初期化します。APIキーで初期化します。

```js
import { QualityForward, TestSuite} from 'qualityforward-node';
const client = new QualityForward(API_KEY);
```

## テストスイート取得

TypeScriptで書いています。JavaScriptで利用も可能です。

```js
const testSuites: TestSuite[] = await client.getTestSuites();
const testSuite: TestSuite = testSuites[0];
```

## テストスイート作成、更新

作成と更新は同じsaveメソッドです。

```js
const testSuite: TestSuite = client.TestSuite();
testSuite.name = 'Test suite name';
if (await testSuite.save()) {
  // 作成成功
} else {
  // 作成失敗
}

testSuite.name = '新しいテストツイート名';
if (await testSuite.save()) {
  // 更新成功
} else {
  // 更新失敗
}
```

## テストスイート削除

```js
if (await testSuite.destroy()) {
  // 削除成功
} else {
  // 削除失敗
}
```

## まとめ

ライブラリがあれば、外部システムとの連携であったり、社内データ連係も簡単になるはずです。ぜひお試しください。

[QualityForward](http://bit.ly/32huG3K)
ubuntu 16.04でZFS on linux 0.8.1を使う

ubuntu16.04で現状最新版であるZFS on linux(0.8.1)を使う方法です。
このバージョンでは[zpool remove](https://shakemid.hatenablog.com/entry/2019/02/06/222913)等の便利機能が追加されています

## 前置き
非公式のPPAパッケージを使用します。このアップデートによって既存zfsの動作が不安定になる可能性があります。(自分の環境では安定して動いていますが念の為)

## 手順
[こちらで](https://launchpad.net/~jonathonf/+archive/ubuntu/zfs)公開されているPPAを使用します

```bash
sudo add-apt-repository ppa:jonathonf/zfs
sudo apt update
sudo apt install zfs-dkms zfs-initramfs
```

## 余談
Ubuntu19.10では公式パッケージとしてZFS on linux(0.8.1)が提供されています。
Python 棒の切り分け

#問題
長さn[cm]の1本の棒を1[cm]単位に切り分けることを考えます。
ただし1本の棒を一度に切ることができるのは1人だけです。
切り分けられた棒が3本あれば、同時に3人できることができます。
最大m人の人がいるとき、最短何回で切り分けられるかを求めてください。
例えば、n = 8, m = 3の時は下図のようになり、
4回で切り分けることができます。

```n=8、m=3の場合.
1回目    |1|2|3|4||5|6|7|8|
2回目    |1|2||3|4| |5|6||7|8|
3回目    |1||2| |3||4| |5||6| |7|8| <= 3人しかいないので1ヶ所残る
4回目    |1| |2| |3| |4| |5| |6| |7||8|
        |1| |2| |3| |4| |5| |6| |7| |8|
```

問題1
n = 20, m = 3の時の回数を求めてください。
問題2
n = 100, m = 5の時の回数を求めてください。

> 引用「プログラマ脳を鍛える数学パズル　著者 長井 敏克」

#回答

```answer_1.py
def cutbar(m, n, current, count):
    if current >= n:
        print("Count:", count)
    elif current < m:
        count += 1
        current = current * 2
        cutbar(m, n, current, count)
    else:
        count += 1
        current += m
        cutbar(m, n, current, count)

count = 0
cutbar(3, 20, 1, count)    # Count: 8
cutbar(5, 100, 1, count)    # Count: 22
```

```answer_2.py
def cutbar(m, n):
    count = 0
    current = 1
    while n > current:
        current += current if current < m else m
        count = count + 1
    print(count)

cutbar(3, 20)    # Count: 8
cutbar(5, 100)    # Count: 22
```

#つまづいたところ
ずばり`current += m`や
`current += current if current < m else m`のような、
なぜ`current（棒の本数）`に`m（人数）`などを足すのかということですね。
最後は無理やりおちつかせた形になりました・・・

原因としては、
問題を読んだはじめの印象が「どんどん棒の本数を倍にしていくんだな」というもので、
それに最後まで引っ張られてしまいました。

確かに`answer_1.py`では、途中まで棒の本数を倍にし続けます。
しかし、棒の本数が人数を超えたあとは、
棒の本数が`n（cm）`を超えるまで`m（人数）`をひたすら足し続けるような処理内容でした。
`answer_2.py`に至っては、棒の本数を倍するなど全くせず、
本数か人数を足していくような処理内容です。
結局、「こういうものなんだな」に近いような感じで自分を納得させました。

`再帰`や`三項演算子`を使う機会となったのは、いい経験になりました。
今後自然に書けるようになって行きたいです。

以上、ご拝読ありがとうございました。
Dockerの使い方メモ

一時コンテナ作成
docker pull イメージ
docker run --rm -it 【コンテナ名指定】/bin/bash  (—rmはexit後にコンテナを削除)

Dockerfileがある場合のイメージビルド
docker build ./   -t 【タグ(イメージ名前)】
コンテナの作成と起動
docker run -it -d —name 【コンテナ名指定】【上記で指定したタグ(イメージ名前)】  (-p ホスト:コンテナ)
コンテナの中に入る
docker exec -it 【上記で指定したコンテナ名指定】/bin/bash

コンテナプロセス確認
docker ps (-aで止まっているコンテナも表示)
コンテナ停止と起動
docker stop/start 【コンテナ名】
コンテナイメージの削除
docker rm 【コンテナ名】
docker rmi 【イメージ名】

全コンテナ停止
docker stop $(docker ps -q)
全コンテナ削除
docker rm $(docker ps -q -a) 
全イメージ削除
docker rmi $(docker images -q)
容量の確認と不要なコンテナイメージ削除
docker system df
docker system prune


＜Dockerfile書き方＞
RUN→イメージ作成時
COPY→ディレクトリのコピー
CMD→コンテナ起動時
WORKDIR→コンテナ内でcdコマンド
LaravelでログインしてそのままそのURLに進む方法

Laravelでログイン後に元の画面に戻る方法は探せばそれなりに出てくるけど、
ログイン後に行こうとしてたURLにそのまま行く方法がなかなか見つからなかったので、自己解決した結果を書いておきます。

考え方としてはログイン画面を開く直前に、行こうとしてるURLを残しておいて、ログイン後そこに戻る、という手法を取ります。


さて、未ログインのままログインが必要な画面に行こうとするときにはAuthenticateミドルウェアを通るため、ここでログイン画面へ行こうとするときにURLを保存します。
このとき、URLはセッションに保存しておきます。

```php:app/Http/Middleware/Authenticate.php
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {
            session(['url.intended' => url()->current()]);
            return route('login');
        }
    }
```

そしてLoginControllerのredirectPath()でセッションを見て、
保存されていたURLにリダイレクトすることで、もともと行こうとしてたURLに行けます。

```php:app/Http/Controllers/Auth/LoginController.php
    public function redirectPath()
    {
        return session('url.intended') ?: $this->redirectTo;
    }

    public function showLoginForm()
    {
        if (empty(session('url.intended'))) {
            session(['url.intended' => url()->previous()]);
        }
        return view('auth.login');
    }
```

ちなみにこのLoginControllerでは、「ログイン後に元の画面に戻る」もやりたいので、
showLoginForm()で、セッションが空の場合にリファラーを指定してます。
GASでローカルのエディタ使いたい

僕はvimmerなのでローカルエディタ派なんです。
だからGASもローカルエディタで書きたいんです。

## claspを用意
[clasp](https://github.com/google/clasp)を使うと良いそうです。
`npm` でインストールできます。

```
sudo npm i @google/clasp -g
```
https://script.google.com/home/usersettings
からGoogle Apps Script APIをオンにしましょう。

## ログインする
```
clasp login
```
をすると規定のブラウザが立ち上がり、Googleアカウントを選ぶ画面になります。

## クローンしてみる
前回作ったスクリプトをクローンしてみます。
Script idはスクリプトの編集画面のURLだと `script.google.com/d/xxxxxx/edit`のxxxxxxの部分になります。

```
clasp clone <script id>
```
をするとクローンされ

```
.clasp.json
appsscript.json
コード.js
```
の3つのファイルができると思います。
この中の `コード.js` に前回書いたコードがあります。

## コードを書き換えてプッシュ
`コード.js` を書き換えます。

```js
function myFunction() {
  Logger.log('hello, world! I love local');
}
```
書き換えたら、

```
clasp push
```
これで反映されたはずなので、ブラウザで変わってるか見てみましょう。

![編集画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128044/fcd2b0d7-2b2b-b962-8eb1-909c51c3682f.png)
反映されてますね。

# まとめ
GASをローカルエディタで扱えるように `clasp` を使ってみました。
これで開発が捗るはずです。

# 参考
[なんかいろいろできそうだからGASを始める（超初心者）](https://qiita.com/gungungggun/items/9e0752e4ba6e7eeb813a)
TypeScriptでoptionalなmethodを定義する

# optionalなmethodの定義方法

```ts
interface Hoge {
  foo?(arg1: string): void
}
```

こう書けば、`foo`はあってもなくてもよく、ある場合にはこの定義に従ってくれます。

公式ドキュメントですぐに探せなかった内容なので、ここで共有いたします。

GitHubでは https://github.com/microsoft/TypeScript/issues/6413
こちらに議論されています、2016年ごろにはもうあったとのこと。

# 知らなかったのでつらい思いをした話
このoptional methodの記法を知らなかった僕は、
**関数プロパティでできるじゃん！** という方法で解決していた。

すなわち下記のように、

```ts:関数プロパティでoptionalを実現
interface Hoge {
  foo?: (arg1: string) => void
}
```

関数をプロパティとして定義するのです。

これでもまあ、ほぼやりたいことは実現できていたのですが、なんと衝撃的なことに、

**「[TypeScriptではメソッドと関数プロパティは違う](https://qiita.com/shinout/items/409fb441d7b77836bff5)」**

という事実があり、これにハマってしまったのです。

ということで↑の記事と合わせてお読みくださいませ...


# playgroundへのリンク
https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgBIHsDmKDeBYAKGWRnXQH4AKOKTARgC5kBnMKUTASiYDd1gAJoQC+hQgHpxyQFYMgeQZAZgyAghgDyAaUIIANnGbNkAQWTAAtgAdNEYxHB6M2ZPgKiCEqYGNrQOoMswPnagUYiP8oAiDIAxDIAODGoa2rrIAEJGZhZWNmhYuITEpOjUtIwsbBycDunEyFAQYACuUCDFzs6uyIBCDIDRDIDJDIAGDIDGDID6DICRDICADJ4+-tKBgBYMgBUMgJcMgD8MkTp6AMLx5pbWYLapRUQkZNn0TCAVxgBG0IWOJaXlVTU7dWIEQA
vuejsでsuggestフォームを作る2



[前回](https://qiita.com/hk41/items/be098bdd3ca897b8227f)はvue.jsをDockerで動かすところまで進めました
第2回目はDockerにElasticsearchの環境をつくるところまでです

やりたいことのおさらい
---
キーワードを入力していくとキーワード候補エリア、関連エリア（もしかして、、、これ？みたいな）が表示されるやつを作ります

Elasticsearch環境作成
---

#### 1.Dockerfileを作成

[Install Elasticsearch with Docker](https://www.elastic.co/guide/en/elasticsearch/reference/6.7/docker.html) を参考にDockerfileを作ります

Amazon Elasticsearch ServiceはElasticsearchバージョン6.7まで対応していますね
今回は6.7を利用します

ディレクトリ構成はこんな感じにします

```bash
$ tree
.
├── Makefile
├── docker-compose.yml
├── dockerfile
│   ├── elasticsearch
│   │   └── Dockerfile
│   └── vue
│       └── Dockerfile
```


```dockerfile:dockerfile/elasticsearch/Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:6.7.2
RUN elasticsearch-plugin install analysis-kuromoji
```
日本語を扱いたいのでkuromojiをinstallしています

#### 2.docker-compose.yamlを作成

```yaml:docker-compose.yaml
version: '3'
services:
  vue_app:
    build: dockerfile/vue
    ports:
      - 9000:9000
    volumes:
      - .:/app
    stdin_open: true
    tty: true
    command: /bin/sh

  elasticsearch:
    build: dockerfile/elasticsearch
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - http.host=0.0.0.0
      - http.port=9200
      - http.cors.enabled=true
      - http.cors.allow-origin=http://localhost:9000
      - http.cors.allow-methods=*   
      - http.cors.allow-headers=*      
      - http.cors.allow-credentials=true
      - bootstrap.memory_lock=true
      - 'ES_JAVA_OPTS=-Xms128m -Xmx128m'      
    ports:
      - "9200:9200"
    volumes:
      - esdata1:/usr/share/elasticsearch/data
    networks:
      - esnet
volumes:
  esdata1:
    driver: local

networks:
  esnet:
```
コンテナ削除後でもデータを利用できるように
データボリュームを作成しておきます

また、ブラウザからESのAPIアクセスが可能なようにCORSを設定します
vuejsは9200で起動するので9200を許可しています

#### 3.起動
```bash
$ make start
 ```

#### 4.起動確認
```bash
$ curl -XGET localhost:9200
{
  "name" : "xxx",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "xxx",
  "version" : {
    "number" : "6.7.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "56c6e48",
    "build_date" : "2019-04-29T09:05:50.290371Z",
    "build_snapshot" : false,
    "lucene_version" : "7.7.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

```

次回はElasticsearchにテストデータを用意する予定です
Firebase Crashlytics　Schemeごとの実装

##Firebase Crashlyticsの実装でつまづいた
Firebase Crashlyticsの実装で、表示されるはずのダッシュボードが表示されなかった。

##SchemeごとにFirebaseに登録して解決
Release、STGなどSchemaごとにアプリとして登録する必要があった。
(アプリとして登録する際にダウンロードするGoogleService-Info.plistと、
アプリID、BundleIDなどが一致する必要があった。)

###BundleIDの設定
TARGET　→　Build Settings　→　Product Bundle Identifierで検索　→　SchemeごとにBundleID設定

###Schemeごとにアプリとして登録

###公式の手順に沿ってClashlyticsを実装
Greengrass 覚書

* この資料の範囲
  * Grengrassを使い始めた時にハマりそうな箇所をまとめます。 
  * Greengrass上へのLambdaのデプロイなど、基本設定はスコープ外です。
      * https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/lambda-functions.html

# デバイス起動時にLambdaを実行する

* ユースケース
  * システム起動時のセットアップなど

``` py
import time

def your_function():
    # 適宜実装

# pythonスクリプトがロードされたタイミングで自分で `your_function` を呼ぶ
your_function()

# handlerは無視する
def function_handler(event, context):
    return
```

# デバイス起動中に処理を繰り返す

* ユースケース
  * 常駐アプリを作る


組み込みでいうところのwhileループ的なものを実現する
タイムアウトを防ぐため、事前にLambdaのLong livedの設定が必要です。
https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/long-lived.html

``` py
from threading import Timer

def your_function():
    # 適宜実装
    # 最後に自分を呼ぶ
    Timer(20, your_function).start()

def function_handler(event, context):
    # 最初の一回目は、function_handlerで呼び出し
    your_function()
    return
```

# Greengrass GroupのDevice登録をせずに物理デバイスごとにShadowを利用

### ユースケース
* Greengrassにぶら下がるセンサーやアクチュエータなどの物理デバイスが１つ以上存在する。
* デバイスとGreengrass Coreが稼働するゲートウェイ間の通信にMQTTを使わない。（BLE、シリアルバス、LoRaなどを使う。）
* 各デバイスごとの状態をDevice Shadowを使って管理したい。（一時的なオフラインが想定される環境でのデバイス・クラウド間の状態の整合性をとりたい）

### 対応のしかた
* Greengrass上のLocal Lambdaで、周辺デバイスデータをフェッチする（やりかたは接続方法次第なので割愛）
* 取得したデータをShadowにセット（ `update_thing_shadow` ）
    * ここで、Thing Name を `gatewayID + "-" + sensorID` のようにする。
    * Greengrass のSubscriptionsの設定で以下を追加
        * Source:Lambda関数名、Target ：Local Shadow Service, Topic: `$aws/things/+/shadow/#`
        * Source:Local Shadow Service、Target ：IoT Cloud, Topic: `$aws/things/+/shadow/#`
* ここで、Topicを `#` としてしまうと、Greengrass がIoT CloudへのPublishをハンドリングしてくれないです。注意です。最低でも `$aws/things/+/shadow/#` のような指定が必要です。

### 補足
* ワイルドカードの箇所は、本番では適切に設定するのが望ましいです
  * 詳しくはこちら： https://docs.aws.amazon.com/greengrass/latest/developerguide/config-dev-subs.html
* この例では、デバイスをIoT CoreのThingとして登録していないため、IoT Coreのデバイス一覧としては表示されないです。Thingを作成することで、デバイス一覧を管理する方法もあります。今回のケースだと、プリンシパルのアタッチは不要です。
  * https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/thing-registry.html 
* （Updated）Named Shadowの機能が追加されたため、ゲートウェイにつながる各エッジデバイスごとに個別のShadowを管理することが可能
    * https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-device-shadows.html



### 実装例

だいぶ雑ですが… 
5秒おきにShadowの更新、Shadowの取得を交互に行います。10個のセンサーのShadowを `myGateway-sensor-0` のような名前で管理します。

``` py
from __future__ import print_function
import json
import greengrasssdk
import logging
from threading import Timer

iot = greengrasssdk.client('iot-data')
ggCoreName = "myGateway"
count = 0

def updateShadow():
    global count
    
    sensorId = count % 10
    
    iot.update_thing_shadow(
        thingName = ggCoreName + "-sensor-" + str(sensorId), 
        payload='{"state":{"reported":{"myState":"' + str(count) + '"}}}'
    )
    count += 1
    Timer(5, getShadow).start()
    return

def getShadow():
    sensorId = count % 10
    response = iot.get_thing_shadow(
        thingName = ggCoreName + "-sensor-" + str(sensorId)
    )
    logging.info(response)
    Timer(5, updateShadow).start()
    return

def lambda_handler(event, context):
    return

updateShadow()
```
なんかいろいろできそうだからGASを始める（超初心者）

# GASとは
Google Apps Scriptの略。
「ガス」と呼びます。
Googleが提供し、JavaScriptベースにしたプログラム言語。
Googleが用意するサーバ上で動き、手軽なのと、Googleが提供するサービスとの連携とかがつくれるので、業務効率化とかでよく使われる印象です。

# さぁ始めよう
## 1. Googleアカウント用意
これは説明省略。
普通にGoogleのアカウントを取得するだけです。

## 2. script.google.comにアクセス
https://script.google.com
にアクセスするとGASが使える画面になる
![GAS画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128044/f807ebea-afa3-0f60-69d1-c099b33f4638.png)

## 3. scriptを書く
新規のスクリプトを押すと、コードがかける画面に遷移します。
![スクリプト画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128044/f86db64a-3eef-c077-d44c-19d5b9ffc810.png)

最初のプログラミングは「Hello World」でしょう。
ということで、

```js
function myFunction() {
  Logger.log('hello, world!'); 
}
```

としてやりましょう。

## 4. 実行
画面上の「実行」＞「関数を実行」＞「myFunction」
もしくは、再生ボタンを押しましょう。
そうすると実行されます。
今回はログをだすプログラムなのでなにも実行された感じがしませんが、「表示」＞「ログ」でログをだしてやると以下のようにログが出力されています。
![ログ画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128044/3d45c82d-7f02-ceed-b8cc-d6f0b77401b7.png)

# まとめ
GASを簡単に始めることができました！
これから少しずつできることを増やしていきたいと思います。
Slack上で動く自動翻訳アプリ(Slack Translation App)を開発＆リリースした話

# はじめに
この度、<a href="https://www.kiara-app.com">Kiara Translation</a>というSlack上で動く自動翻訳アプリ(Slack Translation)をリリース＆Slack Appに公開しました。
開発や公開方法、留意すべき点などについて知見を共有し、今後Slack Appを開発される方の参考に少しでもなればと思います。
また多言語で開発やお仕事をされている方の助けになれば幸いです。
(Slackを多国籍＆多言語で使用しており、何か良いソリューションがないかを考えていたところがこのアプリ開発の始まりになります。)
#仕組みと内容物
仕組みと内容ですが、以下のようになっております。
![スクリーンショット 2019-08-16 13.54.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482888/7b32d5f8-9f42-8d7b-caa4-33315729a202.png)

アプリ(bot)をインストールすると、特定のchannelをSubscribeするようになっております。
ユーザーが何かメッセージを投稿すると、自動でメッセージを翻訳＆投稿してくれるような仕組みです。

何をSubscribeするかは<a href="https://api.slack.com/apps">slackのdashboard</a>で指定できます。
例えばメッセージ投稿以外にも、「ユーザーがchannelにjoinした時」、「メンションが送られてきた時」などもSubscribeすることが出来ます。
#アーキテクチャ
アーキテクチャは以下のようになっています
![スクリーンショット 2019-08-16 13.56.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482888/f62a9c66-a596-a215-2e34-042cc5ebba7f.png)
画像下部の緑の箇所はログや分析などで使用しているだけなので、実際の動作部分は赤枠で囲った箇所のみになります。
勿論、開発するアプリによりますが、非常にシンプルな構成でアプリ開発ができるのもSlack Appの良いところです。

#開発方法
ここで、実際にSlackアプリを開発したり公開する際に、(私の)考え方や開発の流れについて簡単にご説明したいと思います。

## 実現したい機能は何か
当たり前ですが、先ずは実現したい機能を決めます。
今回の場合でいうと「(複数の言語に)自動翻訳させる」という部分がそれに当たります。
重要になるのが次で述べる、どのような方法でそれを実現するか、ということです。

## どのような方法で実現するか
上記で述べた機能をどのような方法で実現するかを考えます。
先ず考えるのがトリガーとなる部分です。
今回の場合でいうと「ユーザーがメッセージ投稿した時」に当たります。
その他にも例えば、「リアクション追加」、「ファイルアップロード」など様々な選択肢が存在するので、どのアクションがトリガーになるのかを決めます。

またスラッシュコマンドは使うのか、ボタンやダイアログなどの機能を使用することはあるのかなどいくつかのアプローチが考えられますので、UI、UX部分も詳細に決めていきます。
(私たちは入力の手間を省くためにスラッシュコマンドは使用せずに、ボタン＆ダイアログのみを使う仕様に決めました。)

|![スクリーンショット 2019-08-16 14.34.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482888/306e6890-edf3-6a2d-d87b-8489718e8951.png)
|:-:|

## スコープの範囲を決める
どのような方法で実現するかを決めたら自ずとスコープの範囲も決まってきます。
Slack社も言っているように、ここでは最低限のスコープ設定になるようにします。
社内利用でも勿論ですが、そのアプリを公開する場合はより注意を払う必要があるかと思います。
https://api.slack.com/scopes

## ローカルで開発を行う
実際に作りたいもののイメージが出来てきたら開発に入っていきます。
ここではほとんどの場合、SlackのDashbord上でOAuth、Event SubscriptionsなどのURLを指定する必要があると思いますが、ngrokを使用すると便利です。
https://api.slack.com/tutorials/tunneling-with-ngrok

## 審査に出す
アプリを公開するためには最後に審査に出す必要があります。
ここでは「適切なスコープが設定されているか」、「ユーザー体験が適切か」などをチェックされます。
リジェクトされた場合は再審査という流れになります。

審査が通っても勝手にリリースされるわけではなく、リリース日をSlack社と調整してリリースすることになります。

審査通過後は以下のような画面になります
↓
<img width="857" alt="スクリーンショット 2019-07-08 16.29.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482888/261bba8a-179f-fb5a-625c-96440614afa8.png">

## 公開後の修正＆デプロイ
サーバーのリリースは自由に出来る認識ですが、当然OAuthのスコープの変更などがある場合は先にレビューを通す必要があるので、注意です。
したがって先にレビューを出して通ってから、コードをデプロイする流れになります

## その他
また別記事で<a href="https://qiita.com/shingo111/items/25f9b6012b2abc11d3d7">Slack Appへの公開に役立つリンク集</a>という記事を書いたので、必要に応じてそちらもご確認頂ければと思います。

# まとめ
ドキュメント、開発ライブラリも整っていてサポートも充実しているので工夫次第で面白い＆便利なものがまだまだ実装出来そうです。
ただ日本語ドキュメントが少ないこともあり、日本発のアプリはそれほど公開されていないのかなといった印象だったので、
これを機に今後ますます日本にも広まって欲しいという思いです。
PHP比較演算子「==」と「===」の違いについて

#比較演算子について
比較演算子を使用する際は`==`ではなく厳密な比較演算子である`===`の方が良いと聞きますが、どう違うのか気になって調べたのでそのことを書いていこうと思います。

##比較演算子`==`について

PHPマニュアル(https://www.php.net/manual/ja/language.operators.comparison.php
)を見ると、こんな定義でした。

| 演算子 | 内容 |
| :-- | :-- |
| \$a == $b | 型の相互変換をした後で \$a が $b に等しい時に TRUE |
| \$a === $b| \$a が $b に等しく、および同じ型である場合に TRUE  |


イマイチピンと来ないのでこんなコードを用意しました。

```
<?php
$a = 120;
$b = "120abcer1ere";

if($a == $b) {
    echo "true",PHP_EOL;
} else {
echo "false",PHP_EOL;
}
```

変数aと変数bが等しければtrue、そうでなければfalseを出力するように記載しています。格納されてる値が違いますが、比較演算子`==`を使用したこのコードを実行するとtrueが出力されます。

これにはキャストが関係しています。

##キャストについて
キャストとは、変数などのデータ型を変換することを意味します。
PHPでは型の指定をしなくても実行に問題がありませんが、型は存在します。PHP側で自動的に型を設定してくれているので普段意識することはありませんが、今回のような比較演算子`==`を使う際に関係してきます。

また、PHPには自動キャストという機能があり、異なる型同士で演算を行うと型を変換します。

今回のコードで言うと、**比較する際に変数bの型を自動的にint型に変換している為「120」が変数bの値となり、trueが出力されます。**

詳しく見ていきます。
型による値の変化を見る為に下記コードを作成しました。

```
<?php
$b = "120abcer1ere";

var_dump($b);
var_dump((int)$b);
```

`var_dump`は変数の型や格納されている値を表示してくれます。
`var_dump((int)$b)`で格納されている値をint型で表示するように指示しています。

実行結果は下記です。

```
string(12) "120abcer1ere"
int(120)
```

そのまま出力すればstring型で格納した値全てが表示されますが、int型を指定するとアルファベット以降に記載した値は出力されません。

今回変数aと変数bが`==`で比較された際にtrue判定になったのは、変数bの型が自動キャストによりint型に変換された為です。

`==`は型の相互変換を行うのでint型に合わせる為trueが、`===`は型の変換を行わないのでfalseが出力されます。

最後に各比較演算子を使用した場合の値を記載します。

```
<比較演算子`==`を使用した場合>
<?php
$a = 120;
$b = "120abcer1ere";

if($a == $b) { //自動キャストされるので、「120 = 120」の比較になりtrueが出力される。
    echo "true",PHP_EOL;
} else {
echo "false",PHP_EOL;
}
```

```
<比較演算子`===`を使用した場合>
<?php
$a = 120;
$b = "120abcer1ere";

if($a === $b) { //自動キャストされないので、「120 = 120abcer1ere」の比較になりfalseが出力される。
    echo "true",PHP_EOL;
} else {
echo "false",PHP_EOL;
}
```

##終わりに
基本的には厳密な比較演算子`===`を使えば自動キャストは行われないので、前情報通りこちらを使用した方が無難そうです。
【PHP】Google Sheet APIで、対象のスプレッドシートの特定範囲の最終行を取得する

## GASだと簡単にできるけども、SheetAPIだとすんなり取れなかったのでメモ

```php5:spreadsheetTest.php
// $this->spreadsheetIdにて特定したスプレッドシートのシート1のA3からI列までの範囲で、入力のある部分を情報として取得
$rowsInfo = $this->service->spreadsheets_values->get($this->spreadsheetId, 'シート1!A3:I');
// 上記のカウントが最終行になるが、項目などを指定している場合は以下の$lastRowに項目分の行をプラスする必要がある
$lastRow = count($row) + 2;
```
anacondaをインストールしたのにcondaコマンドがなかった

# とっくのとうにAnacondaはインストールされていたが...

いかんせん私のMacは大昔に環境構築をし、そこにさらに増築に増築を重ねて、とっ散らかった状態でした。

# とりあえずターミナルでcondaとタイプだ

```
zsh: command not found: conda
```

おやおや、穏やかではありませんねぇ...

# .zshrcを確認

...結論からいうと、condaに関する記述がありませんでした。素直にPATHを通しましょう。

# 具体的な記述

```
% echo "source ~/anaconda3/etc/profile.d/conda.sh" >> ~/.zshrc
```

この後

```
% source .zshrc
```

で.zshrcの内容を反映させましょう。

# 確認

```
% conda --version
conda 4.5.4
```

これでcondaコマンドがターミナルから使用できるようになりました。
MATLABで株価データ自動取得　その２　移動平均

# これまで
前回Yahoo FinanceからデータをDownloadして株価データの自動取得ができた。
今回は移動平均フィルタ（株価の基本的な解析手法）を適用してみる。

# 移動平均フィルタ
殆どの株価チャートでは移動平均線が表示できるようになっている。これは株価の終値に移動平均フィルタを適用した結果を表示したもの。MATLABのFinancial Toolboxでmovavgという関数で提供されている。
例えば5日間の移動平均線を求めたければ次のようにする。

```matlab:
in = rand(100,1);
out = movavg(in, 'simple', 5)
plot(in)
hold on, plot(out)
legend('in', 'out')
shg
```
![plotResult.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/5b7c67c8-8b70-450a-5d0b-905b2ae4778f.png)

# movavgとfilter関数の速度比較
この関数実際に使ってみると結構遅い。MATLAB本体にはfilterという関数があり、フィルタリング処理ができるが、これと比較して実行時間がどうなのか時間測定してみた。

```matlab:
>> tic, out = movavg(sig1, 'simple', 5);, toc
経過時間は 0.028675 秒です。
>> tic, out2 = filter([1 1 1 1 1], 5, sig1); toc
経過時間は 0.000189 秒です。
```
なんとmovavgは150倍も遅い！
filter関数を使ったほうが良さそう。けどよく見てみるとデータの開始部分の結果が若干異なる。

![plotResult2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/669201d7-58a7-cf2a-5e84-fd8b75bcceda.png)

filter関数では、フィルタの内部状態の初期値は指定しなければ0が入っているが、movavgのソースコードを見てみると、初期値の計算で工夫がされている。

```matlab:
>> edit movavg
```

ソースコードで初期値の計算を行う箇所

```matlab:
226    weightsSum = cumsum(weights);
227    weightedMA(1:windowSize,:) = weightedMA(1:windowSize,:) ./ weightsSum';
```
移動平均フィルタ係数の積算値で時系列データを要素ごとに除算している。これを真似て

```matlab:
movAvgDay = [5, 10, 15];    % 5, 10, 15日移動平均

filterCoef5 = ones(1, movaveCoef(1))/movaveCoef(1);
filterCoef15 = ones(1, movaveCoef(2))/movaveCoef(2);
filterCoef30 = ones(1, movaveCoef(3))/movaveCoef(3);
weight5 = cumsum(filterCoef5);
weight15 = cumsum(filterCoef15);
weight30 = cumsum(filterCoef30);

data.AveFilt5 = filter(filterCoef5, 1, data.AdjClose);
    data.AveFilt5(1:movaveCoef(1), :) = data.AveFilt5(1:movaveCoef(1), :)./weight5';

data.AveFilt15 = filter(filterCoef15, 1, data.AdjClose);
    data.AveFilt15(1:movaveCoef(2), :) = data.AveFilt15(1:movaveCoef(2), :)./weight15';

data.AveFilt30 = filter(filterCoef30, 1, data.AdjClose);
    data.AveFilt30(1:movaveCoef(3), :) = data.AveFilt30(1:movaveCoef(3), :)./weight30';
```

# timetableデータの表示
データのクラスがtimetableなのでstackedplotコマンドで一気にプロットできる。

```matlab:
>> stackedplot(data, 'Marker', '+')
```
![stackPlot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477651/f3cc8123-5484-ed44-d25c-06f65094aa90.png)

土日や祝日などデータが欠落している部分が歯抜け表示になってしまうのはイケてない。。。改善の余地あり。

次回は表示について書こう。
LeetCode / Single Number

（[ブログ記事](https://mhiro216.hatenablog.com/entry/2019/08/16/175231)からの転載）

昨夜実家の富山から帰ってきました。
台風さんがフェーン現象を巻き起こしたせいで、北陸は地獄のような気候になってます。
私が兼ねてから主張している国民総打ち水法の制定を真摯に考えるべきかと思います！

[https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A7%E3%83%BC%E3%83%B3%E7%8F%BE%E8%B1%A1:embed:cite]

さて、今日の問題。

[https://leetcode.com/problems/single-number/]

Given a non-empty array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

Example 1:
Input: [2,2,1]
Output: 1

Example 2:
Input: [4,1,2,1,2]
Output: 4

からでは内intのlistに対して、1つだけ存在する1度しか登場しない要素を返す問題です。
事実上、Noteの<b>Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?</b>に対して回答できるかどうかの問題と読み替えて良いと思います。

#### 解答・解説
自力ではextra memoryを使う解法しか思いつかなかったので、まずは2つ示します。

##### 解法1
誰でも思いつく（けどこれじゃ問題の意味がないと気づいて書かない）コードを、一応載せておきます。

```python
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        ans = []
        for n in nums:
            if n in ans:
                ans.remove(n)
            else:
                ans.append(n)
        return ans[0]
```

##### 解法2
こちらの解法は少し難易度が上がるでしょうか。
まずはsetで集合をとり、重複した要素を排除します。この集合の合計値の２倍と、元のlistの差分が答えになります。

```python
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return sum(set(nums))*2 - sum(nums)
```

しかし、set(nums)によって空間計算量が$O(n)$に達するのは変わりません。

##### 解法3
さて、extra memoryを使わない解法ですが、ビット演算子のXOR（排他的論理和）を使います。
XORは、2つの入力のどちらか片方が真でもう片方が偽の時には結果が真となり、両方とも真あるいは両方とも偽の時は偽となる演算のことです。
例えば、以下のように計算されます。

```
>>> 1 ^ 3
2 # 3は2進数で11、1は2進数で01。これのXORは10で、10進数にすると2
```

このXORを利用して、以下のような解法が成立します。

```python
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        for i in range(1,len(nums)):
            nums[0] ^= nums[i]
        return nums[0]
```

これは何をしているかというと、全要素についてXORをとっています。
nums = [2,1,4,5,2,4,1]であれば　2 ^ 1 ^ 4 ^ 5 ^ 2 ^ 4 ^ 1　を計算するわけですが、このとき、

- a = bのとき、 a ^ b = 0
- a ^ b = b ^ a

の性質を利用すると、以下のように計算され、解が得られます。

```
-> 2 ^ 1 ^ 4 ^ 5 ^ 2 ^ 4 ^ 1
=> 2 ^ 2 ^ 1 ^ 1 ^ 4 ^ 4 ^ 5
=> 0 ^ 0 ^ 0 ^ 0 ^ 5
=> 0 ^ 5
=> 5
```
React NativeとExpoで作るiOS・Androidアプリ開発入門 - 3/3 Kindle版 のサンプルアプリを修正

ナカノヒトシさんのKindle本で学ぶサンプルアプリケーションはなかなかのもので勉強にもこれから作ろうと思うアプリの見本にもなるのですが、react-native-elementsがアップデートしたことによりそのままでは動かない。
<a target="_blank"  href="https://www.amazon.co.jp/gp/product/B07L9BQ9K6/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B07L9BQ9K6&linkCode=as2&tag=kokkahasan-22&linkId=80ff1106f641fd9c4f412e8f8d06c2ec"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=JP&ASIN=B07L9BQ9K6&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL160_&tag=kokkahasan-22" ></a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=kokkahasan-22&l=am2&o=9&a=B07L9BQ9K6" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<a target="_blank" href="https://www.amazon.co.jp/gp/product/B07L9BQ9K6/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B07L9BQ9K6&linkCode=as2&tag=kokkahasan-22&linkId=0fb24d3c0f134bd0497a2ca51ca21d5f">React NativeとExpoで作るiOS・Androidアプリ開発入門 - これ一冊でストアリリースまで進める本格的入門書 - 3/3</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=kokkahasan-22&l=am2&o=9&a=B07L9BQ9K6" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />


##例

###FormInput
[前回の記事](https://qiita.com/atomyah/items/165ee9794b032f97c731)で書いたとおり。

###Avatar
`<ListItem>`のプロパティとして`roundAvatar`と`avatar={item.user.profile_image_url}`ではダメで、次のように書く。

```react
<ListItem
    onPress={()=>{this.props.navigation.navigate('WebViewPage', {url:item.url,title:item.title})}
    }
    key={item.id + ":" + item.user.id}
    title={item.title}
    leftAvatar={{
      title: item.title,                           
      source: {uri: item.user.profile_image_url},
      rounded: true
    }   
```

###リスナー
サンプルコードではリスナーされる関数が発火せず。

```react
    componentDidMount() {
        this.props.navigation.addListener('didFocus', this.componentDidFocus()
        this.loadTags();
    }

    componentDidFocus() {
       this.loadTags();
   }
```

以下のようにした。

```react
    componentDidMount() {
        this.props.navigation.addListener('didFocus', () => this.loadTags()) // 変更。
        this.loadTags();
    }
    /*
    componentDidFocus()を削除
    */    
```

###KeywordForm.js

```react
this.refs["form-input"].clearText()
では動かず、
his.refs["form-input"].clear()
で正解
```

```react
<Input ref="form-input" label="新しく登録するキーワード" onChangeText={this.onChangeInputText} />
でInputのValueを関数に渡せず、
<Input ref="form-input" label="新しく登録するキーワード" onChangeText={(text)=>{this.setState({input: text})}} />
で、渡せた。
```

<br>

他にもあっただろうか？

いくつか自分の見落としもあると思うがとりあえず修正して動かせたソースコードをGithubにアップしたのでご参考に。

https://github.com/atomyah/reactnative_practice3

<br>
Pythonで文字列の"True"/"False"をboolとして判別する

## True/False

Python で文字列の `"True"` と `"False"` を bool 型として判定したいときに、どうすればいいか考えていました[^str_bool]。

[^str_bool]: ふと思いついて試してみた `bool('False')` は `True` になったのでした。

要件としてはこんな感じで：

* 型が文字列だったら、文字列の内容で `True/False` を判定する
* それ以外の型だったら、組み込み関数 `bool()` に渡して判定してもらう

いちおう書けたのですが、もっといい方法がありそうな気も……。

```python
def is_true(bexp):
  if type(bexp) == str:
    return bexp.lower() in ('true', 'yes', 'on', 'enable')
  return bool(bexp)
```

これはこれで、コンパクトにまとまっているので、まあいいかな。どの文字列を `True` 扱いにするのかも分かりやすいですし。

## 追補

渡すものが文字列と決まっていて（`None`もないなら）、かつ `True` に絞るなら、もっとずっとシンプルになりますね。

```python
result = bool_string.lower() == 'true'
```

ちょっと分かりにくい？？

## 追補2

コメントで、速くて見やすいコードをいただきました！
ありがとうございます！ @shiracamus

```python
def is_true(value):
    return False if value == 'False' else bool(value)
```

今は修正されて見られなくなっていますが、`eval` を使うのは盲点でした。

```python
eval('True')
```

変な値が入ると落ちますが、渡す値が確実に `"True"`/`"False"` の2値なら、スマートで素敵です。

```python
>>> eval('True')
True
>>> eval('true')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'true' is not defined
```
GASでGoogleDriveからスプレッドシートを操作する

## はじめに

* GAS(Google Apps Script)でGoogle Driveとスプレッドシートをいじっていたとき
  Google Driveのフォルダー内のスプレッドシートをどかっと持ってきていじりたくなった
* 他のQiitaとかみていると
  `SpreadsheetApp.openById('XXXX');` のIDはURLの後ろにある文字列だよ〜といった記事をよく見つけたので、
  Google Driveから開く方法はないのかなと思ってやったときのメモです



## やり方

* File.getId()で取得できるIDでそのままスプレッドシートを開ける

以下サンプル

```javascript
function getSheetFromFolder() {
  // GoogleDriveのフォルダーID
  var FOLDER_ID = "XXXXX"
  var folder = DriveApp.getFolderById(FOLDER_ID)
  // var folder = DriveApp.getRootFolder() // root dirの場合
  var files = folder.getFiles()
  while (files.hasNext()) {
    var file = files.next();
    
    // スプレッドシートのみ抽出
    Logger.log(file.getMimeType());
    if (file.getMimeType() == "application/vnd.google-apps.spreadsheet") {      
      var spreadsheet = SpreadsheetApp.openById(file.getId());
      var str = spreadsheet.getSheetByName("シート1").getRange("A1").getValue();
      Logger.log(str);
    }
  }
  
}
```



## まとめ

* IDはGoogleドキュメント内で共通だった
pythonでWebサイトに自動ログイン

#はじめに
pythonを使用してWebサイトに自動ログインするプログラムを作ってみました。
seleniumを使用。
geckodriverを使用してFireFoxを制御します。

複数のサイトにログインすることを想定して作成してます。
１サイト目は、ログイン後にボタン押下が必要なサイトを想定。対象の検索してクリック。
２サイト目は新規タブで開いてログイン。

なお、投稿者は、pythonを勉強して日が浅いです。
Qiitaの投稿も初です。技術レベルは低いです。

#環境
| OS | 言語 | ブラウザ |
|:-:|:-:|:-:|
|  windows10 |python3.7   |FireFox |


#事前準備
###driverの入手
ドライバーを下記から入手する。
https://github.com/mozilla/geckodriver/releases

###入手したdriver(geckodriver.exe)をpathが通っている場所に配置
PCの環境設定とかする。省略。

###seleniumuのインストール
pip install selenium


#コーディング

```python

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException

browser = webdriver.Firefox()
type(browser)

#Webサイト1 ユーザー名・パスワード入力欄はtextBoxでid有り
browser.get('Webサイト1')
usr_name_el = browser.find_element_by_id('対象のID')
usr_name_el.send_keys('ユーザー名')
usr_pass_el = browser.find_element_by_id('対象のID')
usr_pass_el.send_keys('パスワード')
usr_pass_el.submit()
#ログイン後、あるボタンを押してトップページ遷移させたいサイトのため下記コード。 aタグでクラス名有り
WebDriverWait(browser, 30).until(EC.presence_of_element_located((By.CLASS_NAME, "対象のクラス名")))
link_el = browser.find_element_by_class_name("対象のクラス名")
type(link_el)
link_el.click()

#Webサイト2 
#ユーザー名入力欄はtextBoxでid有り、パスワードはidとclassが無いのでxpathで検索
browser.execute_script( 'window.open()' )　#新規タブ開く
browser.switch_to.window(browser.window_handles[-1])　#新規タブに移動
browser.get('Webサイト2')
usr_name_el = browser.find_element_by_id('対象のID')
usr_name_el.send_keys('ユーザー名')
usr_pass_el = browser.find_element_by_xpath('対象のパス。開発者ツールからコピーするのが楽')
usr_pass_el.send_keys('パスワード')
usr_pass_el.submit()

```

#参考にしたもの
要素の見つけ方
https://selenium-python.readthedocs.io/locating-elements.html

#おわりに
プログラムでブラウザの制御が思ったより簡単に出来たので、
仕事で毎日ログインするようなサイトの自動化をしてみようと思います。