Redmine MS Project インポートプラグイン開発記録 (1)
MS Project のファイル(XML形式)を Redmine のチケットとしてインポートするプラグインを(ちょっとだけ)作ったので、経過をまとめておきます。
ソースは github に置いてあります。
http://github.com/suer/redmine_ms_projects
動機
Redmine って非常にいい感じの issue 管理システムで、Trac と違って複数プロジェクトを管理できたりで普段から使っているのですが、こんないいシステムでも使っていると不満も出てくるものです。
普段、通常プロジェクトが開始するときには線表を引いています。
それで、実際に仕事にとりかかる時に Redmine にチケットを発行していくのですがこれがまためんどくさい。
せっかく線表を引いてるのにチケットを改めて発行するなんて、なんて二度手間。と、思ったので、じゃあ MS Project を Redmine に直接インポートすればいいんじゃね? と考えたのが始まりです。
ちなみに Excel で作成した線表なんて相手にしません。めんどくさそうだから。
開発経過
プラグインディレクトリの作成
プラグインディレクトリの作成から基本的な開発方法については id:mallowlabs:20081121:1227298411 を参照。
プラグインディレクトリは
RAILS_ROOT/vendor/plugins/redmine_ms_projects
としました。
このディレクトリをPLUGIN_ROOTとします。
MS Project ファイルの読み込み
.msp ファイル(バイナリ)を解析するのは面倒なので、MSPをXML形式で保存したものを相手にすることにします。
[PLUGINROOT/app/views/msprojects/index.html.erb] <h2><%= l(:msproject) %></h2> <% form_for :ms_project, :url => {:action => 'index', :project_id => @project }, :html => { :multipart => true } do |f| %> <%= file_field :file, :msproject %> <%= submit_tag l(:button_save) %> <% end %> <%= @added_tasks.size if @added_tasks %>
Rais の基本通りといった感じですが、注意点といえば、ファイルをアップロードする場合は :multipart => true を忘れないこと。
私はこれをよく忘れてハマります。
さて、MSP の XMLファイルを解析するために以下のようなヘルパを書いておきます。
[PLUGINROOT/app/helpers/msprojects_helper.rb] require "rexml/document" module MsprojectsHelper def parse_ms_project xml tasks = [] doc = REXML::Document.new xml doc.elements.each('//Task') do |task_tag| task = Task.new name = task_tag.elements['Name'] task.name = name.text if name date = Date.new start_date = task_tag.elements['Start'] task.start_date = start_date.text.split('T')[0] if start_date finish_date = task_tag.elements['Finish'] task.finish_date = finish_date.text.split('T')[0] if finish_date create_date = task_tag.elements['CreateDate'] date_time = create_date.text.split('T') task.create_date = date_time[0] + ' ' + date_time[1] if start_date tasks << task end tasks end end
汚いなorz
やってるのは Taskタグを探してきて各種データを格納してるだけです。
メインのコントローラ
[PLUGINROOT/app/controllers/msprojects_controller.rb] class MsprojectsController < ApplicationController before_filter :find_project, :authorize, :only => :index helper :msprojects include MsprojectsHelper def index xml = "" tracker = Tracker.find 1 @added_tasks = [] if request.post? xml = params[:file][:msproject].read @tasks = parse_ms_project(xml) @tasks.each do |t| param = { :subject => t.name } issue = Issue.new param issue.project = @project issue.tracker = tracker issue.author = User.current issue.start_date = t.start_date issue.due_date = t.finish_date issue.updated_on = t.create_date @added_tasks << t if issue.save end end end private def find_project @project = Project.find(params[:project_id]) end end
- fileアップロードフォームから受け取ったファイルを読み込む
xml = params[:file][:msproject].read
- 先程作成したヘルパ parse_ms_project で MSP の XMLファイルを解析
@tasks = parse_ms_project(xml)
- Issueオブジェクトを生成。各種データを入力
簡単ですね
@tasks.each do |t|
param = {
:subject => t.name
}
issue = Issue.new param
issue.project = @project
issue.tracker = tracker
issue.author = User.current
issue.start_date = t.start_date
issue.due_date = t.finish_date
issue.updated_on = t.create_date
@added_tasks << t if issue.save
end
注意点としては、Issue クラスは Project クラスとか Tracker クラスとかいろんなクラスに依存があるので、単純にハッシュを渡して new できません。
あと project と tracker 属性は必須なので必ず埋めてやる必要があります。
(tracker は決めうちで 1 にしてますorz)
まとめ
以上で大体できてるわけですがやっつけすぎて実用に耐えません。
既知の問題は以下の通りです。
- MSProjectはリソース(人)を割り当てる機能があるが、それを反映していない。MS Project に書かれた人と Redmine 上のアカウントを紐付けるインタフェースが必要
- トラッカーがバグで固定(!)。これも選択できるインタフェースが必要
- MSProject はタスクの階層やタスク間の依存が表現できる。これは、チケット間の関連付け機能で実現するとおもしろそう!
- 重複するタスクがあっても新規にチケットを作成してしまう
なんにせよ Redmine プラグイン、簡単でおもしろいと思います。
Redmine プラグイン開発は、model 間の関連が把握できれば、なんとかなることが多いです。それは、ソースを読むしか無いですが。
続く Redmine MS Project インポートプラグイン開発記録 (2) - すえひろがりっっっっ!