2012/07/15

RedmineとActiveRecordで番組チケット作成

プランとしてはこんな感じで
  1. 各局のEPGデータ取得のため60秒ずつ録画
  2. egpdumpでxmlに変換
  3. ActiveRecordでxmlからredmineチケットを作成
EPG取得用TS録画からxml変換は簡単
$ recfsusb2n -b [channel] 60 [channel].ts
$ epgdump [channel].ts [channel].xml
こんなで十分。これを必要なチャンネル分実行すればいい。

チケット(Issue)作成
ここが試行錯誤した部分。前記事でも書いたがActiveResourceでやろうと思ったけど思うように進まないので、ActiveRecordでやることにした。


番組データをチケットで表現

  • タイトル=>チケット題名
  • 内容=>チケット内容
  • 録画日=>開始日
  • カテゴリ=>チケットカテゴリ
  • 録画時間長さ=>予定工数(ほんとは時間だけど分のまま扱う)

カスタムフィールド(redmineは至る所にカスタムフィールドを追加できる)

  • 録画開始時間(数値)
  • チャンネル(文字列)
  • ジョブ番号(数値):録画ジョブ管理番号

こんな感じで対応させようと思う。


結果から書くと、こんな感じのスクリプトになった。
#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'rubygems'
require 'active_record'
require 'active_support/core_ext'

ActiveRecord::Base.establish_connection(
                                        :adapter => 'mysql2',
                                        :host => 'localhost',
                                        :username => 'redmine',
                                        :password => 'redmine',
                                        :database => 'redmine'
                                        )

class Issue < ActiveRecord::Base
end

class IssueCategories < ActiveRecord::Base
end

class CustomValues < ActiveRecord::Base
end

class CustomFields < ActiveRecord::Base
  # 使いたいな
end

$entry_count = 0
$ch_name = ''

#
# チケット登録
#
def entry_issue date, start, duration, title, desc, category, channel
  #  p date, start, duration, title, desc, category, channel
  #  return

  # カテゴリ
  cate = IssueCategories.first( :conditions =>
                                {:project_id => 1, :name => category } )
  cate = IssueCategories.create( :project_id => 1, :name => category ) if cate.nil?

  # マッチするチケットを検索
  if Issue.count( :conditions => { :start_date => date, :subject => title }) > 0
    return #登録済み
  end

  # チケット作成
  issue = Issue.create( :project_id => 1, #'番組プログラム'
                        :tracker_id => 1, #'番組'
                        :lft => 1,
                        :rgt => 2,
                        :status_id => 1,
                        :priority_id => 2,
                        :done_ratio => 0,
                        :author_id => 1,
                        :subject => title,
                        :description => desc,
                        :start_date => date,
                        :due_date => date,
                        :estimated_hours => duration,
                        :category_id => cate.id )

  # root_id 更新
  # NULLのままだとRedmineからのチケット更新でクラッシュする
  # 親がない場合は自分のIDを入れるらしい。自分? 更新しか無い?
  issue.root_id = issue.id

  if issue.save
    # 開始時間
    CustomValues.create( :customized_type => "Issue",
                         :customized_id => issue.id,
                         :custom_field_id => 1, #'開始時間'
                         :value => start )
    # チャンネル
    CustomValues.create( :customized_type => "Issue",
                         :customized_id => issue.id,
                         :custom_field_id => 2, #'チャンネル'
                         :value => channel )
  end

  $entry_count = $entry_count + 1

end # entry_issue

#
# TVプログラム登録
#
def entry_program prog
  start = Time.parse( prog['start'] )
  stop = Time.parse( prog['stop'] )
  duration = (stop - start) / 60

  date = start.to_date
  start = start.strftime("%H%M").to_i

#  channel = prog['channel'].gsub(/ontv.*/,'') + $ch_name
  channel = prog['channel']+ '.'+$ch_name
  category = prog['category'].join(".")
  title = prog['title']
  desc = prog['desc']
  desc = "" if desc.kind_of? Hash

  # 5分以下の短い番組は登録しない :todo: DBに設定できると良い
  return if duration <= 5

  # デスクリプションが無い番組は登録しない
  return if desc == ''

  title.gsub!(/(【二】|【デ】|【S】|【字】)/,"")

  entry_issue date, start, duration, title, desc, category, channel
end

#
# メイン
#
xml_file = ARGV.shift + '.xml'
h = Hash.from_xml( open(xml_file).read )["tv"]

h.each_pair {|key,value|
  if key == 'channel'
    $ch_name = value['display_name']
  elsif key == 'programme'
    value.each {|prog| entry_program prog}
  end
}

puts "%d entried" % $entry_count

xml => hash
xmlからhashへの変換はactive_supportのcore_extにあるHashクラスを使って簡単に変換した。hashになればrubyで簡単にキーワードとデータを扱えるようになる。

entry_program
必要な項目を取り出して、タイトルとかちょっと加工する。
5分以下の短い番組は多分観ないし、それまで全部登録するとチケット数が多すぎないか?と思ったので排除した。
それから、メンテナンスや内容が書かれていない不明番組も排除するようにした。

entry_issue
まずカテゴリをチェックして新規なら登録する。
既に登録済みのチケットか確認する。
未登録なら必要項目をセットしてチケットを作成する。

カスタムフィールドの開始時間とチャンネルもチケットとリンクさせて作成する。
大体こんな手順で番組チケット作成が出来た。

注意点は、チケットアトリビュートのlft、rgt、及び root_id だ。
関連チケットや親子関係を表現するためのRailsテクニックらしいが、省略するとredmineがエラーを起こす。特に、root_idは親がない場合は自分のIDを入れておくものらしい。

便利

redmineをフロントエンドにした事で表示部分を全然作らないで済んだ。
チケット検索機能がもともと強力なので、番組検索が思い通り。
しかも検索内容をカスタムクエリーとして保存できるんで自分なりの分類が簡単。

出来てみると思いのほかマッチしそうなので、このまま進めてみようっと。

0 件のコメント:

コメントを投稿