OmniAICreator/Qiita-1.07M huggingface dataset:
Raspberry Pi から Sonos へ AirPlay 再生してみた。

# はじめに
Sonos One、Sonos Beam は AirPlay2 に対応しているので、iPhone/iPad、Mac 以外で Raspberry Pi からも AirPlay 再生できたのでメモに残しておこうと思います。

# 構成
必要最小構成で AirPlay 再生を試みます。

## forked-daapd とは
DAAP (Digital Audio Access Protocol)、MPD (Music Player Daemon)、AirPlay デバイスをサポートしたメディアサーバーです。
Raspberry Pi へ <A HREF="http://ejurgensen.github.io/forked-daapd/">forked-daapd</A> をインストールするだけです。PC のブラウザから操作を行います。
![スクリーンショット 2020-01-03 17.21.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/aae0880f-a05c-d6a4-6d8f-3ed9317c9f54.png)

ブラウザから http://forked-daapd.local:3689 へアクセスするとウェブ画面が表示され、スピーカーから Sonos (画面では「リビングルーム」の名前) を選択します。

![スクリーンショット 2020-01-03 16.37.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/a5bb7b64-ce87-7744-68e5-e9fd717876b6.png)

# 用意したもの
 - Raspberry Pi 3 Model B+  
   OS イメージ (2019-09-26-raspbian-buster-full.img)

# インストール手順

```bash
# repository key を追加
$ wget -q -O - http://www.gyfgafguf.dk/raspbian/forked-daapd.gpg | sudo apt-key add -
# 設定ファイルを編集
$ sudo vi /etc/apt/sources.list
# パッケージを更新
$ sudo apt update
# forked-daapd をインストール
$ sudo apt install forked-daapd
```

```text:/etc/apt/sources.list
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
deb http://www.gyfgafguf.dk/raspbian/forked-daapd/ buster contrib  # ←行を追加
```

# 設定

```bash
# 設定ファイルを編集
$ sudo vi /etc/forked-daapd.conf
```

```text:/etc/forked-daapd.conf
(27行目)
	admin_password = "admin"  # 行をコメントイン、ウェブ画面アクセス用のパスワード (例えば、admin) を設定
(71行目)
	directories = { "/home/pi/Music" }  # 行をコメントイン、音楽ファイルを格納するディレクトリを設定
```

# 起動

```bash
# forked-daapd 起動
$ sudo service forked-daapd start
# forked-daapd 停止
$ sudo service forked-daapd stop
# forked-daapd 再起動
$ sudo service forked-daapd restart
# forked-daapd ログ確認
$ tail -f /var/log/forked-daapd.log
```

# mp3 ファイルを保存

```bash
$ cd Music
/home/pi/Music
$ ls
JohannStraussI-Radetzky-Marsch-2019-AR.mp3
Pachelbel-Canon-in-D-2019-AR.mp3
```

# ウェブ画面へアクセス
ブラウザで
http://forked-daapd.local:3689
へアクセスする。
(Username: admin、Password: admin)
PHPで開発してて出会ったエラー達[随時更新]

出会ったエラーの数だけ人は成長していくと誰かが言っていた。自分用のメモとして、出会ったエラーを書き残してく。

##implode(): Invalid arguments passed in ~
`implode`とか使う時、たまに出くわします。
単純に引数に`array`でないものを渡してしまっている。

```php:error.php

//$band[]が正解
$band = 'John';
$band = 'Paul';
$band = 'George';
$band = 'Ringo';

//配列を渡していない。
$result = implode("\t", $band);
```
【Rails】devise_token_authで新規ユーザー登録時にUnpermitted parameters: が出たときの対処法

## はじめに
gem **devise_token_auth**を使っていて、新規ユーザー登録時に以下エラーが出たときの対処法を残します。

```shell
Unpermitted parameters: 保存されて欲しいカラム名
```

**自分でUserテーブルにカラムを追加して、保存しようとしたら**
発生する内容です。

今回、自分の場合は`:age`と`:gender`を追加しようとしたところ発生しました。（以下参照）

```shell
Unpermitted parameters: :age, :gender
```

これを解決していきます。

## 環境
```yaml
OS: macOS Catalina 10.15.1
zsh: 5.7.1
Ruby: 2.6.5
Rails: 6.0.2.1
```

## 結論:`registrations_controller`に追記

```ruby:registrations_controller
class Api::V1::Auth::RegistrationsController < DeviseTokenAuth::RegistrationsController

  private

  def sign_up_params
    # ここに :age, :genderを追記
    params.permit(:name, :email, :age, :gender, :password, :password_confirmation)
  end

  def account_update_params
    params.permit(:name, :email)
  end
end
```

※反映するには`rails server`再起動が必要です。

## 理由
**Strong Parameterではじかれてしまっている**のが問題でした。

`sign_up_params`の中に追記することで、新規ユーザー登録のときだけ許可されるようになります。

**こいつも許可してあげてよ！**という指定をすればOKですね。


## おわりに
最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:
Windows10上のSpacemacsでCtrl＋vショートカットキーを効かせる

- Magitで `git clone`時にURLを貼り付けるとか、実はSpacemacsのミニバッファ上で貼り付けしたい場合が多い
- Macでは `Command + v`を使えるが、Windowsではそうはいかない。

そこで、 `.spacemacs`に以下の設定を加える。

```emacs-lisp:.spacemacs(抜粋)
(defun dotspacemacs/user-config ()

  ;; Ctrl + vをWindowsの貼り付けに合わせる
  (bind-key "C-v" 'nil)
  (bind-key "C-v" 'clipboard-yank)
)
```

まず既存の `Ctrl + v`キーバインドを滅ぼし、敵がいない状態を確保したのちに貼り付け機能 `clipboard-yank`を割り当てる。

ついVimのyankだと思い込んでいて「コピーじゃないんだよなぁ…貼り付けなんだよなぁ…」とかしばらく悩んでいた。ごめんて。
Pytorchで自作の活性化関数をかく(hard sigmoid)

# ChainerからPytorchへの移植
研究でDeep Learningをしているのですが、先日Chainerのアップデートが終わりを迎えるのを知り、開発元と同様Pytorchにフレームワークを変更することになりました。
手始めに今あるChainerのプログラムからPytorchに移植することにしました。

基本的には関数の名前などを変えるだけでよかったんですが、途中でPytorchにHardSigmoidがないことに気づきました。
ということで自分で作っちゃおうということです。

#実際にかいてみた
...といっても、公式リファレンスに書いてあるのでほぼその通りにやっただけです。
--> https://pytorch.org/docs/master/autograd.html


```python:
class MyHardSigmoid(torch.autograd.Function):
    @staticmethod
     def forward(ctx, i):
         ctx.save_for_backward(i)
         result = (0.2 * i + 0.5).clamp(min=0.0, max=1.0)
         return result

     @staticmethod
     def backward(ctx, grad_output):
         grad_input = grad_output.clone()
         result, = ctx.saved_tensors
         grad_input *= 0.2
         grad_input[result < -2.5] = 0
         grad_input[result > -2.5] = 0
         return grad_input
```

@staticmethodを書かないとwarningが出てきます。
公式のものは、指数関数になっていますが、これをhard sigmoidに変えていきます。

まずforward()には、順伝搬をかきます。
hard sigmoid()は以下のような式になるのでそうなるように書きました。

```math
h(x) = \left\{
\begin{array}{ll}
0 & (x \lt -2.5) \\
0.2x + 0.5 & (-2.5 \leq x \leq 2.5)  \\
1 & (2.5 \lt x)
\end{array}
\right.
```

次にbackward()で、これは逆伝搬を書きます。
微分係数は以下のようになります。

```math
\frac{\partial h(x)}{\partial x} = \left\{
\begin{array}{ll}
0 & (x \lt -2.5) \\
0.2 & (-2.5 \leq x \leq 2.5)  \\
0 & (2.5 \lt x)
\end{array}
\right.
```


そして最後にこれをモデルに適用させます。（モデルの中身は適当です。）

```python:model.py
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        hard_sigmoid = MyHardSigmoid.apply
        return hard_sigmoid(self.conv2(x))
```
これでバッチリ！！...なはず
【画像処理100本ノックに挑戦】Q.35. フーリエ変換　バンドパスフィルタ

# 使用したライブラリ
[【画像処理100本ノック】独自の画像入出力クラスを作る](https://qiita.com/jajagacchi/items/834e8425b84f94df8a49)
[【画像処理100本ノックに挑戦】独自の離散フーリエ変換クラスを作る](https://qiita.com/jajagacchi/items/0b5232aa8ae99b9b8114)

# Q.35. フーリエ変換　バンドパスフィルタ

Q.33、Q.34とほぼ同じですね。

```c++
int main()
{
	PPM ppm("imori.pnm");
	int width = ppm.Get_width();
	int height = ppm.Get_height();

	std::vector<std::vector<std::complex<double>>> f(width, std::vector<std::complex<double>>(height));
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int r = ppm(i, j, 'r');
			int g = ppm(i, j, 'g');
			int b = ppm(i, j, 'b');
			int y = (std::round)(0.2126 * r + 0.7152 * g + 0.0722 * b);
			f[i][j] = y;
		}
	DFT2D dft2d(width, height);
	auto F = dft2d.forward(f);
	F = dft2d.shift(F);
	double r = width / 2;
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			double R2 = (i - width / 2) * (i - width / 2) + (j - height / 2) * (j - height / 2);
			if ( R2 < 0.01 * r * r || R2 > 0.25*r*r) F[i][j] = 0;
		}
	F = dft2d.ishift(F);
	f = dft2d.backward(F);

	PPM ppm2(width, height);
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int val = f[i][j].real();
			if (val >= 256) val = 255;
			if (val < 0)val = 0;
			ppm2(i, j, 'r') = val;
			ppm2(i, j, 'g') = val;
			ppm2(i, j, 'b') = val;
		}
	ppm2.Flush("out.ppm");
	
	return 0;
}
```

![out0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/61a07090-e946-847a-c098-78efc33bba38.png) ![out.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/7db9aa2b-ba85-a6f7-e22e-487215974c54.png)
iOSのショートカットで扱う秘密データの管理方法について

# はじめに

iOSのショートカットを作成する上で一度は悩むことは、ショートカット内部で扱う秘密データ(APIで扱うAPIキーやパスワードなどのトークン文字列)をどう管理するかではないでしょうか。

この管理方法について、検討した内容を共有したい思います。

# 前提

* iOSのショートカットを使用するため、iOS12以上が必要です。
* また、iPhoneの1passwordアプリを使用します。

# よくある管理方法の問題

いろんな人が作成したショートカットの中身を見てみると、以下のように秘密データを管理しているケースに遭遇します。

* メモアプリ(あるいは リマインダーアプリ)に秘密データを保存し、ショートカット実行時にメモアプリから検索する。
* ショートカット内部に秘密データをハードコーディングする。
* ショートカット実行時にアラートを表示し、秘密データを入力させる。

メモアプリから検索する方法は、メモアプリがパスワードで保護されていないため、秘密データを他人に見られる恐れがあります。同様に、ハードコーディングする方法についても、ショートカットの内部処理を見れば、秘密データを他人に見られる恐れがあります。

ショーカット実行時に秘密データをアラートに表示する方法は、上記方法よりは良さそうに感じますが、秘密データをどこからか検索する手間やコピーしたクリップボードの秘密データが残ったままになる恐れがあります。

# 解決方法

上記の管理方法の解決方法として、1passwordのパスワード管理アプリを使う方法を提案したいと思います。

1passwordアプリで管理する利点としては、以下の通りです。

* 秘密データがマスターパスワードで保護されているため、他人に秘密データを容易に見られる心配がない。
* 1passwordのURLスキームを使えば、1passwordの秘密データを検索する(絞りこむ)ことができる。
    * 例えば、```onepassword://search/<検索ワード>```の形式で1passwordを起動できます。
    * URLスキームの仕様の詳細については、[こちら](https://github.com/christopherdwhite/iosWorkflows/blob/master/1password.md)を参照。
* 1passwordに秘密データが保存されていなくても、実行時に秘密データを作成できる。
* 1passwordの秘密データをワンタップでクリップボードでコピーできる。

ショートカットアプリでは1passwordアプリのURLスキームを実行時に呼び出すことができるため、以下のような手順で秘密データをショートカット内部で扱うことができます。

#### 手順詳細
* ショートカットアプリから1passwrordアプリのURLスキームを起動する。
* 起動した1passwordアプリから秘密データをクリップボードにコピーする。
* クリップボードにコピー後、左上のアプリ名を押下し、ショートカットアプリに戻る。
* ショートカットアプリでクリップボードから秘密データを任意の変数にコピーする。
* 安全のためにクリップボードの秘密データをクリア（空文字列をコピー）する
* 任意の変数にコピーした秘密データをショートカット内部で使用する。  

# ショートカットの作成

ここでは、```1passwordアプリからGoogleのアカウントのトークンを表示する```ショートカットの作成例について説明します。

#### 手順詳細
* ショートカットの新規作成画面を開きます。
* アクションの```URLを開く```を追加し、```onepassword://search/google```をURLに設定します。
    * この処理で、1passwordが起動し、```google```のキーワードで検索された状態になります。
* スクリプティングの```待機```を追加します。
    * この処理で、1passwordのアプリからショートカットアプリに戻るまで、ショートカットアプリが待機状態になります。
* アクションの```変数に設定```を追加します。
    * 変数を仮にtokenとして、変数tokenをクリップボードに設定にします。
* アクションの```テキスト```を追加します。
    * 変数tokenを使用して、秘密データを表示するテキストを作成します。
    * ここでは、```トークンはXXXXです```のような形式のテキストになります。
* アクションの```クイックルック```を追加します。
    * 作成したテキストをクイックルックで表示します。
* クリップボード上の秘密データをクリアするために、アクションの```テキスト```を追加し、空のテキストを追加します。
* 最後にアクションの```クリップボードにコピー```を追加し、空のテキストをクリップボードにコピーします。

参考までに、上記で作成したショートカットの[iCloudの共有リンク](https://www.icloud.com/shortcuts/b0f658fd4b9343dfa70526f557c46c2f)を貼っておきます。


# さいごに

秘密データの管理方法について、1passwordを活用する方法について提案しました。秘密データを管理する際の参考になれば幸いです。

# 参考

* [1passwordのURLスキームの仕様の詳細](https://github.com/christopherdwhite/iosWorkflows/blob/master/1password.md)
【SQL】メモ4(ある項目毎の件数(COUNT GROUP BY),重複を削除(DISTINCT))

###目的
SQLの忘却防止。
ある程度件数が増えると、指定の項目が何種類あるのかとか、
指定の項目が何種類あって、各項目について何件あるのか知りたい時がある。

[メモ1](https://qiita.com/knowledge87sun/private/fd507c7734fae07b5aaf)のデータが元。

##１．ある項目毎の件数

__形式：__
*__SELECT 項目名,count(項目名) FROM テーブル名 GROUP BY 項目名__*

```sql
-- type0の種類と、何件存在するか
SELECT type0,count(type0) as cnt_type
 FROM tbl_dino GROUP by type0;
```
####実行結果

|type0|cnt_type|
|---|---|
|epic|6|
|normal|8|
|rare|4|
|regend|4|

##２．重複を除いた、項目名の種類(DISTINCT)

__形式：__
*__SELECT DISTINCT 項目名 FROM テーブル名__*

```sql
-- type0の種類を取得
SELECT DISTINCT type0 FROM tbl_dino;
```
####実行結果

|type0|
|---|
|normal|
|rare|
|epic|
|regend|
【画像処理100本ノックに挑戦】Q.34. フーリエ変換　ハイパスフィルタ

# 使用したライブラリ
[【画像処理100本ノック】独自の画像入出力クラスを作る](https://qiita.com/jajagacchi/items/834e8425b84f94df8a49)
[【画像処理100本ノックに挑戦】独自の離散フーリエ変換クラスを作る](https://qiita.com/jajagacchi/items/0b5232aa8ae99b9b8114)

# Q.34. フーリエ変換　ハイパスフィルタ
> imori.jpgをグレースケール化したものをDFTし、ハイパスフィルタを通してIDFTで画像を復元せよ。

Q.33ローパスフィルタとほぼ同じですね。

```c++
int main()
{
	PPM ppm("imori.pnm");
	int width = ppm.Get_width();
	int height = ppm.Get_height();

	std::vector<std::vector<std::complex<double>>> f(width, std::vector<std::complex<double>>(height));
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int r = ppm(i, j, 'r');
			int g = ppm(i, j, 'g');
			int b = ppm(i, j, 'b');
			int y = (std::round)(0.2126 * r + 0.7152 * g + 0.0722 * b);
			f[i][j] = y;
		}
	DFT2D dft2d(width, height);
	auto F = dft2d.forward(f);
	F = dft2d.shift(F);
	double r = width / 2;
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			if ((i - width / 2) * (i - width / 2) + (j - height / 2) * (j - height / 2) < 0.01 * r * r) F[i][j] = 0;
		}
	F = dft2d.ishift(F);
	f = dft2d.backward(F);

	PPM ppm2(width, height);
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int val = f[i][j].real();
			if (val >= 256) val = 255;
			if (val < 0)val = 0;
			ppm2(i, j, 'r') = val;
			ppm2(i, j, 'g') = val;
			ppm2(i, j, 'b') = val;
		}
	ppm2.Flush("out.ppm");
	
	return 0;
}
```

![out0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/71567bb8-5946-cb82-39eb-78e5f14badb6.png) ![out.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/8c19005e-fc2f-c6e2-0d23-150c20813861.png)
Java8 ラムダ式の filter() で使用する条件をパラメータで渡す

ラムダ式は変数に代入して再利用することができますが、そのフィルタ条件などは固定されています。
[Java8 ラムダ式を変数に代入して再利用する](https://qiita.com/blueskyarea/items/169a04b77a297ead9774)

そのため、そのフィルタ条件を変えたい場合、別々の式を定義する必要があるかもしれません。
例えば、
”文字列の長さが5の文字列のみ”をフィルタするラムダ式と、
”文字列の長さが8の文字列のみ”をフィルタするラムダ式が欲しい場合、以下のように2つの式を定義する方法が考えられます。

```java:lengthFiveOrEight
// 文字列の長さが5
final Predicate<String> lengthEqualFive = name -> name.length() == 5;
// 文字列の長さが8
final Predicate<String> lengthEqualEight = name -> name.length() == 8;
```

条件が常に固定であれば問題ありませんが、そのアプリケーションが柔軟に条件の変更をしたい場合、その数だけ式を定義することになるかもしれません。

この条件（この例では５か８）をパラメータとして受け取ることが出来れば、このような重複を避けることが出来ます。
例えば、以下のようなメソッドを定義する方法が考えられます。

```java:lengthEqualWith
Predicate<String> lengthEqualWith(final Integer expectsLength) {
  return name -> name.length == expectsLength;
}
```

単にラムダ式を変数に代入する代わりに、ラムダ式を返すメソッドに引数を渡し、その引数をラムダ式に適用するような形です。
lengthEqualWith() を呼び出す時に、expectsLength をパラメータとして渡してあげることで、柔軟に条件を変更することが可能となります。

以下のリストを使ってフィルタしてみます。

```java
final List<String> months = 
Arrays.asList("January", "February", "March", "April", "May", "June", "July", "Augast", "September", "October", "November", "December");
```

以下のように lengthEqualWith(N) を適用しています。

```java
System.out.println("Length is 5");
List<String> result1 = months.stream().filter(lengthEqualWith(5)).collect(Collectors.toList());
result1.forEach(System.out::println);
		
System.out.println("Length is 8");
List<String> result2 = months.stream().filter(lengthEqualWith(8)).collect(Collectors.toList());
result2.forEach(System.out::println);
```

以下のような（期待通りの）結果が得られます。

```java
Length is 5
March
April
Length is 8
February
November
December
```

このラムダ式は expectsLength 変数をスコープ内で探しますが、このスコープのことを静的スコープ（または構文スコープ）と呼ばれるようです。
expectsLength 変数がスコープ内に存在（キャッシュ）していれば、ラムダ式はその値を使います。

今回の例では、lengthEqualWith() メソッド内部がその静的スコープになります。
ラムダ式は final のローカル変数にしかアクセスできないため、expectsLength 変数は final である必要があります。

たとえ、final と宣言されていなくとも、final である条件が満たされていれば（つまり、その変数が初期化されており、不変であれば）動作するようです。

ただ、Java としてはそれらの条件が満たされているかを評価する必要がある（コストがかかる）ため、こういった値をキャッシュするのとしないのでは、パフォーマンスに若干の違いが出るかもしれません。
Kubernetesでクラスタを組んだ時にとりあえずPodを作って動作確認したいときに実行するコマンド

```bash
kubectl run aks-nginx --image=nginx
kubectl expose deploy aks-nginx --port=80 --type=LoadBalancer
```

```
yuta@DESKTOP-R0C7EKE:~$ kubectl get service aks-nginx
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
aks-nginx   LoadBalancer   10.0.176.228   52.142.26.3   80:32012/TCP   78s
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293481/1528609d-4f95-53bc-52b0-2d67765e65da.png)
XCodeを使わず、MacApp「Transporter」でipaをAppStoreConnectに簡単にアップロード

納品されたiOSアプリケーションのipaファイルをXCode使わずにAppStoreConnectにアップロードすることができるTransporterというMac Appが公開されてたので使ってみます。

# 前提
- Apple Developerアカウントを保有している
- App Store ConnectのマイAppに新規アプリ作成していること

# Mac App「Transporter」
Mac App StoreでTransporterを入手できるようになりました
https://developer.apple.com/jp/news/?id=10152019a

Appのリンクは[こちら](https://apps.apple.com/jp/app/transporter/id1450874784?mt=12)。期待できそう！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/7227cbff-6434-54f9-a4b1-9d832fd1f4bf.png)

はいはーい同意します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/ef3c5cba-48f7-33d3-ab06-da942be842bb.png)

サインイン画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/9ac11d79-8eae-8ca2-964e-67fefa8ed2ab.png)
AppStoreConnectのアカウントでサインインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/5c789f5a-006f-fc7f-fbe2-b00bc6ba79f7.png)

あら。シンプルな画面。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/022c4921-ed73-eafc-2ca2-dadd260a6a76.png)

ipaファイルをドラッグ＆ドロップすると、以下のようにアプリがデリバリできる状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/11ef74d0-fb46-e8ed-ebc4-08e6e4144e3f.png)

このままデリバリしてもいいのですが、デリバリ失敗すると証明書の中のバージョンを上げて再度デリバリし直したりしないといけなくなるので、デリバリ前に検証を行います。デリバリボタン横のオプションボタンをクリックし、[検証]を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/4d343179-0332-4d27-b5c5-a6823ec2fad8.png)

検証が問題ない場合には正常に検証が終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/6b008479-cedf-1585-2b1a-f352a101a732.png)

>もし検証失敗した場合にはエラーが表示しますので、エラーログで原因調べられます。エラーログの見方は[こちら](https://help.apple.com/itc/transporteruserguide/#/itc0235ef512)や[こちら](https://help.apple.com/itc/transporteruserguide/#/itc6bfa1812a)参考。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/7842866a-bbb2-24ed-d166-2aae371ae580.png)

では、AppStoreConnectにデリバリ転送します。[デリバリ]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/64e08b17-2c61-44b1-3aaf-5ffaa93f7a3c.png)

>電源につながった状態になっていないと警告が表示されるみたいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/ba0459fb-dfcc-df61-54f1-251492a1fbdf.png)

4,5分くらいかかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/f20c4b49-cecc-22a3-1adc-537ffa609628.png)

正常に終わると以下のようにデリバリ済みという表示に変わります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/9ac6c097-f08e-8fa8-3a00-62191ceb3462.png)

AppStoreConnectのApp画面を見ると、ちゃんと転送されてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178016/451ba332-8340-c783-ed69-31fcbf79baec.png)

# 参考リンク
- [iTunesConnectヘルプ-Transporter とは何ですか？](https://itunespartner.apple.com/jp/movies/faq/Transporter_%E8%A8%AD%E5%AE%9A)
- [Transporter ユーザガイド 2.0](https://help.apple.com/itc/transporteruserguide/)
【画像処理100本ノックに挑戦】Q.33. フーリエ変換　ローパスフィルタ

# 使用したライブラリ
[【画像処理100本ノック】独自の画像入出力クラスを作る](https://qiita.com/jajagacchi/items/834e8425b84f94df8a49)
[【画像処理100本ノックに挑戦】独自の離散フーリエ変換クラスを作る](https://qiita.com/jajagacchi/items/0b5232aa8ae99b9b8114)

# Q.33. フーリエ変換　ローパスフィルタ
> imori.jpgをグレースケール化したものをDFTし、ローパスフィルタを通してIDFTで画像を復元せよ。

DFTシフトして高周波を落とし、DFT逆シフトし、逆フーリエ変換すればOKです。

```c++
int main()
{
	PPM ppm("imori.pnm");
	int width = ppm.Get_width();
	int height = ppm.Get_height();

	std::vector<std::vector<std::complex<double>>> f(width, std::vector<std::complex<double>>(height));
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int r = ppm(i, j, 'r');
			int g = ppm(i, j, 'g');
			int b = ppm(i, j, 'b');
			int y = (std::round)(0.2126 * r + 0.7152 * g + 0.0722 * b);
			f[i][j] = y;
		}
	DFT2D dft2d(width, height);
	auto F = dft2d.forward(f);
	F = dft2d.shift(F);
	double r = width / 2;
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			if ((i - width / 2) * (i - width / 2) + (j - height / 2) * (j - height / 2) > 0.25 * r * r) F[i][j] = 0;
		}
	F = dft2d.ishift(F);
	f = dft2d.backward(F);

	PPM ppm2(width, height);
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int val = f[i][j].real();
			if (val >= 256) val = 255;
			if (val < 0)val = 0;
			ppm2(i, j, 'r') = val;
			ppm2(i, j, 'g') = val;
			ppm2(i, j, 'b') = val;
		}
	ppm2.Flush("out.ppm");
	
	return 0;
}
```

![out0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/9f114933-9e09-1407-ba39-6fd06af148b4.png) ![out.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/e3498763-7ea9-c731-a764-bd41ab09c1a1.png)
【画像処理100本ノックに挑戦】Q.32. フーリエ変換

# 使用したライブラリ
[【画像処理100本ノック】独自の画像入出力クラスを作る](https://qiita.com/jajagacchi/items/834e8425b84f94df8a49)
[【画像処理100本ノックに挑戦】独自の離散フーリエ変換クラスを作る](https://qiita.com/jajagacchi/items/0b5232aa8ae99b9b8114)

# Q.32. フーリエ変換
> 二次元離散フーリエ変換(DFT)を実装し、imori.jpgをグレースケール化したものの周波数のパワースペクトルを表示せよ。 また、逆二次元離散フーリエ変換(IDFT)で画像を復元せよ。

画像のパワースペクトルは直流付近の値が大きいため、原点付近だけ真っ白になって全体の分布がよくわからない場合がよくあります。このため、しばしばlog表示されます。今回は数値データとして出力してGnuplotでlog表示します。

```c++
int main()
{
	PPM ppm("imori.pnm");
	int width = ppm.Get_width();
	int height = ppm.Get_height();

	std::vector<std::vector<std::complex<double>>> f(width, std::vector<std::complex<double>>(height));
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int r = ppm(i, j, 'r');
			int g = ppm(i, j, 'g');
			int b = ppm(i, j, 'b');
			int y = (std::round)(0.2126 * r + 0.7152 * g + 0.0722 * b);
			f[i][j] = y;
		}
	DFT2D dft2d(width, height);
	auto F = dft2d.forward(f);
	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < height; j++)
		{
			std::cout << i << " " << j << " " << std::abs(F[i][j]) << std::endl;
		}
		std::cout << std::endl;
	}
	
	f = dft2d.backward(F);
	
	PPM ppm2(width, height);
	for (int j = 0; j < height; j++)
		for (int i = 0; i < width; i++)
		{
			int val = f[i][j].real();
			if (val >= 256) val = 255;
			if (val < 0)val = 0;
			ppm2(i, j, 'r') = val;
			ppm2(i, j, 'g') = val;
			ppm2(i, j, 'b') = val;
		}
	ppm2.Flush("out.ppm");
	
	return 0;
}
```

```
gnuplot> set log z
gnuplot> splot "fourier.txt" using 1:2:3 with pm3d
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520857/0a44c58a-5cdd-5952-e87a-152341734bca.png)

微妙・・・。
4年ぶりにエンジニアに戻った人の話

こんにちは、Qiita初投稿です。

色々あって昨年末に4年間勤めていた外資系のゲーム会社でのプロデューサー業を辞めてエンジニアに戻りました。
前職ではエンジニアではなく、翻訳やアカウントマネージメント系の仕事などをやっていました。
エンジニアブログを書く前に、今回は元エンジニアが4年間経ってエンジニアに戻って世界が一変していた、浦島太郎のような話をしたいと思います。

## 経歴:
### 2011年: 大手企業に新卒でエンジニア就職
秋入社というのもありまさかの**新卒唯一の日本人**、まわりは中国人とインド人ばっかり。入社一日目でエンジニア職人気なさすぎオワタと思う。まわりのエンジニアが毎月行われれていた深夜作業に駆り出されていくのを怯える毎日（結局一度もその作業をすることはなかったが）

### 2013年: ベンチャー企業へ転職
前職ではテスト業務しか触れず、いつの間にかポジションもQAエンジニアになってしまったのでエンジニアらしいことがしたいと一念発起して、当時黎明期だったモバイルアプリ業界へ転職。たまたま入社直後にアプリがヒットし、会社が急成長。カスタマーサポート担当→QAエンジニア→サーバーエンジニアと役割を変え、気づいたらエンジニアリードになっていった。

### 2015年: 外資系ゲーム会社へ転職
ヘッドハンティングで当時日本サーバーを建てようとしていた世界的人気のゲームを運営する会社へ入社。エンジニアではなく語学力＋QA経験でLocalization QAを任される。そのうち、「君これもできるよね？」というノリでユーザーコミュニケーション担当をやっていたら、そちらが本業になりLocalizationチームから外れてMarketingチームへ異動。Abemaでテレビ番組のプロデューサーをやっていたら、半年で今度は当時のesports担当が外れたので急遽、責任者に就任。

### 2019年: 現在の会社へ
前職を8月末に辞表を出し11月末に退社（理由は色々あったので割愛）。就職先を決めていなかったので、「いい機会だし色んな業界の話を聞いてみよう」と多くの会社に面接に行っていたら現在の社長と意気投合、はれて4年ぶりにエンジニアに復職。

## エンジニアに戻ってきて
前置きが長くなってしまいましたが、ここからが本題。まず転職時の感想ですが。

### エンジニア職が好待遇に
昔はエンジニア職といえば3K(Kitsui, Kitanai, Kaerenai)のイメージが強かったです。実際「え、エンジニアなんてやってるんですか？」と大学時代の知り合いに言われたぐらいで、エンジニアなんてさっさとやめてマネージメントに入るのが勝ち組なんて影では言われていました。

正直直近の転職活動の時も、最初は「エンジニア戻るのはないなー」と思って、それでも経歴上エージェントから話が来るので渋々話を聞きにいっていたのですが。

…あれ？エンジニア職の待遇変わってね？

聞くと、今IT系エンジニアの求人倍率は**10倍**なんですね。昔は「私の代わりはいくらでもいるもの」という感じだったのですが、みんなどこへ行ってしまったのか…当然ながら給与も想像より上、同じように応募した翻訳やプロデューサー業よりも上でした。

### 技術の進歩
エンジニアに4年ぶりに戻るとあって、有給消化中はほぼ毎日10時間ぐらい今の技術を勉強に費やしていました。
完全にキャッチアップとはまだまだ遠いですが、思ったことを簡単に書くと、

#### サーバーインフラ構築が簡単に
本当にこれ、当時（2013年ぐらい）だとAWSのEC2とS3とCloudFront使ってサーバーエンジニアが汗と涙で毎回デプロイしてたのに、DockerとEBS組み合わせたら何も考えずにサーバーが建てられる、しかもコンテナモデルだからデプロイが速い速い。あとサーバーレスアーキテクチャを使ったマイクロサービスとか、昔だったらEC2の別サーバーでバッチ処理させるしかなかったのに、凄い（求: 語彙力）

#### Javascriptって便利じゃない？
昔のJavascriptには何度泣かされたことか。しかしいざ、Vue.js触ってみて思ったのが「これ以外の言語で非同期処理って書けるの？」とまで思わされてしまうほど、Javascriptが使える言語になっていたのに驚き。~~でも言語仕様はやっぱり好きじゃない~~

#### Pythonが主流言語に
これが地味に一番嬉しい。大学で時代を先取りしすぎた教授たちに「明日から課題はPythonで提出して」（当時はJavaで提出していた）と言われかれこれ10年以上Pythonを使っているのに、日本でPythonを使うのはスクリプト処理の時ぐらい。サーバーはRuby on Railsだったのも今は昔、若手のエンジニアから「RoR使ってるのは30代後半のおっさん」と言われ軽くショックを受けました。…ええそのせいもあって、弊社はDjangoでサーバー書いてます。

## 最後に
ここまでつらつらと書きました。エンジニアに戻ってまだ日も浅いのでまだまだわからないことが多いのですが、4年ぶりに戻ってきて思っていることは

1. エンジニアは雇うから、育てる職種に
エンジニアの待遇が良くなったという反面、優秀なエンジニアを採用するというのが本当に難しい時代になったと思います。反面、今までのような技術的なハードルは下がってきているので、きっちりと育てることのできるエンジニアをいかに採用し戦力としていくことが重要に思えます。

2. エンジニアマーケットのグローバル化
Pythonなどの言語が主流言語として台頭してくる中で、多くのドキュメントなどのリソースは英語で書かれており、海外には多くのコミュニティも存在します。そのため、日本語だけだと最先端の技術を取り込んでいくための情報量に限りがあります。10年ぐらい前からも言われていることですが、エンジニアとして今優先すべきは英語での語学力だと思っています。合わせて、採用も日本人だけでなく海外の技術者を取り込んでいくことが重要なファクターとなると考えています。

2をふまえて、今の会社でのエンジニア採用は「英語のみ可」に入社後に変更し、社内での英語の義務化を導入するか目下検討しています。1は会社のフェーズとして育てられる環境がないのでまだできていませんが、組織が安定してき次第取り掛かりたいと思っています。

ではでは
Atomのパッケージ【備忘録】

#概要
自分がAtomに入れているコミュニティパッケージのまとめ

#動機
自分がいつも使っている環境を別の環境で構築するのが面倒だから残しておこうと思ったから。設定ファイルをそのままやり取りする方法もあるのかもしれないけど、現時点で自分は調べていない。

#インストール済みのパッケージ
- atom-beautify
- autoclose-html
- autocomplete-paths
- file-icons
- highlight-selected
- japanese-menu
- minimap
- pigments
- trailing-spaces

#あとがき
今回は完全に自分のための備忘録なので、自分以外が見てもあんまり意味は無いかもしれない。今後それぞれのパッケージの説明とかを入れてもう少し自分以外の人の役に立つエントリーにしていくかもしれない。
cscの作法 その29 datagrid


# 概要

cscの作法、調べてみた。
練習問題やってみた。

# 練習問題

datagridをcsvに書き込め。

# サンプルコード


```
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Data.OleDb;
using System.Data;
using System.IO;
using System.Text;
using System.Collections.Generic;

class form1: Form {
	DataGridView gd0;
	form1() {
		Text = "DataGrid";
		ClientSize = new Size(580, 400);
		gd0 = new DataGridView();
		gd0.Location = new Point(50, 20);
		gd0.Width = 500;
    	gd0.Height = 300;
		Controls.AddRange(new Control[] {
			gd0
		});
		Button btn1 = new Button();
		btn1.Location = new Point(50, 330);
		btn1.Text = "load";
		btn1.Click += btn1_Click;
		Controls.AddRange(new Control[] {
			btn1
		});
		Button btn2 = new Button();
		btn2.Location = new Point(150, 330);
		btn2.Text = "save";
		btn2.Click += btn2_Click;
		Controls.AddRange(new Control[] {
			btn2
		});
	}
	void btn1_Click(object sender, System.EventArgs e) {
		Encoding enc = Encoding.GetEncoding("Shift_JIS");
		string file = "test1.csv";
		StreamReader reader = new StreamReader(file, enc);
		string temp = reader.ReadToEnd();
		string[] lines = temp.Split(new string[] {
			"\r\n"
		}, StringSplitOptions.None);
		string[] spLine;
		spLine = lines[0].Split(new char[] {
			',',
			'\t'
		}, StringSplitOptions.None);
		gd0.ColumnCount = spLine.Length;
		for (int i = 0; i < spLine.Length; i++)
		{
			gd0.Columns[i].HeaderText = spLine[i].Trim('"');
		}
		DataGridViewRow[] rows = new DataGridViewRow[lines.Length - 1];
		for (int i = 1; i < lines.Length; i++)
		{
			spLine = lines[i].Split(new char[] {
				',' ,
				'\t'
			}, StringSplitOptions.None);
			for (int j = 0; j < spLine.Length; j++)
			{
				spLine[j] = spLine[j].Trim('"');
			}
			DataGridViewRow row = new DataGridViewRow();
			row.CreateCells(gd0);
			row.SetValues(spLine);
			rows[i - 1] = row;
		}
		gd0.Rows.AddRange(rows);
		MessageBox.Show("ok");
	}
	void btn2_Click(object sender, System.EventArgs e) {
		StreamWriter sw = new StreamWriter("test2.csv", false);
		int rowCount = gd0.Rows.Count;
		if (gd0.AllowUserToAddRows == true)
		{
			rowCount = rowCount - 1;
		}
		for (int i = 0; i < rowCount; i++)
		{
			List<String> strList = new List<String>();
			for (int j = 0; j < gd0.Columns.Count; j++)
			{
				String s1 = (String) gd0[j, i].Value;
				String s2 = Convert.ToString(gd0[j, i].Value);
				strList.Add(s2);
			}
			String[] strArray = strList.ToArray();
			String strCsvData = String.Join(",", strArray);
			sw.WriteLine(strCsvData);
		}
		sw.Close();
		MessageBox.Show("ok");
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}






```


以上。
[備忘録]draggableで移動した要素を固定する

#問題だったこと、やりたいこと
付箋アプリを作成したときに、付箋をdraggableで実装した。

複数枚付箋を作成したときに、一番上の要素を削除したとき、
任意の位置に移動させた付箋が、削除した要素分上に移動してしまった。

![before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559813/d9d59293-2f50-277a-aab4-d0da7de6e8dd.gif)
移動した付箋は、移動先の場所で固定させたかった。

#修正したこと、ソース

移動を終了したときに、positionをabsoluteに変更して、topとleftを計算しなおした。
(最初からabsoluteを指定すると、ADDボタンを押したときに、付箋が重なって作成されたのでボツ)

```HTML:HTML
<div class="container sticky-container">                   
	<div class="row">                                      
		<input type="button" id="add-sticky-button"        
			class="btn btn-s btn-default" value="ADD">     
	</div>                                                 
	<div class="row">                                      
		<div id='sticky-container' class="col-sm-3 col-lg-2
			<div id="init-sticky-container"></div>         
		</div>                                             
	</div>                                                 
</div>                                                     
```

```CSS:CSS
#sticky-container {
	border-style: solid;
	min-height: 150px;
	max-height: 100%;
	position: relative;
}
```

JS　stopイベントでpositionをabsoluteに変えて、
topとleftを再計算する（何かほかに良い方法がありそうな気がする。。）

```JS:　draggableの部分だけ抜出し
$sticky.draggable({
	stop: function( event, ui ) {
		$(this).css('position', 'absolute');
		$(this).css('top', ui.offset.top - $('#sticky-container').offset().top);
		$(this).css('left', ui.offset.left - $('#sticky-container').offset().left);
	}
});
```

#結果
移動後の付箋は、場所固定になる
（移動前の付箋は、自動で詰まる）
![after.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/559813/cd5cb546-8ac3-ce5a-3f13-220376b92e9d.gif)

#前提
そもそもは↓の記事を参考に作成させていただきました。
https://qiita.com/iotas/items/fbf4994877e5c2053787
http://scrumblr.ca/demo
pipenv on WSLでwindowsのpythonが呼ばれてしまう問題

# 状況
少し前まで、WSL(ubuntu18.04LTS)上でpipenvを使っていて、
WSL上のpythonが呼ばれていて全く問題なかったのに、しばらくWSL使っていなかったら気づいたら表題の状況になっていた。

windows側のアップデートやらなんやらしていたので何が原因かはわからない。

# 方法
アプリと機能>アプリ実行エイリアスでpythonをオフにする
![apl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326886/15a36cec-78ef-cf12-8e66-841ec46064e5.png)

# 蛇足
pipenvを入れればいいとか、pyenvを入れればいいとか混乱したので、一回全部消して下記に落ち着いた。[^1]

## pyenv install
```

$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
```

## お好みのpython
```
$ pyenv install 3.8.1
$ python -V
Python 3.8.1
```
## pipenv install
```
$ pip list
Package    Version
---------- -------
pip        19.2.3
setuptools 41.2.0
WARNING: You are using pip version 19.2.3, however version 19.3.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
$ pip install --upgrade pip
$ pip install pipenv
$ pip list
Package          Version
---------------- ----------
certifi          2019.11.28
pip              19.3.1
pipenv           2018.11.26
setuptools       41.2.0
virtualenv       16.7.9
virtualenv-clone 0.5.3
``` 
# pipenvの環境作成
プロジェクトのディレクトリで`pipenv install`でほしいバージョンを指定すると
pyenvですでにインストールしていない場合、インストールするか聞いてくる。

```
$ cd your_project
$ pipenv install --python 3.7
Warning: Python 3.7 was not found on your system…
Would you like us to install CPython 3.7.6 with pyenv? [Y/n]:y
```
PipfileとPipfile.lockが生成されている。

```
$ pipenv shell
(your_project) $ python -V
Python 3.7.6
(your_project) $ pyenv versions
  system
  3.7.6
* 3.8.1 (set by PYENV_VERSION environment variable)
```
無事イケそうです。

# 参考
https://qiita.com/mashita1023/items/10239f5621ef2fc8acb9
https://qiita.com/foewhoew32f320/items/bfa90ae1003e45cefe33
https://github.com/pypa/pipenv/issues/3488
https://github.com/pyenv/pyenv/tree/4e0ba2f47d97acd1284439cff57af059ce376b9d#installation


[^1]: ちゃんと消えてるかはしりません
Windowsをグレースケールにしたい。

たまにWindows端末でもIphoneのカラーフィルタのように、
グレースケール(白黒)にしたいときがあります。
~~無い人はすっと戻るボタンを…~~

## Windowsをグレースケールにしたい。
下のコマンドのみでグレースケール表示にできます。
「Ctrl」+「Windowsロゴ」+「c」


## もし、できない場合

「Windowsロゴ」+「i」->「簡単操作」->「カラーフィルタ」
->「ショートカットキーを使用してフィルターのオンとオフを切り替える」
の左側にあるチェックをクリックする。

改めて、グレースケールのショートカットを試してみてください。
RustでTOMLをファイルに書き出す・structに読み込む

# 動機
設定ファイルを`setting.toml`のような形にして外部に吐き出しておいて，あとで読み込みたい．
RustでTOMLを扱うには[`toml-rs`](https://docs.rs/toml/0.5.5/toml/)を使う．本稿では併せてファイルの入出力を行う．

# 環境
```sh:環境
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15
BuildVersion:   19A603

```

# 最終結果

```sh:ファイル構成
$ tree
.
├── Cargo.lock
├── Cargo.toml
└── src
   └── main.rs

```

```toml:Cargo.toml
[package]
name = "toml-handle"
version = "0.1.0"
authors = ["Scstechr <scstechr@email.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde = { version = "1.0.104", features = ["derive"] }
toml = "0.5.5"

```

```rust:src/main.rs
extern crate serde;
extern crate toml;

use serde::{Deserialize, Serialize};
use std::fs::{self, File};
use std::io::Write;

#[derive(Debug, Serialize, Deserialize)]
struct Setting {
    os: String,
    version: f32,
    users: Vec<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Definition of struct instant
    let setting = Setting {
        os: "Mac OS X".into(),
        version: 10.15,
        users: vec!["John Doe".into(), "Alice Carter".into()],
    };
    println!("\n#Original:\n{:#?}", setting);

    // Write TOML to file
    let mut file = File::create("Setting.toml")?;
    let toml = toml::to_string(&setting).unwrap();
    write!(file, "{}", toml)?;
    file.flush()?;
    println!("\n#TOML:\n{}", toml);

    // Read file and parse to Setting
    let foo: String = fs::read_to_string("Setting.toml")?;
    let setting: Result<Setting, toml::de::Error> = toml::from_str(&foo);
    match setting {
        Ok(p) => println!("#Parsed TOML:\n{:#?}", p),
        Err(e) => panic!("Filed to parse TOML: {}", e),
    }
    Ok(())
}

```

```sh:出力
$ cargo run
   Compiling toml-handle v0.1.0 (/Users/Scstechr/Works/projects/toml-handle)
    Finished dev [unoptimized + debuginfo] target(s) in 1.54s
     Running `target/debug/toml-handle`

#Original:
Setting {
    os: "Mac OS X",
    version: 10.14,
    users: [
        "John Doe",
        "Alice Carter",
    ],
}

#TOML:
os = "Mac OS X"
version = 10.14
users = ["John Doe", "Alice Carter"]

#Parsed TOML:
Setting {
    os: "Mac OS X",
    version: 10.14,
    users: [
        "John Doe",
        "Alice Carter",
    ],
}

```

# おわり
## TODO
解説を追記する．

## 参考
- [Rustでファイルの入出力](https://qiita.com/fujitayy/items/12a80560a356607da6370)
- [RustでTOMLを読み込む](https://cipepser.hatenablog.com/entry/rust-toml)
- [toml - Rust](https://docs.rs/toml/)
IT技術系　新人研修講師　座談会2019 での話（３）

年も開けてしまいました。引き続き各テーブルで話していた内容をサマリーしてみます。
![P_20191223_195201_vHDR_Auto.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100559/afb062dc-a002-a8dd-4449-caa5876d392d.jpeg)

セルフ・コントロール型の研修を導入しているかの話から
下位層をどこまでフォローするか
（手取り足取りまですると実践で力を発揮できなくなってしまうが、かといって放置しても成長しない）

学生から社会人になって、
お金を貰って研修を受講する＝仕事
お金を払って学校へいく＝勉強
の意識の差が存在する。

成長のため、学習へのモチベーション維持のため、出来た＝＞ほめて＝＞メンタル＋　というループを作る。
逆のループもある、出来ない＝＞メンタル－

持ち上げられるのを嫌う新人もいる。（プライド高いのか？）

新人側がなにをされたら嬉しいのかは人それぞれ

新人のPC操作の基礎力が二極化していることも話題になった。
iPad や スマホ を使っているので、キータッチやマウス操作が遅い。
プログラムを学習する際に、致命的。

基礎力もプログラミングの学習も、苦労しなければ身につかない？
それとも苦労しなくても身につく方法はあるのか？
といった話もでていました。



私の考えとして、セルフ・コントロール型研修は、受講者に学習のスキルが身についている場合には有効で、最近の多くの新人は、自分にあった学習スタイルが確立できていない。
そのため、多くの企業では、セルフ・・コントロール型研修は、学習としては時間効率は悪くなると考えます。
研修目的に、学習方法も身につけるが含まれていいる場合は、セルフ・・コントロール型で研修を実施してもよいと思うが、研修初期は混沌とすることを覚悟し、混乱も学習の過程だと達観できるかが勝負でしょう。講師が達観できても、研修運営など関わっている人が短期目線で問題視する事も十分予想できる。研修関係者が目的に照らし合わせ状況を良いか悪いか判断することが大切です。
cscの作法 その28 datagrid


# 概要

cscの作法、調べてみた。
練習問題やってみた。

# 練習問題

datagridにcsvを読み込め。

# 写真

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5d9d2012-1179-b6d6-7e9f-35de3ac6ef64.png)


# サンプルコード


```
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Data.OleDb;
using System.Data;
using System.IO;
using System.Text;
using System.Collections.Generic;

class form1: Form {
	DataGridView gd0;
	form1() {
		Text = "DataGrid";
		ClientSize = new Size(580, 400);
		gd0 = new DataGridView();
		gd0.Location = new Point(50, 20);
		gd0.Width = 500;
    	gd0.Height = 300;
		Controls.AddRange(new Control[] {
			gd0
		});
		Button btn1 = new Button();
		btn1.Location = new Point(50, 330);
		btn1.Text = "test";
		btn1.Click += btn1_Click;
		Controls.AddRange(new Control[] {
			btn1
		});
	}
	void btn1_Click(object sender, System.EventArgs e) {
		Encoding enc = Encoding.GetEncoding("Shift_JIS");
		string file = "test1.csv";
		StreamReader reader = new StreamReader(file, enc);
		string temp = reader.ReadToEnd();
		string[] lines = temp.Split(new string[] {
			"\r\n"
		}, StringSplitOptions.None);
		string[] spLine;
		spLine = lines[0].Split(new char[] {
			',',
			'\t'
		}, StringSplitOptions.None);
		gd0.ColumnCount = spLine.Length;
		for (int i = 0; i < spLine.Length; i++)
		{
			gd0.Columns[i].HeaderText = spLine[i].Trim('"');
		}
		DataGridViewRow[] rows = new DataGridViewRow[lines.Length - 1];
		for (int i = 1; i < lines.Length; i++)
		{
			spLine = lines[i].Split(new char[] {
				',' ,
				'\t'
			}, StringSplitOptions.None);
			for (int j = 0; j < spLine.Length; j++)
			{
				spLine[j] = spLine[j].Trim('"');
			}
			DataGridViewRow row = new DataGridViewRow();
			row.CreateCells(gd0);
			row.SetValues(spLine);
			rows[i - 1] = row;
		}
		gd0.Rows.AddRange(rows);
		MessageBox.Show("ok");
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}






```


以上。
Githubのwebブラウザ上で過去世代との差分を見る方法

ブラウザで差分を取りたいリポジトリを開き
アドレスのリポジトリ名の末尾に　/compare (小文字)を打鍵する
こんな感じ　https://github.com/ユーザID/レポジトリ名/compare

下記の画面が表示されるので、赤線で囲った部分で比較したい世代を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/e7912466-bfd7-e84f-2c39-ffe38f65b692.png)

世代には、History 画面で表示されるハッシュ値を入力する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/dba1a176-0675-0beb-088d-a3ca40e9549a.png)
PostgreSQLで「昨年のデータ」と「現在のデータ」を並べて表示

### 扱うデータ
気温(℃)のデータが、下記のように30分間隔で格納されている

* meas_dt : 計測時刻
* temperature : 気温

```sql
SELECT meas_dt, temperature
FROM thermo_data
ORDER BY meas_dt DESC;

(出力結果)
       meas_dt       | temperature 
---------------------+-------------
 2020-01-03 17:30:00 |        9.70
 2020-01-03 17:00:00 |       10.51
 2020-01-03 16:30:00 |       11.07
 2020-01-03 16:00:00 |       11.59
 2020-01-03 15:30:00 |       11.52
 2020-01-03 15:00:00 |       11.40
 2020-01-03 14:30:00 |       11.50
...
```

### やりたいこと
「昨年(2019年)の同じ時刻の気温」を表す列を新たに追加して、同じ行に表示させたい。
以下がやりたいイメージ

```sql
SELECT meas_dt, temperature, (昨年の気温)
FROM thermo_data
ORDER BY meas_dt DESC;

(出力結果)
       meas_dt       | temperature | (昨年の気温)
---------------------+-------------+-------------
 2020-01-03 17:30:00 |        9.70 |     7.67
 2020-01-03 17:00:00 |       10.51 |     8.29
 2020-01-03 16:30:00 |       11.07 |     8.30
 2020-01-03 16:00:00 |       11.59 |     9.79
 2020-01-03 15:30:00 |       11.52 |     9.84
 2020-01-03 15:00:00 |       11.40 |    10.70
 2020-01-03 14:30:00 |       11.50 |    11.00
...
```

### 相関サブクエリを使って解決
以下のSQL文を作ると、昨年の気温を並べて出力可能！

```sql
SELECT meas_dt, temperature,
       -- 相関サブクエリ。テーブルの別名を「T2」とする
       (SELECT temperature
        FROM thermo_data AS T2
        -- 「昨年の気温」を出力を実現するための制約条件
        WHERE T2.meas_dt = T1.meas_dt + interval '-1 year'
       ) AS temp_lastyear
FROM thermo_data AS T1 -- メインクエリのテーブルの別名は「T1」とする
ORDER BY meas_dt DESC;

(出力結果)
       meas_dt       | temperature | temp_lastyear
---------------------+-------------+--------------
 2020-01-03 17:30:00 |        9.70 |      7.67
 2020-01-03 17:00:00 |       10.51 |      8.29
 2020-01-03 16:30:00 |       11.07 |      8.30
 2020-01-03 16:00:00 |       11.59 |      9.79
 2020-01-03 15:30:00 |       11.52 |      9.84
 2020-01-03 15:00:00 |       11.40 |     10.70
 2020-01-03 14:30:00 |       11.50 |     11.00
...
```
【Rails / MySQL】データ型をintegerからfloatへ変更しnumber_fieldで小数点を扱えるようにする

前回の記事の続きで初めてfloat型を扱ったのでメモ

## やりたい事

- DBに間違ってinteger型で作ったカラムをfloat型に変更
- form_for内でnumber_fieldで小数点を扱えるようにする
- DBに保存した値を合計するメソッドを作る

## 参考

>
【Rails】form_for の number_field で小数を入力できるようにする
https://qiita.com/tegnike/items/07f789eb22c7a7bf6a19
>
マイグレーションを使ったカラムの追加、削除、データ型の変更　[ 自分用メモ ]
https://qiita.com/dawn_628/items/13fa64dc6d600e921ce3

>
【Rails】カラムの合計値を求める！
https://qiita.com/tomokichi_ruby/items/8758a91566957cfc5429

## number_fieldでは整数のみしか扱えない

```erb:new.html.erb
<%= form_for @count_time do |f| %>
 <%= f.number_field :count_hour, class:'input_form' %>
 <%= f.submit '登録', class:'input_submit' %>
<% end %>
```
![スクリーンショット 2020-01-03 17.33.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428016/d5980dfb-f650-6476-61cb-1da60a859b22.png)

というフォームがあって,

小数点の数字をいれると,
![スクリーンショット 2020-01-03 17.35.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428016/e5684731-c1fd-cb59-2dc8-bc5421b2a6c5.png)
というふうになるので

```erb:new.html.erb
<%= form_for @count_time do |f| %>
 <%= f.number_field :count_hour, step: '0.1', class:'input_form' %>
<%# step: '0.1'を追加 %>
 <%= f.submit '登録', class:'input_submit' %>
<% end %>
```
`step: '0.1'`を追加すると小数点以下第一位までの数値を扱えるようになります

## このままだとDBには整数でしか保存されない

DBの `count_hour`は`integer型`なので整数のみしか保存してくれないのでカラムの型を変更する

```terminal:ターミナル
$ rails g migration change_data_count_hour_to_count_time
Running via Spring preloader in process 94544
      invoke  active_record
      create    db/migrate/20200103074431_change_data_count_hour_to_count_time.rb
```

カラム名がcount_hourでテーブル名がcount_timeになります

```ruby:20200103074431_change_data_count_hour_to_count_time.rb
class ChangeDataCountHourToCountTime < ActiveRecord::Migration[5.2]
  def change
    change_column :count_times, :count_hour, :float　#追記。テーブル名は複数形で
  end
end
```

`rails db:migrate`します

これでfloat型に変更できたのでnumber_fieldから送られる小数点の値がDBへ保存できるようになりました

## DBの値をビューへ描画

```ruby:index.html.erb
  <table>
    <tr>
      <th>日付</th>
      <th>時間</th>
    </tr>
    <% @count_time.each do |count_time| %>
      <tr>
        <td><%= count_time.created_at.to_date %></td>
        <td><%= count_time.count_hour %>時間</td>
      </tr>
    <% end %>
  </table>
```
![スクリーンショット 2020-01-03 17.50.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428016/99cd0e51-af83-6b18-90be-1a36407e1abe.png)

## sumメソッドを使い学習時間の合計を表示させる

```ruby:count_time_controller.rb
class CountTimesController < ApplicationController
 def index
  @count_time = CountTime.where(user_id: current_user.id).order('updated_at DESC').limit(5)
  @count_hour = @count_time.sum(:count_hour)
 end
end
```
sumメソッドの引数にカラムを指定するで合計値を出します

## 完成

![スクリーンショット 2020-01-03 18.04.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428016/1c3de7e6-7a6a-8bbc-cbb5-0ebec8cfb1c4.png)

## まとめ
ひとまずこれで自分が最初に想像していた主要機能が完成しました。開発にそんな時間がかからないアプリでしたが知らない事も学べたので良かったかなと。あとは思い付いた追加の機能を開発していこうと思います。

#### 終わり
アラフィフですがGoogle Professional Data Engineerに合格しました 

<a href="http://yayoi-taka.hatenablog.com/entry/2019/12/25/203607?_ga=2.130186063.1355225770.1578041441-475247681.1399178843">アラフィフですがGoogle Professional Data Engineerに合格しました - Idea, COncept, and Technology.</a>からの転載です。
##プロフィール

- 四捨五入すると50歳
- 45歳でコンサルティング会社に転職。前職はメーカー。
- いまだにコーディングしてます。データ分析もやってます。
- 趣味でiPhoneアプリ／Webサービス作ってます。
- AWS 認定ソリューションアーキテクト – アソシエイト取得(2019)

##受験のきっかけ
- AWS SAAを取った勢いもあり、次はGCPだろうということで。
- データ分析をしていることもあってData Engineerを狙いました。

##準備その1　本を読む
AWSと比べてGCPは書籍が少ないですがまずは本を読むところから入るのがいいと思います。ただ、後述の公式ドキュメントも読み込む必要があります。

- [Google Cloud Platform エンタープライズ設計ガイド](https://www.amazon.co.jp/dp/B07C85YMCW)
こちらは全体像を理解する目的で
- [スケーラブルデータサイエンス データエンジニアのための実践Google Cloud Platform](https://www.amazon.co.jp/dp/B07R39RLSQ)
対Professional Data Engineer試験対策という意味では必読。Pub/Sub、Dataflow、BigQuery、Dataprocなどのハンズオンが豊富です。
- [Google Cloud Platformではじめる機械学習と深層学習](https://www.amazon.co.jp/dp/B07F6YR7XT)

##準備その2　ハンズオン
お金はかかりますが手を動かさないことには身に付かないかと。私はQwiklabsのクエストをひたすら繰り返しました。でも、ハンズオンだけでは合格は難しいかと思います。

- [Data Engineering | Qwiklabs](https://www.qwiklabs.com/quests/25)
まさに試験対策用ハンズオン
- [Data Science on Google Cloud Platform: Machine Learning | Qwiklabs]
 (https://www.qwiklabs.com/quests/50)
上で紹介した「スケーラブルデータサイエンス データエンジニアのための実践Google Cloud Platform」のハンズオン。本の説明に沿ってやると結構つまづくのでこちらに沿ってやるのが吉。

##準備その3　公式ドキュメントを読み込む
AWSと比べて、書籍が少ないので頼りになるのは公式ドキュメントです。リンク先にあるサービス、特に「コンセプト」を理解する必要があると思います。BigQuery、Cloud Composer、Bigtable、Dataflow、Dataproc、Pub/Subあたりは一通り理解しておく必要があります。

[ビッグデータ分析プロダクト  |  Data Analytics Products  |  Google Cloud](https://cloud.google.com/products/big-data/?hl=ja)

試験範囲に関しては、こちらの記事も参考になりました。Bigtableは行インデックスの設計、BigQueryは一通りの使い方＋ワイルドカードクエリ、セキュリティ、外部データ連携など、Dataflowはウィンドウ関数や入出力の考え方など。

[Priocept » Google Cloud Certified Data Engineer – Beta Exam Report](http://priocept.com/2017/01/23/google-cloud-certified-data-engineer-beta-exam-report/)

##いざ試験会場へ

持っていくのは申し込み後に届くメールと身分証明書（免許証＋クレジットカードなど）だけです。

##試験を受けて

- 最近はオンライン試験がメジャーなんですね（おっさん感
- 持ち時間は2時間ですが、1時間で一通り流して不安なところを見直すのに30分といった感じでした。
- 提出ボタンを押して「合格」の文字が出たときはやはり嬉しいです。
- 数日後に正式に合格メールが届きました。
- 資格を取るのが目的では無いので、これを活かして色々チャレンジしていきたいと思います。

![証明書](https://api.accredible.com/v1/frontend/credential_website_embed_image/certificate/14395116)
[Go]ポインタをゼロから理解する

# 対象読者
Go言語の文法を学んでる段階の方から、*とか＆を使うケースは覚えたけど何となく使ってしまってる方まで（ポインタ渡しについては触れてません）

# はじめに
ポインタ変数を理解するに当たって、そもそも変数が定義された時どのようなことが行われているのかを理解する必要があります。そのために必要なメモリというものを冒頭で説明したいと思います。

#メモリとは
PCのCPUが処理を行う際に読み書きを行う値（データ）を格納する場所のことです。メモリの最小単位は１バイトであり、１バイトごとにアドレスと呼ばれる識別番号が16進数で割振られています。

ここからはイメージを掴むために画像を入れながら説明します。16進数の適当なアドレスを用いて、メモリを表すとこのような形になります。

<img width="587" alt="メモリとアドレス.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408781/df84a419-6ee0-9b0b-4b5b-d2e36228adc6.png">

# 変数定義の流れ

では実際に変数が定義された際の流れを見ていきましょう。
Int型のaという変数を定義したとします。このaという変数が定義された時メモリは以下のようになります。

<img width="686" alt="変数格納.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408781/1f32f7d7-e84f-feb8-f8e3-f62abcaae7f6.png">

このように変数の型やサイズに合わせてメモリの中のアドレスが確保されるのです。

**＊** アドレスと変数のイメージを掴むことが目的なので、変数に対応した厳密な16進数ではありませんがご理解ください。

# ポインタとは
ここまで変数定義された時の一連の流れを見てきました。
では本題のポインタの説明をします。

ポインタを端的に言うと、「その変数の型情報や大きさを指し示したもの」です。
より具体的には、その**変数が格納されてるメモリ領域の先頭アドレス**を指します。この先頭アドレスに変数の型に関する情報や領域（大きさ）に関する情報が入っているのです。

<img width="686" alt="ポインタ.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408781/3c2539ac-9e26-37e3-f4ff-ff63bd3a06a6.png">

この図において変数aのポインタは75bd5b9ということになり、ここには型や領域に関する情報が入っています。

# Goでの実装例

ではこれらのポインタをgoにおいてどのように扱うか見ていきましょう。

## 変数からポインタを出力する

簡単な実装例として、変数のポインタを出力してみます

#### ①変数から直接取得する場合

```main.go
package main

import "fmt"

func main() {
	var a int
	fmt.Println(&a)

	// 出力結果
	// 0xc000094000
}
```
これは＆で（アンパサド演算子）変数のポインタを出力したものです。

####②ポインタ型を指定して参照する場合

ポインタ型変数には変数のアドレス値が入ります。
よって、先ほどの変数aのアドレス値をポインタ型変数bに代入すれ変数のポインタを出力できます

```main.go
package main

import "fmt"

func main() {
	var a int
	var b *int
	b = &a
	fmt.Println(b)

	// 出力結果
	// 0xc000094000
}
```


こちらは予め定義したポインタ型変数に①で定義した変数aのアドレスを代入したものになります。出力方法は違いますが、ここでの値は等しくなります。

ここで使った特殊記号は以下の2つです。


```main.go
* 
ポインタ型変数の定義
（例　var a *int 
     var b *string  

＆　
変数のアドレスを取得
``` 

#### ③ポインタから変数の値を取得する場合
ポインタ型の指定と同じなので紛らわしいですが、以下のようにします。

```main.go
package main

import "fmt"

func main() {
	var a int
	var b *int
	b = &a
	c := *b
	fmt.Println(c)

	// 出力結果
	// 0
}
```
出力した変数cには何も値が入っていないので0が出力されました。

goの文法を理解する上で避けては通れないポインタですが、基本概念から落とし込み手を動かすことで徐々に慣れていきましょう
Python,Ruby(及び型が明示されていない言語）が糞だと思う点

ソースリーディングをしていて以下のようなコードに出くわした時

```python
class BaseRequestHandler:

    """Base class for request handler classes.
    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.
    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.
    """

    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()
```

request, client_address, serverってなんやねんってなる。
このコードを書いた人はある特定の型のオブジェクトがここに突っ込まれることを想定していると思う。でも型が明示されていない為それが何なのか分からない。呼び出す側を探してそれが何なのか確認しなきゃならない。
コードを書いている人の脳内にある型の情報がソースコード上では省略されているからこんなことになる。
Web系エンジニア1年目にやったこと

‪とりあえずメモ。
(実際業務としてエンジニアしていた期間は半年くらい。)

‪- Udemy JavaScriptの文法のやつ‬
‪- Railsチュートリアル‬
‪- Everyday Rails‬
‪- React公式チュートリアル‬
‪- りあクト！‬
‪- Vue猫本‬
‪- AtCoder 150問くらい‬
‪- Paiza 50問くらい‬
‪- progate HTML・CSS(初級〜上級)‬
‪- Udemy React The Complete Guide 50%‬
‪- 業務で必要なAPIのドキュメント読んだり
- Qiita読んだり書いたり
- 利用するライブラリのGithubのREADME読んだり

## 今年の目標
‪・AtCoder緑‬
‪・モダンポートフォリオ作成‬
・要件定義や基本設計等、上流工程から業務を行えるようになる
‪・副業はじめる‬
CentOS8 - Play - Bootstrap

# Bootstrapの学習と活用
- 未経験者には以下の手順がおすすめ（あくまで個人的意見）
    1. Bootstrapの概要を知る
    2. Bootstrapのテンプレートに触れる
    3. 自分でBootstrapを使ってデザインする 

## Bootstrapの概要を知る

### Bootstrapとは
- 最も有名なWebフレームワークの一つ
- HTML / CSS / JavaScriptから構成されている

### 始めるための前提条件
- HTML
- CSS
- JavaScript（知らなくても問題ない）

### Bootstrapの概要
- グリッドシステム
    - 参考サイトを読んで"グリッドシステム"について理解するだけで十分！
    - グリッドシステムの"イメージ"とタグの使い方を理解できればそれでよい
    - 画像を使って紹介されていると親切なので、理解しやすいサイトを探すのがコツ

## Bootstrapのテンプレートに触れる
### テンプレートを触る理由
- Bootstrap自体は難しいものではないため、アレンジが容易
- そのため、Bootstrapを用いたテンプレートが数多く存在する
    - ほとんどが無料
    - サンプルコンテンツが完成された状態で提供（HTML / CSS）
    - サンプルのテキストを書き換えるだけでwebページが完成する
- 提供されたHTMLファイルに手を加えることが学習に直結する
    - 必要のないページやコンテンツの削除
    - デザインの改修、など
- 知識が乏しいまま自力で模索するよりも遥かに学習効率がよい
    - 但し、テキストの書き換えだけで満足してしまうと学習にならないことに注意

### テンプレート例
- [Start Bootstrap](https://startbootstrap.com/)

## 自分でBootstrapを使ってデザインする

### テンプレートに満足したら
- テンプレートに満足したら、ピュアなBootstrapを使って自力でサイト作りをすればよい
- ただ、趣味・アマチュアレベルであれば本当にそこまで必要か？とも思う（あくまで個人的意見）