OmniAICreator/Qiita-1.07M huggingface dataset:
マウスを押しながら移動最中のイベントを定期的に処理（web ブラウザ内）

こんにちは。
web ブラウザ画面内において、マウスを押しながら移動最中のイベントを検出してみました。そしてマウス座標を定期的[^1]に求めました。

[^1]: ただし今回は真の定期的処理ではないです。真の定期的処理へ書き換えるには ```setTimeout(...)``` を ```timer = setInterval(()=>console.log(posCurrent), timerDelta);``` とするべきですが、反面、マウスを押しながら移動を止めた後も処理は止まらず続行されます。

```console
// console.log
{x: 317, y: 178}
{x: 324, y: 169}
```

```html
<!DOCTYPE html>
<html>
  <body>
  <script>
  const timerDelta = 500; // in ms
  let timer = 0, posOrigin, posCurrent;

  const posMouse = e => ({x: e.pageX, y: e.pageY});

  const mouseDown = e => {
   	posOrigin = posMouse(e);
  	document.addEventListener('mousemove', mouseMove_with_timer);
  };

  const mouseUp = e => {
	document.removeEventListener('mousemove', mouseMove_with_timer);
  };

  const mouseMove_with_timer = e => {
	posCurrent = posMouse(e);
	if (!timer) {
	    timer = 1;
	    setTimeout(() => {timer=0; console.log(posCurrent)}, timerDelta);
	}
  };

  document.addEventListener("mousedown", mouseDown, false);
  document.addEventListener("mouseup", mouseUp, false);
  </script>
  </body>
</html>
```

なお、押しながら移動最中ではなく、移動を終えボタンを離したイベントを対象とするのならば、より簡素になります（下記）。

```javascript
const mouseDown = e => {posOrigin = posMouse(e)};
const mouseUp = e => {console.log(posMouse(e))};
```
GASで動的HTML① HTMLの中のJavascriptを実行して動的HTMLを作成する。(Dog API使う)


# GASで動的HTMLページを作成したかった。

GASのHTMLが気になったので試しに作ってみました。コンテンツとしてDog APIを利用させていただきました。


# Dog APIを使ってみた。
Dog APIはトークン（認証）なし・ヘッダーオプションなしで利用できるので非常にお手軽です！

> Dog API（犬の画像をランダムに1枚返してくれるAPI）
> https://dog.ceo/dog-api/

まずは、Dog APIの仕様を確認してみましょう。

Dog APIのエンドポイントにfetchするとjson形式のresponseが返ってきます。
とてもシンプルなjsonで、jsonの中のmessageという名前のプロパティがあり、そのパラメータとして画像urlが格納されているようです。それを使ってWebページに表示してみたいと思います。

# GASのHTMLの概要

GASのHTMLはちょっと独特です。
基本的には１つの.gsのGASファイルと１つの.htmlファイルが最小構成となります。
詳しい説明は後述の参考サイトなどを参照ください。

Javascript（GASではない）や、CSSも使うことが出来ます。


# 実装

Googleドライブにスタンドアローンのプロジェクトを作成し、同じプロジェクト内に下記の2つのファイルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/1f0388c2-c458-41cc-960b-f55708f8afa5.png)

## コード
### main.gs

```Javascript
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index");
}
```

GASでHTMLする時の基本中の基本の構文です。
引数で渡すのはhtmlのファイル名です。ここではindexとしていますが、それ以外の名前を付けた場合は引数の名前も統一する必要があります。

### index.html

下記はとりあえず表示できれば良いというコードです。
下記だけをを見ると基本的には普通のHTMLと同じですね。（一部GASならではの書き方もあるようです。）

今回は画像を表示するためにimgタグを動的に作成したいと思います。
createElementでimgタグを生成して、そのsrcにDogAPIから取得した画像アドレスを渡してbodyタグにアペンドします。

```HTML

<!DOCTYPE html>
<html>
<body>
  <script>
    const url = "https://dog.ceo/api/breeds/image/random";
    
    function fetchDogImagePath(url){
        return fetch(url).then( (res) => res.json());
    }
    
    async function fetchDogImage (url){
        const response = await fetchDogImagePath(url);
        const imageElement = document.createElement('img');
        imageElement.src = response.message;
        document.body.appendChild(imageElement);
        }
    fetchDogImage(url);
  </script>
</body>
</html>
```


# デプロイする
コードが書けたら、実際に公開するにはデプロイが必要です。
「ウェブアプリケーションとして導入」の設定を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/157e2820-c975-7fc1-0386-8851def5fcd6.png)

表示されたURLをコピーします。
このURLがWebページのアドレスになります。Execute（実行者）は自分、アクセス者はアノニマスにします。
アクセス者の値を変更するとGoogleアカウントログイン済みのユーザでないとアクセスできなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/b1e4756b-898a-51d6-63b4-b4ca16d2b176.png)


# アクセスする
↑で発行されたURLをブラウザのアドレスバーにコピペしてアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/629437c4-6ada-b830-4734-1e907bd6f6ad.png)
わんちゃんの画像が表示されました。


何度かブラウザをリロードすると、そのたびに画像が変わります。

ブラウザ開発者ツールを使ってHTMLソースを見てみると、imgタグがbodyにアペンドされているのがわかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/1af696f6-8aed-3613-0c1e-c73a40cce2c5.png)



GASならではのGoogleDriveファイルと連携したページ作成もできるようで面白そうですね！
色々と試してみようと思います。


# 参考
>（いつも隣にITのお仕事）Google Apps ScriptでWebアプリケーションを作る最初の一歩
> https://tonari-it.com/gas-web-application-beginner/


# 余談
HTMLファイル内で GASの新しいV8ランタイムから使用可能なアロー関数を使っていますが、V8ランタイムを無効にした状態でも動作します。
これはHTMLはクライアント側で動くものだからでしょう。


# 続き

GASで動的HTML②　HTML内でGoogleドライブの画像ファイルを表示させる。
https://qiita.com/jooji/items/b89ce338af359742e813
【プログラミング初心者】Swift練習問題~ソート~

# 問題
引数に整数型の配列を取り、降順ソートした結果を配列として返す関数を作成してください。

```swift
func sort(list: [Int]) -> [Int] {
    ...
}

let list = [10, 11, 5, 15, 9, 2, 5]
let sortedList = sort(list: list)

print(sortedList)
```
```:実行結果
[15, 11, 10, 9, 5, 5, 2]
```
※配列のメソッドに`sort()`がありますが、こちらは今回使用しないで実装してください。

回答例は[こちら](https://qiita.com/euJcIKfcqwnzDui/items/306979a03fc813615720)

# 補足
## ソートとは
並べ替えのことです。
降順ソートは大きいものから順に、昇順ソートは小さいものから順に並べ変えていくことです。

## バブルソート
ソートの方法は色々あります。
その中でも最も単純な方法が**バブルソート**と言われる方法です。

実装の方法は問いませんが、思い浮かばない場合は「バブルソート」で検索し考え方を調べてみてください。
一応考え方の参考を載せておきます。

- *「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典*
    - *https://wa3.i-3-i.info/word14886.html*

#最後に
別の問題は以下に掲載しています。
また本記事とは別でプログラミング未経験からiOSアプリ開発が行えるようになることを目的とした記事を連載しています。
連載も以下にまとめていますのでそちらも是非もご覧ください。
http://naoyalog.com/
いつの間にかDagger-Hiltの公式サイトができていた

# 公式サイト

https://dagger.dev/hilt/

## 読んでいて気になったこと

### Activity/FragmentとSubcomponentの関係が変わった

* https://dagger.dev/hilt/monolithic.html

Dagger.AndroidではActivity毎Fragment毎にSubcomponentを定義していた。

Dagger.Hiltでは１つのコンポーネントで全てのActivityあるいは全てのFragmentにインジェクションを行うということ。

これはかなりの設計の違いと言えると思う。

* https://dagger.dev/hilt/migration-guide

マイグレーションガイドに記載されている内容であるが、今までだとBuilderやFactoryに定義していたComponentの引数は引数として渡せなくなった。

```kotlin
@Component.Builder
interface Builder {
  @BindsInstance
  fun foo(foo: Foo): Builder  // Foo is different per Activity
}
```

そこで、こんな感じに取ってくる必要がある。

```kotlin
// Define an interface the activity can implement to provide a custom Foo
interface HasFoo {
  fun getFoo() : Foo
}

@InstallIn(ActivityComponent::class)
@Module
object FooModule {
  @Provides
  fun provideFoo(activity: Activity) : Foo? {
    if (activity is HasFoo) {
      return activity.getFoo()
    }
    return null
  }
}
```
[Github Actions] artifactsを使おう！

Github Actions には成果物（artifacts）と言って

- ジョブからジョブに受け渡したいもの
- 保存しておきたいもの

を Actions 上に保存できる機能があります。


簡単に利用できるので、是非利用して気持ちいいCICDを書きましょう。


## 使い方

[upload-artifact](https://github.com/marketplace/actions/upload-a-build-artifact) と [download-artifact](https://github.com/marketplace/actions/download-a-build-artifact) が marketplace にあるのでこれ以上何もないのですが、参考に例を書いておきます。


## sample1: docker-compose のログを保管しておきたい

```yaml
name: test
on:
  pull_request:
    branches: [ master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 12.18.0
        uses: actions/setup-node@v1
        with:
          node-version: '12.18.0'
      - name: npm ci
        run: npm ci
        
      - name: Docker set up
        run: |
          docker-compose build
          docker-compose up -d
          
      - name: npm test
        run: npm run test
        
      - name: docker logs save 1
        if: ${{ failure() }}
        run: docker-compose logs > ./docker.log
        
      - name: docker logs save 2
        uses: actions/upload-artifact@v2
        if: failure()
        with:
          name: docker-logs
          path: ./docker.log 
```

`if failure()` で失敗したときのログを保存しています。  
ちなみに actions の実行結果は消えないので、ログ程度ならそのまま標準出力垂れ流しで十分です。


## sample2: build と test で job を分けたい

job間でファイルをやりとりしたい場合ですね。

```yaml
name: build & test
on:
  pull_request:
    branches: [ master ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 12.18.0
        uses: actions/setup-node@v1
        with:
          node-version: '12.18.0'
      - name: npm ci
        run: npm ci
        
      - name: tsc
        run: npm run tsc # ./dist に出力されるとする
        
      - name: save dist
        uses: actions/upload-artifact@v2
        if: !failure()
        with:
          name: dist
          path: ./dist

  test:
    runs-on: ubuntu-latest
    steps:
      - name: Use Node.js 12.18.0
        uses: actions/setup-node@v1
        with:
          node-version: '12.18.0'
      - name: npm ci
        run: npm ci
        
      - name: load dist
        uses: actions/download-artifact@v2
        with:
    	  name: dist
        
      - name: test
        run: npm run test
```

こんな場面あるのか...............？


## まとめ

画像とかなんでも保存できるらしいのでいい使い方あったら教えてください
【VBS】プログレス表示

```vb:progress.vbs
Dim ie

' IE の初期化
Function initializeIe()
  Set ie = CreateObject("InternetExplorer.Application")
  With ie
    .Navigate("about:blank")
    .ToolBar = False
    .StatusBar = False
    ' 幅・高さの設定
    .Width = 300
    .Height = 200
    ' 画面右上に配置する。"parentWindow.screen" はパスカルケースで書くと認識されない
    .Top = 0
    .Left = .Document.parentWindow.screen.Width - 300
    .Document.Charset = "UTF-8"
    .Visible = True
    .Document.Title = "スクリプト実行中"
  End With
End Function

' メッセージを IE に追記する
' 
' 引数のメッセージを IE の最終行に追記する。
' 最終行が表示されるようにスクロール位置を最下部に設定する。
Function updateMsg(value)
  With ie
    .Document.Body.innerHTML = .Document.Body.innerHTML & value & "<br>"
    .Document.Script.setTimeout "javascript:scrollTo(0," & .Document.Body.ScrollHeight & ");", 0
  End With
End Function

' IE を終了する
Function closeIe()
  ie.Quit
  Set ie = Nothing
End Function
```
```vb:main.vbs
Set objFso = Wscript.CreateObject("Scripting.FileSystemObject")
Execute objFso.OpenTextFile("progress.vbs", 1).ReadAll()

' IE の宣言と初期化
initializeIe()

s = "aaa"

updateMsg "処理を開始します..."

' 処理…
For i = 0 To 10
  updateMsg s
Next

' 完了
updateMsg "スクリプト処理完了!"
MsgBox "スクリプト処理が完了しました。"

' IE を終了させスクリプトを閉じる
closeIe()
WScript.Quit
```
>参考
https://neos21.hatenablog.com/entry/2016/06/24/000000
【VBS】プログレス表示

```vb:progress.vbs
Dim ie

' IE の初期化
Function initializeIe()
  Set ie = CreateObject("InternetExplorer.Application")
  With ie
    .Navigate("about:blank")
    .ToolBar = False
    .StatusBar = False
    ' 幅・高さの設定
    .Width = 300
    .Height = 200
    ' 画面右上に配置する。"parentWindow.screen" はパスカルケースで書くと認識されない
    .Top = 0
    .Left = .Document.parentWindow.screen.Width - 300
    .Document.Charset = "UTF-8"
    .Visible = True
    .Document.Title = "スクリプト実行中"
  End With
End Function

' メッセージを IE に追記する
' 
' 引数のメッセージを IE の最終行に追記する。
' 最終行が表示されるようにスクロール位置を最下部に設定する。
Function updateMsg(value)
  With ie
    .Document.Body.innerHTML = .Document.Body.innerHTML & value & "<br>"
    .Document.Script.setTimeout "javascript:scrollTo(0," & .Document.Body.ScrollHeight & ");", 0
  End With
End Function

' IE を終了する
Function closeIe()
  ie.Quit
  Set ie = Nothing
End Function
```
```vb:main.vbs
Set objFso = Wscript.CreateObject("Scripting.FileSystemObject")
Execute objFso.OpenTextFile("progress.vbs", 1).ReadAll()

' IE の宣言と初期化
initializeIe()

s = "aaa"

updateMsg "処理を開始します..."

' 処理…
For i = 0 To 10
  updateMsg s
Next

' 完了
updateMsg "スクリプト処理完了!"
MsgBox "スクリプト処理が完了しました。"

' IE を終了させスクリプトを閉じる
closeIe()
WScript.Quit
```
>参考
https://neos21.net/blog/2016/06/24-01.html
【XAMPP 7.4.6-0 環境】ガチ素人が書くUbuntu 20.04におけるCUI及びCLIのコマンド入力についてのメモ

この記事では、LinuxのディストリビューションであるUbuntu 20.04におけるCUI及びCLIのコマンド入力についてまだ学習始めたばかりのガチ素人がメモとして書いておきます。

##環境
・Ubuntu 20.04 LTS
・XAMPP for Linux 7.4.6-0

##留意点
XAMPPのドキュメントルートはhtdocsフォルダです。

```
/opt/lampp/htdocs/
```

##準備
以下のコマンドを打ってアプリケーションを開きます。

```
sudo /opt/lampp/manager-linux-x64.run
```

その後、Go To Applicationをクリックするとサーバーを動かすか聞かれるので、Start Serversをクリックして、XAMPPを起動しましょう。

##1. フォルダを作成する
フォルダを作成するには、`mkdir`を使います。
〈例〉`/opt/lampp/htdocs/`の中にフォルダwebsiteを作成します。

```
mkdir /opt/lampp/htdocs/website/
```

##2. フォルダの権限を変更する
フォルダの権限を書き込みできるように変更するには、`chmod` を使います。
すべてのユーザーが書き込み・保存できるようにするにはパーミッション777にします。
`-R`が再帰的に変更するというよく分からない意味を持つコマンドです。

```
chmod -R 777 /opt/lampp/htdocs/website/
```

サイトにアクセスして、エラーが出たら、まずこのコマンドを試してみましょう。

##3. 新たにファイルを作成する
新たにファイルを作成するには、`touch` コマンドを入力します。
〈例〉XAMPPの`/opt/lampp/htdocs/website/`にある`index.html`というファイルを作成します。

```
touch /opt/lampp/htdocs/website/index.html
```

##4. ファイルを編集する
予めあるファイルを編集するには、`vi`コマンドを利用します。

```
vi /opt/lampp/htdocs/website/index.html
```

`index.html`が開いたら、コマンドを編集します。`i`キーを押しましょう。
すると編集モードになります。カーソルで選択して、HTMLを入力しましょう。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" href="./style.css" type="text/css">
	<title>Louis Alexaのウェブサイト</title>
</head>
<body>
<h1>Louis Alexaのウェブサイト</h1>
<h2>Louis Alexaのサイトとは？</h2>
<p>
これは、Louis Alexaのウェブサイトです。<br>
</p>
</body>
</html>
```

終わったら、Escを押して編集モードを終了した後、`:wq` とコマンドを入力してEnterを押します。

`w` がコマンドを保存するという意味を持っています。`q` が終了するという意味を持っています。
合わせて、保存して終了するという意味を持つコマンドです。

ついでに`style.css`を作成してしまいましょう。

```
touch /opt/lampp/htdocs/website/style.css
```

```
vi /opt/lampp/htdocs/website/style.css
```

```css:style.css
body{
	background-color:#000000;
	color:#FFFFFF;
}
```

Escキーを押してモードをもとに戻しましょう。
その後、`:wq`を押して保存します。

そうすることで、以下のURLにアクセスできます。
http://localhost/website/index.html

##【補足】root権限で起動するには
`sudo`と`-s`を組み合わせます。

```
sudo -s
```

ログインしていない状態で上記のコマンドを打つとrootのパスワード入力を求められます。パスワードを打ちましょう。
そうすることでroot権限で起動できます。
RTX830が不正アクセスを検知したので対処した

ヤマハの業務用ルータ **RTX830** の **IDS** が不正アクセスを検知したらしく、アラートメールが届いていた。
見落としが無ければ、たぶん今回が初めて。

# 内容

```:ルータの設定
mail server name 1 FOO
mail server smtp 1 smtp.bar.com port=587 smtp-auth foo bar plain
mail template 1 1 From:xxxxx@example.com To:yyyyy@example.com
mail notify 1 1 trigger intrusion * in/out
mail notify 2 1 trigger status all
```

```:受信メール
From: xxxxx@example.com <xxxxx@example.com>
Sent: Tuesday, June 9, 2020 10:05 PM
To: yyyyy@example.com
Subject: Mail for Backup Info/Route Change Info/Filter Info/Status Info/Intrusion Info/Lua Info/Lan Map Info

Model: RTX830
SystemName: RTX830_M5B099999
Revision: Rev.15.02.14
Time: 2020/06/09 22:04:30
Template ID: 1

ID   時刻                インタフェース    不正アクセス検知内容
------------------------------------------------------------------------------
0001 2020/06/09 22:03:59      PP[01] [ in] ICMP timestamp req    
                                           (45.43.33.XXX    -> 202.248.XXX.YYY  )
```

`202.248.XXX.YYY` は、こちらがプロバイダと契約しているグローバルIPアドレス、
`45.43.33.XXX` は発信元のIPアドレスだ。攻撃者ではあるが一応マスキングしておく。
調べてみると、遥々と香港から来なすったようだ。

# ICMP timestamp req とは
アラートに挙がっている `ICMP timestamp req`（タイムスタンプ・リクエスト）を理解するには、**ICMP**（Internet Control Message Protocol）のタイプ別の機能について触れる必要があるので下表に纏める。

|タイプ	|機能                   |
|:------|:----------------------|
|0	|エコー応答 (Echo Reply) |
|3	|宛先到達不能 (Destination Unreachable) |
|4	|送信元抑制 (Source Quench) |
|5	|リダイレクト要求 (Redirect) |
|8	|エコー要求 (Echo Request) |
|9      |ルータ通知 (Router Advertisement) |
|10     |ルータ選択 (Router Selection) |
|11	|時間超過 (Time Exceeded) |
|12	|パラメータ異常 (Parameter Problem) |
|13	|タイムスタンプ要求 (Timestamp Request) |
|14	|タイムスタンプ応答 (Timestamp Reply) |
|15	|情報要求 (Information Request) |
|16	|情報応答 (Information Reply) |
|17	|アドレスマスク要求 (Address mask Request) |
|18	|アドレスマスク応答 (Address mask Reply) |

詳細は [RFC792](https://tools.ietf.org/html/rfc792) を参照して欲しい。

毎度お馴染みの`ping`コマンドは、タイプ8の**エコー要求パケット**を宛先に送り、宛先ノードが、タイプ0の**エコー応答パケット**を返すもの。
ICMPタイムリクエストは、タイプ13の**タイムスタンプ要求パケット**を宛先に送り、宛先ノードが、タイプ14の**タイムスタンプ応答パケット**を返すことで、リモートシステムの現在時刻が得られるもの。

システム時刻を知ることで、攻撃者はTCPシーケンス番号をより正確に推測でき、暗号攻撃を仕掛け易くなる。また、他の手法と組み合わせてリモートシステムのOSを識別することにも使われるらしい。

# 対処
ICMPタイムリクエストに応答しなければ良い。

```sh:コマンド
ip filter 静的フィルタ番号 reject * * icmp 13
```

Web GUI であれば、[IPフィルター] ▶ [静的フィルター] で受信方向の定義を編集する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/6f204fd5-c6e8-5f8c-ab2e-10b1c32f789e.png)

# 設定のテスト
`hping3`コマンドで実際にタイムリクエストを投げてみるのが簡単である。
このとき、グローバルIPアドレスを間違えてしまうと、あなたが攻撃者になってしまうので注意！

```sh:コマンド
apt -y install hping3

hping3 -1 -C 13 aaa.bbb.ccc.ddd
HPING aaa.bbb.ccc.ddd (enp1s0 aaa.bbb.ccc.ddd): icmp mode set, 28 headers + 0 data bytes
```

`hping3`はこちらの記事が詳しい。
https://qiita.com/hana_shin/items/8147777ff42e7dad89d6

RTX830 の SYSLOG で **reject** を確認できれば OK だ。

```sh:SYSLOG
2020/06/09 23:11:40: PP[01] Rejected at IN(200104) filter: ICMP AAA.BBB.CCC.DDD > aaa.bbb.ccc.ddd : time stamp
```

IPパケットフィルタのログを有効にしておくのも忘れずに。（デフォルトは無効になっている）

```sh:コマンド
syslog notice on
```

Web GUI であれば、NOTICE にチェックを入れる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/360cc837-a48e-56b8-83f4-55382306889b.png)
フォームが送信されたら、イベントが発火するようにする

message.js

```
$(function(){
  $('#new_message').on('submit', function(e){
    console.log('hoge');
    e.preventDefault()
  });
});
```

# 1. メッセージ送信フォームをJavaScriptで取得する
form_forで作成したフォームにはあらかじめid属性が付与されている。ブラウザの要素検証を利用して、メッセージ送信フォームにどんなid属性がついているかを確認する。



```
$(function(){
$('#new_message').on('submit', function(e){
```
「id(#mew_meaasge)の、submit(フォームが送信)されたときのイベント処理」をfunction以降に記述する。

# 2. console.logを用いて、イベント発火しているか確認
```
console.log('hoge');
```

consoleに'hoge'を表示させる

# 3. フォーム送信を停止させる
```
e.preventDefault()
```

デフォルトのフォーム送信により画面が遷移してしまう。非同期通信を行うために、preventDefault()を使用してデフォルトのイベントを止める。


# 参照
[JavaScriptのpreventDefault()って難しくない？preventDefault()を使うための前提知識](https://qiita.com/tochiji/items/4e9e64cabc0a1cd7a1ae)

[【jQuery入門】on()によるイベント処理の使い方まとめ！](https://www.sejuku.net/blog/38774)
monaca で音声を再生する(media)際のpath指定

初投稿です。

今回はメモぐらいな感じで書きます。
monacaで音声を再生するに当たっていろいろ大苦戦をしましたという経験談を投稿。

[【Monaca】Mediaプラグインを使う時に気をつけたいこと【Cordova】](https://blog.webico.work/monaca-cordova02)

ほうほう、意外に簡単だなと進めるも音声が再生されない・・・・
(iPhoneの実機ででバックしているのにも関わらず)

いろいろ試していく内にpathが悪いのだと判明するもどのようにすれば良いか。

```javascript:sample.js
function onDeviceReady() {
   music = new Media (getPath()+'〇〇.mp3');
}
document.addEventListener("deviceready", onDeviceReady, false);
function getPath() {
  var str = location.pathname;
  var i = str.lastIndexOf('/');
  return str.substring(0,i+1);
}
```

ディレクトリ構造としてはこんな感じ

```html:a.html
www
|
index.html
movie
  |
  〇〇.mp3
```

あ、

```javascript:sample2.js
function onDeviceReady() {
   music = new Media (getPath()+'movie/〇〇.mp3');
}
document.addEventListener("deviceready", onDeviceReady, false);
function getPath() {
  var str = location.pathname;
  var i = str.lastIndexOf('/');
  return str.substring(0,i+1);
}
```
getPathで指定できるのは、wwwまでの話でしたね・・・・

と言うしょうもない話でした。

[【Cordova】cordova-plugin-media使用時の音楽ファイルパスの指定について](https://cpoint-lab.co.jp/article/201904/9370/)

アンドロイドにもコレで対応できるか定かではないので、こんな感じに設定し直す必要性ありですね。
C++ STL&Win32API uint32_tとDWORDを同じ型として扱う

C++では32ビット環境でも64ビット環境でもuint32_tとDWORDは32ビット整数型ですが、歴史的経緯[^1]から区別されます。C++20では関数テンプレ―トによりこれらを同一視できます。

```cpp
#include <cstdint>     // int32_t, uint32_t, int16_t, uint16_t
#include <type_traits> // is_integral_v

#define STRICT
#include <Windows.h>   // DWORD, ULONG, WORD, SHORT

using namespace std;

template <typename T>
T f(T x)
{
	static_assert(is_integral_v<T> && sizeof(T) == 4, "32ビット整数を指定してください。");

	return x;
}

int main()
{
	// uint32_tとDWORDはコンパイル時に32ビット整数なので何も起きません。
	f(uint32_t(0));
	f(DWORD(0));
	// f(uint16_t(0)); // 16ビット。static_assertでコンパイルエラー
	// f(WORD(0));     // 16ビット。static_assertでコンパイルエラー

	// 符号は確認しないので符号付きでも同様です。
	f(int32_t(0));
	f(ULONG(0));
	// f(int16_t(0));  // 16ビット。static_assertでコンパイルエラー
	// f(SHORT(0));    // 16ビット。static_assertでコンパイルエラー

	return 0;
}
```

主要な部分は以下の一文です。`std::is_integral_v<T>`で整数型、`sizeof(T) == 4`で32ビット（4バイト）であることを確認しています。`static_assert`は条件式が`false`の場合にコンパイルエラーを発生します。メッセージは省略できます。

```cpp
static_assert(is_integral_v<T> && sizeof(T) == 4, "32ビット整数を指定してください。");
```

[^1]: `int`は16ビット環境では16ビット整数でした。現在でも`short int`に名残があります。
自己紹介

こんにちは、初めまして
香港からのiOSエンジニアです

##職業経歴：
- **香港（4年間）：**
    - アプリ会社3つ、iOSエンジニア
    - ゲーム会社1つ、iOSゲームエンジニア
- **日本（2年間）：**
    - 某大手企業（多分）、iOSエンジニア


いままでの経歴はこの感じかな（適度）

### **ってことで、Qiita始まりました（突然）**

（更新はあとで、たぶん）
AtCoder ABC112 D - Partition 最小のパターンから考える

https://atcoder.jp/contests/abc112/tasks/abc112_d

「最小のパターンから考える」の例。
Educational Codeforces Round 84 (Rated for Div. 2) A. Sum of Odd Integers
https://qiita.com/recuraki/items/0b386f1ad47c5cec1d06

この問題を思い出した。(1.5時間考えたA問題)

* 概要
- 整数N,Mが与えられる。
- $a_{1}$+$a_{2}$+..+$a_{N}$ = $M$ を満たす数列aが考える
- 数列aの各要素の最大公約数を求めよ

# こう考えた
最大公約数を$k$とすると、数列bという各要素は1以上である数列を考えると、$M = k(b_{1} + b_{2} + .. + b_{N} )$であることは自明である。
また、この時、。このため、bの合計はN以上でなければならない。なぜなら、1以上の数字がN個あるのだから。

さて、$1 <= M <= 10^{9}$より、Mを因数分解した際の要素は数十程度である。 

このため、Mの因数全てをkで仮置きしよう。
例えば、N=3, M=14という例を考える。
Mの約数は[1,2,7,14]である。この要素をkとする。

k=1としたとき$14/1 = 14$より、数列Bは3要素でこれを満たせる。例えば(5+5+4)である。
k=2としたとき$14/2 = 7$より、数列Bは3要素でこれを満たせる。例えば(2+2+3)である。
k=7としたとき$14/7 = 2$より、数列Bは3要素でこれを満たせる。例えば(1+1+0)とせねばならない。
k=14も$14/14=1$で同様にNGである。

このアルゴリズムを以下のように実装した。

```Python3
# 約数のリストを得る
def make_divisors(n):
    divisors = []
    for i in range(1, int(n ** 0.5) + 1):
        if n % i == 0:
            divisors.append(i)
            if i != n // i:
                divisors.append(n // i)
    return divisors

N, M = map(int, input().split())
l = make_divisors(M)
# mの約数
res = 1
# 全ての約数において、kと仮定する
for k in l:
    x = M // k 
    # この際、Mをkで除した数列bがN以上であるかを判定する
    if x >= N:
        res = max(res, k)
print(res)
```
https://atcoder.jp/contests/abc112/submissions/14143131
railsで新規ユーザー作成中unknown attribute 'password' for User.と怒られた

###
自分ではじめて考えたあるwebサービスをrailsで作成中,ユーザーの新規作成のところでいきなり怒られてしまった

```
unknown attribute 'password' for User.
              
  def create
    @user = User.new(
      name: params[:name],
      email: params[:email],
      password: params[:password]
```

##原因究明
パスワードカラムがないといわれたのでUsersテーブルのカラムを調べてみる

```
$ rails console
$ User.new
Loading development environment (Rails 6.0.3.1)
irb(main):001:0> User.new
   (7.5ms)  SELECT sqlite_version(*)
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
```
たしかにパスワードカラムがない。パスワードカラムはマイグレーションファイルを作成して新たにusersテーブルに作ったはずだが、、、
そのマイグレーションファイルを見てみよう

```
class AddPasswordToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column: :users, :password, :string
  end
end
```
add_columnの後ろ余分なコロンがついてしまっていた
##結論
先ほどのコードミスを訂正してrails db:migrateしたらうまくいった
初歩的なミスだったので気を付けたい
symfony/stringを使ってみる

# はじめに
Symfony5には`symfony/string`という文字列を扱うための新しいコンポーネントが含まれています。今回は軽くそちらを触ってみました。

# symfony/stringとは
公式より引用
> 文字列コンポーネントは、文字列の3つの「単位システム」である、Byte、コードポイント、そして書記素クラスタで機能する単一のオブジェクト指向APIを提供します。

ここで出てきた書記素クラスタとは、 Unicodeにおいて1文字を表す単位の事らしいです。純粋にUnicode文字列を扱えると考えて差し支え無いかと思います。

具体的な使用方法ですが、以下のような事ができます。

## 例1
```php

$text = (new UnicodeString('symfony：stringコンポーネントのテストです、'))
    ->trimEnd('、')
    ->replace('：', '/')
    ->append("。");

echo $text;
```
↓

```
symfony/stringコンポーネントのテストです。
```


## 例2
```php

/** @var UnicodeString[] $texts */
$texts = UnicodeString::wrap(['Hello World', 'Hi World', 'World']);
$isAllEndWithWorld = true;
$isAllEndWithWorld = array_reduce($texts, function ($endWith, UnicodeString $text) {
    return $endWith && $text->endsWith('World');
}, $isAllEndWithWorld);

if ($isAllEndWithWorld) {
    echo 'All text end with world！';
}
```
↓

```
All text end with world！
```

## 例3

```php

$euc = mb_convert_encoding('文字コードの変換', 'EUC-JP');
echo $euc . "\n";

$str = new ByteString($euc);
echo $str->append("\n");
echo $str->toUnicodeString('euc')->append("\n");
```
↓

```
ʸ�������ɤ��Ѵ�
ʸ�������ɤ��Ѵ�
文字コードの変換
```



# 終わりに
今回は、Symfony5に搭載される、文字列操作のための`symfony/string`というコンポーネントを使用してみました。phpでの文字列操作は個人的にイケて無い所が多いなと思っていましたが、オブジェクト思考なAPIが提供される事でとてもスマートに文字列を扱うことができるようになったと感じました。
今回挙げた例はごく一部で、公式にはもっとたくさんのメソッドリファレンスがあります。よかったら見てみてください。

# 参考サイト
https://symfony.com/doc/current/components/string.html
Terraformでリソースをループ処理で作成する

- `count`で台数指定
- `"ec2-%02d", count.index + 1`でタグ名をインクリメント

```
resource "aws_instance" "ec2" {
  count                       = 3
  ami                         = "ami-0f310fced6141e627"
  instance_type               = "t3.micro"
  subnet_id                   = aws_subnet.private_subnet-1a.id
  vpc_security_group_ids      = [aws_security_group.ec2_security_group.id]
  tags = {
    Name = "${format("ec2-%02d", count.index + 1)}"
  }
}
```
mod_auth_openidcとChromeのバージョン問題（SameSite=Noneの互換性）

# 発端
Apacheのリバースプロキシにmod_auth_openidcを入れて、Keycloakで認証を連携を構築している。
[前回の話](https://qiita.com/henatyokotraveler/items/92368a05097156cee387)で、
mod_auth_openidc を最新のVer２.４.２.１にしたところ、
Internal server error でアクセスできない事象が起きた。

Apacheのエラーログはこんな感じ。

```
oidc_restore_proto_state: no "mod_auth_openidc_state_XXX” state cookie found, referer: XXX
oidc_unsolicited_proto_state: could not parse JWT from state: invalid unsolicited response: [src/jose/apr_jwt.c:749:oidc_jwt_parse]: cjose_jws_import_failed:invalid argument [file: jws.c, function]cjose_jws_import, 787]¥n¥n, referXXX
oidc_authorization_response_match_state: unable to restore state, referer: XXX
oidc_handle_authorization_response: invalid authorization response state and no default SSO URL is set, sending an error..., referer: XXX
```

ログレベルをdebugにして、怪しそうなログ

```
oidc_proto_peek_jwt_header: could not parse first element separated by "." from input, refer:XXX
```

# 切り分け：バージョンによる現象発生の違い
同じコンフィグでmod_auth_openidc のバージョンを変えてみると、以下のようになった。

- Ver 2.4.2.1 事象再現
- Ver 2.4.1.1　事象再現
- Ver 2.3.11　事象が起きない

# コミュニティへのメールとブラウザの違い
上記のバージョンの事象再現の違いを、
mod_auth_openidcのGithubにコミュニティのMLに投げて見た。

>Community Support
For generic questions, see the Wiki pages with Frequently Asked Questions at:
https://github.com/zmartzone/mod_auth_openidc/wiki
There is a Google Group/mailing list at:
mod_auth_openidc@googlegroups.com
The corresponding forum/archive is at:
https://groups.google.com/forum/#!forum/mod_auth_openidc
Any questions/issues should go to the mailing list.

すぐに返事がきて、ブラウザの違いを聞かれたので確認したところ、
以下の違いが出た。
※クローズドな環境でブラウザは最新ではない

- Chrome 65.0.3325.146 64bit　事象再現
- Firefox 55.0.2 64bit　事象が起きない
- IE11 11.0.170　事象が起きない

また、`mod_auth_openidc-2.4.0.1`では、Chromeでも事象が起きないことを確認した。

# 原因
コミュニティサポートの回答で、原因が判明した。
https://www.chromium.org/updates/same-site/incompatible-clients

Chromeの51～66のブラウザは、
SameSite=Noneを指定するとリジェクトする仕様で、
これに対し、mod_auth_openidc Ver 2.4.1以降は互換性がない。

mod_auth_openidcのrelease 2.4.1の下記の部分が関連する説明。

>always add a SameSite value (default None) to the Set-Cookie header value; this can be overridden by using the environment variable OIDC_SET_COOKIE_APPEND, e.g.:
SetEnvIf User-Agent ".*IOS.*" OIDC_SET_COOKIE_APPEND=;

日本語の解説だと、
https://qiita.com/ahera/items/0c8276da6b0bed2b580c
の下の方にある`ユーザーエージェント判定について`の箇所が上記リンクをうまく日本語にしている。

>Noneを指定すると拒否する。
Chromium由来の古いバージョンのブラウザーとAndroid WebViewも同様
これは当時Noneが仕様に無かったから。
なお51未満は無視されるのでNoneと同じ動作になる。
Userform.ListBoxで左寄せ列と右寄せ列を混在させる

# 要点
・ListBoxのTextAlign属性は、3-fmTextAlignRightにする（右寄せをデフォルトに考える）
・左寄せにしたい列のデータ末尾に、列幅から十分溢れるだけのスペースを追加する（補足3参照）
### サンプル
![左右混在.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/637201/6a0480c0-71b4-de14-e9a4-e30cf8d976c5.jpeg)
第1列と第4列の右端にスペースが追加されている。

### 補足
1. 左寄せをデフォルトにした場合、右寄せしたい列では、列幅ぴったりとなるように左スペースを埋めないといけない。フォントやフォントサイズのバリエーションを考えると、やりたくない。`Format(foo, "@@@")`で右にシフトさせる方法も、結果は同様（フォントによってはうまく並ばない）。

2. 右寄せをデフォルトにして右スペースを埋める場合、「元のデータの幅＋埋めたスペースの幅 ＞ ColumnWidth属性で設定した列幅」であればOK。埋める個数は、適当に大きな数をひとつ決めればいい。溢れたスペースで元のデータが左欄外に押し出されてしまうことはなく、左に寄って止まる。

3. スペースだけでOKなときもあれば、スペースだけだと無視されて右寄せになってしまうときもある（理由は未判明）。「スペース＋何かの文字」で埋めれば、無視されることはない（"何かの文字" が列内に表示されないよう、スペース数を調整する）。

4. スペースの追加は、ベタに` & "   "`(スペースは必要数だけ)とか、`& Space(50)`や`& String(50, " ")` (50は一例) 等々で。スペースの付加は、地道に`For-In``For-Each``Do-Loop`で反復処理する以外はないんじゃないでしょうか。

5. 表示されるデータを加工してしまうので、レコードを選択して次の作業に使うような場合は、忘れず`Trim`するとか、ListBoxのBoundColumn属性に指定する列は加工しないとか、そもそもBoundColumnは列幅0にして表示しないとか、しましょう。

6. Microsoft Office Professional 2016のExcelとOutlook(いずれもバージョン2003)で動作確認済。
初心者がObject指向を理解するまでに行ったこと

# Object指向を理解するまでに行ったこと
- Javaの基本文法の復習
- 参考書を読破
 - スッキリわかるJava入門　第２版

## そもそもObject指向とは
　参考書には、下記のように記載されていました。
　「オブジェクト指向とは、 **ソフトウェアを開発するときに用いる部品化の考え方** のこと。」
　
　とはいえ、部品化とだけだと理解が難しいと思います。
　そこで、オブジェクト指向を理解するためにこのような手順で理解していきました。

　1. Object指向を使うメリット
　2. Object指向の本質の理解
　3. Object指向の3大機能

### 1. Object指向を使うメリット

　簡単にいうと、楽に開発できることです。
　例えば、複数同じ実装をする時に、Object指向を使うと１回で済みます。
　また、私たちが理解しやすい構造になるため、複雑な内容でもミスを減らし、
　実装できるようになります。

　オブジェクト指向は、
　「プログラミングで作成する仮想世界を現実世界に当てはめること」
　が最大のメリットです。

　したがっって、
　「開発時間を少なくし、実装漏れを少なくでき、チーム開発に向いている」
　と考えて貰えば良いと思います。

### 2. Object指向の本質の理解

執筆中
随時記載していきます。。
GitHubで前にレビューしたときからの差分を確認したいときの小技

プルリクエスト（以下PR）のレビューでフィードバックを返し、修正されたものを再度チェックすることってあると思います。
その時、前回見たときの差分を確認できたら便利なんじゃないでしょうか？

実はそれ、簡単にできるんですよ

ってドヤ顔で書いたら[コメントでもっと便利な方法がある](https://qiita.com/fruitriin/items/49355beff05dfaf336f4#comment-f820f2f317bceeaf33d0) と教えていただきました。
> プルリクの Files changed タブにある「コミットを選択するプルダウン」で Show changes since your last review を選択すると、前回レビュー時からの差分を見ることができます

まあ /compare/ 自体はPRに関係なくGitHub上で使えるのでこの記事はこのままにしておきます。


# /compare/[branch or hash]...[branch or hash] にアクセスする


<img width="830" alt="スクリーンショット 2020-06-09 23.49.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122390/fda406a8-3dc8-a51f-1f8a-6ed29e1acf1c.png">
[Comparing c5d4b6e\.\.\.sample4/increment\_counter · fruitriin/vue\-simple\-practice\-samples](https://github.com/fruitriin/vue-simple-practice-samples/compare/c5d4b6e...sample4/increment_counter)

こんなことができます。
ハッシュは短いハッシュでもフルハッシュでも大丈夫。
`github.com/[owner]/[repo]/compare/[branch or hash]...[branch or hash]` です

<img width="990" alt="スクリーンショット_2020-06-09_23_55_58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122390/52987e6c-007d-2b49-0df2-c9a117962c65.png">
コミットハッシュはここからコピーしましょう。


<img width="1265" alt="スクリーンショット 2020-06-10 0.00.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122390/3f440bd6-20a6-34ca-7aec-7e0f1e7279b3.png">

差分確認はPRのレビュー中だけに限りませんが、GitHub上で手軽に行えるのは便利ですね。

## 補足いただきました
[コミットを比較する \- GitHub ヘルプ](https://help.github.com/ja/github/committing-changes-to-your-project/comparing-commits)
コミット比較テクに .. と ... に違いがあったり ^ で前のコミットと比較したりもできるみたいです
クイックソート２｜リスト内包表記で簡単に

#Pythonの強み
##リスト内包表記でクイックソート
```Python3:quickSortSimple.py
data = [26,13,32,65,76,22,43,87,14,59]

def QuickSort(data):
    if len(data) <= 1: # データが１個ならソートする必要ない
        return data

    # 基準となるデータ（ピボット）としてリストの先頭の値を使用
    pivot = data[0]
    # ピボット以上のデータでリスト作成
    left = [ii for ii in data[1:] if ii >= pivot]
    # ピボット未満のデータでリスト作成
    right = [ii for ii in data[1:] if ii < pivot]

    left  = QuickSort(left)  # 左側のソート
    right = QuickSort(right) # 右側のソート
    # ソート済みデータとピボットとを合わせて返す
    return left + [pivot] + right

print(QuickSort(data))

# 結果
# [87, 76, 65, 59, 43, 32, 26, 22, 14, 13]
```
このたびもピボットとしてリスト先頭の値を使用しておりますが、最初の値、最後の値、適当に選んだ３つの値の中央値など、選択肢は様々で、よい値を選ぶことが早い処理の鍵のようです。
---
###・ソートに関する他の記事
[クイックソート｜大きい順](https://qiita.com/ProOJI/items/61a21974109a115f5571)
[マージソート｜大きい順のソーティング]
(https://qiita.com/ProOJI/items/c34441886a9d60fc9664)
[選択ソート｜備忘録]
(https://qiita.com/ProOJI/items/a7fc63c943f51a4e2e92)
[UE4] クラス関係を整理するClass Viewer

検証Ver:4.25.0

これはプロジェクトの構造を分析する際に役に立つ機能の1つの紹介です。

#1. ケース
　突然ですが問題です。自分が現在知っている事が「`Player`というBPは`CharacterBase`というC++の子である」という場合、「`CharacterBase`の子には他にどのようなBPがあるか」という事を効率的に調べる方法は何があるでしょうか。図示すると以下のようなイメージです。
![2020-06-09_22h24_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/7a9db275-c05b-51ab-e19f-adbe9510e8d5.png)

#2. なぜ困ったか
　これは私がプロジェクトを眺めている時に「どうすれば効率的にそれらを知ることが出来るか」と悩んだ点でした。ここで困った点として以下の2つがあります。

**・Editor上で親クラス名を指定した検索ができない**
　クラス名やアセットタイプなどを頼りにアセット検索することはできますが、親クラス名を指定した検索はありません。[^1]

**・リファレンスビュワー上ではNative Packageしか表示できない**
　以下の図はBPの`Player`クラスとC++の`CharacterBase`の参照を示したものです。
`Show Native Package=TRUE`にするとネイティブクラスとのリファレンスも表示することができます。
![2020-06-09_22h33_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/e1fa174e-67bf-d817-1fcc-5904cebee4c3.png)
　ただし、複数のコードを含むので本来含まれて欲しくないものもNative Packageのリファレンスとして表示されます。
![2020-06-09_22h46_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/ca4ddf79-0edb-dd42-3845-bc603107f05c.png)

#3. Class Viewerで解決
　Class Viewerを確認することが私の中での最適解でした。もしかしたら他にもあるかもしれません。
このツールは[Window]->[Developer Tools]->[Class Viewer]をたどってから開くことができます。
![2020-06-09_23h49_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/ee9fdb3f-1c24-0d0a-61ab-04858cf5d7ea.png)　![2020-06-09_22h17_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/6fdd1acb-a05d-2ddf-4384-2aae7a5b636e.png)

　左図のように検索機能により親クラス`CharacterBase`を名前で検索します。親クラスが見つかったら選択して検索入力を削除すると、右図のように継承関係がリストアップされます。
![2020-06-09_22h56_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/da33ab25-4ef2-b65b-0e51-3e134f04a3c8.png)　![2020-06-09_22h56_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/96336af8-5da4-f289-71a3-66c7023f619e.png)

　プルダウンを選択すると、左図のようにネイティブクラスは直接クラス追加ができたり、右図のようにブループリントクラスはContentsにジャンプする事もできます。
![2020-06-09_23h53_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/dac36c84-bb82-f8d2-8ceb-a83bb8a5fdbb.png)


　デフォルトではフィルタで`Actor Only`、`Placeable Only`が有効になっているので、配置可能なActorのみしかリストアップされていません。このチェックを外すことでUObjectベースのクラスなどもリストアップされるので、データクラスなどの検索も簡単にできるようになります。
![2020-06-09_23h02_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101019/c1aa8c51-e4f2-1eba-2857-e46882015d74.png)

[^1]: 「ありません」というより、正しくは「知らなかった」ですね。
文字が1要素として格納されているリストを文字列にする

# やりたい事
①文字列を1つ1つ要素としてリストに入れる
②1つ1つ要素としてリストに入れた文字を文字列にする。

```python:
#①文字列を1つ1つ要素としてリストに入れる
'Hello'
   ↓
['H', 'e', 'l', 'l', 'o']

#②1つ1つ要素としてリストに入れた文字を文字列にする。
['H', 'e', 'l', 'l', 'o']
   ↓
'Hello'

```

# 動作

## 前提
```python:プログラム
strs = 'Hello'
print(strs)
print(type(strs))
```

```:出力
Hello
<class 'str'>
```

## ①文字列を1つ1つ要素としてリストに入れる
やる事：**list()**に入れるだけ

```python:プログラム
strs = 'Hello'
strs_list = list(strs) #←ここだけ

print(strs_list)
print(type(strs_list))
```

```:出力
['H', 'e', 'l', 'l', 'o']
<class 'list'>
```

## ②1つ1つ要素としてリストに入れた文字を文字列にする。
やる事:空のlistを作成して1要素ずつ足していく。

```python:プログラム
strs = 'Hello'
strs_list = list(strs)

new_strs = '' #文字列にする用の空リストを作成
for i in range(len(strs_list)):
    new_strs += strs_list[i]

print(new_strs)
print(type(new_strs))
```

```:出力
Hello
<class 'str'>
```
Power Point で動画を作成する

#Power Point で簡単な動画を作成します
凝ったものは動画編集ソフトを使ってください。
今回は写真や動画の切り貼りのみで1つの動画を作成します。

基本的な作り方としては
　1.　 素材となるファイルの作成
　2.　 スライドショーのリハーサルをして記録
　3.　 動画として出力する

この3つの手順です。
##1. 素材となるファイルの作成
まず元となるファイルを作成します

###1.1 写真、動画のレイアウト
   

動画を作成するにあたって、素材となる写真やイラスト動画を各ページに作成します。
お好みで作成しても大丈夫ですが、テンプレートもあるのでレイアウトに困ったら使ってみてください。
![コメント 2020-06-09 223012.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539633/0c335a3e-7d66-9e67-8983-740ba121e750.jpeg)
例として「アルバム」と検索するとスライドショーとして使えそうなテンプレートが見つかります。

###1.2 エフェクトの追加

**画面切り替え** を使って適当にエフェクトをつけていきます。
期間を設定するとエフェクトの速度に変化をつけることができます。

もちろん Power Point なのでアニメーションもつけることができます。

![コメント 2020-06-09 232250.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539633/2462e334-a8d2-3be3-0051-862321776479.jpeg)

このときに **自動切換え** にチェックマークを入れて、秒数を指定するとリハーサルをするときに楽になります。

###1.3 BGMの挿入

次にBGMを挿入します。
1枚目のスライドに **挿入 > オーディオ** でBGMとして使いたい音楽を挿入します。
![コメント 2020-06-09 231624.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539633/cc62e9cb-31c7-1550-f9ab-bddd7b3bba53.jpeg)

ここでは初めに **バックグラウンドで再生** を選択してください。
以下の項目にチェックマークがつきます。

-  スライド切り替え後も再生
-  停止するまで繰り返す
-  スライドショー実行中にサウンドのアイコンを隠す

繰り返したくない場合は **停止するまで繰り返す** のチェックマークを外してください。
また、音源の一部分を使いたい場合はトリミングをしたり、フェードをうまく活用してください。
トリミングをするときは *1.2 エフェクトの追加* で設定した時間を目安に範囲を指定するとうまく繫げることができます。注意事項としては **画面切り替え時間 + 切り替え時間** でスライドが遷移するので時間の計算を間違えないようにしてください。

途中でBGMを変更したい場合はBGMが変化する予定のスライドで上記の手順を再度行ってください。

##2. スライドショーのリハーサルをして記録
1枚目のスライドからスライドショーを実行してください。
画面切り替えで「自動切り換え」を設定している場合は傍観するだけです。
暖かく見守りましょう。

自動切り換えの設定をしていない場合は手作業でスライド送りをして最後まで終わらせてください。

##3. 動画として出力する

**ファイル > エクスポート > ビデオの作成**

で動画を出力してください。
このときに **記録されたタイミングとナレーションを使用する** が選択されていることの確認をしてください。
出力には時間がかかる場合がありますが、気長に待ちましょう。
MacBook + Windows10(Bootcamp)で仮想化機能を有効化する

# MacBook + Windows10(BootCamp)で仮想化機能を有効化する

前置き：ここでいう仮想化機能は、Windowsの機能の「仮想マシン プラットフォーム」ではないです。BIOS(というかCPU)のほうのです。

## 動機
Windows10 May 2020 Updateで、ついにWSL2（Windows Subsystem for Linux 2)が正式リリースされました。
WSL2では色々あってDockerが動かせるらしいので楽しみなので、WSL2を動かすための仮想化機能の有効化を頑張る。
（MacBookにはBIOSがなく、BootcampでWindows10を起動すると仮想化機能が無効になっている）

## 想定読者
MacBook + Windows10(Bootcamp)でWSL2を使おうと色々設定進めるうちに、「あれMacBookでBIOS設定ってどうやるんだ」とつまづいた人。

## 環境
- MacBook Air(2014年くらいのモデル)
    - MacBookでも同様？
- Bootcamp + Windows 10

## 方法
MacBook(Air)でBIOS(UEFI)の仮想化機能を有効化する方法は、一旦MacOSXを起動して起動ディスクを`BOOTCAMP Windows`に設定して再起動すること（システム環境設定→起動ディスク）。

OSXで再起動を押すとWindowsが起動してきて、その時は仮想化機能が有効になっている。
その後もWindowsの「再起動」であれば仮想化機能は有効なまま。
（高速スタートアップが有効であれば？）
ただしシャットダウンすると、再度OSX経由でWindowsを起動する必要がある。

## 有効化されていることの確認方法
タスクマネージャー
→パフォーマンス タブ
→CPU
→グラフ下に「仮想化: 有効」とあればOK。無効であればNG。

この状態であれば、`wsl --set-version Ubuntu 2`などのコマンドが成功すると思います！
クイックソート｜大きい順

#クイックソートを実装する

最初に「26」を基準の数（ピボット）とする。
「26」より大きい物をLeftへ
「26」より小さい物をRightへセットする。
左と右に分けられるので
それぞれ分けられたリストを同様の処理をしていく。
Left[32, 65, 76, 43, 87, 59]
Right[13, 22, 14]

次にLeftがソーティングされますがピボットは先頭の「32」です。
これを繰り返すことで下の結果の通りになります。

```Python:QuickSort.py
data = [26,13,32,65,76,22,43,87,14,59]

def QuickSort(data):
    if len(data) <= 1: # データが１個以下ならそのまま値を戻す
        return data

    # 基準となるデータ（ピボット）としてリストの先頭の値を使用
    pivot = data[0]
    left, right, same = [], [], 0 # 初期化（リスト、リスト、0）

    for ii in data:
        if ii > pivot:
            left.append(ii)  # ピボットより大きい場合、左側へセット
        elif ii < pivot:
            right.append(ii) # ピボットより小さい場合、右側へセット
        else:
            same += 1        # 同じ値は移動せず、数だけカウント

    left  = QuickSort(left)  # 左側のソート
    right = QuickSort(right) # 右側のソート
    # ソート済みデータとピボットとを合わせて返す
    return left + [pivot] * same + right

print(QuickSort(data))

# 結果
# [87, 76, 65, 59, 43, 32, 26, 22, 14, 13]
```
「分割統治法」っという言い方もあるようですが、小さい単位に分割を繰り返して処理を再帰的に繰り返します。
ピボットの選び方次第で処理は速くもなればいろいろあるようです。
how to go EC2 from local by ssh

# メモ
自分のmacのlocalで

```
whois$ ssh-add -K myPrivateKey.pem
whois$ ssh-add –L
whois$ ssh –A user@<bastion-IP-address or DNS-entry>
```
入ったら

```
whois$ ssh user@<instance-IP-address or DNS-entry>
```


https://amazonaws-china.com/jp/blogs/security/securely-connect-to-linux-instances-running-in-a-private-amazon-vpc/

終わる時は

```
whois$ logout
```
## mongodb
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-amazon/


## 依存関係問題
https://superuser.com/questions/1202330/conflict-when-installing-mongodb

## node
```
whois$ git clone
whois$  npm install -g pm2
```

## Nginx

```
whois$ sudo yum install nginx
whois$ sudo yum install openssl openssl-devel

```

## セキュリティーグループ
セキュリティーグループに**http** **0.0.0.0/0**を追加
これをしないと, 外からアクセスできない
AWS: 開発者ツール

# 開発者ツール
- CodeCommit
    - ソースコードを管理するGitリポジトリ
- CodeBuild
    - ソースコードのビルド/テスティングサービス
- CodeDeploy
    - ブルドされたモジュール(アーティファクト)のデプロイサービス
- CodePipeline
    - 上記3つのサービスを束ねて、一連の開発プロセスを自動化するサービス
- CodeStar
    - 上記4つのサービスを利用したCI/CD環境を自動構築するサービス

## CodeCommit
ソースコード管理のためのGitリポジトリサービスを提供するマネージド・サービス。

### IAMユーザーを用いた権限管理
HTTPS方式でアクセスする場合は、gitコマンドを利用し、ローカル環境にあるIAMのクレデンシャル情報を用いてGitリポジトリに接続できる。

SSH方式でアクセスする場合は、ターミナル上で秘密鍵と公開鍵のペアを作成し、公開鍵をマネジメントコンソールでIAMに紐付けることでGitリポジトリに接続できる

### 他のAWSサービスとの連携
他のAWSサービスとシームレスに連携が可能。
CodePipelineやその他のCodeサービスを組み合わせることでCI/CD環境を簡単に構築することができる。

## CodeBuild
ソースコードのコンパイル/ビルド環境を提供するマネージド・サービス。ビルド環境を簡単に構築できる。

### ビルドの定義はbuildsspec.ymlに記載
ビルド環境上で何をするかを`buildsspec.yml`上に定義する。

- ビルド環境のセットアップ情報
- ビルド/ユニットテスト実行コマンド
- ビルド前後に実行したい処理
- ビルド後の出力情報


## CodeDeploy
ビルド済みのモジュールのサーバーへのデプロイを自動化するサービス。

### デプロイ先の選択可能
EC2,Lambda,そしてオンプレミスサーバーに対してモジュールをデプロイできる。
EC2やオンプレミスサーバーを対象とする場合、対象サーバーにCodeDeployエージェントをインストールしておく必要がある。
`appsspec.yml`にデプロイ時にどのモジュールを、どこに配置するかといった設定をする。

### デプロイ方式の選択可能
システムのサービスレベルによっては、無停止で新機能をデプロイすることが求められる。
そのためCodeDeployはいくつかのデプロイ方式に対応できるようになっている。

|デプロイ方式|具体的な動き|
|---|---|
|AllAtOnce|関連するサーバーすべてに同時にデプロイを行う方式|
|HalfAtATime|関連サーバー郡の半分のリソースに対してデプロイを行う方式|
|OneAtATime|関連するサーバーを1つずつロードバランサーから切り離して、デプロイを行う方式。|

## CodePipeline
CodeCommit, CodeBuild, CodeDeployのサービスを束ねて、一連の開発プロセスを自動化するサービス。

### 開発プロセスの自動化
- ソースコードのPush
- Pushを検知して→ソースコードのビルド
- ビルド完了を検知して→アーティファクトのデプロイ

という自動化されたパイプラインを作成することができる。
リリースの承認プロセスをパイプラインの途中に挟み込むことも可能。
【Raspberry Pi】BME280で温度・湿度・気圧を計測

備忘録として。

#IC2での接続方法と配線
|名称|機能|I2C接続|配線先|
|---|---|---|---|
|VDD|電源＋|電源＋|3.3V|
|GND|GND|GND|GND|
|CSB|チップ選択|未使用|未使用|
|SDI|データ入力|SDA|GPIO2(3番ピン)
|SDO|データ出力|GND,VDD|GND|
|SCK|クロック入力|SCL|GOIO(5番ピン)|

#I2Cの有効化

```
$ sudo raspi-config
```
Interfacing Options→I2Cを有効化.
設定を有効にするには再起動が必要。

#I2Cを使うためのツールをインストール

```
$ sudo apt-get install i2c-tools
$ sudo apt-get python-smbus
```
...はインストールされていた。。。

i2cdetectでI2Cで接続されているデバイスを検出できる。

```
$ sudo i2cdetect -y 1
```

#湿度、温度、気圧を計測

こちらから提供されているコードを参考にさせていただきました。
[SWITCHSCIENCE/BME280](https://github.com/SWITCHSCIENCE/BME280)

wgetでダウンロードし、`Python27`の中の`bme280_sample.py`を任意の場所に置く。

このコードを動かすのに必要なものをインストール

```
$ sudo pip install smbus2
```

##計測方法
配線後以下のコマンドを入力

```
$ python bme280_sample.py
```
結果

```
temp : 28.58  ℃
pressure : 1017.45 hPa
hum :  56.18 ％
```
Ruby on Railsの使い方

# プログラミングの勉強日記
2020年6月9日　Progate Lv.85
授業での課題でRailsを使った

##昇順降順の並び替え
　データを昇順（asc:小さい順）、降順（desc:大きい順）に並べるときには`order`を使う。

```ruby
books = Book.order("id": "asc")  #idを昇順に並べる
books = Book.order("id": "desc")  #idを降順に並べる
``` 

##取得する数を指定する
　取得する値の上限を指定するには、`limit`を使う。

```ruby
books = Book.limit(100).order("id": "desc") #100個まで値を取得する
```
例のように`order`は`limit`とドット(`.`)を使うことで、1行で記述できる。

##すべての数を取得する
　limitとは反対にすべての値を取得するときには`all`を使う。

```ruby
books = Book.all
```

##欲しい値を取得する
　テーブル内の条件に一致した値を取得するためには`where`を使う。

```ruby 
books.where("author=芥川龍之介") #authorカラムが芥川龍之介のレコードを全て取得。
```
GCPのサーバレスプロダクトからのリクエストのソースIPを固定化する

こんにちは。いきなりですがGCPでいちばん好きなプロダクトはCloud Runです。
皆さん、GCPのサーバレス楽しんで使っていますか？

GCPのサーバレスプロダクト(主に以下のプロダクト)は無料枠もあり、しかも使った分だけ課金されるため、お財布にとっても優しいプロダクトです。

 * Google AppEngine スタンダード環境/フレキシブル環境
 * Cloud Functions
 * Cloud Run

なぜ安いか...それはリクエストが無い夜間など、CPUを使っていない間はインスタンスがゼロスケールされ課金されないためです。
Cloud Functionsの競合プロダクトとしてはAWS Lambda、Azure Functionsがあります。
AppEngineフレキシブル環境の競合プロダクトはAWS ECS、Azure App Serviceだと思います。
AppEngineスタンダード環境、CloudRunの競合プロダクトについては...すみません。未確認です。

# サーバレスプロダクトのソースIPの課題

そんなGCPのサーバレスプロダクトですが、欠点の一つとしてプロダクトの上で実行するアプリから外部サービスへリクエストを出した時のソースIPが不定になることです。
これはアプリがGoogleのデータセンターのどれかで実行され、Googleが持つ膨大な数のIPアドレスのいずれかが使用されるためと推測しています。

大概のアクセスには問題になりません。ですが、いくつかの外部サービスではソースIPアドレスで制限を行っているものがあり、そこにアクセスするのにIaaSを使用せざるを得ないケースがあります。

# Serverless VPC Accessの登場
Serverless VPC Accessや、AppEngineフレキシブル環境ができる前までは、サーバレスプロダクトからGCPのVPC(IaaS上に構築されるVirtual Private Cloud)へのアクセスにはインターネット側に出る必要があり、VPC内インスタンスへ内部IPで直接アクセスすることはできませんでした。

Serverless VPC Accessを用いVPC内のインスタンスを経由することでリクエスト時のIPアドレス固定化ができるようになります。
例としてAppEngineの場合は以下のようになります。

 * AppEngine
    * Severless VPC Accessを設定することで、内部IP向けのアクセスはVPC側に出力されます。
    * 環境変数HTTP_PROXY,HTTPS_PROXYにCompute Engine上に作成したプロキシサーバの内部IPを設定します。(Golangを使う場合)
 * Compute Engine
    * VPC内に外部IPを持たないインスタンスを作成しsquid等でプロキシサービスを起動します。
 * Cloud NAT
    * Cloud NATを設定し、リクエストがVPC内から出ていけるようにします。
    * ここでインスタンスに直接外部IPを割り当てずに、Cloud NATを使うのは少しでも利用料を節約するためです。

一応上記の構成でAppEngine上のアプリからIPを固定化してリクエストが出るようになりますが、やはり不満はあります。IaaS特にCompute Engineを使用しているため、利用料がそれなりにかかることです。

# Cloud Functionsの--egress-settingsオプションの登場

Cloud Functionsをデプロイする際に使用する`gcloud functions deploy`コマンドには--egress-settingsオプションがあります。これはServerless VPC Access使用時に使うものですが、これにallを指定すると外部IPだろうが内部IPだろうがリクエストはVPCに出力されます。

これとCloud NATを組み合わせると、VPC内のインスタンスが不要になります。

 * Cloud Functions
    * デプロイ時にServerless VPC Accessの使用と--egress-settings=allを指定することでCloud Functions内の全てのリクエストをVPC側に出力します。
 * Cloud NAT
    * Cloud NATを設定し、外向けのリクエストがVPC内から出ていけるようにします。

# ~~gcloud alpha run deploy には--vpc-egressオプションが存在する~~

~~もうしばらく待てばCloud RunでもソースIPの固定化が楽にできるようになりそうです。~~

# Cloud Run(beta)

(2020/10/14記)Cloud Run(beta)で--vpc-egressオプションが適用できるようになりました。
このオプションとServerless VPC Access、Cloud NATを使用することでCloud Functions同様、Cloud RunでもソースIPを固定化することができます。

# まとめ

GCPのサーバレスプロダクト上のアプリからソースIPを固定化してリクエストを出したい場合、Cloud FunctionsまたはCloud Runを使用すると運用費が安くなる可能性があります。

Googleさんには、この仕組みをAppEngine ~~やCloud Run~~ にも早く適用して欲しいと願っています。
git cloneしたVueプロジェクトを仮想環境で起動できない！！

#はじめに
タイトルにもある通り、リモートのVueプロジェクトをローカルの仮想環境で動かそうとした際に二転三転とエラーが重なって解決に時間がかかったので、解決方法及び内容理解のための内容です。
#環境
ローカル
Vagrant 2.2.7
Virtualbox 6.1.6
Vue 2.6.11

仮想環境
centos 7.2

共有フォルダの設定

```file:vagrantfile
  config.vm.synced_folder "..", "/home/vagrant/vue_project",
    create: true, owner: "vagrant", group: "vagrant"
```

#内容
まずはgit cloneでリモートリポジトリからソースをクローン

```
git clone [リポジトリURL]
```
今回はソースコードの中にvagrantファイルを入れてあるので、vagrantfileのあるディレクトリまで移動。

```
cd vue_project/vagrant
```
仮想環境立ち上げて、ssh接続

```
vagrant up
vagrant ssh
```

共有フォルダに指定したプロジェクト直下に移動して、プロジェクト起動！

```
$ npm run serve
sh: vue-cli-service: command not found
npm ERR! code ELIFECYCLE
npm ERR! syscall spawn
npm ERR! file sh
npm ERR! errno ENOENT
npm ERR! vue_project@0.1.0 serve: `vue-cli-service serve`
npm ERR! spawn ENOENT
npm ERR!
npm ERR! Failed at the vue_project@0.1.0 serve script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/vagrant/.npm/_logs/2020-06-09T14_01_46_634Z-debug.log

```

エラーが発生しましたが、このエラーはnpmの再ビルドを行っていなかったので起きたのが原因のようです。
なので再度jsモジュールを全部入れ直して、実行する。

```
$ rm -rf node_modules package-lock.json && npm install
npm ERR! code EPROTO
npm ERR! syscall symlink
npm ERR! path ../acorn/bin/acorn
npm ERR! dest /home/vagrant/vue_project/node_modules/@vue/cli-service/node_modules/.bin/acorn
npm ERR! errno -71
npm ERR! EPROTO: protocol error, symlink '../acorn/bin/acorn' -> '/home/vagrant/vue_project/node_modules/@vue/cli-service/node_modules/.bin/acorn'

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/vagrant/.npm/_logs/2020-06-09T14_07_28_990Z-debug.log
```
また、別のエラーが、、、
色々調べてみましたが、今回ここで解決せず詰まってしいました。


#原因・解決策
原因は↓の記事に書いてある通り、
参考記事:https://qiita.com/tuttieee/items/ab9b198cd2aaccb446ff

`VirtualBoxの共有フォルダではシンボリックリンクの作成が禁止されている事`
また
`シンボリックリンクの作成を回避しても，パスの長さがホストOS(Windows)の制限に引っかかってファイルを作成できない事`
が原因のようです。

はじめはピンときていませんでしたが、とにかくそのまま仮想環境上でビルドできないよ、と言われているのであれば解決の手段は色々考えられました。

`WindowsにVueプロジェクトクローン→vagrant up→ssh接続→npm再ビルド(npm install)→npm run serve`
でエラーになるので

>①WindowsにVueプロジェクトクローン→npm再ビルド(npm install)→vagrant up→ssh接続→npm run serve

の順で実行すれば、問題ありませんでした。

個人の環境に依存する方法が嫌であれば
>②vagrant up→ssh接続→仮想環境にVueプロジェクトクローン→npm再ビルド(npm install)→npm run serve

みたいに仮想環境に直接クローンする事でもビルドと起動が成功します。

チームで行う場合は[参考記事](https://qiita.com/tuttieee/items/ab9b198cd2aaccb446ff)みたいに、パスの制限設定をいじったりする方法もありだと思いました。
(自分用)UdemyのReactを動かす環境をdockerで作る(備忘録)

### はじめに
自分用備忘録です。
仕事で急にdockerとdjangoとreactを使うことになったので勉強していきます。
djangoは先にdjango girlsと公式チュートリアルをやったのでReactの動画を見ていきます。
dockerは触りながら覚えていきます。

###Dockerの準備
dockerImageはここを参考に作成
https://qiita.com/kerochelo/items/1063da8893026de5ebbf


```bash:Dockerfile
FROM node:12 ←新しいのにしておく
WORKDIR /usr/src/app
```
ちなみにDockerfileにyarn入ってなくね？と思って
RUN npm install --global yarnってするとERRORになります。
nodeのImageは既にyarnが入っているかららしいです。

```bash:docker-compose.yml
version: '3'
services:
    web:
        build:
            context: .
            dockerfile: Dockerfile
        volumes: 
            - ./:/usr/src/app
        ports:
        - "3000:3000"
        tty: true ←これ追加
```
tty: true入れないとコンテナ立ち上げた時にexitになってしまう。。。
よくわからないがとりあえず入れておきます。
あとでちゃんと調べます。


### Docker起動
Dockerfileとdocker-compose.yml置いている場所に行ってビルド＆起動

```
cd ~/docker-react-app
// Dockerのビルドと起動
docker-compose up -d --build
```


### Reactのモジュール作成
cretate-react-appのコマンド

```
~/docker-react-app/udemy-react-redux-crud-application ←作業ディレクトリ
$docker-compose run --rm web sh -c "yarn global add create-react-app && create-react-app ./udemy-react-redux-crud-application"
```

imageの中に入って作ってもいいです

```
~/docker-react-app ←Dockerfile置いてるディレクトリ
$docker exec -it docker-react-app_web_1 bash
root@b:/usr/src/app#yarn global add create-react-app
root@b:/usr/src/app#create-react-app ./udemy-react-redux-crud-application"
```


ディレクトリはこんな感じになった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168779/8cb21dee-c01a-196f-0928-dcc5e655c52f.png)

create-react-appを立ち上げてみる

```
docker-compose run --rm web sh -c "cd udemy-react-redux-crud-application && yarn run start"
```

立ち上がった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/168779/014a9b4b-47d7-80f7-cb52-2b31df6b20b0.png)


docker-compose.ymlにcommand入れておけばコンテナ起動した時に立ち上げてくれます。

```bash:docker-compose.yml
version: '3'
services:
    web:
        ...
        command: sh -c "cd udemy-react-redux-crud-application && yarn start"
```


### 予定
とりあず、Udemyの動画をベースに勉強しつつ、チュートリアルを眺めていく。
最終的にはDockerでNingx + Django + React + Postgresのコンテナ作って適当なwebアプリ作ってみたい。
覚えなきゃいけないことがいっぱいで大変だけど頑張ろう。
仕事が急に別の業務に変わらない限りは。。。