OmniAICreator/Qiita-1.07M huggingface dataset:
MF クラウド経費データにPowerBI からアクセスしてビジュアライズ

#概要
経費申請から承認、管理までをクラウドで行うことができるマネーフォワード社の「MFクラウド経費」のデータをPowerBI からビジュアライズする方法を紹介します。他のデータソースと併せた分析などにも便利です。

![MF-PowerBI.png](https://qiita-image-store.s3.amazonaws.com/0/116843/62f1f2f6-0d59-a687-a7b5-2639c1567356.png)

#必要なサービス
* MFクラウド経費のアカウント　[無償トライアル](https://expense.moneyforward.com/registration/new?from=ex_top&to=ex_reg_new)あり
* PowerBI デスクトップ
* CData ODBC Driver for MF クラウド経費　[無償トライアル](https://www.cdata.com/jp/drivers/moneyforwardexpense/odbc/)


#MFクラウド経費のAPI キーの取得
[MFクラウド経費 API の使い方](https://qiita.com/jonathanh/items/9abde240db098338a5b0) を参照してAPI キーを取得してください。


#CData ODBC Driver for MF クラウド経費のインストールと接続の確立
1. [https://www.cdata.com/jp/drivers/moneyforwardexpense/odbc/](https://www.cdata.com/jp/drivers/moneyforwardexpense/odbc/) から製品をダウンロードします。
2. .exe ファイルを実行して、インストールを行います。
3. インストールを完了するとODBC DSN 画面および製品ドキュメントが開きます。OAuth 認証情報を入力します。
* Callback URL: https://localhost:33333
* OAuth Client Id: Your Client ID
* OAuth Client Secret: Your Client Secret

入力して、「接続のテスト」を押します。
![スクリーンショット (856).png](https://qiita-image-store.s3.amazonaws.com/0/116843/ce40bfd1-d994-30bc-fa97-c0bbfa1d85b7.png)

入力情報が正しければ、ブラウザで認証画面が開くので、アクセスを承認します。
![mfexpense4.png](https://qiita-image-store.s3.amazonaws.com/0/116843/6739c153-b8a5-220b-6e36-17f6a822dd5c.png)

ODBC DSN 画面で「OK」を押して接続を確立します。

#PowerBI からODBC 経由でMF クラウド経費データの取得からビジュアライズ
1. 「Get Data」から「ODBC」を選択。![image.png](https://qiita-image-store.s3.amazonaws.com/0/116843/d11e5998-a5b3-c855-c622-649b5e00a483.png)
2. 作成済みの「CData MFExpense Source」を選択して、「OK」。
3. Navigator 画面で、ODBC(=dsn=CData MFExpense Source) 下にMF クラウド経費の取得可能なデータがテーブルのリストとして表示されます。ここから分析するデータを選択して、取得します。![スクリーンショット (860).png](https://qiita-image-store.s3.amazonaws.com/0/116843/109382f7-6e60-d0e5-23e3-3bb41b335e31.png)

CData ODBC Driver により、MF クラウド経費のデータはテーブル構造でPowerBI に渡されます。

4. あとは、通常のデータソースと同じようにPowerBI 上でビジュアライズを作ります。
![mfexpense5.png](https://qiita-image-store.s3.amazonaws.com/0/116843/f392fb3b-b0fd-548e-2698-59574079c28b.png)


以上
【Rspec】メソッドスタブ（allow ..to receive）の小さなサンプルメモ

# メモ

```ruby:person.rb
class Person
  def sex
    "man"
  end
  def test(test)
    ""
  end

  def test2(a1, a2)
    ""
  end
end
```


```ruby:person_spec.rb
require 'person'

RSpec.describe Person do

  it {
    person = Person.new

    allow(person).to receive(:sex).and_return("woman")
    expect(person.sex).to eq("woman")

    allow(person).to receive(:sex) { "kure" }
    expect(person.sex).to eq("kure")

    allow(person).to receive(:test2) do |a1, a2|
      a1 + a2
    end
    expect(person.test2("1", "2")).to eq("12")


    allow(person).to receive(:test).with("hoge") do
      "hoge"
    end
    expect(person.test("hoge")).to eq("hoge")
  }

end
```

```sh:実行
 % rspec spec/person_spec_stub.rb
.

Finished in 0.02673 seconds (files took 0.24371 seconds to load)
1 example, 0 failures
```

# 参考

Method stubs - RSpec Mocks - RSpec - Relish https://relishapp.com/rspec/rspec-mocks/v/2-99/docs/method-stubs

13 method stubを使おう | RSpec入門 - プログラミングならドットインストール https://dotinstall.com/lessons/basic_rspec/27913
coreMLのInput数について

# Inputの数には制約は無いが…

以前[こちら](https://qiita.com/Mco7777/items/c5df09e8b68988cdf916#timedistributedconv2dをどうしても使いたい)の記事でTimeDistributedを擬似的に再現する手法を紹介しました。しかし、これにはその時には気づいていなかった罠がありました。

可能な方は手元でInputが512個くらいのモデルをKerasで適当に作ってcoremltoolsでconvertしてXcodeに乗っけてアプリをビルドしてみてください。
そうすると如何にパラメータ数が少なかろうが、

```
let mlmodel = MLModel()
```

と初回のイニシャライズをするだけで30秒くらい待たされます。（iPhone7で確認。もしかしたら最新の機種ではもう少し早いのかもしれません。シミュレータだと何故かもっと早かった。）
しかもその間CPU利用率100%に貼り付き、端末も熱を持ちます。やりすぎるとiPhoneの寿命に関わりそうなので試す方はやりすぎないように :sweat: 

逆にモデルの構造を上手く作ることでInputを数個に減らしてあげられれば、パラメータ数が10万とか100万とかあろうとも1秒未満でイニシャライズできました。


なので現実的にアプリで利用しようと思うとInputを増やして何かを解決するのは得策じゃなさそうですね。。


# Tips
モデルのInput数を減らす際、もちろん `coremltools.converters.keras.convert` に渡す引数のうちの `input_names` のリストの長さも変わるわけですが、それを忘れるとxcodeでコンパイルされる際に

```
coremlc: Error: Error reading protobuf spec. validator error: Layer 'xxxxx' consumes a layer named 'xxxxx' which is not present in this network.
```

というエラーが出ます。'xxxxx'の部分はちゃんと存在するレイヤーの名前が入っているので「not present」と言われる意味がわからないのですが、convert時にinput_namesを適切にしてやることでこのエラーは出なくなりました。
多分コードの深くを追えばこのエラーの理由も分かると思うのですが、パッと見自分がたどり着いた答えを指し示していないエラー文なので混乱しそうだなと。
RubyからRabbitMQにメッセージを送信する

## はじめに

RabbitMQにRubyからメッセージを送信する方法を調べてみました。
なお、使用したバージョンは以下の通りです。

- Ruby 2.5.1
- RabbitMQ 3.7.5

## RabbitMQの設定

RabbitMQ は、起動するとデフォルトで guest ユーザーが生成されます。（http://www.rabbitmq.com/access-control.html#default-state）
guestユーザーは、リモートのホストからはアクセスできないようになっています。(http://www.rabbitmq.com/access-control.html#loopback-users)
本来の運用なら guest ユーザーを削除するのが良さそうです。

とりあえず、今回は、リモートからもアクセスできるようにしたいので、ユーザーを新たに作成します。

```shell-session
$ sudo rabbitmqctl add_user peter xxxxx # ユーザー名 peter パスワード xxxxx でユーザーを作成
Adding user "peter" ...
$ sudo rabbitmqctl list_users # ユーザーが登録されていることを確認
Listing users ...
peter   []
guest   [administrator]
$ 
```

peter 専用の vhost を作成します。 vhost ってのはサブディレクトリみたいなものだと考えれば良いでしょうか。

```shell-session
$ sudo rabbitmqctl add_vhost /nest # /nest という名前の vhost を作ります。
Adding vhost "nest" ...
$ sudo rabbitmqctl list_vhosts # /nest が作られていることを確認します。
Listing vhosts ...
/nest
/
```

peter には、 /nest の中では、何でもやりたい放題にさせます。

```shell-session
$ sudo rabbitmqctl set_permissions -p /nest peter ".*" ".*" ".*" # peter は /nest の中ではやりたい放題にさせます。
Setting permissions for user "peter" in vhost "/nest" ...
$ sudo rabbitmqctl list_permissions -p /nest # peterがやりたい放題できるか確認します。
Listing permissions for vhost "/nest" ...
peter   .*      .*      .*
```

## Rubyからメッセージを送信する

Rubyからメッセージを送信するには、[bunny](https://github.com/ruby-amqp/bunny) gem を使います。

```shell-session
$ gem install bunny
```

rabbitmqとは別のサーバーからの接続して、キューにメッセージを送信するスクリプトを書いてみます。


```ruby
require 'bunny'

connection = Bunny.new(host: 'rabbitmq_server', vhost: '/nest', user: 'peter', password: 'xxxxx')
connection.start
channel = connection.create_channel

# queue を永続化するために durable: true をセット
queue = channel.queue('task_queue', durable: true)

# 10個のメッージを送る
10.times do |i|
  message = "message-#{i}"
  # メッセージも消えないように永続化する
  queue.publish(message, persistent:true)
end
channel.close
connection.close
```

RabbitMQのサーバーで確認します。

```shell-session
$ sudo rabbitmqctl list_queues -p /nest name messages messages_persistent
Timeout: 60.0 seconds ...
Listing queues for vhost /nest ...
task_queue      10      10
```

task_queue には、１０個のメッセージがあって、10個とも永続化されたメッセージであることがわかります。

RabbitMQをリスタートしてもメッセージが消えないことを確認します。

```shell-session
$ sudo /etc/init.d/rabbitmq-server restart
Restarting rabbitmq-server (via systemctl): rabbitmq-server.service.
$ sudo rabbitmqctl list_queues -p /nest name messages messages_persistent
Timeout: 60.0 seconds ...
Listing queues for vhost /nest ...
task_queue      10      10
```

## まとめ

なんだか、RabbitMQ(rabbitmqctl)の使い方の部分が長くなってしまいました。 RabbitMQから読み出す方はまた今度。
お金を最小限に抑えながらPythonエキスパートになる最短ルートを真面目に考えみた

##はじめに
こんにちは。
学生エンジニア兼学生起業家兼データサイエンティストをやりながら、
最近Pythonの啓蒙活動をしている渡辺大智（Taichi Watanabe）です。

最近、

**「Progate以外どこでPython勉強できるのおおお」**
**「Python勉強したいんだが何からすれば良いのかわからねえええ」**
**「pythonを勉強したいので、参考になる本やサイトがあったら教えてください!」**

といったツイートを良く見かけて、
その度にリプを送っているのですが流石に疲れてきたのでここにまとめます。
ってか**Progate人気すぎ！！**
完全にキャズムを超えてますね。
　　　　
　　　　

逆に、

**え、ProgateってそんなにPythonのコース充実してたっけ・・・**
**あそこってHTML,CSSはすごい良かったけど他のコースってそこまでだったような・・・**
**他にも良い教材あるよ！**

と思ったりもしています。


ここでは、
文系出身のくせに大学に入ってからPythonを独学で始め、
PKSHA Technologyのインターンに行ったり、自分でちょっとしたプロダクト開発したり、簡単な競技プログラミング出たりすることができるレベルにまではなった自分の経験から「これ良かったよ！」という教材を紹介します。

**※個人的にDM等で相談もらえれば、その人のやりたいこと、その人の状況に合わせて個人的に学習フローを組むの手伝いますので是非連絡ください（-> [僕のTwitter](https://twitter.com/ahpjop/with_replies)）**
＊既にエンジニアとして働いている人など既にエキスパートの人は逆に自分が相談します！



##▽基礎編
対象：
・プログラミングやってことねええ。
・他の言語はやったことあるんだがPythonは初めて触ります。

おすすめ教材：
・[『基礎Python』](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E-Python-IMPRESS-KISO-%E5%A4%A7%E6%B4%A5/dp/484438015X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1527486400&sr=1-1)
　→ わかりやすいっすねえ
　→ 他の言語やったことある人なら3日くらいで終わります。
　→ 学生は学校の図書館で借りよう！

・Progate
　→ わかりやすいっすねえ
　→ レベルアップとかあったりして、モチベーションが維持できます！
　→ 本だとモチベーションが続かない人におすすめ
　　　　


##▽練習編
対象：
・『基礎Python』読み終わった、or Progate一旦やったんだが次何すればよいんだ？？
・昔Pythonやってたんだがもう忘れちまったなあ

おすすめの教材：
・[Python100本ノック](https://qiita.com/ahpjop/items/373f807d68044cda1c9b)
　→ 入門者でも解ける面白そうな問題100問集めました。
　→ 僕が作りました！
　→ jupyter notebookなど使って自分で解いて見てください

・[CheckiO](https://checkio.org/)
 → 良い問題がいっぱいあります
　→ 人によってはむずかしすぎるかも（そういう人は100本ノックがおすすめ）

・[Paiza](https://paiza.jp/challenges/info)
　→ そこそこCheckiOとかできるようになってきたら挑戦してみよう！
  　　



##▽分野別編
対象：
・Python自体はだいたいわかった
・実務をやってみてえええ
・やりたいことがある

おすすめの教材：
セキュリティとか：
・AtCoderの過去問やりまくる
・AtCoder Beginner Contestとか出てみる

　→ ごめんなさい、そっち系あんま詳しくないです。。。

機械学習とか：
・[『ITエンジニアのための機械学習理論入門』](https://www.amazon.co.jp/IT%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E7%90%86%E8%AB%96%E5%85%A5%E9%96%80-%E4%B8%AD%E4%BA%95-%E6%82%A6%E5%8F%B8/dp/4774176982/ref=sr_1_10?s=books&ie=UTF8&qid=1527486419&sr=1-10&keywords=%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92)
　→ わかりやすいっすねえ
　→ プログラム系

・[『機械学習入門 ボルツマン機械学習から深層学習まで』](https://www.amazon.co.jp/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E5%85%A5%E9%96%80-%E3%83%9C%E3%83%AB%E3%83%84%E3%83%9E%E3%83%B3%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%8B%E3%82%89%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92%E3%81%BE%E3%81%A7-%E5%A4%A7%E9%96%A2%E7%9C%9F%E4%B9%8B-ebook/dp/B06Y41YGNW)
　→ 機械学習の概要が簡単にわかります！
　→　プログラムするより前に、そもそも何ができるのかとか概要を勉強したい！
　→ DeepLearningってなに？とかきになる

・学生だったら東大人工知能開発団体HAITへ!
　→ 大学生で機械学習勉強したいなら行きましょう
　→ 僕に連絡くれれば紹介します。


##▽開発、実績
・Paizaでテスト！
　→とりあえずやってみましょう！

・ハッカソンへ出てみる！
　→ 強そうな仲間を集めて出よう

・[Kaggle](https://www.kaggle.com/)や[SIGNATE](https://signate.jp/)へ挑戦
　→ とりあえずやってみる

・インターンへ行く！
　→ 面白そうなことしている企業があったらとりあえず連絡
　→ DMくれれば企業選びの相談乗ります

・勉強会へ参加！
　-> compassとかサポーターズとかをチェック！


以上

まあ、少しでもこれからPythonを勉強する人にとって参考になればと思います
[Twitter](https://twitter.com/ahpjop/with_replies)ではエンジニアのイベントやニュースなどの発信も行なっていますので、是非フォローしてもらえると嬉しいです。

それでは！
C  CPU取得サンプル


下記を参照している。
http://winter-tail.sakura.ne.jp/pukiwiki/index.php?C%A1%BFC%2B%2B%A4%A2%A4%EC%A4%B3%A4%EC%2F%BC%AB%A5%DE%A5%B7%A5%F3%A4%CEIP%A5%A2%A5%C9%A5%EC%A5%B9%A4%F2%BC%E8%C6%C0%A1%CALinux%A1%CB
ffmpeg でコンテナ変更したファイルのタイムスタンプを、変換前と同じするワンライナー

ls(1) のオプションが gnu ls と違うのでそちら方面の人は参考程度にお願いしますm(__)m

拡張子 .m4a が変換元で .aac に変換した場合は以下の通り。

```text
ls -lAGoFD"%Y%m%d%H%M.%S" *.m4a | awk '{print"touch -t "$7" "$8}' | sed 's|\.m4a|.aac|' | sh
Rubyのオブジェクトの操作（整数、文字列、時刻）

## オブジェクト（指向）とは？

まず、オブジェクトとは日本語で、物体と言う意味です。それでいうと、Rubyにとっては扱える全てのデータ、例えば整数や文字列や時刻などは何らかの「物体」であると言えます。

目に見えないものなので、「モノ」と言い換えてもいいかもしれません。

また、オブジェクト指向とは、ソフトウェア設計に置ける概念の一つです。

**オブジェクトが何らかの役割（メソッド）を持ち、命令を実行できる**という考え方です。

それぞれのオブジェクトと操作方法を少し紹介します。

### 整数

`1`や`123`といった整数は、`Fixnum`クラスです。

`Fixnum`クラスですが、Ruby 2.4からは両者が`Integer`へ統合されています。

`Integer` < `Numeric` < `Object` という継承関係になっています。

https://docs.ruby-lang.org/ja/2.1.0/class/Fixnum.html

```rb
puts 1
puts 1.class
puts 1.class.superclass
puts 1.class.superclass.superclass
puts 1.class.superclass.superclass.superclass

# 実行結果
# 1
# Integer
# Numeric
# Object
# BasicObject
```

`superclass`は親クラスを確認するメソッドが、メソッドチェーンになっています。

#### 整数で使えそうなメソッド

- `next`：次の整数を返す
- `even?`：偶数かどうかを`true` or `false`で判定
- `odd?`：奇数かどうかを`true` or `false`で判定

ちなみに

- evenは４文字で偶数だから偶数判定に使われるメソッド
- oddは3文字で奇数だから奇数判定に使われるメソッド

と覚えると覚えやすかった

```rb
puts 1.next
puts 1.even?
puts 1.odd?

# 実行結果
# 2
# false
# true
```

### 文字列

`a`や`ok, google`などは、`String`クラスです。`String` < `Object` < `BasicObject`という継承関係になっています。

```rb
puts 'a'
puts 'a'.class
puts 'a'.class.superclass
puts 'a'.class.superclass.superclass

# 実行結果
# a
# String
# Object
# BasicObject
```

#### 文字列で使えそうなメソッド

- length は、文字列の長さを計算するメソッド
- upcase は文字列を全て大文字にするメソッド
- gsub は `/Qiita/` で見つかった文字列を `ryosuke` で置き換えるメソッド

```rb
str = 'my name is Qiita'
puts str.length
puts str.upcase
puts str.gsub(/Qiita/, 'ryosuke')
```

### 時刻

時刻を扱う機会は多いかもしれません。いくつか `Time` クラスで使えるメソッドの例を挙げます。

```rb
# 現在時刻の取得
time = Time.now

# 現在時刻を出力
puts time

# イギリスを基準とした協定世界時の9時間後のつまり、日本時刻を出力
puts time.getlocal("+09:00")

# 現在時刻の年だけ出力
puts time.year

# 現在時刻の月だけ出力
puts time.month

# 現在時刻の 日だけ出力
puts time.day

# 現在時刻の月だけ出力
puts time.monday?

# 実行結果
# 2018-05-28 06:40:30 +0000
# 2018-05-28 15:40:30 +0900
# 2018
# 5
# 28
# true
```

---

以上です。他にも色々なメソッドがあると思うので、参考にして見てください！

## 参考

- https://tomoyuki65.com/introduction-to-ruby-3/
技術勉強会を開催する時に考えること

きっと知りたいなと思う人はいると思うので、Publicな場所に書いておこうと思います。

# 考えるべきこと
* 会場（場所）
* 参加料（有料／無料）
* テーマ
* 会場レイアウト
* 懇親会はやるか
    * 懇親会をやる場合は何を用意するか

こんなところでしょうか。それぞれ簡単に掘り下げていきます。


## 会場
私の場合は、所属している会社の会議室を利用しました。
その上で必要な社内政治はありますが、あまり深いことは考えず利用しました。

もし貸会議室を利用するなら、[TKP](https://www.kashikaigishitsu.net/?argument=FhCWxBEG&dmai=a5af00b581faec&gclid=CjwKCAjwrqnYBRB-EiwAthnBFpHekk9iwojHWvzKrWTyMlyda34HukwmC0_MKX6Gl0u1By3AwlLZAxoCCdYQAvD_BwE)が有名ですね。
貸会議室を借りる場合は、参加者からお金を頂くこととなるかと思いますので、その場合の課金方法も考慮が必要です。


## 参加料（有料／無料）
私は基本的に無料でやるべきだと思っています。
有料だと学生（特に高校生以下）にはなかなか厳しいですし、何より心理的ハードルが高いです。

勉強会を開催する目的次第ですが、「楽しいことやろうぜ！勉強会やろうぜ！」的なテンションの場合は無料でできる方法を考えるべきでしょう。

ただ、上でも少し触れましたが、貸会議室や何か有料なものを手配する場合は、参加者から費用を徴収する必要が出てきます。
connpassだとPayPalアカウントさえあれば支払いも楽になるかと思います。（私は実際にやったことはありませんが…）
![スクリーンショット 2018-05-28 15.08.58.png](https://qiita-image-store.s3.amazonaws.com/0/84307/bcfb683d-7f9e-956f-6367-60dc81db665b.png)

## テーマ
とても重要です。
AndroidやiOS、Node.jsなどといった特定のテーマでもよいですし、 `モバイル` などと少し広めのテーマでもよいかと思います。
ただテーマを広げすぎると抽象度が上がってしまい、何がテーマなのかがわからなくなってしまいますので注意が必要です。

## 会場レイアウト
人数が多く集まると、スライドの見やすさや前後との距離、机の有無などを考える必要があります。

### 机の有無
PCを持参する人も多いでしょうから机があった方がよいかと思いますが、
人数が多くなると机を無しにしてイスを多く並べるのもよいでしょう。

会場の前半分は机有り、後ろ半分は机無し、という工夫をしてもよいかもしれません。

### 電源の有無
PCを持参する人は電源が必要かもしれません。
２〜３時間程度であれば大丈夫だと私は思いますが、発表者用としていくつか用意しておくのもよいかもしれません。

## 懇親会はやるか
できる限り実施した方がよいでしょう。
`勉強会は懇親会が本番！` と言う人がいるくらい（私もその1人）ですので、懇親会も重要な項目のひとつです。
会場でピザなどを出前してもよし、近くの居酒屋に移動してもよし…方法はいくらでもあります。

### もし懇親会をやるなら
#### ゴミを片付ける方法があるなら
ピザ◯ットなどでピザを配達してもらい、飲み物をコンビニなどで調達したらよいでしょう。安くできるかと思います。

#### ゴミを片付ける方法がないなら
ケータリング会社を利用するのがよいでしょう。事後のゴミの撤去もやってくれる業者も多く存在します。

#### 会議室でできないなら
近場の居酒屋などで場所を借りてﾜｲﾜｲやるのがよいでしょう。
その場合は大人数なため予約が必要になるので、事前に参加人数を決めておくのがよいでしょう。


## まとめ
「楽しくやろうぜ！」というのを勉強会のモットーで私はやっています。
他にもたくさん考慮すべき項目はありますが、何かあれば追記していきます。
CentOS7 minimal install に Anaconda をインストール

# インストールに必要なライブラリを yum で インストール
```
sudo yum install git bzip2 -y
```
参考：[lbzip2: exec 不能: そのようなファイルやディレクトリはありません](https://qiita.com/bezeklik/items/e2ec12c02b49b0e8b379)

# Anaconda のインストール（2018/12/11）
```
git clone https://github.com/yyuu/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init -)"' >> ~/.bashrc
source ~/.bashrc
pyenv install anaconda3-5.3.1
pyenv rehash
pyenv global anaconda3-5.3.1
echo 'export PATH="$PYENV_ROOT/versions/anaconda3-5.3.1/bin/:$PATH"' >> ~/.basic
source ~/.bashrc
conda update conda
```
参考：[CentOS7にAnacondaの環境構築](https://qiita.com/iamdaisuke/items/3671032a18f5c4bf37d1)
Redmine＋PostgreSQLでのバックアップとリストア方法

個人的な備忘録

# やりたいこと
Redmineを新しいサーバへ移行させるためバックアップとリストアを行いたい。

# 動作環境
新旧共に下記の組み合わせで動作させる
Redmine3.4.3
PostgreSQL9.2.23

# バックアップ方法
1.pg_dumpコマンドを利用してdumpする。
```
pg_dump -U redmine {DB名} > {バックアップファイル名}
```
2./var/lib/redmineをまるごとバックアップする
```
tar -zcvf redmine.tar.gz /var/lib/redmine
```

共に新しいサーバへコピーしておく

# リストア方法
1.Redmine.jpのインストール手順に従い、インストールいていく。
2.項目名【Redmine用データベースの作成】が終わったら下記コマンドでDBをリストアさせる。
DBが空の状態で行う必要がある。デフォルトデータ登録を行ってしまうとエラーが出てしまう。
```
psql -U redmine {DB名} < {バックアップファイル名}
```
3.項目名【Redmineのダウンロード】が終わったら/var/lib/redmineにバックアップしたファイルを展開する。
```
cd /var/lib
tar zxvf redmine.tar.gz
```

# 同じサーバに別DB名でリストアする場合
旧DBのデータを保持しておいて新しいDBにRedmineを接続させにいく
1.新しいリストア用DBを作成する
```
sudo -u postgres createdb -E UTF-8 -l ja_JP.UTF-8 -O redmine -T template0 redmine2
```
2.作成したDBにリストアする
```
psql -U redmine redmine2 < {バックアップファイル名}
```
3.database.ymlで新しいDBを指定する

```:/var/lib/redmine/config/database.yml
database: redmine
↓
database: redmine2
```
4.Redmineを再起動する
```
systemctl reload httpd
```
ブラウザにアクセスすると再起動完了
Chrome拡張機能:複数ファイルをZIPにまとめてダウンロードする

# はじめに
## やりたいこと
サムネイル表示されている画像ファイルをZIPにまとめてダウンロードする。

## 制限事項
たとえChrome拡張機能がダウンロードしたものでも、ローカルのファイルにはアクセスできない。

ゆえに、メモリ上の画像データからZIPデータを作成し、それをダウンロードする。ZIPの作成にはJSZipを使用した。

# 手順1:画像ファイルのパスを探す
サムネイルからリンクをたどってオリジナル画像のURLを取得する。

その際、画像ファイルが置かれているドメインを確認し、manifest.jsonの`permissions`に追加する。(Flickrの場合は、「`https://c1.staticflickr.com/`」など。)ワイルドカードが使えないので、連番が降られている場合はすべての番号を追加する必要がある。

# 手順2:画像ファイルのダウンロード
XMLHttpRequestの`responseType`に`arraybuffer`を指定することで、画像ファイルをバイナリデータとして取得できる。

```javascript:background.js
const DownloadArraybuffer = (url) => {
  const p = new Promise(resolve => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('loadend', () => {resolve(xhr.response);});
    xhr.send();
  });

  return p;
}
```

# 手順3:ZIPデータを作成する

`JSZip`オブジェクトを作成し、`file`メソッドでダウンロードした画像データを追加する。最後に`generateAsync`メソッドを実行すればZIPデータが作成される。

```javascript:background.js
const ZipGenerateAsync = (zip) => {
  return new Promise((resolve, reject) => {
    zip.generateAsync({type: "blob"}).then(resolve);
  });
};

const CreateZipBlob = async (urlList) => {
  const zip = new JSZip();

  for(let i = 0; i < urlList.length; i++) {
    const imageFile = await DownloadArraybuffer(urlList[i]);
    const fileName = i + '.jpg'

    zip.file(fileName, imageFile, {binary: true});
  }
  
  return await ZipGenerateAsync(zip);
}
```

# 手順4:ZIPデータをダウンロードする
`createObjectURL`の引数にバイナリデータを渡すと、ダウンロードするためのURLを発行できる。ダウンロード後は`revokeObjectURL`でURLを破棄し、バイナリデータを開放する。

```javascript:background.js
const DownloadFile = (url, filename) => {
  return new Promise(resolve => {
    chrome.downloads.download({url: url, filename: filename}, downloadId => {
      resolve(downloadId);
    });
  });
};

const Download = async () => {
  ...
  const zipBlob = await CreateZipBlob(urlList);
  const zipUrl = window.URL.createObjectURL(blob);
  const zipName = 'aaa.zip'

  await DownloadFile(zipUrl, zipName);
  window.URL.revokeObjectURL(zipUrl);
}
```
manage.py migrateをおかしな感じにやってしまった。



## 1.djangoでmakemigrations -> migrate がうまくいかない。

```
python3 manage.py migrate
(中略)

django.db.utils.OperationalError: table "common_category" already exists
```
sqliteのDBで、dbファイルだけコピってmigrationsのファイルをなくしてしまったわけです。
その状況で新たにmakemigrationsしても「すでにテーブルが有るぞ」となってしまう。

##2.--fake [app名]　オプションを使ってみる。
migrateの履歴に「ちゃんとmigrateしたよ」と偽情報を流すような感じ。

```
python3 manage.py migrate --fake common
Operations to perform:
  Apply all migrations: common
Running migrations:
  Applying common.0004_auto_20180528_1203... FAKED
#
```

showmigrationsでみてみると無事クリアできてる。

```
# python3 manage.py showmigrations
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
common
 [X] 0001_initial
 [X] 0002_auto_20180307_1640
 [X] 0003_auto_20180521_1522
 [X] 0004_auto_20180528_1203
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
django_celery_beat
 [X] 0001_initial
 [X] 0002_auto_20161118_0346
 [X] 0003_auto_20161209_0049
 [X] 0004_auto_20170221_0000
 [X] 0005_add_solarschedule_events_choices
 [X] 0006_auto_20180210_1226
```


## 3.fakeを外したい。（SQLを再度実行したい）

```
# python3 manage.py showmigrations macaddr
macaddr
 [X] 0001_initial
```

macaddrというアプリを作成し、makemigrationsしました。
その後、python3 manage.py migrate --fake　　　　とした状態。

`python3 manage.py migrate --fake macaddr zero`
というzeroオプションをつけて

`macaddr
 [X] 0001_initial`
↑のチェックを外します

```
 python3 manage.py migrate --fake macaddr zero
Operations to perform:
  Unapply all migrations: macaddr
Running migrations:
  Rendering model states... DONE
  Unapplying macaddr.0001_initial... FAKED
```

```
# python3 manage.py showmigrations macaddr
macaddr
 [ ] 0001_initial
```

チェックがはずれました。
migrateします。

```
# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, common, contenttypes, django_celery_beat, macaddr, sessions, sites
Running migrations:
  Applying macaddr.0001_initial... OK
```

無事テーブルが作成されました。
テーブル消したりしなくていいので焦らないでやること。
MySQL 5.7 で ssl 対応 (Ubuntu 16.04)

# mysql_ssl_rsa_setup

> mysql_ssl_rsa_setup

で /var/lib/mysql に 各pem ファイルが自動的に作成される。

※--datadir オプションでディレクトリ指定可能
※　各pem ファイルのパーミッションで「読み取り」を付加

#my.cnf を編集

/etc/mysql/my.cnf

[mysqld]
・・・
datadir=/var/lib/mysql/mypath
・・・
ssl-ca=/var/lib/mysql/mypath/ca.pem
ssl-cert=/var/lib/mysql/mypath/server-cert.pem
ssl-key=/var/lib/mysql/mypath/server-key.pem
・・・

#専用ユーザ作成
mysql> create user 'ユーザ名' identified by 'パスワード';

#権限設定
mysql> grant all privileges on *.* to 'ユーザ名' identified by 'パスワード' require ssl;

#設定確認
> mysql -u ユーザ名 -p --ssl-ca=/var/lib/mysql/mypath/ca.pem;

mysql> show variables like '%ssl%;

で確認。

>tcpdump -n -X -s 2000 port 3306 -i インターフェース

でパケット確認。
Unityで作られるAndroidManifest内のPackage名を動的に切り替える


下記ページを参考にしました。
http://sakura-bird1.hatenablog.com/entry/2015/09/09/103605

**${applicationId}**を使用するとパッケージ名（BundleID）に紐づいていて定義しておけばビルド時に切り替えてくれる。


## Growthプッシュの定義をメモ
```
    <!-- Growth Push -->
    <meta-data android:name="com.growthpush.notification.icon" android:resource="@drawable/icon_growthpush" />
    <meta-data android:name="com.growthpush.notification.icon.background.color" android:resource="@android:color/black" />
    <meta-data android:name="com.growthpush.dialog.icon" android:resource="@drawable/icon_growthpush" />
    <activity
    android:name="com.growthpush.view.AlertActivity"
    android:configChanges="orientation|keyboardHidden"
    android:launchMode="singleInstance"
    android:theme="@android:style/Theme.Translucent" />
    <service
        android:name="com.growthpush.TokenRefreshService"
        android:exported="false">
      <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID"/>
      </intent-filter>
    </service>
    <service android:name="com.growthpush.RegistrationIntentService"/>
    <service
        android:name="com.growthpush.ReceiverService"
        android:exported="false" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
      </intent-filter>
    </service>
    <receiver
        android:name="com.google.android.gms.gcm.GcmReceiver"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
      <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="${applicationId}" />
      </intent-filter>
    </receiver>

    <!-- for Growth Push End -->  

```

## Growthプッシュのパーミッション定義をメモ
```
  <!-- Growth Push Start -->
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  <uses-permission android:name="android.permission.VIBRATE" />
  <uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
  <permission
      android:name="${applicationId}.permission.C2D_MESSAGE"
      android:protectionLevel="signature" />
  <!-- Growth Push End -->

```
JupyterLabでtqdmでプログレスバー表示して処理状況（学習状況）の可視化

![JupyterLab-Google-Chrome-2018_05_28-14_55_11.gif](https://qiita-image-store.s3.amazonaws.com/0/195903/a5c5b267-a815-e914-bcc3-378a62acbda0.gif)


# 背景
JupyterNotebookでtqdmの記事はたくさんあるが、JupyterLabでtqdmは（特に日本語記事は）なかったため、記録として書き残しておく。

# やりたいこと
- JupyterLabで、tqdmを使って処理状況（≒学習状況）の可視化。  

- forのループではなく、pandasのapply（ラムダ関数）にも適用したい。

# 問題

- 1, 下記のエラーが出る。  
```AttributeError: 'function' object has no attribute 'pandas'```  
<br>
- 2, Error displaying widgetと出る。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195903/6ef3971a-dbc6-0661-45a3-40e0ff2fe21e.png)

# 対策
- [1, importの仕方を変える](https://github.com/tqdm/tqdm/issues/419#issuecomment-321375209)  
```from tqdm._tqdm_notebook import tqdm_notebook```  
<br>
- 2, js有効化と各種インストール

```
# script有効化
jupyter nbextension enable --py --sys-prefix widgetsnbextension

# ipyvolumeのインストール
jupyter labextension install ipyvolume

# jupyterlab-managerのインストール（要：バージョン確認！）
jupyter labextension install @jupyter-widgets/jupyterlab-manager@1.0
```
  
- jupyterlab-managerのバージョンは、JupyterLabのバージョンを確認してから、[ここ](https://github.com/jupyter-widgets/ipywidgets/tree/master/packages/jupyterlab-manager#version-compatibility)で確認する。  
<br>
- JupyterLabのバージョンは、JupyterLab起動後、Help -> About JupyterLab Betaで確認できる。  
<br>
- コード例1  

```
from tqdm._tqdm_notebook import tqdm_notebook
import numpy as np

# tqdm_notebookで囲うだけ
for a in tqdm_notebook(np.arange(1, 10000000, 1)):
    # ここに処理
    a = a
```

- コード例2  

```
# pandasのapplyへ適用
# import
from tqdm._tqdm_notebook import tqdm_notebook

# set description
tqdm_notebook.pandas(desc="Processing:")

# apply
df['hoge'] = df['hoge'].progress_apply(lambda x:  x + 1)
```

- 表示される
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195903/dc5248bf-5939-68a1-8e00-0a9d8422c96e.png)
Vagrantで共有フォルダがマウントされない


## TL;DR
`Vagrant`内に`vboxadd`が存在しないのが原因です。
下記コマンドを打っていくと直ると思います。

```
$ sudo /etc/rc.d/init.d/vboxadd setup
sudo: /etc/rc.d/init.d/vboxadd: command not found
$ cd /tmp
$ wget http://download.virtualbox.org/virtualbox/4.3.20/VBoxGuestAdditions_4.3.20.iso
$ sudo mount -t iso9660 /tmp/VBoxGuestAdditions_4.3.20.iso /mnt
$ cd /mnt/
$ sudo ./VBoxLinuxAdditions.run
$ ls /etc/init.d/ | grep vboxadd
vboxadd
$ sudo reboot
```


## 現象

ある日`Vagrant`を立ち上げようとしたら下記のエラーが出ました、世知辛いのじゃ。

```terminal.app
$ vagrant up
(中略)
==> default: Machine booted and ready!
[default] GuestAdditions seems to be installed (5.1.32) correctly, but not running.
vboxadd: unrecognized service
vboxadd-service: unrecognized service
bash: line 3: setup: command not found
==> default: Checking for guest additions in VM...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

 setup

Stdout from the command:


Stderr from the command:

bash: line 3: setup: command not found
```

この状態でもSSHでつなぐことはできるのですが、`Vagrant`内の共有フォルダ以下が空でした。

## 対処方法
`GuestAdditions`が動いていないとのことなので`vbguest`コマンドで見てみます。

```terminal.app
$ vagrant vbguest --status
[default] GuestAdditions seems to be installed (5.1.32) correctly, but not running.
```

上記のエラーと同じエラーが出ました。
念の為`vbguest`をインストールしてみます。

```terminal.app
$ vagrant plugin install vagrant-vbguest
Installing the 'vagrant-vbguest' plugin. This can take a few minutes...
Installed the plugin 'vagrant-vbguest (0.15.2)'!
```

インストールされています！知ってた！

ここから先は`TD;DR`で書いたことの解説です。
まず、`vboxadd`コマンドが動作しないかの確認です。

```
$ sudo /etc/rc.d/init.d/vboxadd setup
sudo: /etc/rc.d/init.d/vboxadd: command not found
```

いい感じにコマンドが存在しないのでインストールしていきます。

```
$ cd /tmp
$ wget http://download.virtualbox.org/virtualbox/4.3.20/VBoxGuestAdditions_4.3.20.iso

--2018-05-28 02:23:15--  http://download.virtualbox.org/virtualbox/4.3.20/VBoxGuestAdditions_4.3.20.iso
Resolving download.virtualbox.org... 103.2.50.86
Connecting to download.virtualbox.org|103.2.50.86|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 58343424 (56M) [application/octet-stream]
Saving to: `VBoxGuestAdditions_4.3.20.iso'

100%[======================================>] 58,343,424  1.46M/s   in 16s     

2018-05-28 02:23:32 (3.39 MB/s) - `VBoxGuestAdditions_4.3.20.iso' saved [58343424/58343424]
```

インストールできたのでマウントして実行します。

```
$ sudo mount -t iso9660 /tmp/VBoxGuestAdditions_4.3.20.iso /mnt
$ cd /mnt/
$ sudo ./VBoxLinuxAdditions.run
```

`vboxadd`が存在することを確認したら、`vagrant`を再起動します。

```
$ ls /etc/init.d/ | grep vboxadd
vboxadd
$ sudo reboot
```

再起動すると正常に共有フォルダがマウントされていました！やったね！

## 参考リンク
[Mac(Yosemite)でVagrant環境を構築する](http://zow.hatenablog.com/entry/20150116/1421336024)
よく使うviコマンド！

#よく使うviコマンド!

これだけは覚えていきたいviコマンドを紹介します。

viモードにする場合

```
n_hirai$ vi  [ファイル名]

```

**ESC**でコマンドモードへ移行できます。


###テキスト入力
|コマンド| 説明　|
| ---| -------------------- |
|　 I   | カーソルの行頭に文字を挿入 |
| 　 i    | カーソルの左に文字を挿入 |
| 　 a   | カーソルの右に文字を挿入　|
| 　 o   | カーソルの下の行に空行を作って挿入|
|   　O   | カーソルの上の行に空行を作って文字を挿入|
|  　 c   | 文字を変更 |
| 　 C    | カーソル位置から行末までを消して文字を挿入|

###カーソル移動

|コマンド| 説明　|
| ---| -------------------- |
|　 h   | カーソルを左へ 　←|
| 　 j   | カーソルを下へ 　↓|
| 　 k  | カーソルを上へ　↑|
| 　 l  | カーソルを右へ　→|

###ファイル操作

|コマンド| 説明　|
| ---| -------------------- |
|　 ZZ  | 保存して終了|
| :w +enter  | 上書き保存|
| :w ファイル名 +enter | 名前をつけて保存|
| :wq +enter| 保存して終了|
| :q! +enter| 保存せずに終了|
| :q +enter | 終了　|
| :eファイル名　+enter | ファイルを開く
Python split関数の使い方

# Python split関数の使い方備忘録
Pythonのsplit関数を使って便利であったため，備忘録として残しておこうと思う．
初めて投稿するため，拙い文章で恥ずかしいが少しずつ成長したいと思う．
今回の目標はカレントディレクトリの'date'の部分を取り出したい．

```py
current_directry = 'User/name/Desctop/folder/date'
```
カレントディレクトリを'/'で区切る．

```py
x = current_directry.split('/')
```
対話型でみるとこうなる．

```
In [1]: x
Out[1]: ['User', 'name', 'Desctop', 'folder', 'date']
```

この状態で，date部分を取り出したいため，以下のようにするとdateが取り出せる．

```
In [33]: x=l.split('/')[-1]

In [34]: x
Out[34]: 'date'
```
vm-bhyveでUbuntu 18.04

vm-bhyveでUbuntu 18.04をインストールするためのメモ。

インストールは成功するものの、リブート時にGrubで止まってしまいます。grub_run_partition="2"を指定しましょう。

confのサンプルです。

```
loader="grub"
cpu=1
memory=1024M
network0_type="virtio-net"
network0_switch="dmz"
disk0_type="virtio-blk"
disk0_name="disk0"
disk0_dev="sparse-zvol"
grub_run_partition="2"
```

第1パーティションが、BIOS互換用パーティション？になってるようで、明示的にgrub2-bhyveに対して
第2パーティションに設定ファイルがあることを示す必要があるようです。デフォルトだと第1パーティション
を見るようになっています。

[Configuring Grub Guests · churchers/vm\-bhyve Wiki](https://github.com/churchers/vm-bhyve/wiki/Configuring-Grub-Guests)

16.04と18.04でこの辺も変わってますね。

```
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    252:0    0    1T  0 disk
├─vda1 252:1    0    1M  0 part
└─vda2 252:2    0 1024G  0 part /
```
コメントを除去しつつ整形したGoogle Apps Scriptを簡単に取得したい

# 背景と概要
この記事ではGoogle Apps Script（GAS）を作成しているプロジェクトからコメントを除去しつつ整形したスクリプトを取得するための方法について紹介させていただきます。

GASでスクリプトを作成していると、スクリプトのフォーマットに誤りがあれば保存時にそれをエラーメッセージとして指摘されることはご存知のことと思います。これはスクリプトエディタあるいはGoogle Driveでフォーマットを管理しているのではないかと考えていました。そしてこの機能を何らかの方法で利用することができればスクリプトの整形もできるのではないかと期待していました。最近、面白いことに気付きました。例えば、Google Drive上に新しいプロジェクトを作成（スタンドアロン、バウンドスクリプトどちらでも可）し、そのコード.gsへ下記スクリプトをコピーペーストします。

~~~javascript
function myFunction() {
    Logger.log(this);
}
~~~

これを実行すると、``this``の中にプロジェクト内のスクリプトが含まれていることに気付きました。さらにいろいろなプロジェクトでこれを実行してみると、取得されるスクリプトは微妙にスクリプトエディタ上のものとは異なっているように思われました。よく見てみると、コメント文が除去され、さらに整形までされていることが分かりました。[Apps Script API](https://developers.google.com/apps-script/api/)でスクリプトを取得すると、スクリプトエディタ上のそのままの状態が取得されますが、この方法では特にAPIを使用せずにコメントの無い整形されたスクリプトを取得することができます。これは、例えば不要なコメントを削除したり、インデントを修正してスクリプトを見せる場合や、ウェブ上のスクリプトを整形して見やすくする場合などに利用できるのではないかと考えられます。

# 制限
- GASプロジェクトの中の".gs"ファイルでしか使用できません。HTMLやjsonファイルは取得できません。
- プロジェクト内に同一の関数名があればどちらか一方しか取得できません。
- 関数の外側にあるような例えばグローバル変数は取得できませんでした。

面白いと思ったのですが、使用時にはこれらの点に注意する必要があります。

# 使い方
スクリプトを取得するシンプルなサンプルは次の通りです。値自体はオブジェクトになっていますので、必要に応じて作成することができるかと思います。このサンプルを使用する際は、プロジェクト内の.gsファイルへ下記スクリプトをコピーペーストしてから実行します。スクリプトが実行すると、"reformattedScripts.txt"のファイルがルートに作成され、その中にコメントが除去され整形されたスクリプトが保存されます。

~~~javascript
function getScripts() {
  var scripts = Object.keys(this).map(function(e){return typeof this[e] == "function" && e != "getScripts" ? this[e] : ""}).filter(String).reverse();
  DriveApp.createFile("reformattedScripts.txt", scripts.join(""), MimeType.PLAIN_TEXT);
}
~~~

## 結果
サンプルとして、次のようなスクリプトがコード.gsファイル内にあるとして、``getScripts()``を実行します。すると、``getScripts()``以外のファンクションのスクリプトが取得され、ファイルに保存されます。

~~~javascript
function getScripts() {
  var scripts = Object.keys(this).map(function(e){return typeof this[e] == "function" && e != "getScripts" ? this[e] : ""}).filter(String).reverse();
  DriveApp.createFile("reformattedScripts.txt", scripts.join(""), MimeType.PLAIN_TEXT);
}

function myFunction1() {
  // sample comment 1
}

function myFunction2() {
  Logger.log("sample") // sample comment 2
}

// sample comment 3
function myFunction3() {for(var i=0;i<10;i++){for(var j=0;j<10;j++){Logger.log(i+j)}}}
~~~

保存されたファイル内のスクリプトは次の通りです。コメントが除去されて整形されていることが分かります。

~~~javascript
function myFunction1() {
}

function myFunction2() {
    Logger.log("sample");
}

function myFunction3() {
    for (var i = 0; i < 10; i++) {
        for (var j = 0; j < 10; j++) {
            Logger.log(i + j);
        }
    }
}
~~~

本記事の情報が既知の場合は申し訳ありません。

[もしも必要なら英語版はこちらです。](https://gist.github.com/tanaikech/61f69fd2ce181865d3430c260c6a5d0b)
GKEでダウンタイム発生させずにk8sバージョンアップ

# はじめに

GKEではk8sのバージョンアップは3世代離れてしまうと面倒くさくなるので、
頻繁(とは言っても3ヶ月ごと)にアップしていくことを強くお勧めします。

ただし、GKE上で無邪気にk8sバージョンアップしてしまうと、必ずサービスのダウンタイムが発生してしまいます。
そのため、ダウンタイムを発生させたくない場合には工夫する必要があります。

今回は、DNS切り替えにより解決する手順を残します。

**（追記）リージョナルクラスタの場合はマスターノードに冗長性があるため基本的に当方法は不要です。
普通にupgradeすればダウンタイム発生しません。**


# ざっくり手順

- 新しいクラスタを用意する
- DNS切り替える
- 古い環境消す

# 新しいクラスタを用意する

- 新しいクラスタを作る

`--cluster-version`は必ずバージョンアップ後のものを指定してください。デフォルトが使われると意図したものが指定されない場合があります。
`--region`は[指定していた方が良いです](http://nokok.hatenablog.com/entry/2017/12/20/000043)。

```bash
gcloud beta container clusters create cluster-new \
--region=asia-northeast1 \
--network=default \
--cluster-version=1.10.2-gke.1 \
--project=your_project \
--no-enable-autorepair
```

- コンポーネントをデプロイする

お手持ちのk8sのマニフェストをapplyしてください。
持ってない場合は頑張って作ってください。

```bash
kubectl config current-context # 念のため現在どのクラスタを向いているのか確認
kubectl config get-contexts # 一覧取得
kubectl config set-context {cluster_gke_cluster_new} # 向いてなかったら向ける
kubectl apply -f hogehoge.yaml
```

- static ipが割り振られるのを待つ

GKEなら`ingress`か`Serviceのtype:LoadBalancer`を使っていると思います。
待ってれば、そのうち割り振られます。

```bash
kubectl get ing
NAME           HOSTS     ADDRESS         PORTS     AGE
test-ingress   *         XXXXXXXXX   80, 443   1h
```

はい、割り振られたら覚えておいといてください。

# DNS切り替え

ドメインで公開してないとかの人はまあこれ以降は参考程度にお願いします。
ここではGCPのホスティングサービス前提で話します。

- uptime checkを設置

DNS切り替えの前にちゃんとサービスが切り替わってるのか確認しながらやるためにやりましょう。

StackDriver -> Monitoring -> Uptime Checks -> Add Uptime Check

こんな感じで間隔は短めで設定します。

<img src=https://qiita-image-store.s3.amazonaws.com/0/12060/84eac36d-a022-22e1-73f6-c8530a3e1aff.png width=50%/>

- ローカルからも周期的に殴っておきましょう

上記のuptime checkはいろんな地域から殴ってくるのでとても良いのですが、周期が最短1分なのできになる人はローカルでも殴ると良いです。
今回は[vegeta](https://github.com/tsenart/vegeta)を使います。

１秒周期で３時間以上ひたすら殴る設定です。
この状態で放置しておきます。

```
echo "GET https://hogehoge.com" | vegeta attack -duration=10000s -rate=1 -insecure | tee results.bin | vegeta report
```

- アクセスログをみる

StackDriver -> LoggingでLBのログを見ておきましょう。
Cloud HTTP Load Balancer -> 新旧それぞれのLBを選択しておいて２窓で開いておきます。

- レコードセット

ようやくDNS切り替えです。

Network Services -> Cloud DNS
ここで該当のレコードをさっき控えたIPに切り替えます。

前述したアクセスログには、uptime checkやvegetaで殴っているログが出ると思います。
**新ログにログが出ていて 且つ 旧ログにアクセスがこなくなった**時点で切り替えはできているとしましょう。

もしかしたら、事前に１日前くらいからDNSレコードのTTLを短くしておけば、この切り替えが早いかもしれません。

# 旧環境を消す

いらないので消しましょう。情けはいりません。

```bash
gcloud container clusters delete cluster-old --region=asia-northeast1
```

以上
Circular positioningで更に柔軟な配置

# 検証環境
この記事の内容は、以下の環境で検証した。
* Android Studio 3.0.2
* constraint-layout 1.0.2

# はじめに
ConstrantLayoutには様々な設定項目があります。
下記の記事で記述したように、今回は「Centeringとbias」のみについて説明します。

[ConstraintLayoutを理解する前に・・・](https://qiita.com/naoi/items/aedc985e49669f8fff11)

また、レイアウトの配置やViewの基礎知識は下記を参照してください。

[ConstraintLayoutのRelative positioningの基本](https://qiita.com/naoi/items/79416483cce89c058a95)


# 説明前の注意点
Curcular positioningを使用する前に注意点があります。
下記の確認すると「Added in 1.1」とあります。

https://developer.android.com/reference/android/support/constraint/ConstraintLayout.html#CenteringPositioning

ということは、build.gradleを確認しておかないとですね。
現時点で私のbuild.gradleはこうなっていました。

```groovy:build.gradle
dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
 }
```

これでは有効になっていませんね。
下記のように修正しました。

```groovy:build.gradle
dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
 }
```

無事に動きました。

# 配置イメージ
レイアウトファイルを確認するまえに、配置の時に出てくる属性の意味を理解して置かなければなりません。
下図で、属性に設定する箇所を表してみました。特に丸囲み数字が対象となります。

![Circular_Architecture.png](https://qiita-image-store.s3.amazonaws.com/0/99960/a985ae8a-93bb-61fe-a3fe-a2ff3b8ecc20.png)



この図では、goodというTextViewの中心点からの半径と角度でHelloを配置しています。
goodのTextViewがHelloの配置の基準点となっています。
上図見ながら、属性としてはどのような設定項目になっているか確認します。

|番号|属性名|値|
|:--:|:--:|:--|
|①|layout_constraintCircleAngle|基準点の頂点を0として、時計回りで0度〜360度で設定します。|
|②|layout_constraintCircle|基準点のViewを設定します。|
|③|layout_constraintCircleRadius|基準点からの半径を指定します。|

# Designタブのイメージ
レイアウトファイルで設定すると、Designは下記のようなイメージになります。

![circular_design.png](https://qiita-image-store.s3.amazonaws.com/0/99960/8d979733-a0cb-aa7b-35a5-4b6359bafbe9.png)


# まとめ
これまではViewの右や下などの配置だけでしたが、Circular positioningはかなり柔軟なViewの配置が可能ですね。
.gitignoreの変更が適用されない場合の対処法

##コメント
例によって例のごとくいろいろ調べた結果出てきた内容をまとめた次第です。
自分の場合はgitの管理にsource treeを使っているのでそれ準拠になります。

###原因
---------
gitignoreが適用される前に一度gitの管理対象に含まれてしまったファイル類については、後からgitignoreに追加してもignoreが反映されません。

##どうするか
0,自身の編集物がなく、プッシュ、プルも一切ない状態であること
1,source tree右上の方の`ターミナル`をクリック
 ![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/243544/6d5bec72-4c4f-0c8f-82af-e4ec2ba8af49.png)

2,ターミナル上で以下の4コマンドを実行
git rm -r --cached .
git add .
git commit -m "適当なコメントを入れてください"
git push origin "ブランチ名"

###チーム開発の場合それぞれのメンバーが上記手順を行わないとgitignoreが適用されないようなのでコミットする前に必ず実行してください。
simvメモ

# はじめに
完全にいたずら書きです。

# メモ
- マニュアル https://github.com/kovisoft/slimv/blob/master/doc/slimv.txt
- swakn起動, repl: ,c
- swankの終了: ,Q
サーバー立ち上げたままだと前の環境引きずるのでコード書き換えすぎて混乱したときはこれ。
- replのクリア: ,-
- formをtypeしてreplにぶっこむ: ,v
- describe: ,s
- hyperspec: ,h
- undef function: ,u
- in-package: ,g
- compile-file: ,F
- compile(defun): ,D

# その他メモ
- 例外機構 https://qiita.com/matsud224/items/722dcc22f1b735adf076
- テスト https://qiita.com/asciian/items/1c4fa904f42ecb9d872b
- slimvのデバッグ周りのチュートリアル https://kovisoft.bitbucket.io/tutorial2.html https://kovisoft.bitbucket.io/tutorial3.html
- デバッグ一般 http://glider-gun.github.io/blog/2015/12/19/debugging-lisp-part-1-zai-konpairu/
- let g:slimv_swank_cmd = "! xterm -e ros -e '(ql:quickload :swank) (swank:create-server :dont-close t)' wait &"
  :dont-close tすると,Qせんで,cしてもswankが1つだけ立ち上がっているのをつかわれる
- ,SYMBOLはsbclではlistではない(仕様で処理系依存) https://stackoverflow.com/questions/33724300/macros-that-write-macros-compile-error
- style guide https://lisphub.jp/doc/google-common-lisp-style-guide/lispguide.xml
- asdf http://keens.github.io/blog/2014/11/30/quicklisp/
neologd 組み込みlucene kuromoji コンパイル環境のdocker化

mecab-ipadic-neologd辞書を組み込んだ、lucene kuromojiを作成するコンパイル環境をdocker化しました。

## 環境構築
```
git clone https://github.com/ft28/practice.git
cd solr/neologd

docker-compose build
```

## jar ファイル作成
```
docker-compose run jdk ./solr/build.sh
```

上記コマンドを実行すると、mecab, mecab-ipadic, lucene-solrの各リポジトリを取得、dockerコンテナ内でコンパイルが実行され、neologd辞書を含んだ lucene kuromoji jarファイルがsolrディレクトリ以下に作成されます。このjarファイルを、既存jarファイルと置き換えると、solrでneologd辞書を使った形態素解析が可能になります。

## neologd 辞書のコンパイルオプション
build.sh でneologd辞書をコンパイルする際には、[build.shのL59]
(https://github.com/ft28/practice/blob/master/solr/neologd/solr/build.sh#L59) のように、--ignore_noun_ortho　オプションを追加して、一般名詞/固有名詞の表記ゆれ無効にしました。

--ignore_noun_ortho を指定せず辞書を作成して、solr の schema.xmlで、solr.JapaneseBaseFormFilterFactory をフィルタとして登録すると、

```xml:schema.xml
    <fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
      <analyzer>
        <!-- トークン化 -->
        <tokenizer class="solr.JapaneseTokenizerFactory" mode="normal" />

        <!-- 基本形になおす -->
        <filter class="solr.JapaneseBaseFormFilterFactory"/>
      </analyzer>
    </fieldType>
```
「ミツミ」を解析した時の最終出力が「三身」になってしまうという問題が発生します。

|   |   |   |
|---|---|---|
|JT |text|ミツミ|
|   |baseForm|三身|
|JBFF|text|**三身**|

これは neologd-common-noun-ortho-variant-dict-seed.20170228.csv 内で以下のようなデータが作成されているため発生します。

```csv:neologd-common-noun-ortho-variant-dict-seed.20170228.csv
ミツミ,1285,1285,5527,名詞,一般,*,*,*,*,三身,ミツミ,ミツミ
```
そこで、上記辞書データファイルを作成しないよう --ignore_noun_ortho を指定して、辞書データ作成を行います。

## まとめ
普段は、サーバープロセスを動かすために使っているdockerコンテナですが、今回はコンパイル実行環境としてdockerコンテナを活用し、dockerさえ動いていればコマンド一つで必ずコンパイル可能な環境を実現できました。

## 参考
[修正されたmecab-ipadic-neologdの辞書を、Lucene Kuromojiに適用してみる]
(http://d.hatena.ne.jp/Kazuhira/20150316/1426520209)

* jar ファイルのコンパイル手順はこちらのサイトを参考にしました。
swagger-codegenで吐き出されるnpmのpackage名を変更する

[swagger-codegen](https://github.com/swagger-api/swagger-codegen)を使って、デフォルトで以下みたいに吐き出すと、タイトルを使用した適当な名前になってしまう

```console
$ swagger-codegen generate -i swagger.json -l javascript -o ./codegen
```

何かオプションはないのかと、各言語のヘルプを見る。

```console
$ swagger-codegen config-help -l javascript

CONFIG OPTIONS
	sortParamsByRequiredFlag
	    Sort method arguments to place required parameters before optional parameters. (Default: true)

	ensureUniqueParams
	    Whether to ensure parameter names are unique in an operation (rename parameters that are not). (Default: true)

	allowUnicodeIdentifiers
	    boolean, toggles whether unicode identifiers are allowed in names or not, default is false (Default: false)

	sourceFolder
	    source folder for generated code (Default: src)

	localVariablePrefix
	    prefix for generated code members and local variables

	invokerPackage
	    root package for generated code

	apiPackage
	    package for generated api classes

	modelPackage
	    package for generated models

	projectName
	    name of the project (Default: generated from info.title or "swagger-js-client")

	moduleName
	    module name for AMD, Node or globals (Default: generated from <projectName>)

	projectDescription
	    description of the project (Default: using info.description or "Client library of <projectName>")

	projectVersion
	    version of the project (Default: using info.version or "1.0.0")

	licenseName
	    name of the license the project uses (Default: using info.license.name)

	usePromises
	    use Promises as return values from the client API, instead of superagent callbacks (Default: false)

	emitModelMethods
	    generate getters and setters for model properties (Default: false)

	emitJSDoc
	    generate JSDoc comments (Default: true)

	useInheritance
	    use JavaScript prototype chains & delegation for inheritance (Default: true)

	hideGenerationTimestamp
	    Hides the generation timestamp when files are generated. (Default: true)

	useES6
	    use JavaScript ES6 (ECMAScript 6) (beta). Default is ES5. (Default: false)
```

`projectName` が使えそうなので、`-D`オプションにくっつける。

```console
$ swagger-codegen generate -i swagger.json -l javascript -o ./codegen -DprojectName=my-project
```


`config-help` コマンド重要だねというお話でした。
【Rails】コントローラ作成の小さなサンプルメモ

画面を表示するまで。

# 作成するコントローラ

PagesController

# メモ

```zsh:console
 % rails g controller pages
Running via Spring preloader in process 17257
      create  app/controllers/pages_controller.rb
      invoke  erb
      create    app/views/pages
      invoke  test_unit
      create    test/controllers/pages_controller_test.rb
      invoke  helper
      create    app/helpers/pages_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/pages.coffee
      invoke    scss
      create      app/assets/stylesheets/pages.scss
```

作成されたコントローラ↓

```ruby:pages_controller.rb
class PagesController < ApplicationController
end
```

画面表示のためにはあと、ルーティングとビューとアクションの追加が必要

## ルーティング追加

```ruby:routes.rb
resources :pages
```

## ビュー追加

```erb:app/views/pages/index.erb
pages/index.erb
```

## アクション追加

indexアクションの追加

```ruby:pages_controller.rb
class PagesController < ApplicationController
  def index
  end
end
```

明示的に `render template: "pages/index"` とビューファイルを示すこともできるが、ビューファイルが `app/vies/[コントローラ名]/[ビュー名].tml.erb` 命名規則に従っていれば、省略可能。
Python > regex > 書式文字列("[Text:"と"]")だけを置換する

```txt:動作環境
ideone (Python 3.5)
```


### 処理

```
[Tex:Z_c = \frac{1}{2 \pi f C}]
```

を下記のように置換したい (Hatena DiaryからMarkdown形式への置換)。

<pre>
```math
Tex:Z_c = \frac{1}{2 \pi f C}
```
</pre>

### code v0.1

https://ideone.com/BNIbqC

```py
import re

intxt = "[Tex:Z_c = \frac{1}{2 \pi f C}]"
# 以下に置き換える
#```math
#Tex:Z_c = \frac{1}{2 \pi f C}
#```

print(intxt)

wrk = re.sub('\[Tex', '```math```', intxt)
print(wrk)

```

```txt:run
[Tex:Z_c = rac{1}{2 \pi f C}]
```math```:Z_c = rac{1}{2 \pi f C}]
```

右の"]"の扱いをどうするか思案中。

### code v0.2

https://ideone.com/cVI4UL

```py
import re

intxt = r"[Tex:Z_c = \frac{1}{2 \pi f C}]"
# 以下に置き換える
#```math
#Tex:Z_c = \frac{1}{2 \pi f C}
#```

def conv_tex(intxt):
    if r"[Tex:" in intxt:
        wrk = re.sub('\[Tex:', r'```math\r\n', intxt)
        wrk = re.sub(r']', r'\r\n```', wrk)
        return wrk
    return intxt

print(intxt)
res = conv_tex(intxt)
print(res)

```

<pre>
[Tex:Z_c = \frac{1}{2 \pi f C}]
```math
Z_c = \frac{1}{2 \pi f C}
```
</pre>

もうちょっと考えることはありそう。

### code v0.3

- 複数行への対応
- Tex表記以外の置換がおきないように対応

https://ideone.com/aqJcK5

```py
import re


def conv_tex(intxt):
    res = ""
    for elem in intxt.split('\n'):
        if len(res) > 0:
            res += '\r\n'

        if r"[Tex:" in elem:
            wrk = re.sub('\[Tex:', r'```math\r\n', elem)
            wrk = re.sub(r']', r'\r\n```', wrk)
            res += wrk
        else:
            res += elem
    return res

IN_TEXTXT1 = r"[Tex:Z_c = \frac{1}{2 \pi f C}]"
# 以下に置き換える
# ```math
# Tex:Z_c = \frac{1}{2 \pi f C}
# ```

# dummy text
IN_DMYTXT1 = '[[[AAA]]]'

intxt = IN_TEXTXT1 + '\r\n' + IN_TEXTXT1
intxt += '\r\n' + IN_DMYTXT1
print(intxt)
print('---')
res = conv_tex(intxt)
print(res)

```

<pre>
[Tex:Z_c = \frac{1}{2 \pi f C}]
[Tex:Z_c = \frac{1}{2 \pi f C}]
[[[AAA]]]
---
```math
Z_c = \frac{1}{2 \pi f C}
```

```math
Z_c = \frac{1}{2 \pi f C}
```

[[[AAA]]]
</pre>

~~これで使えるだろうか。~~

### code v0.4

結局、複数行対応ではなく、１行単位の処理を使った。

[はてなダイアリー to Markdown > v0.5 > Tex:表記対応](https://qiita.com/7of9/items/fe4e3d46dcd0369a3849)
のcode v0.5のconv_tex()
TeamViewerが急遽使えなくなったのでChromeリモートデスクトップに乗り換えた

TeamViewerから商用利用されていると判断されて、使用中にセッションが強制終了。
申請すればまた使えるようになるらしいけど、無体な仕打ちを受けたのでツールを乗り換えてやろうと思う。
Chromeリモートデスクトップを入れてみたら結構快適だったので共有。

[Chrome リモートデスクトップ 追加用URL](https://chrome.google.com/webstore/detail/chrome-remote-desktop/gbchcmhmhahfdphkhkmpfmihenigjmpp?hl=ja)
[memo] Vue.jsのコンポーネント内でaxiosを使ったときのasync/awaitの書き方


例えばログインAPIを呼び出したとき

```javascript
async submit() {
  const response = await this.$axios.$post('/api/login', {
    email: this.email,
    password: this.password,
  });
  console.log('login reponse', response);
  if (response.username) {
    this.authUser = response.username;
  }
},
```
Paging Libraryと既存のRepositoryクラスを使ってPagingする

# はじめに

Paging Libraryを使いたいが既にRepositoryパターンを適用している場合、公式ドキュメントにあるようなDaoの返り値に`DataSource.Factory` 使うことが難しいケースが多々あるかと思います。
この場合Repositoryをwrapして独自のDataSouceクラスを定義する手法を使うと、既存のRepositoryを活かしつつPaging Libraryを導入することができます。

ここでは以下のようなRepositoryを例にその方法を紹介していきます。

```kotlin
data class User(val id: Long, val name: String, val age: Int)

interface UserRepository {
   /**
    * [User.age]がageよりも大きく、[User.id]がoffsetIdよりも大きいUserを10件取得します
    */
   fun findOlderThan(age: Int, offsetId: Long?) : List<User>
}
```

# DataSourceの構築

DataSourceにはいくつかの抽象クラスが用意されており、データ構造に適したものを選択して使います。

https://developer.android.com/topic/libraries/architecture/paging/data#choose-data-source-type

ここでは`ItemKeyedDataSource`を使って構築してみます。

```kotlin
class UserDataSourceFactory(
    private val age: Int, 
    private val userRepository: UserRepository) : ItemKeyedDataSource<Long, User>() {
    
    override fun getKey(item: User): Long = item.id

    override fun loadInitial(params: LoadInitialParams<Long>, callback: LoadInitialCallback<User>) {
         userRepository.findOlderThan(age, null)
             .let { callback.onResult(it) }
    }

    override fun loadAfter(params: LoadParams<Long>, callback: LoadCallback<User>) {
         userRepository.findOlderThan(age, params.key)
             .let { callback.onResult(it) }
    }

    override fun loadBefore(params: LoadParams<Long>, callback: LoadCallback<User>) {
    }
}

```

`ItemKeyedDataSource<Key, Value>` のKeyには `取得済みがどこまでか`決定できるパラメータを使います。
ここでは `User.id` がその役割を担い `Key` にはLong型をあてています。

`Value` は取得対象のクラス、ここでは `User` がそれにあたります。

# DataSourceを使ってみる

独自に構築したDataSourceは以下のように使用します

1. `DataSource.Factory<Key, Value>` を継承したクラスでDataSourceを提供する
2. `LivePagedListBuilder` で1とpaging設定を施して`LiveData<PagedList<Value>>`を取得
3. 2を購読してviewに反映する

```kotlin
class UserViewModel(private val: userRepository: UserRepository) : ViewModel() {

    fun olderThan(age: Int): LiveData<PagedList<User>> {
         val dataSourceFactory = object : DataSource.Factory<Long, User> {
              override fun create(): DataSource<Long, User> = UserDataSourceFactory(age, userRepository)
         }
         return LivePagedListBuilder(
                   dataSourceFactory, 
                   PagedList.Config.Builder()...build()
         ).build()
    } 

}
```

これで既存のRepositoryクラスに変更を加えることなくPaging Libraryを導入することができるかと思います。

# 参考

https://developer.android.com/topic/libraries/architecture/paging/data

http://androidkt.com/rest-api-pagination-paging-library/