dev_flow 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/dw +7 -1
- data/lib/dev_flow/app.rb +1 -3
- data/lib/dev_flow/commands/clean.rb +0 -0
- data/lib/dev_flow/commands/gantt.rb +51 -0
- data/lib/dev_flow/roadmap.rb +1 -1
- data/lib/dev_flow/task_js.rb +71 -0
- data/lib/dev_flow/version.rb +1 -1
- data/lib/dev_flow.rb +1 -0
- data/tags +0 -0
- data/templates/css/jsgantt.css +53 -0
- data/templates/js/jsgantt.js +1681 -0
- data/templates/jsgantt.html.erb +50 -0
- metadata +9 -3
data/bin/dw
CHANGED
@@ -26,6 +26,7 @@ optparse = OptionParser.new do |opts|
|
|
26
26
|
opts.separator " close : close the task as a leader"
|
27
27
|
opts.separator " release : close a release task"
|
28
28
|
opts.separator " clean[up] : delete local branches that already completed"
|
29
|
+
opts.separator " gantt : create gantt chart html files under docs folder"
|
29
30
|
opts.separator ""
|
30
31
|
opts.separator "Options:"
|
31
32
|
|
@@ -50,6 +51,11 @@ optparse = OptionParser.new do |opts|
|
|
50
51
|
options[:offline] = true
|
51
52
|
end
|
52
53
|
|
54
|
+
options[:docs] = 'docs'
|
55
|
+
opts.on('-d DIR', '--docs DIR', 'document directory for gantt chart') do |dir|
|
56
|
+
options[:docs] = dir
|
57
|
+
end
|
58
|
+
|
53
59
|
opts.on('-v', '--verbose', 'print more messages') do
|
54
60
|
options[:verbose] = true
|
55
61
|
end
|
@@ -67,7 +73,7 @@ command = ARGV[0] || 'info'
|
|
67
73
|
cmd_alias = {'pg' => 'progress', 'update-roadmap' => 'ur', 'clean' => 'cleanup', 's' => 'switch'}
|
68
74
|
command = cmd_alias[command] if cmd_alias[command]
|
69
75
|
|
70
|
-
if %w[info init complete progress close release ur update-roadmap cleanup clean switch].include? command
|
76
|
+
if %w[info init complete progress close release ur update-roadmap cleanup clean switch gantt].include? command
|
71
77
|
options[:command] = command
|
72
78
|
else
|
73
79
|
puts "Unknown command #{command}".red
|
data/lib/dev_flow/app.rb
CHANGED
@@ -27,9 +27,7 @@ module DevFlow
|
|
27
27
|
info "Load local configuration from #{@config[:local_config]}"
|
28
28
|
@config = @config.merge(YAML.load(File.open(@config[:local_config], 'r:utf-8').read))
|
29
29
|
end
|
30
|
-
|
31
|
-
error "Do not work on master branch. Please swith to develop branch." if @git.current_branch == 'master'
|
32
|
-
|
30
|
+
|
33
31
|
# load roadmap into variable @roadmap
|
34
32
|
load_roadmap
|
35
33
|
|
File without changes
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erubis'
|
3
|
+
|
4
|
+
module DevFlow
|
5
|
+
class Gantt < App
|
6
|
+
|
7
|
+
## fetch git log
|
8
|
+
def git_log
|
9
|
+
git_log = ''
|
10
|
+
committer = ''
|
11
|
+
last_header = ''
|
12
|
+
`git log --graph --format=email --date=relative -n 50`.split(/\n/).each do |line|
|
13
|
+
# {{{
|
14
|
+
line.chomp!
|
15
|
+
line.gsub!(">", ">")
|
16
|
+
line.gsub!("<", "<")
|
17
|
+
if /^(?<header_>\W+)From\s+(?<hash_code_>\w+)/ =~ line
|
18
|
+
git_log += "#{header_}<em style='color:grey'>#{hash_code_}</em>\n"
|
19
|
+
elsif /^(?<header_>\W+)From\:\s+(?<committer_>.+)$/ =~ line
|
20
|
+
committer = committer_
|
21
|
+
last_header = header_
|
22
|
+
elsif /^(?<header_>\W+)Date\:\s*(?<date_>.+)$/ =~ line
|
23
|
+
git_log += sprintf("%s<strong>%s</strong> By %s\n", last_header, DateTime.parse(date_).strftime("%F %R"), committer)
|
24
|
+
git_log += header_
|
25
|
+
elsif /^(?<header_>\W+)Subject\:\s+\[PATCH\]\s*(?<message_>.+)$/ =~ line
|
26
|
+
color = 'green'
|
27
|
+
git_log += "#{header_}<strong style='color:#{color}'>#{message_}</strong>\n"
|
28
|
+
else
|
29
|
+
git_log += line + "\n"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
git_log
|
33
|
+
end
|
34
|
+
|
35
|
+
## create gantt chart from templates
|
36
|
+
def process!
|
37
|
+
docs = @config[:docs]
|
38
|
+
html_file = "#{docs}/gantt.html"
|
39
|
+
FileUtils.mkdir_p "#{docs}" unless File.directory? "#{docs}"
|
40
|
+
|
41
|
+
tpl_dir = File.expand_path(File.dirname(__FILE__) + "/../../../templates")
|
42
|
+
FileUtils.cp_r "#{tpl_dir}/css", docs
|
43
|
+
FileUtils.cp_r "#{tpl_dir}/js", docs
|
44
|
+
|
45
|
+
wfh = File.open(html_file, "w:utf-8")
|
46
|
+
wfh.puts Erubis::Eruby.new(File.read("#{tpl_dir}/jsgantt.html.erb")).result(:rm => @roadmap, :is_roadmap => true, :git_log => git_log, :resource => nil)
|
47
|
+
wfh.close
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/dev_flow/roadmap.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
module DevFlow
|
2
|
+
|
3
|
+
class Task
|
4
|
+
|
5
|
+
def task_id
|
6
|
+
self.ln
|
7
|
+
end
|
8
|
+
|
9
|
+
def parent_id
|
10
|
+
self.parent ? self.parent.task_id : 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def resource_name
|
14
|
+
self.resource_names.join ","
|
15
|
+
end
|
16
|
+
|
17
|
+
def dependencies_str
|
18
|
+
self.dependencies.map {|d| d.task_id}.join ","
|
19
|
+
end
|
20
|
+
|
21
|
+
## color reflects the status of a task, return a hex code without heading "#"
|
22
|
+
def color resource_ = nil
|
23
|
+
today = DateTime.now.strftime("%Y%m%d").to_i
|
24
|
+
start_day = self.start_date.strftime("%Y%m%d").to_i
|
25
|
+
end_day = self.end_date.strftime("%Y%m%d").to_i
|
26
|
+
hex = "B0C4DE" # light steel blue as default
|
27
|
+
hex = "808080" unless self.is_workable? # grey for no able to start
|
28
|
+
hex = "FFD700" if start_day == today and progress == 0 # gold for must start now
|
29
|
+
hex = "FFFF00" if self.progress > 0 and self.progress < 100 # blue for working
|
30
|
+
hex = "FFA500" if end_day == today and self.progress < 100 # orange for master complete today
|
31
|
+
|
32
|
+
if resource_
|
33
|
+
hex = "CCCCCC" unless self.resources.include? resource_
|
34
|
+
end
|
35
|
+
|
36
|
+
hex = "008000" if self.is_completed? # green for completed
|
37
|
+
hex = "FF0000" if self.progress < 100 and today > end_day # red for not completed on time
|
38
|
+
hex = "FF0000" if self.progress == 0 and today > start_day # red for late start
|
39
|
+
hex = "EEE8AA" if self.is_deleted?
|
40
|
+
hex = "C0C0C0" if self.is_pending?
|
41
|
+
# puts hex + ":" + self.progress.to_s + ";" + DateTime.now.strftime("%Y%m%d") + ":" + self.start_date.strftime("%Y%m%d")
|
42
|
+
hex
|
43
|
+
end
|
44
|
+
|
45
|
+
def caption
|
46
|
+
return "" if self.children.size > 0
|
47
|
+
cap = self.resource_name
|
48
|
+
cap = sprintf("Pending (%s)", cap) if self.is_pending?
|
49
|
+
cap = sprintf("Deleted (%s)", cap) if self.is_deleted?
|
50
|
+
if self.is_completed?
|
51
|
+
cap += self.completed_at.strftime("%F")
|
52
|
+
else
|
53
|
+
cap += sprintf(':%02d\%', self.progress) if self.progress > 0
|
54
|
+
end
|
55
|
+
cap
|
56
|
+
end
|
57
|
+
|
58
|
+
def as_js resource
|
59
|
+
is_milestone_flag = self.is_milestone? ? 1 : 0
|
60
|
+
is_parent_flag = self.is_parent? ? 1 : 0
|
61
|
+
task_str = " g.AddTaskItem(new JSGantt.TaskItem(%d,'%s','%s','%s','%s','%s',%d,'%s',%d,%d,%d,%d,'%s','%s'));\n"
|
62
|
+
sprintf(task_str, self.task_id, self.display_name,
|
63
|
+
self.start_date.strftime("%m/%d/%Y"),
|
64
|
+
self.end_date.strftime("%m/%d/%Y"),
|
65
|
+
self.color(resource), '', is_milestone_flag,
|
66
|
+
self.resource_name, self.progress.to_i, is_parent_flag,
|
67
|
+
self.parent_id, 1, self.dependencies_str, self.caption)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/dev_flow/version.rb
CHANGED
data/lib/dev_flow.rb
CHANGED
data/tags
CHANGED
File without changes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
// These are the class/styles used by various objects in GanttChart. However, Firefox has problems deciphering class style when DIVs are embedded in other DIVs.
|
3
|
+
|
4
|
+
// GanttChart makes heavy use of embedded DIVS, thus the style are often embedded directly in the objects html. If this could be resolved with Firefox, it would
|
5
|
+
|
6
|
+
// make alot of the code look simpleer/cleaner without all the embedded styles
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
..gantt { font-family:tahoma, arial, verdana; font-size:10px;}
|
11
|
+
|
12
|
+
..gdatehead { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; HEIGHT: 18px }
|
13
|
+
|
14
|
+
..ghead { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 24px; HEIGHT: 20px }
|
15
|
+
|
16
|
+
..gname { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; WIDTH: 18px; HEIGHT: 18px }
|
17
|
+
|
18
|
+
..ghead A { FONT-SIZE: 10px; COLOR: #000000; TEXT-DECORATION: none }
|
19
|
+
|
20
|
+
..gheadwkend A { FONT-SIZE: 10px; COLOR: #000000; TEXT-DECORATION: none }
|
21
|
+
|
22
|
+
..gheadwkend { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 24px; HEIGHT: 20px; background-color: #cfcfcf }
|
23
|
+
|
24
|
+
..gfiller { BORDER-TOP: #efefef 1px solid; BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px }
|
25
|
+
|
26
|
+
..gfillerwkend { BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px; BACKGROUND-COLOR: #cfcfcf }
|
27
|
+
|
28
|
+
..gitem { BORDER-TOP: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px }
|
29
|
+
|
30
|
+
..gitemwkend { BORDER-TOP: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px }
|
31
|
+
|
32
|
+
..gmilestone { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 14px; OVERFLOW: hidden; BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px}
|
33
|
+
|
34
|
+
..gmilestonewkend { BORDER-TOP: #efefef 1px solid; BORDER-LEFT: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px}
|
35
|
+
|
36
|
+
..btn { BORDER-RIGHT: #ffffff; BORDER-TOP: #ffffff; FONT-WEIGHT: bold; FONT-SIZE: 10px; BORDER-LEFT: #ffffff; WIDTH: 12px; COLOR: #cccccc; BORDER-BOTTOM: #ffffff; BACKGROUND-COLOR: #ffffff }
|
37
|
+
|
38
|
+
..hrcomplete { BORDER-RIGHT: #000000 2px solid; PADDING-RIGHT: 0px; BORDER-TOP: #000000 2px solid; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; BORDER-LEFT: #000000 2px solid; WIDTH: 20px; COLOR: #000000; PADDING-TOP: 0px; BORDER-BOTTOM: #000000 2px solid; HEIGHT: 4px }
|
39
|
+
|
40
|
+
..hrhalfcomplete { BORDER-RIGHT: #000000 2px solid; BORDER-TOP: #000000 2px solid; BORDER-LEFT: #000000 2px solid; WIDTH: 9px; COLOR: #000000; BORDER-BOTTOM: #000000 2px solid; HEIGHT: 4px }
|
41
|
+
|
42
|
+
..gweekend { font-family:tahoma, arial, verdana; font-size:11px; background-color:#EEEEEE; text-align:center; }
|
43
|
+
|
44
|
+
..gtask { font-family:tahoma, arial, verdana; font-size:11px; background-color:#00FF00; text-align:center; }
|
45
|
+
|
46
|
+
..gday { font-family:tahoma, arial, verdana; font-size:11px; text-align:center; }
|
47
|
+
|
48
|
+
..gcomplete { background-color:black; height:5px; overflow: auto; margin-top:4px; }
|
49
|
+
|
50
|
+
DIV.scroll { BORDER-RIGHT: #efefef 1px solid; PADDING-RIGHT: 0px; BORDER-TOP: #efefef 1px solid; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; OVERFLOW: hidden; BORDER-LEFT: #efefef 1px solid; WIDTH: 420px; PADDING-TOP: 0px; BORDER-BOTTOM: #efefef 1px solid; BACKGROUND-COLOR: #ffffff }
|
51
|
+
|
52
|
+
DIV.scroll2 { position:relative; PADDING-RIGHT: 0px; overflow:auto ;overflow-x:scroll;overflow-y:hidden; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; WIDTH: 482px; PADDING-TOP: 0px; BACKGROUND-COLOR: #ffffff }
|
53
|
+
|