Tbpgr Blog

元エンジニア 人事 tbpgr(てぃーびー) のブログ

todo_2_json.rb

ソースコード

# -*- coding: Windows-31J -*-

=begin rdoc
=TODOをJSON形式に変換する
=end
class Todo2Json

  # TODO区切り文字:|
  TODO_SEPARATOR = '|'
  # デイリー区切り文字:■
  DAILY_SEPARATOR = ''
  # TODO区切り文字数:5
  TODO_LENGTH = 5
  # task項目位置:0
  COLUMN_TODO_TASK = 0
  # estimate項目位置:1
  COLUMN_TODO_ESTIMATE = 1
  # actualResultsTime項目位置:4
  COLUMN_TODO_ACTUAL_RESULT_TIME = 4
  # date項目位置:0
  COLUMN_DAILY_DATE = 0
  # 不正なマンスリー:'incorrect input monthly'
  ERR_MONTHLY = 'incorrect input monthly'

=begin rdoc
==一行のTODOをJSON形式に変換する
以下の形式のTODOを
  タスク|見積|開始時間|終了時間|実績時間
以下の形式のJSONに変換する
	"todo":{
		"task":"タスク",
		"estimate":"見積",
		"actualResultsTime":"実績時間"
	}
===Param todo 1行のTODO
===Return TODOを変換したJSON文字列
===Contract TODOが正しい書式の場合はJSONに変換して返却する
===Contract TODOが不正な値の場合は空文字を返却する
=end
  def get_todo_json(todo)
    return '' unless is_correct_todo todo
    columns = todo.split Todo2Json::TODO_SEPARATOR
    todo_json =<<"EOS"
"todo":{
  "task":"#{columns[COLUMN_TODO_TASK]}",
  "estimate":"#{columns[COLUMN_TODO_ESTIMATE]}",
  "actualResultsTime":"#{columns[COLUMN_TODO_ACTUAL_RESULT_TIME].chomp}"
}
EOS
    return todo_json.chomp
  end

=begin rdoc
==デイリーをJSON形式に変換する
以下の形式のデイリーを
  2012/02/08
  タスク1|見積1|開始時間1|終了時間1|実績時間1
  タスク2|見積2|開始時間2|終了時間2|実績時間2
  タスク3|見積3|開始時間3|終了時間3|実績時間3
以下の形式のJSONに変換する
  {"date":"2012/02/08"},
  [
    {
      "todo":{
      "task":"タスク1",
      "estimate":"見積1",
      "actualResultsTime":"実績時間1"
      }
    },
    {
      "todo":{
        "task":"タスク2",
        "estimate":"見積2",
        "actualResultsTime":"実績時間2"
      }
    },
    {
      "todo":{
        "task":"タスク3",
        "estimate":"見積3",
        "actualResultsTime":"実績時間3"
      }
    }
  ]
===Param daily デイリー
===Return デイリーを変換したJSON文字列
===Contract デイリーが正しい書式の場合はJSONに変換して返却する
===Contract デイリーが1行しかない場合は不正とし、空文字を返す
===Contract デイリーが2行異常があるが、有効なTODOが1件もない場合は不正とし、空文字を返す
=end
  def get_daily_json(daily)
    return '' unless is_correct_daily daily
    
    lines = daily.lines.to_a
    # 日付部の設定
    daily_json = <<"EOS"
{"date":"#{lines[COLUMN_DAILY_DATE].chomp!}"},
[
EOS

    # TODO部の設定
    (1..lines.length-1).each {|num|
      todo_json = get_todo_json lines[num]
      daily_json +=<<"EOS"
  {
#{todo_json}
  },
EOS
    }
    # 余分な改行文字とカンマを削除
    daily_json = daily_json.chomp!.slice(0,daily_json.size-1)
    daily_json += "\n]\n"
    return daily_json
  end

=begin rdoc
==マンスリーをJSON形式に変換する
以下の形式のマンスリーを
  ■2012/02/08
  タスク0208-1|見積0208-1|開始時間0208-1|終了時間0208-1|実績時間0208-1
  ■2012/02/09
  タスク0209-1|見積0209-1|開始時間0209-1|終了時間0209-1|実績時間0209-1
  ■2012/02/10
  タスク0210-1|見積0210-1|開始時間0210-1|終了時間0210-1|実績時間0210-1
以下の形式のJSONに変換する
	[
		{"date":"2012/02/08"},
		[
			{
				"todo":{
				"task":"タスク0208-1",
				"estimate":"見積0208-1",
				"actualResultsTime":"実績時間0208-1"
				}
			}
		]
	],
	[
		{"date":"2012/02/09"},
		[
			{
				"todo":{
				"task":"タスク0209-1",
				"estimate":"見積0209-1",
				"actualResultsTime":"実績時間0209-1"
				}
			}
		]
	],
	[
		{"date":"2012/02/10"},
		[
			{
				"todo":{
				"task":"タスク0210-1",
				"estimate":"見積0210-1",
				"actualResultsTime":"実績時間0210-1"
				}
			}
		]
	]
===Param monthly マンスリー
===Return マンスリーを変換したJSON文字列
===Contract マンスリーが正しい書式の場合はJSONに変換して返却する
===Contract マンスリーが空文字の場合はエラーとする
===Contract マンスリーがnilの場合はエラーとする
===Contract デイリーが1つもない場合はエラーとする
===Contract デイリーの戻り値が空文字だった場合はJSONのハッシュを追加しない
===Contract 全デイリーの戻り値が空文字だった場合はエラーとする
=end
  def get_monthly_json(monthly)
    raise Todo2Json::ERR_MONTHLY unless is_correct_monthly monthly
    # 区切り文字を分割してマンスリーからデイリーの配列を取得
    days = monthly.split Todo2Json::DAILY_SEPARATOR
    monthly_json = ''
    valid_count = 0
    # 最初の要素は削除する(■yyyy/mm/ddから始まるため)
    days.delete_at(0)
    days.each {|daily|
      daily_json = get_daily_json daily
      # 有効なデイリーではなかったら次のデイリーへ
      next if daily_json.empty?
      valid_count += 1
      daily_json = <<"EOS"
  [
#{daily_json.chomp}
  ],
EOS
      monthly_json += daily_json
    }
    raise Todo2Json::ERR_MONTHLY if valid_count == 0
    # 余分な改行文字とカンマを削除
    monthly_json = monthly_json.chomp!.slice(0,monthly_json.size-1)
    monthly_json = "[#{monthly_json}]"
    return monthly_json
  end

=begin
==マンスリーファイル出力
指定したファイルに対してマンスリーからjsonへの変換処理を行い、
結果を同フォルダに出力する。
===Param monthly_file_path マンスリーのファイル名
===Contract 前提条件:ファイルはtxtのみ(等メソッド以前にチェック済みとする)
===Contract 結果ファイルはマンスリーのtxtと同フォルダに出力する
===Contract 出力ファイル名は入力ファイル名.jsonとする
=end
  def output_json_file(monthly_file_path)
    monthly_file = open(monthly_file_path)
    monthly = monthly_file.read
    monthly_file.close
    
    monthly_json = get_monthly_json(monthly)
    monthly_json_file = open(monthly_file_path.sub('.txt','.json'), "w")
    monthly_json_file.puts monthly_json
    monthly_json_file.close
  end

=begin rdoc
==TODOが正しい形式か確認する
TODOの形式
  タスク|見積|開始時間|終了時間|実績時間
===Param todo 1行のTODO
===Return 正しいTODOならtrue、不正なTODOならfalse
===Contract TODOがnil、空文字以外かつ区切り文字が4つならば正しい書式とする
===Contract TODOがnilなら不正
===Contract TODOが空文字なら不正
===Contract TODOの区切り文字数が3以外なら不正
=end
  private
  def is_correct_todo(todo)
    return false if is_empty_nil todo
    return false unless (todo.split Todo2Json::TODO_SEPARATOR).size == Todo2Json::TODO_LENGTH
    return true
  end
  
=begin rdoc
==デイリーが正しい形式か確認する
デイリーの形式
  日付
  タスク|見積|開始時間|終了時間|実績時間
===Param daily 1日分のTODO
===Return 正しいデイリーならtrue、不正なデイリーならfalse
===Contract デイリーがnil、空文字以外かつ2行以上かつ有効なTODOが1件以上あるならば正しい書式とする。
===Contract デイリーが空文字なら不正とする
===Contract デイリーがnilなら不正とする
===Contract デイリーが1行しかないなら不正とする
===Contract デイリーに含まれるTODOが1件以上あるが全て無効の場合は不正とする
=end
  private
  def is_correct_daily(daily)
    return false if is_empty_nil daily
    # デイリー全体が2行以上なければエラー
    return false unless daily.lines.to_a.length >= 2
    lines = daily.lines.to_a
    # TODO部のチェック
    (1..lines.length-1).each {|num|
      return false unless is_correct_todo(lines[num])
    }
    return true
  end
  
=begin rdoc
==マンスリーが正しい形式か確認する
マンスリーの形式
  タスク|見積|開始時間|終了時間|実績時間
===Param monthly 1日分のマンスリー
===Return 正しいマンスリーならtrue、不正なマンスリーならfalse
===Contract マンスリーがnil、空文字以外かつ有効なデイリーが1件以上ある場合は正しい書式とする。
===Contract マンスリーが空文字なら不正とする
===Contract マンスリーがnilなら不正とする
===Contract マンスリーを区切り文字で分割して要素数が2未満の場合は不正とする
=end
  private
  def is_correct_monthly(monthly)
    return false if is_empty_nil monthly
    days = monthly.split Todo2Json::DAILY_SEPARATOR
    return false if days.size < 2
    return true
  end
  
=begin rdoc
==emptyかnilを判定
===Param str emptyかnilを含む可能性のあるオブジェクト
===Return emptyかnilならtrue,それ以外ならfalse
===Contract emptyならtrue
===Contract nilならtrue
===Contract emptyかnil以外ならfalse
=end
  private
  def is_empty_nil(str)
    return str.nil? || str.empty?
  end

end