ohac-ditz 0.5.1 → 0.5.2
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/Changelog +1 -1
- data/Manifest.txt +0 -1
- data/Rakefile +3 -3
- data/ReleaseNotes +5 -0
- data/bin/coditz.rb +276 -0
- data/contrib/bookmarklet.js +1 -0
- data/lib/ditz.rb +1 -1
- data/lib/ditz/plugins/icalendar.rb +64 -0
- data/lib/ditz/plugins/issue-timetracker.rb +52 -0
- data/lib/ditz/plugins/mercurial.rb +48 -0
- data/lib/ditz/plugins/sha-names.rb +35 -0
- data/man/man1/ditz.1 +1 -1
- data/sheila/sheila.rb +876 -0
- metadata +43 -7
data/Changelog
CHANGED
data/Manifest.txt
CHANGED
data/Rakefile
CHANGED
@@ -8,15 +8,15 @@ class Hoe
|
|
8
8
|
def extra_dev_deps; @extra_dev_deps.reject { |x| x[0] == "hoe" } end
|
9
9
|
end
|
10
10
|
|
11
|
-
Hoe.new('ditz', Ditz::VERSION) do |p|
|
11
|
+
Hoe.new('ohac-ditz', Ditz::VERSION) do |p|
|
12
12
|
p.rubyforge_name = 'ditz'
|
13
|
-
p.author = "
|
13
|
+
p.author = "OHASHI Hideya"
|
14
14
|
p.summary = "A simple issue tracker designed to integrate well with distributed version control systems like git and darcs. State is saved to a YAML file kept under version control, allowing issues to be closed/added/modified as part of a commit."
|
15
15
|
|
16
16
|
p.description = p.paragraphs_of('README.txt', 4..11).join("\n\n").gsub(/== SYNOPSIS/, "Synopsis:")
|
17
17
|
p.url = "http://ditz.rubyforge.org"
|
18
18
|
p.changes = p.paragraphs_of('Changelog', 0..0).join("\n\n")
|
19
|
-
p.email = "
|
19
|
+
p.email = "ohachige@gmail.com"
|
20
20
|
p.extra_deps = [['trollop', '>= 1.9'], ['yaml_waml', '>= 0.3']]
|
21
21
|
end
|
22
22
|
|
data/ReleaseNotes
CHANGED
data/bin/coditz.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
self_file =
|
5
|
+
if File.symlink?(__FILE__)
|
6
|
+
require 'pathname'
|
7
|
+
Pathname.new(__FILE__).realpath
|
8
|
+
else
|
9
|
+
__FILE__
|
10
|
+
end
|
11
|
+
$:.unshift(File.dirname(self_file) + "/../lib")
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'ditz'
|
15
|
+
require 'cinatra'
|
16
|
+
|
17
|
+
# FIXME see http://github.com/jugyo/cinatra/issues/#issue/4
|
18
|
+
begin
|
19
|
+
|
20
|
+
CONFIG_FN = ".ditz-config"
|
21
|
+
PLUGIN_FN = ".ditz-plugins"
|
22
|
+
|
23
|
+
def ditz_plugin_file
|
24
|
+
plugin_dir = Ditz::find_dir_containing PLUGIN_FN
|
25
|
+
File.join(plugin_dir || ".", PLUGIN_FN)
|
26
|
+
end
|
27
|
+
|
28
|
+
def ditz_config_file
|
29
|
+
config_dir = Ditz::find_dir_containing CONFIG_FN
|
30
|
+
File.join(config_dir || ".", CONFIG_FN)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ditz_op(cmd, opts = [], project = nil)
|
34
|
+
config_file = ditz_config_file
|
35
|
+
unless File.exist?(config_file)
|
36
|
+
puts 'do init first.'
|
37
|
+
return
|
38
|
+
end
|
39
|
+
config = Ditz::Config.from config_file
|
40
|
+
config.use_editor_if_possible = false # overwrite
|
41
|
+
config.paginate = "never" # overwrite
|
42
|
+
issue_dir = Pathname.new(config.issue_dir)
|
43
|
+
unless issue_dir.exist?
|
44
|
+
puts 'something wrong.'
|
45
|
+
return
|
46
|
+
end
|
47
|
+
storage = Ditz::FileStorage.new issue_dir
|
48
|
+
project = storage.load if project.nil?
|
49
|
+
op = Ditz::Operator.new
|
50
|
+
op.do cmd, project, config, opts.clone
|
51
|
+
storage.save project
|
52
|
+
project
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_name_from_mercurial
|
56
|
+
path = Ditz.find_dir_containing(".hg")
|
57
|
+
len = path ? path.to_s.size : 100
|
58
|
+
username = `hg show ui.username` || ''
|
59
|
+
username.chomp!
|
60
|
+
if username.size > 0
|
61
|
+
a = username.split(" ")
|
62
|
+
email = a.pop
|
63
|
+
email = email[1..-2]
|
64
|
+
name = a.join(" ")
|
65
|
+
[len, name, email]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_name_from_git
|
70
|
+
path = Ditz.find_dir_containing(".git")
|
71
|
+
len = path ? path.to_s.size : 100
|
72
|
+
[len, `git config user.name`.chomp, `git config user.email`.chomp]
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_name
|
76
|
+
as = [get_name_from_mercurial, get_name_from_git].select{|a|!a.nil?}
|
77
|
+
as.min_by{|a|a[0]}
|
78
|
+
end
|
79
|
+
|
80
|
+
command 'ditz_help' do |arg|
|
81
|
+
ditz_op 'help'
|
82
|
+
end
|
83
|
+
|
84
|
+
command 'init' do |arg|
|
85
|
+
config_file = ditz_config_file
|
86
|
+
len, name, email = get_name
|
87
|
+
unless File.exist?(config_file)
|
88
|
+
config = Ditz::Config.create(
|
89
|
+
:name => name,
|
90
|
+
:email => email,
|
91
|
+
:issue_dir => ".ditz",
|
92
|
+
:use_editor_if_possible => false,
|
93
|
+
:paginate => "never"
|
94
|
+
)
|
95
|
+
config.save! config_file
|
96
|
+
issue_dir = Pathname.new(config.issue_dir)
|
97
|
+
issue_dir.mkdir
|
98
|
+
fn = issue_dir + Ditz::FileStorage::PROJECT_FN
|
99
|
+
project = Ditz::Project.create(
|
100
|
+
:name => "myproject",
|
101
|
+
:components => [ Ditz::Component.create(:name => "mycomponent") ]
|
102
|
+
)
|
103
|
+
project.save! fn
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
command 'todo' do |arg|
|
108
|
+
ditz_op 'todo'
|
109
|
+
end
|
110
|
+
|
111
|
+
command 'add' do |arg|
|
112
|
+
ditz_op 'add', ['-q', arg]
|
113
|
+
end
|
114
|
+
|
115
|
+
command 'bench' do |arg|
|
116
|
+
prj = nil
|
117
|
+
10000.times do |i|
|
118
|
+
prj = ditz_op 'add', ['-q', "foo#{i}"], prj
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
command 'close' do |arg|
|
123
|
+
ditz_op 'close', ['-n', arg] # TODO select 1,2,3
|
124
|
+
end
|
125
|
+
|
126
|
+
command 'comment' do |arg|
|
127
|
+
args = arg.split(' ')
|
128
|
+
issue = args.shift
|
129
|
+
ditz_op 'comment', [issue, '-m', args.join(' ')]
|
130
|
+
end
|
131
|
+
|
132
|
+
command 'drop' do |arg|
|
133
|
+
ditz_op 'drop', [arg]
|
134
|
+
end
|
135
|
+
|
136
|
+
command 'show' do |arg|
|
137
|
+
ditz_op 'show', [arg]
|
138
|
+
end
|
139
|
+
|
140
|
+
command 'changelog' do |arg|
|
141
|
+
ditz_op 'changelog', [arg]
|
142
|
+
end
|
143
|
+
|
144
|
+
command 'log' do
|
145
|
+
ditz_op 'log'
|
146
|
+
end
|
147
|
+
|
148
|
+
command 'shortlog' do
|
149
|
+
ditz_op 'shortlog'
|
150
|
+
end
|
151
|
+
|
152
|
+
command 'html' do |arg|
|
153
|
+
opts = []
|
154
|
+
opts << arg if arg
|
155
|
+
ditz_op 'html', opts
|
156
|
+
end
|
157
|
+
|
158
|
+
command 'grep' do |arg|
|
159
|
+
opts = []
|
160
|
+
opts << arg if arg
|
161
|
+
ditz_op 'grep', opts # TODO support ignore case
|
162
|
+
end
|
163
|
+
|
164
|
+
command 'status' do |arg|
|
165
|
+
opts = []
|
166
|
+
opts << arg if arg
|
167
|
+
ditz_op 'status', opts
|
168
|
+
end
|
169
|
+
|
170
|
+
command 'releases' do
|
171
|
+
ditz_op 'releases'
|
172
|
+
end
|
173
|
+
|
174
|
+
command 'validate' do
|
175
|
+
ditz_op 'validate'
|
176
|
+
end
|
177
|
+
|
178
|
+
command 'release' do |arg|
|
179
|
+
opts = []
|
180
|
+
opts << arg if arg
|
181
|
+
ditz_op 'release', opts
|
182
|
+
end
|
183
|
+
|
184
|
+
command 'reconfigure' do
|
185
|
+
# TODO
|
186
|
+
end
|
187
|
+
|
188
|
+
command 'edit' do
|
189
|
+
# TODO
|
190
|
+
end
|
191
|
+
|
192
|
+
command 'add-reference' do |arg|
|
193
|
+
# TODO interactive only!
|
194
|
+
args = arg.split(' ')
|
195
|
+
issue = args.shift
|
196
|
+
comment = args.join(' ')
|
197
|
+
comment = nil if comment.size == 0
|
198
|
+
ditz_op 'add-reference', [issue] + (comment ? ['-m', comment] : ['-n'])
|
199
|
+
end
|
200
|
+
|
201
|
+
command 'add-component' do |arg|
|
202
|
+
# TODO interactive only!
|
203
|
+
ditz_op 'add-component'
|
204
|
+
end
|
205
|
+
|
206
|
+
command 'set-component' do |arg|
|
207
|
+
args = arg.split(' ')
|
208
|
+
issue = args.shift
|
209
|
+
component = args.shift
|
210
|
+
comment = args.join(' ')
|
211
|
+
comment = nil if comment.size == 0
|
212
|
+
ditz_op 'set-component', [issue, component] +
|
213
|
+
(comment ? ['-m', comment] : ['-n'])
|
214
|
+
end
|
215
|
+
|
216
|
+
command 'add-release' do |arg|
|
217
|
+
args = arg.split(' ')
|
218
|
+
name = args.shift
|
219
|
+
comment = args.join(' ')
|
220
|
+
comment = nil if comment.size == 0
|
221
|
+
ditz_op 'add-release', [name] + (comment ? ['-m', comment] : ['-n'])
|
222
|
+
end
|
223
|
+
|
224
|
+
command 'drop-release' do |arg|
|
225
|
+
ditz_op 'drop-release', [arg]
|
226
|
+
end
|
227
|
+
|
228
|
+
command 'assign' do |arg|
|
229
|
+
args = arg.split(' ')
|
230
|
+
issue = args.shift
|
231
|
+
release = args.shift
|
232
|
+
comment = args.join(' ')
|
233
|
+
comment = nil if comment.size == 0
|
234
|
+
ditz_op 'assign', [issue, release] + (comment ? ['-m', comment] : ['-n'])
|
235
|
+
end
|
236
|
+
|
237
|
+
command 'unassign' do |arg|
|
238
|
+
args = arg.split(' ')
|
239
|
+
issue = args.shift
|
240
|
+
comment = args.join(' ')
|
241
|
+
comment = nil if comment.size == 0
|
242
|
+
ditz_op 'unassign', [issue] + (comment ? ['-m', comment] : ['-n'])
|
243
|
+
end
|
244
|
+
|
245
|
+
command 'start' do |arg|
|
246
|
+
args = arg.split(' ')
|
247
|
+
issue = args.shift
|
248
|
+
comment = args.join(' ')
|
249
|
+
comment = nil if comment.size == 0
|
250
|
+
ditz_op 'start', [issue] + (comment ? ['-m', comment] : ['-n'])
|
251
|
+
end
|
252
|
+
|
253
|
+
command 'stop' do |arg|
|
254
|
+
args = arg.split(' ')
|
255
|
+
issue = args.shift
|
256
|
+
comment = args.join(' ')
|
257
|
+
comment = nil if comment.size == 0
|
258
|
+
ditz_op 'stop', [issue] + (comment ? ['-m', comment] : ['-n'])
|
259
|
+
end
|
260
|
+
|
261
|
+
=begin
|
262
|
+
bin/ditz
|
263
|
+
lib/ditz.rb
|
264
|
+
lib/ditz/operator.rb
|
265
|
+
lib/ditz/model-objects.rb
|
266
|
+
lib/ditz/model.rb
|
267
|
+
=end
|
268
|
+
|
269
|
+
command 'print_hooks' do |arg|
|
270
|
+
Ditz::HookManager.print_hooks
|
271
|
+
end
|
272
|
+
|
273
|
+
# FIXME see http://github.com/jugyo/cinatra/issues/#issue/4
|
274
|
+
rescue => x
|
275
|
+
puts x.backtrace
|
276
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://localhost:1234/new',l=d.location,e=encodeURIComponent,p='?u='+e(l.href)%20+'&t='+e(d.title)%20+'&s='+e(s)+'&y=task&r='+e('First%20Last%20<foo@example.com>')+'&R=2010&c=idea',u=f+p;l.href=u;
|
data/lib/ditz.rb
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
## icalendar ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin adds ability to export full todo list in iCalendar (RFC 2445) format.
|
4
|
+
## It is useful for integration with different pim software like KOrganizer.
|
5
|
+
##
|
6
|
+
## Issues are converted to VTODO entries with completion status set to 50 if
|
7
|
+
## its state is :in_progress, 100 if it's closed and 0 otherwise.
|
8
|
+
## Progress for release is 100 if it's released otherwise it's 99 * closed/all
|
9
|
+
## issues. So maximum for active release is 99 and it's not shown as done until
|
10
|
+
## released.
|
11
|
+
##
|
12
|
+
## Commands added:
|
13
|
+
## ditz todo-ics: generate full todo list in iCalendar format
|
14
|
+
##
|
15
|
+
## Usage:
|
16
|
+
## 1. add a line "- icalendar" to the .ditz-plugins file in the project root
|
17
|
+
|
18
|
+
require 'vpim/icalendar'
|
19
|
+
|
20
|
+
module Ditz
|
21
|
+
|
22
|
+
class Operator
|
23
|
+
operation :todo_ics, "Generate full todo list in iCalendar format", :maybe_release
|
24
|
+
def todo_ics project, config, releases
|
25
|
+
cal = Vpim::Icalendar.create
|
26
|
+
releases ||= project.releases + [:unassigned]
|
27
|
+
releases = [*releases]
|
28
|
+
releases.each do |r|
|
29
|
+
issues = project.issues_for_release r
|
30
|
+
done = 0
|
31
|
+
done = (99 * (issues.select { |i| i.closed? }).length / issues.length).to_int if issues.length != 0
|
32
|
+
if r != :unassigned
|
33
|
+
done = 100 if r.released?
|
34
|
+
parent = "release-#{r.hash}"
|
35
|
+
title = "Release #{r.name} (#{r.status})"
|
36
|
+
else
|
37
|
+
parent = "release-unassigned"
|
38
|
+
title = "Unassigned"
|
39
|
+
end
|
40
|
+
cal.push Vpim::Icalendar::Vtodo.create("SUMMARY" => title, "UID" => parent, "PERCENT-COMPLETE" => "#{done}")
|
41
|
+
issues.each do |i|
|
42
|
+
cal.push todo2vtodo(i, parent)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
puts cal.encode
|
46
|
+
end
|
47
|
+
|
48
|
+
def todo2vtodo todo, parent
|
49
|
+
h = {"SUMMARY" => "#{todo.title}", "UID" => "#{todo.type}-#{todo.id}"}
|
50
|
+
h["RELATED-TO"] = parent if parent
|
51
|
+
h["PRIORITY"] = "3" if todo.type == :bugfix
|
52
|
+
h["PERCENT-COMPLETE"] = case todo.status
|
53
|
+
when :closed
|
54
|
+
"100"
|
55
|
+
when :in_progress
|
56
|
+
"50"
|
57
|
+
else
|
58
|
+
"0"
|
59
|
+
end
|
60
|
+
return Vpim::Icalendar::Vtodo.create(h)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Ditz
|
2
|
+
|
3
|
+
class Issue
|
4
|
+
alias :old_start_work :start_work
|
5
|
+
alias :old_stop_work :stop_work
|
6
|
+
field :time_spent, :generator => :get_time_spent
|
7
|
+
field :mark_time, :generator => :get_mark_time
|
8
|
+
def get_time_spent(config, project)
|
9
|
+
time_spent || 0
|
10
|
+
end
|
11
|
+
def get_mark_time(config, project)
|
12
|
+
self.mark_time || 0
|
13
|
+
end
|
14
|
+
def start_work(who, comment)
|
15
|
+
self.mark_time = Time.now
|
16
|
+
old_start_work(who, comment)
|
17
|
+
end
|
18
|
+
def stop_work(who, comment)
|
19
|
+
self.time_spent += Time.now - self.mark_time
|
20
|
+
self.mark_time = Time.now
|
21
|
+
old_stop_work(who, comment)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ScreenView
|
26
|
+
def self.modulo(seconds, quotient)
|
27
|
+
result, remainder = seconds / quotient, seconds % quotient
|
28
|
+
end
|
29
|
+
def self.humanize(seconds)
|
30
|
+
result = ""
|
31
|
+
%w[year month day hour minute second].zip([3600*24*30*365, 3600*24*30, 3600*24, 3600, 60, 1]).inject(seconds) do |seconds, item|
|
32
|
+
num, seconds = modulo(seconds, item[1])
|
33
|
+
result << "#{item[0].pluralize(num)} " unless num == 0
|
34
|
+
seconds
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
def self.get_time_spent(issue)
|
39
|
+
if issue.status == :paused
|
40
|
+
issue.time_spent
|
41
|
+
elsif issue.time_spent && issue.mark_time
|
42
|
+
issue.time_spent + (Time.now - issue.mark_time)
|
43
|
+
else
|
44
|
+
0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
add_to_view :issue_summary do |issue, config|
|
48
|
+
" Time spent: #{humanize(get_time_spent(issue).to_i)}\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|