OmniAICreator/Qiita-1.07M huggingface dataset:
[初心者向け] Main.storyboardを消してViewControllerごとにStoryboardを作る

#はじめに
XCodeでプロジェクトを作成すると必ず存在するMain.storyboard。
今回はMain.storyboardにViewControllerをいくつも置いて開発をしている初学者向けにこの記事を書きました。
#なぜMain.storyboardを消すのか
デフォルトで入っているものだし、画面遷移の流れを追いやすいのになぜ消す必要があるのか、と私も最初は思っていました。

一般的にチームで開発を行う場合、何人もの人が同時にプロジェクトを編集します。
その際全員が別のViewControllerをいじっていたとしても、そのViewControllerが全てMain.storyboard上にあったらマージする時にコンフリクトが起きてしまう可能性が高いのです。

その対策としてViewControllerとstoryboardを一対一で作成している企業が多いようです。
これなら複数人でブランチを切って開発を進めていっても該当するstoryboard内でしかコンフリクトは起きませんよね。

では実際にやってみましょう。
#環境
・macOS Big Sur バージョン11.2.3
・XCode 12.4
・Swift5.3.2

#① Main.storyboardを削除
Move to trashしちゃってください。
元からあるViewControllerも不要なのでMove to trash

#② 新しくstoryboardとViewControllerを作成
File → New Fileからstoryboardを作成し、ViewControllerを配置します。
Is initial ViewControllerにチェックを入れるのを忘れずに！

#③ Info.plistからMainを削除
今のままでは起動経路がMain.storyboardになっているので、その記述を削除します。
Info.plist内のMainと書いてあるものを−ボタンを選択して消してください。
![スクリーンショット 2021-04-11 23.22.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893593/72bbad57-ab04-f31a-63e7-7ab4b222bb79.png)

#④ SceneDelegateで起動経路を設定
アプリ起動時の初期画面を条件に応じて切り替えたり、初回起動時に何かしたい場合に対応するため、SceneDelegateで起動経路を設定します。
Project→GeneralのMain InterfaceのMainと書かれている箇所を新しく作成したViewControllerにすることでも起動経路を設定できますが、SceneDelegateで設定する場合はこちらは使用しないので削除して空欄にしてください。
![スクリーンショット 2021-04-11 23.26.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893593/dd343e75-b4ea-6eee-34bf-f33835f58ac5.png)
次に、SceneDelegateに以下のようにコードを書きます。

```SceneDelegate.Swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }

        //Windowをインスタンス化
        window = UIWindow(windowScene: windowScene)

        //rootViewControllerに指定したいControllerを指定
        //StoryboardからViewControllerを生成
        window?.rootViewController = UIStoryboard(name: "FirstView", bundle: nil).instantiateInitialViewController()
        window?.makeKeyAndVisible()
    }
```

以上で完了です。実行すると指定したViewControllerが表示されるはずです。
他の画面もStoryboardとViewControllerを一対一で作成し、コードで画面遷移を行うと便利です！
segueを使って画面遷移をしたい場合はStoryboardReferenceを使用すると良いです。
#参考記事
[【Swift】プロジェクト作成時に生成されるMain.storyboardを削除する](https://qiita.com/hayabusabusa/items/f4852c89d6c35f5f0783)
[Xcode 11でStoryboardを使わずに開発する]
(https://medium.com/@shiba1014/xcode-11%E3%81%A7storyboard%E3%82%92%E4%BD%BF%E3%82%8F%E3%81%9A%E3%81%AB%E9%96%8B%E7%99%BA%E3%81%99%E3%82%8B-d43bd95b13e5)
[WordPress]NGINX+php-fpmの設定をメモする

## この記事は
- 個人メモ。

#### 関連記事
- [NGINX+WordPress+AWS Aurora Serverless(MySQL5.6)でなんとか立ち上げてみる](https://qiita.com/YukiiM/items/487c893f402dfa407221)

## /etc/nginx/

- nginx.confはバックアップファイル取得した

```
sudo cp nginx.conf nginx.conf.backup
```

- nginx.conf中身(一部)
    - php-fpmときたらよく書く`location ~~~`は書いていない。
    - 書いても上手くwp-config.phpへアクセスできなかったので
        - （wp-setup-config.php）でDB情報とか書いた時。
    - あまり複雑にしないためにデフォルト同様記載していない。

```
# server nameも書いていない...
server_name _;

# 階層は例です。
# directoryとfileは作成した
root /var/www/html/wp;
```

- conf.dもなにも編集していない。



## php-fpm

- ↓↓↓installしてから変更してない

```
$cat php-fpm.conf

# PHP-FPM FastCGI server
# network or unix domain socket configuration

upstream php-fpm {
        server unix:/run/php-fpm/www.sock;
}
```

## wp-config.php
- 流石に中身見せませんが、手動で作成しました..。
    - 参照：[NGINX+WordPress+AWS Aurora Serverless(MySQL5.6)でなんとか立ち上げてみる](https://qiita.com/YukiiM/items/487c893f402dfa407221)


---
以上。
CloudFormationのスタック一覧を表示するワンライナー(AWS CLI & jq)

# 結論

▼ステータスが`DELETE_COMPLETE`以外のCFn Stackを一覧表示する ※`jq`が必要

```bash
aws cloudformation list-stacks | jq -r '.StackSummaries[] | select( .StackStatus != "DELETE_COMPLETE" ) | .StackName'
```

さらにソート… `| sort`
さらに行数… `| wc -l`

# 補足

[公式ドキュメント](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html)では

```bash
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE
```

となっているが、`CREATE_COMPLETE`以外も表示したいな〜と思った。

そして削除済みのスタックは表示したくないと思った。

[CLI Reference](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/list-stacks.html)を見た感じ、`--stack-status-filter`は複数指定ができるが、not的な指定はできないので`jq`でフィルターした。

なお、AWS CLIには`--filters `と`--query`があるが、（個人的には）`jq`の方が書きやすいので使っていない。
pythonのファイルを扱うpathlibというライブラリ

pathlibでファイル操作を自動化したい。
そのために、まずpathをいくつか使用して慣れてみたい。

F1) Path.glob
現在プログラムフォルダ内に存在するファイルを取得する

例：
from pathlib import Path
current = Path()
for Path in current.glob(('*.jpg')):
    print(Path)

結果：jpgの拡張子のファイルを取得する

F2) サブフォルダ内のファイルも対象にする
**/をつけることで、サブフォルダ内も取得可能。

例：
from pathlib import Path
current = Path()
for path in current.glob('**/*.jpg'):
    print(path)
NAS初期設定(その5) コンパネ＞アプリケーション

# コンパネ＞アプリケーション
## HDMIディスプレイアプリケーション
HDMI出力するためのアプリ。
セットアップが終わってから導入する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/b88e9f36-9713-b6d4-03c6-e209d22989d9.png)


## Multimedia Console
セットアップが終わってから導入検討する。


## Webサーバ
### Webサーバ (設定変更あり)
NASの管理画面(デスクトップ)をブラウザで開くためのWebサーバ。
管理用に運用する。
HTTPは停止しておく。ホームLAN内での運用だからセキュリティリスクはないだろうけど、運用するメリットもないので。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/40d5d8de-8dbd-8188-43c9-82ff363a7c22.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/a2d6c92a-2cec-f311-81c9-13cfa6d89d97.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/a27ca9ef-84ff-db5a-4e3d-58361db58d0e.png)

### 仮想ホスト (設定変更なし)
独自のWebサーバは運用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/77e511f7-9aaa-5092-5b14-b192fda3b144.png)

### WebDAV (設定変更なし)
WebDAVは運用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/f215aa3c-3c7d-6146-25f1-4315d8cf370d.png)


## LDAPサーバー
### LDAPサーバー (設定変更なし)
LDAPサーバーは運用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/24b50a6a-63f6-3dd6-9964-3c81e7478c6c.png)


## SQLサーバー
### SQLサーバー (設定変更なし)
SQLサーバーって記載されるとMS製品に見えるけど、設定項目をみるとMariaDB(MySQL派生)らしい。
DBサーバーは運用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/b97282e8-856f-489e-9902-7824df60c070.png)


## Syslogサーバー
### サーバ設定 (設定変更なし)
Syslogサーバーは運用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/609ef8e1-9036-f614-5a62-1c32d2323c22.png)

### フィルター設定 (設定変更なし)
Syslogサーバーは運用しないため設定不要。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/e8bebfca-0720-1e73-8090-5c2e65c1c220.png)

### Syslogビューア (設定項目ではない)
ログをモニタする画面。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/0c48f26b-61ab-0d96-7237-2a5812ed79e9.png)


## アンチウイルス (検討中)
QTS(NASのOS)にMcAfeeをインストール可能。
　　お値段： $25.00 / 1年

・・・・・・必要？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/37267406-3ed0-7c40-0aae-23cfdcf8baa7.png)


## RADIUSサーバー
### サーバ設定 (設定変更なし)
RADIUSサーバーは運用しない。

メモ：RADIUSとは
ダイヤルアップ回線で従量課金接続を実現するためのアカウント認証(Authentication)と課金管理(Accounting)を行うプロトコル。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/ed7a43af-0f2c-4a9e-9659-43b58cbfc7f9.png)


## TFTPサーバー (設定変更なし)
TFTPサーバーは運用しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/d4c1db18-46fc-27da-9300-293ebe8675fe.png)


## NTPサーバー (設定変更なし)
NTPサーバーは運用しない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/471c7edb-8486-74ed-ec78-b46d9f34debf.png)


以上。
【Python】関数のデフォルト引数の落とし穴

# はじめに
Pythonでコードを書いていてハマったのでメモ
Python使いの人たちには常識かもしれないが多言語使いだと落とし穴になりそう。

# 関数のデフォルト引数挙動
Pythonでは**デフォルト引数は関数定義時に一度だけ評価される**という挙動らしいです。
関数定義時であって**初めに呼び出された時じゃない**という点もポイントです。

下記のコードでは

>現在時刻を表示
↓
print_now関数定義
↓
5秒スリープ
↓
print_now関数呼び出し（現在時刻を表示・・・のつもり）
↓
3秒スリープ
↓
print_now関数呼び出し（現在時刻を表示・・・のつもり）
↓
3秒スリープ
↓
現在時刻を表示

を行っています。

```python
import datetime
import time

print(f'0:{datetime.datetime.now():%Y-%m-%d %H:%M:%S}')

def print_now(msg, now = datetime.datetime.now()):
    print(f'{msg}:{now:%Y-%m-%d %H:%M:%S}')

time.sleep(5)
print_now('1')
time.sleep(3)
print_now('2')
time.sleep(3)
print(f'3:{datetime.datetime.now():%Y-%m-%d %H:%M:%S}')
```

出力結果

```
0:2021-04-11 23:17:28
1:2021-04-11 23:17:28
2:2021-04-11 23:17:28
3:2021-04-11 23:17:39
```

初めて呼び出された時でも呼び出した時でもなく**関数定義時**に計算された値になっている。
これは知らないとハマるって・・・。

公式によると

>デフォルト引数の式は関数が定義されるときにただ一度だけ評価され、同じ "計算済みの" 値が呼び出しのたびに使用されることを意味します。

とのこと。

https://docs.python.org/ja/3.9/reference/compound_stmts.html#function-definitions

以上
AtCoder Beginner Contest 198 参戦記

# AtCoder Beginner Contest 198 参戦記

## [ABC198A - Div](https://atcoder.jp/contests/abc198/tasks/abc198_a)

1分で突破. 書くだけ.

```python
N = int(input())

print(N - 1)
```

## [ABC198B - Palindrome with leading zeros](https://atcoder.jp/contests/abc198/tasks/abc198_b)

3分で突破. 書くだけ.

```python
N = input()

def is_palindrome(s):
    for i in range(len(s) // 2):
        if s[i] != s[len(s) - 1 - i]:
            return False
    return True

for i in range(len(N) + 1):
    if is_palindrome('0' * i + N):
        print('Yes')
        break
else:
    print('No')
```

## [ABC198C - Compass Walking](https://atcoder.jp/contests/abc198/tasks/abc198_c)

9分で突破、WA2. 近すぎるときは2回になるのが盲点だった.

```python
from math import ceil, sqrt

R, X, Y = map(int, input().split())

c = sqrt((X * X + Y * Y) / (R * R))
if c < 1:
    print(2)
else:
    print(ceil(c))
```

## [ABC198E - Unique Color](https://atcoder.jp/contests/abc198/tasks/abc198_e)

30分くらい?で突破. DFS でサクッと突破できた.

```python
from sys import setrecursionlimit, stdin

readline = stdin.readline
setrecursionlimit(10 ** 6)

N = int(readline())
C = list(map(int, readline().split()))

links = [[] for _ in range(N)]
for _ in range(N - 1):
    A, B = map(lambda x: int(x) - 1, readline().split())
    links[A].append(B)
    links[B].append(A)

dp = [(10 ** 18, False)] * N
dp[0] = (0, True)

def f(frm, to, steps, colors):
    if steps == dp[to][0]:
        if C[to] not in colors:
            dp[to] = (steps, True)
    elif steps < dp[to][0]:
        dp[to] = (steps, C[to] not in colors)
    colors.setdefault(C[to], 0)
    colors[C[to]] += 1
    for nto in links[to]:
        if nto == frm:
            continue
        f(to, nto, steps + 1, colors)
    colors[C[to]] -= 1
    if colors[C[to]] == 0:
        del colors[C[to]]

f(-1, 0, 0, {})
result = []
for i in range(N):
    if dp[i][1]:
        result.append(i + 1)
print(*result, sep='\n')
```

## [ABC198D - Send More Money](https://atcoder.jp/contests/abc198/tasks/abc198_d)

50分くらい?で突破、WA2TLE3. まずは変数は10個が最大であることに気づく(別の変数は別の値縛りから). 10!=3628800=3.6×10<sup>7</sup>なので、この問題の「実行時間制限: 5 sec」ならなんとか収まりそうと分かる. 実際は PyPy でもギリギリで定数倍削減する間に TLE の山が発生した.

```python
from itertools import permutations

S1 = input()
S2 = input()
S3 = input()

s1 = [ord(c) - 97 for c in S1]
s2 = [ord(c) - 97 for c in S2]
s3 = [ord(c) - 97 for c in S3]

a = sorted(set(s1 + s2 + s3))
if len(a) > 10:
    print('UNSOLVABLE')
    exit()

if len(a) < 10:
    for i in range(30, 30 + (10 - len(a))):
        a.append(i)

b = [0] * 100
l = set([s1[0], s2[0], s3[0]])
for p in permutations(a):
    if p[0] in l:
        continue

    for i in range(10):
        b[p[i]] = chr(i + 48)

    N1 = int(''.join(b[c] for c in s1))
    N2 = int(''.join(b[c] for c in s2))
    N3 = int(''.join(b[c] for c in s3))

    if N1 + N2 == N3:
        print(N1)
        print(N2)
        print(N3)
        exit()
print('UNSOLVABLE')
```
Python Tesseract + PyOCRを使って画像データのテキスト化

# Python Tesseract + PyOCRを使って画像データのテキスト化

Pythonで画像データをテキスト化はTesseract + PyOCRを使うことで簡単に実現が可能だった

https://github.com/tesseract-ocr/tesseract

https://gitlab.gnome.org/World/OpenPaperwork/pyocr

## 準備

PyOCRインストール

```
$ pip install pyocr
```

Tesseractインストール
※ Windows環境は別方法でインストールしてください

```
$ brew install tesseract
$ ls /usr/local/Cellar/tesseract/4.1.1/share/tessdata/
```

jpn.traineddata取得

```
$ wget https://github.com/tesseract-ocr/tessdata/raw/4.1.0/jpn.traineddata
$ mv jpn.traineddata /usr/local/Cellar/tesseract/4.1.1/share/tessdata/
```

## サンプル

以下の画像をPyOCRを使って解析
![スクリーンショット 2021-04-11 23.02.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59264/32384a4d-d262-8ce3-42bf-4d140c2c1218.png)

```Python
from PIL import Image
import pyocr
import pyocr.builders


def main():
    # OCRエンジンの取得
    tools = pyocr.get_available_tools()
    tool = tools[0]

    # ＯＣＲ実行
    builder = pyocr.builders.TextBuilder()

    with Image.open("images/sample.png") as im:
        result = tool.image_to_string(im, lang="jpn", builder=builder)
        print(result)


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

出力

```
ToysCreation

ト イ ズ ク リ エ イ シ ョ ン
```

---

簡単に画像データからテキストを出力することができました
いいね！と思ったら LGTM お願いします :clap::clap::clap:

【PR】プログラミング新聞リリースしました！ → https://pronichi.com
【PR】週末ハッカソンというイベントやってます！ → https://weekend-hackathon.toyscreation.jp/about/
JavaScript ライブラリ無しでできるだけ簡単にゼロパディング済みの年月日時分秒ミリ秒を個別に取得したい

どんな方法があるかな、とちょっと考えてみました。
まず、素直に考えたらこんな感じでしょうか。

```js
const
    d           = new Date(),
    year        = d.getFullYear(),
    month       = ('0'  + (d.getMonth() + 1)).  slice(-2),
    day         = ('0'  +  d.getDate()).        slice(-2),
    hour        = ('0'  +  d.getHours()).       slice(-2),
    minute      = ('0'  +  d.getMinutes()).     slice(-2),
    second      = ('0'  +  d.getSeconds()).     slice(-2),
    millisecond = ('00' +  d.getMilliseconds()).slice(-3);

console.log(new Date().toString());
console.log(
    year + '年' +  month + '月' +    day + '日 ' +
    hour + '時' + minute + '分' + second + '秒' + millisecond);
```

```txt
Sun Apr 11 2021 22:57:46 GMT+0900 (日本標準時)
2021年04月11日 22時57分46秒017
```

ゼロパディング済みの値が欲しい場合、get系メソッドで取得するなら``slice()``や`padStart()`を使っての処理が一般的ですね。

get系メソッド以外の方法だと、``toISOString()``を使うとゼロパディング済みの値をいい感じに切り出せそうです。

```js
const date = new Date();
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());

const
    _           = date.toISOString().match(/\d+/g),
    year        = _[0],
    month       = _[1],
    day         = _[2],
    hour        = _[3],
    minute      = _[4],
    second      = _[5],
    millisecond = _[6];

console.log(new Date().toString());
console.log(
    year + '年' +  month + '月' +    day + '日 ' +
    hour + '時' + minute + '分' + second + '秒' + millisecond);
```

```txt
Sun Apr 11 2021 22:58:53 GMT+0900 (日本標準時)
2021年04月11日 22時58分53秒871
```

``toISOString()``はタイムゾーンがUTC固定なので、戻り値からそのまま数値を文字列として切り出しても時差によるずれが発生しないよう、``getTimezoneOffset()``で実行環境のタイムゾーンに合わせて補正しています。

IEを無視しても大丈夫なら、もう少し短くなりますね。

```js
const date = new Date();
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());

const [year, month, day, hour, minute, second, millisecond] =
    date.toISOString().match(/\d+/g);

console.log(new Date().toString());
console.log(`${year}年${month}月${day}日 ${hour}時${minute}分${second}秒${millisecond}`);
```

```txt
Sun Apr 11 2021 23:00:05 GMT+0900 (日本標準時)
2021年04月11日 23時00分05秒405
```

変数名なども短くしたいなら

```js
const d = new Date();
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
const p = d.toJSON().match(/\d+/g);

console.log(new Date().toString());
console.log(`${p[0]}年${p[1]}月${p[2]}日 ${p[3]}時${p[4]}分${p[5]}秒${p[6]}`);
```

```txt
Sun Apr 11 2021 23:00:46 GMT+0900 (日本標準時)
2021年04月11日 23時00分46秒021
```

こんな感じでしょうか。
``getTimezoneOffset()``のメソッド名の長さが気になりますが。

``toJSON()``はDateオブジェクトでは``toISOString()``のエイリアスと捉えて良さそうですね。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date

> Date.prototype.toJSON()
> toISOString() を使用して Date を表す文字列を返します。
> JSON.stringify() の代わりに使用してください。

Dateオブジェクトが`Invalid Date`の場合に、`toJSON()`はエラーとはならず`null`を返す等の違いはあります。

```js
const d = new Date('');
console.log(d.toString()); // "Invalid Date"
console.log(d.toJSON()); // null
console.log(d.toISOString()); // RangeError: Invalid
```
SwiftUIを始めてみました

iPhoneアプリに挑戦しようと思って、最近お仕事でStoryboardを用いてアプリを作っています。ただ、いつまでもStoryboardでアプリを作るのも色々大変なので思い切ってSwiftUIの勉強を独学しています。今回は(?)大したことを書いてないのでキャプチャで書いていきます。

## まずは初期のソースを色々いじってみることに
SwiftUIプロジェクトの初期ソースがこれです。SwiftUIからはソースをリアルタイムにプレビューできるので便利ですね。

![スクリーンショット 2021-04-11 21.55.41.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229502/f1383412-6450-e1b9-48f4-d9dfd06b4a01.jpeg)

そして理想のアプリを作るためにソースをいじってみることに。なんだかんだプログラムは割と触ってきたので、「Text」という部分をもう一個下にコピペすれば「Hello, world!」が2行出来るんじゃないかと予測。早速コピペしてみると

![スクリーンショット 2021-04-11 21.55.54.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229502/7f0d3a0c-63a4-e5e5-5ea4-602f66a779e8.jpeg)

いやいや、なんで画面が2画面出るんだよ・・・💦この時点で挫折しかけました。

(もし、2行にする場合は以下のように**VStack**で囲めばいいらしい。)

![スクリーンショット 2021-04-11 21.56.10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229502/db1b8e36-46d6-525e-2ec0-281e901cf026.jpeg)

VStackで囲った箇所はVertical(垂直)に要素を並べるようです。何故VStackがないと2画面にバラけてしまうかはまだ調べきれてないですが・・・💦これだというものがあったらまとめたいと思います。VStackの他にHStackやZStackみたいに囲えるものがあるのです。HTMLで言うところの「div」に近いなと思いました。最近HTMLの仕事を進めていたこともあって、個人的にはStoryboardよりSwiftUIの方が取っ付き易いなと思いました。また気が付いたことや作ったアプリについてまとめて行きたいと思います。

## 参考
https://swiftui.i-app-tec.com/ios/vstack-hstack.html
https://developer.apple.com/documentation/swiftui/view
https://tech.uzabase.com/entry/2019/06/05/141605
Javascript 備忘録4＜関数＞

■関数

```js
function a(num) {
    console.log(num);
}
// 上の関数名と同じ
function a(num) {
   return num * 2;
}
```
→Javascriptの場合は、関数名が被った場合、後に宣言された関数が優先される。

■コールバック関数
→他の関数に渡して実行する関数のこと

```js
// コールバック関数とする
function hello() {
    console.log('hello');
}

function fn(cb) {
// オブジェクトで渡された関数を実行する
    cb();
}
// オブジェクトとしてhelloの関数を渡す
// ()をつけることで関数は実行されるので、渡されただけのhelloは実行されない
// 関数fnの中のcb()にhelloオブジェクトが渡されたことにより、hello関数が実行される
fn(hello);
```

■thisについて
→呼び出し元を参照することができる

```js
const person = {
    name: 'Mike',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}
// 呼び出し下のpersonのオブジェクトを最初に参照する
// その後、helloを参照する
// helloの中にあるthisはperosnオブジェクトを参照することになっている
person.hello();

// resultにはperson.helloオブジェクトを代入する
// ()がhelloにはついていないので、関数の実行はまだしていない状態
const result = person.hello;

// resultに代入したperson.hellの関数を実行する
// コンソールには「Hello」しか出力されない
// functionそのものを参照し、personを経由していないので、nameが取れない
result();
```
■bindについて

```js
// nameプロパティがKimと設定された
// その後、this.nameで参照するさきがname: 'Kim'となる
// このオブジェクトの関数は関数aとは別のものとなる
// bindを読んだ時点では関数は実行されない
const b = a.bind({name: 'Kim'});

// コンソールに「hello Kim」となる
b();
```

■アロー関数について
→ES6以降から使用が可能となった、関数の定義の仕方

```js
// アロー関数を使用する
// ()と{}の間に「=>」を使用する
// 引数が1つの場合は()が不必要となる
const b = name => {
    return 'hello ' + name;
}

// {}内が1行の場合はreturn と{}を省略することができる
// const b = name => 'hello ' + name;

// 関数を実行する
console.log(b('Kim'));

// 引数が2つ以上の時は()を省略することができない
const c = (num1, num2) => {
    return `num1の値は${num1}でnum2の値は${num2}`
}
console.log(c(3, 2));
```
javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件


# トラブルの内容
javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう。

# 動作環境
Mac OS、Windows10
node v12.15.0

## 追記
SalesforceのLightning Web Component(LWC)でのJavascriptの記事です。LWC以外のJavascriptのコードをとして利用する場合は、コードを変更する必要があります。(functionの有無とか) また、コメント欄での指摘も参考にして下さい。また、タイトルに対するより完成度の高いコードを知りたい場合は以下のページを参考にして下さい。
https://qiita.com/noenture/items/02b2e1711e22182c6a97

# 結論
Webサイトを参考にしてJavaの関数をJavascriptに翻訳することで解決しました。正規表現では解決は出来ませんでした。

# 以下、解決への過程
まず次のWebサイトを参考にしました。
[人力検索はてな javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまいます。きれいにCSVに分割する方法を教えてください](https://q.hatena.ne.jp/1306894773)
しかし、このページの回答では、自分のCSVを目的どおりに分割することはできませんでした。自分の場合、以下のようなCSVファイルを分割したいという目的でした。

```
あいうえお太郎,25歳,"¥1,500",東京都
かきくけこ花子,24歳,"¥1,200",さいたま県
```

FileMakerで出力したExcel形式のファイルをCSVに変換すると、このように金額の部分にカンマが付くので、明示的にダブルクォーテーションで囲まれます。このようなCSVを分割した場合に"¥1,500"が"¥1と500"に分かれてしまう。というトラブルを解決したかったのですが、このページに書かれている正規表現ではうまくいきませんでした。

このため、上記サイトの正規表現での解決は諦めて別のWebサイトの方法に頼ることにしました。次に参考にしたのはこのページでした。

[Qiita CSVファイルにカンマの含まれる項目を分割せずに読み込む（Java)](https://qiita.com/takashi-fki/items/c7095ccb41fe169db5f4)

タイトルの通り、Javaで書かれていましたが、このページに書かれているJavaのコードをJavascriptに翻訳して実行したら解決しましたというのが自分の解決方法です。以下、Javascriptのコードを記載します。

``` javascript
  /**
   * CSVの一行を引数に指定すると、
   * ""で囲まれた内部のコンマを分割せずに、
   * コンマで分割して配列の戻り値を返す関数
   * @param {string} line 文字列
   * @return {Array<string>} 配列
   */
  csvSplit(line) {
    var c = "";
    var s = new String();
    var data = new Array();
    var singleQuoteFlg = false;

    for (var i = 0; i < line.length; i++) {
      c = line.charAt(i);
      if (c == "," && !singleQuoteFlg) {
        data.push(s.toString());
        s = "";
      } else if (c == "," && singleQuoteFlg) {
        s = s + c;
      } else if (c == '"') {
        singleQuoteFlg = !singleQuoteFlg;
      } else {
        s = s + c;
      }
    }
    return data;
  }

```
* 上記の関数をLWC(Lightning Web Component)のクラスの中にメソッドとしてコピペしてください。


# 上記csvSplit関数の呼び出し側のコード
* めんどくさがり屋の人のために上記csvSplit関数の呼び出し側のコードも載せておきます。

``` javascript
    // CSVで取得したデータを行ごとに配列にしたものをfor文で処理している。
    for (var i = 1; i < csvArray.length - 1; i++) {
      // カンマで区切られた各データに分割する（これだと、""がある場合ダメ）
      //var csvArrayD = csvArray[i].split(",");

      // ""で囲まれた内部のコンマまで分割されてしまうのを回避
      var csvArrayD = this.csvSplit(csvArray[i]); 
    }
```
解説をすると、普通はsplit(",")でCSVをカンマで分割して配列に代入するという処理でも問題ないのですが、読み込むCSVの内容によっては、「""で囲まれた内部のコンマを分割せずに」処理しないといけない場合があります。このような場合は、this.csvSplit("CSVの行")というように上記の関数を呼び出して実行することで解決できます。for文が気に入らない人など、ここら辺のコードについては、各自の環境・センスに合わせて修正して下さい。

以上です。Good Luck!
xcodeシミュレータ動画撮影〜githubにgifを載せる方法

# はじめに
xcodeのシミュレータで動画を撮影し，githubの自分のリポジトリのREADMEにgif画像を埋め込むまでの流れを簡単にメモしました．

# 手順
## 1. xcodeのシミュレータ動画撮影
### 方法1
シミュレータ起動時に command + r を入力して録画。Desktopに保存される。

### 方法2
.movファイルを保存したいディレクトリに移動し，以下のコマンドをterminalで実行．
control + c キーで録画停止．

```
xcrun simctl io booted recordVideo sample.mov
```

実行後，次のようなエラーがでた場合，

```
xcrun: error: unable to find utility "simctl", not a developer tool or in PATH
```
以下の方法で対処可能．

https://stackoverflow.com/questions/29108172/how-do-i-fix-the-xcrun-unable-to-find-simctl-error


## 2. .mov から .gifに変換

ffmpegを利用して.movから.gifに変換する．installしていない場合，homebrewでインストール．

```
brew install ffmpeg
```

インストール後，
sample.movが存在するディレクトリで，以下のコマンドを実行すると，同ディレクトリ内にsample.gifが生成される．

```
ffmpeg -i sample.mov -vf scale=320:-1 -r 10 sample.gif
```

## 3. gif画像をGithubのwikiにpush

gif画像を埋め込みたいREADMEが存在するリポジトリの"Wiki"で．gif画像用のリポジトリを別に作成する．
urlは赤丸部分．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/2ad19a1f-d0a0-1806-914d-508571c37414.png)

urlよりcloneして，cloneしたディレクトリでgif画像を格納するimagesディレクトリを作成する．
階層は以下の感じ．

```
.
├── Home.md
└── images
    └── sample.gif
```

gif画像をimagesディレクトリに格納後，リポジトリの変更内容をpushする．

push後，以下のurlよりアクセス可能．
https://raw.githubusercontent.com/wiki/<ユーザ名>/<リポジトリ名>/images/sample.gif

## 4. READMEにgif画像を埋め込み

以下のように，記述すると埋め込まれる．なお，"demo"の部分は任意の名前で良い．

```
![demo](https://raw.githubusercontent.com/wiki/<ユーザ名>/<リポジトリ名>/images/sample.gif)
```

# ffmpegでgifの解像度・容量をコントロール
## 解像度を犠牲にして軽量なgifを作成
画像がかなり荒い。

```
ffmpeg -i input.mov -r 10 -vf scale=640:-1 -f gif output.gif
```

## 綺麗なgifの作成（1より少し容量大きい）
個人的には、この方法が一番おすすめ。

```
ffmpeg -i input.mov -filter_complex "[0:v] fps=10,scale=640:-1,split [a][b];[a] palettegen [p];[b][p] paletteuse=dither=none" output-palette-none.gif
```

## 補足
### 動画の再生速度を上げる
以下は4倍速にする例。

```
ffmpeg -i input.mov -filter:v setpts=PTS/4.0 output.mov
```


# 参考
* [GithubやQiitaに載せるgif形式の動画を作成する方法](https://qiita.com/syamaoka/items/4d4054cd75df00460925)
* [ffmpegでとにかく綺麗なGIFを作りたい](https://qiita.com/yusuga/items/ba7b5c2cac3f2928f040)
SELECT文でデータベースの特定のスキーマの各テーブルのサイズを確認する

# SELECT文でデータベースの特定のスキーマの各テーブルのサイズを確認する
データベースの見積もりなどの時に既存のDBがどれくらいのサイズかを確認するために時々使うのでメモ


DB_name_hereのところをデータサイズをSELECTしたいdb名を入れる

```sql
SELECT
  table_name
  , engine
  , table_rows AS tbl_rows
  , avg_row_length AS rlen
  , floor((data_length + index_length) / 1024) AS allkb
  , floor((data_length) / 1024) AS dkb
  , floor((index_length) / 1024) AS ikb 
FROM
  information_schema.tables 
WHERE
    table_schema='DB_name_here'
ORDER BY
  (data_length + index_length) DESC;
```


- table_name（テーブルの名称）
- engine（DBエンジンの種類）　・・・　InnoDB、MyISAM、MEMORYなど
- tbl_rows（レコード件数）
- rlen（平均レコードサイズ[単位: Byte]）
- allkb（テーブル全体のサイズ[単位: KB]）
- dkb（テーブル内のデータが占めるサイズ[単位: KB]）
- ikb（テーブル内のインデックスが占めるサイズ[単位: KB]）



### 参考：
* https://www.wakuwakubank.com/posts/323-mysql-capacity/
* http://www.rexent.co.jp/blog/mysql-%E5%90%84%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E6%AF%8E%E3%81%AB%E5%AE%B9%E9%87%8F%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B
Google colaboratoryで画像にサクッとノイズを加える


```python:ipynb
# 画像をアップロード
from google.colab import files

uploaded_file = files.upload()
uploaded_file_name = next(iter(uploaded_file))

# 画像にノイズを加える
import cv2
import numpy as np
from skimage.util import random_noise

img = cv2.imread(uploaded_file_name)
noise_img = random_noise(img, mode='s&p',amount=0.5)
noise_img = np.array(255*noise_img, dtype = 'uint8')

# 画像を描画
%matplotlib inline
from matplotlib import pyplot as plt
plt.imshow(noise_img)
plt.show()

# 画像をダウンロード
from PIL import Image
Image.fromarray(noise_img).save('edited_image.jpg')
files.download('edited_image.jpg')
```


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542224/0271794b-c7e5-0be7-f045-fa070fa1a60e.jpeg" width=50%>
が下のようになります

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542224/10ba1973-a8c8-6be6-346b-01f52030a554.jpeg" width=50%>


参考
https://theailearner.com/tag/skimage-util-random_noise/

かわいい猫ちゃんはunsplashからとってきてます
https://unsplash.com/photos/IuJc2qh2TcA
Rails RSpec コントローラーテストでログイン状態にする

rails初学者です。
RSpecでコントローラーのテストを実装するにあたり、新規投稿画面にアクセスした場合(newアクション)のテストをログイン/非ログイン状態で分岐させたく、方法を調べたので備忘録としてまとめました。
初学者向けの内容になっています。

## 環境
Ruby 2.6.5
Rails 6.0.3

以下インストール済み
Devise 4.7.3
RSpec 4.0.0
FactoryBot

## 前提条件
deviseのauthenticate_userを使って、postコントローラーのnewアクションに非ログイン状態でアクセスすると、ログイン画面にリダレクトされるようにしています。

```ruby:posts_controller.rb
class PostsController < ApplicationController
  before_action :authenticate_user!, only: [:new]

~中略~

  def new
    @post = Post.new
  end

~中略~

end
```
こちらの機能をテストしていきます。

## Postsコントローラーテスト

#### RSpecでDeviseを使えるようにする。

```ruby:spec/rails_helper.rb
RSpec.configure do |config|

~中略~
  #下のコードを追記
  config.include Devise::Test::IntegrationHelpers, type: :request
end
```
こちらのコードをrails_helper.rbに記述することで、requestテスト時にdeviseのヘルパーを呼び出すことができます。

#### テスト用ユーザーデータを用意
FactoryBotを使ってユーザーのテスト用データを用意します。

```ruby:factories/users.rb
FactoryBot.define do
  factory :user do
    email                 { "test@example.com" }
    password              { "123456" }
    password_confirmation { password }
  end
end
```
ユーザーモデルにnameカラムなどを追加している場合は追加したカラムのデータも記述します。
Fakerを使ってダミーデータを生成しても良いです。

#### posts_spec.rbを作成

```ruby:spec/requests/posts_spec.rb
require 'rails_helper'

RSpec.describe "Posts", type: :request do
  before do
    @user = FactoryBot.create(:user) #FactoryBotを利用してuserデータを作成
  end

  describe 'GET #new' do
    context "ログインしている場合" do
　　　#サインインする
      before do
        sign_in @user
      end
      it '正常にレスポンスが返ってくる' do
        get new_post_path
        expect(response.status).to eq 200 #正常にレスポンスが返されHTTPステータス200が発生
      end
    end

    context "ログインしていない場合" do
      it 'ログインページにリダイレクトされる' do
        get new_post_path
        expect(response.status).to eq 302 ##リダイレクトされHTTPステータス302が発生
      end
    end
  end
```
先ほどrails_helperにDeviseをincludeしたのでsign_inというヘルパーが利用でき、ログイン状態を作ることができます。ログインしている場合とログインしていない場合とで、sign_inを使い分けます。

```ruby
before do
  sign_in @user
end
```
あとはeqマッチャを使って、response.statusを指定します。

```ruby
#正常にレスポンスが返ってきた場合
expect(response.status).to eq 200
#リダイレクトされた場合
expect(response.status).to eq 302
```

(レスポンスステータスは https://developer.mozilla.org/ja/docs/Web/HTTP/Status 参照)


これでテストを実行すると、テストが成功するはずです。
【Vue.js】createdとmountedの違い

# createdとmountedの違い

## 前提
ライフサイクルというものが何なのか理解するのが最初は非常に難しいらしい
利用頻度の高いcreated, mountedで何が行われるのかと2つの区別がはっきりつけばライフサイクルの理解が進むかも

## 結論
created：インスタンス初期化時、DOMが生成される前
mounted：インスタンス初期化時、DOMが生成された後


## ライフサイクルフックとは
- vue.jsの初期化の中の決められたタイミングで実行される関数
- created, mountedもライフサイクルフックの中の2つ
- 各ライフサイクルフックの中にプログラムを記述することでvue.jsの初期化の流れの中で記述したプログラムを必ず実行させることができる

## 本題

### elの中身を知る
elはvue.jsインスタンスがマウントを行う要素で、elを指定した要素の中でだけvue.jsを動かすことができる

vueコンポーネント

```vue

<div id="app">
  <h1>Hello World</h1>
  <button v-on:click="showEl">show el</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
  el: '#app',
  data: {
  },
  methods:{
    showEl : function(){
      console.log(this.$el) //this.$elをでelの中身が確かめられる
    }
  }
})
</script> 
```

#### elの中身 = elはnew Vueインスタンスで指定した要素そのもの

```vue
  <div id="app">
    <h1>Hello World</h1>
    <button v-on:click="showEl">show el</button>
  </div>
```

### createdとmountedの違い
createdとmountedの違いは下記のように説明されている

>createdはDOMがまだ作られていない状態で、mountedではDOMが作成された直後の状態である

以下で確認

```js

new Vue({
  el: '#app',
  data: {
  },
  created : function(){
    console.log('created')
    console.log(this.$el)
  },
  mounted : function(){
    console.log('mounted')
    console.log(this.$el)
  }
})
```
createdではDOMが作成されていないので、this.$elはundefinedとなっており、mountedではDOMの作成が完成していることが確認できた

### よって
createdではDOMの取得ができない
mountedではDOMの取得ができ流ので、ここからDOM操作が行える

## createdでは何が完了しているのか
今度はthisを使ってVueインスタンスそのものを確認する

```js
  el: '#app',
  data: {
    message : 'Hello World'
  },
  created : function(){
    this.message = 'Hello Vue'
    console.log('created')
    console.log(this)
  }
```
dataオブジェクトがリアクティブになっているので、
createdの中でデータプロパテにデータを挿入することは可能

## createdとbeforeCreateの違い
beforeCreateでは、dataの中身を確認することができますが、createdのようにmessageの上書きを行うことはできない

beforeCreateでインスタンスの作成が完了、dataオブジェクトがリアクティブになっていないという説明を受けても理解することができる

# 理解が深まったこと
- elの理解
- beforeCreate, created, mountedのライフサイクルフックの違い

## 参考
https://reffect.co.jp/vue/vue-js-created-mounted-diffrence#i
NAS初期設定(その4.1) ネットワークと仮想スイッチ

# ネットワークと仮想スイッチ

# 概要
仮想環境向けの仮想化設定が見やすい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/3503ed44-2818-1941-b923-abd564976e03.png)

# ネットワーク
## インターフェイス
### インターフェイス (設定変更あり)
NICに名前を設定可能。変えないけど。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/bb749ca3-e2c7-4482-de5c-f3003f38f648.png)

IPv4設定。初期設定で静的アドレスに変更済みのためこのまま。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/731209a4-fb38-93b2-b556-5b699e6d2232.png)

IPv6設定。IPv6は使用しないため無効に変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/19cb7011-0d13-33fd-7921-52b0d91c2230.png)

DNS設定。初期設定で変更済みのためこのまま。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/4022441f-75c8-b505-e0eb-f2966c12df1b.png)

VLANの追加。VLANもひとまず使わないため、何も追加しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/2de82020-2b4a-363d-b81d-12ca3f5b6e46.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/6aaa106d-8f77-7534-5cfd-f7c49073e4fd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/1398e8ca-fc2c-da31-8cc2-9998d1f07072.png)

### Wi-Fi (設定変更なし)
WiFiアダプタはない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/571418b2-6052-65fc-5237-c35d116a43f1.png)

### 仮想スイッチ (設定変更なし)
仮想化設定は今はしない。(使うときに設定する)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/bc8fc317-68a4-765c-b46b-173b252faa4b.png)


## DHCPサーバー
### DHCPサーバー (設定変更なし)
ルータにDHCP機能があるため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/968cf2e6-dc1e-3eee-59a1-9197b4dc954a.png)

### RADVD (設定変更なし)
IPv6は使わないため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/24a33420-624f-9c22-ac8a-46563aa463ed.png)


## 経路 (設定変更なし)
ルーティング設定。当面は使わないため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/2fdab530-21d7-b0a7-c619-f820cd6ee9d1.png)


# アクセスサービス
## DDNS (設定変更なし)
DDNS設定。当面は使わないため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/67e27522-0fa4-5d3c-72b7-d4a8505d8843.png)


以上。
NAS初期設定(その4) コンパネ＞ネットワークとファイルサービス

# コンパネ＞ネットワークとファイルサービス
## ネットワークと仮想スイッチ
[こちら](https://qiita.com/kurapooh/items/e9d374ecd960d3d8be71)を参照。


## ネットワークアクセス
### サービスバインディング (設定変更なし)
NICが複数ある場合に、サービスを運用するNICを選択できる。
当面NICは1つで運用するため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/e6502473-b710-a6fc-991e-021314617e8a.png)

設定する場合はサービス毎に運用するNICをチェックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/97536eb7-351d-f985-ecb1-34e5e203569a.png)

### Proxy (設定変更なし)
プロキシは使用しないため設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/1c2f9b8d-07a8-a3c0-cfcc-85f81f0923c7.png)


## Win/Mac/NFS
### Microsoftネットワーク (設定変更なし)
初期設定で使用するに設定済み。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/7e19cbdf-0213-298f-f8b1-c9c357b92411.png)

詳細設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/81d7368c-94bd-2b82-694c-9950d8caa8eb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/f4ba81b5-10d0-a6da-330c-83d31d59630a.png)

### Appleネットワーク (設定変更なし)
初期設定で使用しないに設定済み。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/21179a9b-33b4-774d-7808-d1096875226c.png)

### NFSサービス (設定変更なし)
初期設定で使用するに設定済み。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/9fb235a6-a605-7681-7bfa-839f27c23094.png)


## Telnet/SSH (設定変更なし)
使用しないため設定しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/dd94fc32-bcc6-9a41-3a98-3110a1fd1e9d.png)


## SNMP (設定変更なし)
SNMPは使用しない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/57e7564e-c309-d9c9-901e-21e19c4b1d8e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/12cd59ad-90f6-818c-ecf8-456edac745e0.png)


## サービス検出
### UPnPディスカバリサービス (設定変更あり)
デフォルト有効になっている。
ホームユース＆広く知ってもらう必要もないため無効に設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/574675c8-57e0-ecd5-ea09-aed1164de445.png)

### Bonjour (設定変更あり)
デフォルト有効になっている。
ホームユース＆広く知ってもらう必要もないため無効に設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/70101bb0-c916-892c-ee9a-5ef5f61270c6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/d839477c-777d-e697-0e20-956eca6bf7b0.png)


## FTP
### FTPサービス (設定変更なし)
FTPは使わないため無効のまま。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/3fbf75e3-557a-7fb9-cb88-7c609dffa1b2.png)

### 詳細設定 (設定変更なし)
設定なし。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/d605dfef-c866-6997-4bd8-6f847d328d86.png)


## ネットワークごみ箱 (設定変更なし)
デフォルトのまま。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407847/81a4455e-dbd6-ae69-c07a-cdb6fa557222.png)


以上。
PHPで言うセッションとクッキーの違い

聞かれたときにパッと答えられなかったので備忘録として

PHPで言うセッション（`$_SESSION`）とクッキー（`$_COOKIE`）の違いは

## セッションとは
PHP公式の説明だと
> PHPのセッションサポート機能は、複数回のアクセスを通じて特定のデータを保持する手段を実現するものです。
> Web サイトの訪問者にはセッションIDというセッションIDと呼ばれるユニークなIDが割りつけられ ます。このIDは、ユーザー側にクッキーとして保存するか、または、URL に埋め込みます。

https://www.php.net/manual/ja/intro.session.php

要は「セッションID」をクッキーorURLに持たせて、その「セッションID」をキーにサーバーにデータを保存しています。（デフォルトだと「セッションID」はクッキーに持たせている）

MAMPのphp.iniの設定では、下記にセッションのデータが残っているはずです

```
session.save_path = /Applications/MAMP/tmp/php
```

「セッションID」はデフォルトで「PHPSESSID」としてクッキーに保存しているので、試しに下記を実行するとアクセスしているユーザーのセッションが全部消えるはずです（消えると言うより取得できなくなる。サーバー側に残っちゃう）

```
setcookie('PHPSESSID', '');
```

セッションの詳しい設定は下記に載っています
https://www.php.net/manual/ja/session.configuration.php


## クッキーとは
PHP公式の説明だと
> PHP は、HTTP クッキー(Cookie)を完全にサポートします。 クッキーは、リモートブラウザに文字列データを保存したり、 再訪するユーザーを特定したりする機構です。

https://www.php.net/manual/ja/features.cookies.php

要はHTTPクッキーそのものです。HTTPクッキーはブラウザ側にキーと値のペアで保存されるデータです。
> HTTP Cookie (ウェブ Cookie、ブラウザー Cookie) は、サーバーがユーザーのウェブブラウザーに送信する小さなデータであり、ブラウザーに保存され、その後のリクエストと共に同じサーバーへ返送されます。
https://developer.mozilla.org/ja/docs/Web/HTTP/Cookies

## まとめ
- セッションはブラウザ＋サーバー側で持ってるデータ
- クッキーはブラウザ側で持ってるデータ

ただしこの話はPHPの話であって
一般的に言われる「セッション」もこうとは限らないです！
一般的に言われる「クッキー」はHTTPクッキーのことと思ってもらっても大丈夫です。
PythonとAnacondaとTensorflowでGPU環境を整える

#メモ書きです．

色々調べて最新版入れればいいだろと思って
https://www.tensorflow.org/install/source_windows?hl=ja
ここから対応表見てPython3.8とTensorflow-gpu ver2.3でやって見たのですがうまく行かず･･･

どうも，TensorflowにはGPU用のビルドがあるらしく
https://medium.com/lsc-psd/tensorflow2-1%E3%81%A7cuda10-1%E3%81%AA%E3%81%AE%E3%81%ABgpu%E3%81%8C%E8%AA%8D%E8%AD%98%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84%E5%95%8F%E9%A1%8C%E3%81%AE%E8%A7%A3%E6%B1%BA%E6%B3%95-6be5137ec216
現在2021年4月11日時点ではTensorflow 2.3をAnacondaのGUIからダウンロードするとmklビルド（CPU用のビルドらしい）が当たるらしく，それでうまく行かなかったようです．

それぞれのバージョンをPython3.7とTensorflow_gpu-2.1.0まで落としてするとAnacondaのGUIからのインストールでもGPU用のビルドが適用され何事もなくGPU認識となりました．

#あとがき
自前でビルドしてしまえば関係ないかもしれないですね．あくまでAnacondaを利用した場合の話です．
###使用したバージョン
tensorflow_gpu 2.1.0 (AnacondaのGUIより）
Python 3.7　（AnacondaのGUIより）	
MSVC 2019　（参考資料[2]より）
cuDNN 7.6　（参考資料[3]より）
CUDA 10.1　（参考資料[4]より）

システム環境変数のPathに[5]に示してあるようなフォルダのパスを追加すること．（エクスプローラから直接確認コピペすること）

#参考資料
[1] https://qiita.com/nemutas/items/c7d9cca91a7e1bd404b6
[2] https://www.kkaneko.jp/tools/win/buildtool.html#S1
[3] https://developer.nvidia.com/rdp/cudnn-archive
[4] https://developer.nvidia.com/cuda-10.1-download-archive-base?target_os=Windows&target_arch=x86_64&target_version=10
[5] https://www.tensorflow.org/install/source_windows?hl=ja
【M5Stack UIFlow】M5Stack本体へのプログラム更新のときに”maybe your device is offline"って怒られた時

M5Stack UIFlowで、M5Stack本体へのプログラム更新は、しょっちゅうエラーで怒られるけど、適当にしたら直ってるので、とくに気にしてなかったです。
でも、やっぱり毎回少なからずストレスを感じるので、自分用にマニュアル化します。

##エラー内容
M5Stack UIFlowで、M5Stack本体へのプログラムの更新は、以下の画像の①または②をクリックします。

[![Image from Gyazo](https://i.gyazo.com/1c0887ddfbfaa1f6714298d746c3c7b7.png)](https://gyazo.com/1c0887ddfbfaa1f6714298d746c3c7b7)

それで、よく以下のエラーで怒られます。
「デバイスがオフラインだよ」って。
Upload code failed, maybe your device is offline check it and retry
[![Image from Gyazo](https://i.gyazo.com/8fb621c65081170c87d3bf38f99ab0d4.png)](https://gyazo.com/8fb621c65081170c87d3bf38f99ab0d4)

##対処方法
原因は、M5Stackが AppMode になっているからなので、設定を変更します。

M5Stackの左側面のボタンを押します。
[![Image from Gyazo](https://i.gyazo.com/fa53d31133d356827b0192ead9affdfc.jpg)](https://gyazo.com/fa53d31133d356827b0192ead9affdfc)

この画面になったら、すかさず　[Setup]に対応したボタンを押します。
[![Image from Gyazo](https://i.gyazo.com/371981702f1a93f34a6036599de5d925.jpg)](https://gyazo.com/371981702f1a93f34a6036599de5d925)

App Modeになっているので、Internet Modeに変更します。

Switch mode > Internet Mode 

[![Image from Gyazo](https://i.gyazo.com/2a6a7d0ed9fb640adc86e64d0cb0c6ae.jpg)](https://gyazo.com/2a6a7d0ed9fb640adc86e64d0cb0c6ae)

左側面のボタンを押します。

UIFlowの以下の①または②をクリックして、プログラムを更新すると、うまくいくはずです。
[![Image from Gyazo](https://i.gyazo.com/1c0887ddfbfaa1f6714298d746c3c7b7.png)](https://gyazo.com/1c0887ddfbfaa1f6714298d746c3c7b7)

<br>
ssh 認証キーを指定してssh接続を行う

# 目的

- ssh接続する際の認証キーを指定する方法をメモ的にまとめる

# 方法

- 下記のようにオプションをつけて`$ ssh`コマンドを実行する。

    ```terminal
    $ ssh -i 認証キー（秘密鍵）までのパス ssh接続先のユーザー名@ssh接続先のIPアドレス
    ```
プログラミング初心者が、httpをざっくりまとめてみた

#httpとは
　httpとは、webユーザとwebサーバのデータ受信に使うプロトコルです。
<font color="ForestGreen">プロトコルとは、通信規約です。崩して言うと、通信方法をまとめたもの。</font>
簡単に言うと、**webユーザとwebサーバが通信する際は、httpという方法で行きましょう**といった感じです。PCやスマートフォンなどインターネットを利用している環境が異なる場合も、共通の通信規約を定めることで、同じ手順でホームページのデータをやりとりできます。
#httpとhttpsの違い
　ちなみに同じプロトコルの一つで、httpsというものがあります。httpとhttpsの違いは、「通信が暗号化されているかどうか」です。httpsは、SSL(セキュリティ機能を追加したプロトコル)を利用しており、下記の機能を持ちます。
1.通信内容を秘匿する暗号機能
2.通信相手の真正性(しんせいせい)
3.通信データが改ざんされていないか検知する機能
#httpの中身
HTTPでサーバとクライアントがやりとりするメッセージ（HTTPメッセージ）は基本的に以下のような形になっている。

```
メッセージヘッダ
空行(CR+LF)
メッセージボディ
```
リクエストメッセージ（クライアント=>サーバへのメッセージ）は具体的には以下のようなものである。(今回は、GETを使いました。)

```
GET /index.html HTTP/1.1 
Host: localhost:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept: */*
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: ja,en-US;q=0.8,en;q=0.6
```
#GETメソッドの特徴
* サーバーからデータをもらってくるときによく使われる
* 送れるデータ量に制限がある
* テキストデータのみ送信できる(バイナリーデータは不可)<br>
<font color="ForestGreen">バイナリーデータとはコンピュータが理解しやすいデータ</font>
* 外部から送信したデータが、確認できる

#GETメソッドでの、サーバーへのデータの送信方法
* リクエストして、サーバーへ送られるデータは、リクエストurlの後ろにきます

```
https://www.google.com/?gws_rd=ssl
```
?より後の文字をクエリストリングといい、送信するデータを表す。
#パーセントエンコーディングとは
送信するデータで、使えない文字が入っている場合別の文字に置き換える方法です。
例えば「あ」➡「%E3%81%82」といった感じで、変換していきます。
#POSTメソッドの特徴
* サーバーに登録する作業を行うときによく使われる
* GETメソッドで送れるデータ量を超えている使用
* 送るデータを外部にばれないようにできる
* バイナリーデータを送りたいとき使う
# POSTメソッドのリクエスト例

```
POST /hoge/ HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: http://localhost:8080/hoge/
Accept-Encoding: gzip, deflate, br
Accept-Language: ja,en-US;q=0.8,en;q=0.6

name=hoge&comment=hoge
```
GETメソッドと同じようにリクエストヘッダの後に改行が入り、リクエストボディが書かれている。
JavaScript canvasで線を引く

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <canvas id="sample" width="500" height="2000"></canvas>
  <script src="main.js"></script>
</body>
</html>
```

```main.js
const draw = () => {
  const xValue = 10;
  const yValue = 60;
  const xMove = 200;
  const textXaxis = 40;
  const textYaxis = 20;
  const canvas = document.getElementById('sample');
  const ctx = canvas.getContext('2d');
  const width = canvas.getAttribute('width');
  const height = canvas.getAttribute('height');

  ctx.clearRect(0, 0, width, height);
  ctx.font = "20px arial";
  ctx.fillText('Shuffle Alphabet', textXaxis, textYaxis);

  const aTozArray = range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x));
  const array = Array.from({length: aTozArray.length}, (v, i) => i);
  const randomArray = shuffle(array);

  ctx.beginPath();
  for (let i = 0; i < aTozArray.length; i++) {
    ctx.fillText(aTozArray[i], xValue, yValue * (i + 1));
    ctx.fillText(aTozArray[i], xValue + xMove, yValue * (i + 1));
    ctx.moveTo(xValue, yValue * (i + 1));
    ctx.lineTo(xValue + xMove, yValue * (parseInt(randomArray[i], 10) + 1));
  }
  ctx.stroke();
}

const shuffle = (array) => {
  for(let i = array.length - 1; i > 0; i--){
    let r = Math.floor(Math.random() * (i + 1));
    let tmp = array[i];
    array[i] = array[r];
    array[r] = tmp;
  }
  return array
}

const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

draw()
```

![shuffle_aphabet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/2df25f5c-12a7-7b09-1c38-94baf607fbfe.gif)



# 参考記事

https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/canvas

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/from

https://qiita.com/komaji504/items/62a0f8ea43053e90555a
git cherry-pickで特定コミットを反映しよう

#初心者でも理解できるgithub講座 cherry-pick編#
###git cheery-pickとは?###
特定のコミットだけを取り出してきたいときに使えるコマンドです。

##使用例###
####git branch構造####
main
├── aaa ブランチ
├── bbb ブランチ

例えば、上記のように、mainブランチを親としてそれぞれaaaとbbbブランチを切ります。
そして、aaaでファイルを変更し、追加・コミットします。
このaaaでコミットされたものをbbbブランチにも反映する時などに、cheery-picを用います。

##git cherry-pick コマンドで特定コミットを反映##
 
まずは、aaaブランチでファイルを作成し、追加(git add)とコミットを行います。
git logコマンドでコミットを確認しましょう。

```
git log

commit 0497a98eed98112e8af84f9c6d816c96ab24734c (HEAD -> aaa)
Author: ~
Date:   Sun Apr 11 20:41:31 2021 +0900

    aaaでコミット(コミットメッセージ)

commit fd037f8ba24b05b9b1214edfd3ed8258f063a694 (origin/main, origin/HEAD, main, bbb)
Author: user <~users.noreply.github.com>
Date:   Sun Apr 11 20:35:41 2021 +0900
```
mainブランチに戻り、

```
git checkout -b bbb
ブランチを切り替えました。
```
いよいよ、aaaブランチでコミットを取り出します。

```
特定コミットの反映コマンド
git cherry-pick コミットID

git cherry-pick  0497a98eed98112e8af84f9c6d816c96ab24734c
#コミットIDはgit logコマンドで確認できます。
```

cheery-pickで特定コミットをbbbブランチに反映しました。
確認してみます。

```
git log
commit 31874f15070ed95ee9b1717d065205bfb238c3fe (HEAD -> bbb)
Author: ~
Date:   Sun Apr 11 20:41:31 2021 +0900

    aaaでコミット

commit fd037f8ba24b05b9b1214edfd3ed8258f063a694 (origin/main, origin/HEAD, main)
Author: ~ <~@users.noreply.github.com>
Date:   Sun Apr 11 20:35:41 2021 +0900
```

このようにgit cherry-pickで特定コミットを反映できます。
今回は、ローカル上で行いましたが、リモートブランチの特定コミットや複数のコミットも取得可能です。

以上
SpreadSheet: 2つの配列をクロス結合する

Google SpreadSheet で2つの配列をクロス結合したい場合があります。
例えば、メンバー各々に決まった属性を割り当てた表を作る場合に、メンバー追加時に属性行も追加する作業は非常に面倒です。
メンバーを追加する時は、メンバー一覧にメンバーの名前だけ追加すれば、あとは各属性行も追加されるようにできれば楽になります。

[公式のドキュメント](https://support.google.com/docs/answer/10307761?hl=ja)に方法が載っていたのですが、初見だと理解ができなかったので、式の要素を分解して理解してみました。
要素ごとに記事を書いています。

- [SpreadSheet: ArrayFormula で二次元配列を作成する](https://qiita.com/matsukatsu/items/f0f42472cd51d2c63058)
- [SpreadSheet: Flatten で二次元配列を一次元配列に変換する](https://qiita.com/matsukatsu/items/ecd3c4bd5dbcfcdc89a9)

### やってみる

公式の手法にしたがってやってみます。
Filter 関数を使用して、空の値をスキップさせます。

```
=arrayformula(split(flatten(filter(W2:W,not(isblank(W2:W)))&"|"&transpose(filter(X2:X,not(isblank(X2:X))))),"|"))
```

メンバーと属性の一覧を作成します。
（上式のW列がメンバー列、X列が属性列）

![一覧1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/9bc37ff6-1571-06b3-8692-14273d40ccbe.png)

メンバーと属性のクロス結合した一覧が作成されました。

![クロス1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/5de05c2a-c211-7b95-35bb-5b225b728df0.png)

メンバーを追加してみます。

![一覧2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/7cecb691-e05f-9d2d-0997-0528480d7506.png)

追加したメンバーの各属性が追加されました。

![クロス2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/74a3705a-7a70-586e-a51a-ed87f08a2e43.png)
Let's Encryptの更新(手動)

## Let's Encryptの手動更新の方法(備忘録)
ホントは自動更新したいけどcronがうまく機能しないので仕方なく手動でやる(気が向いたら自動更新も調べる)

### rootユーザーで入る
```
[ec2-user@ip-172-31-36-245 git_toreka]$ sudo -i
```

### 下記コマンドを打つ
```
[root@ip-172-31-36-245 ~]# /usr/local/bin/certbot-auto renew --post-hook "sudo service httpd restart"
```

こんな感じで出たらOK

```
Upgrading certbot-auto 1.11.0 to 1.14.0...
Replacing certbot-auto...
Your system is not supported by certbot-auto anymore.
certbot-auto and its Certbot installation will no longer receive updates.
You will not receive any bug fixes including those fixing server compatibility
or security problems.
Please visit https://certbot.eff.org/ to check for other alternatives.
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/torekabodymake.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificates are not due for renewal yet:
  /etc/letsencrypt/live/torekabodymake.com/fullchain.pem expires on 2021-07-10 (skipped)
No renewals were attempted.
No hooks were run.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```

`2021-07-10`と3ヶ月後の日付が書いてあるので成功してる(はず)

自動更新ができるようになるまではとりあえずコレで行きます。
少し前にやったことがどんどん忘れていってしまうので、なにかやったらこうやって簡単にでも記録するようにします。
【C#】C#のバージョンと.NETのバージョンを確認する方法

#C#のバージョン確認方法#
コードのどこかに**#error version**を入力する

↓こんな感じで入力すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100934/d9bb7785-1321-b15d-e581-9b4407e47827.png)

↓こんな風に、Visual Studio の警告に出てくる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1100934/d6615ef0-1c76-fe58-1a3d-6d41e1619a25.png)

これだと私の場合はC#8.0

#.NETのバージョン確認方法#
**System.Runtime.InteropServices** をusingして
**RuntimeInformation.FrameworkDescription**を参照すれば良い

```c#
using System;
using System.Runtime.InteropServices;

class Class1
{
    static void Main(string[] args)
    {
        Console.WriteLine(RuntimeInformation.FrameworkDescription);
    }
}
```

結果：

```
.NET Core 3.1.8
```


#参考#
https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.runtimeinformation.frameworkdescription?view=net-5.0#System_Runtime_InteropServices_RuntimeInformation_FrameworkDescription

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives