OmniAICreator/Qiita-1.07M huggingface dataset:
ubuntuでjupyter-labを起動するdockerfileを記述する

#はじめに
udemyでdockerの講座を受けたので備忘録として一部分を自分なりにまとめてみます。
今回はdockerfileにubuntuベースでjupyter-labを起動するコードを書き、実際にコンテナを作成してみたいと思います。

#dockerfileに記述

```dockerfile:dockerfile

# FROM =>　べースとなるイメージの選択する
FROM ubuntu:latest

# RUN => コマンドを呼び出す
# apt-getをアップグレードし必要なパッケージのインストールする
RUN apt-get update
RUN apt-get install -y \
    wget \ 
    vim

#　WORKDIR => コンテナ側のルート直下に任意のディレクトリを作成し移動する
WORKDIR /opt

#　anaconda3のインストールし、元の実行ファイルを削除する
#　wget => URLを指定してファイルをダウンロードする
#　sh => シェルスクリプトを実行する
#　-b => インタクティブな操作を回避する
#  -p => インストール先の指定する
#  rm => 指定したファイルの削除する
#  -f => 強制的に実行する
RUN wget https://repo.continuum.io/archive/Anaconda3-2019.10-Linux-x86_64.sh && \
    sh /opt/Anaconda3-2019.10-Linux-x86_64.sh -b -p /opt/anaconda3 && \
    rm -f Anaconda3-2019.10-Linux-x86_64.sh

#  anaconda3のPATHを通す
#  ENV => 環境変数を変更する
ENV PATH /opt/anaconda3/bin:$PATH

#  pipのアップグレード
RUN pip install --upgrade pip

#  root直下に戻る
WORKDIR /

#  コンテナ起動時にjupyter labを開く
#  CMD => コンテナ起動時に実行するコマンドの指定する
#  "jupyter", "lab" => jupyter labを立ち上げる
#  "--ip=0.0.0.0" => ipの制限をなくす
#  "--allow-root" => rootユーザを許可する、セキュリティ上は良くない
#  "LabApp.token=''" = > トークンなしで起動可能する。セキュリティ上良くない
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root", "--LabApp.token=''"]
```

#イメージのビルド
Dockerfileが置いてあるディレクトリで実行

```docker

docker build -t my-anaconda .
```

`-t`
ビルドするイメージに任意の名前をつけます。

`.`
dockerfileのあるディレクトリを指定しています。
今回はカレントディレクトリである. を指定します。

#コンテナの起動

```dockerfile

docker run -it \
-p 8888:8888 \
--rm \
--name my-container(任意)\
-v Desktop/ds_python:/opt \
my-anaconda
```
<br>
この部分は下記参照してください
[https://qiita.com/kajima/items/178611c4c405267271a6](https://qiita.com/kajima/items/178611c4c405267271a6)

このコンテナをrunすると
<br>

```
I 11:42:41.250 LabApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret
[W 11:42:41.535 LabApp] All authentication is disabled.  Anyone who can connect to this server will be able to run code.
[I 11:42:41.546 LabApp] JupyterLab extension loaded from /opt/anaconda3/lib/python3.7/site-packages/jupyterlab
[I 11:42:41.546 LabApp] JupyterLab application directory is /opt/anaconda3/share/jupyter/lab
[I 11:42:41.548 LabApp] Serving notebooks from local directory: /
[I 11:42:41.548 LabApp] The Jupyter Notebook is running at:
[I 11:42:41.548 LabApp] http://23a5a126fd40:8888/
[I 11:42:41.548 LabApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 11:42:41.552 LabApp] No web browser found: could not locate runnable browser.
```
<br>
このような画面が出てくるとOK

あとはウェブブラウザに`localhost:8888`を入力して確認してみます。
<br>
![スクリーンショット 2020-08-27 20.51.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/671665/468cb382-4487-d930-4528-a62ed44a50ee.png)
<br>
これが画面で出てくるとOK

#終わりに
あとはanacondaに任意のライブラリやパッケージ欲しい時はdockerfileに`RUN`を記述すればインストールできます。
次はWeb開発におけるdockerfileを記述してみたいなと思っています。

#参考
[https://qiita.com/komiya_____/items/96c14485eb035701e218](https://qiita.com/komiya_____/items/96c14485eb035701e218)
[https://www.udemy.com/course/aidocker/learn/lecture/20311429#overview](https://www.udemy.com/course/aidocker/learn/lecture/20311429#overview)
FlutterアプリをiOSのTestFlightで配布する手順【後編】

## 前回までのあらすじ

[前回](https://qiita.com/kz23szk/items/d7c90bea1f6136218456)まででAppStoreConnectにアプリをアップロードできた。

今回で実際に内部テスターにアプリを使ってもらうところまでいく。（はず）

## テスターを追加。

予め使ってもらう人のメールアドレスを控えておく。

`ユーザとアクセス`を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/a41f2745-9965-1899-e3d5-b6463fd6906d.png)

`+`ボタンを押して、追加するユーザの情報を入力していく。役割は`Developer`でよい。（もっと適切な役割があるかもしれないが）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/1a964e33-9247-c736-16e7-987d4988e7ea.png)



## 

[App Store Connect](https://appstoreconnect.apple.com/)を開いてログインする。

`マイAPP`を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/93034ef3-1b51-ab50-0be4-b44c9ca8bc5d.png)

自分のアプリを選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/39351fd5-018c-d85e-a056-9e1d842c4ac0.png)

上タブの`TestFlight`を選択し、左の欄にある`内部グループ`を押して、`テスターを追加`ボタンを押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/523b3ee8-16e5-3cb8-11b7-1305fa046b54.png)

先ほど追加したユーザにチェックをつけて、`追加ボタン`を押す。

テスターに追加されているが`利用可能なビルドなし`と出る:rolling_eyes:

その下の`ビルド`で`輸出コンプライアンスがありません`と出ているのでこれをクリック。

![スクリーンショット 2020-08-27 20.40.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/85fe36f2-19ca-b459-b7db-f6d283dca68d.png)

`はい`を選んで進む。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/4832ed8b-5d6f-cb48-5fcf-0ddab12684a8.png)

ステータスが`テスト中`に変わる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/2fb56c54-6953-5545-6f97-914f1f86b28f.png)

画面をリロードするとテスターのステータスが`招待済み`に変わる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/7f12b99c-087b-06c3-6981-07e2c544ce0d.png)


メールが届くので内容にある`View in TestFlight`を押す。
遷移先の内容に従って`TestFlight`アプリを入れて、右上の下に出ている`コードを使う`を選びコードを入れるとアプリが出てくるのでインストールを押すだけ。出来た:blush:


![iOS の画像 (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107119/0b815fdf-7ef3-229e-3cdb-466f880c8d41.png)

## 終わりに

ネイティブアプリの開発経験がない者として、アプリのコーディングよりビルド、申請がキツイ。
のでなるべく手順を残していこうと思います。

詰まったらできるかぎり相談に乗りますのでtwitterで気軽にDMいただければ:smiley:
Substrate ink!でDebug情報を出力する方法

# substrateとは
- もう何度も書いているので、割愛しますが、ブロックチェーンのフレームワークです。
- 詳細は[**こちら**](https://substrate.dev/)

# ink!とは
- substrateのスマートコントラクト(?)のことです。

# nodeの起動やink!の基本的なことは
- [**ここをご参照下さい**](https://substrate.dev/en/tutorials)

# お伝えしたいこと
- ink!を実装するときにデバッグのためにprintを使う方法です。
- 探せなかっただけかもしれませんが、どこにも記事がなくて、ホントに困りましたので。

# ink!のサンプルコード
```
#[ink(message)]
        fn flip(&mut self) {
            ink_core::env::println("$$$$$$$$$$$$$$$$$$$$ Debug Start $$$$$$$$$$$$$$$$$$$$$$$$$");
            ink_core::env::println(&ink_prelude::format!("value is {:?}", self.get()));
            ink_core::env::println("$$$$$$$$$$$$$$$$$$$$ Debug End $$$$$$$$$$$$$$$$$$$$$$$$$");
            *self.value = !self.get();
        }
```
- Cargo.tomlに以下を追加してください。

```
ink_prelude = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_prelude", default-features = false }

```

# nodeの実行方法
- 開発モード　かつ　デバッグ情報出力　というような意味のようです。
- **この２つがないと出力はされません。**

```
substrate --dev -lruntime=debug
```

# 実行結果
- 無事に出力されました。

```
2020-08-27 20:54:03.044 tokio-runtime-worker INFO substrate  ✨ Imported #23 (0x8444…caab)
2020-08-27 20:54:05.048 tokio-runtime-worker INFO substrate  💤 Idle (0 peers), best: #23 (0x8444…caab), finalized #21 (0x6b0b…9775), ⬇ 0 ⬆ 0
2020-08-27 20:54:06.020 tokio-runtime-worker INFO sc_basic_authorship::basic_authorship  🙌 Starting consensus session on top of parent 0x84448487385f44691a6c9d19d5a47a01af333b105f4ef872c34a49c21131caab
2020-08-27 20:54:06.034 tokio-blocking-driver DEBUG runtime  $$$$$$$$$$$$$$$$$$$$ Debug Start $$$$$$$$$$$$$$$$$$$$$$$$$
2020-08-27 20:54:06.034 tokio-blocking-driver DEBUG runtime  value is false
2020-08-27 20:54:06.035 tokio-blocking-driver DEBUG runtime  $$$$$$$$$$$$$$$$$$$$ Debug End $$$$$$$$$$$$$$$$$$$$$$$$$
2020-08-27 20:54:06.037 tokio-blocking-driver INFO sc_basic_authorship::basic_authorship  🎁 Prepared block for proposing at 24 [hash: 0x4f1b40a60fedf5a688d5a14ae818dbe66a58e49c83b98e5ee53c9f8428577a3f; parent_hash: 0x8444…caab; extrinsics (3): [0x3511…8626, 0xec77…18e3, 0x6215…dd77]]
```
PDOで接続してViewに連想配列で返す方法 

#参考URL
https://blog.codecamp.jp/programming-php-pdo-mysql-1

##サンプル
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
PHP　コントローラ
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
namespace App\Http\Controllers;

use \PDO;
use App\Article;
use Illuminate\Http\Request;

class ArticleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{

    // ドライバ呼び出しを使用して MySQL データベースに接続します
    $dsn = 'mysql:dbname=laravel;host=127.0.0.1';
    $user = 'root';
    $password = '';

    try {
        $dbh = new PDO($dsn, $user, $password);
        echo "接続成功\n";
    } catch (PDOException $e) {
        echo "接続失敗: " . $e->getMessage() . "\n";
        exit();
    }

    // SQL文を準備します。「:id」がプレースホルダーです。
    $sql = 'SELECT * FROM articles ';
    // PDOStatementクラスのインスタンスを生成します。
    $prepare = $dbh->prepare($sql);


    //$sql = 'SELECT * FROM user WHERE id < :id';

    // // PDO::PARAM_INTは、SQL INTEGER データ型を表します。
    // // SQL文の「:id」を「3」に置き換えます。つまりはidが3より小さいレコードを取得します。
    // $prepare->bindValue(':id', 3, PDO::PARAM_INT);


    // プリペアドステートメントを実行する
    $prepare->execute();

    // PDO::FETCH_ASSOCは、対応するカラム名にふられているものと同じキーを付けた 連想配列として取得します。
    $result = $prepare->fetchAll(PDO::FETCH_ASSOC);

    // 結果を出力
    var_dump($result);

    $message = 'Hello world';
    
    return view('test',compact('result'));
    //return view('test', ['message' => $message, 'articles' => $result]);

    // if ($request->filled('keyword')) {
    //     $keyword = $request->input('keyword');
    //     $message = 'Welcome my BBS: ' . $keyword;
    //     $articles = Article::where('content', 'like', '%' . $keyword . '%')->get();
    // } else {
    //     $message = 'Welcome my BBS';
    //     $articles = Article::all();
    // }


    //Model　ららべるのDB
    // $message = 'Hello world';
    // $articles = Article::all();
    // return view('test', ['message' => $message, 'articles' => $articles]);

}
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
ビュー側　コントローラ
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

<title>paiza bbs</title> <style>body {padding: 10px;}</style>
    <h1>paiza bbs</h1>
  
    @foreach ($result as $article)
    ラジオボタンは{{$article['content']}}
    ラジオボタンは{{$article['id']}}
    @endforeach
</body>
【Unity】ratationでpositionやAddForoceの値を変化させる

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// キーを押したら、ゲームオブジェクトを作って投げる
public class OnKeyPress_Throw : MonoBehaviour
{
	public GameObject newPrefab; // プレハブ：Inspectorで指定
	public string pushKey = "z"; // 押すキー：Inspectorで指定
	public float throwX = 0;     // 投げる力：Inspectorで指定
	public float throwY = 3;     // 投げる力：Inspectorで指定
	public float throwZ = 4;     // 投げる力：Inspectorで指定
	public float offsetX = 0f;   // 作る位置の高さオフセット：Inspectorで指定
	public float offsetY = 1f;   // 作る位置の高さオフセット：Inspectorで指定
	public float offsetZ = 0.5f; // 作る位置の高さオフセット：Inspectorで指定

	bool pushFlag = false;

	void Update() // ずっと、行う
	{
		if (Input.GetKey(pushKey)) // もし、キーが押されて
		{
			if (pushFlag == false) // 押しっぱなしでなければ
			{
				pushFlag = true; // 押した状態に
				Vector3 newPos = this.transform.position;
				Vector3 offset = new Vector3(offsetX, offsetY, offsetZ);
				offset = this.transform.rotation * offset;
				newPos = newPos + offset;

				// プレハブからゲームオブジェクトを作って
				GameObject newGameObject = Instantiate(newPrefab) as GameObject;
				newGameObject.transform.position = newPos;
				// 投げる
				Rigidbody rbody = newGameObject.GetComponent<Rigidbody>();
				Vector3 throwV = new Vector3(throwX, throwY, throwZ);
				throwV = this.transform.rotation * throwV;
				rbody.AddForce(throwV, ForceMode.Impulse);
			}
		} else
		{
			pushFlag = false; 		// 押した状態解除
		}
	}
}


```

このコードで当オブジェクトにoffset足した座標を得られる
offset = this.transform.rotation * offset;
newPos = newPos + offset;

このコードで対象オブジェクトに力を与えられる
throwV = this.transform.rotation * throwV;
rbody.AddForce(throwV, ForceMode.Impulse);
契約形態ー準委任型、請負型

応用情報技術者平成30年秋期 午前問66

ベンダX社に対して，図に示すように要件定義フェーズから運用テストフェーズまでを委託したい。X社との契約に当たって，"情報システム・モデル取引・契約書"に照らし，各フェーズの契約形態を整理した。a～dの契約形態のうち，準委任型が適切であるとされるものはどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/36835240-29c3-4f0f-1186-9b291a7e233a.png)

１、準委任契約とは、
通常の委託契約(請負契約)と同様に別の組織に業務を委託する契約ですが、仕事の完成を契約の目的とする請負契約と異なり、委託された仕事の実施自体を目的とする契約形態です。受託者は善良な管理者の注意をもって委任事務を処理する義務を負うものの、仕事の完成についての義務は負いません。**主に業務分析や要件定義、運用テスト工程**などの成果物が特定されていない状況で結ばれます。

つまり、全部をやるのはなくて、協力の仕事ですね。

２、"情報システム・モデル取引・契約書"は、
ユーザとベンダのあるべき理想的なモデルを提示し、情報システムのライフサイクルプロセスの中で、ユーザとベンダの間でどのようなことを決定し、どのようなことを情報共有すればよいか、についての統一的な指針を目指して策定されたものです。

以下に情報システム・モデル取引・契約書より、フェーズの区分と契約類型の対応表を引用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/b76fb4f2-be91-b59a-3d6f-6c587630f631.png)


参照：
https://www.pm-siken.com/kakomon/29_haru/am2_21.html
レイヤーについて

#はじめに

Unity初心者がメモとして学んだ事を書いています





##レイヤーとは


簡単にいうとゲームオブジェクトをグループ分けする為のようなものらしい。


![“shot” 2020-08-27 20.34.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688851/c0280605-0895-7100-8966-f1bc31e4c58d.png)

この画像の右上の「Layer」の「Default」の部分を押すとPlayerやEnemyなど色々出てくる。

Add Layerでレイヤーを自分で作成することもできる。

これを設定することによって「Player」を選択したならそのゲームオブジェクトはPlayerグループという扱いになる。


このレイヤーは違うグループ同士で衝突判定するかどうかなど設定できたりする。

「Edit」→「ProjectSettings」→「Physics」
で↓の画像のようなところにいく。

![Graphics.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688851/7712e341-9379-972c-74f3-2229c723c5d8.png)


したのチャックボックスがいっぱいあるところがゲームオブジェクト 同士で衝突判定をするかどうか設定できる。


見方としては縦軸と横軸のレイヤーが重なっているところのチャックボックスを見て、チェックが入っていれば衝突判定をする、チェックが入っていなければしないような感じ。


この画像だと「Defauly」と「Enemy」は衝突判定する。

「EnemyAttack」と「Enemy」はしない。




衝突判定以外でも応用できるものがあるようだけど今日はここまで。
AWS DocumentDB に Node.js/mongoose で接続する

## やりたいこと

mongo 互換の AWS DocumentDB に mongoose から接続したい。

[mongoose で docker でたてた MongoDB に接続するのにはまった話](https://qiita.com/kurab/items/90b745654f6fc4159e3a)

以前書いたような感じで接続したい。

今回の環境は、AWS で立てた AmazonLinux2 インスタンスからの接続。

```
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"

$ node -v
v14.8.0
```

なお、DocumentDB は、Tokyo Region でも使える。ちと高い気もするが、EC2 インスタンス上に MongoDB をインストールして、バックアップだなんだと諸々の手間を考えると、個人では遠慮したいところだが、会社ならまぁ良いのでは？という感じ。
DocumentDB のクラスターを立ち上げるためには、インスタンスを1つしか立ち上げないとしても、VPC は Multi AZ 構成になっている必要がある。

## DocumentDB の接続方法
特に何も考えずに普通に DocumentDB を立ち上げると、インスタンス情報の Connectivity & Security という項目に、
まずは、pem をダウンロードし、それを使って接続しろと書いてある。

曰く、

> Download the Amazon DocumentDB Certificate Authority (CA) certificate required to authenticate to your instance

> ```
wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem
```

> mongo シェルでこのインスタンスに接続する

> ```
mongo --ssl --host xxxx.xxxx.ap-northeast-1.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username xxxx --password <insertYourPassword>
```

> アプリケーションでこのインスタンスに接続する

> ```
mongodb://xxxx:<insertYourPassword>@xxxx.xxxx.ap-northeast-1.docdb.amazonaws.com:27017/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&retryWrites=false
```

こんな感じ。

## Node アプリから mongoose で接続

Shell を用いての接続はうまくいくけど、node アプリからの接続は、どうしたら良いの？と探してみたところ、stackoverflow に答えがあった。

[connection error while connecting to AWS DocumentDB](https://stackoverflow.com/questions/54397644/connection-error-while-connecting-to-aws-documentdb)

で、結局こうなった。

```javascript:index.js
const mongoose = require('mongoose');
const fs = require('fs');

mongoose.connect('mongodb://xxxx.xxxx.ap-northeast-1.docdb.amazonaws.com:27017', {
	useNewUrlParser: true,
	useUnifiedTopology: true,
	ssl: true,
	sslValidate: false,
	sslCA: fs.readFileSync('./rds-combined-ca-bundle.pem'),
	user: '<userName>',
	pass: '<password>',
	dbName: '<dbName>'
});

const Cat = mongoose.model('Cat', { name: String });

const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));
```

pem ファイルは、fs で読み込まないと、monngoose 側でうまくやってくれたりはしない。
実行してみる。

```
$ node index.js
meow
```

いったっぽい。
Shell から入って確認。

```
rs0:PRIMARY> show collections
cats
rs0:PRIMARY> db.cats.find()
{ "_id" : ObjectId("5f478c677f7fde2e..."), "name" : "Zildjian", "__v" : 0 }
```

成功。

## 余談
MongoDB は、ほぼほぼ JSON な扱いが出来たりなど、アプリ開発者としては、非常に使いやすいが、MySQL などのようにお手軽にインフラ構築が出来ない。し、ちょっと高い。コンテナなどを使えば良いんだろうけど、運用する場合、ただ立ち上がれば良いという話ではないし。
なので、本当に MongoDB でなきゃダメか？は、インフラ担当者と話し合って決めるのが幸せだと思う。
システム化構想の立案

応用情報技術者平成30年秋期 午前問65

ある企業が，AIなどの情報技術を利用した自動応答システムを導入して，コールセンタにおける顧客対応を無人化しようとしている。この企業が，システム化構想の立案プロセスで行うべきことはどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/e8f607ee-e5c5-f8cd-f5a3-f0ad8f466bb4.png)

１、立案
共通フレーム2013では、テクニカルプロセスとして、企画、要件定義、システム開発、ソフトウェア実装、ハードウェア実装、保守の各プロセスが規定されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/7c152d7a-80df-2d74-646b-f8bbd142f335.png)


"システム化構想の立案"は、企画プロセスを構成するプロセスの1つで、経営上のニーズ、課題を実現、解決するために、置かれた経営環境を踏まえて、新たな業務の全体像とそれを実現するためのシステム化構想及び推進体制を得ることを目的とするプロセスです。

システム化構想の立案プロセスには以下の7つのタスクが含まれます。

・経営上のニーズ、課題の確認
・事業環境、業務環境の調査分析
・現行業務、システムの調査分析
・情報技術動向の調査分析
・対象となる業務の明確化
・業務の新全体像の作成
・対象の選定と投資目標の策定


参照：
https://www.sc-siken.com/kakomon/30_aki/am1_25.html
Rails AWS デプロイ後に更新しても反映されなかった時の対処法

# はじめに

苦労して作り上げたアプリをさらに苦労してAWSにデプロイしたという人も多いのではないでしょうか？

私も同じく相当苦労してAWSでデプロイすることに成功しました。

・・・が！

これってローカルでコード書き換えたりした時ってどうやって更新するんだろう？

もちろん**Capistrano**ってやつで（まだ学習してないので何も知らない）
自動でデプロイしてくれるようにできるのかもしれませんがとりあえずは自動ではなく手動(?)で更新したいのです。

最初の認識ではEC2にログインしてアプリのリポジトリで `git pull` したら反映されるものだと思ってやってみましたが更新されておりませんでした。

## 調べてみた結果

調べてみましたがなかなか欲しい情報にヒットせずこんなことで立ち止まってるのは自分だけなのかな？？？

と少し不安になりましたがググりまくってなんとかこれかな？という情報を見つける事ができました。

それが**unicornの再起動**でした。

## 解決方法

```
$ ps -ef | grep unicorn | grep -v grep
```

EC2にログインしてアプリのリポジトリからunicornの状況を確認します。

```
hiroaki   6472     1  0 Aug26 ?        00:00:02 unicorn_rails master -c /var/www/rails/ride_on/config/unicorn.conf.rb -D -E production                                                                                          
hiroaki   7030  6472  0 Aug26 ?        00:00:10 unicorn_rails worker[1] -c /var/www/rails/ride_on/config/unicorn.conf.rb -D -E production                                                                                       
hiroaki   7051  6472  0 Aug26 ?        00:00:10 unicorn_rails worker[0] -c /var/www/rails/ride_on/config/unicorn.conf.rb -D -E production                                                                                       
```

こんな感じで表示されると起動されているので、
unicornのmasterをkillして停止させます。

```
$ kill -9 6472
```
停止させたらこちらのコマンドで確認、何も表示がなければ止まっているので

```
$ ps -ef | grep unicorn | grep -v grep
```

そのあとこちらのコマンドで起動させます。

```
$ bundle exec unicorn_rails -c /var/www/rails/(アプリ名)/config/unicorn.conf.rb -D -E production
```

これで確認してみるとちゃんと本番環境も更新されておりました・・・！

とりあえず更新が反映されて一安心ですがAWSに関してはまだまだ理解がないのでもっと勉強します・・・。
Qiita初投稿

初カキコ…ドモ…
*以下略*

お目汚ししました。
このようなカビの生えたテンプレはさておき、
普段ブログなど全く書かない人間の執筆ですので、
拙く、記載されている情報にも過ちが多い事が予想されます。

また、基本的にこのブログへの投稿は、
私自身がプログラミングを学んでいく中での備忘録や独り言といった体となりますので、
上質な情報をお求めな方は今すぐお帰りください。時間を無駄にしてしまいます。


それではあいさつもそこそこに、
Qiitaへの初投稿ということで、Qiitaとは何か？ということを
自身の理解を深めるために少しまとめてみたいと思います。

Qiitaは「キータ」と呼びます。
下記はキータ公式サイトからの抜粋です。
「Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。」

運営会社はIncrements株式会社で、
社の設立は2012年2月29日となっていますが、
Wikipediaの情報では、Qiita自体の設立日が2011年9月16日となっていますね。
下記URLの情報では、
https://help.qiita.com/ja/articles/qiita-service-name
2011年の秋にサイトを開設したとあるので、
サイトの運営が一段落ついたあたりで会社を設立したということでしょうか？

プログラマのためのナレッジサービスということで、
他の類似サービスに比べコードの記述が読解がしやすい仕組みになっているようです。

執筆画面の右上のところにマークダウン記法が使える旨についてのヘルプがありますが、
私がまだプログラミング勉強中でマークダウン記法がわからないため、
今日の投稿はひとまずこれまで、何のお化粧もされていないテキストでの執筆となります。

これから実のある内容を執筆できるよう力をつけていきますが、
気負いせずに自分のペースで投稿していこうと思います。

あしからず
Next.jsでSSGした静的サイトを（CSR後リロード時404問題を最小コストで解決しながら）S3でホスティングする

# 概要

Next.jsでSSGした静的サイトをS3にデプロイする場合、基本的には `next export` の結果をバケットに入れれば良いんですが、何も考慮せずにただそうすると、CSRしたページでリロードした際に404になります。

他のAWSサービスの導入は管理コスト増加の観点で避けたく、ここではS3とシェルスクリプトだけでなんとかする方法を共有します。
（ただし、シェルスクリプトはawsコマンドに依存しています）

# S3デプロイ時に起きる問題

（実際はS3以外のホスティングサービスでも起きますが）

**`next export` はページを `.html` の拡張子つきで書き出す（`pages/hoge.tsx`であれば`out/hoge.html`のように）のですが、何も考慮しないとURLが `https://hoge.com/hoge.html` のように、 `.html` つきになってしまいます。**

これ何が問題かというと、該当ページにNextのCSRをするとURLは `https://hoge.com/hoge` （`.html`なし）になるので、この状態でリロードすると404になってしまうのです。

## 脱線だけど

Amplifyでデプロイするとこの問題はおきません。
Amplifyを使うことに懸念が無いのならオススメします。

[Next\.jsでSSGしたサイトをAmplifyでホスティングする \- Qiita](https://qiita.com/ryokkkke/items/14e95a5a00e5ed50aaa9)

# 対策

1. `next export` で出来る `.html` なファイルから拡張子を取り除く。
2. S3に配置した後に、拡張子を取り除いたHTMLファイルに `content-type: "text/html"` を付与する。

これらを手で行いたくないので、シェルスクリプトを書きます。

## 1. `next export` で出来るHTMLファイルから拡張子を取り除く。

そもそもCSRが`.html`をつけないのなら、ファイル名から`.html`を削れば良いですよね。
でも、index.htmlに関してはどちらにしろURLに含まれないし、削らなくていっか。ということで

```shell
next export

html_filepaths=$(find ./out -name "*.html" ! -path "*/index.html")
for filepath in $html_filepaths; do
  mv $filepath ${filepath%\.html}
done
```

こんな感じで、**`index.html`以外の`xxx.html`ファイルから拡張子を消します。**
（`next export`の出力先が`out/`の場合）

## 2. S3に配置した後に、拡張子を取り除いたHTMLファイルに `content-type: "text/html"` を付与する。

ただし、`.html`を削ったファイルには、Content-Typeヘッダがつきません。
そうすると、ブラウザでそこにアクセスした際にそれがHTMLだと認識されず、ブラウザのダウンロードが実行されてしまいます。

なので、**上で拡張子を削ったHTMLファイル（S3のオブジェクト）全てに、`Content-Type: "text/html"`をつけます。**

```shell
# 拡張子を削ったファイルのファイル名を回して、S3上のオブジェクトに対してContent-Typeを付与する
for filepath in $html_filepaths; do
  path=${filepath#\.\/out\/}
  key=${path%\.html}

  aws s3api copy-object \
    --bucket $bucket_name \
    --copy-source $bucket_name/$key \
    --key $key \
    --metadata-directive "REPLACE" \
    --content-type "text/html"
done
```

ここで`aws`コマンドを使っているので無い場合はインストール必要です。

CSR後リロード時404問題についてはこれで解決です。

# デプロイスクリプト

上記の対策込みのデプロイスクリプトはこんな感じです。
実際は環境の振り分けとかその他細かい処理がもっとあるけどその辺はここでは省いてます。

```shell
# next exportの実行
npm run export

# 趣旨と関係ないけど、これら消さずにS3に上げるとブラウザからダウンロードできちゃうので消す
find ./out -name ".DS_Store" | xargs rm
find ./out -name ".keep" | xargs rm

# .html なファイルから拡張子を取り除く
html_filepaths=$(find ./out -name "*.html" ! -path "*/index.html")
for filepath in $html_filepaths; do
  mv $filepath ${filepath%\.html}
done

# S3にsync（アップロード）
$bucket_name=hoge_bucket
aws s3 sync ./out/ s3://$bucket_name/ --delete

# Content-Type付与する
for filepath in $html_filepaths; do
  path=${filepath#\.\/out\/}
  key=${path%\.html}

  aws s3api copy-object \
    --bucket $bucket_name \
    --copy-source $bucket_name/$key \
    --key $key \
    --metadata-directive "REPLACE" \
    --content-type "text/html"
done
```


# おしまい

ページが何階層もあるサイトで試してないので、ダメだったらいい感じに直してください🚀

# 追記

続きとしてこちら:arrow_down:の末尾スラッシュ404問題への対応があります。

[Next\.jsのSSGサイトをS3に静的ホスティングした際に起きる末尾スラッシュ404問題を低コストで解決した \- Qiita](https://qiita.com/ryokkkke/items/36f582260d7d39bf13c6)
バランススコアカードの視点：「財務」「顧客」「内部ビジネスプロセス」「学習と成長」

応用情報技術者平成30年秋期 午前問64

IT投資に対する評価指標の設定に際し，バランススコアカードの手法を用いてKPIを設定する場合に，内部ビジネスプロセスの視点に立ったKPIの例はどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/5eeb70d1-1e11-4033-352a-35d1f7ae5cc9.png)


バランススコアカードは、企業のビジョンと戦略を実現するために、「財務」「顧客」「内部ビジネスプロセス」「学習と成長」という4つの視点から業績を評価・分析する手法です。

バランススコアカードのプロセスである各視点：
１、財務の視点
株主や従業員などの利害関係者の期待に応えるため、企業業績として財務的に成功するためにどのように行動すべきかの目標を設定する。KPIの例としては、ROA、ROE、株価、売上高利益率などが挙げられる。

・ROE（Return On Equity）とは、自己資本利益率を指し、株主が拠出した資本（自己資本）を利用して、どの程度の利益を上げているかを示す指標です。
計算式：

　自己資本利益率（ROE）＝当期純利益／自己資本×100


・ROA（Return On Assets）とは、総資産利益率のことを指し、会社が持っている資産を利用して、どの程度の利益を上げているかを示す指標です。
計算式：

　総資産利益率（ROA）＝当期純利益／総資産×100


・売上高利益率とは、売上高のうち何％を利益として残すことができたのかを示す指標
計算式：

　売上高売上総利益率　＝　売上総利益/売上高　×　100


２、顧客の視点
企業のビジョンを達成するために、顧客に対してどのように行動すべきかの目標を設定する。KPIの例としては、顧客満足度、成約率、客単価、クレーム件数などが挙げられる。

・成約率とは、営業担当者がクロージングできた案件の割合のことである。 営業担当者ごとのパフォーマンスを見るための指標として用いられる。

・客単価とは、消費者一人当たりが一度の購買時に支払う平均額です。 例えば飲食店の客単価は、消費者が一度来店したときに支払う平均金額で、店舗の売上高を来店客数で割って求めます。

・クレーム (クレイム、英語: claim) は、サービスに対する苦情や改善要求、契約あるいは法上の権利請求を指す外来語。最近の日本では損害賠償請求やごり押し等による不当な強迫要求や請求の意味で用いられる場合もある。 

３、業務プロセスの視点
財務的目標の達成や顧客満足度を向上させるために、優れた業務プロセスを構築するための目標を設定する。KPIの例としては、納期順守率、平均リードタイム、不良率、改善提案件数などが挙げられる。

４、学習と成長の視点
企業のビジョンを達成するために組織や個人として、どのように変化（改善）し、能力向上を図るかの目標を設定する。KPIの例としては、年間教育／訓練時間、資格取得数、特許取得数、従業員の満足度などが挙げられる。


－－－－－－－－－－－－－－－－－－－－－－－－－－－－－
補足

ITベンダにおけるソリューションビジネスの推進で用いるバランススコアカードの，学習と成長のKPIの目標例はどれか。ここで，ソリューションとは"顧客の経営課題の達成に向けて，情報技術と専門家によるプロフェッショナルサービスを通して支援すること"とする。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/0ab14c32-3096-0753-1386-f437f0b08b6a.png)

１、
・サービスを提供した顧客に対して満足度調査を行い，満足度を5段階評価で平均3.5以上とする。
　⇒×。顧客の視点での目標例です。

・再利用環境の整備によってソリューション事例の登録などを増やし，顧客提案数を前年度の1.5倍とする。
　⇒×。内部ビジネスプロセスの視点での目標例です。

・情報戦略のコンサルティングサービスに重点を置くために，社内要員30名をITのプロフェッショナルとして育成する。
　⇒○。学習と成長の視点での目標例です。

・情報戦略立案やシステム企画立案に対するコンサルティングの受注金額を，全体の15%以上とする。
　⇒×。財務の視点での目標例です。



参照：
https://www.ap-siken.com/kakomon/29_haru/q63.html

ROEとROAとは
https://keiriplus.jp/tips/roe-roa-chigai/#roe%ef%bc%88%e8%87%aa%e5%b7%b1%e8%b3%87%e6%9c%ac%e5%88%a9%e7%9b%8a%e7%8e%87%ef%bc%89%e3%81%a8%e3%81%af%e3%81%a9%e3%82%93%e3%81%aa%e7%b5%8c%e5%96%b6%e6%8c%87%e6%a8%99%ef%bc%9f%e3%81%a9%e3%81%ae%e3%82%88%e3%81%86%e3%81%ab%e4%bd%bf%e3%81%88%e3%81%b0%e3%81%84%e3%81%84%ef%bc%9f


https://www.ap-siken.com/kakomon/28_aki/q62.html
パスワードの簡単な生成方法

次のコマンドで、比較的多くの環境でさまざまな目的に利用可能な長さ32のパスワードを生成することができます。

```shell
base64 /dev/urandom | tr -d -c '[:alnum:]' | head -c 32
```


## 特徴

* 32文字のランダムな文字列を生成します。
    * 生成する文字列長は最後の `-c 32` で指定しています。もっと長い文字列が必要な場合、これを増やしてください。
* 生成されるパスワードはアルファベットと数字のみで構成されているので、多くの用途に簡単に利用できます。
    * 62文字種で32桁あると、記号を含めた93文字種で29桁と同等かそれ以上の情報量があります。
* `/dev/urandom` を使っているので、暗号学的に安全な乱数をもとにしたパスワードが生成されます。
    * 「でも urandom は PRNG だよね？」と気になる人は代わりに `/dev/random` を使ってください(環境によっては `/dev/random` も PRNG の場合があります)。
* 利用コマンドは coreutils 付属のコマンドのみなので、幅広い環境で利用できます。

## 他の選択肢

この方法以外では、次のような選択肢があります。

* `base64 /dev/urandom` の代わりに `openssl rand -base64`  を使う[^openssl]。 openssl も多くの環境でデフォルトで使えるコマンドです。
* `apg` を使う[^apg]。文字列長や利用文字クラスを細かく指定できます。「発音しやすいパスワード」なども生成可能。
    * homebrew をはじめ、各種OS環境のパッケージマネージャによりインストールが可能ですが、現在はあまりメンテされていないようです。
* `pwgen` を使う[^pwgen]。使ったことがありませんが、現代的には apg よりもこちらのほうがメジャーなようです。
* 各種プログラミング言語でプログラムを書く
    * /dev/urandom からランダムなバイト列を取り出し、それを好みの文字集合に一様にマップしてやればOKです。

[^openssl]: (参考文献) [macOS の標準機能でパスワードをランダム生成する方法](https://qiita.com/KEINOS/items/fd49c4ece0890fb5843f)
[^apg]: [APG on GitHub](https://github.com/jabenninghoff/apg)
[^pwgen]: [pwgen on SourceForge](https://sourceforge.net/projects/pwgen/)
WSLでdockerの環境構築

# はじめに
dockerをwslから動かそうと試み、丸一日溶けたので記録しておきます。

また、わざと"WSL"と書きましたが、前提条件は以下です。

- Windows OS ビルド 19041以上
- WSL2であること
- 管理者権限でコマンドプロンプトを立ち上げていること
- docker のバージョンが17.12.1であること
- docker hub にアカウント登録していること

上記の通り、実際はWSL2でないといけないようです。
ですが、WSLで丸1日時間を費やしてしまいましたし、ほかにもWSLで頑張ってググり、困っている方がいるかと思うので、わざとタイトルはこうしました。

ちなみに、WSL1でも最後のhello-worldまでは成功します。
そのあと、**apt-getを使ってパッケージを入れようとすると全く動かなくなります。**
そのため、**あとの作業のためにWSL2にしておくことを推奨します。**

## step1: windowsのアップデート

以下のページからアップデートできます。
[https://www.microsoft.com/ja-jp/software-download/windows10:embed:cite]


## step2: WSL2への変更
以下の記事を参考に進めました。
[https://dev.classmethod.jp/articles/how-to-setup-wsl2/:embed:cite]
ちなみに、私はすでにWSLでUbuntuが入っていたため、新しくUbuntuをインストールすることなく以下を実行して、すでにあるUbuntuをWSL2で動作させるようにしました。


`wsl --set-version Ubuntu 2`

## step3: dockerのインストール
wslを立ち上げ、以下を実行します。

`sudo apt install docker.io=17.12.1-0ubuntu1`

さらに、ユーザーのグループにdockerをセカンダリグループとして追加します。

`sudo usermod -aG docker $USER`

### docker daemonの起動
以下を実行します。

`sudo cgroupfs-mount && sudo service docker start`


以下のコマンドで、起動していることを確認しましょう

`sudo service docker status`


以下のように表示されていればOKです。

`*docker running`


以下を実行して、うまく起動しているかを確認しましょう。

`sudo docker run hello-world`

以下のように表示されていればOKです。

```
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```
cybozu.com の SAML ベース SSO 設定（Azure AD）

# はじめに

ただのメモです。

# 要件

cybozu.com のメールワイズを利用しているが、Microsoft365 で Azure AD が利用できることもあり、シングルサインオン（SSO）したい。

- メールワイズ
    - URL は ```https://ほげほげほ.cybozu.com/m/mw.cgi```
    - ログインアカウントは ```foo```
- Microsoft365 のログインアカウントは ```foo@example.jp```

# 参考資料

[SAML認証を使用したシングルサインオンを設定する](https://jp.cybozu.help/general/ja/admin/list_externalservices/list_saml/saml_settings.html) という cybozu.com のドキュメント

# 課題

以下のように参考資料にサラッと『ユーザーを識別する要素 = **NameID**』書いてある。

> IdPにcybozu.comを登録する
> cybozu.comをSPとして設定するため、IdPに次の情報を登録します。
> 
>（省略）
> 
> ユーザーを識別する要素
> NameID

「NameID...?」 AzureAD の設定項目では **一意のユーザー識別子（名前 ID）** がこれに対応するのはスグに分かったが、デフォルトでは ```user.userprincipalname``` になっている。

前述のようにメールワイズのログインアカウントは ```foo``` で、Microsoft365 のログインアカウントは ```foo@example.jp``` であり、```user.serprincipalname``` は ```foo@example.jp``` なのでこれを何とかしなければならない。

# 解決策

```user.hogehoge``` のような他のプロパティで該当するものがあるのかと探してみたが見つからず、どうしたものかとググっていたら [方法: エンタープライズ アプリケーションの SAML トークンで発行された要求のカスタマイズ](https://docs.microsoft.com/ja-jp/azure/active-directory/develop/active-directory-saml-claims-customization#emitting-claims-based-on-conditions) にたどり着いた。

| 機能 | 説明 |
|:-:|:-:|
| ExtractMailPrefix()|メール アドレスまたはユーザー プリンシパル名からドメイン サフィックスを除去します。 これにより、渡されたユーザー名の最初の部分のみが抽出されます (例: joe_smith@contoso.com ではなく "joe_smith" のみ)。  | 

ということで以下のように設定すれば良かった。

![スクリーンショット 2020-08-27 19.45.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28198/d88d5e21-d91a-35a8-a0ac-21bea1b0d899.png)

# 終わりに

cybozu.com は SSO 以外にも二要素認証に対応していたり、IP アドレス制限ができたり、とセキュリティ関連の機能が豊富で素晴らしい。更に高感度がアップしたのだった（終わり）。
ecsのクラスタがFargate Spotで100%実行する設定

### Fargateは非常に便利

だけど開発や試験的に使うだけなら、70%OFFの料金のFargate Spotでお安く使いたい。クラスタの設定を必ずFargate Spotで実行する設定の仕方は以下です。


cluster名がseleniumの場合

```
$ aws ecs put-cluster-capacity-providers --cluster selenium --capacity-providers FARGATE_SPOT FARGATE --default-capacity-provider-strategy capacityProvider=FARGATE_SPOT,weight=1,base=1
```

以下のコマンドで確認する
`defaultCapacityProviderStrategy`が追加されたらOK

```
$ aws ecs describe-clusters --clusters selenium
{
    "clusters": [
        {
            "clusterArn": "arn:aws:ecs:ap-northeast-1:999999999999:cluster/selenium",
            "clusterName": "selenium",
            "status": "ACTIVE",
            "registeredContainerInstancesCount": 0,
            "runningTasksCount": 0,
            "pendingTasksCount": 0,
            "activeServicesCount": 0,
            "statistics": [],
            "tags": [],
            "settings": [
                {
                    "name": "containerInsights",
                    "value": "disabled"
                }
            ],
            "capacityProviders": [
                "FARGATE_SPOT",
                "FARGATE"
            ],
            "defaultCapacityProviderStrategy": [
                {
                    "capacityProvider": "FARGATE_SPOT",
                    "weight": 1,
                    "base": 1
                }
            ]
        }
    ],
    "failures": []
}

```
WindowsのUACでファイルの書き込みができないのをJava/Kotlinでチェックする

## TL;DR
Windowsサポートするなら`java.nio.file.Files.isWritable`使っといたほうが良い。

## はじめに
意外とインターネットに知見がなかったので自分用メモ。

Java/Kotlinでファイルの保存処理、特にユーザーにディレクトリを選択させて保存するような処理が対象です。

Javaのクラスの話ですが自分が実際にやったのはKotlinなのでサンプルソースはそれに準じます。

## 書き込み権限チェックのやり方
チェック方法が2通りあります。
古いやり方と比較的新しいやり方。

1. `java.io.File.canWrite`
2. `java.nio.file.Files.isWritable`

1.は古き良き`java.io.File`クラスのインスタンスメソッド。
2.は`java.nio.file.Files`というJava 1.7で追加された比較的新しいクラスのstaticメソッド。
というかそもそも`java.nio.file.Files`クラスはstaticメソッドだけを扱っている。

Windows向きには2.を使っておいた方が何かと安全という話。
詳細をこれから説明します。

### java.io.File.canWrite
繰り返すけれどこちらはインスタンスメソッド。

以下サンプル。

```kotlin:sample.kt
val file = File("任意のファイルパス")
val canWrite = file.canWrite() //書き込み可能であればtrue
```

とっても簡単。

これはファイルに書き込み可能属性が付与されているかだけを見ています。
Linux系だといわゆるLinuxパーミッション、Windowsで言えばファイルプロパティの読み取り専用属性が対象。

つまり実行中のJavaプロセスがOSに対して権限を持っているかまではチェックできない。
もっと言うと**WindowsのUACによる権限チェックまではサポートしてくれない。**

そのため、Cドライブ直下などのOS管理者権限がないと書き込めないディレクトリだと、`canWrite`がtrueだったとしてもファイル書き出し時に`IOException`が容赦なく投げられてしまい、はい、障害です（Javaプロセスが管理者権限で動いていればもちろんこの限りではないですが）

### java.nio.file.Files.isWritable
そこで出番になるのがこちらのメソッド。
書き方はこんな感じ。

```kotlin:sample.kt
val file = File("任意のファイルパス")
val canWrite = Files.isWritable(file.toPath()) //書き込み可能であればtrue
```

こちらは**Javaプロセスが書き込み可能な権限を持っているか**を含めてチェックしてくれる。
以下はOracleJDKのドキュメントからの引用。
https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html#isWritable-java.nio.file.Path-
>このメソッドは、ファイルが存在することと、**このJava仮想マシンに、そのファイルを書込み用に開けるようにする適切な特権があること**を確認します。

というわけなので、こちらであればUACも含めたチェックが可能。
ちなみに読み取り権限や実行権限は`isReadable`や`isExecutable`といったメソッドで同じようにチェック可能です。

## 以上です
自分が調べた限りだと、UAC関連はVista時代のインストーラーで困った系の記事しかなくて、マジでOracleのリファレンス以外に情報がなかった。
意外とここで詰まることってないのかな？
というかネイティブアプリの事例がそもそも少ないのか？？

同じ事象で詰まった人の助けになれば幸いです。
Rails5.2でIEでもリンクを保存せずに直接開けるようにする

railsでPDFをsend_dataしているリンクが、IE11では保存の選択肢しかでず、不思議に思っていたら
Rails5.2リリースノートの変更点　https://railsguides.jp/5_2_release_notes.html
にありました。
> セキュアなX-Download-OptionsおよびX-Permitted-Cross-Domain-Policiesをデフォルトのヘッダーセットに追加。 (Commit)

デフォルトでは、脆弱性を突いたファイルをいきなりアプリで開かないようにとの意図かと思うのですが、試したブラウザの中では皆スルーされている中で、IE11だけ「保存しかできない」状態になるのが意外というかなんというか。（いろいろ誤解している可能性あり）

そこでIE11でも直接開きたい場合は例えばこんな感じ。

```
        response.headers["X-Download-Options"] = nil （if　...リスク許容条件...）
        send_file(file_path,:filename => filename,:type=>content_type,:disposition=>'inline')
```

サイト全体適用ならconfigでもできるのかも。
→ コメントで対応例を教えていただきました。

```cinfig/application.rb
config.action_dispatch.default_headers.delete('X-Download-Options')
```
Chrome拡張でURLを取得する

## TL;DR

```javascript
chrome.tabs.query({ active: true, currentWindow: true }, (e) => {
      const url = e[0].url;
      console.log(url);
    });
```

## location.hrefじゃだめなのか

*background page*や*popup view*は通常のタブとは違うコンテキストで動作しているので、`location.href`では取得できません。`location.href`で取得できるのは、`chrome-extension://`から始まるURLのみです。

また、`chrome.tabs.getCurrent(function callback)`で取得できそうな気もしますが、無理です。なぜなら対象とするtabが特定できないからです。

なので、`chrome.tabs.query(object queryInfo, function callback)`の第一引数でタブを特定し、コールバック関数でtabの情報を取得します。なお、この関数では第一引数に当てはまった全てのタブをコールバック関数に渡してくれるので、`e[0]`としています。

他にも多くのオプションがあるので、詳しくは公式のレファレンスを参照してください。
https://developer.chrome.com/extensions/tabs#method-query
モデルのscopeを使ってコードをすっきりさせる！

モデルのscopeについて勉強する機会があったので、紹介させていただきます！

Scopeとは、モデルへのメソッド呼び出しとして参照されるクエリに、
名前を付けてひとまとまりにしたものです。

## Scopeを定義する

例えば、あるBookのidを降順にし、かつ10件表示したいとします。
その場合、以下のようなメソッドを使います。

```ruby
Book.order(id: :desc).limit(10)
```

他にも条件を追加したい時、もっとメソッドを連結させる必要があり、コントローラーの記述量が増えます。
また、あまりにも長いと一見何の処理を行っているか分かりにくくなるかもしれません。

そこでscopeの出番です。

scopeを使うと以下のように書けます。

```ruby
class Book < ActiveRecord::Base
  scope :recent, -> { order(id: :desc).limit(10) }
end
```

このように定義しておけば、コントローラーで「Book.recent」という記述が可能になります。

```ruby
class BooksController < ApplicationController
  def index
    @books = Book.recent
  end
end
```

scopeは再利用ができるので、コントローラーで毎回クエリメソッドを連結させる必要がなく、
コードがすっきりするので読みやすくなります。

また、scopeは重ねて呼び出すことも可能です。

モデルにscopeを追加して

```ruby
class Book < ActiveRecord::Base
  scope :recent, -> { order(id: :desc).limit(10) }
  scope :costly, -> { where("price > ?", 2000) }
end
```
コントローラーで「Book.costly.recent」という記述が可能になります。

```ruby
class BooksController < ApplicationController
  def index
    @books = Book.costly.recent
  end
end
```

## 引数を渡す
スコープには引数を渡すことができます。

```ruby
class Book < ActiveRecord::Base
  scope :created_before -> (time) { where(" created_at < ?", time) }
end
```

引数付きのscopeは、クラスメソッドと同様の方法で呼び出すことができます。

```ruby
Book.created_before(Time.zone.now)
```

しかし、scopeに引数を渡す方法は、クラスメソッドとして定義する方法でも同じことができます。

```ruby
class Book < ActiveRecord::Base
  def self.created_before(time)
    where(" created_at < ?", time)
  end
end
```

ちなみにscopeに引数を渡すよりも、クラスメソッドで定義する方が推奨されています。

## 結局scopeを使うと何が良いのか

**１. クエリに名前を付けられるので、直感的なコードになる**

例えば{ where("price > ?", 2000) }に、costlyという名前をつけると簡潔になります。


**２. 修正箇所を限定できる**

scopeを再利用している場合、scopeの定義自体を修正するだけで済みます。


**３. コードの記述量が減る**

scopeを使った場合、同じ処理でメソッドを何回も連結させる必要がないので、コードがすっきりします。

## 最後に

コードはただ動けば終わりではなく、DRY原則に沿って保守しやすいコードを書けるように意識していきたいです。

そのためにもscopeなど知る必要があるので、リファレンスや技術書を積極的に活用していきたいなと思いました！

以上です！
Ansible Tips: key部分に変数を使用したdictionaryを作成する

備忘
# 課題: key部分に変数を使用したdictionaryを作成したい

例えば以下の様なplaybookにより、"FOO": "BAR"を得たい。

```yaml
- hosts: localhost
  gather_facts: false
  vars:
    foo: FOO
    bar: BAR
  tasks:
    - debug:
        msg:
          '{{ foo }}': '{{ bar }}'
```

しかし実行するとキーには変数を設定できず、意図通りにはならない。

```
$ ansible-playbook -i localhost, site.yml

PLAY [localhost] ****************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": {
        "{{ foo }}": "BAR"
    }
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```


# 対応(NEW):

以下の様に、シンプルにYAMLフロースタイルで記述すれば良い。

```yaml
---
- hosts: localhost
  gather_facts: false
  vars:
    foo: FOO
    bar: BAR
  tasks:
  - debug:
      msg: "{{ { foo: bar } }}"
```

実行結果

```
 $ ansible-playbook -i localhost, -c local a.yml

PLAY [localhost] ****************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": {
        "FOO": "BAR"
    }
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```




# *以下backup(当初の記述)*

# 対応: items2dictフィルター

(他にもっと良い方法があるとは思うのですが、)とりあえずitems2dictフィルターを用いる事で回避可能。

```yaml
- hosts: localhost
  gather_facts: false
  vars:
    foo: FOO
    bar: BAR
  tasks:
    - debug:
        msg: '{{ baz|items2dict }}'
      vars:
        baz:
          - key: '{{ foo }}'
            value: '{{ bar }}'
    - debug:
        msg: "{{ [{'key': foo, 'value': bar}]|items2dict }}"
```

```
$ ansible-playbook -i localhost, site.yml

PLAY [localhost] ****************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": {
        "FOO": "BAR"
    }
}

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": {
        "FOO": "BAR"
    }
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
```

# 参考
https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#items2dict-filter
Mac / VirtualBox（CentOS）からssh接続をする

# 1. VirtualBox設定
- [ファイル]→[ホストネットワークマネージャー]をクリック
![スクリーンショット 2020-08-23 1.52.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/59383e15-9514-988c-defb-68dcd6ac08ce.png)
- 作成ボタンをクリックすると`192.168.56.1`が作られる
 - DHCPサーバーにチェックを入れて、閉じる
 - 他の設定にしたい場合はプロパティで変更する
![スクリーンショット 2020-08-23 1.57.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/34683073-596a-b907-0aec-10d8c611d509.png)
- ssh接続したいCentOSを選択し、設定ボタンを押す
![スクリーンショット 2020-08-23 1.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/d594d5b9-5a57-9c11-4bb9-9fb6b17928ff.png)
- ネットワークを選択

＜アダプター1設定＞
ネットワークアダプターを有効化にチェックを入れる
割り当て：NAT
![スクリーンショット 2020-08-23 2.00.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/b3cb4e4d-a8a5-4187-f3b7-e249689f13b0.png)
＜アダプター2設定＞
ネットワークアダプターを有効化にチェックを入れる
割り当て：ホストオンリーアダプター
名前　　：vboxnet0（ホストネットワークマネージャーで作ったものを選択）
![スクリーンショット 2020-08-23 2.02.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/b26902c0-10b4-f38a-79f9-c367a8c68989.png)

OKを押して、画面を閉じる

# 2. CentOSの設定
- CentOSを起動
![スクリーンショット 2020-08-23 2.05.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/2173681f-0635-167e-f84b-e1a4f4873e57.png)
- rootユーザーでログインし、`nmtui`コマンドを実行
![スクリーンショット 2020-08-23 1.35.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/d9762e59-2893-7817-3de6-1f3e4b37f899.png)
- 「Edit a connecction」を選択
- 下ボタンで`enp0s3`を選択し、右ボタンを押し`<Edit...>`を選択してEnter
![スクリーンショット 2020-08-23 2.07.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/20db270d-63f3-7152-6c40-eae93c7f5c77.png)
- Edit Connectionで以下の設定をして、OKで閉じる
 - IPv6 CONFIGURATIONをAutomaticからIgnoreに変更
 - Automatically connectにスペースを押してチェックをつける
![スクリーンショット 2020-08-23 1.44.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/d4c42ce7-8092-6648-7da4-994b4fd2f402.png)
- 一覧に戻るので、今度は文字化けしている方を選択し、右ボタンを`<Edit...>`を選択してEnter
- Edit Connectionで以下の設定をして、OKで閉じる
 - Profile nameを`enp0s8`に変更
 - IPv4 CONFIGURATIONを`Autmatic`から`Manual`に変更
 - `IPv4の<show>`までカーソルを持っていき、Enter
  - Addressesに`192.168.56.11/24`を追加
 - IPv6 CONFIGURATIONを`Automatic`から`Ignore`に変更
 - Automatically connectにスペースを押してチェックをつける
![スクリーンショット 2020-08-23 2.22.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/c223e348-24ee-798f-ef2c-916eab63f66c.png)

- 一覧に戻ったら、Backまでカーソルを持っていきEnter
![スクリーンショット 2020-08-23 2.18.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696107/c90c727a-e43e-4ad9-7b42-e00e03f06797.png)
- `NetworkManager TUI`に戻ったら、`Quit`を選択しEnter

- ネットワークを再起動

```
# systemctl restart NetworkManager
# systemctl restart network
```

# 3. Macのターミナルからssh接続
- 接続

```
# ssh root@192.168.56.11
```

何か求められた場合は、yesと打ち込む

以上、ssh接続の設定は終了

# 4. 参考
https://qiita.com/ryubb_SU/items/782fd6a0614d9855e3b4
https://qiita.com/ebkn/items/751ed657629ba8d4ab0a
thymeleafでHTMLのページを動的にする（spring + gradle）

前回は[spring + gradle でhelloworldを表示してみる](https://qiita.com/kty000/items/ca08f627f9bacecfa34e)で画面にHTMLを表示しました。
今回は画面を動的に動かしてみたいので、入力した文字を画面に表示させてみたいと思います。初心者の備忘録ですのでおかしな点などありましたらご指摘頂けますと嬉しいです！


完成後はこんな感じです↓
![入力前.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/247afca5-8a19-ed43-25a0-37487c0775ba.png)
送信を押下すると
![入力後.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550109/01358778-a2f8-d897-6c8a-6e09d4282e1e.png)


前回の記事で使用したコードにそのまま書き足していきますが、今回の記事のみ見ている方のためコードは略さず全体を載せていきます。


##HTMLを追加する
前回作成したhello.htmlに入力した文字を表示する箇所と、入力するフィールドを追加します。

```hello.html
<!DOCTYPE html>
	<head>
		<meta charset="UTF-8">
		<title>hello</title>
	</head>
	<body>
		 <h1>Hello World!!</h1>
　　　   // ここから追加↓　
		 <h1 th:text="${text}"></h1>　---1
		 
		 <form th:method="POST" th:action="changeText"> ---2
		 <input type="text" name="inputText" /> ---3
		 <input type="submit"> ---4
		 </form>
　　　  // ここまで追加↑　
	</body>
</html>
```

１．入力した値を表示します。textに渡した値が表示されます。
２．`method`で送るときの方式を指定して、`th:action`はURLを指定しています。
３.テキストフィールドの追加。`name`で渡した値の名前を指定しています。
４.送信ボタンです。

###thymeleafってなに？
th～というのが出てきましたね。これはthymeleafの書き方です。
タイムリーフと読みます。HTMLなどのテンプレートエンジンで、Spring Bootでは標準的に使われています。
javaで処理したデータを画面に表示するときなど、HTMLなので開発をしながら画面で確認ができるのがいいですね。

`build.gradle`に以下を追加して更新すると使用できます。

```
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
```
前回の記事から引き続きこの記事を見ている方は、プロジェクトを作成するときに依存関係でthymeleafを選択したので、追加しなくても記入してあるはずです。

thymeleafでどうやって書くんだ？となったときはこちらの記事がとっても見やすいです
=>[Thymeleafチートシート](https://qiita.com/NagaokaKenichi/items/c6d1b76090ef5ef39482)


##Controllerを追加する
こちらも前回作ったHelloControllerに追加します。

```HelloController.java
package helloSpring.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HelloController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String hello(Model model) {
    	model.addAttribute("text", "ここに入力した文字が表示"); // 追加－－－１
        return "hello";
    }
    
    @RequestMapping(value = "/changeText", method = RequestMethod.POST) // 追加－－－２
    public String changeText(@RequestParam(name = "inputText") String text, Model model) {
    	model.addAttribute("text", text);
        return "hello";
    }
}

```

１．`hello.html`で追加した`<h1 th:text="${text}"></h1>`に初期メッセージをセットします。
２．送信ボタン押下時の関数を追加します。
　・@RequestParamはポストパラメータを受け取るためのアノテーションです。
　・nameはHTMLのinputで指定した名前と同じにし、変数名を定義します。
　・'${text}'にリクエストパラメータで受け取った値をセットします。


以上です。
次はDBに接続してデータを表示させてみたいと思います。

追記：別の現場へ行くことになりしばらくjavaの勉強はおやすみ中ですが、空いた時間に勉強して更新していきます（続きを待っている人いないと思うけど、、）
Zoom の音声を Speechnotes で文字起こしする方法【Mac】

# はじめに

Zoom などのビデオ会議システムは、大変便利ですがまだすべての人がその利益を享受できているわけではありません。
ここでは、Zoom の音声出力を文字起こしすることによって、視覚のみによって会話の内容を把握する方法を紹介します。


# 構成

今回は、できるだけ単純な構成を試みます。

VB-Cable というソフトウェアを使って、ビデオ会議システム Zoom の音声出力と、リアルタイムに文字起こしをしてくれる Speechnotes というサイトを連携します。

* Zoom のマイク入力を Mac の内蔵マイクに設定すると、パソコンに向かってしゃべった音声が Zoom へ入力されます。
* Zoom のスピーカー出力と Mac のサウンド入力を VB-Cable に設定すると、  
Zoom の音声出力は、 VB-Cable を通して Mac へ仮想的に入力されます。
* Speechnotes は、Mac のサウンド入力（実質はZoom のスピーカー出力）をもとに文字起こしをします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159851/763f1b56-99f4-ae81-760f-5e230646013f.png)


# 準備



## Zoom

zoom を起動します。
他の人からの招待、または自分でアプリケーションを立ち上げます。


## VB-Cable

VB-Cable は以下のサイトからインストールできます。
https://www.vb-audio.com/Cable/ 

一度インストールが完了すると、VB-Cable は Mac から音声デバイスとして認識されるようになります。


## Speechnotes

ウェブブラウザを立ち上げて、speechnotes のページにアクセスします。

以下のリンクからアクセスできます。
https://speechnotes.co/ja/

<div style="page-break-before:always"></div>
# 手順

使用する際には、都度 Zoom と Mac それぞれの音声設定をおこなう必要があります。

## Zoom のサウンドを設定する

zoom の画面の下の方にマウスカーソルを持っていくと、メニューが表示されます。
左の方の :microphone: マイクのアイコンが Zoom のサウンド設定です。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159851/af9b32e8-4448-4fc1-47c8-8eb4650596dc.png">

マイクのアイコンの右にある **【^】** マークをクリックすると、メニューが出てきます。

* マイクは、**【内臓マイク】** に設定します。
* スピーカーの設定を **【VB-Cable】** にします。

**ここまでで、 Zoom は、内蔵マイクの音声入力をうけとり、VB-Cable へ音声出力するようになります。**


## Mac のサウンド入力を　VB-Cable に設定する。

<img width="75%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159851/d8e1e860-fc65-0bee-49db-8859fc780913.png">

![image.png]()

①　Mac のディスプレイの右上のメニューバーから **【:loud_sound: サウンド】** をクリックします。
②　出てきたメニューから **【"サウンド"環境設定...】** をクリックします。
③　サウンド 環境設定ウィンドウが出てくるので **【入力】** タブを選択します。
④　**【VB-Cable】** を選択します。

**これで、Mac は、VB-Cable の出力を音声入力として受け取るようになります。**

## Speechnotes を開始する

Speechnotes は、Mac の音声入力を文字起こしします。
https://speechnotes.co/ja/

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159851/c6e99ce7-eccf-5101-21ad-5bdfb72f849d.png">


Speechnotes のページでマイクのアイコン（画像右上の赤いアイコン）をクリックして音声認識を開始します。
IT投資ポートフォリオの投資対象カテゴリを、戦略、情報、トランザクション、インフラの4つに分類される

応用情報技術者平成30年秋期 午前問61

IT投資ポートフォリオにおいて，情報化投資の対象を，戦略，情報，トランザクション，インフラの四つのカテゴリに分類した場合，トランザクションカテゴリに対する投資の直接の目的はどれか。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/f711c5a7-9e70-70f0-91b2-a5e1b58d8ec6.png)


IT投資ポートフォリオの投資対象カテゴリ
を、戦略、情報、トランザクション、インフラの4つに分類して考えるのは、MITスローン情報システム研究センターが提唱するモデル(MITモデル)です。以下の各カテゴリの定義は、経済産業省「業績評価参照モデル（PRM）を用いたITポートフォリオモデル 活用ガイド」からの引用です。


１、戦略
市場における競争優位やポジショニングを獲得することを目的とした投資。具体例としては、導入当初のATM等がこのカテゴリーに該当する

・ATM　
現金自動預け払い機（げんきんじどうあずけばらいき）、ATM（エーティーエム）、（英: cash machine、automated/automatic teller machine）


２、情報
より質の高い管理を行うことを目的とした、会計、マネジメント管理、レポーティング、コミュニケーション、分析等を支援するための情報提供に関連する投資

３、トランザクション
注文処理等のルーチン化された業務のコスト削減や処理効率向上を目的とした投資

トランザクションの意味としては、
１）、取引。「銀行間の―」
２）、コンピュータ システムにおける、永続的なデータに対する不可分な一連の処理。


４、インフラ
複数アプリケーションによって共有される基盤部分を提供するための投資。PCやネットワーク、共有データベース等が該当する


参考：
https://www.ap-siken.com/kakomon/30_aki/q61.html
RaspberryPiでメモリリークに対応した話

メモリリークなんて考えたことなかった。

[以前書いたコード](https://qiita.com/shin1007/items/4d7eb0ef1eee5b551215)をラズパイでは知らせてたらメモリエラーが出てきてだめになりました。ガーベージコレクション`gc.collect()`じゃ対応しきれていなかったので、OSに標準搭載の定時実行機能を利用したよ、という話。
全体としては、「毎7分に起動して、色々読み込んでもらって、毎0分に指定のことを実行してもらう」みたいな処理にした。


# 対策1：OS(RasberryPiOS)標準搭載の定時実行機能の利用
```sh
crontab -e
# nano editorの選択
1
# 以下のものを追記
# 分 時 日 月 曜日 コマンド
7,17,27,37,47,57 * * * * python /home/pi/Desktop/abcdefg.py
# 保存
Ctrl + O
# 決定
Enter
# 終了
Ctrl + X
# 確認
crontab -l
```

# 対策2：実行したら抜けるためのコードを追加
別に`import sys`しなくてもエラー起きて落ちてくれると思うんだけど一応入れたほうがいいかな。

```python
def minute_interval(interval_min: int, at_sec: int, function, *args, **kwargs):
    """RUNS FUNCTION REPETITIVELY
    Args:
        interval_min(int) : Value of minute. If 11, it will operate at 00, 11, 22, 33, 44, and 55.
        at_sec(int) : Value of second. If 5, it will operate at 05.
        function: Function you want to call repetitively
    """
    while True:
        now = datetime.now()

        # when interval_min is 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, or 30
        if now.minute + interval_min < 60:
            wait_min = -now.minute % interval_min
        # when interval_min CANNOT divide 60. e.g: 7,8,9,11,13...
        else:
            wait_min = 60 - now.minute

        now_sec = now.second + (now.microsecond / 1000_000)
        wait_sec = ((wait_min * 60) + (at_sec - now_sec)) % (interval_min * 60)
        print(f'call func after {int(wait_sec/60)} min {wait_sec%60} sec')
        time.sleep(wait_sec)

        function(*args, **kwargs)
        sys.exit(0)
```