OmniAICreator/Qiita-1.07M huggingface dataset:
dockerでSlackの(Classicな)Botを動かしてみる

この記事は[Takumi Akashiro ひとり Advent Calendar 2020](https://qiita.com/advent-calendar/2020/takumi_akashiro "ひとり Advent Calendar 2020")の21日目の記事です。

##初めに
今年はSlackのBotを作らないとなーと思う場面が少し多い一年でしたが、
今後マシンがぶっ壊れたときに、環境構築をまた1からやるのも面倒ですし、
引継ぎ用の環境構築ドキュメントを用意するのもダルいので、
Dockerでいい感じに環境構築して、だれの環境でも簡単に動かすことを考えないとな、と思った次第でございます。

それが23時間ほど前。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">2020年の末にもなってDockerImageの一つも作れないので、克服するために調べよう。2021年にはこの遺恨を残してはいけない(大げさ)</p>&mdash; 赤城 巧 (@takumi_akashiro) <a href="https://twitter.com/takumi_akashiro/status/1340695788247076865?ref_src=twsrc%5Etfw">December 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

そのまま寝て起きて、業務を始め20時から、この記事を書くために動き始めた！
さあ間に合ったのか！間に合ったので記事になっているんですが。


## SlackのBotを作るための準備

今回はRTM(リアルタイムにイベントがとれるヤツ)で動かしたいので、ClassicBotを作ります。
https://api.slack.com/apps?new_classic_app=1


適当にBotをインストールして、`Bot token`を作っておきます。

## 各種ファイルの準備

以下のファイルを用意します。

* src/requirements.txt
    * pipでインストールするモジュールを指定する
* src/app.py
    * botの本体です。
* Dockerfile
    * Dockerイメージを作成するためのファイルです。

今回はslackclientモジュールで適当に作ります。Slack公式が用意しているので安心ですね。

```text:src/requirements.txt
slackclient
```

何か書こうと思いましたが、全く時間的に余裕がないので
Slack公式が用意した、RTMAPIのサンプルコードを使います。
[Real Time Messaging (RTM) — slackclient (Legacy Python Slack SDK)](https://slack.dev/python-slackclient/real_time_messaging.html)

```python:src/app.py
import os
from slack import RTMClient

@RTMClient.run_on(event="message")
def say_hello(**payload):
  data = payload['data']
  web_client = payload['web_client']

  if 'Hello' in data['text']:
    channel_id = data['channel']
    thread_ts = data['ts']
    user = data['user'] # This is not username but user ID (the format is either U*** or W***)

    web_client.chat_postMessage(
      channel=channel_id,
      text=f"Hi <@{user}>!",
      thread_ts=thread_ts
    )

slack_token = os.environ["SLACK_BOT_TOKEN"]
rtm_client = RTMClient(token=slack_token)
rtm_client.start()

```

```Dockerfile:Dockerfile
FROM python:3.8

ENV SLACK_BOT_TOKEN <Botのトークン>
RUN mkdir src
copy src src/

RUN pip install -r ./src/requirements.txt

WORKDIR ./src/
ENTRYPOINT [ "python", "app.py" ]
```

こんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/032fa488-20cb-96be-9881-f68b1af91cd8.png)

## Dockerイメージを作成する

`docker build -t <imagename>:<tag> <DockerFileのディレクトリ>`

今回は
`docker build -t aktk/slack_bot:latest d:\docker\slackbot`


チョー簡単。

## Dockerイメージを動かす

`docker run -d -p 80:80 aktk/slack_bot`

### SlackのBotの前でHelloしてみる
![21_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/8acc28f0-a410-ab1f-ec7f-58c561cb1c7b.gif)
うおおおお！動いた！

## 締め
思った以上に簡単にできてビックリですね！！
こんなに簡単にできるなら、もっと早く触るべきだったなあ、ってちょっと後悔してます。

将来的にはPySide2製のEXE化が必要なツールの環境構築＋ビルドとかにも使いたいですが、
コンテナはLinuxで動いてるっぽいので、もっと調べないと駄目だろうなあ……
[Ruby] 後置 if

参考書で後置 ifについて学びましたので理解するため記事にしてみました。


#後置if
ifやunlessは行頭だけではなく、条件に成立したときに評価してほしい句の後ろに置くことができます。この書き方を「後置if」と呼びます。

ifについてはこちら
https://qiita.com/muffin-men/items/fd8637bb1b98cb369c5b

unlessについてはこちら
https://qiita.com/muffin-men/items/9d3ab806634624f5f29f

```ruby
# これは出力されます
puts 'おはようございます' if true

# これは出力されません
puts 'おつかれさまでした' if false
```

#まとめ
後置ifを使った場合は1行分の処理しか記述できませんし、elseも書けません。処理が複数行にわたっていたり、elseなど他の条件も書きたい場合には通常のifを使います。処理が1行で済む単純なケースでは、後置ifが使われることがよくあります。

#参考書
・ 現場で使える Ruby on rails 5 速習実践ガイド
最低限のターミナルコマンドを使えるようにしてanaconda3をインストールし直す

<details><summary>環境</summary>
機種名:	MacBook Pro
バージョン:　11.1
OS:　macOS Big Sur
</details>
<details><summary>前提条件</summary>
・python: 3.9.0
・anaconda3
</details>
##状況
#####1.問題提起
昨日まで使えていたjupyterlabを今日ターミナルから開こうと
```$jupyter lab```
するも結果は
``zsh: command not found: jupyter``
とのこと。要するにzshにPATHが通っていない様子。
このままではjupyter labが起動できない。
#####2.目的地
PATHを通してターミナルからjupyter labを起動できるようにする。

#####3.手段
下記のいずれかを用いてPATHを通す
① anaconda3をインストールし直す
[Anaconda3をしっかりアンインストールするにはどうしたら良いですか？](https://ja.stackoverflow.com/questions/63312/anaconda3%e3%82%92%e3%81%97%e3%81%a3%e3%81%8b%e3%82%8a%e3%82%a2%e3%83%b3%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e3%81%99%e3%82%8b%e3%81%ab%e3%81%af%e3%81%a9%e3%81%86%e3%81%97%e3%81%9f%e3%82%89%e8%89%af%e3%81%84%e3%81%a7%e3%81%99%e3%81%8b)
[Anaconda3をmacOSから完全にアンインストールする方法](https://weblabo.oscasierra.net/python-anaconda-uninstall-macos/)
② 再度exportでzshファイルに変更を加える

#####4.過程

この時点ではbash、、?　zsh、、？状態。
下手に手を加えて状況が悪化しないように、これもよい勉強の機会だと言い聞かせ
まずはこれらについて調べた。

・参照サイト
↓PATHについての説明
[[Mac]環境変数PATHの設定・変更・追加・確認・順番入れ替えの方法](https://gabekore.org/mac-path-environmental-variable)
<details><summary>メモ用個人的解釈型要約</summary>
  :"PATH"とはPC？への命令の際、位置情報を伝えるもの
  ``例).(PATHが無い時)やきそばパン買ってこいよ←どこで？全国行っちゃうよ？見つかんないよ？？
　　    (ある時)"コンビニ"でやきそばパン買ってこいよ←徒歩30分のとこがあったな、売り切れてなければいけるか←今ココ``
</details>
↓PATHについての説明2(コマンドの概念とPATHの探し方)
[PATHを理解して、コマンドの在りかを探してみよう](https://www.atmarkit.co.jp/ait/articles/1605/09/news013_3.html)
<details><summary>メモ用個人的解釈型要約</summary>
  :「コマンド」とは厳密にはファイルの名前で"PATH"を通してそれを指定することで行動につながる
  ``例)."コンビニ"まで徒歩３０分までだったな、「自転車を使う」としよう``
</details>
↓zsh及びbash(いわゆるシェル)についての説明
[bashとzshの違い。bashからの乗り換えで気をつけるべき16の事柄](https://kanasys.com/tech/803)
<details><summary>メモ用個人的解釈型要約</summary>
  :bashは前から使われていたもの、zshは新しめ。まあ新しいぶん改良はされてるよね。
　``例).bash→ヤキソバパンカテコイ(ポケベルあんまわからん)
　　　　zsh→焼きそばパン買ってこい(スマホ)``
</details>
ログも残っていないためPATHの戻しようがなく、基本的なコマンドも一部使えない状態のため``conda install anaconda-clean``
も実行できない状態。したがって手段①は現状期待できず。
検索しながらトライアンドエラーを繰り返し６時間ほど経過。

もう何がなんだかわからなくなってきた。
なによ！condaの意気地なし！condaなんかもう知らない！！

するとふと、以下の記述を目にした。
>これである程度のコマンドは動くようになったので真っ先に~/.zshrcを直しました。
[環境変数(PATH)を上書きして「command not found」](https://qiita.com/yut-kt/items/bb699ff1fa6a76614762)


↑上記サイトによるとターミナルにて下のコマンドを入力すると"ある程度のコマンド"が動くようになるらしい。すぐさまターミナルに入力し実行。
``export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin``


せめてcondaよ立ち上がってくれ、、
``conda install anaconda-clean``　→実行

、、た、立った！condaが立った！！

あとは手段① anaconda3をインストールし直すだけだ！
[Anaconda3をしっかりアンインストールするにはどうしたら良いですか？](https://ja.stackoverflow.com/questions/63312/anaconda3%e3%82%92%e3%81%97%e3%81%a3%e3%81%8b%e3%82%8a%e3%82%a2%e3%83%b3%e3%82%a4%e3%83%b3%e3%82%b9%e3%83%88%e3%83%bc%e3%83%ab%e3%81%99%e3%82%8b%e3%81%ab%e3%81%af%e3%81%a9%e3%81%86%e3%81%97%e3%81%9f%e3%82%89%e8%89%af%e3%81%84%e3%81%a7%e3%81%99%e3%81%8b)
[Anaconda3をmacOSから完全にアンインストールする方法](https://weblabo.oscasierra.net/python-anaconda-uninstall-macos/)
上記サイトを参考に再インストール。

ターミナルを起動し直し、ついでにpcも再起動し``jupyter lab``を実行。

ブラウザぶぉーん(起動音)
玉くるくるー
見慣れたインターフェースぶぁーん(起動音)

やったぁぁぁぁーーーーー！！！！！(昇天)

ということで

#####5.結果
anacondaの再インストールにて解決

再インストールまでの道のりが辛すぎて途中諦めそうになりましたが、
作業しながらつまんでいたチョコポテトのおかげでやり遂げることができました！
ありがとう炭水化物オン炭水化物！！
僕のような迷い人に役立つことを祈ります！
では！
[Ruby on Rails] Rails serverがCtrl + C で停止できない時の停止方法

#はじめに
`rails server` のコマンドを打ったターミナルを閉じてしまった時など、 `Ctrl + C` でサーバーを停止することができなくなった時の対処法を解説していきます。

##環境
macOS Catalina バージョン　10.15.7

#手順
##`rails server` のプロセス番号(PID)を調べる

```
$ lsof -i:3000
```

実行すると現在作動中の `rails server` のプロセス番号(PID)が表示されます。


##`kill` コマンドの実行
表示されたプロセス番号(PID)を指定して`kill` コマンドを実行します。

```
$ kill -9 プロセス番号(PID)
```

これで停止されるでしょう。
RSpecを使ってテストコードを書いているが、うまくいきません

#はじめに
　この記事を読んでも、何かの解決に繋がることはありませんので、ご容赦ください。個人的な悩みのアウトプットです。

##今の悩み
　Fackerを使ってユーザーのテスト用の情報をビルドして、ちゃんと保存することができるかのテストを行っています。

###課題

```ruby:factories/users.rb

FactoryBot.define do
  factory :user do
    Faker::Config.locale = :ja
    class_room_id { 1 }
    email { Faker::Internet.free_email }
    first_name { Faker::Name.first_name }
    last_name { Faker::Name.last_name }
    nickname { Faker::Name.last_name }
    attendance_number { Faker::Number.between(from: 1, to: 40) }
    password = Faker::Internet.password(min_length: 6)
    password { password }
    password_confirmation { password }

  end
end
```

```ruby:user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'ユーザー登録' do
    before do
      @school = FactoryBot.create(:school)
      @class_room = FactoryBot.create(:class_room)
      @user = FactoryBot.build(:user)
    end
    it '全ての項目が入力されていれば、登録できること' do
       expect(@user).to be_valid
    end
  end
end
```
「全ての項目が入力されていれば、登録できること」これが保存できない！！！！


- 仮説１ 
外部キー(class_room_id)にしているとアソシエーションしていても、ダメなのか。

- 仮説２
ヘルパーメソッドでnumber_field（class_room_id）に直接、値を入れてはダメなのか。

##最後に

　相談しているわけではないので、数少ないコードだと思うのですが、もしエラーの原因が分かる方がいましたら、ご教授ください。
Goクイズ: int = float64

以下のコードを実行すると何が起こるでしょうか?

```go
package main

import "fmt"

const a int = 2

type number = int
type int = float64

func main() {
    const b int = 2
    const c number = 2
    fmt.Println(1 / a, 1 / b, 1 / c)
}
```

1. `0 0.5 0` が出力される
2. `0.5 0.5 0` が出力される
3. `0.5 0.5 0.5` が出力される
4. コンパイルエラー

<details><summary>回答</summary>
`0.5 0.5 0.5` が出力されます． [block と scope](https://golang.org/ref/spec#Declarations_and_scope) の問題で type alias はあまり関係ありません．
Go標準の `int` 等の識別子は最も外側のブロックである `universe block` に存在し，内側のブロックで再宣言可能です．また，上記コードで宣言した `number` や `int` は `package block` なので `main` パッケージ全体で有効です．言い換えれば，上記のコードに現れる `int` は `type` 宣言の前か後かに関わらず全て `main` パッケージの `int` でそれは float64 を指します．なのでa,b,cは全て浮動小数点数型になります．
</details>
node.jsで下ネタと自動化

# 概要
ターミナルで重い処理走らせて、ネットサーフィンしてたら、ターミナルの処理が終わってるのに気づかなかったって事よくありますよね（えっ）

こんな時に使える最低なライブラリをnode.jsで作成しました。

名前はtinntinn
下ネタではないです。
それが証拠にnpmライブラリとして配布出来ました。

使い方は簡単。ターミナルで
yarn add global tinntinn
でインストールし、
tinntinn
とターミナルに打つとベルがチンチンなります。

つまりbashで
'何か重い処理' && tintinn
とすれば、処理が終わった瞬間に音で気づけるって訳です。

ちなみにソースコードは３行

```js
#!/usr/bin/env node
console.log = function () { } // play.jsが出力する不要なconsole.logを非表示に変更
require('play').sound('./alarm.mp3')
```
github
https://github.com/t-kabaya/tinntinn

俺のtinntinnは最高だぜというために作ったライブラリですが、自作したOSSの中で今、一番役に立っています笑
気に入ったらstarください。

注意: 一部mac osでは音が鳴らないようです。
Xamarin.Forms の local image のファイル名の制限

画像ファイルに適当な名前を付けてハマりました。`123.png` という名前を付けたのですが...

## Local image

Xamarin.Forms で **Image** クラスの **Source** プロパティに local image を指定するときには、使えないファイル名があるので注意が必要です。公式ドキュメントの「[Images in Xamarin.Forms](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/images)」によれば、次の通りです。

> only lowercase letters, numerals, the underscore, and the period are allowed

小文字、数字、アンダースコア、ピリオド。数字も大丈夫なのでは？ Android プロジェクトの Resources/drawable に画像ファイルを置いて試してみましょう。先頭の文字にはもう少し制限があることが分かります。

| ファイル名 | 説明 |
|---|---|
| `sample.png` | 可。 |
| `123sample.png` | 不可。Resource.designer.cs のビルドに失敗します。Resource.designer.cs に `123image` という変数が書き出されますが、C# の変数は数字で開始することはできません。 |
| `_123sample.png` | 可。 |
| `.sample.png` | 不可。ビルドは通りますが、画像は表示されません。Resource.designer.cs に書き出されません。 |
| `s.ample.png` | 不可。次のようなビルドエラーとなります。: file name cannot contain '.' other than for specifying the extension. |

iOS ではアセットカタログを使うので、何でもいけます。何でも？ さすがに `/` は使えません。しかし、iOS だけで使える名前にしても仕方がないので、上記の制限の範囲で名前を付けることになります。

`.png` は省略できます (PNG 以外の拡張子は省略できません)。次のどちらの書き方も有効です。

```xml
<Image Source="sample.png"/>
<Image Source="sample"/>
```

アセットカタログの名前に `.png` を付けるのは変な感じがするから？ そのせいでピリオドが使えなくなってる？ このあたりの事情は分かりません。

## Embedded image

共通プロジェクトの embedded image なら数字で開始することもできます。

| ファイル名 | 説明 |
|---|---|
| 'sample.png` | 可 |
| `123sample.png` | 可 |
| `_123sample.png` | 可 |
| `.sample.png` | 可 |
| `s.ample.png` | 不可 |

embedded image では、画像ファイルのパスの区切りにピリオドを使います。例えば `App1\images\sample.png` ならば次のように指定します。

```c#
Image1.Source = ImageSource.FromResource("App1.images.sample.png");
```

`s.ample.png` が不可なのは、`s` フォルダの下を探しに行ってしまうからでしょう。ちなみに、`.sample.png` は `"App1.images..sample.png"` という指定で表示できました。

また、local image とは違って、`.png` は省略できないようです。
【教材】JavaScriptの教材を作ってみました4　〜Dateオブジェクト〜

※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。

半人前のくせに、偉そうに教材を作ってみました。

####学べる技術
- Dateオブジェクト
- 配列の操作
- console.table



##問題１
年と月を引数に与えたら、(例: (2010, 10))最終日の曜日を漢字（例：火)でログ出力する関数を作ってください。

<details><summary>回答例</summary><div>

```js
const getDay = (year, nextMonth) => {
  let days = ["日", "月", "火", "水", "木", "金", "土"];
  let target = new Date(year, nextMonth, 1 - 1);
  console.log(days[target.getDay()]);
}
  //月は0から起算するため、nextMonthに1を入れた場合2月と判断します。
  //1-1は1日の前日と言う意味です。つまり入力した月の翌月の1日の前日。つまり今月の最終日が取得できます。（もちろん0でもいいです。)
  //getDay();で曜日が取得できます。0〜6で返る。日曜日が0。
```
</div></details>

##問題2
異なる2つの年月日を引数(例:("2010-04-08","2020-12-21"))にとり、その間が何日間であるかをログ出力する関数を作ってください。

<details><summary>回答例</summary><div>

```js
const getDate = (day1, day2) => {
  let Day1 = new Date(day1);
  let Day2 = new Date(day2);
  console.log((Day2.getTime() - Day1.getTime()) / (1000 * 60 * 60 * 24));
}


//getTime関数は、1970年1月1日0時0分0秒からの経過ミリ秒を返します。
//2日間の、経過ミリ秒の差を出してから、1000ミリ秒*60秒*60分*24時間で割って日数に直します。
```
</div></details>

##問題3
make_calendar関数に月を引数に与えたら、(例: 　make_calendar(2020, 12)　)下記のようにカレンダー風にログ出力するプログラムを作成してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/da02aaee-2b9b-c04a-080e-e872f8f8bb65.png)


<details><summary>回答例</summary><div>

```js

const day = ["日", "月", "火", "水", "木", "金", "土"]; //初期配列

const preMonth = (firstDay) => { //1日より前の部分作成
  for (let i = 0; i < firstDay; i++) { //今月の初日の曜日の数値回ループ 例:火曜なら2
    day.push('');//初期配列に ''を追加
  }
}

const toMonth = (lastDate) => { //今月部分作成
  for (let i = 0; i < lastDate; i++) { //今月の最終日回繰り返す
    day.push(i + 1);//初期配列に １から今月の最終日まで追加
  }
}

const getDay = (target) => { //曜日取得関数
  return target.getDay();
}

const make_calendar = (year, month) => { //本体

  const firstDate = new Date(year, month - 1, 1);//今月の初日取得
  const lastDate = new Date(year, month, 0);//最終日取得

  preMonth(firstDate.getDay()); //引数は初日の曜日。
  toMonth(lastDate.getDate());  //引数は最終日の日付。

  const num = Math.floor(day.length / 7); //ループ回数を決定

  const calendar = []

  for (let i = 0; i < num + 1; i++) { //numは切り捨てられてるため+1
    calendar.push(day.splice(0, 7));//７個づつ切り出して、push
  }
  console.table(calendar); //console.tableで二次元配列をテーブル表示
}
```
</div></details>

※回答例はあくまで一例です。あまり鵜呑みにしないでください。
いろいろあったのでゲームを作ってみた

[スターフェスティバル Advent Calendar 2020](https://qiita.com/advent-calendar/2020/stafes) の21日目です。

# はじめに
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/d9bdeb51-14e6-96eb-40e4-36d9a1e6f77b.png">
LCDを使って久々にarduinoで遊ぼうと思っていたのですが、うまいことプログラムの書き込み出来ずに21日が終わりそうなので急遽ゲーム作りへ路線変更します。

# なにをつくるか
とりあえず、パッと何を作るか決めます

## 技術的には

- reactで作る
- 時間がないのでreactnativeにする（クソゲーとか作る時にRNでつくってるので
- Expoはつかうよ

## どんなゲームにするか

- 21を目指すブラックジャック的なやつ
- 1-10の数字がランダムで2つくばられて追加で引くか引かないか
- Aの概念はいれない（ブラックジャックではAが1or10になる
- オフライン対戦（対面に人がいる想定
- 21をオーバーまたはパスでターンが変わる

とりあえずこんな感じにします

# 作る
時間もないので作っていきます

**とりあえずinit**
`expo init bj --npm`
bjはブラックジャックの略

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/ea3fd4e8-f374-9926-2c1a-ef5ff216b497.png)

あれ、`blank (TypeScript)` って前からあったかな？
（ts自体最近からしっかり使うようになったのであまり意識してなかった)
とりあえすtsで進めます。（練習も兼ねて）

**ユーザーの名前を入力させる**
Topページに名前を入力するところを設置しました。

<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/2f0496ee-f470-4147-a99c-06740c162df9.png">

**ゲーム開始画面**
ユーザー名を決めたら、ゲームの画面へ移動しアラートでゲームの開始をお知らせ
この時に、初回の２回分の数字を追加しておく（ここ、何でたか表示すれば良かったなと反省。データは持っているので出すだけですが

<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/68ae14fb-b5e2-5ad7-7fe9-5852bb215642.png">

<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/9087ef9f-8ab7-27df-a645-e584e74fb22d.png">

**引くを押したら、カードが引かれ加算される**
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/577e32f0-7884-1545-17fe-6fd2b9b710f5.png">


# 完成
<img width="200" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82179/ff608b22-c92f-c00d-02ee-b74b0d35b662.gif">

# まとめ
時間が足りなかったので完成図のみを画像で貼り付けるだけになってしまいました...
~~コードはそのうちどっかに載せるかもです。~~
githubに載せました
https://github.com/shota1995m/bj-app

そして、先攻のユーザーがバーストした場合、後攻のユーザーはそのまま勝ちというゲーム性の問題も発覚しました。
ディーラーはとても重要な役割ですね。

初めてのアドベントカレンダーで何をすれば良いかよくわからなかったですが楽しかったです。
来年もやるならもうちょっとマシな記事を書けるようになっていたいですね
M1 Macでpuppeteerを動かす方法（2020年12月21日時点）


# 動作環境
Mac Book Air 2020 M1
macOS Big Sur 11.0

# エラー内容
[ヘッドレス Chrome Node API 「Puppeteer」 - Qiita](https://qiita.com/bezeklik/items/c6448d50ff0efb45829e)を参考にpuppeteerを動かしてみようとした時に下記コマンドが arm64ではエラーが出たのでその対処方法を書きます。

```zsh
yarn add puppeteer
```

```zsh
buzzword111@buzzword111-MacBook-Air Puppeteer % yarn add puppeteer
yarn add v1.22.10
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
error /.../Puppeteer/node_modules/puppeteer: Command failed.
Exit code: 1
Command: node install.js
Arguments: 
Directory: /.../Puppeteer/node_modules/puppeteer
Output:
The chromium binary is not available for arm64: 
If you are on Ubuntu, you can install with: 

 apt-get install chromium-browser

/.../Puppeteer/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserFetcher.js:112
            throw new Error();
            ^

Error
    at /.../Puppeteer/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserFetcher.js:112:19
    at FSReqCallback.oncomplete (node:fs:199:21)
info Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.

```

# puppeteerを動かす方法
## 1.ARM64版のGoogle Chromeをインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/a8054144-9b8b-2450-370f-76c0e7471a74.png)
インストールするGoogle Chromeの実行ファイルは下記に配置されます。
```/Applications/Google Chrome.app/Contents/MacOS/Google Chrome```

## 2.```puppeteer```の代わりに```puppeteer-core```をyarn addする。

```zsh
yarn add puppeteer-core
```

```
buzzword111@buzzword111-MacBook-Air Puppeteer % yarn add puppeteer-core
yarn add v1.22.10
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
success Saved 51 new dependencies.
...
```

## 3.puppeter 5.5.0時点ではまだM1 Macに対応していないのでパッチを手動であてる。
```javascript
// node_modules/puppeteer-core/lib/cjs/puppeteer/node/Launcher.js L:73
if (os.arch() === 'arm64' && false) { // ← && falseを追記
    chromeExecutable = '/usr/bin/chromium-browser';
}
```

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/f5963cd4-371e-7e01-ab47-a20eedf9bd12.png)
> https://github.com/puppeteer/puppeteer/issues/6641#issuecomment-748965065

***このパッチを適用しないと、Google Chromeの実行パス(executablePath)が固定で ```/usr/bin/chromium-browser```で上書きされてしまう。***


## 4.動作確認
```javascript
const puppeteer = require('puppeteer-core');

(async () => {
  const browser = await puppeteer.launch({
    executablePath:
      '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  })
  const page = await browser.newPage()
  await page.goto('https://example.com')
  await page.screenshot({ path: 'example.png' })

  await browser.close()
})()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136617/a4e7a09a-edf3-31f9-94aa-9dda7c950c6e.png)
フクロトジの高可用性のヒミツ

## はじめに

[2020アドベントカレンダー](https://adventar.org/calendars/5938) 21日目です！


まず、はじめに


**「フクロトジ」というサービスはご存知ですか？**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295396/eed058f8-48b2-ddbc-c38b-de5eeea6b4d8.png)

フクロトジはクリエイターは**秘密の写真・映像・音声を有料で公開**、ユーザは**”袋とじ”の感覚で楽しむ**ことのできる18禁SNSで、2020年6月末にリリースしました！

2020年12月に入り、**ユーザ総数50000人を突破**した、今勢いのあるサービスです！

ぜひ「覗き」にきてください！(スマートフォンのみアクセスが可能です)

　↓↓↓
[フクロトジトップへ！](https://fukurotoji.tokyo/)
　↑↑↑

私はサービス提供元の株式会社ハイボールでフクロトジの開発にエンジニアとして携わっています。

## タイトル「フクロトジの高可用性のヒミツ」

この記事ではフクロトジの可用性について触れます。

## 読者対象
・フクロトジユーザで、**開発者が具体的にどんな対応しているのか気になる方**
・フクロトジの**サーバが落ちないか気になる方**

## 可用性(Availability)とは

**システムが停止することなく稼働し続ける能力**を意味し、稼働率として表せます。

例として、必ず90時間に一度障害が発生して停止するシステムがあって、障害の発生の度にシステム再稼働に10時間を要するならば、このシステムは100時間で90時間しか稼働しないため、可用性(=稼働率)は90％です。

**フクロトジではこの可用性がほぼ100%**で、サーバがダウンし、アクセスできなくなったことはないです！（外部のサーバーを利用する決済やログインの機能が一時的に利用できなくなったため”ほぼ”の表記になります）

## この可用性に起因するものは何か

フクロトジのほぼ100%可用性を担保するものは一体何なのか？

### 1. インフラの対応

サーバのアクセス数が増加してもそれに耐えうるような負荷分散の構成になっており、またシステムの再稼働に大幅な時間を要しません。

### 2. コードの完全性・テストの充実

機能開発をしたエンジニアがコード自体の完全性を担保すべくテストコードの実装や、デプロイ前の確認などを入念に行っています。

### 3. CSチームによる確認

デプロイにおける最終確認として、開発された機能が要件にあっているかを多重に確認して万一でもエラーがしないよう努めています。


## 可用性に加えて、ユーザビリティの向上の施策

・画像のアップロードをバックグラウンド処理化し、ロード画面の待機時間を削減
・画像の表示にかかる時間を画像サイズの軽量化と、キャッシュの機能を利用することで削減
・アイコンによる操作のガイドや、購入画像のフクロトジを開封するときのアニメーションなどの楽しめる要素

以上のようにただ”どの時間でも利用できる”だけでなく、”利用することの心地よさ”にも追求しています。

## まとめ

**『アクセス急増によりサーバがダウンした！』**といった現象が起こるくらいにユーザがさらに増えることを期待して🙌
備忘録(Ruby:基礎文法:クラスとインスタンス)

### クラス
値の元となるもので値の共通のルールを定義する
共通のルールを守った上で個別のデータを作ることもできる
クラス名は半角英大文字から始める

```ruby
class クラス名
  # 変数やメソッドの定義
end
```

### インスタンス
クラスを元にして作られるデータ
クラスとは異なり実態をもつ
インスタンスはクラスが使用できるnewメソッドを実行して生成する

### newメソッド
クラスは定義しなくてもnewという特別なメソッドを持っている

例:

```ruby
class Car

end

fire_truck = Car.new  # Carクラスのインスタンスを生成
puts fire_truck  # インスタンスを出力
```
[Rails] Herokuでデプロイする一連の流れ

デプロイ
------
`ネットワークを通じてwebアプリケーションを公開すること`をデプロイといいます。
具体的にいうと
誰でもアクセス可能なサーバーを用意し、その中にwebアプリケーションを設置します。

Heroku
------
`アプリケーションの公開、運用を行うためのサーバー`です。
Gitと連携してリポジトリをHerokuへ反映することで、
Heroku上で反映したアプリケーションの操作が可能になります。

Heroku CLIをインストール
--------------------
`CLIをインストールすると、
herokuというコマンドが使用できるようになり、ターミナルからHerokuへアクセスが可能になります。`

公開したいアプリケーションディレクトリで下記コマンドを実行

```言語:ターミナル
 brew tap heroku/brew && brew install heroku
```

インストールしたら下記のコマンドで成功を確認する

```言語:ターミナル
heroku --version 

heroku/7.20.0 darwin-x56 node-v12.1２.2   ← # versionが出力されたら成功

```

Herokuにログイン
-------------
下記コマンドを実行し、ログイン情報を入力する

```言語:ターミナル
 heroku login --interactive
 => Enter your Heroku credentials.
```

Herokuにアプリケーションを作成
-------------------------
```言語:ターミナル
heroku  create  アプリケーション名
```
```言語:ターミナル
git config --list | grep heroku   # 成功を確認
```

Herokuのデータベース設定
--------------------
HerokuではデフォルトでPostgreSQLというデータベースが設定されています。
今回はMysqlに設定を変更してみます。

`ClearDBアドオン`は、MySQLを使うためのツールです。
これを追加すると、HerokuでMySQLを使用できるようになります。

```言語:ターミナル
heroku addons:add cleardb   # ClearDBアドオンを追加
```

データベースをMySQLに設定できました。
Ruby on Railsを使う場合は、MySQLに対応するGemについて考慮する必要があり、そちらの設定を変更します。

```言語:ターミナル
# ClearDBデータベースのURLを変数heroku_cleardbに格納

heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`
```
その後データベースのURLを再設定します。

```言語:ターミナル
heroku config:set DATABASE_URL=mysql3${heroku_cleardb:4}
```

Herokuにmaster.keyを設置
------------------------
因みに下記はcredentials.yml.encをmaster.keyで復合して確認するコマンドです。

```言語:ターミナル
EDITOR="vi" bin/rails credentials:edit
```

Herokuに環境変数（箱）を設置し、その中にmaster.keyを入れて
セキュリティ性を上げます。

環境変数はOSが提供するデータ共有機能の1つで、
`「どのディレクトリ・ファイルからでも参照できる変数」`を設けることができます。
今回は、外部に漏らしたくない情報を環境変数に設置してみます。

```言語:ターミナル
# heroku config:set 環境変数名="値"
  heroku config:set MASTER_KEY=`cat config/master.key`
```

設置したら成功したか確認します。
今回だったら”MASTER＿KEY”という変数名で値が設置されていたら成功です。

```言語:ターミナル
heroku config 
```

アプリケーションの情報をHerokuへ追加
------------------------------
開発環境のアプリケーションにに追加機能を実装し、
その変更をHeroku上のアプリケーションにも反映したい場合は
`masterブランチにコミットし、Herokuにプッシュします。`

下記コマンドでHerokuへコミットをプッシュします。

```言語:ターミナル
git push heroku master
```
Herokuでmigrationを実行
----------------------
アプリケーションの情報を反映できたので、
あとはマイグレーションの情報を反映して終了です。

```言語:ターミナル
heroku run rails db:migrate
```

結果確認
-------
下記コマンドで公開したアプリケーションの情報を確認します。

その後、表示されたURLにアクセスすれば
公開されたアプリケーションが見れます。

```言語:ターミナル
heroku apps:info
```
---------------------------------------------------------------------------------
この記事は下記の記事を参考させていただきました
https://qiita.com/NaokiIshimura/items/eee473675d624a17310f
よく使う（僕が）vscode.command

まず初めにvscodeマインド
1.全部覚えようとしない。
2.とりあえず触ってみる。


.shift + カーソル
範囲選択

.command + カーソル
端に移動

.option + 左右
単語移動

.shift + command + カーソル
端に移動+範囲選択

.option + shift + 左右
単語移動+範囲選択

.command + z
元に戻す

.command + shift + z
やり直す

.command + d
複数範囲選択

・option + クリック
複数選択

・option + command + カーソル
上下複数選択

・option + Shift + ドラッグ
ドラッグで複数選択できる
[Ruby] 条件分岐 unless文

参考書でunless文について学んだため記事にしてみました。

認識違いがあった場合などは是非ご指摘下さい。

#unless文とは
if文は条件の結果が真だったときに処理を実行しますが、それとは反対に、偽だったときに処理を実行するunless文という処理構文があります。

ifについてはこちらにあります。
https://qiita.com/muffin-men/items/fd8637bb1b98cb369c5b

今回はunless文 / if文を比較してみたいとおもいます。
それでは下記のコードにてご説明します。

####unless文
```ruby
age = 16
unless age >= 20
  puts "未成年者にはお酒を提供できません"
end
```

####if文
```ruby
age = 16
if age < 20
  puts "未成年者にはお酒を提供できません"
end
```

unless文はifの条件を裏返して利用するものであり、unless文で表現できることはif文でも表現可能です。
同じことが書けるのならば、どっちを使ったら良いのか疑問を抱くはずです。

上記のコードではunlessのときは「20歳以上かどうか(もし違うなら)を意識することになるのに対して、ifのときは「20歳未満かどうか」を意識することになります。20歳以上かどうかという条件をほかでも多用している場合は、それをそのまま書けるunless文のが分かりやすいです。


#まとめ
基本的にはif文を使うのが分かりやすいですが、条件の内容として直感的に把握しやすい方を利用するためにはunlessを選びます。

#参考書
・ 現場で使える Ruby on rails 5 速習実践ガイド
Next.jsで作ったLIFFアプリをFirebaseで認証する

## 概要
LIFFアプリをFirebase Authenticationで認証する方法です。

## 前提知識

- LIFFアプリの概要
- typescript
- React
- Nextjs

## 流れ

1. LIFFアプリにログイン
2. フロント側でLIFFの情報`id token`を取得
3. 取得した`id token`を認証用apiにpost
4. サーバー側で`id token`の有効性を検証する
5. 検証したらFirebase Authenticationでカスタムトークンを発行して返却
6. サーバーから送られてきたカスタムトークンでFirebase authにログインする

## サーバー側のコード
Firebase Authenticationのカスタムトークンを発行するAPIをNextjs api routesで作成する
https://nextjs.org/docs/api-routes/introduction

[LINE APIの公式ドキュメント](https://developers.line.biz/ja/reference/line-login/#verify-id-token)から仕様は確認できます

```typescript:pages/api/verify.ts
const Verify = async (req, res) => {
    // id tokenを取得
    const idToken = req.body.idToken;

    // id tokenの有効性を検証する
    const response = await fetch('https://api.line.me/oauth2/v2.1/verify', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: qs.stringify({
            id_token: idToken,
            client_id: process.env.LIFF_CHANNEL_ID,
        }),
    });
    const data = await response.json();
    if (response.status !== 200) {
        // IDトークンが有効ではない場合
        res.status(400).send(data.error);
        return;
    }

    // LINE IDでfirebaseトークンを発行して返却
    const token = await auth.createCustomToken(data.sub);
    res.status(200).send(token);
};
```

## フロント側のコードの一部(React)

```typescript
useEffect(() => {
        if (!liff.isLoggedIn()) {
            // LIFFログインしていなかったらリダイレクト
            router.push('/login');
            return;
        }
        auth.onAuthStateChanged(user => {
            if (user) {
                setCurrentUser(user);
            } else {
                // 作成したapiにidトークンをpost
                fetch('/api/verify', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        idToken: liff.getIDToken(),
                    }),
                }).then(response => {
                    response.text().then(data => {
                        // 返ってきたカスタムトークンでFirebase authにログイン
                        auth.signInWithCustomToken(data).then(response => {
                            const user = response.user;
                            setCurrentUser(user);
                        });
                    });
                });
            }
        });
    }, []);
```
DataPortal: 新機能さかのぼり（ピボット テーブルでの「欠落データ」表示の管理）

DataPortalのまだ知らない使えそうな機能を求めて、新機能の一覧を遡ってみました。

ピボット テーブルでの「欠落データ」表示の管理
> ピボット テーブルでの欠落データの表示方法を管理できるようになりました。標準的な表と同様に、ピボット テーブルでデータが欠落している場合に、空白、ハイフン、または「データなし」という表記のどれを表示するか選択できます。

早速試してみます。

![スクリーンショット 2020-12-21 23.18.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/cbc223f2-5ec0-c724-89f2-2ccffcb02a07.png)
![スクリーンショット 2020-12-21 23.18.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/7df13d9b-141f-066e-e613-eda6fa86642b.png)

ピボットテーブルで様々な欠落データの表現ができるようになっていました。
いつからか当たり前のように使っていたので、そういえば昔はできなかったなと往時を振り返りました。

新機能をさかのぼってみて、けっこうピボットテーブルの更新があったという事に気づきました。
ピボットテーブルはわりとよく使うので更新はありがたいのですが、できたら結構以前から要望があがっている[列合計との比率](https://issuetracker.google.com/issues/77605771)を表示する機能が実装されてほしいです。
Docker exposeプラグインでngrokが払い出したURLを取得する

[Docker exposeプラグイン](https://github.com/lukaszlach/clip/tree/master/clips/expose)は最終的にclip-exposeというコンテナを立てるのだが、残念ながら4040ポートをフォワーディングしないのでlocalhost:4040でURLを取得できない。

clip-exposeコンテナのIPを調べて```http://[コンテナのIP]:4040/api/tunnels```とすればURLを含んだjsonファイルを取得できるのだが、docker exposeコマンドを使うたびにIPが変わるようなのでいちいち調べたのでは意味がないので調べたところ、

参考：[How to get IP address of running docker container](https://stackoverflow.com/questions/43692961/how-to-get-ip-address-of-running-docker-container/47226863)

```
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
```

これでコンテナのIPが取得できるようなので、

```
curl http://$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' clip-expose):404
0/api/tunnels
```

とすればURLを含んだjsonファイルが取得できる模様。
Git＆Github＆python&VScode　個人的忘備録

個人的な　忘備録です

 Git For Windowsのインストール手順
https://opcdiary.net/technical/programming/%E7%A7%81%E5%AE%B6%E7%89%88-git-for-windows%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%89%8B%E9%A0%86/

最低限しておくといいgitconfigの設定
https://qiita.com/hayamofu/items/d8103e789196bcd8b489

GitHubとVSCODEの連携方法
https://qiita.com/yu0313/items/4f95fc0b7e544c42e107

連携できたけどアップデート（っていうのか？）ができない

VSCodeをgitとGitHubと連携してみる
https://note.com/cd_ss_829/n/n4e7d80723381#q5uWW

pipインストール
https://gammasoft.jp/python/python-library-install/#install

無断リンクなので問題あればご連絡ください。
まったく0知識スタートなのでこうしたいいよということがあればご教授ください。
全然わかりません。
備忘録(Ruby:基礎文法:処理に名前をつけて使用する)

## メソッドを自分で定義する
メソッドは予め用意されたものだけでなく、自分で定義することもできる
メソッドとは、決まった処理のまとまり
プログラム中でメソッド名を書くだけで、メソッドのdef メソッド名からendの間に書かれた処理が実行さ
れる
メソッドの定義部分はそのメソッドが実行されるまで読まれない


```ruby
def メソッド名
  # 実行する処理
#defの横に書いた文字がメソッド名となる
end
```

例:

```ruby
# メソッドの定義
def say_hello
  puts "Hello World"
end

# メソッドの実行
say_hello
```

def~endの部分は初めは読み飛ばされる
say_helloでメソッドを呼び出し、初めて定義部分を読み、定義内の処理が実行される


### 戻り値
メソッドから呼び出した場所に渡される値
メソッドが処理された後の最終的な値のこと
例:

```ruby
def sample
  "1"
  "2"
  "3"         # 最後の行が戻り値になる
end

puts sample   # => 3
```

### return文
メソッド内でreturn ◯◯と記述すると、returnの後ろに続けた式がそのメソッドの戻り値になる
returnを利用した時点で戻り値が決まり、メソッドはその行の処理を終えると強制的に終了
Rubyではreturn文を省略できる。省略した場合、メソッドの戻り値はそのメソッドの中で最後に実行された式の値になる
例:

```ruby
def sample
  "1"
  "2"
  "3"

  return "4"  # ここで処理は終わり

  "5"         # 呼ばれない
  "6"         # 呼ばれない
end

puts sample   # => 4
```

### スコープ
定義した変数が使える範囲
基本的にメソッドの外に定義されている変数は、メソッドの中では使えない
その逆に、メソッドの中で定義された変数は、メソッドの外では使えない

### 引数
メソッドなどに渡すことのできる値のこと
引数によって、メソッドは外にある変数(スコープ外の変数)の値をメソッド中で扱うことができる
メソッド定義時に()のなかに記述しておき処理に利用するカリ引数と、メソッドを呼ぶ際に()のなかに渡す値を記述する実引数がある
実引数と仮引数の名前は、同じでも別の名前でも動作に違いはない
引数は複数用意でき、左から順に「第一引数」「第二引数」...「第○引数」と呼ぶ
実引数と仮引数の数は、必ず一致している必要がある


```ruby
def メソッド名(仮引数)
  # 処理
end

# メソッドの呼び出し
メソッド名(実引数)
```

例:

```ruby
def sample(number)  # ②仮引数numberで、実引数「3」の値を受け取る
  puts number * number  # ③「3」が代入されたnumberを使って「3 * 3」を行い出力する
end

sample(3)  # ①数値「3」を実引数にしてメソッドを呼び出し
```

### times, eachメソッドのスコープ範囲
times, eachメソッドはメソッドの外で定義した変数でも、引数を使用しなくても使用できる
メソッドの中で定義した変数はdefと同様使用できない

例:

```ruby
num = 1
lists = [1, 2, 3]

lists.each do |list|
 num = num +list
end

puts num

# 出力結果
7
```

例:

```ruby
lists = [1, 2, 3]

lists.each do |list|
 num = 1
 num = num + list
end

puts num

# エラーが出る
```
オフショア開発におけるベンダーの選択方法

オフショア開発の導入を検討されている企業はよく次の疑問に直面します。
・中国、フィリピン、ベトナム、台湾等のアジア地域、あるいは、ポランド、ウクライナ等のヨーロッパ諸国の中から、どこのベンダーを選択したほうが良いですか？
・私の企業にとって、どのオフショアモデルが一番適していますか？
・ベンダーのどのようなクライテリアを重視したほうが良いですか？
・オフショアベンダーを最終決定する前に、どんなリサーチが必要ですか？

日本企業向けにオフショア開発立ち上げアドバイザーを務める山崎孝氏はMobilunityの「日本企業対オフショアリング。オフショアリングがうまくいかない日本企業はなぜ多いのか?」オンラインウェビナーではそれぞれの質問に答えていただきました。

その中、
「現状、オフショアを始めるフローの提案
①国の絞込み
②ベンダー選定　給与・スキル　オフショアモデル
③ベンダー比較
④決定

オフショアベンダーを最終的に決定する前のリサーチの提案
(1) 離職率
オフショアでのリクルートコストは、運用に影響があるため重要
(2) 費用の内訳（給与・トータルコスト）
給料が安くても、トータルコストが上がる可能性がある。
また、国ごとの需要によって、エンジニアスキルのコストが変わるので、各ベンダー毎で調べる必要がある。
(3) スキルセットによる求人数
スキルセットに合う求人数」
と山崎氏が教えていただきました。

詳細は下記の動画で確認することができます。
https://www.youtube.com/watch?v=W8SwsMBCqmM
枯れたアイデアの水平思考


この記事は、[「MAヒーローズ・リーグ Advent Calendar 2020」](https://qiita.com/advent-calendar/2020/heroesleague)の21日目の記事です。

ヒーローズ・リーグ オンライン2020に、「29時間時計」と言う作品を応募致しました。
時間を前借りするという表現で、時間を進み方を変えるアイデアの作品ですが、実を言うとアイデアは再利用です。

## 時間を進み方を変えるアイデアの原点

今から5年前、MA11の頃に応募した作品[「いまなんじ」](https://protopedia.net/prototype/170)という作品が、29時間時計の原点になります。

![スクリーンショット 2020-12-13 22.14.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/f12b295c-7285-d58c-749f-efb18013e004.png)

この作品は、時計の読めない子供用の作品でした（当時、娘は6歳と3歳）
1周で24時となる24時間時計を採用しています。
文字盤の数字も廃止し、子供に1日にでやること（幼稚園の準備や、寝る前の準備など）を絵で描いてもらい、子どもの感覚に時間を合わせる方法をとっています。


コチラの作品は、[MA11の2ndSTAG](https://we-are-ma.jp/blog/2ndbattle_2/)で発表したので、ひょっとしたら覚えていらっしゃる方もいるかもしれません。

## 子供用から自分向けに

毎日1~2時頃まで、作業をしていると、昔はもっと夜が長かったのにな〜と思うことがありました。
記念日だったり、楽しい日だったり、そんな日をもう少し伸ばす方法はないのかなそう考えるようになりました。

#### 実はみんな使ってる1日を伸ばす方法

実は1日を伸ばす方法をみんな使っています。
深夜営業されてるお店がよく使ってる25時とかの表現方法ですね。

[30時間制](https://ja.wikipedia.org/wiki/30%E6%99%82%E9%96%93%E5%88%B6)なるもので、以下のようなルールで時間を表現します。

|24時間制|	30時間制|
|---|---|
|4日　00:00（=3日　24:00 ＊）|	3日　24:00|
|4日　01:00	|3日　25:00|
|4日　02:00	|3日　26:00|
|4日　03:00	|3日　27:00|
|4日　04:00	|3日　28:00|
|4日　05:00	|3日　29:00|
|4日　06:00	|4日　06:00（=3日　30:00 ＊）|
|4日　07:00	|4日　07:00|

30時を過ぎると6時になり、翌日になるものです。

これも1日を伸ばしていますが、24時を超えいるので今日が伸びている感じはしません。

そこで思い出したのが、上記に記載した[「いまなんじ」](https://protopedia.net/prototype/170)
この作品と同じように、時間を進み方を変えることとしました。

**何時からゆっくりにする？**
　→できるだけ、普段には影響が内容にする
　→23時以降なら影響はないだろう（自分の感覚）

**ゆっくりしたあとはどう調整する？**
　→30時間制が6時をターゲットにしていると言うことは何か意味があるはず
　→6時を基準に速度を戻すようにし、5〜6時を時間の調整させる

上記より朝5時までは、ゆっくり進むように決めたので
30時間 - 1時間（5時まで）なので、「29時間時計」となりました。

また、[「いまなんじ」](https://protopedia.net/prototype/170)のアイデアを再利用するにあたり、
子供向けから、自分向けにアレンジしました。

自分が使用するものだから、以下の内容にこだわりました。

* 見た目はお洒落な感じ
* シンプルな操作

##### お洒落な感じ
普段使っても違和感がないものをイメージしました（ってことは木かな）
[mui](https://mui.jp/)を参考に、Seriaさんで販売されている[Natural Woodのリメイクシート](https://ameblo.jp/pokapoka-hidamari4/entry-12412996618.html)を貼りました。
また、時間速度が変わる場合、60秒で1分にはならないので、違和感を減らすため、秒の表示はなしにしています。

#####  シンプルな操作
[「いまなんじ」](https://protopedia.net/prototype/170)は、スマホを使って細かく制御できましたが、できるだけシンプルにしたいと思い、ボリュームで時間を変えられるようにしました。
時間のメモリは入れないようにしました。感覚的に操作したかったからです。

そして、出来あったがのがコチラ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/e9007324-70e6-622d-7ba0-b79eff4f502b.png)
リメイクシートが少し厚いため、文字が滲んでいますが、ほぼイメージ通りになったと思います。

## 使ってみて
予想以上に、時間が遅れてることへの違和感がないことがわかりました。
むしろ、全然進まない時間は、安心感すらあります。
安心感と疲労感で、気持ちよくお布団につき、寝不足が加速していきました。

本当の時間が見えると、意味がないのでPCの時計もアナログ表示にして、ぱっと見て時間がわからないようしてます。
![スクリーンショット 2020-12-13 22.32.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/1a96532d-c563-671c-5a99-b4b5e183abe6.png)

カラダに優しい時間を早める機能も途中で実装しましたが、
残念ながらこちらは私のカラダに合わないようで、違和感しかなかったです。

この時計は、たぶんずっと使い続ける気がします。
自分が作った作品で一番活用しているかもしれません。

## 昔のアイデアの作品も再活用しよう！
アイデアも使い方を変えると、新しいものとして活用することができることがわかりました。
以前発表した物だから。。とか、もう数年も前ものだから・・なんて思わず、見せ方や使い方を変えるだけで面白いものができると思います。
他の人の作品を見るのもいいですが、過去の自分の作品にも良いヒントが詰まっているかもしれません。
ミニッツリピーターを作る part.2

# ミニッツリピーターとはなにか

超スーパー高級腕時計に搭載された機能である。
詳細は前回を参照。

<https://qiita.com/antk/items/d67d601096161d77f515>

前回 `print` で書いていた鐘の音をサウンドの再生に置き換える。

## SEを鳴らす

ミニッツリピーターの生音を取り込んだmp3がどこかにフリー音源として公開されていないか探したが見つからなかった。
mp3のオリジナル音を鳴らすのは今回パスして、iOSに最初から入っているSEでそれっぽいものを鳴らすことにする。ユーザビリティ的には紛らわしいかもしれないが。

```swift
// AudioToolboxを使う
import AudioToolbox

// 音を決める
let lowGong:SystemSoundID = 1054

// 音を鳴らす
AudioServicesPlaySystemSound(lowGong)
```

基本的にこの3つがあればiOS備え付けのSEを鳴らすことができる。
ちなみに上記の `1054` 番はガラスのコップを箸で叩いたような音が鳴る。
どんな種類のSEがあるかは以下の記事を参照。

**iOSのシステムサウンドを確認する**
<https://dev.classmethod.jp/articles/ios-systemsound/>

一応ぼくは『音を書く→コンパイル』というどんくさい作業で一応全部聴いて音を決めた。
多分もっといい方法があるはず。

## 実装する

前回の記事のコメント欄でいただいたアドバイスを元にしたリファクタリングも含める。
実機のゴングが鳴るタイミングを参考に遅延処理で調整する。

```swift
import UIKit
import AudioToolbox

class ViewController: UIViewController {
  
  override func viewDidLoad() {
    super.viewDidLoad()
  }
  
  @IBAction func actionButton(_ sender: UIButton) {
    
    let now = Date()
    let time = Calendar.current.dateComponents([.hour, .minute], from: now)
    
    // hour を12時間で丸める
    let hour = time.hour! % 12
    // 60分を15分単位に分割した数
    let quarter = time.minute! / 15
    // 15分に満たない残り分数
    let minute = time.minute! % 15

    // 低音ゴング
    let lowGong:SystemSoundID = 1054
    // 高音ゴング
    let highGong:SystemSoundID = 1013
    
    // 第一ゴング
    for _ in 0 ..< hour {
      AudioServicesPlaySystemSound(lowGong)
      
      // 0.5秒待つ
      Thread.sleep(forTimeInterval: 0.5)
    }
    
    // 第二ゴングが鳴る場合遅延処理
    if quarter > 0 {
      Thread.sleep(forTimeInterval: 0.7)
    }
    
    // 第二ゴング
    for _ in 0 ..< quarter {
      AudioServicesPlaySystemSound(highGong)
      // 0.4秒待つ
      Thread.sleep(forTimeInterval: 0.4)
      AudioServicesPlaySystemSound(lowGong)
      
      // 0.5秒待つ
      Thread.sleep(forTimeInterval: 0.5)
    }
    
    // 第三ゴングが鳴る場合遅延処理
    if minute > 0 {
      Thread.sleep(forTimeInterval: 0.7)
    }
    
    // 第三ゴング
    for _ in 0 ..< minute {
      print("ding")
      AudioServicesPlaySystemSound(highGong)

      // 0.5秒待つ
      Thread.sleep(forTimeInterval: 0.5)
    }
  }
}
```

以上。
コンパイルして鳴らしてみるとわりとそれっぽく聴こえる。
ただこの実装だとボタンを押すたびに処理が走るため、連打するとゴングがいつまでも鳴り止まなくなってしまう。`@IBAction` の処理が終わるまでは操作を受け付けないという方法が見つからなかったので、もしご存知の方がいれば教えていただけると嬉しいです。
心を燃やせ！～旧ConoHaとの思い出～

#はじめに
この記事は「[ConoHa Advent Calendar 2020](https://qiita.com/advent-calendar/2020/conoha)」の第21日目の記事です．今年で3年連続の参加になります．タイトルは，最近すごい勢いで人気が出ているある漫画からいただきました！　このConoHa Advent Calendarに参加されている皆さんは，もうさまざまな分野のスペシャリストで，いろんなことに取り組まれているので，実用的なネタは紹介しません．今回は，突然きた悲しい連絡と，その思い出を振り返っていきたいと思います．

#旧ConoHaのサービス終了の連絡？！
2020年12月8日頃だったと思いますが，ConoHaから一通のメールが来ました．そのタイトルは，「【重要】[ConoHa] 2015年5月17日以前に作成されたアカウント向けサービス終了のお知らせ」ということで，簡単には，旧ConoHaのサービスが終了するということです．一瞬迷惑メールに見えたのですが，しっかり読んでみると本当のようでした．そのことが説明されているWEBサイトもありました．そのWEBサイトは[こちら](https://cp.conoha.jp/#wm1247)になります．画像の一部はこちら！
![001.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/a2026467-db4f-3086-d549-7f23f62fca41.png)
結構前（調べてみると2013年7月のようでした）からサービスが始まって，比較的すぐに利用を開始したと思います．当時は，VPSプランでLinuxしか選択できませんでした．初期費用が無料だったのが利用の決め手になったのを覚えています．あと，「このはちゃん」，清楚かわいいのも決め手でした！　現在も，VPSの2GBプランを2台利用しています．HDDプランでストレージの速度も今のSSDプランよりはずっしりとしているのですが，それでもいろいろなサービスが展開できたので重宝していました．

#旧ConoHaの画像を残しておきたい！
これから時が流れていくと旧ConoHaを紹介しているコンテンツも少なくなってくると思うので，旧ConoHaの画像を残しておきたいと思います．

まずは，ログイン画面！
![002.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/67d3ccbb-ec71-c5b5-e014-a95f3e4947ed.png)
これは，今のConoHaのログイン画面なのですが，旧ConoHaユーザーは「2015年5月17日以前に登録されたお客様はこちら」からログインします．

![003.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/81fee439-22c7-0319-6ba2-2fb58e5d1d06.png)
この頃から「このはモード」は実装されていて，いつもページ画面を眺めることができました．今のこのはちゃんとは少し絵柄が違うのも個人的には好きなポイントです．

![004.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/97977d4f-a8b1-e9cc-cc23-0fa48ca72287.png)
ログインすると，右上のログインボタン付近のこのはちゃんが出現します．このギミックがかわいかったですヾ(≧∇≦)

さて，できることは，このコントロールパネルのメニューから選択できました．
![005.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/6d3ff9de-10b0-764f-7328-a0288136a6c4.png)
このはモードにするといろいろなところがかわいくなるので，ずっと気に入っていました．

![006.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/4f9129eb-9e0f-1105-ae1a-cfbff3b49e82.png)
サーバーリストはこんな感じ！　今も，WEBサーバとメールサーバが稼働中！　本当にお世話になりました(≧∇≦*)
今ログインして気づいたのですが，「新ConoHa移行」というのが表示されていますね．押してみました．
![007.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/2c18abd9-2cf6-fe7c-6ead-71b8f8354ade.png)
![008.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/29159559-3bbc-1a83-5336-2bdc70d95037.png)
ちゃんと設定というか選択をしていないので，この画面がでましたが，エラー画面？とともにこのはちゃんの表情が変わるのもよかったです．

![009.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311851/da5bcef8-b6ab-581f-eaa9-bab71fb79f4f.png)
あるVPSのコンソールはこんな感じでした．今のConoHaと比べると質素な感じですが，必要十分な機能が備わっていました．
今もちゃんと動いています！

以上が，主要な画面になります．ずっとこのはちゃんがかわいいサービスでした．

#これからについて
今のConoHaは，ストレージがSSDになり，LinuxのVPSだけではなく，Windowsサーバープランも充実し，レンタルサーバ的な使い方ができるWingというほぼ最速のレスポンスのサービスも始まっています．それなのに，価格は非常に安いです．実は私もすでに今のConoHaを契約しています．VPSもWingも使い勝手がとってもよくなりました．定期的にアンケートも送られてきて，ユーザの声にいつも耳を傾けてくれているので，ユーザとしては使い心地がよいです．もちろんこのはちゃんのかわいさもどんどん増しています．この前は，Vtuberデビューもされていました．これからも応援していきたいと思いますヾ(≧∇≦)　旧ConoHaとはお別れになってしまいますが，この年末年始は，出歩いたりすることもないので，ゆっくりと今のConoHaに旧ConoHaのデータを移していこうと思います．

以上，旧ConoHaとの思い出でした！　心を燃やして，来年もConoHaを使っていきたいと思います！
使うわりに、忘れてしまう！Linuxコマンドのメモ

ファイル検索など使うわりに、忘れてしまう！
困るので、メモる。
#ファイルの探し方

##ファイル名での検索
/などで検索しないで、検索したいファイルが含まれる(再帰的にでも)であろう場所に行って

```bash
find . -name  "*.log"
```
とする。
参考 [Saba note | findとかgrepとかを使って再帰的検索など](https://saba.omnioo.com/note/1015/find%E3%81%A8%E3%81%8Bgrep%E3%81%A8%E3%81%8B%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%86%8D%E5%B8%B0%E7%9A%84%E6%A4%9C%E7%B4%A2%E3%81%AA%E3%81%A9/)

##ファイルの中身で検索(再帰的にgrep)

いくつもディレクトリがあって、目的のファイルを探したいとき。

```bash
find ./ -name '*.py' | xargs grep 'check'
```

参考 [Saba note | findとかgrepとかを使って再帰的検索など](https://saba.omnioo.com/note/1015/find%E3%81%A8%E3%81%8Bgrep%E3%81%A8%E3%81%8B%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%86%8D%E5%B8%B0%E7%9A%84%E6%A4%9C%E7%B4%A2%E3%81%AA%E3%81%A9/)

#コマンド結果を一個ずつ受け取って実行
そのディレクトリにある .pyのファイルをすべて一個ずつ実行したい場合。

```bash

ls  *.py | xargs -I {} python {} 
```

参考:[【 xargs 】コマンド――コマンドラインを作成して実行する：Linux基本コマンドTips（176） \- ＠IT](https://www.atmarkit.co.jp/ait/articles/1801/19/news014.html)

#ファイルの更新を確認する
##ファイル更新を自動lsで目視で確認
ファイルの容量と更新時刻を2秒おきに自動更新される。ファイルの更新を目視で確認できる。

```bash
watch ls -lt
```
更新日時の新しいものが上になる用にならぶ。
やめたいときは、ctrl-cを押す。

よく使うので、.bashrcにaliasとして登録してもいい。

```bash:.bashrc
alias wl='watch ls -lt'
```
登録後、bashと打ってbashを起動しなおすか、source .bashrc として.bashrcをもう一度読み込めばwlで watch ls -lt が実行されるようになる。


##1ファイルの中身を自動更新で確認する場合
一個だけのファイルで、書き足されて行くファイルの中身を確認する場合

```bash
less みたいファイル名
```
としたあと　F を入力すると更新を随時見れる。ctrl-Cで中断

#ディレクトリサイズの確認
ディレクトリに行って、これを入力すればディレクトリサイズを再帰的に確認できる
cpする前に重いフォルダがないか確認すると良い。

```
du -h
```
-hは人間に見やすい単位MB、GBなどになおしてくれるオプション

# tree でディレクトリ構成を見る

```bash 
tree
```
condaでインストールできる
https://anaconda.org/eumetsat/tree
[Ruby] 条件分岐 if文

現在、参考書を進めながら勉強しているのですが学んだことのアウトプットとして記事にしてみました。

#if文
if文は「if 条件 条件が成り立った時に実行したい処理 end」という形で書けばプログラムが実行されます。
基本形は下記のサンプルコードになります。

```ruby
if # 条件A
  # 条件式a
else 
  # 条件式b
end
```

if文の条件式がfalseとなった処理には、elseを使用します。if文が実行されるとelse文は実行されず、if文が実行されないとelse文が実行されるようになります。

#if elsif elseの形
if文で分岐させる条件を2パターンから3パターンに増やしたい場合は、elsifで条件文を追加します。
下記がサンプルコードになります。

```ruby
if # 条件A
  # 条件式a
elsif # 条件B
  # 条件式b
else
  # 条件c
end
```

それではサンプルコードに値を入れてみたいと思います。

```ruby
number = 1
if number == 1
  puts '数値は1です'
elsif number == 2
  puts '数値は2です'
else
  puts '数値は1や2以外です'
end
```

上記のコードを実行すると、number == 1が真となるため、'数値は1です'が表示されます。

#まとめ
if文は後に条件式を記述して、その条件が真と評価されたときにその下の処理を実行します。
また、elsifはパターンを増やしたい時などに記述します。無くてもよいし、複数記述しても構いません。

#参考書
・ 現場で使える Ruby on rails 5 速習実践ガイド
win機からair mac timecapsuleのデータを見るとき

参考：https://social.technet.microsoft.com/Forums/en-US/81ea51ec-fbf0-4071-bba0-f80ea8b1676c/accessing-time-capsule-disk-with-windows-10?forum=win10itpronetworking
1,
![clip_201221_224812.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/fc466c19-85d1-1421-e21d-334d8db2289c.png)

2,
![clip_201221_224827.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/09a1c987-7416-e621-2c05-a579f8386aef.png)

3,
![clip_201221_224856.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/6a3d0f59-7852-9e8c-d6d2-ba09eccd234a.png)


ここでPC再起動

air mac ユーティリティ等よりIP確認
![clip_201221_224731.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/7c216b0f-db75-1d0b-3b57-d69d222f5a0b.png)

エクスプローラー等から対象IPへアクセス
![clip_201221_225054.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/3eff433b-782a-fae2-8001-49feb4d1ea19.png)

ユーザー名/パスワードを入力して終わり
![clip_201221_225238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243544/4b329b5a-2139-dc2e-eded-27b36b371ff2.png)
【初心者】PHPで連想配列を作成する

# PHPでの連想配列の作成方法
前回、PHPでの連想配列の作成方法として、

```PHP
$h = ["apple" => "a", "banana" => "b", "calot" => "c", "orange" => "d"];
```
このような方法を勉強した。
この方法だと、あらかじめ与えられたキーとバリューが必要だった。
今日はこの方法以外にも連想配列を作成する方法があった（というよりも本来は今回の方が使いやすい？）ので覚えておくことにした。

## 入力された値から連想配列が作れる？
```PHP
$h["配列のキー"] = "配列の値";
```
この方法を使用すると、入力された値を取得して連想配列を作成することができることに加え、配列に情報を追加していくことも容易だった。
この方法はどんどん使っていきたい。
git-flowをライブラリを使って厳密に導入してみた。

## 何をしたか
`git-flow`というものがあります。これは、`git`リポジトリの管理手法の一つで、Vincent Driessenmという方の提唱するブランチモデルだそうです。

これまで、`git-flow`の存在は知っていたのですが、見よう見まねでなんとなく導入していました。この度、[ライブラリを使ってしっかり導入していく方法](https://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html)があると知ったので、自分自身のために、導入手順をメモしていきます。

なお、`git-flow`の実践方法は優れた記事が何本も出ており、特に、以下の記事はブランチ作成、pull、pushと一通りの流れが紹介されていました。

[Git-flowって何？](https://qiita.com/KosukeSone/items/514dd24828b485c69a05)

## 導入方法

### ライブラリのインストール
まずは`git-flow`のライブラリを入手します。
訳あって、macとWinのマシンの両方で導入したので、両方をメモしていきます。

```
# macOS
$ brew install git-flow-avh

# Ubuntu(WSL)
$ apt-get install git-flow
```

### 初期化
次にプロジェクトのルートディレクトリに移動し、`git-flow`の初期化をします。

```
$ git flow init
```
対話形式で、いくつか`git-flow`の設定に対する質問がなされます。[全てデフォルトの設定にすることが推奨されるようです](https://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html)(こちらのページ下方の記述による)。

### ブランチの切り方
`git-flow`には、`master`、`develop`、`feature`、`hotfix`、`release`などのブランチがありますが、それぞれの意味は、[先にあげたこちらのページ](https://qiita.com/KosukeSone/items/514dd24828b485c69a05)が詳しかったので、割愛させてください。

`develop`から`feature`（作業用）ブランチを切るには、以下のコマンドをうちます。

```
git flow feature start 001_first-branch
```
これにより、`feature/001_first-branch`というブランチが作成されます。
`feature/001_first-branch`にブランチを移動して、作業を続けます。

### pushをするとき

これはいつも通りですね＾＾

```
git push origin feature/001_first-branch
```

プルリクは、girhub上で作って、マージします。

### マージ後
以下のコマンドを打つようです。（まだそこまで行ってないので、試せていない）

```
git flow feature finish 001_first-branch
```
これをすると、以下のことが行われるそうです。

- `001_first-branch`ブランチの`develop`へのマージ
- 開発用ブランチ(`001_first-branch`)の削除
- `develop`ブランチへのスイッチ

## その他作業のコマンド
まだ、開発を始めたばかりなので、あまりコマンドを売っていないのですが、

- 開発が終わって`develop`にマージするとき
- `develop`をリリースにマージするとき
- 他の人の修正を取り込むとき

などで[それぞれコマンドが違う](https://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html)ようですので、調べつつ実装し、続きをQiitaにまとめたいと思います。
RDSのバックアップ(gzip)をS3に作成する＆ハウスキープのcronを設定するスクリプト

# また触った時に忘れないための備忘録

- 頻度：1日1回（毎朝10時）
- 格納先：s3://hogehoge-dev/rds_backup/rds_hogehoge_db_yyyymmdd_hhmmss.sql.gz
- S3のライフサイクル：14日前
    - 対象プレフィックス：rds_backup/

## バックアップ用スクリプト

```bash
#!/bin/bash -l

HOGEHOGE_DB="hogehoge_db"
HOGEHOGE_DB_USER=""
HOGEHOGE_DB_PASS=""
HOGEHOGE_DB_HOST="hogehoge.ap-northeast-1.rds.amazonaws.com"
DUMP_FILE_NAME="rds_hogehoge_db_`date +%Y%m%d_%H%M%S`.sql.gz"

AWS_PROFILE_NAME="hogehoge-develop" # 事前にprofileを同名で作成しておくこと
S3_DIR="s3://hogehoge-dev/rds_backup/"

mysqldump -u ${HOGEHOGE_DB_USER} -p${HOGEHOGE_DB_PASS} -h ${HOGEHOGE_DB_HOST} ${HOGEHOGE_DB} | gzip > /tmp/${DUMP_FILE_NAME}

/usr/bin/aws s3 cp /tmp/${DUMP_FILE_NAME} ${S3_DIR} --profile ${AWS_PROFILE_NAME}

rm /tmp/${DUMP_FILE_NAME}
```

## 定期実行の設定

```bash
$ crontab -e
no crontab for ec2-user - using an empty one
crontab: installing new crontab

$ crontab -l
00 10 * * * /home/ec2-user/bin/save_rds_backup_to_s3 > /dev/null 2> /tmp/save_rds_backup_to_s3_error
```
[Oracle Cloud] ESS Jobから複数選択パラメータ有(Multi-Selection Parameter) BI Publisherレポートの呼出検証

## 複数選択パラメータ有(Multi-Selection Parameter) BI Publisherレポート

BI Publisherレポートのパラメータ・タイプは、テキスト／メニュー／日付／検索の4種類があります。
パラメータ・タイプを「メニュー」に選択し、オプションの「複数選択」にチェック入れると、

![multi_selection_bip.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/7f5feb28-f88b-6394-809f-378fca5dee88.jpeg)

メニューのリストから複数選択パラメータを設定できます。（値リストには5つの値を固定値Labeln:Valuenで設定しています）

![multi_selection_bip_parameter.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/6854de82-56d6-a90f-9ef3-b7ed28291af4.jpeg)

複数選択した状態でBI Publisherレポートを実行すると、以下のようにレポートに値が渡されます。（下図のレポートは、渡されたパラメータをそのまま出力するレポート）

![multi_selection_report.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/07ca347f-7af9-dd34-ad0e-df02d0b7ae8c.jpeg)

複数選択したパラメータは、[Value1,Value2,...,ValueN]で渡されているのがわかりました。

## ESS Jobに複数選択パラメータが存在するか

筆者が知っている限りでは、ESS Jobのパラメータに選択リストが存在するものの、複数選択パラメータは存在していません。
つまり、BI Publisherと同様に選択リストを設定し、ユーザに複数選択させることができません。

## それでもESS Jobから複数選択パラメータ有(Multi-Selection Parameter) BI Publisherレポートを呼出したいための検証

それでもESS Jobに登録し、プログラムなど機械的に呼出したいと考えて、
ESS Jobのパラメータを以下のように文字列に設定してBI Publisherを登録し、

![ess_job.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260307/9c362559-cb0d-cfc9-f5f5-cca1105167db.jpeg)

BI Publisherを呼出せるか検証してみました。

## 検証結果

|ESS Jobパラメータ|ESS Jobステータス|出力結果|
|:---:|:---:|:---:|
|Value1|成功|Value1|
|Value3|成功|Value3|
|Value6|エラー|-|
|Value1,Value3|エラー|-|
|[Value1,Value3]|エラー|-|
|Label1|エラー|-|

ESS JobのパラメータにValueを1つで渡せたものの、複数Valueを渡せないことがわかりました。
（上記検証環境のバージョンは20Dです）

## 参考情報

* [Can I enable multi-selection in the ESS job parameter?](https://www.oracle.com/webfolder/community/business_intelligence/4330424.html)
* [How to create Custom ESS Job in Oracle Cloud/Fusion](https://www.oracleknowhow.com/create-ess-job-in-oracle-cloud/)