OmniAICreator/Qiita-1.07M huggingface dataset:
【Android】端末の位置を測定する

# はじめに
Android端末の位置(Azimuth/Pitch/Roll)を測定する方法について解説。
加速度センサーと地磁気センサーを組み合わせることで、端末の位置を測定。

https://developer.android.com/guide/topics/sensors/sensors_position?hl=ja

# Azimuth/Pitch/Rollについて

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694762/16b49850-86df-caf5-d699-d70e690e2f6e.png" width=50%>


上記の図の通り。地面と水平に、北向きに端末を置いた場合、Azimuth/Pitch/Rollともに0となる。

# コード

* センサーイベントリスナー(`SensorEventListener`)インターフェースを継承。
* センサーマネージャー(`sensorManager`)を定義し、`onResume`時にリスナーを登録、`onPause`時にリスナーを解除する。
* `onSensorChanged`メソッド内で端末の位置を測定。

```MainActivity.kt
class MainActivity : AppCompatActivity(), SensorEventListener {
	/** ViewBinding */
	private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

	/** センサーマネージャー */
	private val sensorManager by lazy { getSystemService(SENSOR_SERVICE) as SensorManager }

	private val accelerometerReading = FloatArray(3)
	private val magnetometerReading = FloatArray(3)

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(binding.root)
	}

	override fun onResume() {
		super.onResume()

		//加速度センサー
		val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
		if (accelerometer != null) sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)

		//地磁気センサー
		val magneticField = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
		if (magneticField != null) sensorManager.registerListener(this, magneticField, SensorManager.SENSOR_DELAY_NORMAL)
	}

	override fun onPause() {
		super.onPause()
		sensorManager.unregisterListener(this)
	}

	override fun onSensorChanged(event: SensorEvent) {
		//加速度センサー
		if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
			System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
		}

		//地磁気センサー
		if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
			System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
		}

		val rotationMatrix = FloatArray(9)
		val orientationAngles = FloatArray(3)

		SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading)
		SensorManager.getOrientation(rotationMatrix, orientationAngles)

		val azimuth = Math.toDegrees(orientationAngles[0].toDouble())
		val pitch = Math.toDegrees(orientationAngles[1].toDouble())
		val roll = Math.toDegrees(orientationAngles[2].toDouble())
		//TODO ここに処理を記載
	}

	override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
}
```
SQLでのselectを死ぬほど楽にする関数を作った。

## select、めんどくさいですよね?
あのプリペアドステートメントとか、苦しくて苦しくてやっていられるもんじゃあありません。
そこで、SQLでselectを死ぬほど楽にする関数を作りました。
一応、セキュリティーを考えているつもりですが、何か問題点があったら教えてください。

## コード
```php
function fetch_mysql_data($host, $dbname, $password, $table_name) {
    // MySQLへの接続
    $pdo = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8", $dbname, $password);
    // エラー時の例外処理
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    // データの取得
    $stmt = $pdo->prepare("SELECT * FROM {$table_name}");
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    return $result;
}

```

## デリケート
以前、この種の関数をqiitaに投稿した際、データベースパスワードなどのセキュアな情報をいちいち持ち運ぶのはどうか、という意見をいただいたことがあります。
その際は、pdoオブジェクトを自分で作ってしまい、それを組み込むように適宜変更してください。
（丸投げ）

## 使い方
```php
$host = "localhost";
$dbname = "mydatabase";
$password = "mypassword";
$table_name = "mytable";

$data = fetch_mysql_data($host, $dbname, $password, $table_name);

print_r($data);
```
こんな感じにしてください。


## 終わりに
問題点・アドバイスがあれば、ぜひ教えていただけると助かります。
Pythonのリストを見やすく改行する方法

最終更新日：2023/03/01

# はじめに
Pythonのリストを見やすく改行する方法を書きます．
具体的には，stateというリストを持つときに，出力例のように出力する方法です．

入力例
state=['My', 'name', 'is', 'john']

出力例
My
name
is
john


# 経緯
Pythonのモジュール検索パスを確認するときに見づらかったので

# 開発環境
Mac OS Montery v.12.6
Python 3.9.11



# 目次
<!-- タイトルとアンカー名を編集 -->
1. [実際のコード](#実際のコード)
1. [細かいところ](#細かいところ)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 実際のコード
```python:test.py
def main():
    state = ['My', 'name', 'is', 'john']
    print('従来の表示')
    print(state)
    print('改行した表示')
    print(*state, sep='\n')

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

```console:出力
従来の表示
['My', 'name', 'is', 'john']
改行した表示
My
name
is
john
```
<a id="#Chapter2"></a>
# 細かいところ
リストに*をつけることによって，[]と,を消す．
sepで半角スペースの代わりに\nの改行コードを入れる

そのおかげで，本来半角スペースが与えられるところに改行が行われる

```python:
print(*state)
print(*state, sep='\n')
```

```
My name is john
My
name
is
john
```
Dockerについて

# 「Docker」とは
仮想化ソフトウェア、非常に動作が軽量なコンテナ型のアプリケーション実行環境のこと

# Dockerの仕組み
### コンテナ型仮想化（Docker）
コンテナはホストOSから見ると単一のプロセスとして扱われ、カーネル部分をホストOSと共有するため、リソース使用量が非常に少ない
仮想的な動作環境をホスト型仮想化より少ないコンピュータリソースで実現する環境を構築する

# メリット

## 1. アプリケーションを少ないリソースで効率良く実行できる
## 2. 「Infrastructure as Code」によってインフラ環境のバージョン管理ができる
「Infrastructure as Code」‥インフラ環境の状態をソースコードとして管理し、その内容を自動的にサーバーへ設定する仕組み
Dockerはイメージの構成を「Dockerfile」、コンテナの構成を「docker-compose.yml」というテキストファイルに記述できる ため、gitなどのバージョン管理システムでインフラ環境の状態を記録することができる。
## 3. 「Immutable Infrastructure」を実現することができる
「Immutable Infrastructure」‥アプリの実行環境を使い捨てとし、環境を都度作り直すことにより構成変更を行う方法
 いつでも古い状態のサーバーを廃棄して「パッチのつぎはぎがない綺麗な状態」のサーバーを再構築することができます。

# imageとコンテナ
### Dockerイメージ
「Dockerイメージ」とはOSやアプリケーションを含んだテンプレートのこと。

### コンテナ
「Dockerコンテナ」は元となるDockerイメージから生成された仮想サーバーです。
Dockerコンテナはホストマシン上の一つの プロセス として実行されます。

https://qiita.com/odekekepeanuts/items/4604f530fe25786240db


# Docker composeとは
Docker Composeとは複数のDockerコンテナを定義し実行するDockerアプリケーションのためのツールです。
つDockerコンテナを楽にまとめて起動したい時に使う
OpenCVのimshowでエラーが出る時の対処法 [WSL2 / Ubuntu]

# 概要
PythonのOpenCVで`imshow`を実行したときにエラーが出る場合の対処法です。

## 環境
- WSL2
    - Ubuntu 20.04
- Python 3.8.10
- pip 23.0.1


## エラー内容

```
cv2.error: OpenCV(4.6.0) /io/opencv/modules/highgui/src/window.cpp:1267: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Cocoa support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function 'cvShowImage'
```


# 解決策
エラー文に表示されているパッケージのインストールとOpenCVの再インストールを行います。

## 1. OpenCVのアンインストール
```bash
pip3 uninstall opencv-python
```

## 2. `libgtk2.0-dev`と`pkg-config`のインストール
```bash
sudo apt update
sudo apt install -y libgtk2.0-dev pkg-config
```

## 3. OpenCVの再インストール
```bash
pip3 install opencv-python
```


再インストールが完了すると、このように`imshow`が実行できるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468561/f09d99d6-0dec-0aaa-375b-56fd004d1ab4.png)


# 参考
> Btw, sudo apt install libgtk2.0-dev pkg-config and then re-compiling OpenCV fixed this.

[GUI apps built with OpenCV from WSL vcpkg don't work #4104](https://github.com/microsoft/vcpkg/issues/4104#issuecomment-414515989)
Unixシェルで使用する"$"とは？

ふと、いつも目にする`$`はどういう意味があるんだろうと思って調べてみた。

説明としては、`Bash`や他の`Unixシェル`で、変数を表すために使用される特殊な文字です。
例えば、変数`FOO`に値を設定して、その値を使用する場合、以下のように記述します。

```shell
FOO="bar"
echo $FOO
```
この場合、変数`FOO`には`bar`が設定されており、`echo`コマンドを使用して変数の値を表示するために、`$`を使用して`$FOO`という変数名を指定しています。

`$`は、変数を展開するためにも使用されます。
例えば、以下のようにコマンドを記述すると、`$HOME`という変数がユーザーのホームディレクトリのパスに展開されます。

```shell
ls $HOME
```
また、`$`は、シェルの環境変数を参照するためにも使用されます。
例えば、`$PATH`変数は、実行可能ファイルが格納されているディレクトリのリストを含んでいます。

#### また、他にもこんな意味がある
- `$*：`コマンドライン引数全体を一つの文字列として扱います。
- `$@：`コマンドライン引数を別々の引数として扱います。
- `$?:`直前に実行したコマンドの終了ステータスを表します。0以外の場合はエラーが発生。
- `$!：`直前にバックグラウンドで実行されたジョブのプロセスIDを表します。
- `${VAR:-default}：`変数VARの値が未定義または空である場合、デフォルト値を使用します。
- `${VAR#pattern}：`変数VARの値から、先頭にマッチするpatternを削除します。
これらの例は、Bashや他のUnixシェルで一般的に使用される`$`の使用例の一部です。
そのため、シェルスクリプトやコマンドラインにおいて、`$`は多機能な特殊文字であり、シェルの機能を活用するために幅広く使用されています。

#### $*：コマンドライン引数全体を一つの文字列として扱います。
```shell
#!/bin/sh

# 引数を一つの文字列として扱う
echo "引数の数：$#"
echo "引数の内容：$*"
```
このスクリプトは、`$#`変数でコマンドライン引数の数を取得し、`$*`変数で全ての引数を一つの文字列として取得します。
例えば、以下のようにこのスクリプトを実行すると、引数の数と内容が出力されます。

```shell
$ sh example.sh foo bar baz
引数の数：3
引数の内容：foo bar baz
```

#### $@：コマンドライン引数を別々の引数として扱います。
```shell
#!/bin/sh

# 引数を別々の引数として扱う
for arg in "$@"
do
  echo "$arg"
done
```
このスクリプトは、`$@`変数を使用して、全てのコマンドライン引数を別々の引数として取得し、forループを使用して各引数を順番に出力します。例えば、以下のようにこのスクリプトを実行すると、引数が順番に出力されます。

```shell
$ sh example.sh foo bar baz
foo
bar
baz
```

#### $?: 直前に実行したコマンドの終了ステータスを表します。0以外の場合はエラーが発生。
```shell
#!/bin/sh

# 直前のコマンドの終了ステータスを出力する
ls /nonexistent
echo "直前のコマンドの終了ステータス：$?"
```
このスクリプトは、存在しないディレクトリに対してlsコマンドを実行することでエラーを発生させ、`$?`変数で直前のコマンドの終了ステータスを取得します。例えば、以下のようにこのスクリプトを実行すると、直前のコマンドの終了ステータスが出力されます。

```shell
$ sh example.sh
ls: cannot access '/nonexistent': No such file or directory
直前のコマンドの終了ステータス：2
```

#### $!：直前にバックグラウンドで実行されたジョブのプロセスIDを表します。
```shell
#!/bin/sh

# バックグラウンドでジョブを実行する
sleep 10 &
pid=$!
echo "ジョブのプロセスID：$pid"
```
このスクリプトでは、`sleep`コマンドをバックグラウンドで実行し、そのプロセスIDを`$!`変数で取得しています。
プロセスIDを変数`pid`に保存し、echoコマンドで出力しています。このスクリプトを実行すると、ジョブのプロセスIDが出力されます。

```shell
$ sh example.sh
ジョブのプロセスID：12345
```
この例では、`sleep`コマンドをバックグラウンドで実行することで、プロンプトが即座に戻ってきます。
その後、`$!`変数を使用して、バックグラウンドで実行されたジョブのプロセスIDを取得しています。
このように、`$`!変数を使用することで、ジョブのプロセスIDを簡単に取得することができます。

https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html
Espressif ESP32シリーズ開発環境 esp-idf を使い始める

## はじめに

低価格で高機能なEspressif社のESP32シリーズのチップ、いくつかの開発環境があるが、やはり骨までしゃぶりたかったらesp-idfを使うのが良さそうだ。この記事は、私が備忘録として残しておくためを第1目的として書いておく。似たような環境の人は参考になるかもしれない。

## 前提環境

* macOS 13.6
* Homebrew
* ESP32を搭載した各種ボードたち

## esp-idfの導入

とりあえず~/espディレクトリに導入してるけど、これは他でもよい。他にEspressifのリポジトリをクローンするつもりならば、Espressifのリポジトリをまとめておいておくためのディレクトリがあっても良いかもしれないな。

```
$ mkdir ~/esp
$ cd ~/esp
$ git clone --recursive https://github.com/espressif/esp-idf
$ cd esp-idf
$ ./install.sh
$ . ./export.sh
```

'master'ブランチをクローンしてるけど、製品版のビルドならば、5.0ブランチでなければならないかもしれない。4.4ブランチでは以下のコマンドたちは使えないかもしれない。

## プロジェクトの開始

今どきは下記のようにするのが良さそう。

* create-projectコマンドを使ってプロジェクトを作成
* set-targetコマンドを使ってチップに応じた環境を作成
* ごそごそ開発
* ビルドと書き込み

ということで、esp32-s3チップの場合、こんな感じかな？

```
$ cd ~/src # プロジェクトたちを置いてあるディレクトリね
$ idf.py create-project testproj
$ cd testproj
$ idf.py set-target esp32s3
$ emacs main/testproj.c
$ idf.py flash monitor
```

気がついたことは下記。

* zshを使っているのだが、コマンドはタブキーで保管できる。
* 'create-project'コマンドは、プロジェクト名のサブディレクトリを作ってくれる。
* ターゲットのチップ名も保管してくれるので大変便利。
* 'compile_commands.json'も作ってくれるので、lspとかを設定しているEmacsならばCのソースコードを書いているときにちゃんと保管してくれると思う。
* シリアル通信の速度はおそらく115200bps

他にもコンポーネントをどう作るかとか、自動的にダウンロードするのにはどうするかとか、いろいろ知っといたほうが良いことはあるっぽいが、そのうち書き足そう。

Happy hacking!
Golandのdocker環境利用によるデバッグ

**やりたいこと**
WindowsにインストールされているGoland(jetbrains製IDE)でdocker環境を利用してデバッグします。
⇒Linux環境でないと動かない場合に便利

**Windows環境でテスト**
都合によりWindows環境では失敗…
![222.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/c761f24c-5e81-8293-cd1d-61b6bde9eae4.png)

**docker環境でテスト**
「実行構成の編集」選択
![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/884bb13e-98c4-0e45-e9c1-d621364ba24c.png)

実行場所：「docker」選択
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/d115607b-f37b-4d8c-77d7-4af999187ef4.png)

「次へ」選択（実行オプション-vでWindowsのgo packageをコンテナにマウントさせるとコンテナ内でgo downloadが行われなくてもパッケージを利用できる）
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/5834156c-ad33-6ffe-d566-24c436870898.png)

「次へ」選択
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/2a444a1b-a102-9aa4-c9da-b7fc286e2fd1.png)

「適用」⇒「リモートターゲット上にビルドする」
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/5de1ee6e-b366-bbad-c475-e68ae0a5807f.png)


テストが通る🙌
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3114591/24c406ba-a132-765b-e896-70cba3f6a8d1.png)
初めてフロントエンドとバックエンドの連携（Flask+React）をやってみた時の疑問と対処法

jQuery全盛期にフロントエンドエンジニアをやっていたんですが、JavaScriptにめっきり触れなくなって最近はPythonで小物を作っているという私がやってみた時の疑問です。

# ディレクトリ構成はどうすれば良いの?

とりあえず分けておくのが良さそうなので、プロジェクトフォルダの下にfrontendとbackendを作りました。

- my_project
    - frontend
    - backend

プロジェクトフォルダの下にfrontendという名前でreactのアプリを作成します。
```
npx create-react-app frontend
```

バックエンド側はbackendフォルダの下に…

# gitのリポジトリは分けたほうが良いの?

[Render.com](https://render.com/)を使いたくて、リポジトリは2つ必要？と思いましたが、一つのリポジトリで問題ありませんでした。

フロントエンド
- name:my_project
- TYPE:Static Site
- Root Directory:frontend

バックエンド
- name:my_project-backend
- TYPE:Web Service
- Root Directory:backend

という感じで2つのサイトを登録してRoot Directoryにそれぞれのディレクトリを指定することで、GitHubの一つのリポジトリを参照させることができました。

# ポート問題
Flaskのデフォルトポートは5000番ですが、MacのAirPlay等の別サービスが既に使用していると、`OSError: [Errno 98] Address already in use`のようなエラーが発生します。Flaskで使用するポートは下記のように指定できます。

```Python:Python
if __name__ == '__main__':
    app.run(debug=True, host='localhost', port=8000)
```

# いまどきの非同期通信はどうやって書くの？
fetchを使います。具体的な書き方は後述。
[Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)

# CORS
CORSの設定なしでフロントエンドからバックエンドにアクセスしようとすると、下記のようなエラーが表示されてしまいます。

> Access to fetch at 'http://localhost:8000/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

CORSは無闇に別のオリジンからの要求を受け入れないようにするため、アクセスを許可しても良いオリジンを制限するものです。

```
pip install Flask-Cors
```
でインストールし、制限なしでとりあえず試すだけの場合は、
```Python:Python
from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

CORS(app)
```
としてからPythonのコードを書いていきます。

参考
[Flask-CORS](https://flask-cors.readthedocs.io/en/latest/)

フロントエンド側は、先ほどのfetchで投げる際に、`mode: "cors"`を指定します。（下記参照）

# データの受け渡し部分はどうやって書くの？

やっとここまで来ました。

```JavaScript:frontend
const onClickHandler = async () => {
    const response = await fetch(URL, {
        mode: "cors",
        method: "post",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({name: name}) // 1 フロントエンドから送信するデータ
    })
    const json = await response.json() // 3 バックエンドから受信したデータ
    console.log(json["greeting"])
}
```

```Python:backend
from flask import Flask, request

@app.route('/', methods=['POST'])
def index():
    data = request.json.get('name') # 2 フロントエンドから受信したデータ
    return {"greeting": "Hello! {}".format(name)}
```

# 以上です

Reactの勉強をするのが目的だったのですが、ここまで来るだけで大変でした。
引き続き頑張ります。
多項式x**2023-1を多項式x**4+x**3+x**2+x**1+1で割った余り「2023京都大学理系第1問 問2」<2>をwolframalphaとsympyでやってみたい。

できました。
以下の<1>を参照して下さい。申し訳ありませんでした。

https://qiita.com/mrrclb48z/items/6ebdc201cf511bf7fbb2



~~できませんでした。~~


# wolframで、できませんでした。

https://www.wolframalpha.com/input?i=%28x**2023-1%29+mod++%28x**4%2Bx**3%2Bx**2%2Bx**1%2B1%29&lang=ja

標準の計算時間制限を超えました...

# wolframで、解より?できませんでした。
駿台様とよく似ていますか？
https://www2.sundai.ac.jp/sokuhou/2023/kyd1_suu1_1.pdf

https://www.wolframalpha.com/input?i=%28%28x**2023-1%29-%28x**3-1%29%29+++%2F++%28x**4%2Bx**3%2Bx**2%2Bx**1%2B1%29&lang=ja

https://www.wolframalpha.com/input?i=%28%28x**2023-1%29-%28x**3-1%29%29+++mod+++%28x**4%2Bx**3%2Bx**2%2Bx**1%2B1%29&lang=ja

# sympyで、できませんでした。
```python
from sympy import *
var('x')
print("#",Mod(x**2023-1,x**4+x**3+x**2+x**1+1))
# Mod(x**2023 - 1, x**4 + x**3 + x**2 + x + 1)
```
real,positiveで
```python
from sympy import *
x=Symbol('x',real=True,positive=True)
print("#",Mod(x**2023-1,x**4+x**3+x**2+x**1+1))
```
終了しませんでした。

# 参考
#### wolframalphaで

https://www.wolframalpha.com/input?i=%28x%2B5%29+mod+%28x%2B3%29++&lang=ja

xを正と仮定した場合の別の形
2

#### sympyで

```python
from sympy import *
x=Symbol('x',real=True,positive=True)
print("#",Mod(x+5,x+3))
# 2
```
abstract class

abstract classはnewできない
subclass of abstract classはnewできる

```java
package sampleproject;

import java.util.List;

import java.util.function.Function;
import java.util.function.ToIntFunction;
abstract class AbstractClass {
    
}
class SubClass extends AbstractClass {
    
}
class ConcreteClass {
    
}
public class SampleProject {
    public static void main(String[] args) {
        AbstractClass ab = new AbstractClass();
        ConcreteClass ct = new ConcreteClass();
        SubClass sc = new SubClass();
    }
}
```
```
C:\Users\java\Documents\NetBeansProjects\SampleProject\src\sampleproject\SampleProject.java:19: エラー: AbstractTestはabstractです。インスタンスを生成することはできません
        AbstractTest ab = new AbstractTest();
```
GitHub PackagesにGradle+Kotlin MultiplatformでMaven Packagesを公開する

GitHub Actionsを使うのが楽ですが、他の方法でも可能です。毎回調べているので書いておきます。
なお今回はKotlin DSLでGradleのビルドスクリプトを書きます。

## チェックリスト
- [ ] `publishing`にGitHub Packagesのリポジトリが追加されているか
- [ ] 認証情報が追加されているか
- [ ] `gradlew`のパーミッションが実行可能か
- [ ] `build` `publishToMavenLocal`が正常に実行できるか
### 環境
- Windows 10
- Intellij IDEA
- GitHub EducationとPro
- Java 11
- Kotlin 1.8.10
- Gradle 7.4.2
- Gradle Kotlin DSL

## 公開する

### GitHub Packagesのリポジトリを追加する。
公開先のリポジトリを追加します。

#### プラグインの追加
Gradleのプラグインとして、`maven-publish`が必要になります。
```kotlin:build.gradle.kts
plugins {
    kotlin("multiplatform") version "1.8.10"
    id("maven-publish")
}
```
#### リポジトリの追加
`url`の部分は`https://maven.pkg.github.com/{OWNER}/{REPOSITORY}`です。
これを間違えると消せなくなるらしいのでご注意を…
`credentials`の部分は環境によって変えてください。[^1]
下の例はGitHub Actionsで実行する場合、そのまま使えます。
```kotlin:build.gradle.kts
plugins {
    //さっきの
}

kotlin{
    //省略
}

android{
    //省略
}

publishing{
    repositories{
        maven{
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/multim-dev/emoji-kt")
            credentials {
                username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
                password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
            }
        }
    }
}
```

### CIを構成する
環境変数にユーザー名とトークンを追加するだけです。
#### GitHub Actionsの場合、
`Publish Java Package with Gradle`というものを使うことで数クリックで追加できます。
Java 11の場合本当に何もしなくても使えるので便利です。

#### その他 TeamCityとかの場合
~~なぜTeamCityなのか、私が使っているからです。~~
上の例をそのまま使った場合
`env.USERNAME` にユーザー名
`env.TOKEN` にトークン
を追加します。
要は環境変数に追加するだけです。

### gradlewが実行できるようにする
これのためにこの記事を書いたと言ったら過言
これを忘れるとGitHub Actionsでコケます。普段はIDEの機能を使うので忘れがちです。
`git update-index --chmod=+x gradlew`

ここでGitHubにプッシュ

### build publishToMavenLocalが正常か
変なことしてなければ大抵は動きますが、マルチプロジェクト構成だったりすると失敗することもあるのでとりあえずMaven Localにpublishしてみます。
Maven Localは初期だと `~/.m2/repository`にあります。
#### 確認すること
:::note warn
注意点
androidに関しては単に追加するだけでは作成してくれません[^2]
:::


- [ ] ビルドが正常に通るか、作成したいものが作成されているか(当たり前ですが…)
- [ ] `{group}/{rootProject.name}`があるか
- [ ] `{group}/{rootProject.name}-jvm`があるか
- [ ] `{group}/{rootProject.name}-js`があるか
- [ ] その他構成したプラットフォームのものがあるか
- [ ] local.propertiesや他の設定ファイルがなくても実行できるか

:::note warn
注意点
Kotlin Multiplatformの場合、publishするものの設定をしなくてもデフォルトで設定されています。
:::

### 公開する
#### GitHub Actionsの場合
GitHub Actionsで`Publish Java Package with Gradle`をそのまま使用した場合、リリースするとActionsが実行されます。テスト時はversionを`SNAPSHOT`でやるといいと思います。

#### その他
`gradlew build publish`
が実行されるように設定してください

## 最後に
使用するときも認証情報を追加する必要があります。面倒ですがまぁ…
これが今回作ったやつ[kmp-logger](https://github.com/multim-dev/kmp-logger)
 Kotlin Multiplatformでslf4jとAndroidのloggerとconsole.logを使えます。


[^1]: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry
[^2]: https://kotlinlang.org/docs/multiplatform-publish-lib.html#publish-an-android-library
SwiftUIの「ビューモディファイア (View Modifier)」でif文を使う方法

ifメソッドはSwiftUIのビューモディファイアではないため、SwiftUIビューのインスタンスには直接使用できません。SwiftUIビューで使用できるifヘルパーメソッドを定義する必要があります。

以下は、この目的のために拡張を使用して定義されたifヘルパーメソッドの例です。

```swift
extension View {
    func `if`<Content: View>(_ condition: Bool, content: (Self) -> Content) -> some View {
        if condition {
            return AnyView(content(self))
        } else {
            return AnyView(self)
        }
    }
}

````
これを使用するには、以下のようにビューを修正してください。


```swift
Text("Hoge")
    .background(.gray)
    .if(2 == 3) {
        $0.cornerRadius(50)
    }


```



ここで、ビューモディファイアのチェーンにifメソッドを追加し、条件とビュー修飾子のクロージャーを渡すことができます。注意点として、if文で使用できるのは条件分岐だけであり、関数呼び出しを含む複数行の式は使用できません。


このコードは、SwiftUIのビューモディファイアである「if」を定義しています。このビューモディファイアを使用すると、条件によってビューに修飾子を追加したり、削除したりすることができます。

このビューモディファイアの定義は、ジェネリックを使用して構築されています。ジェネリックは、様々なビュータイプに対応するために使用されています。条件が true の場合、ビューに content() で指定した修飾子が追加されます。条件が false の場合、元のビューが返されます。

このコードを使用すると、if文でパラメーターを操作するようにビューモディファイアを使うことができます。例えば、.if(2 == 3) { $0.cornerRadius(50) } のように使用すると、条件が false の場合は、ビューの修飾子は何も変更されません。条件が true の場合、ビューに指定された修飾子が追加されます。
Ubuntu22.04初期設定覚書

Ubuntuのインストールからサーバ向けに使える状態まで持っていく覚書。
インストールとsshとufwを設定する。

### ubuntuのインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522264/04d18821-e735-a36b-cfee-505eb4dcad90.png)

Try or Install Ubuntuを選択。
その後インストールを選択。
言語は日本語、キーボード配列はUSを選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522264/abd919fd-ab1f-a157-a527-c68ce408394a.png)
今回デスクトップ環境は一応入れるがオフィス等はいらないので最小インストールを選択。
アップデートとドライバは入れる。
ディスクを削除してUbuntuをインストールを選択。
タイムゾーンはTokyo
ホスト名ユーザ名は適宜。

これでインストールが走る。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522264/51b7f016-c0fb-1efb-80a3-0c2ef8775a67.png)

起動後初回設定は全て次へで問題ない内容。

### sshの設定
起動したらまずsshを入れる。
`sudo apt install openssh-server -y`
入ったら`arp -a`か`ifconfig`でipを調べて別PCからアクセス。
`ssh username@192.168.0.0 -p 22`
何も設定していないのでパスワード認証で入れる。

入れたら色々アップデートする。
`sudo apt update -y && sudo apt upgrade -y`

色々いじるためにvimをインストール
`sudo apt install vim -y`

ssh公開鍵の設定
`touch .ssh/authorized_keys`
`vim .ssh/authorized_keys`
公開鍵を入れる、sshd_confやufwをいじる前に必ずやる

sshd.confのバックアップ
`sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config_original`

sshのセキュリティ設定
`sudo vim /etc/ssh/sshd_config`
今回はポートの変更、rootログインの禁止、パスワードログインの禁止
```
$ diff /etc/ssh/sshd_config_original /etc/ssh/sshd_config
14c14
< #Port 22
---
> Port {{任意のポート}}
33c33
< #PermitRootLogin prohibit-password
---
> PermitRootLogin no
57c57
< #PasswordAuthentication yes
---
> PasswordAuthentication no
```

sshdデーモンを再起動して設定を反映させる
`sudo systemctl restart ssh`

入り直してみる
`exit`
`ssh username@192.168.0.0 -p {{sshd_confに設定したポート}}`
新しいポートで公開鍵認証で入れれば設定は効いている

### ufwの設定

今回は初期インストールの時点でufwは入っていた。
入っていないなら`sudo apt install ufw -y`

sshのポートを開ける
`sudo ufw allow {{sshd_confに設定したポート}}`
有効化
`sudo ufw enable`
再起動
`sudo ufw reload`

この状態で一旦exitしてからsshが繋がることが確認できればディスプレイとHIDは外して問題ない

初期設定としてはこれで問題ない。お好みでapacheなり入れればサーバとして機能する。
[Route53] フェイルオーバールーティングにてフェイルオーバーとフェイルバックが発生したことを検知する

# この記事について

Route53にはフェイルオーバールーティングというルーティングポリシーがあります。

フェイルオーバールーティングには以下の３つの要素があります。

- プライマリレコード
- セカンダリレコード
- ヘルスチェック

ヘルスチェックではチェック対象のURL(もしくはIPアドレス)を指定して、HTTP/HTTPSリクエストを送信します。ステータスコードが200であればヘルスチェックが正常であると判定されます。それ以外のステータスコードの場合は異常と判定されます。

フェイルオーバールーティングではヘルスチェックを定期的に行い、ヘルスチェックが正常である場合はプライマリレコードのDNSクエリを返します。ヘルスチェックが異常の場合はセカンダリレコードのDNSクエリを返します。

プライマリレコードからセカンダリレコードへの切り替わりことをフェイルオーバーと呼びます。
ヘルスチェックが異常から正常に戻った際は、セカンダリレコードからプライマリレコードに切り替わります。これをフェイルバックと呼びます。


この記事ではこれらフェイルオーバーとフェイルバックが発生したことをCloudWatch Alarmで検知する方法について記載します。

# Rout53 ヘルスチェックのモニタリング

Route53にはヘルスチェックステータスというメトリクスがあります。
このメトリクスが取りうる値は0か１のみであり、ヘルスチェックが正常である場合は1、異常である場合は0となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/7d0b0e70-c040-66a4-dd3b-96fa670619f7.png)

このヘルスチェックステータスを利用して、CloudWatch Alarmを設定することで、フェイルオーバーとフェイルバックを検知することが可能です。

# CloudWatch Alarmの設定

フェイルオーバー検知用のアラームとフェイルバック検知用のアラームを２つ作成します。

## フェイルオーバー検知用のアラーム

CloudWatch Alarmの設定には"メトリクスと条件"と"アクション"があります。
"メトリクスと条件"で対象のメトリクスと閾値を定義して、"アクション"で閾値を超過した際のアクションを定義します。

"メトリクスと条件"はフェイルオーバーでもフェイルバックでも同じになります。
"アクション"のアラーム条件のトリガーがフェイルオーバーとフェイルバックでは異なります。

また私はSNSトピックに通知を送信して、それをNewRelicに送信することでNewRelicでフェイルオーバーとフェイルバックの監視をできるようにしております。この記事ではアクションにおける通知先については言及しないため、各々の好みに合わせてカスタマイズいただければ良いかと思います。


### メトリクスと条件

| 項目 | 値 | 備考 |
|:-:|:-:|:-:|
| メトリクス(名前空間 メトリクス名) | AWS/Route53 HealthCheckStatus  |   |
| HealthCheckId | 作成したヘルスチェックのHealthCheckId |  |
| 統計 | 最小 | 
| 条件 しきい値の種類 | 静的 |   |
| 条件 アラーム条件 | HealthCheckStatusが1よりも低い |  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/3ef0b4f2-08df-e54b-9d8d-8f93240622a3.png)

### アクション

| 項目 | 値 | 備考 |
|:-:|:-:|:-:|
| アラーム条件のトリガー | アラーム状態 | |
| 次のSNSトピックに通知を送信 | 任意 | |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/e08fc35a-e0b7-5655-b3da-1a91bca1ee06.png)


## フェイルバック検知用のアラーム

### メトリクスと条件


| 項目 | 値 | 備考 |
|:-:|:-:|:-:|
| メトリクス(名前空間 メトリクス名) | AWS/Route53 HealthCheckStatus  |   |
| HealthCheckId | 作成したヘルスチェックのHealthCheckId |  |
| 統計 | 最小 | 
| 条件 しきい値の種類 | 静的 |   |
| 条件 アラーム条件 | HealthCheckStatusが1よりも低い |  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/3ef0b4f2-08df-e54b-9d8d-8f93240622a3.png)

### アクション


| 項目 | 値 | 備考 |
|:-:|:-:|:-:|
| アラーム条件のトリガー | OK | |
| 次のSNSトピックに通知を送信 | 任意 | |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435280/f246b8a0-dc3c-30dd-ff83-16171d5f7afe.png)

## 通知内容

CloudWatch AlarmからSNSトピックへの通知(JSON形式)の中に`Message.AlarmName`があります。このキーにはCloudWatch Alarmのアラーム名が入りますので、これでフィルタリングすればフェイルオーバとフェイルバックの発生を監視ツール(NewRelicやDatadog)で検知することが可能です。


# 参考

[フェイルオーバールーティング](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/routing-policy-failover.html)
[CloudWatch を使用したヘルスチェックのモニタリング](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/monitoring-health-checks.html)
[Amazon Route 53 のヘルスチェックと Amazon CloudWatch を使用したリソースのモニタリング](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/monitoring-cloudwatch.html)
DB Browser for SQLiteのテーブル作成ダイアログ上でcreated_atを入力する方法

## 課題

SQLiteはCREATE文でcreated_atを作る場合なら、
```sql
CREATE TABLE samples (中略, created_at TEXT NOT NULL DEFAULT (DATETIME('now', 'localtime')));
```
のようにするとインサート時に特に指定がなければ現在の時刻が入るようになります。
しかしながら、DB Browser for SQLiteのテーブル作成ダイアログのDEFAULT欄に
```
(DATETIME('now', 'localtime')) 
```
と入力すると、自動でクォーテーションで括られて 
```
'(DATETIME(''now'', ''localtime''))'
```
のようになってしまいます。

## 解決

どうすればいいかというと単純で、
```
=(DATETIME('now', 'localtime'))
```
というように頭にイコールをつければ良いです。

Excelみたいな感じですね。  
以前はやり方知らなくてわざわざSQLで追加してたので、同じ事で困っている人や将来の自分に向けて記事にしておきます。
GitHub Actionsを使ってさくらサーバーに自動でデプロイしてもらう

# やりたいこと
GitHubのmainブランチにマージしたら勝手にデプロイサーバーも更新。


## やりたいことの流れ
1. GitHubのmainブランチにpushしたら、
1. GitHub Actionsを使ってさくらサーバーにsshで接続して、
1. GitHub Actionsを使ってサーバーで`git pull origin main`を実行する

# やりかた

## さくらサーバーの設定
これしないと沼る。

さくらレンタルサーバーのサーバーコントロールパネルで
セキュリティ→国外IPアドレスフィルタリング→無効(制限しない)
にチェックして保存。
![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3069552/692db2f6-7604-7524-5120-2fa523879db9.png)

## GitHub Actionsの設定
### 設定の流れ
1. ssh設定して、
1. サーバーに一旦デプロイして、
1. GitHub Actionsで使う変数を登録
1. GitHub ActionsのWorkflowを書く

### sshの設定
sshの公開鍵は、登録の仕方が2つある。
①どのレポジトリでも使えるのと、②各リポジトリで使えるのと2つあるみたい。

①どのプロジェクトでも使えるのは、作業PCのsshを保存して、
②各リポジトリで使うのは、サーバーとかを登録する。

多分今回は②が良い。

①は右上の自分のアイコン→settings→SSH and GPG keys→New SSH key
②はリポジトリ→Settings→Deploy keys→Add deloy key
で公開鍵を登録する。
公開鍵は`cat ~/.ssh/id_rsa.pub`で表示できるのでそれをコピペする。

### サーバーで一旦デプロイ
初回だけは環境整備とかする。
サーバーにsshで接続して、cloneして、環境整えて、一旦デプロイ。

さくらサーバーの場合は、
`ssh [アカウント名]@[ホスト]`でその後にサーバーパスワードを入力する。

こんな感じだと思う。
`ssh hoge@hoge.sakura.ne.jp`

### GitHub Actionsで使う公開したくない変数を登録
GitHub ActionsはWorkflowというファイルに処理手順とかを書く。これはgitで管理されて、公開されるため、ここに直接sshのパスワードなどを書くわけにはいかない。なので、秘密の変数に入れておく。

レポジトリ→Settings→Secrets and variables→Actions→New repository secret

Nameが変数名、Secretが値。


こんな感じで登録しておく
|Name|Secret|
|---|---|
|SERVER_DEPLOY_DIR| `cd`で移動するディレクトリ|
|SERVER_HOST| ホスト|
|SERVER_USERNAME|ユーザー名 |
|SERVER_PASS| 今回はパスワードを使ってssh接続する|

### Workflowを書く
リポジトリのActionsタブ
set up a workflow yourself
を選択

`.github/workflows/` に***.ymlファイルができる。
上のパスのところにファイル名を入力する。

中身はこんな感じ。
``` yml
name: sakura

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
     - name: Checkout Repository
       uses: actions/checkout@v3
     - name: Deploy
       uses: appleboy/ssh-action@master
       with:
         host: ${{ secrets.SERVER_HOST }}
         username: ${{ secrets.SERVER_USERNAME }}
         password: ${{ secrets.SERVER_PASS }}
         script: |
           cd ${{ secrets.SERVER_DEPLOY_DIR }}
           git pull origin main         
```

`name:sakura`:ワークフローの名前
`on:`:なにをきっかけでActionを起こすか。今回はpushとpull_request
`jobs:`:ワークフローの中に複数のjosを設定可能。複数のjosを行う1つのワークフロー。こんかいはjobは1つしかない。
`build:`:jobの名前はbuild
`runs-on: ubuntu-latest`:このjobをどこで実行するか。ubuntuの最新で実行する。
`steps:`:各jobの実行単位
`name:`:そのstepの名前


`uses`:何を使うか。[リポジトリの場所]@[バージョン]となっていて、URLの、github.com/以降に[リポジトリの場所]を貼り付けるとそのリポジトリを見れる。



例えば、
`uses: appleboy/ssh-action@master`は`appleboy/ssh-action`というリポジトリの`master`ブランチを使う。
[appleboy/ssh-action](https://github.com/appleboy/ssh-action)

`with:`など、uses以降はなにを使うかによって書き方が変わるので、リポジトリのRead.meを見て書き方を見るのが良さげ。
ここでは、`script:`を使って、ssh接続を行った後のコマンドを記述している。

記入が終わったら、下のStart commitボタンを押してコミットして終了。
Actionsタブで実行結果を確認できる。


以上！
GitHub Actionsを使ってさくらサーバーにsshしてコマンド叩くWorkflowの設定でした。
さくらサーバーの設定で沼らなければ簡単..!?
定積分 √x*log(x**2)「2023京都大学理系第1問 問1」をwolframalphaとsympyでやってみたい。

グラフがでます。
wolframalphaで計算できました。sympyで計算できませんでした。
オリジナル

https://youtu.be/7wP8TErQSXE

上と同じです。大学入試数学問題集成＞テキスト

https://mathexamtest.web.fc2.com/2023/202310541/2023105410100mj.html

# wolframで
なかなか、log(2)がでません。

https://www.wolframalpha.com/input?i=integrate%28x**%281%2F2%29*Log%28x**2%29%2C%28x%2C1%2C4%29%29&lang=ja

integral_1^4 sqrt(x) log(x^2) dx = 8/9 (4 log(64) - 7)≈8.5649

https://www.wolframalpha.com/input?i=8%2F9+%284+log%2864%29+-+7%29&lang=ja

別の形
8/9 (24 log(2) - 7)

# sympyで
グラフはでました。計算できませんでした。エラーがでました。
```python
from sympy import *
var('x')
y=x**(1/2)*log(x**2)
print(y)
plot(y,(x,1,4))
#print("#",integrate(y,(x,1,4)))
#...PolynomialError(msg)...
```

plotコマンドは、縦横比？異なります。

![Figure_99.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109698/5ef174c6-1218-1dc5-be20-deed146ffa9c.png)
vim終了時にコマンド実行してみた

ファイル編集時に自動で,コードをgithubにアップロードしたいなと思いしらべてみました．(ただこの方法だと特定のリポジトリだけアップロード機能をつけることができない...)

以下のサイトによるとシェルスクリプトのパスが通った場所に`以下のようなファイルを`vim`という名前でおくとできるようです．

```
#!/bin/bash
/usr/bin/vim "$@"
echo otukare
```

https://ja.stackoverflow.com/questions/11684/vim%E7%B5%82%E4%BA%86%E6%99%82%E3%81%AB%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E8%B5%B7%E3%81%93%E3%81%97%E3%81%9F%E3%81%84

シェルスクリプトのパスが通った場所は以下のようにして確認できます．
```
$ echo $PATH
/opt/ros/noetic/bin:/home/dev/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
```

http://motw.mods.jp/Mac/PATH.html

今回は`~/.local/bin`内に上に記載した`vim`という名前のファイルを作成しました．
その後実行権限を付けて上げると`vim`終了時に`echo hello`が実行されました.

```
$ vim bbbb
otukare
```
季節ARIMAモデルでも22年の出生数80万人割れは予測できず・・・23年の予測をしてみる

増川　直裕

### 想定よりも出生数の減少が進んでいる日本
"2022年の出生数が初の80万人割れ、想定よりかなり早いペースで出生数の減少が進んでいる" というニュースが大きな話題になっています。確かに日本の将来を考えると、出生数が想定以上に減っているのは大きな社会問題だと思います。

実は昨年(2022年）はじめに、時系列分析でよく用いられる**季節ARIMAモデル**を使って、[2014年～2021年における月ごとの出生数から2022年の出生数を予測](https://qiita.com/JMP_Japan/items/16e71bf04eaf2e1499d2)していました。そのときに得られた2022年の出生数の予測値は839,796人です。実際の出生数は799,728人だったので、ARIMAモデルによる予測値はおおよそ4万人ほど多く予測してしまったことになります。

別の考え方をすると、<font color="Red">実際は過去のデータから予測できる出生数より大幅に少なかった、</font>ともいえるでしょう。

### 季節ARIMAモデルによる出生数の予測

季節ARIMAモデルは、周期性がある時系列データにあてはめるモデルの一種であり、移動平均(MA)モデル、自己回帰モデル(AR)モデル、和分(I)モデル、周期性(s) の組み合わせで構成されています。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/475e5a30-7d11-bcea-762f-1d22e1cdb135.png)
季節ARIMAモデルでは上記のARIMAの次数(p,d,q)、季節ARIMAの次数（P,Q,R）、1周期における時点数（周期）を指定する必要があり、これらの次数の決め方が悩ましいところであったりします。

今回の例では、周期は12(12か月周期)で良いですが、それ以外の次数は次のステップ1、ステップ2の手順により決めています。

**ステップ1 . 季節ARIMAモデルの次数決定**
-2014年～2020年を学習データとして、さまざまな次数（0,1,2）の季節ARIMAモデルをあてはめる。
-2021年を検証データとして、あてはまりを評価する統計量(RMSE)を使い最適な次数を決定する。
![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/758c2b19-e2cd-cb01-4524-a102c1cb5e89.png)
**ステップ2. 決定した次数を使って2022年を予測**
ステップ1.で決定した最適な次数を用い、2014年～2021年に対して季節ARIMAモデルをあてはめ、2022年を予測する。
![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/dd97d4ae-a862-47f6-b0df-fade4ee49d13.png)
ステップ1ではJMPの「**複数のARIMAモデル**」という機能を使い、さまざまな次数の季節ARIMAモデル（今回の例では729個のモデル）をあてはめています。RMSEが最も小さくなったモデル（最も良いモデル）は、季節ARIMA(2,0,0)(2,0,0)12 です。

ステップ2ではステップ1で求めた季節ARIMAモデルを用い、2022年の月ごとの出生数を予測しています。

下図は月ごとの出生数を示していますが、黒い点は実際の出生数、赤色の折れ線は季節ARIMAモデルによる出生数の予測値を、赤色で塗りつぶされた区間は予測における95%信頼区間を示します。
![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/a76eacd8-e6ec-b287-13a8-31039ca9e35a.png)
図の右側、水色で囲んだ領域が2022年の月ごとの予測になり、1月～12月の予測値を足し算して、2022年の出生数の予測値839,796人を算出しました。

2022年の実測値と予測値の部分を拡大した下の図を確認してみましょう。2022年の月ごとの予測値（赤色の折れ線）、実測値（黒い点）をみると、<font color="Red">1月、8月はかろうじて予測より実測の方が上回っているのですが、それ以外の月はすべて予測より実測が下回っているのです。</font>
![画像5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/75cd3c79-d409-f364-50f4-5414b3b94885.png)
季節ARIMAモデルの折れ線をみると、2月に少なくなる、秋ごろに多くなるといった月ごとの傾向はとらえられているのですが、特に3月～7月までの出生数の下振れが大きく影響しているように見えます。

### 2023年における出生数の予測
前節のステップ1, 2の方法を用い、2014年～2022年の出生数データを使って、2023年の出生数の予測をしてみます。

最も良いモデルは季節ARIMA(2,1,1)(2,1,2)12 となり、このモデルをあてはめて、2023年の月ごとの出生数を予測したのが下図です。
![画像6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417820/9948dab8-8730-eb14-b90d-498febcc3d27.png)
この結果より求められる2023年の出生数の予測値は745,468人です。2022年の大幅な減少がモデル選択、予測に反映され、2023年はさらに5万人以上も減少すると予測しているのです。

ただ、あくまでもこの予測値は過去の値から季節ARIMAモデルという統計モデルによって求められたものです。今後の政府による少子化対策や社会状況の変化などによって、実際の出生数が大幅に上振れする可能性もあります。

今回紹介した季節ARIMAモデルは、"あくまで過去の状況から考えると、将来はこのぐらいになると予測されますよ"　と提示しているに過ぎないのです。ただ今後公開される出生数と比較して、予測より多かった、少なかったと考察する目安にはなります。さて、2023年はどうなるのでしょうか？ 上振れを期待です！

<font color="Red">**統計ソフトの使いやすさ、面白さを試してみませんか？JMPの全機能を30日間試せるトライアル版は下のリンクから！**</font>
### [**JMPトライアル版ダウンロード**](https://www.jmp.com/ja_jp/download-jmp-free-trial.html?utm_campaign=bl&utm_source=jmpblog&utm_medium=article)
stream().map(), stream().mapToInt()

mapはFunction
mapToIntはToIntFunction
ToIntFunctionはreturn int

```java
package sampleproject;

import java.util.List;

import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.IntStream;
import java.util.stream.Stream;

class Item {
    int i;
    Item(int i){
        this.i = i;
    }
    int getI() {
        return this.i;
    }
}
public class SampleProject {
    public static void main(String[] args) {
        List<Integer> l = List.of(1,2,3,4,5);
        List<String> l2 = List.of("1","2","3","4","5");
        int[][] array = {{1,2},{3,4},{5,6}}; 
        Function<Integer,String> f = a -> String.valueOf(a) + "xxx";
        l.stream().<String>map(f).forEach(System.out::println);
        ToIntFunction<String> tf = s -> Integer.valueOf(s);
        List<Item> l3 = List.of(
                new Item(1),
                new Item(2),
                new Item(3),
                new Item(4),
                new Item(5)
        );
        l2.stream().mapToInt(tf).forEach(System.out::println);
        Stream<int[]> st = Stream.of(array);
        st.flatMapToInt(x -> IntStream.of(x)).forEach(System.out::println);
        System.out.println(l2.stream().mapToInt(tf).average().getAsDouble());
        l3.stream().mapToInt(Item::getI).average().getAsDouble();
    }    
}
```
```
1xxx
2xxx
3xxx
4xxx
5xxx
1
2
3
4
5
1
2
3
4
5
6
3.0
```
【効果検証】DID！DID！

忙しい人向けの結論
-
・DIDはトリートメントグループと性質が似たコントロールグループを用意し、比較することでバイアスを減らしながら効果検証する手法。
・DIDで用意するコントロールグループはトリートメントグループと平行トレンド仮定をおいている。何らかの共変量により平行トレンド仮定が崩れているなら、その共変量を回帰分析の式に含める必要がある。

DIDが必要になる瞬間
-
例えば、ある地域で値下げ施作を実施した場合、地域全体で値下げを行うことになるので、介入グループは手に入るがコントロールグループは手に入らない場合がある。また、RCTが難しい予算等の都合上難しい場合などによって、コントロールグループが手に入らない場合もある。このように場合に工夫によって擬似的にコントロールグループを用意し、効果検証するための手法の１つにDIDがある。

DID(Difference　in　Differences)のアイデア
-


イギリスのコレラ感染の例で説明する。イギリスのある町で水源を共有するA社とB社がいる。医師のジョン・スノウはコレラ感染が空気感染か水感染かのどちらの経路で起こるかを調べている。１８５2年にA社が供給水の水源を汚染の少ない上流に移したことを利用し、感染経路の検証を行う。仮にコレラの死者数が少なくなっていれば、水感染である可能性が高いことがわかる。

### 前後比較
１８４９年時にはA社とB社はテムズ川の似た地点から水の供給していたが、A社は１８５２年には汚染の少ない上流に供給源を移動している。

ここで１８４９年と１８５４年をそれぞれ、介入前、介入後と考え、その前後で死者数を比べることを前後比較という。前後比較では、時期によるバイアスが入り込むため、前後比較のみでは効果検証することが難しい。

### 地域比較
A社とB社は同じ街に水を供給しているので、A社の水を利用していた人たちをトリートメントグループ、B社の水を利用していた人たちをコントロールグループとし、比較することを地域比較と呼ぶ。似た地域を選び、比較してもその地域固有のバイアスが入り込むことになるので、正確な効果検証をすることは難しい。

### バイアスの少ない効果検証をするための結論は
性質の似た地域をコントロールグループとして準備し、介入前後の時期で比較をすれば、バイアス（地域や時期）を避けて効果検証することができる。

発想の根幹にあるのは、介入が行われなかった場合のコントロールグループをどうやって用意するかということである。また、別の言い方をすると、バイアスを減らすためのコントロールグループ（介入したこと以外性質が似ているグループ）をどうやって用意するかというところにある。

DIDで用意するコントロールグループに対して、１つだけ重要な前提を置いている。それが平行トレンド仮定。

平行トレンド仮定
-
コントロールグループとトリートメントグループで検証したい効果をみた場合、その効果量は平行にあることが前提となっている。

例えば地域AとBのコレラの死亡者数を見る場合、介入がなければAとBの死亡者数は平行になっていることが前提としてあげられる。もし仮に平行ではない場合、分析には平行の前提を崩す原因となる共変量を組み込む必要がある。

![いんがすいろん-7 2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2707093/c8b27e35-984e-7cc5-aa9a-cbf42110a5be.jpeg)


DIDが推定しているものを数式で
-
検証したい効果量を$\tau$とする。

$Y_{1854,treat}$が１８５４年のトリートメントグループにおける死亡者数
$Time_{1854}$が時間（１８５４年）の固有の効果量
$Area_{1854}$が地域（トリメートメントグループ）による効果量
$\tau$:が介入による効果量


```math
Y_{1854,treat} = Time_{1854} + Area_{treat} + \tau  ・・・①
```

```math
Y_{1849,treat} = Time_{1849} + Area_{treat} ・・・②
```

```math
Y_{1854,control} = Time_{1854} + Area_{control} ・・・③
```

```math
Y_{1849,control} = Time_{1849} + Area_{control} ・・・④
```

上記の式を利用し効果量$\tau$を推定する。
step1:時間による前後比較をし、地域によるバイアスを取り除く。①ー②と③ー④を計算。

```math
Y_{1854,treat} - Y_{1849,treat} = Time_{1854} - Time_{1849} + \tau ・・・⑤
```

```math
Y_{1854,control} - Y_{1849,control} = Time_{1854} - Time_{1849}　・・・⑥
```

step2:地域間で比較をして、時間によるバイアスを取り除く。⑤ー⑥

```math
Y_{1854,treat} - Y_{1849,treat} - ( Y_{1854,control} - Y_{1849,control} ) = \tau
```

これが検証したい効果量。

![いんがすいろん-8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2707093/0d4cc033-3407-e60e-0bb2-10c9657eabca.jpeg)


余談：DIDの考え方を発展させて（CausalImpact等）
-
DIDの肝になる部分は、「コントロールグループをどうやって用意するか」にある。DIDでは平行トレンド仮定を満たす似たグループを用意し対処する。そのような方法でも良いが、最近は機械学習によるトレンドの予測等が発達してきたので、予測を用いてコントロールグループを作成し、効果検証するような手法も存在する。

参考文献
-
効果検証入門〜正しい比較のための因果推論/計量経済学の基礎
数当てゲーム(Python3)

***Python3***で数当てゲームを実装しました

```python
# 数当てプログラム2
# 新規作成 2023/3/1
import random
#名前入力表示
print("Input your name ",end="")
# 入力した文字列を変数に渡す
name = input()
#正解の数値を乱数を使って作成
answer = random.randint(1,10)
#カウント回数
count = 0
while True:
  #数値入力表示
  print("Input your number ",end="")
  #入力した数値を変数に渡す
  num = int(input())
  count += 1
  if answer == num:
    print("%sさん" %name)
    print("%i回目で正解です" %count)
    break
  elif answer > num:
    print("%sさん、正解はもっと数字が大きいです" %name)
  elif answer < num:
    print("%sさん、正解はもっと数字が小さいです" %name)
  
    
```
iterable interface, iterator method()

iterable interfaceを実装するとiterator methodをoverrideし
iterator methodでIterator instanceをreturnする
①while(hasNext()), next()で回すか、
②for(x:y)で回す、暗黙的にiterator()のcallもやってくれる

```java
package sampleproject;

import java.util.ArrayList;
import java.util.List;

import java.util.Iterator;

class Hello implements Iterable<String> {
    public String name;
    public List l;
    @Override
    public Iterator<String> iterator() {
        System.out.println("i am iterator");
        return l.listIterator();
    }

    public Hello(String name) {
        this.name = name;
        this.l = new ArrayList<String>();
        l.add(name);
    }
    public void add(String name) {
        l.add(name);
    }
}
public class SampleProject {
    public static void main(String[] args) {
        Hello h = new Hello("sato");
        h.add("suzuki");
        for(String s:h) {
            System.out.println(s);
        }
        Iterator<String> i = h.iterator() ;
        while(i.hasNext()) {
            String s = i.next();
            System.out.println(s);
        }
    }    
}
```
```
i am iterator
sato
suzuki
i am iterator
sato
suzuki
```
ContentServerの高可用性構成構築メモ

ContentServerを高可用性構成で構築した際のメモ。

〇環境
Product:Content Server 7.53
DB:MaxDB 7.9
OS:Windows Server 2019
HA:WSFC、SIOS DataKeeper

以下のSAPブログ、SAP Noteを参照して対応。
アウトラインはブログ、詳細はNoteが参考になった。

【SAP On Azure : High-Availability Setup of SAP Content Server 7.53 with MaxDB in Windows Environment】
https://blogs.sap.com/2020/04/11/sap-on-azure-high-availability-setup-of-sap-content-server-7.53-with-maxdb-in-windows-environment/

【SAP Note：2255600 - MSCS: Manual Installation of SAP Content Server Database in MSCS Environment】
https://launchpad.support.sap.com/#/notes/2255600

〇構築の流れ
①ContentServer#1をインストール
②ContentServer#2をインストール
③CS#1にMaxDBをインストール
　インストール先ドライブ
　　-Global Program Drive：ローカル
　　-Global Data Drive：共有ドライブ
　　-Installation and Private Data Drive：ローカル
④SDBUPDコマンドでPrivate Data Driveを共有ドライブに変更
　※InstallationドライブとPrivate Data Driveを分離
⑤WSFCにMaxDBを統合
⑥CS#2にMaxDBをインストール、WSFC調整

※WSFCにはContentServerのサービスをリソースとして登録し、Failoverの際に再起動するようにすること。Failover後にMaxDBへ通信できなくなる。
MySQLのWordPressをMariaDBのサーバーへ移行

データーベースにMySQLを利用して制作されたWordPressのウェブサイトを、MariaDBのウェブサーバーへ移行させる際に、innodb_strict_modeがデフォルトの場合、エラーとなってしまいます。

移行先のサーバーのinnodb_strict_modeをOFFすると回避できます。

お客様はPLESKを利用したサーバーでしたので「my.cnf」ファイルの[mysqld]に「innodb_strict_mode=OFF」を追記いたしました。
Tableau Tips：年齢計算

- `datediff("year", [生年月日], today())` だけでOKかと思ったらダメだった。
	* 両日付を"year"でtruncateしたもの同士を比べてる、みたいな動き。
		* 生年月日： 2022-12-31 -(truncate)→ 2022-01-01
		* Today() ： 2023-01-01 -(truncate)→ 2023-01-01
		* →結果：1 (year)←だめじゃん
- 正しい計算式に修正したものの、次回また年齢計算の式を書くときまでに多分忘れるのでメモ。

# 式
* 計算フィールドの内容

	``` 年齢
	if
		[生年月日] > today()
	then
		null
	else
		datediff("year", [生年月日], today())
		-
		int(
		  dateadd(
		      "year",
		      datediff("year", [生年月日], today()),
		      [生年月日]
		  ) > today()
		)
	end
	```

# 解説
- `datediff("year", [生年月日], today())`  の部分は前述の通り
	- これを「YearDiff」と呼ぶことにする
- 生年月日にYearDiffを足してみた結果が…
	- today()より大きい
		- →today()の年の誕生日をまだ迎えてない→年齢 = YearDiff - 1
	- today()以下
		- →today()の年の誕生日をもう迎えている→年齢 = YearDiff
- `dateadd( ... ) > today()`は、true or falseが返ってくる
	- `int( ... )` で数値に変換すると、1 or 0になる

# テスト用データ
``` ages.csv
生年月日
2020/2/28
2020/2/29
2020/3/1
2020/3/2
2021/2/28
2021/3/1
2021/3/2
2022/2/28
2022/3/1
2022/3/2
2022/12/31
2023/2/28
2023/3/1
2023/3/2
2023/12/31
```

# 結果
- ![スクリーンショット 2023-03-01 12.25.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102123/fc02bd2a-0667-6823-a4c0-52b2deb0f620.png)
	- ちなみに上記のテストを行ったのは2023-03-01です…あしからず

# 雑感
- 計算フィールド内で一時的に変数を使えたらいいのにな…
	```
	$year_diff = datediff("year", [生年月日], today())
	
	if
		[生年月日] > today()
	then
		null
	else
		$year_diff
		-
		int(dateadd("year", $year_diff, [生年月日]) > today())
	end
	```
	- みたいな。

# 参考文献
- [誕生日から年齢を計算する方法 | Tableau Software](https://kb.tableau.com/articles/howto/how-to-calculate-age-from-the-date-of-birth?lang=ja-jp)
プリザンターでアプリケーションエラー出てしまったときのエラー内容の確認方法

 # はじめに
日々プリザンターを使っているときに、プリザンターの画面下部に赤帯で表示されるエラーはほとんどの場合SysLogsテーブルに記録されています。そのため、お問い合わせをいただく際にSysLogsテーブルの内容をご提供いただいています。今回の記事では、お客様ご自身でのエラー内容の確認方法を記載しようと思っています。

 # アプリケーションエラー
今回お問い合わせいただいた内容はアプリケーションエラーが起きてしまうとのことでした。このように画面に表示されるエラーはプリザンターに処理が届いているものの、処理が正常に完了しなかったことを意味します。プリザンター内で処理が正常終了しなかったときはSysLogsテーブルを見てみましょう。ほとんどの場合、SysLogsテーブルにエラー内容が記載されています。

 # SysLogsテーブル
SysLogsテーブルの内容は下記マニュアル(FAQ：操作ログ(SysLogsテーブル)の内容について)をご参照ください。では、実際にSysLogsテーブルのどこを見ればエラー内容が見れるのでしょうか。下記マニュアルにある「<ログの項目>」の項目名「ErrMessage」に記載されますが、この列はエラー内容が記録されます。ちなみに、エラーが発生していない場合はNULLが記録されます。そのため、画面下部に赤帯でエラーが表示されたら、SysLogsテーブルのErrMessageを確認してみてください。下記SQLを実行することでSysLogsテーブルのErrMessageのみ表示することができます。また、ログを確認するためのSQLは下記マニュアル(FAQ：プリザンターのログを確認したい)をご参照ください。

```sql
select ErrMessage from [Implem.Pleasanter].[dbo].[SysLogs]
```
https://pleasanter.org/manual/faq-contents-of-syslogs

https://pleasanter.org/manual/faq-view-syslogs

 # さいごに
今回の記事ではエラーの内容を確認する方法を記載しましたがいかがでしたでしょうか。お問い合わせをいただいた際もSysLogsテーブルを確認します。確認してみると意外とあっさり問題解決してしまうことがあります。何より、自分自身で問題を解決できるようになったら、さらに快適にプリザンターを使うことができます。
[メモ] docker-compose が起動しないよ (extends version)

# はじめに
つたないおっさんエンジニアです。
docker-compose up でエラーになり、なんでじゃー！とハマっていたので、備忘録的なメモです。

(これ、新着に上がらないよね？恥ずかしい)

# TL;DR
docker-compose v1でエラー発生
```
ERROR: The Compose file './docker-compose.yml' is invalid because:
Unsupported config option for services.hogehoge: 'extends'
```
docker-compose v2にバージョンアップしたら動いた。

# 環境
Ubuntu on WSL2

# 概要
```
sudo docker-compose version

docker-compose version 1.25.0, build unknown
docker-py version: 4.1.0
CPython version: 3.8.10
OpenSSL version: OpenSSL 1.1.1f  31 Mar 2020
```

```
docker-compose up

ERROR: The Compose file './docker-compose.yml' is invalid because:
Unsupported config option for services.hogehoge: 'extends'
```

なんで動かないんじゃぁ。

ggrとversion 3から非対応です、と。
（結果からすると、 docker-compose.yml version 3 から、ではなく、docker-compose側の仕様変更だったのかな？と思うけど。それ言うと、docker-compose.ymlもdocker-composeの仕様ですよね、と、揚げ足のOverwrideに入るので、気にしないで・・・）

断念してextendsしている内容をdocker-compose.ymlにコピペしまくろう、と思ったら、docker-composeのgithubを見たら、v2.5.0が最新になっていたので、とりあえずバージョンアップしてみた。

https://github.com/docker/compose/releases

# バージョンアップ手順
参考にさせていただいた記事、ほんと感謝
https://kazuhira-r.hatenablog.com/entry/2022/04/30/002702

```
sudo mkdir -p /usr/local/lib/docker/cli-plugins
sudo curl -SL https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose

sudo chmod a+x /usr/local/lib/docker/cli-plugins/docker-compose
```

元記事にも記載されている通り、このやり方だと dockerのcli-pluginsとしてインストールされるので、コマンドが `docker-compose` では `docker compose` に変わるため、注意です。

・・・単に、
```
sudo curl -SL https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose
```
↓
```
sudo curl -L https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
```
だけですわー。
※実行権限の付与は必要よー

・・・`-S` を付けると駄目なんだけど、なんで・・・？エラー出力するだけなはずなのに・・・

# 最後に
大人しくDocker Desktopを使うのが良かったのでは？
気にしてはいけない。
Amazon RDSにEC2経由でSSH接続する（HeidiSQL）

EC2を踏み台にしてRDSに繋げようとして少し手間取ったので。
HeidiSQL→EC2→RDSのイメージです。他のDBクライアントでも参考になると思います。

# ポートフォワード
まずSSM Session Managerでポート転送できるようにします。
やり方は [SSM Session Managerを使ってポートフォワードする](https://dev.classmethod.jp/articles/ssm-session-manage-port-forwarding/) などを参考にしてください。
私は上記サイトの例の通りEC2インスタンスの22番ポートをローカルの10022番にポートフォワードする設定で行いました。

（注意点）
上記サイトの例の通りのコマンドだとLinuxでは動作するようですがWindowsのコマンドラインやPowershellだと以下のようなエラーが出てしまうため、JSON部分をエスケープする必要があります。
（ちなみに、Windowsターミナルだとエスケープする必要がなかったので、まずはどちらかで試してみてエラーが出たらエスケープしてみてください）

```
Error parsing parameter '--parameters': Invalid JSON: Expecting property name enclosed in double quotes:
```
以下のコマンドを
```
$ aws ssm start-session --target i-0123456789ABCDEFG --document-name AWS-StartPortForwardingSession --parameters '{"portNumber":["22"],"localPortNumber":["10022"]}'
```
こんな感じにエスケープする
```
$ aws ssm start-session --target i-0123456789ABCDEFG --document-name AWS-StartPortForwardingSession --parameters '{\"portNumber\":[\"22\"],\"localPortNumber\":[\"10022\"]}'
```

# HeidiSQLの設定
設定項目は以下のようになります。
## 設定タブ
![h1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930440/ea4ce100-8273-0b4e-2b02-e83f3d9d2eb5.png)
* ネットワーク種別：MySQL(SSH tunnel)
* ホスト名/IP：AWS RDSのホスト名
* ユーザー：DBユーザー名
* パスワード：DBパスワード
* ポート：DBのポート番号

## SSHトンネルタブ
![h2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930440/d5a10ef4-7391-3131-61be-7c64b9f67024.png)
* SSH executable：plinkを選択します。無い場合は [Download PuTTY: latest release](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) からplink.exeをダウンロードしてHeidiSQLがインストールされているフォルダに保存します。
* SSH host+port：ポート転送しているので上記で設定したポート番号にします（ここでは10022）。
* ユーザー名：EC2のユーザー名
* パスワード：SSHのパスワードが設定されている場合は入力します。無い場合は空でOKです。
* SSH timeout：接続に結構時間がかかるので40にしておきます。
* Private key file：SSH鍵ファイルを指定します。pemだと失敗するためppkに変換する必要があります。[Download PuTTY: latest release](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) からputtygen.exeをダウンロードして変換します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/930440/112d76ed-338e-6bc5-1594-7f781e2936f4.png)
Loadボタンを押してpemファイルを選択し、Generateを押すと変換されるのでSave private keyボタンを押してppkとして保存します。

以上の設定が済んだらHeidiSQLの接続ボタンを押せば接続が開始されます。
接続が開始されるとポートフォワードを走らせているコンソールにはConnection accepted for session [xxxx-0123456789abc]のようなメッセージが表示されるはずです。
環境によるかもしれませんが、接続が完了するまで結構時間がかかるので気長に待ちましょう（私の環境では接続完了するまで40秒程度かかりました）。一度接続されてしまえば直接接続しているよりは多少もっさりしている感じはあるものの操作自体はほぼストレス無くできます。
rcloneを使ってCloudflare R2へのバックアップ

rcloneを使用して、Cloudflare R2にバックアップを作成する方法を説明します。


## Cloudflare R2の概要
エグレス料金ゼロのオブジェクトストレージと言われており通信量によらず利用できます。
またS3互換のAPIを提供しているためaws cliを使って直接通信することもできます。
ref: https://www.cloudflare.com/ja-jp/products/r2/

## rcloneとは何ですか？
クラウドストレージサービスに汎用的にアクセス可能なコマンドラインツールです。
例えばS3やGoogle Drive、OneDrive、Dropboxなどにアップロードするのを同じIFでコミュニケーションすることができます。
ref: https://rclone.org/


## Cloudflare R2へのバックアップを作成する前に必要なもの
1. Cloudflare アカウント
1. rcloneのインストール
1. APIキーの取得
1. 設定ファイルの作成

## rcloneのインストール
```shell
brew install rclone
```

ref: https://rclone.org/install/

## Cloudflare R2にバックアップを作成する方法
1. バケットを作成するボタンからバケット名を決めて作成する(ここではbucketnameという名前で進めます)
1. https://testid.r2.cloudflarestorage.com/bucketname の形式でエンドポイントが発行されるのでメモしておく
1. R2 API トークンの管理のページを開き、API トークンを作成する
1. トークンとシークレットをメモする


## rcloneの設定ファイルを作成する

```conf
# ~/.config/rclone/rclone.conf にテキストファイルを作成する
[test_bucket]
type = s3
provider = Cloudflare
access_key_id = # 4.トークン
secret_access_key = # 4.シークレット
endpoint = # 2.エンドポイント
acl = private
```

## Cloudflare R2にバックアップを作成する方法

```shell
# DCIMのフォルダをtest_bucketの設定に従ってsyncする
rclone sync ./DCIM test_bucket:
```


## バックアップの復元方法

```shell
rclone sync test_bucket: ./DCIM 
```