OmniAICreator/Qiita-1.07M huggingface dataset:
【Rails 退会】railsで簡易的な退会機能を作る

# 【Rails 退会】railsで退会機能を作る

## 前提条件
モデル：Userモデル
コントローラー：Usersコントローラー
ルーティング：resources :usersを記述

## 機能をつける前の実装プロセス設計
①投稿機能をつける場合と同様に、Userのidをキーとしてレコードを取得
②そのレコードをdestroyメソッドを用いて、削除する

## 実際にやってきましょう！
### Users_controller.rbをいじいじ

```ruby:users_controller
class UsersController < ApplicationController
  def destroy
    @user = User.find(params[:id]) #特定のidを持つ情報を取得
    @user.destroy
    flash[:success] = 'ユーザーを削除しました。'
    redirect_to :root #削除に成功すればrootページに戻る
  end
end
```

### viewをいじいじ

```erb:〇〇.html.erb
<%= link_to "退会する",user_path(current_user.id), class: "destroy-user",method: :delete %>

<%# ここで、(current_user.id)でしっかり引数にログインユーザーのidを渡してあげること！
```

これだけで完了！
高度な退会機能はまだ作れなかった...
とりあえず！！一旦これで！
VCIが消失その３

#結論
位置を入れる階層をチェックしよう。
位置はサブアイテムより下の階層だけに入れよう。

#背景
VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で（特に初心者に多い）結構な割合で**作ったはずのVCIを呼び出しても出てこない**（見つけられない）事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

#対象
all user(主に初心者）

#症状
- 目の前に出てくることを期待していたものが出現しない
- そもそも出てこない
- VCIが変なところに出現する
- 
等

#原因
***位置を入れる階層を間違えてる***

#どういうことか
VCIを出力する際に、GameObjectの階層（サブアイテムの階層）に入れられているTransformの値はすべて無視されます。
そのため、VCIが消失その２にで紹介したY=1に設定しようとして、GameObjectの階層にY=1を入れてしまうと、unity上では位置が動いているのに、バーチャルキャストで取り出した際にやはり地面に出てしまっていることがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/ab064451-a8d0-5925-2e10-005b3869770c.png)


#解決策
単純にGameObjectのtransformの値を確認しましょう。ここに値が入っていて良いことはあまりないので、(0,0,0)になっているか確認する癖をつけるといいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/a215a3f2-b505-558c-6965-0d4fffc4fa18.png)
そして、上図のように値を入れて再度確認しましょう。

#ちなみに
わけも分からず矢印（unityで出てくる三色の矢印）を弄って位置を設定していると、はるか上に行ってしまうパターンもあります（１敗）
後ろに出てくることもあります（１敗）
床の下にめり込むこともあります（２敗くらい、、、たぶん、、、）
やはり透明な背景で落ち着いて探すのが望ましいです。

そして、こんなときもやはり適当なcubeが入っているとロストしにくいです。
高速化メモ

アニメーターの高速化
アニメーターを呼ぶときにそのまま文字列で指定するのではなくハッシュキーに変換してから呼ぶと早くなる
例
文字列をハッシュに変換する
当たり前だと思われますがhashのほうがはやいのでハッシュに変換するといい
マネージャーを作ってまとめて回せるとなおよい
static readonly int WalkStaticHash = Animator.StringToHash("Walk");

void Start()
{
	Animator anim = GetComponent<Animator>();
	
	//遅いもの
	anim.Play("Walk");

	//早い
	anim.Play(WalkStaticHash);

}

Object型は実際は値型（intなど）を扱うことができないがボックス化によって値型も格納することができる。
int n =10;

//Boxing(ボックス化)
object o=n;

//UnBoxing(ボックス化解除)
n = (int)o;
ボックス化の内容は
まず最初にintを持っているオブジェクトを生成する
その後そのボックスに代入したい値を入れる
最後にボックスをオブジェクトに渡すことで実装している

public class IntBox:object
{
	public int Value;
}

void Start()
{
	int n =10;

	//Boxing(ボックス化)
	IntBox box =new IntBox();
	box.Value = n;
	object o = box;

	//UnBoxing(ボックス化解除)
	n = ((IntBox)o).Value;
}
回避方法
Textの場合だったらobjectをそのまま使うのではなくToString()メソッドでStringにしてあげることで避けられるだがGCAllocは発生する
Agile開発に息づくJust in Timeと後工程引き取り

**ジャスト・イン・タイム（Just in Time：JIT)**のポイントは「必要なものを、必要な時に、必要な量だけ後工程が引き取る」とTPSでは定義されています。
後ろの工程が、その工程に必要モノを必要な量だけ必要な時に取りに行くという意味です。この後ろの工程とは最終的にはモノを必要とするお客様まで対象となります。
**お客様が必要な時に、必要なモノを必要な量を提供する**と言い直すと分かりやすくなります。
これは、在庫低減から作り過ぎのムダを抑える事ができます。

これを、我々のソフトウェア開発に置き換えてみます。
お客様はユーザー、必要なモノはサービス、サービスは同じものを量としてとらえる概念は無いので単一とします。必要な時は同じでサービスを必要とする時となります。
Agile開発での**要求（実現したいサービス）はユーザーストーリー**で表現されます。このユーザーストーリーのルールには**Independent（独立性）、Small（小さい・簡潔）**があります。このルールでユーザーストーリーは実現したい**単一のサービス**を示しています。

一つのプロダクトバックログは一つのユーザーストーリーが当てはまります。
このプロダクトバックを優先順位で並べたリストがプロダクトバック・リストになります。
この優先順位付けはプロダクトオーナーがそのビジネス価値で決めるというのが一般的ですが、ユーザーの要求をベースに開発を進める様なケースでは**ユーザーが必要とする優先順位**の方が現実的です。
先のほどのJITの定義に当てはめると、この**優先順位のつけ方により、後工程引き取り**が実現できることになります。

ここまで説明を続けると、懸命な読者の方はもの作りのJIT思想がプロダクト・バックログ・リストで実現されている事を容易に理解できると思います。

もう一つ、ユーザーストーリーはSmall（小さい・簡潔）であるべきとルールがあります。さらに一つのスプリントに収まるように小さくすべきとも説明されています。
**優先順位（ユーザーの必要な度合い）で低いものはユーザーストーリーでは定義せず、エピックで記載して、スプリントに入る直前までにはユーザーストーリーとして記述する**というルールがあります。
これもJITの思想に従っていて、必要とされるまではムダになるかもしれない作業（ここではエピックからユーザーストーリーへの組立）を抑止できるのです。

従来手法の開発では使用されないサービスを多く作ってしまうムダを内包していましたが、**Agile開発ではこのように、基本的なプラクティスにJIT**の思想が息づいています。

スプリント毎にサービスを実現し、ユーザーレビューで確認し、ユーザー承認されれば、即サービスをリリースするという**Scrumの基本的なプロセスもJIT**の思想が息づいています。
Dockerの全imageを強制全消去

自分用のメモです。
もし使い方を間違えてたら教えてください。

Dockerのイメージをコンテナを消しても-fでオプション設定してもどうしても消せない時がありその後何とか時間をかけて解決しました。

docker image rm $(docker image ls -a -q) -f

これで消えました。

docker-comopseの時は

docker-compose down --rmi all
の後
docker rmi （イメージID）



DockerでネットワークがおかしくなりMYSQLが立ち上がらなくなりましたのでネットワークの削除コマンド
networkを確認する
docker network list

ネットワークの一括削除
docker network prune
【Swift】JavaScriptやRubyみたいにStringを空文字でsplitしたい！

# こういうやつ

JavaScriptやRubyなら簡単。

```javascript
"Hello, World".split('') // -> ["H", "e", "l", "l", "o", "," ...]
```

# String#split(separator: Character)じゃダメなの？

```swift
"Hello, World".split(separator: "")
// Error: Cannot convert value of type 'String' to expected argument type 'String.Element' (aka 'Character')
```

そもそも実行できない。

# String#components<T:StringProtocol>(separatedBy separator: T)

```swift
"Hello, World".components(separatedBy: "") // -> ["Hello, World"]
```

お、惜しい！（惜しくない）

# 正解 → Array(_ s: Sequence)

```swift
Array("Hello, World") // -> ["H", "e", "l", "l", "o", "," ...]
```

というわけで、Arrayのイニシャライザが使えるようです。
いざ調べても、たどり着くのに苦労したので記事にしてみました。

# 参考

Convert Swift string to array
https://stackoverflow.com/questions/25921204/convert-swift-string-to-array
初心者がはまりがちなDockerのエラーに対する解決法

# 記事の背景
Le Wagon Tokyo (https://www.lewagon.com/ja/tokyo) という、主にRubyとRailsを教えるフランス発の英語のプログラミングスクールを卒業後、Dockerを勉強し始めました。卒業後に取り組んだプロジェクトでDockerの構築を行ったのですが、多くのエラーに直面しました。独学で試行錯誤しながらエラー解決を行ったので、非常に多くの時間がかかってしまいました。私のように、Dockerを初めて触ってエラーに苦労した人も多いと思うので、私が使った解決方法を参考にしていただければと思い、この記事を書くことにしました。

# 1 PermissionError (Permission denied :‘~~~/your_app/tmp/db’)
Dockerの公式ドキュメントを参照する限り、このエラーはLinuxユーザーだけのようですが、LinuxユーザーにとってPermissionは厄介になりうるので共有します。このエラーは、Dockerが一時的なデータベース(dbフォルダ)を作るのですが、現在ログインしているLinuxユーザーがそのデータベースを使う権利がないため発生します。

まずはこのコマンドでそのテンポラリーフォルダに移動します。

```
$ cd ~~~(ご自身のアプリまでのパス)/(ご自身のアプリ名)/tmp
```

以下のコマンドでどのユーザーがdbフォルダを使えるか確認します。

```
$ ls -la
```

現在ログインしているLinuxユーザーの名前が表示されていなければ、そのユーザーに以下のコマンドで権限を与えます。

```
$ sudo chown -R (Linuxユーザーの名前) . 
password for (Linuxユーザーの名前):
```

sudoコマンドなので、パスワードの入力が求められます。ここまで行けば、"docker-compose build"や"docker-compose up"など主要なDockerコマンドは問題なく実行できるはずです。

注：
似たようなエラーで、私は"FATAL: could not open file “
global/pg_filenode.map”: Permission denied"というエラーをよく見たのですが、こちらは"docker-compose stop"→"docker-compose up"でリスタートすれば大丈夫です。

# 2 No space left on your device
Dockerfileの設定によって、Dockerイメージの容量が大きくなってしまった場合に起きるエラーです。不要なイメージ並びにコンテナは以下のコマンドで簡単に削除できます。

```
$ docker image prune
$ docker container prune
```
これらのコマンドを入力すると、以下のメッセージが表示されます。

```
WARNING! This will remove all dangling images (もしくはcontainers).
Are you sure you want to continue? [y/N]
```
dangling imagesは不要なイメージという意味なので、"y"を入力してください。容量が確保され、PermissionError解決時と同様の主要なDockerコマンドを実行できるようになっているはずです。

もしもさらに削除する必要がある場合は、以下で削除するイメージを探します。

```
$ docker images
```
消したいイメージのIDを確認した後、以下で削除します。

```
$ docker image rm -f (消したいイメージのID)
```


# 参考文献
英語ですが、この本はRuby on RailsのプロジェクトでDockerを使う方法をかなり詳細に論じているので、おすすめです。

Docker for Rails Developers: https://pragprog.com/titles/ridocker/

残念ながら、英語も含めてRails用のDockerの資料はまとまっているものが少ないので、特にRails歴が浅く、Dockerについての易しい資料が読みたいということであれば、英語ですが読む価値はあると思います。

#Mediumの英語の記事
本テーマに関してより詳細に、Mediumに英語で記事を書いたので、そちらもご覧になってみてください。

https://medium.com/@shogohida_81081/5-solutions-to-common-errors-of-docker-for-beginners-c04dc1237c78
VCIが消失その２

#結論
実は地面にある

#背景
VR配信・SNSサービスのバーチャルキャストでは、他のVRSNSに見られない仕組みとしてVCIというものがあります。VCIはバーチャル空間で取り扱えるアイテムであり、好きな場所で好きなタイミングで取り出すことができ、その挙動はスクリプトを用いて制御することでインタラクティブな効果を奏することができます。

そのVCIを制作するにあたって、制作の最初の段階で（特に初心者に多い）結構な割合で作ったはずのVCIを呼び出しても出てこない（見つけられない）事が起きます。

本記事ではこの現象に対する事例分けをし、それぞれの対処方法を紹介し、、、たいのですが、結構多岐にわたるので、そのうちの一つをご説明します。

作者のモチベーションが続けばもっと書きます

#対象
主に初心者

#症状
目の前に出てくることを期待していたものが見当たらない

#どういうことか
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/aec999bb-e611-8005-1343-85ca0741dc07.png)

実はというかよくよく考えると当たり前なのですが、何も設定しないと、アイテムは座標(0,0,0)に配置されます。これは一体何処なのかと言うと、目の前の地面です。最初に作るものは小物か何かでしょうから、場合によってはサイズが小さすぎるのと複合して何処にあるのかわからなくなります。出てこないと思ったら、焦らずまず地面の上を探してみましょう。

#解決策
サブアイテムの"Transform"のPositionのYを1（m）に設定すること。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494837/be3c65bf-33a5-88d5-b564-7420295fc2ec.png)
ただここで注意！座標はサブアイテム（かそれ以下）に入れること！GameObject(一番上の階層）に入れてもunity上では意図した場所にいるように見えるけど、実際は変わっていないなんてことが起きてしまいますので気をつけて。

#ちなみに
アバターやアイテムによっては、取れなくなって、場合によっては触ることすらできなくなったりします。
昔はこうなると手の施しようがなくて、修正しても更新できないので一旦ログイン画面まで戻る必要がありました。

今はメニューボタン長押しでハンドレーザーを出して更新もできるし、引き寄せもできるようになったので、その場で修正できるようになりました。便利♪開発陣のみなさまありがとうです。
Rustでは++すら許されない

- [JavaScriptで+++は許されない+ ++は許される - Qiita](https://qiita.com/matyapiro31/items/19d62605632dd90d0647)
- [C/C++ では+++は許される - Qiita](https://qiita.com/Nabetani/items/ea9e141f650492b9e147)

を受けて。そもそも `++` 要るのか？というお話です。

Rustに限らずモダン言語だと `++` を許さないやつ結構多いです。Pythonとか。

```Rust:Rust
fn main() {
    let mut i = 0;
    // コンパイルエラー
    // i++;
    // ++i;
    // こう書かなければならない
    i += 1;
    println!("{}", i);
    // i += 1 の返り値は () なので以下もコンパイルエラー。
    // println!("{}", (i += 1));
    // 以下はOK (++i相当)
    println!("{}", {
        i += 1;
        i
    });
    // 少し回りくどい (i++相当)
    println!(
        "{}, {}",
        {
            let res = i;
            i += 1;
            res
        },
        i
    );
}
```

そもそもRustは `mut` をつけてものを書くということ自体避けるので上のようなコードを書くことはほぼありえないでしょう。(つまりタイトルでRustの名前をお借りしたということですごめんなさい)

以下に示すようなC言語コードの未規定動作(要出典)の存在が原因でなくなったのだと思います。僕自身がよくやらかすので残しておきます。

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

void func(int a, int b, int c) {
  printf("a = %d, b = %d, c = %d\n", a, b, c);
}

int main() {
  int i = 0;
  // 0, 1, 1 を期待
  func(i, ++i, i++);

  return 0;
}
```

実際の出力は環境依存です。(確認できた環境では右から評価されるらしく `2, 2, 0` になりました)

そもそも `++` 演算子は可読性も良くない気がします。自分はC言語でも `i++` はforループの更新文以外では書かない流派に改宗しました。
【javascript】イテラブル 文字列

この記事は以下の書籍を参考にして執筆しました。

- [入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)

```javascript
[..."Isasaka"]//["I", "s", "a", "s", "a", "k", "a"]
```
文字列を反復すると一連の文字が生成される
#なぜ文字なのか
それを決めるのは文字列のイテレータ
文字の代わりにカスタムイテレータで文字列のイテレータを書き換えてみる。

```javascript
const myString = Object("Iterables are quite something")

myString[Symbol.iterator] = function*() {
  for (const word of this.split(' ')) yield word
}

const words = [...myString]// ["Iterables", "are", "quite", "something"]
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)

ここでは文字列を作成し、文字列オブジェクトを作成するためにObject呼び出しで囲んでいる
こうしないとプロパティを更新しても維持できない。

次は文字の代わりに単語を生成するカスタムイテレータを文字列の@@iteratorに割り当てる。

オブジェクトの@@iteratorを上書きするときは注意
オブジェクト自体のイテレータを使って反復処理をした場合、無限ループになる。

```javascript
const myArray = Object([2, 3, 5])

myArray[Symbol.iterator] = function*() {
  const copy = [...this] //カスタムイテレータの中で
                        //thisを反復しようとしている。
  copy.reverse();
  for (const item of copy) yield item
}

const a = [...myArray]
```
>出典：[入門JavaScriptプログラミング](https://www.amazon.co.jp/%E5%85%A5%E9%96%80JavaScript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-JD-Isaacks/dp/479815864X/ref=sr_1_3?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1F76CKY9CIJ66&dchild=1&keywords=%E5%85%A5%E9%96%80javascript%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0&qid=1595991879&s=books&sprefix=%E5%85%A5%E9%96%80javascript%2Cstripbooks%2C-1&sr=1-3)


イテレータの中で[...this]をしていると、
値を取得するためにthisの反復処理をしようとする
→イテレータを使用する必要がある
→すでに入れテータの中にいるので再帰呼び出し
Pythonでは`+++`も`+++++`も`++++++++`も許される

バズっているようなので相乗りします。

[C/C++ では+++は許される](https://qiita.com/Nabetani/items/ea9e141f650492b9e147)
[JavaScriptで+++は許されない+ ++は許される](https://qiita.com/matyapiro31/items/19d62605632dd90d0647)

# Pythonでは`++`も`+++`も`++++++++++`も許されます!

```py
a = 1
b = 2
print(a+b) # OK
print(a++b) # OK
print(a+++b) # OK
# ...
print(a+++++++++++++++b) # OK
```

すなわち、`+`を繋げてどんなに長い「演算子」をつくったとしても動きます。

# 上でつくった「演算子」は、前置演算子としても使えます。

上でつくった「演算子」は2項演算子ですが、同様に前置演算子バージョンもあります。

```py
a = 1
print(+a) # OK
print(++a) # OK
print(+++a) # OK
# ...
print(+++++++++++++++a) # OK
```

# `-----`でもよい。

`+`が嫌いな方は、`-`を使って「演算子」を構成してもよいでしょう。

```py
a = 1
b = 2
print(a-b) # OK
print(a--b) # OK
print(a---b) # OK
# ...
print(a---------------b) # OK
```

もちろん、前置演算子バージョンも使えます。
また、`+`と`-`を適宜まぜあわせて使ってもいいです。(`+---+-+++-+`のように)

# 用途

ソースコードの区切り

```py
print("first")
print("second")
print("third")
0-+-+-+-+-+-+-0
print("100 times")
```

# JavaScriptだとダメ

Javascriptの場合、以下のように3文字以上の「演算子」はエラーとなる可能性が高いです。

```js
let a = 1
console.log(+++a) // NG
console.log(a+++) // NG
console.log(++-a) // NG
```

ただし、以下はOKです。

```js
let a = 1, b = 2
console.log(-++a) // OK
console.log(+-+a) // OK
console.log(a++-b) // OK
console.log(a-++b) // OK
console.log(a++-++b) // OK
```

# なぜダメなの?

PythonとJavaScript (or C)の違いは、インクリメント演算子`++`が定義されているかどうかです。例えば`+++++`という「演算子」がある場合は演算子を最小一致で切り出していくので、`++` `++` `+`という3つの演算子に分割されます。
`++`演算子は右側(or 左側)が左辺値(変数)でなければならないため、エラーとなります。

# 思ったこと。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">インクリメンタル演算子がある言語の場合、大体どれも同じ挙動をしますね。<a href="https://t.co/7RRNSuFLBk">https://t.co/7RRNSuFLBk</a></p>&mdash; yasuo_ozu (@yasuo_ozu) <a href="https://twitter.com/yasuo_ozu/status/1283109344901398528?ref_src=twsrc%5Etfw">July 14, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

※ `++`が存在しない言語でも、Luaの場合は`++`のようにつなげて書くと`+` `+`のように分割されず、エラーになるようです
文字列を検索して，検索文字列が見つかったら，行頭から，検索文字列直前までの文字列を表示するプログラム

Javaで文字列を検索して，検索文字列が見つかったら，行頭から，検索文字列直前までの文字列を表示するプログラムを作っています。
※太文字斜文字でユーザ入力
文字列をコピー＆ペーストで入力

このような実行結果にしたい。
↓
<img width="244" alt="スクリーンショット 2020-07-15 19.55.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674840/e35e2913-f61a-0f18-eac9-b222e6909075.png">

現在の実行結果
<img width="338" alt="スクリーンショット 2020-07-15 19.56.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674840/02dd3cb8-76bc-7b1f-35fb-fd4008e36bb3.png">

現在のソースコード
<img width="560" alt="スクリーンショット 2020-07-15 19.57.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674840/632c3e25-86f8-1370-ddb0-f9fcac822608.png">

補足情報
PC：　MAC
エディタ：　Atom
PUN2でPhoton Animator ViewのParamaterType.Triggerが同期しない時の処置

# トリガータイプのアニメーションが同期しない場合の対処
これで２週間ほどつぶしたので、備忘録。

表題通り、PUN2でPhoton Animator Viewを使用してアニメーションを同期しようとしたとき、「ParamaterType.Trigger」でのアニメーションが同期しない問題の対処です。

これは、Animator.SetTrigger()の発生がUpdate内の１フレのみであるため、SetTriggerの呼び出し処理の後にPhotonAnimatorViewの処理が呼ばれないとイベントが検出できないためです。

ちなみにParamaterType.Triggerを設定していると、コンソールにPhotonAnimatorView: When using triggers, make sure this component is last in the stack.という説明文が出ます（エラーでなくても必ず出る）。


## 方法１　SetTriggerを同じゲームオブジェクトのスクリプトから呼んでいる場合

インスペクターの中で、SetTriggerを呼んでいるスクリプトが上、PhotonAnimatorViewが下になるようにコンポーネントの順序を変えます。マニュアルでもその方法が記載されています。

https://doc.photonengine.com/ja-jp/pun/v2/troubleshooting/known-issues
> PhotonAnimatorView And とTrigger
> Animator Controllerの Triggerパラメータを使用していて「PhotonAnimatorView」を使って同期をおこないたい場合には、問題が発生しないよう処理方法を検討することが重要です。

> Triggerの性質上、Triggerはアニメーションイベントが開始した際にのみ有効化され、次のUpdate()の直前に非有効化されます。
GameObject上のコンポーネントは、宣言された順に実行されます。
実行順序の設定を編集すると、GameObjectのコンポーネント上の実行順序に影響を及ぼします。 「PhotonAnimatorView」コンポーネントは、Triggerを起動するコード後に実行する必要があります。 このため、これはスタックの最下部に設定したほうが安全です。 あるいは、少なくともAnimator.SetTrigger(...)を使用してTriggerを起動するコンポーネントの 下に設定してください。
「PhotonAnimatorView」Inspectorは、様々なパラメータの最新値を表示します。 trueとなるべき時にTriggerが的確にtrueになっているかを、パブリッシュ前でもチェックすることができます。 trueになっていない場合には、この特定のTriggerがネットワーク上で同期されていない可能性があります。

https://doc-api.photonengine.com/ja-jp/pun/v2/class_photon_1_1_pun_1_1_photon_animator_view.html
> トリガーパラメータを使用する場合、トリガを設定するコンポーネントがPhotonAnimatorViewより、GameObjectのコンポーネントのスタック上で高い位置にあることを確認してください トリガーは、1つのフレームの間のみ、trueとして起動されます。

## 方法２　SetTriggerを呼ぶスクリプトのExecution OrderをPhotonAnimatorViewより先にする

私の場合はSetTriggerを呼ぶスクリプトがPhotonAnimatorViewと異なるコンポーネントにアタッチされていたので、この設定ができません。
調べた挙句、Script Execution Orderの設定で美味く動作するようになりました。

Project Settings内のScript Execution Order設定で、Animator.SetTrigger()を呼んでいるスクリプトがPhotonAnimatorViewより先に呼ばれるようにします。

![PhotonExeOrder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19010/aa89a5a0-faf7-5617-e50d-f671b32383df.png)

私の場合、演出のアニメ呼び出し、コントローラー入力のアニメ呼び出し、ボットプレイヤーのアニメ呼び出しのコンポーネントから呼んでいたので３つを登録し、全部をPhotonAnimatorViewより上にしています。

Known-issuesの日本語版だとこれが「実行順序の設定」と訳されていて見逃しており、英語でExecution Orderって書いてあったのでやっとProject SEttingsをいじればいいのね、と気が付きました。。。

https://doc.photonengine.com/en-us/pun/v2/troubleshooting/known-issues#photonanimatorview_and_triggers

## その他
CacheDiscreteTriggersがなんかしてくれそうな気もする
1分で立ち上げるZABBIX server

## 用意するもの

Docker 環境
今回は、WindowsのDocker Desktop環境でやりました。

## ダウンロードと実行

```powershell
> docker pull  zabbix/zabbix-appliance
> docker run --name my-zabbix-appliance -p 8022:80 -p 10051:10051 -d zabbix/zabbix-appliance
```

## ブラウザでアクセス

http://localhost:8022/

初期パスワード
ID: Admin
Password: zabbix
です。

ここまで１分ほどです。

お手軽でしたが、入ったバージョンはちょっと古く
zabbix_server (Zabbix) 4.4.6 でした。

次はagent設定が必要です。

### Docker imageの詳細

https://hub.docker.com/r/zabbix/zabbix-appliance/
初心者によるプログラミング学習ログ 371日目

#100日チャレンジの371日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
371日目は、
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>昨日の作業・やったこと⇩<br><br>●寝る直前の筋トレ5分<br>●ブログ1記事作成<br>●プログラミング学習371日目1.5h<br>・メンターの課題<a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E3%81%A4%E3%81%AA%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアとつながりたい</a><a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a><a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a></p>&mdash; ぱぺまぺ@社畜✕投資✕ブロガー (@ppmp65) <a href="https://twitter.com/ppmp65/status/1283135365939728384?ref_src=twsrc%5Etfw">July 14, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
スクロールバー上にリンクを乗せてスクロールさせないCSS

# スクロールできない

スクロールバーでスクロールできないサイトの原因を調べてみた:thinking:

![linktag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311399/0479b1fd-32e5-8128-e206-e78ad7f065ab.png "スクロールバーの上なのに重なる")

このようにスクロールバー上の広範囲にリンクタグが設定され、スクロールバーをドラッグできない。

次のスタイルシートでoverflowやabsolute等を指定すると実現できる。

```html
<style>
  .scrollbar {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    overflow-y: scroll;
  }
  .linktag {
    right: 0;
    top: 50px;
    bottom: 0;
    position: absolute;
	background-color: rgba(230, 236, 240, 0.2);
  }
  .longheight {
    height: 2000px;
	background-color: #f7f9f9;
  }
</style>

<div class="scrollbar">
  <div class="longheight"></div>
</div>

<a href="." class="linktag">リンクタグ</a>
```

Google ChromeとFirefoxで動作確認した。

`overflow-y: scroll` はボックスに収まらない場合、スクロールバーを表示する。

`position: absolute;top: 0;right: 0;left: 0;bottom: 0;`のどれが欠けても動作しないので、特別な意味があるかもしれない。

# position: absolute; top: 0; right: 0; left: 0; bottom: 0;とは何か？

調べてみた。

>  top と bottom の両方が指定されており、 position が absolute または fixed に設定されており、かつ height が未指定 (auto または 100% のどちらか) の場合は、 top と bottom の距離が尊重されます。

[bottom - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/bottom)

# 結論

CSSの`position: absolute;top: 0;right: 0;left: 0;bottom: 0;`は親要素と同じ大きさにできる。

`width:100%;height:100%;`では親要素と同じ大きさにならない。
PythonでIMEをONにする

題名のままです。
なぜかググっても引っかからず、試行錯誤の末なんとかなったので、メモとして残しておきます。
アプリ立ち上げ時にimm32.ImmSetOpenStatus(h_imc, True)してもONになりません。
テキストエントリークリック時のイベントに仕込むとONになりました。
以下がコードです。

2021/07/01 追記
vimのIME制御を参考に、macとLinuxのIME制御も出来るようにしました。
ただし、macはデバックしていませんので、どなたか環境がある方コメント下さい。
https://github.com/fuenor/im_control.vim/blob/master/doc/im_control.jax

```
import ctypes
import platform
import subprocess
import tkinter


def ime_on(event):
    pf = platform.system()
    if pf == "Windows":
        user32 = ctypes.WinDLL(name="user32")
        imm32 = ctypes.WinDLL(name="imm32")
        h_wnd = user32.GetForegroundWindow()
        h_imc = imm32.ImmGetContext(h_wnd)
        imm32.ImmSetOpenStatus(h_imc, True)
        imm32.ImmReleaseContext(h_wnd, h_imc)

    elif pf == "Darwin":
        applescript = r'tell application "System Events" to keystroke (key code {104})'
        subprocess.run(["osascript", "-e", applescript])

    elif pf == "Linux":
        try:
            subprocess.run(["ibus", "engine", "mozc-jp"])  # ibusの場合
            subprocess.run(["fcitx-remote", "-o"])  # fcitxの場合
            subprocess.run(["echo", "1", ">", "$UIM_FEP_SETMODE"])  # uimの場合(合っているのか不明)
        except FileNotFoundError:
            pass


def ime_off(event):
    pf = platform.system()
    if pf == "Windows":
        user32 = ctypes.WinDLL(name="user32")
        imm32 = ctypes.WinDLL(name="imm32")
        h_wnd = user32.GetForegroundWindow()
        h_imc = imm32.ImmGetContext(h_wnd)
        imm32.ImmSetOpenStatus(h_imc, False)
        imm32.ImmReleaseContext(h_wnd, h_imc)

    elif pf == "Darwin":
        applescript = r'tell application "System Events" to keystroke (key code {102})'
        subprocess.run(["osascript", "-e", applescript])

    elif pf == "Linux":
        try:
            subprocess.run(["ibus", "engine", "xkb:jp::jpn"])  # ibusの場合
            subprocess.run(["fcitx-remote", "-c"])  # fcitxの場合
            subprocess.run(["echo", "0", ">", "$UIM_FEP_SETMODE"])  # uimの場合(合っているのか不明)
        except FileNotFoundError:
            pass


root = tkinter.Tk()
labelOn = tkinter.Label(text="IME ON")
labelOn.grid(row=0, column=0)
labelOff = tkinter.Label(text="IME OFF")
labelOff.grid(row=0, column=1)
txtOn = tkinter.Entry()
txtOn.bind(sequence="<Button-1>", func=ime_on)
txtOn.grid(row=1, column=0)
txtOff = tkinter.Entry()
txtOff.bind(sequence="<Button-1>", func=ime_off)
txtOff.grid(row=1, column=1)
root.mainloop()
```
新しいMacに綺麗にPython環境を構築し、Jupter Notebookを導入する方法（Catalina 10.15.5）

# 0. はじめに
Macの機種変更をしたのでローカルのJupterNotebookを使うために環境構築（インストール）をした際のメモです。
## 0-1. 前提事項
Macは標準でPython2.xが導入されています。ただ、Python3.xを扱いたいので、まずはPython3.xを導入します。
(Python2.xは他のアプリ等で使われる可能性もあるので、綺麗に残しておきます。)
また、個人の環境なので、そこまで必要ないのかもしれませんが、venvをつかって環境を分離します。venvを使うと、導入パッケージを複数独立して切り分けることが出来ます。（Python 3.3からは標準で導入されています。）
pyenvをつかった、Python2.xとPython3.xの切り分けは実施しません。
OSはCatalina 10.15.5です。

##0-2. 導入ステップ
1.1. Homebrewのインストール
1.2. Python3のインストール
1.3. pipのアップデート
1.4. venvの環境設定
1.5. 必要そうなライブラリのインストール
1.6. Jupyter Notebookのインストール

#1. 導入
ここらからが導入ステップです。
#1.1. Homebrewのインストール
導入方法を[こちら](https://qiita.com/Yanagawa_Yoshihisa/items/a2441d98d3c08250a8ed)を確認ください。

##1.2. Python3のインストール

```:ターミナル
$ brew install python3
```

#1.3. pipのアップデート
```:ターミナル
$ pip3 install --upgrade setuptools
$ pip3 install --upgrade pip
```
pipはPythonで使用されるパッケージマネージャーです。Python3のインストール時に同時に導入されます。Python3.xなのでpip3です。Permission系のエラーが出た場合は、Sudoをつけて実行してみてください。

#1.4. venvの環境設定
```:ターミナル
$ cd [導入ディレクトリ]
$ python3 -m venv [環境名]
```
これにより、[導入ディレクトリ]に[環境名]のフォルダが作成され、その下位に必要なコンポーネントが導入されます。
下記のコマンドによりvenvが有効化されます。ターミナルに（env）が追加されます。終了する場合は、｢deactive｣とコマンドして下さい。

```:ターミナル
$ . [環境名]/bin/activate
$ deactivate
```
#1.5. 必要そうなライブラリのインストール
Jupterで必要そうなPythonのライブラリをインストールします。

```:ターミナル
$ pip install numpy
$ pip install scipy
$ pip install scikit-learn
$ pip install Pillow
$ pip install pandas
$ pip install matplotlib
```
##1.6. Jupyter Notebookのインストール
```:ターミナル
$ pip install jupyter
```
#2.Jupter Notebookの起動
```:ターミナル
$ jupyter notebook
```
#3.おまけ（Jupyterthemesを用いた見た目の変更）
標準の設定は白ベースで、個人的にはフォントサイズも少し小さいのであまり好みではありません。Jupyterthemesという見た目を変えるパッケージを導入します。

```:ターミナル
$ pip install jupyterthemes
$ pip install --upgrade jupyterthemes
```
お好みのテーマを設定します。テーマの詳細はご自身で調べて下さい。

```:ターミナル
jt -t oceans16
```
私の設定です。

```:ターミナル
jt -t oceans16 -T -N -kl -fs 11 -ofs 10 -tfs 11 -dfs 9 -ofs 10 -cellw 80%
```

#3.おまけ2（JupyterLabの導入）
Jupter Notebookは2020年7月現在の状況で、開発は終了し、後継である｢JupyterLab｣への移管が正式に発表されています。
まだベーダ版ですが、拡張機能などいろいろとこちらの方が充実しているので、こちらを使うのも一つの方法だと思います。
（詳しくは[こちら](https://qiita.com/Yanagawa_Yoshihisa/items/4c6f1662f78123e7a988)に記載しております。）

インストール

```:ターミナル
$ pip install jupyterlab
```

起動

```:ターミナル
$ jupyter lab
```
以上です。

#最後に
初心者にもわかるように、Pythonで機械学習を実施する際の必要な知識を簡便に記事としてまとめております。
目次は[こちら](https://qiita.com/Yanagawa_Yoshihisa/items/a3fa521d89c471b3e71f)になりますので、他の記事も参考にして頂けると幸いです。
rubyのバージョン上げてrails sしようとしたらLibrary not loaded


エラーの抜粋
```
/Users/hoge-user/projects/freee-auth/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require': dlopen(/Users/hoge-user/projects/freee-auth/vendor/bundle/ruby/2.6.0/gems/mysql2-0.4.10/lib/mysql2/mysql2.bundle, 9): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (LoadError)
  Referenced from: /Users/hoge-user/projects/freee-auth/vendor/bundle/ruby/2.6.0/gems/mysql2-0.4.10/lib/mysql2/mysql2.bundle
  Reason: image not found - /Users/hoge-user/projects/freee-auth/vendor/bundle/ruby/2.6.0/gems/mysql2-0.4.10/lib/mysql2/mysql2.bundle
```

Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (LoadError)
と言っているのでbundle doctor

```
The following gems are missing OS dependencies:
 * mysql2: /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib
 * mysql2: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib
```

mysqlが依存しているopenssl系の何かが足りないと言っている。
rubyのバージョンアップに伴ってopensslを入れ替えたからか。
ということでmysqlをinstallしなおして依存しているgemライブラリを入れ直す

```
bundle exec gem uninstall mysql2
bundle install # mysqlのgemも一緒に入るはず
```

rails s すると正常に動いた
```
$ bundle exec rails s -p 3099
=> Booting Puma
=> Rails 5.2.4.3 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 4.3.1 (ruby 2.6.6-p146), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://127.0.0.1:3099
* Listening on tcp://[::1]:3099
```
Nuxt.jsのクエリパラメータの使い方覚書

#ソースコード全文
遷移元

```hello.vue
<template>
  <div>
    <p>名前：</p>
    <input type="text" v-model="name" />
    <p>入力値：{{name}}</p>
    <hr />
    <p>血液型：</p>
    <input type="text" v-model="blood_type" />
    <p>入力値：{{blood_type}}</p>

    <button @click="jumpPage">飛ぶ</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: "",
      blood_type: ""
    };
  },
  methods: {
    jumpPage() {
      this.$router.push({
        path: "bye",
        query: { name: this.name, blood_type: this.blood_type }
      });
    }
  }
};
</script>
```

遷移先

```bye.js
<template>
  <div>
    <button @click="back">戻る</button>
    <button @click="result">渡ってきたクエリ</button>
    <p>名前：{{this.name}}</p>
    <p>血液型：{{this.blood_type}}</p>
  </div>
</template>
<script>
export default {
  data() {
    return {
      name: "",
      blood_type: ""
    };
  },
  methods: {
    back() {
      this.$router.go(-1); //画面遷移後、元の画面に戻る
    },
    result() {
      this.name = this.$route.query.name; //渡ってきたクエリパラメータをローカルなデータ代入
      this.blood_type = this.$route.query.blood_type;
    }
  }
};
</script>

```

#point
1. 遷移元：`this.$router.push`でページ遷移
2. 遷移先：`this.$route.query.hogehoge`でクエリパラメータ`?hogehoge=~~`で渡ってきた`~~`のところを取得（ちなみに遷移先のパラメータ全文は`http://localhost:3000/bye?name=aaaa&blood_type=A`）
3. 遷移先`this.$router.go(-1)`で元のページに戻ることができる。
Delphi/C++Builder/RAD Studio 10.4 Sydneyのオンラインドキュメントをブラウザのアドレスバーから検索したり、閲覧中のウェブページで選択中のキーワードで検索したりする

## サマリ

Delphi/C++Builder/RAD Studio 10.4 Sydney は Windows, macOS, iOS, Android, Linux 向けの開発が行えますが、25年間の歴史があるため、Google 検索等で検索すると過去バージョンに関する情報が見つかったりして、欲しい情報がすぐに見つからないことがあります。しかしオンラインドキュメントはバージョンごとに提供されていますので、使用するバージョンのオンラインドキュメントだけに検索スコープを絞って検索すれば、ノイズを減らすことができます。

10.4 の場合はオンラインドキュメントを次のURLで検索できます。%s は検索したいキーワードに置換してください。

```
http://docwiki.embarcadero.com/RADStudio/Sydney/j/index.php?search=%s
```

たとえば http://docwiki.embarcadero.com/RADStudio/Sydney/j/index.php?search=FireDAC+サンプル のようにすれば、10.4 向けのFireDAC のサンプルに関する検索結果が取得できます。

さて、この検索URLをブラウザの検索エンジンに登録すれば、アドレスバーからキーワード検索できるようになります。またブックマークレットにしておいて任意のページで選択中のキーワードを検索するような方法もとれます。


## ブラウザの検索エンジンに登録する

基本的な方法はこちらの記事の通りなので、アドレスバーから検索したい方はこちらを読んで登録してください。この記事では Chrome, Firefox向けの方法を説明しています。
https://qiita.com/kazinoue/items/214b08660c46fbe290a1


## アドレスバーからの手入力での検索ではなく、閲覧中のページのキーワードを選択して検索したい。

閲覧中のサイトで見つけたキーワードで検索するときに、アドレスバーからの検索だと次の4ステップでの操作となり、少し面倒のような気がします。

1. 選択する
2. コピーする
3. アドレスバーに移動する
4. 検索エンジンを選択するキーワードと検索キーワードを入力する

でも、「ブラウザで選択中のキーワードを検索するブックマークレット」を作れば、そのキーワードですぐに docwiki.embarcadero.com を探せます。

これは次のようなブックマークレットのコードとして実装できます。構文を見やすくするためにインデントと改行を含めていますが、実際のブックマークレットではこれが改行なしの一行のコードになります。

```javascript
javascript:(function(){
    var q = window.getSelection ? window.getSelection().toString() : document.selection.createRange().text;
    if ( q.length > 0 ) {
        var g = 'http://docwiki.embarcadero.com/RADStudio/Sydney/j/index.php?search=' + q;
        window.open(g);   
    }
})();
```

ブラウザへのブックマークレットの登録は、任意のページをブックマークしたのち、そのページのURLを上記のブックマークレットのコードに差し替えるだけです。

そうすると、このようにページ内の文字列を選択してブックマークレットを選ぶだけで検索実行できます。

![licecap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149350/6ab00fdc-00dd-ffea-4612-8e0f9fc9930c.gif)


ブックマークレットを作ったり操作するよりは右クリックメニューでやりたい、という場合は上記のコードを流用した [Tampermonkey](https://www.tampermonkey.net/) 向けのスクリプトを作成し、右クリックメニューから検索実行できるようにする方法もあります。

```javascript
// ==UserScript==
// @name         Search docwiki.embarcadero.com (10.4 Sydney)
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  選択した文字列を docwiki.embarcadero.com で検索する
// @author       Kazuhiro Inoue
// @match        *
// @grant        none
// @run-at       context-menu
// ==/UserScript==

(function() {
    'use stricts';

    var q = window.getSelection ? window.getSelection().toString() : document.selection.createRange().text;
    if ( q.length > 0 ) {
        var g = 'http://docwiki.embarcadero.com/RADStudio/Sydney/j/index.php?search=' + q;
        window.open(g);
    }
})();
```

実際にやってみると、右クリックメニューに Tampermonkey の項目があり、そこから今回作成したスクリプトで検索実行できます。
![licecap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149350/90141d4e-3774-b942-bfd7-8cc2846433a0.gif)


今回利用した Tampermonkeyは任意のユーザーコードをブラウザで利用できるようにする拡張機能です。以前ならこの手の機能は自分専用の拡張機能として作成してブラウザにインストールすればよかったのですが、最近のブラウザは拡張機能のストア以外からのインストールに制約があります。自分専用の機能拡張を行う場合は Tampermonkey のように任意のコードを実行できるブラウザ拡張機能を利用するほうがカスタマイズが行いやすい状況があります。

ただしTampermonkey のように任意のコードを容易に拡張機能として実行できる仕組みを利用する場合は、自分が理解できるか、または信頼できる入手先からのコードだけを用いてください。コードの内容を理解できない場合は、たとえばこの記事のコードでも使用を避けてください。
webシステムで認証するときのパスワードの扱い方

# 事の発端

webシステム初心者として認証するときにパスワードをどのように扱えばいいかよくわからない。
とりあえず以下のことに気をつけたい。

- frontend → backend へusername、passwordをPOSTする際に、平文は避けたい
- DBに登録しておくパスワードは平文で保存したくない
- DBに登録したパスワードは、簡単に復号化させたくない

# 構成

- frontendはjavascript/typescript でcrypto が使えれば何でも良いはず
- backendも同様にjavascript/typescript でcrypto、bcrypt が使えること。expressを想定しています
- 認証するのは独自のユーザ情報が格納されたDB内のデータで認証

# どうやって暗号化・復号化するかしてDBに格納するか

[こちらの記事](https://qiita.com/ysti/items/05aaf14b303ff6b13519)を参考にさせていただきました。
bcryptに関しては[こちらの記事](https://qiita.com/tatsumi44/items/83ac5c18f213e22ed322)を参考にされていただきました。

流れはこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65929/ea4f8e2b-1cc2-d20f-be8b-f8bcf389a606.png)

とりあえず、frontend、backendの両方で以下のような関数とパスフレーズを保持。

```typescript
import crypto from 'crypto';

const passphrase = 'hogehoge';
const algorithm = 'aes-256-ctr';

function encrypt(pass: string): string {
  const cipher = crypto.createCipher(algorithm, passphrase);
  let crypted = cipher.update(pass, 'utf8', 'base64');
  crypted += cipher.final('base64');
  return crypted;
}

function decrypt(decryped_pass: string): string {
  const decipher = crypto.createDecipher(algorithm, passphrase);
  let dec = decipher.update(decryped_pass, 'base64', 'utf8');
  dec += decipher.final('utf8');
return dec;
```

hash化は以下の処理で生成。

```typescript
import bcrypt from 'bcrypt';

let hashed_password = bcrypt.hashSync(password, 10);
```

# どうやって認証させるか


流れはこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65929/69261b6e-fce7-c002-12b6-c76d850db7aa.png)

まずはDBに保存するためのhash化。ユーザ登録時にはこのhash化した値をパスワードとして保存。


entity はちょっと省きますが、こんな感じでusernameに紐づくデータを
検索して、見つかったらパスワードを復号化し、hash値と比較。

```typescript
import bcrypt from 'bcrypt';
import { createConnection, getRepository } from "typeorm";
import { User } from "../entity/User";

const user = getRepository(User).findOne({username: username});
if (user) {
  const dec = decrypt(req.body.password);
  if (bcrypt.compareSync(dec, hash_password)) {
    res.sendStatus(200);
  } 
}

res.sendStatus(401);
```

とりあえず、こんな感じでやれば目的は達成できてるんじゃないかな。。。
ここまでお付き合いしていただきありがとうございます。
間違ってrebaseしてリモートリポジトリにpushしちゃったけど、戻したい

表題の通り慌てたので、備忘録。

## 経緯
ブランチで作業してからrabaseした後、問題ないかと思ってリポジトリにpushしたが、
間違いが判明し、戻したくなった。ある程度作業した後にrebaseするとrebase元のコミットとごちゃごちゃになってしまう。
これの対応方法は別途調べるとして、結果から言うと下記で戻しました。

```
git reflog 
git reset --hard HEAD@{番号}
git push -f origin ブランチ名
```

## git reflog
履歴やブランチの切り替え履歴を参照できる。
git resetを行うとgit logでは履歴が消えてしまうが、git reflogでは履歴が残る。

## git reset --hard HEAD@{番号}

--hard
「HEADの位置・インデックス・ワーキングツリー」全てが対象
最後のコミットの状態に戻す

他のオプションとして、

| オプション | 対象 | 使用例 |
|:------------------:|:------------------:|:------------------:|
| --mixed | オプションを指定しない場合はデフォルトでこれになる。｢HEADの位置・インデックス｣が対象 |        addを取り消したい時など        |
| --soft  |「HEADの位置」のみ | コミットのみ削除など       |

HEAD@{番号}はgit reflogで確認できる

## git push -f origin ブランチ名
強制的にpushする
※共有されたブランチで使用する場合には要注意。

以上です。
いいねやQiitaやTwitterのフォローいただけると励みになります！
他にも方法がありましたら、コメントお待ちしております。
宜しくお願いします〜

## 参考
[git pull と git pull –rebase の違いって？図を交えて説明します！](https://kray.jp/blog/git-pull-rebase/)
[【 git revert／git reset 】コマンド――コミットを取り消す](https://www.atmarkit.co.jp/ait/articles/2006/11/news025.html#_ga=2.223338203.1934198456.1594788553-267913256.1592824826)
[git push を強制する -f (–force)と –force-with-lease](http://www-creators.com/archives/5168)
[[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法](https://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe)
【React Native】Font Awesomeのアイコンを使う

React Nativeの学習中にFontAwesomeのアイコンを使用するやり方が若干めんどくさかったので、
メモとして残しておく

# ライブラリのインストール

```
# expo-fontのインストール 
npm install expo-font --save
```

# FontAwesomeのサイトから使用するアイコンをダウンロード
公式サイト
[FontAwesome.com](https://fontawesome.com/)

### ①Start For Freeをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/99576d9d-3d2c-428a-9415-41c40c557ce3.png)

### ②画面を下にスクロールしてdownloadをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/448de85c-6448-3a32-07c3-c86de3d15e08.png)

### ③Download Font Awesome Free for the Webをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/c1944f9a-0c62-eaef-bdfd-dab1be06ff28.png)

### ④ダウンロードしたwebfontsを`自分のproject/assets/fonts`の配下に置く
※今回は`fa-solid-900.ttf`を使用する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/f1d7c395-a37d-8089-e758-325219cb00d4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/3dc49848-c895-68f2-d475-b1549f820b08.png)

# 使用するアイコンを決める
### ①公式サイトのSearch Iconsで使用したいアイコンを検索する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/b8bbdb47-d3d3-210c-4bb8-c657c98f14ab.png)

### ②使用するアイコンのユニコードをコピーする
※今回だと`f062`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514870/624b9ab1-73b9-7092-5960-0c8a3b7dd1ed.png)




# 実装
```App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

import * as Font from 'expo-font';
import fontAwesome from '../../assets/fonts/fa-solid-900.ttf';

export default class App extends React.Component {
  // fontAwesomeのLoad 
  componentDidMount() {
    Font.loadAsync({
      FontAwesome: fontAwesome,
    });

  render() {
    return (
      <View style={styles.container}>
        <Text>
          // \u + 先ほどコピーしたユニコードにする
          {'\uf062'}
        </Text>
      </View>
    );
  }
}

// 省略
```

# 補足
これで一応表示はできるが、実際はLoadをasync awaitで待ったり、
ユニコードを直接指定するのではなく、expo/vector-iconsを使って名前を指定したりするけど
とりあえず今回は表示まで。。
PULSEで顔画像を毛穴や質感が分かるくらいの高画質に変換する

#1.はじめに
![スクリーンショット 2020-07-14 14.08.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/f844b4b7-9a84-abf2-15d3-55daebb16e13.png)
　皆さんは、低解像度の画像を高解像度の画像に変換したいと思ったことはありませんか。

　2020年6月デューク大は従来8倍が最大であった高解像度変換を64倍にまで引き上げる **PULSE** という手法を発表しました。これにより、目元も口元もハッキリしないモザイクの様な顔画像が**毛穴や質感が分かるくらいの高画質に変換できます。**

　今回は、Githubで公開されているソースコードを試してみた結果をご紹介します。なお、コードは Google Colab で作成し Github に上げてありますので、自分でやってみたい方は、この[**「リンク」**](https://github.com/cedro3/pulse/blob/master/Pulse_test.ipynb)をクリックし表示されたシートの先頭にある**「Colab on Web」**ボタンをクリックすると動かせます。 

#2.アルゴリズム
![transformation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/06ecf34c-3e51-7061-cf91-0708dc6f788e.gif)
　アルゴリズムは、**学習済みGANモデル**を使って**「ある高解像度画像から低解像度変換した画像」**を生成し、これと**「低解像度の画像」**との差を**ロス**として、**ロスを最小化する様な高解像度画像を求める**というものです。

　気になるのは、学習済みGANモデルを使っているので、学習に使った画像は上手く高解像度変換できると思いますが、学習に使っていない画像も上手く行くのかということです。

　ということで今回は、色々な顔画像からわざと低解像度画像(32×32ピクセル)を作り、これをどの程度正確に高解像度変換(1024×1024ピクセル)できるのか見て行きたいと思います。最適化回数(steps)は1000とします。

#3.学習済みの顔画像（FFHQデータセット）
　**PULSE**で使われている学習済みGANモデルは[StyleGAN](https://github.com/NVlabs/stylegan)です。従って、学習には[FFHQデータセット](https://github.com/NVlabs/ffhq-dataset)が使われており、この顔画像は少なくとも上手く行くはずです。それでは、FFHQデータセットから3つの画像を取り出してやってみます。

![スクリーンショット 2020-07-14 15.21.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/56902c0f-8ce4-a854-fe49-5ece3a481d02.png)

　Low Resolution は低解像度画像(32×32)、High Resolution はそれを高解像度変換した画像(1024×1024)、Real はLow Resolution を作成した元画像です。従って、**High Resolution が Real にどれだけ近づいているかで高解像度変換の精度が分かります。**当たり前ですが学習済み画像では、問題なく高解像度変換できています。 

#4.未学習の顔画像（外国人）
　それでは、未学習の顔画像（外国人）ではどうでしょうか。Web検索で取得した3人の外国人の顔画像でやってみます。

![スクリーンショット 2020-07-14 15.21.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/45489765-fbe9-314d-3a7d-b2c13a0979d0.png)
　ほほー、外国人なら学習に使っていない顔画像でも、上手く行くようです。


#5.未学習の顔画像（日本人）
　それでは、未学習の顔画像（日本人）ではどうでしょうか。Web検索で取得した3人の日本人の顔画像でやってみます。確か、FFHQデータセットには、あまり日本人は居なかったようなので、どうなるでしょうか。

![スクリーンショット 2020-07-14 15.21.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/22a5c82d-b029-cbff-0a9b-2ce21536d7d4.png)

　思ったらより良い出来です。High Resolution の方が Real に比べて、目がちょっと違うのと顔のシワが多い様に思いますが、まあまあではないでしょうか。最適化回数(steps)を増やすと、もう少し改善されるようです。

#6.現実場面を想定したテスト
　さて、今までは高画質画像をわざと32×32ピクセルに縮小して、それを高画質画像に変換するテストを行って来ましたが、現実場面でやりたいのは初めから低画質である画像を高画質に変換したいわけです。ここでは、そのテストをやってみます。

　元乃木坂46の初代キャプテン桜井玲香の[Wekipedia](https://ja.wikipedia.org/wiki/桜井玲香)には、冒頭にこんな画像があります。
![reika.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/77c46108-4808-04cd-3d50-9b180a773f9f.jpeg)
　画像サイズは 190×253ピクセルで、顔の部分だけだと90×90ピクセルくらいでしょうか。それでは、この画像から顔画像を切り取り高画質変換してみます。最適化回数(steps)はガッツリ6000でやってみます。

![reika_S_6000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209705/d2b0c0cb-0bee-eacf-3d06-051f18a4d780.png)


　おっ！　これ、結構いけてると思いませんか。

#7.まとめ
　予想を超える高画質変換が可能なことに驚きました。考えてみれば、32×32ピクセルの画像は、1024次元のベクトルと同じ情報量を持っているわけで、そこには高画質画像を引き出すためのポテンシャルは十分あるわけですね。

　それにしても、デューク大面白いことを考えました。従来の方法とは全く違うPULSEという手法、興味深いです。
node_modulesをLambdaレイヤーにアップロードしてライブラリを使用する

# 経緯
- 同じライブラリを使用しているLambda関数が増えた
- コンソール画面からコードを編集できるようにしたい
- 上記の理由から、layerに共通ライブラリを登録し、それを読み込ませることでLambda関数を軽量化する

# 環境
- ローカルOSはWindows10（Macでもで順は同様）
- LambdaはNode.jsを使用

# node_modules準備

- 『nodejs』という名前のフォルダを作成する

<font color="Red">※名前がめちゃくちゃ大事です。layerはnodejs/node_modulesのフォルダ構成じゃないと読み込んでくれません（正確に言えばopt直下に生成されるのでopyt/nodejs/node_modulesとなりますが気にしなくて良いです）</font>

① 新規で準備する場合

→作ったフォルダ直下にnode_modulesを作成

```
npm install 必要なライブラリ名
```


② すでにnode_modulesフォルダが有る場合

→node_modulesフォルダを先程作ったnodejsフォルダに移動する

- nodejsフォルダをzip化する

# Lambdaレイヤーの作成

- AWS Lambdaのレイヤーから「レイヤーの作成」をクリック

![SnapCrab_NoName_2020-7-15_13-7-27_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553852/583f058d-b239-5b72-0377-b516eb7f15eb.png)

- 名前を入力
- 先程zip化したフォルダをアップロード
- ランタイムを選択（今回はNode.jsを選択）
- 「作成」ボタンをクリック

![SnapCrab_NoName_2020-7-15_13-10-2_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553852/48e696c7-cdf7-b6ec-6d04-23ce3dd658fe.png)


これでレイヤーは完成です

# Lambda関数への適用

- 適応するLambda関数の「設定」→「デザイナー」→「Layers」を選択し、下の「レイヤー」→「レイヤーの追加」をクリック

![SnapCrab_NoName_2020-7-15_13-16-44_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553852/e49ed33c-124a-92e7-ed4e-26c0930ef68f.png)

- 名前とバージョンを先程作ったレイヤーに設定（新規で作った場合はバージョン1です）

![SnapCrab_NoName_2020-7-15_13-21-10_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553852/7d288ce3-4ba9-d0b3-8b9d-df990a55bcb4.png)

- 各関数からレイヤーに上げたモジュールを削除する


これでパッケージとして動いていた時と同じ動作をするはずです
Scullyのセットアップ手順[Angular]

# はじめに
Angular製の静的サイトジェネレータ「Scully」を使ってJAMstackアーキテクチャで開発することになったので、その作業手順をメモ。(実施は2021年なので、ヴァージョン等古いです。。)


# 環境
```
Angular CLI: 9.1.10
Node: 10.21.0
OS: darwin x64

Angular: 9.1.11
... animations, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.10
@angular-devkit/build-angular     0.901.10
@angular-devkit/build-optimizer   0.901.10
@angular-devkit/build-webpack     0.901.10
@angular-devkit/core              9.1.10
@angular-devkit/schematics        9.1.10
@angular/cli                      9.1.10
@ngtools/webpack                  9.1.10
@schematics/angular               9.1.10
@schematics/update                0.901.10
rxjs                              6.5.5
typescript                        3.8.3
webpack                           4.42.0
```

# セットアップ手順
まず、Angular CLIがなければ、インストール。

ここで、最新ヴァージョンのAngular10を入れたせいで後々詰まって、最初に手戻りました。。
このissueに書いてあるように、v9.1.6をインストールすれば上手くいきました。
https://github.com/scullyio/scully/issues/543

```
$ npm install -g @angular/cli@9.1.6
```
そして、Angularの新規プロジェクトを作成します。自分はmy-appというプロジェクト名に。
Scullyではルーターがアプリケーションに存在する必要があるので、routingはYesを選択。
stylesheetのフォーマットはSCSSを選択しました。

```
$ ng new my-app
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS
```

そして、必要なライブラリを追加します。

```
$ cd my-app

$ ng add @scullyio/init
```
これでセットアップは完了

# 実行

```
$ ng build
$ npm run scully
```
以上、コマンドで実行して問題なく完了すれば、
dist/static/配下に静的ファイルが生成されているので、ファイルをホスティングすればOK。
「アクセスが拒否されました」のプロセスの強制終了

現在実行中のプロセスをコマンドプロンプトから終了するためには、以下のようにtaskkillコマンドを実行します。
```
taskkill /IM /F Sample.exe
```
管理者権限で実行しているプロセスに対して上記コマンドを実行すると

```
エラー: プロセス "Sample.exe" (PID 15448) を強制終了できませんでした。
理由: アクセスが拒否されました。
```
というエラーメッセージが表示され、プロセスが終了できないことがあります。
この場合は、以下のコマンドを試してみてください。

```
wmic process where "name='Sample.exe'" delete
```
実行後、「インスタンスは正しく削除されました。」と表示されればプロセス終了です。
CNNの畳み込み層のinputとoutputのshapeの変換式のメモ

<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_CHTML"></script>
<script type="text/x-mathjax-config">
 MathJax.Hub.Config({
 tex2jax: {
 inlineMath: [["\\(","\\)"] ],
 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
 }
 });
</script>

CNNの畳み込み層での変換によってFeature mapsがどう変換されるのか分からなかったので調べた．

# 結論
普段pytorchを使用するので，[pytorchのドキュメント](https://pytorch.org/docs/stable/nn.html#conv2d)を調べたら書いてあった．導出もできれば良かったが，今回は結果だけわかれば良かったのでまたいつか．

$$ 
H_{out} = \left\lfloor\frac{H_{in} + 2 \times \text{padding}[0] - \text{dilation}[0] \times (\text{kernel\_size}[0] − 1) − 1}{\text{stride}[0]} + 1\right\rfloor 
$$

$$
W_{out} = \left\lfloor\frac{W_{in} + 2 \times \text{padding}[1] - \text{dilation}[1] \times (\text{kernel\_size}[1] - 1) - 1}{\text{stride}[1]} + 1\right\rfloor
$$

$H_{in}$：変換前のhight
$W_{in}$：変換前のwidth

$H_{out}$：変換後のhight
$W_{out}$：変換後のwidth

$\text{kernel\_size}[0]$：hightのkernel\_size(デフォルトは1)
$\text{kernel\_size}[1]$：widthのkernel\_size(デフォルトは1)

$\text{padding}[0]$：hightのpadding(デフォルトは0)
$\text{padding}[1]$：widthのpadding(デフォルトは0)

$\text{dilation}[0]$：hightのdilation(デフォルトは1)
$\text{dilation}[1]$：widthのdilation(デフォルトは1)

$\text{stride}[0]$：hightのstride(デフォルトは1)
$\text{stride}[1]$：widthのstride(デフォルトは1)
Arduino(C++11)でコールバック関数を無名関数として書く(名前空間の汚染について)

[前回](https://qiita.com/dojyorin/items/4bf068aef2b248f1306e) に続き、Arduinoの小ネタです。

Arduinoでコールバック関数を書くとき、予め関数を宣言してから、コールバック登録関数にその関数を代入するのが普通でした。

```c++:Arduino
uint32_t callback(uint8_t arg){
    return arg * 2;
}

app.setCallback(callback);
```
しかし、ArduinoはC++11の新機能であるラムダ式が使えます。

ラムダ式を使うことで、JavaScriptのようにコールバック関数を無名関数として登録することができます。

```js:JavaScript
app.addEventListener("load", ()=>{
    // hogehoge
});
```


# 書き方
```c++:Arduino
app.setCallback([](uint8_t arg){
    return (uint32_t)(arg * 2);
});
```
このようになります。

最初の`[]`はキャプチャと言い、外側にある変数をラムダ式の中へ取り込むことができます。
しかし、凝ったことをしない限りは`[]`で何もキャプチャしないような使い方になると思います。

```c++
uint8_t num = 0;

// num を "コピー"
auto lambda1 = [num](){
    Serial.println(num);
}

// num を "参照"
auto lambda2 = [&num](){
    Serial.println(num);
}
```
次の`(){}`は、普通の関数と同じく、引数と中身になります。
ただし注意点としては、ラムダ式の返り値は`return`値から推論されます。
なので、コールバックの返り値が数値型など複数の型となりえる場合は、返したい型へ明示的にキャストしてあげると良いです。

```c++
// app.setCallbackの関数の返り値は uint32_t とする
app.setCallback([](uint8_t arg){
    // 返り値は暗示的に uint8_t と推論される => NG
    return arg++;
    // 返したい型 uint32_t へ明示的にキャストする
    return (uint32_t)arg++;
});
```
最後に、コールバック関数の場合は必要ありませんが、末尾に`()`を付けることでそのラムダ式を即時実行できます。

```c++:IIFE
auto lambda = [](uint8_t arg){
    return arg;
}(0xFF);
// 末尾に () を付ける
// 引数も代入できる
```



# 利点
ラムダ式を使用する利点としては、名前空間の汚染を抑えることができます。
即時実行関数としても使えることから、変数スコープを制限するためのクロージャとしても強力です。

例えば、Arduinoでは`setup()`関数の中でコールバック登録などの各種初期化をすると思います。
そして、通常であればコールバック関数はグローバル空間で宣言します。

```c++:例1
MyApp1 app1;
MyApp2 app2;

volatile uint32_t foo = 0x12345678;

void callback1(){
    Serial.println(foo);
}

void callback2(){
    Serial.println(foo);
}

void setup(){
    app1.setCallback(callback1);
    app2.setCallback(callback2);
}
```
変数`foo`は、スケッチの至るところから変更される可能性がある想定です。

この時点で関数`callback1`と`callback2`がグローバル空間を汚染しています。
これを、ラムダ式を使用した書き方にすると、以下のようになります。

```c++:例2
MyApp1 app1;
MyApp2 app2;

volatile uint32_t foo = 0x12345678;

void setup(){
    app1.setCallback([](){
        Serial.println(foo);
    });

    app2.setCallback([](){
        Serial.println(foo);
    });
}
```


# おまけ
グローバル空間の汚染を防ぐ手段として、名前空間`namespace`を利用するのも有効な手です。

例えば、グローバル空間に宣言しなければならない変数は、このように名前空間としてひと纏まりにすると、分かりやすくなります。

```c++:Arduino
namespace Config{
    volatile bool foo = false;
    volatile uint8_t bar = 0x00;
}

// 名前空間名::変数 で使える
void setup(){
    Serial.println(Config::foo);
}
```

また`namespace`に名前を付けないで使用する "無名名前空間" と手法があります。
これは、ソースファイルが複数に跨っている場合、ソースファイル単位でスコープを限定するのにとても役立ちます。

```c++:Arduino
// 名前を付けない
// このソースファイルのみで使え、他のソースファイルからは参照できない
namespace{
    volatile bool foo = false;
    volatile uint8_t bar = 0x00;
}

void setup(){
    // そのまま使える
    Serial.println(foo);
}
```
「Anvil」を使ってみた。

Python勉強して、Djangoのチュートリアルも一通り済ませたところで、「Anvil」というWebアプリが作成できるプラットフォームの話題が耳に入ってきたので、さらっと触ってみましたので、感想です。
※技術的な情報はほぼありません。すみません。

[Anvil](https://anvil.works/)
![anvil_toppage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667657/71f94631-e8b4-abea-486d-5b228238fc3e.png)

一応、「Start Here」の3つのチュートリアルは済ませました。総計で4～5時間程度と思います。
[Anvil/Learn Centre/Start Here](https://anvil.works/learn/tutorials#starthere)

---
- 良い点
    - 比較的、単純なユーザーインターフェース。
        - 感覚としては、ExcelでVBAアプリを作成するのに近いかと思います。
    - コード(ロジック)とデザイン(ビュー)が完全分離されていてわかりやすい。
        - 好みや慣れもあると思いますが、HTMLにコード埋め込んだりすると「読みにくいな～」とか思ってしまうので。
    - 基本的な機能の利用はブラウザ上で完結する。
        - 開発環境を手元に作る必要がないので、始めるのが簡単です。
        - 外部データの取り込み(データベースへのアクセスなど)もできるので、まだ試していませんが、ローカルPC上のデータとかクラウド上のデータベースとも繋げそうです。
    - 無料プランでも作成したWebアプリの公開が可能。
        - URLが固定、など制限はありますが、万人に使ってもらうようなアプリ(サイト)を作るのでなければ問題ないと思います。
    - Pythonの基本的な記述方法が理解できていれば、OK。
        - 一応、HTMLやCSSの直書きもできるようですが、画面デザインはドラッグアンドドロップとプロパティの設定でほぼほぼ完結するので、HTML/CSSを知らなくてもそれっぽい画面が作れます。
        - 同様にデータベースを扱う際にもSQLの知識などは不要です。とはいえ、Pythonでデータベース上のデータを扱う方法については多少かじっておく方が理解は早いと思います。

- 悪い点
    - チュートリアル＆ドキュメントなどすべて英語。
        - 中学生レベルの英文法とブラウザの翻訳拡張機能である程度どうにかなりますが、苦手意識があるとちょっと辛いです。
        - 動画や、キャプチャ画面が多いので、英語読めなくてもなんとなくできてしまうかもしれません。

---
- お勧めできる人
    - HTML/CSSで画面デザインができない／苦手な人。
    - Pythonを勉強して、Pythonで何かできないかな？、と模索中の人。
    - プログラミング勉強中で「何か動く」と楽しい人。

がっつり使い込む使い方もできそうですが、そうでもない人でも扱いやすそうに感じたので、日々の事務作業の効率化や、プログラミングの勉強、など間口は広いように思いました。

以上、乱文失礼いたしました。