OmniAICreator/Qiita-1.07M huggingface dataset:
The Bitter Lesson (AIに関する苦い教訓) を翻訳してみた

##はじめに

今回は、コンピュータサイエンティストの Richard Sutton 氏が2019年3月13日に投稿した「 Bitter Lesson 」という記事がAI界隈で波紋を広げておりますので日本語訳をしてみました。記事の原文は[こちら](http://incompleteideas.net/IncIdeas/BitterLesson.html)を参照ください。Richard Sutton 氏についての詳しい情報は、[こちら](http://incompleteideas.net/)からどうぞ。

##The Bitter Lesson (AIに関する苦い教訓)
70年間に渡る AI 分野の研究で分かったことは統計処理における一般的な手法の活用が最も効果的であるということです。その理由としては大きく2点あります。

1. 計算処理のユニット１つあたりに対する費用が、指数関数的に減少している
2. 以前までは計算処理に使えるリソースに限度があったが近年の技術発達で膨大な計算が可能となった

短期間で明確な成果をあげるために、過去の研究者たちは人間の知識をデータ化し活用する方法を模索しましたが、長期的な観点からすると、統計処理手法の活用を模索する方が重要でした。また人間の知識をデータに変換し活用する方法は、計算を活用する方法を余計に複雑にするため互いに相反するものでした。

以下では、AI 研究者たちがこれまで経験した苦い教訓の中でも、主要なものを紹介していきます。

###Computer Chess (チェス)
1997年、チェスの世界チャンピオンにコンピュータチェスが勝利しました。そのコンピュータには、深層学習をベースとした手法が使用されていました。
この件はチェスという特別な構造に対する人間の理解を活用したメソッドについて探究していた多くのコンピュータチェス研究者たちを、落胆させました。
人間の知識をベースとしたメソッドで人間に勝つことを求めていた彼らからすると、特別なハードウェアとソフトウェアを搭載したコンピュータチェスによる、「総当たり攻撃」探索というメソッドで優勝したという事実は受け入れがたいものでした。

###Computer Go (囲碁)
チェスの約20年後には囲碁においても同様の事件が起きました。当初は人間の知識やゲームの特殊性を活かして探索を減らす研究に、多大な労力が割かれていました。しかし、探索が大規模に効果的な活用をされ始めると初期の努力は無駄なものであったことが証明されました。
他にも重要だったのは、価値関数を学習させるために 自己学習を使用したことでした。自己学習し、そして通常の学習を行うことは、膨大なコンピュータ処理で探索を行うことに匹敵する行為でした。AI 研究において、「探索」と「学習」の2つは膨大な計算を活用するための重要な要素です。
チェスの場合も囲碁の場合も、研究の初期段階では、探索を減らす為に人間の知識を利用することに研究者たちは多大な労力を払っていました。しかし、探索と学習を擁した研究の方がより大きな成果を得る結果となったことで、これまでの労力は無駄となりました。

###Speech Recognition (音声認識)
音声認識の分野は、1970年代に DARPA の支援のもとで初期の研究が行われました。
研究の中には、人間の言語や音素、音道といった特殊な人間の知識をベースとしたメソッドもあれば、HMMs をベースとしたより統計学的で計算処理を行うメソッドもありました。
そして今回も、統計学を利用したメソッドの方が人間の知識をベースとしたメソッドを勝ることとなりました。そして、統計と計算処理が自然言語処理の分野を数十年かけて少しずつ支配していきました。
近年の音声認識の分野における深層学習の台頭は、この一貫した方向性における最新の出来事です。
深層学習メソッドは非常に優れた音声認識システムを生成するために、人間の知識に頼る代わりに、膨大なトレーニングセットから学習し、そして計算処理をします。
チェスや囲碁といったゲームでもそうでしたが、研究者たちは毎回、自分たちの頭の中の考え方といった知識をシステムに組み込もうとします。しかし、膨大な計算処理が可能となり、それを利用するのが一番効果的であることが証明された今は、そのような試みは無駄であることが証明されました。

###Computer Vision
コンピュータビジョンでも似たようなパターンが見られました。初期のメソッドでは、ビジョンを SIFT 特徴の観点から探索、あるいは一般化円筒やエッジの探索と考えていました。しかし、今日ではそれらは全て使われていません。現在の深層学習ニューラルネットワークは、畳み込みの概念と、ある種の不変性というこの2つを使うだけでより良い成果を出しています。

##全体の総括
上記の内容はどれも苦い経験ですが、その経験から完全に学べていないことは、我々が同じ過ちを繰り返していることからも明白です。今後、同じ様な過ちをしない為にも、これらの苦い経験について学ぶ必要があります。

以下に、この苦い経験におけるポイントを歴史的観点からまとめました。

 - AI 研究者たちは、しばしば彼らのエージェントの中に知識を構築しようとする
 - ドメイン知識のデータ化は短期的には有効だが、長期的には研究の停滞や成功が遠のく事態となる
 - 探索と学習による計算処理といった真逆のアプローチにより、成功のきっかけが自然と起こる
 - これらの成功は当初予定していた人間中心的なアプローチではないため、消化不良な苦い体験となる


そして、これまでの苦い経験から学んだことは、以下の2つです。

 1. 可能な計算処理がどれだけ膨大になっても、一般的なメソッドが最も効果的であること
  - そのメソッドの中でも「探索」と「学習」の2つがとても有用である

 2. 実際の人間の脳の構造は、どうしようもないくらい複雑であること
  - 簡単に人間の脳と同じ構造のものを作成しようとするのは止めるべき 
   

##おわりに
以上が　Richard Sutton 氏による「 Bitter Lesson 」の翻訳のまとめとなります。
ビジネス的な成果をあげるという観点ではAIという技術を特殊なものにせずクラウド/APIといった汎用的な技術として扱うことが重要であると私は理解しました。今回の記事以外にも様々な記事が Richard Sutton 氏の HP にあるので、ぜひ見ていただければと思います。
[PHP]IN句のプレースホルダーを配列から作成する

動的なプレースホルダの生成方法ってどう書くんだっけなと毎回ググってる気がする。。

## やりたいこと
あるデータ配列を元にプレースホルダーSQL文を作成したい

## 結果
```
$sql = 'WHERE column IN (' . implode(',', array_fill(0, count($data), '?')) . ')';
```
ヤマハルータ(RTX1200)からログをCentOS６に送ってみた

# はじめに
ヤマハルータ(RTX1200)のログをLinuxに集めてみました。

# 参考
* http://www.rtpro.yamaha.co.jp/RT/FAQ/Syslog/what-is-syslog.html
* http://akira-arets.blogspot.com/2016/03/rtx1200loggingwithrsyslog.html

# 環境
* ルータ
  - ヤマハルータ(RTX1200)
* ログ収集サーバー
  - CentOS6.10

* ログ収集デーモン
  - syslog → rsyslog

# 動作概要
RTX1200からログ収集サーバーにUDP/514でログを送信

```
    ------+-----------------------------+-------
          |                             |
    +-----+------+               +------+------+
    | RTシリーズ |               | コンピュータ|
    +------------+               +-------------+
                      514/UDP
      syslog →syslog メッセージ→ syslogd (syslogデーモン)
        ↑                            ↓
    logしたい情報           syslog.confでの指示によりファイル化などをする
```

# syslogとは
* 役割
  - 各アプリケーションからログを受け取りファイルに書き込む
  - ログを他のサーバーに送信することもできる

# rsyslogとは
* CentOS6ではsyslogの後継で高機能なrsyslogが採用されている。役割は同じ
* rtx1200ではsyslogが採用されている

# CentOSの設定
* 設定ファイル
  - /etc/rsyslog.conf

```
<ファシリティ>.<プライオリティ> 出力先
```

## ファシリティとは
* ログの種類。mail、cron、ntpなど
* local0-local7は自由に使えるファシリティ。rtx1200からのログはこのどれかで定義する

## プライオリティとは
* 優先順位のこと
* emerg
  - 緊急事態
* alert
  - 警報(すぐ対応必要)
* crit
  - 危険
* error
  - エラー
* warning
  - 警告
* notice
  - 注意
* info
  - 情報
* debug
  - 問題に対応するための詳細な情報
* none
  - なし

どのアプリケーションからのどのレベルのログをどこに書き込むか指定する。レベルごとに指定できるのでレベル毎に書き出すファイルを変えることもできる。

# ヤマハルータからログを受け取る設定
## 設定ファイル /etc/rsyslog.conf
udp/514から受け取る設定

```
$ModLoad imudp
$UDPServerRun 514
```

デフォルトだと/var/log/messagesに出力されるので`local5.none`で出力しないようにする

```
*.info;mail.none;authpriv.none;cron.none;local5.none    /var/log/messages
```

local5(ヤマハルータ)からのログを/var/log/rtx1200.logに書き込む

```
# RTX
local5.*                                                /var/log/rtx1200.log
```
あとはiptabltesでudp 514ポートを開放する

# ヤマハルータの設定
administratorになってから
ログの送信先を設定

```
# syslog host IPアドレス
```

ファシリティ(ログ種別)をCentOSに合わせる

```
# syslog facility local5
```

出力したいログのレベルを決定する

```
# syslog notice on
# syslog info on
# syslog debug on
```

## ヤマハルータのログのレベル
* noticeレベルの情報
  - パケットフィルタリングで、落としたパケットの情報などを ログします。

* infoレベルの情報
  - 通常の情報をログします。

* debugレベルの情報
  - 障害解析などの為に、ISDNやPPPのデバッグ情報をログします。

# 補足
webサーバなどのログをwebサーバで保管するのではなくsyslogの仕組みを使って他のサーバで保管、一括管理することでログが改ざんされることを防ぐこともできる。

# まとめ
* 上記設定で特に問題なくログを収集することができました。logrotateの設定を入れたほうが良いかもしれません。今度はログの解析をやってみようと思います。
IE11にてBlueprintのPopoverの背景色がおかしくなる問題の対処


## はじめに

タイトルの通り、Popoverのcontentの背景色がなぜか真っ白になったり・ツラついたりする現象に遭遇したので、対処方法を共有します。

## 原因

IE11とPopoverが展開するcontainerのスタイルの指定が相性が悪いため。
デフォルトでは、containerには `transform: translate3d(xxx, xxx, xxx)` なスタイルが適用されています。

## 対処方法

Popoverコンポーネントのmodifiers propsに以下のようなオブジェクトを渡すことにより、この問題を回避できます。
※IE11の場合のみ、falseにするという条件分岐を入れてもいいかと思います

```javascript

const hoge = {
  computeStyle: {
   gpuAcceleration: false 
  }
}
```

## 参考

https://popper.js.org/popper-documentation.html#modifiers..computeStyle.gpuAcceleration
Spring-Retryでリトライ処理の実装

## はじめに

（DBへの）データ登録の失敗時にリトライ処理を入れたい場合があり、その際に、Spring-Retryでリトライ処理を実装してみましたので、メモを残しておきます。

今回は例として、unique制約のあるカラムにデータをinsertする際に、同じ値があればリトライするというケースです。

## 下準備

### ①build.gradleのdependenciesに以下を追記し、Spring-Retryを追加

```gradle

compile('org.springframework.retry:spring-retry')
compile('org.springframework.boot:spring-boot-starter-aop')
```

### ②利用したい@Configurationなクラスに@EnableRetryを追加

```java
@Configuration
@EnableRetry
public class AppConfig { ... }
```

## リトライ処理

### Retryable

下記のサンプルのように@Retryableをつける事により、UniqueConstraintExceptionが発生した際は（デフォルトで上限3回まで）リトライするようにしています。

```java
@Retryable(value = { UniqueConstraintException.class })
public void retryService() throws UniqueConstraintException {
  this.dao.insert("hash化された文字列が入るよ!");
}

```

また、maxAttemptsを設定し、3回以上リトライすることもできる。
※下記の例では５回リトライしている

```java
@Retryable(value = { UniqueConstraintException.class }, maxAttempts = 5)
```

他にもリトライの間隔をbackoffで指定できたり、複数のExceptionを扱うこともできます。

```java

@Retryable(value = { UniqueConstraintException.class, HogeException.class }, maxAttempts = 5, backoff = @Backoff(delay = 5000))

```

### Recover

指定された回数をリトライ後に、さらにUniqueConstraintExceptionが発生して場合には、@Recoverを追加した関数が呼び出される

```java
@Recover
public void recover(UniqueConstraintException e) {
  // ... do something
}
```


## 参考

https://www.baeldung.com/spring-retry
https://github.com/spring-projects/spring-retry
hamlの基本ルール

プログラミング勉強中です
お気付きの点があれば、ご指導お願いします！


##記述の基本ルール

!!!
DOCTYPE宣言


％
開始、終了タグを示す

例えば
%pは<p></p>と同じ意味

. はクラス
＃はid　を表す

hamlはこんな感じですけど

```haml:hoge.haml
!!!
 %html
  %body
   %p.hoge hello world
   %p#hoge hello world
   %p.hoge.hoge hello world
```
htmlに戻すとこんな感じ

```html:hoge.html
<!DOCTYPE html>
 <html>
  <body>
   <p class="hoge">hello world</p>
   <p id="hoge">hello world</p>
   <p class="hoge hoge">hello world</p>
  </body>
 </html>
```

##属性の付け方



##コメントの書き方
コメントの書き方は２通り

```haml
/　コメントになる
-# コメントになる
```

##改行
１行で表示したい場合

```haml:hoge.haml
   %p hello world
```
```haml:hoge.html
   <p> hello world </p>
```
改行したい場合はインデントを使う

```haml:hoge.haml
   %p 
     %li
```
```html:hoge.html
   <p>
     <li>
     </li>
   </p>
```

#id classの付け方
idはシャープ（＃）、クラスはドット（.)

このhtmlファイルを

```html:hoge.html
<div class="hoge"></hoge>
```

hamlファイルにすると以下のようになる

```haml:hoge.haml
%div.#main.hoge 
```

ちなみに%divは省略可能。なので上と下のコードは同じ意味

```haml:hoge.haml
%div.#main.hoge  -#上下同じ意味
#main.hoge
```

##rubyの式を挿入する


ハイフン（-)で変数の指定が可能
イコール（＝）で変数を表示する


```hoge:hoge.haml
- num =5  /変数の定義
= num　　　/ numが表示される
```

rubyの式を使うこともできます

```haml:hoge.haml
- fruits =['りんご','みかん']
- fruits.each do |fruit|
   %p = "#{fruit}"
```
htmlに変換するとこんな感じ⇩

```html:hoge.html
<p>りんご</p>
<p>みかん</p>
```

#haml⇆htmlのサイト


ウェブ上でhamlとhtmlをできるサイトがある
以下参考⇩

http://htmltohaml.com/
Ruby のパターンマッチを利用して任意のメソッドが定義されているかどうかを判定する

[ブログ記事](http://secret-garden.hatenablog.com/entry/2019/05/05/143449)からの転載です。


と、いうのが bugs.ruby に来ていたので。

* [Feature #15824: respond_to pattern for pattern match - Ruby trunk - Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/15824)

```ruby
class Runner
  def run
  end

  def stop
  end
end

runner = Runner.new
case runner
in .run & .stop
  :reachable
in .start & .stop
  :unreachable
end
```

ほしい気持ちはわかるんだけど流石に上記の提案だと情報が欠落しすぎているのとそもそもパターンマッチでやるべきこと？と思ってしまい個人的にはいまいち。
なんかもっといい感じの構文だといいとは思うんですが…。


と、言うことで既存のパターンマッチで出来ないかやってみました。

```ruby
using Module.new {
  refine Object do
    # パターンマッチ内部では #deconstruct_keys を暗黙的に呼び出してそれで判定を行っている
    # そこで deconstruct_keys を経由してメソッド情報を取得することでパターンマッチで利用できるようにする
    # { メソッド名: 値... } となるような Hash を返す
    def deconstruct_keys(keys)
      keys.select { |name| respond_to?(name) }.to_h { |key| [key, send(key)] }
    end
  end
}

def check(obj)
  case obj
  in { run: _, stop: _ }
    :reachable
  in { start: _, stop: _ }
    :unreachable
  else
    :none
  end
end


class Runner
  def run
  end

  def stop
  end
end

runner = Runner.new
p check(runner)
# => :reachable


class Runner2
  def start
  end

  def stop
  end
end

runner2 = Runner2.new
p check(runner2)
# => :reachable
```

Ruby のパターンマッチでは暗黙的に `#deconstruct_keys` （や `#deconstruct`）が呼び出され、その戻り値を参照してパターンマッチを評価します。  
上記の実装では `{ メソッド名: 値... }` というような Hash をパターンマッチで使用できるようにすることで

```ruby
  case obj
  in { run: _, stop: _ }
    :reachable
  in { start: _, stop: _ }
    :unreachable
  else
    :none
  end
```

というようなパターンマッチをかけるようにしています。  
これならメソッドが定義されているかどうかを判定することも出来ますし『任意のメソッドの値』をキャプチャすることも出来ます。  

```ruby
  # obj.run の値をキャプチャする
  case obj
  in { run: status, stop: _ }
    p "status is #{status}"
    :reachable
  in { start: status, stop: _ }
    p "status is #{status}"
    :unreachable
  else
    :none
  end
```

これ、かなり汎用性が高そうなので普通にほしい。ってか、すでに機能としてありそう。


## 参照

* [Pattern matching - New feature in Ruby 2.7 - Speaker Deck](https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7)
【Scala】抽象型で型判定時、型消去に影響されない方法

抽象型を使ったmatch式で以下のように書くと、

```scala
class ClassA
class ClassB extends ClassA

def example1[A <: ClassA](value: ClassA): Unit = {
  value match {
    case _: A => println("match")
    case _ => println("unmatch")
  }
}
```

`[warn] abstract type pattern A is unchecked since it is eliminated by erasure
(抽象型Aは型消去によって削除されるため、チェックされていません。)`

のようにコンパイル時にwarningが出る。

ifを使った場合も同様。

```scala
def example2[A <: ClassA](value: ClassA): Unit = {
  if(value.isInstanceOf[A])  println("match")
  else println("unmatch")
} // => [warn] abstract type A is unchecked since it is eliminated by erasure
```

## 型消去後のコード
コンパイラにより型消去された後、抽象型が上限境界の型(ここではClassA)に置き換えられるらしい。

```scala
def example1(value: ClassA): Unit = {
  value match {
    case _: ClassA => println("match")
    case _ => println("unmatch")
  }
}

def example2(value: ClassA): Unit = {
  if(value.isInstanceOf[ClassA])  println("match")
  else println("unmatch")
}
```

## 対策
scala.reflect.ClassTagを使う。
ClassTag.runtimeClassで実クラスが取得できる。
match式も内部的には同様？

```scala
def example1[A <: ClassA](value: ClassA)(implicit a: scala.reflect.ClassTag[A]): Unit = {
  value match {
    case _: A => println("match")
    case _ => println("unmatch")
  }
}

def example2[A <: ClassA](value: ClassA)(implicit a: scala.reflect.ClassTag[A]): Unit = {
  if(a.runtimeClass.isInstance(value))  println("match")
  else println("unmatch")
}
```

context boundを使うと以下のように簡略化して書ける。

```scala
def example1[A <: ClassA : scala.reflect.ClassTag](value: ClassA): Unit = {
  value match {
    case _: A => println("match")
    case _ => println("unmatch")
  }
}

def example2[A <: ClassA : scala.reflect.ClassTag](value: ClassA): Unit = {
  if (implicitly[ClassTag[A]].runtimeClass.isInstance(value)) println("match")
  else println("unmatch")
}
```
Yahoo注文検索APIをhttpieでたたく方法


## 本稿で学べること

* [Yahoo!注文検索API](https://developer.yahoo.co.jp/webapi/shopping/orderList.html)をhttpieでたたく方法と、そのために最低限必要なオプションの指定。

## 前提条件

* [httpie](https://httpie.org/)がインストール済み。
* アクセストークンが取得済み。
* ストアアカウントのIDが分かる。

## 

```bash
#!/bin/bash

set -eu

ACCESS_TOKEN="ここにアクセストークンを書く"

URL="https://test.circus.shopping.yahooapis.jp/ShoppingWebService/V1/orderList"
AUTH_HEADER="Authorization: Bearer ${ACCESS_TOKEN}"

echo -n '<?xml version="1.0" encoding="UTF-8" ?>
<Req>
　<Search>
　  <Condition>
　　  <OrderTimeFrom>20190101000000</OrderTimeFrom>
　  </Condition>
　  <Field>OrderId,Version</Field>
　</Search>
　<SellerId>ここにストアアカウントIDを書く</SellerId>
</Req>' | http -v POST ${URL} 'Content-Type: application/xml' "${AUTH_HEADER}"
```

## 実行結果の例

```http
POST /ShoppingWebService/V1/orderList HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Authorization: Bearer [[...中略...]]
Connection: keep-alive
Content-Length: 241
Content-Type: application/xml
Host: test.circus.shopping.yahooapis.jp
User-Agent: HTTPie/1.0.2

<?xml version="1.0" encoding="UTF-8" ?>
<Req>
　<Search>
　  <Condition>
　　  <OrderTimeFrom>20190101000000</OrderTimeFrom>
　  </Condition>
　  <Field>OrderId,Version</Field>
　</Search>
　<SellerId>***-*******</SellerId>
</Req>
```

```http
HTTP/1.1 200 OK
Age: 0
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 181
Content-Type: application/xml; charset=UTF-8
Date: Tue, 07 May 2019 09:14:02 GMT
P3P: policyref="http://privacy.yahoo.co.jp/w3c/p3p_jp.xml", CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"
Vary: Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block

<?xml version="1.0" encoding="UTF-8" ?><Result><Status>OK</Status><Search><TotalCount>2</TotalCount><OrderInfo><OrderId>snbx-******-10000001</OrderId><Version>1</Version><Index>1</Index></OrderInfo><OrderInfo><OrderId>snbx-******-10000002</OrderId><Version>1</Version><Index>2</Index></OrderInfo></Search></Result>
```

## 関連

* [yahoo-id.sh: Yahoo APIのアクセストークンを取得する開発ツール - Qiita](https://qiita.com/suin/items/a40bb903604e9f3215f9)
* [Yahoo! ID連携(v1)でアクセストークンを取得する手順をHTTPだけで説明する - Qiita](https://qiita.com/suin/items/a47ab0412c46e32537bf)
GCPのApp Engineでインスタンスにsshするワンライナー

SERVICEに好きな値を入れて使ってください。
RUNNINGな最新バージョンのいずれかのインスタンスにsshできます。

```bash
SERVICE=api-production && INSTANCE_ID=$(gcloud app instances list --service=$SERVICE --format="table(id)" --filter="VM_STATUS=RUNNING" --sort-by=VERSION | tail -n 1) && gcloud app instances enable-debug $INSTANCE_ID && gcloud app instances ssh $INSTANCE_ID --version=$(echo $INSTANCE_ID | sed -e 's/^[^0-9]*//g' -e 's/-.*//g') --service=$SERVICE
```

## 解説

以下のコマンドでインスタンスIDを取得します。
serviceでの絞り込み、formatでの出力形式指定、filter機能、sort-by機能を使っています。

```bash
gcloud app instances list --service=$SERVICE --format="table(id)" --filter="VM_STATUS=RUNNING" --sort-by=VERSION
```

以下のコマンドでインスタンスのデバッグ機能を有効にします。
デバッグ機能が有効でないとsshできないみたいです。

```bash
gcloud app instances enable-debug $INSTANCE_ID
```

以下のコマンドでインスタンスにsshします。
冗長ですが、versionとserviceを指定しないと、sshできませんでした。

```bash
gcloud app instances ssh $INSTANCE_ID --version=$(echo $INSTANCE_ID | sed -e 's/^[^0-9]*//g' -e 's/-.*//g') --service=$SERVICE
```
【git編】いい感じのbashrcファイルができたから見てほしい！

gitを使うという面で地味に使いやすくなるbashrcファイルができました。

bashrcは、bashの設定ファイルです。
これをいじれば生産性がやや上がります！

# みてほしい！

```.bashrc

# 日本語対応
export LANG='ja_JP.UTF-8'

# lsの結果を色付きで表示してくれる
alias ls="ls --color=auto"
# ○○するときに、本当に○○していいか聞いてくれる系のエイリアス
alias rm='rm -i'
alias mv='mv -i'
alias cp='cp -i'


# .. だけで親ディレクトリにいける！
alias ..='cd ..'

# laで隠しファイルも含めて全部表示してくれる
alias la="ls -a"

# git系のエイリアス
alias gb="git branch"
alias gco='git checkout'
alias gcm='git checkout master'
alias gl='git pull'
alias gc='git commit'
alias gp='git push'
alias gst='git status'
alias ga='git add'

# 新しいブランチをあけてpushするときに「--set-stream」みたいなのが面倒だったので、gp2でgit pushできるようにした
alias gp2=gp_set

function gp_set(){
	branch_name=$(git symbolic-ref --short HEAD)
	git push --set-upstream origin $branch_name

}


# 仕事頑張れるエイリアス
alias nemui="echo 'うぇ～～～～い'"
alias tukareta="echo 'ほんまそれな～～'"
alias gbz="echo 'がんばるぞ～～！！！'" # gbzはがんばるぞの略
```


# 特におすすめした地味にうれしいエイリアス

```
# 新しいブランチをあけてpushするときに「--set-stream」みたいなのが面倒だったので、gp2でgit pushできるようにした
alias gp2=gp_set

function gp_set(){
	branch_name=$(git symbolic-ref --short HEAD)
	git push --set-upstream origin $branch_name

}
```

`gp2`って名前適当すぎるけど、それはまあよしとして、いつ使うかを説明したい！！

新しいブランチを開けて、そこでpushしようとするとこうなりますよね。

```
$ git push
fatal: The current branch test3 has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin test3
```

まじかーって思いながら`git push --set-upstream origin test3`をコピーして、貼り付けて、Enterを押すんですけど、ちょっと面倒くさい。

というわけで、

```
$ gp2
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 224 bytes | 224.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'test3' on GitHub by visiting:
remote:      https://github.com/*******/********/pull/new/test3
remote:
To github.com:*********/git-study.git
 * [new branch]      test3 -> test3
Branch 'test3' set up to track remote branch 'test3' from 'origin'.
```

はい！完璧！
自動でブランチ名を取得して、コマンドを実行してくれます。

というわけで地味に便利なgitのエイリアスでした。
カスタムコントローラのテストクラス入門

visualforceページのコントローラを手作りしたときのテストクラスの作り方です。
基本形。

```
//テスト対象のVisualforceページを生成
PageReference pageRef = Page.[ページ名];
//生成したテスト対象ページをカレントページに設定
Test.setCurrentPage(pageRef);
//パラメータが必要ならセット
ApexPages.currentPage().getParameters().put('[パラメータ名]', '1000001');

Test.startTest();
//コンストラクタだけで全メソッドを動かせるなら生成するだけでよい
[コントローラ名] controller = new [コントローラ名]();
//検索か何かでセッターに値をセットしたいならこんな風に
controller.[バインド変数名] = '入力値など';
Test.stopTest();
```
WSL（Ubuntu18.04）にDockerを使わずCodiMDを入れる

## はじめに
WSL（Ubuntu 18.04）にDockerを利用せずにCodiMDを立ててみましたので、メモを( ..)φ

## nodejsとnpmのインストール

```bash
$ sudo apt install nodejs
$ sudo apt install npm
```

## yarnをインストール
yarnはパッケージマネージャーで、npm同様、こちらもインストールします。

```bash
$ cd ~
$ npm install -g yarn
$ yarn install
```

## PostgreSQLにDBを作成
※PostgreSQLのインストールと初期設定は省略します。
PostgreSQLに利用するDBを作成します。「new_role」、「password」、「codimd」は任意で変更します。

```terminal
$ psql -U postgres
postgres=# CREATE ROLE new_role WITH LOGIN PASSWORD 'password';
postgres=# create database codimd owner new_role encoding 'UTF8';
postgres=# \q
```

## Firewallのポートを開放
「3000」を開放します。自分の環境では、「ufw」で開放しようとしたら以下のようなエラーが出たため、firewalld-cmdを利用しました。

■失敗例

```terminal
$ sudo ufw allow 3000
ERROR: problem running iptables: iptables v1.6.1: can't initialize iptables table `filter': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.
$ sudo ufw reload
ERROR: problem running iptables: iptables v1.6.1: can't initialize iptables table `filter': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.
$ sudo ufw status
ERROR: problem running iptables: iptables v1.6.1: can't initialize iptables table `filter': Table does not exist (do you need to insmod?)
Perhaps iptables or your kernel needs to be upgraded.
```

■成功例

```bash
$ sudo firewall-cmd --zone=public --add-port=3000/tcp  --permanent
```

## CodiMDの導入
※本当は「/opt」へ配置するのが正解らしいですが、（勉強不足だったので）自分は「/usr/local」に配置しました。

GitHubからCodiMDを取得します。

```bash
$ cd /usr/local
$ git clone https://github.com/hackmdio/codimd.git
```
セットアップ用のスクリプトを実行します。

```bash
$ cd codimd
$ bin/setup
```

これから編集するので、設定ファイル（config.json）のバックアップを作成します。

```bash
$ cp config.json config.json.org
```

設定ファイル（config.json）の「db」部分を編集します。
※今回は「development」を変更します。
※設定値は「PostgreSQLにDBを作成」で入力した値（ロール、パスワード、DB名）を利用します。また、「host」や「port」は各自の環境に合わせて変更してください。

```bash
$ vi config.json
 1|  "development": {
 2|          "loglevel": "debug",
 3|          "hsts": {
 4|              "enable": false
 5|          },
 6|          "db": {
 7|             "username": "new_role",
 8|             "password": "password",
 9|             "database": "codimd",
10|             "host": "localhost",
11|             "port": "5432",
12|             "dialect": "postgres"
13|          }
14|      },
```

DB設定ファイル（.sequelizerc）についてもバックアップを取り、「url」部分を変更します。

```bash
$ cp .sequelizerc .sequelizerc.org
$ vi .sequelizerc
 1| 'url':'postgres://new_role:password@localhost:5432/codimd'
```

DBのmigrationを実行します。

```bash
$ node_modules/.bin/sequelize db:migrate
```
npmでbuildを実行します。

```bash
$ npm run build
```

## 実行と確認
「development」の設定しか行っていませんので「development」しか指定できませんが、まずは以下のコマンドを実行します。

```bash
$ NODE_ENV='development' node app.js &
```

アクセスするとログがばんばん出てきたので、以下を実行しました。

```bash
$ NODE_ENV='development' node app.js > /dev/null 2>&1 &
```

Webブラウザで「 http://localhost:3000/ 」にアクセスするとCodiMDのサイトが確認できます。

## 最後に
HackMDを利用したときにすでに分かっていたことですが、CodiMD便利ですね。
共有しながら皆でつっつけるのが非常に効率的です。
react nativeでstorybook動かしたい

# storybookってなんぞや？
storybook([公式サイト](https://storybook.js.org/))とはコンポーネント毎のスタイルガイドを作ることができるツールの一つです。
コンポーネントのカタログ集みたいなイメージです。
デザイナーと開発者の認識のズレをなくすためのツールとして使うことが多いようです。

# 開発環境
react native cli ... 2.0.1
react native ... 0.59.4
@storybook/react-native ... 4.1.18
react-native-storybook-leader ... 1.8.0

# 早速実装してみる
まずはnpm install

```console:ターミナル
$ npm install --save-dev @storybook/react-native
```

通常通りコンポーネントを作成する。

```react:/src/compoennts/atoms/button.js
import React from 'react'
import PropTypes from 'prop-types'
import { TouchableHighlight } from 'react-native'

export default function Button({ onPress, children }) {
  return <TouchableHighlight onPress={onPress}>{children}</TouchableHighlight>
}

Button.defaultProps = {
  children: null,
  onPress: () => {}
}

Button.propTypes = {
  children: PropTypes.node,
  onPress: PropTypes.func
}
```

作成したコンポーネントがあるディレクトリに`index.stories.js`があることを確認する。
`index.stories.js`を用意し作成したコンポーネントをimportする。


```react:/src/components/atoms/index.stories.js
import React from 'react'
import { Text } from 'react-native'
import { storiesOf } from '@storybook/react-native'
import Button from './Button'

storiesOf('Button', module)
  .add('with text', () => (
    <Button onPress={() => {}}>
      <Text>Hello Button</Text>
    </Button>
  ))
```

今後は複数のディレクトリを参照していきたいので[react-native-storybook-loader](https://github.com/elderfo/react-native-storybook-loader)を導入する。

```console:ターミナル
$ npm install --save-dev react-native-storybook-loader
```
`react-native-storybook-loader`は複数のディレクトリをstorybookにしたい時に便利なパッケージです。
設定を`package.json`のconfigに書きます。

```json-doc:package.json
{
    ...
    "scripts": {
        ...
        "prestorybook": "rnstl",
        "storybook": "storybook start"
    },
    "config": {
        "react-native-storybook-loader": {
            "searchDir": [     // 検索対象のディレクトリ
                "./src"        // 今回はsrcの中を検索する
            ],
            "pattern": "**/*.stories.js", 　// どういう形のファイルが対象なのか
            "outputFile": "./storybook/storyLoader.js" // アウトプット先のファイル
        }
    },
    ...
}
```

storybookを起動する。

```console:ターミナル
$ npm run storybook
```

http://localhost:7007 にアクセス
**別ターミナル** (storybookを終了しない)でreact nativeを起動する

```console:別ターミナル
$ react-native run-ios
```

![](https://i.imgur.com/K9Pcrxb.png)
↑simulator画面

![](https://i.imgur.com/Y1ICiJI.png)
↑ブラウザ(localhost:7007)画面

# まとめ
これでstorybookが開発を妨げることが少なくなりました。
今後もどんどん開発しやすくしていきたいです!
Cocoapodsのライブラリ開発における注意点

# ライブラリのソースコードを変更してもデバッグ時に反映されない問題

## 開発環境
- Xcode Version 10.2 (10E125)
- Cocoapods Version 1.6.1

Cocoapodsのライブラリを開発している際、Demoプロジェクトを開発しながらもライブラリのソースをいじりたくなるタイミングがあると思います。
`Pods/Development Pods/LibraryName`ディレクトリの中にソースがあるので、そのまま変更して問題ないのですが、ソースコードを変更しても実行した際に反映されないと言う問題が起きていました。
その問題を解消した方法を記載します。
Cleanすると反映されるようになりますが、根本的な解決策ではありませんよね。

## 解決方法
1. `Menu bar > File > Workspace settings ...` を選択。
2. `Build System` を `New Build System(default)`から `Legacy Build System`に変更する。

これらの手順を踏むことで、ライブラリのソースコードが反映されるようになります。

めでたしめでたし！

教えていただいた @shoheiyokoyama さんありがとうございました🙇‍♂️
prepared statementからsyntax errorが出て驚いた話

##前書
prepared statementなのに値によってsyntax errorが出て混乱したのでメモ

##現象
全く関係ない二列に同じ値を設定するとDuplicate column nameが発生する

```SQL
-- 実行出来る
select
    'abc'
    , 'abc';

-- 実行出来る
select
    *
from
    (
        select
            'abc'
            , 'xyz'
    ) as temp;

-- 実行出来ない
select
    *
from
    (
        select
            'abc'
            , 'abc'
    ) as temp;
```
※リテラル部分は実際にはprepared statementから指定して実行した

##対処法
~~ASを使用し列名を設定する~~
dual表を使用する

```SQL
-- 実行出来る
select
    *
from
    (
        select
            'abc' as c1
            , 'abc' as c2
    ) as temp;

-- 多分これが一番正しい
select
    'abc'
    , 'abc'
from
    dual;

-- 次点
select
    'abc'
    , 'abc'
from
    (
        select
            1
    ) as temp;
    
```
※リテラル部分は実際にはprepared statementから指定して実行した

##因みに
なんでこんな構成が出現したのかというと、insert into valuesにwhere条件を付けたくてinsert into selectに変換
その際from句が必要になったため
MySQLにdualがある(互換性のためらしい)ことを知っていればこんなことには
Railsのdeviseで新規登録するとき、親のモデル（belongs_toなもの）も一緒に作る方法

# 環境
- Rails 5.2.2
- Ruby 2.5.3

# 前提
- User belongs_to Organization
- Organization has_many Users
- Userを新規登録するとき、Organizationも一緒に作りたい（というか作れないとエラーで登録できない）

# コード

```ruby
class ApplicationController < ActionController::Base
  ...
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [organization_attributes: [:name]])
  end
end
```

```erb
# app/views/devise/registrations/new.html.erb
...
<% resource.organization ||= Organization.new %>
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :email,
                required: true,
                autofocus: true ,
                input_html: { autocomplete: "email" }%>
    <%= f.input :password,
                required: true,
                hint: ("#{@minimum_password_length} characters minimum" if @minimum_password_length),
                input_html: { autocomplete: "new-password" } %>
    <%= f.input :password_confirmation,
                required: true,
                input_html: { autocomplete: "new-password" } %>

    <%= f.fields_for :organization do |organization_form| %>
      <%= organization_form.input :name %>
    <% end %>
  </div>

  <div class="form-actions">
    <%= f.button :submit, t("devise.sign_up") %>
  </div>
<% end %>
...
```

# 参考
https://github.com/plataformatec/devise#strong-parameters
https://stackoverflow.com/a/7987480/7824640
【iOS】オープンソースのOCRライブラリ調査

## はじめに

iOSで利用可能なオープンソースのOCRライブラリを調査してみました。

## 前提条件

下記条件で調査しました。

- 無料であること
- オフラインで利用できること
- サーバサイドやクラウドサービスを介さず利用できること
- 手書き文字は考慮しないこと
- 日本語の認識が可能であること
- 日本語学習データの作成 or チューニングが可能なこと

## 調査結果

以下の3つのライブラリをピックアップしてみました。

||[Tesseract OCR iOS](https://github.com/gali8/Tesseract-OCR-iOS)|[SwiftOCR](https://github.com/garnele007/SwiftOCR)|[SwiftyTesseract](https://github.com/SwiftyTesseract/SwiftyTesseract)|
|:--|:-:|:-:|:-:|
|OCRエンジン|パターン認識|ニューラルネットワーク|LSTMニューラルネットワーク|
|日本語学習データの提供|有|無|有|
|ライセンス|MIT license|Apache License,<br>Version 2.0|MIT License|
|依存ライブラリ|Tesseract 3<br>Leptonica<br>libpng<br>libjpeg<br>libtiff|Swift-AI<br>GPUImage<br>Union-Find|Tesseract 4<br>Leptonica<br>libpng<br>libjpeg<br>libtiff|

### Tesseract OCR iOS

OCRエンジンにGoogleが開発を進めているTesseractを採用しているだけあって、情報量が多く人気もあるライブラリです。ただし、Tesseract本体、及び、対応する学習データのバージョンが若干古いため、日本語の認識精度に関しては疑問符が付きます。現在進行系でTesseract最新バージョンの実装を進めている（？）模様。

### SwiftOCR

Tesseractよりパフォーマンスに優れる点をアピールしているライブラリです。ただし、短い英数字コードの認識が得意らしく、誌や文を認識したい場合はTesseractが良いと説明しています。日本語を認識するためには学習データを作成する必要があり、トレーニングツールが提供されているとは言え、十分な精度を望むのであれば導入に相応の時間を要しそうです。

### SwiftyTesseract

Tesseract OCR iOSに同じく、TesseractをOCRエンジンとして採用しているライブラリです。Tesseract OCR iOSとの違いは最新安定バージョンのTesseractを採用している点で、パフォーマンスと認識精度が大きく向上しています。ただし、Tesseract本体のAPIを完全にはサポートしておらず、利用できない機能があるので注意が必要です。

## まとめ

ここ数年で見聞きする機会が多くなった「ディープラーニング」によって、OCRライブラリの文字認識精度が飛躍的に向上している印象を受けました。
調査したライブラリの中では、SwiftyTesseractが最も手軽かつ実用向きではないかと考えます。
他にも有用なライブラリをご存知の方がいらっしゃいましたら、ぜひご教示ください。

## おまけ

クラウド可かつ課金可まで条件を緩めると、[Firebase向けML Kitのテキスト認識（OCR）](https://firebase.google.com/docs/ml-kit/recognize-text)一択な気がします。
【Xcode】GitHubにpushした瞬間にTabの幅が変わってしまう

先輩エンジニアにコードレビューで
「インデントの乱れは心の乱れ！！！！ :anger: 」
と叱られて
「あれっ！？！？Xcode上だと何もおかしくないのに！？！？」
と混乱してしまったそこのあなたのためにこの記事を残します。

## tl;dr
**tabの幅がおかしい時は `XCode -> Preferences... -> Text Editing -> Indentation`の設定を弄れ**


## 症状
**Xcode上では何も違和感なかった(むしろ `control+i`コマンドでインデント調整をしている)コードをGitHubリモート上にpushした途端インデント幅が変わる**
(macOS Mojave: 10.14.4, XCode: 10.2.1)

- ローカル(Xcode)上
<img width="156" alt="スクリーンショット 2019-05-07 17.24.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/8d5215b2-6a18-bf68-9c13-cbf0305f5a0e.png">
（よしよし、インデント調整もしたしpushしよう）

- リモート(GitHub)上
![スクリーンショット 2019-05-07 17.25.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/7896c36e-1467-1eaa-252f-a1a92dcae92c.png)
(？ :thinking: )
(リモートだとTab幅が2文字分深くなってしまいました)

## 解決法
`XCode -> Preferences... -> Text Editing -> Indentation`内の
`Prefer indent using`を
`Tabs`-> `Spaces`に変更。これだけ。
![スクリーンショット 2019-05-07 17.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/203273/407b0984-2931-0a2b-4765-ff90090c7394.png)
TabsによるインデントだとXcode上の見た目が何文字でも6文字に固定されてしまう？ことを知りませんでした
もちろん、シンプルに `Tab width`や`Indent width`が想定のものと違っている可能性もあるので要確認です

## まとめ
設定できる項目と設定の影響範囲はちゃんと覚えようね

## おまけ
人のコードを見る時にインデント幅を変えたい場合はURLに `?ts=4`とかしてあげましょう
Source: https://github.com/tiimgreen/github-cheat-sheet#adjust-tab-space
wp-api.jsでWordPressの記事情報を取得する

WordPressにはWP APIを呼び出すためのライブラリがデフォルトで同梱されています。
## 読み込みを有効化する
デフォルトでは読み込みされません。
明示的にロードするようにしてやる必要があります。

```php
wp_enqueue_script( 'wp-api' );
```

これで`window.wp`から利用できるようになります。

## 投稿を取得する

wp-api.jsはBackbone.jsを利用していますので、それっぽい書き方になるみたいです。
使ったことがないので実際どうなのか判断できませんが、書き方はそれっぽいのかなとおもっています。

```js
const post = new window.wp.api.models.Post( { id: 1 } )
post.fetch()
console.log( post.attributes )

{
  "id": 1,
  "date": "2019-04-26T12:11:27",
  "date_gmt": "2019-04-26T03:11:27",
  "guid": {
    "rendered": "https://127.0.0.1:8443/?p=1"
  },
  "modified": "2019-04-26T12:11:27",
  "modified_gmt": "2019-04-26T03:11:27",
  "slug": "hello-world",
  "status": "publish",
  "type": "post",
  "link": "https://127.0.0.1:8443/2019/04/26/hello-world/",
  "title": {
    "rendered": "Hello world!"
  },
  "content": {
    "rendered": "\n<p>WordPress へようこそ。こちらは最初の投稿です。編集または削除し、コンテンツ作成を始めてください。</p>\n",
    "protected": false
  },
  "excerpt": {
    "rendered": "<p>WordPress へようこそ。こちらは最初の投稿です。編集または削除し、コンテンツ作成を始めてください。</p>\n",
    "protected": false
  },
  "author": 1,
  "featured_media": 0,
  "comment_status": "open",
  "ping_status": "open",
  "sticky": false,
  "template": "",
  "format": "standard",
  "meta": [],
  "categories": [
    1
  ],
  "tags": [],
  "_links": {
    "self": [
      {
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/posts/1"
      }
    ],
    "collection": [
      {
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/posts"
      }
    ],
    "about": [
      {
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/types/post"
      }
    ],
    "author": [
      {
        "embeddable": true,
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/users/1"
      }
    ],
    "replies": [
      {
        "embeddable": true,
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/comments?post=1"
      }
    ],
    "version-history": [
      {
        "count": 0,
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/posts/1/revisions"
      }
    ],
    "wp:attachment": [
      {
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/media?parent=1"
      }
    ],
    "wp:term": [
      {
        "taxonomy": "category",
        "embeddable": true,
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/categories?post=1"
      },
      {
        "taxonomy": "post_tag",
        "embeddable": true,
        "href": "https://127.0.0.1:8443/wp-json/wp/v2/tags?post=1"
      }
    ],
    "curies": [
      {
        "name": "wp",
        "href": "https://api.w.org/{rel}",
        "templated": true
      }
    ]
  }
}

```
元新人が感じたRPA開発での敵

この記事は、

- 1年で20業務のRPA化を行ってきた程度の新人が
- 頭の固い旧時代の上長に
- 「言ってやりたかった！」というような文句をまとめた（まとまっていない）記事です。

ウン番煎じです。
UiPathでの技術的な話は [新人がUiPathでのRPA開発を1年間経験して編み出した開発ナレッジ](https://qiita.com/allein-s/items/7e0d3756289f6966c554) をどうぞ

#敵の名は
誤解
オーバーヘッド
標準

#誤解 - RPAでないとできないことはほとんどない
**RPAは比較的早くスクリプトを作る手段である**

誤解されがちで、いろんな人が誤解を解こうとしてまだいまいち浸透していない事実ですが、
RPAで実現するのは「今までできなかったこと」ではなく、「べつにやればできたけどコスパ悪くてやらなかったこと」です。

RPA以外でやると遅い！高い！うまい！
となるものを
RPAでやることにって早い！安い！普通！
となってトータルで考えるとRPAのがいいかな？という考え方です。
（デジタルレイバーとか擬人化してるからわかりにくいんだと思うんですよね。。。）

お客さんにとっては、工数を抑えられるので安く済むことが魅力なので、
開発を請け負う側としては、従来通りの開発工数分のお金を請求して儲ける、人月ビジネスモデルだと先細り待ったなしです。
**私のチームは先細りました。**

#オーバーヘッド - RPA開発は要件定義から開発までは一人でやった方がいい
**開発工数に対して、伝言ゲームに割く時間の割合を小さくするため。**

上記より、開発が早いことがRPAにとっての美徳であることを示しましたが、
RPA開発の敵＝早さの敵＝オーバーヘッド だと考えております。
私の職場では、私が新人だったということもあり、要件定義のみRPA開発経験のない上長が行い、
自分は簡単な口頭説明と要件書だけもらって開発するというスタイルでした。
RPA導入しようとする企業ならほとんどありえないのでしょうが、以下のような問題があります。

- 要件がある程度固まってから、RPAで実現できるかという話が来るので、「できないよ」ってなったときの無駄が巨大
- RPA開発経験のない営業寄りの人が切った要件なのでQAの余地が広大無辺
- RPAでは開発を開始してからでないとわからないことが多い→一括でQAを行うことが難しい→QA回数の増加
- QAの度上長を経由する為、「量」×「頻度」×「上長への説明時間」で老化する
- 開発より案件の掘出しと要件切る方が時間がかかるので、開発しかしない人の姿はまるで社内ニート
- この作業無駄じゃね？RPA化の前に業務フロー改善したら？と開発者が思っても現場の人に提案するタイミングがない
- 経由地点が増えると比例して増える報告/管理/説明Excelファイル
- これらを経て作り出されたRPAが現場の「なんか違うんだよな」でお蔵にシューして超エキサイティング

現場の人間と開発者が机を並べることができたら最低でも上記の問題はクリアーされるはずです！
（新たな問題が発生するのだとは思いますが・・・）

#標準 - RPAにとって標準化はあまり意味がない（最低限でいい）
**標準という形で無限に近いRPA実行環境の差異を一般化することは難しいため。**

RPAはロジック部分よりも例外への対処に工数がかかるシステムです。
環境からくる例外、通信状態からくる例外、連携先ウェブサイトのレイアウト変更からくる例外・・・
などなど、開発に完璧に漏れがなくても、よその影響を受けて例外を出すRPAは、
まるで教育に問題がなくても、思春期に悪い友達を作ってグレてしまう息子のようです。（？）

つまりがちがちにコーディング標準を作り上げるのではなく、必要最低限にとどめることを意識するべきであり、
それよりもベストプラクティスを共通部品に詰め込んで共有する方が、多くの状況に対応でき、
標準へ対応するための工数の間延びを避けることにつながるでしょう。
その代わりインシデントを含めたナレッジの共有は密にするべきです。

#もしかして新人にはRPAは向いていないのでは・・・？
**向いていないと思います！**

現場の状況やある程度の専門知識を理解してあげないと、言われたまま作るようになってしまいます！（ました！）
（でも猛者にRPA振ったら開発者も多分安定しなさすぎて嫌がられるだろうし、人件費も高くつくので、結局新人に降りてくるのだと思います。
あとコンサルがプログラミングレスって言ってるのを上長が真に受ｋ）

#おわりに
**駄文にここまでお付き合いいただき、ありがとうございます。**

半分ぐらい愚痴のようになってしまいましたが、こんなことを思いながら1年間を過ごしてまいりました。
できるだけ融通を利かせるべきというのは、1開発者のわがままに聞こえるでしょうし、都合のいいことを言っている自覚もありますが、
私が使っていたUiPathというRPAツールでは、
[UiPath Go](https://go.uipath.com/) という共通部品のマーケットプレイスでユーザー間のベストプラクティスの共有を推進しております。
が、私のいた職場では「信用できん！使わんでおけ！」と聞かん坊で、車輪を再開発する羽目になったりもしました。
企業として、~~どこの馬とも知れない~~どのような方が作ったかわからないものは使えない、というような姿勢もわかるのですが、
web系のjavascriptとかは普通に個人で作ったライブラリ使ってるし、外国産の割と攻めたツール使っておきながら、
なんで考え方だけ日本式を維持して守りに回るのよとかも言いたかったかもしれません。
とりあえず現場へのQA戻り待ち伝言ゲームモラトリアムをいい感じに消化できたのでここまでにしておきます。
重ねて、ありがとうございました。
【BluePrism】キューデータの詳細を確認する

キューデータにはコレクションデータをセットすることができます。
ただ、BPのコントロールルームではそのコレクションの中身まで確認することはできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186221/dfe76ec4-3ef3-eca0-b200-9948cb17fe72.png)

過去のプロジェクトで、キューデータのエビデンスを残す必要があったので、
直接DBのテーブルを参照し、レコードをExcelに貼ってました。

キューデータが格納されているのは、**BPAWorkQueueItem**というテーブルです。
**BPAWorkQueue**テーブルの**id**と
**BPAWorkQueueItem**テーブルの**queueid**でJoinして
どのキューのデータなのかが分かる形にしても良いでしょう。

コレクションデータが格納されているのは、**BPAWorkQueueItem**テーブルの**data**カラムです。
以下のように格納されているのが分かります。

```xml
<collection>
  <row>
    <field name="Path" type="text" value="C:\Users\XXXX\XXXX\XXXX\XXXX" />
    <field name="Folder" type="text" value="C:\Users\XXXX\XXXX\XXXX\XXXX" />
    <field name="Name" type="text" value="XXXX" />
    <field name="Extension" type="text" value=".xlsm" />
    <field name="Created" type="datetime" value="2019-04-10 04:48:52Z" />
    <field name="Last Accessed" type="datetime" value="2019-04-17 15:05:12Z" />
    <field name="Last Written" type="datetime" value="2019-04-17 15:05:12Z" />
    <field name="Read Only" type="flag" value="False" />
    <field name="Bytes" type="number" value="XXXX" />
  </row>
</collection>
```

ちなみにデータ整形はよく↓を使ってます。
※個人情報等が含まれるデータの場合は別ツールで整形してます。
[HTML整形ツール（プチ・デバッグ機能付き） - Pika's Home](http://u670.com/pikamap/htmlseikei.php)
[linux] SELinuxの状態 [CentOS7]

#**SELinuxの状態**

enforcing   ... SELinuxは有効で、アクセス制限もあり。　  

permissive  ... SELinuxは有効だが、アクセス制限はなく、警告を行う。

disable     ... SELinuxは無効。



#確認方法

vi /etc/selinux/config　を端末にて実行。

SELINUX=[上の3つのどれか状態]

[上の3つのどれか状態]を書き換えて保存し、「:w」
エディターから出て、 「：q」
再起動すると、　　「reboot」
変更できる。

＊設定を変更した際、再起動を必ずしていください。
Ruby 配列

#配列の生成

```
a = [1, 2, 3]
 => [1, 2, 3]
a.class
 => Array
```

```
Array[1, 2, 3]
 => [1, 2, 3]
```

```
Array.new(3, "str")
 => ["str", "str", "str"]
```

```
Array.new([1, 2, 3])
 => [1, 2, 3]
```

```
Array.new(5) {|i| i * 3}
 => [0, 3, 6, 9, 12]
```

#配列に要素追加

```
a = [1, 2, 3]
 => [1, 2, 3]
```

###<<メソッド

```
a << 4
 => [1, 2, 3, 4]
```

###concatメソッド（破）

```
a.concat[5, 6]
 => [1, 2, 3, 4, 5, 6]
```

###insertメソッド

```
a.insert(3, 9)
 => [1, 2, 3, 9, 4, 5, 6]
```

```
a.object_id
 => 47409483356240
b = a + [10]
 => [1, 2, 3, 9, 4, 5, 6, 10]
b.object_id
 => 47409481926320
```

###unshiftメソッド

```
b.unshift(10) (破)
 => [10, 1, 2, 3, 9, 4, 5, 6, 10]
```

#配列の要素変更

###[]=メソッド

```
a = [1, 2, 3]
 => [1, 2, 3]
a[1] = 10
 => 10
a
 => [1, 10, 3]
a[1..2] = [10, 11]
 => [10, 11]
a
 => [1, 10, 11]
a[8] = 5
 => 5
a
 => [1, 10, 11, nil, nil, nil, nil, nil, 5]
```

###fillメソッド

```
a = [1, 2, 3]
 => [1, 2, 3]
a.fill("s")
 => ["s", "s", "s"]
a.fill("t", 1..2)
 => ["s", "t", "t"]
a
 => ["s", "t", "t"]
a.fill(1..2){|index| index}
 => ["s", 1, 2]
```

###replaceメソッド

```
a = [1, 2, 3]
 => [1, 2, 3]
a.object_id
 => 47409483213220
a.replace([4, 5, 6])
 => [4, 5, 6]
a.object_id
 => 47409483213220
```

#配列の要素参照

```
a = [1, 2, 3]
 => [1, 2, 3]
```

###[]メソッド

```
a[1]
 => 2
a[1..2]
 =>[2, 3]
```

###atメソッド

```
a.at(1)
 => 2
#要素 < indexの場合、nilが返る
```

###values_atメソッド

```
a.values_at(1)
 => [2]
```

###fetchメソッド

```
a.fetch(4)
IndexError: index 4 outside of array bounds: -3...3
a.fetch 4, "ERROR"
 => "ERROR"
a.fetch(4){|n| "ERROR #{n}"}
 => "ERROR 4"
```

###firstメソッド

```
a.first
 => 1
a.first(2)
 => [1, 2]
```

###lastメソッド

```
a.last
 => 3
```

###assocメソッド

```
a.assoc(3)
 => [3, 4]
#配列の配列を検索。最初の要素が指定された値と==で、その配列を返す。
```

###rassocメソッド

```
a.rassoc(4)
 => [3, 4]
#index1の要素を検索。
```

#配列の要素調べ

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
```

###include?メソッド

```
a.include?(3)
 => true
a.include?(6)
 => false
```

###indexメソッド

```
a.index(4)
 => 3
a.rindex(4)
 => 3
#該当しない場合、nilを返す。
```

#配列の要素削除

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
```

###delete_atメソッド

```
a.delete_at(2)
 => 3
a
 => [1, 2, 4, 5]
```

###delete_ifメソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.delete_if{|n| n % 2 == 0}
 => [1, 3, 5]
```

###deleteメソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.delete(3)
 => 3
a = [1, 2, 4, 5]
a.delete(10)
 => nil
a = [1, 2, 4, 5]
```

###clearメソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.clear
 => []
```

###slice!メソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.slice!(2, 2)
 => [3, 4]
a
 => [1, 2, 5]
```

###shiftメソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.shift(2)
 => [1, 2]
a.shift
 => 3
a
 => [4, 5]
#先頭から指定された数だけ要素を取り除いて返す。指定がなければ１。
```

###popメソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a.pop(2)
 => [4, 5]
a.pop
 => 3
a
 => [1, 2]
```

### - メソッド

```
a = [1, 2, 3, 4, 5]
 => [1, 2, 3, 4, 5]
a - [1, 2]
 => [3, 4, 5]
a - [1, 3, 5, 7]
 => [2, 4]
```

#配列の演算

### | メソッド

```
[1, 2, 3] | [1, 3, 5]
 => [1, 2, 3, 5]
#和集合
```

### & メソッド

```
[1, 2, 3] & [1, 3, 5]
 => [1, 3]
#積集合
```

#配列の比較

### == メソッド

```
[1, 2, 3] == [1, 3, 5]
 => false
[1, 2, 3] <=> [1, 3, 5]
 => -1
#左辺が大きければ、0。右辺が大きければ-1。同じ0。
```
RaspberryPi3BモデルにPytorch v1.1.0をインストールする

### 追記(2019/5/7)
急ぎも無くなったので，`v1.1.0`が入るかどうか検証
### 追記(2019/5/8)
およそ8時間ほどかかって無事にインストールできることを確認しました．

## 動機

RaspberryPi 3BモデルにPytorchをインストールする必要があって，
色々試行錯誤してなんとかインストールができたので，その時のメモ．

## Pytorchのバージョンについて
2019年5月7日現在，pytorchのバージョンは`v1.1.0`になっている ~~けど，
Caffe2とかも一緒にインストールしようとするため，すごく重い．~~
 ~~インストールできるか検証中．~~
無事にインストールが確認できました．

~~githubのissueとかを見る限り，`v0.3.1`のインストールまではできる様子．~~
~~(`v0.4.0`もインストールできたというコメントもあったが未検証)~~

そこで，pytorchは`v0.3.1`を選択．

```bash
$ git clone https://github.com/pytorch/pytorch --depth 1 --recursive -b v0.3.1
```

### 一部コードを修正

このままビルド・インストールしようとすると，下記のエラーにぶつかる
> Build Error: no matching function for call to setattr(torch::jit::Node*&, ...) 

https://github.com/pytorch/pytorch/issues/4472

なので，この[パッチ]
(https://github.com/pytorch/pytorch/pull/4473)を当てる必要がある．

具体的には`pytorch/tools/autograd/templates/VariableType.cpp`において，

```cpp:16行目
#include <iostream>
#include <functional
#include <cstddef> //追加
```

```cpp:38行目
// template<unsigned long N> // これを
template<std::size_t N> // に変更
```

## RaspberryPiの前準備

[これ](https://gist.github.com/fgolemo/b973a3fa1aaa67ac61c480ae8440e754)に従って，

```bash
$ sudo dd if=/dev/zero of=/swap1 bs=1M count=2048
$ sudo mkswap /swap1
```

でスワップ領域を作成して，

```bash
$ sudo vim /etc/fstab
```

```sh:ファイルの一番下の行
/swap1 swap swap
```
を追記．終わったら，

```bash
$ sudo swapoff /swap0
$ sudo swapon /swap1 # Hoge is Busyみたいなのが出ても問題なし
```

これで準備は終わり，あとはひたすらコマンドをうつ．

## インストールコマンド周り

```bash
$ export NO_CUDA=1
$ export NO_DISTRIBUTED=1
$ export MAX_JOBS=2  # すごく大事
$ export USE_MKLDNN=0 #　すごく大事
```
ここで，`MAX_JOBS=2`にしないと，pytorchがデフォルトでコア4個扱うため，ラズパイがフリーズしたり落ちたりする．（これで1日x3回くらい溶かした）

pytorchに必要なライブラリをインストール．

```bash
$ sudo apt-get install libopenblas-dev cython libatlas-dev \
m4 libblas-dev python3-dev cmake python3-yaml
```

```bash
$ cd pytorch
$ python3 setup.py build # v1.1.0ではno longerと書かれるが，この1行がいらないかは不明
$ sudo -E python3 setup.py install
```

これでインストールが終わるはず．

```bash
$ python3
Python 3.5.3 (default, Sep 27 2018, 17:25:39)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.1.0a0+2356fac'
```

となれば成功．
mysql laravel で ユーザー別にランキング順位を求める

例えば 3位/1000人中
とかやりたい。

ただ、これは関連テーブルをSUMして・・・とやるのは無理みたい。
ということで、裏でパッチ処理させておいて、メインテーブルに格納。
それをソートする。
socials テーブル
id sum_social_day7pageviews

があって
指定ユーザーIDが
sum_social_day7pageviews　順に何位か？を求める。


参考
https://qiita.com/hmuronaka/items/1afc132ddf400363efc2

```

DB::statement(DB::raw('set @c:=0'));//こうやって set を分けておくのが味噌
$res = DB::select("SELECT tmp.id, tmp.sum_social_day7pageviews, tmp.rank rank FROM (SELECT id, sum_social_day7pageviews, @c:=@c+1 rank FROM uranaibako.socials ORDER BY sum_social_day7pageviews DESC) tmp WHERE id=296640879");


```

これで結果が

```

Array
(
    [0] => stdClass Object
        (
            [id] => 296640879
            [sum_social_day7pageviews] => 97
            [rank] => 2
        )



```

やったね！
gcc の glibc newlib より速いクイックソートqsort (qs12 qs13)、 平均計算時間最速をめざして

#qsort の改良(qs12) と 新しいソート(qs13) について
昨年(2018-6-7)、https://qiita.com/t-kawa/items/99408891b18bd1f87ae6
で新しく開発したクイックソート qs9 qs10 のベンチマークテストを
お願いしました。

今回、qs10を改良したqs12 と qs12にmpsを混合したqs13 を開発しました。
gcc の qsort より 1.1倍～3倍 高速です。
キー値が2種(男女など)では10倍高速な場合もあります。

お使いの処理系でベンチマークテストをしていただけないでしょうか。
下記の場所に移動してプログラムをダウンロードして下さい。

http://ww51.tiki.ne.jp/~srr-cake/qsort/qs13/index.html

私の場合のベンチマークテスト(newlib glibc qs12 qs13 の比較)の結果を次のコメント欄に記します。
Laravelでよく使うバリデーションルール

Laravelでよく使うバリデーションルールをメモしました。

# めっちゃ使う
* required 
必須チェック

* numeric 
数値であるか

* integer 
整数であるか

* max:値 
文字列の場合は、文字数になるし、数値の場合は数値になるという賢いやつ！！

* min:値 
文字列の場合は、文字数になるし、数値の場合は数値になるという賢いやつ！！

* between:min,max 
> フィールドが指定された最小値と最大値の間のサイズであることをバリデートします。  

めっちゃ使うとかいいながら、存在を忘れていた。

* email
メールアドレスの形式であるか

* confirmed
パスワード（確認用）、メールアドレス（確認用）でよく使う
>フィールドがそのフィールド名＋_confirmationフィールドと同じ値であることをバリデートします。
  
ということなので、htmlのname属性に注意！

# 日付関連
* date
日付の形式であるか

* after:日付 
対象日より後の日付であるか
> 日付はPHPの`strtotime`関数で処理されます。 
> `strtotime`により評価される日付文字列を渡す代わりに、その日付と比較する他のフィールドを指定することもできます。

* after_or_equal:日付 
対象日以降の日付であるか

* before:日付 
対象日より前の日付であるか

* before_or_equal:日付 
対象日以前の日付であるか

* date_equals:日付
> バリデーションされる値が、指定した日付と同じことをバリデートします。



# いまさら気づいた
今後、使おう。

* nullable 
nullも許可するよ。 
よくある場面は、メールアドレスが任意入力だけど、確認用メールアドレスと一致しているかチェックするときとか

* alpha 
> フィールドが全部アルファベット文字であることをバリデートします。

* alpha_dash  
> フィールドが全部アルファベット文字と数字、ダッシュ(-)、下線(_)であることをバリデートします。

* alpha_num  
> フィールドが全部アルファベット文字と数字であることをバリデートします。


# メールアドレスのバリデーションサンプル
`confirmed`、`email`、`nullable`を使ってみる。

* メールアドレスは任意入力
* メールアドレスが入力されている場合は、確認用メールアドレスと一致している必要がある

```php
public function rules()
{
    return [
        'email' => 'confirmed|email|nullable',
    ];
}
```
【2019年5月】Scrapy の自作パイプラインで Firestore に結果を叩き込む

# Scrapy の結果を Firestore に出力したい。

Scrapy のドキュメントにはオリジナルのパイプラインで MongoDB にデータ投入する、というサンプルが掲載されておりました。

- [Write items to MongoDB](https://docs.scrapy.org/en/latest/topics/item-pipeline.html#write-items-to-mongodb)

これにインスパイアされて直接、Firestore にデータを投入してみよう、という試みです。

いくつかハマりポイントがあったのでメモしておきます。

# 対象環境

- Python: Anaconda + 3.7

Jupyter notebook から動かしてみてます。

# 準備

## 1. パッケージのインストール

以下のコマンドで scrapy と google-cloud-apiをインストールします。

```python:jupyter_notebook
!conda install scrapy -y
!pip install google-cloud-firestore
```

## 2. サービスアカウントの作成

Google Cloud Platform の管理画面から **Cloud Firestore 編集者**、**Firebase 管理者** のロールを持ったサービスアカウントを作成し、json形式で秘密鍵をダウンロードしてください。

ダウンロードしたファイルは "cred/service_account.json" など適当な名前でnotebookから参照できる場所に置いておきます。

サービスアカウントからAPIで firestore を叩く場合、**セキュリティルールは無視されます**。
というか、サービスアカウントのパーミッションが効きます。書き込む場合は **Firestore 編集者**と**Firebase 管理者** のロールが必要です。

※ ただし今回使用した Firestore は Firebaseから有効にした Firestoreでした。**Firebase 管理者**が本当に必要なのかどうか、環境に応じて検証が必要ですね。。。

## 3. クローラー、Spider の作成

ここでは Scrapy の Spider 実装には触れません。よろしく実装して下さい。
Cloud Firestore も Mongo 同様にドキュメント型のデータベースでありますので scrapy.Item を base とする item なら dict(item) でデータ投入できます。
安心して Spider 作って下さい。

# 自前パイプラインの実装

`scrapy` コマンドで `generate` した場合、プロジェクトの直下に pipelines.py が生成されていると思います。

これを以下のように置き換えます。

```python:pipelines.py
# -*- coding: utf-8 -*-

from google.cloud import firestore

class CloudFireStorePipeline(object):
    
    def __init__(self, collection_name):
        self.collection_name = collection_name

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            collection_name = crawler.spider.name
        )

    def open_spider(self, spider):
        self.client = firestore.Client.from_service_account_json('./cred/credential.json')
        self.db = self.client.collection(self.collection_name)
    
    def process_item(self, item, spider):
        self.db.add(dict(item))
        return item
```
ここではspiderの名前がそのままコレクション名としてデータを保存しています。
コレクション名が決め打ちであればコンストラクタや`from_crawler`の実装は不要です。

`./cred/credential.json` の箇所は上記のサービスアカウント作成の際にダウンロードしたjsonファイルの保存場所に差し替えてください。

# パイプラインの有効化

`settings.py`にて上記のパイプラインを有効にする必要があります。
ただし、`settings.py`でコメントアウトされている箇所をコメント外してクラス名を修正するだけでOKです。

```python:settings.py
...
ITEM_PIPELINES = {
    'myProject.pipelines.CloudFireStorePipeline': 300,
}
...
```

上記の `myProject` は Scrapy のプロジェクト名に差し替えてください。

# 実行

以下のコマンドでクローラーを実行します。例によって jupyter notebook から実行です。

```python:jupyter_notebook
!scrapy crawl mySpider
```

`mySpider` は実行する Spider の名前です。


# 参考サイト

- [firebaseのCloudFireStoreの使い方 (Python3編)](https://qiita.com/_YukiMori/items/60c9cceb31eeda09ac35)
- [Scraping files & images using scrapy, scrapinghub and Google Cloud Storage](https://medium.com/@acowpy/scraping-files-images-using-scrapy-scrapinghub-and-google-cloud-storage-c7da9f9ac302)
- [Scrapy doc: Item Pipeline](https://docs.scrapy.org/en/latest/topics/item-pipeline.html)
- [Python Client for Google Cloud Firestore](https://googleapis.github.io/google-cloud-python/latest/firestore/index.html)
- [Source code for google.cloud.client](https://googleapis.github.io/google-cloud-python/latest/_modules/google/cloud/client.html)