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.
@@ -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
+