OmniAICreator/Qiita-1.07M huggingface dataset:
[OpenRPA] エクセルデータ読込、書込み方法

OpenRPAの操作方法が探してもなかなか見つからなかったので、手探りで分かった操作方法を記録しています。

 
##自動で動かく内容
動作検証でさとふるの控除上限シミュレーターに以下のエクセルデータ
を読込、Y列の「控除上限額（目安）」にデータを入れます。
この動きは、エクセルのデータを読込、書込みが含まれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/718459/77a13175-75ce-b9e1-f7ef-76c2706b5be3.png)


 

#１．読込方法
1-1 ”ツールボックス”から、「範囲読込」を中央の”デザイナーパネル"に持ってくる
1-2 ファイル名を指定する。
1-3 画面右側の”プロパティ”にある「DateTable」を任意の文字列を入力してデータベース名を決める(画像では、『dt』と記入)。”ヘッダー行を使用”を「Ture」にする。
1-4 画面下側の”変数”を開き、「変数の作成」をクリックし、「名前」を③で入力した文字列を記入し、「変数の型」を「System.Data.DataTable」を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/718459/e465f8e8-a739-12f1-a081-2b9cb4e65184.png)



上記の取り込みにより、RPAが動作時に一時的にエクセルのデータをデータテーブルに格納されます。
また、「ヘッダー行を使用」をTureにすることで、1行目を項目名として認識します。

#２．取込んだデータ行を使用する
2-1 ”ツールボックス”から、「Foreach DataRow」を中央の”デザインパネル”持ってくる。
　　※「Foreach DataRow」は、中のアクティビティをデータテーブルの行ごとに繰り返し行う「for文」のような物
2-2「Foreach DataRow」の中央には任意の文字列を入力(画像では、『row』と記入)、右側には1-3のデータベース名を入力。

2-3 ”ツールボックス”から、「A←B Assign」を「Foreach DataRow」の中に持ってくる。
　　※「A←B Assign」は変数を宣言するもの。
2-4 左のボックスに任意の文字列を入力、右のボックスには『2-2で決めた文字列.(“使用したいエクセルデータの行の項目名“).ToString』と記入（画像では、『row("給与収入").ToString』と記載）
　　※『row("給与収入").ToString』は文字列(String型)として変数に代入している。数値(int型)として格納する場合は、『Convert.ToInt32(row("給与収入"))』と記入する

2-5 画面下側の””を開き、「変数の作成」をクリックし、「名前」を任意の文字列記入(画像では『給与収入』)、「変数の型」を「String」を選択する。
　　※数値を変数に代入している場合は「変数の型」を「Int32」にする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/718459/3ad3c744-6c72-0358-09f1-5205f32e9efe.png)


これにより、変数にデータテーブルの中身が順次格納されるようになりました。
画像の例だと、『給与収入』の変数にエクセルデータのE列の給与収入が順次String型で格納されるようになります。

 

#３．データを書き込む
3-1 ”ツールボックス”から、「A←B Assign」を「Foreach DataRow」の中に持ってくる。
3-2 左側のボックスには、『2-2で決めた文字列.(“書込みたいエクセルデータの行の項目名“)』を記入(画像では、『row("控除上限額（目安）")』と記入)
　　右側のボックスには、入力したい文字列または、変数を入力する。
　　※画像は、レコーディングでブラウザのデータを入れ込むため、『item.value』を記入しています。

3-3  ”ツールボックス”から、「範囲を書込み」を中央の”デザイナーパネル"のに持ってくる「Foreach DataRow」の外側に持ってくる。
　　※中でも結果同じになりますが、行分書込み作業をしてしまうので、処理が重くなります。
3-4 ファイル名を1－2で指定したファイル名を指定する。
3-5 「範囲を書込み」の”プロパティ”で「DataTable」を1－3で決めたデータベース名を記入
　　「セル」をエクセルデータの一番左上のセルを指定する(画像では、A1が一番左上になります)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/718459/55f00426-3de8-5fba-6b65-c7a0ad910dcf.png)

 
#最後に
OpenRPAはUiPathとかなり似ていますが、所々違っています。
「この程度、記事にしなくてもわかるだろ！！」って考えている方が、いましたら
お願いですから、**この程度でもわからない人の為にひと肌脱いで操作方法の記事を書いて頂けると嬉しいです！**
Windows で WSL2（Ubuntu） を使ってDocker 開発環境を作る

この手の記事はｎ番煎じですが、なんぼあってもいいですからね。

# なんでそんなもの使うの

Windows には WSL (Windows Subsystem for Linux) という Windows 上で動く Linux が乗っています。これが 2020 年にバージョンアップし、 WSL2 になったことで Windows で Docker をつかった開発がとってもやりやすくなりました。まだまだ痒いところに手が届くと言った面では足りないところもありますが、業務で利用するには十分使えるようになってきてます。

Windows で Docker を使おうとすると、これまでは Windows の上に仮想環境（Hyper-V）がのってその上に Dokcer エンジンが動いて、という構造だったので、パフォーマンスが悪い、VirtualBox と併用できないなどの弊害がありました。これが WSL2 になって仮想環境を介さず WSL2 上で直接 Docker エンジンが動くようになって、デメリットがなくなりました！

さっそく、使ってみましょう。

# WSL2 を有効化する

参考にした文書はこちら
https://docs.microsoft.com/ja-jp/windows/wsl/install-win10#update-to-wsl-2

## 「Linux 用 Windows サブシステム」オプション機能を有効にする

管理者として PowerShell を開き、以下を実行します。

```powershell
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
```

PCを再起動します。

## 「仮想マシン プラットフォーム」オプション機能を有効にする

管理者として PowerShell を開き、以下を実行します。

```powershell
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
```

PCを再起動します。

## WSL2 をデフォルトにする

管理者として PowerShell を開き、以下を実行します。

```powershell
wsl --set-default-version 2
```

## Linuxディストリビューションを選択しインストール

Microsoft Store を開き、Ubuntu をインストールします。バージョンが書かれていないものを選ぶと最新が入ります（今回は 20.04 がインストールされました）

![SnapCrab_NoName_2021-2-5_12-4-44_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16728/fd34a285-c90c-9218-6fd1-5c835757c71d.png)

## 初期設定
もうインストールは終わりました。他のWindowsアプリケーションと同じようにターミナルが起動できます。初期ユーザ/パスワードを設定し、ライブラリのアップデートをしておきましょう。

```shell
sudo apt update
sudo apt upgrade -y
```


# Dockerのインストール

こうしきからもってきます
https://hub.docker.com/editions/community/docker-ce-desktop-windows


インストール中に WSL2 サポートを使うか聞かれるのでON
![2020-07-31-050213.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16728/c98eac75-88a6-8fb7-cb8c-54940cee5614.png)

ここを設定しておきます
![SnapCrab_NoName_2021-2-5_12-42-38_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16728/974ad381-78d9-9f26-40bf-43cb1e59d4da.png)
![SnapCrab_NoName_2021-2-5_12-42-18_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16728/535cf0a4-bd75-e96f-b82f-c8625133c2a4.png)




`wsl -l -v`コマンドを打って動いているかどうか確認しましょう。

```PowerShell
PS C:\Users\user> wsl -l -v
  NAME                   STATE           VERSION
* docker-desktop-data    Running         2
  Ubuntu                 Running         2
  docker-desktop         Running         2
```
三つ出てきましたが、

* docker-desktop：Dockerを動かすためのエンジン
* docker-desktop-data：イメージなどの格納先

どちらも WSL の環境のようです。僕の環境だと先に Docker が入っていたせいか、WSL の規定が Docker 仮想環境になってしまっていたので、`wsl -s` コマンドで Ubuntu のほうにかえておきます。

```PowerShell
PS C:\Users\user> wsl -s Ubuntu
PS C:\Users\user> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop-data    Running         2
  docker-desktop         Running         2
```

# Visual Studio Code の環境を作る

Visual Studio Code はすでにインストールされている者とします。 [Remote WSL
という拡張](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl)をインストールします。


Ubuntu 側に入ります。
プロジェクト用のディレクトリなどを適当に掘って

```
code .
```
で起動すると、Windows 側で WSL 内のディレクトリが初期で開かれた状態になります。


# 大事なこと

WSL2で作成されたファイルなどはWindowsローカルに置かれますが、こちらをいじってはいけません。
画像が縦長か横長か自動判別してclassをつける


事情があってobject-fitが使えない時に使いました。

	//画像が縦向きか横向きかclassをつける
	function imgAddClass(imgArr){
	  if(imgArr.length > 0){
	    for (let i = 0; i < imgArr.length; i++) {
          var e = imgArr[i];
          var width = e.naturalWidth;
          var height = e.naturalHeight;

          if(width < height){
            e.classList.add('tate-img');
          }else{
            e.classList.add('yoko-img');
          }
	    }
	  }
	}
	
	//判定したい要素を指定
	var imgs = document.querySelectorAll('li img');
	
	window.addEventListener('load', function(){
	  imgAddClass(imgs);
	}
Introducing Microsoft Viva　『必要な知識とあなたをつなぐ』

# Microsoft Vivaとは
　2021年2月4日、Microsoftは、リモートワーク時代の[”統合された従業員エクスペリエンスプラットフォーム”「Viva」](https://www.youtube.com/watch?v=6aDLjVQZmno&feature=youtu.be)を発表しました。Vivaは、日常業務の流れの中で、コミュニケーション、洞察、知識、学習を統合します。また、プライバシーを尊重し、会社のデータを保護するよう設計されています。
　Vivaは、Microsoft TeamsとMicrosoft Graph Extensibility Frameworkに基づいて構築されているため、開発者は既存のスキルを活用できます。

# [Viva Connections](https://www.youtube.com/watch?v=tXl1hccX6KE&feature=youtu.be)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/0977dbca-4057-08d0-07f5-ffac9e042357.png)
　Teamsなどの社内のコミュニケーションツールへのゲートウエイ。どこででも、あらゆるデバイスで、カスタマイズ可能なコミュニケーションツールを使用することができます。Microsoft Azure ActiveDirectroyのグループに基づき、MicrosoftStream, SharePoint, Yammer およびMicrosoft Teamsが、適切なメンバーに、適切なタイミングで情報を伝えることができます。

# [Viva Insights](https://www.youtube.com/watch?v=ax3MTUAqrZg&feature=youtu.be)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/b313930c-e51d-f67f-0516-dd5b0fe59e0e.png)
　従業員の生産性と幸福度を分析し、回復力を支援します。パーソナライズされたリマインダーで、業務に集中する時間やコーチングの時間、学習や休憩時間、そして[「仮想通勤」](https://thebridge.jp/2020/09/microsoft-teams-virtual-commute-headspace-meditation-the-first-part) の時間を設けて振り返りと明日の予定の確認を行います。幸福を優先し、生産性の向上を助けるため、チームメンバーは推奨事項とベストプラクティスを受け取ります。　
　また、マネージャーやリーダーに向けては、育成のための分析や推奨事項をダッシュボードにマッピングして提供します。Glintと連携もできます。そこでは、チームが燃え尽き症候群や孤立などのリスク、健康的なバランスの取れた働き方が行われているかなどの作業パターンを可視化できます。
　Viva Insightsは、Zoom、Workday、SAPSuccessFactorなどのサードパーティのツールからデータを活用することもできます。

# Viva Learning
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/253ccfcc-749a-52aa-8fde-7b83244b349b.png)
　Microsoft Teamsと一体化した社内研修ツールです。自分に割り当てられているコンテンツやお勧めのコンテンツをすべて一か所で見つけることができます。会社固有のコンテンツのほか、LinkedIn LearningとMicrosoft Learn以外にもCornerstone OnDemand、Coursera、eDX、Glint各種学習ライブラリからコンテンツを検索して選択できます。Viva Learningを使用すると、仕事の流れの中でシームレスに学習を開始することができます。学習内容について共有することもでき、学習文化を育みます。また、Viva Learningは、AIを使い、パーソナライズされた推奨事項を提供します。
　マネージャーは、学習内容やオンボーディングを確認することができます。チームメンバーにコンテンツを割り当てることで、アジャイルな業務を行いながら学習と成長を優先することができます。

# [Viva Topics](https://www.youtube.com/watch?v=ugE2xW2UnNs&feature=youtu.be)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/0468f653-abf6-ecef-7d83-e73dc4069438.png)
　Microsoft Teams内で使用できる情報ツール。Viva Topicsは、Microsoft Graphを使用し、AIによってトピックページを自動的に構築します。Teams内のトピックにカーソルを合わせると、トピックカードに要約と主要なドキュメントの一覧が表示されます。検索やアプリの切り替えなしで行えます。時間のかかる検索から従業員を開放します。
　カードを選択すると、詳細ビューのあるトピックページを開くことができます。Viva Topicsが収集した重要なドキュメント、トピック、ディスカッションとコミュニティ、対象分野の専門家など、もっと学ぶのを助けるための情報があります。
　マネージャーは、この情報を管理および編集できます。また、公開する前に提案されたトピックを確認することもできます。
　Vivaは情報をトピックに整理するだけでなく、プロフィールカードを見て、これまでの成果を確認したり、専門家に質問を提出したりすることができます。
　更に多くの情報が必要な場合には、TeamsのTopicsアプリを使用してください。Microsoft 365だけでなく、130を超えるコンテンツコネクタやAPIを使用してService Nowなど、他のソースから情報を統合します。セキュリティ、コンプライアンス機能により、従業員は、権限のあるコンテンツだけを見ることができます。

# まとめ
　Microsoft Vivaは、Microsoft製品に限らず、多様な製品を統合させるプラットホームです。今、リモートで働く従業員は確かなエンゲージメントと多くのサポートを必要としています。今だから求められているものをMicrosoftは作りました。
R StudioでEmacsキーバインディング設定

Rを用いた統計分析を行うにあたって、便利なIDEであるR Studioをインストール。学生時代からEmacsに親しんできた私には方向キーを用いた移動などがどうしてもうまく扱えないため、R StudioにおいてもEmacsキーバインディングにできないか調べたところ、[Emacs Editor Keybindings](https://support.rstudio.com/hc/en-us/articles/210928128-Emacs-Editor-Keybindings)を参考に設定できた。

1. メニューバー[Tools]->[Global Options...]を選択
![r1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/14bc95b1-c1f2-63f1-3bdb-e8cc6e752446.png)

2. [Code]タブ内のKeybindingsにて「Emacs」を選択
![r2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/7bd899f2-bc90-8998-61be-b94aabe7dc24.png)

3. [Apply]->[OK]をクリック

以上でEmacsキーバインディングを扱える。基本的なものはEmacs通り使えるのだが、C-hでのBackspaceなどは設定されていなかった。[Customizing Keyboard Shortcuts](https://support.rstudio.com/hc/en-us/articles/206382178-Customizing-Keyboard-Shortcuts?page=2)を参考にキーバインディングをカスタムしようとするも、Backspaceの項目がなくできず。Backspaceはあきらめるか・・・。
PDOでデータベースに特定のテーブルがあるか確認する方法

## 環境
他のDBや言語とかでも考え方だけ真似れば使えるはず

- Windows
- PHP
- PostgreSQL

## コード

```table_check.php
try{
	  // データベースに接続
	  $set = new PDO('pgsql:host=****; dbname=****;', '***', '****');	
	  echo "接続に成功しました!!<br/><br/>";

	  //テーブルの有無を判断
      //@error_401 さんのコメントより
	  $sql = "SELECT 1 FROM information_schema.tables WHERE table_name = 'abc'";
      $query = $set->query($sql);
      $data = $query->fetchAll(PDO::FETCH_ASSOC);
	  if($data['0']['?column?'] == 1){
	  	echo "テーブルが存在しました。";
	  }

	  //エラー表示
	  }catch(PDOException $error) {
?>

<div style="color: red"><?php print($error->getMessage()); ?></div>

<?php
	}
	// データベースとの接続を切断
	$set = null;
?>
```

## 解説

**`$sql = "SELECT 1 FROM information_schema.tables WHERE table_name = 'abc'";`** で、存在しているかどうかを知りたいテーブルが存在しているかどうかを取得できる。
取得の際、オブジェクトになるため、 **`$data = $query->fetchAll(PDO::FETCH_ASSOC);`**で配列に変換する。
そうすると、以下のような配列ができる。

**`array(1) { [0]=> array(1) { ["?column?"]=> int(1) } }`**

存在していれば**`['?column?']`**の値が**`1`**になる。
以上のコードを使って、テーブルの存在を確かめ、**`if($data['0']['?column?'] == 1)`**でテーブルが存在していた際の処理を記入していく。
アルゴリズム線形検索と二分検索の違い

## 線形検索アルゴリズム
線形検索アルゴリズムとは、配列からデータを検索するアルゴリズムです。
線形検索は配列がソート（整列）されていない場合にも適応することができることが特徴です。
配列から検索できるのは線形検索と二分検索ですが、これらの何が違うのでしょうか。

## 線形検索と二分検索の違い
これらの違いは、処理の仕方が異なるということです。
目的は同じで配列の中から結果を出しますが、処理の内容が異なるので、処理の完了までの時間も異なります。

### 二分検索
二分検索の処理はソートされているのが、条件の一つになります。
ソートされている状態で、配列の中央からデータを分割にして値の検索を行います。

### 線形検索
線形検索は、二分検索と異なり、配列がソートされていない状態での検索が可能になります。
検索開始地点は配列の先頭から検索していく特徴があります。

```
       1, １０, ４, ８, 5, ７, ６, ３, 9, ２
     　→１から順番に期待している値を検索する
```

線形検索の特徴は、
・ソートする必要がないこと。
・ソートされていない配列に適応することができること。
・左から順番に検索を行うので、データが大きい時や期待しているデータが最後にある時は処理が二分検索に比べて長くなってしまうこと。

１が最初に来て期待する値も１あれば、二分検索よりも早い処理をしますが、大きいデータや最後にあるデータを見つけつとなると線形検索は劣ってしまうことです。

## Rubyのコードを書いてみる

rubyをコードで表すとこのようになります。
indexメソッドは配列を左から処理をするメソッドの一つです。

```ruby
  array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  target = 6
  result = array.index(target)

  if result
    puts "#{target}は#{result + 1}番目にある"
  else
    puts "#{target}は配列には存在しない値"
  end
```

## 最後に
処理の期待する値が同じでも処理が異なると、速さも変わってくると思うとどの場面に適応すべきかなど理解できるようになったと思います。
わかりにくいことがあったり、アドバイスがあればコメントお願いします。
HackerRank : Recursive Digit Sum

* 問題URL　https://www.hackerrank.com/challenges/recursive-digit-sum/problem
* 九去法を使用した

```cpp
#include <bits/stdc++.h>

using namespace std;

vector<string> split_string(string);

// Complete the superDigit function below.

int superDigit(string n, int k) {
    if(k>0){
        long long res=0;
        for(int i=0;i<n.size();i++)res+=n[i]-'0';
        n=to_string(res*k);
        return superDigit(n, 0);
    }else{
        long long res=0;
        for(int i=0;i<n.size();i++){
            res+=(n[i]-'0')%9;
            res%=9;
        }
        return res==0?9:res;
    // cout << "idx:" << idx << "n:" << n.c_str() << boolalpha << isFirst << "\n";
    /* if(n.length()==1)return stoi(n);
    int res=0;
    string temp=n;
    for(int i=1;i<k;i++)n+=temp;
    for(size_t i=0;i<n.length();i++)res+=n[i]-'0';
    n=to_string(res);
    return superDigit(n,0); */
    }
}

int main()
{
    ofstream fout(getenv("OUTPUT_PATH"));

    string nk_temp;
    getline(cin, nk_temp);

    vector<string> nk = split_string(nk_temp);

    string n = nk[0];

    int k = stoi(nk[1]);
    
    int result = superDigit(n, k);

    fout << result << "\n";

    fout.close();

    return 0;
}

vector<string> split_string(string input_string) {
    string::iterator new_end = unique(input_string.begin(), input_string.end(), [] (const char &x, const char &y) {
        return x == y and x == ' ';
    });

    input_string.erase(new_end, input_string.end());

    while (input_string[input_string.length() - 1] == ' ') {
        input_string.pop_back();
    }

    vector<string> splits;
    char delimiter = ' ';

    size_t i = 0;
    size_t pos = input_string.find(delimiter);

    while (pos != string::npos) {
        splits.push_back(input_string.substr(i, pos - i));

        i = pos + 1;
        pos = input_string.find(delimiter, i);
    }

    splits.push_back(input_string.substr(i, min(pos, input_string.length()) - i + 1));

    return splits;
}
```
SOCIAL NETWORK

"https://www.facebook.com/dienlanhlimosavn 
https://twitter.com/limosavn 
https://www.youtube.com/channel/UCqDwOP32oIFHybD-hzW9JeA 
https://www.instagram.com/dienlanhlimosavn/
https://profiles.wordpress.org/dienlanhlimosavn/ 
https://www.pinterest.com/dienlanhlimosavn/ 
https://dienlanhlimosavn.wordpress.com/ 
https://www.pearltrees.com/limosavn 
https://www.allmyfaves.com/dienlanhlimosavn/
www.linkedin.com/in/dienlanhlimosavn "
Elixirの偶数奇数判定

elixirでは、rem(x, 2) であまりを求められる。
https://elixir-lang.jp/getting-started/basic-operators.html

例えば、奇数のとき足して、偶数のとき何もしない場合、こんな感じにかけばよいと思える。

```elixir
  defp calc(sum, number) when rem(number, 2) == 1 do
    sum + number
  end

  defp calc(sum, _line) , do: sum
```


しかし、xがマイナスのときremは-1を返すので、numberがマイナスのとき無視されてしまう。

よって、奇数の場合、こっちのほうがいいかも。

```elixir
  defp calc(sum, number) when rem(number, 2) != 0 do
    sum + number
  end

  defp calc(sum, _line) , do: sum
```

偶数チェックはこれでよい。

```elixir
  defp calc(sum, number) when rem(number, 2) == 0 do
    sum + number
  end

  defp calc(sum, _line) , do: sum
```
CPythonを読む(2) Parserを読むなら 3.10 以降にしよう

CPythonのソースを読んでいます。

Parser(構文解析器）の周りに興味があるならば、バージョン 3.10 以降のコードを読むことをお勧めします。 3.9 からPEGパーサーという新しい仕組みが導入されたのですが移行期にあたり、旧パーサーのコードも残っていてオプションで切り替えられるようになっています。このため新旧の似たようなソースファイルが残っていてとても混乱します。

3.10 では、これらの古いファイルが消されていて遥かに読みやすくなりました。リリースノートの(以下のリンク) の Rmoved のところに、その事が書いてあります。

https://docs.python.org/ja/3.10/whatsnew/3.10.html#summary-release-highlights
bundle install と yarn installの違いについて

#記事の内容
bundle install と yarn installの共通点と違いから、それぞれのinstallの意味について述べている。

#まとめ
・bundle install と yarn installの両方とも、ライブラリをインストールするコマンドで、インストールするものがアプリ内で使えるかどうかをチェックしている。

・違いはbundle installの対象がgemで、yarnはそのJavaScript版である。

```
bundle install
```

```
yarn install
```

※ライブラリとは、他のプログラムと組み合わせて使用するために、複雑なプログラムを1つのセットにしたもの。

#バージョン
rubyのバージョン　ruby-2.6.5
Railsのバージョン Rails:6.0.0

#bundle install と yarn installの共通点
・ライブラリをインストールしている
・installするライブラリが「アプリケーションで使用できるかどうか」をチェックしている
・installするライブラリが、使用言語等のバージョンと一致しているかどうかをチェックしている
（一致：ライブラリが使用できるかどうかの観点で一致しているかどうか）

#bundle install と yarn installの相違点
・bundle install → installするライブラリがgem
・yarn install → installするライブラリがJavaScriptについてのライブラリ

#bundle installとは
ライブラリをインストールするコマンドで、インストールする者がアプリ内で使えるかどうかをチェックしている。

bundlerというgemが他のgemのバージョンを管理しており、使用言語等のバージョンを含めて、gem同士でアプリ内で使用できるかをチェックしている。

#yarn installとは
ライブラリをインストールするコマンドで、インストールする者がアプリ内で使えるかどうかをチェックしている
bundleインストールのJavaScript版

yarnはパッケージマネージャーであり、gemのbunderのようなものである。
javascriptに関係するパッケージ（ライブラリをまとめたもの）のバージョンを管理して、ライブラリ同士でアプリ内で使用できるかをチェックしている。
ImageJ Macroの配列について

環境　`ImageJ 1.53c`

```
aa = newArray(5);	// 配列長5の一次元配列
print(aa.length);	// 常に5
```

配列の初期値は0で、数値として読み出しても文字列として読み出してもすべて0になっている。**配列長は常に宣言時の長さ**である。

一般の変数は特に宣言せずに使えるが、配列として使うなら宣言が必要で、宣言していない変数`bb`にいきなり`bb[0] = 1;`とはできない。ただし、関数の戻り値として返ってくる配列を受ける場合は、宣言しなくて大丈夫。

配列長は適宜伸縮しないので、`Array.concat`で延長したり、`Array.trim`で添字が若い部分を取り出す。

```
aa = Array.concat(aa, newArray(4));
print(aa.length);			// 9
aa = Array.trim(aa, 3);
print(aa.length);			// 3
```

作成できる配列は一次元のみ。

配列として宣言された変数は配列長情報を持ったポインタのようになっているみたいで、配列同士の代入操作では内容がコピーされるわけではなくエイリアスの作成が起こる。

```
aa = newArray("A", "A", "A");
bb = newArray("B", "B", "B", "B", "B");
cc = newArray("C", "C");
bb = aa;
cc = aa;
```

とすると、aa、bb、ccすべてA, A, Aとなり、

```
Array.print(aa);	// 結果はA, A, A
Array.print(bb);	// 結果はA, A, A
Array.print(cc);	// 結果はA, A, A
```

どの配列変数名で中身を変更してもすべて更新される。

```
bb[1] = "B";
cc[2] = "C";
Array.print(aa);	// 結果はA, B, C
Array.print(bb);	// 結果はA, B, C
Array.print(cc);	// 結果はA, B, C
```

配列として宣言した変数名に、添字をつけずに代入操作を行うと配列ではなくなり、普通の変数になる。

```
aa = newArray(5);
aa[0] = "A";
Array.print(aa);	// 結果はA, 0, 0, 0, 0
aa = "a";
Array.print(aa);	// エラーになる
print(aa);		// 結果はa
```

---
20210205　`Array.concat`、`Array.trim`について追記しました。
sshからexitしたときにターミナルのタイトルを消去したい

# 状況

bashでsshを切断した時にターミナルのタイトルにsshの接続先 ``user@xxx.xx.xxx.xx:~`` というのが残るのが気になり、その方法を以前調べて設定したものの、情報が見つけにくいのでメモ代わりに残すことにする。

# どうするのか
簡単に言うと、リモート側の ``~/.bash_logout`` に以下の設定を追加するだけでいい。

```bash
# ~/.bash_logout: executed by bash(1) when login shell exits.

# Clears terminal's title
printf '\e]0;\a'
```
# 注意
おそらくbashでないと効果がないと思われます。
AzureADの姓名未記入が原因でAWS Single Sign-Onが使えなかった話

## 結論

ADユーザーの姓と名はしっかり記入しましょう。

## 問題

Azure AD で新規作成したユーザーがAWS Single Sign-On（以降AWS SSO）で認証されない事象が発生。
４ヶ月前に（ほぼ）同じ手順で作成したユーザーは同現象の確認はされず。
※姓名の入力有無という違いが発生しているのですが、問題発生時の私は違いを認識していません

AWS SSOができないユーザーではこのように「無効なMFA認証情報」という表示がされます。
![スクリーンショット 2021-02-05 10.38.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603571/7cabaa7a-cb0c-aafb-552e-208620d9e6db.png)


## エラーの原因

結論にも書きましたが、ADユーザーの姓と名の入力が欠如していたことが原因でした。
ADユーザー作成時、名前は必須項目ですが姓と名は未記入でも問題ありません。
一方、AWS側の情報では姓と名の項目が必要です。
したがってADユーザーの情報をもとにAWSのログインユーザーを作成できないため、AWS SSOに失敗していたのでした。

私のケースでは、短期間の研修用にユーザーを作成したので姓と名の記入について横着をしました。
一般的に本格運用しているADではあまり起きない事象かもしれませんが、それ故に本エラーに対する対処法がなかなか発見できなかったため苦労しました。


## エラーの原因確認

ここからはエラーコードチェックの手順について、忘備録もかねて記載します。

ユーザーに表示されるエラーメッセージからADのMFA周りを確認したあなた（と私）！
それは盛大なミスリードです。

２１年２月４日現在、Azure ADには**プロビジョニングログ**という機能がプレビューされています。
ここでADのユーザー・グループのプロビジョニングについて確認可能です。
今回の件で言うと、AD側ユーザーをもとにAWSにもユーザーが作成されます。
この操作は自動で行われますが、失敗しても対して通知してくれません。（通知設定を変更できたりしたらごめんなさい）
成功、失敗についてはこのプロビジョニングログで確認しましょう。

失敗したログを選択すると、作業ステップの詳細について表示されます。
タブからトラブルシューティングと推奨事項を選択します。
するとエラーコードとエラーメッセージについて、英語で記載があります。

あとはエラーコードで検索かけたりしましょう。
本件では、エラーメッセージは原因についてはクリティカルな記載をしていませんでした。

![errormessage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/603571/071a16b0-3265-a82f-d801-23203cc10e5a.png)


## 参考
https://docs.microsoft.com/en-us/answers/questions/2820/scim-provisioning-error-with-aws-sso.html
M1 Macで Raspberry Pi PicoのLチカ

Raspberry Pi Picoを入手したので、[Get Started with MicroPython on Raspberry Pi Pico](https://hackspace.raspberrypi.org/books/micropython-pico)の学習用書籍で紹介されているThonny(Python IDE for beginners)を使って見ました。
紙媒体書籍（139ページ）は有償ですが、PDFが無料配布されています。

# 動作環境
- MacBook Pro (M1 2020)
- macOS Big Sur 11.1
- Raspberry Pi Pico

# microPythonをダウンロード
microPython[公式ページより、安定版のrp2-pico-20210202-v1.14を[ダウンロード](https://micropython.org/download/rp2-pico/)します。
![micropython.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/94653033-6dfe-ec34-83ed-f224598bf966.jpeg)

# microPythonをインストール
(1)Raspberry Pi Picoの基板上のBOOTSEL(白いボタン)を押したまま、MacへUSB接続します。
(2)MacのFinderにRPI-RP2のドライブとして見えるので、ダウンロードしたUF2ファイルをドラックアンドドロップします。
![macOS.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/e00e62cc-6d05-01d0-7434-58b70d90821d.jpeg)

# microPythonのIDEツールをダウンロード
Thonny(Python IDE for beginners)を[ダウンロード](https://thonny.org)して、インストールします。
![IDE.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/c1af0191-adde-74d7-b4aa-51b86ab34ebc.jpeg)

# Thonny(Python IDE for beginners)の設定
メニューバーのRunのSelect Interpreterから、デバイスとポートを設定します。

![IDE2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/38535a74-d6ab-b36c-693a-4ee1875f337c.png)

![IDE3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/e7ce69a3-0fa3-e86a-402c-868da84e9843.png)

# Lチカのソースコード

```python
import machine
import utime
led = machine.Pin(25, machine.Pin.OUT)
while True:
    led.value(1)
    utime.sleep(0.5)
    led.value(0)
    utime.sleep(0.5)
```

# Thonny(Python IDE for beginners)の実行画面
![IDE1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452335/57176a5d-156e-bc4b-efcb-003075e7995a.jpeg)

# 感想
M1 Macに特別なドライバーソフトをインストールすることなく、数分で開発環境を構築できました。
お手軽に試すにはC言語より、MicroPythonの方が良いと思いました。

# 参考
- [Raspberry Pi Picoで サーボモーターを動かす](https://qiita.com/SamAkada/items/0f3454fb1e65bec3a94e)
- [Rasberry Pi PicoのmicroPythonでI2CとADCを使ってみる](https://qiita.com/SamAkada/items/07f4929a9fcfbfd942bc)
Firebaseに保存した際に、そのTimestampを取得する方法（Android）

使用環境
ソフト　Android Studio
言語　Java

Firebaseにデーターを保存するときに、タイムスタンプを取得する方法について解説します。

ちなみに、タイムスタンプというのは画像でいうと以下の-MSから始まるもののことで、保存先が重複しないようにFirebaseが発行しているサーバー時間のようなものです。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315874/4ea79df0-8da7-6cfb-79b5-538fbbaa1955.png)

タイムスタンプは、保存時に.push()を入れることによって、発行できます。
そのため、
```refMag.push().setValue("保存する内容")
```
ではなく
```refMag.child("タイムスタンプに変わる文字列").setValue(post)
```
にすることで、タイムスタンプの部分を好きな文字列に変えることが出来ます。ただし、タイムスタンプは重複を防ぐためには欠かせないものなので、.push()でタイムスタンプを使用するのが望ましいです。

ここで、問題になるのがFirebaseに保存されているデーターの書き換えです。
タイムスタンプを取得しないと、個々の書き換えたいデーターにたどり着くことができません。
そこで、```.getKey()```を使用します。これで、タイムスタンプを取得することができます。

まず、最初にタイムスタンプを事前に発行し、String Keyに入れましょう。

```java:qiita.rb
String key = refMag.push().getKey();
```
そして、String型のKeyに代入されているタイムスタンプを```.child()```を使って、そのタイムスタンプの場所に保存します。

```java:qiita.rb
refMag.child(key).setValue("保存したいもの");
```

そして、String keyに保存されている内容を何かしらで参照できるようにすれば、Firebaseのデーターをピンポイントで参照、書き換えができます。

参考にしたサイト　[Working with Firebase Realtime Database Lists
](https://www.techotopia.com/index.php/Working_with_Firebase_Realtime_Database_Lists)
ログイン機能を実装している時にはバリデーションしよう

備忘録

#はじめに
ログイン機能を実装していると、ユーザーが入力したテキストの状態に応じてアプリも反応しなくてはいけない場面が出てくると思います。

例えば、

・何も入力していない時

・明らかに使えない文字が使われている時

など。

そういう書式チェックする事なんかを`バリデーション`というのですが、今回はその実装方法を備忘録として書いていこうと思います。

#実装コード

```swift:InputUserInfoValidationError.swift
enum InputUserInfoValidationError: Error {
    case textEmpty, invalidEmail, invalidPassword

    init?(email: String?, password: String?) {
        // メールアドレスが入力されていない時
        guard
            let email = email,
            !email.isEmpty else {
            self = .textEmpty
            return
        }
        // パスワードが入力されていない時
        guard
            let password = password,
            !password.isEmpty else {
            self = .textEmpty
            return
        }
        // [a-zA-Z0-9@._-]を除く文字を定数化
        let okText = "[^a-zA-Z0-9@._-]"

        // 明らかに使えない文字を使っている場合
        if email.range(of: okText, options: .regularExpression, range: nil, locale: nil) != nil {
            self = .invalidEmail
            return
        }
        if password.range(of: okText, options: .regularExpression, range: nil, locale: nil) != nil {
            self = .invalidPassword
            return
        }

        // 何も問題なければnilを返す
        return nil
    }

    // アラートのメッセージに使用する
    var messege: String {
        switch self {
        case .textEmpty:
            return "入力不備があります"

        case .invalidEmail:
            return "このメールアドレスは\n使えません"
            
        case .invalidPassword:
            return "このパスワードは\n使えません"
        }
    }
}
```

こんな風に`ユーザー入力の正当性を判断するバリデーションのロジックが書かれたファイル`を別に作ってあげます。

では呼び出す側のコードを実装していきましょう↓

※アラートはこの方法で実装していきます。
[UIAlertControllerをファイルを分けて実装してみる](https://qiita.com/reo0612/items/b1fa1e11b3792baa72b8)

```swift:ViewController
// ユーザーが入力したテキストを代入することでバリデーションが行われる
if let validationErrorMessage = InputUserInfoValidationError(email: userEmailTextField.text, password: userPasswordTextField.text)?.messege {
   // エラー内容を表示する
   Alert.okAlert(vc: self, title: validationErrorMessage, message: "")
   return
}
```
呼び出す側はこのようになりました。

これで、代入されたテキストの状態に応じてエラー内容を表示することができますね。

#おわりに
今回は、メンターにコードレビューして頂いた時に指摘された部分です。

私はMVCアーキテクチャを採用してアプリを開発しているのですが、`Controllerにバリデーションの処理を書いていた`ことが問題でした。

Controllerの役割としては`ViewやModelから貰ったデータから別のViewを表示させる`だけで、そのデータを処理して次にどのViewを出すのかという部分(`ビジネスロジック`)はControllerは行いません。

なので今回のように`ユーザーが入力したテキストの状態がどうなっているかを判断するビジネスロジック`は別ファイルで行い、`アラートを表示する`という部分だけControllerが行っています。

うーん、これはもっと深くMVCアーキテクチャについて知る必要がありそうですね。
CSSの詳細度の早見表を作ってみた

コーディングの塾のお仕事でCSSを教えているときに、
IDが優先されるなどの「詳細度」について教えるタイミングがあって、
日本語でパッと見で分かりやすく伝えられないかなと思って作りました。

※詳しい内容は以下の参考にさせていただいたサイトをよく読んでいただければ幸いです。

#参考サイト 

* [詳細度 - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/Specificity)
* [Specifishity :: Specificity with Fish](https://specifishity.com/)
* [Specifics on CSS Specificity | CSS-Tricks](https://css-tricks.com/specifics-on-css-specificity/)
* [エンジニアはもう一度CSSとちゃんと向き合ってみよう - 詳細度編](https://qiita.com/izumin5210/items/8ae78cb4f4bd325bccb4)

#作ったもの

![CSS詳細度 早見表](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40940/69696615-1cb1-d47c-a42c-5d409c0371fc.png)

#以下、数え方の例

ちなみに、例4とか同じタグではないので比べるものではありませんが、
「例4 &gt; 例2と3 &gt; 例1」の順に詳細度は高いです。

##例1

※結合子の `>` は数えない！

![例1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40940/ce8624a9-edd4-d5c3-6d44-1ef125300d4f.png)



##例2

![例2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40940/105dceff-02e8-32eb-9f55-64f00dcbc70b.png)


##例3

※ `:not()` は数えない！

![例3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40940/2b6a5ac4-39be-0b19-11eb-18774fd92c80.png)

##例4

![例4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/40940/083c1fdd-835a-f3ea-d1c4-43be6c38792b.png)
二分検索（バイナリーサーチ）とは何か

## 検索アルゴリズムについて
検索アルゴリズムとは、データがソート（整列）されている場合に使用することができ配列に入ったデータを高速で検索することができます。

しかし、配列が整列されている場合のみに使用することができるので、配列を整列する考慮が必要になってきます。配列が整列されていない場合は、線形検索を用いることが可能になります。その場面毎に使い分けることができます。
整形検索はこちらです。↓
https://qiita.com/WI2rz/items/aa5893f1092515990769

## 解説
二分検索は配列の中央の数値と比較し、検索範囲を絞ることで、データを見つけることができます。


今回は１〜１０の入った配列から二分検索をするとします。
検索したい値は６だとします。

```
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
　　                 ↑中央
         5以下は切り捨て。
```
この場合の中央とは１０/２とすると５になりますので、５が中央にあたります。
中央と値を比較をします。すると5 < 6なり５より左は必要ないため、配列の候補からは外されてしまいます。
そのため結果としては以下のようになります。

```
        6, 7, 8, 9, 10
              ↑中央
　　　　　8以上は切り捨て
```
そして先ほどと同じように、残りの配列から中央を決めて、値と比較を行います。
今回であれば、中央は８になりますので6 < 8となります。
ですので、８以上の値は候補から外れます。

```
        6, 7
```
このようにして、最後の数値が一つになるまで繰り返し行われます。
そして最後に残った数値が期待する値と一致すれば、結果として返ってきます。
一致しない場合は、別の用意した、結果で返るようにすれば完成です。

上記では残り6,7が残りました、この時の中央はどちらでも構いません。
６にすれば、中央と値が等しくなり６の結果が返ってきます。
反対に、７が中央とすれば、6 < 7になりますので、７は候補から外れます。

```
         6
```

結果として、残るのは６ですので期待した値と一致するようになりました。
このようにして、配列を検索毎に中央で分割することで、高速で検索を実現することが可能になります。
rubyでコードに表すと、このようになります。

```ruby
def array_search(array, num_elements, target)
  left = 0
  right = num_elements - 1
  while left <= right
    center = (left + right) / 2
    if array[center] == target
      return center
    elsif array[center] < target
      left = center + 1
    else
      right = center - 1
    end
  end
    return -1
end

array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
target = 6
num_elements = array.length

result = array_search(array, num_elements, target)
puts "#{target}は配列の#{result + 1}番目にある "


#6は配列の6番目にある
```

## 以上
アルゴリズムは今後プログラミングに関わらず、必要不可欠だと思いますので積極的に取り込んでいきたいと思います。
修正箇所やアドバイスがあればコメントお願いします。
Linuxサーバー関連備忘録

##OSが最後に再起動された日時を確認する

```shell
last reboot
```

##決まった日に処理するスクリプト

```shell
rebootDate=`date "+%d"`
if [ ${rebootDate} -eq "15" ]; then
  #例：15日に実行したい処理をここに書く
fi
```
Apple M1版 MacでEmscripten SDK 設置問題

#問題点
<b>Apple M1 搭載のモデル(Mac Mini)で、Emscripten SDKのインストールに問題。</b>

EMSDKをダウンロードし、

% ./emsdk install latest
>Error: No tool or SDK found by name 'sdk-releases-upstream-4764c5c323a474f7ba28ae991b0c9024fccca43c-64bit'.


の問題があり、そこでソースからのインストールを試みました。

% ./emsdk install sdk-upstream-master-64bit
>…
…
Done installing tool 'llvm-git-master-64bit'.
<b>Manifest error: No tool by name 'node-12.18.1-64bit' found! This may indicate an internal SDK error!</b>
Traceback (most recent call last):
  File "/Users/＜ユーザー名＞/emsdk/./emsdk.py", line 3082, in <module>
    sys.exit(main())
  File "/Users/＜ユーザー名＞/emsdk/./emsdk.py", line 3062, in main
    success = tool.install()
  File "/Users/＜ユーザー名＞/emsdk/./emsdk.py", line 1840, in install
    return self.install_sdk()
  File "/Users/＜ユーザー名＞/emsdk/./emsdk.py", line 1851, in install_sdk
    success = tool.install()
AttributeError: 'NoneType' object has no attribute 'install'

LLVMまではインストールできたが、Nodeの設置に問題があるということで、emsdk_manifest.jsonを見るとその記述があり、スキップさせてみましたが、後でNodeがないとエラーが出ました。

それから、この記事に辿り着き、問題は(とりあえず)解決しました。
https://github.com/emscripten-core/emsdk/issues/671


#解決方法

① ダウンロードしたemsdkフォルダ内、<b>emsdk.pyの112行目</b>を以下に修正。
<b>machine = 'x86_64' </b>



② Terminal を、Rosettaを使用して開くようにする
ユーティリティ → ターミナル を右クリック → 情報を見る → 一般情報内の → <b>「Rosettaを使用して開く」</b>


③ この後、もう一度SDKのインストールを試みる
% <b>./emsdk install sdk-upstream-master-64bit</b>


④ Activateする
% <b>./emsdk activate sdk-upstream-master-64bit</b>



* インストール完了後、emscriptenへの<b>PATH登録</b>は必要です。
* 参考にしました記事にもあるように、あくまでこれは<b>ビルド過程を固定させて通しているだけ</b>ですし、本質的なサポートが必要でしょうね。



#環境

#####以下、自分の環境になります。<br>私はNodeのバージョンをnvmで修正しましたので12.18.1になっていますが、最新のバージョンでも今回の問題では大丈夫ではないかと思います。


<b>a. Homebrew </b>
% brew --version
Homebrew 2.7.7


<b>b. Node</b>
% nvm --version
0.37.2

% node --version
v12.18.1


<b>c. Python</b>
% pyenv --version
pyenv 1.2.22


% python --version
Python 3.9.1


<b>d. XCODE</b>
% xcodebuild -version 
Xcode 12.3
Build version 12C33


<b>e. GIT</b>
% git --version
git version 2.30.0


<b>f. Make</b>
% make --version
GNU Make 3.81


<b>g. CMake</b>
% cmake --version
cmake version 3.19.3
脱メールしてMicrosoft Teamsに変えたら、業務が円滑になり、とても快適になった話

うちの会社ではMicrosoft Teamsを数年前に使い始めましたが、最近ではコミュニケーションの8割程度がTeamsになりました。

メールは全社アナウンスや、社外向けにしか使っていない状況です。

「まだメール使ってるんですか？」

そんな言葉を数年前の自分にかけたいぐらい便利なのですが、そこに至るまでは紆余曲折ありました。

そこで、今回はMicrosoft Teamsの便利さと、その移行の顛末を記したいと思います。


# メリットの数々

まずは、うちの会社で感じた、移行のメリットから。

これらのメリットが移行の説得材料になればと思います。

## 書く文章量が減った

「ありがとうございます。よろしくお願い致します。」だけのメールをCcで入れられるアレって、無駄だなーと思っていた方に朗報です。

メールだと、宛先だけじゃなく、場合によっては「お疲れ様です。」などの前置きと、文章の体裁など様々な点を気にする必要がありました。

しかし、グループウェアは掲示板のように誰かが投稿したコメントに返信していくだけなので、文章量が少なくて済みます。

また、文章は投稿した後にいくらでも編集できるので、送った後に「やっちまった！」ということも減ります。

## 「イイね」

Teamsになってから、コミュニケーションに変化が起こりました。

それは、投稿へのリアクションです。

メールには無い機能ですが、（実はOutlook onlineならありますが・・）

自分の書いたことに何らかのリアクションがあると、自然とちょっとした情報でも展開しようという気になり、

気づけば活発にコミュニケーションが取れる雰囲気になっていました。これは非常に良いことです。


## 共同編集

Teamsでは、Officeファイルを共同編集できます。(実際はOffice 365の機能ですが)

今までメールで、このファイルに追記してねという案件は、ファイルをダウンロードして書き込み、

ファイル名を_name.xlsxのように変更して送り返すのような手間があったと思います。

これがTeamsになるとURLからファイルを開けば、そのままファイルを編集出来て、複数人同時編集も可能です。

そのため、最後にまとまったファイルを統合する必要が無くなりますので、効率が良いです。



# 移行へのハードル

では、グループウェアの良さが分かったところで、ここからは移行までの紆余曲折を記したいと思います。

まず、うちの会社ではOffice 365は契約してるものの、全社的にTeamsを使おうというアナウンスは出ておらず、好きに使って良いというスタンスでした。（未だにそう）

そんな中、とある小さなプロジェクトに呼ばれた際に、Teamsを活用していました。

いざ、自分のチームに戻ってきたときに、「Teams便利なので使いませんかー？」と働きかけ、周りの人たちは使ってくれることになりました。

しかしながら、チーム以外の方とのやり取りはメールですから、Teams 20% メール 80%となれば、段々と使い分けが面倒になり、使わなくなっていきます。

確かに便利なのですが、あくまで「皆で使えば」なんですよね。


# 誘い込み作戦

ここで立ち止まって、どうやったら皆使うようになるだろう？と考えた時に、

「皆が知りたい情報がそこにしか無ければ、使わざるを得ないのでは？」

と思い付き、当時私が担当していたファームウェアのリリース案内をTeamsで見れるようにしました。

「毎回メールが来るのが鬱陶しいという方がいたので」という適当な言い訳をしつつ、Jenkinsから自動Postするようにしたのです。

「移行しますよ」の案内を出しても、その後しばらくは「リリースメールどうしたっけ？」と問い合わせがあるわけです。

最初だけだと思い、ここは辛抱してTeamsを案内し続けます。

そのような取り組みで、じわじわと「Teamsってツールがあるらしいぞ」と名前が浸透していきました。


# プロジェクトになった

ある時、社内で業務効率化のタスクフォースが結成されることになり、そこに抜擢されることになりました。

その場にアイディアとして「Teams使えば、メールにかける時間が最適化され、業務効率化につながりますよ！」と持ち込んだところ、

とある方から「年に一度、ある重要な会議に向けた資料を作るために、その時期になるとメールと資料が大量に飛び交っている」というプロジェクトがあるとの情報。そこのリーダーを巻き込んで、今年はTeamsでやってみようということになりました。

実際にやってみたところ大成功で、Teamsの名が一気に知れ渡りました。


# 思わぬきっかけ

移行にさらに拍車をかけたのは、このコロナ禍です。

オンラインミーティングツールが必要になったので、必然的に皆がインストールせざるを得ない状況になったのです。

俺は使わんぞと頑なだった人達も一気に移行が進みました。

グループウェアとオンラインミーティングツールがオールインワンになっているので、使う側とすれば分かりやすくて便利なんですよね。

# 今では

前述したように、仕事上のコミュニケーションにメールは使わなくなりました。

まだ全社へのアナウンスなどはメールですが、そう頻繁に来るものでもないので、

私のメールボックスは、最近ではそのほとんどがメールマガジンです（笑）

数年前はここまですっきり移行できるとは思いませんでしたが、いざこうなってみると、もう戻れません。

#Teamsが苦手なこと
Teamsだって苦手なことがあります。

それは、TODO管理です。

メールだと、タスクが存在するものはフラグを付けておいて、順に消化するという使い方ができました。

例えば、コードレビューの依頼が何件も来ている場合などです。

Teamsはどんどん新しい情報が流れていくので、過去に遡って未完了項目を拾う作業が苦手です。

メッセージを保存しておくという機能もありますが、イマイチ使いづらい・・・

ここは割り切って、コードレビュー依頼はメールで飛んでくるようにして、そちらで管理することにしています。

Teamsもメールもそれぞれ得意な事だけ任せちゃえばよいと思います。

# やっぱりTeamsは良い

グループウェアがいくら普及しても、棲み分けが完全に出来ているため、メールは決して無くなりませんが、メールじゃなくてもよい部分は積極的にグループウェアに移行した方が幸せになれます。

どこでTeams使おうか悩むなら、実際使ってみると「これもTeamsでいいじゃん」という場面に出くわすので、都度切り替えていけばよいと思います。

この記事が、移行に悩む方の参考になれば幸いです。
PostGISでGeometryTypeを取得する方法

# テーブルにレコードが存在する

図形データを登録する際に、そのテーブルの図形タイプに適した形式にフォーマットしなければならないケースがあった場合、事前にテーブルの図形タイプを取得しなければなりません。
既にデータが存在するテーブルであれば適当なレコードのgeometry列に対して<a href="https://postgis.net/docs/GeometryType.html">GeometryType</a>関数を使うことで取得することも可能です。

```
SELECT
	GeometryType(geometry_column) AS geometry_type
FROM
	polygon_table
LIMIT 1
```

結果: POLYGON

# テーブルにレコードが存在しない

しかし、テーブルにレコードが存在しない場合は上記のような方法は利用できません。
ではどうするかというと、GEOMETRY_COLUMNSビューを利用します。

<a href="https://aginfo.cgk.affrc.go.jp/docs/pgisman/2.2.0/using_postgis_dbmanagement.html#geometry_columns">参考サイト</a>より抜粋
>2.0.0より前のPostGISでは、直接編集可能なgeometry_columnsはテーブルでした。時々、実際のジオメトリカラムとの同期が取れていませんでした。PostGIS 2.0.0では、GEOMETRY_COLUMNSは、以前の版と同じ外見構造を持ちますが、データベースシステムカタログから読むビューになりました。構造は次の通りです。

```
View "public.geometry_columns"
      Column       |          Type          | Modifiers
-------------------+------------------------+-----------
 f_table_catalog   | character varying(256) |
 f_table_schema    | character varying(256) |
 f_table_name      | character varying(256) |
 f_geometry_column | character varying(256) |
 coord_dimension   | integer                |
 srid              | integer                |
 type              | character varying(30)  |
```

次のようなSQLで任意のテーブルの図形タイプ(GeometryType)を取得できます

```
SELECT
	"type" AS geometry_type 
FROM
	geometry_columns
WHERE
	f_table_name = 'polygon_table'

```
結果: POLYGON
ActiveStorageで恒久URLの取得をサポート(Rails v6.1.0)

# 今まで
今までのActiveStorageは `rails_representation_url` を利用して恒久URLを発行できていたが、Railsガイドにも載っておらず割とハック的な感じでの利用でした

参考
https://qiita.com/shwld/items/07a2cdc19c38156e52ae
https://qiita.com/nmbakfm/items/f7c1f9ea77346891bb8c

# ActiveStorageで公開URLの取得が可能に
Rails 6.1 にあげる作業をしていた時に素敵なアップデートを発見
https://github.com/rails/rails/releases/tag/v6.1.0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280595/abaf59a2-4faf-982a-cb5b-0d147cdec22d.png)

どうやら `rails_storage_proxy_path(_url)` と言うのと `rails_storage_redirect_path(_url)` と言うヘルパーが追加されたようです。

## さっくりと手元で確認
```rb
class User < ApplicationRecord
  has_one_attached :avator
end

user = User.first
rails_storage_proxy_path(user.avator)
# => "/rails/active_storage/blobs/proxy/*****/sample_avator.png"
rails_storage_redirect_path(user.avator)
# => "/rails/active_storage/blobs/redirect/*****/sample_avator.png"
```

なるほどblobsの後ろがproxyなのかredirectなのかで違うのですね
localhostで該当のURLにアクセスしたらちゃんと画像が表示されました
![スクリーンショット 2021-02-05 11.26.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280595/2cc88831-9e16-ceca-17a8-cf7843b689db.png)

## それぞれの挙動を確認してみた
### rails_storage_proxy_urlにアクセスしてみる
`HTTP/1.1 200 OK` で返ってきていることがみて取れる
Railsの方からファイルサービスにアクセスしてファイルデータを200ステータスで返してきている

```bash
$ curl -I http://localhost:3000/rails/active_storage/blobs/proxy/*****/sample_avator.png
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Date: Fri, 05 Feb 2021 02:27:10 GMT
ETag: W/"0749f09799210b2111ecea087fd54baa"
Last-Modified: Fri, 31 Dec 2010 15:00:00 GMT
Content-Type: image/png
Content-Disposition: inline; filename="sample_avator.png"; filename*=UTF-8''sample_avator.png
Cache-Control: max-age=3155695200, public
X-Request-Id: f15dee76-089f-46f9-b336-e965bec65b38
X-Runtime: 0.005844
```

### rails_storage_redirect_urlにアクセスしてみる
`HTTP/1.1 302 Found` で返ってきていることが見て取れる
Railsの方でファイルサービスに時限式URLを取得してきているそこにリダイレクトさせているのかな

```bash
$ curl -I http://localhost:3000/rails/active_storage/blobs/redirect/*****/sample_avator.png
HTTP/1.1 302 Found
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Date: Fri, 05 Feb 2021 02:27:31 GMT
Location: http://localhost:3000/rails/active_storage/disk/*****/sample_avator.png
Content-Type: text/html; charset=utf-8
Cache-Control: max-age=300, private
X-Request-Id: 87c221d3-e894-41ef-bebf-e65f155a40a6
X-Runtime: 0.015136
```

# 結論
便利！
今までは自前でActiveStorageをラッピングして公開エンドポイントを取得できるメソッドを生やしていたので愛でたく削除PRを投げられました
firewalldで接続拒否(drop)しているIPアドレス(sources)を抽出・改行して表示するワンライナー

firewalldで拒否（dropゾーン）しているIPアドレスを抽出・改行して表示するワンライナー。

ちなみに以下に出てくるIPアドレスは実際に攻撃（=脆弱性を探すリクエストを公開サーバに送信）に使われていたものです。

## 環境

| 項目 | 値 |
|:--|:--|
| OS  | Amazon Linux2  |
| firewall-cmd -V  | 0.4.4.4 |


## 結果

以下で期待を実現できましたが、極めてハードコーディング。何かシンプルな記述はないかな。

```
# firewall-cmd --zone=drop --list-all | grep sources | sed 's/  sources: //g' | sed 's/ /\n/g'
```

出力例

```
131.213.85.78/32
51.79.192.195/32
45.155.205.108/32
106.12.50.249/32
119.45.145.115/32
128.14.133.58/32
18.197.125.181/32
20.51.243.137/32
20.52.131.71/32
142.93.153.242/32
157.230.58.110/32
```
s3からディレクトリごと再帰的にダウンロードしたい

ディレクトリごと再帰的にダウンロードボタンは2021年2月現在
まだコンソールにはないようです。

# 前提
AWS CLIを使用します。
CLIとはコンソールでやっている操作をコマンドで操作することです。
今回のようにコンソールでできないこともできたり
連番フォルダを1万個作るなどのように
コンソールでぽちぽち操作では時間がかかることを一瞬でできたりします。

[公式サイト](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-s3-commands.html)

学習コストもかかりますが受けられるメリットはそれより大きいと思います。

# aws cliを使ってディレクトリを再帰的にダウンロード

```recursive.shell

$ aws s3 cp s3://test_bucket/test/ ./folder --recursive
（念のためrecursiveは再帰的という意味）
aws s3 cp バケットのパス ダウンロード後のローカルのパス オプション

```

#最新バージョンのみのオブジェクトを対象として、S3 バケットの合計サイズを確認する
ディレクトリを再帰的にダウンロードした後に確認する時に使えます。

```onlyLatestTotalS3.shell

$ aws s3 ls s3://examplebucket/ --summarize --recursive --human-readable

```


# バージョニングされたバケットのバージョンも含めた合計サイズ取得
s3はgitのようにバージョン管理も行ってくれます。
その過去のバージョンも含めたデータ合計サイズを知りたい時のコマンドです。

```S3ls.shell

$ aws s3api list-object-versions --bucket examplebucket --query "[sum(Versions[].Size)]"

```

# バケットからファイル名を指定して１つダウンロード

```s3cp.shell

$ aws s3 cp s3://examplebucket/filename ./currentdirectory

```


# バケットから最新の100件を表示

```s3ls.shell

$ aws s3 ls s3://examplebucket --recursive | sort -r -n -k 3 | head -n 100

```
Figma: Font Awesome のアイコンを使う方法

<a href="https://fontawesome.com" rel="noopener" target="_blank">Font Awesome</a> へアクセスします。

<img src="https://i.gyazo.com/f3d19e033841531df9c23eed9fdfdbbc.png" alt="Font Awesome 公式サイト" />

<br>
<hr>

アイコン部分をクリックしてコピーします（マウスオーバーで「<strong>Copy Unicode Glyph</strong>」と出るアイコンをクリックします）。

<img src="https://i.gyazo.com/343dc020d65bc95ea9c6a335c507beb0.png" alt="アイコン情報をコピーします" />
<br>
Figma にペースト（Ctrl + V) します。
<br>
<hr>

テキストフォントを <strong>Font Awesome 5 Free</strong> にし、スタイルを<strong> Solid </strong>を選択（フォントによっては Regular ?)します。
※ フォントを「Font Awesome 5 Free」にしただけでは反映されません。<span class="red">スタイルの指定が必要！</span>（私はここでハマりました！）

<img src="https://i.gyazo.com/bc10d07cadf06a55438b3ebe48c0953e.png" alt="Figma でのフォント設定" />

<br>
以上です。

※自分の[ブログ](https://web-atelier-midori.com/blog/?p=3233&preview=true)でも公開しています
Gravio 4でセンサーとのペアリングが切れたときにGmailに通知する

[Gravio][]のレンタルセンサーはZigbeeというプロトコルを使うものが多いです。このZigbee、レシーバーとのペアリングが必要なのですが、いろいろ試してる間に他のGravioにペアリングしてしまっていたのをリセットしたり~~、電池が切れてペアリングが外れたり~~することがたまにあります。
そんなときには、HubKitの「通知用メールサーバー設定」を設定しておくと、ペアリングが解除されたときにメールが飛んできて便利です。

# 使用環境

* Raspberry Pi 4 Model B w/4GB RAM
* Ubuntu Server 20.04.1 LTS
* Gravio HubKit v4.1.0-6380
* Gravio Studio v4.1.5432.0

# 手順
# Googleアカウントでアプリパスワードを作成
Gmailを使う場合、２段階認証が有効にしてあるとちょっとテクニックが必要で、「アプリパスワード」を作成して使います。通常のGmailのパスワードは使えないので注意してください。

Googleアカウントを開き、「セキュリティ」-「アプリパスワード」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/9ca430b1-8d0e-0e9f-49bd-f8a0802dc8e4.png)

本人確認のためパスワードを入力して続行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/dc114823-e305-3c04-1470-68e2c2a2f2d6.png)

その他を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/861a7c86-31ee-ad78-e0a6-bf8d1c29788e.png)

Gravioなど、名前を入力し、生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/981c14cd-388f-0305-557e-4bb905d5d9e9.png)

アプリパスワードが生成されますので、これを後で使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/455e1320-b4f5-c5c0-b9cd-996e4ed2f6b4.png)

ちなみに、このアプリパスワードは二度と表示されません。削除することしかできないので注意してください。忘れてしまった場合はまた作成しましょう。

## メールサーバーの設定
Gravio Studioから対象のHubに接続し、「設定」タブを開きます。
「HubKit情報」のページが開かれてますので、下記のようにメールサーバーの設定を指定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/ee8f92fd-e34a-6363-ac0f-136caa9d3eaa.png)




| 項目 | 設定内容 | Gmailの場合 |
|:-----------|:------------|:---|
|メールサーバー|SMTPサーバーのホスト名|smtp.gmail.com|
|ポート番号|SMTPのポート番号|587|
|送信メールアドレス|通知メールのFromアドレス|ご自分のメールアドレス|
|SMTPユーザー名|SMTP認証のユーザー名|Gmailのメールアドレス|
|パスワード|SMTP認証のパスワード|Googleアカウントで作成したアプリパスワード|
|送信先メールアドレス|通知先のToアドレス|通知先のメールアドレス|

## テスト送信
「テストメール送信」ボタンをクリックします。少し時間が立つとダイアログが出て完了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/35a06e7a-e002-a97e-d9f2-75b64bb4eb95.png)

Gmailでテストメールが届きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/7d23614c-0719-47a2-d7a0-0b5f727285e7.png)

## 設定する
最後に「設定する」をクリックして設定を保存します。これをしないとサーバーには保存されませんので注意してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/9cf97282-94d5-6b65-5e3a-d935f4668f48.png)

## 試してみる
試しにペアリングを切ってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/46a86a5f-eaaf-e0eb-f0b9-f4c17a8b2964.png)

このようなメールが届きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41603/a66fdb3a-d88c-b0a7-034f-8465e9ae15d1.png)


# ポイント
* Gravioの通知メールサーバーを設定しておくと、ペアリングが切れたときにメールで通知が届く
* Gmailを使うときにはアプリパスワードを使用する

# 参考
* [Gravio][]
* [設定タブ](https://doc.gravio.com/manuals/gravio4/1/ja/topic/settings-tab)
* [アプリ パスワードでログインする - Gmail](https://support.google.com/mail/answer/185833?hl=ja)


[Gravio]:https://www.gravio.com/jp

(2022/2/17追記)
電池切れの際にはこの仕組が使えないことが判明したため、記載を削除しました。
binary_searchメソッドを使って数字を何番目か検索する方法　　初心者です🔰

以下の配列に任意の値が存在するかどうか、そして何番目に存在するのか、検索するコードを作成しましょう。
添字が0の要素、つまり以下の配列における「1」は「配列の0番目に存在する」と表現します。

```ruby
array=[1,3,5,6,9,10,13,20,26,31]
```
任意の値が配列内に存在しない場合は、「値は配列内に存在しません」と表示し、
存在する場合は、配列の何番目にあるかを表示してください。

検索はバイナリーサーチ（2分割検索）を使用して行います。

答え

```ruby
def binary_search(array, number_of_elements, target)
  left = 0
  right = number_of_elements - 1
  while left <= right
    center = (left + right) / 2
    if array[center] == target
      return center
    elsif array[center] < target
      left = center + 1
    else
      right = center - 1
    end
  end
  return -1 
end

array=[1,3,5,6,9,10,13,20,26,31]

puts "検索したい数字を入力してください"
target = gets.to_i
number_of_elements = array.length

result = binary_search(array, number_of_elements, target)

if result == -1
  puts "#{target}は配列内に存在しません"
else
  puts "#{target}は配列の#{result}番目に存在します "
end
```

前半で中央要素の定義と検索したい要素が右か左かにあるかを確かめながら検索をしています
何も当てはまらないときは return -1 を返します。
例題で31を代入していた物があったのでそちらも紹介します。

```ruby
#1回目のループ
leftは0
rightは9
5行目：centerは4（小数点以下は切り捨てされます）
6行目：if array[center]==31
          #=>array[center]は9なのでfalse
8行目：elsif array[center] < target
          #=>array[center]は9であり、31より小さいのでtrue
11行目：leftはcenterに+1して5となる 

#2回目のループ
leftは5
rightは9
5行目：centerは7
6行目：if array[center]==31
          #=>array[center]は20なのでfalse
8行目：elsif array[center] < target
          #=>array[center]は20であり、31より小さいのでtrue
11行目：leftはcenterに+1して8となる 

#3回目のループ
leftは8
rightは9
5行目：centerは8
6行目：if array[center]==31
          #=>array[center]は26なのでfalse
8行目：elsif array[center] < target
          #=>array[center]は26であり、31より小さいのでtrue
11行目：leftはcenterに+1して9となる 

#4回目のループ
leftは9
rightは9
5行目：centerは9
6行目：if array[center]==31
          #=>array[center]は31なのでtrue
```
となります。
この問題の感想ですがスクール卒業時期が今月なのですがこの問題は初心者向けなのでしょうか？
かなり難しく感じまだ自分の実力では解けませんでした。
早く一人前のエンジニアになりたいと思う日々です。
🦆Manta60ビルドログ🦆

![iOS の画像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66988/73cc56c4-26fb-0b55-8c0f-f5ade12533e7.jpeg)


去年、おうち時間のお供として [Manta60](https://yushakobo.jp/shop/consign_manta60/) を作ったので、その記録です。
半年近く Qiita の下書きに眠っていたので叩き起こしました。
なお、この記事は Manta60 を使って書いています。

# Manta60を選んだ理由

[Mint60](https://lilakey.com/products/mint60-starter) で自作キーボードに入門し、これが2つ目の自作キーボードになります。
今回は、なるべく Mint60 とは異なる要素を持ったキーボードを作ろうと思い、以下の条件を満たすものを探しました。

- Row-Staggered ではない
    - Column-Staggered か Ortholinear（格子配列）
- なるべくホットスワップ
    - キースイッチはソケット式，ProMicro はコンスルーといったように，トラブルがあったときに交換しやすくしたい。
    - このへんは Mint60 からの学び。
- パーツの調達
    - Mint60 ではキースイッチやキーキャップなど全て揃ったものを購入していたので、今回は自分で調達してみたかった
- 表面実装
    - ただ単に，今まで表面実装をしたことがないので，挑戦してみたい。

上記の条件を満たし、かつ個人的な好みとして「親指キーが多い」を満たすものとして、Manta60 を選択しました。

# 買ったもの

[ビルドガイドの事前準備の項](https://github.com/KamoNanban/Manta60/blob/master/documents/manta60_buildguide.md#0%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99) を参考に必要なものを揃えていく。

- Manta60 rev.1.1  
https://yushakobo.jp/shop/consign_manta60/  
※2021年2月現在は在庫切れ
- ProMicro  
https://yushakobo.jp/shop/promicro-spring-pinheader/  
- LED(WS2812B)  
ピカリ館で買った。  
https://www.akiba-led.jp/product/933
- キースイッチ  
AliExpressで買った。  
Mint60では赤軸を選択したが，今回は茶軸を選択。  
https://www.aliexpress.com/item/32980039972.html
- キーキャップ  
AliExpressで買った。  
プリントが目立たないキーキャップということでこちらを選択。  
https://www.aliexpress.com/item/32861971291.html
- 温調はんだこて(FX600)
- フラックス
- カプトンテープ  
表面実装ということで、この辺を改めて買い揃えました。このへんはAmazonで購入。


AliExpressでの部品調達も目標のひとつだったので，こちらも達成。
日本に到着するまでは早かったのですが，日本に到着してから2週間くらい待たされて，結局手元に届くまで1ヶ月ほどかかりました。 

# 組み立て
https://github.com/KamoNanban/Manta60/blob/master/documents/manta60_buildguide.md
を参考に進めていきました。

# ハマったこと

## LEDが光らない
Twitter で作者の鴨南蛮氏に質問しました。
基板上での LED の順序を教えていただいたので、それに従って確認していったら解決できました:bow:

その後気づいたんですが，[KiCad の図面が GitHub にある](https://github.com/KamoNanban/Manta60)ので，こちらをもとに確認すればよかったです。

しくじり原因としては，LED の各端子に載せるはんだが全体的に甘かったこと。一つずつはんだを載せ直したら光るようになりました。

## ボトムプレートを組み合わせると回路が短絡する

ボトムプレートをつけると、意図しない入力が行われたり、LED が荒ぶったりしていました。
結局，まだ根本原因はわかっていませんが、スペーサを増やしたら直りました。
ただまあ，この辺もはんだが甘い部分が残ってるのが原因な気がしています。見直し大事。


# 感想
## Manta60の感想

- 親指キーの多さは最高。
    - ほとんど親指でやりたいことができる。
    - 今はオーソドックスに Space, Return, BackSpace, Command あたりをマッピングしている。
- 格子配列もはじめは慣れなかったが，一週間くらいつかったら慣れた。
    - 慣れると Row-Staggered より入力しやすい。
    - 一方で，通常の Row-Staggered キーボードが打ちづらくなってしまった。
    - もともと会社と自宅の両方で分割キーボードを使いたかった背景もあって，会社に Mint60 を置きっぱなしにしようと思っていたのですが，上述の通りとなってしまい結局 Manta60 を持ち歩くことに。。
- かばんに入れて運ぶとたまにキースイッチごと外れてることがある
    - ホットスワップの宿命
- デフォルトのキーマップだと，B キーが右手側に来るため，はじめはちょっと違和感があった。
    - これはマッピング変えることも検討したが，検討しているうちに慣れたのでこのままにしてある。

## To Be Continued...

Manta60 でエンドゲームかなと思ったのですが、40％キーボードが欲しくなっています。
60％キーボードはやっぱり数字キーの行が遠く感じるし、数字キーがなかったとしてもレイヤを適切に使用すればよいので。

また、Column-staggered キーボードも気になっています。格子配列よりも入力が楽そう。