OmniAICreator/Qiita-1.07M huggingface dataset:
【UE4】IKで地面の傾きに合わせる②／第13回UE4ぷちコン進捗 - 2020/02/18

[1]:http://historia.co.jp/ue4petitcon13

[株式会社ヒストリアさん主催の第13回UE4ぷちコン][1]の作業進捗。

# IKで地面の傾きに合わせる②

![20200218_1955.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/d76c19a5-42f0-636c-74b6-91d4afa47796.gif)

昨日棚上げした課題：

- IKの距離が長すぎて足が伸び切るところがある
- IKの距離が短すぎて足がひっくり返るところがある

の解決をした。

やったことは泥臭い。
pelvisボーンをfoot IK側に引き寄せるか、もともとのアニメーション側に戻すかを Animation Curve を（手動で）用意した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/8a87aaa6-f16a-2832-c758-9780e08b3518.png)

具体的には、足が接地しているときはIK側に引き寄せて、宙に浮いているときは元のアニメーション側に寄せたことになる。
これで地面の傾きに足を合わせる周りはいったんまとまったことにする。

> 抽象的過ぎて読み手に分からないな、これは…
> いづれ、(暇ができたら)ちゃんと書きます。たぶん…


## ちょっとゲームパッドで動かしてみる

![20200218_2215.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/c3ad4705-29e0-c14b-54b3-dab02908a7e5.gif)

スティックの入力の方向をカメラ座標系で取り扱う周りでしばしハマったのは秘密。
傾いた地面でも、ちゃんと台地を蹴って走っている感じがするので（自分的には）OKとする。
速度も加速度もなしで、入力値をダイレクトに移動距離にしているので、動きが苦い。
これについてはまた今度。

今日はここまで。
一度gitにアップしてしまった物をgitignoreする。

最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです！

#一度、git上にあげたものをignoreする

以前、[robots.txtの内容・書き方](https://qiita.com/hirokik-0076/items/e54375b87c27d37ac88b/)を作成したんですが

robots.txtをStaging環境だけに反映し、本番環境には反映させないために一度、develop branchに上げた後、gitignore ファイルに追加する必要がありました！

master branchにも反映されてしまうのを防ぐために！
しかし、.gitignoreに記載したのに反映されず。

#問題
一度gitにアップしたものを、.gitignoreに追加したことが原因

>キャッシュにインデックスが残っている状況

#解決方法

こちらの方の方法通りにしたら無事、gitignoreに正しく反映されました！
[.gitignoreに記載したのに反映されない件](https://qiita.com/fuwamaki/items/3ed021163e50beab7154/)

>手順
1:.gitignore 編集
2:キャッシュを削除
3:commit & push

####robots.txtのgitのキャッシュを削除

```
$ git rm -r --cached robots.txt  //ファイル指定してキャッシュ削除
```

これで、gitでは管理されなくなりmasterに影響を与えずに済みました！
Photogrammetryのキャプチャー結果にパッチを当てる

Photogrammetyで写真から実物大のモデルを作るのは楽しいです
しかし、特徴点が少ない平面や反射・透過の物体は苦手です
例えば、こちらのケーキのポップは虫食いみたいで見た目がよくありません
![200217a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/da8a4bf0-96ce-1c9f-3e3c-d332122c0428.png)

今回は簡易に穴ぼこを補修する方法を紹介します
RealityCaptureとBlenderを使いました

注意：
確実に成功するとは限りません
Blenderで行うモデルの修正内容やExportの状態に依存するようなのですが、完全に原因を把握できていません
少しずつ補修部分を増やしていく方が良いと思います

##1. RealityCaptureでモデルを出力する（FBX形式）
とりあえず、Reality Captureで最低限度の状態のモデルを生成し、結果をFBX形式でExportします
今回はスマホのカメラの写真44枚だったので、使用料金は1.08$です
Imagesに入っている写真に対して課金されるので、以降のExportにはお金がかからないのが助かります
（課金額は決まった単位があるので、実際は29.9€で買った8000Mpix分のクレジットから切り崩して使います）
![200217a3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/46db0220-325d-9081-eb3b-5d3ba3436a68.png)

##2. Blenderで穴が開いている部分に別のメッシュを置く
先ほど作ったFBXファイルをImportします
商品名のプレートは板なので、planeをextrudeして、モデルと合うように大きさと位置を調整します
Before
![200217b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/1e4a5d7a-9b87-4f16-c492-bc297d3b15bf.png)
After
![200217c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/57ffa3fa-a691-724e-8574-e1d2a516f5cb.png)

##3. BlenderからObj方式でモデルを出力する
Importしたオブジェクトと追加したオブジェクトをJoinで一つにまとめ、選択・アクティブにして、Obj形式で出力します
チェック欄の一番上のSelection Onlyにチェックを入れます
方向のUpがZupですが、Forwardは-Xか-Yのどちらかを選択します
![200217d2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/16c25987-2c97-ca25-bf41-7abf722ef295.png)

実は、だいぶ成功確率は上がってきたのですが、この時の設定を完全に把握できていません
ExportしたいモデルをJoinでまとめなかったり、Materialのチェックを外さなかったら、次のステップでエラーが出て進めなかったことがあったり、挙動が安定しなかったです
![200217e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/3e0cdd66-833a-3ae1-b09b-bbc4f0163dc2.png)

詳しい方のコメントをいただけると助かります


##4. FBX出力した同じファイルに、作成したObjファイルをImportする
RealityCaptureに戻り、ReconstructionにあるImport Modelを選び、さきほど作ったObjファイルを読み込みます
![200217f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/2a1d2549-84dd-b6e5-edcc-f52380a6c589.png)
必要に応じてUnwrapします
![200217f2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/55eaf13f-4b87-83bb-fed5-34803d91110c.png)
Sceneのscene renderをSweetにすると、こんな感じに表示されます
![200217f3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/93871778-56e7-b9b7-6eac-26362345ecd8.png)

Blenderで追加したオブジェクトのメッシュが大きすぎると、Textureの解像度が低くなってしまいました
Unwrapのテクセルサイズの調整で対応できなかったら、Blenderに戻って追加するオブジェクトのメッシュの大きさをloop cutなどで小さくします

##5. RealityCaptureでTextureを貼る
こちらのようになりました
![200217f5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/8c79eac6-2ddc-8278-56d1-85ca6baacd50.png)
ちなみにBeforeはこのような感じでした
![200217g.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/bc2481fa-4650-c8ff-2399-f3fd3baf8d39.png)

##まとめ
結局、写真からメッシュの位置を再構成して、カメラの位置から貼るべきテクスチャを選んで貼っているはずなので（と理解していますが）、対応したスケールでモデルのメッシュを補ってあげれば、反射などでキャプチャーできなかった平面も再現できるのでしょう

他にも良い方法がありましたら、ご教示いただけますよう、よろしくお願いします
Flutter製のアプリ・サービスなど

# ◆国内

## ●yourbride

婚活アプリ

https://apps.apple.com/jp/app/hun-huo-jie-hunapuriyoubride/id872476884

https://play.google.com/store/apps/details?id=jp.youbride.android&hl=ja

開発者記事
https://developer.diverse-inc.com/entry/2019/07/04/173000

## ●chompy

フードデリバリー

https://apps.apple.com/jp/app/id1489433617

https://play.google.com/store/apps/details?id=com.syn_inc.chompy&hl=ja

## ●カウル

マンションの売買アプリ

https://apps.apple.com/app/id1143903849?mt=8

https://play.google.com/store/apps/details?id=jp.co.housmart.kawlu&hl=ja

開発者記事
https://note.com/yamarkz/n/nd9716541d8ad

## ●ポルト

月額制で参考書を読めるサービス（無料もあり）

https://apps.apple.com/jp/app/id1477467323?ign-mpt=uo%3D4

https://play.google.com/store/apps/details?id=com.studyplus.porto

開発者記事
https://tech.studyplus.co.jp/entry/2019/09/27/113643

## ●テニスベア

空きテニスコート検索アプリ

https://apps.apple.com/jp/app/%E3%83%86%E3%83%8B%E3%82%B9%E3%83%99%E3%82%A2-tennisbear/id1463750385?mt=8

https://play.google.com/store/apps/details?id=net.tennisbear.tennisbearapp&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1

# ◆海外

一覧
https://flutter.dev/showcase

## ●Stadia

Googleによるクラウドゲームサービス

https://apps.apple.com/us/app/google-stadia/id1471900213

https://play.google.com/store/apps/details?id=com.google.stadia.android&hl=en_US

## ●Google Ads

Googleによる広告管理アプリ

https://apps.apple.com/us/app/google-ads/id1037457231

https://play.google.com/store/apps/details?id=com.google.android.apps.adwords

## ●Alibaba

中国版AmazonのAlibabaの通販アプリ

https://itunes.apple.com/cn/app/%E9%97%B2%E9%B1%BC-%E6%8C%82%E9%97%B2%E9%B1%BC-%E9%97%B2%E7%BD%AE%E8%83%BD%E6%8D%A2%E9%92%B1/id510909506?mt=8

https://play.google.com/store/apps/details?id=com.taobao.idlefish&e=-EnableAppDetailsPageRedesign

## ●Baidu

中国最大の検索エンジン

https://apps.apple.com/cn/app/id1464625444

## ●グルーポン

クーポンサービス

https://itunes.apple.com/jp/app/groupon-gurupon/id352683833?mt=8

https://play.google.com/store/apps/details?id=com.groupon&hl=ja

## ●The New York Times -KENKEN

KENKENという日本発の数理パズルをウェブ上で遊べるFlutter for Web製アプリ

https://www.nytimes.com/games/prototype/kenken
robots.txtの内容・書き方

最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです！

#robots.txtを追加する必要が発生！
robots.txtは「サイト内の指定したページに対して、検索エンジンがクロールを行わない様に命令できる」機能

>サイト内検索結果ページや自動生成ページ等、検索エンジンに表示される必要のないページへのクロールを制御する事で、あなたのサイトをより最適化する事ができ、場合によっては大きなSEO改善効果に繋がるケースがある。

上記のことをしたい場合は、robots.txtファイルを追加して以下の様な中身にする！

```robots.txt
User-Agent:*
Disallow:

Sitemap:http://example.com/sitemap.xml
```

細かい命令方法はこれらで設定できる

![Screen Shot 2020-02-18 at 8.05.29 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394657/1263424e-c60e-3ea0-d902-2a07a7fdf259.png)

[各項目の簡単な設定方法](https://bazubu.com/robots-txt-16678.html/)

#参考記事
[robots.txtとは？作り方・書き方・確認方法を解説します](https://www.gc-seo.jp/journal/seo/robots-txt//)
Rで中心極限定理を確認してみた

# まえがき
Rの勉強を始めたのでアウトプットとしてQiitaに投稿していきます。
初心者の域を出ておりませんのであしからず。

※R version 3.6.2 / RStudio Version 1.2.5033

# 中心極限定理
標本から抽出するサンプルサイズが大きくなるにつれて、いい感じになっていくっていうアレです(適当)。
今回はサイコロを100回振ったときの平均値を、n回繰り返すと平均3.5を中心に収束していく様を確認したいと思います。

# 中心極限定理シミュレーション
```dice_mean.r
dice_mean_function <- function(n){
  dice.mean <- c(NULL)
  for (i in 1:n){
    dice <- as.integer(runif(100, 1, 7))
    dice.mean <- c(dice.mean, mean(dice))
  }
  dice.mean.table <- table(dice.mean)
  print(mean(dice.mean))
  print(var(dice.mean))
  print(sd(dice.mean))
  barplot(dice.mean.table)
}
```

## メモ
- c(NULL)で初期化しておく
- runifで一様乱数を発生させるn回、1〜7までの値
- as.integerで整数値にコンバートしている
- c(a, b, c,・・・)はベクトル
- y <- c(y, x)と自分自身を入れることで要素を追加できる(初期値NULLは1回目の代入で消える)
- 関数内でprint文を仕込まないとデバッグできない
- tableで度数を集計してくれる
- meanで平均値を計算
- varで分散を計算
- sdで標準偏差を計算
- barplotはそのまんま棒グラフ

# コード修正版
※コメント頂きましたので追記します(2020.02.19)
上記コードは教訓として残します
実行結果は修正版に差し替えました

```dice_mean.r
dice_mean_function2 <- function(n){
  dice <- matrix(sample(6, n * 100, replace=TRUE), n)
  dice.mean <- rowMeans(dice)
  dice.mean.table <- table(dice.mean)
  print(mean(dice.mean))
  print(var(dice.mean))
  print(sd(dice.mean))
  barplot(dice.mean.table)
}
```

## メモ
※コメント引用
- n*100 個の整数乱数を n×100 の行列として発生させます
- 行方向の 100 個の平均値を n 行に渡って計算するのを rowMeans() で計算します

# 実行結果
※コード修正版に差し替えました
## dice_mean_function2(100)
```
[1] 3.5076
[1] 0.03804065
[1] 0.1950401
```
![dice_mean2_100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/ef6bcf75-7477-2a40-6dd6-4ad63fd8295e.png)


## dice_mean_function2(1000)
```
[1] 3.49644
[1] 0.02816209
[1] 0.1678156
```
![dice_mean2_1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/4740015f-0eb5-d934-5750-3c90d715c571.png)


## dice_mean_function2(10000)
```
[1] 3.496958
[1] 0.02935194
[1] 0.1713241
```
![dice_mean2_10000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/8dd475de-f718-0e88-d05d-98d68d28507a.png)


## dice_mean_function2(100000)
```
[1] 3.500313
[1] 0.02904138
[1] 0.1704153
```
![dice_mean2_100000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/f65af985-ab88-3d10-e653-9bda4c5f3db3.png)
フォントサイズの定義の仕方について

フォントサイズをコードで書いている中で、emやrem、%が出てきたのでその関係について調べました。

## 定義方法

フォントサイズの定義方法は、絶対値と相対値の2つがあります。

- 絶対値：他の要素に依存せずに定義される。px
- 相対値：他の要素によって定義される。%、em、rem、vw、vh


## それぞれのフォントサイズの定義について

### px

絶対値での指定となるので、どのデバイスでも、同じピクセルサイズで表示されます。

### %

考え方：「親要素の◯%」となります。
stylusで書くとこのような感じです。


```javascript

html
  font-size 20px

p
  font-size 50% /* これは10pxになる */

```

### em
考え方：「親要素の◯」となります。
基本的には%と同じ考え方で、小数点で考える点だけが違う点となります。
emは、アルファベットの大きさのベースとなる「M」に由来しているとのことです。

```javascript

html
  font-size 20px

p
  font-size 0.5em /* これは10pxになる */

```


### rem
考え方：「もととなっている要素の◯」となります。
emに「root」のrを足したのがこのremで、親要素が複雑でわかりにくくなった場合も基準点を同じにしておくことで簡単に対応できるのがメリットになります。
ルート要素とはhtml要素のフォントサイズのことです。

```javascript

html
  font-size 100% /* 100%は16pxになる */

div
  font-size 2em /* これは32pxになる */

```

### vw

考え方：「画面幅の◯」となります。
vwは「viewport width」の略で、ビューポートの幅に対してどれくらいの幅にするのかを定義しています。
画面幅によって細かく文字サイズを調整できるので、目立つ文字列に使用されたりします。

```javascript

p
  font-size 0.05vw /* 画面幅が1000pxのとき、50pxのフォントサイズ。500pxの画面幅のとき、25pxのフォントサイズ */

```

## どのように使うのが良いのか

ブラウザごとにフォントの大きさを変える必要がない場合は、remやemで書くのが良いでしょう。
remかemかはその状況にもよります。

基準となるフォントサイズを `font-size = 62.5%` とすると、そのフォントサイズを10pxにすることができます。

ですので

```javascript

html
  font-size 62.5% /* これで基準が10pxになる　*/

h1
  font-size 3rem /* h1は基準の3倍、つまり30pxになる　*/

```

とすることができます。
これで、かなり使いやすく、かつ変更にも強いフォントサイズを作ることができます。

また一つ学びました。
LightGBMTunerの処理を覗いてみる

# はじめに
昨年9月末の[PyData.Tokyo](https://pydatatokyo.connpass.com/event/141272/)での[発表](https://www.slideshare.net/pfi/pydatatokyo-meetup-21-optuna?ref=https://pydatatokyo.connpass.com/event/141272/presentation/)で紹介されていたLightGBMTunerがついに実装された。

- [Optuna の拡張機能 LightGBM Tuner によるハイパーパラメータ自動最適化 \| Preferred Networks Research & Development](https://tech.preferred.jp/ja/blog/hyperparameter-tuning-with-optuna-integration-lightgbm-tuner/)

LightGBMに限らず、モデルのパラメーターは独立ではなく相互に作用し合っている。
そのため、パラメーターチューニング時には各パラメーターを段階的にチューニングした方が高い精度を期待できる。
影響が大きい（と思われる）パラメーターから順にチューニングしていくというのがコンセプト。

# 確認環境
- Windows10
- Python 3.7
- optuna 1.0.0

# 処理内容
![](https://tech.preferred.jp/wp-content/uploads/2020/01/image7.png)
実装は[こちら](https://github.com/optuna/optuna/blob/c14bca923105a75e7a3f0daf08790c2850318bbd/optuna/integration/lightgbm_tuner/optimize.py#L268)

|No.|内容|メソッド名　|パラメーター名|チューニング範囲|試行回数|
|:-----------------|------------------:|------------------:|------------------:|------------------:|:------------------:|
|1|feature_fraction(1回目)|tune_feature_fraction()|feature_fraction|0.4~1.0|7回|
|2|num leaves|tune_feature_fraction()|num leaves|0~1(optuna.samplers.TPESampler使用)|20回|
|3|bagging|tune_bagging()|bagging_fraction, bagging_freq|0~1(optuna.samplers.TPESampler使用)|10回|
|4|feature_fraction(2回目)|tune_feature_fraction_stage2()|feature_fraction|1回目の最適値±0.08の範囲（0.4~1.0の範囲から外れる値は除く）|3~6回|
|5|regularization|tune_regularization_factors()|lambda_l1, lambda_l2|0~1(optuna.samplers.TPESampler使用)|20回|
|6|min data in leaf|tune_min_data_in_leaf()|min_child_samples|5, 10, 25, 50, 100|5回|


気になった点としては以下の通り。

- feature_fractionを2段階でチューニングしている効果は？
- num leavesより先にfeature_fractionなの？
- lambda_l1、lambda_l2のチューニングが0～1と狭め（今まで最大値100まで取ってたが広げ過ぎ？）

好みに合わせてチューニング内容を変更したい場合、現状ではモンキーパッチでrun()を実装する必要がある。


# 補足
もしパラメーターの意味合いが分からなければ、下記リンクで気持ちを理解してあげるところから始める。
[勾配ブースティングで大事なパラメータの気持ち \- nykergoto’s blog](https://nykergoto.hatenablog.jp/entry/2019/03/29/%E5%8B%BE%E9%85%8D%E3%83%96%E3%83%BC%E3%82%B9%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%A7%E5%A4%A7%E4%BA%8B%E3%81%AA%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%81%AE%E6%B0%97%E6%8C%81%E3%81%A1)
【UE4】一人マルチプレイヤー

<h2>変身するキャラクター</h2>
本郷猛と仮面ライダー1号のように、一人プレイでキャラクターを使い分けることも多いでしょう。
UE4でも実装例はたくさんあると思って色々探したのだけど見つかりませんでした。

https://docs.unrealengine.com/ja/Gameplay/HowTo/RespawnPlayer/Blueprints/index.html
同じキャラクターの再生(生き返り)を参考に生き返ったときに違うキャラクターになるようにします。

図にあるように、レベルブループリントを編集します。

![lefthalf.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588692/627b3f6d-5cdb-f974-7759-6c473a02257d.png)

キーボードの「Ｑ」を押したしたときに、キャラクターの位置を取得し、変数にセットします。
「SpawnTransform」変数をセットしたところで、最初のキャラクターをDestoryします。

![righthalf.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588692/9ea8ebce-9434-9004-2a80-d08669ab3bda.png)

SpawnActorで、新しいキャラクターを出します。「SpawnTransform」変数をゲットして位置を決めます。
Spawnするキャラクター(クラス)はここでは決め打ちしていますが、変数を使えば、キャラクターも選べると思います。
まだそこまでの知識がないのでここでは決め打ちします。

新しくspawnしたキャラクターをコントローラに渡します。これで操作ができるようになります。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">変身するキャラクター<a href="https://twitter.com/hashtag/UnrealEngine?src=hash&amp;ref_src=twsrc%5Etfw">#UnrealEngine</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://t.co/ZqQHCGNf6w">pic.twitter.com/ZqQHCGNf6w</a></p>&mdash; 特命社長 (@tokumeishatyo) <a href="https://twitter.com/tokumeishatyo/status/1229752040756088832?ref_src=twsrc%5Etfw">February 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
instagram graph api 登録のちょっとした手順

案件でPHPの投稿を取得し、サイト内に表示させる機会がありました。
珍しくないことではあるが、実案件としては初めての機会であり、取得→表示に手こずったので（説明をきちんと読まない悪い癖のせいでもあるが）、忘れないようにメモしておきます。

# API
この類のAPIは以下が挙げられるかと思います。
https://web.analogstd.com/tips/posts/api/instagram-grapgh-api-facebook.php

## Instagram API
提供元：Instagram

言わずもがなですが、2020年に利用停止となりますので、今から使う方はいないでしょう。。。
現状、使用しているサイトは見直しが必要ですね

## Instagram Graph API
提供元：Facebook
今回こちらを採用しました。

# トークンの取得
利用には、トークンの取得が必要です。今回、この手順はディレクターにおまかせして、諸々登録ご完了後に必要そうな情報を一式いただきました。
なので、今回は説明を割愛します。また、機会があれば改めてまとめることにしよう。
すごいめんどくさいらしいです。。。

最終的に、必要となる情報は

- `無期限のトークン（場合によって、第３のトークンと呼ばれる。）`
- `Instagramのビジネスアカウント（Facebookビジネスより登録。）`

すで無期限のトークンを頂いていましたが、そもそもそれが何なのかわからなかったので、見直しがてら自分で作業を行いました。

# 無期限のトークンの取得
取得した`トークン２`を使って下記のurlにアクセスします。
https://graph.facebook.com/v3.0/me?access_token=【トークン2】
すると、オブジェクトが表示されるはずです。

```
{
 "name":"hoge",
 "id":"fuga"
}
```

`トークン2`と`ID`を使用して、下記にアクセスします。
https://graph.facebook.com/v4.0/【ID】/accounts?access_token=【トークン2】
(※vはバージョンです。適宜変更してください。自分はv4.0だったはず)
以下のように表示されたらおｋです。

```
{
"data":[
{
 "access_token":"fugafuga",
 "category":"",
 
// 省略
}
```

この`access_token`が`第三のトークン（無期限のトークン）`と呼ばれる最終必要情報です！（ここまでが長い〜）
表示されたアクセストークンを、facebook for developerのアクセストークンデバッカーに入力して、諸々設定を確認
とりあえず、有効期限は受け取らないにしておくといいかと思います。
(この辺の作業、ディレクターに行っていただいたので詳細は割愛させてください。。。。)

# 投稿の確認
ここまで間違っていないか確認します。
https://graph.facebook.com/【アクセストークンデバッガーで確認できるページID】/feed?fields=permalink_url,id,created_time,message,from,full_picture,attachments{unshimmed_url}&access_token=【無期限のトークン】

問題なければ、instagramの投稿情報がもろもろ表示されるはずです。

```
{
 "data": [
  {
   "permalink_url": "https://facebookurl",
   "id": "000",
   "created_time": "2019-",
   "message": "hogehoge", 
  // 略
```

perma_linkにアクセスして、問題なく該当の投稿が表示されればおｋです。

# instagram ビジネスアカウント
無限のトークンの他に必要な情報です。下記、アクセスします。
https://graph.facebook.com/【アクセストークンデバッガーで確認できるページID】?fields=instagram_business_account&access_token=【無期限のトークン】
以下のように表示されたらおｋです。

```
"instagram_business_account": {
 "id": "1234567890"
},
"id": "1234567890"
```


# 出力
いよいよ出力します。
今回は、jsonとしてデータを受け取り、jsで表示処理を別途行います。(ajaxでとりあえずconsole出力してみましょう。)
こんな感じでしょうか。
（セキュリティの問題上、jsで処理するのではなく、PHPで処理しましょう。）

```php:get.php
$accessToken = 'hogehoge'; // アクセストークン
$businessId = '111111'; // ビジネスアカウントID
$fields = 'media{caption,media_url,permalink,timestamp}'; // 取得するデータのパラメータ
$url = 'https://graph.facebook.com/v4.0/'.$businessId."?fields=name%2Cmedia.limit(4)%7Bcaption%2Clike_count%2Cmedia_url%2Cpermalink%2Ctimestamp%2Cthumbnail_url%2Cmedia_type%2Cusername%7D&access_token=".$accessToken;

$jsonData = file_get_contents($url);
$json = json_decode($jsonData, true);
echo json_encode($json["media"]["data"]);
```

# 最後に
記事の書き始めから、2週間ほど経過しており少しすでに曖昧になっている点も有りました。（冒頭、忘れないためのメモ書きという趣旨を書いていましたが。。。）間違いがありましたら、申し訳ありません。
GAS gasを使いGoogleCalendarに予定を自動で登録する

# ゴール
月初めにGoogleCalendarの25日に家賃を払う予定を自動で作成する。
## 新規のGoogleAppsScriptのプロジェクトを作成する
Googleドライブを開き、「新規」から「GoogleAppsScript」を開く。
選択肢にない場合はアプリを追加より追加する。
![新規gas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350501/ca888a0b-2542-2728-5637-57d651eb3c03.png)

新規のプロジェクトが表示される。
![無題のプロジェクト.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350501/d6163d8e-d453-5cda-fe0d-8072309ab12b.png)

左上の「無題のプロジェクト」のラベルをクリックしプロジェクト名を変更して保存しておく。
今回のプロジェクト名ははeditCalendarとする。

## GoogleAppsScriptを作成する

### 完成コード
```コード.gs
function createEvents() {
  var calendar = CalendarApp.getDefaultCalendar();
  var date = new Date();
  var startDate = new Date(date.setDate(date.getDate() + 24));
  var endDate = new Date(date.setDate(date.getDate() + 1));
  var title = (date.getMonth() + 1) + '月の家賃支払い';
  calendar.createAllDayEvent(title, startDate, endDate);
}
```
### 詳細

### GoogleCalendarを取得するAPI

```
  var calendar = CalendarApp.getDefaultCalendar();
```

### 必要な変数を用意する

```
  //関数が実行される日付
  var date = new Date();
  //予定の開始日は1日に実行する想定なので開始日は+24日する
  var startDate = new Date(date.setDate(date.getDate() + 24));
  //予定の終了日は終日のため+1でよい
  var endDate = new Date(date.setDate(date.getDate() + 1));
  //登録される予定のタイトル
  var title = (date.getMonth() + 1) + '月の家賃支払い';
```

### GoogleCalndarに登録する

```
  calendar.createAllDayEvent(title, startDate, endDate);
```

### 参考
DoogleAppsScriptのCalendarリファレンス
https://developers.google.com/apps-script/reference/calendar/calendar

## 試しに実行してみる
### 手順
1. 実行タブから関数を実行、createEventsをクリック
2. １回目は承認が必要である。
  1. 承認が必要ですというダイアログが表示されるので「許可を確認」。
  2. アカウントを選択する。
  3. このアプリは確認されていませんが表示された場合は左下のeditCalendarに移動を押す。
  4. 許可
3. 実行されたの確認してみる。
4. 今日から24日後に予定が登録される。

![実行.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350501/cf544ecc-9d19-7535-0209-a1a7a41cb81a.png)

## トリガーを設定する

### トリガーとは
トリガーとはスクリプトを実行するための引き金となるもの。
ユーザの操作などによりイベントが発生する。
使用するイベントと実行する関数を指定することで登録できる。

### トリガーの設定方法
1. メニューの現在のプロジェクトのトリガー（時計のアイコン）をクリックする。
![トリガー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350501/1a91e15f-b56d-7c0d-0ded-67fb58352d39.png)

2. トリガーの追加
3. 以下の項目を設定して登録する。
  - 実行する関数：createEvents
  - イベントのソースを選択：時間主導型
  - 時間ベースのトリガータイプを選択：月ベースのタイマー
  - 日を選択：1日
  - 時刻を選択：任意

# 最後に
GoogleCalendarはよく確認するので、忘れがちな家賃の支払いの予定を自動でGoogleCalendarに登録してみました。
25日になったらSlackに通知を送信されたかったのですがまだ着手できず...
GSuiteは便利な機能が多いのでスクリプトで活用できていけたらと思います。
atom-runnerでnumpyをimportできなかった

#症状
パイソンスクリプトファイルをATOM上で実行する際、下記コマンドでエラーが発生
>import numpy

numpyライブラリを読み込むための文。
なおAnacondaPrompt上では実行できていた。

#エラー表示
下記メッセージが出ていた。
>ImportError: DLL load failed: 指定されたモジュールが見つかりません。

#対処法
pathが通っていなかったことが問題のよう。
windowsの環境設定でpathを追加する。
下記ページを参考に、設定を行い解決。
　[AtomでPythonを開発環境構築時に起きる問題、まとめて解決](https://lets-hack.tech/programming/environment/atom-python-env/)
「/etc/hosts」ファイルについて

最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
Webエンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです！

#/etc/hosts ファイル

hostsファイルは使う事は多いのですがキチンと理解できていなかったのでしっかりとまとめる。
「/etc/hosts」ファイルは、ホスト名とIPアドレスを対応させるためのファイル

```:/etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
```

各行に、IPアドレスと、それに対応するホスト名を設定できる！

>インターネット上では、DNSサーバがドメインとIPの組み合わせを記録するようになっています。ただ、コンピュータ内のhostsファイルはDNSより優先的に参照されるようになっています。そのため、コンピュータ内の設定はDNSより優先されます。

#hostsファイルの場所
Macのhostsファイルは /private/etc/hosts に配置されています。/private/etc から /etc を参照するシンボリックリンクがあるため、/etc/hosts からも参照・編集可能です。

```
/private/etc/hosts
または
/etc/hosts
```

Linuxを含むUnix系のOSの場合、/etc/hosts に配置されています。

Windowsの場合、C:¥windows¥system32¥drivers¥etc に配置されています。

#hostsファイルの編集

```
$sudo vi /etc/hosts
```

IP アドレスとホスト名を入力したら良い

```
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
 127.0.0.1       localhost
 192.168.0.10    example.com
```

#参考記事
・[/etc/hosts」ファイル](https://linuc.org/study/knowledge/506//)
・[hostsファイルの場所やら編集/設定するメモ(mac、linux、windows)](https://www.tweeeety.blog/entry/2014/11/18/133508//)
・[【Mac】hostsファイルの場所と変更方法！ターミナルとテキストエディットどちらでも！](https://satopedia.com/mac-hosts//)
・[/etc/hosts ファイルの設定](http://archive.linux.or.jp/JF/JFdocs/LFS-BOOK/chapter07/hosts.html/)
AtCoder ABC052 C - Factors of Factorial

https://atcoder.jp/contests/abc052/tasks/arc067_a

# 題意
- N!の約数の数を述べよ(mod 10^9 +7)
- 制約: 1 <= N <= 1000

# 考え方
例えば、$6! = 6 \times 5 \times 4 \times 3 \times 2 \times 1  $の場合、因数は30個となる。
これらを因数分解して考えると、$ 6 = 3 \times 2 $ であり、、$ 4 = 2 \times 2 $であるから、
$6! = 3 \times 2 \times 5 \times 2 \times 2 \times 3 \times 2 $と言い換えられる。
この右辺の任意の組み合わせが約数となるが、上記の式から適当に数と取ってしまうと、`2を2つとる`場合が複数あり、`4`という約数を複数カウントしてしまう。ここで、上記の素因数を以下のように整理する。

```
5
3 3
2 2 2 2
```

となる。かきかたを変えると、

```
5を: とらない, 1つとる
3を: とらない, 1つとる, 2つとる
2を: とらない, 1つとる, 2つとる, 3つとる, 4つとる
```

のように、各素因数について`見つかった数+1`が考えられるのでこれをかければ、この数の独立した約数の数をカウントできることに他ならない。

Nがたかだか1000のため、素因数分解は愚直でよく、また、カウントする配列も愚直に実装すればよい。ある約数について、発見された数が`0`である場合は、`選ばない`という選択肢しかないので、`1`として組み合わせの数に掛け算すればよい。


# コード
因数分解モジュール: https://qiita.com/snow67675476/items/e87ddb9285e27ea555f8

```Python
mod = 1000000000 + 7
 
def factorization(n):
    arr = []
    temp = n
    for i in range(2, int(-(-n ** 0.5 // 1)) + 1):
        if temp % i == 0:
            cnt = 0
            while temp % i == 0:
                cnt += 1
                temp //= i
            arr.append([i, cnt])
    if temp != 1:
        arr.append([temp, 1])
    if arr == []:
        arr.append([n, 1])
    return arr
 
n = int(input())
dat = [0] * 2000 # 約数として求められたある数の合計
 
# N! の各要素の素因数分解をする
res = 1
for i in range(1, n + 1):
    d = factorization(i)
    for j in range(len(d)):
        dat[d[j][0]] += d[j][1]
 
for i in range(2, n + 1):
    res *= (dat[i] + 1)
    res %= mod
 
print(res)
```
https://atcoder.jp/contests/abc052/submissions/10185638
Rでサイコロシミュレーションしてみた

# まえがき
Rの勉強を始めたのでアウトプットとしてQiitaに投稿していきます。
初心者の域を出ておりませんのであしからず。

※R version 3.6.2 / RStudio Version 1.2.5033

# サイコロシミュレーション
```dice.r
dice_function <- function(n){
  dice <- as.integer(runif(n, 1, 7))
  dice.table <- table(dice)
  print(dice.table)
  print(prop.table(dice.table))
  hist(dice,
       breaks=c(0, 1, 2, 3, 4, 5, 6),
       probability=T,
       main="Histogram of 1-6",
       xlab="dice",
       col="gray")
}
```

## メモ
- runifで一様乱数を発生させるn回、1〜7までの値
- as.integerで整数値にコンバートしている
- 関数内でprint文を仕込まないとデバッグできない
- tableで度数を集計してくれる
- prop.tableで相対度数にしてくれる
- hist(plot系共通)にmain, xlab, ylabでラベルを付与できる
- probability=Tで相対度数で描画できる
- breaksで幅を指定できる/bins指定だといい感じにやってくれる？
- c(a, b, c,・・・)はベクトル

# コード修正版
※コメント頂きましたので追記します(2020.02.19)
上記コードは教訓として残します
実行結果は修正版に差し替えました

```dice.r
dice_function2 <- function(n){
  dice <- sample(6, n, replace=TRUE)
  dice.table <- table(dice)
  print(dice.table)
  print(prop.table(dice.table))
  barplot(prop.table(dice.table),
          main="Barplot of 1-6",
          xlab="dice",
          ylab="probability",
          col="gray")
}
```

## メモ 
※コメント引用
- 乱数は，1〜6の整数値を取るので，sample() で求める
- 整数乱数なので，hist() ではなく barplot() で描画する

# 実行結果
※コード修正版に差し替えました
## dice_function2(100)
```
dice
 1  2  3  4  5  6 
22 20 14  9 15 20 
dice
   1    2    3    4    5    6 
0.22 0.20 0.14 0.09 0.15 0.20 
```
![dice2_100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/dfbcb754-9e6b-7717-d971-591d2cb1b2ce.png)

## dice_function2(1000)
```
dice
  1   2   3   4   5   6 
177 169 155 159 162 178 
dice
    1     2     3     4     5     6 
0.177 0.169 0.155 0.159 0.162 0.178 
```
![dice2_1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/bbfa4968-e187-75db-b8ae-f343c7573592.png)


## dice_function2(10000)
```
dice
   1    2    3    4    5    6 
1703 1687 1594 1669 1649 1698 
dice
     1      2      3      4      5      6 
0.1703 0.1687 0.1594 0.1669 0.1649 0.1698 
```
![dice2_10000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/0ce82e98-8719-3870-26be-cb1ef4a48a11.png)


## dice_function2(100000)
```
dice
    1     2     3     4     5     6 
16762 16645 16593 16580 16656 16764 
d
ice
      1       2       3       4       5       6 
0.16762 0.16645 0.16593 0.16580 0.16656 0.16764 
```
![dice2_100000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273970/4047fee6-3063-53b4-5b04-67072277db63.png)
C言語でchar型とint型を変換したい

##　はじめに
この記事ではchar[文字]型とint[整数]型の変換について解説します。
文字コードに関する話になるので、変換の仕組みはC言語以外でも同じだったりします。

##　先に方法から
int型変数をn、char型変数をcとしたとき、それぞれ変換する際は以下の方法があります。

```cpp
    n+'0'//整数→文字
    c-'0'//文字→整数
```
##### 使用例

```cpp
#include<stdio.h>

int main(void){
	int n = 5;
	char c = '2';
	
	printf("%c \n", n+'0');//整数→文字
	printf("%d \n", c-'0');//文字→整数
	
	return 0;
}
```
###### 出力

``` 
5
2
```
きちんと変換できていますね。
では、この仕組みを解説していきます。


##　文字をint型として表示する
printf関数のフォーマット指定子で、char型ではなくint型の%dを使います。
試しに、以下の出力を見てみましょう。

```cpp
	printf("%d \n", '0');
	printf("%d \n", 'a');
	printf("%d \n", 'A');
```
###### 出力

``` 
48
97
65
```
出力結果からは、表示された値は元の文字と関係ないように見えますね。
これは、int型として表示するとその文字の**文字コード**に表示されるからです。
##　文字コード表

| 文字コード   |      文字 |
|:--|--:|
| 46|.|
| 47|/|
| 48|0|
| 49|1|
| 50|2|
| 51|3|
| 52|4|
| 53|5|
| 54|6|
| 55|7|
| 56|8|
| 57|9|
| 58|:|
| 59|;|
文字コードの変換方式はASCII、UTF-8、Shift-JISと様々あります。これらを含むほとんどの変換方式では、0～9の文字は順番に文字コードが割り当てられています。```'0'```の出力が```48```の理由はここにあったんですね。

##　整数をchar型として表示する
前のプログラムの出力をchar型として表示してみましょう。
今度のフォーマット指定子はchar型の%cを使います。

```cpp
	printf("%c \n", 48);
	printf("%c \n", 97);
	printf("%c \n", 65);
```
###### 出力

``` 
0
a
A
```
出力結果は、前のプログラムの文字になっていることが分かります。つまり、int型をchar型として表示した場合、その文字コードに割り当てられた文字が表示されているのです。


##　変換の仕組み
文字と文字コードの関係が分かったところで、初めに紹介した変換方法の仕組みが見えてきます。
文字コードの割り当ては順番に行われているので、これを利用し0の文字コードを基準に変換します。int型→char型の場合は0との和、char型→int型の場合は0との差で表すことができますね。


##　注意点
int,char型の値は、0～9でないと正常に動作しません。
もし変換を関数化する場合は、与えられた値の範囲に問題がないか確認するべきでしょう。

C言語では、0の文字コードを基準にするのではなく、単純に48を基準にする方法もあります。ですが、0の文字コードが48として割り当てられていない可能性も十分にありますし、尚且つ不自然なのでこれはやめるべきです。

##　参考
[ASCII(Wikipedia)】](https://ja.m.wikipedia.org/wiki/ASCII)
[初歩なこと]画像投稿をしたらGithubに大量の画像が上がっちゃうのを阻止したい時

## 画像投稿機能をつけるときにやっておくこと

投稿した画像のデータを保存する必要がないとき、
ディレクトリの`.gitignore`というファイルに以下を記入する。

```ruby:.gitignore
#末尾に追記
public/uploads/*
```

***完了です***

#### gitignoreとは？
コミットしたくないファイル・ディレクトリを指定できる設定ファイルのことです。
通常画像投稿の機能をつけると画像は自動的に`public/upload`というディレクトリに
格納されます。
そこで、このファイル名を***gitignore***へ追加しておくことでgithubへ画像がコミットされなくなるということです。（コミットされないだけで画像自体はフォルダへ入ってきますが。）

### 終わりに
gitignoreのことすっかり忘れて、コミットの時に大量の画像に危く履歴を圧迫されそうになりました。
備忘録として残しておこうと思います。
ETロボコンに向けてEV3RTのアップデートをする(macOS Catalina)

## はじめに
ETロボコン2020年に向けて開発環境バージョンのβ7-3が出ていたのでアップデートしたいと思いいろいろ調べ時間がかかったため，備忘録です．

### 対象
-  2018，2019年のロボコンでmacOSを使用して開発環境していた人たち
- 自身のmacをmacOS Catalinaにアップデートした人
- gccなどのコンパイル自体はもう終わっている人

## 手順
- [TOPPERS wikiの開発環境ダウンロードページ](https://dev.toppers.jp/trac_user/ev3pf/wiki/Download)(1)から```ev3rt-beta7-3-release.zip```をダウンロード
- EV3で使用しているmicroSDカード内のデータを移動させたのち```hrp2/sdcard```のデータをmicroSDカードにコピー
- ```ev3rt-beta7-3-release```内の```hrp2.tar.xz```を展開
- cfgバイナリファイルを```ev3rt-beta7-3-release/hrp2/cfg/cfg```にコピー *1
- ```ev3rt-beta7-3-release/hrp2/sdk/workspace```内にあるhelloev3に対して```make app=helloev3```をしてコンパイル *2
- microSDカード内の```ev3rt/apps```に作成したappバイナリファイルをコピーし動作確認


*1 2020/2/18時点は，cfgバイナリファイルをダウンロードできないため，β7-2で使用していたcfgバイナリファイルを```ev3rt-beta7-3-release/hrp2/cfg/cfg```にコピーする方法で解決

*2 2020/2/18時点は，一部のバイナリファイルがmacのソフトウェア検証に引っかかるため実行できない場合があります．解決策の一つは```システム環境設定 -> プライバシーとセキュリティ```(2)(3)で逐次許可していく方法があります．

## さいごに
アップデートはとても簡単なのですがcfgバイナリファイルダウンロード出来んのなぜだろう？

## 参考文献
(1) [開発環境構築のマニュアル-ダウンロード](https://dev.toppers.jp/trac_user/ev3pf/wiki/Download)
(2) [macOSユーザーガイド-開発元が未確認のMacアプリケーションを開く](https://support.apple.com/ja-jp/guide/mac-help/mh40616/mac)
(3) [Macで「開発元を確認できないため、開けません」と表示された時の対処法](https://gori.me/mac/mac-tips/104874)
BigQueryでクエリロジックのテストを書く

[BigQueryで虚無からSQLだけでデータを作る](https://qiita.com/tamanobi/items/f939980765672747a9f6)　と `ERROR()` を使う

# ERROR
https://cloud.google.com/bigquery/docs/reference/standard-sql/debugging_functions?hl=ja

これを実行すると結果が合っていた場合は、クエリが実行され、間違っているときはエラーになる。

## SQL
```sql

#standardSQL
WITH TEST_DATA AS (
-- テストデータ
SELECT * FROM UNNEST(ARRAY<STRUCT<time TIMESTAMP, event STRING, id INT64>>
[
  (TIMESTAMP "2020-02-17 00:00:00+00", 'すごいイベント', NULL)
  , (TIMESTAMP "2020-02-18 01:00:00+00", 'awesome event', 123)
]))
, EXPECTED AS (
-- 期待する結果

SELECT * FROM UNNEST(ARRAY<STRUCT<time TIMESTAMP, event STRING>>
[
  (TIMESTAMP "2020-02-17T00:00:00", 'すごいイベント')
])
)
, ACTUAL AS (
-- ここがクエリ
SELECT time, event FROM
  TEST_DATA
WHERE time < TIMESTAMP_ADD(TIMESTAMP "2020-02-18T12:00:00", INTERVAL -1 DAY)
)

-- diffを出す
, diff AS (
SELECT *, "actual - expected" AS description FROM (SELECT *, ROW_NUMBER() OVER() AS n FROM ACTUAL EXCEPT DISTINCT SELECT *, ROW_NUMBER() OVER() AS n FROM EXPECTED) UNION ALL
SELECT *, "expected - actual" AS description FROM (SELECT *, ROW_NUMBER() OVER() AS n FROM EXPECTED EXCEPT DISTINCT SELECT *, ROW_NUMBER() OVER() AS n FROM ACTUAL)
)

-- diffがなければOK
SELECT IF(NOT EXISTS(SELECT * FROM diff), "OK", ERROR("クエリが間違っています"))
```

## クライアントから呼び出せばいい？？？
うーん、もっといい方法はないだろうか……。何らかのプログラミング言語のBigQueryクライアントから呼び出して言語標準の辞書ベースで比較するの一番ラクだろうか。
【備忘録】terraformでバケットが存在するのに"querying Cloud Storage failed: storage: bucket doesn't exist"と表示されてしまう話

# 概要

GCSにBackendを設定するために`terraform init`をした時に、タイトルの通り、こんなエラーが表示されます。

```:terraform
Error: Error inspecting states in the "gcs" backend:
    querying Cloud Storage failed: storage: bucket doesn't exist

```

これは文字通りterraformの項目に書かれているところのバケットが読めないっていう意味のエラーなんですが、
修正しても全然改善されないということが起こります。

# 対処法

[こちら](https://stackoverflow.com/questions/59053993/failed-to-get-existing-workspaces-querying-cloud-storage-failed-storage-bucke)に書かれている方法そのままです。

これで解決します。

```sh:解決法
terraform init -reconfigure
```

ええ〜。。。そんなの書いてないよ〜って泣いてました。笑
迷える誰かに伝わりますように...
Line Botでブロードキャストメッセージを使って友達全員にメッセージを一斉送信した。

## はじめに
Line Botで友達全員にメッセージを送るときにブロードキャストメッセージを使うと簡単にできたのでメモ。

## 送信方法
Google App Scriptを使用。
__CHANNEL ACCESS TOKEN__をLine Botのアクセストークンに置き換えてください。

```javascript
const token = 'CHANNEL ACCESS TOKEN';

function broadcast() {
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/broadcast', {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + token,
    },
    payload: JSON.stringify({
      messages: [
        {
            type: 'text',
            text: 'メッセージ１'
        },
        {
            type: 'text',
            text: 'メッセージ２',
        }
      ]
    }),
  });
}
```

### 実行結果
<img width="375" alt="LineCap.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/582655/d2171511-36d3-cbd2-da6f-5f5b7a9b9598.jpeg">


## 参考
- [Messaging APIリファレンス | LINE Developers](https://developers.line.biz/ja/reference/messaging-api/#send-broadcast-message)
- [GASとLINE Messaging APIでpushメッセージのLINEbotを作る！](https://qiita.com/n_oshiumi/items/a1a02e03093825f41e01)
helm3での依存関係を持たせるのは2種類

# 使い道
例えば、WebとDBのシステムだと先にDB起動させておかないといけない。
そういう時に使う

# chartsディレクトリにいれる

そうすると先に実行される。
その中のchartsディレクトリにいれるとさらに先に実行される。

```
~/D/helm ❯❯❯ tree mychart
mychart
├── Chart.yaml
├── charts
│   └── mychart-0.1.0.tgz
├── templates
│   └── configmap.yaml
└── values.yaml
```

# dependency

指定する
gitリポジトリを直接指定とかもできる

```
# file: requirements.yaml
dependencies:
  - name: memcached
    version: 2.8.3
    repository: "@stable"
  - name: redis
    version: 8.0.8
    repository: "@stable"
```

#　依存関係系のコマンド

インターネット上のリポジトリからchartsディレクトリにダウンロードしてくる
つまり結局chartsディレクトリの仕組みを使っているんですねなるほど

```
helm dependency update チャート
```
Idris の環境構築をする（Mac OS）

こんにちは, ねりあです.

この記事は Idris という言語についての記事になります.

僕は別件で依存型に関する記事を書いていたんですが, その途中で
「依存型の話を書いてるのに Idris 触ったことないの〜プークスクスw」
と脳内の自分が煽ってくるので, とりあえず環境構築したので, その話を記事にします.

## インストール

まずは install です.
と言っても以下の通り, brew install するだけです.

```bash
$ brew update
$ brew install idris
```

これで, Idris のインストール終了です.
インストールというか環境構築が終了してしまいました.
以下でバージョンが表示されれば完了です.

```bash
$ idris -v
1.3.2
```

## プログラムを実行してみる

流石にこれで終わるのは味気ないので, 世界に挨拶ぐらいはしましょう.
以下を作成します.

```haskell:hello.idr
main: IO()
main = putStrLn "Hello World"
```

あとは実行するだけです.
コンパイルして実行する方法と REPL から実行する方法の2種類があります.

### コンパイルして実行する方法

以下の通り実行すればコンパイルして実行することができます.

```bash
$ idris hello.idr -o hello
$ ./hello
Hello World
```

### REPL で実行する方法

`idris` コマンドで REPL を起動でき, REPL 内でも実行できます.

```bash
$ idris
     ____    __     _                                          
    /  _/___/ /____(_)____                                     
    / // __  / ___/ / ___/     Version 1.3.2
  _/ // /_/ / /  / (__  )      http://www.idris-lang.org/      
 /___/\__,_/_/  /_/____/       Type :? for help               

Idris is free software with ABSOLUTELY NO WARRANTY.            
For details type :warranty.
Idris> :load hello
*hello> :exec main
Hello World
```

REPL 内では `:` を付けるとコマンドとして認識してくれます.
REPL を抜けたい場合は `:quit` で抜けることができます.

## まとめ

まあ, 大したことを話していないので, まとめもくそもないのですが,
Idris は依存型について調べているとサンプルコードで大量に見ます.
依存型が何かという話は現在記事を執筆中なので, またの機会に.
helm3でhelm installコマンド実行時に「Error: must either provide a name or specify --generate-name」 と出る

chartsディレクトリの中にチャートファイルをそのまま入れているとなぜかエラーになる。
理由はよく分からないけれど、パッケージングすると動くみたいです。

## helmのバージョン

```
~/D/helm ❯❯❯ helm version                                                                  ✘ 1
version.BuildInfo{Version:"v3.1.0", GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa", GitTreeState:"clean", GoVersion:"go1.13.8"}
```

## エラーメッセージ

```
~/D/helm ❯❯❯ helm install mychart
Error: must either provide a name or specify --generate-name
```

## 構成


```bash
~/D/helm ❯❯❯ tree mychart
mychart
├── Chart.yaml
├── charts
│   ├── charts
│   ├── templates
│   │   └── configmap.yaml
│   └── values.yaml
├── templates
│   └── configmap.yaml
└── values.yaml
```

# 対処手順
chartsディレクトリ内のチャートをパッケージングする

```
~/D/helm ❯❯❯ cd mychart
~/D/h/mychart ❯❯❯ helm package charts                                                    
Successfully packaged chart and saved it to: /Users/yuta/Desktop/helm/mychart/mychart-0.1.0.tgz
```

## 構成
次のような構成にする

```
~/D/helm ❯❯❯ tree mychart
mychart
├── Chart.yaml
├── charts
│   └── mychart-0.1.0.tgz
├── templates
│   └── configmap.yaml
└── values.yaml
```

```bash
helm install mychart --generate-name
```
2つの分かち書きの対応を計算する

言語処理をする際，mecabなどのトークナイザを使って分かち書きすることが多いと思います．本記事では，異なるトークナイザの出力（分かち書き）の対応を計算する方法とその実装（[tokenizations](https://github.com/tamuhey/tokenizations)）を紹介します．
例えば以下のような，sentencepieceとBERTの分かち書きの結果の対応を計算する，トークナイザの実装に依存しない方法を見ていきます．

```
# 分かち書き
(a) BERT          : ['フ', '##ヘルト', '##ゥス', '##フルク', '条約', 'を', '締結']
(b) sentencepiece : ['▁', 'フ', 'ベル', 'トゥス', 'ブルク', '条約', 'を', '締結']

# 対応
a2b: [[1], [2, 3], [3], [4], [5], [6], [7]]
b2a: [[], [0], [1], [1, 2], [3], [4], [5], [6]]
```

# 問題

先ほどの例を見ると，分かち書きが異なると以下のような差異があることがわかります

1. トークンの切り方が異なる
2. 正規化が異なる (例: ブ -> フ)
3. 制御文字等のノイズが入りうる (例: #, _)

差異が1.だけなら簡単に対処できそうです．二つの分かち書きについて，1文字ずつ上から比べていけば良いです．実際，以前spaCyに実装した`spacy.gold.align`([link](https://github.com/explosion/spaCy/pull/4526))はこの方法で分かち書きを比較します． 
しかし2.や3.が入ってくると途端にややこしくなります．各トークナイザの実装に依存して良いならば，制御文字を除いたりして対応を計算することができそうですが，あらゆるトークナイザの組み合わせに対してこのやり方で実装するのは骨が折れそうです．
[spacy-transformers](https://github.com/explosion/spacy-transformers)はこの問題に対して，[ascii文字以外を全部無視する](https://github.com/explosion/spacy-transformers/blob/88814f5f4be7f0d4c784d8500c558d9ba06b9a56/spacy_transformers/_tokenizers.py#L539)という大胆な方法を採用しています．英語ならばそこそこ動いてくれそうですが，日本語ではほとんど動きません．
ということで今回解くべき問題は，上記1~3の差異を持つ分かち書きの組みの対応を計算することです．

# 正規化

言語処理では様々な正規化が用いられます．例えば

- [Unicode正規化](https://unicode.org/reports/tr15/): NFC, NFD, NFKC, NFKD
- 小文字化
- アクセント削除

などです．上記一つだけでなく，組み合わせて用いられることも多いです．例えばBERT多言語モデルは小文字化+NFKD+アクセント削除を行なっています.

# 対応の計算法

2つの分かち書きを`A`, `B`とします．例えば`A = ["今日", "は", "いい", "天気", "だ"]`となります．以下のようにして対応を計算することができます．

1. 各トークンをNFKDで正規化し，小文字化をする
2. `A`, `B`のそれぞれのトークンを結合し，2つの文字列`Sa`, `Sb`を作る. (例: `Sa="今日はいい天気だ"`)
3. `Sa`と`Sb`の編集グラフ上での最短パスを計算する
4. 最短パスを辿り，`Sa`と`Sb`の文字の対応を取得する
5. 文字の対応からトークンの対応を計算する

要するに適当に正規化した後に，diffの逆を使って文字の対応を取り，トークンの対応を計算します．肝となるのは3で，これは編集距離のDPと同じ方法で計算でき，例えば[Myers' algorithm](http://www.xmailserver.org/diff2.pdf)を使えば低コストで計算できます．
1.でNFKDを採用したのは，Unicode正規化の中でもっとも正規化後の文字集合が小さいからです．つまりヒット率をなるべくあげることができます．例えば"ブ"と"フ"はNFKDでは部分的に対応を取れますが，NFKCでは対応を取れません．

```python
>>> a = unicodedata.normalize("NFKD", "フ")
>>> b = unicodedata.normalize("NFKD", "ブ")
>>> print(a in b)
True
>>> a = unicodedata.normalize("NFKC", "フ")
>>> b = unicodedata.normalize("NFKC", "ブ")
>>> print(a in b)
False
```

# 実装

実装はこちらに公開しています: [GitHub: tamuhey/tokenizations](https://github.com/tamuhey/tokenizations)

中身はRustですが，Pythonバインディングも提供しています．Pythonライブラリは以下のように使えます．

```console
$ pip install pytokenizations
```

```python
>>> import tokenizations
>>> tokens_a = ['フ', '##ヘルト', '##ゥス', '##フルク', '条約', 'を', '締結']
>>> tokens_b = ['▁', 'フ', 'ベル', 'トゥス', 'ブルク', '条約', 'を', '締結']
>>> a2b, b2a = tokenizations.get_alignments(tokens_a, tokens_b)
>>> print(a2b)
[[1], [2, 3], [3], [4], [5], [6], [7]]
>>> print(b2a)
[[], [0], [1], [1, 2], [3], [4], [5], [6]]
```

# 終わりに

先日，[Camphr](https://qiita.com/tamurahey/items/53a1902625ccaac1bb2f)という言語処理ライブラリを公開しましたが，このライブラリの中で`pytokenizations`を多用しています．transformersとspaCyの分かち書きの対応を計算するためです．おかげで，2つのライブラリを簡単に結合できるようになり，モデルごとのコードを書く必要がなくなりました．地味ですが実用上非常に役に立つ機能だと思います．
KubernetesのAWS Ingress Controllerをhelm3でデプロイする

# 公式リポジトリ

https://github.com/helm/charts/tree/master/incubator/aws-alb-ingress-controller

# 手順

## リポジトリの追加

```bash
helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
```

## デプロイ

```bash
helm install incubator/aws-alb-ingress-controller --set clusterName=eks-cluster --set autoDiscoverAwsRegion=true --set autoDiscoverAwsVpcID=true --namespace kube-system --generate-name
```

## デプロイ確認

```
helm list
```

出力結果

```
NAME                                 	NAMESPACE	REVISION	UPDATED                             	STATUS  	CHART                            	APP VERSION
aws-alb-ingress-controller-1582017381	default  	1       	2020-02-18 18:16:24.343653 +0900 JST	deployed	aws-alb-ingress-controller-0.1.13	v1.1.5
```
docker-airflow で ログイン認証機能を設定する

![スクリーンショット 2020-02-18 20.11.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20307/83bd41d6-985f-a85f-429f-980645733cfd.png)


[docker-airflow](https://github.com/puckel/docker-airflow)でログイン機能を設定したい人向け。
docker-airflowの場合、[公式ドキュメント](https://airflow.apache.org/docs/stable/security.html#web-authentication)の手順通り実行すると、スクリプトでのユーザー登録でこける。

```
sqlite3.OperationalError: no such table: users
```

なので、自力でpasswordのハッシュ値生成して、直接ユーザー登録する必要があるので、手順をメモ。
CeleryExecutorの前提で書くけど、LocalExecutorでも同じだと思う。


# 1. airflow.conf を編集する

`${PROJECT}/config/airflow.cnf` の `[webserver]` の項目のauthの設定(airflow-1.10.9ではL300あたり)を以下のようにする

```
~略~

[webserver]

~略^

# Set to true to turn on authentication:
# https://airflow.apache.org/security.html#web-authentication
authenticate = True
auth_backend = airflow.contrib.auth.backends.password_auth 
```

とりあえず、これでログイン必須になる。アクセスするとログイン画面は表示される。

# 2. パスワードのハッシュを取得

pythonスクリプトで取得する。ハッシュが取れればなんでもok。

```sh
# なければ、flask-bcryptをインストール
pip install flask-bcrypt

# pythonスクリプトを実行しハッシュを取得
python
>>> from flask_bcrypt import generate_password_hash
>>> generate_password_hash('your_password', 12)

# 出力されたハッシュ値をメモ
```

# 3. postgreSQLに直接ユーザー情報を登録する

直接postgreSQLでuser登録を行う。

```
# コンテナ一覧表示
docker ps

# postgreSQLのコンテナにsshログイン
docker exec -it ${PSQL_CONTAINER_ID} bash

# airflowユーザーでpsqlにログイン
psql -U airflow

# ユーザーをinsert
INSERT INTO users (username, email, superuser, password) VALUES ('your_name', 'your_email', True, '２で取得したハッシュ値');
```

これでdockerを再ビルドして、起動すればok。

# 参考
[Fail to create user on users table on Docker Webserver Container](https://github.com/puckel/docker-airflow/issues/201)
React Native Expoでiost.jsを読み込む


React Native Expoから[iost.js](https://github.com/iost-official/iost.js/tree/master)を読み込む方法です。

## 手順
### 1.必要なパッケージのインストール

```
npm install rn-nodeify -g
npm install --save crypto-browserify iost expo-random
```

### 2. crypto以外のstandard libraryをインストール
以下を実行してstandard libraryのReact Native版をインストールしてください。

```
rn-nodeify --install 'buffer,events,http,https,os,process,stream,tty,url,util,vm,zlib' --hack
```

ルートフォルダにshim.jsができるので、App.jsの先頭にimport文を追記します。

``` js
import './shim.js'
```

### 3. randomBytesをexpo-randomのgetRandomBytesAsyncに変える
tweetnaclフォルダのnacl-fast.jsを書き換えます。

以下の変更後のコードをnode_modules/tweetnacl/nacl-fast.jsと入れ替えてください。
https://gist.github.com/kosamit/fa678743be94fab49019611cf8360768#file-nacl-fast-js

#### node_modules/tweetnacl/nacl-fast.jsの変更内容
expo-randomのパッケージを読み込み、crypto.getRandomValuesをRandom.getRandomBytesAsyncに変えました。

差分↓

``` javascript
@@ -2363,24 +2363,24 @@
 (function() {
   // Initialize PRNG if environment provides CSPRNG.
   // If not, methods calling randombytes will throw.
+  var Random = require('expo-random');
   var crypto = typeof self !== 'undefined' ? (self.crypto || self.msCrypto) : null;
   if (crypto && crypto.getRandomValues) {
     // Browsers.
     var QUOTA = 65536;
-    nacl.setPRNG(function(x, n) {
+    nacl.setPRNG(async function(x, n) {
       var i, v = new Uint8Array(n);
       for (i = 0; i < n; i += QUOTA) {
-        crypto.getRandomValues(v.subarray(i, i + Math.min(n - i, QUOTA)));
+        await Random.getRandomBytesAsync(v.subarray(i, i + Math.min(n - i, QUOTA)));
       }
       for (i = 0; i < n; i++) x[i] = v[i];
       cleanup(v);
     });
   } else if (typeof require !== 'undefined') {
     // Node.js.
-    crypto = require('crypto');
-    if (crypto && crypto.randomBytes) {
-      nacl.setPRNG(function(x, n) {
-        var i, v = crypto.randomBytes(n);
+    if (true) {
+      nacl.setPRNG(async function(x, n) {
+        var i, v = await Random.getRandomBytesAsync(n);
         for (i = 0; i < n; i++) x[i] = v[i];
         cleanup(v);
       });
```

### 4. iost.jsを読み込む
使いたい場所で以下のコードでnode_modules/iost/indexを読み込んでください。
require('iost')とするとcryptoパッケージが読み込めないエラーが発生します。

``` js
import IOST from './node_modules/iost/index';
```

## 注意
### 1. rn-nodeifyで入れたパッケージは再インストールする必要がある場合がある
rn-nodeifyで入れたパッケージはたまに再インストールしなければいけない場合があります。
以下をpackage.jsonのscriptsに追記し、再起動したらnpm run postinstallを実行してください。

``` package.json
"scripts": {
	…
    "postinstall": "rn-nodeify --install 'buffer,events,http,https,os,process,stream,tty,url,util,vm,zlib' --hack"
  },
```

### 2. bs58パッケージはIOSTから読み込む
bs58はrequireせずにimportしたIOSTから使ってください。

``` js
let bs58 = IOST.Bs58;
```
BigQueryで虚無からSQLだけでデータを作る

テストデータを作るときに便利！

# カラム名付きリレーション
`UNNEST`、`ARRAY`、`STRUCT`を使えば、カラム名付きリレーションを作り出せる。

```sql
#standardSQL
SELECT * FROM UNNEST(ARRAY<STRUCT<id INT64, name STRING>>
[
  (1, 'COMPパウダー')
  , (2, 'COMPグミ')
  , (3, 'COMPドリンク')
])
```

|行|	id	|name	|
| --- | --- | --- |
|1|	1|COMPパウダー|
| 2 |2  | COMPグミ |
| 3 | 3|COMPドリンク|
Jasper Reports ハイパーリンクでコンテンツリソースをダウンロードする

# はじめに
コンテンツリソース（Excel,PDF等）をレポートのハイパーリンクからダウンロードするのに苦戦したため、備忘録として記事にした。

## 前提
 - JaspersoftStudioの基本操作はマスターしている
 - ダウンロード対象のファイルがリポジトリからはダウンロードできる状態

# やり方
リンク用のテキストフィールドを作成したら、ハイパーリンクの設定

リンクタイプ：Reference
リンク先："https://MYSERVER/jasperserver-pro/fileview/fileview/Parentfolder/resouce.xlsx"
リンク先は環境によって変更。詳細は解説参照。

## 解説
リンクタイプは、外部ソース参照の為「Reference」を使用する。
<details><summary>（参考）ハイパーリンクの種類</summary><div>

|リンクタイプ|説明|
|:--|:--|
|Reference|通常のURLで外部ソースを参照する|
|LocalAnchor|現在レポートの特定アンカーに遷移する |
|LocalPage|アンカーではなく、現在レポートの別ページに遷移する|
|RemoteAnchor|外部ドキュメントにある特定のアンカーに遷移する |
|RemotePage|外部ドキュメントの特定のページに遷移する|
|ReportExecution|サーバー内のレポートを実行する（ドリルダウン等で使用）|
</div>
</details>
リンク先の構成は下記のようになっている。

|URL部|説明|
|:--|:--|
|https://|HTTP接続の場合はhttp://|
|MYSERVER/|ホスト:ポート番号|
|jasperserver-pro/|アプリケーション名|
|fileview/fileview/|ファイル参照の為の決まり文句|
|Parentfolder/|rootを除くファイルパス(プロパティで確認できるパス)|
|resouce.xlsx|ファイル名(拡張子もいれる)|
**fileview/fileview/**　以外を環境に合わせてリンク先のURLを編集すれば良い。

# 最後に
Jaspersoftはリンク先によって記載方法が異なるので注意。
ダッシュボード、アドホックについても追々記事にしていきます。

## 参照
- [How to reference a Content Resource in a hyperlink -JasperCommunity](https://community.jaspersoft.com/questions/815462/how-reference-content-resource-hyperlink)
- [Anchors, Bookmarks, and Hyperlinks -JasperCommunity](https://community.jaspersoft.com/documentation/tibco-jaspersoft-studio-user-guide/v60/anchors-bookmarks-and-hyperlinks)
- [JasperReports - Hyperlink Sample (version 6.11.0)](http://jasperreports.sourceforge.net/sample.reference/hyperlink/index.html)
UiPath Studio Xとは！Studioと何が違うの？

UiPath Studio Xが公開されてからしばらく経ちましたが、UiPathユーザーの皆様はStudioXを使用したことがありますか。

今回はStudio Xについてまとめてみました！

※本記事はUiPath Talk vol.2で登壇した内容と同じものです。


# UiPath Studio Xとは？

Studio Xの大きな特徴は以下の2点です。

1. プログラミングスキルがない人でも使える
2. 機能やアクティビティを最低限のもののみに絞っている


## 1. プログラミングスキルがない人でも使える

まず、Studio Xでは変数・データ型という概念がありません。裏側では当然変数で処理をしていますが、表立って変数という概念を出さない作りになっています。

次に、プログラミングの基本である「順次」「分岐」「繰り返し」に関して、StudioXではかなり簡単になっています。

* 順次 ⇒ フローチャートやステートマシンを廃止し、シーケンスのみにした
* 分岐 ⇒ 条件分岐のアクティビティの使用方法がより易しくなっている
* 繰り返し ⇒ 繰り返しのアクティビティの使用方法がより易しくなっている

分岐と繰り返しについては、うまく説明できないので実際に触ってみて感じてほしいです！

## 2. 機能やアクティビティを最低限のもののみに絞っている

標準搭載のアクティビティ数はStudioで約270個あったものが、StudioXでは約50個しかありません。頻繁に使う主要なアクティビティしかありません。

そして、なるべくビジネスユーザーでも自動化できるようにしよう...！！と考えられた結果、 **レコーディング機能・デバッグ機能がないではありませんか！！**
（レコーディング機能は今後追加される予定があるそうです）

それだけでなく、トライキャッチやスロー・再スロー・リトライスコープなど、エラー処理系のアクティビティはありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/70b640a4-3601-f301-e9b4-eb62f70c8901.png)


# StudioとStudioXはどう違うの？

## Studioは大規模・複雑な業務の自動化向き

大規模な業務や、複雑な業務を自動化するのに向いており、Studioに詳しいUiPathエンジニアがせっせと開発します。

この場合、RPAに疎い会社は外注せざるを得なくなります。

ザ・UiPathエンジニアの出番！という感じですね。

## StudioXは単純・簡単な業務の自動化向き

単純な業務や処理が簡単な業務の自動化に向いており、業務の担当者が自ら自動化をすることができます（StudioXがそういうコンセプトで作られています）

RPAに疎い会社、疎い人材でも自動化をすることができ、非エンジニア向きの製品となっております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/dd0e01f4-2458-771e-b081-c2cfade4bd1c.png)


# Studio XはROIが低い（低そうな）業務を自動化できる！

なぜStudio Xが誕生したか？その理由は、**ROIが低い（低そうに見える）業務の自動化は後回しにされがちだから**です。


単純な業務や処理が簡単な業務は、せいぜい人でで処理しても1~2分だったりします。（違ったらごめんなさい）
その処理が月間どのくらいの頻度で発生するかは業務によってさまざまだと思いますが、このような業務はROIが低い！とみなされがちです。

しかし、このような業務でも、「ストレスを感じている社員がいる」、「こういう細かい業務が途中途中で入ってくるから他の業務に集中できない」などの理由で自動化したいケースは多いです。

そこで！わざわざ外注しなくても、処理が簡単な業務なら自分たちで自動化してしまおうぜ！！！
⇒ StudioX使おうぜ！！

となるわけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/69a9ab62-e1ee-2181-b9be-e799e49d64ce.png)


# StudioとStudioXはどちらがいい？

ケースバイケースという回答はナンセンスだと思います。大事なのはどういうケースのときにどちらを採用したほうが良いのか？という基準を明確にすることです。

**ここからは100%個人の考えで、反論や意見も大歓迎です。**

## 良いワークフローとは？

StudioとStudioXどちらを採用するか？を考えたときに個人的な回答は「（ビジネスユーザーが開発するときは）StudioXで良いワークフローが作れるならStudioX、作れないならStudioを採用しましょう！」です。


その良いワークフローというのを個人的な観点から説明します。

良いワークフローを測る観点は以下の3つがあると思っています。

* エラーが発生した時に修正しやすい（可読性、保守性）
* そもそもエラーが発生しない（安定性）
* 処理が早い（処理速度）

そして、この中で一番大事なのは何でしょうか...？

**「そもそもエラーが発生しない（安定性）」**

これに尽きると思います。

RPAの場合、ユーザーの要望通りのアウトプットが出てくると100点！ですが、そうでなければ0点です。0点か100点の世界だと思います。エラーが発生せずに要望通りのアウトプットが出たら、処理が少しくらい遅くたって、ワークフローが少しくらい解読できなくたって、ユーザーからしたらあまり重要じゃないと思うんです。


まあしかし、処理速度やワークフローの内部も重要なので、「StudioとStudioXはどちらがいい？」に対する僕の回答は「（ビジネスユーザーが開発するときは）StudioXで安定したワークフローが作れるならStudioX、作れないならStudioを採用しましょう！（ただし明らかに処理速度がかなり遅かったり、解読不能なワークフローができてしまう場合を除く）」です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238739/7d4b1d70-b821-500f-7bd5-aa6d9248ad1c.png)


# おわりに

最後かなり個人的な意見も書かせてもらいましたが、

ビジネスユーザーがStudioXを用いて安定したワークフローが作れると、自動化する業務の範囲も増えるのではないでしょうか。

簡単な業務は内製化して、複雑な業務は外注するという方法が今後増えてきそうです。

ただ、StudioXは機能を削減している分、できないこともかなり多いので、その辺は注意すべき点でしょう。

というわけで、今回はStudioXについての紹介を終わります。最後までご覧いただきありがとうございました！