OmniAICreator/Qiita-1.07M huggingface dataset:
【Chart.js】Railsでレーダーチャートを簡易表示するまで

自己学習目的としてChart.jsで簡易表示するまでをまとめてみます。

#Chart.jsとは
無料のオープンソースJavaScriptライブラリ。
主にバー、ライン、エリア、パイ、バブル、レーダー、ポーラー、散布図の8つを動きをつけてCanvas で描画表示できるもの。

公式サイトのリンク
https://www.chartjs.org/

#環境
OS: Catalina v10.15.3 
Ruby:  v2.6.3
Rails: v5.2.4.1


#手順

1. 新規プロジェクトの作成
2. ディレクトリ移動
3. DB作成

```
$ rails _5.2.4_ new chart_sample_app
$ cd chart_sample_app
$ bundle exec rake db:create
```

エディタから`Gemfile`を開き、`Chart.js`のgemを追加する。

```ruby:Gemfile

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false

gem 'chart-js-rails', '~> 0.1.4' #=> 追加
```

追記後にインストール。

```
$ bundle install
```

コントローラーを作成。

```
＜ 書き方 ＞
rails g(generate) controller <コントローラー名> <アクション名>

$ rails g controller chart index
```

```app/controllers/chart_controller.rb
class ChartController < ApplicationController
  def index
  end
end
```
　
ルーティングの追加（起動　→　ビュー画面へ）

```config/routes.rb
Rails.application.routes.draw do
  root 'chart#index'
end
```



Chart.jsの読み込み
`application.js`の一番下に追記

※ 注意
Rails5まではsprocketsを利用したアセットパイプラインでjavascriptやcssを管理されていますが、
Rails6からはWebpackerでの管理になっているため、「**javascriptsファイルが見つからず分からない...**」という面倒な場合はRails5で新規作成が無難だったりするようです。

```app/javascript/packs/application.js
// about supported directives.
//
//= require Chart.min　追加
//= require rails-ujs
```

`layouts`直下ビュー画面`app/views/chart/index.html.erb`に記入。
今回はscriptを直接記入する。


```erb

<canvas id="myChart"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
  type: 'radar',
  data: {
    labels: ["Japanese", "Mathematics", "English", "Social studies", "Science"],
    datasets: [{
      label: 'A_kun',
      backgroundColor: "rgba(0,0,80,0.4)",
      borderColor: "rgba(0,0,80,1)",
      data: [80, 90, 60, 70, 100, 70]
    }, {
      label: 'B_kun',
      backgroundColor: "rgba(0,255,0,0.4)",
      borderColor: "rgba(0,255,0,1)",
      data: [90, 75, 80, 65, 80, 70]
    }]
  }
});
</script>
```

URLにアクセスすると表示

```
http://localhost:3000/
```

デフォルトのままのため中央の値は60。
(ディスプレイよっては内側ラインが薄くなるかもしれないです)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434223/f584fa37-98fd-b207-8936-2b7fc8ab7cbb.png)


最後までご覧いただきありがとうございましたm(_ _)m


#参考
[Chart.js(英)](https://en.wikipedia.org/wiki/Chart.js)
[Chart.jsでグラフを描画してみた](https://qiita.com/Haruka-Ogawa/items/59facd24f2a8bdb6d369)
[Rails generate の使い方とコントローラーやモデルの命名規則](https://qiita.com/higeaaa/items/96c708d01a3dbb161f20)
ネクストエンジンAPI-EXCELVBAを用いたマスタ登録＆更新処理

##はじめに
 [前回](https://qiita.com/hystatsuya/items/4ce62c887be3f0846deb)から期間が空いてしまいました。すみません。
今回は更新系のAPIを使用してみたいと思います。
トークン類は前と同様、適当なセルに置いておけば良いです。

セル範囲を選択し、その状態でマクロを呼ぶとその選択範囲を
そのまま更新データとしてネクストエンジンにアップする、といったサンプルになります。

更新情報(新規登録も可能)を入れたCSVデータを、Selectionより自作します。
商品マスタ項目のヘッダ(syohin_code,syohin_name等)も必要です。

Selectionの要素を1から参照していくと、左→右、最終列が終わると次の行
といった感じで移動していくので、CSVを作成するのに都合が良いね。
最終列以外はカンマ、最終列は改行(VbCr)を入れます。それの繰り返しで
パラメータとするCSV文字列を作り上げます。

商品情報の更新として、API
h ttps://api.next-engine.org/api_v1_master_goods/upload
を呼び出します。


###サンプルコード
```VB:test.bas
Private Sub CommandButton1_Click()
'API商品情報登録
    Dim Token As String 'アクセストークン
    Dim Refresh As String 'リフレッシュトークン
    Dim jsonObj As Object 'パースしたjson用のオブジェクト
    Dim httpReq As New XMLHTTP60
    Dim i As Integer 'loopカウンタ
    Dim rngToken As Range
    Dim rngRefresh As Range
    Dim ws As Excel.Worksheet
    Dim cnt As Long 'レコード件数
    Dim queNo As String
    Dim rngdt As Range
    Dim strdt As String
    
    Set ws = Worksheets("token")
    Set rngToken = ws.Range("B2")
    Set rngRefresh = ws.Range("B3")
    
    '画面からアクセストークン・リフレッシュトークンをセットする
    Token = rngToken.Value
    Refresh = rngRefresh.Value
    
    '選択範囲セレクションからデータ文字列を作成する
    'セレクションの数だけLoop
    Set rngdt = Selection
    
    For i = 1 To rngdt.Count
        If i Mod rngdt.Columns.Count <> 0 Then
            '最終列以外はカンマを打つ
            strdt = strdt & rngdt(i).Value & ","
        Else
            '最終列は改行を打つ
            strdt = strdt & rngdt(i).Value & vbCr
        End If
    Next
    
    Debug.Print strdt
    With httpReq
        'リクエストURLとエンドポイント
        .Open "POST", "https://api.next-engine.org/api_v1_master_goods/upload"
        'ヘッダ
        .setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        'パラメータを入れて送信
        .send ("&access_token=" & Token & "&refresh_token=" & Refresh & "&data_type=csv&data=" & strdt)
        'Debug.Print .responseText
        'jsonをパースする
        Set jsonObj = JsonConverter.ParseJson(.responseText)
    End With
    Set httpReq = Nothing
    
    '取得したトークンをそれぞれ画面に戻す
    If jsonObj("result") = "success" Then
        rngToken.Value = jsonObj("access_token")
        rngRefresh.Value = jsonObj("refresh_token")
        rngToken.Offset(0, 1).Value = jsonObj("access_token_end_date")
        rngRefresh.Offset(0, 1).Value = jsonObj("refresh_token_end_date")
        Set rngToken = Nothing
        Set rngRefresh = Nothing
        
        'キューIDを取得
        queNo = jsonObj("que_id")
        Call MsgBox("キューNo：" & queNo & vbCr & "にて処理完了しました", vbInformation, "処理終了")
    Else
        Call MsgBox(CStr(jsonObj("message")), vbExclamation, "エラー")
    End If
           
    Set jsonObj = Nothing
    Set rngdt = Nothing

End Sub

```
JSONの戻り値は、
異常終了ではエラーメッセージ
正常終了でバッチのキューIDが返ります。
また、商品マスタ一括登録画面にログが残ります。


##参考サイト
[ネクストエンジンAPIにEXCELブックから接続する](https://qiita.com/hystatsuya/items/4ce62c887be3f0846deb)
[【VBA】JSONファイルの内容をEXCELファイルに読み込む](https://minor.hatenablog.com/entry/2016/10/11/205527)
[宇宙一わかりやすい？VBA-JSONを使ったJSONパースのしかた](https://qiita.com/emesh/items/0834a5ad373d2ade6a93)
Python3 の Rest で Cloud Firestore のデータを削除 (Delete)

```py:firestore_delete_rest.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	firestore_delete_rest.py
#
#					Jan/29/2020
#
# ------------------------------------------------------------------
import  sys
import  json
import  requests
#
from get_token import get_token_proc
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
key_in = sys.argv[1]
print("%s" % key_in)
#
token = get_token_proc()
#
project = 'project-jan25-2020'
#
url="https://firestore.googleapis.com/v1/projects/" + project + "/databases/(default)/documents/cities/" + key_in
#
try:
	rr=requests.delete(url,headers={"Authorization": "Bearer " + token})
	print(rr.text)
#
except Exception as ee:
	sys.stderr.write("*** error *** in requests.delete ***\n")
	sys.stderr.write(str(ee) + "\n")
#
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

get_token.py はこちら
[Python3 の Rest で Cloud Firestore のデータを作成 (Create)](https://qiita.com/ekzemplaro/items/db84c399c4da3a122f22)

実行コマンド

```bash
export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_delete_rest.py t0924
```
【Windows10】の開発環境をPython3で構築

## 概要
Windows10でPython3をインストールし、開発環境の構築までの流れを自分用にメモ。

## システム構成
・Windows(Windows10　64bit)
・Python  3.8.1
・pip 20.0.2
・Virtualenv

#1. Python3をインストール
![python3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576171/cb5cfcf3-9453-1152-72bc-10ca35ddee21.png)

上記の[公式サイト](https://www.python.org/downloads/)にアクセスし、<b>Download</b>の部分にカーソルを合わせてWindowsを選択してください

今回は、<b>Python 3.8.1</b>をインストールして説明していきます。
![Python3パッケージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576171/bc3a9037-1f47-207b-d752-dce2b87e58dc.png)

32bit版Windowsにインストールするなら
<b>Windows x86 web-based installer</b>

64bit版なら
<b>Windows x86-64 web-based installer</b>
をダウンロードしてください

# パッケージのインストール

![py-install1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/576171/436dfc64-e879-a693-7fd5-b89efba3f5c1.png)

1. ダウンロードしたパッケージを実行します。

2. <b>"Add Python 3.X to PATH"</b>をチェックしてPATHを通します。

3. Install nowをクリックしインストールします。

## Python３のインストールが完了したことを確認


1. コマンドプロンプト又はPowershellを起動
2. Pythonのバージョンの確認

 ```コマンドを入力.
 >python -V
 ```
```このように表示されれば大丈夫です.
  python3.8.1
```

#2.仮想環境の作成

## 2-1.Virtualenvのインストール
Virtualenvは、pip コマンドでインストールできます。

```
C:\> py -m pip install virtualenv
```

今回は仮想環境名をmyvenvとして説明します。

```
C:\Users\user1> py -m virtualenv myvenv
Using base prefix 'C:\\Users\\user1\\AppData\\Local\\Programs\\Python\\Python36'
New python executable in C:\Users\user1\py3env\Scripts\python.exe
Installing setuptools, pip, wheel...done.
```

C:\Users\user1\myvenv\ に Python3.8 用の仮想環境が作成されました。

##2-2.仮想環境に入る
作成した開発環境名にScripts\activate.batを打ち込んで実行します。

```コマンドプロンプトの先頭に(myvenv)と表示されたらOK!!.
C:\Users\user1>myvenv\Scripts\activate
(myvenv) C:\Users\user1>
```

####仮想環境から出る

仮想環境から出るには <code>deactivate</code> コマンドを実行します。

```以下のコマンドを入力.
C:\Users\user1>myvenv\Scripts\deactivate
```
Ruby: メソッド間での引数の受け渡し

Ruby: メソッド間での引数の受け渡し。
忘れないようにメモ。
メソッドは呼び出されるまで動かない受け身な子。

def register_data(data)
  puts '名前を入力してください'
  name = gets.chomp
  puts '年齢を入力してください'
  age = gets.chomp
  puts '身長を入力してください（m単位）'
  tall = gets.to_f
  puts '体重を入力してください'
  weight = gets.to_f
  person = { name: name, age: age, tall: tall, weight: weight, bmi: **calculate_bmi(tall, weight)**}
  data << person
end

def **calculate_bmi(tall, weight)**
  bmi = weight / tall / tall
end
物体検出でのmean IoUの計算

```python
import numpy as np

# get IoU overlap ratio
def compute_mean_iou(y_pred, y_true):
  mean_iou = []
  for i in range(y_pred.shape[0]):
    area_a = (y_pred[i][2] - y_pred[i][0]) * (y_pred[i][3] - y_pred[i][1])
    area_b = (y_true[i][2] - y_true[i][0]) * (y_true[i][3] - y_true[i][1])
    iou_x1 = np.maximum(y_pred[i][0], y_true[i][0])
    iou_y1 = np.maximum(y_pred[i][1], y_true[i][1])
    iou_x2 = np.minimum(y_pred[i][2], y_true[i][2])
    iou_y2 = np.minimum(y_pred[i][3], y_true[i][3])
    iou_w = iou_x2 - iou_x1
    iou_h = iou_y2 - iou_y1
    if iou_w < 0 or iou_h < 0:
	    mean_iou.append(0.0)
    else: 
      area_iou = iou_w * iou_h
      iou = area_iou / (area_a + area_b - area_iou)
      mean_iou.append(iou)
  return sum(mean_iou)/len(mean_iou)

# [x1, y1, x2, y2]
a = np.array(([[0, 0, 1, 3],[0, 0, 1, 3]]), dtype=np.float32)

b = np.array(([[0, 0, 1, 1],[0, 0, 1, 1]]), dtype=np.float32)
print(compute_mean_iou(a, b))
```

https://github.com/yoyoyo-yo/Gasyori100knock/blob/master/Question_91_100/answers/answer_93.py
Tkinterのラベルを書き換える方法

# はじめに
pythonのGUI作成ライブラリ『tkinter』で、
ボタンを押したときにラベルが書き換わるという処理の書き方が分からずに困った。

テキストボックスに入力した数値を受け取り、平均を出力するアプリを作る、という本来の目的・内容に合わせてその対処法を書いていく。

# 環境

Python 3.6.8

# 処理方法

wordsという変数名のラベルを"rewrited"と書き換える場合、
ボタンを押したときの処理の中で、

```python
words["text"] = "rewrited"
```

と書き加えてやればよい。

# より具体的な説明、というよりコード例

```python
import tkinter as tk

root = tk.Tk()
root.title(u"平均算出アプリ")
root.geometry("400x300")


# ボタンを押したときの処理

def buttoneffect(event):
    value = textbox.get()
    split_value = value.split(",")
    int_values = list(map(int, split_value))
    average = sum(int_values) / len(int_values)

    textbox.delete(0, tk.END)  # テキストボックスの中身を初期化する
    words4["text"] = "平均値は%sです。" % average  # ラベルの中身を書き換える


# ボタンやテキストボックスの配置

words = tk.Label(text=u"入力された値の平均値を算出します。", font=("", 12))
words.pack()

words2 = tk.Label(text=u"カンマ区切りで数値を入力してください。", font=("", 12))
words2.pack()

textbox = tk.Entry()
textbox.insert(tk.END, "Input this box.")
textbox.place(x=140, y=70)

button = tk.Button(text=u"計算する")
button.bind("<Button-1>", buttoneffect)
button.place(x=150, y=100)

words3 = tk.Label(text=u"統計データ", font=("", 12))
words3.place(x=50, y=140)

words4 = tk.Label(text=u"　", font=("", 12))  # 平均値の出力に利用する空のラベル
words4.place(x=140, y=160)


root.mainloop()

```

# 参考

PythonのTkinterを使ってみる
https://qiita.com/nnahito/items/ad1428a30738b3d93762

Tkinter ラベルテキストを変更する方法
https://www.delftstack.com/ja/howto/python-tkinter/how-to-change-the-tkinter-label-text/
Python3 の Rest で Cloud Firestore のデータを更新 (Update)

```py:firestore_update_rest.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	firestore_update_rest.py
#
#					Jan/31/2020
#
# ------------------------------------------------------------------
import  sys
import  json
import  datetime
import  requests
#
from get_token import get_token_proc
# ------------------------------------------------------------------
def get_current_time_proc():
	date_xxx = "%s" %  datetime.datetime.today()
	date_yyy = date_xxx.split()
	date_mod = "%sT%sZ" %  (date_yyy[0],date_yyy[1])
#
#	print(date_mod)
#
	return date_mod
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
key_in = sys.argv[1]
population_in = int(sys.argv[2])
print("%s\t%d" % (key_in,population_in))
#
token = get_token_proc()
#
project = 'project-jan25-2020'
#
url_base="https://firestore.googleapis.com/v1/projects/" + project + "/databases/(default)/documents/cities/" + key_in
url = url_base + "?updateMask.fieldPaths=population&updateMask.fieldPaths=date_mod"
#
unit_aa = {}
unit_aa["population"] = {"integerValue": population_in}
date_mod = get_current_time_proc()
unit_aa["date_mod"] = {"timestampValue": date_mod}
payload = {"fields": unit_aa}
str_json = json.dumps(payload)
headers = {"Authorization": "Bearer " + token,
		"Content-Type": 'application/json'}
#
try:
	rr=requests.patch(url,data=str_json,headers=headers)
	print(rr.text)
#
except Exception as ee:
	sys.stderr.write("*** error *** in requests.delete ***\n")
	sys.stderr.write(str(ee) + "\n")
#
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

get_token.py はこちら
[Python3 の Rest で Cloud Firestore のデータを作成 (Create)](https://qiita.com/ekzemplaro/items/db84c399c4da3a122f22)


実行コマンド

```bash
export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_update_rest.py t0922 98456200
```
Python3 の Rest で Cloud Firestore のデータを読む (Read)

```py:firestore_read_rest.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	firestore_read_rest.py
#
#					Jan/31/2020
#
# ------------------------------------------------------------------
import  sys
import  json
import  requests
#
from get_token import get_token_proc
# ------------------------------------------------------------------
def row_proc(unit_aa):
#	print(unit_aa['name'])
#	print()
	tt = unit_aa['name'].split('/')
	llx = len(tt)
	key = tt[llx - 1]
#
	fields = unit_aa['fields']
#	print(fields)
	name = fields['name']['stringValue']
	population = fields['population']['integerValue']
	date_mod = fields['date_mod']['timestampValue']
	str_out = key + "\t"
	str_out += name + "\t"
	str_out += str(population) + "\t"
	str_out += str(date_mod)
	print(str_out)
#	print()
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
token = get_token_proc()
#
project = 'project-jan25-2020'
#
url="https://firestore.googleapis.com/v1/projects/" + project + "/databases/(default)/documents/cities"
#
try:
	rr=requests.get(url,headers={"Authorization": "Bearer " + token})
	dict_aa = json.loads(rr.text)
#
	for unit_aa in dict_aa['documents']:
		row_proc(unit_aa)
except Exception as ee:
	sys.stderr.write("*** error *** in requests.get ***\n")
	sys.stderr.write(str(ee) + "\n")
#
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

get_token.py はこちら
[Python3 の Rest で Cloud Firestore のデータを作成 (Create)](https://qiita.com/ekzemplaro/items/db84c399c4da3a122f22)

実行コマンド

```bash
export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_read_rest.py
```
redis の db の中身をluaでダンプする


[これ](https://qiita.com/arc279/items/8d8e458d86ff083ba24d) の続きというか、
redis-server に lua を投げて、サーバの中で SCAN してキーと値を返すやつを清書したので、メモがてらに。

## 環境

```bash
$ python -V
Python 3.7.2

$ redis-cli info | grep redis_version
redis_version:5.0.5
```

## コード

hash とか set の時は `HSCAN` とか `ZSCAN` とかに適宜読み替えてください。

```lua:dump.lua
local cr    = KEYS[1]
local num   = KEYS[2]
local match = KEYS[3]

local cr2, keys = unpack(redis.call("SCAN", cr, "MATCH", match, "COUNT", num))

local values = redis.pcall("MGET", unpack(keys))
return {cr2, keys, values}
```

```python:export.py
#!/usr/bin/env python
import os
import redis
import click

DEFAULT_REDIS_URL = "redis://localhost:6379/3"
DEFAULT_LUA_FILE  = "dump.lua"

def scan_iter(r, lua_script, bulk=2000, match="*"):
    f = r.register_script(lua_script)

    cr = "0"
    while True:
        cr, keys, values = f(keys=[cr, bulk, match])
        if cr == b"0":
            break

        yield from zip(keys, values)


@click.command()
@click.option("--redis-url", envvar="REDIS_URL", default=DEFAULT_REDIS_URL)
@click.option("--lua-file", envvar="LUA_FILE", default=DEFAULT_LUA_FILE, type=click.File("r"))
@click.option("--output", default=os.sys.stdout, type=click.File("w"))
def cmd(redis_url, lua_file, output):
    r = redis.StrictRedis.from_url(redis_url)
    r.ping()

    for k, v in scan_iter(r, lua_file.read()):
        output.write(k.decode("utf-8"))
        output.write("\n")
        output.write("%d" % len(v))
        output.write("\n")


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

## 実行

```bash
$ pip install redis click
```

### サンプルデータ投入

```bash
$ jq -n -r 'range(10000)|"set key\(.) value\(.)"' | redis-cli -n 8
```


### ダンプしてみる

```bash
$ ./export.py --redis-url redis://localhost:6379/8
```

おわり。
Google colabでshpファイル（シェープファイル）を読み込む際のDriverErrorへの対処方法

##結論
アップロードするファイルは`***.shp`だけでなく、
`***.shx`
`***.dbf`
`***.sbn`
`***.sbx`
も同じ階層にアップロードする。

##理由
[StackExchangeの回答](https://gis.stackexchange.com/questions/262505/python-cant-read-shapefile)
→
`The actual shapefile (.shp) is useless without the companion files: .dbf, .shx, .prj etc..`
とのことです。
なんでかはよくわかりませんでした（.shpはshapefileの構成要素であり、shapefileそのものではない？）。

知ってる人いたら教えていただけるとありがたいです。

##実装
###成功例
```py:1st_code_cell
#geopandasのインストール
pip install geopandas

#インポート
import geopandas as gpd
```

アップロードしたファイル
`test.shp`
`test.shx`
`test.dbf`
`test.sbn`
`test.sbx`

```py:2nd_code_cell
test_content = gpd.read_file("test.shp", encoding='shift-jis')
display(test_content)
```

→成功！

###失敗例
```py:1st_code_cell
#geopandasのインストール
pip install geopandas

#インポート
import geopandas as gpd
```

アップロードしたファイル
`test.shp`
のみ

```py:2nd_code_cell
test_content = gpd.read_file("test.shp", encoding='shift-jis')
display(test_content)
```

→失敗

```py:
DriverError: Unable to open cities.shx or cities.SHX. Set SHAPE_RESTORE_SHX config option to YES to restore or create it.
```


##参考：
[StackExchangeの回答](https://gis.stackexchange.com/questions/262505/python-cant-read-shapefile)
[Rでシェープファイルを読み込む](https://qiita.com/nozma/items/bf8936df6b3a3882285a)
Ruby インストール時の rdoc エラー[CentOS] 

##CentOSでRubyインストール時に[rdoc]エラー
####AWS Lightsail CentOSで発生
$ sudo rbenv install 2.6.3
→　make: *** [rdoc] Killed

下記のように変更すればインストールされました
$ sudo RUBY_CONFIGURE_OPTS=--disable-install-doc rbenv install 2.6.3

参考：
https://github.com/rbenv/ruby-build/issues/156#issuecomment-54922357
Python3 の Rest で Cloud Firestore のデータを作成 (Create)

 こちらと同じことを Rest で行います。
[Python3 で Cloud Firestore のデータを作成 (Create)](https://qiita.com/ekzemplaro/items/4288176394947283c84a)

```py:firestore_create_rest.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	firestore_create_rest.py
#
#					Jan/31/2020
#
# ------------------------------------------------------------------
import  sys
import  json
import  requests
#
from get_token import get_token_proc
# ------------------------------------------------------------------
def add_data_proc(url_base,token,key,name,population,date_mod):
	unit_aa = {}
	unit_aa["name"] = {"stringValue": name}
	unit_aa["population"] = {"integerValue": population}
	unit_aa["date_mod"] = {"timestampValue": date_mod}
	payload = {"fields": unit_aa}
	str_json = json.dumps(payload)
	headers = {"Authorization": "Bearer " + token,
		"Content-Type": 'application/json'}
	url = url_base + "?documentId=" + key
	try:
		rr=requests.post(url,data=str_json,headers=headers)
		print(rr.text)
	except Exception as ee:
		sys.stderr.write("*** error *** in requests.post ***\n")
		sys.stderr.write(str(ee) + "\n")
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
token = get_token_proc()
#
#print(token)
#
url="https://firestore.googleapis.com/v1/projects/project-jan25-2020/databases/(default)/documents/cities"
#
add_data_proc(url,token,'t0921','宇都宮',38921,"2010-10-5T00:15:07.0Z")
add_data_proc(url,token,'t0922','小山',56417,"2010-9-12T01:16:07.0Z")
add_data_proc(url,token,'t0923','佐野',92185,"2010-12-8T02:17:07.0Z")
add_data_proc(url,token,'t0924','足利',39546,"2010-7-4T03:18:07.0Z")
add_data_proc(url,token,'t0925','日光',18492,"2010-10-2T04:19:07.0Z")
#
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

```py:get_token.py
# -*- coding: utf-8 -*-
#
#	firestore_token.py
#
#					Jan/31/2020
#
# ------------------------------------------------------------------
import  os
import  sys
import	subprocess
# ------------------------------------------------------------------
def get_token_proc():
	token = ""
	command=["gcloud","auth","application-default","print-access-token"]
#
	try:
		my_env = os.environ.copy()
		token_byte = subprocess.check_output(command,env=my_env)
		token = token_byte.decode('utf-8')
	except Exception as ee:
		sys.stderr.write("*** error *** in subprocess.check_output ***\n")
		sys.stderr.write(str(ee) + "\n")
#
	return	token[:-1]
# ------------------------------------------------------------------
```

実行コマンド

```bash
export GOOGLE_APPLICATION_CREDENTIALS="***.json"
./firestore_create_rest.py
```
"1週間で身に付くC++言語の基本"をといてみた

http://cpp-lang.sevendays-study.com/problem1.html
をといてみた。
1-1

```cpp
#include <iostream>

using namespace std;

int main()
{
        cout << "C++\n";
        return 0;
}
```

1-2

```cpp
#include <iostream>

int main()
{
        std::cout << "prigramming in C++ Language" << std::endl;
        return 0;
}
```

1-3

```cpp
#include <iostream>

using namespace std;

int main()
{
    cout << "ONE TWO THREE" << endl << "FOUR FIVE SIX" << endl;
    return 0;
}
```

1-4

```cpp
#include <iostream>

using namespace std;

int main()
{
        cout << "数値を入力してください: ";
        int a;
        cin >> a;
        cout << a << "を2倍した数は、10です。" << endl;

    return 0;
}
```

1-5

```cpp
#include <iostream>

using namespace std;

int main()
{
        int a[2];
        for (int i = 0; i < 2; i++) {
                cout << i << "つ目の数:";
                cin >> a[i];
        }

        cout << a[0] << "+" << a[1] << "=" << a[0]+a[1] << endl;
        cout << a[0] << "-" << a[1] << "=" << a[0]-a[1] << endl;

    return 0;
}
```


1-6

```cpp
#include <iostream>

using namespace std;

int main()
{
        char a[2][256];
        cout << "姓を入力:";
        cin >> a[0];
        cout << "名を入力:";
        cin >> a[1];
        cout << "名前は「" << a[0] << a[1] << "」です。" << endl;

    return 0;
}
```

1-7

```cpp
#include <iostream>

using namespace std;
int main(){
    cout << "HelloWorld." << endl;
}
```
CarrierWave、MiniMagickでリサイズしたい【確認画面付】

## CarrierWave、MiniMagickを使用した画像投稿のリサイズ

```uploaders/post_image_uploader.rb
class PostImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick
  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  ## 追記
  process :resize_to_fit => [300, 300]
  version :thumb do
    process :resize_to_fit => [400, 400]
  end
  ## ここまで
end
```

## 確認画面がある場合【おまけ】

**ルーティング**

```routes.rb
  resources :posts do
    collection do
      post :confirm
    end
  end
```

**コントローラー**

```posts_controller.rb
  def confirm
    @post = Posg.new(post_params)
    render :new if @post.invalid?
  end
```

**ビュー(new)**

```new.html
<%= form_with(model: post, local: true, url: confirm_posts_path) do |form| %>
  <% if post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(post.errors.count, "error") %> 画像が保存できませんでした:</h2>

      <ul>
      <% post.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :画像の説明 %>
    <br>
    <%= form.text_field :title %>
    <br>
    <%= form.label :画像 %>
    <br>
    <%= image_tag(@post.image.url) if @post.image && @post.image.url %>
    <%= form.file_field :image %>
    <%= form.hidden_field :image_cache %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>
```

**ビュー(confirm)**

```confirm.html
<h1>確認画面</h1>

<%= form_with(model: @post, url: posts_path, local: true) do |form| %>
<p>コメント:<%= @post.title %></p>
<%= image_tag(@post.image.url) if @post.image && @post.image.url %>
<%= form.hidden_field :image_cache %>
<%=form.hidden_field :title %>
<br>
<button type="button" name="button">
  <%= form.submit "投稿する" %>
</button>
<button type="button" name="button">
  <%= form.submit "戻る", name: "back" %>
</button>
<% end %>
```

以上です！
【Unity】ビルドしたアプリの実行時エラーをSlackに通知する

## はじめに

![ダウンロード (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55365/a189b784-5b3b-a48f-1bbf-dfb25b30ee49.png)

リモートで実行時のエラーを確認できる機能があると便利だなーと思い、簡単に実装してみました。インターネットに繋がっていればプラットフォームによらずに使えて便利な方法です。開発環境とかだと気軽に導入できると思うので、良かったら参考にしてみてください。

## 環境

- Windows 10 Home
- Unity 2019.2.17f1

## 手順

こちらの記事を参考に、[Incoming Webhooks]を導入してください。現在は[Integrations]でなく[Apps]という名称になっています。画面に従いポストするチャンネルや、投稿するbotのアイコンを設定しておいてください。また、この画面に表示される`Webhook URL`は今後使うのでメモしておいてください。
[Slack API (Incoming Webhooks) が簡単すぎた - Psycho Frame](http://docs.hatenablog.jp/entry/slack-Incoming-webhooks)

次にUnity側でエラーログをフックしていきます。`Application.logMessageReceived`に対してイベントを登録することで、ログ出力のタイミングに処理を差し込むことができます。
参考: [Application-logMessageReceived - Unity スクリプトリファレンス](https://docs.unity3d.com/ja/current/ScriptReference/Application-logMessageReceived.html)

今回はその仕組みを利用して、こんな感じのコンポーネントを作成してあげます。これを通知させたいシーン上でアタッチしておいてください(用途に応じて`DontDestroyOnLoad`や通常のクラス化などすると良いかと思います)。

```SlackNotificatior.cs
// 動作確認していないコードなのでご了承ください
using System;
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public sealed class SlackNotificatior : MonoBehaviour
{
	void Awake()
	{
		Application.logMessageReceived += OutputLogMessage;
	}

	void OnDestroy()
	{
		Application.logMessageReceived -= OutputLogMessage;
	}

	void OutputLogMessage(string text, string stackTrace, LogType type)
	{
// Editorでエラーログを通知していたらきりがないので除外する
#if !UNITY_EDITOR
			if (type == LogType.Error || type == LogType.Exception)
			{
				PostText(text, type.ToString());
			}
#endif
	}

	// TODO: ここにWebhook URLを入れる
	const string url = "";
	public void PostText(string text, string type)
	{
		var data = new PostData()
		{
			username = Application.platform.ToString(),
			color = "#D00000",
			fields = new PostData.Field[] { new PostData.Field() { title = type, value = text } }
		};
		var json = JsonUtility.ToJson(data);
		StartCoroutine(Post(url, json));
	}

	IEnumerator Post(string url, string json)
	{
		var postData = System.Text.Encoding.UTF8.GetBytes(json);
		var request = new UnityWebRequest(url, "POST");
		request.uploadHandler = (UploadHandler)new UploadHandlerRaw(postData);
		request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
		request.SetRequestHeader("Content-Type", "application/json");
		yield return request.SendWebRequest();
	}

	[Serializable]
	class PostData
	{
		public string username;
		public string color;
		public Field[] fields;
		[Serializable] public class Field { public string title; public string value; }
	}
}
```

後半ではjsonをWebhook URLに投げる処理を書いてあります。[Incoming Webhooks]のページに載っている[Attachment Formatting]を参考に、いい感じにフォーマットすることができます。赤いバーが出るとそれっぽいですよね。

![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55365/9112f302-5de4-4e84-1663-4f332bdefe5f.png)

ポイントとして、botの名前部分に実行環境を入れています。こうすることでクロスプラットフォーム開発でもどの環境でのエラーかどうかを区別することができるようになります。その他ビルドや端末の情報を埋め込んでも良いと思います。

## 参考

[UnityからSlack投稿する - Qiita](https://qiita.com/iwaken71/items/bbc5bde1f0eb05a2a9b0)
discordBOTで大きいサイズのスタンプ機能を実装する

# はじめに
本来discordに備え付けの「emoji」機能を利用して、あらかじめ自分で用意した画像をやりとりできるが、
emoji機能では画像が小さくなりすぎてしまうという問題があるため、それを解決しようと考えた。

# 解決策
特定のキーワードに反応して画像を送信するBOTを作成し、大きいサイズの画像をユーザー間で簡単に共有できるようにする。

# 環境
Python 3.6.8
discord.py 1.2.5

# 実際のコード

```python
import discord

TOKEN = 'hoge'  # 自身のBOTに割り当てられたトークンを入力

# 起動時の処理
@client.event
async def on_ready():
    print('We have logged in as %s' % client)

# メッセージを受信したとき、スタンプを送信する処理
@client.event
async def on_message(message):

    if message.author == client.user:
        return

    if message.content == '/img':  # 画像出力のトリガーにしたいキーワード
        await message.channel.send(file=discord.File('img.png'))  # 出力する画像のパス

client.run(TOKEN)
```

複数のスタンプを用意したい場合は、

```python

if message.content == '/img':
    await message.channel.send(file=discord.File('img.png'))
```

を繰り返す。
CDK関連リンク

# 背景
CDK関連のドキュメントやGithubの検索を毎回調べるのしんどい
ので、リンク集作りました

# リンク集
- Document
    - [Getting Started With the AWS CDK - AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html)
    - [API Reference · AWS CDK](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html)
    - [CDK API Reference - Python](https://docs.aws.amazon.com/cdk/api/latest/python/modules.html)
    - [AWS リソースおよびプロパティタイプのリファレンス - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)
- Github
    - [Github Search for CDK(Python)](https://github.com/search?utf8=%E2%9C%93&q=cdk+-filename%3A__init__.py+language%3APython&type=Code&ref=advsearch&l=Python&l=)
    - [Github Search for CloudFormation](https://github.com/search?q=AWSTemplateFormatVersion+language%3Ayaml&type=Code)
    - [aws-samples/aws-cdk-examples: Example projects using the AWS CDK](https://github.com/aws-samples/aws-cdk-examples#Python)
    - [eladb/awesome-cdk: A collection of awesome things related to the AWS Cloud Development Kit (CDK)](https://github.com/eladb/awesome-cdk)
ORA-01045: user username lacks CREATE SESSION privilegeとなった時の対応方法

- 環境
    - Windows10 Pro 64bit
    - SQL*Plus: Release 12.2.0.1.0 Production
    - (AWSのRDS)Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production

# 事象 : ユーザを作成してログインしようとしたらログインできなかった
```sql
$ sqlplus username/password@hoge.pon.ap-northeast-1.rds.amazonaws.com:1521/sid

SQL*Plus: Release 12.2.0.1.0 Production on 金 1月 31 17:28:36 2020

Copyright (c) 1982, 2017, Oracle.  All rights reserved.

ERROR:
ORA-01045: user username lacks CREATE SESSION privilege; logon denied


ユーザー名を入力してください: username
パスワードを入力してください: password
ERROR:
ORA-12560: TNS:protocol adapter error


ユーザー名を入力してください:
```

# 原因 : 「CREATE SESSION」権限がないので接続できない
初心者王道の失敗です。
[ORA-01045: user xxx lacks CREATE SESSION privilege; logon denied - 小さい頃はエラ呼吸](http://replication.hatenablog.com/entry/20121119/1353291645)
[新規ユーザー作成時に最低限割り当てるべき権限 | 技術情報 | 株式会社コーソル](https://cosol.jp/knowledge/knowledge_post/ob0003_new_user/)

## 対応 : 権限を付与する
```sql
-- 権限のある人でログイン
$ sqlplus ponsuke/tarou@hoge.pon.ap-northeast-1.rds.amazonaws.com:1521/sid

SQL*Plus: Release 12.2.0.1.0 Production on 金 1月 31 17:42:04 2020

Copyright (c) 1982, 2017, Oracle.  All rights reserved.

最終正常ログイン時間: 金 1月  31 2020 17:41:38 +09:00


Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
-- 接続して使えるようにいろいろ権限を付与
SQL> grant create session,create table,create view,create sequence,create trigger,create synonym,unlimited tablespace to username;

権限付与が成功しました。
```

```sql
-- もう一度作ったユーザでログインする
$ sqlplus username/password@hoge.pon.ap-northeast-1.rds.amazonaws.com:1521/sid

SQL*Plus: Release 12.2.0.1.0 Production on 金 1月 31 17:42:33 2020

Copyright (c) 1982, 2017, Oracle.  All rights reserved.



Oracle Database 12c Standard Edition Release 12.2.0.1.0 - 64bit Production
に接続されました。
SQL>
```
標本平均の標準偏差の分布検証【統計検定2級・母集団のパラメータ未知の検定・標本平均の上側確率】


統計検定2級で標本平均についての問題が出てきますが、その一つに
「標本平均のばらつき(標準偏差)は 

```math 
\frac{population\ sd}{\sqrt{sample\ size}} 


```

に従う」

とされます。
これを確認します。

最初に結論から言うと、標本平均は母集団平均から正の方向にズレることもあれば負の方向にズレることもある。
しかし、標本は母集団平均の周辺から(曖昧な表現ですが)遠く離れることはあまりない。
よって母集団の標準偏差をサンプルサイズで割ったくらいの範囲に縮むのです。
一方標本平均は母集団平均の近くから抽出される確率が高いので、
標本平均は母集団平均と近しい値になります。

#検証

```r
n=10000
m=100
s=30
hist(rnorm(n, m, s))
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/05772850-c078-189b-bd59-1b0d79237bdd.png)

こんな正規分布からサンプルサイズ100の標本を作る。
その標本の平均の分布を確認する。

```r
norm_data <- rnorm(n, m, s)

bind_mean <- NULL
bind_sd <- NULL

for(i in 1:100000){

set_number <- sample(n)
group_1 <- norm_data[set_number[1:100]]

sample_mean <- mean(group_1)
sample_sd <- sd(group_1)

bind_mean <- c(bind_mean, sample_mean)
bind_sd <- c(bind_sd, sample_sd)
}

par(mfrow=c(2, 1))
hist(rnorm(n, m, s),xlim=c(0, 200), main = paste0("population sd is ", s))
hist(bind_mean, xlim=c(0, 200), main = paste0("sample mean sd is ", round(sd(bind_mean), 3)))

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275572/280ce333-26d2-88a8-8844-497d969ec5d5.png)

確かに母集団の平均に近い値周辺に散らばっている。
標本平均の分散を計算で求めてみると、2.975となった。


```math 
\frac{population\ sd}{\sqrt{sample\ size}} 
```

が成り立っているのか。


```r
> s / sqrt(100)
[1] 3
```

もともと創り出した母集団の標準偏差は30であり、サンプルサイズ100の平方根は10なので、
30割る10となるので3と計算される。
2.975は近しい値をとっているといえる。


#ちなみに標本平均は

ヒストグラムで確認して既に「母集団平均」と「複数の標本平均の平均」は近い値になっているように見えるが念のため。

```r
> m
[1] 100
> mean(sample_mean)
[1] 101.4024
```


こうした分布が発見されたために母集団のパラメータが未知の時の検定ができるようになったわけです。

#どんな時につかうの？

例としてこんな問題を使う。

>全国のテストのデータから算出した母数によると、点数は正規分布に従い、平均50標準偏差10だった。(母平均、母標準偏差が既知)
5人のテストの点数の平均が52点以上になる確率は？（標本平均）


ここで注意しなければいけないのは「5人の平均」というところ。
たった1人のテスト受験者が52点以上をとる確率は？と聞かれたら、純粋に標準正規分布に変換し、Zスコアを出す。
↓
標準正規分布の上側確率の表を見ながらZスコアの位置を調べ、確率(問題の答え)を得る。

```math
Zscore=\frac{x-\mu}{\sigma}
```

となるが、「5人の平均」について語るときには5人の中に高い点を取る人、低い点を取る人がランダムに混ざった、その前提で52点となる確率を聞かれている。
即ち、「5人の標本平均」を標準正規分布に直すためには、「標本標準偏差」で割ってやらなければいけない。
本記事で書いた通り、標本平均の標準偏差はサンプルサイズに影響を受けるので、

```math
Zscore=\frac{\bar{x}-\mu}{\frac{\sigma}{\sqrt{n}}}
```

と計算する必要がある。
ここまで計算してしまえば、標準正規分布に従うZスコアになったので上側確率のZスコア表と見比べて答えを得ればいい。











#以上
郵便番号から住所を自動で補完する機能。

#概要
住所登録するときに郵便番号を入力すれば、自動で住所が出てくるやつ。
便利ですよね。
こんな感じです。
[![Image from Gyazo](https://i.gyazo.com/996c929b863a25c3c0b139dfb00d9f0a.gif)](https://gyazo.com/996c929b863a25c3c0b139dfb00d9f0a)

#使うもの
・jp_prefectureというgem
これは４７都道府県をいちいち全部書かなくても勝手に変換してくれます。
・jquery.jpostal.jsというプラグイン
これは、郵便番号を入力すると町までを予測してくます。


#手順

###1.jquery.jpostal.jsを導入しよう！
https://github.com/ninton/jquery.jpostal.js/
このリンクからjquery.jposta.jsというファイル(ぜんぶ)を取得して、
assets/javascript/に置いて下さい。

###2.gem入れよう！
```ruby:Gemfile
gem 'jp_prefecture'
gem 'jquery-rails' 
```

bundleしましょう。

###３.mifrationファイルに住所系のカラムを入れよう！
```ruby:mifrationファイル
t.string :postcode
t.string :prefecture_code
t.string :address_city
t.string :address_street
t.string :address_building
```

postcode ：郵便番号
prefecture_code　：県
address_city　：市
address_street　：長村
address_building　：部屋番

です。
一番上のはint型でもokです。

`$ rails db:migrate`をしましょう。

###4.県を取得しよう！
modelから取得できます。

```ruby:user.rb
jp_prefecture :prefecture_code
       
 def prefecture_name
 　JpPrefecture::Prefecture.find(code: prefecture_code).try(:name)
 end
       
 def prefecture_name=(prefecture_name)
   self.prefecture_code = JpPrefecture::Prefecture.find(name: prefecture_name).code
 end
```

これで県拾えまっせ。

次にビュー行きます。
form作ってこれ入れます。

```ruby:new.haml
    
    = f.text_field :postcode
    = f.collection_select :prefecture_code, JpPrefecture::Prefecture.all, :name, :name, include_blank: true
    = f.text_field :address_city
    = f.text_field :address_street
    = f.text_field :address_building
```
県だけセレクト、モデルから直で県が取得できます。


###5.jQueryでjpostalを使おう！
controller作ればcoffeeファイル出来ます。

app/assets/javascripts/user.coffee

```ruby:users.coffee
$ ->
  $("#user_postcode").jpostal({
    postcode : [ "#user_postcode" ],
    address  : {
                  "#user_prefecture_code" : "%3",
                  "#user_address_city"            : "%4",
                  "#user_address_street"          : "%5%6%7"
                }
  })
```

最後params作って完了！

```ruby:controller
def create
  params.require(:user).permit(:postcode, :prefecture_name, :address_city, :address_street, :address_building)
end
```
カーソル下の単語をvimgrepで検索する

vimgrepでいつも検索したい単語をベタ打ちしてたので、カーソル下の単語をセットする方法を調べたメモ。

検索したい単語上で

```
:vim 
```
まで入れたあとに `CTRL-R` `CTRL-W` でカーソル下の単語がセットされる。


```
:help c_CTRL-R_CTRL-W
```
でhelpが引ける

## 参考
[Google グループ](https://groups.google.com/forum/#!topic/vim_use/yat3w6dfWr8)
Rancherで立てたKubernetesに直接kubectl接続する

Rancherを利用すると、Kubernetesクラスタを容易に構築・管理できるが
こんな疑問が。

```
・もしRancherサーバが死んだらどうなる？

・Rancherで立てたKubernetesクラスタには直接アクセスできる？
```

答えは

```
・Rancherサーバが死んでも、Rancherで立てたk8sクラスタは死なない
　※ホストサーバが異なる事が前提

・Rancherで立てたKubernetesクラスタへはkubectlで直接アクセスできる
```

※v2.3.0以上で検証しました。古いバージョンではその限りではないようです。

参考：
https://rancher.com/docs/rancher/v2.x/en/cluster-admin/cluster-access/kubeconfig/#accessing-rancher-launched-kubernetes-clusters-without-rancher-server-running

#Rancherで立てたKubernetesに直接kubectl接続

Rancherでk8sクラスタを立てると、直接アクセス可能なエンドポイントをデフォルトで生成してくれる。

ただし、RancherがRKEで立てたものが対象で、EKSなどで立てたものは知らん。とのこと。
>The authorized cluster endpoint only works on Rancher-launched Kubernetes clusters. In other words, it only works in clusters where Rancher used RKE to provision the cluster. It is not available for clusters in a hosted Kubernetes provider, such as Amazon’s EKS.

具体的には、

① k8sクラスタにアクセスしたいサーバにkubectlをinstall
② ~/.kube/config に、Rancherで立てたk8sのkubeconfigファイルをコンソールからコピペ
③contextオプションでクラスタ名を指定してkubectl接続

```
kubectl --context [クラスタ名] get pod
```
※クラスタ名は .kube/config 参照


試しに、Rancher Serverを停止させて、k8sクラスタへの接続を確認したところ
正常に動作していた。
また、Rancher Serverを再起動させると、Rancher側も復活。



ちなみに、Production環境のRancher ServerはHAが推奨されている
それは、k8sを管理するRancher Serverをk8sで立てる というややニワタマ的手法 w
https://rancher.com/docs/rancher/v2.x/en/installation/k8s-install/
デストラクタ

```py:1
class Person(object):
    def __init__(self, name):
        self.name = name
    
    def say_something(self):
        print('I am {}. Hello'.format(self.name))
        self.run(3)
    
    def run(self, num):
        print('run '*num)

    def __del__(self):
        print('Good-Bye')

person = Person('Tom')
person.say_something()
```

```:1の実行結果
I am Tom. Hello.
run run run 
Good-Bye
```

```py:2
class Person(object):
    def __init__(self, name):
        self.name = name
    
    def say_something(self):
        print('I am {}. Hello.'.format(self.name))
        self.run(3)
    
    def run(self, num):
        print('run ' * num)
    
    def __del__(self):
        print('Good-Bye')
    
person = Person('Tom')
person.say_something()
print('#######################')
```

```:2の実行結果
I am Tom. Hello.
run run run 
#######################
Good-Bye
```

print('#######################')の後に、
コードがなく、
personオブジェクトが使われないとなった場合に、
デストラクタである__del__関数が呼び出される。

2の様にではなく、
#######################の上に
Good-Byeを出力させたい場合は、
del personでpersonオブジェクトをデリートしてしまえばよい。

```py:3
class Person(object):
    def __init__(self, name):
        self.name = name
    
    def say_something(self):
        print('I am {}. Hello.'.format(self.name))
        self.run(3)
    
    def run(self, num):
        print('run ' * num)
    
    def __del__(self):
        print('Good-Bye')
    
person = Person('Tom')
person.say_something()
del person
print('#######################')
```

```:3の実行結果
I am Tom. Hello.
run run run 
Good-Bye
#######################
```
pythonのglobで再帰的にファイルを取得

備忘録。
python3.6で、特定のディレクトリ配下の全てのjpg画像を再帰的に読み込んで、名前順でソートする方法。


```python
import glob
file_list = sorted(glob.glob('directory_name/**/*.jpg', recursive=True))
```
iTerm2でProfiles（背景や色などのテーマ）を気分で変えたいよね

来る日も来る日も見るターミナル画面。
自分はターミナルにiTerm2を使用していますが、毎日色や背景が同じだとちょっとだけ憂鬱。

気分で変えたいけど、いちいち設定ファイルから変更するのは面倒。
しかし、実はこのiTermのテーマの設定ファイル（Profile）はターミナル画面から簡易的に変えることが可能と知ったので試してみた！

参考：[iTerm2 のプロファイルを peco で切り替える](https://kakakakakku.hatenablog.com/entry/2018/11/03/135615)

## iTerm2のProfiles設定
前提としてすでにiTerm2上で自分の好みの色や背景画像を設定していること。

自分は以下のように設定しています。
<img width="351" alt="Preferences.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/7d51dbef-7bc4-20de-98f4-4e1515b8d41a.png">


- Default（これが通常
- develop（開発環境
- stage（検証環境
- production（本番環境
- logviewr（ログ用

※開発環境や、検証環境、本番環境で自分はターミナルの色を変えています。（事故防止
さらに自分の好きなテーマをポコポコ追加していけばあとあと変えられるので色々設定しちゃいましょう。

### 設定ファイルを自由に選べるようにpecoをインストールする

そもそもpecoを初めてこの作業で知ったのですが、すごいですね。
**「標準入力されたデータをインクリメンタルサーチをして選択したら出力する」**　

実際の挙動はこんな感じです。
![Jan-31-2020 16-40-52.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/370814e0-66a7-4879-70c3-c8715a1c660e.gif)

まあなんか検索してくれるのです。（雑）
詳しくは以下の記事を見るとわかりやすいです。

参考記事：[え、まだpecoを使ってないの？？？] (https://qiita.com/vintersnow/items/08852df841e8d5faa7c2）

とりあえずインストールや！

```bash
$ brew install peco
```

### iTerm2のProfile（設定）ファイルを作る
ホームディレクトリにiTermのProfileの名前に紐づいたファイルを作成します。
自分は以下のように設定しているので以下のファイル名で記述。

- Default（これが通常
- develop（開発環境
- stage（検証環境
- production（本番環境
- logviewr（ログ用


```bash
$ vi ~/.iterm_profile

# テーマ名で記述する
Default
develop
stage
production
logviewr
```
### pecoでProfilesを読み込む
実際にターミナルでpecoで上記で作成したiterm_profileを読み込むようにする。
以下の設定でiTerm2のProfilesの変更が可能なので以下のコマンドでbashrcにエイリアスを設定します。

```bash
$ vi ~/.bashrc

alias term='echo -ne "\033]1337;SetProfile=$(peco ~/.iterm_profile)\a"'
```

`wq`で保存したら`source ~/.bashrc`

#### ターミナル上で実行してみる
実際にターミナルで設定したエイリアスコマンドで実行すると以下のようになります。

![Jan-31-2020 17-16-28.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/3ce3cd26-2771-1974-2bd9-a968d386f09e.gif)

ログの際には自分が大好きな「宝石の国」の痛ターミナル...。

これで好きな時にターミナルの色や画像を変えられるので気分転換になる！終わり。
コードを改修する際の5つの注意点！

# はじめに
メモがてらコード改修する際の注意点を書いていきます！

# 修正対象のモジュールを一覧化して共有する
開発者が自分一人なら大丈夫かと思いますが、
複数人で改修を行う場合、コーディングが被ると

Aさん「ふー！改修終わった！保存！」

Bさん「はー！改修終わった！保存！」←Aさんと同じモジュール

Aさん「あれ！？俺が書いたコードが書き換わってる！」

B(やべ・・・)

となる可能性があるので

Aさん「このモジュール触ってるからなんかあったら声かけてね！」

Bさん「はーい」

と情報共有しておくことによって無駄な工数が減ります。

よくあるのが、Googleのスプレッドシートで対象モジュールを一覧化して作業者を割り振ったりしていましたね。

そして対象モジュールがそのまま本番環境へのアップロード対象にもなるので作っておいて損はないかと！。

# バックアップを取っておく

Aさん「このモジュールの改修無くなったから戻しておいて！」

Bさん「バックアップ取ってないっす！」

Aさん「何～～～！？」

まあ本番環境で改修するチャレンジャーな話は聞いたことないので開発環境で改修している場合は本番環境のコードを拝借すれば大丈夫ですが、バックアップを取らずに更新するのはお客様側の心象があまり良くないように思います。
~~この人大丈夫かな？みたいな~~

一回コードをローカルに落としたら別ファイルなどで「202001311500_○○」のようにバックアップを取っておくと後々の「どの部分改修したっけ？」にも対応できます。

# 有識者からポイントを伺う
Aさん「このコードすげーなげーな！コメントもないし・・・一から読んでいくしかないか・・・」

よりかは改修だとそのコードを書いた方やプロジェクトの有識者が同じ現場でいたりするのでどのような挙動をするか聞いておくと理解が早いです。

あと「そんな資料あるんだったら先に連携してくださいよ～」
ってなることがあるので関連資料があるかも聞いたほうがいいですね！

# 改修の仕様を聞いておく

改修した部分をコメントアウトして取っておいたり、改修した場所を
`// ○○プロジェクト  R.Kobayashi 2020.01.31 START`
`// ○○プロジェクト  R.Kobayashi 2020.01.31 END`
のように囲ってあったりと改修した箇所を消したらいけないプロジェクトとかもありますよね。
~~いつまで残しておくんだろうって気持ちはありますが~~

# 完了報告をする
改修が終わったら、上長に報告するなり、スプレッドシートのモジュールのステータスを完了に変えてプロジェクトメンバーに共有し、次の作業に取り掛かりましょう！
[備忘録] githubのレポジトリの移行方法

# githubのレポジトリの移行方法の流れ

```
# 移行先のレポジトリを用意

# 移行元をミラーオプションでcloneする
git clone --mirror git@github.com:aaa/aaa.git

# cloneしたフォルダに入る
cd cloneしたフォルダ

# push先のurlを移行先に変更する
git remote set-url --push origin git@github.com:bbb/bbb.git

# urlを確認する
git remote -v

# 移行元の変更をとる
git fetch -p origin

# 移行先にpushする 上書きなので移行先のデータは消える
git push --mirror

# プルリクやissueは移行できない [remote rejected]になる
# githubがプルリクを保持するためのrefsを保持しているがこれはreadonlyのためらしい
# localにrefが残っちゃうので消す
git show-ref
git for-each-ref --format="ref=%(refname)" --shell refs/pull | while read entry; do   eval $entry;   git update-ref -d $ref; done
git for-each-ref --format="ref=%(refname)" --shell refs/tags | while read entry; do   eval $entry;   git update-ref -d $ref; done
git for-each-ref --format="ref=%(refname)" --shell refs/heads | while read entry; do   eval $entry;   git update-ref -d $ref; done

```

参考
https://qiita.com/ysti/items/be67a6e3c5754fac088a
https://qiita.com/takumiabe/items/639ae10025d086eb9ecb
python環境でcsv, pickle, joblib, parquetの読み書き速度と容量を比較

# やったこと

pythonで2次元配列データを一時保存するときによく使う
1. pickle.dump
2. joblib.dump
3. pyarrowに変換してparquet保存
4. pd.write_csv
のそれぞれについて読み書き速度と保存容量を比較しました。

# 結論

- 圧縮率と速度ならpickle protocol=4
- 一部だけ読んだり書いたりを繰り返すような場合はpyarrowでparquet保存

が良さそう

# 試行環境

CPU: Xeon E5-2630 x 2 chip
VRAM: 128GB
Windows8 64bit
python 3.6

# 比較に使ったデータ

機械学習用の特徴量データで試行しました
・pandas.DataFrameの 536行178886列 0.77GB
・pandas.DataFrameの4803行178886列 6.87GB

# 比較結果

#### 0.77GBのDataFrame

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/77c53801-f58b-1d9e-39e9-e06ecf90e682.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/c0677aa6-12f5-06cd-b9f5-1854daa03818.png)

#### 6.87GBのDataFrame

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/625f6982-ef6f-9d67-f428-e1e289832584.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277233/ae0bb524-0e23-06fd-1447-b6a577599a1a.png)

# 結果について

#### pickle
圧縮率と速度だけ見るとpickleのprotocol=3以降が優秀で、特にprotocol=4は4GB以上の書き出しにも対応しているのでめちゃめちゃ便利です。ただし、ubuntu環境のpython3.6以前ではprotocol=4に問題があるのか読み書きともそのままでは動きません。python3.7以降では正常に動きますので、環境が確保できる場合、または容量が小さい場合はpickleが良さそうです。

#### joblib
pickleと比べると圧縮率も読み書き速度もちょっと中途半端ですが、python3.6環境でも4GB以上の読み書きが可能なのでpackageの都合でpythonを新しく出来ない環境の方はこちらでも良いかもしれません。

#### pyarrow => parquet
joblib compress=0と同等の圧縮率と読み書きの速度がありながら、指定行指定列の読み書きが可能というのが魅力です。特に書き込みが速いのでランダムに読み書きが入る場合はこれが良さそうです。

# 環境の影響

環境の影響はすごく大きいようで、別マシンでOSも違う環境で実験したら20倍違うところが4倍しか違わなかったりしました。実際にお使いになる環境でテストした方がよさそうです。
JWTを3分で完全に理解する

OAuth2.0やOpenIDConnectの説明の中でよく目にするJWTとはなにかについて。
基本的にはOpenIDFoundationのドキュメント、[JWT](https://openid-foundation-japan.github.io/draft-ietf-oauth-json-web-token-11.ja.html)を元にしています。

## お断り
今回はJWEによる暗号化手順を含めたJWEの詳細には触れません。~~JWEの翻訳ドキュメントがなくて整理するのが面倒だったので。~~
また、JWS及びJWE（まとめてJOSEと呼ばれている）については批判も多いですが、批判されている点についても今回は触れません。

## 概要
JWTはJSONオブジェクト自体。JWSはJWTを署名保護する手段。JWEはJWTを暗号化する手段。
つまりJWTの中には、JWSによって署名されたJWT、JWEによって暗号化されたJWT、その両方が施されたJWTが存在します。
また、プレーンなJWTについても仕様として示されています。

## JWT(JSON Web Token)とは
JWTはエンコードされたJSONオブジェクトとしてクレームのセットを表します。
JWTはBase64でエンコードされたパートを`.`で連結したURL-safeな文字列として表現されます。
パートの数はJWS、JWEのどちらとして利用されているかによって異なります。

上記から、JWTという単語には二つの解釈が存在しうるようです。

- 後述するJWTクレーム・セットだけを指してJWTと呼ぶ
- `.`で連結したURL-safeな文字列を指してJWTと呼ぶ

様々な筆者がどちらの解釈で`JWT`を使っているかは文脈で判断する必要があります。
以後に登場するJWTは全て後者の解釈で利用しています。

### [JWS(JSON Web Signature)](https://openid-foundation-japan.github.io/draft-ietf-jose-json-web-signature-14.ja.html)とは
JSONオブジェクトをベースとした構造を用いて、デジタル署名やMACsにより保護されたコンテンツを表現するための手段。
JWT自体はBase64エンコードされただけのJSONオブジェクトなので、やりとりの間に改ざんが行われる可能性があります。
対策として、JWTにJWSによる署名を付与したり、JWEによる暗号化を施します。

### クレームのセットとは
JSONオブジェクトは以下のように、０以上の名前/値のペアで表されます。[RFC4627](https://www.ietf.org/rfc/rfc4627)

```
{
    "name": "value"
}
```

この`名前：値`のペアが`クレーム名:クレーム値`と表現され、ひとまとめにクレームと呼ばれます。
クレームのセットと表現されているのは、複数のクレームの組み合わせのことです。

### パートとは
先述の通り、JWSとしてか、JWEとしてか、利用のされ方でパートは異なります。
例として、JWSとして利用されるJWTの場合、以下の3つのパートが存在します（JWEの場合は５つ）。
なお各パートは、**パート毎にURF-8のオクテット配列にした上でBase64エンコードが施されています**。

- JWTヘッダ（JWSヘッダ）
- JWTクレーム・セット（JWSペイロード）
- JWS署名

#### JWTヘッダ
JWTヘッダは以下のようなJSONオブジェクトで、JWTに適用される暗号化オペレーションに関するパラメータを持ちます。

```
{
    "typ": "JWT",
    "alg": "RSA256"
}
```

JWTヘッダは、JWTがJWSを用いてデジタル署名されているならJWSヘッダ、JWEを用いて暗号化されているならJWEヘッダとも呼びます。
使用しているアルゴリズムを示す`alg`パラメータが`none`の場合、JWTはプレーンJWTであり、JWTヘッダはJWTヘッダでしかありません。

なお同じJSONオブジェクトなのでややこしいですが、JWTクレーム・セット以外のJSONオブジェクトの中身はパラメータと呼ばれます。
クレームと呼ばれるのはJWTクレーム・セットのJSONオブジェクトの中でだけのようです。

#### JWTクレーム・セット
JWTクレーム・セットは以下のようなJSONオブジェクトで、JWTによって伝搬されるメッセージをクレームのセットで表現しています。

```
{
    "iss":"joe",
    "exp":1300819380,
    "http://example.com/is_root":true
}
```

クレーム・セットには`iss`や`exp`のような予約済クレームの他に、`http://example.com/is_root`のような独自のクレームを含めてもよいとされます。
また、予約済クレームは必須ではないため、クレーム・セットに含まれない場合もあります。
JWTクレーム・セットは、JWTがJWSを用いてデジタル署名されているならJWSペイロードとも呼びます。
プレーンJWTの場合は、JWTクレーム・セットでしかありません。

#### JWS署名
JWS署名はJWSヘッダとJWSペイロードを`.`で連結したURL-safeな文字列に対して、JWSヘッダの`alg`で示したアルゴリズムで署名したものになります。
プレーンJWTの場合、JWS署名は作りようが無いですが、JWTとしては以下のように3つ目のパートに空文字が連結されます。

```
// 本来は改行はしない

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
.
```

### 署名の検証
JWSによって利用されたJWTについては署名を検証し、改ざんされていないことを確認します。
JWSヘッダーの`kid`パラメータのヒントを元に、発信者から公開鍵を取得し、`alg`パラメータのアルゴリズムと合わせて署名を検証します。
また、暗号鍵に[JWK（RFC7517）](https://openid-foundation-japan.github.io/rfc7517.ja.html)が利用されている場合、JWSヘッダーの`kid`とJWKの`kid`パラメータは一致するように利用できます。

当然ですが、検証は自分で実装しません。危ないので。
鍵と必要な情報を用意したら、各言語のJWTライブラリにお任せしてしまいましょう。

## まとめ
JWT、完全に理解できました。
これでOAuth2.0とOIDCのトークンの扱いも完璧です。
実際は署名の検証が終わったらトークン毎に異なる、クレーム・セットの検証が発生するのでこれで完璧では無いですけど。