gen-tj 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *~
2
+ *.html
3
+ css
4
+ icons
5
+ scripts
6
+ pkg
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -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")
@@ -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
+
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ module GenTJ
2
+ class GenTJ
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -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
+
@@ -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.
@@ -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"
@@ -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
+