OmniAICreator/Qiita-1.07M huggingface dataset:
Vimの挿入モードで背景色を変える

#結論

以下のコードを`.vimrc`に追記する。

``` vim:.vimrc
augroup InsertHook
    autocmd!
    autocmd InsertEnter * hi Normal ctermbg=235 "挿入モード時の色
    autocmd InsertLeave * hi Normal ctermbg=0 "通常モード時の色
augroup END 
```

## ちょっとした解説

`autocmd`の指定で、`InsertEnter`は挿入モードになるとき、`InsertLeave`は挿入モードから抜けるとき、それぞれ自動で`hi`(`highlight`)コマンドで色を変更する。`*`の部分では、すべてのファイルにマッチするようにしているため、`*.c`とすれば、cファイルにのみ適応なども可能。`Normal`は全体的に変わる模様。

`ctermbg`は色つきターミナルでの背景色。前景色(つまり文字色)は`ctermfg`になる。GVimの場合は`guibg`と`guifg`になる。背景色と文字色とを一緒に変更することも可能。

`autocmd`の詳細は[autocmd - Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/autocmd.html#InsertChange)を参照。`hi[ghlight]`コマンドに関しては、[syntax - Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/syntax.html)で。

色指定に関しては、[ANSI Color Specific RGB Sequence Bash - Stack Overflow](https://stackoverflow.com/questions/15682537/ansi-color-specific-rgb-sequence-bash)を参考にした。サンプルでは、色つきターミナルでの256色指定を用いている。

##他にしたこと

_2018/11/27 変更_

環境によっては、挿入モードを抜けた際に、すぐに切り替わらないので、以下も追記した。

```vim:.vimrc
set ttimeoutlen=0
```

これはタイムアウトが有効になっている場合に、設定も有効になる。タイムアウトは、

```vim
set timeout? ttimeout?
```

のどちらかがオンになていれば、有効である。

[vim/gvimの色設定について - fudist](https://sites.google.com/site/fudist/Home/vim-nihongo-ban/vim-color)では、以下の設定が紹介されているが、挿入モード中のカーソルキーなどが使えなくなる場合があるため、上記の設定がうまく行かなかった場合に試すとよさそう。

``` vim:.vimrc
if has('unix') && !has('gui_running')
    inoremap <silent> <ESC> <ESC>
endif
```

@h_east さんのコメント( https://qiita.com/L_Y/items/81395569675d7e55861d#comment-86a57f86855ab1278d7c )より。情報ありがとうございました。

##参考文献

[vim/gvimの色設定について - fudist](https://sites.google.com/site/fudist/Home/vim-nihongo-ban/vim-color)
[autocmd - Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/autocmd.html#InsertChange)
[syntax - Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/syntax.html)
[options - Vim日本語ドキュメント](https://vim-jp.org/vimdoc-ja/options.html)
[ANSI Color Specific RGB Sequence Bash - Stack Overflow](https://stackoverflow.com/questions/15682537/ansi-color-specific-rgb-sequence-bash)
[おさらい autocmd/augroup - Qiita](https://qiita.com/s_of_p/items/b61e4c3a0c7ee279848a) (@s_of_p  さん)
UiPathで匿名関数を使う

#そもそもRPAでそれってどうなの？

と、言ってしまうと身も蓋もないけれども。
VB.NETプログラミングをそこそこ習熟した人であれば、時と場合によっては使えるかもしれないテクニック。

たとえば。
現在、消費税は8%なのだけど、UiPathで消費税を計算させるのは割と冗長になる。
将来的な税率変更に備えて、たとえば「TaxRate = 1.08」みたいな変数を初期化して使うことはできる。
でも消費税を計算するところでは、

```
CInt(価格 * TaxRate)
```

みたいなAssignをボコボコ置くわけで。
ましてや、将来、複数税率になるかもしれない、なんて話もある。
その時に毎回、IFアクティビティで分岐して処理するのはWorkflow的に美しくない。
正攻法でいえば別Workflowに切り出して、Invoke Workflowしても良いのだけど、オーバーヘッドが今度は気になる。

ではどうするか。

#ラムダ式使えばいいじゃん

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316523/210597b9-eda5-8794-2e1c-06b69214767a.png)

たとえばこんな感じで変数の型を指定する。
Func< まで入力すればずらーっと候補は出るので、あとは引数のInt32とBoolean、戻り値のInt32を設定すればいい。

これで何ができるか。
具体的にAssignする内容は以下のようになる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316523/d25502bd-0d3d-da00-6864-4792ba6a666f.png)

これだけだと「何が何だか」状態。右辺の内容は、

```
Function(price, highRate) CInt(price * IF(highRate, 1.1d, 1.08d))
```

こんな感じ。引数1(price)が税抜き価格、引数2(highRate)が税率高いほうかどうかの設定。

これが何に使えるか。
Assignアクティビティで一発で、かつ明瞭簡潔に「税込み金額」が計算できる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316523/bd1d96e8-fa06-654a-9e1f-101589f484f2.png)

こんな感じ。

・・・RPAを「プログラミングレス」なツールとして見た場合は邪道も甚だしいけど、時と場合によってはWorkflowがスッキリするかもしれない、ということで。
VB.NETにどっぷり踏み込んだことのあるUiPathユーザーの方は、ネタの引き出しとして使ってみてください、という話でした。
Arch Linuxで、visudoなどで使用されるエディタの変更

Arch Linuxで、sudoファイルを変更するときに```visudo```のコマンドを打つと、vimのエディタになる場合があります。

これを変更したい！っていうためのメモ的な記事です。

# まずはエディタのインストール
入れたいコマンドエディタが入っていない場合、pacmanなどのパッケージインストーラーでインストールします。

例としてnanoを入れます。 *ちなみに私はnano派です。*

```bash
pacman -S nano
```

画面の指示に従ってインストールします。

# 変更

```/etc/profile``` や ```/etc/profile.d/***.sh``` に以下のような記述を入れます。

```bash:/etc/profile
export EDITOR=EDITOR_COMMAND #EDITOR_COMMANDをエディタのコマンドに置き換える(相対可能？)
```

以下は例です。

```bash:/etc/profile
export EDITOR=nano
```

profile.d下にスクリプトを置いた場合は実行権付与を忘れずに。

# テスト

visudoなどのコマンドを打って試してみてください。任意のエディタになっていたら成功です。
辞書のテンプレートをクラスで定義する

(たぶんけっこう基本的な話)

```console
{key1: value1,
 key2: value2, ...,
 subdic1: {
     key_s1: value_s1,
     key_s2: value_s2, ...
 },
 subdic2: {
     sub2sub1: {
         key_s2s1: value_s2s1,
     }
}
```

のような構造の辞書をクラスで定義して簡単に生成できないかなーと思ってやってみた。

まずは1階層のクラス。

```python
class MyData:
    def __init__(self):
        self.prop1 = 0

data = MyData()
print(data)
```

```console
<__main__.MyData instance at 0x...>
```

うん、これじゃない。これを回避するには組み込み関数vars()を使うといいとのこと。

```python
data = vars(MyData())
print(data)
```

```console
{'prop1': 0}
```

そうそう、こういうこと。次に2階層では、

```python
class MyData:
    class MySubData:
        def __init__(self):
            self.subprop1 = 0
    def __init__(self):
        self.prop1 = 0
        self.sub1 = MyData.MySubData()

data = MyData()
print(data)
```

```console
{'prop1': 0, 'sub1': <__main__.MySubData instance at 0x...>}
```

うん、これじゃない。これを回避するには、

```python
        self.sub1 = vars(MyData.MySubData())
```

```console
{'prop1': 0, 'sub1': {'subprop1': 0}}
```

そうそう、こういうこと。

・・・でもなんか違う。子の要素を直接設定することもできなくはないけど、なんとなくこれじゃないと誰かが叫んでいる。

教えてぐーぐる先生！ということで、

こちらの記事 (http://blog.beanz-net.jp/happy_programming/2008/11/python-5.html) が参考になった。(ありがとうございます。)

共通のメソッドは親で定義して、

```python
class Oya:
  def __str__(self):
    return str(self.__dict__)
  def __repr__(self):
    return str(self.__dict__)
  def __iter__(self):
    return self.__dict__.iteritems()
  def __getitem__(self, key):
    return self.__dict__[key]
  def __setitem__(self, key, value):
    self.__dict__[key] = value

class MyData(Oya):
    class MySubData(Oya):
        def __init__(self):
            self.subprop1 = 0
    def __init__(self):
        self.prop1 = 0
        self.sub1 = MyData.MySubData()

data = MyData()
data.prop1 = 9
data['sub1']['subprop1'] = 99
print(data)
```

```console
{'prop1': 9, 'sub1': {'subprop1': 99}}
```

たぶんこれでいい。

階層がどんなに深くなってもOK。プロパティには辞書的にもプロパティ的にもアクセスできる。辞書として使うときにはvars()なりで。

(追記)
と思ったんだけど、vars()で取り出した辞書でtype(data\['sub1'])すると\<type 'instance'>ってなっちゃうので、inner classまではうまく辞書化できていない模様。これは難しい。

暫定対処としてinner classは個別にvars()して置換してあげればなんとかならないことはない。

とりあえずinstanceとかいう意味不明なものに判定されないよう、クラスの定義時に

```python
class Oya(object):
```

としておくと、typeしたときクラス名で表示されるようになるようだ。(python 2.x)
あとは再帰的にvars()するような関数を持たせる感じかなぁ。

こんな感じ。

```python
def zenvars(obj):
    dic = {}
    if isinstance(obj, Oya): dic = vars(obj)
    else: return dic
    for i in dic:
        if isinstance(dic[i], Oya):
            dic[i] = zenvars(dic[i])
    return dic
```
Excel 関数の基本の復習


エクセルの「相対参照」「絶対参照」「複合参照」についてはググってね！

## 乱数発生
![1例6.png](https://qiita-image-store.s3.amazonaws.com/0/211162/a561a607-f757-0bc1-71df-09b9a40c8c85.png)

## 最大値

![1例5.png](https://qiita-image-store.s3.amazonaws.com/0/211162/0ed0c3a0-5ee9-1114-4488-6e1cfa670094.png)

## 合計値

![1例4.png](https://qiita-image-store.s3.amazonaws.com/0/211162/63e7d00e-b0db-043a-b7c0-a3550cd76e96.png)

## 指数関数とシグモイド関数
![1例3.png](https://qiita-image-store.s3.amazonaws.com/0/211162/d168ff35-4d20-0a43-c18f-d01ac28653a2.png)

## 差の平方和
この例では Q = (a-x)^2 + (b-y)^2 + (c-z)^2
![1例8.png](https://qiita-image-store.s3.amazonaws.com/0/211162/5dc5f414-2497-7ec2-bf3a-b1e0f777c0dd.png)

## 積和
この例では S = x*a + y*b + c*z
![1例9.png](https://qiita-image-store.s3.amazonaws.com/0/211162/c3e3b70c-0783-d10f-3b4e-6ad4f486d347.png)

## IF文
![1例10.png](https://qiita-image-store.s3.amazonaws.com/0/211162/e7561e55-aa27-a4cf-cb52-946d86fa9d0c.png)
人に迷惑がかからない超初心者向けgitマニュアル

## gitは便利だが敷居が高い
gitは慣れると手放せない物ですが、慣れるまでは「迷惑かけたらどうしよう」という心理的コストがめちゃくちゃ大きかった記憶があります。
そこで初心者だった当初、**チームに迷惑をかけないこと**を軸に自分用マニュアルを作成していました。

迷惑さえ掛けなければ何しても良いというか、**迷惑を掛けても元に戻せれば問題ない**と思えたらgitを覚えるスピードが上がったので、誰かの役に立てればと思い共有します。

## 基本的なgitへのコミットの流れ
・ローカルとgit labの狭間にファイルを置く（誰にも気づかれずに戻せる）
`git add [ファイル名]`
・ローカルとgitlabの狭間に記録を残す（gitlabに記録は残るけど、誰にも迷惑かけずに戻せる）
`git commit [ファイル名]`
・gitlab（リモートブランチ）に反映させる（ぐちゃぐちゃにすると誰かに迷惑がかかる）
`git push origin/ブランチ名`

## 間違えたブランチで作業していたことに気づいた時
・一旦ローカルの差分を闇にしまう
`git stash`
・作業したいブランチに切り替える
`git checkout [作業したかったブランチ]`
・さっきしまった差分を、作業ブランチに反映させる
`git stash pop`

## 複数人で作業時、コミット前にやること
誰かが常にgitlabの状態を更新しているため、何も考えずにコミットするとコンフリクトを起こす。
#### コミットする前に、最新のリモートブランチの状態にする。
・自分が今どのブランチにいるかを確認する
`git branch`
・ローカルの差分を一旦しまう
`git stash`
・gitlabの、ブランチ情報をローカルに取得
`git fetch`
・gitlabの、プロジェクトの中身をローカルに取得
`git rebase origin [取得したいプロジェクトのブランチ名（大体Developmentとかmasterとか）]`

## もしもコンフリクトしてしまったら
・コンフリクトは、リモートブランチ（git lab）の状態と、自分のローカルの状態に差分があるまま統合しようとした時に起きる（歴史の改ざんになるので）
#### コミット時の状態にはすぐ戻せるので、こまめにコミットしておくこと
####１. 手で直す
・どこがコンフリしてるのか確認する
`git diff`
・少しだけだったら、手で消す

#### ２. 前にコミットした状態に戻しちゃう
・コンフリクト起こしているファイルを確認
`git status`
・最後にcommitしたのがいつだったか確認する
`git log`
・一番最後にcommitした状態に戻す
`git reset HEAD [コンフリ解消したいファイル名]`
・前の状態に戻ったか確認（cleanになってたらok）
`git status`

#### ローカルブランチにコミットしたあとに `git rebase origin *****`して、コンフリしたら
・コミット実行前に巻き戻す
`git rebase origin ****` 
`git rebase --abort` 
・コンフリクトしたコミットを調整したコミットで上書きして次に進む
`git rebase --skip`

## 終わりに
どれも慣れると当たり前のことですが、gitは自由度が大きい分、「結局なんなの」に陥りがちですよね。
まだまだ勉強中の身なので、間違いがあったらコメントか[twitter](https://twitter.com/muggyteaa) にてお気軽にご指摘頂けると非常に喜びますー
swift viewDidLoad時に画面遷移したい。

 viewDidLoad時に画面遷移しようとして10分ぐらい困ってたので同じように困ってる方のための記録です。

# viewDidLoadにそもそも画面遷移はできない。
viewDidLoadに画面遷移できない理由は「viewDidLoadとはまだviewを読みこむ処理」だから。
結論としてはviewDidAppearを使うことで画面表示時の画面遷移を行うことができる。

よくいろんな記事で以下のようなライフサイクルを見たことがあると思います。
![llife.png](https://qiita-image-store.s3.amazonaws.com/0/254495/f51da2e2-a38d-62f3-7505-3dcecd6cca0e.png)
ふむふむ、なるほど。で、何故に画面遷移ができないの?って困った時にいろいろ調べていたら以下を見つけました。
　[iOS開発におけるUIViewControllerのライフサイクルイベントまとめ](https://stalte.hatenablog.com/entry/2018/08/03/102040)
とても腑に落ちたのでまとめると、viewライフサイクルは、

```
①loadView
②viewDidLoad
③viewWillAppear
④viewWillLayoutSubviews
⑤viewDidLayoutSubviews
⑥viewDidAppear

⑦viewWillDisappear
⑧viewDidDisappear
```
大まかには以下のような意味になるので、

```
①Viewを読み込むよ
②Viewを読み込んだよ
③Viewを表示するよ
④View(SubViews)をうまい事配置するよ
⑤View(SubViews)をうまい事配置したよ
⑥Viewを表示したよ

⑦View消すよ
⑧View消したよ
```
⑥のviewDidAppearで初めて画面遷移等の処理を行うことが可能になる。
コードにすると以下のようになります。

```swift:viewDidLoad内に画面遷移を書き込んだ場合。
 override func viewDidLoad() {
        super.viewDidLoad()
        //ここに画面遷移処理を記述しても走らない。
    }

override func viewDidAppear(_ animated: Bool) {
         //ここに画面遷移処理を記述するときちんと走る。    
        }


```

自分なりにスッキリしました。viewDidLoad時にでも画面遷移の方法をご存知の方がいればコメントを下さい。
自分の LINE に Ruby で通知を送る

# やりたいこと

長い処理が終わった後などにスマホに通知がほしいです。Slack への通知でもいいのですが、個人的には LINE の方が気づきやすいので LINE へ通知してほしいです。

# 方法

[LINE Notify](https://notify-bot.line.me/ja/) というサービスを使います。まずサービスを使用するために必要なトークンを発行します。

🔽 https://notify-bot.line.me/my/ にアクセスします。LINE のメールアドレスとパスワードでログインする必要があります。そして「トークンを発行する」ボタンをクリックします。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/6697/66c756af-f83f-6f7c-8b8c-f8487a5183ef.png)

🔽 任意のトークン名を入力します。そして通知を送信するトークルームを選択します。僕は個人宛にメッセージを送信してほしいので「1:1 で LINE Notify から通知を受け取る」を選択しました。それから「発行する」をクリックします。

![02.png](https://qiita-image-store.s3.amazonaws.com/0/6697/74b52a35-7abd-e9a1-d986-33c7bf19f9b8.png)

🔽 発行されたトークンが表示されるのでメモしておきます。

![03.png](https://qiita-image-store.s3.amazonaws.com/0/6697/c3ba9ac3-5faa-d328-1e02-24638815f519.png)

次に LINE Notify の API を使用するための Ruby プログラムを用意します。

```rb
require 'net/http'
require 'uri'

class LineNotify
  TOKEN = 'ここに発行したトークンを記入する'.freeze
  URL = 'https://notify-api.line.me/api/notify'.freeze

  attr_reader :message

  def self.send(message)
    new(message).send
  end

  def initialize(message)
    @message = message
  end

  def send
    Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |https|
      https.request(request)
    end
  end

  private

  def request
    request = Net::HTTP::Post.new(uri)
    request['Authorization'] = "Bearer #{TOKEN}"
    request.set_form_data(message: message)
    request
  end

  def uri
    URI.parse(URL)
  end
end
```

では実際に API を叩いてみます。

```rb
LineNotify.send('🐢 長い処理が終わりました。')
#=> #<Net::HTTPOK 200  readbody=true>
```

![04.png](https://qiita-image-store.s3.amazonaws.com/0/6697/a6dad744-df59-3c75-feff-46b0e861497f.png)

LINE に通知が無事に届きました :tada:
論理削除実装時のdevise対応

[devise](https://github.com/plataformatec/devise)による認証を行っているrailsアプリで、[kakurenbo\-puti](https://github.com/alfa-jpn/kakurenbo-puti)なんかで論理削除を実装しても、そのままではログインができてしまう。

論理削除したユーザーがログインしている場合は強制ログアウトし、ログインできないように、該当のモデルに下記のメソッドをオーバーライドすることで対応できる。
`deleted_account`に、認証時のメッセージを登録する。

```ruby:YourModel.ruby
  # ensure user account is active
  def active_for_authentication?
    super && !soft_destroyed_at
  end

  # provide a custom message for a deleted account
  def inactive_message
    !soft_destroyed_at ? super : :deleted_account
  end
```

```yaml:config/locales/devise.ja.yml
ja:
  devise:
    failure:
      deleted_account: 'このアカウントは退会されています。'
```
多次元の勾配でAutogradを実行する

#事の経緯
基本的なネットワークであれば

```python
output = net(input)
loss = loss_fn(output)
optimizer.zero_grad()
loss.backward()
optimizer.step()
```

このように、5行で終わってしまう誤差逆伝播ですが、このbackward（）が使えるのは

Tensorがスカラーの時です。

諸事情があり、多次元のTensorを使って勾配を繋げ、Autogradしたいとします。(笑)

(すなわち、勾配の一部を外部ライブラリに依存する形で求めたがために、Autogardの始点がスカラーでなくなってしまった時)

チェインルールで計算するため、勾配をAutogradに渡すやり方を説明します。

色々調べた結果以下

#やりかた

```Python
output = net(input)

##(なんらかの処理で、勾配を求める)##
##(some_function内では外部ライブラリに依存した処理が行われており)##
##(autogradが適用出来ない)##
outputGrad = some_function(output, ground_Truth)

optimizer.zero_grad()
torch.autograd.backward([output], [outputGrad])
optimizer.step()
```

これで勾配を渡すことが出来ます

事情が分かる人には分かると思います。。。（笑)
Railsのモデルのscopeを理解しよう

# はじめに
Railsを学習していて、**「モデルのscopeってどうやって使うんだろう？」**となったことがありました。Railsのモデルのscopeはどのような場面で、どうやって使うのかについて説明して行こうと思います。

# modelのscopeとは
そもそもmodelのscopeとは、Active Recordの機能の一部です。以下[Ruby on Rails ガイド](https://railsguides.jp/)より
> スコープを設定することで、関連オブジェクトやモデルへのメソッド呼び出しとして参照される、よく使用されるクエリを指定することができます。スコープでは、where、joins、includesなど、これまでに登場したすべてのメソッドを使用できます。どのスコープメソッドも、常にActiveRecord::Relationオブジェクトを返します。このオブジェクトに対して、別のスコープを含む他のメソッド呼び出しを行なうこともできます。
単純なスコープを設定するには、クラスの内部でscopeメソッドを使用し、スコープが呼び出されたときに実行して欲しいクエリをそこで渡します。

上記を簡潔にするとモデルのscopeとは**複数のクエリをまとめたメソッド**になります。

# modelのscopeを使うと使わないとでは何が違うか？
実際にUserを例に説明して行こうと思います。
Userコントローラーのindex内で`@users`が`where(deleted: false).order(created_at: :desc)`のようにUserを呼び出しています。（`deletedカラム`が**false**の時、`created_atカラム`を**降順**で呼び出す）しかし、`where`や`order`のクエリメソッドが連結しています。これでも呼びだせるため良いですが、**メソッドが連結しているため若干読みづらい**です。

```app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    @users = User.where(deleted: false).order(created_at: :desc)
  end
  ...
end
```


Userモデルで以下のように**「active」、「sorted」、「recent」**という3つのscopeを定義しましょう。

```app/model/user.rb
class User < ApplicationRecord
  ...
    # deletedカラムがfalseであるものを取得する
    scope :active, -> { where(deleted: false) }
    # created_atカラムを降順で取得する
    scope :sorted, -> { order(created_at: :desc) }
    # activeとsortedを合わせたもの
    scope :recent, -> { active.sorted }
  ...
end
```

scopeは再利用が可能です。そのため、繰り返し何度も呼び出す際は毎回クエリメソッドを連結で呼び出すよりも、scopeを定義したほうがコードがスッキリして読みやすくなるということです。それなら以下のようにしても良いのでは？と思われる方もいるかと思います。

```.rb
scope :recent, -> { where(deleted: false).order(created_at: :desc) }
```

なぜ、3つのscopeに分けたかというと、再利用という点に着目した際に`where(deleted: false).order(created_at: :desc)`とあらかじめ連結しておくよりも`where(deleted: false)`と`order(created_at: :desc)`に分けておくことにより、個々でつかうことが可能となるため広く利用可能になるからです。また、`active`と`sorted`を合わせた`recent`を定義することでコントローラーから簡単に呼び出すごとができます。

```app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    @users = User.recent
  end
  ...
end
```

比較するとscopeを定義して、複数のクエリを集約したメソッド`recent`を作ったほうが**簡潔に書ける**と思います。

```app/controller/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    # scope recentを定義しなかった場合
    @users = User.where(deleted: false).order(created_at: :desc)
    # scope recentを定義した場合
    @users = User.recent
  end
  ...
end
```

# まとめ
- モデルのscopeを定義することにより、複数のクエリを一つのメソッドとしてまとめることができる。
- コントローラーで複数のクエリを書くよりもscopeを使えば、コードがスッキリする。
pythonでconcurrentでメモリがもりもり増大する

それなりの量のテキストをそれなりに時間の掛かるREST APIにかけようと思って、concurrentを使って↓のような並列処理を書いたのだけど、使用メモリがあっという間に膨れ上がってプロセスが死ぬ。

```python
executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
wait_executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)

def proc(line):
  # REST APIを呼び出す処理

def wait_task(future):
    future.result()

for line in sys.stdin:
    future = executor.submit(proc, line)
    wait_executor.submit(wait_task, future)
```

最後にstdinのループの最後にgc.collect()を入れたら使用メモリは50MBくらいで落ち着くようになったけど、正しい解決方法ってなんなんだろ。
とりあえず Rails の DB の内容をサクッと CSV 出力したいときのためのコード

# コード

```rb
class ExportCsv
  attr_reader :model, :dir

  # 第 1 引数に出力したいモデル、第 2 引数に出力先のディレクトリを指定する。
  def self.call(model, dir)
    new(model, dir).call
  end

  def initialize(model, dir)
    @model = model
    @dir = Pathname(dir)
  end

  def call
    filepath.open('w:SJIS') do |f|
      f.puts(csv_string)
    end

    filepath
  end

  private

  def csv_string
    options = { headers: headers, write_headers: true, force_quotes: true }

    CSV.generate(options) do |csv|
      model.find_each do |record|
        csv << record.attributes.values
      end
    end
  end

  def filepath
    dir / filename
  end

  def filename
    "#{model.table_name}_#{Time.zone.now.strftime('%Y%m%d%H%M%S')}.csv"
  end

  def headers
    model.column_names
  end
end
```

# 使用例

上記のクラスを定義した上で、Rails コンソールで次のコードを実行する。

```rb
ExportCsv.call(Girl, '/Users/quanon/Downloads')
#=> #<Pathname:/Users/quanon/Downloads/girls_20181126185252.csv>
```
git pushしたら自動的に本番環境でgit pull されるWordPress(KUSANAGI)の共同開発環境

#はじめに
過去にWordPressサイトを共同開発することがありました。バージョン管理はをgithubでやっていましたが、本番への反映が面倒だったので自動化したお話


これまではgithubにソース(テーマファイル)を``git push``したあとに
サーバのストレージをMacに``sshfs``コマンドを使いマウントしてから``git pul``lするという面倒なことをやっていた

#環境
 - Mac OS MAMPにローカルサーバを立ててテストをしていた
 - 本番環境へのサーバへSSHログイン可能
 - 本番環境は[KUSANAGI](https://kusanagi.tokyo/ )でラクラク構築

#実装
###サーバ側の作業1
ログインユーザはkusanagi
リポジトリに使うディレクトリを作成

```bash
$ mkdir /var/git_service/wp_dev/hogehogehuga.com.git/
```

リポジトリに移動

```bash
$ cd /var/git_service/wp_dev/hogehogehuga.com.git/
```

###Macでの作業

```bash
$ cd /Applications/MAMP/htdocs/wordpress/wp-content/themes/hogehogehuga.com/
$ git init
$ git add .
$ git commit -m '1st'
```

``.git``の設定ファイルを変更

その一番下の行に ``kusanagi@hogehogehuga.com:/var/git_service/wp_dev/hogehogehuga.com.git`` を追加

```bash
$ vi .git/config
```

```:.git/config
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = git@github.com:merarli/wp_hogehogehuga.com.git
fetch = +refs/heads/*:refs/remotes/origin/*
url = kusanagi@hogehogehuga.com:/var/git_service/wp_dev/hogehogehuga.com.git
```

pushしてgithubとサーバのリポジトリにファイルがあれば成功

###サーバ側の作業2
サーバのリポジトリからクローンする

```bash
$ cd /home/kusanagi/hogehogehuga.com/DocumentRoot/wp-content/themes/
$ git clone /var/git_service/wp_dev/hogehogehuga.com.git
```


クローンしたファイルの中にデータがあるか確認

```bash
$ cd /home/kusanagi/hogehogehuga.com/DocumentRoot/wp-content/themes/hogehogehuga.com
$ ls
```



githubにもpushされているか確認(Macのプラウザで確認)

####自動化設定
サーバのリポジトリの設定ファイルを変えるために移動

```bash
$ cd /var/git_service/wp_dev/hogehogehuga.com.git/hooks
```

post-update.sampleをpost-updateとしてコピー

```bash
$ cp post-update.sample post-update
```

設定ファイルを以下のように設定

```bash
$ vi post-update
```

```:post-update
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".

cd /home/kusanagi/hogehogehuga.com/DocumentRoot/wp-content/themes/hogehogehuga.com/
git --git-dir=.git pull origin master
```

実行権限を与える

```bash
$ chmod a+x post-update
```

実行して動けば成功

```bash
$ ./post-update
```
【JavaScript】関数の引数をオブジェクト形式にして、さらに初期値を設定する。

JavaScriptの関数に引数が複数ある場合、オブジェクト形式にすると、値の意味がわかりやすくてとても便利です。
さらに、引数に初期値を設定してあげれば、関数の設定を変えたいときだけ引数を書けば良いので、より便利に関数を使うことができます。

```javascript
const func = ({ hoge = 'foo' } = {}) => console.log(hoge);

func(); // 'foo'
func({ hoge: 'bar' }); // 'bar'
```

こんな感じ。

関数`func()`の実引数を空にしたときは、コンソールログに文字列`foo`が表示され、オブジェクト形式で`hoge`プロパティの値を設定すると、そのプロパティ値が適用されるので、文字列`bar`が表示されます。

便利ですね。

でも、なぜこのような挙動になるのでしょうか？？

## 分割代入でオブジェクトの初期値を設定
```javascript
const func = ({ hoge = 'foo' } = {}) => console.log(hoge);
```

上記関数の`{hoge = 'foo'} = {}`部分は、**分割代入**の記法を使用しています。
分割代入のサンプルっぽく、引数の中身を独立させてみます。

```javascript
const obj = {};
const { hoge = 'foo' } = obj;
// const { hoge = 'foo' } = {}; と同じ

console.log(obj.hoge) // undefined
console.log(hoge); // 'foo'
```

オブジェクトの分割代入では、左辺を`{ hoge = 'foo' }`の形にすることで、初期値を設定できます。
また、右辺のオブジェクトのプロパティが`undefined`の場合は、左辺の初期値が適用されるので、`hoge`の値は文字列`foo`になります。

もし、右辺のオブジェクトのプロパティに値があった場合は、そちらが適用されるので、以下のように`hoge`の値が文字列`bar`になります。

```javascript
const obj = { hoge: 'bar' };
const { hoge = 'foo' } = obj;
// const { hoge = 'foo' } = { hoge: 'bar' }; と同じ

console.log(obj.hoge) // 'bar'
console.log(hoge); // 'bar'
```

上記の内容を踏まえて、改めて関数を見てみましょう。

```javascript
const func = ({ hoge = 'foo' } = {}) => console.log(hoge);

func(); // 'foo'
func({ hoge: 'bar' }); // 'bar'
```

実引数が空の場合は、仮引数の`{ hoge = 'foo' } = {}`が適用され、`hoge`が文字列`foo`になります。
逆に、実引数にオブジェクトが指定されている場合、仮引数内は`{ hoge = 'foo' } = { hoge: 'bar' }`の状態になるので、`hoge`は文字列`bar`になります。

ちなみに、仮引数の`= {}`を消してしまうと、実引数が空のときに代入元のオブジェクトがなくなってしまうので、エラーになってしまいます。

```javascript
const func = ({ hoge = 'foo' }) => console.log(hoge);

func(); // Uncaught TypeError
func({}); // 'foo'
func({ hoge: 'bar' }); // 'bar'
```

## オブジェクトの分割代入に関して

ここからは余談ですが、オブジェクトの分割代入を少し掘り下げてみます。

```javascript
const obj = {};
const { hoge = 'foo' } = obj;

console.log(obj.hoge) // undefined
console.log(hoge); // 'foo'
```

オブジェクト`obj`の`hoge`プロパティが`undefined`の状態ということは、以下のように書き換えることができます。

```javascript
const obj = { hoge: undefined };
const { hoge = 'foo' } = obj;

console.log(obj.hoge) // undefined
console.log(hoge); // 'foo'
```

さらに、左辺の`{ hoge = 'foo' }`は、**オブジェクトのプロパティとプロパティ値が同じだから省略されている**と考えられるため、

```javascript
const obj = { hoge: undefined };
const { hoge: hoge = 'foo' } = obj;

console.log(obj.hoge) // undefined
console.log(hoge); // 'foo'
```

こう書き換えることもできます。
`hoge` ばっかりだと分かりづらいので、ちょっと変えてみます。

```javascript
const obj = { hoge: undefined };
const { hoge: val = 'foo' } = obj;

console.log(obj.hoge) // undefined
console.log(val); // 'foo'
```

こうやってみると、**プロパティ値の変数`val`に、文字列`foo`を代入している**ようにみえますね。
つまり、オブジェクトの分割代入は、**オブジェクトのプロパティ値を変数とし、そこに値を代入している**と考えられるわけです。

```javascript
const _val = 'foo';
const obj = { hoge: _val };
const val = obj.hoge;
console.log(val); // 'foo'
```

こんな感じで。

分割代入はなかなか理解しづらい記法ですが、こうやって紐解いてみるとわかりやすいですね。
Augurのマーケット内訳(2018-11-26)

予測市場のプラットフォームである[Augur(オーガー)](https://www.augur.net/)の、2018-11-26現在のマーケットの内訳です。
カテゴリ名を元に分類し、そのカテゴリに含まれるマーケット数を合計しました。


# マーケットの内訳(mainnet)

メインネット上には現在722個のマーケットがあります。

PRE_REPORTING(売買受付中)のマーケットの内訳は、
暗号通貨関連(crypt currency)：283(+4)
スポーツ(sports)：110(-11)
政治(politics)：101(+1)
経済(finance)：71(+0)
その他(other)：157
でした。

![20181126-augur-market-count.png](https://qiita-image-store.s3.amazonaws.com/0/142577/f81cd1c9-08ff-8b26-a3ec-8e25dfa0af57.png)

# 感想

仮想通貨全体の価格の下落があったので、もっと暗号通貨関連のマーケットが増えるかと思いましたが、微増にとどまりました。
今後もウォッチを続けたいと思います。
MySQLのイベントスケジューラを使う

MySQLのイベントスケジューラを使った時の覚書です。

#環境
MySQL 5.6.16

#設定方法
###1.スケジューラ設定を確認

イベントスケジューラを使うには、MySQLの変数'event_scheduler'の値をONにしてあげる必要があります。
まずは現在の設定がどうなっているか確認します。

```sql:
show variables like '%event%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| event_scheduler | OFF   |
+-----------------+-------+
```
オフになっていたので、ONにします。

```sql:
set GLOBAL event_scheduler=ON;
```
再度設定を確認。オンになっています。

```sql:
show variables like '%event%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| event_scheduler | ON    |
+-----------------+-------+
```
###2.イベントを作成

基本的な構文は以下です。

```CREATE EVENT <イベント名> ON SCHEDULE <スケジュール> DO <実行するクエリ> ```
【詳細】https://dev.mysql.com/doc/refman/5.6/ja/create-event.html

SCHEDULEは、'AT <タイムスタンプ>'または'Every <インターバル>'を指定します。

今回は、毎日0:00に実行したいので'on schedule'に'every 1 day'、
'starts'に明日の0:00:00を指定します。


```sql
delimiter $$
create event <イベント名>
on schedule every 1 day
starts '2018-XX-XX 00:00:00'
do
begin
     <ここにクエリを書く>
end$$
delimiter ;
```

これでイベントが作成されました。
作成したイベント一覧を確認するには、以下コマンドを使います。

```sql
show events
```
###3.イベントの編集

作成したイベントを編集するには、'alter event'を使用します。
'create event'と構文は同じです。

```sql
delimiter $$
alter event <イベント名>
on schedule every 1 day
starts '2018-XX-XX 00:00:00'
do
begin
     <ここにクエリを書く>
end$$
delimiter ;
```
###4.イベントの削除

イベントの削除には、'drop event'を使用します。
後ろにイベント名を指定してあげるだけでOKです。

```sql
drop event event_name
```
ECSのデプロイ時に一定確率で静的ファイルが404になる問題を回避する

## ECSのデプロイにおける404エラー問題
ECSへのデプロイ時に、**ページに必要な静的ファイル（cssやjs）が404エラーになる**可能性があります。

さっと書いた図を載せておきます。

![dO_12e9048RlynHpr8DPuIaYS2BeO1Up1rXq22lMSjUUFsUZsgGk7dPX-EPd_a-LjcdnOBW5a1-Z5QOPuG9pldLiBCO0ghJrNLaM0aBxfSrwxm6P-YUWKEQSzdbI5wNBMX_HDfXYC8ldePTmQowpZA9tA6VRjHgtnIuBAhwohioJJKucX8e7SLT6ydBVewwSqn4ehJqe8_orVLqROKr3Q_ffJL_MzB5.png](https://qiita-image-store.s3.amazonaws.com/0/109606/908f3048-addf-0665-3fd3-ada3baf8bd3b.png)

ECSでは古いバージョンのタスクと新しいバージョンのタスクが同時に起動している状態が数分続きます。これは以下のような問題を引き起こします。

1. ユーザーが`/`にアクセスする
2. ECSのサービスが新しいTask（以下TaskA）にリクエストを流し、TaskAが`/`のHTMLを返す
3. ユーザーが返ってきたTaskAのHTMLで必要な、`/TaskA.js`というファイルをリクエストする
4. ECSのサービスが古いTask（以下TaskB）にリクエストを流す
5. しかしTaskBには`/TaskA.js`が存在しないため404エラーを返す

### CloudFrontによるエラーのキャッシュ

**CloudFrontを利用している場合、オリジンのエラーをデフォルトで5分間キャッシュする**ため、さらに404エラー時間が伸びてしまう可能性があります。またエラーのキャッシュを無効にしたとしても、404エラーはデプロイ中の数分間は一定確率で発生し、根本的な解決になりません。またCDNのメリットであるDos攻撃の対策も、意味を成さなくなってしまいます。

## AWSの回答は「S3とCloudFrontを使って」
この問題の対処法をAWSの技術サポートに問い合わせてみたところ、「そちらの解決方法としては、**静的コンテンツをS3にアップロードしてCloudFrontで配信して欲しい**」という返事をもらいました。

おそらくですが、このようなデプロイの問題は本来アプリケーション側で個別に対応するべき問題だということでしょう。アプリケーションやインフラの構成によって対応するべき範囲は違いますし、どこまで許容するべきかといった所は運用方法によって変わってきます。そのため個別に各々が対応するしかないのでしょう。

ロードバランサーであれば、スティッキーセッションを利用してユーザーは一貫性のあるオリジンの参照ができますが、現在ECSでそのようなものは提供されていない模様です。

## S3へ安全なデプロイを行うために
ということで静的ファイルをS3にアップロードし、ユーザーが静的ファイルを参照する際はCloudFrontからS3を見にいくようにします。

このS3へのデプロイ時のポイントとしては、**S3に静的ファイルの古いバージョンと新しいバージョンを同時に存在**させておく事です。

### S3へ同期
awscliの`s3 sync`を使うとローカルのファイルをS3のディレクトリへ同期させることができます。同期というのは同一のファイルがあれば上書き、それ以外は新規アップロード、という形です。

これによってバージョンによってファイルの名前が違う限り安全にデプロイできます。

### ライフサイクルによる解決は危険性がある

S3のライフサイクル機能を使って、削除用のタグが付いたファイルを削除するという解決方法も考えたのですが、削除タイミングによる問題がありました。

まずライフサイクルの削除のタイミングは以下のように決まってます。
参考: [ライフサイクル設定の要素 - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/intro-lifecycle-rules.html#intro-lifecycle-rules-number-of-days)

- オブジェクト作成日時からの日数（作成日時は最終更新日時、つまりメタデータのLast Modifiedのことで、そもそもS3では作成日時を保持していない）
- ルールに指定された日数をオブジェクトの作成時間に加算し、得られた日時を翌日の午前 00:00 UTC (協定世界時) に丸める

つまり日本では午前8:59に「ライフサイクルルールの経過日数を超えているファイル」に削除タグを付けたら、1分後に削除されてしまうのです。（タグの付加ではメタデータのLast Modifiedは更新されないため）

上記がライフサイクルによる課題解決ができなかった理由です。なのでシンプルに同時に3つのバージョンをデプロイさせる方法で解決しました。
IBM のImage Caption Generatorを試した結果。


画像に説明文を自動でつけてくれるという、Image Caption Generator を試してみました。

https://developer.ibm.com/exchanges/models/all/max-image-caption-generator/

試行１

最初に試したのはこのページ。
＜全日本ジュニア女子フリー＞初めての全日本ジュニアを笑顔で滑り終えた本田紗来（撮影・長久保　豊）Photo By スポニチ

https://www.sponichi.co.jp/sports/news/2018/11/25/gazo/20181125s00079000341000p.html


結果１

```javascript
{
  "status": "ok",
  "predictions": [
    {
      "index": "0",
      "caption": "a woman holding a tennis racquet on a tennis court .",
      "probability": 0.002356312321211884
    },
    {
      "index": "1",
      "caption": "a woman holding a tennis racquet on top of a tennis court .",
      "probability": 0.0010367872597283113
    },
    {
      "index": "2",
      "caption": "a young girl holding a tennis racquet on a tennis court .",
      "probability": 0.0006049253967977137
    }
  ]
}
```

→テニス選手と認識されました。確かに両手は挙げています。


試行２
次に試したのはこれ。

スポニチアネックス 11/26(月) 9:27配信 賜杯を手に、父の一哉さん（前列右）らと喜びを分かち合う貴景勝（撮影・中村　与志隆）
https://headlines.yahoo.co.jp/hl?a=20181126-00000074-spnannex-spo.view-000

結果２

```json
{
  "status": "ok",
  "predictions": [
    {
      "index": "0",
      "caption": "a group of people sitting around a table .",
      "probability": 0.005747581504647952
    },
    {
      "index": "1",
      "caption": "a group of people sitting around a table with wine glasses .",
      "probability": 0.0011312559562743495
    },
    {
      "index": "2",
      "caption": "a group of people sitting at a table with wine glasses .",
      "probability": 0.0009399691413672182
    }
  ]
}
```
→たしかにたくさんの人がいるし、ワイングラスみたいなものも持っている。

試行３

最後にこちらを試してみた。
＜オリックスファンフェスタ＞引退投球を行う佐藤達（撮影・北條　貴史） Photo By スポニチ
https://www.sponichi.co.jp/baseball/news/2018/11/25/gazo/20181125s00001173413000p.html

```json
{
  "status": "ok",
  "predictions": [
    {
      "index": "0",
      "caption": "a baseball player pitching a ball on top of a field .",
      "probability": 0.008243567877787124
    },
    {
      "index": "1",
      "caption": "a baseball player pitching a baseball on a field .",
      "probability": 0.006943362905417302
    },
    {
      "index": "2",
      "caption": "a baseball player pitching a ball on a field .",
      "probability": 0.00647863833444942
    }
  ]
}
```

→こちらは正解！！

で、ここまで見て思ったのですが、ニュースサイトのような場合は写真の説明を見るのがいちばんよいかもですね。。
マスタプレイリストによるHLS冗長構成

# はじめに

動画配信を行う構成は色々ありますが、
今回はEC2を2台作成して、マスタプレイリストを使ってHLSを冗長化する構成を試してみます。

## 構成図

<img width="904" alt="スクリーンショット 2018-11-26 12.19.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/277934/c2b0e362-2224-3866-141c-6d5c862680e2.png">

構成はこのようなイメージです。
映像の入力から配信までの経路を2つ用意して冗長化する仕組みになります。

- **Main** ： 配信ソフト - EC2 - S3 - CF - Player
- **Backup** ： 配信ソフト - EC2 - S3 - CF - Player

　
※動画ファイルをS3に格納してライブ配信する方法は、チームメンバーの記事に掲載されているため割愛します。
➡︎[「HLSライブストリーミングサーバーs3配信」](https://qiita.com/khagi/items/1e88f21b027b115a4ac7)

## 構築の流れ

1. 事前にマスタプレイリストを作成し、S3バケットに配置する。
2. 配信ソフトからEC2インスタンスのMain/Backup両方にストリームを流す。

## 視聴側の想定動作

3. プレイヤー側ではマスタプレイリストを視聴するため、通常時はMainストリームの方が表示される。
4. Main側でインスタンス停止やS3転送が上手くいかない場合は、Backupストリームに切り替わる。

## マスタプレイリスト作成

Apple Developer Documentを参考に以下のように作成しました。
（今回はABRを導入しています。）

【参考】
[Using HTTP Live Streaming](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/UsingHTTPLiveStreaming/UsingHTTPLiveStreaming.html)
→「Redundant Streams」

```master_playlist.m3u8
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=480x270
https://xxxxxxxxxxxxx.cloudfront.net/main/teststream_a_low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=400000,RESOLUTION=480x270
https://xxxxxxxxxxxxx.cloudfront.net/backup/teststream_b_low.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=960x540
https://xxxxxxxxxxxxx.cloudfront.net/main/teststream_a_mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,RESOLUTION=960x540
https://xxxxxxxxxxxxx.cloudfront.net/backup/teststream_b_mid.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2000000,RESOLUTION=1280x720
https://xxxxxxxxxxxxx.cloudfront.net/main/teststream_a_high.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2000000,RESOLUTION=1280x720
https://xxxxxxxxxxxxx.cloudfront.net/backup/teststream_b_high.m3u8
```

# 検証

Safariの開発モードで動画を視聴する。
→*「開発」タブ - 「Webインスペクタを表示」 - 「ネットワーク」*

### 通常時

マスタプレイリストの順番が上に記述されているメインストリームの方が表示されることを確認できました。
想定通りの挙動となりました。

### 障害時

Main/Backup両方のインスタンスにストリームを流している状態で、
Mainインスタンスを停止させます。

結果、わずかな寸断のみで、Backupストリームの動画に切り替わることが確認できました。

　
ちなみに、Video.jsを利用してChromeから確認したところ、
切り替わるタイミングで、下記のようなメッセージが出力されました。

```
VIDEOJS: WARN: Problem encountered with the current HLS playlist. Playlist no longer updating. Switching to another playlist.
```

>＜Google翻訳＞：現在のHLSプレイリストで問題が発生しました。プレイリストが更新されなくなりました。別のプレイリストに切り替えます。


このように、**Redundant Streams**では、
**一定期間プレイリスト（m3u8ファイル）が更新されないと、別のストリームに切り替わる挙動となる**ことが分かりました。

## ブラウザによる違い

上記のようにSafariでは冗長化は問題なく、障害時にBackupの方に切り替わりことが確認されました。
ただ、Chromeでは、上手く切り替わる時と切り替わらない時があり、挙動は安定しませんでした。

## おわりに

今回の検証を行う前は、インスタンス停止だとS3にプレイリストとtsファイルが残ってしまい、
プレイヤーではS3に残ったtsファイルを読み込み続けて、動画がループすることを想定していました。

実際には、一定期間プレイリストが更新されないと別のストリームに切り替わってくれたので、一安心でした。
ブラウザによる挙動が違うという謎は未だ残っていますが……それはまたの機会に。
Diverse Podcastを支える技術

Diverse Advent Calendar 2018の3日目です

前回は @k-motoyan さんの[Goで環境変数を読み込んでバイナリに埋め込む](https://qiita.com/k-motoyan/items/dcf70208b99d6e0e9334)でした。
問題意識から、ツールを作って公開するまでが記事になっています。
ツール公開までする姿勢を私も見習っていきたいです。



さて、[株式会社Diverse](https://diverse-inc.co.jp/)では、[Diverse Podcast](https://podcast.diverse-inc.com)というPodcast番組を配信しています。
配信開始から半年ほど経ったので、どのようにしてこの番組を企画/収録/配信しているのかをご紹介したいと思います。


# 効能

まず、会社がPodcast配信をしてどんな意味があるのでしょうか？

こうした取り組みをすることで社内に生じた（と思う）効果をご紹介します。

## 採用面接などの時に会社の雰囲気をわかった上で来てくださる方が多くなった

2018年12月現在、弊社は絶賛エンジニアを募集中です。

採用面接や、いわゆるカジュアル面談の際に、すでに弊社について調べてきてくださる方が多く、Podcastを聴いてくださっている、という方もいらっしゃいました。

「社内の事情と、社員の雰囲気がわかった」「面白い取り組み」とコメントをいただくこともあり、ありがたい限りです。


## 司会業スキルが身につく

（複数回収録している私だけかもですが）こうした番組の司会というのは意外と考えることが多くあります。

* 誰からどんなエピソードを聞きだしたら面白いか、会社のPRにつながるか
* どうやったらエピソードを楽しく話してもらえるか
* どうしたら聴いてもらいやすいテンションに持っていけるか
* どうしたら属人化させずに番組を継続できるか

こうした司会業スキルが身につくと、ぶっちゃけ**モテます**[^1]。
[^1]: 懇親会などで話が弾むの意（意中の人にモテるかどうかはわかりません）


同僚社員のみなさまの力を借りながらですが、こうしたことを考えて解決していく機会を経験できます。


# 心構え

Diverse Podcastは不定期更新で、ぶっちゃけ音質もあまり気にしていません。
なぜなら、 **収録をがんばらない** を基本方針にしているためです。

このPodcast番組は、普段表に出てこない、社員のしていること、普段の雰囲気、何を考えているのか、などを外から見えるようにすることを目的としています。
会社の中を外から見えるようにするためには、見えるようにし続けることが大切です。

そんななか、月イチ更新のノルマがあったり、ノイズキャンセリングやイコライジングを頑張ったり、余計な音が入り込まないように気を遣いまくっていたら疲れて、続けるどころではなくなってしまいますよね。

そのため、Diverse Podcastでは、基本的に無編集で公開しています。

初めは私が収録担当という形になっていましたが、今では @imaizume さんも独自に収録を行ってくださるようになって、私の手を離れてきたかなと思っています。



# 技術

上記のような心構えなので、全然難しいことはしていません。

## 収録マニュアル

誰でも収録から公開までできるように、Diverse Podcastは収録マニュアルを用意しています。
だいたい以下のことがまとまっているので、あとはネタと話したい人がいれば、誰でも収録できます。

* 機材の場所
* 収録場所
* 収録の前に話者同士で共有しておくこと
* 番組のだいたいの流れ
* 録音方法
* 公開方法


あと、どの社員に何を聞いたら面白そうか、といったネタ帳も用意しています。

## 録音

2018年12月初旬現在、マイクにはYAMAHAの[YVC-300](https://sound-solution.yamaha.com/products/uc/yvc-300/index)を、サンプリングと編集には[Audacity](https://www.audacityteam.org)を使用しています。


YVC-300は一台で複数人の会話を収録できるので便利です。
が、声量の大きい人の声を拾うと他の人の声を拾ってくれない、声の高い人の声（特に女性の声）を拾ってくれない、など問題があるので他のマイクへの乗り換えを検討しています。
良いマイク情報があれば、ぜひコメントをください :bow: 


## サイト

r7kamuraさんの[Yattecast](https://r7kamura.github.io/yattecast/)を使用しています。
サーバーはGithub Pagesではなく、後述の別のものを使用しているため、それに合わせて多少改造しています。

[リポジトリ](https://github.com/diverse-inc/diverse-podcast)は公開していますので、ご自由にご覧ください。

## 配信サーバー

[Netlify](https://www.netlify.com)を使用しています。
Diverse PodcastではエピソードごとにPRを作成する運用にしているのですが、NetlifyはPRのブランチごとに一時的なサイトURLを用意してくれるため、プレビューが簡単にできて助かっています。

あと転送も早いのですぐに聴けて便利。



<hr>

Diverseでは今後も情報発信の機会を設けて、社内的には外部発信慣れの促進を、社外的には会社について知ってもらうことを促進していきます＼\ ٩( 'ω' )و /／

明日も @k-motoyan さんが担当してくださる予定です！
楽しみ！！
pandasでカテゴリごとのone-hotエンコーディング

# Abstract 
pandasでカテゴリごとに最大値、最小値を抽出するのは、難しくないです。
今回は、最大値、最小値にラベルをつけるスクリプト考えていて、結構長めにつまったのでメモを残します。


# Situation
| idx | category_id | id | incremental |   
| --- | --- | --- | --- |
| 0 | 1 | 1 | 1 | 
| 1 | 1 | 2 | 2 | 
| 2 | 1 | 3 | 3 |
| 3 | 2 | 4 | 1 | 
| 4 | 2 | 5 | 2 | 
| 5 | 2 | 6 | 3 | 
| ... | ... | ... | ... | 

みたいなテーブルを考えます。
この時、以下の問いを考えます。

> カテゴリごとの最大値、最小値に対してフラグを持たせる

最終的には、こんな感じに慣ればいいです。

| idx | category_id | id | incremental | is_first | is_last |   
| --- | --- | --- | --- | --- | --- | 
| 0 | 1 | 1 | 1 | 1 | 0 | 
| 1 | 1 | 2 | 2 | 0 | 0 | 
| 2 | 1 | 3 | 3 | 0 | 1 |
| 3 | 2 | 4 | 1 | 1 | 0 |
| 4 | 2 | 5 | 2 | 0 | 0 |
| 5 | 2 | 6 | 3 | 0 | 1 | 
| ... | ... | ... | ... | ... | ... |

# Script 
csvを読むとして書きます。

```python:
import pandas as pd 

df = pd.read_csv('file_name')

df['is_first'] = np.where(df.index.isin(df.groupby('category_id').incremental.idxmin()), 1, 0)
df['is_last']  = np.where(df.index.isin(df.groupby('category_id').incremental.idxmax()), 1, 0)
```

カテゴリが増えてもとりあえず、対応はできます。

# TODO

```text
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
```
が出ます。現状、解決策見つかっていないので改善の余地ありです。
vimでマークダウン編集時にバッククォートが表示されない場合

# 問題
vimでmarkdownファイルを編集している時に，バッククォート（backquote)が表示されない．

原因が分からずに半年くらい使ってました．見えないバッククォートが何回打たれたのかが分からず，大変苦労していました．

# 原因
markdownファイルを編集しやすくしようと入れていたプラグイン
```plasticboy/vim-markdown```
の設定ミスでした．

元々の原因はvimの***conceal機能***によるものです．
concealはダブルクォーテーションなどを非表示にしたり，Tex形式で書いたコードを見やすく整形してくれる機能です．

この機能がプラグインレベルで設定されているため，バッククォートが非表示になっていました．

# 解決方法
.vimrcなどの設定ファイルに以下を書き込む．

```vim:.vimrc
let g:vim_markdown_conceal = 0
```

# 汎用的な話について
もともと「vim markdown backquate 表示されない」などの特化した内容で検索をしていたので，なかなか良い記事に出会えませんでした．
conceal機能を知っていればすぐに解決したと思います．

markdownファイルじゃないけど，同じような悩みをしている方がいましたらconceal機能をオフにするといいと思います．
フォルダ.vim/以下にて
``` grep -r conceal ./* ```
を実行し，ヒットしたプラグインについて片っ端からオフにオフにすると良いです．

# おわりに
conceal自体は使い所を間違わなければ非常に便利な機能ですね．
しかし何も知らない初心者からすると，どうやってオフにすれば良いのか分からないという問題があり，解決に至るまで非常に時間が掛かると思いました．

この記事は自分のようにバッククォートが非表示になる理由が分からない方の手助けになると幸いです．
[Rails]active_model_serializersでレスポンスボディにモデル名のキーがほしい

```json
{
  id: 1,
  name: 'taro'
}
```

じゃなくて、

```json
{
  user: {
    id: 1,
    name: 'taro'
  }
}

```

こうしたい。

```config/initializers/active_model_serializer.rb
ActiveModel::Serializer.config.adapter = :json
```

### 参考
- [Not able to have the root · Issue #964 · rails-api/active_model_serializers · GitHub](https://github.com/rails-api/active_model_serializers/issues/964)
React初心者の実行の流れに関するメモ書き

#はじめに
趣味でReactを触ろうと思い、ぼちぼち触っているものの
覚えた端から忘れていくので自分用にメモ。

## Reactの実行の流れ
React実行の流れとしては
トップページ（index.html）→index.js→Apps.jsの流れで動いていく

### トップページ(index.html)
アプリが動作するメインのページ
以下のように記述されている。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <body>
      <div id="root">
      /* コンポーネントを出力する場所 */
      </div>
    </body>
  </head>
</html>
```

### index.jsについて
Reactアプリを実行する際、一番最初に呼び出されるファイル

```js:index.js
import React from 'react';
import ReactDOM from 'react-dom';

import './index.css';
import App from './App';

import registerServiceWorker from './registerServiceWorker';

ReactDom.render(<App />,document.getElementById('root');

registerServiceWorker();
```

#### renderメソッドとは
index.jsで使われているRenderメソッドについて。

```js:index.js
ReactDOM.render(<App />,document.getElementById('root'));
```
> render(comp, target)
> ・comp: 出力するコンポーネント
> ・target: 出力先の要素

上記コードの例で言えば、下記のAppコンポーネント(./App配下のApp.jsなど)を
index.htmlの「id="root"」の要素に出力せよという意味になる。

### Appコンポーネント（App.jsなど)
コンポーネント＝ページを構成するUI部品。
以下にAppコンポーネントを定義する

``` js:App.js
class App extends Component {
    render() {
        return(
            <div className = "App">
                <header className = "App-header">
                    <img src={logo} className="App-logo" alt="logo" />
                    <h1 className="App-title">Welcome to React</h1>
                </header>
                <p className="App-intro">
                    To get started, edit <code>src/App.js</code> and save to reload.
                </p>
            </div>
        );
    }
}

export default App;
```
- コンポーネントを定義する際には、Componentクラスを継承する。<br>
  class/extendsなどはES6(ES2015)導入された。
- コンポーネントは頭文字は大文字で表す必要がある。
- renderメソッドの戻り値はJSXで記述されたタグ

## 整理
これまでの流れ整理し、
index.jsでAppコンポーネントを呼び出し、Appコンポーネントのrenderメソッドが
出力内容を生成し、index.htmlの(index.jsで）指定した箇所が
反映されるといった流れになります。

## JSXとは
Reactにおいて必須なJSXについて。
JSXはざっくり言うとJavaScript上でHTMLのコードを使えるJavaScriptの拡張構文。
JSXはBabelでJavaScriptに変換される。

以下の例がJSX。

```js:app.js
import React from 'react';

function render() {
  return <div>hogehoge</div>;
}
```

JSXがBabelで変換されると以下のように出力される。

```js
import React from 'react';

function render() {
  return React.createElement('div', null, 'hogehoge');
}
```

以上です。
Debian Linux9系（stretch）でElixir 1.7をいごかした

Debian Linux 9系（stretch）で最新のElixir1.7をいごかしました。


# ざっくりまとめると
- Debian9標準のapt-getでインストールできるElixirは1.3系
- Elixir公式サイトの手順でインストールできるElixirは1.6系
- Debian9 testingでインストールできるElixirは1.7系（sidも1.7系）
- Debian9 testingでインストールする場合、先にerlang-baseをインストールする必要がある
- できた:hugging:


# 環境
Debian Linuxのバージョンは9.6。

```shell
root# cat /etc/debian_version
9.6
```


# はじめに
Debian9系（stretch）で最新のElixir1.7系をいごかしかきまとめます。


# インストール方法の選定
Debian9標準のapt-getでインストールできるElixirは1.3系なのでNG。[Elixir公式サイトの手順](https://elixir-lang.org/install.html#unix-and-unix-like)でインストールできるElixirは1.6系だったのでこれもNG。[Debian9 testingな環境だと1.7.4をインストールできる](https://packages.debian.org/testing/elixir)とのことなのでこちらを採用しました。


# セットアップとインストール
基本的にはstableを使うための設定を記載します（ファイルがないので作成する）。

```shell
root# vi /etc/apt/apt.conf.d/99target
APT::Default-Release "stable";
```

/etc/apt/sources.listにtesting用の行を追記（testingのコードネームはbusterらしい）した後、アップデートを実行します。

```shell
root# vi /etc/apt/sources.list
deb http://ftp.jp.debian.org/debian buster main

root# apt-get update
```

erlang-baseをインストールした後elixirをインストールします。

**erlang-baseをインストールせずに`apt-get install elixir/buster`をすると、古いバージョンのerlangがインストールされてしまいelixirが実行できない状態になるので、先に`apt-get install erlang-base/buster`を実行する必要があります。**

<details><summary>やってしまった場合は…</summary>
`apt-get remove elixir`→`apt-get autoremove`を実行して依存関係を含めたパッケージをアンインストールした後erlang-baseから順にインストールします。
</details>

```shell
！！！インストールの順番大切！！！
root# apt-get install erlang-base/buster
root# apt-get install elixir/buster
```

実行できるかどうかの確認とバージョンの確認をします。

```shell
root# iex --version
Erlang/OTP 21 [erts-10.1.1] [source] [smp:1:1] [ds:1:1:10] [async-threads:1]

IEx 1.7.4 (compiled with Erlang/OTP 21)
```


# まとめ
- Debian9系（stretch）で最新のElixir1.7系が実行できるようになりました:hugging:
AWS上に簡単なServerless環境を作ってみよう。②

#S3のバケット作成
１．	サービス画面からS3をクリックする。

２．	バケット作成をクリックし、以下のように情報を入れ、作成をクリックする。
※ほかの情報はディフォルトのまま
バケット名：自分の名前-serverlesswebsite
リージョン：アジアパシフィック（東京）

３．	index.htmlの中身の"YOUR-API-GATEWAY-LINK-HERE"を先ほど作成したAPIGatewayのURLに変更する。

```ruby:index.html
<html>
<script>

function myFunction() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
        document.getElementById("my-demo").innerHTML = this.responseText;
        }
    };
    xhttp.open("GET", "APIGateway URL ", true);
    xhttp.send();

}

</script>
<body><div align="center"><br><br><br><br>
<h1>Hello <span id="my-demo">Stylez AWS member!</span></h1>
<button onclick="myFunction()">Click me</button><br>
<img src="S3バケットURL"></div>
</body>
</html>
```

４．	S3バケット画面から自分が作成したS3バケットにチェックを入れ、「パブリックアクセス設定を編集する」をクリックし、以下のように進める。
①「選択されたバケットのパブリックアクセス設定」は何も選択せずに保存をクリックする。
②「確認」と入力する。

５．	作成したバケットをクリックし、「アップロード」→「index.html、error.html」のファイルを追加する。

```ruby:error.html
<html><body><h1>There has been an error!</h1></body></html>
```

![S3ファイルアップロード②.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/03b232cd-8522-dec3-b5e7-11b67973f74d.png)

６．「index.html、error.html」をそれぞれクリックし、「アクション」→「公開する」を選択する。
![S3ファイルアップロード③.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/8428dc7f-6eda-9ffe-9f10-d5f5b223d147.png)

７．「プロパティ」→「Static website hosting」をクリックし、以下の情報を入力、保存する。
インデックスドキュメント ：index.html
エラードキュメント：error.html

![S3プロパティ変更.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/6d0770c7-a523-7e92-7953-c8558d859f61.png)

８．「Static website hosting」の保存できていることを確認した上で、S3エンドポイントをクリックする。
![S3プロパティエンドポイントクリック.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/e122666e-d787-cac3-7b4e-72cea40f7151.png)

９．表示された画面からClick meを押してみる。
![click me.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/4e9a9ef1-17c1-e237-02ee-77a76a0a5605.png)
システムがカオスになる４つの要因

皆様、いかがお過ごしでしょうか？
オークファンで基盤技術を担当している　@gessy0129 です

今回はシステムがカオスになる要因
というかオークファンのシステムの中でこれはカオスだと思ったものを発表したいと思います

# 1. mount, mount, mount, mount

大量のデータを効率よく使うために一台のサーバを複数台のサーバでmount して使用しています
結果、あるサーバがどのサーバにmount されてるのか分からなくなってきました。。。
ヒドいものだと10台位のサーバから利用されているのではないかと思います

__解決策__

EFS やS3に移行をしていっています

# 2. ドキュメントはどこ？

弊社は数年前からQiita:teamを利用し、最近kibelaに載せ替えました
それ以外にも有象無象のチャットツールを頻繁に変更したり、ドキュメント置き場を変更したりしてきています
チャットツールの変遷だけでも、Skype　-> Talknote -> Slack と遷移しています
また、ドキュメント置き場もQiita:teamやら、GoogleDrive、Redmineのwiki、MicrosoftのSharepointなど様々です
結果、　**開発時にドキュメントは残す** ということは習慣づき、レビューでもドキュメントのレビューが入りますが、
**運用時にドキュメントを探さないで再度ドキュメントを構築する** という事になっています

__解決策__

ドキュメントを探すことを習慣づけてます
それ、kibelaにあるよ！が合言葉

# 3. 車輪の再発明

**古いシステムをリビルドしたい！**
よくある欲求だと思います
しかし、10年以上運営しているとシステム自体がかなり巨大化しており、全部のシステムをリビルドするのは莫大な時間とコストがかかります
そこでやりがちなのが、 **特定の機能だけをリビルドする** というものです
結果、なにが起きるか？
リビルド中も~~何故か~~その機能のPDCAは走り続け、いざリニューアルをしようとした時に古いものにはあるけど新しいものにはない
そんな事がよくあります
そして、じゃぁ、古いところにあるものは古い方を使い、新しいものは新しく作るものだけにしよう
こうして、同等の機能が持つものが2つ動くことになります

__解決策__

頭を抱えてます。。。

# 4. /etc/hosts がいっぱい

弊社、内部DNSは用意してないので、サーバーを見る時はIPアドレス直指定が過去のルールでした
しかし、プログラム内にIPアドレスがいっぱいあるとよく分からなくなってくる
そう考えた昔の担当者がいたのでしょう
`/etc/hosts` にいっぱい情報が書いてあって、そこを内部DNSとしています
しかもこの内部DNS、書き方のルールが統一されていない
例ですが同じIPアドレスなのにこんな風に書き方が違うのです

```
192.101.11.52 kensaku.aucfan.naibu
192.101.11.52 search.aucfan.private
```

__解決策__

AWSが素晴らしいことにinternal なRoute53を提供してるので、現在そこに移行中
ただ、おいおいマジか。。はまだあります。。。

# まとめ

カオスになる要因は他にもいろいろあると思います
しかし、長年運営しているといろいろと弊害も出てきます
その弊害を対応していくといろんな技術が身につくので弊社へのエントリーぜひお待ちしております！
AWS上に簡単なServerless環境を作ってみよう。①

#はじめに
AWS上にLambdaの関数を定義し、その関数とAPIGatewayを結び付け、予め用意したS3バケット内にある画像が見えるようにする非常にシンプルなサーバーを立てるための手順をまとめてみました。2部構成です。

#LambdaとAPIGatewayの作成

１．	サービス画面からLambdaクリックする。

２．	Lambda画面から関数の作成クリックする。


３．	関数の作成から一から作成を選択した上で、以下のように作成する。

名前：名前：自分の名前_ServerlessWebpage

ランタイム：Python 3.6

ロール:1つ以上のテンプレートから新しいロールを作成します。

ロール名：自分の名前_LambdaExecution

ポリシーテンプレート：シンプルなマイクロサービスのアクセス権限

![Lambda作成.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/13db3c10-6ad9-48f9-cffc-516ec534b967.png)

４．作成完了後、以下のように実施し、保存をクリックする。

➀「関数コード」に「serverlesswebsite.py」の内容を書き込む。
※関数の中身にある名前のところは自分の名前に変更する。

```ruby:serverlesswebsite.py
def lambda_handler(event, context):
    print("In lambda handler")
    
    resp = {
        "statusCode": 200,
        "headers": {
            "Access-Control-Allow-Origin": "*",
        },
        "body": "自分の名前"
    }
    
    return resp
```

②「基本設定」の説明の内容を書き込む。
説明：自分の名前 basic Lambda Website

５．トリガーの追加でAPI Gatewayを追加し、以下のように選択した上で、保存をクリックする。
API:新規APIの作成
セキュリティ：AWS IAM
![APIGatewayトリガー.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/35e63987-953b-b9d4-cf1e-e9547ed95f40.png)

６．作成されたAPI Gatewayをクリックする。
![APIエンドポイント.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/2efa137f-b770-9090-951f-45b44846c943.png)


７．API Gateway画面からリソースのANYが選択されている状態でアクション→メソッドの削除をクリックする。
![API anyを削除.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/4ad39478-9594-a816-1d47-3a5eb3edd924.png)

８．以下のように新しいメソッドを作成する。
①アクション→メソッドの作成する。
![API メソッド作成①.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/700a0f6d-9ded-c0b7-9130-56a4e2500ae2.png)


②GET→チェックボタンをクリックする。
![API メソッド作成②.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/83c3671c-3e1a-56ce-2609-d2a0bfc47dde.png)

③以下を入力し、保存をクリックする。
統合タイプ：Lambda 関数
Lambda プロキシ統合の使用：チェックする。
Lambda 関数：自分のLambda関数
デフォルトタイムアウトの使用：チェックする。
![API メソッド作成③.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/96985a34-2983-bbf3-6bfb-4ff392028566.png)

９．API Gateway画面からリソースのGETが選択されている状態でアクション→APIのデプロイをクリックし、以下の内容を追記する。
デプロイされるステージ：default
デプロイメントの説明：自分の名前 deployment
![API メソッド作成④.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/84e6b97b-adbd-2220-2c9e-3432f093d4f5.png)


１０．作成されたら、ステージが選択されている状態で、default→GETをクリックし、「URL の呼び出し」のURLを押して自分の名前が出ることを確認する。
![APIにて動作確認.PNG](https://qiita-image-store.s3.amazonaws.com/0/285485/0649e1c0-f6fd-df1d-2a72-3dfc9063f510.png)
Modal indicating downloading

# What did I want to do?

---

![downloading.gif](https://qiita-image-store.s3.amazonaws.com/0/45822/61e57b11-d0d4-87f2-d097-ddfa4b0979e4.gif)

---

1. Click the button
2. Show the modal indicating downloading
3. Close the modal when it's done

---

# Step.3 is not easy

---

# I found 2 ways

---

- A. Use cookie (set cookie from server side)
    - ref: https://stackoverflow.com/questions/1106377/detect-when-browser-receives-file-download
- B. Use callback
    - I like it.

---

# How do you implement?

---

# I used `blob()` method in JS

```javascript
const downloadZip = () => {
  modal.style.display = "block"
  fetch(document.URL + "download", {
    method: 'POST',
    headers: { "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" },
    body: "company=" + inputCompany.value + "&president=" + inputPresident.value
  })
    .then(response => response.blob())
    .then(blob => {
      var url = window.URL.createObjectURL(blob);
      var a = document.createElement('a');
      a.href = url;
      a.download = "antisocial-capture.zip";
      document.body.appendChild(a);
      a.click();
      a.remove();
      modal.style.display = "none"
    });
}
```

---

# Thank you
Nextcloud のオートタギング機能

#はじめに
前回の記事で、[タグ付け機能](https://qiita.com/mmisu/items/376f1b3dbee819515b26)を紹介しました。今回は、ファイルがアップロードされた時に、自動でタグ付けされるオートタギング機能について紹介します。

#プラグイン
Files automated taggingというプラグインです。有効にします。
![画像111.png](https://qiita-image-store.s3.amazonaws.com/0/308127/5e3b630b-c994-8e33-2e86-7b549eb4d756.png)

#設定方法
管理画面にワークフローという項目があるので選択すると、ファイルの自動タグ付けのルールが設定できます。ルールの内容は、別記事の[Nextcloudのファイルアクセス制限](https://qiita.com/mmisu/items/ae2b8e2f86ea818ec8cf)で紹介した内容と同じです。
![画像118.png](https://qiita-image-store.s3.amazonaws.com/0/308127/5e591c86-f969-08b4-32af-4283a3459059.png)

試しに、シンプルな内容でルールを追加します。「営業」というグループがアップしたファイルがタグ付けされるという内容です。
![画像119.png](https://qiita-image-store.s3.amazonaws.com/0/308127/c3a0822b-cb66-9612-c877-eb5a9ae9bec2.png)

設定後、営業グループのユーザがアップロードしたファイルは、自動でタグ付けされています。
![画像120.png](https://qiita-image-store.s3.amazonaws.com/0/308127/19d296f3-2d4e-37ea-ce67-a85545eb49cc.png)

例えば、こんな設定はいかがでしょうか。「営業」というグループが会社外からスマフォアプリでアップロードしたファイルは、管理者しか閲覧できないタグを自動でつくという内容です。
![画像121.png](https://qiita-image-store.s3.amazonaws.com/0/308127/9dc018b4-6545-4026-9c7c-8f12b4fbfe63.png)

#まとめ
ファイルアクセス制限と同じように、もっと細かいルール付けをすれば、きちんとしたファイル管理ができるようになると思います。私個人では、そんなに知見がないもので、会社で検証したり、お客様の事例が出てくればよいのになって感じですね。

この記事は、[スタイルズ社のNextcloudサイト](https://nextcloud.stylez.co.jp/nextcloudblog)にも公開しております。