OmniAICreator/Qiita-1.07M huggingface dataset:
Python数学シリーズ① 転置

## 経緯
[深層学習の本](https://www.amazon.co.jp/%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92-Ian-Goodfellow/dp/4048930621)を読み進めていこうと思っているのですが、ひたすら文字を読んでると眠くなってしまうので、出てきた項目をpythonで実装しながら理解を深めていこうと思います。需要があるかはわかりませんが、シリーズ化していこうと思います(笑)。今回は転置です。最近、c++とcばかりでpythonを忘れてきているので、復習も兼ねてpythonで実装していこうと思います。
**追記: 一応シリーズ化してみました(笑)。目次は[こちら](https://qiita.com/Hiroaki-K4/items/90b2f86558ef825e5d21)。**

## 転置
本の定義(24ページ)では、左上の角から始まって右下までの主対角線と呼ばれる行列の対角線に対する鏡像であると言っています。詳しくは[こちら](https://risalc.info/src/transpose-matrix-properties.html)をどうぞ。

### 実装
簡単な方法はnumpyを使った方法があります。[こちら](https://note.nkmk.me/python-list-transpose/)を参考にしました。

```main.py
import numpy

def main():
    l_2d = [[0, 1, 2], [3, 4, 5]]
    arr_t = np.array(l_2d).T.tolist()
    print(l_2d)
    # [[0 3]
    #  [1 4]
    #  [2 5]]
```
**追記:** @shiracamusさんからコメント頂いた、他の転置の方法はこちら

```python3
>>> l_2d = [[0, 1, 2], [3, 4, 5]]
>>> [*zip(*l_2d)]
[(0, 3), (1, 4), (2, 5)]
>>> list(map(list, zip(*l_2d)))
[[0, 3], [1, 4], [2, 5]]
>>> [[*row] for row in zip(*l_2d)]
[[0, 3], [1, 4], [2, 5]]
>>> [list(row) for row in zip(*l_2d)]
[[0, 3], [1, 4], [2, 5]]
```
上記のような方法を使えば瞬殺ですが、自作してみます。考え方としては、まず配列に入っている各配列の要素(上の例だと0と3)を１つの配列にして、新しい2次元配列の最初の要素として追加します。これを1と4、2と5でも同じことをやるようなイメージで今回はやってみました。さっき思いついたまま実装しただけなので、もっと良い方法があれば教えてください。実装はこんな感じです。

```main.py
def transpose(arr):
	col = len(arr[0]) # この場合は3
	row = len(arr)  # この場合は2
	ans_list = []
	i = 0
	while i < col:
		tmp_list = []
		j = 0
		while j < row:
			tmp_list.append(arr[j][i])
			j += 1
		ans_list.append(tmp_list)
		i += 1
	return ans_list

def main():
	l_2d = [[0, 1, 2], [3, 4, 5]]
	print(transpose(l_2d))
    # [[0 3]
    #  [1 4]
    #  [2 5]]
```
**追記:** @shiracamusさんからコメント頂いた、for文でのやり方はこちら

```python3
def transpose(arr):
    col = len(arr[0]) # この場合は3
    row = len(arr)  # この場合は2
    rows = []
    for c in range(col):
        cols = []
        for r in range(row):
            cols.append(arr[r][c])
        rows.append(cols)
    return rows
```
## まとめ
こんな感じでゆる~く深層学習の本を読み進めていこうと思います。

**間違いや質問、ご意見等ありましたらお気軽にコメントください。頑張って答えますので(笑)。**
【Python】ライブラリのインポート方法

# Pythonのモジュールとは?
一度作成した関数やクラスを，再利用するためにファイルとして分割しておいたもの。
個々の`.py`ファイルを__モジュール__または__ライブラリ__と呼ぶ。
Pythonでは，ファイル名から拡張子を除いた部分をモジュール名としている。
各`.py`ファイルをインポートすることで，モジュール内の関数やクラスを使用することが可能となる。

## モジュールのインポート方法(基本)
一番基本となるインポートの仕方はこう。

* モジュールとしたいソースファイル

```test.py
class AAA:
  def __init__(self, a):
    self.a = a
  def get_a(self):
    return self.a
  def add_a(self):
    self.a += 1
```

* インポートする側のソースファイル

```main.py
# import "モジュール名"
import test

# インポートしたモジュールの関数(メソッド)やクラスを使用する場合
A = test.AAA(1) # モジュール名.クラス名or関数名
A.add_a() # インスタンス.メソッド名
b = A.get_a # b=2
```

## インポート時に名称を付ける
名前をつけてあげることで，長いモジュール名などを省略できちゃう。
例）`import pandas as pd`
　　`import numpy as np`

* インポートする側のソースファイル

```main.py
# import "モジュール名" as "付けたい名前"
import test as t

A = t.AAA(1)  # 付けた名前.クラス名or関数名
A.add_a() # インスタンス.メソッド名
b = A.get_a # b=2
```

## 直接インポートしてしまう
モジュール名の表記を省略出来るため，頻繁に使用するクラスとかで行う。
例）`from keras.layers import Conv2D`
　　`from PIL import Image, ImageDraw`

* インポートする側のソースファイル

```main.py
# from "モジュール名" import "クラス名or関数名"
from test import AAA

A = AAA(1)  # クラス名or関数名
```

## 全部インポート
モジュールから大量にインポートする場合に楽チン。モジュール内の全ての関数やクラスがインポートされる。
例）`from PIL import *`

* インポートする側のソースファイル

```main.py
# from "モジュール名" import *
from test import *

A = AAA(1)  # クラス名or関数名
```

## パッケージからインポートする
複数のモジュールが格納されているディレクトリのことを__パッケージ__と呼称する。

* インポートする側のソースファイル

```main.py
# ~~~ディレクトリ構成~~~
# カレントディレクトリ-src-test.py
#                   └main.py

# import "パッケージ名.モジュール名"
import src.test
A = src.test.AAA(1)  # パッケージ名.モジュール名.クラス名or関数名

# or

# from "パッケージ名" import "モジュール名"
from src import test
A = test.AAA(1)  # モジュール名.クラス名or関数名
```

## Pythonの標準ライブラリ
よく使いそうなやつ

| モジュール名     | 概要 |
|:----------------|:----|
|  os             | OS関連 |
|  os.path        | パス関連 |
|  sys            | Python実行環境 |
|  io             | 入出力 |
|  datetime       | 日時 |
|  calendar       | カレンダー |
|  time           | 時間 |
|  re             | 正規表現 |
|  math           | 数学 |
|  random         | 乱数 |
|  statistics     | 統計 |
|  json           | json |
|  csv            | csv |
|  sqlite3        | SQLite |
|  zipfile        | zipファイル |
|  tarfile        | tarファイル |
|  html           | HTML |
|  html.paser     | HTML解析 |
|  http           | HTTP |
|  urllib         | URL |
|  urllib.request | URLリクエスト |
|  socket         | ソケット |
Mavenでたまに役立つ小ネタ

[同じ内容をZennにも投稿しました。](https://zenn.dev/backpaper0/articles/maven-useful-tips)

---

実際に作業しているときにちょくちょく役立つ小ネタです。

## ビルド時の小ネタ

ビルド時に`-DskipTests`でテストの実行をスキップすることは皆さんよくやると思います（？）。

同じように`-Dmaven.test.skip`でもテストの実行をスキップできますが、こちらはテストコードのコンパイルもスキップするのでテストコードがコンパイルエラーになっていても大丈夫です（？？）。

更に`mvn package -Dmaven.test.skip`を実行しても`test`スコープの依存関係が壊れている場合（依存解決できない場合。Mavenセントラル以外のリポジトリを参照しているプロジェクトなんかでまれによくある）はビルドエラーになってしまいます。
そういった場合は`mvn package`せず、`mvn compile jar:jar`（`packaging`が`war`の場合は`jar:jar`ではなく`war:war`）すると`test`スコープの依存関係を解決せずにビルドを行うので大丈夫です（？？？）。

## ビルド失敗時の調査まわりの小ネタ

`-e`オプションを付けるとビルド失敗時にスタックトレースが出力されます。
経験的にMavenはスタックトレースを見てもすぐには原因特定できないこともしばしばですが、無いより全然良いので基本的には`-e`オプションを付けておくとよいでしょう。

それから`-X`オプションを付けるとデバッグログが出力されます。
大量のログが出力されてコンソールを遡るのが大変なので、`-l`オプションを併用してログファイルへ書き出すのがオススメです。

こんな感じです。

```
mvn -X -l maven.log test
```

調査の過程でMavenのコードを読みたくなった場合は[https://github.com/apache/maven](https://github.com/apache/maven)から取得すると良いでしょう。

## デプロイ時の認証まわりの小ネタ

JARやWARを[Nexus](https://hub.docker.com/r/sonatype/nexus3)のようなMavenリポジトリへデプロイする際、Mavenは`settings.xml`からデプロイ先となるサーバーの認証情報を読み取ります。

CI/CDツールでデプロイする場合、次のような`settings.xml`をソースコードと同じ場所に置き、`-s`オプションで指定する(コマンド例 `mvn -s settings.xml deploy`)方法があります。

```xml
<settings>
    <servers>
        <server>
            <id>nexus</id>
            <username>deployuser</username>
            <password>deploypassword</password>
        </server>
    </profiles>
</settings>
```

なお、パスワードをハードコーディングしたくない場合は`password`要素を次のようにすることで環境変数から読み取ることも可能です。

```xml
<password>${env.DEPLOY_PASSWORD}</password>
```

この場合、環境変数`DEPLOY_PASSWORD`からパスワードを読み取ります。
CI/CDツールはビルド時に環境変数へ秘密情報を設定するものがありますが、そのような環境で使える方法です。

また、ローカルの`settings.xml`でもパスワードをハードコーディングしたくない場合、暗号化することが可能です。
詳しくはMaven公式ドキュメントの[Password Encryption](https://maven.apache.org/guides/mini/guide-encryption.html)のセクションを参照してください。

## ヘルプまわりの小ネタ

`mvn --help`で`mvn`コマンドのヘルプが見られますが、`mvn <プラグイン>:help`とすることでプラグインのゴールを一覧できます。
例えば`mvn compiler:help`を実行すると次のような出力を得られます。

```
[INFO] Apache Maven Compiler Plugin 3.8.0
  The Compiler Plugin is used to compile the sources of your project.

This plugin has 3 goals:

compiler:compile
  Compiles application sources

compiler:help
  Display help information on maven-compiler-plugin.
  Call mvn compiler:help -Ddetail=true -Dgoal=<goal-name> to display parameter
  details.

compiler:testCompile
  Compiles application test sources.
```

また`compiler:help`のところに書かれていますが、更に特定のゴールに関する詳細を見ることも可能です。
ゴールの詳細ではパラメーターの説明が見られます。

プラグインの設定を書くときに便利です……プラグインをググって公式ドキュメントへ辿り着くのとどちらが早くて便利かはわかりませんが！

あと[maven-help-plugin](https://maven.apache.org/plugins/maven-help-plugin/index.html)も役立つときがあるので、存在を頭の片隅に置いておくことをオススメします（力尽きたので説明は割愛します、、、）。

## おわりに

実際に手を動かしているときはもっといろいろやっているような気もしますが、リラックスしているときに思い出せる小ネタはとりあえずこんなところです。

以上です。
[Rails]日本語対応のためのGem 'rails-i18n'の使い方

##目的

Gem 'rails-i18n'を使って、日本語対応させること。
このGemを導入するときに詰まった部分をメモとして残す。

##開発環境
・Ruby 2.6.4
・Rails 5.2.3

##参考資料
[Rails 国際化 (i18n) API](https://railsguides.jp/i18n.html)

[Railsのi18nによる日本語化対応](https://qiita.com/shimadama/items/7e5c3d75c9a9f51abdd5)

[I18n::InvalidLocaleData ロケールファイルを翻訳できない]
(https://qiita.com/niwa1903/items/10154b8cbe1cd0416495)

##導入手順
###①gem 'rails-i18n'をインストールする。

```Gemfile.
gem 'rails-i18n', '~> 5.1'
```
```
$ bundle install
```
RailsのGemをインストールするときの、いつもの流れです。

###②デフォルトの言語を日本語に設定する。そしてi18nの複数ロケールファイルが読み込まれるようpathを通す。

```config/application.rb
module App
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2

    # 2行追記:デフォルトのlocaleを日本語(:ja)にする.
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end
```
モジュールの中に2行追記してください。この記述がないと、Gemをインストールしても日本語対応してくれないようです。

###③localesフォルダの中にja.ymlファイルを作る
####・modelの日本語対応

```config/locales/activerecord/ja.yml
ja:
    activerecord:
     models:
       user: 'ユーザー'
       board: '掲示板'
     attributes:
       user:
         email: 'メールアドレス'
         password: 'パスワード'
         password_confirmation: 'パスワード確認'
         last_name: '姓'
         first_name: '名'
```

model全体に対応させるときはmodelsの階層を作り、
カラム名を対応させるときはattributesの階層を作ります。

####・view,controllerの日本語対応

```config/locales/view/ja.yml
ja:
   users:
     new:
       title: 'ユーザー登録'
       to_login_page: 'ログインページへ'
     create:
      success: 'ユーザー登録が完了しました'
      fail: 'ユーザー登録に失敗しました'
```
view/ja.ymlに記述することで、controllerにも適応されます。
ただ、インデントをきっちり２つずつにして階層構造を作らないと、日本語適用されないよう。


ja.ymlの書き方については、以下のRailsガイドの記事が参考になります。
[探索の「遅延」（lazy lookup）](https://railsguides.jp/i18n.html#%E6%8E%A2%E7%B4%A2%E3%81%AE%E3%80%8C%E9%81%85%E5%BB%B6%E3%80%8D%EF%BC%88lazy-lookup%EF%BC%89)


###④viewやcontrollerで、設定した日本語を表示させる

```
ja:
  books:
    index:
      title: "ほげほげ"
```

上記のような階層構造になっていれば、以下のようにして、app/views/books/index.html.erbビューテンプレート内部でbooks.index.title値にアクセスできます。

```
<%= t '.title' %>  または  <%= t ('.title') %>
```

省略せずに書くと以下になります。

```
<%= t 'books.index.title' %>
```


####これでGem 'rails-i18n'を使って、日本語対応できるはずです！
ROS Noetic のインストール ubuntu20.04

これまではmelodicを使用していたが、PCの新調に伴いubuntu20.04を入れ、ros-noeticの環境構築


#ソースリストをセットアップ

```
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
```
#キーを設定
```
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

```

#Debianパッケージインデックスが最新であるか
```

sudo apt update
```

#パッケージインストール
```
sudo apt install ros-noetic-desktop-full
sudo apt-get install python3-rosinstall
```
10分以上かかる。。。


早速ワークスペースを作成する。。。が

```
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/src
catkin_make

コマンド 'catkin_make' が見つかりません。次の方法でインストールできます:

sudo apt install catkin

```


？？？一応、指示通りに



```
sudo apt install catkin

パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
インストールすることができないパッケージがありました。おそらく、あり得
ない状況を要求したか、(不安定版ディストリビューションを使用しているの
であれば) 必要なパッケージがまだ作成されていなかったり Incoming から移
動されていないことが考えられます。
以下の情報がこの問題を解決するために役立つかもしれません:

以下のパッケージには満たせない依存関係があります:
 catkin : 依存: python3-catkin-pkg (>= 0.4.14-2) しかし、インストールされようとしていません
E: 問題を解決することができません。壊れた変更禁止パッケージがあります。


```


一度端末を消して、再起動


```
cd ~/catkin_ws
catkin_make

```

ビルド完了！まだ何も入れていないが　/build /develが出来ているのを確認！
AmplifyでREST API作成する時、ファンクション名にハイフンを付けない

※すでにこの内容は [@jagaimogmog](https://twitter.com/jagaimogmog) 氏によりAmplifyチームに要望いただきました。感謝！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://t.co/xKGm0cD7oA">https://t.co/xKGm0cD7oA</a><br><br>そもそもfunction nameにハイフン使いたい！という要望もあると思うので、そちらは開発チームも認識しているようです<br><br>とりあえずは $amplify add function 時にエラーを吐いてほしいとコメント</p>&mdash; Jaga@AWS Amplify (@jagaimogmog) <a href="https://twitter.com/jagaimogmog/status/1339786657516568577?ref_src=twsrc%5Etfw">December 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


## 結構ハマったので書いておきます。

AmplifyでREST APIを作成時にファンクション名を指定しますが、ここでハイフンを含むファンクション名を使うと、CloufFormationがエラーとなり、AWS上にサービスが作成されません。

Errorと１行だけでてしれっと終わる場合もありますし、私の自宅のmacでは

![amplify-not-end.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59457/68e06b35-feea-fc46-b035-17b78dcb01bc.jpeg)

のようになり、Fetchingで延々止まっています。

##原因がわからず右往左往

Amplifyに限らず、OSSを利用する際には様々な記事やチュートリアル、公式ドキュメントなど様々な情報を統合して使用している方が多いかと思います。
私もその一人です。

理解が深まる前段階でこういう状態に出くわすと、どこに原因があるのか正直なところあたりが付けられずに右往左往してしまいます。

##焦らずAmplify remove function(or remove api)

一旦削除して、再度再作成しましょう。
メール送信サーバ負荷試験の為、受信サーバのI/Oを0にした話

メール送信サーバの負荷試験を行うに辺り、受信用メールサーバでのメール処理がボトルネックになるとメール送信サーバ側でメールが滞留してしまい性能を正しく測ることができません。

受信用のメールサーバを大量に用意して分散する前に、まずは単体のメールサーバのパフォーマンスを最大限まであげ、解決しない場合に初めて増設を検討します。

負荷テストにおいて受信用メールサーバで注意したいこととして、
大量にメール受信した場合に、I/Oがボトルネックにならないようにする必要がありますので今回はこの問題を解決し、その他Linux Kernelやpostfixのチューニングに関しては割愛致します。

今回受信用メールサーバの構成は以下になります。

```
OS: CentOS 8.3
Middleware: postfix-3.3.1
```

# 1. postfix設定
まずはPostfixのメールスプール処理でI/Oが発生しないように/var/spool/postfixをtmpfsに設置し、メモリ上で処理します。

## 1-1. tmpfsを/mnt/spoolにマウント
```
# mkdir /mnt/spool
# mount -t tmpfs -o size=256M tmpfs /mnt/spool
```

## 1-2. postfixのメールスプール保存先にtmpfsを指定。
```text:/etc/postfix/main.cf
--- /etc/postfix/main.cf.orig   2020-12-19 16:18:47.264686621 +0900
+++ /etc/postfix/main.cf        2020-12-19 16:19:34.745687975 +0900
@@ -28,7 +28,8 @@
 # See the files in examples/chroot-setup for setting up Postfix chroot
 # environments on different UNIX systems.
 #
-queue_directory = /var/spool/postfix
+#queue_directory = /var/spool/postfix
+queue_directory = /mnt/spool/postfix

 # The command_directory parameter specifies the location of all
 # postXXX commands.
```

## 1-3. postfixのPIDファイル保存先を変更します
```text:/usr/lib/systemd/system/postfix.service
--- /usr/lib/systemd/system/postfix.service.orig        2020-12-19 16:25:11.974758482 +0900
+++ /usr/lib/systemd/system/postfix.service     2020-12-19 16:25:36.594764623 +0900
@@ -5,7 +5,7 @@

 [Service]
 Type=forking
-PIDFile=/var/spool/postfix/pid/master.pid
+PIDFile=/mnt/spool/postfix/pid/master.pid
 EnvironmentFile=-/etc/sysconfig/network
 ExecStartPre=-/usr/libexec/postfix/aliasesdb
 ExecStartPre=-/usr/libexec/postfix/chroot-update
```

## 1-4. postfixを起動
```
systemctl daemon-reload
systemctl start postfix
```

# 2. メールボックスへのメールを保存しない
次に受信したメールをメールボックスに保存せずに/dev/nullに捨てる専用アカウントを用意します。

## 2-1. devnull@[DOMAIN]へメールを/dev/nullに捨てる
```text:/etc/aliases
devnull:       /dev/null
```

```
## /etc/aliase.dbへ反映
# newaliases
```

# 3. メールログをtmpfsに移動
大量のメール受信時にはメールログへの書き込みにも配慮が必要ですので、メールログもtmpfsへ書き込みます。

## 3-1. rsyslogの設定変更
```text:/etc/rsyslog.conf
--- /etc/rsyslog.conf.orig      2020-09-30 22:19:06.000000000 +0900
+++ /etc/rsyslog.conf   2020-12-19 16:55:48.292582516 +0900
@@ -57,7 +57,7 @@
 authpriv.*                                              /var/log/secure

 # Log all the mail messages in one place.
-mail.*                                                  -/var/log/maillog
+mail.*                                                  -/mnt/logs/maillog


 # Log cron stuff
```

## 3-2. メールログを/mnt/logsへ出力
```
# mkdir /mnt/logs/
# systemctl restart rsyslog
# systemctl restart postfix
```

設定はここまでで完了。
メール送信サーバからdevnull@[メール受信サーバのドメイン]へメールを送信し、I/Oが発生しないことを確認して下さい。

以上です。
「Ubuntu」のホームディレクトリの場所はどこ？（コメント欄にて解決済み）

# Ubuntuのホームディレクトリってどこ...?
* grepコマンドなどを使って、用意したテキストファイル内の文字検索をしようと思った。
* 用意した検索対象のテキストファイルを、Ubuntuのホームディレクトリに配置しようとしたのに見つからない！！
* 迷子

# 環境
* OS：Windous10 Home
* Ubuntu：Version 20.04 LTS

# 調べてみたら...こんなところに。わかるわけない。
* バリバリの初心者には見当もつきませんでした。自分の場合は以下の場所にあり、たどり着けました。

```
C:\Users\<ユーザ名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState\rootfs\home\<ユーザ名>
```
pipコマンド

## pythonのライブラリの管理ツール`pip`の使い方

### インストール

```:基本
pip install ライブラリ名
```

```:バージョンを指定
# ダウングレードも可能
pip install ライブラリ名==バージョン
```
```:インストール可能なバージョン番号を表示
pip install ライブラリ名==
# エラー文が表示されるが、エラー分の中にインストール可能なバージョンが表示される
```
```:ライブラリのアップグレード
# 以下どちらのコマンドもおなじ
pip install -U ライブラリ名
pip install --upgrade ライブラリ名
```

### アンインストール
```:基本
pip uninstall ライブラリ名
```

### インストール済のライブラリ確認

```:基本
# インストール済のライブラリを表示
pip list
```
```:最新版かどうか確認
pip list --outdated
```

```:ライブラリ詳細を確認
pip show ライブラリ名
# 依存関係はRequiresに表示される
```

### インストール済のライブラリの保存

```:基本
pip freeze > requirements.txt
# 保存ファイル名は何でもOKだが、requirements.txtが一般的によく見る
```

```:保存したライブラリ名をインストール
pip install -r requirements.txt
```

### 依存関係簡略化

#### pipdeptree

pipdeptreeは依存関係をツリー状で表示する
※pipdeptreeは標準で入っていないため、要インストール

```:pipdeptreeのインストール
pip install pipdeptree
```

```:全ライブラリの依存関係をツリー状で表示
pipdeptree
```

```:指定のライブラリの依存関係を表示
pipdeptree -p ライブラリ名
```

#### pip-autoremove

pip-autoremoveは依存関係にあるライブラリをまとめて削除する
※pip-autoremoveは標準で入っていないため、要インストール

```:pip-autoremoveのインストール
pip install pipdeptree
```

```:依存関係にあるライブラリをまとめて削除
pip-autoremove ライブラリ名
```


[参考:Pythonライブラリのインストール － pipの使い方](https://gammasoft.jp/python/python-library-install/)
ディレクター歴2ヶ月半の私が早くもぶつかった壁

こんにちは。
ウェブクルー Advent Calendar 2020 の20日目の記事です。
昨日は @wc-kawakami さんの「バックエンドエンジニアと学ぶAdobe After Effects入門」でした。

私は新卒1年目で、ディレクター修行に日々奮闘中の@miiiiiitsu541と申します。

web系の会社に入って、憧れのディレクターになりたい！サイト制作に直接携わりたい！
という気持ちを伝え、
ありがたいことに1年目からディレクターを経験させていただいてます。
　

ディレクターってなに？どんなスキルが必要なの？ということに関しては、多くの方が記事をあげているので、よろしければ覗いてみてください。

>
[▶＠ssssakiiさん　「チーム開発〜開発ディレクターって何者？〜」](https://qiita.com/ssssakii/items/c185c9a7016a9e804f8b)
>
[▶＠yoshiokaCBさん　「フリーランスのためのWEBディレクターの仕事について（まとめ）」](https://qiita.com/yoshiokaCB/items/c72a201a819eb994fba0)


今回は、ディレクターってなに？といった前段のところはあえてすっ飛ばし、
まだディレクター歴たった2ヶ月半の私が、
この短期間の実務の中でぶつかった壁のひとつをお話できればと思います。


未来の自分への備忘録として、
また、これからディレクターとして仕事をしたい！といった方の参考になれば嬉しいです。
（もしこの記事を読んで「参考になった！」と思ってくれる方がいれば、未来の私が泣いて喜んでいることでしょう。）


#制作チームとの意思疎通

ディレクターの重要な仕事の一つに、「イメージや仕様を的確に伝える」というものがあります。
案件の立案者＝製作者　が同一人物であれば話は早く、この段階ではそこまで苦労しません。

ところが、他部署からの依頼案件であったり、ディレクター自身が立案した施策であったりと、
案件の立案者≠制作者　であることが圧倒的に多いです。


立案者と実際に手を動かす人が異なるため、立案者が「この施策で何を意図しているのか」「どういったデザインイメージを持っているのか」ということを制作チームに伝えるこの段階でミスってしまうと、
まったくイメージと違うものが出来あがってしまうことがあります。

完成時のクオリティを高めるためにも、「的確に意図を伝えること」は、
ものすごく重要なディレクターの仕事だと私は思っています。

###デザインイメージを私が実際に伝えるとき

####1.Excelで完成イメージ図を作成
![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/964013/c758f46b-67bc-d3e7-f399-ddcabf40a338.png)


こんな感じでアニメーションコマのイメージをひとつずつExcel上で再現します。
※実務で使用した資料から、今回の投稿用に一部修正しております。

####2.「目立たせたい」「ユーザーに最も伝えたい」要素の優先順位を記載

>▼訴求の優先順位
　①「●%安くなる」など、実際にどのくらい安くなるのかのイメージ
　②悩んでいる人→喜んでいる人
　③「引越し料金」「一括見積もり」というワード

こちらを記載することで、デザイナーさんもディレクターの意図を汲み取りやすくなり、
お渡ししたイメージ図以上に、断然良いものが返ってくることがあります。

####3.口頭ですり合わせ

>・単身のイメージは男性じゃなきゃだめ？
・スーツである必要は？
・色要素はいくつまで入れてもよい？

など、仕様書上で伝え切れていなかった細部を5分程度ですり合わせます。

####4.デザイナーさんによる作業、そして完成！

出来上がったものをみると、思わず

デザイナーさんすごい！
何もかも思った通り！！！！！
　
と、声に出したくなるほどのものをお返しいただきました。

これは上手く意思疎通ができた例です。

#簡略化することで起こってしまった認識のずれ

私がぶつかった壁はここから。
毎回イメージ図を作成して、仕様を詳細にテキストでまとめて・・・
と案件ごとに時間をかけて丁寧に仕様書を作ることができればよいのですが。
　

任せていただける案件も少しずつ増え、
同時並行でいくつもの案件が進んでいく中で
全案件の「仕様を伝える」段階にそこまで時間を割けません。

口頭やテキストでも卒なく意図を伝えることができる案件に対して、
「仕様書を作らずに進めている」とおっしゃる先輩のスピード感溢れる仕事を模倣し、
私もやってみました。
 　
　



・・・・

が、・・・・・

・・・・


全然できない！
　

返ってきたデザインを見て
「そういうことではなかった・・・」
「この部分伝え忘れてた・・・」
といった場面にたびたび直面しました。

そうなると、デザイナーさんに直しの依頼をするのも申し訳なくなる上に、
デザイナーさんも自分自身も、多くの時間を失ってしまいます。
　

「こんな感じで伝わるよね」
「なんとなく汲み取ってくれるよね」

と、100%だと思い込んで伝えたこちらの情報は、
受け取った側からすると完成イメージを組み立てるための、ほんの数割の情報であった、
ということが身に染みてわかりました。
　

今でもこの壁を乗り越えるために絶賛奮闘中なので、「こうすれば的確でかつより簡単に伝えることができるんだ！」という正解は見つかっていません。

しかし、伝える・伝わるといったトピックスを取り上げている記事は多くあり、
仕事をする上で、ディレクターに限らずほとんどの方にとって、必要なスキルです。

>[▶"伝える"ではなく"伝わる"よう話すコツ3](https://president.jp/articles/-/25219?page=1)

「伝え方」については様々なところで勉強になるなと思うタイミングがあるので、
次また記事を書く機会があれば、
実務の中で体感した「相手に伝えるコツ」といったテーマで書いてみようかなと思います。


#さいごに

いかがでしたでしょうか。
今回はディレクターになって早2ヶ月半の私がぶつかった「伝え方」の壁についてお話ししました。

仕事の中で直面する壁はまだまだ他にも沢山ありますが、
超えるべき壁が多ければ多いほど、
そこには成長のチャンスが転がっていると思います。

一人前のディレクターになれるよう、頑張るぞ！

明日は@wcsakuraiさんです。
よろしくお願いします！
Jetson AI 認定制度取得のための準備

#はじめに

もともとRasberyPIに興味があったんですが、社内チャットでJetson Nano開発キットの廉価版と同時にそれを使ったNVidiaのAI認定制度が開始されたとの情報が流れてきましたので、良い機会と思い開発キットを購入してみました。

#Jetson Nano 2G 開発キット
エッジAI利用可能なGPUを搭載したNVidia製開発者向けキットです。
ubuntuベースの開発環境が用意されており、気軽にエッジAIの開発が試せます。
本体価格は7000円前後です。電源等別途用意する必要がありますが、10000円をやや超えるぐらいで始められます。

Jetson Nano 2G 開発キット：https://developer.nvidia.com/embedded/jetson-nano-2gb-developer-kit

#Jetson AI 認定制度
NVidiaが実施するJetsonを使用したAI認定制度です。  認定コースには下記の2つがあります。

* Jetson AI Specialist  
開発者向けの認定コースです。  
無料の学習コースの修了と自作プロジェクトをNVidiaに送り審査に合格する必要があります。
プロジェクトの認定基準項目は下記のとおりです。詳細はリンクを参照してください。

| 項目 | 内容 |
|:--|:--|
|AI   | AIについて理解しており、Jetsonを用いて効果的にAI技術を使用していること  |
| 影響／独創性  | 独創性があり、AIを適用して、自分自身または社会が直面する課題や問題を解決または対処していること  |
| 再現性  | プロジェクトを構築して使用するために必要なソリューション、コード、およびリソースはすべてリポジトリに含まれており簡単に実施できること  |
|  プレゼンテーション／ドキュメント |  リポジトリにプロジェクトの構築/実行に必要な手順を図や画像とともに文書化したreadmeが存在すること |

* Jetson AI Ambassador  
教育者向けの認定コースです。  
Specialistの要件に加え、面接試験の合格が必要になります。

今回はJetson AI Specialistの認定を目指します。 
Jetson認定制度：https://developer.nvidia.com/embedded/learn/jetson-ai-certification-programs

#開発環境

無料の学習コースを行うためには下記機材が必要になります。

ハードウェア要件([Hardware Requirements](https://developer.nvidia.com/embedded/learn/jetson-ai-certification-programs)参照)
>NVIDIA Jetson Nano™ Developer Kit or Jetson Nano 2GB Developer Kit*
MicroSD card (64 GB UHS-1 recommended, 32 GB UHS-1 minimum)
Micro-B USB cable
Power supply [Jetson Nano Developer Kit](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-2gb-devkit#intro)
Camera (Logitech C270 USB webcam or Raspberry Pi camera module v2)
PC or laptop with an SD card slot (Windows, Mac, or Linux)

用意した開発環境は下記のとおりです。
一部あまっていた資材を使いまわしています。

| 項目 | 機材 |
|:--|:--|
| 開発キット  | Jetson Nano 2GB 開発者キット  |
| 電源  |  LABISTS Raspberry Pi 4B TypeC電源ケーブル 5V 3A スイッチ付き PSE取得 [^1] |
| microSDカード  | サンディスク microSD 64GB UHS-I U3 V30 書込最大60MB/s Full HD & 4K SanDisk Extreme SDSQXA2-064G-EPK エコパッケージ  |
|  ケース |  NVIDIA Jetson Nanoケース、冷却ファン付きNVIDIA Jetson Nano開発者キット用アクリルケース（3.0-5.8V）シェルエンクロージャークーラー [^2] |
| カメラ  | ロジクール ウェブカメラ C310n（Zoom用に用意していたもの）  |
| USB2.0A-microB接続ケーブル  | KindleFireHDの充電ケーブル  |
| HDMIケーブル  | あまっていたケーブル  |
| LANケーブル  |  あまっていたカテゴリー6LANケーブル |
| 開発用PC  | Windows10PC  |


  
次回は無料の学習コースについて記載します。

[^1]: 5V.3A必須です。最初に携帯充電用の5V.2Aの電源を使用していたところ起動中に再起動がかかってしまいました。
[^2]: Jetson Nano 4G用のケースのため前フタの電源接続部が干渉します。前フタは閉めずに使用しています。
まっさらな状態から「Ubuntu」で「grepコマンド」を使って文字検索をするまでにやったことメモ

# 初めに
* Ubuntuってなに？<br>
おすすめサイト：https://wa3.i-3-i.info/word15508.html

# 実行環境
* OS：Windous10 Home
* Ubuntu：Verison 20.04 LTS

# Ubuntuのインストール
* Windowsストアからインストールしました。<br>
https://www.microsoft.com/store/productId/9N6SVWS3RX71

# Ubuntuのバージョン確認コマンド
* Ubuntuのバージョン確認をする際、以下のコマンドを実行しました。

```ruby:Ubuntuのバージョン確認コマンド
$ cat /etc/issue
```
自分の場合は以下のように出力されました。

```
Ubuntu 20.04 1 LTS \n \l
```

# sudoコマンドのパスが通っていないことに気づいた
1. 以下のディレクトリに移動する。

```
C:\Users\<ユーザ名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu20.04onWindows_79rhkp1fndgsc\LocalState\rootfs\etc\sudoers.d
```
2.つき当たりの```sudoers.d```フォルダ内にある```99-snapd.conf```というファイルをメモ帳などで開き、```secure_path```の部分を以下のように編集した。

```
# Allow snap-provided applications to work with sudo
Defaults    secure_path += /usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
```
※一応バックアップは取って！

# 「Permission Denied」エラーを解消するために
* catとgrepコマンドを実行しようとしたら出てきたエラー。
* 用意した検索対象のテキストファイルを開く権限がなかったため発生。
* 用意したテキストファイルを編集する権限を与えるため、以下のコマンドを実行した。

```
$ chmod 764 -c <ファイル名>
```
※ この「Permission Denied」エラー時に権限を与える方法については、以下のサイトがわかり易いです。
https://lpic.hatenablog.com/entry/chmod%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95
bundlerについての復習

# 初めに
学習中に何度も出てきたが、自身で定義がきちんとされていなかったため、再度復習の意味もこめて作成したものであります。
間違い、補足等ございましたら、お手数おかけしますがご連絡いただけますと幸いです。

## agenda
- bundler とは
- Gemとは？
- bundle コマンド
- 

## bundlerとは
- Gemの関係性を管理し、必要なGemやバージョンを**合わせてインストール**することができるツール（Gemは複数が依存関係にあり、それら全てをインストールする必要があるが、bundlerを使用すれば一括でのダウンロードが可能となる）。
- bundlerによってインストールされたGemは**Gemfile.lock**というファイルに記憶される。

## Gemとは？
- Rubyにおけるライブラリ、拡張機能のこと。個人的にOne Tabをよく使用する。
※One Tabとは大量に開いているタブをまとめて1つにしてくれるChromeの拡張機能のこと。

## Gemfileとは？
- 導入したいGemが書かれているファイル。bundlerによってインストールされたGemの情報（名前とバージョン）も記載されている。

## bundler使用方法
- bundke init
  --------インストールしたいGemを列挙したGemfileを作成するためのコマンド。
- bundle install
  ------GemfileをもとにGemをインストールするコマンド。
- bundle update
  -----その名の通り、Gemに新しいバージョンがあれば更新するコマンド。
   


# 引用
Samurai Blog
https://www.sejuku.net/blog/19426
AtCoder ABC 186 E - Throne

ABC 186のEがさっぱりだったので復習。

### 問題

[AtCoder ABC 186 E - Throne](https://atcoder.jp/contests/abc186/tasks/abc186_e)

- N個の座席が円周状に並んでいて、1つは玉座
- 初期位置は玉座から時計回りにS個ずれている
- 1回の移動で、時計回りにK個ずれる
- 何回移動すれば玉座に座れる?

以下、座席の位置を0～N-1、玉座を0とする。初期位置はSで、位置0に到達するための回数を求める問題となる。

### 解法の概略

- 最大公約数(GCD)で行けるかどうか判定
- 拡張ユークリッドの互除法(通称extgcd)を用いて逆元を求める
- 逆元×移動したい個数が答え

### 最大公約数と移動可能性

NとKの最大公約数(GCD)をgとする。KとNは何倍してもgの倍数なので、ループして戻ってきてもgの倍数ごとの位置となる。すなわち、最小の移動単位がgであることを意味している。
特に、gが1より大きいときについて考えると、g未満のずれを解消することはできないということである。開始位置Sからg単位でしか移動できないので、位置を0にするためにはS % g = 0(Sをgで割った余りが0)でなければならない。
なので、まずNとKの最大公約数gを求め、S % g = 0でなければ不可とし、そうでない場合は、NとKとSをgで割っておく。そうすると以降、NとKは互いに素となるので、扱いやすくなる。

### 移動回数と周期性

NとKが互いに素のとき、同じ場所に戻ってくる回数について考えてみる。初期位置が0で、a回移動したあとの位置bは、Nごとに1周するのでNの余りとなり、b = (a × K) % Nと表すことができる。同じ場所に戻ってくるのはb = 0、すなわちa × KがNの倍数となるときである。それはNとKの最小公倍数(LCM)ということになるが、NとKは互いに素なので最小公倍数はN × Kである。つまり、N回移動するごとに同じ場所に戻ってくる。(周期Nごとに、N個の異なる位置を取る)

### 逆元と任意の場所への移動

問題を少し変形して、一つ隣の位置に行くことを考えてみる。もしa回の移動で一つ隣の位置に行けるならば、b個先の位置には、a × b回の移動で行けるはずである。
この問題では位置をNの余りで考えるので、Nを法とする合同式で扱っていることになる。それをmod Nと記述する。このような世界で便利な数に逆元がある。逆元はかけると1になる数であり、x × K ≡ 1 (mod N)となるxが逆元である。これはつまり、x回移動すると位置が1進むことを意味している。すなわち、逆元が求まれば移動回数が計算できそうである。

### 逆元の計算

ax + by = gcd(a, b)となるx, yは拡張ユークリッドの互除法(通称extgcd)で計算できる。ここではextgcdによりKx + Ny = 1となるx, yを計算する。mod Nで考えるとNyが消えてKx ≡ 1 (mod N)となる。xがmod NにおけるKの逆元であり、x回移動すると一つ隣の位置に行ける。
サンプルにあるN=998244353, S=897581057, K=595591169で逆元を計算すると-145577299となった。ただし求めたいのは正の移動回数なので、これを直接答えには使えない。ここでN回ごとに同じ位置に戻る周期性を利用すると、-145577299 + N = 852667054回の移動で隣に行けることがわかる。

### 移動回数の計算

初期位置Sから位置0に移動したいので、N - S個移動する。
隣に移動するのに(x + N) % N回必要なので、((x + N) % N) × (N - S)回移動すればよい。さらに、N回ごとに同じ位置に戻るので、答えは((x + N) × (N - S)) % Nとなる。また、N回周期なのでこれが最小値となる。
サンプルにあるN=998244353, S=897581057, K=595591169だと((-145577299 + 998244353) × (998244353 - 897581057)) % 998244353 = 85832276046249984 % 998244353 = 249561088となり出力例と一致した。
よくあるextgcdの実装では戻り値としてgcdを返すので、それを利用するなら以下のようになる。(C++)

```c++
long long x, y;
long long g = extgcd(k, n, x, y);
long long ans = (((x + n) * (n - s)) % n) / g;
```

### 余談

pが素数のときはフェルマーの小定理から$ a^{p - 2}$ mod pでaの逆元が計算できるが、この問題ではNが合成数のこともあるので適用できない。

### 参考情報

- プログラミングコンテストチャレンジブック(蟻本)第2版 p.109
- [拡張ユークリッドの互除法 〜 一次不定方程式 ax + by = c の解き方 〜](https://qiita.com/drken/items/b97ff231e43bce50199a)
golangでランダムな整数配列を作る

## はじめに
アルゴリズムの勉強をする上に、実装したアルゴリズム検証用に毎回テスト用の配列を作るのはめんどくさい。。。
そのため、アルゴリズム検証用にランダムな並びをもつ配列の作成方法を検討しました。

### 実行環境
- macOS Big Sur 11.1
- go 1.14

## 配列をつくる
ベースとなる配列を作ってみます。

```go:main.go
package main

import "fmt"

func main() {
	nums := []int{2, 5, 3, 8, 7, 1}

	fmt.Println(nums)
}
```

実行すると以下のように出力されます。

```
 $ go run main.go
[2 5 3 8 7 1]
```

配列ができていることが確認できます。
しかし、このままでは複数の配列を試すのにいちいち配列の中身を入れ替えないといけません。

## 配列の中身をランダム化する
`math/rand`パッケージを導入し配列の中身をランダムにします。
引数に配列を指定し、0から1000までのランダムな値を配列の中身に入れる`randomValue`関数を作成します。

```go:randomValue
func randomValue(values []int) []int {
	lenVaules := len(values)
	for i := 0; i < lenVaules; i++ {
		values[i] = rand.Intn(1000)
	}
	return values
}
```
上記の関数を`main.go`に追加し配列を作成します。

```go:main.go
package main

import (
	"fmt"
	"math/rand"
)


func randomValue(values []int) []int {
	lenVaules := len(values)
	for i := 0; i < lenVaules; i++ {
		values[i] = rand.Intn(1000)
	}
	return values
}

func main() {
	// 空の配列を作成
	nums1 := make([]int, 10)
	nums2 := make([]int, 10)

	// 作成した関数を利用しランダムな値を代入
	randomValue(nums1)
	randomValue(nums2)

	fmt.Println("nums1: ", nums1)
	fmt.Println("nums2: ", nums2)

}
```
実行した結果は以下になります。

```
$ go run main.go
nums1:  [81 887 847 59 81 318 425 540 456 300]
nums2:  [694 511 162 89 728 274 211 445 237 106]
```

ランダムな値が設定されていることが確認できます。
しかし、上記の場合、乱数を生成する際に一定のシード値を元に作成しているため、
実行するたびに同じ結果となります。
実行する度に異なる値を試すことができず不便です。

### 実行するたびに異なる値が生成されるようにする
実行する度に異なる値が呼べるようにSeed関数を利用します。
`time`パッケージを追加し、`time.Now().UnixNano()`をシード値にすることで実行する度に異なる値が得られるようにします。

```go:main.go
package main

// time を追加
import (
	"fmt"
	"math/rand"
	"time"
)

func randomValue(values []int) []int {
	lenVaules := len(values)
	rand.Seed(time.Now().UnixNano()) // 追加部分
	for i := 0; i < lenVaules; i++ {
		values[i] = rand.Intn(1000)
	}
	return values
}

func main() {
	nums1 := make([]int, 10)
	nums2 := make([]int, 10)

	randomValue(nums1)
	randomValue(nums2)

	fmt.Println("nums1: ", nums1)
	fmt.Println("nums2: ", nums2)

}
```





以下のように実行する度に配列の中身がランダムに変わることが確認できます。

```
$ go run main.go
nums1:  [402 966 929 144 151 894 788 951 644 412]
nums2:  [179 436 524 222 937 729 362 14 621 166]
```

## まとめ
関数を利用しランダムな配列を作成できるようになりました。
今回作成した関数を利用し、実際にアルゴリズムによって配列がソートされるか検証していきたいと思います。
【Ruby】テーブルに対しての設定値・備忘録

date型のカラムを追加しようとしてエラーが出てしまった話。


```
% rails db:migrate

== 20201220042102 DeviseCreateUsers: migrating ================================
-- create_table(:users)
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Invalid default value for 'birthday'
/Users/bon/projects/furima-32411/db/migrate/20201220042102_devise_create_users.rb:5:in `change'
/Users/bon/projects/furima-32411/bin/rails:9:in `<top (required)>'
/Users/bon/projects/furima-32411/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'

Caused by:
ActiveRecord::StatementInvalid: Mysql2::Error: Invalid default value for 'birthday'
/Users/bon/projects/furima-32411/db/migrate/20201220042102_devise_create_users.rb:5:in `change'
/Users/bon/projects/furima-32411/bin/rails:9:in `<top (required)>'
/Users/bon/projects/furima-32411/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'

Caused by:
Mysql2::Error: Invalid default value for 'birthday'
/Users/bon/projects/furima-32411/db/migrate/20201220042102_devise_create_users.rb:5:in `change'
/Users/bon/projects/furima-32411/bin/rails:9:in `<top (required)>'
/Users/bon/projects/furima-32411/bin/spring:15:in `<top (required)>'
bin/rails:3:in `load'
bin/rails:3:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

```


なんか前もこんなことあったような・・・
birthdayにはdate型を指定していたがエラー文からして以前やったものに近い気がしていた。
色々と調べたところやはりdate型はdefault値で空が設定されており、
「**default: ""**」の設定が不要らしい。
マイグレーションファイルから記述を削除して再度マイグレート、成功。

```

t.date :birthday, null: false　　　#←ここに「　default: ""　」があったためエラーに

```

動的値と静的値、ややこしい。
何回も使う関数を、いつでもどこでも呼び出したい。。。

皆さん、何度も使う関数を使う度にメソッドで定義するのは面倒ですよね。

今回は、Vue.jsで「いつでも、どこでも実行できる関数」があったら便利だなと思ったので備忘録として残していきます。

モジュールのインストールなども無く、驚くほど簡単なので、是非学習の参考にしてください！！！

それでは説明していきます！

#フォルダ、ソースファイルを作成#

srcディレクトリの中に、任意のフォルダと関数を書いていくソースファイルを作成します。
今回フォルダは「mixin」、ソースファイルは「globalMethods.js」とします。

```
cd src
md mixin
cd mixin
copy nul globalMethods.js
```

そして、globalMethods.jsに何度も使う関数を定義します。

```globalMethods.js

export default {
  methods: {
    initFacebook() {
      window.fbAsyncInit = function() {
        window.FB.init({
          appId: "685243755466253", //アプリID
          status: true,
          cookie: true,
          xfbml: true,
          version: "v9.0",
        });
      };
      console.log("init完了");
    }
}

```

今回は、FacebookSDKでよく使うFB.init()を例に挙げていきます。

次に、main.jsにて今作ったファイルをインポートします。

```
import Vue from "Vue";
import gv from "../src/mixins/globalMethods";

Vue.mixin(gv);

```

これで、initFacebook()をいつでも呼び出すことができます。

実際に呼び出すと以下のようになります。

```App.vue

<template>
  <div id="app"> 
      <router-view/>
  </div>
</template>

<script>
  export default{
   async created() {
      await this.initFacebook();  //初期化
    }
  }
</script>

```

以上、「いつでも、どこでも実行できる関数」の紹介でした！

良かったら、LGTM、コメントお待ちしております。

また、何か間違っていることがあればご指摘頂けると幸いです。

Thank you for reading
FlaggerでBlue/Greenデプロイメントを試した

先日FlaggerでBlue/Greenデプロイメントをやる案件があり、FlaggerのインストールからBlue/Greenデプロイメントの動作を確認するところまで一通りを試したので、結果を残しておく。
今回はSlackへの通知を設定したので、更新があったりプロモーションが完了したりするとSlackに通知が来る様になっている。

# Flaggerインストール及びBlue/Greenデプロイメント
## Flagger インストール
基本的には以下のページを参照。Istioを有効にしてデプロイしたGKEクラスタ向けの手順もあるが、より汎用性の広い手順を試した。
https://docs.flagger.app/install/flagger-install-on-kubernetes

Flaggerのインストールは基本的にhelmを使って行う。まずはhelmのリポジトリを追加する。

```bash
helm repo add flagger https://flagger.app
```

次にFlaggerで使用するCanary CRDを追加する。名称はCanaryだが、カナリアデプロイのほか、Blue/green deploymentでも使用する。

```bash
kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml
```

Flaggerで使用するメトリクスサーバとしてprometheusを使用する。今回はistioサンプルのprometheusインストール用プロファイルを使用する(あらかじめどこかしらにistioのリリースをダウンロードしている前提。ダウンロードしていない場合、istioのリポジトリを直接指定しても可)。

```bash
Kubectl apply -f ./istio/samples/addons/prometheus.yaml
```

Flaggerをhelmでインストールする。flaggerはhelmインストール時の引数で機能を有効にするので、今回はメッシュプロパイダーとしてistio、メトリクスサーバとしてprometheus、通知先としてslackを登録する。あらかじめslackにアプリを登録し、webhook urlを取得しておく必要がある。また、通知先のチャネルはアプリ側のチャンネルに上書きされる。

```bash
helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set crd.create=false \
--set meshProvider=istio \
--set metricsServer=http://prometheus:9090 \
--set slack.url=https://hooks.slack.com/services/your/webhook/url \
--set slack.channel=general \
--set slack.user=flagger-app
```

## Blue/green deployment
Blue/green deploymentは以下のページを参照しつつ検証した。
https://docs.flagger.app/tutorials/kubernetes-blue-green
また、使用したコードは以下のリポジトリに保存した。
https://github.com/uS-aito/argocd-and-flagger-labo

Blue/green deploymentの検証はtest namespace上で行うので、まずはtest namespaceを作成する。

```bash
kubectl create ns test
```

まず、各種テストで使用するPodをデプロイする。

```bash
kubectl apply -k github.com/weaveworks/flagger//kustomize/tester
```

次に、サンプルアプリをデプロイする。

```bash
Kubectl apply -f app -R
```

Podinfoコンテナを含むdeploymentリソースと、CPU使用率を参照してpod数を2-4の範囲で調整するhpaリソースがデプロイされた。
flaggerはまずprogressive deliveryで管理したいdeploymentをデプロイしておき、次にそのdeploymentを`spec.targetref`で指定したcanaryリソースをデプロイする。このcanaryリソースは指定されたdeploymentを基にしたprimary deployment(リソース名は元のdeploymentに-primaryをつけたもの)をデプロイし、元のdeploymentのpod数を0にスケールする。その後、元のdeploymentに変更があるたび、canary deployment(リソース名は元のdeploymentに-canaryをつけたもの)をデプロイし、メトリクスの確認等の動作を行なった上で、トラフィックを誘導する。

Canaryリソースをデプロイする。

```bash
Kubectl apply -f canary.yaml
```

これ以降、元のdeploymentにpodの再生成が必要な更新が加えられるたび、canary deploymentの作成以降の動作が行われる。その様子は以下のコマンドで確認することができる。

```bash
kubectl -n test describe canary/podinfo
```

## Slackへの通知
flaggerのインストール時にSlackのwebhook URLを指定したので、何かあるたびにSlackへ通知が来る様になっている。その通知の例は以下の通り。

**更新が成功した場合**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/40f82a4f-b12b-a92a-e427-555a8e9bf4c7.png)
Canary alaysisとなっているが、やっていることはBlue/Green Deploymentである。

**更新が失敗した場合**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/fb785c4b-c9ff-3234-3ea3-314b26271464.png)
一度デプロイしたものの、メトリクス条件を満たさなかったのでロールバックが発生した場合の例。
Railsで ActiveModel::MissingAttributeError (can't write unknown attribute　〇〇) 

備忘録です
間違っていたら教えてください

```
ActiveModel::MissingAttributeError (can't write unknown attribute `image`):
```
imageカラムが書き込めないエラー

###原因　
フォームに対する型が違う？

マイグレーションファイル

```
t.string :image
```

フォーム入力画面

```
<%= f.file_field :image, class: 'input-box_image_file',value: "#{@tweet.image}"%>
```

###修正

型を変更してみる

```
t.text :image
```
これでエラー解決しました！
GitHub 覚え書き 2 コミット～マージ

# 初めに
前回、GitHub 覚え書きを書きました。
今回はその続きです。内容はコミット～マージまでです。

## コミット

前回の記事で作成したブランチ（test1）で作業を行います。
以下のように編集してみます。

![git1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6147e515-0f23-64d8-b911-b1d9c0f6ffd5.png)

Sourcetreeを見るとコミットボタンに "1" がついています。
これはコミットしていない編集内容があるということです。
historyの説明にも同じようなことが書かれています。
（今の作業ブランチを赤で囲いました。作業ブランチが間違っていないか確認するようにしています。）

![git2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/f3437404-b332-41b1-07f1-066b017614e8.png)

先ほどの編集をコミットします。
そのために左上の赤で囲ったファイルステータスをクリックします。（編集内容は画面右で確認できます。）
その後、選択をインデックスに追加をクリックします。

![git3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/663e6cb1-aafa-9892-ba03-7028e6fc4b48.png)

次にコミットメッセージを書きます。
ここでは「コミットテスト」としました。

![git4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b6c6a3e3-fafe-0b2d-c7e8-29cf7d8340f0.png)

コミットした後はhistory画面に戻ります。
コミットボタンについていた "1" が消え、コミットされたことがわかります。

![git5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/ceda9b3f-dbd1-8b52-5d41-97bd27025246.png)

続いて、今おこなった編集をリモートに反映させるためにプッシュします。
test1ブランチのチェックボックスにチェックを入れます。すると自動的にリモートブランチの場所にもローカルブランチと同じブランチ名が表示されます。
これを確認したらプッシュボタンを押します。

![git6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/c5939e6e-943b-0011-ca74-c0116e20d412.png)

プッシュした後、GitHubのコンソール画面を開き、testリポジトリを確認します。
続いてプルリクエストを出します。
赤い枠で囲った Compare & pull request ボタンを押します。

![git7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/25933dd6-b38b-33ca-6409-76a4cdf2d593.png)

どのブランチからどのブランチにマージしてほしいのかを一番上の赤い枠で囲った部分に指定します。
その後プルリクエストのタイトル、内容を記載します。
（タイトルはデフォルトで直前のコミットメッセージになります。）

![git9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/499b90bf-69f0-a978-7902-6c721c589f85.png)

同じ画面の右側でマージ認証者（Assigner）を指定します。

![git10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/e87ce849-3f79-ad68-aa02-c76b832d47b2.png)

<strong>プルリクエストを出す前に、Files changedを確認します。</strong>
これは画像が悪い（マージ前の画面を載せるべきでした）のですが、以下の File changed をクリックすることで確認できます。

![git15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/fadbb30c-2399-a5e5-7251-91f8373c35fe.png)


プルリクエストを出すと以下の画面に遷移します。
最後に編集内容をmainブランチに取り込むため、マージします。
赤い枠で囲った三角ボタンをクリックするとマージの種類を選択できます。
ここではデフォルトのMerge pull requestをクリックします。

![git11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/b01d40c2-f670-fc7f-9ced-543620790a71.png)


その後確認画面が出ますが、これも確認をクリックして進めるとMergeされます。

![git14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/257d9075-14b0-4aab-4f0f-5c354709f24d.png)

## 振り返り
本日は以下の内容について書きました。

- コミット
- プッシュ
- プルリクエスト
- マージ
【AWS】1ヶ月でソリューションアーキテクト取得【SAA】

コロナ自粛期間中で在宅勤務になったことで業務後の自学習時間が思いのほか確保できたので、ソリューションアーキテクトを取得してみることにした。結果として1ヶ月で取得できたのでその記録を記す。

## 基本スペック

IT業界歴は3年くらい。経験内容は、VBAを1年ちょっととWEBのサーバサイドを半年、残りはテスターとドキュメント作成業務。AWSを業務で使用することはほとんどなく、趣味でEC2とかLambdaをちょっと触ったことがあるくらい。

## 資格試験を受験する動機

成功体験とスキルを身に付けるためにソリューションアーキテクト- アソシエイトを受験することにした。目標を設定しそれを達成することでやれば出来ると自分に自信がつき、SAAを保持していれば一応のスキルを証明することが出来るのでこれは一石二鳥だろう。1ヶ月で取得することを目標に設定した。

## 学習方法

参考書を利用して試験にでるサービスをの基本を押さえ、あとは問題集を解くことで試験合格に必要な知識を身に付けることにした。学習に使用した参考書と問題集は下記の通り。

- この１冊で合格！　AWS認定ソリューションアーキテクト - アソシエイト テキスト&問題集
- 【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）

Udemyの問題集は難易度高めの問題で構成されており、実際に解いたら1周目は4割程度しか正解しなかった。試験受けるまでに2周したが2周目も4・5割程度しか解けず結構焦った。まぁでも本試験はこれよりも簡単だろうと思い本試験に臨んだ。

## 試験受験

Udemy の模擬試験よりも簡単かなと思ったが、思いの外自信をもって回答することができず実感として4割位の出来で解きながらこりゃやばいと思った。試験時間は130分だったので時間いっぱいまでつかって頭フル回転で問題を解くことにした。

## 結果

試験結果はテストセンターの画面上ですぐに確認できる。ヤベー絶対落ちた〜と思いながら恐るおそる画面を確認したら、「合格」という文字があって心の中でやったー！と叫んだ。いや〜、本当にうれしかったな〜。
![aws-saa-certification-1024x77.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/780549/c688021a-7475-1e10-55e0-19a5ca49e06c.png)


この調子で残り2つのアソシエイト資格を合格して3冠達成するぞ！
【EKS】ワーカーノードにユーザデータを設定する【CloudFormation】

業務で EKS を使用したインフラ構築の案件があった。EKS 自体は他の案件でやったことがあったので今回もおなじように構築しようとしたら、「プロキシ設定しないとインターネットに接続できません。」と言われた。ほぅ、プロキシの設定ってどうやるねん、もっというと、EKS のワーカーノードにプロキシの設定ってどうやんねん、と大変困った。まぁ、ワーカノードってEC2インスタンス群なので、ユーザデータにプロキシ設定書いてあげればいいかって感じだったが、EKS のワーカノード設定画面にユーザデータ設定項目がない！！もしかして、ワーカノードのインスタンにユーザデータの設定って出来ないの？

## CloudFormationを使用してワーカノードにユーザデータを設定する

ワーカノードにどうやってユーザデータを設定すればいいかは、そのものずばりでプロキシの設定方法とともに公式ページに記載がある。よくあるケースなのかな？

- [ユーザーデータを使用して Amazon EKS ワーカーノードの HTTP プロキシの設定を自動化する方法を教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-http-proxy-configuration-automation/)
- [Amazon EKS Linux ワーカーノードの起動](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/launch-workers.html)

つまりは CloudFormation を利用してユーザデータを設定するのである。CloudFormation を使用したワーカノードの作成サンプルも提供されているのでそれを参考にしユーザデータの挿入の箇所をプロキシ設定用に変更すればよい。

```yaml
~~省略~~
      InstanceType: !Ref NodeInstanceType
      KeyName: !Ref KeyName
      SecurityGroups:
        - Ref: NodeSecurityGroup
      UserData: !Base64
        "Fn::Sub": |
          Content-Type: multipart/mixed; boundary="==BOUNDARY=="
          MIME-Version:  1.0
          --==BOUNDARY==
          Content-Type: text/cloud-boothook; charset="us-ascii"
          
          #Set the proxy hostname and port
          PROXY="proxy.local:3128"
          MAC=$(curl -s http://169.254.169.254/latest/meta-data/mac/)
          VPC_CIDR=$(curl -s http://169.254.169.254/latest/meta-data/network/interfaces/macs/$MAC/vpc-ipv4-cidr-blocks | xargs | tr ' ' ',')
          
          #Create the docker systemd directory
          mkdir -p /etc/systemd/system/docker.service.d
          
          #Configure yum to use the proxy
          cloud-init-per instance yum_proxy_config cat << EOF >> /etc/yum.conf
          proxy=http://$PROXY
          EOF
~~省略~~
```

## CloudFormation 楽しい

実は CloudFormation を触るのは今回が始めてだった。名前と機能は知っていたが覚えることが多そうで学習コストが高めたっだので敬遠していた。今回しかたなく触ってみたのだが、こりゃ便利だわ。各サービスの設定値や依存関係がぱっと見で把握できし、コードで管理しているので、いつ・誰が作成しても同じ構成になる。これからは IaC も勉強しなきゃですな。
Flutter TDD Clean Architecture Course その7 - Network Info

- [こちらのチュートリアル](https://resocoder.com/2019/09/23/flutter-tdd-clean-architecture-course-7-network-info/)の解説をします。
- チュートリアルを読むのが早いと思いますが、[概要をまとめた](https://qiita.com/maria_mari/items/8fdd92af13a36c663360)のでこちらもご参照ください。


- 今回はNetworkInfoの実装をします

---

# packageの変更
- Android端末でのWiFi接続の挙動が保証されないため、変更

```package.yaml
dependencies:
  # Swap the connectivity package for this
  data_connection_checker: ^0.3.4
```

# NetworkInfoの実装

## Test
- resultとして Future<bool>型の値を取得して、検証している
    - awaitせず、Future<bool>同士で比較している
- expectにてmatcherを使わず、同一インスタンスとして比較している
    - `==` と同じ比較になっている

```network_info_test.dart
import 'package:clean_architecture_tdd_prep/core/network/network_info.dart';
import 'package:data_connection_checker/data_connection_checker.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

class MockDataConnectionChecker extends Mock implements DataConnectionChecker {}

void main() {
  NetworkInfoImpl networkInfo;
  MockDataConnectionChecker mockDataConnectionChecker;

  setUp(() {
    mockDataConnectionChecker = MockDataConnectionChecker();
    networkInfo = NetworkInfoImpl(mockDataConnectionChecker);
  });

  group('isConnected', () {
    test(
      'should forward the call to DataConnectionChecker.hasConnection',
      () async {
        // arrange
        final tHasConnectionFuture = Future.value(true);

        when(mockDataConnectionChecker.hasConnection)
            .thenAnswer((_) => tHasConnectionFuture);
        // act
        // NOTICE: We're NOT awaiting the result
        final result = networkInfo.isConnected;
        // assert
        verify(mockDataConnectionChecker.hasConnection);
        // Utilizing Dart's default referential equality.
        // Only references to the same object are equal.
        expect(result, tHasConnectionFuture);
      },
    );
  });
}
```

- 実装

```network_info.dart
import 'package:data_connection_checker/data_connection_checker.dart';

abstract class NetworkInfo {
  Future<bool> get isConnected;
}

class NetworkInfoImpl implements NetworkInfo {
  final DataConnectionChecker connectionChecker;

  NetworkInfoImpl(this.connectionChecker);

  @override
  Future<bool> get isConnected => connectionChecker.hasConnection;
}
```

# 学んだこと
## サードパーティーライブラリを隠蔽化するクラスを作る
- `NetworkInfo().isConnected` は `DataConnectionChecker().hasConnection`を読んでいるだけである
    - ライブラリの変更等があった場合、この小さいクラスの修正に留められる
        - 単一責任の原則の実践であり、ソフトウェアの変更に強い設計となっている

## テストでFutureをawaitせずともassertできる
- Future<bool>同士で比較できる
Laravelのファイルストレージにカスタムドライバを追加する

Laravel には Flysystem PHP パッケージを統合したファイルシステムが提供されている。ローカルのファイルシステムと S3 を操作できるドライバがデフォルトで提供されているのみなので、カスタムドライバを作成して Laravel のファイルシステムを拡張してみる。

## extend メソッドの使用

ファイルシステムを拡張するためには Storage ファサードの extend メソッドを使用する必要があり extend メソッドにはドライバ名と``League\Flysystem\Filesystem``を返すクロージャを指定する必要がある。``League\Flysystem\Filesystemは League\Flysystem\AdapterInterface`` を引数にとるので、ファイルシステムを拡張するには ``League\Flysystem\AdapterInterface`` を実装したクラスを作成する必要がある。

```php
// League\Flysystem\Filesystem の実装の一部

namespace League\Flysystem;
...
class Filesystem implements FilesystemInterface
{
    ...
    public function __construct(AdapterInterface $adapter, $config = null)
    {
        $this->adapter = $adapter;
        $this->setConfig($config);
    }
    ...
}
```

## AdapterInterfaceを実装する

``League\Flysystem\Filesystem`` のコンストラクタに渡すために ``League\Flysystem\AdapterInterface``を実装したクラスを作成する。実装する必要がある API の一覧は[https://flysystem.thephpleague.com/docs/usage/filesystem-api/](https://flysystem.thephpleague.com/v1/docs/usage/filesystem-api/)に記載されているので、引数の型まで合わせて実装してあげる。今回は AdapterInterface を継承した抽象クラスである ``League\Flysystem\Adapter\AbstractAdapter`` を継承した具象クラスを作成する。

```php
// AdapterInterface を実装する

use League\Flysystem\Adapter\AbstractAdapter;

class SampleStorageAdapter extends AbstractAdapter
{
    public function write($path, $contents, $config)
    {
        // 実装する
    }

    public function writeStream($path, $resource, $config)
    {
        // 実装する
    }
        ...
}
```

## カスタムドライバを登録する

Flysystem アダプタを実装したクラスを用意したらサービスプロバイダの boot メソッド内で Storage ファサードの extend メソッドを使ってカスタムドライバを登録する。

```php
namespace App\Providers;
...
class SampleServiceProvider extends ServiceProvider  
{
  ...
  public function boot()
  {
        Storage::extend('sample', function ($app, $config) {
            $sample = new SampleStorageAdapter($config);
            return new Filesystem($sample, $config);
        });
  }
}
```

作成したカスタムドライバを使用するためには、まず``filesystem.php``にディスク情報を登録する。

```php
...
'sample-disk' => [
    'driver' => 'sample',
],
```

``config/filesystem.php``にディスク情報を登録したら Storage ファサードの disk メソッドを使用して disk 名を指定することでディスクインスタンスにアクセスすることができる。

```php
Storage::disk('sample-disk')->read('path/to/get/content')
```
Docker で phpunit を実行する

php のテスティングフレーワークに phpunit がある。テスト環境は開発マシンに依存させず、簡単に構築できるようにしたいため docker を使って用意できるか試してみた。今回は、Dockerfile と composer を使って phpunit の実行環境を作ってみた。

## Dockerfile で composer をインストール

phpunit は composer を使ってインストールするため、まずは docker container に composer をインストールしておく。インストールの仕方は、[composer の公式にかかれている](https://hub.docker.com/_/composer)multi-stage builds を使ってインストールする。

Dockerfileの中身

```docker
FROM php:7.2-cli

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
# composer のインストール
COPY --from=composer /usr/bin/composer /usr/bin/composer

RUN apt-get update -yqq \
    && apt-get install git zlib1g-dev libsqlite3-dev -y \
    && docker-php-ext-install zip \
    && docker-php-ext-install pdo_mysql

WORKDIR /var/www
```

## composer で phpunit をインストールする

phpunit を composer を使ってインストールする。

### docker イメージの作成

先ほど作った Dockerfile からイメージを作成する。

```bash
docker build . -t php:composer
```

### ホストディレクトリをマウントして container を起動する

ホストディレクトリをマウントして起動させる理由は、作成したコードとテストコードを container 内で実行できるようにするため。

```bash
docker run -it --rm -v $(pwd):/var/www php:composer /bin/bash
```

### phpunit をインストールする

phpunit をインストールしましょう。composer.json を用意していたら composer install でインストールしましょう。

```bash
composer require --dev phpunit/phpunit
```

composer.json を使用する場合は下記の内容を記述し、composer installを実行

```json
{
    "require-dev": {
        "phpunit/phpunit": "^8.4"
    }
}
```

## phpunit を実行する

実際に動くか試すため下記のコードを用意する

```php
<?php
require('vendor/autoload.php');
use PHPUnit\Framework\TestCase;
class UserTest extends TestCase {
    public function testExample() {
        $expected = 'hoge';
        $this->assertEquals($expected, 'hoge');
    }
}
```

下記のコマンドからテストを実行する

```bash
vendor/bin/phpunit test.php
```

出力

```bash
root@4b1af08e1cd8:/var/www# vendor/bin/phpunit test.php 
PHPUnit 8.4.3 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 257 ms, Memory: 4.00 MB

OK (1 test, 1 assertion)
root@4b1af08e1cd8:/var/www#
```