OmniAICreator/Qiita-1.07M huggingface dataset:
DataCatalogで出てくる用語

# はじめに
2019/4に行われたGoogleNext@サンフランシスコで発表された`Google Cloud Platform`（以下GCP）の新サービスの一つに`Data Catalog`というものがあります。

これは何かというと「フルマネージドでスケラービリティの高いデータ検出およびメタデータ管理サービス」（公式より）で、もっとざっくりというと`GCP`上のリソースを簡単に検索でき、またメタデータを付与して管理できるサービスです。

この`DataCatalog`ですがConsole画面から使うと`BigQuery`、`Pub/Sub`などの異なるサービスのリソースを同一画面から簡単に検索できて便利なのですが、CLIやAPIから利用しようとするとあまり見慣れない用語がよく出てきて初めはとっつきにくかったりします。

用語に関してドキュメント等に詳しく書いてあればいいのですが、`DataCatalog`はβ版（2019/12/29現在）という事もあり調べてもあまり情報が出てこないのが現状です。

`DataCatalog`が正式にリリースされればもっと公式の情報も充実すると思いますが、今現在（2019/12/29現在）情報が少ないので私が`DataCatalog`を利用する時に調べて分かった内容を共有できればと思います。

# 注意点
この記事の内容は`DataCatalog`がβ版の時の情報であり今後変更がかかる可能性もありますので、その点ご注意ください。

# 用語
## Entry
`Entry`はGCP上のリソースを表します。
`BigQuery`のデータセット・テーブル、`Pub/Sub`のトピックなどの`GCP`上のリソースは`DataCatalog`では`Entry`というエンティティで表されます。

`Entry`は内部で自動生成されたIDを含むURL形式の`name`というフィールドを持っており、これで一意に表されます。

```shell:sample
# Entryのname
projects/<project-id>/locations/<location>/entryGroups/<entrygroups>/entries/<entry-id>
```

CLI、APIでリソースの情報を取得したりする場合はこの`name`を使用したりします。

## EntryGroup
`EntryGroup`は`Entry`を論理的にまとめたものです。

`DataCatalog`では自動的に`BigQuery`と`Pub/Sub`の`EntryGroup`が作成されており、それぞれ`@bigquery`、`@pubsub`となります。

`BigQuery`上の`Entry`であるデータセット・テーブルなどは自動的に`@bigquery`に所属します。

## Scope
検索範囲を表します。
CLI・APIでは`include_org_ids`、`include_project_ids`、`include_gcp_public_datasets`などで検索範囲を組織、プロジェクト、公開データセットかどうかを指定できます。

```shell:sample
# 指定したプロジェクトIDの範囲でfooという名前のリソースを検索する
$ gcloud beta data-catalog --include-project-ids=<project-ids> 'name:foo'
```
`DataCatalog`では検索する際に自動的に検索範囲を選択しようとするので、検索する際には`Scope`の指定が必要になってきます。

## SearchResult
検索結果。
検索でヒットしたものが何か（`BigQuery`のデータセット・テーブルなど）とそのリソースがどこにあるかの参照などの概要が返ってきます。

CLI・APIで検索した結果から`Entry`の詳細な情報を取得したい場合は、`SearchResult`を元に`Entry`の情報を取得しにいく必要があります。

## TagTemplate
名前の通りタグテンプレートを表します。
`TagTemplate`も`Entry`と同じくURL形式の`name`で一意に表されますが、`Entry`と異なり自動生成されたIDは持たず、ユーザーが指定したテンプレートIDがIDとなります。

```shell:sample
# test_templateというIDで作成している場合
projects/<project-id>/locations/<location>/tagTemplates/test_template
```

## Tag
こちらも名前の通りタグを表します。
同じくURL形式の`name`により一意に表され、`Entry`と同じく自動生成されたIDを持ちます。

```shell:sample
projects/<project-id>/locations/<location>/entryGroups/<entry-group>/entries/<entry-id>/tags/<tag-id>
```
`Tag`は元となる`TagTemplate`の`name`を保持しています。

# まとめ
簡単ですが`DataCatalog`で出てくる主要な用語をまとめてみました。
下記のリンクも分かりやすかったので合わせて見てみて下さい。

# 参考リンク
[Google Cloud Data Catalog hands-on guide: a mental model](https://medium.com/google-cloud/data-catalog-hands-on-guide-a-mental-model-dae7f6dd49e)
[gcp-datacatalog-diagrams - github](https://github.com/ricardolsmendes/gcp-datacatalog-diagrams)
[Cloud Storage filesets](https://cloud.google.com/data-catalog/docs/how-to/filesets?hl=JA)
同一シート内の同一数式を選択するEXCELマクロ

# はじめに
セルに数式を入力し、そのセルをコピーした場合、セルに設定された数式は同じものですが、相対参照を使った場合、数式バーに表示される文字列は違ったものとなります。
この時に、数式バーに表示される文字列が異なっていても相対参照が同じ数式のセルを選択するマクロになります。


# まずは数式をセルに入力
まずB2,B7,B12,B17セルにそれぞれ左上のセルを参照する数式を入力します。
その時
B2セルには絶対参照
B7セルには相対参照
B12セルには列のみ絶対参照
B17セルには行のみ絶対参照
となる数式を入力します。
その後それぞれのセルを横３列、縦３行にそれぞれコピーします。
実行結果はこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/52d3a1c9-1372-31e9-7b18-7b243b1ccb33.png)

その内容を数式で表示した内容は以下になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/ff856264-f73f-7b97-dd02-2f9125e1eb1b.png)

この時、罫線で囲んだ部分がそれぞれ同じ数式ということになります。

# ソースコード
```vb
Public Sub SelectSameFormula()
    'ActiveCellの数式と同一の数式のセルを検索の対象とする
    Dim strFormulaR1C1 As String
    strFormulaR1C1 = ActiveCell.FormulaR1C1
    If strFormulaR1C1 = "" Then
        Exit Sub
    End If
    
    '総当たりするセルの領域を数式の入力されたセルに限定する
    Dim objFormulaRange As Range
    Set objFormulaRange = Cells.SpecialCells(xlCellTypeFormulas)
    
    Dim Result As Range
    Set Result = ActiveCell
    Dim objCell As Range
        
    '数式の入力されたセルを総当たりして同じ数式のセルを取得
    For Each objCell In objFormulaRange
        If objCell.FormulaR1C1 = strFormulaR1C1 Then
            Set Result = Application.Union(Result, objCell)
        End If
    Next
    
    Call Result.Select
End Sub
```

# 使い方
選択したい数式の入力されたセルを選択して
SelectSameFormula()マクロを実行してください。
同じ数式の入力されたセルがすべて選択されれば成功です。
アクティブセルに数式が設定されていなければ何もしません。

# 解説
数式をR1C1形式で表示した結果をみれば一目瞭然のように、
R1C1形式で取得した数式が等しいセルをすべて選択しているだけです。

# 余談
ここからは以下の記事のための参考記事となります。

https://qiita.com/takahasinaoki/items/898078ec37591a380cc1

もし、RangeオブジェクトのプロパティにFormulaR1C1がなくFormulaしかなければ、実現は不可能でしょうか？
実は、VBAにはConvertFormulaという関数が用意されていてA1形式の数式をR1C1形式に変換することが出来ます。

上記のソースのFormulaR1C1を使用している個所を以下に書き換えれば同様の結果を得ることが出来ます。
変更前`strFormulaR1C1 = ActiveCell.FormulaR1C1`
変更後`strFormulaR1C1 = Application.ConvertFormula(ActiveCell.Formula, xlA1, xlR1C1, , ActiveCell)`
変更前`If objCell.FormulaR1C1 = strFormulaR1C1 Then`
変更後`If Application.ConvertFormula(objCell.Formula, xlA1, xlR1C1, , objCell) = strFormulaR1C1 Then`

重要なのはConvertFormula()関数の第4引数と第5引数です。
第4引数についてはマイクロソフトのヘルプによれば次の通り
>変換された参照型を指定する**XlReferenceType** の定数です。
>この引数を省略すると、参照の種類は変更されません。

第5引数についてはマイクロソフトのヘルプによれば次の通り
>1つのセルを含む Range オブジェクトを指定します。
>このセルは、相対参照の基点となります。

マイクロソフトのヘルプは相変わらず不親切で意味が解りません、
内容は変更前または変更後の形式にR1C1形式を指定した場合の、
相対参照の起点となるセルを指定せよということです。
※引数を省略した場合は以下の実験用マクロの結果、セル関数から実行された場合は、セル関数の入力されているセル(Application.ThisCell)。マクロから実行された場合は、ActiveCellが適用されるようです。

つまり今回のケースでは
第4引数は省略することにより、絶対参照は絶対参照のまま、相対参照は相対参照のままR1C1形式に変換させます。
第5引数は変更後の形式にR1C1形式を指定したため、相対参照時の起点となるセル（今回のケースでは数式の入力されているセルそのもの）を指定します。

```vb:実験用
'*****************************************************************************
'[概要] ConvertFormula()関数の第5引数を省略した場合のR1C1形式の起点となるセルを調査する
'[戻値] R1C1形式の起点となるセルのアドレス
'*****************************************************************************
Public Function RelativeTo() As String
    Dim str As String
    str = Application.ConvertFormula("A1", xlA1, xlR1C1)
    'A1セルからの相対位置の符号を反転　例：R[-1]C[-1] → R[+1]C[+1]
    str = Replace(str, "-", "+")
    RelativeTo = Application.ConvertFormula(str, xlR1C1, xlA1, , Range("A1"))
End Function
```
うつ病の僕がアプリ開発をする理由

　こんにちは。ヨースケです！
この１週間**ウイルス性急性胃腸炎**で苦しんでましたw今はお薬をもらってだいぶ楽にはなりましたが、発症当時は1時間に1回は下痢が止まらず中々眠れませんでした...頭痛と発熱もありましたがインフルじゃなくて良かったら...

## うつ病の僕がアプリ開発をする理由
　これには２つ理由があって、１つ目はまたアプリ開発がしたい！と思ったのがきっかけで、大学生の時に授業の一環でアプリを作ってから面白いな！またやりたい！と思ったのが強いての理由かもしれません(笑)。

　そして２つ目はうつ病になってから退職して1年1ヶ月ほど経つのですが、その半年間はほとんどと言って良いほど何もしてきませんでした。地学の勉強とかマイクラで遊んだりとか...やっぱり何かしないと！穴を埋めなければ！と思いアプリ開発をしています。自分でもやっぱり焦ってると感じますが、でも何かはしなくてはなぁーと思ってしまいます。

　開発は牛歩並みですが(笑)勉強しつつアプリを作ってます。今年は１週間前に身体を壊してしまったので、それからはパソコンを立ち上げてません(笑)。アプリ開発再開発来年入って落ち着いてからやろうかなと思います。

　Qiitaw始めて１ヶ月ほどですが今年はありがとうございます。また来年、自分のペースでぼちぼち上げていきますのでよろしくお願い致します。
Vimでカーソルを動かさずにコピペする方法

こんにちわ、ゴリラです。

Vimにはちょっとした便利なExコマンドがあるのですが、あまり知られていない様子なので軽く紹介します。
今回はVimでカーソルを動かさずにコピペする方法について紹介していきます。

# やり方
Exコマンドの`:[range]t {address}`を使います。これは`[range]`行を`{adress}`行の下にコピーするコマンドです。
`[range]`行は複数行を指定することができます。

例えば、カーソル`|`が`package`の行にあっても、`:4t4`を入力したEnterを押すと4行目の下に4行目をコピーしてペーストしてくれます。

## 前
```go
  1 package |main
  2
  3 func main() {
  4     fmt.Println("Hello Gorilla")
  5 }
```

## 後
```go
  1 package main
  2
  3 func main() {
  4     fmt.Println("Hello Gorilla")
  5    |fmt.Println("Hello Gorilla")
  6 }
```

`:t`は特に離れている行をそのまま現在行に持ってくると行ったときに便利です。わざわざカーソルを移動させる必要がないのが大きなメリットと思っています。
ぜひ試してみてください。
TL;DRは用法・用量を守って正しくお使いください


# 要約
- 今読んでいるところがTL;DR。すなわち要約。
- TL;DRはネットスラング。フォーマルには使えない。
- TL;DRに限らず、なんとなくで使わない。

# TL;DRとは
Qiitaの記事序盤によく見かける言葉の1つです。
`Too Long ; Don't Read`の略で、訳すと「(この記事は)文章が長すぎて読めなあぁい(半ギレ)」となります。これが転じて「要約」という意味あいで使われるようになりました。

## もともと非難の意味がある
TL;DR自体、インターネット上のスラングに位置付けられている言葉です。直訳でわかるように、冗長な文章を投稿したユーザーに対する非難の意が込められています。
これを、`「これからちょっと長い文章を書くけれど、はじめにまとめを書いておくから許してね」`という自戒の意とした利用法が転じて、現在の要約にたどり着いたものだと推測します。
とはいえ、スラングであること、もとはネガティブな言葉であることの２点から、**少なくとも公的な場、ドキュメントで利用するものではない**ことがわかります。

# こんな使い方はやめて
## なんとなくTL;DRで始める
**意味を知ったうえで使ってほしい**です。
どう言う意味でTL;DRを使っているのかな？という記事をたまに見かけます。Qiitaでは冒頭にTL;DRを記述する記事がそれなりに存在するため、それらに右ならえで使っているような感じでしょうか。
分からない言葉は調べて、**自分で意味を理解した上で使用する**ようにしましょう。これはTL;DRに限った話ではなく、一般的なことです。コンセプト、コンセンサス・・・などといったカタカナ言葉を、意味を理解した上で使用できているでしょうか。


## Too Longって言ってるのにLong
`なんとなくTL;DRで始める`に当てはまるのかもしれませんが、TL;DRの項目なのに、何行にも文字が連なっている記事もちらほら目にします。完全な自己矛盾に陥っています。せめて5行以内におさめてほしいです。

# アヤしい使われ方
## 概要か、要約か
勝手な感覚ですが、QiitaでのTL;DRは*概要*の意味合いで使われていることの方が多いように思います。
スラングなので、概要として記述しているから誤りだとは断言できません。そもそも*概要*と*要約*は類義語であり、似たような言葉であることに違いないです。とはいえニュアンスの違いとして、以下が挙げられます。

- *概要*：**全体感**を簡潔に示すもの。「これから話すことをおおざっぱに言うとこんな感じ」
- *要約*：重要な**要素を抜き出し**たもの。「私の言いたいことはコレとコレ！」

もう少し具体的には次のようになります。

- *概要*から話す：A案とB案の違いを述べた上で、どちらを採用するかについて話します。
- *要約*から話す：A案を採用しようと思います。そこに至った経緯や理由をこれから話します。

聞き手の印象は全然違いませんか。
*概要*から話す場合は、結局どっちなのかは終盤まで分からないため、もし説明が長かったら、「もういいから結論だけ教えてよ」という言葉が飛び交うかもしれません。
他方で*要約*から話す場合では、結論自体は述べているので、説明が長かったとしても、「分かった、とりあえずA案でいくということでいいね」というような運びになります。[^1]
以上をふまえて、TL;DRの項には**要約を書いて欲しい**というのが個人的な想いです。結論から話し/書き始めて、本題に入る方が、`より短時間で伝わる`でしょうし、Too Long;Don't Readという元の言葉に対しても、*要約*が*概要*に比べて適していると思います。

[^1]: やや極端な流れです。現実的に必ず結論から入ればよい場合ばかりではありませんし、結論だけで経緯を理解してもらわないということも危険です。
Ubuntu 18.04.3 LTS でOSとVimのクリップボードを共有する方法

ども、ゴリラです。

最近買ったThinkPadにUbuntuを入れて念願のLinuxマシンを使える状態にしましたが、
Vimのクリップボードレジスタを使えるようにするまで苦労したので、メモとして残します。
同じことで困っている方の助けになったら嬉しいです。

# 結論
UbuntuでVimをビルドするときは`libxmu-dev`をインストールした上で、`--with-x`オプションが必要です。

```sh
# 必要なライブラリ
sudo apt install libxmu-dev

# ビルド
./configure --with-x --enable-multibyte --enable-fail-if-missing && make && sudo make install

# ビルド終わったらちゃんと `+clipboard` になっているのを確認 
$ vim --version | grep clip
+clipboard         +keymap            +postscript        +vartabs
+eval              -mouse_jsbterm     -sun_workshop      +xterm_clipboard

```
RDBMSの制約　プラマリキー（主キー）ってなんだ？一意が保証される？

データベースについて勉強していた時RDBMSの制約というのがでてきた。

その中に**「一意性制約（UNIQUE KEY)」**というのがあるらしい。
※ここでは一意性制約（UNIQUE KEY)ではなくプラマリキーが正しい
「なるほど。わからん。」

**「一意　＝　唯一無二」**と置き換えて

ここでよくあるユーザー登録を例に制約の必要性について考えてみる。

・サイトを利用するために当たり前のようにユーザー登録がありメールアドレスなど個人のデータを入力して登録する。

・その際サイトの裏側ではデータベースに入力したデータ（ここではメールアドレスにあたる）が入る。

・その後サイトを利用するためにログインをする必要がある。このとき登録されているかどうか。つまり、データベースにデータ（登録したメールアドレス）がはっているかどうか確認する。

・確認するためのデータが必要で（ここではメールアドレス）そのデータは**唯一無二でなければ特定することができない**。（同じメールアドレスが複数あったらどのデータを呼び出したらいいかわかんなくなっちゃう）

↑ではメールアドレスが主キーになっている。

だから**一意が保証されている（主キー）必要がある！**

Railsだとidとか特定できるデータであれば主キーにあたる。

納得した―。今日もいい勉強したな！
馬場真哉『RとStanではじめるベイズ統計モデリングによるデータ分析入門』の誤植情報を集める

馬場真哉『RとStanではじめるベイズ統計モデリングによるデータ分析入門』(講談社, 2019)

- 出版社サイト：https://www.kspub.co.jp/book/detail/5165362.html
- サポートページ：https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/
- GitHub：https://github.com/logics-of-blue/book-r-stan-bayesian-model-intro

どこを探しても誤植情報のまとまったページを見つけられなかったので集めてみます。
サポートページに寄せられたコメントからも拾ってあります。
他にも見つけた方はコメントか編集リクエストで教えてください。

|ページ|説明|発見者|
|-----:|:--|:--|
|16|「単にモデルと呼称することもあります」が2回繰り返されている|hoxo_m|
|20|誤「毎月8月」正「毎年8月」|hoxo_m|
|69|誤「カーネルの比Kernel(-1.27)/Kernel(-0.94)はまたしても小さな値に」正「Kernel(-1.78)/Kernel(-0.94)」|[TK](https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/#comment-4552)|
|80|誤「.Rploj」正「.Rproj」|hoxo_m|
|165|式(3.35) cijとΣの間にイコール記号が必要|[TK](https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/#comment-4552)|
|186|誤「データは第2部第2章でも使った」正「第3部第2章」|hoxo_m|
|225|[図3.9.2が書籍通りに描画できない](https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/#3-5)|[初心者](https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/#comment-4543)|
|239|製品数0店員数0のときに売上が上がるのはおかしい(外挿の問題)|hoxo_m|
|262|誤「A,B,...J」正「A,B,...,J」|hoxo_m|
|276|誤「independend」正「independent」|[TK](https://logics-of-blue.com/r-stan-bayesian-model-intro-book-support/#comment-4555)|
|276|誤「正規分布に絞ることで分析が用意になります」正「容易」|hoxo_m|
|331|誤「`# 基本構造時系列モデルの推定`」正「`# 自己回帰モデルの推定`」|hoxo_m|
||ポアソン分布の表記が p213 では Poiss()、p340,341 では Poisson() になっている。表記揺れ。|hoxo_m|

また、誤植とは異なりますが、**brms** パッケージの `marginal_effects` 関数は、次バージョンでは `conditional_effects` にリネームされるようです。

- https://github.com/paul-buerkner/brms/blob/master/NEWS.md

また、すぐにではありませんが、本書でよく使われている `fitted` 関数も `pp_expect` にリネームされるかもしれません（次バージョンではどちらも使えます）。

- https://github.com/paul-buerkner/brms/issues/644

年末に著者（馬場真哉さん）の新しい本が発売されたようですね。こちらも要チェックです！

- 『R言語ではじめるプログラミングとデータ分析』http://www.socym.co.jp/book/1238

Enjoy!
yarn installで"The engine "node" is incompatible with this module"というエラーが発生した件

## yarnをinstallしようとしたら、以下のエラーが発生しました

```bash
error get-caller-file@2.0.5: The engine "node" is incompatible with this module.
Expected version "6.* || 8.* || >= 10.*". Got "9.5.0"
error Found incompatible module.
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
```

### 環境
Ruby: 2.6.3
Rails: 5.2.3


## メッセージを和訳してみた
「バージョンは、6.* または8.* または10.*以上がいいんだけど、あなたは9.5.0だったよ」
てなとこかな？

## 解決策
#### nodeのバージョンを確認

```bash
$ node -v
v9.5.0
```
やっぱし。

#### インストール済みのnodeのバージョンを確認

```bash
$ nvm ls
      v8.0.0
      v8.9.0
      v8.11.1
      v8.16.0
->    v9.5.0
      v9.10.0
       system
default  v9.5.0
```
9.5.0以外にもインストールされてた。

#### インストールできるバージョン一覧を表示させる

```bash
$ nvm ls-remote
        v0.1.14
        v0.1.15
        v0.1.16
// 省略
        v13.3.0
        v13.4.0
        v13.5.0
```
めちゃめちゃたくさん出てきた。

#### 特定のバージョンをインストールする
今回は10.0.0にします。

```bash
$ nvm install 10.0.0
Downloading and installing node v10.0.0...
Downloading https://nodejs.org/dist/v10.0.0/node-v10.0.0-darwin-x64.tar.xz...
######################################################################## 100.0%
Computing checksum with shasum -a 256
Checksums matched!
Now using node v10.0.0 (npm v5.6.0)
```

#### バージョンを確認

```bash

$ node -v
v9.5.0
```
まだ変わってなかった。

#### バージョンを固定する

```bash
$ nvm use 10.0.0
Now using node v10.0.0 (npm v5.6.0)
```

#### 再度、バージョンを確認する

```bash
$ node -v
v10.0.0
```
バージョンを変更できた。

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

```bash
$ yarn install
```
無事にインストール成功！
TSPをQAOAで解いてみた

#概要
本記事では，TSP(Traveling Salesman Problem)を量子アルゴリズムの一つであるQAOA(Quantum Approximate Optimization Algorithm)で解くときの実装を紹介しています．
ここでは量子プログラミングを行うことなくQiskit Aquaを用いる方法を取っています．
言語はPython3.7，IBMが提供するQiskitを使用しています．
備忘録なので詳しい内容は聞かないでください．

##QAOAとは
NISQアルゴリズムの一つとして考えられていて，量子アニーリングと同様に，組み合わせ最適化問題の解を求めるためのアルゴリズムである．
詳しい解説はここでは避ける．

##QISKIT
Qiskitとは量子コンピュータ用のオープンソースのフレームワークである．また，本記事ではQiskit Aquaを使う．これは，ユーザー自身が量子プログラミングを行うことなく使用できるツールである．現在，化学，AI，最適化，金融の分野がサポートされている．

##TSP
巡回セールスマン問題は，都市の集合が与えられたとき，すべての都市を丁度一度ずつ巡り出発地に戻る巡回路のうち，総移動コストが最小のものを求める組み合わせ最適化問題である．

#TSPをQAOAで解く
ライブラリをimportする

```python:
from qiskit.aqua.translators.ising import tsp
from qiskit.aqua.input import EnergyInput
from qiskit import BasicAer
from qiskit.aqua.algorithms import QAOA
from qiskit.aqua.components.optimizers import  POWELL
from qiskit.aqua import QuantumInstance
from qiskit.aqua import aqua_globals
```

プログラム本体

```python:
#cityの数
n = 3
# 座標を取得(0～100の範囲でx,y座標を取得．三つ目の引数は行列のサイズ)
coord = aqua_globals.random.uniform(0, 100, (n, 2))
# TSPDataに変換．与えられた座標から隣接行列を作成
ins = tsp.calc_distance(coord, 'tmp')

qubitOp, offset = tsp.get_tsp_qubitops(ins, penalty=1e5)
algo_input = EnergyInput(qubitOp)

seed = 10240

optimizer = POWELL()
qaoa = QAOA(qubitOp, optimizer, 1)

backend = BasicAer.get_backend('statevector_simulator')
quantum_instance = QuantumInstance(backend, seed_transpiler=seed)

result = qaoa.run(quantum_instance)
```

結果を表示

```python:
x = tsp.sample_most_likely(result['eigvecs'][0])

print('energy:', result['energy'])
print('time:', result['eval_time'])
print('tsp objective:', result['energy'] + offset)

print('solution:', tsp.get_tsp_solution(x))
```

n=3の時のTSPData

```
TspData(name='tmp', dim=3, coord=array([[66.73333554, 32.83574073],
       [45.99618836, 47.41884594],
       [45.92600616, 25.75398833]]), w=array([[ 0., 25., 22.],
       [25.,  0., 22.],
       [22., 22.,  0.]]))
```

結果

```
energy: -348052.5262025418
time: 57.140453577041626
tsp objective: 252050.97379745822
solution: [2, 0, 1]
```

#まとめ
Qiskit Aquaの使い方の備忘録です．
量子プログラミングの知識がなくてもQAOAが使えるというのはなかなか凄いことだと思いました．
しかし，シミュレーター上なので計算時間がかかることころが難点かなと．
また，[0,0,0]という結果が出るところが気になりましたね．
最後に，初めてQiitaで投稿したので読みにくいところがあるかもしれません．申し訳ありません．

追記：現在tspの回路を作ろうと頑張っています
[TypeScript]importを絶対パスで書く設定

以下の設定を追加すると、import 時の記述を絶対パスで記述したときに `src` 以下を読みに行ってくれる。

```json
// tsconfig.json
{
  "baseUrl": "src"  
}
```

https://www.typescriptlang.org/docs/handbook/module-resolution.html#base-url
iPodをwebサーバとして使う

#はじめに
初めてまして
初投稿です!
今回はiPodでApacheを動かす方法について書きたいと思います(需要...)

#環境
iPod touch (6th generation)
ios 12.4.4

#iSHをインストール
まず「[TestFlight](https://apps.apple.com/jp/app/testflight/id899247664?ign-mpt=uo%3D4)」をインストールします.
次に[公式サイト](https://ish.app/)のJoin the TestFlight beta!のTestFlight betaをクリックしてiSHをインストールできます.

#Apache2の導入
iSHを起動したら

```shell:shell
apk add apache2
```
を実行するとApache2がインストールされます.
Apache2の設定ファイルは

```
/etc/apache2/httpd.conf
```
ルールディレクトリは私の環境では

```
/var/www/localhost/htdocs
```
でした.
次に

```shell:shell
httpd -D UNDERGROUND
```
を実行してください
~~iPod上でブラウザを起動して127.0.0.1もしくはlocalhostにアクセス~~
LAN内の他のデバイスからiPodのipアドレスにアクセスしてください
この時iSHを閉じてホームに戻るとページが表示できません.
It works!と表示されれば成功です.

#最後に
こんなにしょうもない記事を最後まで読んでくださりありがとうございましたm(._.)m
誤りがあれば指摘していただけるとありがたいです.

家に大量のiosデバイスがあるならロードバランサ使って自宅サーバにでき...ないですね.多分
GCPにwindows環境を立ててリモートデスクトップで遊ぶ

## はじめに
macしか持ってないけどでwindows環境が欲しいと思い、GCPにwindows-server立ててリモートデスクトップしたのでそれのメモ

## windows serverを用意する
GCPのアカウント作成をしてクレカ登録までしておく
GCEコンソールでVMを新規作成
ブートディスクに**Windows Server 2019 Datacenter**を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/e9ccf06c-cc59-6207-f2dd-be51e61fe9d2.png)

IPはデフォルトでエフェメラルIP取れるのでOK(時々IP変わるかもしれないので注意)


その後、作成されたインスタンスを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/6319a84c-2e8b-c096-2a5e-e649e10b2667.png)

**windowsパスワード設定**ボタンを押してユーザを作成、パスワードをmemo _φ(･_･
↑これが、windowsのサーバに入るための初期アカウントとなる

## RDPをインストール
https://chrome.google.com/webstore/detail/chrome-rdp/cbkkbcmdlboombapidmoeolnmdacpkch?hl=ja
**Chrome RDP**をインストール


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/8f9a62ce-2e79-e62e-5bb8-4689b9cabdd9.png)

こんな感じの画面になる
このテキストボックスにはさっき作成した自分のサーバの**外部IP**を入力してConnect
<img width="1272" alt="スクリーンショット 2019-12-29 15.22.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/ca6515f4-b041-24a0-4129-d2b3945c07ad.png">
うまく動かないことがあるので、だめだった場合はRDPを終了して再起動:pray:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/ea575aec-dad1-3337-e427-c5d47f75c4d4.png)

Connectするとこの画面になるので、ここで
Domainは空っぽのまま
Usernameとpasswordはさっき**windowsパスワード設定**で設定したユーザ名とパスワード


## ログイン成功
windowsサーバへのリモートログイン完了
デフォルトブラウザがIEだったり、時間がUTCなのでJSTに変えたり、実際に使うためにはもう少し細かな設定が必要そう
あと自分はn1-standard-4で作ったけど結構動作カクつくので、もう少しマシンスペック上げたり、GPU追加で入れたりしないと行けないかもです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178971/6a9eda27-af87-7e64-d563-0534abdd51b9.png)

あと今フルオープンなので、ファイアウォールの設定しないと怖いですね。
Rubyにおけるprint,puts,pの違いについて　随時編集

#Rubyにおけるprint,puts,pの違い


print 'hello (print)' # 改行なしで出力
puts 'hello(put)' # 改行ありで出力
p 'hello(p)' # デバッグ用出力（データ形式がわかる）
アーキテクチャ周りで書きたい事関連で調べていたらレビューのポイントをかける説 ニャ



## 論

* IEEE 1471

> システムの構成要素、それら相互や環境との関係性、及びその設計と発展をガイドする原則を包含するシステムの基本的組織化

* 補足
    * 記述のためのメタモデルと定義を提供する
    * システムの利害関係者 (stakeholder) の関心 (concern) を取り扱うべきと明言する
    * 本質的に多面的ビュー (multi-view) であり、全ての利害関係者の関心を適切に捉えた1つのビューは存在しないことを断言する
    * 視点 (viewpoint) とビュー (view) を分離する。視点は、関心のセットとそれらの関心を取扱うためアーキテクチャを記述するため使われる表現／モデリング技法を識別する。そしてビューは、1つの特定のシステムへ1つの視点を適用した結果である
    * 要求内容と、アーキテクチャ記述に順ずることがその視点とそのビューの間に1対1の対応を持つという考えを確立した
     * 根拠を獲得することとアーキテクチャ記述内におけるビュー間の整合の無さ／未解決な課題を識別するためのガイダンスを提供する


* Bass, Len; Paul Clements, Rick Kazman
 > ソフトウェアコンポーネント、それらの外部特性、またそれらの相互関係から構成される。概要レベルの設計に関する早期の決定を促し、プロジェクト間でのコンポーネントとパターンの設計を再利用することを可能にする

* Martin Fowler (※1より)
> 重要かつ変更が困難な意思決定である

* Grady Booch
> システムを形作る重要な設計上の意思決定であり、その重要性は変更のコストによって図られる

* Michael Keeling
> ソフトウェアが望ましい品質特性や他の性質をどうやって達成していくかについての重要な設計判断が集まったもの

* Neal Ford
> 運用可能まで抽象化したもの

* Ralph Johnson
>「（それがなんであれ）重要なもの」

## 考慮特性

アーキテクチャに関連するxxx性を書いてます（いろいろありすぎ問題）

**品質特性**

品質を表す特性があって、それぞれでトレードオフがある


```RASIS: Reliability（信頼性）,Availability（可用性）,Serviceability（保守性）,Integrity（完全性）,Security（機密性）```

```ユーザビリティ (usability):有効さ (effectiveness),効率 (efficiency),満足度 (satisfaction),利用状況 (context of use)```


and more ...



**機能・非機能、システム要件**

ステークホルダーとの間で決まる事項と、それから発生する品質特性が主だったもの


**構造特性**

システムの構成から発生する特性

**Feature vs Capability**

アジリティとサスティナビリティの機能とシステムとチームを考慮したバランス調整



## レビューのポイント

短期的にするなら QCD で良いはず、中・長期的なら戦略的な視点を考慮したレビューが必要となる


* Quality
  * xxx性が適切度合い: 要求が明確度依存で、適切か否かは判断可能
* Cost
  * リソース: 人とシステムランニングコストの麺で適切か否か判断可能
* Deliver
  * 速度と構成： 要求と速度感依存で適切か否か判断可能


## Reference

* [IEEE 1471](https://ja.wikipedia.org/wiki/IEEE_1471)
* [※1 思考停止しないアーキテクチャ設計 ➖ JJUG CCC 2018 Fall](https://www.slideshare.net/kawasima/jjug-ccc-2018-fall/2)
* [RASIS](https://ja.wikipedia.org/wiki/RASIS)
* [ISO/IEC 25000:2005](https://www.iso.org/standard/35683.html)
* [システム及びソフトウェア品質の見える化、確保及び向上のためのガイド](https://www.meti.go.jp/policy/it_policy/softseibi/metrics/product_metrics_appendix.pdf)
* [非機能要求記述ガイド](https://www.ipa.go.jp/files/000004469.pdf)
* [非機能要求（要件）に関する取組みと『非機能要求グレード』の展開](https://www.ipa.go.jp/files/000008386.pdf)
* [ユーザビリティ](https://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3)
【Git】余計なディレクトリ/ファイルの削除と複数レポジトリの統合


######**不要なディレクトリ/ファイルの削除**

``` 
#必要なディレクトリだけを残して、不要なディレクトリ/ファイルを削除する
git filter-branch -f --subdirectory-filter [残したいディレクトリ]
```

######**レポジトリの統合**

```
#統合したいレポジトリとそのユーザーを指定
git remote add [AnyName] git@github.com:[UserName]/[RepoName].git
git fetch [AnyName]
git merge --allow-unrelated-histories [AnyName]/[Branch]
```
#### --allow-unrelated-historiesについて
無関係なヒストリを持つ２つのブランチをマージしようとすると
`fatal: refusing to merge unrelated histories`と出力されマージできません。
この問題を解決するために`--allow-unrelated-histories`を指定しています。



+ 参考記事
    + [GitHubでFork/cloneしたリポジトリを本家リポジトリに追従する](https://qiita.com/xtetsuji/items/555a1ef19ed21ee42873)
    + [【Git】2つのGitリポジトリを履歴を保持して統合したい](https://qiita.com/nozomi-kawai/items/5efe8e47e54ebccd0bf8)
    + [初めてGitHubリポジトリにpushしたらrejectedエラーになったときの対応メモ](https://qiita.com/takanatsu/items/fc89de9bd11148da1438#%E3%82%B0%E3%82%B0%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%E3%82%89git-fetch--git-merge-originmaster%E3%81%97%E3%81%A6%E3%81%8B%E3%82%89push%E3%81%99%E3%82%8C%E3%81%B0ok%E3%81%A8%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%82%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%A7%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B--merge%E3%82%A8%E3%83%A9%E3%83%BC)
    + [git最強のオプション filter-branch](https://qiita.com/Spring_MT/items/f60c391b5dbf569a1d12)
Laravel6.0でphpコンテナに入れない


Laravel6.0にAuth機能を追加したあとphpコンテナに入れなくなりました。
解決するまで道のりをまとめました。



### Laravel6.0にAuthを追加する方法はこちら

[https://qiita.com/yknsmullan/items/0f04f8098f1a87ffece8](https://qiita.com/yknsmullan/items/0f04f8098f1a87ffece8)


#####docker-compose up -dでphpに入れない
```
sample_mysql_1   docker-entrypoint.sh mysqld      Up       0.0.0.0:3306->3306/tcp
sample_nginx_1   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp    
sample_php_1     sh -c /usr/src/wait-for-it ...   Exit 1   
```
psでupになっていなければ入れていません

#####docker-compose up　でエラー確認
```
 Symfony\Component\Debug\Exception\FatalThrowableError  : Class 'Laravel\Ui\UiServiceProvider' not found
```
Laravel\Ui\UiServiceProviderがないと言われました


#####UiServiceProviderを入れ直すので今入っている物を消します。

composer.json
>"require": {
    "php": "^7.2",
   "fideloper/proxy": "^4.0",
   "laravel/framework": "^6.2",
   "laravel/tinker": "^2.0",
   "laravel/ui": "^1.1"      //←消去
    },

composer.lock
以下全消去

>{
            "name": "laravel/ui",
            "version": "v1.1.2",
            "source": {
                "type": "git",
                "url": "https://github.com/laravel/ui.git",
                "reference": "0287d4eee80aad718bdf7f90117cfe720c493064"
            },
中略
            ],
            "time": "2019-12-08T21:46:50+00:00"
        },

`composer update`　でまっさらの状態にします。
これでUiがなくなったのでphpコンテナに入れるようになります


`docker-compose up -d`　でコンテナ立ち上げる


`composer require laravel/ui`　で入れ直す

`php artisan ui vue --auth`　authを入れる　全部yesでOK

`docker-compose exec php bash` 　phpコンテナに入る

`npm run dev`で完成


追記
cross-env: command not found が出たら
`npm install webpack`でインストールしてください
初学者のためのGIT入門（ローカルからGithubにpushするまで) 

# 初学者のためのGIT入門 
Gitを使って、ローカルからGithubにpushするまでの流れを簡単にまとめてみました。

## git initでローカルリポジトリ準備
```コマンド
$ git init
```
「Initialized empty Git repository in...」と表示される。
# git add でファイル選択
```
$ git add ファイル名
```
# 選択したファイルをメッセージ付きで記録(コミット）
```
$ git commit -m "メッセージ"
```
ここまでで、リモートにアップロードする準備は完了。

#Githubのリモート登録
```
$ git remote add　origin リモートURL
```
リモートURLの見つけ方。
https://www.atmarkit.co.jp/ait/articles/1701/24/news141_3.html
"fatal: remote origin already exists."と表示される場合。
http://pyoonn.hatenablog.com/entry/2014/10/29/191744
# リモートにファイルをアップロード(プッシュ)
```
git push origin master
```
 [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to リモートURL 
が表示される場合。
https://www.softel.co.jp/blogs/tech/archives/3569
https://qiita.com/Hiroki-IT/items/1671514eb659880e5bef
! [rejected]        master -> master (fetch first)
error: failed to push some refs to リモートURL 
が表示される場合。
https://wakky.tech/433518761-html/
https://qiita.com/takanatsu/items/fc89de9bd11148da1438
力技も。
http://blog.digital-squad.net/article/132085683.html
以上、Gitを使って、ローカルからリモートのGithubにファイルをプッシュするまでの方法でした。
Djangoでbitflyerの収支管理アプリを作ってみる（90.[MVC]という概念を理解したい）

画面の理解力が全くないため

※私はHTMLとCSSを軽く触れる程度で、PHPでチャット作ったことはあります。
　しかもバックエンドばかり勉強しててフロントエンドはザルみたいな実装…
　BootStrapとか全然わかんなくてHTMLとCSSだけで泣きながら作ったのも今ではいい思い出。
　~~触ってないからまるで進歩していない~~

とりあえずMVCってなんぞ？というところを理解したくこの記事を別立てしました。

![プレゼンテーション1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314602/26341684-b9c5-4d0d-36a6-5a7086d7c28c.png)

Model、View、Controlerの頭文字を取ってMVC。

大雑把に役割を把握するなら

Model：データベースとのやり取り
View：表示画面を錬成
Controller：司令塔

って感じになりますかね。

プロジェクトにはいくつでもアプリケーションを登録することができ、アプリケーションひとつひとつがMVCを持てます。

今回作ろうとしている収支管理アプリでは、以下のような構成になる予定です。

```
ログイン画面、ユーザー登録画面はUsersデータベースを呼び出して使う
→同じアプリケーションの中に共存させる

その他の画面はデータベースを必要としない
→別アプリケーションにする必要は特にない
```

どんな感じでアプリケーションを作っていくかは次でやっていきます。
Colaboratory 上で事前学習済みモデルを使い、自前画像のラベルを予測する。

## 最終的なノートブック

- https://colab.research.google.com/drive/1TqFR4JCujLWEYDdeNxl3rMd8SMxRltc2

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/b08a75a8-8251-1270-6eac-98a57c611024.png)


## Colaboratory 上でノートブックを作る

[Colaboratory](https://colab.research.google.com/notebooks/) 上で、

- `ファイル` > `Python 3 の新しいノートブック`

から新しいノートブックを作ります。

- `編集` > `ノートブックの設定`

を選択すると次のようなポップアップが表示されるので、`ハードウェア アクセラレート` のドロップダウンメニューから `GPU` を選びましょう。

<img width="536" alt="Screen Shot 2019-12-25 at 16.49.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/de4da49f-d326-8598-8240-64c1de05c426.png">

## 事前学習済みモデルを読み込む

### Tensorflow のバージョン設定

```
!pip list | grep tensorflow
```

上記を実行すると、バージョンが `1.15.0` であることがわかります。

<img width="1366" alt="Screen Shot 2019-12-25 at 16.57.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/219d9c52-e200-f47e-be18-344d36a851fa.png">

```
!pip install -U tensorflow
```

上記を実行すると次のようなエラーが出ますが、今回の範囲内では問題ありませんでした。

<img width="1360" alt="Screen Shot 2019-12-25 at 17.01.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/b3a0772d-2fcf-3559-1a16-46b685812cb4.png">

もう一度 `pip list` でバージョンを確認しておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/0e96fb3e-0226-71c6-cabd-dac167411b07.png)

### ResNetを読みこむ

たったこれだけです。

```
import tensorflow as tf

resnet = tf.keras.applications.ResNet50V2(include_top=True, weights="imagenet")
```

入力と出力の形式を確認します。
入力画像は `224x224` であることがわかります。

```
resnet.input_shape
```

```
resnet.output_shape
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/ad6cacad-e5d1-a925-a9b5-c2a2642af8cd.png)

### 入力に使う自前画像をアップロードして、読み込む

左ペインの `ファイル` から画像をアップロードします。
ここでは、`dog.jpg` という名前です。

次に、画像を読み込み、ResNetの入力用に変形します。

```
import numpy as np
from PIL import Image

img = Image.open("dog.jpg")
input = np.array(img.convert("RGB").resize((224, 224)))
input.shape
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/eecfc190-cdbc-efee-9027-de85a2cd2bbd.png)


### ラベルを予測します

前処理をする必要があるみたいです。
`入力した画像の数xラベル数` になっていることがわかります。

```
preprocessed = tf.keras.applications.resnet_v2.preprocess_input(np.array([input]))
output = resnet.predict(preprocessed)
output.shape
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/97e0faae-9ddb-7f7a-cf6f-025804a77290.png)

結果をデコードします。

```
result = tf.keras.applications.resnet_v2.decode_predictions(output, top=10)[0]
result
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/7f73a688-22c4-6369-5675-15c5b0cae320.png)

- ワンチャンよりもくまもんが注目されて、 `teddy` がトップで予測されています。
- 3番目に `toy_poodle` が無事予測されています。
- ケージのおりから `shopping_cart` が予測されているのでしょうか。
- `pick` はわからないですが、ピックと一緒にギターの弦が写っている画像が多いみたいなので、ショッピングカートと同じ理由でしょうか。

入力画像と比較すると、概ね納得できる結果でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32636/b08a75a8-8251-1270-6eac-98a57c611024.png)
Rustで”HelloWorld!”しようとしたら「error: linker `cc` not found」と表示された時の対処方法

こんにちは。
2019年も残すところ今日を含めて3日となりました。
今年は9連休ということもあり、新しい技術や気になっているプログラミング言語などに入門してみようと考えている人も多いかと思います。
私も、最近AWSがプロジェクト支援を開始したRustに入門してみることにしました。
早速、インストールしてチュートリアルなどをやっていこうと思ったのですが、最初からつまずいたので同じ所でつまずいた人に参考になればと思っています。

# TL;DR
gccコンパイラがインストールされていないと、エラーになるので気をつけましょうという話

# 環境
* AWS EC2(Amazon Linux2)

# 手順
AWS環境で検証していますが、EC2の立ち上げまでの手順は省略します。

## Rustのインストール
以下の日本語ドキュメントを参考に実施しています。
https://doc.rust-jp.rs/the-rust-programming-language-ja/1.6/book/getting-started.html

公式サイト(英語)
https://www.rust-lang.org/

`1.14`以降のバージョンをインストールするため、以下のコマンドを実行します。

```bash
$ curl https://sh.rustup.rs -sSf | sh
```

途中でインストール方法を聞かれますが、デフォルトの`1`を選択してエンターを押下します。

```bash
$ curl https://sh.rustup.rs -sSf | sh
・・・(省略)・・・
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1
・・・(省略)・・・
```

以下の様なメッセージが表示されたらインストールが終わりました。

```bash
Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH
environment variable. Next time you log in this will be done
automatically.

To configure your current shell run source $HOME/.cargo/env
```

メッセージに記載されている`source`コマンドを実行してシェルの内容を反映しましょう。

```bash
$ source $HOME/.cargo/env
```

正しくインストールできているか確認します。

```
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)
```

バージョンが確認できたので正しくインストールできているようです。

## 「Hello,World!」のプログラムを書く
ドキュメントの手順通りに進めていくので、フォルダを作成します。

```bash
$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world
```

次に、プログラムを書いていくファイルを作成します


```bash
$ vi main.rs
```

作成したファイルに以下のコードを記述して保存します。

```rust:main.rs
fn main() {
    println!("Hello, world!");
}
```

保存が終わったら実行してみましょう。

```bash
$ rustc main.rs
```

すると以下の様なエラーが表示されます。

```bash
error: linker `cc` not found
  |
  = note: No such file or directory (os error 2)

error: aborting due to previous error
```

色々調べてみると`gcc`のコンパイラが必要みたいなので、インストールします。

```bash
$ yum install -y gcc
```

インストールが終了したらもう一度`rustc main.rs`を実行してみます。特にエラーもなく`main`の実行ファイルが作成されたみたいです。

```
$ ls
main  main.rs
```

作成された実行ファイルを実行してみると「Hello, World!」が表示されました。

```
$ ./main
Hello, world!
```

# まとめ
調べて見ると意外と単純なことでした。
環境構築の部分は入門者にとっては、最初の関門ですね。
まだまだ、休みは長いのでじっくりチュートリアル進めていくことにします。
内容に不備ありましたら、コメントで教えて頂けると幸いです。
chat.postMessageを利用したpythonでのslack botの開発

ひとつ前の記事でメンションに特定の文字列を返すbotを作成しました。
https://qiita.com/tokoroten_346/items/0aa9c04d5d3f2a956cd2

今回はbotにもう少し機能を持たせていきます。

ファイルの構造は前回同様こんな感じ

```
hello
 ├ hello.py              # botを起動するファイル
 ├ slackbot_settings.py  # botの設定を書くファイル(slackのトークンなど)。
 └─plugins
   └┬ __init__.py          # 空でいい…らしい 
    └ bot_module.py        # 今回はここに記述してbotの機能を足していく
```

早速bot_module.pyに記述してbotの返答を追加していきます。
まずは以下の機能を作ります。
・メンションの特定の単語に対する返答(respond_toを使用)
・チャンネル内の特定の単語に対する返答(listen_toを使用)

コードはこんな感じ

```python:bot_module.py
from slackbot.bot import respond_to
from slackbot.bot import listen_to

# respond_toはメンションすると応答する
@respond_to('Hello')
def mention_function(message):
    #slackに応答を返す
    message.reply('World')

# listen_toはチャンネル内の単語に応答する
@listen_to('おはよう')
def lesten_function(message):
    #slackに応答を返す
   message.reply('ニホンゴワカリマセン')
```
@respond_toでメンションに対する応答を。
@listen_toでチャンネル内に対する応答を記述してます。

その他のコードは前回と同じ

```python:hello.py
from slackbot.bot import Bot

def main():
    bot = Bot()
    bot.run()

if __name__ == "__main__":
    print("Hello bot")
    main()
```

```python:slackbot_settings.py
# 先ほど取得したbotアカウントのトークンを指定する
API_TOKEN = "xxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxxxxxxxx"

# このbotのデフォルトの返答を書く
DEFAULT_REPLY = "おはようございます"

# プラグインスクリプトを置いてあるサブディレクトリ名のリスト
PLUGINS = ['plugins']
```

このbotを実行すると以下のようになります。
![botの返事追加.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/a13cef37-def8-0ea2-1c48-e008b45155b3.png)

「Hello」のメンションに対しては「World」を、チャンネル内の「おはよう」には「ニホンゴワカリマセン」を、登録していないメンションには「返事がない…」を返します。
ちなみにメンションとチャンネル内の投稿が逆だとうまく反応してくれません。
![botの返事エラー例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/38fa1803-a14a-bf22-14b4-e72f2b0b0f8a.png)

listen_toとrespond_toで返答が可能になったので次はchat.postMessageのapiを利用していきます。
https://api.slack.com/methods/chat.postMessage

bot_module.pyを下記のように変えていきます。

```python:bot_module.py
from slackbot.bot import respond_to
from slackbot.bot import listen_to
import json
import requests

# respond_toはメンションすると応答する
@respond_to('Hello')
def mention_function(message):
    post_url = 'https://slack.com/api/chat.postMessage'
    token = 'xxxx-xxxxxxxxx…'  #Bot User OAuth Access Tokenを記述
    channel = 'xxxxxxx'        #チャンネルのURLの末尾についている文字列
    username = '挨拶bot'       # botの名前
    icon_emoji = ':ghost:'     # 表示アイコン(今回はゴーストにします)
    text       =  'おはよ～'    #slackに応答を返す

    #json形式で記述
    attachments = [{
        'text': text,
    }]

    payload = {
        'token': token,
        'channel': channel,
        'username': username,
        'icon_emoji': icon_emoji,
        'attachments': json.dumps(attachments)
    }

    res = requests.post(post_url, data=payload)
    print (res.status_code)

# listen_toはチャンネル内の単語に応答する
@listen_to('おはよう')
def lesten_function(message):
    #slackに応答を返す
   message.reply('ニホンゴワカリマセン')
```

これを実行すしslacktest君にHelloとメンションを送ると
![ゴースト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542570/88c52f89-a2a4-765e-8fd2-c0571550cd25.png)

アイコンがゴーストの挨拶bot君がおはよ～と返してくれます。
chat.postMessageを使うことでアイコンの変更やjsonでの記述が出来るようになりました。
よく使いそうなGitコマンド

#1.環境
git version 2.24.1.windows.2

#2.用語の整理
1. リモート/ローカル
    1. リモート
        1. リモートリポジトリ
    2. ローカル
        1. ローカルリポジトリ
        2. ステージ
        3. ワークツリー
2. 「.git」ディレクトリの中身
    1. リポジトリ
        1. 圧縮ファイル
        2. ツリーファイル
        3. コミットファイル
    2. インデックスファイル
    3. 設定ファイル

#3.コマンド
##git --version
##git --help

##[TODO]git --config
ユーザー名の設定
メールアドレスの設定
エディタの設定

##git init
ローカルリポジトリを新規作成する。

##git clone
リモートリポジトリのコピーをローカルに作成する。

##git add
ワークツリーでの変更をステージに追加する。

- git add ファイル名
- git add ディレクトリ名
- git add .
    - 全変更が対象

##git commit
ステージに記録されている変更をローカルリポジトリに記録する。

- git commit
- git commit -m "メッセージ"
    - エディタを起動せずにコミット
- git commit -v
    - 変更内容を表示

##git status
ローカルでの変更状況を確認する。

- ワークツリー　⇔　ステージ
- ステージ　⇔　リポジトリ

##git diff
ローカルでの変更差分を確認する。

- git diff
- git diff ファイル名
- git diff --staged
    - ステージ　⇔　ローカルリポジトリの差分

##git log
変更履歴を確認する。

- git log --oneline
- git log -p ファイル名
- git log -n コミット数

##git rm
- git rm ファイル名
    - ローカルリポジトリ、ステージ、ワークツリーから削除する？
- git rm -r ディレクトリ名
- git rm --chached ファイル名
    - ローカルリポジトリから削除する？

[TODO]：rmコマンドはステージに対してはどういう影響を与えるのか？

##git mv
##git remote add
##git push
##git config
##.gitignoreファイルのこと

##git checkout
##git reset HEAD
##git commit -amend

##git remote
##git fetch
##git pull
##git merge
##git remote show
##git remote rename

##git branch


# 4.[TODO]確認したいこと
- コマンドの引数に「--」を付ける/付けないの違い。
Vue.jsでのmethods, computed, watchの使い分け

備忘録としてのメモです。

# computed
- 算出プロパティ
- **リアクティブな依存関係に基づいてキャッシュさ**れる
- **リアクティブな依存関係が更新されたときにだけ再評価**され、依存がない場合は更新されない

以下、リファレンスより
> Vue インスタンスに組み込まれる算出プロパティ (Computed property) です。すべての getter や setter は、自動的に Vue インスタンスに束縛された this コンテキストを持ちます。

> 算出プロパティはキャッシュされ、そしてリアクティブ依存が変更されたときにだけ再算出します。ある依存関係がインスタンスのスコープ外の(つまりリアクティブではない)場合、算出プロパティは更新されないことに注意してください。

# methods
- よくあるメソッド
- **再描画が起きる度に実行**される

以下、リファレンスより
> Vue インスタンスに組み込まれるメソッドです。VM インスタンスでは、これらのメソッドに直接アクセスでき、ディレクティブの式で使用することもできます。すべてのメソッドは、Vue インスタンスに自動的に束縛された this コンテキストを持ちます。

# watch
- 監視プロパティ
- **定義済のプロパティを監視し、変更があった場合に実行**される

以下、リファレンスより
> キーが監視する評価式で、値が対応するコールバックをもつオブジェクトです。値はメソッド名の文字列、または追加のオプションが含まれているオブジェクトを取ることができます。Vue インスタンスはインスタンス化の際にオブジェクトの各エントリに対して $watch() を呼びます。

# サンプル
- Aコースへ応募するボタン（`computed`）を押すと、関係のないBコースへの応募ボタン（`methods`）も呼ばれてしまうことがわかります。
- 逆に、Bコースへの応募ボタン（`methods`）を押した時は、Aコースへ応募するボタン（`computed`）は呼ばれません。
- また、Aコースの応募数のを監視（`watch`）し、5秒後にカウントがリセットされることも確認できました。

```html
<div id="support">
  <p>現在のAコースへの応募数: {{ countA }}</p>
  <p>現在のBコースへの応募数:  {{ countB }}</p>
  <br>
  <p>現在の状態（computed）{{ computedResult }}</p>
  <p>現在の状態（methods） {{ methodsResult() }}</p>
  <br>
  <button @click="countA ++">Aコースに応募する</button>
  <button @click="countB ++">Bコースに応募する</button>
</div>
```

```js
new Vue({
  el: "#support",
  data: {
    countA: 0,
    countB: 0
  },
  computed: {
  	computedResult: function() {
        console.log('Aコースへの応募を受付');
    	return this.countA > 5 ? '応募は締め切りました' : '応募受付中です'
    }
  },
  methods: {
  	methodsResult: function() {
        console.log('Bコースへの応募を受付');
    	return this.countB > 5 ? '応募は締め切りました' : '応募受付中です'
    }
  },
  watch: {
  	countA: function() {
    	var vm = this;
      setTimeout(function(){
      	vm.countA = 0
      }, 5000)
    }
   }
})
```
wordpress バックアップファイルをGCSに自動でアップする

##### 経緯
普段、HPに使っているwordpressのバックアップは、プラグインでバックアップファイルを自動作成するよう設定しています。使用しているプラグインはBackWPupというもの。
これは指定フォルダにバックアップファイルを作成してくれ、定期実行も設定できる優れものなのですが、クラウドストレージへのアップも自動化したいと考えていました。
<img width="987" alt="スクリーンショット 2019-12-29 15.06.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74331/af82ca2c-6176-bb12-7587-4aa1da3cb737.png">

環境
GCP 
+ google compute engine VM
+ Bitnami wordpress + apache stack
+ google cloud storage

### 手順
##### インスタンスのアクセス権を書き込みできるように変更
デフォルトではこのGCPのインスタンスはGCSへの書き込みアクセス許可がありません
<img width="291" alt="スクリーンショット 2019-12-29 14.49.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74331/b91758d2-b441-cd8c-b83a-9f75b3d79777.png">
このため、アクセス権を変更します。

1. インスタンスを停止
2. インスタンスを編集、ストレージアクセス権を「読み取り/書き込み」に変更
3. インスタンスを起動

##### フォルダの変更検知をしてアップするよう設定
フォルダ変更検知にinotifyを使いたいのでapt-get install します
$ sudo apt-get install -y inotify-tools

新規ファイルの作成の変更検知
+ 新規作成だけでいいので、-e CREATE オプション
+ 継続で検知させるため -m オプション
$ inotifywait  -m -e CREATE /opt/bitnami/apps/wordpress/htdocs/wp-content/uploads/backwpup-85ccd3-backups

GCSへのバックアップファイルのコピー
+ 既にあるのであればアップしないよう-n オプション
$ gsutil cp -n /opt/bitnami/apps/wordpress/htdocs/wp-content/uploads/backwpup-85ccd3-backups/*.zip gs://dist-bucket/wp/

上記コマンドを合成
+ ログアウトしても継続するようnohup &
+ パイプで渡す際にパスを調整する必要があるので --format "%w%f"
+ 複数同時に作られてもいいよう while (
$ nohup sudo inotifywait -e CREATE --format "%w%f" -m /opt/bitnami/apps/wordpress/htdocs/wp-content/uploads/ba
ckwpup-85ccd3-backups | while read -r file; do gsutil cp -n $file gs://dist-bucket/wp/; done &

<img width="751" alt="スクリーンショット 2019-12-29 15.05.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74331/4d71ccd5-fe40-441e-b1cc-a4b537a132d3.png">
Pandasでよくやる処理まとめ1 （CSV、Excelファイル関連操作）

そろそろ業務でPandasを使いそうな気配がしてきているので
何番煎じか分からないけど、よく使う処理をまとめてみたいと思います。
※動作検証には、Pandasのバージョン0.25.1を使用しています。

### CSVファイルから読み込む場合

```python
import pandas as pd

# ファイル名：sample.csv
# 以下、ファイルの中身を記載
# 1,サンプル太郎,30
# 2,サンプル花子,25
# 3,サンプル二郎,28
df = pd.read_csv('sample.csv', 
                 # Noneだとヘッダーなし、数値を指定するとその行をヘッダーと解釈する
                 header=None, 
                 # インデックスにするカラムを指定する ※ここでは、先頭のカラム(1,2,3の部分)
                 index_col=0, 
                 # カラム名を定義する
                 names=('name', 'age'),
                 # カラムのデータ型を定義する
                 dtype={'name': str, 'age': int}
                )
```

### Excelファイルから読み込む場合

```python
import pandas as pd

# ファイル名：sample.xlsx
# ※ファイルの内容は上記のCSVファイルと同じ
df = pd.read_excel('sample.xlsx', 
                   dtype={'name': str, 'age': int}
                  )
```

### Excelファイルから複数シートを同時に読み込む場合

※データ分析よりは、Pandasを使ってツールを作りたいときに知ってると便利

```python
import pandas as pd

excel_book = pd.ExcelFile('sample.xlsx')

# 読み込んだシートのシート名の一覧がリストで取得出来る
sheet_name_list = excel_book.sheet_names

# シート名を指定すると、そのシートの内容が取得出来る
df = excel_book.parse('sheet1')
```

### データフレームの内容をCSVファイルに出力する場合

```python
import pandas as pd

# 第一引数は出力ファイル名
df.to_csv('sample.csv', 
          # ヘッダーが不要の場合に、Falseを設定する
          header=False, 
          # インデックスが不要の場合に、Falseを設定する
          index=False, 
          # 出力するカラムを制限する場合に設定する
          columns=['name']
         )
```

### データフレームの内容をExcelファイルに出力する場合

```python
import pandas as pd

# 第一引数は出力ファイル名
df.to_excel('sample.xlsx', 
          # ヘッダーが不要の場合に、Falseを設定する
          header=False, 
          # インデックスが不要の場合に、Falseを設定する
          index=False, 
          # 出力するカラムを制限する場合に設定する
          columns=['name']
         )
```
Git 管理下でgit clone をしたときに発生する不整合を直す



# 起こったこと

## Git管理下でgit clone すると面倒に。
Git管理しているプロジェクトなどでモジュール追加したくてgit cloneしたが、
ファイル自体は本体でバージョン管理したいから
git clone した階層の.git/ を手動で削除すると
本体gitのほうで不整合が生じる模様。

(Deployサーバーでエラーが出る。)

```
Failing build: Failed to prepare repo
Error checking out submodules: fatal: No url found for submodule path 'path-to-target' in .gitmodules
Failing build: Failed to prepare repo
failed during stage 'preparing repo': Error checking out submodules: fatal: No url found for submodule path 'path-to-target' in .gitmodules
```


# 解決策

以下のようにするととりあえず解決
(outputが出ないときもあるが。。)

```
$ git rm -r --cached --ignore-unmatch path-to-target/
```

あとは、普通にcommit すれば直るよう。

```
$ git commit
```


# 回避策は?

Git管理下でgit cloneしたいなら、

```
$ git submodule add https://github.com/sachiotomita/project path-to-target
$ git submodule deinit path-to-target
$ git rm path-to-target
$ # git config -f .gitmodules --remove-section submodule.path-to-target # もしgit v1.8.5 以前なら
```

するか、直接zipでダウンロードするしかないか。
ESP32でDashボタンを作る(ESP32-DevKitC+MicroPython+IFTTT)

ESP32-DevKitCでAmazon Dashボタンもどきを作ります。
C言語はわからないのでMicroPythonです。
簡単です。

# 準備
ESP32-DevKitC
IFTTTアカウントとWebhookをトリガーとしたアプレット

# 開発環境
Windows 10
Visual Studio Code
Putty
Python 3.7(ストア版)

# 開発環境構築

## Pythonの仮想環境作成

```console
python -m venv .venv
.venv\Scripts\activate
```

## ライブラリー導入

```console
pip install esptool adafruit-ampy
```

## ESP32にMicroPythonファームの導入

http://micropython.org/download#esp32
からファームウェアをダンロードします。
（2019/12/29の最新版はesp32-idf3-20191220-v1.12.bin）

```console
esptool --port COM4 erase_flash
esptool --chip esp32 --port COM4 write_flash -z 0x1000 esp32-idf3-20191220-v1.12.bin
```

`COM4`は環境に合わせて修正します。

これでMicroPythonのファームウェアが導入でき、シリアル接続をすると対話インタプリタモード（REPL）でMicroPythonがESP32上で実行できます。

Puttyで接続する場合は`COM4`ポートにSpeed`115200`を指定し、シリアル接続します。

![コメント 2019-12-29 143339.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/dda1d4f7-2401-7325-0ae4-df7ab3ce26a2.png)


# Dashボタンプログラムの導入

MicroPythonはREPLで実行もできますが、ソースコードをESP32側に導入することでも実行できます。`boot.py`と`main.py`というファイルが標準で用意されていて、`boot.py`のあとで`main.py`が呼ばれる仕組みのようです。
`main.py`を書き換えます。

```python:main.py
wifi_ssid = '[Wi-Fi SSID]'
wifi_password = '[Wi-Fi パスワード]'
get_url = '[IFTTTのWebhook URL]'

def do_connect():
  import network
  sta_if = network.WLAN(network.STA_IF)
  if not sta_if.isconnected():
    print('connecting to network...')
    sta_if.active(True)
    sta_if.connect(wifi_ssid, wifi_password)
    while not sta_if.isconnected():
      pass
  print('network config:', sta_if.ifconfig())

try:
  do_connect()

  import urequests
  r = urequests.get(get_url)
finally:
  pass

import machine
print('deepsleeping..')
machine.deepsleep()
```

IFTTTのWebhookは予め準備しておいてください。
処理の流れとしては

1. 電源が入る
2. Wi-Fi接続
3. IFTTTにリクエスト送信
4. ディープスリープに入る

となってます。

肝心のDASHボタンの`ボタン`は、基板上に用意されている`EN`ボタンを使います。
ボタンが押されたらリセットし、頭から処理をするだけです。処理が終わったらディープスリープに入って省電力モードに入るとうわけです。

簡単！

電池の持ちは実験してみようと思います。
Dockerコンテナ・イメージ全削除コマンド

# 一切合切全部消したい

取り扱いにはご注意を！

```bash:bash
docker stop $(docker ps -q)
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -q)
```

```fish:fish
docker stop (docker ps -q)
docker rm -f (docker ps -aq)
docker rmi -f (docker images -q)
```
#Stripe でトライアル期間のあるサブスクリプション＝定期支払いを作成しようとすると初回請求に繰越金額が含まれる様だが、これは何？

単純に顧客の支払いに繰越金額の請求予定があり、それが反映されているだけのようだった。「顧客残高」を参照のこと。


![image](https://user-images.githubusercontent.com/13635059/71538704-c9fc7f00-2972-11ea-951f-42a54903d22b.png)
![image](https://user-images.githubusercontent.com/13635059/71538708-e4cef380-2972-11ea-87c7-423c7f6947c7.png)

![image](https://user-images.githubusercontent.com/13635059/71538713-1647bf00-2973-11ea-9731-8dd6c75f48e4.png)


# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2886








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
【AWS】AWS Trusted Advisorについて

# Trusted Advisorについて
Trusted Advisorでは4つのカテゴリに関するベストプラクティス（チェック）が提示される

- コスト最適化
- パフォーマンス
- セキュリティ
- 耐障害性

## インジゲーターについて
- 赤色  
 今すぐ対応が必要
- 黄色  
 調べてみる価値がある
- 緑色  
 全てが整った素晴らしい状態
Cloud Build を使って静的ページを Cloud Storage にデプロイする

こんばんわ！ @ktoshi です！
今日は静的ページのデプロイについてお話します。

# 目的
静的ページを Cloud Storage を使って公開したい。
でも、バージョン管理をしているから、CI/CDっぽいことをやりたい。
それなら、CloudBuild を使ってレポジトリへのコミットをトリガーにファイルの転送をしちゃおう！
なお、本手順では一部ベータ機能を利用していますので、ご利用は自己責任でお願いいたします。

# 準備
以下のものを準備してください。  

* GoogleCloudPlatform のアカウント
* [gcloud](https://cloud.google.com/sdk/gcloud/reference/?hl=ja) コマンドのインストール
* Git コマンドの設定

# Cloud Source Repositories の作成
数多のレポジトリがありますが、Cloud Build を使う上では CloudSourceRepository を
使うのが便利です。
なお、他のレポジトリとミラーリングをすることができるので、今お使いのレポジトリを使いながら、CoudSourceRepository を使うことができます。

```shell
# レポジトリの作成
gcloud source repos create SAMPLE_REPO --project="SAMPLE_PROJECT"
```
これで空のレポジトリの作成が完了です。簡単ですねー。

# Cloud Storage の作成
肝心の公開用となるストレージを準備します。
とりあえず、サンプルということで権限設定とかリージョン設定は適当に。。。

```shell
# バケットの作成(gs:// に続くストレージ名称は全ユーザで一意な必要があります)
gsutil mb -c standard -l asia-northeast2 -p SAMPLE_PROJECT gs://sample_storage_name

# 公開用の権限付与
gsutil acl ch -u AllUsers:R -p SAMPLE_PROJECT gs://sample_storage_name
```
これで公開用のストレージの準備が終わりました。

# Cloud Build のトリガー作成
レポジトリへのコミットをトリガーに自動でビルドとデプロイ処理を行うためのトリガーを作成します。

```shell
# トリガーの作成
gcloud beta builds triggers create cloud-source-repositories --repo="SAMPLE_REPO" --branch-pattern=".*" --build-config="cloudbuild.yaml" --project="SAMPLE_PROJECT"
```
これで「SAMPLE_REPO」にコミットをすれば、cloudbuild.yaml の設定を参照してビルドとデプロイが走るようになります。

# 実践
環境の準備が整ったので実際にデプロイができるところまでいろいろやってみましょう。

## ローカルレポジトリの作成
皆さんご存じだとは思いますので、特には記載しません。
あくまで手順の一つとしてご認識ください。

```shell
# 作業用ディレクトリの作成
mkdir sample_project

# 作業用ディレクトリへ移動
cd sample_project

# 初期化
git init
```

## リモートレポジトリの登録
CloudSourceRepositories をリモートレポジトリとして登録します。

```
git config --global credential.https://source.developers.google.com.helper gcloud.cmd

# リモートレポジトリに追加
git remote add google https://source.developers.google.com/p/PROJECT_NAME/r/SAMPLE_REPO
```

## レポジトリの登録確認
上記で登録したリモートレポジトリが正しく動作しているか確認します。
作業用ディレクトリに適当なファイル（README.md）などを作成しておいてください。

```
git add .
git commit -m "First Commit."
git push --all google
```
これでエラーが表示されなければ、正しく動作しています。
ただ、このままではビルドが動きません。

## ビルド設定ファイルの作成
ルートディレクトリに cloudbuild.yaml を作成し、
その中にビルドの設定を記載します。

```yaml:cloudbuild.yaml
steps:
- name: gcr.io/cloud-builders/gsutil
  args: ['rsync', '-d', '-r', '.', 'gs://sample_storage_name/']
```
このファイルをコミットすると Cloud Build が動き出します。
実は最初のコミットの時も動いているのですが、 cloudbuild.yaml が存在しないのでエラーになっています。

## 確認
Cloud Storage にファイルが上がっているか確認しましょう。

```shell
gsutil ls gs://sample_storage_name/
```
これでファイルが出力されればOK！！

# まとめ
静的ファイルでもバージョン管理をする時代、いちいちサーバにアップロードするなんて面倒ですよね。
そういうときにはこのような仕組みを作っておくと便利ですね。
まあ静的なサイトの事例は少なそうですが、Vue.js などのビルドと一緒にとやると必要性はあがりそうですね。
実は今回の記事を書こうとした大元は Vue.js のビルドも混みでやっていたので、それはまた別の記事で書こうと思います。
それでは皆様も素敵なライフをビルドしてください。
良いお年をー。