OmniAICreator/Qiita-1.07M huggingface dataset:
Powershell＋Selenium Chromeの環境構築について

#初めに
弊社では、Powershell＋Internet ExplorerのWEBスクレイピングによる自動化を行ってきましたが、マイクロソフト社から2022年6月15日以降Internet Explorerのサポート打ち切りが発表されました。
代替手段として、powershell + selenium chromeで環境構築を行った際の覚書記事になります。（2021/11/11現在）

powershell + selenium edgeでの環境構築も検討しましたが、現webdriverのバージョンではremote-debugging-portを使ってのブラウザ再操作ができなかったため（EdgeのChromeドライババージョンのエラーがでる）最終的にChromeでの対応となりました。

#環境
OS ：WINDOWS10　21H1

powershell : バージョン:  5.1.19041.1320（OS標準）

chrome : バージョン: 95.0.4638.69(記事作成時の最新)　
※CHROMEブラウザの「ヘルプ」→「Google Chromeについて」で確認できます

#POWERSHELLでのselenium Chrome環境構築について（概要）
①SeleniumのWebDriverをダウンロードする
②ダウンロードされたファイル（nupkgファイル）から、DLLファイルを獲得する。
③Chrome Driverをダウンロードする
④POWERSHELLスクリプトにDLしたファイルを組み込む。

#POWERSHELLでのselenium Chrome環境構築について（詳細）
##①SeleniumのWebDriverをダウンロードする

###selenium.devにアクセスし、C#のdriverをダウンロードする
https://www.selenium.dev/downloads/

画面を少しスクロールしたところにある、Selenium Clients and WebDriver Language BindingsのC#版が対象。クリックするとファイルがダウンロードされます。そのままでは使えないので②の工程を実施してください。

![2021-11-11_15h46_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300537/e7effb17-78fc-fa65-e87c-5a7fd951a6f6.png)


##②ダウンロードされたファイル（nupkgファイル）から、DLLファイルを獲得する。

###該当ファイルを右クリック→プロパティを開き、セキュリティの許可するをチェック
セキュリティ設定がOFFのままだと、処理に影響があるみたいです。

![2021-11-11_15h58_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300537/e38e2664-a1a3-bd8e-60ff-f91fb18b2bea.png)

###該当ファイルの拡張子をzipに変更の上、解凍しDLLファイルを獲得する。

ファイルをselenium.webdriver.4.0.1.nupkgからselenium.webdriver.4.0.1.zipにリネームしてください。
リネームしたZIPファイルを解凍してください。
powershellのVersionが5.*.*の場合は、net48フォルダの中にあるDLLファイルが該当のファイルになります。
※powershellのVersionが6.*.*の場合はnetstandardのDLLを利用するようです。
![2021-11-11_16h04_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300537/d586245d-11eb-48f4-736c-dec8ec0843f1.png)

## ➂Chrome Driverをダウンロードする
ダウンロードサイトから、現在利用中のCHROMEのバージョンに応じたドライバーをダウンロードする。
![2021-11-11_16h25_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300537/4479520d-b03f-8359-d05a-206a26dcb7b4.png)


![2021-11-11_16h52_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300537/314a7f15-0be3-076b-2c97-bf4e869d1b47.png)

https://chromedriver.chromium.org/downloads


##④POWERSHELLスクリプトにDLしたファイルを組み込む。

参考に、”開きたいURL”に格納されたURLのHTMLソースをクリップボードに格納するスクリプトです。

~~~ powershell:test.ps1

$webDriverDllPath = "seleniumドライバの格納場所"
$chromeDriverDirPath = "CHROMEドライバの格納場所"

#dll読み込み
Add-Type -Path $webDriverDllPath

#ドライバにオプションを読み込み用
$Options = New-Object OpenQA.Selenium.chrome.ChromeOptions

#DebuggerAddressを指定したCHROMEを操作する場合
#$Options.DebuggerAddress = "127.0.0.1:9222"
#

#chrome起動
$chromeDriver = New-Object OpenQA.Selenium.Chrome.ChromeDriver($chromeDriverDirPath,$Options)

#日本語化け対応 文字コードをSHIFT-JISに
#chcp 932

#URL開く
$chromeDriver.Url = "開きたいURL"

#表示中のページソースを$htmlに格納
$html=$chromeDriver.PageSource

#htmlの内容をクリップボードにコピー
$html|CLIP

exit

~~~

#最後に
旧処理から、ほとんど関数関係の互換がなく、ほとんど書き換えになってしまいましたが
動作はかなり安定するようになりました。
Seleniumの機能は便利なものが多いようなので、活用していければと思います。
論文メモ：Mutual Learning to Adapt for Joint Human Parsing and Pose Estimation

# 論文
**論文名**
*Mutual Learning to Adapt for Joint Human Parsing and Pose Estimation*

**著者**
*Xuecheng Nie, Jiashi Feng, and Shuicheng Yan.*

**学会**
*ECCV2018*

https://openaccess.thecvf.com/content_ECCV_2018/html/Xuecheng_Nie_Mutual_Learning_to_ECCV_2018_paper.html


# 概要
　Human ParsingとPose Estimationを同時に行う、かつ相互に利益を適応させることによってお互いの性能を向上させることができるMutual Learning to Adapt(MuLA)。畳み込みニューラルネットワークで実装された本手法はエンドツーエンドで学習可能であり、複数のデータセットで優れた性能を示す。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2246925/0a85d0d6-2731-28ec-d7c1-ea4225ac7d19.png', width=500>

>Xuecheng Nie, Jiashi Feng, and Shuicheng Yan. Mutual learning to adapt for joint human parsing and pose estimation. In ECCV, pages 519–534, 2018.

Human Parsing：人物のパーツごとにセグメンテーションをするタスク
# 貢献
- HumanParsingとPoseEstimationを共同かつエンドツーエンドで学習可能なモデル提案
- 2つのタスクの利益の相互適応モジュール
- 複数のデータセットでSOTA

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2246925/6e19e2b1-78d7-fe05-5250-68fdffb2785c.png', width=500>
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2246925/d35a8289-98d7-696b-10f3-9d436d0f728a.png', width=500>

>Xuecheng Nie, Jiashi Feng, and Shuicheng Yan. Mutual learning to adapt for joint human parsing and pose estimation. In ECCV, pages 519–534, 2018.

# 評価
- LIPデータセットにおいて最先端
- PASCAL-Person-Partデータセットにおいて最先端

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2246925/06df5c92-afda-a6d5-f221-cfaf07bc6b3a.png', width=500>
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2246925/b162a58a-2413-e5a8-4055-1afac8bd0c43.png', width=500>
>Xuecheng Nie, Jiashi Feng, and Shuicheng Yan. Mutual learning to adapt for joint human parsing and pose estimation. In ECCV, pages 519–534, 2018.

LIP：Look Into Person。人物のパーツラベル19個、キーポイント16個。5万枚の画像。
メモ〜orderingの使い方

今回のお題
-
今回はdjangoアプリの`ordering`という属性値について取り上げます。

orderingとは
-
そのモデルのインスタンスを並べる際の順番を決めるための属性値。

`order_by`メソッドと同じく、属性値と`+-`の組み合わせで並び順を指定する。

具体例
-
`ordering`は`class Meta`の中で指定します。

```python
class CustomUser(AbstrctUser):
  shop = models.ForeignKey("shop.Shop", on_delete=models.CASCASE)
  user_type = models.IntegerField(
    choices = [
      (1, "スタッフ"),
      (2, "責任者"),
      (3, "本社管理職"),
    ]
  )

  class Meta:
    ordering = ["shop", "-user_type"]
```
上記の例であれば、まず`shop`という属性値が昇順になるようにグループを作った後にその中で`user_type`が降順になるように並べます。

また、並び替えの基準にする**属性値が一つの場合であっても必ずリスト形式にする必要がある**ので注意してください。

完成図
-
[![Image from Gyazo](https://i.gyazo.com/13c71b325153602599a7a0ce37e73fc9.png)](https://gyazo.com/13c71b325153602599a7a0ce37e73fc9)
Ubuntu 20.04.1にしました

Ubuntu 20.04.1
を入れ直したので、いろんなソフトを入れなおし

#SAC
パスを通したが
``$ sac``

sac: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
とエラー

``$ sudo apt-get install libncurses5 libncurses5:i386``

とインストールしたあと
``$ sac``
SEISMIC ANALYSIS CODE [11/11/2013 (Version 101.6a)]
 Copyright 1995 Regents of the University of California

SAC> 


使えました

#win32tool

``/usr/local``に``w32tools``のディレクトリごと追加
.bash_profileに以下のパスを追加
``#export PATH=/usr/local/win32tools/bin:$PATH                                                                                                                                  
export PATH=/usr/local/win32tools/w32tow1.src:$PATH
export PATH=/usr/local/win32tools/win2sac.src:$PATH
export PATH=/usr/local/win32tools/catwin32.src:$PATH``
Rubyで小数点以下の扱い方。

# はじめに
Rubyで小数点以下の記述を扱うときにこの記事を見てください。

# 小数点以下の入力を受け取る。
こちらのメソッドは Integer クラスではなく、 Float クラスになります。

```
入力
1.5
```

```Ruby
input = gets.to_f #1.5
```

# 切り捨て
小数点の切り捨てを行う場合は、Float クラスの floor メソッドを使用します。

```Ruby
1.4.floor # 1
1.5.floor # 1
-1.4.floor # -2
-1.5.floor # -2
```

### 小数点以下 N 桁の切り捨て
Float クラスの floor メソッドでは、小数点以下の桁数は指定できません。そのため、数値を一度 BigDecimal に変換したのち、floor メソッドを指定します。
floor メソッドの引数には小数点以下の桁数を指定します。

BigDecimal のオブジェクトを生成するときは、引数にStringクラスを指定しなければなりません。
to_s メソッドで Float を String に変換しています。最後に to_f で Float に戻しています。


```Ruby
require 'bigdecimal'
 
BigDecimal(1.23456.to_s).floor(2).to_f # 1.24
BigDecimal(1.23456.to_s).floor(3).to_f # 1.234

```

# 切り上げ
小数点の切り上げを行う場合は、Floatクラスのceilメソッドを使用します。

```Ruby
1.4.ceil  # 2
1.5.ceil  # 2
-1.4.ceil # -1
-1.5.ceil # -1
```

### 小数点以下 N 桁の切り上げ


```Ruby
require 'bigdecimal'
 
BigDecimal((1.23456).to_s).ceil(2).to_f # 1.24
BigDecimal((1.23456).to_s).ceil(3).to_f # 1.235
```

# 四捨五入

数値を四捨五入する場合は、 roundメソッドを使用します。

```Ruby
1.4.round # 1
1.5.round # 2
-1.4.round # -1
-1.5.round # -2
```

### 小数点以下 N 桁の四捨五入

```Ruby
1.23456.round(2) # 1.23
1.23456.round(3) # 1.235
123456.round(-2) # 123500
123456.round(-3) # 123000
```
# 終わりに

プログラムはメソッド一つで記述が狂います。
小数点が絡んでくるプログラムは特に厄介なので、早めにマスターしましょう！
ffmpeg-pythonを使って動画をトリミングする

#はじめに
自分なりにffmpeg-pythonの使い方をまとめる
`ffmpeg.exe`を`subprocess`で操作するよりは目に優しそうだったので…

#ffmpeg-pythonの取得
pipでインストール。

```
pip install ffmpeg-python
```

※ちなみに、似たようなラッパーがいくつかあるので注意。2ミス。

```
○　pip install ffmpeg-python
------------------------------
×　pip install ffmpeg
×　pip install python-ffmpeg
```

#ffmpegの取得

ffmpeg-pythonはただのラッパーなので、ffmpeg.exe本体がないと使えない。FFmpeg.exeが見つからなくてもFileNotFoundになるので、事前に公式サイトから取得しておくこと。
※ちなみに、動画の編集だけならffmpeg.exeだけでいいが、動画ファイルから動画情報を取得するffmpeg.probe()という処理を実行するためには`ffprobe.exe`も必要なので注意。

https://www.ffmpeg.org/



# 利用方法
## 環境変数
「パスを通せ」と他の紹介記事では書かれていたが、スクリプトと同一フォルダに.exeを配置すればよさそう
##動画をトリミングする
以下をやってみる。
- 5秒目から10秒間を切り取り
- FPSを落として
- 画質を落として、出力する（crfは数値：小＝高画質、数値：大＝低画質）
- 出力ファイルがあった場合は上書きする。

```py
imort ffmpeg

ffmpeg.input('input.mp4',ss=5,t=10)\ #動画開始後、5秒目から、10秒間の動画を切り出す。
        .filter('fps', fps=15, round='up')\ #FPSを変換する。FPS:10に落とす。
        .output('output.mp4',crf=30)\ #動画をエンコードする。画質をcrf=30に指定
        .run(overwrite_output=True) #もし既存ファイルがあっても上書き保存する

```
## 動画を10秒ごとに分割する
動画ファイルの長さを抽出して、10秒ごとに分割するForループを作ってみた。

```py
# %%
#動画の長さをffmpeg.probeで取得して小数切り上げ
info=ffmpeg.probe('input.mp4')
t=float(info["format"]["duration"])
t=int(-(-t // 1))
#0秒から動画の長さまで10秒刻みの数列を作成
start,stop,step=0,t,10
li=list(range(start,stop,step))+[stop]
li

#%%
#10秒おきに動画を分けて保存。
for i in range(len(li[:-1])):
    FILEIN="input.mp4"
    param={
        "FILEOUT":"{}_output_{}_{}.mp4".format(FILEIN[:-4],li[i],li[i+1]),
        "TIME_START":li[i],
        "TIME_END":li[i+1],
        "FPS":10,
    }
    ffmpeg.input(FILEIN,ss=param["TIME_START"],t=param["TIME_END"]-param["TIME_START"])\
        .filter('fps', fps=param["FPS"], round='up')\
        .output(param["FILEOUT"],crf=30)\
        .run(overwrite_output=True)
```

#おわりに
必要十分な気がする。

#参考記事
もし結合がしたければ以下の記事が参考になりそう。
https://blog.shikoan.com/ffmpeg-python-concat/
【AWSセキュリティ基礎】AWS WAF・Shield・Firewall Manager

# AWS WAFとは
- AWSで提供されているWAF(Web Application Firewall)のマネージドサービス
- 通常のファイアウォールが、送信元/宛先のCIDRやポート番号で通信の許可/拒否を判断するのに対し、WAFはクライアントからWEBアプリケーションへのリクエスト内容を見て、通信の許可/拒否を判断する
- SQLインジェクションやクロスサイトスクリプティングなどの攻撃に対するルールが提供されており、ALBやAPIGatewayなどの保護に利用することが可能

## 攻撃検知のルール
- AWS WAFのルールには「マネージドルール」と「ユーザー定義ルール」の2種類があり、組み合わせて使用することにより、効果的に攻撃から保護することができる。

### マネージドルールとは
- 複数のルールを組み合わせてセットにしたもの
- AWSや各セキュリティベンダより提供されている
- 専門知識や検証などを必要とせず、容易に利用することができる
- 更新は各ベンダにより自動的に適用されるため、運用コストの削減が可能

### ユーザー定義ルールとは
- ユーザ独自で作成するルール
- 以下の4種類を作成することができる
    - **IP制限**：特定のIPアドレスからの接続制限
    - **レートベースルール**：同一IPアドレスからの接続数制限
    - **特定の脆弱性に関するルール**：脆弱性に関するリクエスト制限
    - **悪意のあるhttpリクエストを判別するルール**：httpリクエストの内容について、不正とみなす条件を設定し制限を行う

## WAFの設置例
WAFはCloudFrontやELB、API Gatewayに対応しており、各サービスの前段に設置することで、リクエストの内容をチェックし、不審なリクエストをブロックする。
![キャプチャ_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494752/ae06517b-e65d-ae17-cdab-6f8bd1d21db1.png)


# AWS Shieldとは
- DDoS攻撃からシステムを保護するためのサービス
- StandardとAdvancedの2種類がある

### AWS Shield Standard
- 無償で自動的に適用されている
- 小規模なシステム向け
- 保護対象はインターネットに面したすべてのAWSサービス
- AWSの受信トラフィックを検査・分析し悪意のあるトラフィックをリアルタイムで検知する
- 自動化された攻撃緩和技術が組み込まれており、ネットワーク・トランスポートレイヤの一般的なDDoS攻撃からの保護を提供する
- アプリケーション層のDDoSから保護できるWAFと併用すれば、基本的なDDoS攻撃への対策ができる（WAFの利用料金は別途課金される。）

### AWS Shield Advanced
- 有償でより高度なDDoS攻撃からの保護、DDoS攻撃に起因するコスト増加への保護を提供
- 大規模でDDoS攻撃に狙われやすいシステム向け
- 保護対象はEC2、ELB、CloudFront、GlobalAccelarator、Route53
- 対象リソースのトラフィックからベースラインを作り、異常なトラフィックを検知すること（＝アノマリー型検知）が可能
- AWS DDoSレスポンスチーム（DRT）からのサポートを24時間365日受けることができる
- 追加料金なしでAWS WAFの利用が可能
- DDoS攻撃により利用料が急増するなどの被害を受けた場合、増加した料金の調整リクエストを行うことが可能
- 1年間のサブスクリプション契約が必要

## Shieldの全体像
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494752/8698a849-1b61-34bb-6d52-75c7ee72a66d.png)


# AWS Firewall Managerとは
AWSにおいてファイアウォールにあたるサービスのAWS WAF、Shield Advanced、Security Groupのポリシーを一元管理するためのサービス。
Firewall Managerであらかじめセキュリティルールを定義しておくことで、自動的にルールを適用できるようになる。
AWS Organizationsと統合されているため、同じセキュリティルールを複数のアカウントに適用することなども可能。

Firewall Managerで管理できるサービスは以下の３つ。

- AWS WAF
- AWS Shield Advanced
- Amazon VPC Security Group

Firewall Managerの利用を開始するには、以下4つの前提条件を満たすことが必要となる。

- AWS Organizationsへの参加および設定
- Firewall Manager管理者アカウントの設定
- AWS Configの有効化
- Network FirewallおよびDNSファイアウォールポリシーでリソース共有を有効化

## Firewall Managerの全体像
![キャプチャ_1112_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494752/df5f890b-81c8-d47b-da59-c16af7344e5b.png)


# 参考

https://www.amazon.co.jp/%E8%A6%81%E7%82%B9%E6%95%B4%E7%90%86%E3%81%8B%E3%82%89%E6%94%BB%E7%95%A5%E3%81%99%E3%82%8B%E3%80%8EAWS%E8%AA%8D%E5%AE%9A-%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3-%E5%B0%82%E9%96%80%E7%9F%A5%E8%AD%98%E3%80%8F-NRI%E3%83%8D%E3%83%83%E3%83%88%E3%82%B3%E3%83%A0%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE/dp/4839970947#

https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/fms-chapter.html
gitで発生した問題対応から学んだことの整理


#状況

masterは現在release分を対応します。
developでは、次のreleaseの課題に対応するために分けて対応しました。
master branchで修正が発生し、develop branchも修正が発生した状態です。 
現在releaseがstoreに公開されたため、次のreleaseをmasterに合わせる必要がありました。 
どちらのbranchも修正が発生したため、conflictが発生しました。 
これを修正するため、develop branchから新しいbranchを作成しました。そのbranchでmasterにrebaseして、conflictを修正してmergeをしました。


#問題

修正された部分はちゃんとmasterに含まれましたが、

developbranchで修正した部分がすべてひとまとまりになり、それぞれ修正した履歴がまともに見られない状況になりました。 
(-> develop branchはrebaseにしない方がいいそうです。 )

（+develop branch とmaster branch と繋がらないままコードの差分が依然として見られる異常な状況になりました。 )


#対応1 : mergeしたものを再び戻す

mergeしたものを再び戻す必要性がありました。
mergeする前の段階に戻るためには、checkoutを使用します。 

```Git checkout -B master 922bc3bd9```

(```Reset--hard```と似ていますが履歴が残っているというメリットがあるようです。 ```Reset--soft``` かな？ もう少し調査が必要です。)

これをそのままgitにpushしてもrejectされます。 ```git push -f```をしてもrejectされます。

その理由はgitのprotectionのためなので、
SettingのBranchで一時的にmasterの名前を修正します。

```(ex) master_tmp)```

そして、再びgit push -fを実行すれば、現在のmasterが(問題のbranchを)mergeされる前の状態に戻ります。 
さっき修正したmasterの名前を修正します。（master_tmp -> master）


#対応2 :master branchの変更をdevelopに取り込む
mergeしたものは戻したが、二つのBranchがconflictが出るという問題は、依然として残っています。 
master branchの変更をdevelopに取り込む作業をします。 

1.まず、origin/developから新しいbranchを作ります。 
```Git checkout -b feature/merge_master_to_develop origin/develop```
2. ```Git merge –no -ff origin/master```を実行します。 
3.conflictを修正します。 
4.これをpushします。 
5.gitでbase branchをdevelopに変更します。 
6.変更点（Files changed）でconflictを解決した部分のみ表示されます。 

まず、このprをmergeをします。（developがmasterにmergeをするのに問題がない状態にします。）

以降は簡単になります。 conflictがないdevelop branchをそのままmasterにmergeします。 

- gitでNew Pull Requestでcompare branchをdevelopに変更します。
Google form を使って整理番号を振り管理&メールを送る方法

google form では，フォームが入力を受けとった際，スクリプトを実行することができます．この機能を使って整理番号を振ってメールを返しつつスプレッドシートにその番号を保存し番号を管理するスクリプトを作成したので紹介します．こちらは予約フォームになどに使用することを想定しております．ご自由にお使いください．（不具合は自己責任で．）
なお，今回作成には Windows 10 の Chrome を使用しています．


#Google formでスクリプトを使用する方法
Google form の作成画面の三点リーダをクリックすると下記のメニューが現れる．ここからスクリプトエディタを選択する．

![スクリーンショット (79).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281231/90ee710a-f29f-f261-dc56-dc2ac9e1787b.png)

## メールアドレスの収集
Google form 編集ページから [設定] タブを開き，[回答] \> [メールアドレスを収集する] を選択する．この際，[回答のコピーを回答者に送信] はオフのままにしておく．

## スプレッドシートの作成
Google form 編集ページから [回答] を開き，緑のスプレッドシートのマークをクリックし，回答を保存するスプレッドシートを作成する．
スプレッドシートが開かれた後，後で使うためフォームへのURLをコピーしておく．
またこの時に，フォームの最左列を選択し，右クリックして [左に1列挿入] を選択する．この挿入した列に整理番号を保存することになる．

## コード
スクリプトエディタを開くと [コード.gs]が開かれているので，以下に書き換える．

```
function myFunction(e) {
  
  const applicant = "";
  const itemResponse = e.response.getItemResponses();
  let sendToAddress = e.response.getRespondentEmail();
  var body = "";

  for (let i = 0; i < itemResponse.length; i++){    
    const formData = itemResponse[i];
    const title = formData.getItem().getTitle();
    const response = formData.getResponse();
    body +=　"[" + title + "] : " + response +"\n";
  }
  
  var spreadsheet = SpreadsheetApp.openById("$$$$");
  var Sheet = spreadsheet.getSheets()[0];
  var range = Sheet.getRange(Sheet.getLastRow(),1);
  var id = Sheet.getLastRow()-1
  const customer_id = ('0000' + id).slice(-4);
  console.log(customer_id);
  body = "整理番号 : "+customer_id +"\n"+ body
  if(range.isBlank() == true){
    range.setValue("=ROW()-1");
  }

  
  console.log(sendToAddress);
  console.log(body);
  var mailTitle = 'ご予約ありがとうございます．';//メールのタイトルを設定
  MailApp.sendEmail(sendToAddress,mailTitle,body);
}
```

```
  var spreadsheet = SpreadsheetApp.openById($$$$);
```
の\$$$$の部分にはフォームの回答が入力されるスプレッドシートのIDを記述する．
IDは，先ほどコピーしておいたスプレッドシートのURLの
https://docs.google.com/spreadsheets/d/$$$$/
の\$$$$の部分となる．
また，bodyはメール本文であるため，送りたい内容に合わせて変更すればよい．同様に，mailTitleも変更可能である．
上記の例では，本文は整理番号と質問内容・回答となる．
ただしメールの送信者はform作成に使用したアカウントのアドレスで固定される．

続いてApps script の左のタブから [設定] \> [「appsscript.json」マニフェスト ファイルをエディタで表示する] にチェックを入れた後，エディタに [appsscript.json] が追加されているのでそれを開き，

```
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  
  "oauthScopes": ["https://www.googleapis.com/auth/forms.currentonly" , "https://www.googleapis.com/auth/forms", "https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/script.send_mail"]
}
```

と変更．


編集後，[コード.gs]を開き，実行ボタンを押す．その際権限の許可を求めるページが出てくるので許可する．google以外の配布であると表示されるが，構わず許可する．（本作成者が信頼できないと考える場合は拒否して構わない）この実行はイベントの入力がないため必ず失敗するが問題ない．

## トリガーの追加

Apps script の左のタブから[トリガー]をクリックし，右下の＞[トリガーを追加]をクリックし，イベントの種類を選択をフォーム送信時にして保存．
![スクリーンショット (81).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281231/5956677f-0e1a-b62f-7a4c-eacd42852713.png)



以上で実行可能な状態になる．
Raspberry Pi 3にghc8.10.1をインストールしてみた

Raspberry Pi 3にghc8.10.1をインストールしてみたので記事にします。

当初stackをインストールしたくて、通常のLinuxにインストールするように

```
curl -sSL https://get.haskellstack.org/ | sh
```

や

```
wget -qO- https://get.haskellstack.org/ | sh
```

を試してみましたが、だめでした。
CPUがArmのRaspberry Piには、現時点ではstackをインストールできないようです。

# GHC8.10.1のインストール

## 環境
Raspberry Pi 3
OS: Rasbian 32bit
インストールアカウント：デフォルト（pi）

## 調べる
Raspberry Piへのインストール方法をググってみると、公式にwikiがありました。
[Raspberry Pi](https://wiki.haskell.org/Raspberry_Pi)  

上記ページのリンクのトップに下記がありました。
[The state of GHC on ARM](https://www.haskell.org/ghc/blog/20200515-ghc-on-arm.html)  

ここにインストール方法が載っています。

以下インストール方法です。

## 注意事項

※インストールはRasbian 32bitで成功しました。
　Rasbian 64bitでインストールを行ってみましたが、インストールの途中でエラーになりました。

※インストールはデフォルトのアカウント（pi）で成功しました。
　最初、別に作成したアカウントでインストールを始めたら、インストールでこけました。
　ダウンロードするファイルの所有権が（pi）になっています。所有権を変更すれば成功していたかもしれません。

## インストール方法

まずは、LLVM 9（GHCがARMでのコード生成に使用）をインストールする必要があるようです。

```
sudo apt-get install llvm-9
```

ダウンロード

```
wget http://downloads.haskell.org/~ghc/8.10.1/ghc-8.10.1-armv7-deb9-linux.tar.xz
```

解凍

```
tar -xf ghc-8.10.1-armv7-deb9-linux.tar.xz
```

※公式サイトではファイル名が間違っていましたので注意です。

フォルダの移動とコンフィグ＆インストール

```
cd ghc-8.10.1

./configure CONF_CC_OPTS_STAGE2="-marm -march=armv7-a" CFLAGS="-marm -march=armv7-a"

sudo make install
```

## 動作確認

バージョンの表示とハローワールド

```
$ ghc --version
$ cat > Hello.hs <<EOF
> main = putStrLn "hello world!"
> EOF
$ ghc Hello.hs
$ ./Hello
hello world!
```

小職の場合は上記を実行すると、下記のようなエラーになりました。

```
/usr/bin/ld.gold: エラー: -lgmp が見つかりません
```

なのでファイル（gmp）を検索

```
sudo find / -name *gmp*
```

下記フォルダにファイルが見つかりました。

```
/usr/lib/arm-linux-gnueabihf/libgmp.so.10.4.1
```

リンクを作成しました。

```
cd /usr/lib/arm-linux-gnueabihf

sudo ln -s libgmp.so.10.4.1 libgmp.so
```

あらためて動作確認すると、ハローワールドが表示されました。

```
pi@raspberrypi:~/Documents/project/make/haskell/hello $ cat Hello.hs
main = putStrLn "hello world!"

pi@raspberrypi:~/Documents/project/make/haskell/hello $ ghc Hello.hs
[1 of 1] Compiling Main             ( Hello.hs, Hello.o )
Linking Hello ...

pi@raspberrypi:~/Documents/project/make/haskell/hello $ ./Hello
hello world!
```

インストール後は別のアカウントでもGHCは動作しました。

以上
GeneXusアナリスト試験体験記

# はじめに
GeneXusとはローコード開発のソフトウェアです。
その開発元であるGeneXusが認定を行っている資格試験(今回はアナリスト試験)をうけた感想を
後続する皆さんにお伝えするべく投稿します。

**※今回紹介する内容は当時のもののため、現在の情報と異なることがあります。
必ずGeneXus資格試験の公式サイトをご確認ください。**
正式な情報は[こちら](https://www.genexus.jp/training/518)です。

# 試験概要
正確には**GeneXus 16 アナリスト認定資格試験**というみたいです。
資格のグレードは4つあり、簡単な順から

- ジュニア・アナリスト試験
- アナリスト試験
- シニア・アナリスト試験 
- インストラクター試験

となっています。
またその他に過去のバージョンで取得した資格のバージョンのアップグレード試験もあります。

##　アナリスト試験
私が受験したアナリスト試験は受験形式はオンラインで、出題形式は選択式です。
問題はだいたい30問程度あり、各問題に点数が表示されており正解率が70％以上あると合格です。




### 試験内容
内容についてはあまり言えないのですが、公式サイトにある模擬試験よりも**数段難しく**感じました。
Basicコースの内容に加えて、数問程度ですがGeneXus16,15のアップグレード教材からも問題が出るため、そちらの対策もしておいたほうがいいです。

### 勉強方法
基本的には以下の項目を繰り返し実施しました。

1. **Basicコースの動画、教科書の復習**
一番基本の勉強法だと思います。
再度見直すことで、最初受講した際に見たときではよくわからず見ていたことがわかりました。
模擬試験や超解説でわからなかったテーマを何回も確認しました。
通しで2~3回くらいは見直したと思います。

1. **模擬試験**
公式サイトにあるジュニアアナリスト、アナリスト模擬試験を各2~3回ほど実施しました。
人って不思議なもので、一度解いた問題も少し期間を空ければ忘れてしまって
「見たことあるからこの選択肢だ!」ということはなかったです。(個人差ありそうですが…)

1. **模擬試験復習**
今回の勉強方法で一番時間をかけた部分です。
模擬試験の回答には解説がなく、なんで間違えたのか、なんでその選択肢が正解なのかが全くわかりません。
そのため、わからなかったところに加え、まぐれで当たったものや不安な箇所を徹底的に復習する必要があります。
わからなかったテーマの動画や教科書の見直し、それでもわからないときは実際にGeneXusにてその設計等を実装しました。
また問題の内容によって点数が違うため、点数が高い問題の復習を最重要、最優先で行いました。

1. **超解説ブログ**
こちらのサイトで過去のジュニアアナリスト模擬試験の解説を行っています。
ジュニアアナリストという一番難易度が低い試験ではありますが、昔の試験だからなのか難易度が高くやりごたえがあります。
また公式の模擬試験にないわかりやすい解説がのっておりとても役に立つと思います。

https://g-mind.blogspot.com/p/genexus.html

## 注意点
1. Zoomのインストールが必要
1. カメラがあるPCが必要
1. 当日はカメラON
1. 第3者がいない静かな部屋が必要(自宅でもOK)
1. 当日カメラから本人確認を行うため、本人確認書類がいる(免許書等)
1. 試験の申し込みは2週間前まで
1. 試験中のメモ等はできなかったと思います
1. アップグレード教材の確認も重要
1. 事前メールに張り付けてあるURLからオンライン資格試験等の注意事項があるので確認する

# 終わりに
現在の模擬試験の解説3種を今後投稿していきたいと思います。

皆様の合格をお祈りしています。

# 参考文献

https://www.genexus.jp/
Laravel8にVue.js導入時のエラー改修



## Laravel8にVue.js導入
参考 ：https://migisanblog.com/laravel-vue-install/#Vuejsをインストールする


とりあえず、「app.blade.php」に以下記述
参考：https://biz.addisteria.com/laravel8_vue_install/#toc9


```php:app.blade.php
<div id="app">
    <example-component></example-component>
</div>

<script src="{{ mix('js/app.js') }}"></script>
```

## chromeのデベロッパーツールを見るとコンソールでエラー


```
ERROR in ./resources/js/components/ExampleComponent.vue 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
 <template>
     <div>
     <button type="button" class="btn m-0 p-1 shadow-none">
```



## webpack.mix.jsを修正

で以下参考に修正
https://zakkuri.life/laravel-mix-npmのパッケージをupgradeしたらコンパイルできなく/

```
mix.js('resources/js/app.js', 'public/js').vue()  ← 「.vue()」を追記する
    .sass('resources/sass/app.scss', 'public/css');
```

でnpm run dev実行


```
Additional dependencies must be installed. This will only take a moment.
 
Running: npm install vue-loader@^15.9.7 --save-dev --legacy-peer-deps
 
Finished. Please run Mix again.
```

##「npm install vue-loader@^15.9.7 --save-dev --legacy-peer-deps」実行する

再びnpm run dev

でLaravel8にVue.js環境整った



参考
https://zakkuri.life/laravel-mix-npmのパッケージをupgradeしたらコンパイルできなく/
https://blog.maro.style/post-1526/
Bootstrapのnode_modulesファイル内のcssをoverride出来なかった話

表題の通り、Bootstrapでcssをoverrideしようとしたところ適用されず、沼にハマりかけたので記録を残します。


##実行環境
- Windows10 20H2
- Vagrant 2.2.18
- Ubuntu 20.04.3 LTS
- Ruby on Rails 6.0.4.1
- jQuery 3.5.1
- bootstrap 4.5.0


##問題
１． HTMLファイル内にalertテスト用のdivを作成

```HTML
<div class="alert alert-success">alertクラス</div>
```

２． オーバーライド用のscssファイルを作成。
[app/javascript/stylesheets/bootstrap_overrides.scss]

```css
.alert-success {
  background-color: pink;
}
```

３． scssファイルを読み込んでるマニフェストファイルにscssファイルのパスを通す。
[app/javascript/packs/application.js]

```JavaScript
import '../stylesheets/bootstrap_overrides.scss';
```

これでbootstrap_overrides.scssの内容が適用されて、alert-successクラスがoverrideされるはずでしたが、反映されませんでした。
![error.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2259269/a4d0efff-7044-70a8-2653-bae05b3529b4.png)



##解決方法
マニフェストファイルに記載したscssファイルの拡張子を消す。

[app/javascript/packs/application.js] 

```JavaScript
import '../stylesheets/bootstrap_overrides';
```

ページをリロードするとalertクラスがoverrideされました。
![error-result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2259269/4b486026-fc97-86a7-52af-f6d00316cac6.png)


##余談
一度オーバーライドした後に"bootstrap_overrides.scss"の内容を変更し、
application.jsで.scssと拡張子を書き加えたところ今度はしっかりオーバーライドしてくれました。
Arduinoでクラスを使うときに引っ掛かったこと

# 失敗１：unknown type name 'class'
エラー

```
~.h  , unknown type name 'class'; did you mean 'labs'?
 class EKF_class{
 ^~~~~
 labs
```


ヘッダファイル

```C++
#ifndef EKF
#define EKF
#include "Arduino.h"


class EKF_class{
private:
    static float invSqrt(float x);
    float qw;
    float qx;
    float qy;
    float qz;
    float wx;
    float wy;
    float wz;
    float mgIrap[3][3] = { { 0.       ,  64.4551274, -68.3301152},{-65.2699143,   0.       ,  71.0792784},{56.5946296, -58.1367176,   0.      }} ;

    //正確には、inv_I * alpha  のこと。　T = alpha * V  - beta * dphi
    //dphiを
    float inv_I[3] = {10.396,-10.9436,-7.7065};

public:
    EKF_class(void);
    /*
    void add3_3(float A_pt[3][3],float B_pt[3][3]);
    void eq3_3(float A_pt[3][3],float B_pt[3][3]);
    void mul3_3(float A_pt[3][3],float B_pt[3][3]);
    void disp3_3(float A[3][3]);
    */
};
#endif
```

### 原因

https://forum.arduino.cc/t/arduino-101-multiple-source-files/458647/3

ライブラリのCコードの名前を「--.c」にしていた
「--.cpp」にする必要がある。

### 原因2(2023/12/28　追加)
同様のエラーが再度発生、cppに変更したが解決せず。

* コンストラクタ追加
* cpp側にヘッダファイルインクルード追加
* ヘッダファイルが2つある時に#ifndef 名前で同じ名前にしてるとだめ
* メンバ変数へのアクセスを`Class_name::member`に修正

https://www.kinjo-u.ac.jp/ushida/tekkotsu/cpp/ifndef.html

上記のどれかで解決

クラス作成例

https://qiita.com/GANTZ/items/8af143ed99b2f843b176

# 失敗1-2

```
does not name a type
```
コンストラクタについたエラー



### 原因
Classの宣言がうまくいっていないということ。classが宣言できていない

### 解決
cppファイル内にhファイルをincludeするように変更

(Qiitaの# と　##って違いが弱くて見にくいね・・・###にしたらいいのかな)


# 失敗2

```
cannot convert '<brace-enclosed initializer list>' to 'float' in assignment
```

```
A_w[3][4] = {{-2*qy,2*qz,-2*qw,2*qx},{2*qx,2*qw,2*qz,2*qy},{2*qw,-2*qx,-2*qy,2*qz}};
```

### 原因
すでに、定義した配列に対し{}で代入することはできない。


# 失敗３

```
EKF.cpp: In member function 'void EKF_class::make_A()':
```
どこがエラーかはわかるが、何がよくないかは不明
と思ったが、この文章はmake_A()という関数内でエラーがあるよと言っている
この文章の下に本当のエラー原因があります。

# 失敗4

```
exit status 1
expected unqualified-id before ')' token
```

```
error: abstract declarator '<unnamed class>' used as declaration

```

コンストラクタの宣言に発生するエラー

### 苦痛
ハゲそう。ガチでゴミ
理由がわからない
ChatGPTに全コード渡してもわからず・・・
極限までコードを簡略化しても消えない

### 解決

```
#ifndef Th_control
#define Th_control

/*

*/

#endif
```
はダメ

```
#ifndef TH
#define TH

/*

*/

#endif
```
なら通った。

インクルードガードの書き方の問題？？

プリプロセッサは大文字しか対応できないとかあるのかな。

とりあえず、辛い。
わからない。時間がかかる・・・
変な落とし穴が嫌い。
誰だこの仕様作ったの！！！！！！！！！！
【Flutter】WebViewの高さをWebページの高さに合わせる

#　はじめに
使用するパッケージは[webview_flutter](https://pub.dev/packages/webview_flutter)です。

SNS埋め込みとかでよく使うWebViewですが、
**Columnの中に入れると`A RenderFlex overflowed by Infinity pixels on the bottom`でアプリが落ちます。**
これ自体は`Expanded`などを使えば解決します。

ただ、**WebViewの中身をスクロールするのではなく、Flutter側でWidgetをまるっとスクロールさせたい**、みたいな場面ってあると思います。
（今回の話は、その為にColumnに入れて使いんたいんじゃい！という話）

固定の高さを持たせて、かつそれをWebページの高さに合わせれば解決なのですが、パッケージ自体にはそんな機能はついておりません。
（Webページの高さを取得できるようにした[webview_flutter_plus](https://pub.dev/packages/webview_flutter_plus)などもありますが、**今回は使いません。**）

では、どうやって高さを取得するかというと、**`runJavascriptReturningResult`**を使用します。

# `runJavascriptReturningResult`とは？
これは、`WebViewController`のメソッドで、**WebViewの中でJavascriptを実行する事ができます。**
似たようなメソッドに`runJavascript`がありますが、こちらは戻り値が**Future\<void>**なので今回の用途では使えません。
戻り値がFutureなので、awaitするのを忘れないようにしましょう。

以下サンプルです。

```dart
class WebViewTest extends StatefulWidget {
  const WebViewTest({Key? key}) : super(key: key);

  @override 
  State<WebViewTest> createState() => _WebViewTestState();
}

class _WebViewTestState extends State<WebViewTest> {
  late WebViewController _webviewController;
  String _webviewUrl = 'WebViewのURL';
  double _height = 0; // WebViewの高さ

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          適当なWidget,
          ...
          
          SizedBox(
            height: _height, // 高さ指定（ここではまだ0）
            child: WebView(
              initialUrl: _webviewUrl,
              onWebViewCreated: (controller) async {
                _webviewController = controller; // コントローラ取得
              },
              // WebViewが読み込みが完了できたら
              onPageFinished: (_) async {
                double _contentHeight = double.parse(
                  // JSで高さを取得し、doubleにパース
                  await _webviewController
                    .runJavascriptReturningResult('document.documentElement.scrollHeight;'),
                );
                setState(() {
                  _height = _contentHeight; // WebViewの高さを更新
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}
```

これでWebViewの読み込みが終わり、JSが実行できたら高さがWebページの高さに変わります。

# 注意点

WebページがJSで画面を構築するページの場合、 **`onPageFinished`のタイミングで画面構築が終わってない場合があります。**
その場合、`javascriptChannels`を使ってWebページ側から通知してもらうか、`Future.delayed`でタイミングをずらしましょう。
（SNSの埋め込みとかだと、通知用のコードを埋め込んでもらうのとかは無理なので`Future.delayed`を使うことになると思います。）

`Future.delayed`の場合だと、Webページ側の読み込みによって指定する時間が大きく異なると思うので、
ストレスが掛からない程度に大きめに取っておくといいと思います。
（SizedBoxをAnimatedContainerに変えてアニメーションさせたり、AnimatedOpacityでフェードさせたりしてもいいかも）
Next.js アプリでURLのクエリ文字列は隠しつつ遷移先にパラメータを渡す

:::note info
本記事では分かりやすさを重視するため、F1 を題材として取り扱っています。
:::

[F1 Racing Teams 2021 - Ferrari, McLaren, Mercedes and more](https://www.formula1.com/en/teams.html)

Next.js における Link コンポーネントの基本的な使用方法は公式ドキュメントを参照してください。
[next/link | Next.js](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link)

## 本題

例えば以下のように、URL には識別子を含めずに名前のみで構成したい、といった場合です。

```:URL例
https://yourhostname.com/Mercedes/Lewis-Hamilton
```

この URL は`[オリジン]/[チーム名]/[ドライバー名]`という構成になっています。
実際のところ、こういったケースでは`チーム ID`や`ドライバー ID`も同時に遷移先に渡したい場合がほとんどだと思います。
`チーム ID`や`ドライバー ID`などの識別子を受け取らない場合、遷移先ではチームやドライバーの情報を文字列で検索しなければならないからです。
プログラマーであれば「それは避けたい」と直感的に思うのでは無いでしょうか。

識別子を含める場合、URL は以下のようになると思います。

```:URL例
https://yourhostname.com/Mercedes/Lewis-Hamilton?teamId=1&driverId=1
```

少々ばかり冗長に見えますし、これでは名前のみで構成したいという目的が達成されていません。

### コード例
この場合のコード例を以下に示します。

```jsx
const url =
  `/${team.teamName}/${driver.driverName}`.replace(/\s/g, "-") +
  `?teamId=${team.teamId}&driverId=${driver.driverId}`;

return (
  <li key={url}>
    <Link href={url}>{driver.driverName}</Link>
  </li>
);
```

[サンプルコード - github](https://github.com/naoki-funawatari/test-next-routing/blob/ac90c64f7e5318b448ab9c4c8d3271fd1244e6f5/src/components/Nav.jsx)

パラメータは`useRouter`をラップしたカスタムフックを自作し受け取っています。

```js
import { useRouter } from "next/router";

const useParams = () => {
  const router = useRouter();

  return {
    teamId: router.query?.teamId ?? null,
    driverId: router.query?.driverId ?? null,
  };
};

export { useParams };
```

### 動作例
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213286/c70fa6eb-1507-05fb-ae5d-5dddea7315f3.gif" width="300px" />

## 対処

state などを用いる方法もあると思いますが、今回は動的ルーティングを用いて解決しました。
[next/link - Dynamic routes | Next.js](https://nextjs.org/docs/tag/v9.5.2/api-reference/next/link#dynamic-routes)

### コード例
コード例を以下に示します。
`Link`コンポーネントの`href`プロパティにクエリ文字列を付与した URL、`as`プロパティに表示したい URL を設定しています。

```jsx
const asUrl = `/${team.teamName}/${driver.driverName}`.replace(/\s/g, "-");
const url = `/[team]/[driver]?teamId=${team.teamId}&driverId=${driver.driverId}`;

return (
  <li key={url}>
    <Link href={url} as={asUrl}>
      {driver.driverName}
    </Link>
  </li>
);
```

[サンプルコード - github](https://github.com/naoki-funawatari/test-next-routing/blob/0c3fbe7ac8a05e110773a0c3f8fd898f4c2a36a8/src/components/Nav.jsx)

### 動作例
URL からクエリ文字列が取り除かれているところ以外の違いはありあません。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213286/b16ff9af-0013-3656-18d4-51543edafe6c.gif" width="300px" />

無事に表示上の URL からクエリ文字列を隠蔽することに成功しました。

## 課題

ここまで実装すれば、目的は一応達成できています。
しかし課題も見えてきました。
まず、この実装では F5 を押されたときに元の画面を表示できません。
クエリ文字列が物理的に存在しないため、画面更新後にそれらを参照することができないためです。

## 感想

ReactRouter でのルーティングの方法が頭にこびりついていて、ファイルシステムベースでルーティングすることになかなか馴染めず苦戦しました。
なんて思っていたら ReactRouter も新バージョンが出てるみたいですね。
書き方が少し変わってるみたいなのでこちらもキャッチアップする必要がありそうです。
Elastic BeanstalkでAmazon Linux 2に移行する際に気をつけること

# はじめに

Amazon Linux AMIのプラットフォームが非推奨となったため、Amazon Linux 2への乗り換えを実施したのですが、いろいろとハマった箇所があったので解決法とともに紹介したいと思います。

プラットフォームの変更によりファイルの配置などが大幅に変更されているので、新しい環境を立ち上げてデプロイするだけではまともに起動することも出来ません。
`.ebextensions`を使用したカスタマイズを行っている場合は特に注意が必要です。

# 環境

弊社ではRuby on Railsを使用しているので、Ruby2.7のAmazon Linux 2プラットフォームを選択しました。

- Ruby 2.7 AL2バージョン 3.3.4

※移行時点（現在は3.3.6）

# 乗り換え時の注意点

## Procfile

アプリケーションの実行はProcfileを使用するようになりました。
アプリケーションソースのルートディレクトリにあるProcfileが参照されます。
開発時にforemanを使用している方は開発用のProcfileをデプロイ時に含めないように注意する必要があります。

## Pumaのバージョン

Procfileが存在しない場合、アプリケーションサーバーとしてプリインストールされているPumaが使用されます。
Gemfileで指定しているPumaのバージョンと環境のPumaのバージョンがズレているとエラーが発生して正常に起動されません。

```puma/puma.log
[3119] - Gracefully shutting down workers...
=== puma startup: 2021-08-19 12:54:53 +0900 ===
=== puma startup: 2021-08-19 12:54:53 +0900 ===
[9446] + Gemfile in context: /var/app/current/Gemfile
[9448] + Gemfile in context: /var/app/current/Gemfile
[9448] ! Unable to start worker
[9448] /opt/rubies/ruby-2.7.4/lib/ruby/site_ruby/2.7.0/bundler/runtime.rb:302:in `check_for_activated_spec!'
[9448] Early termination of worker
[9453] + Gemfile in context: /var/app/current/Gemfile
[9446] ! Unable to start worker
[9446] /opt/rubies/ruby-2.7.4/lib/ruby/site_ruby/2.7.0/bundler/runtime.rb:302:in `check_for_activated_spec!'

```

BundlerでインストールされたPumaを使用する場合はProcfileに下記のように記述します。

```Procfile
web: bundle exec puma -C /opt/elasticbeanstalk/config/private/pumaconf.rb
```

https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/ruby-platform-procfile.html

## awslogsの設定

アプリケーションのログファイルをCloudWatch Logsへ取り込むには以前と同様に`.ebextensions`で設定が可能です。
ただし、以前は`service`コマンドを使っていたところがAL2から`systemctl`を使用するように変わっているので注意が必要です。

### 以前の形式

```
commands:
  "01":
    command: chkconfig awslogs on
  "02":
    command: service awslogs restart
```

### AL2版

```
commands:
  "01":
    command: systemctl enable awslogsd.service
  "02":
    command: systemctl restart awslogsd
```

https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/instance-configuration/logs-streamtocloudwatch-linux.config

## nginx（リバースプロキシ）のカスタマイズ

以前は`.ebextensions`でnginxのcond.d以下に設定ファイルを追加したり、nginxをリスタートするように設定していましたが、AL2では決められたパスにファイルを置いておくだけで自動的に取り込まれる仕組みになりました。

詳しい設定方法はドキュメントの「リバースプロキシの設定」の項目をご覧ください。

https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/platforms-linux-extend.html

ただし、アプリケーションソースに含める必要があるため、環境変数などに依存したカスタマイズは出来ませんので注意してください。

`.platform/hooks/prebuild/`に設定ファイルを書き出すスクリプトを置くことで細かい制御が可能ですが、長くなるため説明は省略します。

# まとめ

プラットフォームの更新でファイル構成についてはかなり変わってしまった印象を受けました。
AWSのデベロッパーガイドにかなり詳しく説明が記載されているので作業を始める前に一度確認しておくことをおすすめします。
CodeBuild内のDockerデーモンにProxy設定をする方法


Proxy環境下では `http_proxy` 、 `https_proxy` 、`no_proxy` の環境変数を利用すると思います。

特にDockerを利用する場合はdockerd自体がProxyの設定を認識する必要があります。通常は以下のように設定したりします。

https://docs.docker.com/config/daemon/systemd/#httphttps-proxy

しかし、CodeBuildにおいてはジョブ開始時からDockerが立ち上がった状態です。そこで以下のようにdockerのプロセスを一度落として、その後起動することでProxy設定を読み込むことが可能です。

```yaml
version: 0.2

# Proxy関連の環境変数は事前設定されている前提
phases:
# 省略
  build:
    commands:
      - docker info
      - kill -9 $(cat /var/run/docker.pid)
      - while kill -0 $(cat /var/run/docker.pid) ; do sleep 1 ; done
      - /usr/local/bin/dockerd-entrypoint.sh
      - docker info
# 省略
```


2回目の `docker info` ではProxyの設定が入っていると思います。

```
 HTTP Proxy: http://proxy.local
 HTTPS Proxy: http://proxy.local
 No Proxy: 169.254.169.254,169.254.170.2  
```
佐々木さんが勤怠管理をお手伝いするよ

勤怠管理システムを作りました。
名は**SASAKIsan**。敵の名は**miyamoto**。
昔いたベンチャー企業でみやもとさんという勤怠管理botを使っていました。Slackから出勤と退勤の時間を打刻できるので非常にありがたく利用しておりました。

https://github.com/masuidrive/miyamoto

そんなみやもとさんですが、SlackとGAS連携が前提なのと時たまバグが発生するのが曲者でした。何奴。
8月にバグが出る件は直っているようですね。

そこで、導入の敷居がもっと下がればと思い開発したのがSASAKIsanです。

https://github.com/nisyuu/SASAKIsan

サンプルをHerokuサーバーで立ち上げているので、遊んでいただいて構いません。

[SASAKIsan on Heroku](https://sasakisan.herokuapp.com/)
*※アカウント登録はできないのでご注意を*

**Adminユーザー**
email : kojiro_sasaki@ykonishi.tokyo
pass  : foobar

**Adminでないユーザー**
email : example@railstutorial.org
pass  : foobar

# 技術構成
佐々木さんはRuby on Railsで開発しました。
Railsを個人的に使用したのは初めてなので、チュートリアルをベースに開発しています。
ローカルの仮想環境にはDockerを採用しました。

## バージョン
- Ruby 2.7.4
- Rails 6.1.4.1

Herokuでも動作確認済みなので、そのままデプロイしても問題ないと思います。

# 導入について
GitHubにパブリック公開しているので、自由にご利用ください。
クローンいただいて魔改造しても問題ありません。

環境の立ち上げ方法はREADME.mdに記載しています。Docker環境構築でインストールに時間がかかると思いますが、辛抱ください。

# 仕様
[Ruby on Railsチュートリアル](https://railstutorial.jp/)を参考にして開発したので、ベースはチュートリアルに近い仕様になっています。
そのため、アカウント有効化のメール送信や画面デザインなどはチュートリアル通りです。
勤怠管理の部分だけはオリジナルとなっています。

## ロール
ロールはadminかadmin以外の2つです。
adminはユーザーと勤怠の削除が可能です。
勤怠の編集は登録したユーザー本人のみが可能となります。

## 勤怠管理
ログイン後、ホーム画面に行くと本日の勤怠状況が表示されます。
画面左に出勤と書かれたボタンがあると思うのですが、ここを押すと出勤記録が付きます。

![スクリーンショット 2021-11-11 15.17.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243199/950c3bc4-f4ab-8d97-41c0-25c70acb08f0.png)

出勤ボタンを押すと出勤時刻が記録され、今度は退勤ボタンに切り替わります。
![スクリーンショット 2021-11-11 15.18.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243199/63ddc0b8-252f-3cff-a0ce-41f4d154df66.png)

退勤ボタンを押すと退勤時刻が記録されてボタンは押せなくなります。
翌日になると押せるようになります。
![スクリーンショット 2021-11-11 15.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243199/3e1668fc-ce86-8602-bd3f-3c63a3d84b1c.png)

プロフィール画面に行くと個々人の勤怠状況が閲覧できます。

# Herokuへのデプロイ
基本的にHerokuへのデプロイは公式の手順に従えば、問題なくできるはずです。

https://devcenter.heroku.com/ja/articles/getting-started-with-rails6

postgresqlを利用する場合は、**database.yml**をこちらのように変更すると接続ができます。
`your_app`のところだけ各自Herokuアプリ名を入れてください。

```yml:database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: app_development
  host: db
  username: root
  password: password

test:
  <<: *default
  database: app_test
  host: db
  username: root
  password: password

production:
  <<: *default
  database: your_app
  username: root
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>
```

# まだまだ続くよ
CSV出力やみやもとさんのようにチャットツールから出退勤できる機能も検討中です。
~~1日に複数回出勤・退勤を打刻するパターンもありえるので、ここは近々改修しようと考えています。~~
↑
>2021/11/11 改修しました。
https://github.com/nisyuu/SASAKIsan/commit/557e46fb9dbd2a0c55f0d6dd6930de59b31a7dd0
Kubernetesの便利プラグインを使えるようにするkrewをインストールする

# 概要

[Krew](https://github.com/kubernetes-sigs/krew)は、[kubectl plugin](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/)を使いやすくするツールです。

# インストール

## macOSの場合

```bash
brew install krew
```

## Arch Linuxの場合

```bash
yay krew
```

# 設定

`~/.zshrc`に`~/.krew/bin`パスを追加。

```bash
export PATH="${PATH}:${HOME}/.krew/bin"
```

追加したら、`source ~/.zshrc` として再読み込みしておく

# ctxプラグイン

複数のKubernetesクラスタを使っている場合、切り替える必要がある。その切り替えを瞬時に行えるようにするプラグイン。

__インストール__

```zsh
kubectl krew install ctx
```

__一覧を見る__

```zsh
kubectl ctx
```

__切り替える__

```zsh
kubectl ctx kind
```

# nsプラグイン

namespaceを一覧したり、切り替えたりを簡単にできるようになる。

__インストール__

```zsh
kubectl krew install ns
```

__一覧を見る__

```zsh
kubectl ns
```

__切り替える__

```zsh
kubectl ns kube-system
```
Shopify Hydrogenのメモ

[Shopify](https://www.shopify.com/)のストアフロントを作成するReactベースのフレームワーク[Hydrogen](https://shopify.dev/custom-storefronts/hydrogen)を試してみた。
(Shopify用ということを読み飛ばしていて汎用的なフレームワークと勘違いした…)

公式ページにも記載されている通り、まだDeveloper previewなので注意。
本記事ではv0.6.3を使用した。

## 使用ライブラリ等

|  | ライブラリ等 | メモ |
|--|--|--|
| UI | [React](https://reactjs.org/) | Server Componentsを使っているためv18を使用 |
| UIコンポーネント | [Headless UI](https://headlessui.dev/) + [Tailwind CSS](https://tailwindcss.com/) |  |
| ルータ | [React Router](https://reactrouter.com/) | v6がリリースされたばかりだが、2021/11/11時点ではv5を使用 |
| Web API | [GraphQL](https://graphql.org/) | [graphql-tag](https://github.com/apollographql/graphql-tag)を使用 |
| ビルド | [Vite](https://vitejs.dev/) |  |
| サーバサイド | [Express](https://expressjs.com/) |  |

## TypeScript

`yarn create hydrogen-app` で作成されるscaffoldではJavaScriptが使用されているが、特に何も設定を追加せずともTypeScriptを使用できる。(Viteの恩恵)
ただしコンパイルオプション等を変更する場合は `tsconfig.json` を追加する必要がある。
Oracle Data SafeでAutonomous Databaseを管理する

Oracle Databaseをより安全に利用するためのクラウドベースのデータベース・セキュリティ・サービスです。
セキュリティ対策が後手になりがちなデータベースに対して、CISベンチマークを基準にしたリスク評価やマスキング、監査ログ監視等の運用によりデータベースをセキュアな状態に保つことが可能です。

主に以下の5つの機能で構成されています。
セキュリティ・アセスメント（Security Assessment）
ユーザー・アセスメント (User Assessment）
機密データ検出（Sensitive Data Discovery ）
データ・マスキング（Data Masking）
アクティビティ監査（Activity Auditing）

Oracle Cloud上のOracle DBだけでなく、オンプレミスや他社クラウドのOracle DBも同様に管理することが可能です。サポートしているCloud Databaseサービスは[こちら](https://docs.oracle.com/en/cloud/paas/data-safe/admds/target-database-registration-overview.html#GUID-34BA45C2-A48E-45CE-B9E3-D2D7B7D4E3F6)

Oracle Cloud Databaseが対象の場合は無償です。※アクテビティ監査は100万レコード/月まで無量。オンプレミス、他社クラウド上のOracle DBの場合は有償です。価格は[こちら](https://www.oracle.com/jp/cloud/price-list.html#data-safe)


ここでは、Oracle Cloud InfrastructureのコンソールからOracle Data Safeを有効化し、Autonomous Databaseを管理するまでのひと通りの手順を紹介していきます。以降の操作は、テナンシー管理者で実施します。

## Data Safeの有効化
- OCIコンソールメニューから、Oracle Database -> データ・セーフを選択し、データ・セーフの有効化をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/e19568a1-98ff-7ef4-60e0-91a88aa16bc8.png)

- 有効化が完了すると、セキュリティ・センターと表示が変更される。また、下部に各データベースの登録ウィザードのメニューが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/0f90ea50-9e02-764e-0fe6-1c0b33227ec8.png)

- 自分がテナンシー管理者かどうかを確認する。ココをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/54205fe5-28ee-42b8-608d-0488fbd74d6e.png)

- 下記の表示が表示されている場合は、Data Safeに必要な権限が付与されているので、以下からのデータベースの登録が可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/77fdbb39-dfbb-e76e-22ec-312f4af86446.png)

## Autonomous Databaseの登録
- Data SafeのコンソールからAutonomous Databaseのウィザードを開始をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/ef6a5168-16ec-5e19-2ec1-e7902cf28b75.png)


- ターゲットのADBを選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/220d021e-144e-30ab-8517-6da3d5a9e7a7.png)

- プライベートIPのADBの場合は、Data Safeからの接続を受けるためのプライベート・エンドポイントを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/9dd0da21-334f-192a-cdca-8dfd6ff8adb1.png)

- セキュリティ・リストまたはネットワーク・セキュリティ・グループに追加する。Ingress/Egressルールは指定先に自動追加される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/79540040-8502-848a-65f4-ac6d11bee422.png)


- 接続設定の完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/41e7f5b2-7dc8-434d-81b0-47c25d85b7e1.png)


- Data Safeに登録後、下記のようにセキュリティ・アセスメントとユーザー・アセスメントの実行結果が示されていれば登録完了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/afd6500f-f0c3-931d-fae9-0080ae51c893.png)


今回は、テナント管理者で実施したので、必要な権限はすべて保有しているので、とくにポリシーなどを割り当てる必要ありませんでした。
実際の運用では、ユーザーやグループごとに機能や参照できる範囲を指定するなどのアクセス制御が必要かと思います。詳しくは[ドキュメント](https://docs.oracle.com/en/cloud/paas/data-safe/admds/users-and-groups-oracle-data-safe.html#GUID-E3A78363-3E5E-4D92-8371-51C71A48A4AC)を参照ください。
【Windows】Windows Store が動作しないときに試すべきこと

#はじめに
Windows10(20H2) の Windows Store はアプリインストールに Microsoft アカウントが必須となっていますが、アカウントのサインイン時に「インターネットが必要です。エラーコード:0x800704cf」というエラーが発生することが多々あります。

本記事はこの不具合への対処方法です。

#原因
エラーコード:0x800704cf はインターネット接続ができない場合の一般的なエラーです。原因はネットワークにありますが、このエラーコードだけでは原因を特定できません。よくある事態としてはブラウザからは Yahoo! 等のサイトを見ることができるのに Windows Store ではエラーなることがあります。

#解決方法
##Windows Storeのリセット（WSReset.exe）
Win+Rで「ファイル名を指定して実行」を開き、名前に "WSReset.exe" を入力して実行します。

この方法は Microsoft 公式のトラブルシュートにも書かれている内容ですが、これで解決すれば楽なものです。多くの場合これだけでは解決しません・・・。

##ネットワークアダプタのTCP/IPv6をOFFにする
コントロールパネルから「ネットワークとインターネット＞ネットワーク接続」を選択して、ネットワークアダプタの一覧を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21973/cf33c278-86fa-322c-3b93-90ec6fcd9837.png)

インターネット接続に使用しているアダプタを右クリックして「プロパティ」を開き、「インターネット プロトコルバージョン6(TCP/IPv6)」のチェックを外して無効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21973/b0787cc3-8f3f-cd8b-9081-6015314235e6.png)

設定ができたら念のため、WSReset.exe を実行して、Windows Store を開きましょう。今度は Microsoft アカウントでサインインできたはずです。

#最後に
これって Windows Store がIPv6に対応していないように見えるのでバグなのでしょうか？それともネットワーク環境との相性？いずれにしてもブラウザは正常動作するし、デフォルトのネットワークアダプタ設定ではIPv6は有効なので、ここに問題があると気づかない可能性が高くはまりやすいと思いました。ネットにも情報が少ないので記事にしましたが、解決につながれば幸いです。
Qiita初投稿です

# 概要
2021年9月から、独立系SIer→Webサービスを自社開発している企業にキャリアチェンジしたエンジニアです。
今までいわゆるROM専でさんざんお世話になってきたQiitaですが、
自身のスキルアップのため、得たノウハウを自身へ定着させるため、また誰かの役に立てばいいなという思いで
これからアウトプットしていければと思っています。

この投稿は初回ということでその決意表明となります。

# なぜアウトプットするのか？
・とにかく継続的に書くことで文章力、表現力、精度が上がるはず
・アウトプットのスピード向上
・未来の自分への資産
・技術コミュニティへの貢献
・所属組織への貢献

と、様々理由がありますが、ともかく日々インプットしたものを定期的にアウトプットすることで自身の糧にしていきたいと思っています。
拙い文章になることが多いとは思いますが、見かけることがあればよろしくお願いします。
SQL ServerのDrop Databaseでデータベースを削除する

Drop Databaseで削除するとデータベースが使用中のため削除できない時があります。
Management StudioのGUIから削除する時には切断にチェックを入れて削除するとできます.

しかし、Azure Data Studioだとその設定が見当たらなかったため、クエリことにしました。
以下の方法で実施

```
ALTER DATABASE [DATABASENAME] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
DROP DATABASE [DATABASENAME]
```
Glue Jobでpandas has no attribute 'json_normalize'に四苦八苦した

# 前提(長くて説明下手です)
<!-- 発端や概要を記載 -->
ETL処理をGlue jobを用いて、自動化しようとしたときに

```python
import pandas
from pandas.io.json  import json_normalize
df = pandas.json_normalize()
#~ 中略 ~ 
```
というように処理をGlueスクリプトに書いていたが、jobを実行してみるとエラーログには何も書かれておらず、module 'pandas' has no attribute 'json_normalize'というログのみ書かれていた。

またjobのstatusは、「Succeeded」になっているが、肝心のcsvファイルはs3にアップロードされていない。
この時初めて知ったのだが、json_normalizeはpandasのversion >= '1.0.0'で実装されており、Glueでのpandasのversionを確認したところ、0.~であり、1.0.0より前のversionだった。

Glue スクリプトでは、pipを叩くことはできないので、外部モジュールとして「pandas >= 1.0.0」を取り入れる必要がある。
ここまでが前提のお話

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [setupスクリプトについて](#setupスクリプトについて)
1. [wheelファイルの作成](#wheelファイルの作成)
1. [ライブラリパスの通し方](#ライブラリパスの通し方)
1. [参考文献](#reference)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
#setupスクリプトについて
setup.pyファイルについて

```python
from setuptools import setup

NAME = 'test_wheel'
DESCRIPTION = 'for test'
REQUIRES_PYTHON = '~=3.6.0'
VERSION = '0.0.0'
REQUIRED = [#特別に必要なモジュールをversion指定でimportするため
    'pandas >= 1.1.0',
]

setup(
    name=NAME,#パッケージ名
    version=VERSION,
    #install_requires=["packageA", "packageB"],
    python_requires = REQUIRES_PYTHON,
    #extras_require={}
    #entry_points={}
)
```
今回はNAME,DESCRIPTION,REQUIRES_PYTHON,VERSION,REQUIREDでsetup内の引数を指定した。
ほかにも引数は多くあるみたいだけれど、今回は使わないので説明はしないです。
気になる方は、
[setupスクリプトを書く](https://docs.python.org/ja/3/distutils/setupscript.html)
を参照していただけたらなと思います。

<a id="#Chapter2"></a>
#wheelファイルの作成
setup.pyから.whlファイルの作成
先程、setup.pyファイルを作成したので、次にwheelファイルを作成します

```
$ python3 setup.py bdist_wheel
```
上記をターミナルで実行すると、以下のディレクトリ構造の形でファイルが生成されます

```
.
├── setup.py
├── build
│   └── build~
├── dist
│   └── {NAME}.whl
└── {NAME}.egg-info
    └── 5つくらいのファイル(今回は省略)
```
今回使うのは、distフォルダの
{NAME}.whl
になります

<a id="Chapter3"></a>
#ライブラリパスの通し方
先程、wheelファイルを作成しましたが、これをどうGlue jobに適用するの？という話をします
1.S3にwheelファイルをアップロードする
2.Glue job詳細のPythonライブラリパスの部分にwheelファイルのS3 URIを貼り付ける
上記の２点を行った後にGlueスクリプトを実行すれば良い。



<a id="#reference"></a>
# 参考文献
- [Glue Python Shell Job でWheelファイルを参照](https://dev.classmethod.jp/articles/yoshim-glue-wheel/)
- [AWS Glueで自作関数のimport](https://qiita.com/YujiHamada3/items/8d171c74f2f951d702b8) 
- [AWS official document about shell job](https://docs.aws.amazon.com/ja_jp/glue/latest/dg/add-job-python.html#create-python-extra-library)
Sync Gateway機能解説：同期〜リビジョン


Sync Gatewayによるリビジョンについて解説します。

## Couchbase Mobileにおけるドキュメント

Couchbase Mobileでは、ドキュメントは次のもので構成されます。

- ドキュメントID
- 現在のリビジョンID
- JSONドキュメント本体
- メタデータ

ドキュメントへの変更（作成と削除も含む）は、リビジョンとして記録されます。
ドキュメントのリビジョンはメタデータ内に含まれ、メタデータにはリビジョンの履歴がリビジョンツリーとして保存されます。

画像、オーディオ、その他のマルチメディアオブジェクトなどのバイナリデータは、ドキュメントとは別に、blob（または添付ファイル）と呼ばれるエンティティに保存されます。BLOBを変更しても、リビジョンは生成されません。

## リビジョン
Couchbase Mobileは、ドキュメントが作成、更新、または削除されるたびにリビジョンを作成します。
言い換えると、各ドキュメントは、一連のリビジョン（リビジョンツリー）で構成されます。

各リビジョンには、ドキュメントIDに加えて一意のリビジョンIDが与えられます。

リビジョンIDは、データの複製されたコピーに同時に変更が行われたときに発生する競合を解決するときに使用されます。

リビジョンIDは、2つの部分で構成されています。

- 世代ID(generation ID): これは、ドキュメントが存在するデータベースに固有の連続した自動インクリメント番号です。Couchbase Liteは単純な整数を生成します。Sync Gatewayは、より複雑で長いbase64値を生成します。

- ドキュメントの内容から派生したハッシュ

アプリケーションは、リビジョンIDの内容に依存する処理ロジックを持つべきではありません。

## リビジョンツリー
ドキュメントのリビジョンツリーは、ドキュメントの存続期間を通じて発生した各リビジョンを順番に記録します。
ツリーの先端であるリーフノードが、現在のリビジョン（ ドキュメントの最新バージョン）です。

リビジョンツリーの成長はそのままでは無制限であり、パフォーマンスレベルを維持するには、不要なリビジョンを削除する必要があります。このプロセスは、リビジョンツリープルーニング（剪定）といわれます。

### プルーニング(剪定)

リビジョンが追加されるたびに、不要なリビジョンを削除するプロセス（プルーニング）が自動的に実行されます。


### 構成
構成ファイルの`revs_limit`設定を使用して、保持されるリビジョンの数を変更できます。

たとえば、`revs_limit`が1000の場合、アルゴリズムは最も短い非トゥームストーンブランチの中の、最近の1000のリビジョンを保持し、他のリビジョンを削除します。

適切な`serevs_limit`の値は、Sync Gatewayで、自動競合解決が有効になっているかどうかによって異なります。自動競合解決が有効になっている場合、`revs_limit`を少なくし過ぎると、競合解決プロセスが適切に行われない可能性が生じます。


リビジョンプルーニングとデータベースサイズ管理の詳細については、以下のブログを参照することができます。

https://blog.couchbase.com/database-sizes-and-conflict-resolution/#pruning

## リビジョンキャッシュ

ドキュメントにアクセスするたびに、そのリビジョンツリーもキャッシュされます。

リビジョンキャッシュがいっぱいになると、Sync Gatewayは古いリビジョンを削除して、新しいリビジョン用のスペースを確保します。

### 構成
構成ファイル内の`rev_cache`設定を使用して、リビジョンキャッシュのサイズを制御できます。`rev_cache`設定には次のふたつがあります(エンタープライズエディションでのみ利用可能です)。

- `rev_cache.size`：メモリにキャッシュされるリビジョンの総数を指定します。この設定を調整することで、Sync Gatewayのメモリ消費量を調整できます。
- `rev_cache.shard_count`：リビジョンキャッシュが分割される数。根拠なく、デフォルト値から変更することは推奨されていません。



## 参考情報

https://docs.couchbase.com/sync-gateway/3.0/revisions.html