OmniAICreator/Qiita-1.07M huggingface dataset:
AWS Lake Formation とは

## 勉強前イメージ

lakeってことはデータ取り込むような場所ってことかな？

## 調査

### AWS Lake Formation とは

AWS Glueの拡張のようなもので
AWSでデータレイクを構築・運用するためのマネージドサービスで、
安全なデータレイクを数日でセットアップ出来るのが特徴になります。
データの移動や保管、クリーニング処理を素早く行うことが出来ます。
実体はAWSの各種サービス(s3,IAM,Glueなど)をラップしたものになります

### データレイクとは？

データをそのまま生データで格納せきるストレージリポジトリで、
非構造化データをそのままの形式で保管することが出来ます。

### メリット

- データレイクをすばやく構築する

データの移動、保存、カタログ化、消去をすばやく実行できます。
また、S3内のデータを頻繁に使用されるクエリ用語で整理し、まとめ、効率性を向上します。

- セキュリティ管理を簡素化する

セキュリティ、ガバナンス、監査のポリシーを一元で 1つの場所で定義出来るので、サービスごとに行う必要がありません。
また、定義したポリシーは全体に適応できます。

- データにセルフサービスアクセスを提供する

データカタログを構築することによって、分析対象のデータセットを適切に検索できます。

### 用語(各コンポーネント)の説明

- データレイク
	- データカタログの実体としてs3に保管されたデータ
	- 構造化データも非構造化データもどっちも格納できる
- データアクセス
	- データへのアクセス権限を管理するもの
	- IAM
- ブループリント
	- データレイクにデータを格納するためのテンプレート
	- ワークフローを作成できる
- ワークフロー
	- ブループリントから生成される関連ジョブの格納先
	- 実体はAWS Glueのクローラーとトリガー
- データカタログ
	- メタデータストアで、メタデータでデータを管理
	- 実体はそのままGlueのデータカタログ

## 勉強後イメージ

s3とかIAMを駆使してデータレイクを作ったサービスって感じ？
元のAWS Glueがわからんからぴんとこないかも

## 参考

- [AWS Lake Formation](https://aws.amazon.com/jp/lake-formation/?whats-new-cards.sort-by=item.additionalFields.postDateTime&whats-new-cards.sort-order=desc)
- [AWS Lake Formationの概要と使用するメリットとは？](https://www.acrovision.jp/service/aws/?p=1243)
- [AWS Lake Formationのチュートリアルをやってみた！](https://dev.classmethod.jp/articles/lakeformation_tutorial/)
- [20191001 AWS Black Belt Online Seminar AWS Lake Formation](https://www.slideshare.net/AmazonWebServicesJapan/20191001-aws-black-belt-online-seminar-aws-lake-formation-198737098?ref=https://dev.classmethod.jp/)
意外と使われていない「Ctrl + D」

「Ctrl + D」1つで下記のような処理ができることご存じですか。

* コピペ（＝「Ctrl + C」 & 「Ctrl + V」）
* 削除（＝Delete）
* その他

実際使える場面をご紹介します。

## コピペ（＝「Ctrl + C」 & 「Ctrl + V」）
1. MS Office 全般で、挿入したオブジェクトを1個複製できます。Excel と PowerPoint でよく使います。
1. MS Excel では真上のセルの「値＋書式」を複製できます。これも便利！

## 削除（＝Delete）
「Delete」キーと同様に使えます。
1. エクスプローラー でファイルやフォルダを削除（ゴミ箱に移動）することができます。
1. Outlook でメールや予定を削除できます。これは要注意！

## その他
1. Chorme や Firefox、Edge など殆どのブラウザでは、現在のウェブページをブックマーク（お気に入り）に追加できます。
1. MS Word では、フォント設定用のダイアログが表示されます。
1. プログラマなら誰もが知っている Eclipse では、カーソル行を削除できます。私は本当によく使います。
1. VS Code では、「選択した項目を次の一致項目に追加」という機能のショートカットキーであり、複数項目を同時編集することができます。
AWS SESでたまにメールが送られない（2021年3月頃から）

AWSのSESを使っている本番環境で急にメールが **「たまに」** 送られなくなった。
ログにはこんなメッセージが。

```
[_lastResponse:protected] => Array
    (
        [0] => Array
            (
                [code] => 535
                [message] => Signature Version 2 is deprecated for use with SES from March 26, 2021. 
From that date on, we are progressively rejecting such requests. 
To resolve the issue, you must migrate to Signature Version 4. 
If you created your SMTP credentials in the SES Console, 
please create new credentials and replace the former ones. 
If you are deriving Signature Version 2 credentials from a IAM user,
 please start using the Signature Version 4 algorithm:
 https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html
            )
    )
```

どうやらcredentialsの仕組みが変わったからなんか対応しろと言ってるらしい。
[リンク先](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html)を見ると難しいことが書いてあるが対応は

SESの画面で「Create My SMTP Credentials」ボタンからIAMユーザ再発行して
そのID、パスワードに置き換えましょう。それだけで良さそうです。

![スクリーンショット 2021-04-16 10.11.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/df181d82-5446-b3d7-9bbd-b6a4cfbbc388.png)


色々難しいことを書いてあるサイトが多かったけどこれだけでした。
【DRF】Django REST FrameworkのSerializerとF()を同時に使う

今回のテーマはdjango.db.modelsにあるFクラスと、DRFのSerializerを使う時に気をつけなければならないことを短く紹介したいと思います。

## 結論
Serializerをイニシャライズする前に、```F()```を使って```.save()```したインスタンスに対して、
```instance.refresh_from_db()```
を呼びましょう。

## 説明
基本的に、Fを使った後にインスタンスの情報にアクセスしたいなと思ったときにはこれを思い出すといいと思います。もちろんSerializerに限る話ではありません。

例えば、いいね機能を実装しているとします。
いいねをすると言う機能は、ユーザーが同時に大量にアクセスすることがあり得るので、Fを使うことでちゃんと+1、-1されるように実装します。

しかし、Serializerでシリアライズするときに内部では、定義されたフィールドに応じて型がキャストされるので、```F('count') + 1```のような表現は許容されません。

```F('count') + 1```のような表現はCombinedExpressionと呼ばれていて、int型に変換することはできませんよというエラーが出てしまいます。

それを回避するために、いったんDBとのやりとりをリフレッシュすることで、モデルのインスタンスから正しい形の(Fが走り終えた後の)データを取得することができ、正しくシリアライズすることができるようになります。

さらに詳しい説明は、[Django refresh_from_db](https://docs.djangoproject.com/en/3.1/ref/models/instances/#django.db.models.Model.refresh_from_db)

コロナに負けず頑張りましょう！
[JavaScript] フォームの内容を文字列化⇔復元する

HTMLのフォームの内容を文字列化したり、その文字列を元にフォームの内容を復元する知見。


## ソースコード

###フォーム文字列化
第1引数にフォーム要素を渡します。戻り値はJSON文字列です。
**``id``がある要素のみ文字列化されます。**

```js:文字列化
function form_toJSON(form){
    const json = {}

    for(const el of form.querySelectorAll('[id]')){
        if(['file','submit','reset'].includes(el.type)){
            continue
        }
        else if(el.type === 'checkbox' || el.type === 'radio'){
            json[el.id] = el.checked
        }
        else if(el.tagName === 'SELECT'){
            json[el.id] = {}
            for(const option of el.options){
                json[el.id][option.value] = option.selected
            }
        }
        else{
            json[el.id] = el.value
        }
    }

    return JSON.stringify(json)
}

```


###フォーム復元化
第1引数にフォーム要素、第2引数にフォーム内容を文字列化したものを渡します。


```js:復元化
function form_restore(form, json){
    json = JSON.parse(json ?? '{}')

    for(const el of form.querySelectorAll('[id]')){
        const value = json[el.id]

        if(value === undefined || ['file','submit','reset'].includes(el.type)){
            continue
        }
        else if(el.type === 'checkbox' || el.type === 'radio'){
            el.checked = value
        }
        else if(el.tagName === 'SELECT'){
            for(const option of el.options){
                if(option.value in value){
                    option.selected = value[option.value]
                }
            }
        }
        else{
            el.value = value
        }
    }
}
```
※主要なフォーム部品には対応したつもりですが、フォーム部品は多すぎて全てに対応できているかは不明です。非対応の部品があれば教えてくれると有難いです。

## 使用例
ページ離脱時にフォーム内容をセッションストレージに保存し、ページ読み込み時に復元するサンプル。

```js
addEventListener('beforeunload', event => sessionStorage.setItem('sample', form_toJSON(form)))
addEventListener('DOMContentLoaded', event => form_restore(form, sessionStorage.getItem('sample')))
```

## メモ

フォームの文字列化にあたって最初はFormDataを使っていたが、checkedやselectedがfalseの場合に文字列化されないという欠点があった。文字列化時と復元時でHTMLが同じならFormDataで十分だと思うけど、異なる場合を考慮すると現行の方が良いかなという感じです。
RedMine備忘録

自分用の雑多なメモ書き。半年後の自分は他人と同じなので共有しておく。

#1.インストールなど


#2.プラグイン関係


#3.設定

## 3-1.プロジェクト関連

### 3-1-1.サブプロジェクトの削除
[管理]-[プロジェクト]から削除可能。
注意：削除したいプロジェクトの管理画面から削除はできない。

## 3-2.ワークフロー関連

### 3-2-1.フィールドの必須が反映されない

ロールが「管理者」になっていないか確認。「すべて」にする必要がある。
macOS用のスニペットアプリ「Snippety」がプログラマには超便利

macOSでスニペットといえば、「Clipy」や「Dash」といったアプリや、「Alfred」のスニペット機能を使っている人も多いはず。
そんな中、プログラマだからこそ使いこなせるアプリ「Snippety」が便利なので紹介します。

以下のサイトからAppStoreに飛んで、AppStoreからインストールしてみましょう。
（無料です）

https://wojciechkulik.pl/snippety

# スニペット機能は基本

インストールして起動すると、メニューバーにアイコンとして表示されます。

そして、使うときには[Shift]+[Command]+[Space]を押すと、候補が表示され、選択するだけで入力できます。
![snippet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309006/272cc079-d5e2-7e4d-efd0-c21a7dd6b277.gif)

このスニペットはメニューバーのアイコンやショートカットキーから新たに作成できます。
これだけであれば、VSCodeのスニペットでも十分ですし、他のアプリでも簡単です。

# インクリメンタルサーチが可能

登録してあるスニペットが増えてくると、候補を選ぶのが大変になります。
しかし、候補が表示された画面で検索欄に入力すると、その内容が含まれるスニペットがインクリメンタルに絞り込まれます。
![snippet2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309006/f0ad4534-fbb0-4582-30e3-df6f7c8ff0cc.gif)

先頭でなくても、一部を入力するだけで順次補完されるので、記憶の片隅にある単語を入れれば見つけられます。
また、キーワード設定やグループ分けもできるので、うまく設定しておけばさらに高速に見つけられます。

# テキストの変換があらかじめ用意されている

大文字への変換(Upper Case)や小文字への変換(Lower Case)、行の結合(Join)、分割(Split)、ソート(Sort)などはText Transformationとして用意されており、簡単に使えます。
![snippet3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309006/015bbb36-0823-1f3e-3304-361fda649601.gif)

# ワンライナーでコマンドを実行できる

スニペットだけなら他のツールでも十分ですが、プログラマにおすすめなのが、コマンドを実行できることです。
例えば、ある範囲を選択して「say」コマンドを実行すれば、選択した文章を読み上げてくれます。
これを、コマンドでターミナルから実行するのではなくスニペットとして「say」を登録しておくのです。

単一のコマンドだけでなく、パイプも使えるので、「sort | uniq」と指定しておけば、重複を除去できます。
![snippet4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309006/c165a4d6-9a3c-0831-b5fe-f2e07dc2c677.gif)

もちろん、「bc」コマンドに渡せば計算式を計算した結果で置き換えることも可能です。
![snippet5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309006/0bb987e9-f849-8744-0f2e-ec854b784c44.gif)

もっと高度なシェルスクリプトを書けば、複雑な処理を実行して置き換えることもできます。

# iCloudで連携できる

保存したスニペットはiCloudで連携できるので、複数の端末の間で同期できます。
さらに、iOSアプリも用意されており、iPhoneやiPadから気になった言葉をスニペット登録できます。
（文字列を選択して、「共有」→「Snipetty」を選ぶ）
入力するときにはアプリから選択して、クリップボードにコピーして貼り付けられます。

# M1 Macでも動く

ユニバーサルアプリになっており、Apple Silicon搭載のM1 Macでも動作しますので、最新の環境でも問題ありません。

# まとめ

単純なスニペットであればVSCodeでも十分ですし、ワンライナーを実行するだけならターミナルで十分かもしれません。
でも、これがテキストエディタだけでなく、ブラウザの入力欄やメールソフトでもショートカットキー1つで使えるので、設定すればするほど便利になります。
このようなアプリが無料で使えることに感謝。

詳しくは作者のサイトを見てください。
https://wojciechkulik.pl/snippety
Postgresql 2つのSELECT文の結果をJOINして出力する

`conversion` テーブル

| Column | Type | 
| -- |  -- | 
| referer | String  |
| conversion_date | Datetime |
 
が存在するときに、以下のようなデータを取得したい

| ページパス | 過去30日間 | 過去60日間 | 
| -- |  -- | -- | 
| https://example.com/page=1 | 30  | 60 | 
| https://example.com/page=2 | 30  | 60 | 

SQLで二つのSELECTの結果をJOINしてテーブル一覧表記する方法を調べていてとりあえず動いたものがこれ。

```
SELECT * FROM (
    SELECT
    conversions.referer as referer,
    count (*) as last60DaysCV
    FROM conversions
    WHERE conversion_date between CURRENT_DATE - 61 AND CURRENT_DATE - 31
    GROUP BY referer
) a
JOIN (
    SELECT
    conversions.referer as referer,
    count (*) as last30DaysCV
    FROM conversions
    WHERE conversion_date between CURRENT_DATE - 30 AND CURRENT_DATE
    GROUP BY referer
) b ON a.referer = b.referer
ORDER BY b.last30DaysCV DESC;
```
Bashなどのコマンド備忘録

# Bash 備忘録

### grepでコメント行と空行を表示しない

悲しいけどよく使うので

```
ubuntu@ubuntu18.04$ grep -vE "^#|\s*#|^\s*$" /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
...
...
...
```


### sed で（半角の）空白文字をアンダーバーに置換する

```
  echo -e "hoge huga \thage" >foo
  ubuntu@ubuntu18.04$ cat foo
  hoge huga       hage
  ubuntu@ubuntu18.04$ sed -e 's/[[:space:]]/_/g' foo
  hoge_huga__hage
```

参考: https://qiita.com/richmikan@github/items/b6fb641e5b2b9af3522e
参考: https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE

### 1時間以内にログインしたユーザ

※ログイン中のユーザは除く

```
# lastlog -t 1|grep "$(date +'%-d %H' -d '1 hour ago')"
root             pts/5                     Thu Aug 5 05:55:55 +0900 2019
```

2時間以内のユーザログインチェックはこんな感じ

```
# lastlog -t 2 |
  grep -E "$(date +'%-d %H' -d '2 hour ago')|$(date +'%-d %H' -d '1 hour ago')|$(date +'%-d %H')"|
  grep -v "^root"|
  sed -e "s/\s*pts...\s*/ /g"
ubuntu 127.0.0.1  Thu Aug 5 05:55:55 +0900 2019
```

### 簡易的に,(Comma)での文字列分割(変数展開とcut)

```
#!/bin/bash

moji="hoge,huga,hige"
# hoge
echo "${moji%%,*}"
# huga
echo "$(echo ${moji}|cut -d',' -f 2)"
# hige
echo "${moji##*,}"
```

```
実行結果
hoge
huga
hige
```
IFS変更なしでもっと簡単なのないかな


1コマンド限定でIFS変更Ver(@angel_p_57 情報提供ありがとうございます)

```
moji="hoge,huga,hige"
IFS=, read -ra mojis <<< "$moji"
echo "${mojis[0]}"
echo "${mojis[1]}"
echo "${mojis[2]}"
```

### AWSでよく見る UTC timestamp をJSTで見やすく変える

example:
2013-08-01T23:59:59.000Z
2020-09-02T12:29:59.205Z

Zがあれば削る
2013-08-01T23:59:59.000
2020-09-02T12:29:59.205

```bash
$ date --date "2013-08-01T23:59:59.000 9hours" "+%Y/%m/%d %H:%M:%S"
2013/08/02 08:59:59
```

xargsで複数処理

```bash
$ cat|xargs -I{} sh -c 'date --date "{} 9hours" "+%Y/%m/%d %H:%M:%S"' << EOF
2013-08-01T23:59:59.000
2020-09-02T12:59:59.205
EOF
2013/08/02 08:59:59
2020/09/03 21:29:59
```

### サービスの起動と自動起動設定有効化のワンコマンド

```bash
systmectl enable [サービス名] --now
```

### 手軽にyesでCPUに負荷かける

```bash
yes | xargs -L 1 -P 8 >> /dev/null
```

https://yohei-a.hatenablog.jp/entry/20141007/1412690972

ArchLinux: 
https://man.kusakata.com/man/systemctl.1.html

### Netcat Tips

netcatでcurl代替
https://stackoverflow.com/questions/27745104/convert-curl-command-to-netcat

### URL regex

```bash
regex='^(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]\.[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]$'
regex='http(s?)://[0-9a-zA-Z?=#+_&:/.%-]+'
# GitHubPR URL regex
# https://github.com/[0-9a-zA-Z?=#+_&:/.%-]+/pull/[0-9]+$`

[[ $1 =~ ${regex} ]] || echo "Invalid URL Format"
```
Webサーバーを作りながらJavaをマスターする　改訂版

#やってみて・・・
少し意味がわかったぐらい。あんまり深みにはまりすぎると問題なので・・・
#TakeAway
以下の記事は読み込んでおこうと思う。
Pathsについて
https://www.baeldung.com/java-nio-2-path
名前付きグループについて
https://qiita.com/jnchito/items/cceb669cb06fc044f411

#完了条件
改訂版がついてる場合
#要旨
ものづくりしながらJavaをマスターする。
#やること・やらないこと
##やらないこと
Java８以降
業務の仕様
データベース関連
##やること
サーバー
##インプット
###サーバー
リンク
https://github.com/todokr/simple-http-server
https://speakerdeck.com/todokr/xin-siihurokuraminkuyan-yu-falsexue-hifang-httpsahawozuo-tutexue-hu-java-scala-clojure
https://nowokay.hatenablog.com/entry/20160419/1461032474
##アウトプット
・一旦端から動かしてみる。
##内容
目次
・動きの確認　コード等
・コードを動かす。
・TakeAway
##動きの確認 事前準備
![スクリーンショット 2021-04-16 10.18.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271383/925c93f8-3be0-360c-4556-b9ad3a995ebb.png)
![スクリーンショット 2021-04-16 10.23.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271383/6cfe8787-e633-1c00-1482-c3977b5ed09e.png)
python3 -m http.server

###通信の中身の確認
```html
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.3
Date: Fri, 16 Apr 2021 02:43:07 GMT
Content-type: text/html
Content-Length: 1395
Last-Modified: Fri, 02 Apr 2021 03:28:36 GMT


<!--
  Copyright 2016 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  limitations under the License.
-->

<!-- referenced in: https://developers.google.com/web/tools/chrome-devtools/javascript -->

<!doctype html>
<html>
  <head>
    <title>Demo: Circle Ten </title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      h1 {
        font-size: 1.5em
      }
      input, button {
        min-width: 72px;
        min-height: 36px;
        border: 1px solid grey;
      }
      label, input, button {
        display: block;
      }
      input {
        margin-bottom: 1em;
      }
    </style>
  </head>
  <body>
    <h1>Demo: Demo: Circle Ten</h1>
    <label for="num1">Number 1</label>
    <input placeholder="Number 1" id="num1">
    <label for="num2">Number 2</label>
    <input placeholder="Number 2" id="num2">
    <button>Number 1 + (Number 1 * 0.9) * Number 2 </button>
    <p></p>	
    <script src="get-started.js"></script>
  </body>
</html>
```

#分析
SimpleJavaHttpServer.java
↓
Request.java
↓
RequestParser.java
↓
RequestHandler.java
↓
Response.java

#分析
Server
適切なエラー通知の設定の話

[株式会社TECH LUCK](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)という会社で代表兼エンジニアをしている齊藤です。

DXプロジェクト、開発プロジェクト、Rails開発などでお困りごとがありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご相談をいただけますと幸いです。
以下のような問題に対応することが可能です。
- プロジェクトでRailsエンジニアが足りなくて困っている
- Railsのバージョンアップをしたいがノウハウ・リソースが足りなくて困っている
- オフショア開発をしているが、要件の齟齬やコード品質が悪いので改善したい

また、Railsエンジニアも募集しておりますので、興味がありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご連絡いただけますと幸いです。

# 前提

この記事を3行でまとめると以下になります。

エラー通知を設定する際には以下の2つを考慮しよう。

- **誰のどのようなエラーを検知したいのか？**
- **エラーを検知してどのようなアクションに繋げたいのか？**

# 背景
すでに本番環境で稼働しているWebアプリのプロダクト開発にエンジニアとして加わることになりました。
しかし、本番環境で何かしらのエラーが起き多彩にエラー通知をして、エラーが起きたことを誰かが把握することはしていない状況となっていました。

このような状況だったので
「リリース前の動作確認やテストコードでは捉えられていないエラーが起きているかもしれない、、、。」
という不安がありました。

そのため、エラー通知をするようにすればいいのではないかと考え、エラー通知をするように実装・設定しました。
チーム内でのコミュニケーションはSlackを使っているため、Slackに `#error` チャンネルを作成し、そこに通知を流すように設定し、チームメンバーの招待をしました。

これで一安心だ、と思いきや初日から地獄に陥りました。

# 通知地獄の始まり
エラー通知と行ってもエラーには種類があります。503エラー、500エラー、404エラー、403エラー、etc...。
あまり深く考えていなかったため、あろうことかをすべてをエラー通知する設定にしていました。

勘のいい方だとお気づきだと思いますが、400系のエラーもすべて補足するためbotによる不正なURLアクセスのエラーもすべて通知することになります。
そのため `#error` チャンネルは、botによる不正なURLアクセスのエラー通知で埋まってしまい、メンバーが通知をオフにするという状況になってしまいました。


画像はbotによる不正なURLアクセスのエラーとなっている様子です。（Slackの通知音が1日中鳴り止まなかった。）
![エラー通知が多すぎる.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92443/c5f570cf-f903-53d6-7c5c-170d7cb80412.png)

# エラー通知はなぜ必要なのか？
このままではエラー通知を設定した意味がないということで、エラー通知の目的を改めて見直すことにしました。
「botによる不正なURLアクセスのエラーって検知したいの？」「エラー通知を受け取ったらアクションを起こすようにしないと意味がないよね？」など、さまざまなことを考慮した結果、軸となるのは以下の2つだということがわかりました。（記事の冒頭にも書いた２つです。）

- **誰のどのようなエラーを検知したいのか？**
- **エラーを検知してどのようなアクションに繋げたいのか？**

そこから、私たちのチームは以下のような目的でエラー通知の設定することにしました。

**プロダクトを適切に使っているユーザーが、開発の実装の不備でエラーが起きていないか検知したい。それによって、重要度の高いエラーであれば、通知を受け取ってすぐに修正をする。緊急でなければどこかのタイミングで修正をする方針を立てる。**

そこで、500エラーだけに絞り、他のエラーバッサリと捨ててエラー通知をしないようにしました。

# 地獄からの脱出
500エラーだけに絞ると、1日中通知されていたエラー通知は2日に1件ほどになりました。
そのような状態になると、エラー通知がきたら必ずアクションにつなげることができるようになり、「エラー通知きたので作業止めて一旦見てみます」というアクションを各自の判断で取れるようになりました。

知り合いの企業では、「通知が多すぎて、エラー通知がきても誰も何もしようとしない。」という、エラー通知のオオカミ少年的な状況になっているところもあると聞きます。
そうならないように、適切なエラー通知を設定することが大事ですね。
EventBridgeのターゲットがクロスリージョン対応したのでルールを作成してみた

# はじめに

EventBridgeのターゲットにクロスリージョンのEventBridgeBusを指定できるようになりました。
クロスアカウントのクロスリージョンにも対応可能です。

https://aws.amazon.com/about-aws/whats-new/2021/04/amazon-eventbridge-introduces-support-cross-region-event-bus-targets/

https://aws.amazon.com/jp/blogs/compute/introducing-cross-region-event-routing-with-amazon-eventbridge/

今までクロスリージョンを設定する際はLambdaやSNSを経由していましたが、その手段をとらずに直接指定できます。
管理が楽ですし、本来必要のないリソースを作成する手間も無くなるので、嬉しいアップデートです。

なお、直接クロスリージョンの別サービスをターゲットにすることはできません。
いつかアップデートされて別のサービスのクロスリージョン対応が実現されると嬉しいですね。

# 対応リージョン(2021.4.16現在)
まだ対応リージョンが少ないので、今後のアップデートに期待です。

ターゲットに指定できるリージョン

- バージニア北部
- オレゴン
- アイルランド

クロスリージョンイベントを送信できるリージョン

- バージニア北部
- オレゴン
- カリフォルニア北部
- バーレーン
- アイルランド
- ストックホルム
- パリ
- 東京
- シンガポール
- シドニー


# 作成してみた

東京リージョンでスケジュールしたEventBridgeを作成し、ターゲットにバージニア北部を指定できます。
同一アカウント内のクロスリージョンであれば、ターゲットEventBridgeBusのリソースベースポリシーを設定する必要はありません。

![スクリーンショット 2021-04-16 7.47.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344711/ad7c39ff-3ce4-09fe-d164-a70f68410f19.png)

ターゲットに指定できないリージョンを指定すると、このようなエラーが表示されます。

![スクリーンショット 2021-04-16 7.49.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344711/40fc6967-04d8-ab6b-19f0-7e8dc9f5b953.png)

クロスリージョンへのイベント送信をサポートされていないリージョンのターゲット選択画面は以下の通りであり、サポートリージョンと表記が異なることが分かります。

![スクリーンショット 2021-04-16 7.50.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/344711/1ed0fc40-1764-04df-7cec-2779492c1a7a.png)


# 料金

クロスリージョンの利用金はカスタムイベント料金として計上します。
百万件の公開済みカスタムイベントごとに 1.00USDです。

https://aws.amazon.com/jp/eventbridge/pricing/
Laravelで検索条件をページネーションに反映する方法

# 環境
Dockerで構築したLEMP環境
・Laravel 6.20.22
・Nginx 1.8
・MySQL 8.0
・PHP 7.4.16

# 概要
Bladeテンプレートで以下のPaginationリンクを設置し、ページめくりをすると、検索フォームで入力した検索条件が反映されずにページがめくられてしまう

```
{{$variables->links()}}
```

### 同様の事例
・[teratail >> laravelでページングの検索条件を別ページに渡って保持する方法](https://teratail.com/questions/193544)
私と同様に検索条件がページネーションに反映されない方のご質問に対して、以下に示す結論で記載するコードに書き換えると良いという回答あり。
→助かります！！！

# 結論
何も考えずに、ページネーションのリンクを以下のコードに書き換えれば終了。

```php:blade.php
{{$variables->appends(request()->query())->links()}} //$variablesはご自身の値を使用ください。
```

意味としては、
・appends()メソッドで配列を渡す
・request()メソッドで、ページで送信されるリクエストを取得する
・request()メソッドの中で、さらにquery()メソッドを使うことでリクエストの中のクエリストリングのみを取得できる。
※クエリストリングとは、urlの中の```?名前=○○```のような```?```以降の値のこと。

解説は以下の記事が特に参考になりました。
・[laravelで検索結果ページネーションを有効にするカンタンな方法](https://blog.dododori.com/create/program/laravel-search-page/)
・[Laravelマニュアル >> リクエストの取得](https://readouble.com/laravel/6.x/ja/requests.html)
・[e-Words >> クエリストリング](https://e-words.jp/w/%E3%82%AF%E3%82%A8%E3%83%AA%E6%96%87%E5%AD%97%E5%88%97.html)
[JavaScript] オブジェクトの値で配列をソートする

オブジェクトを格納した配列に対して，オブジェクトの特定のプロパティを基準にソートする方法を紹介します。

# この配列を age でソートしたい

```javascript
const arr = [
  {name: 'David', age: 25},
  {name: 'Bob', age: 30},
  {name: 'John', age: 25},
  {name: 'Tom', age: 37},
  {name: 'Rachel', age: 28},
  {name: 'Sam', age: 40},
  {name: 'Alice', age: 25},
];

```

# 結論

```javascript
const arr = [
  {name: 'David', age: 25},
  {name: 'Bob', age: 30},
  {name: 'John', age: 25},
  {name: 'Tom', age: 37},
  {name: 'Rachel', age: 28},
  {name: 'Sam', age: 40},
  {name: 'Alice', age: 25},
];

const newArr = [...arr].sort((a, b) => (a.age > b.age) ? 1 : -1);
console.log(newArr);
```

```
[
  { name: 'Alice', age: 25 },
  { name: 'John', age: 25 },
  { name: 'David', age: 25 },
  { name: 'Rachel', age: 28 },
  { name: 'Bob', age: 30 },
  { name: 'Tom', age: 37 },
  { name: 'Sam', age: 40 }
]
```
# age が同じ場合は name でソート 

```javascript
const arr = [
  {name: 'David', age: 25},
  {name: 'Bob', age: 30},
  {name: 'John', age: 25},
  {name: 'Tom', age: 37},
  {name: 'Rachel', age: 28},
  {name: 'Sam', age: 40},
  {name: 'Alice', age: 25},
];

const newArr = [...arr].sort((a, b) => (a.age > b.age) ? 1 : (a.age === b.age) ? ((a.name > b.name) ? 1 : -1) : -1);
console.log(newArr);
```

```
[
  { name: 'Alice', age: 25 },
  { name: 'David', age: 25 },
  { name: 'John', age: 25 },
  { name: 'Rachel', age: 28 },
  { name: 'Bob', age: 30 },
  { name: 'Tom', age: 37 },
  { name: 'Sam', age: 40 }
]
```

文字列の大小比較は辞書順ですが「大文字は小文字より小さい」などの直感的にわからない規則もあるので注意が必要です。

# 解説

MDN Web Docs の [Array.prototype.sort()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#description) を読むのが早いです。

比較関数 compare(a, b) の使い方を簡単に言うと
**a, b** の順 → **-1**
**b, a** の順 → **1**

コールバック関数の処理部分が少しややこしいです。

```javascript
(a.age > b.age) ? 1 : (a.age === b.age) ? ((a.name > b.name) ? 1 : -1) : -1
```

if文で書くとこうなります（分かりやすいように冗長な記述にしています）。

```javascript
if(a.age > b.age){
    return 1;
}else{
    if(a.age === b.age){
        if(a.name > b.name){
            return 1;
        }else{
           return -1;
        }
    }else{
        return -1;
    }
}
```

# 蛇足(配列のコピーについて)

**sort()** は配列そのものを変更する**破壊的なメソッド**です。
元の配列を維持するために **[...arr]** とコピーしてから使いました。
配列のコピーは **arr.slice()** や **arr.concat()** でもできます。

但し，いずれのコピーも内部のオブジェクトは shallow copy であることに注意してください。

```javascript
const arr = [
  {name: 'David', age: 25},
  {name: 'Bob', age: 30},
  {name: 'John', age: 25},
  {name: 'Tom', age: 37},
  {name: 'Rachel', age: 28},
  {name: 'Sam', age: 40},
  {name: 'Alice', age: 25},
];

const newArr = [...arr]; // 配列のコピーを作成
newArr[0].name = 'Hello';  // 'David' を 'Hello' に変更

console.log(arr);
```

```
[
  { name: 'Hello', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'John', age: 25 },
  { name: 'Tom', age: 37 },
  { name: 'Rachel', age: 28 },
  { name: 'Sam', age: 40 },
  { name: 'Alice', age: 25 }
]
```

オブジェクトを deep copy したい時は，lodash の cloneDeep() を使うのがおすすめです。
【Rails】database.ymlの「気になる」を調べてみた

# 背景
Railsのデータベースを作成する時にdatabase.ymlを修正したりしますが、その中で気になっていたところについて関連記事を探してまとめてみました。

# database.ymlの中身
まずはdatabase.ymlファイルを開いてみました。保存場所は「hoge/config/database.yml」にあります。
アプリ名は「hoge」という名前で作成。

```yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  socket: /tmp/mysql.sock

development:
  <<: *default
  database: hoge_development

test:
  <<: *default
  database: hoge_test

production:
  <<: *default
  database: hoge_production
  username: root
  password: <%= ENV['DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock

```

ファイルの中身は大きく分けて①default②development③test④productionとなっていることが分かります。①のdefaultは基本設定で②のdevelopmentは開発環境、③のtestはテスト環境、④のproductionは開発環境であることが何となく分かります。上記の中でも気になった記述がいくつかあったので調べてみました。

# 「&default」、「<<:」、「*default」について
①「&」はアンカー
「&」の部分はアンカーという「YAML」の記述方法。「YAML」とは今回でいうと拡張子が「.yml」になっているファイルのこと。今回の場合、「database.yml」を指します。「&」に続けて「default」という名前をつけると、「default」という名前でハッシューのデータを保存することができます。つまり、「&default」とすると、

```yaml
default:
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  socket: /tmp/mysql.sock
```
の内容が「default」という名前で丸ごと保存されることになります。

②「＊」はエイリアス
「＊」をつけることによって、アンカーの内容を呼び出します。

③「<<:」の意味は？
エイリアスで呼び出した内容を付け足すという意味。「マージ」するといいます。

要するに同じ内容を繰り返し書くのではなくて、「default」という名前で変数みたいに保存しておいて、重複しそうな箇所に付け加えることですね。

# ENV.fetch("RAILS_MAX_THREADS"){ 5 }とは
ENVはルビのオブジェクトであり、ENV["環境変数名"]で環境変数を呼び出すことができます。「.fetch」はENVオブジェクトが持つメソッド。「.fetch("環境変数名"){ 値 }」とすると「"環境変数名"=key」が存在すれば環境変数の値を返し、存在しなければデフォルト値として{ 値 }を返す意味になります。環境変数はMACの場合ターミナルで「vim ~/.zshrc」と入力すれば確認できます。「export RAILS_MAX_THREADS='値'」がなければ「pool: 5」になりますね。

# poolとは
「コネクションプール」。データベースへ接続した時の接続状態を保存しておく役割をします。データベースに再接続する場合、この「コネクションプール」を利用することによって接続時間を短縮する仕組みらしいです。この「コネクションプール」の数が足りないとリクエストに対する待ち時間が増えてしまい、「ActiveRecord::ConnectionTimeoutError」などが発生するんだとか。デフォルト値は５。

# adapter
接続するデータベースの種類。Railsで利用できるデータベース公式ドキュメントでは
>
DB2
Firebird
Frontbase
MySQL
Openbase
Oracle
PostgreSQL
SQLite
SQL Server
Sybase

になっています。

#socketとは
ネットとプログラムを繋げる出入り口を示します。mysqlの場合、localhost（自分のpc)では「/tmp/mysql.sock」で、 RPM【Red Hat Package Manager】のCentOS上だと「/var/lib/mysql/mysql.sock」になるみたいです。

#結論
ネットワークの知識は知っておくとプログラミングとは直接関係はないけど、知っておくことによってより理解が深まり学習が楽しくなります。特にバックエンドエンジニアにとってはサーバーとのやりとりでデータベースは欠かせないものなので、関連知識を身につけるのは非常に良いと思いました。また、ymlというファイル形式についても学習を進めたいと思います。

# 参考サイト&記事
https://railsdoc.com/config
https://qiita.com/ryouya3948/items/ba3012ba88d9ea8fd43d
https://qiita.com/katsuyuki/items/42b3c69bcd76c44ad64a
https://qiita.com/sukebeeeeei/items/576f109d57218c8397d5
https://qiita.com/terufumi1122/items/b5678bae891ba9cf1e57
https://www.wakuwakubank.com/posts/488-it-yaml/
https://docs.ruby-lang.org/ja/latest/method/ENV/s/fetch.html
https://qiita.com/megadreams14/items/32a3eed4661e55419e1c
https://dev.mysql.com/doc/refman/5.6/ja/problems-with-mysql-sock.html
vue.js × firebaseによる再認証機能

#前提
使用言語：vue.js、firebase
開発環境：vueCLI

#再認証機能とは
firebaseのAuthenticationに登録されている自分のユーザー情報を更新・変更するときにパスワードを入力して、以前に設定したパスワードと合致すればユーザー情報を更新・変更できるというもの。
ちなみにconsole.logによる開発ツールでパスワードを確認することはできない。

#コードは以下の通り
EmailAuthProvider.credentia（)に自分で入力したメールアドレスとパスワードを引数として渡す。
EmailAuthProvider を使って認証情報を発行し、reauthenticateAndRetrieveDataWithCredentialで再認証。

```csharp:main.vue
const user = firebase.auth().currentUser
const credential = firebase.auth.EmailAuthProvider.credential("email", "password")
user.reauthenticateWithCredential(credential)
```

#実装例
テキストフォームに入力したpasswordをv-modelでcredential()に渡すことで、再認証の処理が開始される。
ユーザー再認証→　パスワード合致OK→　メールアドレス変更処理　という流れで処理される。

```csharp
data(){
  return {
    password: "
 }
},
methods: {
    onReAuth(){
       //ユーザー再認証
       const user = firebase.auth().currentUser
       const credential = firebase.auth.EmailAuthProvider.credential(user.email, this.password)
        user.reauthenticateWithCredential(credential)
         .then(()=> {
         console.log("再認証完了")
        })
        //メールアドレス更新
         .then(() =>{
            const user = firebase.auth().currentUser
            user.updateEmail(this.email).then(()=> {
                console.log("メールアドレス更新完了")
            })
         .catch((error)=> {
                console.log(error)
            })
         })
      }
    }
```
```csharp
<v-text-field
    v-model="password">
</v-text-field>
```

参照リンク
https://tenderfeel.xsrv.jp/javascript/3606/
LinuC合格用 1週間でLPICの基礎が学べる本 6日目まとめ

# ファイル操作を行うコマンド
![6日目まとめ-ファイル操作を行うコマンド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1124047/4309654b-a385-de56-5782-fe6b9797b68f.png)

# ソフトウェアパッケージ
![6日目まとめ-ソフトウェアパッケージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1124047/c8d866f9-5242-6ef2-d94d-d640410ece74.png)

# パーティション
![6日目まとめ-パーティション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1124047/2c691b76-a554-269a-7fec-433c82729121.png)

# ファイルシステム
![6日目まとめ-ファイルシステム.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1124047/724187ae-4bfd-18b9-05d2-47760e075b5c.png)

|ディレクトリ名|役割|
|-----------|----|
|/bin|一般ユーザでも利用可能な、システム管理に必須のコマンドを格納|
|/boot|Linuxの起動に必要なファイルやLinuxカーネルを格納
|/dev|デバイスファイルを格納|
|/etc|システムの設定ファイルや、サービスの起動スクリプトファイルなどを格納|
|/home|ユーザのホームディレクトリ|
|/lib|共有ライブラリファイル、カーネルモジュール（デバイスドライバなど）を格納|
|/mnt|マウントポイントとなるディレクトリを格納|
|/proc|カーネル情報にアクセスするための仮想ファイルシステム|
|/root|rootユーザのディレクトリ|
|/sbin|システム管理に必須コマンドを格納。rootユーザのみ使用可能|
|/tmp|一時ファイルを格納|
|/usr|プログラムやソフトウェア、ライブラリなどを格納|
| /usr/bin|一般ユーザのコマンド（緊急時のシステム管理に必須ではない）を格納|
| /usr/sbin|管理者コマンド（緊急時のシステム管理に必須ではない）を格納|
| /usr/lib|共有ライブラリを格納|
| /usr/local|システムごとにインストールしたプログラムやドキュメントを格納|
| /usr/share|システムアーキテクチャに依存しないファイルやドキュメントを格納|
| /usr/src|プログラムのソースコードを格納|
|/var|書き換えが頻発するファイルを格納|
| /var/cache|一時キャッシュ|
| /var/log|ログファイルを格納|
相席居酒屋風時計を作ってみた

#はじめに
大阪を歩いていると、相席居酒屋の看板が目に入る時ありますよね。
お店によっては、現時点での男性客と女性客の人数が表示されてるのですが、
ほとんど女性客のほうが多いイメージがあって、これ実は時計なんじゃね...?って思ったんで
最近javascriptの勉強を始めだしたこともあり、勉強がてら作成してみました。

#作成物
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1106668/8a8db2a1-fe50-ee3f-e9f7-54e4c0e1a6cd.png)

https://dym330.github.io/shared_table_clock/

#javascript
やってることは、1秒毎に現在の時間、分を特定のIDを持つ場所に出力しているだけです。

```javascript
const hoursClock = () => {
  const now = new Date();
  const hours = now.getHours();
  document.getElementById("hours").innerHTML = hours
}

const minutesClock = () => {
  const now = new Date();
  const minutes = now.getMinutes();
  document.getElementById("minutes").innerHTML = minutes
}

hoursClock();
minutesClock();

setInterval(hoursClock, 1000);
setInterval(minutesClock, 1000);
```

#終わりに
すごく簡単なものではありますが、寝る前に思いついたものを1時間程度で形にできたので、楽しかったです。
更に勉強を重ねて、いつかは社会の役に立ちそうなものを作りたい。。。
docker-compose buildで503が返ってきてハマった時のメモ

久しぶりにDockerで開発しようとしたらdocker-compose build時にエラーが出てハマったのでメモ

# 環境
`OS:Windows10(WSL2:Ubuntu20.04LTS)
Docker Desktop:20.10.5
`
  
# 症状
Dockerfileとdocker-compose.ymlを書いて、いざ`docker-compose build`と思った矢先

`failed to solve with frontend dockerfile.v0: failed to create LLB definition: unexpected status code [manifests 〇〇]: 503 Service Unavailable
`とのエラー 

# 試したこと
解決策だけ見たい人は読み飛ばしてください



##### 1. Docker DesktopのDocker Engineの設定変更　
Docker DesktopのDocker Engineの設定から
`buildkit: false`
に変更するも効果なし

##### 2. Docker Desktopのアップデート
これも変わらず

# 結論
`rm ~/.docker/config.json`からの`docker login`

dockerHubのパスワードを変えたとかで`~/.docker/config.json`の内容が間違っててdockerイメージを持ってくるのに失敗してた様子
なので、一回設定ファイルを消して再ログインすることで正しい認証情報を再設定→めでたく`docker-compose build`成功

buildのエラーというよりはdocker.ioとの通信に失敗してたという方が正しいのかも
【自分用まとめ】アンビエントオクルージョンとはナンゾ

分かりやすい記事があったので参照(というかこれが全て)
#参考
[アンビエントオクルージョン・はじめの一歩](http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302)
#アンビエントオクルージョンとは
アンビエント(ambient) = 環境
オクルージョン(occlusion) = 遮蔽

つまりアンビエントオクルージョンとは **環境遮蔽度** を表す量(0～1の値)
#そもそも光の影響って？
ある注目点に届く光は，
光源から直接届く**直接光**と光源があらゆる物体に反射して間接的に届く**環境光**(間接光)がある．
>![光の影響](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015202440.png)
><div style="text-align: right;">出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a></div>

実際には，環境光を遮る物体が存在することがしばしばある．
>![遮蔽物](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015205313.png)
><div style="text-align: right;">出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a></div>

この**遮蔽物によって環境光がどれだけ遮られるか**という**遮蔽度**こそがアンビエントオクルージョン項である．

#CGでの使われ方
以上より注目点に届く光は，
**直接光 + (環境光×環境遮蔽度)**
によって計算される

#環境光の影響
話題を少し戻して，環境光の光量についてだが，以下の手法によって近似されるらしい

・アンビエント光とよばれる定数値を使う
・環境光の分布を画像として保持(Image Based Lighting)

#見え方の違い
直接光のみ
>![遮蔽物](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015210048.png)
>出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a>

環境光のみ(定数の場合)
>![遮蔽物](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015215233.png)
>出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a>

アンビエントオクルージョン項(0~1の分布)
>![遮蔽物](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015210308.png)
>出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a>

直接光 + (環境光×環境遮蔽度)
>![遮蔽物](http://cdn-ak.f.st-hatena.com/images/fotolife/A/Ambient-Occlusion/20131015/20131015215302.png)
>出典:<a href="http://ambientocclusion.hatenablog.com/entry/2013/10/15/223302">アンビエントオクルージョン・はじめの一歩</a>
Nuxt.jsでFirebase Realtime Databaseの読み書きを行う

Nuxt.jsのWebアプリから、FirebaseのRealtimeDatabaseの読み書きを行ったので、手順をまとめておきます。

# Firebase Realtime Databaseの作成
事前にFirebaseコンソールからプロジェクトを作成し、Webアプリを追加します。
![create_app.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/77c339bd-2986-7cd0-83ce-0a7f8239d32f.jpeg)
その後、[Realtime Databaseを作成](https://firebase.google.com/docs/database/web/start?hl=ja)してください。
作成時の各設定は任意に行ってOKです。
![create_db.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/4efe3bab-167e-6197-8a80-ba055a7ef030.jpeg)

# Nuxt.js側にFirebaseの設定

Nuxt.js側でFirebaseを操作するため、必要なモジュールをインストールして設定していきます。
今回は、Nuxt.jsでのFirebaseの操作に便利な [nuxtjs/firebase](https://firebase.nuxtjs.org/)もインストールします。

下記コマンドを実行してください。

```sh
npm install --save firebase @nuxtjs/firebase
```

次に、 `nuxt.config.js` に下記設定を追加します。

```js:nuxt.config.js
export default {

  modules: [
    [
      '@nuxtjs/firebase',
      {
        config: {
          apiKey: '<apiKey>',
          authDomain: '<authDomain>',
          projectId: '<projectId>',
          storageBucket: '<storageBucket>',
          messagingSenderId: '<messagingSenderId>',
          appId: '<appId>',
          measurementId: '<measurementId>'
        },
        services: {
          database: true // Realtime Databaseの使用を宣言
        }
      }
    ]
  ],

}
```

`<apiKey>` など、それぞれの項目に指定する値は、Firebaseコンソールの「プロジェクトの設定」から、下記のように確認可能です。
（Nuxt.jsのプロジェクトに追記する際はenvファイルを利用するなどしてください）
![firebase_setting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/ac6ab74e-ddcd-3493-1998-9fdf651fc600.jpeg)

# データの読み書き

基本的にデータの読み書きは[ドキュメント](https://firebase.google.com/docs/database/web/read-and-write?hl=ja)通りです。
今回は例として、下記のように実装します。

```vue:pages/index.vue
<template>
  <div>
    <button @click="increment">send</button>
    <span>count:</span>
    <span>{{ count }}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    };
  },
  mounted() {

    // RealtimeDatabaseの更新を検知
    this.$fire.database.ref('count').on('value', (snapshot) => {
      this.count = snapshot.val();
    })
  },
  methods: {

    // RealtimeDatabaseを更新
    increment() {
      this.$fire.database.ref('count').set(
        ++this.count
      );
    }
  }
}
</script>
```

ページを確認すると、このように表示されます。
![page.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/190cce22-cc11-8c3b-cb79-dd48482bb464.png)
「send」ボタンを押すことで、 `increment()` が呼ばれ、Realtime Database上の値が更新されます。
![rtdb.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/0882c289-74c1-d356-40e0-8a8db0fe0a9d.jpeg)
Realtime Database上の値が更新されると、Webアプリ側は更新を検知して自動で値が同期されます。
![page2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/279032/466506d9-f62b-d6ff-e390-7a3e33c28b63.png)

これだけです！
さくっと導入でき、とても簡単ですね！
Datasaber 課題：Viz作成-01

## Datasaber 課題：Viz作成-01

### ■ この文書は何か：
Datasaber 課題で、なんでもいいからデータを用いて Viz を作成して公開せよ！！
っていうフリースタイルな課題があったため、``やったこと``と``学んだこと``をめちゃくちゃラフにまとめる。

### ■ 結論：
- やったこと；
    - 時系列データを使って、<B>時間発展をインタラクティブに可視化できる dashboard を作成</B>した
<br>Special Thx :tada: Ref. http://www.workout-wednesday.com/2021w11tab/
- 学んだこと；
    - ダッシュボードアクションの有効性
    - データ結合して、array を簡単に作成（これは Python でも簡単な処理だが...）
    - ページに時系列(Year）を設定することで、動的なプロットを簡単に作れて、サクサク動作！！
- 成果物；
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194994/f3d08206-6561-7ed9-f525-782230ede367.png)

### ■ 以下、作業 log.

#### 1. データセットの用意
今回は、世界の地域別に、収入と平均寿命の時系列遷移についてを題材に、以下の 4 つのデータを用いた。
① 各国における平均収入の時系列（Year）データ
② 各国における人口の時系列（Year）データ
③ 各国における平均寿命の時系列（Year）データ
④ 国とリージョンのリレーションテーブル
（Special Thx :tada: Ref.：https://data.world/missdataviz/wow2021-w11）
これらのデータをいい感じに関係付ける。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194994/83ed432e-923a-8e86-cb17-2a20ffbb60c5.png)

#### 2. 作図（シート編）
今回は、ダッシュボードに載せるシートを以下 3 つに分割して作成した。特に特筆すべき点なし...
- シート1：メインの絵
- シート2：ページに連動して変わる Year 
- シート3：リージョンにおけるカラム

#### 3. 作図（ダッシュボード編）
ダッシュボードで工夫した点は 2 つ。``①ダッシュボードアクション``と``②ページ変えると変化するテキスト``
##### ① ダッシュボードアクション
Datasaber ハンズオン課題を経験してると、何度もやってるから体に染みつき始めているが、、、
> メインに対して、地域を選択したときに、データをハイライトしてあげますよーっていうアクション

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194994/cca3fb76-263c-4ed9-100b-721bc102a18c.png)

##### ② ページ変えると変化するテキスト
シート作ってそこに Year をテキストとして配置。
これ、わざわざ１枚シート作る意味あるのかな？メインで Year のテキスト作ると、確かに各データに対して Year がマッピングされてしまうが...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194994/a25ef5a5-567a-e08e-53e4-893ee7ce210e.png)

#### 4. Tableau Public に Viz 投稿
これで、無事課題達成 :yellow_heart: 
https://public.tableau.com/shared/KCTDQRR4B?:display_count=y&:origin=viz_share_link

### ■ 考察：
せっかく可視化したし、簡単に特徴を羅列しておくと、
- 時間発展に伴い、横軸の分散増大（ぱっと見） → 貧富の差が顕著に見えている
- 全体傾向として右肩上がりの図 → 収入多いほど、平均寿命長い
    - 冪指数は、時間発展に伴って上に凸の二次関数で plot できそう（直感） → 医療の発展に伴って、低収入でも平均寿命長くなってる
- EAST ASIA PACIFIC と SOUTH ASIA の収入増加率エグい...
Powershellで環境変数を設定する

# Powershellで環境変数を設定する

Powershellでは環境変数は、ドライブに保存されている。
ドライブといえば、C:、E:のファイルシステムのドライブを想像してしまうが、Powershellではデータの保管場所という意味でドライブという言葉が使われている。

環境変数は、env という名前のドライブが定義されている

ドライブという呼び名の通り、CDでそこへ移動したり、 Get-ChildItemで子要素をリストしたりもできる。
ほ～って感じですね。


## 一時的に設定する場合

一時的に環境変数を変更したい場合は、$envに対して値を設定してやればOK

``` ps
$env:SomeValue = "This is a new value"
```

## 恒久的に環境変数を変更したい場合

恒久的に環境変数を変更してやりたい場合は、.NETのオブジェクトを通して取得・変更ができる

``` ps
[System.Environment]::GetEnvironmentVariable('PATH','machine')

[System.Environment]::SetEnvironmentVariable('FOO', 'bar',[System.EnvironmentVariableTarget]::Machine)

```


## 蛇足

どんなドライブが存在しているかは、コマンドレット Get-PSDriveで取得することができる
Providerの欄を見ると、どのタイプのドライブなのか判断ができる。



``` ps
Name           Used (GB)     Free (GB) Provider      Root                                               CurrentLocation
----           ---------     --------- --------      ----                                               ---------------
Alias                                  Alias
C                 392.84         82.31 FileSystem    C:\                                                   Users\hogehoge
Cert                                   Certificate   \
D                                      FileSystem    D:\
E                2433.71        360.68 FileSystem    E:\
Env                                    Environment
F                2141.91        652.49 FileSystem    F:\
Function                               Function
G                 407.53         58.23 FileSystem    G:\
H                2733.60         60.79 FileSystem    H:\
HKCU                                   Registry      HKEY_CURRENT_USER
HKLM                                   Registry      HKEY_LOCAL_MACHINE
I                1960.81        833.58 FileSystem    I:\
Variable                               Variable

```

FileSystemやEnvironmentの他に、Certificate、Function、Registryなんてのもあり、
Providerにデータベースを追加することもできるらしい。

https://docs.microsoft.com/ja-jp/sql/powershell/sql-server-powershell-provider?view=sql-server-ver15


## 参考

https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.1

https://mcpmag.com/articles/2019/03/28/environment-variables-in-powershell.aspx

静的なクラスとメソッドの使用

https://docs.microsoft.com/ja-jp/powershell/scripting/samples/using-static-classes-and-methods?view=powershell-7.1#getting-environment-data-with-systemenvironment
自然言語処理用学習データを動画内から収集したかった...

# 目的
動画内からセリフのみを抽出して文字起こしをする。

# 実行環境
Windows10 Home
Python3.7

# 使用したライブラリなど
youtube-dl
speech_recognition
spleeter 2.1.2
FFmpeg

# 実行する順番
1. 諸々のインストール
2. 目的の動画取得
3. 動画内の音声抽出
4. 文字起こし

# 1. 諸々のインストール

**⚠️ Pythonの環境が一通り整っている事を想定しております。ごめんなさい。**
**今回はpipを使用して色々インストールさせて頂きます。**

## youtube-dl のインストール

**youtubeに限らずダウンロードする時に便利なのです！**

~~~
pip install youtube_dl
~~~

## speech_recognition のインストール

**音声認識をする時などにお世話になっております。**

~~~
pip install SpeechRecognition


↓ Pyaudioも欲しい ↓
pip install pyaudio
~~~

インストール確認のために以下のコマンドを実行！！！

~~~
python -m speech_recognition
~~~

何か言って！　と言われるので **英語** で話してみると...

![image](https://user-images.githubusercontent.com/55323475/114880364-f88abb80-9e3c-11eb-8d2f-a5e7e8e9c3a3.png "いんすこ確認")

認識結果が表示されるようであれば完了です！！！

## spleeter のインストール

**ボーカルやらドラムやらベースやら音声を分離できます！！！**

~~~
pip install spleeter
~~~

インストールできたか確認します！

~~~
spleeter --help
~~~

以下のような文やhelpが出たら完了です！！！

~~~
Usage: spleeter [OPTIONS] COMMAND [ARGS]...
~~~

## FFmpeg のインストール

**音声や動画を扱うときによく見かけます**

[FFmpeg](https://www.gyan.dev/ffmpeg/builds/)のサイトをスクロールして **release** のLinksから自分に合ったものを選んでDLしてください！

![image](https://user-images.githubusercontent.com/55323475/114881328-dcd3e500-9e3d-11eb-8ba4-953eae16abaf.png "ffgazou")

**D** や **C** の直下にreleaseフォルダを作ると後々楽です。

**※ システム環境変数にPathを通してほしいです!!!**

# 2. 動画の取得

youtube-dlで動画を取得してみましょう！

~~~
youtube-dl (DLする動画のURL) -x -f "bestaudio" --audio-format wav --audio-quality 0
~~~
これを実行するとwav形式で高音質downloadをしてくれます。

**オプションの説明**

* -x　or　--extract-audio　→　音声のみダウンロード

* -f　or　--format ”piyopiyo”　→　ファイル形式を指定できます[くわしくはこちら](https://github.com/ytdl-org/youtube-dl#format-selection)へ

* --audio-format　→　downloadフォーマットを指定してます

* --audio-quality　→　音質を指定できます（0～9　小さいほど高音質）

他にも色々あるみたいです。

# 3. 動画内の音声抽出
spleeterを使って動画内の音声を抽出します！
**抽出というよりは分割なんですけどね。**

~~~
spleeter separate (分割したいファイルへのPath) -o (出力先のPath) -p spleeter:2stems
~~~

これを実行すると音声がvocalとaccompanimentに分割されます！
初回実行のみ、自動でdownloadするものがあるので少し時間がかかります。

**オプションの説明**

* -o　(出力先のPath)　→　出力先のフォルダを指定してください

* -p　spleeter: **n** stems　→　( **n** = 2 , 4 , 5 ) で分割されます

  * 2stems → vocal / accompaniment
  * 4stems → vocal / bass / drum / other
  * 5stems → vocal / bass / piano / drum / other

**※ 分割元動画が長すぎると途中で処理が落ちる場合があります!!!**

# 4. 文字起こし
speech_recognitionを使って実際に文字起こしをしてみます


~~~Python:Python
import speech_recognition as sr
AUDIO_FILE = "   " #ここに抽出した音声ファイルのPathを

r = sr.Recognizer()
with sr.AudioFile(AUDIO_FILE) as source:
    audio = r.record(source)
print('音声データの文字起こし結果：\n\n', r.recognize_google(audio, language='ja'))
~~~

**すると...**

![image](https://user-images.githubusercontent.com/55323475/114889734-53c0ac00-9e45-11eb-8dc1-9ab7e96e137a.png "結果")

# 結果　まとめ
一応目的としていた、動画からセリフのみを抽出して文字起こしをすることはできた。
しかし、文字起こし結果は扱えるようなものではなかった...
結局人力で書き起こすことにした。
**人力最強！人力最強！**
 
**何か良い方法ご存じでしたら是非教えてほしいです。m(_ _)m**

# 最後に
今回初めてQiitaに記事を投稿しました。
至らない点ばかりではありますが、目に留めていただきありがとうございました。

# 参考
>youtube-dl
>https://github.com/ytdl-org/youtube-dl/blob/master/README.md#readme
>speech_recognition
>https://pypi.org/project/SpeechRecognition/
>spleeter
>https://github.com/deezer/spleeter
>FFmpeg
>https://rikoubou.hatenablog.com/entry/2019/11/07/144533
「消費支出」について解けた疑問

##消費支出を少し深堀り

毎月、総務省統計局が**「家計調査報告」**の中で、**「消費支出」**を[公表](https://www.stat.go.jp/data/kakei/sokuhou/tsuki/index.html)しています。
**「家計調査（二人以上の世帯）2021年(令和３年)2月分　（2021年4月6日公表）」**分を可視化しました。

>**消費支出**
>>消費支出（二人以上の世帯）は，  1世帯当たり  252,451円
>>前年同月比　：　実質 6.6%の減少      名目 7.1%の減少
>> 前月比（季節調整値）　：　実質 2.4%の増加

少々工夫を加え、**政府統計の総合窓口「e-Stat」**からデータを取得し、主要品目ごとに推移と前年同月比を可視化しました。
使用したツールは**Power BI Desktop**です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/1aa219d5-63cd-3b8f-4d65-0989ec602e38.png)

ただ、今回は上記推移を可視化する手順の紹介ではありません。
しばらくわからなかった「消費支出」に関する疑問が解けたので、その記録用にここに記載させて頂きます。

##数字が合わない

消費支出とは、**[「家計調査　収支項目分類の基本原則」](https://www.stat.go.jp/data/kakei/2004np/04nh03.html#:~:text=%E3%80%8C%E6%B6%88%E8%B2%BB%E6%94%AF%E5%87%BA%E3%80%8D%E3%81%A8%E3%81%AF%EF%BC%8C,%E6%B6%88%E8%B2%BB%E6%94%AF%E5%87%BA%E3%81%AB%E5%90%AB%E3%81%BE%E3%82%8C%E3%82%8B%E3%80%82)**によると、以下のようになります。

>原則として日常の生活を営むに当たり必要な商品やサービスを購入して支払った現金支出及びカード，商品券等を用いた支出であるが，仕送り金や贈与金等の移転的支出も含まれる。なお，商品やサービスの購入と一体となって徴収される消費税，自動車取得税等も消費支出に含まれる。
>「消費支出」は支出の目的により，食料，住居，光熱・水道，家具・家事用品，被服及び履物，保健医療，交通・通信，教育，教養娯楽及び「その他の消費支出」に大別している。

昨年後半からデータの取得を始めたのですが、数字が合わないことがありました。
例えば、今回4月6公表値の場合、e-Statから取得した値と**[月次結果（概要及び統計表）](https://www.stat.go.jp/data/kakei/sokuhou/tsuki/pdf/fies_mr.pdf)**の値は以下記の状況でした。

| 費目 | e-Stat | 月次報告 |
|:-:|:-:|:-:|
| 消費支出  | 252,451  | 252,451  |
| 食料  | 69,829  | 72,308  |
| 住居  | 15,136  | 15,138  |
| 光熱・水道  | 26,590  | 26,592  |
| 家具・家事用品  | 9,552  | 9,705  |

##「用途分類」と「品目分類」の2種類があった

**消費支出**の数字は一致しているのと、増減傾向を見る分には困らなかったのでしばらく放っておきましたが、やはり気になったので、調べてみました。
その結果行き着いたのが**「用途分類」と「品目分類」**。
消費支出にはこの2種類の分類があるという記述を見つけました。
こちらです。
　↓
　[ 3　用途分類と品目分類は何が違うのですか？](http://www.stat.go.jp/data/kakei/search/before.html#a03)
　（家計調査の説明ページ「家計調査のデータを探す前に」から）

上記ページによると

>「用途分類」とは，世帯で購入した財やサービスを，その世帯で使うか，それとも贈答用や接待用として他の世帯のために使うかという使用目的によって分類する方法であり，交際のための費用（世帯外の人に対して支出する贈答，接待費）だけをまとめて交際費に分類します。一方，「品目分類」とは，こうした使用目的にかかわらず，同じ財やサービスは同じ項目に分類する方法です。

とのことです。

##数字が合った

そこで、e-Statで取得していたデータから**食料**と**食料（交際費）**に絞り込みました。
それぞれ値は以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/e7804fca-147d-2cab-065e-185b766545f4.png)

>69,829　+　2,479　=　72,308

前述の**月次報告**の数字と合いました。
さらに、よく見てみると、e-Statで**「用途分類」と「品目分類」**それぞれ別々にデータが分かれていることに気づきました。
今まで**「用途分類」**を取得データしていたのです。
調べるのと同時に、e-Statの問い合わせ先にも質問をしました。
翌日に同様の回答を頂き、これで解決です。
なお、「消費支出」は消費支出項目内の内訳をすべて足し上げたものなので、「用途分類」「品目分類」によらず一致します。

もっと前に調べておくべきだったと今さらながら反省しました。
これで安心してデータの取得・推移確認をできます。

**了**
ビットコインブロックチェーンのアドレスもどきを生成してみた

勉強内容のアウトプットです。

```js
// ライブラリ読み込み
const bitcoinjs = require('bitcoinjs-lib')
const crypto = require('crypto')
const base58Check = require('bs58check')

// 乱数（秘密鍵）を生成し、生成した秘密鍵から公開鍵を作成（非圧縮公開鍵）
const keyPair = bitcoinjs.ECPair.makeRandom({rng: crypto.randomBytes, compressed: false})
const publicKey = keyPair.publicKey
console.log('Public Key: ' + publicKey.toString('hex'))

// 公開鍵からビットコインブロックチェーンのアドレスを生成する。
// 最初に公開鍵のSHA256-RIPEMD160ダブルハッシュを生成。
const preHash = sha256(publicKey)
const publicKeyHash = ripemd160(preHash)

// 作成するアドレスは1から始まる（シングルシグアドレス）とし、この場合バージョンバイトは00となる。
const versionByte = Buffer.from('00', 'hex')

// 「バージョンバイト + 公開鍵SHA256-RIPEMDハッシュ」を作る。
let payload = Buffer.concat([versionByte, publicKeyHash])
console.log('Public Key Hash: ' + payload.toString('hex'))

// 今回はチェックサムを付与する
payload = concatCheckSum(payload)
console.log('Payload: ' + payload.toString('hex'))

// Base58エンコードして完成
const address = base58Check.encode(payload)
console.log('Address: ' + address)

// 関数群
// SHA256生成関数
function sha256(data) {
  return crypto.createHash('sha256').update(data).digest()
}
// RIPEMD160生成関数
function ripemd160(data) {
  return crypto.createHash('ripemd160').update(data).digest()
}
// チェックサム付与関数
function concatCheckSum(data) {
  const checkSum = sha256(sha256(data)).slice(0, 4)
  console.log('Check Sum: ' + checkSum.toString('hex'))
  return Buffer.concat([data, checkSum])
}

```