GanttProject のデータを ruby で パースしてみた
HappyMapper をつかって GanttProject のデータファイル(xml形式) をパースして、csv に変換させてみた。
出力書式を csv 形式にしたが、プログラム上では ファイル内容は ruby の object に変換できているので、各種の出力形式に変形させるのは簡単だ。
# xfy/xvcd で ganttproject データを表示/編集できるようにしたいとも思っている。
この作業を通じて、GanttProject の XML データ構造もほぼ 理解できたし、HappyMapper の内部もだいぶ 理解できた。
# freemind のデータや XBRL のインスタンスデータも、 happymapper で十分に扱えそうだ。
# parse した結果の Object を to_yaml して、memcached や、RDB に保持すると面白いかもしれない。
以下にソースコードと、実行例を示す。
# niffty の homepage にもファイル群を upload してある。
# See - http://homepage2.nifty.com/youichi_kato/src.html
# ganttproject データの解析(2009-01-01)
$ cat ganttproject.rb
# 2009-01-02 katoy
# ganttproject の xml データを happymapper で解釈する。
# happymapper (0.1.2)
# ganttroject 2.0.9
#
# happymapper には、次のパッチを当てる
# - root エレメントを解釈できるようにする。 (:deep の扱い)
# - 再帰構造を解釈できるようにする (:deep の扱い)
#
# *** happymapper-0.1.2-org/lib/happymapper.rb2009-01-01 16:29:34.000000000 +0900
# --- happymapper-0.1.2/lib/happymapper.rb2009-01-01 16:49:23.000000000 +0900
# ***************
# *** 62,67 ****
# --- 62,68 ----
# def parse(xml, o={})
# options = {
# :single => false,
# + :deep => true,
# :use_default_namespace => false,
# }.merge(o)
#
# ***************
# *** 73,79 ****
# node.register_default_namespace(namespace.chop)
# node.find("#{ namespace}#{get_tag_name}")
# else
# ! doc.find(get_tag_name)
# end
#
# collection = create_collection(nodes, namespace)
# --- 74,84 ----
# node.register_default_namespace(namespace.chop)
# node.find("#{ namespace}#{ get_tag_name}")
# else
# ! if options[:deep]
# ! doc.find("//#{ get_tag_name}")
# ! else
# ! doc.find("./#{ get_tag_name}")
# ! end
# end
#
require 'rubygems'
require 'happymapper'
require 'pp'
$KCODE = 'utf8'
module GANTT_PROJECT
class Field
include HappyMapper
tag "field"
attribute :id, String
attribute :name, String
attribute :width, Integer
attribute :order, Integer
end
class View
include HappyMapper
tag "view"
attribute :id, String
element :field, Field
end
class DayType
include HappyMapper
tag "day-type"
attribute :id, String
end
class Calendar
include HappyMapper
tag "calendar"
attribute :id, String
attribute :name, String
# has_one :default_week, DefaultWeek
# has_one :overriden_day_types, overrideDayTypes
# has_one :days, Days
end
class DayTypes
include HappyMapper
tag "day-types"
has_many :dayType, DayType
has_one :calendar, Calendar
end
class DateStr
include HappyMapper
tag "date"
attribute :year, String
attribute :month, String
attribute :date, String
end
class Calendars
include HappyMapper
tag "calendars"
has_many :date, DateStr
has_one :day_types, DayTypes
end
class Depend
include HappyMapper
tag "depend"
attribute :id, String
attribute :type, String
attribute :difference, String
attribute :hardness, String
end
class Task
include HappyMapper
tag "task"
attribute :id, String
attribute :name, String
attribute :color, String
attribute :meeting, String
attribute :start, Date
attribute :duration, Integer
attribute :complete, Integer
attribute :priority, Integer
attribute :expand, String
has_many :task, Task, :deep => false
has_many :depend, Depend
def print(nest = 0)
puts "#{' ' * nest}id=#{id} name=[#{name}]"
self.task.each do |t|
t.print(nest + 1)
end
end
end
class TaskProperty
include HappyMapper
tag "taskproperty"
attribute :id, String
attribute :name, String
attribute :type, String
attribute :valuetype, String
end
class Tasks
include HappyMapper
tag "tasks"
attribute :color, String
has_many :taskproperties, TaskProperty
has_many :task, Task, :deep => false
end
class Resource
include HappyMapper
tag "resource"
attribute :id, String
attribute :name, String
attribute :function, String
attribute :contacts, String
attribute :phone, String
end
class Resources
include HappyMapper
tag "resources"
has_many :resource, Resource
end
class Allocation
include HappyMapper
tag "allocation"
attribute :task_id, String, :tag=>"task-id"
attribute :resource_id, String, :tag=>"resource-id"
attribute :function, String
attribute :responsible, String
attribute :load, String
end
class Allocations
include HappyMapper
tag "allocations"
has_many :allocation, Allocation
end
class Role
include HappyMapper
tag "role"
attribute :id, String
attribute :name, String
end
class Roles
include HappyMapper
tag "roles"
attribute :rolesetName, String
has_many :roles, Role
end
class Project
include HappyMapper
tag "project"
attribute :name, String
attribute :company, String
has_many :view, View
has_one :description, String
has_one :calendars, Calendars
has_one :tasks, Tasks
has_one :allocations, Allocations
has_one :resources, Resources
end
end
if __FILE__ == $0
GANT_FILE = 'g.xml'
file_contents = File.read(GANT_FILE)
proj = GANTT_PROJECT::Project.parse(file_contents, :single => true)
proj.tasks.task.each do |t|
t.print
end
pp proj.view
pp proj.calendars
pp proj.allocations
pp proj.resources
end
$ gant2csv.rb
# 2009-01-02 katoy
# ganttproject のファイルから、csv 形式を得る。
require "ganttproject"
# resource.id -> name の Hash
@person_name = { }
def make_person_name_info (res)
res.resource.each do |r|
@person_name[r.id] = r.name
end
end
def search_persons(allocs, id)
p_names = []
allocs.allocation.each do |a|
if id == a.task_id
p_names << @person_name[a.resource_id]
end
end
p_names
end
def csv_task (task, allocs, nest = 1)
# finish = start + task.duration
start = task.start.strftime("%y/%m/%d")
peoples = search_persons(allocs, task.id)
puts "#{task.id},\"#{' ' * nest}#{task.name}\",#{start},#{task.duration},#{task.complete},\"\",\"#{peoples}\",\"\","
task.task.each do |t|
csv_task(t, allocs, nest + 1)
end
end
GANT_FILE = 'g.xml'
file_contents = File.read(GANT_FILE)
proj = GANTT_PROJECT::Project.parse(file_contents, :single => true)
make_person_name_info(proj.resources)
puts "ID,名前,開始日,期間,進捗,ウェブリンク,リソース,メモ,"
proj.tasks.task.each do |t|
csv_task(t, proj.allocations)
end
puts
puts "ID,名前,e-mail,電話番号,役割,"
proj.resources.resource.each do |r|
puts "#{r.id},\"#{r.name}\",\"#{r.contacts}\",\"#{r.phone}\",\"#{r.function}\","
end
$ cat g.xml // ganttproject の download ファイル中にあるサンプルデータを整形、改名したもの
<?xml version="1.0" encoding="UTF-8"?>
<project name="My house building project" company="Myself LLC"
webLink="www.myselfllc.net" view-date="2006-01-01" view-index="0"
gantt-divider-location="369" resource-divider-location="322" version="2.0">
<description>xxx</description>
<view zooming-state="default:6" id="gantt-chart" />
<view id="resource-table">
<field id="0" name="名前" width="54" order="0" />
<field id="1" name="役割" width="45" order="0" />
</view>
<!-- -->
<calendars>
<day-types>
<day-type id="0" />
<day-type id="1" />
<calendar id="1" name="default">
<default-week sun="1" mon="0" tue="0" wed="0" thu="0"
fri="0" sat="1" />
<overriden-day-types />
<days />
</calendar>
</day-types>
<date year="2006" month="2" date="14" />
</calendars>
<tasks color="#99ccff">
<taskproperties>
<taskproperty id="tpd0" name="type" type="default"
valuetype="icon" />
<taskproperty id="tpd1" name="priority" type="default"
valuetype="icon" />
<taskproperty id="tpd2" name="info" type="default"
valuetype="icon" />
<taskproperty id="tpd3" name="name" type="default"
valuetype="text" />
<taskproperty id="tpd4" name="begindate" type="default"
valuetype="date" />
<taskproperty id="tpd5" name="enddate" type="default"
valuetype="date" />
<taskproperty id="tpd6" name="duration" type="default"
valuetype="int" />
<taskproperty id="tpd7" name="completion" type="default"
valuetype="int" />
<taskproperty id="tpd8" name="coordinator" type="default"
valuetype="text" />
<taskproperty id="tpd9" name="predecessorsr" type="default"
valuetype="text" />
</taskproperties>
<task id="0" name="Architectural design" color="#99ccff" meeting="false"
start="2006-01-09" duration="26" complete="75" priority="1" expand="true">
<task id="9" name="Create draft of architecture" color="#99ccff"
meeting="false" start="2006-01-09" duration="10" complete="100"
priority="1" expand="true">
<depend id="10" type="2" difference="0" hardness="Strong" />
<depend id="12" type="2" difference="0" hardness="Strong" />
</task>
<task id="10" name="Prepare construction documents" color="#99ccff"
meeting="false" start="2006-01-23" duration="15" complete="65"
priority="1" expand="true">
<depend id="17" type="2" difference="0" hardness="Strong" />
</task>
<task id="17" name="Agreement on architectural plan " color="#000000"
meeting="true" start="2006-02-13" duration="1" complete="0"
priority="1" expand="true">
<depend id="1" type="2" difference="0" hardness="Strong" />
</task>
</task>
<task id="11" name="Interior design" color="#99ccff" meeting="false"
start="2006-01-23" duration="10" complete="33" priority="1" expand="true">
<depend id="6" type="2" difference="0" hardness="Strong" />
<task id="12" name="Pre-design" color="#99ccff" meeting="false"
start="2006-01-23" duration="5" complete="100" priority="1" expand="true">
<depend id="13" type="2" difference="0" hardness="Strong" />
<depend id="14" type="2" difference="0" hardness="Strong" />
</task>
<task id="13" name="Furniture selection" color="#99ccff"
meeting="false" start="2006-01-30" duration="5" complete="0"
priority="1" expand="true" />
<task id="14" name="Equipment planning" color="#99ccff" meeting="false"
start="2006-01-30" duration="5" complete="0" priority="1" expand="true">
<notes><![CDATA[Embedded devices, kitchen, washing machine, dryer etc]]></notes>
</task>
</task>
<task id="7" name="Construction phase" color="#99ccff" meeting="false"
start="2006-02-15" duration="76" complete="0" priority="1" expand="true">
<depend id="20" type="2" difference="0" hardness="Strong" />
<task id="1" name="Foundation building" color="#99ccff" meeting="false"
start="2006-02-15" duration="15" complete="0" priority="1" expand="false">
<depend id="2" type="2" difference="0" hardness="Strong" />
</task>
<task id="2" name="Ground Floor building" color="#99ccff"
meeting="false" start="2006-03-08" duration="20" complete="0"
priority="1" expand="false">
<depend id="4" type="2" difference="0" hardness="Strong" />
</task>
<task id="4" name="First Floor building" color="#99ccff"
meeting="false" start="2006-04-05" duration="20" complete="0"
priority="1" expand="false">
<depend id="5" type="2" difference="0" hardness="Strong" />
</task>
<task id="5" name="Roof" color="#99ccff" meeting="false" start="2006-05-03"
duration="10" complete="0" priority="1" expand="false">
<depend id="18" type="2" difference="0" hardness="Strong" />
</task>
<task id="16" name="Connect to communications" color="#99ccff"
meeting="false" start="2006-05-18" duration="10" complete="0"
priority="1" expand="true" />
<task id="18" name="Construction completed " color="#000000"
meeting="true" start="2006-05-17" duration="1" complete="0"
priority="1" expand="true">
<depend id="6" type="2" difference="0" hardness="Strong" />
<depend id="16" type="2" difference="0" hardness="Strong" />
</task>
</task>
<task id="8" name="Decoration phase" color="#99ccff" meeting="false"
start="2006-05-18" duration="11" complete="0" priority="1" expand="true">
<task id="6" name="Walls" color="#99ccff" meeting="false" start="2006-05-18"
duration="5" complete="0" priority="1" expand="false">
<depend id="15" type="2" difference="0" hardness="Strong" />
</task>
<task id="15" name="Furniture" color="#99ccff" meeting="false"
start="2006-05-25" duration="3" complete="0" priority="1" expand="true">
<depend id="20" type="2" difference="0" hardness="Strong" />
</task>
<task id="20" name="Bring your family here" color="#000000"
meeting="true" start="2006-06-01" duration="1" complete="0"
priority="1" expand="true" />
</task>
</tasks>
<resources>
<resource id="1" name="Jack House" function="Default:1"
contacts="jack.house@myselfllc.net" phone="0044 077345456" />
<resource id="0" name="John Black" function="4"
contacts="john.black@myselfllc.net" phone="+44 0794353567" />
<resource id="2" name="Michelangelo" function="0"
contacts="mickelangelo@heaven.net" phone="078 9059056" />
<resource id="3" name="Tom White" function="1"
contacts="tom.white@myselfllc.net" phone="07978978978" />
<resource id="4" name="Peter Green" function="1"
contacts="peter.green@myselfllc.net" phone="0797897856" />
<resource id="5" name="George Brown" function="1"
contacts="george.brown@myselfllc.net" phone="07967766447" />
<resource id="6" name="John Silver" function="2"
contacts="john.silver@myselfllc.net" phone="07778967889" />
</resources>
<allocations>
<allocation task-id="9" resource-id="1" function="Default:1"
responsible="false" load="50.0" />
<allocation task-id="9" resource-id="2" function="0"
responsible="true" load="100.0" />
<allocation task-id="10" resource-id="2" function="0"
responsible="true" load="100.0" />
<allocation task-id="12" resource-id="1" function="Default:1"
responsible="true" load="100.0" />
<allocation task-id="12" resource-id="2" function="0"
responsible="false" load="50.0" />
<allocation task-id="13" resource-id="1" function="Default:1"
responsible="true" load="100.0" />
<allocation task-id="14" resource-id="1" function="Default:1"
responsible="true" load="50.0" />
<allocation task-id="14" resource-id="2" function="0"
responsible="false" load="50.0" />
<allocation task-id="1" resource-id="0" function="4"
responsible="false" load="100.0" />
<allocation task-id="1" resource-id="5" function="1"
responsible="false" load="100.0" />
<allocation task-id="1" resource-id="3" function="1"
responsible="false" load="100.0" />
<allocation task-id="1" resource-id="6" function="2"
responsible="true" load="100.0" />
<allocation task-id="2" resource-id="5" function="1"
responsible="false" load="100.0" />
<allocation task-id="2" resource-id="3" function="1"
responsible="false" load="100.0" />
<allocation task-id="2" resource-id="4" function="1"
responsible="false" load="100.0" />
<allocation task-id="2" resource-id="6" function="2"
responsible="true" load="100.0" />
<allocation task-id="4" resource-id="5" function="1"
responsible="false" load="100.0" />
<allocation task-id="4" resource-id="3" function="1"
responsible="false" load="100.0" />
<allocation task-id="4" resource-id="4" function="1"
responsible="false" load="100.0" />
<allocation task-id="4" resource-id="6" function="2"
responsible="true" load="100.0" />
<allocation task-id="5" resource-id="3" function="5"
responsible="false" load="100.0" />
<allocation task-id="5" resource-id="6" function="2"
responsible="true" load="100.0" />
<allocation task-id="16" resource-id="0" function="4"
responsible="true" load="100.0" />
<allocation task-id="16" resource-id="4" function="1"
responsible="false" load="100.0" />
<allocation task-id="6" resource-id="5" function="1"
responsible="false" load="100.0" />
<allocation task-id="6" resource-id="3" function="1"
responsible="false" load="100.0" />
<allocation task-id="6" resource-id="4" function="1"
responsible="false" load="100.0" />
<allocation task-id="6" resource-id="6" function="2"
responsible="true" load="100.0" />
<allocation task-id="15" resource-id="1" function="Default:1"
responsible="true" load="100.0" />
<allocation task-id="15" resource-id="3" function="1"
responsible="false" load="100.0" />
</allocations>
<vacations />
<taskdisplaycolumns>
<displaycolumn property-id="tpd3" order="0" width="117" />
<displaycolumn property-id="tpd4" order="1" width="92" />
<displaycolumn property-id="tpd5" order="2" width="90" />
</taskdisplaycolumns>
<previous />
<roles roleset-name="Default" />
<roles>
<role id="0" name="Architect" />
<role id="1" name="Bricklayer" />
<role id="2" name="Foreman" />
<role id="3" name="Decorator" />
<role id="4" name="Excavator operator" />
<role id="5" name="Roofer" />
</roles>
</project>
$ ruby gant2csv
ID,名前,開始日,期間,進捗,ウェブリンク,リソース,メモ,
0," Architectural design",06/01/09,26,75,"","","",
9," Create draft of architecture",06/01/09,10,100,"","Jack HouseMichelangelo","",
10," Prepare construction documents",06/01/23,15,65,"","Michelangelo","",
17," Agreement on architectural plan ",06/02/13,1,0,"","","",
11," Interior design",06/01/23,10,33,"","","",
12," Pre-design",06/01/23,5,100,"","Jack HouseMichelangelo","",
13," Furniture selection",06/01/30,5,0,"","Jack House","",
14," Equipment planning",06/01/30,5,0,"","Jack HouseMichelangelo","",
7," Construction phase",06/02/15,76,0,"","","",
1," Foundation building",06/02/15,15,0,"","John BlackGeorge BrownTom WhiteJohn Silver","",
2," Ground Floor building",06/03/08,20,0,"","George BrownTom WhitePeter GreenJohn Silver","",
4," First Floor building",06/04/05,20,0,"","George BrownTom WhitePeter GreenJohn Silver","",
5," Roof",06/05/03,10,0,"","Tom WhiteJohn Silver","",
16," Connect to communications",06/05/18,10,0,"","John BlackPeter Green","",
18," Construction completed ",06/05/17,1,0,"","","",
8," Decoration phase",06/05/18,11,0,"","","",
6," Walls",06/05/18,5,0,"","George BrownTom WhitePeter GreenJohn Silver","",
15," Furniture",06/05/25,3,0,"","Jack HouseTom White","",
20," Bring your family here",06/06/01,1,0,"","","",
ID,名前,e-mail,電話番号,役割,
1,"Jack House","jack.house@myselfllc.net","0044 077345456","Default:1",
0,"John Black","john.black@myselfllc.net","+44 0794353567","4",
2,"Michelangelo","mickelangelo@heaven.net","078 9059056","0",
3,"Tom White","tom.white@myselfllc.net","07978978978","1",
4,"Peter Green","peter.green@myselfllc.net","0797897856","1",
5,"George Brown","george.brown@myselfllc.net","07967766447","1",
6,"John Silver","john.silver@myselfllc.net","07778967889","2",
最近のコメント