gen-tj 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/README.rdoc +36 -0
- data/Rakefile +29 -0
- data/bin/gen-buglist +40 -0
- data/bin/gen-crosscheck +42 -0
- data/bin/gen-resources +36 -0
- data/bin/gen-tj +44 -0
- data/gen-tj.gemspec +30 -0
- data/lib/gen-tj.rb +1 -0
- data/lib/gen-tj/crosscheck.rb +76 -0
- data/lib/gen-tj/gen-tj.rb +87 -0
- data/lib/gen-tj/genbuglist.rb +63 -0
- data/lib/gen-tj/genresources.rb +80 -0
- data/lib/gen-tj/task.rb +77 -0
- data/lib/gen-tj/version.rb +5 -0
- data/samples/Makefile +27 -0
- data/samples/README.rdoc +26 -0
- data/samples/project.tjp +73 -0
- data/samples/reports.tji +128 -0
- data/samples/resources.yml +26 -0
- metadata +126 -0
data/README.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= gen-tj -- generate taskjuggler plans from FATE
|
2
|
+
|
3
|
+
This generates Taskjuggler (www.taskjugger.org) plans from (carefully
|
4
|
+
designed) FATE entries combined with Bugzilla assignments.
|
5
|
+
|
6
|
+
== Commands
|
7
|
+
=== gen-buglist -- generate taskjuggler plans from Bugzilla
|
8
|
+
|
9
|
+
gen-buglist runs a named query and converts the resulting bug list
|
10
|
+
into tasks
|
11
|
+
|
12
|
+
Usage
|
13
|
+
gen-buglist name-of-query > buglist.tji
|
14
|
+
|
15
|
+
=== gen-resources -- generate TJ resources (incl. vacations)
|
16
|
+
|
17
|
+
gen-resources takes a "resource.yml" file and checks (via
|
18
|
+
present.suse.de) the vacations.
|
19
|
+
|
20
|
+
Usage
|
21
|
+
gen-resources > resources.tji
|
22
|
+
|
23
|
+
=== gen-tj -- generate TJ task list from FATE assignments
|
24
|
+
|
25
|
+
gen-tj generates a TaskJuggler task list (.tji file) from
|
26
|
+
FATE assignments prioritized by a FATE relation tree
|
27
|
+
|
28
|
+
Usage
|
29
|
+
gen-tj relationtree-name tji-file
|
30
|
+
|
31
|
+
|
32
|
+
== Makefile
|
33
|
+
|
34
|
+
Combining the three tasks (resources, buglist, tasklist) can easily be
|
35
|
+
done with a Makefile as provided in the samples directory.
|
36
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
extra_docs = ['README*', 'samples/*' ]
|
5
|
+
|
6
|
+
task :default => [:test]
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << File.expand_path('../test', __FILE__)
|
10
|
+
t.libs << File.expand_path('../', __FILE__)
|
11
|
+
t.test_files = FileList['test/test*.rb']
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'yard'
|
17
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
18
|
+
t.files = ['lib/**/*.rb', *extra_docs]
|
19
|
+
t.options = ['--no-private']
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
STDERR.puts "Install yard if you want prettier docs"
|
23
|
+
require 'rdoc/task'
|
24
|
+
Rake::RDocTask.new(:doc) do |rdoc|
|
25
|
+
rdoc.rdoc_dir = "doc"
|
26
|
+
rdoc.title = "dm-bugzilla-adapter #{DataMapper::Adapters::BugzillaAdapter::VERSION}"
|
27
|
+
extra_docs.each { |ex| rdoc.rdoc_files.include ex }
|
28
|
+
end
|
29
|
+
end
|
data/bin/gen-buglist
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/genbuglist'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <query-name>"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
query = ARGV.shift
|
37
|
+
|
38
|
+
usage "No query name given" unless query
|
39
|
+
|
40
|
+
GenTJ::GenBuglist.run query
|
data/bin/gen-crosscheck
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/crosscheck'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <title>"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
title = ARGV.shift
|
37
|
+
|
38
|
+
usage "title missing" unless title
|
39
|
+
|
40
|
+
STDERR.puts "Checking '#{title}'"
|
41
|
+
|
42
|
+
GenTJ::Crosscheck.run title
|
data/bin/gen-resources
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
27
|
+
require 'rubygems'
|
28
|
+
require 'gen-tj/genresources'
|
29
|
+
|
30
|
+
def usage msg
|
31
|
+
STDERR.puts "*** Error: #{msg}"
|
32
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} [<resources.yml>]"
|
33
|
+
exit 1
|
34
|
+
end
|
35
|
+
|
36
|
+
GenTJ::Genresources.run(ARGV.shift || "resources.yml")
|
data/bin/gen-tj
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2011 SUSE LINUX Products GmbH
|
4
|
+
#
|
5
|
+
# Author: Klaus Kämpf <kkaempf@suse.com>
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
|
27
|
+
def usage msg
|
28
|
+
STDERR.puts "*** Error: #{msg}"
|
29
|
+
STDERR.puts "Usage: #{File.basename(__FILE__)} <treename> <prjname>"
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
34
|
+
require 'rubygems'
|
35
|
+
require 'gen-tj'
|
36
|
+
|
37
|
+
treename = ARGV.shift
|
38
|
+
usage "treename missing" unless treename
|
39
|
+
|
40
|
+
prjname = ARGV.shift
|
41
|
+
usage "prjname missing" unless prjname
|
42
|
+
|
43
|
+
GenTJ::GenTJ.main Dir.getwd, treename, prjname
|
44
|
+
|
data/gen-tj.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "gen-tj/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gen-tj"
|
7
|
+
s.version = GenTJ::GenTJ::VERSION
|
8
|
+
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.authors = ["Klaus Kämpf"]
|
11
|
+
s.email = ["kkaempf@suse.de"]
|
12
|
+
s.homepage = ""
|
13
|
+
s.summary = %q{Generate Taskjuggler plans from Bugzilla and FATE}
|
14
|
+
s.description = %q{Given a list of resources (developers), a
|
15
|
+
Bugzilla named query and a FATE relationtree, this tool generates a
|
16
|
+
TaskJuggler overview of current work assigned to developers.
|
17
|
+
Additionally, it checks present.suse.de for vacations and public
|
18
|
+
holidays.}
|
19
|
+
|
20
|
+
s.add_dependency("dm-keeper-adapter", ["~> 0.0.4"])
|
21
|
+
s.add_dependency("dm-bugzilla-adapter", ["~> 0.0.1"])
|
22
|
+
|
23
|
+
s.rubyforge_project = "gen-tj"
|
24
|
+
|
25
|
+
s.files = `git ls-files`.split("\n")
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
28
|
+
s.extra_rdoc_files = `git ls-files -- {samples}/*`.split("\n")
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
end
|
data/lib/gen-tj.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'gen-tj/gen-tj.rb'
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# crosscheck.rb
|
3
|
+
#
|
4
|
+
# Crosscheck if features are in relationtree
|
5
|
+
#
|
6
|
+
# Usage: crosscheck "<match>"
|
7
|
+
#
|
8
|
+
# Example: ruby crosscheck.rb "Manager 1.2.1"
|
9
|
+
#
|
10
|
+
# <match> must exist in feature title _and_ name of relation tree
|
11
|
+
#
|
12
|
+
#
|
13
|
+
|
14
|
+
$: << File.join(File.dirname(__FILE__), "..", "dm-keeper-adapter", "lib")
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'dm-keeper-adapter'
|
18
|
+
require 'nokogiri'
|
19
|
+
|
20
|
+
module GenTJ
|
21
|
+
class Crosscheck
|
22
|
+
def Crosscheck.run title
|
23
|
+
|
24
|
+
DataMapper::Logger.new($stdout, :debug)
|
25
|
+
keeper = DataMapper.setup(:default, :adapter => 'keeper',
|
26
|
+
:url => 'https://keeper.novell.com/sxkeeper')
|
27
|
+
require 'keeper/feature'
|
28
|
+
require 'keeper/relationtree'
|
29
|
+
require 'keeper/relation'
|
30
|
+
DataMapper.finalize
|
31
|
+
|
32
|
+
# Get all features with a matching title
|
33
|
+
|
34
|
+
features = {}
|
35
|
+
Feature.all(:title.like => title).each do |f|
|
36
|
+
features[f.id] = f
|
37
|
+
end
|
38
|
+
|
39
|
+
unless features.size>0
|
40
|
+
STDERR.puts "No features matching '#{title}' found"
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the corresponding relationtree
|
45
|
+
relationtree = Relationtree.first(:title.like => title)
|
46
|
+
|
47
|
+
unless relationtree
|
48
|
+
STDERR.puts "No relationtree matching '#{title}' found"
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
# Now iterate through the relationtree and remove matching features
|
53
|
+
|
54
|
+
size_of_relation_tree = 0
|
55
|
+
relationtree.relations.each do |relation|
|
56
|
+
target = relation.target
|
57
|
+
size_of_relation_tree += 1
|
58
|
+
t_id = target.to_i
|
59
|
+
if features.delete(t_id).nil?
|
60
|
+
puts "Target #{t_id} has a bad title"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if size_of_relation_tree == 0
|
65
|
+
STDERR.puts "Relationtree '#{title}' has no features"
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
# Print out left over features
|
70
|
+
|
71
|
+
features.each do |id,f|
|
72
|
+
puts "Missing in relation tree - #{id}:'#{f.title}'"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#
|
2
|
+
# gentj.rb
|
3
|
+
#
|
4
|
+
# Generate TaskJuggler tasks from Fate relation tree
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# ruby gentj.rb <relation-tree-title> <tji-name>
|
8
|
+
# Example:
|
9
|
+
# ruby gentj.rb "Project tree" project
|
10
|
+
# -> generates 'project.tji' file
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'dm-bugzilla-adapter'
|
15
|
+
require 'dm-keeper-adapter'
|
16
|
+
require 'nokogiri'
|
17
|
+
require File.join(File.dirname(__FILE__),'task')
|
18
|
+
|
19
|
+
class Task
|
20
|
+
|
21
|
+
def add_relations relations, flags = nil
|
22
|
+
relations.each do |relation|
|
23
|
+
id = relation.target
|
24
|
+
f = Feature.get(id)
|
25
|
+
|
26
|
+
unless f.milestone =~ /^1\.2/
|
27
|
+
STDERR.puts "Skipping feature #{id} with milestone '#{f.milestone}'"
|
28
|
+
next
|
29
|
+
end
|
30
|
+
|
31
|
+
raise "No feature #{id}" unless f
|
32
|
+
prio = relation.sort_position
|
33
|
+
t = Task.new f.title, id
|
34
|
+
case f.developer
|
35
|
+
when /<email.*>/
|
36
|
+
alloc = Nokogiri.XML "<alloc>#{f.developer}</alloc>"
|
37
|
+
alloc.xpath("//email").each do |mail|
|
38
|
+
mail.text =~ /(.*)@(.*)/
|
39
|
+
t.allocations << $1
|
40
|
+
end
|
41
|
+
when /(.*)@(.*)/
|
42
|
+
t.allocations << $1
|
43
|
+
else
|
44
|
+
t.allocations << "dev"
|
45
|
+
end
|
46
|
+
|
47
|
+
if relation.parent
|
48
|
+
t.add_relations relation, :noprio => true
|
49
|
+
end
|
50
|
+
t.priority = prio unless flags && flags[:noprio]
|
51
|
+
|
52
|
+
self.add t
|
53
|
+
end # relations.each
|
54
|
+
end # def
|
55
|
+
|
56
|
+
end # class
|
57
|
+
|
58
|
+
module GenTJ
|
59
|
+
class GenTJ
|
60
|
+
def GenTJ.main dir, treename, subtitle
|
61
|
+
DataMapper::Logger.new($stdout, :debug)
|
62
|
+
keeper = DataMapper.setup(:default, :adapter => 'keeper', :url => 'https://keeper.novell.com/sxkeeper')
|
63
|
+
require 'keeper/feature'
|
64
|
+
require 'keeper/relationtree'
|
65
|
+
require 'keeper/relation'
|
66
|
+
DataMapper.finalize
|
67
|
+
|
68
|
+
# get 'my' relationtree
|
69
|
+
|
70
|
+
relationtree = Relationtree.first(:title => treename)
|
71
|
+
unless relationtree
|
72
|
+
STDERR.puts "No relationtree named '#{treename}' found"
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
title = relationtree.title
|
77
|
+
|
78
|
+
task = Task.new(title,subtitle)
|
79
|
+
|
80
|
+
task.add_relations(relationtree.relations)
|
81
|
+
|
82
|
+
File.open(File.join(dir,"#{subtitle}.tji"), "w+") do |f|
|
83
|
+
f.puts task.to_tj
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# named_query2tji.rb
|
3
|
+
#
|
4
|
+
# Convert list of bugs (generated by named query) to TJ include
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# named_query2tji <name-of-query>
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'dm-bugzilla-adapter'
|
12
|
+
|
13
|
+
# extend DataMapper Bug
|
14
|
+
class Bug
|
15
|
+
def to_tj
|
16
|
+
a = @assigned_to.split('@').first.gsub(".","_")
|
17
|
+
s = @summary.gsub("\"","\\\"")
|
18
|
+
s = "task bug_#{@id} \"#{s}\" {\n"
|
19
|
+
s << " allocate #{a}\n"
|
20
|
+
s << " effort 2d\n"
|
21
|
+
s << "}\n"
|
22
|
+
s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module GenTJ
|
27
|
+
class GenBuglist
|
28
|
+
def GenBuglist.run query
|
29
|
+
DataMapper::Logger.new(STDOUT, :debug)
|
30
|
+
keeper = DataMapper.setup(:default, :adapter => 'bugzilla',
|
31
|
+
:url => 'https://bugzilla.novell.com')
|
32
|
+
require 'bugzilla/named_query'
|
33
|
+
DataMapper.finalize
|
34
|
+
|
35
|
+
f = NamedQuery.get(query)
|
36
|
+
|
37
|
+
bugs = f.bugs
|
38
|
+
|
39
|
+
# sort bugs according to priority
|
40
|
+
|
41
|
+
prios = {}
|
42
|
+
|
43
|
+
bugs.each do |bug|
|
44
|
+
prios[bug.priority] ||= []
|
45
|
+
prios[bug.priority] << bug
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "task bugs \"Bugs\" {"
|
49
|
+
prios.each do |k,v|
|
50
|
+
pval = "_other"
|
51
|
+
if k =~ /P(\d)+.*/
|
52
|
+
pval = $1
|
53
|
+
end
|
54
|
+
puts "task p#{pval}_bugs \"Bugs with prio #{k}\" {"
|
55
|
+
v.each do |bug|
|
56
|
+
puts bug.to_tj
|
57
|
+
end
|
58
|
+
puts "}"
|
59
|
+
end
|
60
|
+
puts "}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#
|
2
|
+
# Generate TJ resources from YAML file + present.suse.de:9874
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
require 'net/telnet'
|
7
|
+
|
8
|
+
module GenTJ
|
9
|
+
class Genresources
|
10
|
+
|
11
|
+
#
|
12
|
+
# Create vacations entry for 'name', write to 'to'
|
13
|
+
#
|
14
|
+
# Use present.suse.de:9874 to extract dates
|
15
|
+
#
|
16
|
+
def Genresources.create_vacations name, to
|
17
|
+
tn = Net::Telnet.new('Host' => 'present.suse.de', 'Port' => 9874, 'Binmode' => true)
|
18
|
+
collect = false
|
19
|
+
tn.cmd(name) do |data|
|
20
|
+
# STDERR.puts "<#{name}> -> #{data}"
|
21
|
+
data.split("\n").each do |l|
|
22
|
+
if l =~ /^Absence/
|
23
|
+
collect = true
|
24
|
+
end
|
25
|
+
next unless collect
|
26
|
+
if l[0,1] == "-"
|
27
|
+
collect = false
|
28
|
+
next
|
29
|
+
end
|
30
|
+
dates = []
|
31
|
+
l.split(" ").each do |date|
|
32
|
+
next unless date =~ /2011/
|
33
|
+
dates << date
|
34
|
+
end
|
35
|
+
case dates.size
|
36
|
+
when 1: to.puts " vacation #{dates[0]}"
|
37
|
+
when 2: to.puts " vacation #{dates[0]} - #{dates[1]}"
|
38
|
+
else
|
39
|
+
STDERR.puts "#{dates.size} dates for (#{name}) '#{l}'"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
tn.close
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Create taskjuggler 'resource' entry for login, write to 'to'
|
48
|
+
#
|
49
|
+
# 'value' is either a string (name of resource)
|
50
|
+
# or a Hash (denoting a team)
|
51
|
+
#
|
52
|
+
def Genresources.create_tj login, value, to
|
53
|
+
return if login == "_name"
|
54
|
+
name = value.is_a?(String) ? value : value['_name']
|
55
|
+
to.puts "resource #{login} #{name.inspect} {"
|
56
|
+
case value
|
57
|
+
when String: create_vacations name, to
|
58
|
+
when Hash
|
59
|
+
value.each do |k,v|
|
60
|
+
create_tj k, v, to
|
61
|
+
to.puts "flags team"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
raise "Can't handle #{value.class} for #{login}"
|
65
|
+
end
|
66
|
+
to.puts "}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def Genresources.run input, output = nil
|
70
|
+
resources = YAML::load(File.open( input ))
|
71
|
+
|
72
|
+
out = File.open(output) rescue STDOUT
|
73
|
+
|
74
|
+
out.puts "flags team"
|
75
|
+
resources.each do |login, value|
|
76
|
+
create_tj login, value, out
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/gen-tj/task.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Class representing a task (aka Fate feature)
|
3
|
+
#
|
4
|
+
class Task
|
5
|
+
attr_reader :title, :effort, :duration, :status
|
6
|
+
attr_accessor :allocations
|
7
|
+
|
8
|
+
def initialize(title, fate = nil)
|
9
|
+
# title = '[manager , ***] blah blah'
|
10
|
+
if title =~ /\[(.*)\](.*)/
|
11
|
+
# $1 = full match, i.e. '[manager , ***, D] blah blah'
|
12
|
+
# $2 = sub match, i.e 'manager, ***, D'
|
13
|
+
@title = $2.strip
|
14
|
+
if $1 =~ /(.*),(.*)[,(.*)]?/
|
15
|
+
case $2.strip
|
16
|
+
when "*": @effort = "2d"
|
17
|
+
when "**": @effort = "1w"
|
18
|
+
when "***": @effort = "2w"
|
19
|
+
when "****": @effort = "4w"
|
20
|
+
else
|
21
|
+
@effort = $2
|
22
|
+
end
|
23
|
+
if $3 && $3 == "D"
|
24
|
+
@status = :done
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@title = title
|
29
|
+
end
|
30
|
+
@fate = fate
|
31
|
+
@allocations = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def add task
|
35
|
+
@subtasks ||= []
|
36
|
+
@subtasks << task
|
37
|
+
end
|
38
|
+
|
39
|
+
def priority= prio
|
40
|
+
@priority = prio.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Convert Task to tj
|
45
|
+
#
|
46
|
+
def to_tj
|
47
|
+
s = "task "
|
48
|
+
if @fate
|
49
|
+
s << "fate_#{@fate}"
|
50
|
+
else
|
51
|
+
s << "task_#{self}"
|
52
|
+
end
|
53
|
+
t = @title.tr("\"", "'")
|
54
|
+
s << " \"#{t}\" {\n"
|
55
|
+
s << " priority #{1000-@priority}\n" if @priority
|
56
|
+
s << " start ${now}\n"
|
57
|
+
if @subtasks
|
58
|
+
@subtasks.each do |task|
|
59
|
+
s << task.to_tj
|
60
|
+
end
|
61
|
+
else
|
62
|
+
if @allocations.size == 1
|
63
|
+
s << " allocate #{allocations[0]}\n"
|
64
|
+
else
|
65
|
+
@allocations.each do |rsrc|
|
66
|
+
s << " allocate #{rsrc} { mandatory }\n"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
s << " duration #{duration}\n" if @duration
|
71
|
+
s << " effort #{effort}\n" if @effort
|
72
|
+
end
|
73
|
+
s << "}\n"
|
74
|
+
|
75
|
+
s
|
76
|
+
end
|
77
|
+
end
|
data/samples/Makefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Name of TaskJuggler binary, change to 'tj' for TaskJuggler 2.x
|
2
|
+
TASKJUGGLER = tj3
|
3
|
+
|
4
|
+
# Name of Relationtree in FATE
|
5
|
+
RELATIONTREE = "Name of Relationtree"
|
6
|
+
|
7
|
+
# Name of query in Bugzilla
|
8
|
+
QUERYNAME = "Name of Bugzilla Query"
|
9
|
+
|
10
|
+
all: project.tjp resources.tji buglist.tji
|
11
|
+
gen-tj $(RELATIONTREE) project
|
12
|
+
$(TASKJUGGLER) $<
|
13
|
+
|
14
|
+
resources.tji: resources.yml
|
15
|
+
gen-resources $< > $@-tmp
|
16
|
+
mv -f $@-tmp $@
|
17
|
+
|
18
|
+
buglist.tji:
|
19
|
+
gen-buglist $(QUERYNAME) > $@-tmp
|
20
|
+
mv -f $@-tmp $@
|
21
|
+
|
22
|
+
clean:
|
23
|
+
rm -f buglist.tji
|
24
|
+
rm -f resources.tji
|
25
|
+
rm -f *.html
|
26
|
+
rm -rf css icons scripts
|
27
|
+
|
data/samples/README.rdoc
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
= Sample files for gen-tj
|
2
|
+
|
3
|
+
This directory contains samples for files _you_ need to provide in
|
4
|
+
order to use gen-tj
|
5
|
+
|
6
|
+
Look into the files for further comments.
|
7
|
+
|
8
|
+
== resources.yml - Allocatable resources for this project
|
9
|
+
|
10
|
+
A YAML (www.yaml.org) formatted file describing your resources
|
11
|
+
(developers, testers, documentation, etc.)
|
12
|
+
|
13
|
+
== project.tjp - Project description
|
14
|
+
|
15
|
+
A TaskJuggler project file describing your project (name, start date,
|
16
|
+
end date, ...), public holidays, extra tasks (i.e. testing), and
|
17
|
+
milestones.
|
18
|
+
|
19
|
+
== reports.tji - Reports to generate
|
20
|
+
|
21
|
+
This file describes which reports to generate. The sample generates
|
22
|
+
HTML formatted
|
23
|
+
* Overview - list of tasks
|
24
|
+
* Status - daily status
|
25
|
+
* Development - A list of tasks showing the resources assigned to each task
|
26
|
+
* Resource - A graph showing resource allocation. It identifies whether each resource is under- or over-allocated for.
|
data/samples/project.tjp
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
project my_project "Name of project" 2011-08-01 - 2015-08-01 {
|
2
|
+
weekstartsmonday
|
3
|
+
timeformat "%a %Y-%m-%d"
|
4
|
+
}
|
5
|
+
|
6
|
+
# german public holidays
|
7
|
+
|
8
|
+
vacation "Christi Himmelfahrt" 2011-06-02
|
9
|
+
vacation "Pfingstmontag" 2011-06-13
|
10
|
+
vacation "Fronleichnam" 2011-06-23
|
11
|
+
vacation "Mariä Himmelfahrt" 2011-08-15
|
12
|
+
vacation "Tag der Deutschen Einheit" 2011-10-03
|
13
|
+
vacation "Allerheiligen" 2011-11-01
|
14
|
+
vacation "2. Weihnachtstag" 2011-12-26
|
15
|
+
vacation "Workshop" 2011-07-11 - 2011-07-15
|
16
|
+
vacation "Hackweek" 2011-09-26 - 2011-09-30
|
17
|
+
|
18
|
+
# Define resources (generated by gen-resources)
|
19
|
+
include "resources.tji"
|
20
|
+
|
21
|
+
# Define FATE tasks (generated by gen-tj)
|
22
|
+
include "project.tji"
|
23
|
+
|
24
|
+
# Define Bug assignments (generated by gen-buglist)
|
25
|
+
|
26
|
+
include "buglist.tji"
|
27
|
+
|
28
|
+
#
|
29
|
+
# Testing tasks
|
30
|
+
#
|
31
|
+
|
32
|
+
task testing "Beta/RC testing" {
|
33
|
+
task beta1_testing "Beta 1 testing" {
|
34
|
+
# Everyone in the 'dev' team will do testing
|
35
|
+
allocate dev
|
36
|
+
duration 2w
|
37
|
+
# Insert a proper dependency here - whats the criteria to start beta testing ?
|
38
|
+
depends !!fate_my_project
|
39
|
+
}
|
40
|
+
task beta2_testing "Beta 2 testing" {
|
41
|
+
allocate dev
|
42
|
+
duration 2w
|
43
|
+
depends !beta1_testing
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
#
|
48
|
+
# Milestones
|
49
|
+
#
|
50
|
+
# Milestones only have dependencies, but no duration/effort and no allocations
|
51
|
+
#
|
52
|
+
|
53
|
+
task milestones "Milestones" {
|
54
|
+
task start "start" {
|
55
|
+
start ${projectstart}
|
56
|
+
}
|
57
|
+
task alpha0 "alpha 0" {
|
58
|
+
# Insert a proper dependency here - whats the criteria to release Alpha0 ?
|
59
|
+
depends !!my_project.feature_for_alpha0
|
60
|
+
}
|
61
|
+
task alpha1 "alpha 1" {
|
62
|
+
# Alpha 1 comes after Alpha 0
|
63
|
+
depends !alpha0
|
64
|
+
# Insert a proper dependency here - whats the criteria to release Alpha1 ?
|
65
|
+
depends !!my_project.feature_for_alpha1
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
#
|
70
|
+
# Description of (HTML) reports to generate
|
71
|
+
#
|
72
|
+
|
73
|
+
include "reports.tji"
|
data/samples/reports.tji
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# A sample reports.tji file
|
3
|
+
#
|
4
|
+
#
|
5
|
+
|
6
|
+
navigator navbar {
|
7
|
+
hidereport 0
|
8
|
+
}
|
9
|
+
|
10
|
+
macro TaskTip [
|
11
|
+
tooltip istask() -8<-
|
12
|
+
'''Start: ''' <-query attribute='start'->
|
13
|
+
'''End: ''' <-query attribute='end'->
|
14
|
+
----
|
15
|
+
'''Resources:'''
|
16
|
+
|
17
|
+
<-query attribute='resources'->
|
18
|
+
----
|
19
|
+
'''Precursors: '''
|
20
|
+
|
21
|
+
<-query attribute='precursors'->
|
22
|
+
----
|
23
|
+
'''Followers: '''
|
24
|
+
|
25
|
+
<-query attribute='followers'->
|
26
|
+
->8-
|
27
|
+
]
|
28
|
+
|
29
|
+
macro AlertColor [
|
30
|
+
cellcolor plan.alert = 0 "#00D000" # green
|
31
|
+
cellcolor plan.alert = 1 "#D0D000" # yellow
|
32
|
+
cellcolor plan.alert = 2 "#D00000" # red
|
33
|
+
]
|
34
|
+
|
35
|
+
textreport frame "" {
|
36
|
+
header -8<-
|
37
|
+
== Development Project ==
|
38
|
+
----
|
39
|
+
<[navigator id="navbar"]>
|
40
|
+
----
|
41
|
+
->8-
|
42
|
+
footer "----"
|
43
|
+
textreport index "Overview" {
|
44
|
+
formats html
|
45
|
+
center '<[report id="overview"]>'
|
46
|
+
}
|
47
|
+
|
48
|
+
textreport "Status" {
|
49
|
+
formats html
|
50
|
+
center -8<-
|
51
|
+
<[report id="status.dashboard"]>
|
52
|
+
->8-
|
53
|
+
}
|
54
|
+
|
55
|
+
textreport development "Development" {
|
56
|
+
formats html
|
57
|
+
center '<[report id="development"]>'
|
58
|
+
}
|
59
|
+
|
60
|
+
textreport "ResourceGraph" {
|
61
|
+
formats html
|
62
|
+
title "Resource Graph"
|
63
|
+
center '<[report id="resourceGraph"]>'
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
taskreport overview "" {
|
69
|
+
columns hierarchindex, name, start, end, effort, duration,
|
70
|
+
chart { ${TaskTip} },
|
71
|
+
resources { width 150
|
72
|
+
listtype bullets }
|
73
|
+
|
74
|
+
footer -8<-
|
75
|
+
=== Staffing ===
|
76
|
+
|
77
|
+
All project phases are properly staffed. See [[ResourceGraph]] for
|
78
|
+
detailed resource allocations.
|
79
|
+
|
80
|
+
->8-
|
81
|
+
|
82
|
+
}
|
83
|
+
|
84
|
+
taskreport status "" {
|
85
|
+
columns wbs { width 50 }, name { width 150 },
|
86
|
+
start { width 100 }, end { width 100 },
|
87
|
+
effort { width 100 },
|
88
|
+
alert { tooltip plan.journal
|
89
|
+
!= '' "<-query attribute='journal'->" width 150 },
|
90
|
+
status { width 150 }
|
91
|
+
|
92
|
+
taskreport dashboard "" {
|
93
|
+
headline "Project Dashboard (<-query attribute='now'->)"
|
94
|
+
columns name { title "Task" ${AlertColor} width 200},
|
95
|
+
resources { width 200 ${AlertColor}
|
96
|
+
listtype bullets
|
97
|
+
# listitem "<-query attribute='name'->"
|
98
|
+
start ${projectstart} end ${projectend} },
|
99
|
+
alerttrend { title "Trend" ${AlertColor} width 50 },
|
100
|
+
journalmessages { width 350 ${AlertColor} }
|
101
|
+
hidetask ~hasalert(0)
|
102
|
+
# sorttasks alert.down, delayed.end.up
|
103
|
+
period %{${now} - 1w} +1w
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
|
108
|
+
# A list of tasks showing the resources assigned to each task.
|
109
|
+
taskreport development "" {
|
110
|
+
headline "Development - Resource Allocation Report"
|
111
|
+
columns hierarchindex, name, start, end, effort { title "Work" },
|
112
|
+
duration, chart { ${TaskTip} scale day width 500 }
|
113
|
+
timeformat "%Y-%m-%d"
|
114
|
+
hideresource ~(isleaf() & isleaf_())
|
115
|
+
sortresources name.up
|
116
|
+
}
|
117
|
+
|
118
|
+
# A graph showing resource allocation. It identifies whether each
|
119
|
+
# resource is under- or over-allocated for.
|
120
|
+
resourcereport resourceGraph "" {
|
121
|
+
headline "Resource Allocation Graph"
|
122
|
+
columns no, name, effort, rate, weekly { ${TaskTip} }
|
123
|
+
loadunit shortauto
|
124
|
+
# We only like to show leaf tasks for leaf resources.
|
125
|
+
hidetask ~(isleaf() & isleaf_())
|
126
|
+
sorttasks plan.start.up
|
127
|
+
}
|
128
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Sample resources.yml file
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# Format: YAML - see yaml.org
|
6
|
+
#
|
7
|
+
# Assumptions
|
8
|
+
#
|
9
|
+
# login names are shared between FATE and Bugzilla
|
10
|
+
#
|
11
|
+
#
|
12
|
+
|
13
|
+
login1: "Real name1"
|
14
|
+
login2: "Real name2"
|
15
|
+
|
16
|
+
# You can structure developers into teams
|
17
|
+
# Start with team nickname (here 'team') and indent
|
18
|
+
# Use the '_name:" attribute to give the team a name
|
19
|
+
|
20
|
+
team:
|
21
|
+
_name: "Name of team"
|
22
|
+
# you can also use teams within teams
|
23
|
+
subteam:
|
24
|
+
_name: "Name of subteam"
|
25
|
+
login3: "A subteam developer"
|
26
|
+
login4: "A team developer"
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gen-tj
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Klaus K\xC3\xA4mpf"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-28 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: dm-keeper-adapter
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
- 4
|
34
|
+
version: 0.0.4
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: dm-bugzilla-adapter
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 29
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 0
|
49
|
+
- 1
|
50
|
+
version: 0.0.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: |-
|
54
|
+
Given a list of resources (developers), a
|
55
|
+
Bugzilla named query and a FATE relationtree, this tool generates a
|
56
|
+
TaskJuggler overview of current work assigned to developers.
|
57
|
+
Additionally, it checks present.suse.de for vacations and public
|
58
|
+
holidays.
|
59
|
+
email:
|
60
|
+
- kkaempf@suse.de
|
61
|
+
executables:
|
62
|
+
- gen-buglist
|
63
|
+
- gen-crosscheck
|
64
|
+
- gen-resources
|
65
|
+
- gen-tj
|
66
|
+
extensions: []
|
67
|
+
|
68
|
+
extra_rdoc_files: []
|
69
|
+
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- README.rdoc
|
73
|
+
- Rakefile
|
74
|
+
- bin/gen-buglist
|
75
|
+
- bin/gen-crosscheck
|
76
|
+
- bin/gen-resources
|
77
|
+
- bin/gen-tj
|
78
|
+
- gen-tj.gemspec
|
79
|
+
- lib/gen-tj.rb
|
80
|
+
- lib/gen-tj/crosscheck.rb
|
81
|
+
- lib/gen-tj/gen-tj.rb
|
82
|
+
- lib/gen-tj/genbuglist.rb
|
83
|
+
- lib/gen-tj/genresources.rb
|
84
|
+
- lib/gen-tj/task.rb
|
85
|
+
- lib/gen-tj/version.rb
|
86
|
+
- samples/Makefile
|
87
|
+
- samples/README.rdoc
|
88
|
+
- samples/project.tjp
|
89
|
+
- samples/reports.tji
|
90
|
+
- samples/resources.yml
|
91
|
+
has_rdoc: true
|
92
|
+
homepage: ""
|
93
|
+
licenses: []
|
94
|
+
|
95
|
+
post_install_message:
|
96
|
+
rdoc_options: []
|
97
|
+
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 3
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
requirements: []
|
119
|
+
|
120
|
+
rubyforge_project: gen-tj
|
121
|
+
rubygems_version: 1.5.2
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Generate Taskjuggler plans from Bugzilla and FATE
|
125
|
+
test_files: []
|
126
|
+
|