OmniAICreator/Qiita-1.07M huggingface dataset:
【Rust】Docs.rsでビルドエラーが出たらどうしたらいい？

# ローカルではビルド出来てたのに...？

Rustで作ったクレートを、[Crates.io](https://crates.io)にパブリッシュすると、自動で[Docs.rs](https://docs.rs)のほうにそのクレートのドキュメントが生成されます。

ただ、僕がとあるクレートをパブリッシュした後Docs.rsを確認してみると以下のようなメッセージでビルドエラーを告げられました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143170/353de3ac-5db4-6b59-4290-85b94cf665f2.png)

要は、ビルド失敗してるからbuild logを見てみてねと書かれています。

build logは以下のようになっていました。カオスですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143170/626f080d-7298-b915-b20a-5702179c1669.png)

# 原因と対処
まずDocs.rsは、アップロードされたクレートに対してデフォルトでlinux用にビルドするらしいです。僕のクレートはwinapiというwindows専用のクレートを使っていたため、当たり前ですがlinuxでは動かず、このような「ほとんどののシンボルが存在しません」というようなエラーが出ていたようです。

私の場合、以下のようにCargo.tomlに書き込むことでDocs.rsがwindows用にビルドするように命令し、エラーを消すことができました。

```toml
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
targets = ["aarch64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]
#こういうのもあり
#features = ["everything"]
```

# どうしても原因が分からない人のための救済措置

実はrustチームは、Docs.rsのビルドでエラーが出た方の質問を、Githubのissueを通して受け付けています。
**[このリンク](https://github.com/rust-lang/docs.rs/issues/new/choose)**からCrate Build Failureを選択し、issueを発行してみましょう。
**僕の場合はその日のうちに返信が来て、解決することができました。(2021/11/05)**本当に親切なチームです。感謝です。

以上、Docs.rsのビルドエラーで困った人用のメモでした。
Python機械学習/summarytoolsでデータの概要把握

# Introduction
summarytoolsパッケージ内のdfSummaryを使ってデータ概要を可視化する方法について解説．

# パッケージインストールとインポート
```py
# ! pip install summarytools
from summarytools.summarytools import dfSummary
import pandas as pd
import seaborn as sns
```

# データの準備
今回は，seaborn内のタイタニックデータを使用．

```py
data = sns.load_dataset('titanic')
data.head()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/931058/17f448b9-b213-df5d-62a3-f88298a58c5a.png)

# データ概要の可視化
`dfSummary`でデータ概要を可視化

<details><summary>引数の詳細</summary><div>

- data：データフレーム
- max_level = 10：カテゴリー変数の表示数
- show_graph = True：グラフ表示の有無
- tmp_dir = './tmp'：画像のディレクトリ
- is_collapsible = False：ページ折り畳みの有無

個人的には，1)画像は保存しない，2)ページは折りたたむ；以下のスタイルがおすすめ

```py
dfSummary(data, tmp_dir = '', is_collapsible = True)
```
</div></details>

```py
dfSummary(data)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/931058/e36a422f-4df6-de46-7787-d57c4930d038.png)

実行してみると，自分の環境ではint64型の列がうまく表示されない現象が起きた．
この理由が分かる人は，是非ともコメントで教えてほしい．
とりあえずint64型の列を一度int32型に変換したら改善した．

```py
col_int = data.select_dtypes(include='int64').columns
data[col_int] = data[col_int].astype(int)
```
```py
dfSummary(data)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/931058/77653969-c145-4d65-5c32-5ed0054fb82b.png)

# Conclusion
`dfSummary`はとりあえずデータの概要を把握したいときに，非常に簡単に使える関数なので活用されたし．

------------------------------------------
------------------------------------------

# code
```py
# パッケージインストールとインポート
# ! pip install summarytools
from summarytools.summarytools import dfSummary
import pandas as pd
import seaborn as sns

# データの準備
data = sns.load_dataset('titanic')
data.head()

# データ概要の可視化
col_int = data.select_dtypes(include='int64').columns
data[col_int] = data[col_int].astype(int)
dfSummary(data)
```
PHPの=>と->の違い

PHPに最近入門したのですが、JSを既に知っているとPHPの`=>`と`->`に大変混乱させられます。アロー関数では全くないんですよね。。(Javaでも=>は同じような意味なのに...)

###`=>`は配列にアクセスするときに使う

```index.php
$arrSamp = array(
    0 => 'Ringo',
    1 => 'Mikan',
    2 => 'Ichigo',
    3 => 'Kiwi'
);

print_r($arrSamp);

//表示結果
//Array
//(
//    [0] => Ringo
//    [1] => Mikan
//    [2] => Ichigo
//    [3] => Kiwi
//)

//Json Objectにも
echo json_encode(["error" => ["type" => "server_error", "message"]]);

//結果
//{"error":{"type":"server_error","0":"message"}}
```

###`->`はオブジェクトのメソッドやプロパティへアクセスするときに使う

```Sample.php

class Sample {
        function hoge() {
           echo 'hoge';
        }
}
```

```index.php

$Hoge = new Sample();
$Hoge->hoge(); //hoge
```

JSなら`Sample.hoge()`で呼び出すところですが、これが`->`になるんですね。めちゃくちゃ紛らわしいです。結構忘れるので備忘録。
中華EtherCAT スレーブを買ってみた その2

前回の続き

https://qiita.com/fa_yoshinobu/items/95185f53108078ff8bee

中華のEtherCat IOモジュールを買ったのだが動かしてみるには入力や出力に何かを接続しないといけない。
お金もかかるしめんどくさいので考えてみた結果がこれと接続すること。

https://qiita.com/fa_yoshinobu/items/7bbf5d590bc0f909087a

PLCとIOモジュールを接続してSoftGOTでボタンやランプを作れば最小限の費用でテストできるのではと思った。

# 配線
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/3d6527e1-f71b-2643-d240-0c3af3dc5f1f.png)

Markは線番なので同じ番号の所どうしが接続されます。
INEXBOT-IO-R2のVCCは基板内でVCCが接続されています。
つまりP24とつながってます。
下の板はセリアで買った桐のまな板です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/a96c5a0d-538f-3850-33fa-1e0b173584d9.png)

# SoftGOT
SoftGOTでこういう画面を作りX/Yをモニタできて、YはON/OFF出来るようにしてみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/30f1fec9-0f3a-301f-8c1e-e9d814498699.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/882e76e6-12ed-3aa2-410b-24b026f2c398.png)

これで準備は出来たのでTwinCATを勉強しよう
Hugoの基礎知識

基本的な事柄をまとめていきます。都度更新です。

## 変数の設定

三段階の設定の仕方があります。

- config.yaml/toml/json...サイト全体へ適用される機能
	- https://gohugo.io/getting-started/configuration/
- the front matter...コンテンツタイプのインスタンスにメタデータを添付しておくことができる機能
	- https://gohugo.io/content-management/front-matter/
- default page variables...ページレベルの変数を保持する機能
	- https://gohugo.io/variables/page/


## パーティアル

テンプレートを小分けに定義しておける機能。

- Partial Templates...リストやページのテンプレートに含まれる、コンテキストを考慮した小さなコンポーネントのこと
    - https://gohugo.io/templates/partials/
- Shortcodes...コンテンツファイルの中にあるシンプルなスニペットのこと
    - https://gohugo.io/content-management/shortcodes/
htmlファイル内にjsが書かれている場合のデバック方法

これ知らなかったのでかなり便利！！
詳しくは[参考サイト](https://qiita.com/madobon/items/cf1a22b7960840bb1a65)を見てください！！

以下のコードを入れるだけで開発ツールにJSファイルとして出てくる
ファイル名はお好きにつけてください

```
//# sourceURL=hello-world.js
```
Powershellの便利な$Profile設定例

# $Profileとは？
.bashrcみたいな設定ファイルのこと。私は「[PowerShellの.bashrc的な設定ファイルこと$Profileについての紹介](https://qiita.com/smicle/items/0ca4e6ae14ea92000d18)」の記事で知りました。

# 設定例
備忘録として残しておきます。親フォルダへの移動（..）やEmacsモードは上記リンクの記事を参考にさせていただきました。

```powershell
# omit hidden files
function ols {Get-ChildItem | Where-Object {$_.Name -notmatch '^[._]'}}

# touch
Set-Alias -Name touch -Value New-Item

# grep
Set-Alias -Name grep -Value Select-String

# cd to a parent directory
function ..() {Set-Location ../}

# substitute file or folder names
function ss($Before, $After, $Path = ".") {
    $items = Get-ChildItem -Path $Path -Filter "*${Before}*"
    for($i = 0; $i -lt $items.Length; $i++) {
        $target = Join-Path $Path $items[$i].Name
        $newname = $items[$i].Name -replace $Before, $After
        Rename-Item -Path $target -NewName $newname
    }
}

# like bash
Set-PSReadLineOption -EditMode Emacs -BellStyle None

```
AWS CLIの設定の仕方(アクセスキーIDとシークレットアクセスキーの取得編)

AWSにポートフォリオをデプロイするためにCLIの設定を行いました。

AWSのconfigure(設定)には
・アクセスキーID
・シークレットアクセスキー
・AWSリージョン
・出力形式
上記の4つの情報を入力しなければなりません。

まずIAMのIdentify and Access Manegementのユーザーを開き、ユーザを選択して、認証情報のタブを選択し、アクセスキーを作成のボタンをクリックします。そしてMFAデバイスの割り当ての管理をクリック。
MFAデバイスの管理で仮装MFAデバイスにチェック入れ、続行。

そうするとQRコードを表示することができるので、それをスマホのauthenticator (googleの認証システムなど)を使って読み取ります。

そうすると６桁の数字が一定時間おきに表示されるので、そのコードをMFAコードに入力します。（入力するMFAコード欄は二つあるのですが、同じコードは打ち込まないでください。）
karidofaceでどのように3Dモデルを動かしているのか検証してみた（Mediapie, Three.js, VRM）　①口編

```
/**
 *  先日、rich yeemachineさんより、karidofaceでトラッキングに利用しているコードが公開されました🎉
 *  https://github.com/yeemachine/kalidokit
 *  ので、ナンバリングしていましたがこの企画は本記事にて終了します🙏
 */
```



## はじめに
karidofaceというwebカメラとブラウザだけでLive2D & 3D トラッキングができるwebサービスがあり、
そこでMediapipeを使用してThree.js上でVRMモデルを動かしていて、どのように実現しているか気になったため検証してみました。

https://3d.kalidoface.com/

https://github.com/yeemachine/kalidoface-3d

### Mediapipeとは
- 動画などのメディアの推論をデバイスで行うためのフレームワーク
- パイプライン（機械学習に必要な処理をまとめる仕組み）を提供
- 前処理から描画までを素早く行える

https://google.github.io/mediapipe

公式でカスタマイズ可能なオープンプンソースのSolutionが提供されており、karidofaceではそのうちのHolisticが使用されています。
<img src="https://google.github.io/mediapipe/images/mobile/holistic_sports_and_gestures_example.gif" >
https://google.github.io/mediapipe/solutions/holistic.html


## 口の動きの検証
取り合えずはじめに口のトラッキングを検証しました。
（今回は口だけの検証なのでHolisticではなくFaceMeshという顔のトラッキングのみを行うSolutionを使用しました。）


まずはじめに

VRMの口の動きは、 <b>「a」 ~ 「o」</b> のどの形かを指定し、

次に <b>0 ~ 1</b> の数値を指定してどれくらいの開き具合かを指定して動かします。

![スクリーンショット 2021-11-07 16.57.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/9279caa5-75ac-cafa-0c87-343d63c01321.png)

一方 FaceMesh ではwebcameraの映像を基にトラッキングしたデータを、3d空間上の座標として取得できます。

```js
[
  {
    x: 0.5567474961280823, 
    y: 0.5287743806838989, 
    z: -0.00007370980165433139, 
    visibility: undefined // 描画域に表示されるかどうか
  },
  ...
]
```

口の動きをVRMモデルに反映するためには、この座標データから <b>「a」 ~ 「i」</b> のうちどの形になっているかと、<b>0 ~ 1</b> のうちどの開き具合かを検出する必要があります。

#### 検証1 縦横比で母音検出
まずは縦横比を利用することを考えました。
<b>「o」の場合は1:1</b>というように、比率で各母音と開き具合を取得できるのではという案です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/db07db54-fdd8-abed-580e-e237abfc4f7c.png" width="600px">
<font color="gray">※ FaceMeshで取得したlandmarkを口部分だけthree.jsで書き出したもの（数字は各landmarkのindex）</p>
<br>
ただ縦横比だけだと正確に母音を検出できないケースがあり
調べた結果、Tensorflow BlogではHu Momentsという画像の類似度判定アルゴリズムで解決していました。

> 口の輪郭の形状がどの程度一致しているかを判断

https://blog.tensorflow.org/2020/07/using-machine-learning-in-browser-to-lip-sync.html

<br>
また他の技術ブログでは、「a」の形の開閉のみに簡略化していました。↓

https://www.m3tech.blog/entry/2019/12/19/133408

#### 検証2 

画像の類似度判定を行えば正確に口の開閉を再現できそうですが、今回はそこまで正確に判別する必要はないのと、karidofaceでも全母音対応はしていなさそうなので、比率を基にするのはそのままで、単純な計算で<b>「a」「i」「o」</b>の母音のみに再現できないかを検証しました。

![voewl.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391719/abd11e41-afa1-503d-965c-0f9c40f79523.gif)
<font color="grey">※ 利用モデル: https://3d.nicovideo.jp/alicia/</font>
<br>

方法としては表情を動かした時に、変化の少ない箇所を基準として、縦の距離と横にどれくらい開いているかを計算しています。
（今回は37と267の座標の距離を基準にしてみましたが、表情を変えた時に変化の少なそうな場所ならどこでも良いです）

この方法で、karidofaceと同じぐらいの精度でVRMモデルの口の動きを再現できました。


## 次回
次は頭、腕、上半身、手を検証していきます。
PNG圧縮ツールpngquantの使い方を解説

## はじめに
画像ファイル(PNG)を圧縮する必要があり、初めてpngquantを使ったので、インストール方法や使い方をまとめました！
今まではブラウザベースの無料PNG圧縮サービスを使っていたのですが、pngquantの方がプログラマー向けで使いやすかったです。

## 対象者
この記事は下記のような人を対象にしています。

- 駆け出しエンジニア
- プログラミング初学者
- 「画像圧縮するのにブラウザとか専用のツール使うのは面倒」というプログラマー
- 上司から「pngquant使ってPNG圧縮して」と言われた人(自分ですw)

## 結論
PNG圧縮をササッと終わらせたい！
そんな時はターミナルに下記コマンド打てばOK！

`pngquant --ext .png --force --speed 1 {your_dir}/*.png`

## PNG圧縮ツールpngquantとは
[pngquant](https://pngquant.org/)はmacのターミナルで使えるPNG圧縮ツールです。

### PNG圧縮ツールpngquantのメリット

- 複数ファイルを一括変換できる
- macのターミナルでサクッと実行できるのでプログラマー向き 

なんと言っても、**「ターミナルで実行できる」**ことが一番のメリットではないでしょうか。
開発をしていると、出来るだけテキストエディタとターミナルだけで済ませられると気が散らなくて良いですよね。

**「黒い画面は怖い...」**という方はブラウザベースの無料PNG変換ツールを使うのがおすすめです。

## PNG圧縮ツールpngquantのインストール方法
pngquantはHomebrewでサクッとインストールしましょう。
なにそれ？と言う方は[Homebrew公式ドキュメント](https://brew.sh/index_ja)を参照ください。
`brew install pngquant`

## PNG圧縮ツールpngquantの使い方
macのターミナルで下記コマンドを実行するだけです！

```
// 特定の画像だけ圧縮
pngquant {your_dir}/example.png

// 特定のディレクトリ内の画像全部圧縮
pngquant {your_dir}/*.png
```

## PNG圧縮ツールpngquantのオプション

```
// 同一ファイル名で上書き
// 指定しない場合`-fs8.png`が自動でついてファイルが新規作成される
pngquant --ext .png {your_dir}/example.png --force

// 圧縮率を指定 (JPEGの圧縮率みたいなものらしい)
pngquant --quality=65-80 {your_dir}/*.png

// 圧縮スピード (1が最も遅く、10が最も速い。遅い方が圧縮率が高い、デフォルトは3)
pngquant --speed 1 {your_dir}/*.png

// IE6用
pngquant --iebug {your_dir}/*.png
```

私が使った上で最適だと思ったオプションはこれ。

```
// 圧縮率は最高＋ファイルを上書き
pngquant --ext .png --force --speed 1 {your_dir}/*.png
```


## おわりに
PNGの圧縮ツールpngquantの使い方についてまとめました。
ターミナルを使用するので、万人向けではありませんが、プログラマ向けのPNG圧縮ツールです。
PNG圧縮はpngquantでサクッと終わらせて、開発に集中しましょう。

## 参考記事
[pngquant](https://pngquant.org/)
[png画像の圧縮には pngquant を使おう [Mac/Win]](https://qiita.com/thanks2music/items/309700a411652c00672a)
[PNG画像ファイルを圧縮してブログを高速化した](https://blog.kapiecii.com/posts/2020/05/23/compress-png-image/)
[pngquantで一括圧縮する](https://qiita.com/tusimarimo/items/0c0e666fecdda2ac927a)
cargo exampleコマンドのススメ

# cargoとは
Cargoは、Rustのビルドシステム兼、パッケージマネージャです。([参照](https://doc.rust-jp.rs/book-ja/ch01-03-hello-cargo.html))

# exampleとは
パッケージ内のexamplesフォルダ内のソースコードを実行するサブコマンドです。

# cargoで想定されるパッケージレイアウト

```
.
├── <benches>
│   └── <benches.rs>
├── <examples>
│   └── <examples.rs>
├── src
│   ├── <bin>
│   │   └── <another_executable.rs>
│   ├── <lib.rs>
│   └── main.rs
├── <tests>
│   └── <tests.rs>
├── <Cargo.lock>
└── Cargo.toml
```

:::note alert
<>で囲ったファイル又はフォルダは、cargo newコマンドでは作られません。
:::

## フォルダ名について

Cargoで作成したプロジェクトフォルダには、
あらかじめ規約が設けられています。

例えば、`benches` `bin` `examples` `tests`という
4種のフォルダ名には、暗黙的に役割が与えられています。

これらのフォルダが存在している場合は、
下記のオプションを使うことでフォルダ内の
Rustファイル（.rs）に対する操作が簡単に行えます。

```bat
cargo run --example [ファイル名]    # examplesフォルダ 
          --bin     [ファイル名]    # binフォルダ

cargo test    # testsフォルダ内のテスト実行

cargo bench   # benchesフォルダ内のベンチマーク実行
```

# 最後に伝えたいこと

ソースファイルを沢山作って実験する際は、examplesフォルダに入れて、
```powershell
cargo example [実行したいファイル名]
```
で実行を行った方がmain関数が書かれた沢山のソースファイルが
共存できるので便利だと思います。
JSONがなんとなくしかわかってなかったので少しみてみた

変数や配列をデータをPHP/java/他言語と通信する際に”文字列”にして通信する
オブジェクト変数をテキスト化したものと思えばOKみたい
そうしないとDBなどに渡せないみたい

僕は今javaメインなので調べて見ると、ライブラリを使っていじくるみたい
有名ライブラリ（Gson、Jackson）

JSで遊んでみた

```javascript
const o1 = {
    name : "sora",
    email : "@gmail.com"
}
// オブジェクトをJSON化（テキストにしてあげる）
const json = JSON.stringify(o1);
localStorage.setItem("OBJ", json);
const val = localStorage.getItem("OBJ");
// JSONをオブジェクトに戻してあげる
const obj = JSON.parse(val);
console.log(obj);
```
Cloud9環境へDockerのインストール方法 コンテナ起動

## 概要

前回作成したCloud9の環境でとりあえずdockerをインストールしてコンテナを起動してみます。コンテナは[こちら](https://qiita.com/mongamae-nioh/items/7525be957f4e5e10d403)のAdventCalendarを作成してみます。

## Dockerインストール
### Cloud9へのDockerインストール
* 以下のコマンドでDockerをインストールします。
* インストールされていた場合はスキップします。

``` shell
docker -v
sudo yum install -y docker
```
* 以下のコマンドでdocker composeもインストールしておきます。

``` shell
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose -v
```


### AdventCalendarの作成
* 以下のコマンドでコンテナをダウンロードします。

```shell
sudo docker run -it --name コンテナ名 -p ポート:3000 mongamaenioh/radvent:latest /bin/bash
```
* コンテナ名を`adventCalendar` ポートを`8080` としたコマンドは以下になります。

```shell
sudo docker run -it --name adventCalendar -p 8080:3000 mongamaenioh/radvent:latest /bin/bash
```
* ダウンロード後以下のコマンドで起動できます。

```shell
cd /home/radvent
export SECRET_KEY_BASE=`bundle exec rake secret`
bundle exec rails server -e production
```
* ブラウザでアクセスするためにセキュリティグループを追加します。
* インバウンドの設定に 設定したポート番号(ここでは8080)を設定し
てIPは自分のIPを設定します。
![スクリーンショット 2021-11-06 222623.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657697/686a1c55-ed96-811e-aa3c-83d095a491fe.png)

* 表示名などを変更したい場合は一度WEBアプリを停止して(Ctrl+C で止まります)以下のコマンドで修正します。

```shell
# 年月日の修正
vi /home/radvent/config/initializers/constants.rb
# タイトルの修正
vi /home/radvent/app/views/welcome/index.html.haml
```

* 年の部分を 2021に変更した結果は以下のように表示されます。
![スクリーンショット 2021-11-06 215950.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/657697/2bf1232c-d4bd-935b-0d61-e2aae9ff9c54.png)

* 一度Cloud9を停止後もう一度コンテナを起動したい場合は以下のようにします。

```shell
# コンテナIDを確認
docker ps -a
# コンテナを起動 040cddd29921 の部分はコンテナID
docker start 040cddd29921
# コンテナにログイン 040cddd29921 の部分はコンテナID
docker attach 040cddd29921

# WEB アプリの起動
cd /home/radvent
bundle exec rails server -e production
```
GoogleスプレッドシートのAPI化

##目次
　・Googleスプレッドシートへのデータ登録
　・steinによるAPI作成

#Googleスプレッドシートへのデータ登録
まずはGoogle　Drive上でGoogleスプレッドシートを新規作成
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/d6bf0c1e-4682-8ae8-b83c-a8ee73e1ec95.png)
スプレッドシートにデータを入力します。
![無題1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/365d0c4c-8e06-302f-5d36-47397aa6368d.png)
以上でスプレッドシートへのデータの保存が終わりました。

#SteinによるAPIの作成
API で GSS から商品データを取得できるようにするため、GSS の API が簡単に作成できる Stein というサービスを使います。

Stein は無料枠でも十分に実務で使用できますが、もし無料枠を超えてきた際に 正攻法である Google App Script (GAS) で WebAPI 化すると良いでしょう。

Stein の料金体系については、以下の公式ドキュメントを参考にしてみて下さい。 →Turn Google Sheets into a database API. Ship fast and manage your data with ease - Stein
###Steinに新規登録、ログイン
[Stein](https://steinhq.com/)のサービスから新規登録、ログインします。
![無題2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/6af3f7fa-6275-b1e7-32c7-68d710c2e75e.png)
使用用途は適当に決めて大丈夫です。

#SteinとGoogleスプレッドシートとの連携
そのまま初回のプロジェクト設定が始まります。 まずは GSS で作成したシートの URL が必要なので、以下のようにコピーします。
![無題1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/253571b0-f711-f737-3c91-cc6c418c28dc.png)
![無題3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/d4bfdd61-abfe-64d8-2115-b588f3332456.png)
コピーしたURLを貼り付けます。

以上で設定完了です。ダッシュボードに移行します。発行された API の URL をコピーして、ブラウザに貼り付けて、動作確認しましょう。
![無題4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/455b7c1e-c840-fa33-ae09-87561de5889e.png)
以下のようにエラーが出てきます。
![無題5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/0705d96e-6854-c06c-d6e8-738ed255507e.png)

####解決方法はコピーした API の URL の直後に /シート名 と追記することです。
以下のように表示されれば成功です。
![無題5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2034987/b3a09a5d-093e-ad5a-522f-623ab1c49d26.png)
上記のように記述することを、JavaScript Object Notation (JSON) 形式といいます。JSON 形式とは JavaScript のオブジェクのようにテキストデータを記述するルールです。
RansackでActionTextの中を検索する

# はじめに
Ransackは、複雑な検索機能でも簡単に作れるGemです。そして、ActionTextはRails6から導入されたリッチテキストを簡単に製作できる機能です。
RansackでActionTextの中を検索できないという記事がありましたが、どうやら検索できるようなので、記事を書こうと思いました。

# 注意
RansackやActionTextの導入は割愛させていただきます。
大事なところだけです。
また、恥ずかしながら私自身がRailsを勉強中なこともあり、完全に理解しれきていないと思いますので、他の記事でも調べて頂いた方がいいと思います。最大限頑張って説明させていただきます。

# 結論
Ransackで検索するためには、モデルにアソシエーションを追加する必要がありました。
結論としては、has_oneの行が必要ということです。
2つ同じ指定のように思えますが、片方が読み書き用で、片方が検索用と考えてもらうといいと思います。

```app/models/message.rb
class Message < ApplicationRecord
  has_rich_text :content
  has_one :content, class_name: 'ActionText::RichText', as: :record
end
```

```app/views/messages/index.html.erb
 <%= search_form_for @q, url: search_messages_path do |f| %>
    <%= f.label :title_or_content_body_cont, 'テキスト検索' %>
    <%= f.search_field :title_or_content_body_cont %>
```

# 説明
結論の例では、messageモデルに、「has_rich_text :content」でActionTextを紐付けています。
ここまではRailsガイドのとおりです。これがないとActionTextのデータが生成できません。

しかし、この「has_rich_text」に関しては、Ransack対応していないようです。
そのため、更にもう1行追加が必要です。

それが「has_one :content, class_name: 'ActionText::RichText', as: :record」です。
私は「class_name」と「as」は見慣れない記述だったので、Railsドキュメントを探したところ次のように記述がありました。

| オプション | 説明 |
| -- | -- |
| class_name | 関連名と参照元のクラス名を異なるものにしたい場合に指定 |
| as | ポリモーフィック関連を定義 |

class_nameで指定されたほうを見に行っているようです。
つまり、ActionText::RichTextクラスを参照しているようです。

asのポリモーフィック関連とは、テーブルのリレーションが直接接続されていないテーブルにアソシエーションを可能にすることができるようです。ただし、対象のレコードは「polymorphic: true」で生成されている必要があり、ActionText内のbelogns_toでも「polymorphic: true」の指定が必要になります。

確認してみると、ActionTextのマイグレーションファイルを見るとたしかにデフォルトで「polymorphic: true」が入っています。

```db/migrate/XXXXXXXXXXXXXXXX_create_action_text_tables.action_text.rb
class CreateActionTextTables < ActiveRecord::Migration[6.0]
  def change
    create_table :action_text_rich_texts do |t|
      t.string     :name, null: false
      t.text       :body, size: :long
      t.references :record, null: false, polymorphic: true, index: false

      t.timestamps

      t.index [ :record_type, :record_id, :name ], name: "index_action_text_rich_texts_uniqueness", unique: true
    end
  end
end
```

また、RailsのGithubを確認すると、ActionText::RichTextクラスにて、recordのbelogns_toで「polymorphic: true」が指定されていることが確認できます。
（おそらくこのファイル）

```rails-main/actiontext/app/models/action_text/rich_text.rb
module ActionText
  class RichText < Record
    self.table_name = "action_text_rich_texts"

    serialize :body, ActionText::Content
    delegate :to_s, :nil?, to: :body

    belongs_to :record, polymorphic: true, touch: true
```

これらにより、（今回の例では)messageから、直接つながっていないaction_text_tablesにアクセスできるようになり、そのbodyを検索することが可能になりました。

# 参考

[Go Rails / How do I search Action Text with Ransack?](https://gorails.com/forum/how-do-i-search-action-text-with-ransack)
[GitHub rails/rails](https://github.com/rails/rails)
[Railsガイド](https://railsguides.jp/action_text_overview.html)
[Railsドキュメント](https://railsdoc.com/page/has_one)
[Qiita / アソシエーションにおけるclass_nameの定義！](https://qiita.com/wacker8818/items/eccdf0a63616feb14a70)
[Qiita / Railsのポリモーフィック関連付けを理解する](https://qiita.com/tanutanu/items/726e7097c6ca4a9ca6e3)
入力バッファの読み捨て

```c:main.c
#include <stdio.h>

int main(void)
{
    char buf1[5] = {0}, buf2[5] = {0};
    printf("Input1: ");
    fgets(buf1, sizeof(buf1), stdin);
    // 読み捨て
    while (getchar() != '\n')
        ;
    printf("Input2: ");
    fgets(buf2, sizeof(buf2), stdin);
    // 読み捨て
    while (getchar() != '\n')
        ;
    puts(buf1);
    puts(buf2);
    return 0;
}
```

## 結果

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208928/d059257b-bbf8-fdf7-8a6a-e92c8cf2765d.png)
Gitのコマンド一覧(2) - branch, checkout, remote-

# 概要
[Gitのコマンド一覧(1)](https://qiita.com/sepakTama/items/8abfd998975512c2fea2)では、ローカルリポジトリを作成する`git init`から、ローカルリポジトリに反映された変更点をリモートリポジトリへ反映させる`git push`までを記した。本記事では、ブランチとは何かから説明をし、ブランチに関するコマンドをまとめ、それに付随するoriginやローカルやリモートについて説明する。(rebase等に関してはGitのコマンド一覧(3)を参照すること)

# 主に使っているコマンド
- init
- add
- commit
- push
- clone
- status
- log
- reflog
- **branch**
- **checkout**
- **remote**
- fetch
- merge
- pull
- rebase
- cherry-pick
- reset
- rm

# ブランチの考え方
Gitを扱う上で重要になってくるのがブランチという考え方である。例えば、多人数で一つのシステムを開発している際に、ブランチを切らないと

## clone
```Git
git clone [http]
```

リモートリポジトリをローカルに複製するために用いるコマンドである。（リモートとローカルの連携はどうなっているのだろうか）

## branch
筆者は2通りの使い方をしている。

- `git branch`
    - 現在いるブランチを確認することができる。 
- `git branch [branch名]`
    - 現在いるブランチから新たにブランチを立てることができる。（注：ブランチを生成するだけでチェックアウトはされていない）

## checkout
筆者は2通りのの使い方をしている。

- `git checkout [branch]`
    - [branch]へ切り替える時に用いる。（[branch]が存在しない場合、エラーを吐く）
- `git checkout -b [branch]`
    - `git branch [branchA]` -> `git checkout [branchA]`と同義。
    - ブランチを新しく生成し、生成したブランチへチェックアウトする。

## remote
私はリモートリポジトリを追加/登録する際や、リモートリポジトリを確認（リポジトリに格納されているファイルのことではない）する際に用いている。

- `git remote -v`
    - リモートリポジトリの情報を確認することができる。
![git_remote_v.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/05b24082-d4bb-4505-5875-cceba691b3f6.png)


# ブランチを切ってバージョンを管理しよう
開発したシステムを新しい機能を実装したり、発見したバグを修正する場合に、メインのブランチ（恐らくmasterブランチもしくはdevelopブランチ）を直接改修することは一般的にはしない。その理由として、実装したものがエラーを吐いたり、ライブラリの衝突などが発生した場合に、システムが動かなくなる可能性があるためである。ここら辺の考え方はA/Bテストと近いような気がしているが、実際に動かしているシステムとは別の（裏の）ところで改修を加え、実際に問題がなかったら本システムにその変更部分を反映させるのがいいだろう。

## 改修部分に応じてブランチを切ろう
上司から改修タスクAを任されたとしよう。現状リモートリポジトリとローカルリポジトリの状態は同じで下図のようになっているとする。

**ここに図を載せる**

この時、以下のようにステップを取ることによってタスクを管理することができる。

```Git
git branch
git checkout -b taskA
```

もしくは

```Git
git branch
git branch taskA
git checkout taskA
```

でも可能である。`git branch`はできれば毎回推奨したい。というのも、現状いるブランチを確認せずにチェックアウトするとたまに予想もしないブランチから枝（ブランチ）が生える可能性があるからだ。ブランチを切った後に気付いても修正可能だが、少し（筆者にとってはかなり）面倒なので、簡単なチェックでミスを減らせるのであるのだから、やらない手はないだろう。

グラフ等で現状を確認できるが上記のコマンドを入力するとmasterブランチの最新断面からtaskAのブランチが生成され、taskAのブランチにチェックアウトする。

![branch_step1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/e7302a29-d56c-d276-1f5d-721def24b80d.png)

ブランチを切った後は改修を加え、`add -> commit -> push`を行い、リモートリポジトリにローカルで行った改修を反映させよう。

![branch_step2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/543b1365-093f-7fe7-cfb8-70d421cfe670.png)

一通り改修が終わったら、元のブランチにマージ（合同開発者がいる場合はマージリクエストをお勧めする）する。

![branch_step3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/2cbb439c-41c7-9bc4-5be6-32a4decc9ddb.png)

# 次のブログでは
[次のブログ](https://qiita.com/sepakTama/items/74b8c28da4d300a62135)では、リモートリポジトリの情報を持ってくる　`fetch`等の考え方に関して説明する。

https://qiita.com/sepakTama/items/74b8c28da4d300a62135
OS/実行環境の情報を取得する【Node.js/C#/Python】

Node.js/C#/Pythonの3言語にてOS/実行環境の情報の取得方法を調べたのでここに備忘録を記す。

# Node.js
```js
"use strict";
const os=require("os");
 
(function(){
    // プラットフォームID
    console.log(process.platform);
    // OS名
    console.log(os.type());
    // OSバージョン
    console.log(os.release());
    // OSアーキテクチャ
    console.log(process.env.PROCESSOR_ARCHITEW6432||process.arch);
 
    // Node.jsバージョン
    console.log(process.version);
    // Node.jsのアーキテクチャ
    console.log(process.arch);
})();
```
[ideone](https://ideone.com/k2EVS4)

## 実行結果
```sh
# Windows10
win32
Windows_NT
10.0.19042
x64
v16.12.0
x64

# ideone
linux
Linux
4.19.0-16-amd64
x64
v11.12.0
x64
```

# C<span>#</span>
```csharp
using System;
using System.Runtime.InteropServices;
 
class Program{
    static void Main(){
        // プラットフォームID
        Console.WriteLine(Environment.OSVersion.Platform);
        // OSバージョン
        Console.WriteLine(Environment.OSVersion.Version);
        // OS名+バージョン
        Console.WriteLine(Environment.OSVersion.VersionString);
        // OSアーキテクチャ
        Console.WriteLine(RuntimeInformation.OSArchitecture);
 
        // .NETバージョン
        Console.WriteLine(RuntimeInformation.FrameworkDescription);
        // 実行プロセスのアーキテクチャ
        Console.WriteLine(RuntimeInformation.ProcessArchitecture);
    }
}
```
[ideone](https://ideone.com/Vr7dZB)

## 実行結果
```sh
# Windows10
Win32NT
10.0.19042.0
Microsoft Windows NT 10.0.19042.0
X64
.NET 5.0.11
X64

# ideone
Unix
4.19.0.14
Unix 4.19.0.14
X64
Mono 5.20.1.19 (tarball Thu Apr 11 09:02:17 UTC 2019)
X64
```

# Python
```py
import sys
from os import path
import platform
 
if __name__=="__main__":
    # プラットフォームID
    print(sys.platform)
    # OS名
    print(platform.system())
    # OSバージョン
    print(platform.release())
    print(platform.version())
    # OS名+バージョン
    print(platform.platform())
    # OSアーキテクチャ
    print(platform.machine())
 
    # 実行コマンド名
    print(path.splitext(path.basename(sys.executable))[0])
    # Pythonバージョン
    print(platform.python_version())
    # Pythonアーキテクチャ
    print(platform.architecture()[0])
    # Pythonバージョン(REPL起動時のアレ)
    print(str(sys.version))
```
[ideone](https://ideone.com/pTGTvF)

## 実行結果
```sh
# Windows10
win32
Windows
10
10.0.19042
Windows-10-10.0.19042-SP0
AMD64
pypy3
3.8.12
64bit
3.8.12 (279d80ac2079, Oct 17 2021, 05:26:02)
[PyPy 7.3.6 with MSC v.1929 64 bit (AMD64)]

# ideone
linux
Linux
4.19.0-16-amd64
#1 SMP Debian 4.19.181-1 (2021-03-19)
Linux-4.19.0-16-amd64-x86_64-with-Ubuntu-19.04-disco
x86_64
python3
3.7.3
64bit
3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0]
```

---
以上。
exec関数を使って、ファイル読込で取得したPythonコードの文字列を実行

:::note warn
<b>個人的なメモ</b>
別ファイルの処理を呼ぶ際は通常、import文を使いますが、
exec関数を使って、擬似的に値のやり取りをする方法を書きます。
:::

# やりたいこと
1.exec関数を使う前に別ファイルで使う変数を擬似的に宣言
2.exec関数を通して変数を授受し、グローバル変数の如く扱う
3.結果を表示

# ソースコード

## Pythonから読み込むテキストファイル

```text:input.txt
dictionary["key1"]="value1"
dictionary["key2"]="value2"
dictionary["key3"]="value3"
dictionary["key4"]="value4"
dictionary["key5"]="value5"
```

## Pythonソースコード
```python:execution.py
from os.path import dirname
with open(dirname(__file__) + '/input.txt') as f:
    variable_name = 'dictionary'
    # テキストファイル内の変数と同名のキーを持つ連想配列を作成
    result = {variable_name: {}}
    # exec関数の利用
    # 第1引数: テキストファイルの全文を指定
    # 第2引数: 作成した連想配列を指定
    # 第3引数: 指定した場合、第2引数よりも優先して処理される
    exec(f.read(), result)
    # テキストファイルに書かれたキーと値を持つ連想配列を表示
    # {'key1': 'value1', 'key2': 'value2', 'key3': 'value3', 'key4': 'value4', 'key5': 'value5'}
    print(result[variable_name])
    dictionary = result[variable_name]
    # 値を表示
    # value1
    print(dictionary['key1'])
```

# まとめ
exec関数を使うと別ファイルに書かれている処理を実行した後、
そのまま使い続けることができる。
Hadoop/Hive 面经总结

# 概要
看了同事分享的面经，自己做点总结
面经网址： https://www.iteblog.com/archives/10040.html

# Hadoop
## Hadoop写流程
1. Client上传请求。NameNode检查权限，文件重名
1. Client文件切分。默认block 128MB
1. 向NameNode获得DataNode地址
1. 和DataNode建立连接，进行传送
1. 一个block传送完毕后重复3，4过程

### Hadoop写失败处理
- DataNode写完会返回ack确认。如果没有ack确认，NameNode会复制DataNode，然后下线DataNode

## Hadoop读过程
1. Client向NameNode请求block位置
1. NameNode检查DataNode然后提供最近的活的DataNode地址
1. block的读取是并行的
1. 每次读完一个block，进行checksum验证。如果有问题会通知NameNode
1. 合并全部block

### Hadoop读失败处理
- Checksum验证。发现错误会通知NameNode，并且获取下一个block副本的DataNode地址

## NameNode启动
### 首次启动
1. 格式化文件系统，生成fsimage
1. 读取fsimage进内存，等待DataNode注册
1. 启动DataNode
1. 出现文件操作时生成edit

### 二次启动
1. 读取fsimage和edits文件
1. 合并成新的fsimage
1. 创建新的edit
1. 启动DataNode

## DataNode的添加
1. Install jdk和ssh
1. 修改Hosts，添加NameNode的IP
1. 修改Config文件
1. 启动DataNode进程
1. 设置Load Balance

## Secondary NameNode
- 用于合并NameNode的edit logs到fsimage

## Kerberos
![](https://static.packt-cdn.com/products/9781783558964/graphics/8964OT_06_01.jpg)

## 高可用结构
高可用结构里面不需要Secondary NameNode
log的更新由JournalNode担当
![](https://raw.githubusercontent.com/heibaiying/BigData-Notes/master/pictures/HDFS-HA-Architecture-Edureka.png)

## MapReduce
![Architecture](http://4.bp.blogspot.com/_j6mB7TMmJJY/SS0CEJLklnI/AAAAAAAAAGQ/ogPGJ3WYpt4/s400/P4.png)
大图： http://4.bp.blogspot.com/_j6mB7TMmJJY/SS0CEJLklnI/AAAAAAAAAGQ/ogPGJ3WYpt4/s1600-h/P4.png

### Map过程
1. split。切割split文件
1. spill（溢出写入） to disk
    1. sort
    1. combine。局部汇总，适用于求和类，不适用于求平均值
1. merge

### Reduce过程
1. copy
1. sort
1. reduce

### shuffle
- map的2，3和reduce的1，2
- 压缩机制。
    - 为了减少IO
    - snappy最常用

## Tez
### Tez vs MapReduce
![](https://www.wikitechy.com/tutorials/hive/img/hive-images/hive-on-mr-vs-hive-on-tez-.png)

## YARN
![Architecture](https://img2018.cnblogs.com/blog/1271254/201910/1271254-20191008160008965-1100694847.png)

- 管理节点
    - Resource Manager
- 工作节点
   -  NodeManager
    -  Application Master
    -  Container

### 三种调度模型
1. FIFO Scheduler
1. Capacity Scheduler
1. Fair Scheduler

### 怎么限制不同的用户的资源使用量
Queue: unfunded, schedule

# Hive

![Hive结构](https://i.stack.imgur.com/17mZy.png)


## Hive内部表和外部表
- 内部表由Hive管理，外部表（external）由HDFS管理
- 内部表删除。metadata+object
- 外部表删除。metadata only

## Hive支持索引吗
- Hive有索引。用于制定HDFS文件路径和偏移量
- Hive索引不支持主键或者外键

## 文件存储类型
ORC/Parquest
- 压缩
- 二进制文件

## Metastore
- 是个service
- 通过metastore获得metadata
- 多个client可以同时访问metastore并不需要知道保存metadata的mysql的用户名和密码

## 如何提高Hive效率
- Partition
    - 合理分割， 防止数据倾斜
- Map
    - 适当增大分割文件大小
    - Map前进行合并
    - 增加Map的数量
- Reduce
    - 提高每个reduce的数据处理量
    - 调整reduce数量（并不是越多越好，消耗资源）
- Query
    - 用group by代替distinct等

# Spark
![architecture](https://cdn.analyticsvidhya.com/wp-content/uploads/2020/11/spark_Architecture.png)
![with yarn](https://static.javatpoint.com/tutorial/spark/images/spark-architecture.png)
![row object](https://databricks.com/wp-content/uploads/2018/05/rdd.png)

# Kafka
![kafka architecture](https://miro.medium.com/max/590/1*lu6wtETiXfeG23fIUJi-hA.png)
サルにはわからない!!リモートリポジトリを利用するgit初期操作まとめ

<h3>
clone→add→commit→pushのやり方
</h3>
<h2>前提条件</h2>
gitインストール済み

<h2>環境</h2>
・Windows10
・端末：Windows PowerShell

<h2>clone~pushの具体的な操作</h2>
<h3>1.リモートリポジトリのclone</h3>

ブラウザでGithubにログインし、使いたいリポジトリを見つけたら利用したいリモートリポジトリのURLをコピーする。
そしてクローンしたいディレクトリに端末のコマンド`cd`等で移動して端末で以下のように記述。

```
git clone [URL]
```

デフォルトブランチではないブランチをクローンする場合は
```
git clone -b [cloneしたいブランチ名]　[URL]
```
と記述する。

<h3>2.コードの編集</h3>
端末で以下の記述でbranchを新たに作成する。

```
git branch [作成したいブランチ名]
```

そして以下の記述でブランチを移動する
```
git switch [移動したいブランチ名]
```

ここまで出来たらコードを編集します。
各自好きなようにやってください！

<h3>3.編集した部分を記録</h3>
編集した部分をローカルリポジトリに記録するためにaddを行う。以下の「.」を末尾につけることですべての編集を記録できる。

```
git add .
```
<br>

commitでaddを確定して変更内容を添える。<br>
```
git commit -m '記述したい内容'
```

<h3>4.リモートリポジトリへのpush</h3>
commit後pushを行う

```
git push origin [追加したいブランチ名]
```

または
```
git push origin [pushしたいローカルブランチ名]:[push先のリモートブランチ名]
```
とすると理解しやすいでしょう。

[追加したいブランチ名]で指定したブランチとしてリモートリポジトリに保存される。

:::note alert
なおチーム開発時はmainブランチにpushすることは避けましょう。
:::
pgsql 関数の定義とNULL入力の挙動

#### 新しい関数を定義する
CREATE FUNCTION関数を使用する。

- 2つの値を引数にとり足し算を返す関数を定義した。 
- `RETURNS NULL ON NULL INPUT`に関係なく1つでもnullを受け取れば仕様上nullを返す。

```
DROP FUNCTION IF EXISTS add_sample_1107(integer, integer);
CREATE FUNCTION add_sample_1107(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT
    ;

select * from add_sample_1107(1,200);
select * from add_sample_1107(null,1000);
select * from add_sample_1107(null,null);
```

- `RETURNS NULL ON NULL INPUT`をうまく使ってみる。次の関数は2つの引数をとるが、演算をしないので返り値の方がIS NOT NULLならば通常値が返ってくる。
- 以下のように2つ目の引数はNULLでもちゃんと1が返ってくる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1347377/13fd8ff8-73a5-b2b1-89ee-65e69d61331c.png)

- ここで`RETURNS NULL ON NULL INPUT`オプションを設定すると以下のように一番最後の一引数がNULLになってしまうとNULLでない返り値がNULLになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1347377/5616bcd5-e8ba-ca61-46b9-b9e0fbfd2e76.png)
iOSのショートカットアプリでLast-Modified レスポンスヘッダーを参照してWEBページの更新を確認する

目的
--
クリプタクトというサイトのお知らせページの更新状況を確認する

https://grid.cryptact.com/announcements


条件
--

* RSS非対応でも使用できること
* お知らせページの更新があるときだけブラウザを立ち上げて確認する

作成したもの
--
ショートカットアプリ：WEBページ更新確認

https://www.icloud.com/shortcuts/de0c29a3ecaa4f019e382ae7fb8196d7

上記お知らせページからLast-Modified レスポンスヘッダーを取得して更新があればアナウンスページを表示します。
使用したiOSバージョンはiOS15.1になります。

ショートカットのアクションについて
--
ショートカットの詳細はユーザーガイドを参照してください。

https://support.apple.com/ja-jp/guide/shortcuts/welcome/ios


**使用するアクション**

* URLからヘッダを取得(JSON)
* 取得したヘッダ（キー：値）から任意の文字列をキーに指定し、キーに対する値を取得
* 変数の保存、取得
* クリップボード保存、取得
* if文
* アラート
* ショートカットの実行停止

処理の流れ
--
1. 事前にLast-Modifiedヘッダを保存したファイルからLast-Modifiedヘッダを取得して変数（oldLastMod)に保存
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/b859d8fd-a521-518b-25e5-ab2d0459dd19.png" width="320px">

2. アナウンスページより新規に取得したLast-Modifiedヘッダをクリップボードに保存
※ Last-Modified レスポンスヘッダーが未設定の場合は未設定であることを表示してショートカットの実行を停止します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/42198609-6802-bbb3-8f92-4416192f7c9d.png" width="320px">

3. クリップボードの値と変数（oldLastMod)が等しいか確認
※ショートカットのif文は複数の変数を指定できないためクリップボードに保存した値と比較します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/0af24801-5c96-02d3-f898-d5dd1be53b7d.png" width="320px">

4. 等しい場合、更新なしを表示してショートカットの実行を停止
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/184ea384-0164-1a84-1f3f-f7b0a9615dcc.png" width="320px">

5. その他(等しくない)の場合、更新ありを表示してLast-Modifiedヘッダを保存したファイルを上書きし、アナウンスページを表示してショートカットの実行を停止
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/74f4b627-96a6-ff18-7f99-119d2fdfcc89.png" width="320px"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/defccaae-9551-15a8-c7f4-a457b524de95.png" width="320px">
 ブラウザ起動とLast-Modifiedヘッダの更新結果
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/c23fa2ef-4c25-c046-1dc3-eedc5d808246.png" width="320px"> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1832768/04c5817e-1b6d-0b3a-56ec-0f002f69642f.png" width="320px">



まとめ
--
WEBページの更新を確認するためショートカットアプリを使用して作成しました。
個人的には更新内容までチェックできると良かったですが、条件が細かくなり変更もしづらいのでiPhone上で作成するのは現実的ではないと思いました。(それならコードを書いた方が良いです。) 
ただ、iPhoneで確認できるのは楽で良いので、PCで作成したコードを基に実行できるようになると良いなと思います。

### 補足
* ショートカットアプリはiOS12でdiffbotというスクレイピングツールが使用できたようですが、iOS15ではサポートされていないようです。
* 以前、Pythonistaが使えましたが、こちらも非サポートのようです。

参考
--
iOSのショートカットでブログの更新を監視してみた
https://qiita.com/shayao3/items/eca39afbb38df7e95784

iPhoneでスクレイピング（ショートカットアプリ）
https://qiita.com/sentencebird/items/e537ad94bdd0f697f432
【discord.js】～ なぜ私はdiscord.jsを選ぶのか ～

## 前書き
最近というか前々から私はdiscord.jsでの開発をメインにしています。
旧垢含め、時々discord.pyの開発を多めにやっていたこともありましたが、やっぱりdiscord.jsの開発に戻ってきます。
なぜ私がdiscord.pyやdiscord4J、discordrbではなく、discord.jsを選んだのかをご紹介したいと思います。

## 初心者でも比較的簡単に開発できる
1番目にこれかなと思っています。
私のようなプログラミングをし始めた初心者でも比較的簡単に開発ができると思っています。情報が多いので、初心者でも調べれば分かるなんてこともあります。
意味は分かってなくてもYoutube見てなんとなくコードを書いてみたら動く、なんてこともあります。（一応意味が分かっていた方が良いとは思います）

## 情報が多い
先程と繋がりますが、情報がめちゃくちゃ多いです。
Discord.js Japan User Groupというものもあります。

https://scrapbox.io/discordjs-japan/

試しにGoogle先生に「discord.js 作成方法」と「discord.rb 作成方法」と調べてみましょう。
まず「discord.js 作成方法」と調べてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940116/e2d34af8-dc89-7c86-c686-df592ed01e30.png)
検索結果が199万件出たと書かれています。
「discord.rb 作成方法」ではどうでしょうか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1940116/71e74a81-b496-c3d8-8007-2060667b4aca.png)
36万9000件出たと書かれてます。

まぁこれだけでは完全には判別できませんが、他のより多そうというのはわかったと思います。
情報が多いため、開発しやすいことが多いです。
ほかの言語では日本語の記事が一切ない。なんてことも多いですが、discord.jsではまじでめちゃくちゃあります。まじで

## 開発環境が整えやすい
discord.js を開発するためには大体、JavaScriptというプログラミング言語とNode.jsというサーバサイドで動くJavaScriptが必要となります。
JavaScriptはwindowsで標準で入っている事が多く、Node.jsもネットからダウンロード・インストール等すればすぐに動くことが多いです。
そのため、開発環境を整えやすく、割と早めに開発することが可能です。

## ほかの言語はどうなのか
Githubで調べてみて出てきたやつの中で気になったものなどを集めて、そのレポジトリのリンクを貼りました。

* **discord.py** (Python)

https://github.com/Rapptz/discord.py

* *Discord4J** (Java)  

https://github.com/Discord4J/Discord4J

* **discordgo** (Go)

https://github.com/bwmarrin/discordgo

* **discordrb** (Ruby)

https://github.com/discordrb/discordrb

* **Discord.Net** (.NET) 

https://github.com/discord-net/Discord.Net

* **DiscordPHP** (PHP)

https://github.com/discord-php/DiscordPHP

## まとめ
まだdiscord.jsのすべてを理解しているわけではないので、ほかにいいところや課題点等ありましたら是非教えてくださると嬉しいです。

* **初心者でも比較的簡単に開発できる**
* **情報が多い（特に日本語など）**
* **開発環境が整えやすい**
SOQL高速化メモ

最近、仕事で何百万ものオブジェクトのデータを取得するという問題に遭遇しました。そのため、SOQLを高速化する方法について調査を行いました。

####参考
https://qiita.com/TaaaZyyy/items/be0c95bedfcbb4f05e77

一般的に、SOQLを高速化する方法は以下いくつかあります。

1.クエリをセレクティブにする
2.不要な項目を取得しない
3.数式項目を取得しない
4.リレーションクエリを使用しない
5.WHERE句に数式項目を使用しない
6.検索結果の件数が少なくなるように条件を設定する

その中に、わかりにくいのは「クエリをセレクティブにする」ことだと思います。

##クエリをセレクティブにする
テーブルの完成スキャンではなく、インデックスを使用するようにする（セレクティブなクエリ）と検索が高速になる。
大規模データベースで極めて効果的。	

####標準インデックス付き項目およびカスタムインデックス付き項目										
クエリオプティマイザでは、各インデックスのデータ分配に関する統計情報を含むテーブルが保持されます。
このテーブルを使用して、インデックスを使用することでクエリの処理時間を短縮できるかどうかを判断するために、事前クエリを実行します。
たとえば、取引先オブジェクトに Account_Type という項目があるとします。この項目は、Large、Medium、または Small の値をとることができ、項目にはカスタムインデックスが付けられています。									

Salesforce で次のようなクエリが生成されたとします。
SELECT * FROM Account WHERE Account_Type__c = 'Large'
										
クエリオプティマイザでは、Account_Type 項目の値が Large であるレコードの数を判断するために、内部統計情報テーブルに対して事前クエリが実行されます。この数がオブジェクトの合計レコード数の <strong>10%</strong> または <strong>333,333</strong> レコードを超える場合、クエリではカスタムインデックスが使用されません。

#####標準インデックス付き項目
条件の一致率が、最初の 100 万件のレコードで <strong>30% 未満</strong>、その他のレコードで <strong>15%</strong> 未満である場合に使用されます (最大で 100 万件)。
たとえば、標準インデックスは次の場合に使用されます。
200 万件のレコードを含むテーブルに対してクエリが実行され、検索条件が 450,000 件以下のレコードに一致する。
500 万件のレコードを含むテーブルに対してクエリが実行され、検索条件が 900,000 件以下のレコードに一致する。

#####カスタムインデックス付き項目
検索条件が合計レコード数の <strong>10% 未満 (最大で 333,333 件)</strong> に一致する場合に使用されます。
たとえば、カスタムインデックスは次の場合に使用されます。
500,000 件のレコードを含むテーブルに対してクエリが実行され、検索条件が 50,000 件以下のレコードに一致する。
500 万件のレコードを含むテーブルに対してクエリが実行され、検索条件が 333,333 件以下のレコードに一致する。

ただし、次の場合、セレクティブなフィルタにはならない。
1.検索条件の演算子が、!=、NOT CONTAINS、NOT STARTS WITHなどの否定演算子。
2.検索条件に CONTAINS 演算子が使用され、スキャンされる行数が 333,000 を超える。
3.空の値またはNULLと比較している(NULLを含まないインデックスの場合)。
4.INで比較されるコレクションに空の値が含まれるとセレクティブにならない。
5.中間一致、後方一致

#####セレクティブなクエリ	
次の条件を満たすとき、セレクティブなクエリ隣、実行速度が高速かされる
1.シンプルフィルタ：いずれかのフィルタがセレクティブであること 
※ANDで接続された条件句のこと
2.複合フィルタ：すべてのフィルタがセレクティブであること 
※ORで接続された条件句のこと

##インデックス
#####標準インデックス														
・ID
・Name
・RecordTypeId
・Division
・Email(取引先責任者とリード)
・CreatedDate
・SystemModStamp
・LastModifiedDate (LastModifiedDate < DateField となるときはインデックスは使用されない)
															
#####カスタムインデックス														
・参照関係項目
・主従関係項目
・外部IDが設定された項目
・ユニークが設定された項目
・カスタムインデックスが付与された項目
:::note 
infoSalesforceに依頼が必要
:::

##測定方法							
クエリプランツールを使用する。

#####使用方法
1. 開発者コンソールで、Help > Preferences で表示される設定画面の Enable Query Plan をtrue に変更。
2. Query Editor にクエリを記入。
3. Query Plan ボタンをクリック。
tensorboardでとりあえずグラフだけ可視化したいときのwith構文

tensorflowの計算グラフを描画するwith構文です。このスコープ内のtf.functionなどをtensorboardに描画します。

(tensorflowの解説についての記事を書いている途中なのですが、先にこれだけ載せておきます)

```python
import contextlib
import datetime

import tensorflow as tf

@contextlib.contextmanager
def draw_graph(name: str = 'myfunc'):
    """
    計算グラフをtensorboardに描画する。
    ログは`./tf_function/(name)_YYmmdd-HHMMSS`のフォルダに格納される。
    args:
        name: str(optional)
    """
    stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    logdir = f'tf_function/{name}_{stamp}'
    writer = tf.summary.create_file_writer(logdir)
    try:
        tf.summary.trace_on()
        yield
    finally:
        with writer.as_default():
            tf.summary.trace_export(name=name, step=0, profiler_outdir=logdir)
            # `tf.summary.trace_off()`は不要(上のtrace_exportで最後にされる)
        writer.close()
        print(f'Saved file to {logdir}.')
```

# Example
```python
with draw_graph('add_and_power_3'):
    @tf.function
    def my_function(x, y):
        """
        f(x, y) = (x + y) ^ 3
        """
        with tf.name_scope('op_add_and_power_3'):
            xpy = x + y
            ret = xpy ** 3
            return ret

    x = tf.constant(1.0)
    y = tf.constant(2.0)
    z = my_function(x, y)
    print('result:',z)
```

結果:

```text
result: tf.Tensor(27.0, shape=(), dtype=float32)
Saved file to tf_function/add_and_power_3_20211107-191032.
```

![スクリーンショット 2021-11-07 191213.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/783413/ad090fb6-ec11-3a4a-963e-e8f6f297675d.png)
ESP32のBLE機能を使って、Zwiftアプリと連携する

# 概要紹介
オンラインサクリングアプリのZwiftでは自転車のペダルを漕ぐ力出力：パワーの情報をもらって自転車のアバターが走行する仕組みとなっております。(ケイデンスなどでも可)
今回、通信環境の構築を一度したかったので試しにESP32のBLE通信を使って擬似的なパワー情報を生成してZwift内のアバターを動かしてみました。

# 環境
・Zwiftアプリ：iPad
・ESP32

#github
https://github.com/shohei0990/powercycle_10

# 参考元
https://blog-arduino-cc.translate.goog/2020/10/08/upgrading-a-cheap-exercise-bike-with-a-nano-33-ble-sense/?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=nui,op

https://github-com.translate.goog/Tschucker/ArduinoBLE-Cycle-Power-Service?_x_tr_sl=en&_x_tr_tl=ja&_x_tr_hl=ja&_x_tr_pto=nui,op

#BLE通信内容
参考元でほぼソフトはできていたので、自分のESP32のBLE通信の内容に合わせて更新したものとなります。まずは参考元をソフトを確認。

```php
BLEService CyclePowerService("1818");
BLECharacteristic CyclePowerFeature("2A65", BLERead, 4);
BLECharacteristic CyclePowerMeasurement("2A63", BLERead | BLENotify, 8);
BLECharacteristic CyclePowerSensorLocation("2A5D", BLERead, 1);
```
ServiceID：1818, CharacteristicID：2A63 (Notifyが連続読込)となります。

```php
short power;
unsigned short revolutions = 0;
unsigned short timestamp = 0;
unsigned short flags = 0x20;
byte sensorlocation = 0x0D;
```

```php
bleBuffer[0] = flags & 0xff;
bleBuffer[1] = (flags >> 8) & 0xff;
bleBuffer[2] = power & 0xff;
bleBuffer[3] = (power >> 8) & 0xff;
bleBuffer[4] = revolutions & 0xff;
bleBuffer[5] = (revolutions >> 8) & 0xff;
bleBuffer[6] = timestamp & 0xff;
bleBuffer[7] = (timestamp >> 8) & 0xff;
```
powerを送信している部分が配列の[2]と[3]となっているので、適当にpowerに値を入れてBLUの送信変数の[2]と[3]に入れれば良さそうです。他の部分はちょっとよくわからないので、そのまま同じものを入れておきます。

今回使うソフトへの反映は下記。それぞれのUUIDに余分なものがついていますが初期の8桁の下4桁しか使用しないので、そこだけ先ほどのUUIDの値にします。

```php
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

#define SERVICE_UUID        "00001818-0000-1000-8000-00805F9B34FB" 
#define CHARACTERISTIC_UUID "00002A63-0000-1000-8000-00805F9B34FB" 
```
powerの値をloop周期に合わせて加算します。

```php
        adj_time += interval;

        if(adj_time > 200){     
            power_add  = 0;
            adj_time   = 0;
        }else{
            power_add += 1;
        }

          power = 100 + power_add;
        
        buff[0] = flags & 0xff;
        buff[1] = (flags >> 8) & 0xff;
        buff[2] = power & 0xff;
        buff[3] = (power >> 8) & 0xff;
        buff[4] = revolutions & 0xff;
        buff[5] = (revolutions >> 8) & 0xff;
        buff[6] = timestamp & 0xff;
        buff[7] = (timestamp >> 8) & 0xff;
```

あとはZwiftを起動して、パワーソースを選択します。”ESP32”の項目があるかと思いますので選択します。Zwiftの動作画面でPowerの値が変化していればOKです。

![IMG_01B6767DAE7F-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820416/bc80b24d-7a7a-b7f3-a7aa-b1d201ded446.jpeg)


![IMG_0449.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1820416/935db2a3-5a40-4b07-3c3c-c476a2eb2b29.jpeg)

Zwiftにpower値を送れるようになったので、今後サイクリング用のデバイスを自作して連携できるようにしていきたいと思います。
【Gitの基本】リモートとローカルって何？

# 概要
本ブログではGitの本質的な部分（おそらくかなり重要な気がする）であるリモートとローカルの考え方について説明する。リモートとローカルが何かを理解することによってこの後につながっていく、ブランチであったりの理解が捗ると思われる。また、具体的なコマンドを知りたい人は[次のブログ](https://qiita.com/sepakTama/items/8abfd998975512c2fea2)を参照すること。
本ページでは以下について説明する。
- Gitとは
- リモートとローカル
- originって結局何？

# 1.Gitとは
Gitとは分散型バージョン管理システムのことであり、多人数で一つの開発や分析をする際に非常に便利である。何が特に便利かというと
- 過去に行った編集を参照したり、過去のバージョンに戻れる
- 多人数で同一ファイルを操作できる
が挙げられる。
では具体的に見ていこう。(具体的なコマンドを見たい人は[ここ](https://qiita.com/sepakTama/items/8abfd998975512c2fea2)までスキップ)

# 2. リモートとローカル
## 2-1. リモート
上で挙げたように、一つの開発を大多数で行うのがメリットの一つである。と、言うことは全員で共有できるフォルダ（Gitでは**リポジトリ**と呼ぶ)があるのは想像できるだろう。この共有できるリポジトリを**リモートリポジトリ**という。

## 2-2. ローカル
では、全員がリモートリポジトリを同時に操作できるのかというと、それはとても危険である。同時に編集を行うと編集のログを残せなくなる（だろう）し、同じファイルを編集することで編集の衝突が起こる場合もある。
この解決策として、各々のユーザがリモートリポジトリの内容と同じものを自身のPC（**ローカル**)上で編集を行い、その編集内容をリモートリポジトリに反映させるのがよい。このリモートリポジトリとに対応するローカル上のリポジトリを**ローカルリポジトリ**と呼ぶ。

![local_remote.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/4978702c-9f67-4615-616a-f266789f0d09.png)

# 3. gitを使ってみよう
リモートリポジトリとローカルリポジトリの作成について概要を説明する。（READMEの追加まで）

## 3-1. アカウント作成
まあ、まずは何といってもgitのアカウントが必要であるため、gitを扱える、**GitHub**か**GitLab**でアカウントを作成しよう。

## 3-2. リモートリポジトリ作成
アカウント作成が完了したら次にリモートリポジトリの作成をしよう。GUIでの操作なので細かいことは[他のブログ](https://qiita.com/shi_design_/items/5a77e259ea2feeeb48c3)や[公式サイト](https://docs.github.com/ja/get-started/quickstart/create-a-repo)を確認したい。
注意するべきことは、リモートリポジトリは作成したが、ローカルリポジトリを設定していないため、この段階では、ただリモートリポジトリがあるだけである。

## 3-3. ローカルリポジトリの設定
リモートリポジトリを作成したため、次はローカルリポジトリの設定である。（実は超簡単）
ターミナルを開き、ローカルリポジトリに設定したい（ログを残したい）ライブラリまで移動し、

```Git
git init
```

を入力するだけである。ローカルリポジトリとして設定できたか確認するために

```
ls -a
```

を実行し、.gitという名の隠しフォルダがあることを確認しよう。この.gitファイルに1で説明したメリットの一つ「すべてのログが残る」が格納されている。

## 3-4. ローカルリポジトリとリモートリポジトリの接続
3-3でローカルリポジトリを設定したが、現状では、ローカルリポジトリがどのリモートリポジトリと対応しているかを設定していない。そのため、以下のコマンドを3-3で行ったパスと同じところで行う。

```
git remote add origin <リモートリポジトリのHTTPの部分>
```

ここで、<リモートリポジトリのHTTP部分>とは3-2でリモートリポジトリを作成した際に、そのリモートリポジトリのURL（注意:webサイトのURLとは別）を確認することができ、それをコピーしたもの。

![github_http.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2207583/43541d0b-05c3-f634-1d32-3d2ae4ba5c84.png)


## 3-5. add > commit > push
詳しくは[次のブログ](https://qiita.com/sepakTama/items/8abfd998975512c2fea2)で。

https://qiita.com/sepakTama/items/8abfd998975512c2fea2
kotlinで画面のタップイベントを実行する方法


kotlin学習中メモ

## onTouchEventを設定

```MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    // 画面タッチ処理はoverride fun onTouchEventの中に書く
    override fun onTouchEvent(event: MotionEvent?): Boolean {
    	// ここに処理を追記していく
        return super.onTouchEvent(event)
    }
}
```

画面をタップした時になんらかの処理をしたい場合は、onTouchEventを設定してその中に実行したい処理を書いていけばOK

試しに３つの画像を用意して、タップ、移動、指を離した時の３パターンで表示される画像を切り替えてみます。

## 画像の配置

app/src/main/res/drawableに３つの画像を配置。
![スクリーンショット 2021-11-07 18.42.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174952/53ed81c0-6eed-ced4-b974-d9a5b87ca8c2.png "スクリーンショット 2021-11-07 18.42.39.png")


## レイアウトに初期画像を表示
app/src/main/red/layout/activity_main.xml
ImageViewを使ってdrawableに配置した画像１つを表示
私の場合はcat01.pngを設定

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" app:srcCompat="@drawable/cat01" android:id="@+id/iv"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
```

## タップイベントを実行
・MotionEvent.ACTION_DOWN　タップされた時のアクション
・MotionEvent.ACTION_MOVE　タップされた状態でスワイプしたときのアクション
・MotionEvent.ACTION_UP　タップ状態から指がはなれた時のアクション

```MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    // 画面タッチ
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        // view取得
        var iv :ImageView = findViewById(R.id.iv)

        // event?で null許容
        // event.actionが呼び出されたら発火
        when(event?.action) {
            MotionEvent.ACTION_DOWN -> iv.setImageResource(R.drawable.cat02) // タップ
            MotionEvent.ACTION_MOVE -> iv.setImageResource(R.drawable.cat03) // 移動
            MotionEvent.ACTION_UP -> iv.setImageResource(R.drawable.cat01) // 移動
        }

        return super.onTouchEvent(event)
    }


}
```

これでタップした時には、cat02の画像で、スワイプしたらcat03の画像、指を話したらcat01の画像が表示されるようになった。