dev_flow 0.3.1 → 0.4.0
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/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
|
+
|