gwtf 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/gwtf CHANGED
@@ -48,7 +48,7 @@ pre do |global,command,options,args|
48
48
  Gwtf::Items.setup(project_dir)
49
49
  end
50
50
 
51
- @items = Gwtf::Items.new(project_dir)
51
+ @items = Gwtf::Items.new(project_dir, global[:project])
52
52
 
53
53
  true
54
54
  end
data/lib/gwtf.rb CHANGED
@@ -14,6 +14,15 @@ module Gwtf
14
14
  end
15
15
  end
16
16
 
17
+ def self.projects(root_dir)
18
+ Dir.entries(root_dir).map do |entry|
19
+ next if entry =~ /^\./
20
+ next unless File.directory?(File.join(root_dir, entry))
21
+
22
+ entry
23
+ end.compact.sort
24
+ end
25
+
17
26
  # borrowed from ohai, thanks Adam.
18
27
  def self.seconds_to_human(seconds)
19
28
  days = seconds.to_i / 86400
@@ -4,21 +4,42 @@ command [:list, :ls, :l] do |c|
4
4
  c.default_value false
5
5
  c.switch [:all, :a]
6
6
 
7
+ c.desc 'Show a short summary for all projects'
8
+ c.default_value false
9
+ c.switch [:summary, :s]
10
+
11
+ c.desc 'Show a overview for all projects'
12
+ c.default_value false
13
+ c.switch [:overview, :o]
14
+
7
15
  c.action do |global_options,options,args|
8
- count = {"open" => 0, "closed" => 0}
16
+ if options[:summary]
17
+ projects = Gwtf.projects(global_options[:data])
18
+ longest_name = projects.map{|p| p.length}.max
9
19
 
10
- @items.each_item do |item|
11
- count[ item[:status] ] += 1
20
+ projects.each_with_index do |project, idx|
21
+ puts "Items in all projects:\n\n" if idx == 0
12
22
 
13
- flags = []
14
- flags << "D" if item.has_description?
15
- flags << "C" if item.closed? && options[:all]
23
+ items = Gwtf::Items.new(File.join(global_options[:data], project), project)
24
+ stats = items.stats
16
25
 
17
- puts "%5s %-4s%-12s%8s" % [ item.item_id, flags.join, Time.parse(item.created_at).strftime("%F"), item.subject ] if (options[:all] || item.open?)
18
- end
26
+ puts "%#{longest_name + 3}s: open: %3d: closed %3d: total: %3d" % [ project, stats["open"], stats["closed"], stats["total"] ] unless stats["total"] == 0
27
+
28
+ end
19
29
 
20
- puts
21
- puts "Project %s items: %d / %d" % [ global_options[:project], count["open"], count["open"] + count["closed"] ]
30
+ puts
31
+ elsif options[:overview]
32
+ Gwtf.projects(global_options[:data]).each do |project|
33
+ items = Gwtf::Items.new(File.join(global_options[:data], project), project)
34
+
35
+ puts items.list_text(options[:all]) if items.item_ids.size > 0
36
+ puts
37
+ end
38
+ else
39
+ puts
40
+ puts @items.list_text(options[:all])
41
+ puts
42
+ end
22
43
  end
23
44
  end
24
45
 
@@ -5,7 +5,26 @@ command [:new, :add, :n, :a, :c] do |c|
5
5
  c.default_value false
6
6
  c.switch [:edit, :e]
7
7
 
8
+ c.desc 'Schedule a reminder about this task'
9
+ c.default_value false
10
+ c.flag [:remind]
11
+
12
+ c.desc 'Mark item done after sending reminder'
13
+ c.default_value false
14
+ c.switch [:done]
15
+
16
+ c.desc 'Only alert if the item is still marked open'
17
+ c.default_value false
18
+ c.switch [:ifopen]
19
+
20
+ c.desc 'Email address to send to'
21
+ c.default_value Etc.getlogin
22
+ c.flag [:recipient, :r]
23
+
8
24
  c.action do |global_options,options,args|
25
+ subject = args.join(" ")
26
+ raise "Please supply a short desciption for the item on the command line" if subject == ""
27
+
9
28
  if options[:edit]
10
29
  raise "EDITOR is not set" unless ENV.include?("EDITOR")
11
30
 
@@ -22,10 +41,18 @@ command [:new, :add, :n, :a, :c] do |c|
22
41
  end
23
42
 
24
43
  item = @items.new_item
25
- item.subject = args.join(" ")
44
+ item.subject = subject
26
45
  item.description = description if description
27
46
  item.save
28
47
 
48
+ if options[:remind]
49
+ STDOUT.sync
50
+
51
+ print "Creating reminder at job for item #{item.item_id}: "
52
+
53
+ item.schedule_reminer(options[:remind], options[:recipient], options[:done], options[:ifopen])
54
+ end
55
+
29
56
  puts item
30
57
  end
31
58
  end
@@ -19,39 +19,52 @@ command [:remind, :rem] do |c|
19
19
  c.default_value false
20
20
  c.switch [:send]
21
21
 
22
+ c.desc 'Mark item done after sending reminder'
23
+ c.default_value false
24
+ c.switch [:done]
25
+
26
+ c.desc 'Only alert if the item is still marked open'
27
+ c.default_value false
28
+ c.switch [:ifopen]
29
+
22
30
  c.action do |global_options,options,args|
23
31
  raise "Please supply an item ID to remind about" if args.empty?
24
32
 
25
33
  unless options[:send]
26
34
  raise "Please specify a valid at() time specification" unless args.size > 2
27
35
 
28
- at = args[1..-1].join(" ")
29
-
30
36
  STDOUT.sync
31
37
 
32
38
  print "Creating reminder at job for item #{args.first}: "
33
- system "echo gwtf --project='%s' remind --recipient='%s' --send %s|at %s" % [ global_options[:project], options[:recipient], args.first, at]
39
+
40
+ @items.load_item(args.first).schedule_reminer(args[1..-1].join(" "), options[:recipient], options[:done], options[:ifopen])
34
41
  else
35
42
  item = @items.load_item(args.first)
36
43
 
37
- begin
38
- tmp = Tempfile.new("gwtf")
39
- tmp.write(item.summary)
40
- tmp.rewind
44
+ unless options[:ifopen] && item.closed?
45
+ begin
46
+ tmp = Tempfile.new("gwtf")
47
+ tmp.write(item.summary)
48
+ tmp.rewind
41
49
 
42
- if global_options[:project] == "default"
43
- subject = "Reminder for item %s" % [ args.first ]
44
- else
45
- subject = "Reminder for item %s in %s project" % [ args.first, global_options[:project] ]
46
- end
50
+ if global_options[:project] == "default"
51
+ subject = "Reminder for item %s" % [ args.first ]
52
+ else
53
+ subject = "Reminder for item %s in %s project" % [ args.first, global_options[:project] ]
54
+ end
55
+
56
+ system("cat #{tmp.path} | mail -s '#{subject}' '#{options[:recipient]}'")
47
57
 
48
- system("cat #{tmp.path}|mail -s '#{subject}' '#{options[:recipient]}'")
49
- ensure
50
- tmp.close
51
- tmp.unlink
58
+ if options[:done]
59
+ item.record_work("Closing item as part of scheduled reminder")
60
+ item.close
61
+ item.save
62
+ end
63
+ ensure
64
+ tmp.close
65
+ tmp.unlink
66
+ end
52
67
  end
53
68
  end
54
69
  end
55
70
  end
56
-
57
-
@@ -20,10 +20,14 @@ command :shell do |c|
20
20
 
21
21
  STDOUT.sync = true
22
22
 
23
- print "Optional description for work log: "
23
+ print "Optional description for work log (start with done! to close): "
24
24
 
25
25
  begin
26
26
  description = STDIN.gets.chomp
27
+ if description =~ /^(done|close)!\s*(.+)/
28
+ description = $1
29
+ item.close
30
+ end
27
31
  rescue Exception
28
32
  puts
29
33
  description = ""
data/lib/gwtf/item.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  module Gwtf
2
2
  class Item
3
3
  attr_accessor :file
4
+ attr_reader :project
4
5
 
5
- def initialize(file=nil)
6
+ def initialize(file=nil, project=nil)
6
7
  @item = default_item
7
8
  @file = file
9
+ @project = project
8
10
 
9
11
  load_item if file
10
12
  end
@@ -76,6 +78,15 @@ module Gwtf
76
78
  flag
77
79
  end
78
80
 
81
+ def compact_flags
82
+ flags = []
83
+ flags << "D" if has_description?
84
+ flags << "C" if closed?
85
+ flags << "L" unless work_log.empty?
86
+
87
+ flags
88
+ end
89
+
79
90
  def summary
80
91
  summary = StringIO.new
81
92
 
@@ -113,9 +124,7 @@ module Gwtf
113
124
  end
114
125
 
115
126
  def to_s
116
- flag = " (#{flags.join(',')})" unless flags.empty?
117
-
118
- "%s%s: %s" % [item_id, flag, subject]
127
+ "%5s %-4s%-12s%8s" % [ item_id, compact_flags.join, Time.parse(created_at.to_s).strftime("%F"), subject ]
119
128
  end
120
129
 
121
130
  def to_hash
@@ -167,6 +176,15 @@ module Gwtf
167
176
  update_property(property, value)
168
177
  end
169
178
 
179
+ def schedule_reminer(timespec, recipient, done=false, ifopen=false)
180
+ command_args = ["--send"]
181
+ command_args << "--recipient=%s" % [ recipient ]
182
+ command_args << "--done" if done
183
+ command_args << "--ifopen" if ifopen
184
+
185
+ system "echo gwtf --project='%s' remind %s %s | at %s" % [ @project, command_args.join(" "), item_id, timespec]
186
+ end
187
+
170
188
  # simple read from the class:
171
189
  #
172
190
  # >> i.description
data/lib/gwtf/items.rb CHANGED
@@ -20,15 +20,18 @@ module Gwtf
20
20
  end
21
21
  end
22
22
 
23
- def initialize(data_dir)
23
+ def initialize(data_dir, project)
24
24
  raise "Data directory #{data_dir} does not exist" unless File.directory?(data_dir)
25
25
 
26
+ @project = project
26
27
  @data_dir = data_dir
27
28
  @config = read_config
28
29
  end
29
30
 
31
+ # Creates a new blank item with the next available ID, saves it and saves
32
+ # the global config with the next id that should be assigned
30
33
  def new_item
31
- item = Item.new
34
+ item = Item.new(nil, @project)
32
35
  item.item_id = @config["next_item"]
33
36
  item.file = File.join(@data_dir, "#{item.item_id}.gwtf")
34
37
 
@@ -38,16 +41,19 @@ module Gwtf
38
41
  item
39
42
  end
40
43
 
44
+ # Loads an item from disk
41
45
  def load_item(item)
42
46
  raise "Item #{item} does not exist" unless File.exist?(file_for_item(item))
43
47
 
44
- Item.new(file_for_item(item))
48
+ Item.new(file_for_item(item), @project)
45
49
  end
46
50
 
51
+ # Reads the overall gwtf config file
47
52
  def read_config
48
53
  JSON.parse(File.read(Items.config_file(@data_dir)))
49
54
  end
50
55
 
56
+ # Saves the overall gwtf config
51
57
  def save_config
52
58
  raise "Config has not been loaded" unless @config
53
59
 
@@ -56,16 +62,52 @@ module Gwtf
56
62
  end
57
63
  end
58
64
 
65
+ # Array of integer IDs that belong to this project in ascending order
59
66
  def item_ids
60
67
  Dir.entries(@data_dir).grep(/\.gwtf$/).map{|i| File.basename(i, ".gwtf")}.map{|i| Integer(i)}.sort
61
68
  end
62
69
 
70
+ # Returns the path where the JSON data for a item might be found in the current project
63
71
  def file_for_item(item)
64
72
  File.join(@data_dir, "#{item}.gwtf")
65
73
  end
66
74
 
75
+ # Iterates over all items in a project
67
76
  def each_item
68
77
  item_ids.each {|item| yield load_item(item) }
69
78
  end
79
+
80
+ # Returns an array of total, open and closed items for the current project
81
+ def stats
82
+ count = {"open" => 0, "closed" => 0}
83
+
84
+ each_item do |item|
85
+ count[ item.status ] += 1
86
+ end
87
+
88
+ count["total"] = count["open"] + count["closed"]
89
+
90
+ count
91
+ end
92
+
93
+ # Returns a blob of text that represents a list of items in a project
94
+ def list_text(all=false)
95
+ list = StringIO.new
96
+ items = StringIO.new
97
+
98
+ count = {"open" => 0, "closed" => 0}
99
+
100
+ each_item do |item|
101
+ count[ item[:status] ] += 1
102
+
103
+ items.puts item if (all || item.open?)
104
+ end
105
+
106
+ list.puts "Project %s items: %d / %d" % [ @project, count["open"], count["open"] + count["closed"] ]
107
+ list.puts
108
+ list.puts items.string
109
+
110
+ list.string
111
+ end
70
112
  end
71
113
  end
data/lib/gwtf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Gwtf
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gwtf
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - R.I.Pienaar
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-03-12 00:00:00 +00:00
18
+ date: 2012-03-13 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency