OmniAICreator/Qiita-1.07M huggingface dataset:
mysqlで複数のorder by設定したときに少しハマった件

# mysqlで複数のorder by指定

構文 is

```sql
SELECT *
FROM tables
ORDER BY name1, name2
;
```

知っている人からすると当たり前で何を今更って感じかもしれないのですが、
name2でソートしてから、name1でソートされるみたいでした。

## 動作確認のログ

```bash
vagrant@homestead:~$ mysql -V
mysql  Ver 14.14 Distrib 5.7.31, for Linux (x86_64) using  EditLine wrapper

mysql> create database sample;
Query OK, 1 row affected (0.00 sec)

mysql> use sample;
Database changed

mysql> create table users (id int, name1 varchar(10), name2 varchar(10));
Query OK, 0 rows affected (0.04 sec)
```

```sql
insert into users values 
(1, 'A', '1')
,(2, 'B', '1')
,(3, 'B', '1')
,(4, 'C', '1')
,(5, 'C', '2')
,(6, 'D', '1')
;
```

```sql
mysql> select *
    -> from users
    -> order by id;
+------+-------+-------+
| id   | name1 | name2 |
+------+-------+-------+
|    1 | A     | 1     |
|    2 | B     | 1     |
|    3 | B     | 1     |
|    4 | C     | 1     |
|    5 | C     | 2     |
|    6 | D     | 1     |
+------+-------+-------+
6 rows in set (0.00 sec)
```

```sql
mysql> select * from users order by name1, name2;
+------+-------+-------+
| id   | name1 | name2 |
+------+-------+-------+
|    1 | A     | 1     |
|    2 | B     | 1     |
|    3 | B     | 1     |
|    4 | C     | 1     |
|    5 | C     | 2     |
|    6 | D     | 1     |
+------+-------+-------+
6 rows in set (0.00 sec)

mysql> select * from users order by name2, name1;
+------+-------+-------+
| id   | name1 | name2 |
+------+-------+-------+
|    1 | A     | 1     |
|    2 | B     | 1     |
|    3 | B     | 1     |
|    4 | C     | 1     |
|    6 | D     | 1     |
|    5 | C     | 2     |
+------+-------+-------+
6 rows in set (0.00 sec)
```

## その他

昇順、降順をいじる場合

name1順にならべたいけど、name2は降順でいてほしい。

```sql
mysql> SELECT * FROM users ORDER BY name1 ASC, name2 DESC;
+------+-------+-------+
| id   | name1 | name2 |
+------+-------+-------+
|    1 | A     | 1     |
|    2 | B     | 1     |
|    3 | B     | 1     |
|    5 | C     | 2     |
|    4 | C     | 1     |
|    6 | D     | 1     |
+------+-------+-------+
6 rows in set (0.00 sec)
```
PROJ 7.2 の標高変換

Proj で、国土地理院が公開しているジオイドデータを利用できるようになりました。

## ジオイドとは

水は高いところから低いところに流れます。

ですが、引力は質量に比例し距離の二乗に逆比例しますので、同じ距離にある海域（海水）よりも山岳部（鉱物）の方に強く引き寄せられます。

潮汐や波を考えず、重力的に均衡が取れた海水面をジオイドと呼び、そのジオイド面からの高さを「標高」といいます。普段使っている標高のことです。

一方、地球全体をモデル化した回転楕円体からの高さを「楕円体高」といいます。また回転楕円体からジオイド面までの高さをジオイド高といいます。

![ジオイドの関係](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215290/d46e7046-d188-9739-2ded-84a8ad1e661c.png)
[国土地理院ウェブサイト - ジオイド - ジオイドを知る](https://www.gsi.go.jp/buturisokuchi/grageo_geoid.html)より　（[国土地理院コンテンツ利用規約](https://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html)に基づき複製）

## 標高の求めるにはジオイドデータが必要

衛星測位等では楕円体高は計算で求めることができますが、標高はジオイド高がわからないと求めることができません。

そしてジオイド高は規則性がないため、精密な重力測定によって得られた場所ごとの値が必要となります。そのひとつが国土地理院が提供するジオイドデータです。

* [国土地理院 - ジオイド・モデル（GSIGEO2011）](https://www.gsi.go.jp/buturisokuchi/grageo_geoidseika.html)
* [国土地理院 - 基盤地図情報ダウンロードサービス](https://fgd.gsi.go.jp/download/menu.php)

## PROJ での変換

```console
$ proj
Rel. 7.2.0, November 1st, 2020
usage: proj [-bdeEfiIlmorsStTvVwW [args]] [+opt[=arg] ...] [file ...]

$ echo 34.290 135.630 0.0 | PROJ_NETWORK=ON cs2cs -f "%.4f" EPSG:6667 EPSG:6697
34.2900	135.6300 -39.8601

$ echo 34.290 135.630 0.0 | PROJ_NETWORK=ON cs2cs -f "%.4f" EPSG:6667 EPSG:6674+6695
-189632.0416	-34062.4530 -39.8601
```

楕円体高から標高に変換した例です。（楕円体高 0.0m 地点を、標高すなわちジオイド面からの高さに変換しているので負数になります。）

検算は[国土地理院 - 測量計算サイト - ジオイド高計算](https://vldb.gsi.go.jp/sokuchi/surveycalc/geoid/calcgh/calcframe.html)ですることができます。（ジオイド高すなわち回転楕円体表面からの高さを求めているので、上記変換とは正負逆になります。）

PROJ-data もインストールしていれば `PROJ_NETWORK=ON` はなくてもよいです。

## EPSG における JGD2011

ちなみに EPSG に登録されている JGD2011 は以下のとおりです。

| EPSG | 説明 |
|:----:| ---- |
| 6666 | JGD2011 地心直交座標系 (X, Y, Z) |
| 6667 | JGD2011 緯度、経度、楕円体高 |
| 6668 | JGD2011 緯度、経度 |
| 6669 | JGD2011 平面直角座標系1系 |
|  :   |  |
| 6687 | JGD2011 平面直角座標系19系 |
| 6688 | JGD2011 UTM zone 51N |
|  :   |  |
| 6692 | JGD2011 UTM zone 55N |
| 6695 | JGD2011 標高 |
| 6697 | JGD2011 緯度、経度、標高 (EPSG:6668+6695) |
独自ドメインを取得し、サイトをSSL化対応するまでを全てAWS上で行ってみた。

この度AWS SAA試験を受験することに決め、勉強の一環として
AWSで独自ドメインの取得からそれをサイトに割り当て、SSL化対応するまでを実際にやってみました。
以下備忘録として完了までの手順をまとめてみようと思います。

# 必要手順
・EC2インスタンスを取得
・インスタンスにElastic IPの割り当て
・Route53で独自ドメインの取得
・ACMでSSL証明書の取得
・ELBを設定
・DNSを設定

大まかな手順は以上なのですがこれらの手順の中身をざっと説明していきます。

##EC2インスタンスを取得
まずは今回ドメインを付与する予定のwebサーバーをEC2にて作成します。

##インスタンスにElastic IPの割り当て
EC2に割り当てられたパブリックIPは停止状態から起動する際にそのつど変更されてしまいます。
そのためElastic IPを割り当て、パブリックIPを固定します。

##Route53で独自ドメインの取得
AWSのサービスの一つであるRoute53にはドメインを登録できる機能があるのでこちらで今回使う独自ドメインの登録を行います。
ドメインには種類があり、料金は種類に応じて変動します。
最低でも１年間からの年間契約であり、今回は１年間の契約で登録しました。

##ACMでSSL証明書の取得
これもまたAWSのサービスであるACM(AWS Certificate Manager)でSSL証明書の発行ができます。
証明書のリクエスト画面から追加予定のドメイン名を入力し、進むとDNSまたEメールでの検証方法が提示されますのでどちらかを選択し、状況が発行済みとなれば使用できます。

##ELBを設定
調べていく中で知ったのですがACMで発行した証明書をEC2に与えることはできないらしく、
そのためロードバランサーを設置しそちらに証明書を使用していきます。

##DNSを設定
Route53にて作成したドメインのAレコードを選択し、ロードバランサーを設定します。

##ブラウザにて確認
以上で作成したwebサイトへのSSL化対応が完了です。
各手順ごとの細かい作成内容は記載しなかったのですが公式のチュートリアルでインスタンスの作成からDNSの設定までまとめられていますので同じことをやってみる方は参考にしてみてください。
https://aws.amazon.com/jp/getting-started/hands-on/get-a-domain/
windowsでのpython開発環境構築例（wsl2, vscode, pipenv）

# 背景
- windows上でpython開発環境を構築
- プロジェクト毎に環境を分けたい
- Anacondaを使いたくない
    - too much感がある
    - 調べ物をするときに、conda前提で書かれている例が少ない（ような気がする）
   
# Summary
- いろいろ試しましたが、WSL2, Ubuntu20.04, pipenv, VisualStudioCodeが使いやすいと判断
    - 試した過程は記述していません
    - 手順だけ記述します、慣れれば5分くらいで構築できます
  
# 前提
- Windows 10 Home (2020/10ぐらい)
- VisualStudioCode（以下、VSCODE）がインストールされていること

# 手順
- WSL2の環境をセットアップする（ここが一番時間かかる）
    - ※InsiderProgramに入らなくても、いつの間にかWin10HomeでもWSL2が使えるようになっていた
    - 手順はこちらが一番参考になる
        - https://docs.microsoft.com/ja-jp/windows/wsl/install-win10
    - MicrosoftStoreからUbuntu20.04を探してインストールしておく
    - 無事にUbuntu20.04が起動できたら、更新しておく
        - `$ sudo apt update`
        - `$ sudo apt upgrade`
    - ※BIOSでCPUの仮想化がENABLEになっていないと、起動時にエラーが発生するので要注意 
- python3がデフォルトでインストールされている
- pip3をインストール
    - `sudo apt install python3-pip`
- pip3でpipenvをインストール
    - sudoでインストールすること
        - [参考サイト](https://www.it-swarm.dev/ja/python/pipenv%EF%BC%9A%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%8C%E8%A6%8B%E3%81%A4%E3%81%8B%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93/834117829/)
- 任意のプロジェクトフォルダを作成しておく
    - 例：
        - `$ mkdir ~/work/python-test`
        - `$ cd ~/work/python-test`
- 初期化
    - プロジェクトフォルダ直下で下記を実行
        - `pipenv --python=/usr/bin/python3`
            - FIXME: pipenv --python 3をやろうとするとwindowsのpythonを見に行ってしまうので明示的に指定
                - [参考サイト](https://qiita.com/foewhoew32f320/items/bfa90ae1003e45cefe33)
        - Pipfileが生成されていることを確認
- 使いたい任意のパッケージをインストール
    - `pipenv install [package]`
    - Pipfile.lockファイルが生成されていることを確認
- VSCODEの拡張機能をインストール
    - 「Remote - WSL」
- VSCODEとWSLの接続
    - いろいろやり方がある。
        - 方法１：VSCODEの左下のバーから接続して、VSCODEにてUbuntu上のフォルダを開く
        - 方法２：Ubuntu上で`$ code`してから、VSCODEにてUbuntu上のフォルダを開く
        - 方法３：Ubuntu上でプロジェクトフォルダ内に移動して`$ code .`
        - ※初回のcodeコマンド実行時にVSCODEのサーバープログラムが自動でインストールされる 
- VSCODE内でpython開発用の拡張機能をインストール
    - 最低限「ms-python.python」だけあればよい
    - ※拡張機能自体は接続先毎にインストールが必要になるので要注意
- プロジェクトフォルダ内に適当なpythonファイルを作成して開発スタート（**.py）
- 左下のpython interpereterで、([directory name]: pipenv)のものを選ぶ（初回は大体pipenvになっていない）
        - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143120/adeb850c-003f-a0b8-c8c8-9a973a2f0c3d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143120/342572a2-ddc2-c13e-e520-c6fd9cb76614.png)
- 以上で実行、デバッグ可能となる！
    - 実行方法、デバッグ方法の説明は割愛

# おまけ
- バージョン管理
    - PipfileとPipfile.lockは両方ともバージョン管理すること
    - gitなどで、別の環境で開発を開始する場合は、両ファイルが置いてあるフォルダで、`$ pipenv install`すれば自動的に依存モジュールがインストールされて開発環境が作られる
- 仮想環境を削除する場合
    - `$ pipenv --rm`
- インストールされるモジュールの場所
    - デフォルト
        - `~/.local/share/virtualenvs/[フォルダ名]-[ランダム文字列]`
    - プロジェクトフォルダ配下に置きたい場合
        - `$ export PIPENV_VENV_IN_PROJECT=true`
        - プロジェクトフォルダ配下で初期化
        - プロジェクトフォルダ配下に.venvフォルダができて、その中に設置されるようになる
Codility lesson time complexity と 自分の回答メモ (Python)

#Task: Maxcounters
個人の感想です。

##①Speed N*M

    def solution(N, A):
        anslist = [0] * N
        maxvalue = 0
        for i in A:
            try:
                anslist[i-1] += 1
            except:
                maxvalue = max(anslist)
                anslist = [maxvalue] * N
        return anslist

#####感想と学び
毎回tryの中でmaxやるとN*2になってしまうと思ったので、
必要な時だけ限定してmaxすることを試みた。が、当然ながらN+1の登場頻度に依存してしまうのでN*Mになるよなと。納得。

##②Speed: N+M

    def solution(N, A):
        anslist = [0] * N
        maxchecker = 0
        for i in A:
            try:
                anslist[i-1] += 1
                if anslist[i-1] > maxchecker:
                    maxchecker = anslist[i-1]
            except:
                anslist = [maxchecker] * N
        return anslist
感想と学び：なんでN **+M** ???わからない。。。要検討。そしてspeed N or Log(N)にたどり着けていない。
【備忘録】Nuxt + APIGateWay 毎回CORSで引っかかる。対処法

# エラー
![スクリーンショット 2020-11-03 23.16.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/472ecb82-f49b-69df-e046-bdb3cb8e0163.png)

よく Nuxtでaxiosを利用するのですが、毎回このエラーで躓きます。

次引っかかった時のためにメモ



## APIGatewayを確認する
![スクリーンショット 2020-11-04 0.22.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/e075d861-2bbe-1270-db99-392b7cd0aa7d.png)

## デプロイする
![スクリーンショット 2020-11-04 0.22.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/d88bbcf3-e678-f838-06fa-5d7751c10337.png)

## Lambdaに追記

```
 return {
   'statusCode': 200,
   'headers': {
     'Content-Type': 'application/json',  // 追加
     'Access-Control-Allow-Origin': '*'   //追加
   },
   'body': JSON.stringify({ status:’ok’, })
 }
```

## @nuxtjs/proxyを導入

``` nuxt.config.js

modules: [
    "@nuxtjs/axios",'@nuxtjs/proxy' //追記
  ],

proxy: {
    '/api': {
      target: "APIのURLを入力", // https://xxx-api.ap-northeast1.amazonaws.com/1
      pathRewrite: {
        '^/api' : "ページを開くURLを入力" // https://xxxxxxxxxxx.netlify.app
        }
      },
    },
  

```

```index.vue

async register() {
      const response = await this.$axios.$post(
        "叩きたいAPIURL", // https://xx.ap-northeast-1.amazonaws.com/1/register
        {
          type: "new",
        }
      );
    },


```


#2020/10/17追記

safariとchromeでcorsの処理が異なるらしく、アンドロイドでデバックしていたので気づかなかった。
iphoneやmac safariでエラーが多出し、困った。

https://twitter.com/inoue2002/status/1324892071119892482?s=20

特にプリフライトエラーでよくこけた。
これは単純なリクエストで無い場合にoptionリクエストが送られる。
このリクエストでエラーが出ていたために発生したエラーだった。

対処法としては、
![スクリーンショット 2020-11-07 20.35.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/ac19e5fa-8eb6-d841-01f3-5f6a510b9ac1.png)

エンドポイントんいoptionsメゾットを追加し、![スクリーンショット 2020-11-07 20.36.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/8b46e6e2-17fc-6443-2d00-b2367ad4cef0.png)

Mockエンドポイントへとつなげた。




他にもいろいろ試してみた方法があったため記述する。

![スクリーンショット 2020-11-07 20.37.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/d8b27369-83b7-712d-2de0-17c4d7d88cf0.png)
![スクリーンショット 2020-11-07 20.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/6e4f71ef-f162-3ffe-2dc1-9c2449840625.png)

![スクリーンショット 2020-11-07 20.38.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488939/83e57791-264f-679b-388a-779b5a404de7.png)

## 参考文献

https://note.com/kanoemon145/n/n48a50b62dd85
camelotで点線を実線として処理する（ハフ変換）

# はじめに

camelotは点線が苦手でよく失敗するので調べてみると以下の参考の記事が見つかりました

camelotはopencvで抽出しているので点線を書き換えればいいみたいなのでハフ変換で点線を抽出し実線で上書きしてみたらうまくいきました

# 参考

[Pythonを使えばテキストを含むPDFの解析は簡単だ・・・そんなふうに考えていた時期が俺にもありました](https://qiita.com/mima_ita/items/d99afc28b6f51479f850)

[camelotで点線を実線として処理する](https://needtec.sakura.ne.jp/wod07672/2020/05/03/camelot%e3%81%a7%e7%82%b9%e7%b7%9a%e3%82%92%e5%ae%9f%e7%b7%9a%e3%81%a8%e3%81%97%e3%81%a6%e5%87%a6%e7%90%86%e3%81%99%e3%82%8b/)

こちらの記事の横の点線のPDFを使わせていただきます

https://github.com/mima3/yakusyopdf/blob/master/20200502/%E5%85%B5%E5%BA%AB%E7%9C%8C.pdf

# ハフ変換

[OpenCVのハフ変換による直線検出](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html)

![data1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/a78aa06a-903f-5dd6-637f-85657f8acba1.png)

ハフ変換で直線抽出

![houghline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/62db725b-ee54-e0d7-9d3f-a04870cfab30.png)

# 千葉のGo To Eatの加盟店一覧のPDF

![data1(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/f8ecf721-be9b-da17-8d34-694d9c937eea.png)

ハフ変換で水平の直線のみ抽出

![houghline(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/19fc5750-c25a-f757-0e05-5f973a27bb3a.png)

# プログラム

```python
import cv2
import numpy as np

import camelot

# パッチ作成

def my_threshold(imagename, process_background=False, blocksize=15, c=-2):

    img = cv2.imread(imagename)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    edges = cv2.Canny(gray, 50, 150, apertureSize=3)

    lines = cv2.HoughLinesP(
        edges, rho=1, theta=np.pi / 180, threshold=80, minLineLength=3000, maxLineGap=50
    )

    for line in lines:
        x1, y1, x2, y2 = line[0]
        # 水平の場合はy1 == y2、垂直の場合はx1 == x2のifでフィルタする
        cv2.line(img, (x1, y1), (x2, y2), (0, 0, 0), 1) 

    if process_background:
        threshold = cv2.adaptiveThreshold(
            gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blocksize, c
        )
    else:
        threshold = cv2.adaptiveThreshold(
            np.invert(gray),
            255,
            cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY,
            blocksize,
            c,
        )
    return img, threshold

camelot.parsers.lattice.adaptive_threshold = my_threshold

tables = camelot.read_pdf("data.pdf", pages="all")

tables[0].df
```
# パッチ摘要前
点線部分が反応しないため縦に結合してしまっている
![Screenshot_2020-11-04 Google Colaboratory(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/9c2d8a8f-5672-6596-61e2-5be9eb3471a7.png)

# パッチ摘要後
![Screenshot_2020-11-04 Google Colaboratory.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36151/a735665c-ca06-13bd-b4a1-62d237c262a5.png)
3分で分かる！sessionとcookieの違いを超ざっくり解説

![スクリーンショット 2020-11-04 0.35.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/e4f8de0f-1f4d-224a-0e95-63aa5b176b67.png)
#　sessionとcookieの共通した役割

まずsessionとcookieは情報を記憶するといった共通の役割があります！
これがこんがらがる原因ですね、、、

# cookieとは
まずcookieとはブラウザで持っているファイルのことを表しています。
クロームだったりファイヤーフォックスだったり色々とありますね。

例えばログイン情報だったりもブラウザにcookieを使って保存してあります。
後わかりやすいのが広告ですね！！

![スクリーンショット 2020-11-04 0.35.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/7cddcaef-27ea-11a9-e2be-5aecaf94bffb.png)

僕なんかは最近転職についてはQiitaの記事も色々見ているのでこんな広告が表示されています。
これはサードパーティークッキーといった機能を使ってその人のcookieの情報から適した広告を表示しています！

Cookieのキャッシュがたまりすぎるとchromeの動作が重くなる原因にもなるのでブラウザが重いと感じた場合はcookieのキャッシュも見直して見るといいかもしれません！
https://www.webhack.jp/web-service/google-chrome%E3%81%8C%E9%81%85%E3%81%84%EF%BC%81%E9%87%8D%E3%81%84%EF%BC%81%E5%8E%9F%E5%9B%A0%E3%81%A8%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95-%E9%AB%98%E9%80%9F%E5%8C%96%E3%81%AE%E8%A3%8F%E6%8A%80/#i-3

# sessionとは
cookieがブラウザに情報を保存するのに大してsessionはサーバー上に情報を保存します！
よくログイン情報などをサーバー上に保存することが多いです！
サーバー内で情報を保持し、複数ページに渡って情報を共有します！

例えばAさんでログインしたとしたら、ユーザー編集ページにアクセスしたら編集できるのはAさんのデータですよね？
これはセッションによってログイン情報が記憶されているからです！

あとはショッピングサイトなどにもカートに追加した商品などが他のページにいってもその記録が残っていたりします。
これもsessionを利用しています。
ただ基本的にsessionはブラウザを閉じるとその情報は失われます。

cookieに比べるとサーバー情に情報を保存するので安全性は高いと言えます

# まとめ
・保存場所
cookie→ブラウザ
session→サーバー

・保存容量
cookie→上限あり(ブラウザが重くなる原因にもなる)
session→ほぼ上限なし

・セキュリティ面
cookie→危険性あり
session→cookieに比べると危険性は低い
初投稿『マークダウン記法』

#マークダウン記法について

投稿を初めて行くにあたりマークダウンでスラスラ記入していけるようになりたい。
素早く打ち込める姿を想像するとマスターしたいと思ったりしたので、
はじめのアウトプットに選びました！
よかったらみていってください。
（Qiita　マークダウン記法一覧表・チートシートを参照）

##見出し
```
#見出しh1
##見出しh２
###見出しh3
####見出しh4
#####見出しh5
######見出しh6
```
ハッシュ記号を使い見出しを表します。
シンプルで数が増えるごとに見出しのサイズが変わっていきます。

##リンクの挿入
>```
[タイトル]（URL)
```
>>```
例[Qiita](http://qiita.com/)
```
>

・結果
>[Qiita](http://qiita.com/)

リンクを貼りたい文字列を入力して（）内にURLを貼ります。
##引用
・書き方

>\>テキスト
>>\>>テキスト（ネスト）
>

\>を文頭に記載すると引用になります
引用はネスト（親子関係）にもできます。
##番号付きリスト
・書き方

```
1. テキスト
2. テキスト
   3. テキスト
```
・結果
>1. テキスト
>2. テキスト
>  3. テキスト

テキストの前に空白が入っていることに注意。
##リスト
・書き方
>```
* テキスト
　　* テキスト
　　* テキスト
```

・結果
>* テキスト
   * テキスト
   * テキスト

・こちら 「*」 は「-」 でも記載が可能。
・箇条書き中はタブでインデントを下げられます。
##太字
・書き方
>```
**テキスト**
```

・結果
**テキスト**

ちょっとわかりやすいように引用をせずに書いてみました。
**違い分かりますかね？**
##斜体
・書き方

```
*テキスト＊
```

・結果
>*テキスト*

お洒落テキスト
*お洒落テキスト*
##打ち消し線
・書き方
>```
~~テキスト~~
```

・結果
>~~テキスト~~

##水平線
>```
***
```

・結果
>***

##テーブルの挿入
・書き方
>```
列数x行数　（）
```

・結果
>| 1 | 2 | 3 |
|:-:|:-:|:-:|
|   |   |   |
|   |   |   |

3x2の記述でこちらのテーブルを挿入項目が現れる。

* 補足として
  * 左寄せ 「:--」
  * 右寄せ 「--:」
  * 中央寄せ 「:-:」
  
##コード
```
短いコードは`code`のように記載できる。
```
>短いコードは`code`のように書けます。

```python:sample.py
print('複数業のコードもかけます。ファイル名もかけます。')
```


##折りたたみ
```
<details>
<summary>ここに要約を書きます</summary>
要約より後ろの文は折りたたみが展開されることで表示されます。
改行も反映されます。
</details>
```
<details>
<summary>ここに要約を書きます</summary>
要約より後ろの文は折りたたみが展開されることで表示されます。
改行も反映されます。
</details>

##画像の埋め込み
```
![代替えテキスト](http://hogehoge.png "画像タイトル")
```
>![代替えテキスト](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F45617%2F015bd058-7ea0-e6a5-b9cb-36a4fb38e59c.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=5dde76efbc19c2c307ee96c7ccbb4972"画像タイトル")

##その他
* htmlタグが何種類か使えるらしくマークダウンで表現するのが難しいことなどはhtmlタグを使うといいかもしれないこと
* 公開されているQiita記事のURLの末尾に「.md」をつけるとマークダウンの記載内容がみれます。

##参考にした、参考になるサイト
* [Qiita マークダウン記法 一覧表・チートシート](https://qiita.com/kamorits/items/6f342da395ad57468ae3#fn1)
* [Qiita マークダウン記法 一覧](https://qiita.com/oioigohan/items/f47f772e936e71f31168)
* [Markdown記法 チートシート](https://qiita.com/Qiita/items/c686397e4a0f4f11683d#%E6%B3%A8%E9%87%88)
* [Qiitaの数式チートシート
](https://qiita.com/PlanetMeron/items/63ac58898541cbe81ada)
一番小さい値(ruby編)

#一番小さい値をRubyで求める

#入力される値
n_1
n_2
n_3
n_4
n_5
#期待する出力
n_1, n_2, n_3, n_4, n_5 のうち最も小さい数字を出力する
#条件
すべてのテストケースにおいて、以下の条件をみたす。
・1 ≦ n_1, n_2, n_3, n_4, n_5 ≦ 100
#入力例1
10
12
4
8
46
#出力例1
4
#回答
```java:
n_1 = gets.to_i
n_2 = gets.to_i
n_3 = gets.to_i
n_4 = gets.to_i
n_5 = gets.to_i

result = [n_1, n_2, n_3, n_4, n_5 ]
puts result.min
```
#出力結果
4
#解説
##n_1からn_5までの変数に入力例である10から46までをgets.to_iを用いて代入する
##次にresultという変数にn_1からn_5までの数値を配列に入れて代入する
##最後にputsでresultを出力するが、最小の数値だけを求めたいのでminを用いて出力する
##minをmaxにすれば最大値を出力する事もできる
##n_3だけを出力したければputs result[2]にすればn_3の値を出力できる
#以上
GitBook がしれっと HonKit になってた件

# HonKit

[公式ドキュメント](https://github.com/HonKit/HonKit)

HonKit 製作者によると GitBook -> HonKit への移行の経緯は以下の通りです。

>GitBookはMarkdownからドキュメントページや書籍を作成するツールですが、 以前OSSで公開されていた[GitBook(legacy)](https://github.com/GitbookIO/gitbook)はDeprecatedとなって開発は止まっています。
>
>>⚠️ Deprecation warning:
>>
>>As the efforts of the GitBook team are focused on the GitBook.com platform, the CLI is no longer under active development. All content supported by the CLI are mostly supported by our GitBook.com / GitHub integration. Content hosted on the legacy.gitbook.com will continue working until further notice. For differences with the new vesion, check out our documentation.
>>
>代わりにOSSではない https://www.gitbook.com/ の方に移行する形になっています。 つまり、新しいGitBookはGitBook.com上でのホスティングサービスとなっています。
>
>（中略）
>
>
>これを解決するため、[GitBook(legacy)](https://github.com/GitbookIO/gitbook)をForkして[HonKit](https://github.com/HonKit/HonKit)を作りました。
>
>
> 引用元：[GitBookをForkしてHonKitを作りました - Web Scratch](https://efcl.info/2020/06/19/githon/)

GitBook の OSS の開発が止まってしまったので、それの後続として HonKit を fork して開発が続けられていたみたいです。

また、使用ライセンスは GitBook と同じく Apache License 2.0 になります。

>GitBook is licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/GitbookIO/gitbook/blob/master/LICENSE) for the full license text.
>
> 引用元：[HonKit](https://github.com/HonKit/HonKit)

一応、Apache License 2.0 のライセンスは勝手に再配布などをしなければ商用利用しても良いものになります。

【参考】
・ [商用利用できるオープンソースライセンスはけっきょくどれで何をすればいいのか？](https://qiita.com/0xfffffff7/items/efbb65521d7708f2db7d)
・ [Apache License 2.0とは？ライセンス内容をやさしく解説](https://reikawatanabe.com/whats-apache-license-v2/)

# GitBook と HonKit の違い

GitBook と HonKit の違いについては Honkit 製作者の記事が1番わかりやすかったです。

- [GitBookをForkしてHonKitを作りました - Web Scratch](https://efcl.info/2020/06/19/githon/)

変更点は book.json で行っていたプラグインの管理を、package.json と npm でのパッケージ（プラグイン）管理に寄せただけで、あとは既存のままで動くとのことでした。

具体的には book.json の `plugins` に入れていたプラグインを `honkit-plugin-*` または `gitbook-plugin-*` （ `*` はプラグイン名に置き換える）という名前で `npm install <package>` して、`gitbook install` の代わりに `npm install` してくださいとのことです。

その他、build や serve などのコマンドが `gitbook xxx` から `honkit xxx` に変更されました。

詳しくは[公式ドキュメント](https://github.com/HonKit/HonKit)を参考にしてください。

試しに GitBook を使っていた環境を HonKit に移行してみたのですが、上記のように、package.json を作成して、必要なプラグインを `npm install` した後に `honkit build` または `honkit serve` すれば動きました。

使用感は GitBook と変わりませんでした。
Windows10でUSキーボードを用いたMac likeな環境作成

# 概要
MacからWindows10に移行しなけらばならなかった皆さん。更に、USキーボードを使ってんだって皆さん。正直に言ってWindows10に移行した際にとても混乱しませんか？私は混乱しました、そして作業効率落ちてんなーと実感しました。

そんな私が少しでもMacLikeに使っていきたいと悪戦苦闘しながら設定した方法について、まとめます。

# 環境
- OS: Windows10
- 使用キーボード: [Happy Hackers Keyboard Proffessional2](https://www.pfu.fujitsu.com/hhkeyboard/hhkbpro2/standard.html)

# 望む環境
- US配列のキーボードに対応してほしい
- Macみたいにスペースキーの両隣でローマ字入力と半角英字入力を切り替えたい

# 設定方法
- キーボードのレイアウトを英語(米国)に変更する

設定 -> 時刻と言語 -> 言語 -> キーボード -> 既定の入力方式の上書き -> 英語(米国)
と設定する。これでキーボード配列はUS仕様に変更されたはずです。
しかし、問題点としてスペースキーの両隣がWinキーになってしまっていると思います。これは面倒ですね。Altに変えましょう。

-  Winキー -> Altキーに変更 and Altキー -> Winキーに変更

[PowerToy](https://github.com/microsoft/PowerToys/releases/)の最新版をインストールしてください。これはMicrosoftが公開しているWindowsOSを快適にするツールです。下記がgithubのREADMEに記載されている説明文です。greaterなproductにしてくれます。

```text
Microsoft PowerToys is a set of utilities for power users 
to tune and streamline their Windows experience for greater productivity.
```

PowerToyを用いることで、Keyboardのレイアウトを変更できます。下記のようにKeyboard Mananger -> Remap Keysを設定してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86761/69ae7f2d-251d-9e01-c8b5-ec5e6b1445df.png)
これでよさげなキーレイアウトになったはずです。

- 左Altキーで半角英字入力、右Altキーでローマ字入力の入力方式の変更を行う
下記の方の資料を参考に変更してください(丸投げ)
https://atooshi-note.com/us-keyboard-alt-mac/


# 以上
皆様に快適なWindowsLifeをm
npm-scripts の if 文ワンライナーで else の処理に何も書かなかったら怒られる件

# 事象

タイトルの通りです。

# 実行環境

```
$ node -v
v14.13.1

$ npm -v
6.14.8
```

# 実験

以下の3パターンを用意。

- hoge:a
    - else の処理あり。
- hoge:b
    - else をつけるが処理を省略。
- hoge:c
    - else ごと省略。

```json:package.json
{
  （省略）
  "scripts": {
    "hoge:a": "if TRUE; then echo hogehoge; else echo mogemoge; fi",
    "hoge:b": "if TRUE; then echo hogehoge; else; fi",
    "hoge:c": "if TRUE; then echo hogehoge; fi"
  },
  （省略）
}
```

## hoge:a（else の処理あり）

もちろん怒られない。

```
$ npm run hoge:a

> vpass_api_spec_document@1.0.0 hoge:a /Users/sugurutakahashi/git/vpass_api_spec_document
> if TRUE; then echo hogehoge; else echo mogemoge; fi

hogehoge
```

## hoge:b（else をつけるが処理を省略）

**怒られる。**

```
$ npm run hoge:b

> vpass_api_spec_document@1.0.0 hoge:b /Users/sugurutakahashi/git/vpass_api_spec_document
> if TRUE; then echo hogehoge; else; fi

sh: -c: line 0: syntax error near unexpected token `;'
sh: -c: line 0: `if TRUE; then echo hogehoge; else; fi'
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! vpass_api_spec_document@1.0.0 hoge:b: `if TRUE; then echo hogehoge; else; fi`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the vpass_api_spec_document@1.0.0 hoge:b script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/sugurutakahashi/.npm/_logs/2020-11-03T15_03_31_621Z-debug.log
```

## hoge:c（else ごと省略）

怒られない。

```
$ npm run hoge:c

> vpass_api_spec_document@1.0.0 hoge:c /Users/sugurutakahashi/git/vpass_api_spec_document
> if TRUE; then echo hogehoge; fi

hogehoge
SuguruTakahashiMBP sugurutakahashi ~/git/vpass_api_spec_document $ 
```

# まとめ
**npm-scriptsのif文ワンライナーで else の処理を省略する場合は `else` とは書いてはいけない。**
AWS-CDKのエラー ENOENT: no such file or directory, open ‘/asset-input/node_modules/hoge’

AWS-CDKで今まで何も問題なくデプロイできていたTypeScriptのLambda関数で、ある日突然デプロイ時にエラー`ENOENT: no such file or directory`が発生するようになりました。

```
% cdk deploy
Bundling asset hogeStack/fuga/Code/Stage…
Building hoge.ts…
🚨 Build failed.
Error: ENOENT: no such file or directory, open ‘/asset-input/node_modules/aws-amplify/lib/index.js’
Error: ENOENT: no such file or directory, open ‘/asset-input/node_modules/aws-amplify/lib/index.js’
Failed to bundle asset hogeStack/fuga/Code/Stage, bundle output is located at /Users/a/work/cdk/cdk.out/bundling-temp-52790e2ed2ab60b0cf0a67c80b7d81e7a04f6181172a1a2161b2d92a5411d441-error: Error: docker exited with status 1
Subprocess exited with error 1
```

`rm -r node_modules && yarn install`してみても解消しません。少し調べたところ、Lambdaのデプロイに使用しているaws-lambda-nodejsモジュールが、ParcelというJavaScriptバンドラを使ってTypeScriptへのトランスパイルをしていることがわかりました。

Parcelはキャッシュを使うようで、CDKディレクトリの中に`.parcel-cache`という名前のディレクトリを発見しました。これを削除してから`cdk deploy`したところ、エラーは無事起きなくなりました。
【Heroku】独自ドメイン取得～SSL化まで【完全無料】

#今回のゴール

勉強用に制作したHerokuアプリについて、独自ドメインかつSSL化まで無料で適用する

#全体の流れ
独自ドメインを取得 ～ Heroku側にドメイン名適用 ～ CloudflareをプロキシとしてかませてSSL化 ～ freenom側にCloudflareのネームサーバー名を設定	
	
#やった事詳細
###① freenomで独自ドメインを取得
* 使いたいドメイン名を入力⇒取得可能な一覧が出てくるので、0円のものの中から選択する

[freenom ドメイン取得ページ](https://my.freenom.com/domains.php)
[参考）freenomで無料ドメインを取得する](https://note.com/dafujii/n/n406f385651e2)
	
###② Heroku側で取得したドメインを設定	
* Settings＞Domains＞add domainからドメイン名追加
	
[参考）Django製サービスをHeroku×独自ドメインでリリースするまで](https://trslog.com/post-6018/)
※ こちらに記載されているHerokuでのSSL化は、無料プランの場合適用不可	
	
### ③ CloudFlareを使いDNSでプロキシ～SSL化
* サイトを追加し、DNS管理の画面から下記の通り入力
タイプ：CNAME
名前：独自ドメイン名
コンテンツ：Herokuでのドメイン名（https://を省いたもの）

* SSL関連の設定は大体下記のような感じだったかと
SSL/TLS 暗号化モード：フレキシブル
（訪問者－Cloudflare間を暗号化。
　Webサーバー(Heroku側)に証明書をインストールせず使うのでこれを選びます）
https設定：ON

※ 2021.07.09追記
Railsアプリをデプロイした際、暗号化モードについてフレキシブルにしていると下記エラーが出ました。
このような場合はフルに変更すると解消する事もあるようです。
（私の場合は解消しました！ありがとうございます・・・！！）
[Heroku×CloudflareでHTTP Origin Header didn't match request.base_urlというエラーがでる
](https://qiita.com/mzmt/items/2b44e82f2aa90ebf4623)

* ネームサーバー1と2が表示されるのでコピーしておきfreenom側に設定（次項参照）

[参考①）Heroku (無料プラン)で独自ドメインでSSLを使う方法](https://www.serversus.work/topics/yhyqln2f0bgw2yuak8vw/)
※こちらにはHeroku側のDNSターゲットをCloudflareのコンテンツ欄に入力するとありますが
入れるとエラーになったため、上記方法で行っています。

[参考②）無料で個人Webサイトをhttps/SSL暗号化する方法:Cloudflare共有SSL証明書/Let’s Encrypt](https://www.apollomaniacs.com/knowhow_addssh_https.htm)

###③ freenom側のネームサーバー名をCloudFlareのものに変更
* ログイン後にトップページ下部のMy Domains＞取得ドメイン一覧より
今回のドメイン名右側の「Manage Domain」をクリック

* Manage Freenom DNS＞Edit Nameserversをクリックした画面より
「Use custom nameservers (enter below)」を選択
Name Server1と2にCloudFlareでコピーした各内容を貼り付けて保存

###④ Cloudflareから「OK牧場」のメールが届く
* 「（独自ドメイン名）のアクティベーションに成功しました。」というメールが届いたら
有効に動作しているようです。
ブラウザ上でチェックしてみてください！

#おまけ
Herokuさん無料版だと30分アクセスなかったら寝ちゃう（スリープモードに入っちゃう）らしいので
スケジューラの設定もやっておいた方がよいかと。

やれやれだぜ:blush:

[Herokuの無料dynoをスリープさせないで24時間稼働させる4つの方法](https://casualdevelopers.com/tech-tips/how-to-prevent-idling-of-free-dyno-on-heroku/#Herokudyno)

※ 私は回避策①UptimeRobotさんのお世話になってます。
JavaScript sortにおける比較関数の仕様

JavaScriptで配列の要素(数値)を昇順・降順に並び替えるために、漠然とsortに比較関数を渡して書いていたので、sortに比較関数を渡したときの挙動を理解するために詳細を調べました。
#ソートプログラム
```JavaScript:sort.js
let array = [1, 5, 4, 2, 3];
console.log(array.sort((a, b) => a - b)); //[1, 2, 3, 4, 5]
console.log(array.sort((a, b) => b - a)); //[5, 4, 3, 2, 1]
```
シンプルなソートプログラムは上記のようなものになるかと思います。
比較関数内の返り値を、昇順のときは第一引数から第二引数、降順のときは第二引数から第一引数を減算した結果にしています。
このようなsortのコールバック関数の返り値の違いがどのようにソート順に違いを生むのでしょうか。
#sortのコールバック関数の返り値ごとの挙動
sortはコールバック関数として、下記のような引数を二つ取り、値を返す比較関数を定義できます。

```JavaScript:compare.js
compareFunction(a, b) {
  //処理内容
}
```
[MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)によると、sortにコールバック関数が与えられたとき、undefined以外の全ての配列要素はその返り値に基づきソートされます。(undefinedは配列の末尾に並べられます。)
返り値別のソート結果は下記です。

- 0未満のとき
 - aはbより小さいインデックスにソートされる
- 0のとき
 - aとbは互いにインデックスを変更しない
- 0より大きいとき
 - aはbより大きいインデックスにソートされる

ここで冒頭のプログラムを参照すると、昇順にソートするときは返り値がa-bとなっており、仮にa=5, b=1だった場合はa-b(=4)は0より大きいことから、a(=5)はb(=1)よりも大きいインデックスにソートされ、結果として昇順となります。

逆に降順にソートするときは返り値がb-aとなっており、昇順のときと同様にa=5, b=1の場合を考えるとb-a(=-4)は0未満のため、a(=5)はb(=1)よりも小さいインデックスにソートされ、結果として降順となります。

このようにして、冒頭のソートプログラムは昇順・降順の違いを生んでいるのですね。
【初心者向け】予め登録したデータを、選択コマンドで入力フォームに自動入力させたい

# 困っていたこと
- プルダウンに予め登録したデータをセットして、それを選択すると、フォームに自動入力させたいと思っていた
- いろんな記事を試してみたが、formヘルパーを使用していると上手くいかない方法が多かった
- formヘルパーをやめるなどの方法もあったが、セキュリティのためにformヘルパーを使えることが望ましいと考えた

# やりたいこと
- 予め雛形となるデータを登録しておく
- 選択コマンドで選択すると、入力フォームに自動入力される
- 単なるテンプレートではなく、登録時に数値を変えることも可能にしたい

# 開発環境
    ruby '2.6.6'
    gem 'rails', '~> 6.0.3', '>= 6.0.3.3'

# 実装内容
- undone_actionモデル(カラムは、action_nameとdefault_time)には、予めデータが登録されている状態
- undone_actionsをセレクトボックスにセット
- セレクトボックスを選択すると、イベントが発生し、入力フォームのsavings_nameとearned_timeに値が入る

```erb:new.html.erb
<%= form_with(model: @savings_record, local: true) do |f| %>
  <div class="form-group>
    <label>My 不要行動</label>
    <select multiple id="my_undone_action" class="form-control" onChange='auto_input()'>
      <option disabled selected value>Choose...</option>
      <% current_user.undone_actions.each do |undone_action| %>
        <option value= "<%= undone_action.action_name %>&<%= undone_action.default_time %>"><%= undone_action.action_name %></option>
      <% end %>
    </select>
  </div>

  <div class="form-group">
    <%= f.label :savings_name, "不要行動" %>
    <%= f.text_field :savings_name, autofocus: true %>
  </div>

  <div class="form-group">
    <%= f.label :earned_time, "所要時間(分)" %>
    <%= f.number_field :earned_time, autofocus: true %>
  </div>

  <div class="actions">
    <%= f.submit "貯金する" %>
  </div>
<% end %>

<script>
  var auto_input = function () {
    var element = document.getElementById("my_undone_action");
    document.getElementById("savings_record_savings_name").value = element.value.split("&")[0];
    document.getElementById("savings_record_earned_time").value = element.value.split("&")[1];
  }
</script>
```
# 完成形
### 選択コマンド、クリック前
![スクリーンショット 2020-11-03 23.40.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688968/8a8f1cb5-e6b1-3584-8475-fe279a187a26.png)

### 選択コマンド、クリック後
![スクリーンショット 2020-11-03 23.43.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688968/8c8efb24-9ee2-7be7-8afd-e858ac6f6e7d.png)


#　最後に
web系企業に転職を目指して、ポートフォリオ作成中に躓いたポイントを投稿しました。同じ悩みを持った誰かの一助となれば幸いです。
WSLでxdg-openする（備忘録）

## これなに？
コンソールからURLを指定してブラウザを開きたかったのですが、
`exec: "xdg-open": executable file not found in $PATH`
と言われてうーん…ってなった時の備忘録です。

## 環境
-  Ubuntu 20.04.1 LTS (WSL2)
-  Node.jsの実行環境がある
-  npmかyarnが使える

## 解決策
```bash
# wsl-openをグローバルにインストールする
npm install -g wsl-open  # npmの場合
yarn global add wsl-open # yarnの場合

# シンボリックリンクを貼る
sudo ln -s $(which wsl-open) /usr/local/bin/xdg-open
```

## わーい！
![open.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286055/68eba962-edb9-d12b-47b7-7e7f81304bde.gif)

## 参考
[wsl-open：WSL で xdg-open する](https://neos21.hatenablog.com/entry/2020/09/06/080000)
SETエンジニアとは

最近、エンジニア職種の中に**SETエンジニア**があります。

**SETエンジニアとは**

そもそも「SETエンジニア」とは何だろうか　？？

**SETエンジニア（Software Engineer in Test）** の略になりますが・・と言いつつ、それでもよくわからないですね。

何をするかというと**テストのインフラクチャの部分の「品質向上活動」** であります。


それでもわかりにくいです。

そのため、下記の内容でSETエンジニア業務とは？を深めていければと。

```ruby:
1.自動テスト品質性を説く
2.テストフレームワークの導入(Selenium,Testcafe,MagicPod,Cypress,Autifyなど)
3.リグレッションテストの継続的インテグレーション (CI)、継続的デプロイ (CD)
4.リファクタリング活動（コード品質を高める）
5.リリース計画（どのようなフローなのか、可視化できる数値とは？）
6.非機能自動化対応（負荷・セキュリティなど）
7.QAエンジニアとの連携（品質をどう高めていくか、互いの領域はどこ？）
8.リスクマネージメントとは（自動するメリット、本当に必要があること）
9.SETエンジニアの役割を周りに周知
```

**自動テスト品質性を説く**

>そもそも自動化すべきなのかも含め、自動化することで品質がどう作用されるか。マニュアルテストとの比較が必要です。

**テストフレームワークの導入**

>昔はSeleniumの主流イメージでしたが、昨今は多くのテストフレームワークが用意されていますね。
MagicPodやAutifyなど非エンジニアでも使えるE2Eツール。


**リグレッションテストの継続的インテグレーション (CI)、継続的デプロイ (CD)**

>オンプレ時代は、Jenkinsでしたが、今はCircleCIやGitLabCIあたりでしょうか。

**リリース計画（どのようなフローなのか、可視化できる数値とは？）**

>1ヶ月後、3ヶ月後、半年後、1年後どのようなリリースになっているか？

対象プロダクトは？どのようなシナリオで、レポートにどのような数値が必要なのか。


**非機能自動化対応（性能、セキュリティなど）**

>非機能のテストは、どう自動化に組み込んでいくかになります。リリース後アクセス数が多くサーバーが落ちてしまう（スケールする必要があるか）
また、脆弱性の問題（クロスサイトスクリプティングやSQLインジェクションなど）は無いか。


**QAエンジニアとの連携（品質をどう高めていくか、互いの領域はどこ？）**

>2000年代から2010年代前半は、QAエンジニアがマニュアルテストと自動化テストに取り組んでいた。当然、プログラミング知識の壁がありましたが、SelenuimIDEでFirefoxからWebサイトを自動化する（レコーディング機能を利用）。で、他のブラウザでも自動化したいのでSeleniumWebDriverを導入し、ChromeやIEなどクロスブラウザ対応していたような・・・。2000年代とは言わず、現場によっては今もそうなのかもしれません。


連携という部分では、QAエンジニアがどんなテストフローでプロダクトの品質を向上させたいか確認し、その向上部分にどう自動化フェーズが噛み合うのか。ただなんとなく自動化しても、


意味のあるもの。向上数値として測れるもの。費用対効果が見込まれるもの。

**リスクマネージメントとは（自動するメリット、本当に必要があること？）**

>ただ自動化して動かしたところで、動く負債ようになってはいけないので、対象プロダクトに対し、自動化するメリットを定義する。

また、本当に自動化が必須なのか？

ある意味、マニュアルテストよりも時間がかかりメンテナンスコストが多くなるようなことがないように。であれば、現状のままで良いでは？となります。
Ruby 給料の計算プログラム

# はじめに

今回は標準入力からアルバイトの給与の計算プログラムを作っていきます。

```
給料は以下のルールで決まっています。

・9 時から 17 時まで : 時給 X 円 (通常の時給)
・17 時から 22 時まで : 時給 Y 円 (夜の時給)
・それ以外の時間 : 時給 Z 円 (深夜の時給)

あなたの N 日間の出勤時刻と退勤時刻が 1 時間単位で与えられます。
N 日間の合計で得られる金額を計算してください。

入力例 の場合、4 日間で時給 X 円、Y 円、Z 円となる時間帯を満遍なく働いており、
金額の合計は Z×9 + X×8 + Y×5 + Z×1 = 1500×9 + 1000×8 + 1300×5 + 1500×1 = 29500 円となります。
```
```
入力例
1000 1300 1500
4
0 9
9 17
17 22
22 23
```
```
出力例

29500
```

# ソース
```ruby.rb
m = gets.split.map &:to_i
# 自給額の取得
nums = gets.to_i
# 出勤日数の取得

times = []
while time = gets
    times << time.chomp.split(' ').map(&:to_i)
end


sum=0
(0..nums-1).each do |i|
    (times[i][0]+1..times[i][1]).each do |t|
            
        if t<=9
            sum += m[2]     
        elsif t<=17
            sum+= m[0]    
        elsif t<=22
            sum+= m[1]
        else
            sum+= m[2]
        end
    end
end

puts sum
```

# 解説
```ruby.rb
times = []
while time = gets
    times << time.chomp.split(' ').map(&:to_i)
end
```
複数行（３行目以降の出勤時間）の複数要素を取得。
while time = getsとすることで、標準入力の値を全て取得するまで繰り返されます

```ruby.rb
sum=0
(0..nums-1).each do |i|
    (times[i][0]+1..times[i][1]).each do |t|

           # 自給計算処理
 
    end
end
```
```ruby.rb
(0..nums-1).each do |i|

 #i変数を取り出すと
 # ０ １ ２ ３
```
出勤日数をi変数で一つづつ取得する

```ruby.rb
(times[i][0]+1..times[i][1]).each do |t|

 # t変数を取り出すと(1日目）
 # 1 2 3 4 5 6 7 8 9
```
1日の出勤時間帯をt変数で取得する
＊例ではこれを四日間繰り返す

```ruby.rb
if t<=9
   sum += m[2]     
 elsif t<=17
   sum+= m[0]    
 elsif t<=22
   sum+= m[1]
 else
   sum+= m[2]
end
```
if文でt変数で取得する１時間ごとの自給を繰り返し取り出しsum変数に代入する

例だと四日分繰り返した後にsum変数を出力して合計金額出す！！
これで一応プログラムの完成です。

# 最後に
一応プログラムはできましたが修正点がたくさんあると思います。
間違っっているところはご指摘いただけると幸いです。
エラーメッセージの日本語化

#はじめに
　対象年齢が低いアプリケーションを作成しているため、エラーメッセージを日本語にしたいと思い、導入した。
###エラーメッセージの日本語化
Gemfileに
①　`rails-i18n`を記述する。

②

```ruby:config/application.rb
module アプリ名
  class Application < Rails::Application
    (中略)
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end
```

configから始まっている下の２行を追記する。

③　config/locales/modelsの配下に、ja.ymlを作成する。

```ruby:config/locales/models/ja.yml
ja:
  activerecord:
    models:
      モデル名: モデル名の日本語訳 
    attributes:
      モデル名:
        カラム名: カラム名の日本語訳
```

他のモデルやカラムも追記することができる。
vagrantのプラグインがno such name (https://gems.hashicorp.com/specs.4.8.gz)と表示されてインストールできないとき

# tl;dr

`vagrant plugin install`コマンドのケツにこれをつけましょう。

`--plugin-clean-sources --plugin-source https://rubygems.org`

# 発生事象

```shell
# vagrant plugin install sahara vagrant-libvirt vagrant-mutate
Installing the 'sahara' plugin. This can take a few minutes...
Vagrant failed to load a configured plugin source. This can be caused
by a variety of issues including: transient connectivity issues, proxy
filtering rejecting access to a configured plugin source, or a configured
plugin source not responding correctly. Please review the error message
below to help resolve the issue:

  no such name (https://gems.hashicorp.com/specs.4.8.gz)

Source: https://gems.hashicorp.com/
```
# 解決策

つけたらなおりました。IPv6無効化とかあまり関係なかった。

`vagrant plugin install sahara vagrant-libvirt vagrant-mutate --plugin-clean-sources --plugin-source https://rubygems.org`

# 参考
- https://github.com/hashicorp/vagrant/issues/11008
- http://memories.zal.jp/WP/blog/20180224_2968.html
(Windowsにて) CUDA,cuDNNのバージョン確認。cupy.show_config()とか。結局、いまいち(←いやいや良くできてるよ、この記事）。

#概要
インストールされているCUDAや、cuDNNのバージョンを確認する方法として、

*  nvcc -V
*  cudnn.h(の記載内容を確認する)

という方法が示されている記事が沢山ある。
ただ、理想は、pythonとかから、実際にGPUをちょこっと起動とかするついでに、バージョンを調べるみたいなのが間違いがなくていい。上記のcudnn.hとかだと、有効になってないファイルをみてしまうとかのミスがありうるから。

#バージョン確認方法
##cupyの利用（ **cupy.show_config()** )

**cupy.show_config()**で、以下のように確認できます。

:notepad_spiral:  紹介しておいてなんですが、下記において、いわゆる**CUDAのバージョン**は、どれなんだろう。また、そもそも、**cupy**を登場させているが、本来、**cupy**は関係ないのでは？

⇒　**【結論】概要に記載のnvcc-Vとcudnn.h方式が、ベターな気がする。**
　　　　（tensorflowとかGPUを起動しながら、、、確認できる方法があれば、知りたい。）

⇒　**【結論（見直し）】やはり、自分の手作業でファイルを確認するのは、避けたいので、それに比べてこのやり方は、良い！！**
バージョンは、

```
CUDA Driver Version   : 11000
```
であり、↓　ではない。

```
CUDA Runtime Version  : 10010
```

また、cuDNNは、

```
cuDNN Version         : 8004
```

（自分で、LGTMを1個！！ ↓ 自分ではうてないので。。。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342207/fb7fa239-75e8-1344-d1f5-594a5c2fa096.png)

```
D:\_qiita\_cupy>python
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import cupy
>>> cupy.show_config()
CuPy Version          : 8.0.0
CUDA Root             : C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1
CUDA Build Version    : 10010
CUDA Driver Version   : 11000
CUDA Runtime Version  : 10010
cuBLAS Version        : 10100
cuFFT Version         : 10010
cuRAND Version        : 10010
cuSOLVER Version      : (10, 1, 0)
cuSPARSE Version      : 10010
NVRTC Version         : (10, 1)
Thrust Version        : 100906
CUB Build Version     : 100800
cuDNN Build Version   : 8003
cuDNN Version         : 8004
NCCL Build Version    : None
NCCL Runtime Version  : None
cuTENSOR Version      : None
>>>
```

#まとめ
特にありません。
意外と難しかった。
(CUDAの複数バージョンを利用したい人とかに有効と思い、記事を作成しようとしました。）
コメントなどあれば、お願いします。:candy:
Ubuntuのswap領域拡張

# 背景
大学生の頃にドスパラで購入したデスクトップPC(i5(2400)/メモリ8GB)にUbuntu20.04をインストールして自宅用のメインPCにしているのだが、さすがに8年以上前から使い込んでいるため最近メモリが死んでしまった。といっても、厳密にはメモリではなくマザーボードのメモリスロット部分が死んだ。なので、メモリを買い換えれば解決するレベルではなく、マザーボードを買い替えるくらいのレベルだ（マザボ買い換えるくらいなら新しいPCを買ったほうがいい）
幸い、メモリ4スロットのうち2スロットはまだ生きており、2GB*2枚指しの4GBで起動できるので、冬のボーナスで新しいPCを買うことにして、それまではこのまま耐えることにしたのだが、さすがに4GBだとChromeを起動しながらDockerコンテナ起動したりするとSwap領域まで食いつぶしてフリーズしてしまうので、とりあえずswap領域を拡張して凌ぐことにした。

そういうわけで、OSインストール後にSwap領域を拡張する方法を備忘録の意味も兼ねて今回記事にすることにした。

# Swap領域のための特殊ファイルを作成
Windowsでもそうだが、Swap領域は専用のシステムファイルで実現できるらしい。Ubuntu20.04だと`swapon`コマンドで確認できる。自分の環境では`/swapfile`がすでに存在しており、これがSwap領域として認識されているようだった。

```
$ swapon -s
ファイル名	タイプ		サイズ	使用済み	優先順位
/swapfile	file	2097148	1851152	-2
```

`/swapfile`がすでにあるので`/swapfile2`を作成してSwapファイルとして認識させる。

```sh
$ sudo fallocate -l 2G /swapfile2   # 2GBのファイルをアロケーション
$ sudo chmod 600 /swapfile2         # rootのみ読み書き可能に設定
$ sudo mkswap /swapfile2            # swap領域として設定
$ sudo swapon /swapfile2            # swap領域としてマウント
$ swapon -s
$ sudo vim /etc/fstab
$ grep swapfile /etc/fstab 
/swapfile                                none            swap    sw              0       0
/swapfile2                                none            swap    sw              0       0
$ reboot
$ swapon --show
```

無事にSwap領域が拡張できた。これでなんとかフリーズせずに使い続けることができるようになった。

# 蛇足
Swap領域のサイズって実メモリの2倍が理想って昔言われてたのを思い出したのだが、今はメモリがかなり安価に用意できるし、サーバはクラウドで設定値いじるだけで容易にメモリ増設できるようになったので、Swap領域のサイズとか全く意識しなくなっていたのだが、こういうことがあるとデフォルト値のままでなくカスタマイズしたほうがいいのかもしれないと思い直した。
にしても、最近のブラウザはメモリバカ食いしすぎだ。メモリ4GB＋Swap2GBだとブラウザ使うだけでフリーズの危険があるって、WindowsXPのときはメモリ2GBで十分贅沢だと思っていたのに、インフレしすぎだよな。
時間制限を設けて毎秒処理を実行させるワンライナー【coreutils、timeout 使わない】

# 前提

`coreutils` や `timeout` を使うのであれば[こちら](https://qiita.com/ma2saka/items/741b614418bf8fce04de)を参照してください。

今回が Mac に brew で何かを入れたくなかったので使わない方法を模索した結果になります。

# やりたいこと
時間制限を設けて毎秒処理を実行させるワンライナーを書くこと。

もっと具体的には一定時間 「waiting...」 と毎秒 echo し続けて待たせること。

正直、「waiting...」と一回表示させるだけでもよかったのですが、コンソールが動いていないと長時間待たせるときに、ユーザーが不安になるのかなーと思った次第です。

# 結論

```:時間制限を設けて毎秒処理を実行させるワンライナー
for i in `seq 実行させたい時間(秒)`; do 実行させたいコマンド; sleep 1; done
```

```:サンプル
$ for i in `seq 5`; do echo waiting...; sleep 1; done
waiting...
waiting...
waiting...
waiting...
waiting...
```

# 参考

## seq

for文の省略にはよく seq が用いられます。

[【 seq 】コマンド――数字の列を出力する](https://www.atmarkit.co.jp/ait/articles/1705/18/news033.html)

```:seqの挙動
$ seq 5
1
2
3
4
5
```

# 応用版

実際使ってみたところ、1秒ごとに「waiting...」が表示されると、コンソールが忙しかったので、2秒ごとにしたくなりました。

一応、2秒だけではなく n秒 でも対応できるように修正して使いました。

## 結論（n秒ごとに実行する版）

```
for i in `seq 実行させたい時間(秒)`; do if [ $i%n秒 -eq 0 ]; then 処理; else; fi; sleep 1; done
```

```:サンプル
$ for i in `seq 5`; do if [[ $i%2 -eq 0 ]]; then echo waiting...; else; fi; sleep 1; done
waiting...
waiting...
```

## 応用版の解説 

ただif文のワンライナーと組み合わせただけです。

```:if文ワンライナー
if [ 条件式 ]; then 処理; else 処理; fi
```

以下は `$i` が 2 で割って割り切れた時のみ echo するワンライナーの例です。

```:サンプル
if [[ $i%2 -eq 0 ]]; then echo waiting...; else; fi
```
データベースとモデル

# モデルとは
モデルはデータを取り出す、データの保存など、データのやり取りをする役割である。
モデルを使うことで、データベース言語を記述する必要がなくなり見通しの良いプログラムができる。
モデルクラスはAppModelを継承してつくる。AppModelにはfindやsaveなどデータとやりとりするメソッドが定義されている。
これらの関数を実行するとsql文が発行される仕組みになっている。

#CakePHPにおける命名規則の基本的な考え方
* モデル名は単数形、それ以外でデータを扱うものは複数形にする
* クラスとして定義されているファイルはキャメルケース、クラスとは直接的に関係のないものはアンダースコアケース。
* Viewのフォルダ名は単数形

#データベース操作関数のポイント

 **conditionsによる条件設定**

*シンプルな条件の書き方*

`'Board.name like' => '%ABC%'`



*複雑な条件の書き方*

`'Board.name like ?' => array('%ABC%')`
この条件から生成されるsql文
↓
`where 'Board'.'name' like '%ABC%'`

つまり「?」の部分に配列の値が入る。

以下のように文字列に変数を埋め込まなくても
`$data = $this->Board->find('all',array('conditions'=>array("Board.id between {$first} and {$end}")));`

`"Board.id between ? and ?"=>array(2,5)`
のように書くことができる。

**マジック検索**
「このフィールドの値が〇〇のもの」とうい単純な検索を利用する場面は多い。その際、conditionsで都度条件を書くのは労力がいる。マジック検索を条件を書かずにデータを取得することができる。

`findAllByフィールド名（値）`

（例） findAllByName("kuma")

**データの更新**

idが同じであれば、値の追加と同様に`モデル名->save($this->data)`の一文でデータの更新ができる。
ただ、カラム名をIDと大文字していた場合、idとは別カラムと認識されてしまい更新されなかった。

*学び*
カラム名は小文字に統一しよう。

https://oki2a24.com/2012/08/29/table-column-should-be-lower-case-character-in-cakephp2/

**その他便利関数**

* 条件に一致したものを全て更新　updateAll(更新内容の配列、条件の配列）
* 条件に一致したものを全て削除　deleteAll(条件)
* 特定のフィールドのみ更新 saveField(フィールド名,値)


#モデルのクラス変数について
命名規則に沿った書き方をすれば、よしなにテーブルやフィールドを特定してデータのやり取りができた。
設定なしに自動でテーブル設定などができるのは便利な反面、柔軟性に欠けている。
モデルのクラス変数を使うことで自動設定された情報を書き換えることが可能だ。

**例**

* データベースの指定 `$useDbConfig = "使用する変数名"`
データベースの設定は、database.phpのDATABASE_CONFIGというクラスに$defalutという変数に連想配列ではいっている。これとは別に、あらかじめ$mysettingなどの変数を作って設定を保存しておけば、$useDbConfigでデータベースの変更をすることが可能である。
* テーブルの指定 `$useTable = テーブル名`
CakePHPではモデル名から推測してテーブルを指定するが、$useTable変数に設定すれば他テーブルを使うこともできる。
* レコードの並び順を指定 `$order = 並び順の指定`
このクラス変数を設定しておくことでデフォルトの並び順をしていできる。`$order = モデル名.カラム名`とすることでカラムごとに設定することもできる。そのため、クエリを発行するたびに並び順の設定をする必要がなくなる。

#コールバックメソッドについて
データ操作の前後で実行されるメソッドがある。

beforeFindではfindを実行する前に、limitを設定するなどのクエリの調整をすることができます。
afterFindでは、取得したデータの編集ができます。
beforeSaveは保存処理の前に実行され、特定の条件の場合保存するなどの制御ができます。

そのほかに、afterSave、onError、beforeDelete、afterDelteなどがあるこれらも、同様の考え方で使える。

#バリデーション
バリーデンションとは入力された値をチェックする仕組みである。モデルで定義して使う。
コントローラからモデルに保存処理が実行されると、モデル内のバリデーションで値チェックをされて
チェックを通れば、データベースへ保存される。チェックを通らなければレコードを追加できないとみなされメッセージが表示される。

###バリデーションの設定

バリデーションはモデルで`$validate`プロパティに以下のように設定るする。

```php

public $validate = array(
 'フィールド名' => array(
   'rule' => '〇〇',
   'required' => true,
   'on' => 'create',
   'message' => '〜〜〜です'
 )
)
```

ruleは必須項目で、ここで値チェックのルール、例えば空を許さない、記号を許さない、クレッジトカード番号かどうかなどを設定する。
また、ruleは数多く準備されているので実際に使う時に都度調べて利用すると良い。
ruleは独自に生成することも可能である。その際は、正規表現または独自メソッドを利用する。

*正規表現パターン*

'rule'にcustomという値を利用して、２つめの要素に独自の正規表現のパターンを書く。

`'rule' => array('custom','正規表現のパターン')`

*独自メソッドパターン*

第一引数で渡された値に対して、なにかしらのチェックを行いチェックを通ればtrueを返す。

```php
public function バリデート名($data,引数1,引数2){
   //何かしらの値チェック処理
   return 真偽値;
}
```
RedisをMessageBrokerがわりに使う

pythonで。Redisのpub/subはデータ残せないので使わないとしたら。

```python
import redis
class RedisQueuer():
    def __init__(self) -> None:
        settings = get_settings()
        self.redis_client = redis.Redis(
            host=settings['redis']['host'],
            port=settings['redis']['port']
        )
        self.topic_name = settings['topic_name']
    def dequeue(self):
        return self.redis_client.brpop(self.topic_name)
    def enqueue(self, message):
        self.redis_client.lpush(self.topic_name, message)
    def complete(self, job):
        return 'completed'
```
Flutter Columnで"The method '>' was called on null."エラー

エラー内容を見てもすぐに気づけずちょっとハマったので備忘録に

## 事象
Column使用箇所に下記エラーが発生
![errorMessage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311273/ed7d2fc8-9f8a-cf8d-f280-cdd1a1d8d9e8.png)

## 原因
Columnのネストが発生していた。

意図せず親Widgetと子WidgetでColumnが重複していたため起こっていたようです。
Column × RowはOKですが、同じもの同士はNGです。
(エラー内容が分かりにくい。。。)
AIP/RMS のライセンス

## この記事は
AIP や RMS を利用する際は、O365 E3 や EMS E5 など、ライセンス体系がわかりずらいです。
特に、同じ内容なのに名称が異なるということがあるのでまとめます。

# 対応表
|識別ID|Azure ポータルの表記 | Office/Microsoft ポータルの表記 |
|:-:|:-:|:-:|
|RMS_S_PREMIUM|Azure Information Protection P1|Azure Rights Management Premium|
|RMS_S_PREMIUM2|Azure Information Protection P2|Azure Information Protection Premium P2|
|MIP_S_CLP1|Information Protection for Office 365 - Standard|Information Protection for Office 365 - Standard|
|MIP_S_CLP2|Information Protection for Office 365 - Premium|Information Protection for Office 365 - Premium|
|RMS_S_ENTERPRISE|Azure Rights Management|Microsoft Azure Active Directory Rights|

* RMS_S_ENTERPRISE
    * 文章の保護・権利付けに必要となります。
* RMS_S_PREMIUM/RMS_S_PREMIUM2
    * AIP クライアントをベースとしたラベル付け、付加機能を利用できます。
    * P2 ではコンテンツの内容に基づいた自動ラベル付けも利用できます。
* MIP_S_CLP1/MIP_S_CLP2
    * Office 365/Microsoft 365で今後展開が進む MIPでのライセンス

[ライセンス一覧](https://docs.microsoft.com/en-us/azure/active-directory/enterprise-users/licensing-service-plan-reference)

[AIP のライセンスによる機能差異](https://azure.microsoft.com/ja-jp/pricing/details/information-protection/)

[MIP のライセンスによる機能差異](https://docs.microsoft.com/ja-jp/office365/servicedescriptions/microsoft-365-service-descriptions/microsoft-365-tenantlevel-services-licensing-guidance/microsoft-365-security-compliance-licensing-guidance#information-protection)
【pytorch】torch.zeros vs torch.zeros_like

torch.zerosとtorch.zeros_likeの違いが気になったので書く。
#torch.zerosの使い方
値が0のtensorを返す。

```
>>> torch.zeros(3, 2)
tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])
>>> torch.zeros(3)
tensor([0., 0., 0.])
```

#torch.zeros_likeの使い方
あるtensorの値をすべて0にしたいときに使う。

```
>>> input = torch.empty(3, 2)
tensor([[3.2561e+09, 3.0936e-41],
        [0.0000e+00, 0.0000e+00],
        [3.2557e+09, 3.0936e-41]])
>>> torch.zeros_like(input)
tensor([[0., 0.],
        [0., 0.],
        [0., 0.]])
```
上の例はinputの値をすべて0にしている。
Mattermost dockerでListen Addressを設定画面から変更してしまったときの対処法

## はじめに

本記事は[mattermost/mattermost-docker](https://github.com/mattermost/mattermost-docker) team edition v5.28.1で設定画面からListen Addressを変更してしまって立ち上がらなくなったときの対処方法を記述したものです．

## 経緯

以下のようにNginxのリバースプロキシで80/443ポートに来たアクセスをMattermostの8065ポートに飛ばす構成で組んでいました．

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850127/bf0dddfd-2cfd-5967-6edc-4540fe0d61ba.png width=200px alt=structure>

しかし，システムコンソールを見ると Listen Addressが**:8000**となっていたので，おかしいなと思って**:8065**に書き換えてしまったのが始まりです．

![Screenshot from 2020-11-03 22-31-37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850127/bc1fe393-1599-013e-05fe-5c138e3727b5.png)

その後dockerの設定を書き換えてrestartしてしまうとmattermostが立ち上がらなくなってしまいました．

[公式のKnwon Issue](https://github.com/mattermost/mattermost-docker#known-issues)にも以下のように書き換えないようにと書いています．

> Do not modify the Listen Address in Service Settings.

## 対処法

Mattermostのdocker imageは設定を変更してうまく動作しなくなってもコンテナ自体は立ち上がります．なので起動した状態でコンテナ内部で作業します． 
docker imageのベースがalpine Linuxなのでshellはashです.
mm_container_nameはMattemostのコンテナ名です.

```bash
docker exec -it <mm_container_name> /bin/ash
```

内部にシェルにアタッチできたら以下のようにconfig/config.jsonを書き換えます

```ash
cd config
sed -i -e 's/:8065/:8000/' config.json
```

書き終えたらShellを抜けてdockerコンテナを再起動すれば復活します

## 正しい設定

Mattermostの[公式ドキュメント](https://docs.mattermost.com/administration/changelog.html#id351)に以下のような記述があります．  
> To improve the production use of Mattermost with Docker, the docker image is now running a as non-root user and listening on port 8000. Please read the upgrade instructions for important changes to existing installations.

ということで，通常使うポートは8000番になったとのことなのでNginxのReverse Proxy設定を変更します．  

<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/850127/bf1733ec-6db7-5cc8-6c5a-1ee1e807ade1.png width=200px>