OmniAICreator/Qiita-1.07M huggingface dataset:
フロントエンド未経験から1週間程度で作るポートフォリオ(Vue.js AWS)

# 動機・やったこと
インフラ&バックエンドは少しだけ経験がありつつもフロントエンドについてはhtmlとcssの基礎の基礎を少し知っている程度で何もできることが無く、これまで機能最低限の質素な見た目のWEBアプリしか作成経験がありませんでした。
そんなわけで何か勉強しようと思い立ち、**フロントエンドの基礎学習** 兼 **自分のアウトプットをまとめる場所**をつくるため、Vue.jsでポートフォリオサイトの作成を行いました。

# 作ったもの
以下が作成したページです。
https://hamabe.info

# 意識した点・技術メモなど
大雑把な振り返りのため、具体的な技術の解説は今回は割愛させていただきます。

## パッと見で「イイ感じ」であること

100％主観ですが、フロントエンドで最も重要な要素は「イイ感じ」であることだと思っています。なのでまずは「最低限自分が『イイ感じ』だと思えるサイト」であることを目標にしました。
「イイ感じ」をもう少し具体的に言うと **「人やお店や会社の公式サイトにアクセスした時に『なんかショボいな』と感じない程度のクオリティ」** です。これが今回の目標でした。
客観的に見てこれが達成できているのかは微妙なところですね…。コメントいただけると嬉しいです。

## 様々なジャンルのアウトプットを統括できるサイト構成

今回エンジニアとしてのみではなく、自分がやってきたこと（電子工作、楽器演奏、ブログ、絵 など）を全て統括できる、いわば**自分の公式サイト**のようなものを作ろうと考えました。そのために汎用的使い回せるコンポーネントを作成し、それを並べる形でYouTubeやQiitaやnoteなど異なるプラットフォームでのアウトプットをまとめて表示できるようにしています。よくあるパターンですね。
これを通して「コンポートを使い回す」というVue.jsの基本的な考え方を感覚的に理解することができました。

## 外部APIの利用

axiosを用いた外部APIとの連携も試してみようと思い、上記のコンポーネントに[note API](https://note.com/hagure_melon/n/n964ff6f7ad0e)で取得した情報を渡すことで自分のnoteの最新記事を自動取得・表示を行っています。その際**CROS（クロスオリジンリソースシェアリング）**という壁にぶつかり少しハマってしまいましたが、なかなかいい勉強になりました。


## AWS S3+CloudFrontの構成

AWSの学習も兼ねて、作成したページはS3の静的ホスティング機能を使って公開し、 お名前.comで取得したドメインをRout53で紐付け、ACMで無料SSL証明書を発行しCloudFront経由でSSL化、という構成で公開してみました。
この構成での注意点として、CloudFrontを経由した場合S3のファイルを更新した際にCloudFront側のキャッシュをクリアしないと更新が直ちにホームページ上に反映されない点があります。よって今回はLambdaを使ってS3バケットの更新をトリガーに自動でCloudFrontのキャッシュクリアを行う設定も行いました。
今後 API Gateway + Lambda で作成したAPIを連携させたWEBアプリの作成なんかもやってみたいと思います。




# 所感・要改善点など

## フロントエンド所感

今回ロゴの作成やカラーリングなど、デザイン面もイチかバチか全て自分でやってみたのですがどうしても素人の限界を感じてしまいました。（素人の付け焼き刃でどうすることもできない領域だからこそデザイナーという職業が必要なのですね。）
また、フロントエンドのコーディングは普段のプログラミングで使う脳とは全く違う部分を使っている感覚で新鮮でした。今の時点では慣れの問題もあって、正直フロントはあまり向いていないのでは…？という所感ではあるのですが、コードがダイレクトに見た目に反映される楽しさはバックエンドにはないもので、とても良い経験になりました。

## デザインに終わりは無い

勉強を初めて3日程度でおおまかなページ全体は完成できたのですが、その後のcssやコンポーネントの微調整にまじで終わりがないなと思いました。結局ある程度の及第点というか、「一旦この辺でやめておこう」となるまでに1週間ほどかかったのですが、デザインの恐ろしさというとフロントエンドの恐ろしさというか、その断片を味わったような気がします。
もちろん納期がある場合は話が別ですが…。

## とにかくコードが汚い

僕のいい点でもあり悪い点でもある（と思っている）のですが、新しい言語やフレームワークを学ぶ時どうしても体系的に学ぶ前に勢いで書き始めてしまう癖があります。そのせいで「こうやれば動くかな」という試行錯誤の跡がそこらじゅうに残っていたり、「こういう書き方のほうがいいんだな」みたいな気づきが断片的にしか反映されていなかったりして汚いコードが爆誕してしまいます。
特に今回はフロントの予備知識があまりにも少なく、cssの散らかり具合などかなり見るに耐えない状況です。少しずつリファクタリングをやっていこうと思います…。
仕事でコーディングする場合などはもう少しちゃんとやるのですが、どうしても個人開発・学習の範疇だとその点が大雑把になりすぎてしまうので困ったものですね。

## 機能面の要改善点

その他機能面で気になっている点として、非同期通信のレンスポンスによってコンポーネントの表示にラグが産まれてしまうという点があります。これについては近いうちに読み込み待ちの表示を実装できればと思っています。また、今後**Outputs**の件数が増えることを想定すると、ページ全体がどんどん縦に長くなってしまうため、全件表示用のページを別途作成しVueRouterで遷移するようにするなど、メインページのコンテンツ量が増えすぎないようなつくりに改善したいと思っています。


# 最後に
お読みいただきありがとうございました。最後にもう一度URLを載せておきます。せっかくなので、興味があれば各コンテンツも覗いていただけると嬉しいです。
https://hamabe.info
現状少しコンテンツが寂しいので、今後も積極的にアウトプットを行い本ページを充実させて行きたい次第であります…！

# 参考サイト
https://alliance7.blogspot.com/2019/01/google-formaxioscros.html
https://blog.s0014.com/posts/2018-12-03-s3-https/#hl8
https://qiita.com/kurararara/items/d76776a7dc2d763a068b
https://qiita.com/shuheq/items/5d905e1c0f9916496485
https://qiita.com/p1ass/items/a01578b782f17f573510
https://qiita.com/kskinaba/items/dcf9693dd034517e114a
AWS VPC概要



AWS VPCの概要についてまとめる。学習目的兼備忘録。

## VPCとは

* AWSが提供するAWSアカウント専用の仮想ネットワーク。
* VPC上でAWSのリソース(EC2など)を起動し、利用することができる。

![VPC_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/3b4bb546-ee2a-b526-9ff5-16bc814dbae6.png)


## VPCの主な設定項目

| 項目                                | 説明                                                         |
| ----------------------------------- | ------------------------------------------------------------ |
| **CIDRブロック**                    | サブネット(ネットワークの分割単位)を指す。<br />VPC作成時にネットワーク範囲をCIDRで決め、そのネットワークをさらに小さいサブネットに分割して使用する。 |
| **アベイラビリティゾーン(AZ)**          | サブネットを物理的に配置する場所(データセンター)。           |
| **インターネットゲートウェイ(IGW)** | インターネットへの出入口。<br />VPCをインターネットに接続する場合に必要。 |
| **ルーティング**                    | どのデータをどこに送るかを制御する。<br />IGWとのデータ送受信を設定し、VPCからインターネットへ接続することができる。<br />設定は、ルートテーブルで保持する。 |
| **セキュリティグループ**            | インスタンス単位で設定を行う仮想ファイアウォール。<br />インバウンド(データが入ること)、アウトバウンド(データが出ること)トラフィックをコントロールする。<br />1インスタンスに対して、最大5つ設定可能。 |
| **ネットワークACL**                 | サブネット単位で設定する仮想ファイアウォール。<br />インバウンド、アウトバウンドトラフィックをコントロールする。<br />1サブネットに対して、1つ設定可能。 |



## VPC作成の流れ

### ①AWSにログインする

1. マネジメントコンソールを開き、リージョンを選択する。
2. VPCダッシュボード画面を開く。

### ②VPCを作る

1. VPC名を設定する。
2. CIDRブロックを設定する。
3. テナンシー(ハードウェア占有)を選択する。※利用する場合、料金が上がる。

### ③サブネットの設定を行う

1. サブネット名を設定する。
2. 対象VPCを選択する。
3. AZを選択する。
4. サブネットのCIDRブロックを設定する。

### ④インターネットに接続する

1. IGWを作成する。
2. IGWとVPCを接続する。
3. ルートテーブルを作成する。


## サブネット

* サブネット=大きなネットワークを小さく分割したネットワーク
* 分割することで通信できる範囲を狭め、ファイアウォールを設定し、セキュリティ境界を作る目的で使用する。
* AWSの場合、サブネットをどこのAZに置くかを設定する。

### CIDR表記

* ネットワーク範囲(IPアドレスの数)の表記方法。
* 「/16」のように「/」の後ろにネットワーク長を書いて表記する。
* 計算例
  * 172.31.0.0/16の場合
        * 172.31.0.0：範囲の先頭IPアドレス
        * 16:CIDR
            * サブネットマスク表記：255.255.0.0
            * 10進数：2^(32-**16**)=65536
        * 172.31.0.0から172.31.255.255まで(65,536個分)がネットワークの範囲

## 参考
* [図解即戦力 Amazon Web Servicesのしくみと技術がこれ1冊でしっかりわかる教科書](https://www.amazon.co.jp/%E5%9B%B3%E8%A7%A3%E5%8D%B3%E6%88%A6%E5%8A%9B-Amazon-Web-Services%E3%81%AE%E3%81%97%E3%81%8F%E3%81%BF%E3%81%A8%E6%8A%80%E8%A1%93%E3%81%8C%E3%81%93%E3%82%8C1%E5%86%8A%E3%81%A7%E3%81%97%E3%81%A3%E3%81%8B%E3%82%8A%E3%82%8F%E3%81%8B%E3%82%8B%E6%95%99%E7%A7%91%E6%9B%B8-%E5%B0%8F%E7%AC%A0%E5%8E%9F/dp/4297108895)
WindowsでHoudiniを使おうとしたらWindowsError: [Error 234] が出る

# 具体的な症状
node infoをクリックすると

```
File "C:\PROGRA~1\SIDEEF~1\HOUDIN~1.499\python27\lib\site-packages\pkg_resources.py", line 1700, in find_on_path
    for entry in os.listdir(path_item):
WindowsError: [Error 234] : 'c:\\users\\hogehoge\\*.*'
```
とエラーが出る。
`pkg_resources.py` の中でなにかが起こっているそう。

# 解決策 (注:対症療法です)
忙しい人のために先に解決策を示しておくと
上記の`pkg_resources.py`の1700行目の

```python:pkg_resources.py
for entry in os.listdir(path_item):
```
を

````python:pkg_resources.py
for entry in os.listdir(path_item.decode('utf-8')):
```
とする。
文字コードを`utf-8`でデコードしてねと指定しているんですね。

# 環境
- Windows10 
- Houdini18.0.499 Indie

# 解決に至った経緯
`WindowsError: [Error 234] Houdini` でググると中国圏の質問サイトが何個か引っ掛かります。
[ここ](https://zhidao.baidu.com/question/1817890531816849108.html)に書いてあることを試してみたらうまく動いた感じです。

# 所感
 Windowsボケっとしたままセットアップすると勝手に日本語でセットアップ始める弊害か文字コード周りでしばしば詰まりますね...
このエラーに関して日本語の情報が見当たらなかったので残しておきます。誰かの助けになれば..!
続・RaspberryPiとデジタルサーボモータで自動アンロック装置を自作してみた

##プロローグ
2018年に辞めた昔の会社のブログ
https://www.indetail.co.jp/blog/170519/
でマイクロサーボを使用した記事を投稿していたのですが、いつ消されるかも分からないので、今のうちにQiitaに引っ越しついでに、サーボをパワーアップさせてみました。

##チェックポイント
###プログラム
記事のポイントだけ抜粋しますが、まずはサーボをコントロールするプログラムです。
最近は色々なライブラリが出てますが、当時使ったデバイスとしてインストールされるServoBlasterは、扱いが簡単なので、今でも便利だなと思いました。
もう開発は止まってるようですが、今でも以下から手に入ります。
https://github.com/richardghirst/PiBits/tree/master/ServoBlaster

インストール後は以下の様に書くとサーボが動きます。
％数で動く位置が決まりますが、これは実際に動かしてみて丁度よい位置を探るしかないですね。
4=のところは、マイクロサーボを接続しているGPIOの番号によって変わりますので、実際の環境に合わせて変更する必要があります。

```pushit.sh
/bin/echo 4=17% > /dev/servoblaster
/bin/echo 4=58% > /dev/servoblaster
```

###デジタルサーボ
次にアンロックボタンを押すアームを動かすサーボモータですが、2017年5月19日の記事で使用したマイクロサーボ（トルク2.5kg/m(4.8v)のTowerPro SG92R）だと、やはりトルク不足でした。
記事公開時は良かったのですが、すぐ開かなくなって、ドアを外からドンと押してあげないとボタンが押ささらない（道産子弁）という残念な状態に(笑)

2018年6月からは横浜に住んでいるので、札幌には月イチしか戻らないし、それでも良いかなと思っていたのですが、札幌のマンションの火災報知器の交換工事が入ることになり、水曜日だったのでその日に戻るのは無理という問題が発生してしまいました。

リモートでアームを動かすことはできるのですが、外からドンと押してあげる時のコツを人に伝えるのが難しくて、電話で教えながらタイミングよくドアドンして開けてもらうのに、とてつもなく苦労しました。
玄関の中にはインターネット経由で見れるビデオカメラも設置しているので、内側から見ていたらアームが動いてひっこんだあとにドアドンしてたりしてタイミング遅いーって思ったり、ドアドンが短すぎてボタンが押ささるところまでいかなかったりで、30回位トライして貰ってやっと開きました(笑)
慣れてる私は毎回一発で開けられるんですけどね。

またこういう事があったら、困るなと思って探したら、10倍位のトルクを持ったデジタルサーボが、お手頃な価格で売ってました。
https://www.sunfounder.com/product-20kg-high-torque-servo.html
※上記はメーカ直販サイトですが、私はAmazonで買いました。

このサーボを使う上で一つ気になったのは、マイクロサーボはアナログサーボで、これはデジタルサーボなので、同じプログラムで動くのか？というところでした。
ググってもその辺を解説してる記事を見つけられなくて、とりあえず作ってみようと思いました。
で、作ってみたら、同じプログラムで動きましたが、数値は調整しないと全然違う位置まで動いちゃう感じでした。
サーボモータはアナログでもデジタルでも動作させる為の仕様は同じみたいですね。

以下は2.5Kgのマイクロサーボを20Kgのデジタルサーボに交換したあとの、動画です。

https://youtu.be/CJQrps3JmbM

アームの動くスピードがゆっくりになりましたが、非常に力強くボタンを押してくれて一発開錠です！

ここに辿り着くまでには、サーボのサイズやネジ穴が違うので、固定するためのアングルを調整したり、ロボットアームの長さを調整してドリルで穴を開けなおしたりと、地味に工作の方が苦労しました。

###スマホからのコントロール方法
最後にスマホから開錠させるための方法も記載しておきますが、ここは2017年の記事と一緒です。

RaspberryPiはルーターの中のローカルIPしか持たせていないので、ルーターで静的IPマスカレードを設定して、特定のポートにアクセスが来たらRaspberryPiに転送するようにしています。

ルーターまでアクセスするときは、IPだと変わる可能性があるので、無料のDDNSサービスを利用して、名前でアクセスできるようにしています。
RapberryPi上に、DDNSのIP更新を定期的に実行するツール入れておけば、IP変更があっても追随されますし、DDNSにありがちな定期更新しないと契約解除される仕様があっても安心です。

スマホ上にはsshクライアントを入れておいて、そこからRaspberryPiにログインして、開錠用のシェルスクリプトを叩けばドアが開くという寸法です。
WEBなどのGUIを作ってブラウザでやりたくなる人もいるかもしれませんが、無用にリッチにしてトラブルの原因を増やしてもしょうがないので、sshだけでできるようにしています。

以上、2017年に作った自動アンロックシステムをパワーアップした話でした。
符号化方式ーハフマン方式

応用情報技術者平成30年秋期 午前問5

符号化方式に関する記述のうち，ハフマン方式はどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/9b2e7944-c48c-5d6e-04c3-fa7209c4cb6b.png)


１、ハフマン方式
ハフマン符号化方式は、可変長の符号化方式で、出現確率が高いデータには短い符号を、低いデータには長い符号を与えることで圧縮を効率よく行う方法です。特に、出現確率に差がある場合には固定長の符号化よりも高い圧縮率になります。


２、ランレングス法の説明
0と1の数字で構成する符号の中で，0又は1の連なりを一つのブロックとし，このブロックに長さを表す符号を割り当てる。

・ランレングス符号化とは、主に画像データの圧縮に用いられる符号化方式の一種で、連続する同一の値を「色×回数」という列の長さ（run-length）の情報に置き換える方式のことである。

ランレングス符号化では、例えば画像の中に「白白白黒黒黒黒白黒黒」というデータの並びがあった場合、「白3黒4白1黒2」というデータに変換される。ランレングス符号化は単純な画像データほど効果的な圧縮が可能であり、可逆圧縮であるため完全にデータを再現できるというメリットを持っている。FAXなどではランレングス符号化によるデータ圧縮方式が採用されている。


３、二進化十進数(BCD：Binary Coded Decimal)の説明
10進数字の0～9を4ビット2進数の最初の10個に割り当てる。

一般に二進法の4桁(ニブル)は、0から15までの整数を表すことができる。二進化十進法ではこのうちの最初の10個を有効な数値として扱う。
例えば、127 という整数値は、 0001、0010、0111 という3つのBCDで表される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/6a484545-4ccf-a418-639d-cfee53327210.png)


４、サンプリングの説明
連続した波を標本化と量子化によって0と1の数字で構成する符号に割り当てる。


ーーーーーーーーーーーーーーーーーーーーーーーーーーー
補足
a，b，c，d の4文字からなるメッセージを符号化してビット列にする方法として表のア～エの4通りを考えた。この表は a，b，c，d の各1文字を符号化するときのビット列を表している。メッセージ中の a，b，c，d の出現頻度は，それぞれ，50%，30%，10%，10% であることが分かっている。符号化されたビット列から元のメッセージが一意に復号可能であって，ビット列の長さが最も短くなるものはどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/25c52363-327a-06a5-5c05-fe2308c92b3e.png)

注意する必要な点は、一意に複号可能かもチェックするのが必要ですね。
単な出現頻度により、ビット列の長さが最短だけではないです。

そうすると、
ア、イは一意に複号できないですね。
　ア（一意に復号できない例）
　bb(11)、d(11)
  ア（一意に復号できない例）
　abc(00110)、aada(00110)

ウ、エは一意に復号できる。


参照：

https://www.ap-siken.com/kakomon/28_haru/q4.html

https://www.ap-siken.com/kakomon/30_aki/q5.html

ランレングス符号化
https://www.weblio.jp/content/%E3%83%A9%E3%83%B3%E3%83%AC%E3%83%B3%E3%82%B0%E3%82%B9%E6%B3%95

二進化十進表現
https://ja.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E5%8C%96%E5%8D%81%E9%80%B2%E8%A1%A8%E7%8F%BE
mongooseで指定した範囲だけ取得 | MongoDB 

## mongooseで指定した範囲だけ取得 | MongoDB 

やりたいことは、

フロントReact、バックエンドNodeでWebアプリを作ってます。

トップページでページングなり無限スクロールなどしたい。

その場合、getでページ番号を渡して、その件数だけNodeのmongoから取得していくしかない。

## skipとlimitで出来る

```js
    var query = Product.find({}).skip(0).limit(5)
    query.exec(function(err,data){
      if (err) throw err;
      res.json(data);
    });
```

## 参考

[how to set limit,offset,skip in mongoose query
](https://stackoverflow.com/questions/39719021/how-to-set-limit-offset-skip-in-mongoose-query)
飲んだビールを記録できるアプリをNoCodeのGlideでつくってみた所感

SNSでNoCodeがバズワードとなってきており、
「どこまでできるのか」の感触くらいは知っておきたかったのでNoCodeシリーズの中でも特に学習コストが低そうなGlideと呼ばれるサービスを使ってみた。

##作ったもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/6118399f-2682-4b80-e882-a7d44394ad44.png)
(※↑プロトタイプです BeerLog: https://vvvwj.glideapp.io/)

飲んだビールログを残すことができるアプリを作ってみた。PWAに対応している。世界中で飲んだビールを記録したかったのが動機だ。
View側に関しては個人で下手にスタイリングするよりもきれいに仕上がっている気がする。
見た目に関しては問題ないかと。

ワンタップでスタイリングを選択できるのでCSSを触る必要は全くない。
そもそもコーディングはできないかと。

１ページにつきスプレッドシートの１Sheetが対応している。適当に列と値を入力して、GUI操作で必要な箇所に必要な列を選択するだけ読み込めるから簡単だ。

##動作は遅いし痒いところに手が届かない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651103/02b6b5d9-0734-32c2-30cd-4b22bc8cf454.png)
ビールの詳細ページをみて欲しいのだが、「このビールを飲んだ」をボタンにしたかったがチェックマークで妥協することとなった。しかも数秒〜しないとスプレッドシートへ結果が反映されなかった。

「レート」に関してもワンタップで付けられるようにしたかったのだがセレクトボタンとして星の
数を選択せざるを得なかった。

##エンジニアが必要となる場面
データがスプレッドシートを経由しなければならない制限がある。スプレッドシートの制限を超えたいのであればスプレッドシートからPOSTやGETするなりの必要がある。（違う方法があったり変わっていたらすみません。）
「スプレッドシートを経由するAPIを立てて〜」という場面でエンジニアが必要になるかと。

またビールを記録できるアプリでもそうだったがチェックマークを入れたまま保持するためにIF判定をスプレッドシート内で実行できるように式を挿入したりとエクセル好きでないと少々辛い場面もあった。
ここら辺もエンジニアの出番となる可能性があるだろう。

## プロトタイプとしては十分？
よく言われるのはマーケティング施策として周囲の反応をもらうためになら使えるだろうという意見。果たして本当にそうなのか。
・UIに融通が聞かない
・ページ間でデータを送ろうとすると面倒
・ちょっと気の利いたことをやろうとするとGoogleSpreadSheetに計算式などいれる必要があってコーディングできる人はそっちの方がやりやすい

こうした制限があったので思った通りの形にできなかった。そのため、「これがイメージです」と見せたとしても実際につくりたかったデザインと異なってしまうことを考えると逆にプロトタイプとして見せられないんじゃないかと思ってしまう。
（あくまでちょっと触っただけの魔改造などは一切施したことのない人間なので参考程度でお願いします）

## 情報まとめアプリとしては有用
いいねを押したり、読み書きの権限をユーザーに与えようとするとユーザー毎のスプレッドシート履歴を用意してユーザー毎のデータと同期させないといけない。
どうやって実装すればいいのか軽く触っただけだと分からなかった。
しかし、シンプルに文字と画像、動画だけを並べてアプリとするのであれば１からアプリを作るより遥かに楽だし大して問題もないと思う。

アプリという形でまとめることができるのでクローズドにまとめ情報を複数メディアを使った形で共有したい場合は大アリだと思う。

## コーディングもできると尚良い
GlideはアプリをGUIでアバターを選ぶように簡単に作れてしまうお手軽さを代償に、ちょっと凝ったことをさせようとすると途端に選択肢がなくなってしまうところがある。
歯痒いところはコーディングを挟めるなどできたらもう少し面白いものを楽に作れるんじゃないかと。

Adaloと呼ばれる人気ツールに関してはReactなどを使ってオリジナルのプラグインを作って挟み込めるそうなので暇になったら触ってみようかなと思う。
Power Query: REST APIでListから取得したRecordをTableに変換するには

Power BI DesktopにREST APIでテーブルデータを読み込んでいるときに、Power Query内でまずリスト (List)として表示され、クリックしてドリルスルーするとレコード (Record) の一覧として表示されます。これをテーブルとして表示させるにはどうすればいいでしょうか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/d5eabc96-5aef-8785-7b32-68639a21f2e3.png)

**答え:**
[リストツール]の「テーブルへの変換」ボタンを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/abd1c3cc-8190-1be3-304a-6a57a763b99d.png)

ボタンをクリックすると、「テーブルへの変換」ダイアログボックスが表示されます。そのまま「OK」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/2cfb9156-2b31-6ab4-59f7-e1040b7e3170.png)

すると以下のような表示になり、右上に「展開」ボタンが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/cf09baa7-dfc2-bffc-8652-4318af9ebeb1.png)

これをクリックすると、テーブルに表示すべき列を選択する画面が出ます。既定ではすべて選択されているため、「OK」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/05e96b44-33c6-a328-02b4-6da6d4ec9b2a.png)

その結果、Power Query上でテーブルとして表示されるようになりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479715/10b56089-ff49-3fae-e435-e3fc4f924b7b.png)
BNFで表現されたあるプログラム言語の構文

応用情報技術者平成30年秋期 午前問4

次に示す記述は，BNFで表現されたあるプログラム言語の構文の一部である。次に示す記述は，BNFで表現されたあるプログラム言語の構文の一部である。<パラメタ指定>として，正しいものはどれか。

　　<パラメタ指定>::＝<パラメタ>｜(<パラメタ指定>，<パラメタ>)
　　<パラメタ>::＝<英字>｜<パラメタ><英字>
　　<英字>::＝a｜b｜c｜d｜e｜f｜g｜h｜i

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/de739556-dd4e-eac3-ce53-54c44d560611.png)

ロジックとしては、
＜パラメタ指定＞::＝(＜パラメタ指定＞，＜パラメタ＞)
→ ((＜パラメタ指定＞，＜パラメタ＞)，＜パラメタ＞)
→ (((＜パラメタ指定＞，＜パラメタ＞)，＜パラメタ＞)，＜パラメタ＞)

のように、
　((((＜パラメタ＞，＜パラメタ＞)，＜パラメタ＞)，＜パラメタ＞)，＜パラメタ＞)
の構文が適切となる。

つまり、「( )」、「,」が必須ですね。且つ、イのような二重括弧もだめですね。括弧中でも「パラメタ指定」なので、「( )」、「,」も必須ですね。
よって、イウエは間違い、アが正しいだろうな。

ーーーーーーーーーーーーーーーーーーーーーーーーーー
補足
次の規則から生成することができる式はどれか。

〔規則〕
　　＜式＞ ::＝＜変数＞｜(＜式＞＋＜式＞)｜＜式＞＊＜式＞
　　＜変数＞ ::＝A｜B｜C｜D

 
　ア、A＋(B＋C)＊D　　　　　　イ、(A＋B)＋(C＋D)
　ウ、(A＋B)＊(C＋D)　　　　　エ、(A＊B)＋(C＊D)


答えにつきまして、
＋の場合は、両側が「( )」 が必要であること。
よって、ア、イ、エは間違いですね。


参照：

https://www.ap-siken.com/kakomon/18_haru/q8.html

http://fast-macro.com/Johosyori/h30AkiOuyou/h30AkiOuyou-04.html

http://beginner-goran.com/bnf/
正規分布ー９０点以上の得点者を推定する

応用情報技術者平成30年秋期 午前問3

受験者1,000人の4教科のテスト結果は表のとおりであり，いずれの教科の得点分布も正規分布に従っていたとする。90点以上の得点者が最も多かったと推定できる教科はどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/e44d367d-e4fe-9e4f-3f7f-ec695b29d0a6.png)


１、正規分布は、平均値を中心に左右対称の山のようなカーブを描く確率分布で、平均と標準偏差だけで分布に関する全ての特性が規定できるという特徴があります。

２、標準偏差は、データの分布のばらつきを表す尺度で、正規分布では平均値と標準偏差(σ[シグマ])、および度数の間には次の関係が成り立ちます。
　・平均±σの範囲に全体の約68%が含まれる
　・平均±2σの範囲に全体の約95%が含まれる
　・平均±3σの範囲に全体の約99%が含まれる
各教科の90点以上の得点者の割合は、点数分布を図で表してみると一目瞭然です(90点以上の部分を赤色で示しています)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/b112cc20-75e7-cedc-200d-8a87066197e6.png)

正規分布ですね、私は、全然忘れてしまいました。
概念と計算方法が分からないと、難しいですね。


参照：
https://www.nw-siken.com/kakomon/30_aki/am1_3.html
【vue-router】Vue.js流のページ遷移


#templateタグでのページ遷移（宣言的）

```vue:
<router-link to=""></router-link>
```

###実際の使い方

ユーザーマイページ(id: 1)にページ遷移したいとする

```vue:
//オブジェクト
<router-link to="/profile"></router-link>

//名前付きルート
<router-link :to="{ name: 'user', params: { userId: 1 } }">ユーザーマイページ</router-link>
```

アンカータグの内部的なプログラムとして、router.push()というソースコードになる。



#scriptタグ内でのページ遷移（プログラム的）

```vue:
this.$router.push()
```

###実際の使い方

ユーザーマイページ(id: 1)にページ遷移したいとする

```vue:
//文字列パス
this.$router.push('profile')

//オブジェクト
this.$router.push({ path: '/profile' })

//名前付きルート
this.$router.push({ name: 'user', params: {userId: '1' } })
```
【メモ】CSS設計の基礎の基礎

HTML/CSSはProgate,ドットインストール、Udemyなどでちゃんと学習していたし、
案件もありがたいことに継続的にいただけてたのですが
基礎のCSSセレクタの種類が抜けてるところがあったのでまとめ。

私が覚えてなかったところなので別に全部を集めたわけじゃないです。




**【タグとクラスの二重指定】**

--- HTML ---

```
<p>アイウエオ</p>
<p class="example">カキクケコ</p>
<span>サシスセソ</span><br>
<span class="example">タチツテト</span>
```

--- CSS ---

```
p.example {
    color: red;
}

```


上記の記述だと
exampleクラスのついているpタグなので
カキクケコだけが赤になる。




**【タグの中から厳選】**

--- HTML ---

```
<p>アイウエオ</p>
<p><span>カキクケコ</span></p>
<span>サシスセソ</span>
```

--- CSS ---

```

p span {
    color: red;
}

```


上記の記述だと
pタグの中のspanに適応されるので
カキクケコだけが赤になる


**【隣接セレクタ指定】**
--- HTML ---

```
<p>アイウエオ</p>
<div>カキクケコ</div>
<p>サシスセソ</p>
<p>タチツテト</p>
<p>ナニヌネノ</p>
<p>ハヒフヘホ</p>
<p>マミムメモ</p>
```

```
div + p {
  color: red;
}
```

上記の記述だと
divの下のpにのみ適用されるから
サシスセソだけ赤になる



**【複数セレクタ選択】**
--- HTML ---

```
<div>アイウエオ</div>
<p>カキクケコ</p>
<span>サシスセソ</span>
```

--- CSS ---

```
div , span {
  color: red;
}
```

複数選択されるので
アイウエオとサシスセソが赤くなる。




恥ずかしながら
上記のセレクタの違いが曖昧でした…！

これを機に曖昧から抜けるぞ。
待ち行列処理時間


応用情報技術者平成30年秋期 午前問2

コンピュータによる伝票処理システムがある。このシステムは，伝票データをためる待ち行列をもち，M/M/1の待ち行列モデルが適用できるものとする。平均待ち時間がT秒以上となるのは，処理装置の利用率が少なくとも何%以上となったときか。ここで，伝票データをためる待ち行列の特徴は次のとおりである。
　・伝票データは，ポアソン分布に従って発生する。
　・伝票データのたまる数に制限はない。
　・1件の伝票データの処理時間は，平均T秒の指数分布に従う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/6d230e60-1293-3288-551d-7bc46e544fe9.png)


１、待ち行列モデルでの平均待ち時間とは、
サービス(処理)要求が発生してから、実際にサービス(処理)を受けるまでの時間を指します。

平均待ち時間を求める公式は、下記です。

　{利用率／(1－利用率)}×平均サービス時間


本質問は、仮に、利用率はｘ％とすると、
{x/(1-x)}*T　≧　T　ですね。

つまり x > 1-xですね。ｘ≧０．５ですね。

まあ、それほど難しく計算必要ないかもしれません。
仮に１件の処理時間がT秒とする場合、50％の利用率とすると、前回の伝送が終わり次第、待ったなく、次件がきますね。それ以上とすると、待ち時間が発生して、処理時間がT秒以上になりますね。


参照：

https://www.ap-siken.com/kakomon/20_haru/q31.html

MM1モデル待ち行列
https://www.ikeriri.ne.jp/mm1/１
RDSをlambdaで定期的に停止を開始を行う

# はじめに

勉強も兼ね個人開発しているアプリをawsで公開しているのですが、AWSのEC2とRDSの料金が結構高い。
特にRDSは最低スペックでも確か2000円くらいかかってしまう。

とりあえず料金下げるため開発を行わない平日の昼間はRDSを停止する様にして、ちょっとでも節約したかったのでlambdaで自動的に
平日の昼間は停止する様にしました。

lambdaの言語に関してはRubyを使いました。

# lambdaの作成

まずlambdaを作成します

![スクリーンショット 2020-08-22 19.05.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/6d01c1d7-20f6-4f09-7fb1-012dc3eb79eb.png)

作成したらlambdaの詳細の画面から

アクセス権限を表示して、実行ロールのロール名の部分をクリックします。

そしてポリシーのアタッチからRDSの権限を付与します。

権限に関しては調べるの面倒だったので自分はRDSのFullAccessを付与しました。


# lambdaのコード

次にlambdaのコードの部分

![スクリーンショット 2020-08-22 19.10.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/37107b8c-c248-4d7a-29a7-4646bb846a8d.png)


に下記のコードを貼り付けます

```ruby

require 'json'
require 'aws-sdk'

def lambda_handler(event:, context:)
    ENV['TZ'] = 'Asia/Tokyo'
    
    client = Aws::RDS::Client.new(
        region: 'ap-northeast-1'
    )

    current_time = Time.now
    p "現在の時刻は #{current_time.strftime('%H時%M分')}"
    
    if current_time.hour == 18
        p '現在は夕方なのでRDSを起動します。'
        resp = client.start_db_instance({
          db_instance_identifier: "sb-develop"
        })
        p '起動完了'
    else
        p '就寝時間なのでRDSを終了します。'
        resp = client.stop_db_instance({
          db_instance_identifier: "sb-develop"
        })
        p '一時停止完了'
    end
    
end

```

Aws::RDS::Client のインスタンスを作成して

stop_db_instance、start_db_instanceで起動、停止ができます。

他にもいろいろできます=>https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/RDS/Client.html


# lambdaのスケジュールを設定

スケジュールに関しては　EventBridge (CloudWatch Events)　で行えます。

トリガーの追加から EventBridge (CloudWatch Events) を選択して作成します。

今回は平日の夕方6時に起動して、平日の夜中0時に停止する形で

これ↓が平日の夜中0時にlambdaを実行する例です。

![スクリーンショット 2020-08-22 19.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207921/0398e7a0-5945-2b58-cd78-e7304edcd6b1.png)


作成したら同じ様にcloudwatch eventsの
cronの部分を

```
cron(0 18 ? * MON-FRI *)
```

にして作成します。

これで平日の夜中から昼間は停止することができます。
vimで「E325:ATTENTION」エラーの対処法

# vimでエラー
vim操作でエラーが出たときの対処法をメモしておきます。

```
# vim .bash_profile
```
こちらのcmdを打つと下記のようなエラーが出てきました。

```
E325: ATTENTION
Found a swap file by the name ".bash_profile.swp"
          owned by: hogehoge   dated: 木  6 27 13:52:56 2019
         file name: ~hogehoge/.bash_profile
          modified: YES
         user name: hogehoge   host name: hogehoge.local
        process ID: 25913
While opening file ".bash_profile"
             dated: 水  7 10 10:29:16 2019
      NEWER than swap file!

(1) Another program may be editing the same file.  If this is the case,
    be careful not to end up with two different instances of the same
    file when making changes.  Quit, or continue with caution.
(2) An edit session for this file crashed.
    If this is the case, use ":recover" or "vim -r .bash_profile"
    to recover the changes (see ":help recovery").
    If you did this already, delete the swap file ".bash_profile.swp"
    to avoid this message.

Swap file ".bash_profile.swp" already exists!
[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort: 
```
# 原因
原因は以前の操作時に保存せず消してしまったかららしいです。
終了するときは下記のリンクのcmdを使いましょう。
https://qiita.com/kon_yu/items/b8864ff566b8b67a9810

# 対処法
`.bash_profile.swp`ができているので下記のコマンドで探す

```
＃ ls -la 
```

```
-rw-r--r--     1 hogehoge  staff  12288  6 27  2019 .bash_profile.swp
```
みつかりました。

```
＃ rm .bash_profile.swp
```

これで`.bash_profile.swp`のエラーが消え通常どうりの操作ができるようになりました。
XAMPPについて

#プログラミングの勉強日記
2020年8月22日
xamppを初めて扱ったので、xamppについてまとめる。

#XAMPPとは
　XAMPPは「ザンプ」と読む。Webアプリケーションの実行環境を構築するソフトウェアのこと。XAMPPはコマンド入力なく、詳細設定ファイルを変更することなく実行環境をボタンのクリックで環境構築できる。
　Xはクロスプラットフォーム、AはApache、MはMariaDB(旧バージョンはMySQL)、PはPHP、PはPerlの意味を持つ。これらのソフトウェアを使用することでWebアプリケーションを作成できる。

##X：クロスプラットフォーム
　どのWindowsやMac、LinuxなどのOSであっても同じ仕様で動作すること。

##A：Apache
　Apaccheは世界で50％以上の高いシェアを誇るWebサーバーソフトウェア(HTTPサーバー)。ApacheはOSに依存しないことやオープンソースであること、信頼性などから人気である。他のサーバーと比較して設定がすぐに完了できる。

##M：MarinaDB
　MarinaDBはMySQLから枝分かれしたデータベースシステムのこと。MySQLよりも高いパフォーマンスを発揮できる。（※XAMPPを起動するとMySQLの表示になるが、その中身はMarinaDBに入れ替わっている）

##P：PHP
　PHP(Hypertext Preprocessor）は、HTMLに埋め込むことができ、Web開発によく使用されるスクリプト言語。サーバーサイドでコードを実行するためによく利用される。

##P：Perl
　PerlはC言語とシェルの良いところを備えたようなプログラミング言語。テキスト処理に対して強力でライブラリモジュールが豊富。

#XAMPPの特徴
##いいところ
　XAMPPは大変な詳細設定を飛ばして手軽にインストールして実行できる。詳細設定は自由に変更できるが、簡単にインストールできるので、環境構築に詰まることが少ない。
　Drupal、Joomla!、WordPressなど広く使われているアプリケーションをインストールするためのツールであるBitnami（ビットナミー）と連携することで、XAMPP上にアプリをインストールできる。
　ローカルでもクラウドでもインストールできる。
　データベースはMarinaDB以外も使用することができる。（PostgreSQLにおいて、別にサーバーを用意する）

##悪いところ
　XAMPPは複数のソフトウェアをパッケージングされているので、XAMPPをインストールするとApache、MarinaDB、PHPのバージョンが一律に固定されてしまう。それぞれのアプリを所持していた場合は、XAMPPに合わせないといけない。また、最新のバージョンに対応させるためにはXAMPPが対応するまで待たないといけない。


#参考文献
[『XAMPP』について徹底解説！その特徴からできることまで、分かりやすく解説します。](https://www.geekly.co.jp/column/cat-technology/1908_005/)
[初心者向け！XAMPPの基礎知識とインストール方法](https://ferret-plus.com/8403)
相補演算、排他的論理和

応用情報技術者平成30年秋期 午前問1

任意のオペランドに対するブール演算Aの結果とブール演算Bの結果が互いに否定の関係にあるとき，AはBの(又は，BはAの)相補演算であるという。排他的論理和の相補演算はどれか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/74660ed5-357f-4d9e-e7cc-2b059ef86b4a.png)

１、相補演算とは、集合演算によって得られる結果が互いにもう一方の演算の補集合となっている関係、すなわちAとA，X AND YとNOT (X AND Y)のような関係になっているものをいいます。

２、排他的論理和(XOR)は、2つの入力値が異なれば真、同じであれば偽を返す論理演算で、演算結果は次のような真理値表となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591618/5e03d9c4-5f6e-83ef-474b-a0133e60969e.png)

なので、排他的論理和の相補演算になるのは、XORの補集合(XORのベン図の白い部分)が結果として得られる演算なので、答えとして適切なのは「等価演算」ということになります。


参照：

https://www.ap-siken.com/kakomon/24_haru/q1.html
dockerで脳死環境構築　rails 6

自分用メモです。

# dockerでのreils環境構築
https://qiita.com/me-654393/items/ac6f61f3eee66380ecd7
これをもろパクリしました。


#ターミナル
$ mkdir myapp 
$ cd myapp
$ code .
$ touch Dockerfile //中身を記載
$ touch Gemfile　//中身を記載
$ touch Gemfile.lock
$touch entrypoint.sh　//中身を記載
$ touch docker-compose.yml //中身を記載

#ファイル内コピペする
##Dockerfile

```
FROM ruby:2.6.6

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
    && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client yarn
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
```
##Gemfile 

```
source 'https://rubygems.org'
gem 'rails', '6.0.3'
```

##entrypoint.sh

```
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

##docker-compose.yml

> 
```
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

>

#ターミナル

```
$ docker-compose run web rails new . --force --no-deps --database=postgresql

$ docker-compose build
```

#ちょいめんどいやつ
データベースの情報を設定するために、config/database.ymlを変更し、コマンドでDBを作成する。

##config/database.yml

```
# 設定箇所のみ抜粋
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password # docker-compose.ymlのPOSTGRES_PASSWORDで指定した値
  pool: 5
:
:
development:
  <<: *default
  database: myapp_development
:
:
test:
  <<: *default
  database: myapp_test
```
#ターミナル

```
$ docker-compose run web rake db:create

$ docker-compose up
```

**成功！**
http://localhost:3000

**よく使うコマンド**

$ docker ps 起動中のコンテナを確認

$ docker-compose down　コンテナ消す

$ docker ps -aq | xargs docker rm　コンテナ全消し
SlackとJenkinsの連携(2020/8)

先日、SlackとJenkinsの連携にかかる設定を行ったのですが、Jenkins側の設定内容が、[参考](#参考)にさせていただいた記事の内容から変わっていたので、2020/8時点の設定内容を残そうと思います。

# 環境
Amazon Linux2
Jenkins 2.235.5
Slack Notification Plugin 2.40
Slack

# Slack側の設定

1.メニューの[App]から「Jenkins CI」を検索し、追加ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/88515da3-5f61-5aed-588d-74856fce7d06.png)
2.Slackに追加ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/73065ee4-aca3-f6a6-2154-20d129f66b85.png)
3.Jenkinsを追加したいチャンネル(ここでは#testjenkinsとしている。)を選択し、JenkinsCIインテグレーションの追加ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/b1c333f0-d894-3cf7-7ef1-90521ab34aca.png)
4.セットアップの手順のStep3にある「チームサブドメイン」と「インテグレーション用トークン認証情報 ID」をメモしておく。なお、文中にあるグローバル Slack 通知者設定(Global Slack Notifier Settings)は、2020/8時点ではSlackとなっていて、入力項目も変わっている。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/bfafcfde-10d5-998e-841d-849e9d934d2b.png)

# Jenkins側の設定

1.メニューの[Jenkinsの管理]→[システムの設定]を選択し、スクロールしてSlack欄を表示する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/2b653c62-3b9a-ba7a-2bff-edec594e67b5.png)

2.WorkspaceにSlackの設定でメモしたチームサブドメインを入力する。

3.CredentialのプルダウンからJenkinsを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/12dd68d1-27c5-c2b9-a32d-f5ba036f4fde.png)
4.下表のとおり入力し、追加ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/eeaca963-5c2d-92d9-135c-9839e274e594.png)

|項目名  |設定値  |
|---|---|
|Domain  |グローバルドメイン  |
|種類  |Secret Text  |
|スコープ  |グローバル  |
|Secret  |Slackが発行したインテグレーション用トークン認証情報 ID  |
|ID	  |任意(ここではslack-tokenとする。)  |

5.Credentialに上記3のIDを入力する。Test Connectionボタンを押下し、Successが表示されれば設定はOKなので、保存ボタンを押下する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/73707e56-b8a0-3922-e67c-f9e0191cf27f.png)

6.Slackと連携させたいジョブを選択し、「ビルド後の処理の追加」からSlack Notificationsを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/df69c02e-1863-71b6-4268-c4ea35e4a868.png)

7.通知したいものをチェックして保存ボタンを押下する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/0290d3ae-ccb9-6842-e3ed-e0557a7c34cf.png)

# 実行
ジョブを実行するとSlackに通知が飛んでくる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/be59900e-0537-cac8-2206-4e0892c5eb82.png)

できてしまえば簡単ですが、Slack側のJenkins周りの設定に関する説明が古いままとなっていたりと、設定するのに少し苦戦しました。

# 参考

以下の記事を参考にさせていただきました。

[JenkinsとSlack連携](https://qiita.com/haruto830/items/7a9798a57d9b2471a15c)
[Jenkinsのビルド結果をSlackに通知させる](https://qiita.com/unsoluble_sugar/items/a42795d108624fcd02d4)
Arch LinuxでRust開発環境を整えてみた

Rustの公式サイトを見たけど、PackageManagerを介さないやり方しか載ってなかったので調べてみました。

## 方針
基本的にArchWikiに載ってる通りに進めるとうまくいきました。
https://wiki.archlinux.jp/index.php/Rust

Rustのビルドはnightlyは怖いので、stableにします。

## 手順
1. rustupをインストール
2. rust stableをインストール
3. rlsをインストール
4. vscodeをインストール
5. vscodeにrust用の拡張機能をインストール

## rustupをインストール
Rustをそのままインストールするより、rustupを用いて、Rustやライブラリを管理するのが、Rust公式で推奨されています。

また、pacmanでは、rustupとrustは片方だけインストールできるようになっています。（両方インストールしようとすると、先にインストールした方を削除するか確認される。）

以下のコマンドでインストールできます。

```shell
sudo pacman -S rustup
```

## rustをインストール
stableをインストールします。
```shell
rustup default stable
```

## rlsをインストール
rust用のLSPをインストールします。（LSPとは簡単に言えば、エディタをIDEにする補助ツールのことです。）

```
rustup component add rls rust-analysis rust-src
```

## vscodeをインストール
以下のコマンドでインストールします。

```
sudo pacman -S code
```

## vscodeにrust用の拡張機能をインストール
以下の拡張機能をインストール
インストールすると入力補完が効くようになります。
https://marketplace.visualstudio.com/items?itemName=rust-lang.rust

いろんな拡張機能をまとめているこっちでも良さそうです。（上記の拡張機能も含まれています）
https://marketplace.visualstudio.com/items?itemName=swellaby.rust-pack
クラウド用語メモ

# クラウドリフト
オンプレ環境の中身をそのままクラウドに載せること。

# クラウドシフト
リフト→シフトの流れを指し、クラウド最適化していくこと。
オンプレ環境のメールサーバ→Amazon SES, Amazon WorkMailなど。
FirebaseAuthで返ってくるエラーメッセージ

#はじめに
`Flutter`で`FirebaseAuth`を使ってユーザ認証を行うとき、自分が行っているエラーハンドリングについて解説します。

#環境
 - **Flutter** : 1.20.1
 - **firebase_auth** : 0.18.0+1

#コード

```dart:auth.dart
import 'package:firebase_auth/firebase_auth.dart';

class Auth {
  final _firebaseAuth = FirebaseAuth.instance;

  Future<String> createUser(String email, String password) async {
    try {
      final result = await _firebaseAuth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      return result.user.uid;
    } catch (error) {
      if (error.toString() ==
          '[firebase_auth/invalid-email] The email address is badly formatted.') {
        return 'error:email';
      }
      if (error.toString() ==
          '[firebase_auth/email-already-in-use] The email address is already in use by another account.') {
        return 'error:exist';
      }
      print(error);
      return 'error:unknown';
    }
  }

  Future<String> signIn(String email, String password) async {
    try {
      final result = await _firebaseAuth.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
      return result.user.uid;
    } catch (error) {
      print(error);
      if (error.toString() ==
              '[firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.' ||
          error.toString() ==
              '[firebase_auth/invalid-email] The email address is badly formatted.') {
        return 'error:email';
      }
      if (error.toString() ==
          '[firebase_auth/wrong-password] The password is invalid or the user does not have a password.') {
        return 'error:password';
      }
      return 'error:unknown';
    }
  }

  Future<void> signOut() async {
    return _firebaseAuth.signOut();
  }
}
```

#kwsk（詳しく）
 `try-catch`でエラー判定を行います。
（エラー文をあらかじめ把握しておき、それに応じたエラーメッセージを返す。）


##createUserWithEmailAndPasswordの場合

###[firebase_auth/invalid-email] The email address is badly formatted.

Authに渡したメールアドレスが、メールアドレスの形をしていない場合に出るエラーです。
（例：email : aaaaa）

###[firebase_auth/email-already-in-use] The email address is already in use by another account.

メールアドレスが既に登録済みである場合に出るエラーです。

##signInWithEmailAndPasswordの場合

###[firebase_auth/user-not-found] There is no user record corresponding to this identifier. The user may have been deleted.

登録されていないメールアドレスの場合に出るエラーです。

###[firebase_auth/invalid-email] The email address is badly formatted.
Authに渡したメールアドレスが、メールアドレスの形をしていない場合に出るエラーです。
（例：email : aaaaa）

###[firebase_auth/wrong-password] The password is invalid or the user does not have a password.

入力したパスワードが、登録したものと異なる場合に出るエラーです。

#ユーザーへのエラー表示
上の関数で返した値を判定して、関数終了時に`Dialog`で認証が成功したか失敗したかを表示しています。

エラー文のcaseをいくつか持っているので、返却値が`error:unknown`でない場合は理由も表示しています。

#おわり
FirebaseAuthを使うことでfirestoreへのユーザーデータ保存の管理がとてもやりやすくなるのでオススメです。（uidを取得してcollection名にするなど）

おわります。
画像の膨張収縮処理

#実行環境
[Google Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)

#Google Colaboratoryで画像を読み込む為の準備
```python:
from google.colab import files
from google.colab import drive
drive.mount('/content/drive')
```

#必要なライブラリの読み込み
```python:
import cv2 #opencv
import numpy as np
import matplotlib.pyplot as plt 
%matplotlib inline
```

#画像準備
```python:
img = plt.imread("/content/drive/My Drive/Colab Notebooks/img/Lenna.bmp")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
```


#コード
```python:
#オリジナル画像
plt.subplot(2,3,1)
plt.title("Original", fontsize=10)
plt.imshow(gray)

kernel = np.ones((3,3),np.uint8)

#膨張 カーネルサイズ領域から輝度の高いものを選ぶ
plt.subplot(2,3,4)
plt.title("dilate", fontsize=10)
dst = cv2.dilate(gray,kernel,iterations = 1) #iterations：膨張回数
plt.imshow(dst)

#収縮 カーネルサイズ領域から輝度の低いものを選ぶ
plt.subplot(2,3,5)
plt.title("erode", fontsize=10)
dst = cv2.erode(gray,kernel,iterations = 1) #iterations：収縮回数
plt.imshow(dst)

plt.show()
```
##結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610285/886c1388-f31e-54ef-be29-27acda46fa7f.png)

うまく使えば欠損値の補完やノイズ除去に使える
PythonでWindowsのディレクトリで使用できない文字列を変換して作成する

## 概要
Windowsでは下記の半角文字列が禁則文字であり、ディレクトリ作成時に使用できない。
`￥　／　：　＊　？　”　＜　＞　｜`
半角文字列の場合は、全角文字に変換してディレクトリ作成を行うプログラムを下記に記載

## 前提条件
- Windows 10
- Python3.7


### サンプルPG
- プログラム概要
    - 正規表現を用いて禁則文字が存在する場合は全角変換する

```create_dir.py
import os
import re

trans_tone = {
    u'\:': u'：',
    u'\/': u'／',
    u'\¥': u'',
    u'\?': u'？',
    u'\"': u'”',
    u'\<': u'＜',
    u'\>': u'＞',
    u'\*': u'＊',
    u'\|': u'｜'
}

def main():

    dir_name = ":/\?<>*|"
    dir_name = multiple_replace(dir_name, trans_tone)

    os.makedirs(dir_name)


def multiple_replace(text, adict):
    """ 一度に複数のパターンを置換する関数
    - text中からディクショナリのキーに合致する文字列を探し、対応の値で置換して返す
    - キーでは、正規表現を置換前文字列とできる
    """

    rx = re.compile('|'.join(adict))

    def dedictkey(text):
        """ マッチした文字列の元であるkeyを返す
        """
        for key in adict.keys():
            print(key)
            if re.search(key, text):
                return key

    def one_xlat(match):
        return adict[dedictkey(match.group(0))]

    return rx.sub(one_xlat, text)


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

### 参考ページ
- [ファイル（フォルダー）名に関する制限](https://www.itc.u-toyama.ac.jp/el/win7/restricted.html)
- [辞書による複数箇所の文字列置換方法](https://teratail.com/questions/9921)
- [[python] 一度に複数のパターンを置換する関数(正規表現を置換前文字列とできる版)](http://omoplatta.blogspot.com/2010/10/python_30.html)
エッジ検出(ラプラシアン、Sobel、Canny)

#実行環境
[Google Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)

#Google Colaboratoryで画像を読み込む為の準備
```python:
from google.colab import files
from google.colab import drive
drive.mount('/content/drive')
```

#必要なライブラリの読み込み
```python:
import cv2 #opencv
import matplotlib.pyplot as plt 
%matplotlib inline
```

#画像準備
```python:
img = plt.imread("/content/drive/My Drive/Colab Notebooks/img/Lenna.bmp")
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
```


#コード
```python:
#オリジナル画像
plt.subplot(2,3,1)
plt.axis('off') 
plt.title("Original", fontsize=10)
plt.imshow(gray)

#ラプラシアン
plt.subplot(2,3,4)
plt.axis('off') 
plt.title("Laplacian", fontsize=10)
dst = cv2.Laplacian(gray,ddepth = -1)
plt.imshow(dst)

#Sobel
plt.subplot(2,3,5)
plt.axis('off') 
plt.title("Sobel", fontsize=10)
dst = cv2.Sobel(gray,ddepth = -1,dx = 0,dy = 1) #dx,dyで微分字数を決める。
plt.imshow(dst)

#Sobel
plt.subplot(2,3,6)
plt.axis('off') 
plt.title("Canny", fontsize=10)
dst = cv2.Canny(gray,threshold1 = 64,threshold2 = 128)
#threshold1と2は、小さいほうがエッジ同士の接合に用いられる。
#大きいほうが強いエッジの初期検出に使われる。

plt.imshow(dst)
plt.show()
```
##結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610285/35cdd7dd-d4e9-3434-0f62-0d465edbff2c.png)
Boost.Qiのqi::skip_flag::dont_postskipを追求する

Boost.Qiのparse_phraseにqi::skip_flag::dont_postskipというオプションがある。

英語では、
"boost::spirit::qi::skip_flag::dont_postskip passed to boost::spirit::qi::phrase_parse() tell the parser not to skip spaces that occur after a successfully parsed digit, but before the first unsuccessfully parsed character. "
となるが、よくわからない。。

コードを見てみる。。

      1 #include <boost/spirit/include/qi.hpp>
      2 #include <string>
      3 #include <iostream>
      4 
      5 using namespace boost::spirit;
      6 
      7 int main()
      8 {
      9   std::string s;
     10   std::getline(std::cin, s);
     11   auto it = s.begin();
     12   bool match = qi::phrase_parse(it, s.end(), ascii::digit, ascii::space,
     13     qi::skip_flag::dont_postskip);
     14   std::cout << std::boolalpha << match << ":" << *it << '\n';
     15   if (it != s.end())
     16     std::cout << std::string{it, s.end()} << '\n';
     17 }

実行してみる。。

<pre>

# ./a.out 
1234
true:2
234
# ./a.out 
1 2345
true: 
 2345
# ./a.out 
1 2345 59
true: 
 2345 59

</pre>

上記から、初めの数字１文字（ここでは"1")をパースした後は、スペースをスキップしないということだ。

"but before the first unsuccessfully parsed character. "
は、逆に、初めの数字("1")をパースするまでは、スペースをスキップするということになる。

実行してみる。。。

<pre>
# ./a.out 
 234
true:3
34
# ./a.out 
 2345 04
true:3
345 04
</pre>

確かに、初めの数字2をパースするまでは、スペースをスキップしている。。。

また、これは少しわかりにくいが、14行目の*itをはじめの数字をパースあとの文字を指している。

     14   std::cout << std::boolalpha << match << ":" << *it << '\n';
     15   if (it != s.end())
     16     std::cout << std::string{it, s.end()} << '\n';

なので、" 234"で2をパースした場合は3を指すことになる。。

くどいが、こう変更するといいかもしれない。

     1#include <boost/spirit/include/qi.hpp>
     2#include <string>
     3#include <iostream>
     4
     5using namespace boost::spirit;
     6
     7int main()
     8{
     9  std::string s;
    10  std::getline(std::cin, s);
    11  auto it = s.begin();
    12  bool match = qi::phrase_parse(it, s.end(), ascii::digit, ascii::space,
    13    qi::skip_flag::dont_postskip);
    14  *it--;
    15  std::cout << std::boolalpha << match << ":" << *it << '\n';
    16  *it++;
    17  if (it != s.end())
    18    std::cout << std::string{it, s.end()} << '\n';
    19}

14行目で、マッチした数字(digit)へポインタを巻き戻し、16行目でもとに戻している。

<pre>
$ ./a.out 
 2345 04
true:2
345 04
</pre>

個人的には経験はないが、qi::skip_flag::dont_postskipがあるからには、こういうことをしたい（数字で出るまでスペースをスキップしたい）ケースがあるのだろう。
mapbox-gl-js でも Google Map と同じように2本指でタッチ操作するようにしたい

スクロールズーム（`scrollZoom`）とドラッグ移動（`dragPan`）を無効にするパラメーターを
コンストラクタにオプションとして渡すと、マウス操作やタッチ操作で地図を動かせなくなる

https://docs.mapbox.com/mapbox-gl-js/api/map/#map-parameters
https://docs.mapbox.com/mapbox-gl-js/example/toggle-interaction-handlers/

```js
const map = new mapbox.Map({
  container: 'map',
  scrollZoom: false,
  dragPan: false,
  accessToken: 'YOUR ACCESS TOKEN'
})
```

このままだと、地図移動が出来なくなるので Google Map と同じように2本指でタッチ操作できるようにするためにイベントハンドラを設定する

https://docs.mapbox.com/mapbox-gl-js/api/map/#map-events

`TouchEvent` にアクセスするために `originalEvent` を使い `touches.length` で指の本数を判定
タッチ操作開始時に `dragPan` を有効化し、タッチ操作終了時に無効化する

https://docs.mapbox.com/mapbox-gl-js/api/events/#maptouchevent#originalevent
https://developer.mozilla.org/ja/docs/Web/API/TouchEvent/touches

```js
map.on('touchstart', (ev) => {
  if (ev.originalEvent.touches.length === 2) {
    map.dragPan.enable()
  }
})
map.on('touchend', () => {
  if (map.dragPan.isEnabled()) map.dragPan.disable()
})

```

PC向けに特定キー（CmdやCtrl）を押している場合にマウス操作できるようにするためにイベントハンドラを設定する
※ Windows 環境で未検証

```js
map.on('mousedown', (ev) => {
  if (ev.originalEvent.metaKey || ev.originalEvent.ctrlKey) {
    map.dragPan.enable()
  }
})
map.on('mouseup', () => {
  if (map.dragPan.isEnabled()) map.dragPan.disable()
})
```

`mouseleave` や `mouseout` も設定しないといけない気がする
VirtualBoxにCentOSをインストールする際に、画面が見切れる場合の対処法

VirtualBoxにCentOSをインストールする際に直面する問題について、対処法を載せたいと思います。

#事象
さて、VirtualBoxに仮想マシンも立てたことだし、CentOSインストールするぞ！
Install CentOS Linuxを押してと・・・。
![WS000024.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/43aa5a89-c143-57bc-f24d-2d9908ec0b32.jpeg)


あれ、画面の右端のほうが見切れてる。
スクロールバーも見当たらないし、これじゃあ何が書いてあるかわからない・・・。
![WS000023.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/bb4ce7dc-9717-fadd-6925-24d2f9a62a8f.jpeg)

#原因と対処法
VirutalBoxでCentOSをインストールする際の画面解像度が、デフォルトで800x600になっていることが原因のようです。

対処法は下記になります。
インストール時に一番最初に表示される画面にて、[Install CentOS Linux]を選択した状態で、[Tab]キーを押下します。
![WS000024.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/b24858a5-5df8-2fb8-5524-185c0868ee9e.jpeg)


画面下部に表示される[vmlinuz...]の末尾に、画面解像度の設定値を追記し、[Enter]を押下します。
 追記前：vmlinuz initrd=initrd.img inst.stage2=hd:LABEL=CentOS-8-2-2004-x86_64-dvd quiet
 追記後：vmlinuz initrd=initrd.img inst.stage2=hd:LABEL=CentOS-8-2-2004-x86_64-dvd quiet resolution=1280x720
![WS000025.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/7cbcb0f5-22f5-4248-5f1d-cf32a6ec75f2.jpeg)


画面全体が表示されるようになりました！
![WS000027.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/5b9e6e32-38bd-355d-2364-439a8b955c97.jpeg)

#※追記
VirtualBoxの仮想マシン設定にて、[ディスクプレイ]の[グラフィックスコントローラー]がVMSVGA(VMwareのグラフィックスコントローラー)となっていると、インストール画面で設定した画面解像度が反映されないそうです。
VMSVGAとなっている場合は、VBoxSVGA(VirtualBoxのグラフィックスコントローラー)に変更するとちゃんと反映されるようになります。
![WS000028.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689573/fb9c5289-10d3-e134-ed18-e6349b31bf65.jpeg)
PlaidML(OpenCL) で AMD GPU で Transformer 

## 背景

* 最終的には自前で OpenCL なりで BERT, GPT-3 など学習できるようにしたいが, まずは動作確認を兼ねて既存で動くものがほしい
* クロスプラットホームで動かしたい

https://github.com/kpot/keras-transformer

を試します.

## 環境

* Linux Ubuntu 18.04 HWE(kernel 5.4)
* AMD GPU(amdgpu-pro driver)
  * Radeon VII あたり. 
  * Navi では動かない. https://plaidml.github.io/plaidml/ の対応リストに Navi は無い.
* miniconda
 
## PlaidML setup and keras-transformer run

とくに難しいことはありません.

conda で環境を作り, pip install するだけです.

https://github.com/kpot/keras-transformer

を参考に必要なパッケージを pip で入れましょう.

あとは環境変数に `KERAS_BACKEND=keras.backend.plaidml` を指定して実行するか,
実行 python スクリプトで明示的に指定します.

https://plaidml.github.io/plaidml/

### experimental.json が見つからない場合

https://github.com/plaidml/plaidml/issues/370

たぶん python と pip が別の python 環境を使っていて, 関連するライブラリが見つかっていない(ファイルパス問題)と思われます.

```
python -m pip install plaidml-keras
```

と, `python -m pip` でパッケージインストールするのがよいでしょう.

## Issues

RX5700(Navi) + amdgpu-pro OpenCL では, plaidml-setup でデバイスを認識してくれませんでした.

あんまり新しすぎるのはだめなのかも?
(Radeon VII は認識しました)

## No multi GPUs

PlaidML は複数 GPU サポートしていません.

大規模学習したいときはやはり自前でライブラリを書くことになるでしょう.

## TODO

* PlaidML は MLIR ベースになるのが予定されています. いずれは MLIR -> SPIR-V -> OpenCL(or Vulkan) 実行とかもできるのでしょうか
Vue.jsのプロジェクトにBlazor Webassemblyを統合する(その1)

#1. Blazorをインストールする
1. dotnet Core SDKをインストールする。[ここから](https://dotnet.microsoft.com/download/dotnet-core/3.1)。
2. Vue.jsのフォルダ内にBlazorプロジェクトを作成する。`dotnet new blazorwasm -o プロジェクト名`
3. ファイルの作成は`dotnet new razorcomponent`を使っているが別に使う必要はない。
4. プロジェクトの実行は `dotnet run`構成はDebugになる。
5. プロジェクト構成ファイルは`プロジェクト名.csproj`だが、このファイルで設定を書き換えるのは時間の無駄。やりたければVisual Studio(Codeではない)でGUIから設定するか素直にコマンドラインオプションを変えよう。
6. `プロジェクト名/index.html`を編集して、rootパスを変更する。`base`タグのところを割り当てたいディレクトリにする。
7. ビルドする。`dotnet publish`と`dotnet build`の違いはディレクトリに静的ファイルが含まれるかどうか。
8. `dotnet publish`でビルドできるか確認する。`package.json`に次のscriptを追加する。`"dotnet": "dotnet publish ./プロジェクト名/ -c Release --verbosity minimal &&cp -r ./blazor-web/bin/Release/netstandard2.0/publish/プロジェクト名/* ./dist/&&mv ./dist/dist ./dist/blazor"`
9. これで`dist/blazor`ディレクトリにビルドされる。
【Kubernetes】手軽にPodの状態を監視してTeamsに通知したい

# 背景
Kubernetesを用いる場合にはPodは割とカジュアルに再作成されるため、Podの死活を監視したくなる場合がある。
特に開発の途上や小さいチームにおいては、きっちりとした監視を運用していくハードルの観点や、技術習得観点を考慮し、簡素な監視が必要になるタイミングがある。
そうした場合に、Prometheusなどのデファクトな監視基盤ではなく、一時的なつなぎとして使用できる簡易的な監視ツールが欲しくなった。
それと、純粋にGoでプログラムを書いたことがなかったため、簡素なツールを作る目的でPodの監視ツールを作成した。
<a href="https://github.com/iaoiui/PodMonitoringTool"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/iaoiui/PodMonitoringTool.png" width="460px"></a>

# [PodMonitoringTool](https://github.com/iaoiui/PodMonitoringTool)の概要

PodMonitoringToolは正常でないPodを見つけたときにTeamsにアラートメッセージを送ってくれるツールである。
Teamsに通知されたメッセージを確認することで、休日でも夜中でも運用者がすぐにKubernetesの元に駆けつけることができる。



![Screen Shot 2020-08-22 at 17.20.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/500cf365-f960-47e8-93cc-e7cbb28f91fe.png)


Podの正常性については、例えば下記のようにPod内コンテナがReadyになっていない場合や、PodのステータスがRunningでない場合などが想定される。
![carbon-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/2d57d0a3-53f1-bb83-69d6-d887bfe33647.png)


こうした場合に、Teamsにて異常のあるPodについて通知を受け取ることができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/94861/93a8dba6-741c-4301-3647-201484756910.png)

上記にまとめていないが、監視間隔の設定やPod正常時にも一報をくれるHeartbeat設定なども実装した。
詳細は、Githubリポジトリ側に記載した。

# まとめ

- Prometheusなどのデファクトなツールがある一方で、簡素なツールが役立つケースがあることについて説明した。
- Kubernetes上のPodの正常性を簡易的に監視するツールを作成し、Podの障害時にTeamsにてアラートを受け取れることを確認した。
HackMDを使ってweb上でmarkdownファイルを編集・管理する

# はじめに
日頃，研究やアプリ開発をする際に，新たに発見したことや苦労した部分などをメモとして残すことがあります．その際に，`markdown`ファイルとして残すことが多いのですが，ローカルのストレージに保存しておくのではなく，Web上に残したいと考えました．以下の理由があります．

- ローカルのストレージを使用しすぎると，パソコンの動作が重くなる
- 数台のパソコンを使用している場合，ローカルにファイルを残すと別のパソコンで使えない
- GoogleDriveなどを使用する場合，保存容量の上限がある

これらの理由から，`markdown`ファイルはWeb上で管理することにしました．`Qiita`で管理してもよいですが，メモを共有したくない場合があると思います．色々調べた結果，`HackMD`というWebアプリを使用することにしました．

# HackMD
`HackMD`は，Web上で`markdown`を記述できるエディタであり，保存もしておけるWebアプリです．`HackMD`を選んだ理由として，以下の利点があります．

- 無料かつ簡単に登録することができる．
- リアルタイムプレビューができる
- Qiitaを使用している感覚で使用できる
- sublimetext, emacs, vimモードがデフォルトで使える
- 公開範囲，編集可能な範囲の設定ができるため，プライベートにもパブリックにも使える
- 自分で作成した記事に対してtag検索ができるため，ファイルにtagをつけることで記事をジャンル別に管理し，目的の記事を簡単に見つけられる
- URLを作成し，他人に共有することで，簡単に記事を共有できる


# HackMDの使い方
インストール～便利な機能まで紹介します．

## インストール
[HackMDのホームページ](https://hackmd.io/)で`新規登録`ボタンから登録をします．

![](https://i.imgur.com/DxJ29rI.png)

## ログイン後
登録してログインが完了したら，`メモを作成`ボタンを押すことで，`markdown`ファイルが作成され，編集画面が表示されます．編集画面では，はじめに以下の設定をする必要があります．

- 右上の共有マークを選択し，編集可能，閲覧可能者の制限を設定する
- 画面下でEditorのモードを設定できる (emacs, vim, sublimetext)
- 画面下でスペース幅や背景の色なども変更できる

記事にタイトルとタグをつけるには，`markdown`ファイルの先頭に以下のように記述します．タグは複数記述することが可能です．

```
---
title: PythonでのHello World
tags: Python, Ubuntu
---
```

また，記事に画像を挿入するには，ローカルにある画像をコードにドラッグアンドドロップすることで挿入できます．その他は普通の`markdown`記述と何ら変わりません．

## 記事の検索
ログイン後の画面から，tagを用いて記事を絞り込むことができます．検索窓にtagを入力すると，記事にtagが設定されていればヒットします．複数tagを用いたAND検索などもできます．他にも，ファイルを更新順に並べたりして目的の記事を探すこともできます．

## 終わりに
今回紹介したHackMDにより，`markdown`のメモをWeb上で管理し，管理している中で探したい記事を簡単に検索することができるようになりました．`markdown`で記述したファイルの管理をWeb上で行いたい，編集も同時に行いたいという方がいましたら試してみてください．
ズームレベルと名目縮尺の関係（主観）

# 背景
ウェブ地図ではズームレベルと縮尺との関係を議論することがあります。

画面が紙をどう再現しているかということで議論する時代は終わりました。ディスプレイのピクセルピッチ (0.28 とか) を考慮して、「画面に定規を当てたら、何分の１になるのか」を議論するのはもはやナンセンスでしょう。

なぜならば、画面と眼球との関係は、紙面と眼球との関係とは違うからです。

さらに、画像タイルの時代とベクトルタイルの時代との間で、画面にどれだけ線画を詰め込むかという流行も変わりました。フラットデザインのころからだと思いますが、なるべく間の開いた感じにして、線のエッジをそれほど立たせずに、詳細の情報は深めのズームに入れています。

ベクトルタイルが主流化した段階では、オーバーズームのコストが下がるので、深めの表示ズームレベルが多用されるようになりました。結果として、ズームレベル18で全てを書き込み、ごたごたした地図を見ていただく、ということが少なくなったように思います。

# ズームレベルと名目縮尺との関係（主観）
ベクトルタイル主流化後の、ズームレベルと縮尺との関係は、私はこんな感じだと思っています。

- z=22 で 1:75
- z=21 で 1:150
- z=20 で 1:300
- z=19 で 1:600
- z=18 で 1:1250
- z=17 で **1:2500**
- z=16 で 1:5000
- z=15 で 1:10000
- z=14 で 1:20000
- z=13 で 1:40000
- z=12 で 1:100000
- z=11 で **1:200000**
- z=10 で 1:400000
- z=9 で **1:1000000**
- z=8 で 1:2000000
- z=7 で 1:4000000
- z=6 で 1:8000000
- z=5 で 1:15000000
- z=4 で 1:30000000
- z=3 で 1:60000000
- z=2 で 1:100000000（1億分の１の地球儀が直径13cmですね。）
- z=1 で 1:200000000
- z=0 で 1:400000000

Mapbox GL JS で思いっきりひいたとき、ズームレベルは 0.68 とかで止まるようです。

上記の感触に2倍以内の調整をかけて、全体のデザインと合わせて考えていく、ということを私はしているような気がします。ベクトルタイル技術的に、詰まったデザインのよりも間の開いたデザインのほうが性能を確保しやすいこともあり、かなり間を開け気味でやる気がします。

間を開け気味のデザインのほうが、デバイス的にも、人間的にも、帯域的にも、ユニバーサルなデザインになりやすい傾向があると思っています。

結果として、ベクトルタイルは、**データズームレベルの底は浅目に、表示ズームレベルの底は深めに**、ということでもあります。

## 追記
- Mapbox GL JS の maxZoom のデフォルトは 22 で、理論的最大は 24 です。z = 24 は、ざっくり 1:20 だとか 1:10 だとかを意味することになるのかもしれません。つまり、現在の技術では、ラジコンカー程度までの世界の縮図表現が可能だけれども、縮尺 1:1 だとか、さらには世界の拡大図だとかを表現するには、実は Mapbox GL JS の現在の実装は向いていないということになると思います。