ゲームスコアをユーザーごとにCronを使って定時CSV出力する

# はじめに
ここではゲームなどで得たスコアなどをユーザーごとに一覧表示させるためにCSV出力します
cronを使って定時処理することでいつでも最新のデータを取得できるようにします。

# やること

- rake タスクの作成し、ユーザーデータからCSVファイルを出力
- wheneverを使ってcronを設定し、定時実行処理をする

# 手順
### rake タスクを生成
[こちら](https://qiita.com/asadsexyimp/items/47375a12f7d05e812ff2)を参考にrakeタスクを作りましょう

テーブルでは以下のものを使っていきます

```ruby:db/schema.rb
ActiveRecord::Schema.define(version: 20190131081330) do

  create_table "games", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC" do |t|
    t.string "type", null: false
    t.integer "score", null: false
  end

  create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC" do |t|
    t.string "account", default: "", null: false
  end

end
```


タスク実行処理では以下のコードを書いていきます。

```ruby:lib/tasks/create_csv.rake
require 'csv'

namespace :create_csv do
  desc "ユーザースコアの表示"
  task :export => :environment do

    # スコアの順番を調整するためのネスト化したハッシュの初期化
    score_hash = Hash.new { |h,k| h[k] = {} } # スコアハッシュ keyにゲーム名とアカウント valueにスコア
    count_hash = Hash.new { |h,k| h[k] = {} } # カウントハッシュ keyにゲーム名とアカウント valueにスコアのカウント

    user_accounts = User.all.map(&:account) # ユーザーのアカウント名を格納[配列]

    User.all.map do |user|
      account = user.account

      user.games.each do |game|
        type = game.type
        score = game.score

        #　ネスト化したハッシュを探るため、digを使って２つのキーが存在しない場合はnilを返す
        if score_hash.dig(type + " 01", account).nil? # 一人のユーザーに対し、そのゲームのスコアが存在しない場合

          # スコア・カウントハッシュにゲーム名とアカウントをキーに入れて valueにスコア
          score_hash[type + " 01"][account] = score 
          count_hash[type][account] = 1 

        else # すでにその人がゲームをプレイして２回目以降のスコアを入れたい時

          # カウントハッシュを足しながらストリング変換で新たなハッシュを格納
          count_hash[type][account] = count_hash[type][account].to_i + 1 
          new_type = test_name + " " + count_hash[test_name][account].to_s.rjust(2, "0") # 2桁で0詰めして後でソートしやすくする
          score_hash[new_type][account] = score
        end

      end
    end

    # ハッシュのソート、最初のゲーム名のキーで順番入れ替え、そのままだと配列で変えるので.to_hでハッシュ変換
    hash = score_hash.sort_by { |k, k2, v| k }.to_h

    csv_export = CSV.generate do |csv|
      # 1行目
      # ゲームの名前とユーザーの名前を出力flattenは配列を全ての要素を取り出してくれます
      csv << ["game_name",user_accounts].flatten 

      # ２行目以降
      hash.each_key do |key| #ここでのkeyはゲーム名

         csv <<
          [key,
          User.all.map do |user|
            hash.dig(key, user.account) # スコアが存在すればスコア、なければnilを出力
          end].flatten

      end

    end
    # 現在時間を設定して
    current_time = DateTime.now.to_s
    File.open("./" + current_time + "_user.csv", 'w') do |file|
      file.write(csv_export)
    end
    # ダウンロード
    stat = File::stat("./" + current_time + "_user.csv")
    send_file("./" + current_time + "_user.csv", :filename => "./" + current_time + "_user.csv", :length => stat.size)
  end


end
```
これで、ローカルにデータを落とす準備はできました。
ここからcronを使って定時実行するための処理をしていきます。

# wheneverを使って定時処理
[参考](https://qiita.com/yumiyon/items/388fbb84450f49a6ab0d)
### gemのwheneverをgemfileに追加してbundle実行

```ruby:Gemfile
gem 'whenever'
```
```
bundle install --path vendor/bundle
```

### 設定ファイルを作成
```
bundle exec wheneverize . 
    [add] writing `./config/schedule.rb'
    [done] wheneverized! 
```
### できたファイルに処理を追加

```config/schedule.rb
require File.expand_path(File.dirname(__FILE__) + "/environment")
set :output, 'log/cron.log'

# 1日毎に回す
 every 1.day, :at => '0:00 am' do
   rake create_csv:export # タスク処理
 end
```

## cronに反映する

- 設定の確認 

```
bundle exec whenever 
bundle exec crontab -e
```
- cronにデータを反映 

```
bundle exec whenever --update-crontab 
```
- cronからデータを削除
 
```
bundle exec whenever --clear-crontab
```
---

### これで定期的にcsvをローカルに出力することができました！！
それにしてもwhenever便利だなぁ