worked 0.1.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.tar.gz.sig +0 -0
- data/History.txt +4 -0
- data/License.txt +341 -0
- data/Manifest.txt +24 -0
- data/README.rdoc +79 -0
- data/Rakefile +27 -0
- data/bin/worked +14 -0
- data/config/website.yml +2 -0
- data/lib/worked.rb +8 -0
- data/lib/worked/cli.rb +37 -0
- data/lib/worked/inputgrammar.treetop +80 -0
- data/lib/worked/inputparser.rb +52 -0
- data/lib/worked/recorder.rb +13 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +77 -0
- data/tasks/website.rake +4 -0
- data/test/test_grammar.rb +68 -0
- data/test/test_helper.rb +15 -0
- data/test/test_worked.rb +37 -0
- data/test/test_worked_cli.rb +59 -0
- data/website/javascripts/rounded_corners_lite.inc.js +285 -0
- data/website/stylesheets/screen.css +137 -0
- data/website/template.rhtml +49 -0
- metadata +133 -0
- metadata.gz.sig +1 -0
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/worked'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('worked', Worked::VERSION) do |p|
|
7
|
+
p.developer('Mirko Stocker', 'me@misto.ch')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = p.name
|
10
|
+
p.extra_deps = [
|
11
|
+
['treetop','>= 1.2.4'],
|
12
|
+
]
|
13
|
+
p.extra_dev_deps = [
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
+
]
|
16
|
+
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# task :default => [:spec, :features]
|
data/bin/worked
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created by Mirko Stocker on 2009-2-12.
|
4
|
+
# Copyright (c) 2009. All rights reserved.
|
5
|
+
|
6
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/worked")
|
7
|
+
|
8
|
+
require "worked/cli"
|
9
|
+
|
10
|
+
CONFIG = File.join(Dir.getwd, ".worked")
|
11
|
+
|
12
|
+
File.open(CONFIG, "a") do |file|
|
13
|
+
Worked::CLI.execute(STDOUT, ARGV.join(" "), file)
|
14
|
+
end
|
data/config/website.yml
ADDED
data/lib/worked.rb
ADDED
data/lib/worked/cli.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'worked/recorder'
|
3
|
+
|
4
|
+
module Worked
|
5
|
+
class CLI
|
6
|
+
def self.execute(stdout, arguments, file)
|
7
|
+
|
8
|
+
Recorder.new(file).record(arguments)
|
9
|
+
|
10
|
+
#options = {
|
11
|
+
# :path => '~'
|
12
|
+
#}
|
13
|
+
|
14
|
+
#parser = OptionParser.new do |opts|
|
15
|
+
# opts.banner = <<-BANNER.gsub(/^ /,'')
|
16
|
+
# This application is wonderful because...
|
17
|
+
|
18
|
+
# Usage: #{File.basename($0)} [options]
|
19
|
+
|
20
|
+
# Options are:
|
21
|
+
# BANNER
|
22
|
+
# opts.separator ""
|
23
|
+
# opts.on("-p", "--path=PATH", String,
|
24
|
+
# "This is a sample message.",
|
25
|
+
# "For multiple lines, add more strings.",
|
26
|
+
# "Default: ~") { |arg| options[:path] = arg }
|
27
|
+
# opts.on("-h", "--help",
|
28
|
+
# "Show this help message.") { stdout.puts opts; exit }
|
29
|
+
|
30
|
+
# opts.parse!(arguments)
|
31
|
+
#end
|
32
|
+
|
33
|
+
#path = options[:path]
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
grammar InputGrammar
|
3
|
+
|
4
|
+
rule root
|
5
|
+
time " on " activity
|
6
|
+
end
|
7
|
+
|
8
|
+
rule activity
|
9
|
+
.*
|
10
|
+
end
|
11
|
+
|
12
|
+
rule time
|
13
|
+
time_from / time_in_words / time_range / time_in_digits
|
14
|
+
end
|
15
|
+
|
16
|
+
rule time_from
|
17
|
+
"from " am_pm_time {
|
18
|
+
def from
|
19
|
+
am_pm_time.to_24h
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
rule time_range
|
25
|
+
f:am_pm_time " to " t:am_pm_time {
|
26
|
+
def from
|
27
|
+
f.to_24h
|
28
|
+
end
|
29
|
+
|
30
|
+
def to
|
31
|
+
t.to_24h
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
rule am_pm_time
|
37
|
+
time_in_digits am_pm:(" am" / " pm" / "am" / "pm")? {
|
38
|
+
def to_24h
|
39
|
+
|
40
|
+
if methods.include?("am_pm") && am_pm.text_value =~ /pm/
|
41
|
+
return time_in_digits.total + 12.hours
|
42
|
+
end
|
43
|
+
|
44
|
+
time_in_digits.total
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
rule time_in_words
|
50
|
+
minutes / hours m:(" "? minutes)? {
|
51
|
+
def total
|
52
|
+
minutes.to_i.minutes + hours.to_i.hours + m.to_i.minutes
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
rule time_in_digits
|
58
|
+
integer m:( ":" integer )? {
|
59
|
+
def total
|
60
|
+
integer.to_i.hours + m.to_i.minutes
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
rule hours
|
66
|
+
integer " "? ( "hour" "s"? / "h" )
|
67
|
+
end
|
68
|
+
|
69
|
+
rule minutes
|
70
|
+
integer " "? ( "minute" "s"? / "m" ) {
|
71
|
+
def total
|
72
|
+
integer.to_i.minutes
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
rule integer
|
78
|
+
[0-9]*
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'treetop'
|
3
|
+
require 'worked/inputgrammar'
|
4
|
+
|
5
|
+
class Treetop::Runtime::SyntaxNode
|
6
|
+
|
7
|
+
# Remove all clutter ('hour', ':', etc) from the
|
8
|
+
# time and turn into an integer
|
9
|
+
def to_i
|
10
|
+
text_value.gsub(/\D/, '').to_i
|
11
|
+
end
|
12
|
+
|
13
|
+
def from
|
14
|
+
time_to_seconds_from_day(DateTime.now - total)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to
|
18
|
+
time_to_seconds_from_day(DateTime.now)
|
19
|
+
end
|
20
|
+
|
21
|
+
def time_to_seconds_from_day t
|
22
|
+
t.hour.hours + t.min.minutes
|
23
|
+
end
|
24
|
+
|
25
|
+
# Default value so we don't have to care whether
|
26
|
+
# there really is a minute component
|
27
|
+
def minutes
|
28
|
+
0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class InputParser
|
33
|
+
|
34
|
+
def self.parse source
|
35
|
+
|
36
|
+
root = InputGrammarParser.new.parse(source)
|
37
|
+
|
38
|
+
from, to = root.time.from, root.time.to
|
39
|
+
|
40
|
+
if from > to
|
41
|
+
from -= 24.hours
|
42
|
+
end
|
43
|
+
|
44
|
+
now = DateTime.now
|
45
|
+
midnight = DateTime.new(now.year, now.month, now.day)
|
46
|
+
|
47
|
+
[midnight + from.seconds, midnight + to.seconds, root.activity.text_value]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/worked.rb'}"
|
9
|
+
puts "Loading worked gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/txt2html
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
begin
|
5
|
+
require 'newgem'
|
6
|
+
rescue LoadError
|
7
|
+
puts "\n\nGenerating the website requires the newgem RubyGem"
|
8
|
+
puts "Install: gem install newgem\n\n"
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
require 'redcloth'
|
12
|
+
require 'syntax/convertors/html'
|
13
|
+
require 'erb'
|
14
|
+
require File.dirname(__FILE__) + '/../lib/worked.rb'
|
15
|
+
|
16
|
+
version = Worked::VERSION
|
17
|
+
download = 'http://rubyforge.org/projects/worked'
|
18
|
+
|
19
|
+
class Fixnum
|
20
|
+
def ordinal
|
21
|
+
# teens
|
22
|
+
return 'th' if (10..19).include?(self % 100)
|
23
|
+
# others
|
24
|
+
case self % 10
|
25
|
+
when 1: return 'st'
|
26
|
+
when 2: return 'nd'
|
27
|
+
when 3: return 'rd'
|
28
|
+
else return 'th'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Time
|
34
|
+
def pretty
|
35
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_syntax(syntax, source)
|
40
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
41
|
+
end
|
42
|
+
|
43
|
+
if ARGV.length >= 1
|
44
|
+
src, template = ARGV
|
45
|
+
template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
|
46
|
+
|
47
|
+
else
|
48
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
49
|
+
exit!
|
50
|
+
end
|
51
|
+
|
52
|
+
template = ERB.new(File.open(template).read)
|
53
|
+
|
54
|
+
title = nil
|
55
|
+
body = nil
|
56
|
+
File.open(src) do |fsrc|
|
57
|
+
title_text = fsrc.readline.gsub(/^=/, "")
|
58
|
+
body_text = fsrc.read
|
59
|
+
syntax_items = []
|
60
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
61
|
+
ident = syntax_items.length
|
62
|
+
element, syntax, source = $1, $2, $3
|
63
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
64
|
+
"syntax-temp-#{ident}"
|
65
|
+
}
|
66
|
+
body_text.gsub!(/^===/, "h3.")
|
67
|
+
body_text.gsub!(/^==/, "h2.")
|
68
|
+
body_text.gsub!(/^- /, "* ")
|
69
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
70
|
+
body = RedCloth.new(body_text).to_html
|
71
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
72
|
+
end
|
73
|
+
stat = File.stat(src)
|
74
|
+
created = stat.ctime
|
75
|
+
modified = stat.mtime
|
76
|
+
|
77
|
+
$stdout << template.result(binding)
|
data/tasks/website.rake
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class TestGrammar < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_hours
|
6
|
+
assert_equal 5, hours("5 hours")
|
7
|
+
assert_equal 5, hours("5hours")
|
8
|
+
assert_equal 5, hours("5 h")
|
9
|
+
assert_equal 5, hours("5h")
|
10
|
+
assert_equal 5, hours("5")
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_minutes
|
14
|
+
assert_equal 10, minutes("5 h 10 minutes")
|
15
|
+
assert_equal 10, minutes("5 h 10 m")
|
16
|
+
assert_equal 10, minutes("5 h 10m")
|
17
|
+
assert_equal 10, minutes("5h10m")
|
18
|
+
assert_equal 10, minutes("5:10")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_hour_ranges
|
22
|
+
assert_equal 4.hours, range("13 to 17")
|
23
|
+
assert_equal 4.hours, range("1 to 5")
|
24
|
+
assert_equal 4.hours, range("1pm to 5pm")
|
25
|
+
assert_equal 4.hours, range("1 pm to 5 pm")
|
26
|
+
assert_equal 16.hours, range("1 am to 5 pm")
|
27
|
+
assert_equal 2.hours, range("11 to 1pm")
|
28
|
+
assert_equal 2.hours, range("11pm to 1am")
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_hour_minute_ranges
|
32
|
+
assert_equal 4.hours + 30.minutes, range("1 to 5:30")
|
33
|
+
assert_equal 4.hours + 10.minutes, range("13:30 to 17:40")
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_with_activity
|
37
|
+
assert_equal "Testing", activity("5 hours on Testing")
|
38
|
+
assert_equal "Testing and Refactoring", activity("1 to 5 on Testing and Refactoring")
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_from_time
|
42
|
+
assert_in_delta 2.hours, range("from #{DateTime.now.hour - 2}:#{DateTime.now.min}"), 10 #seconds of delta
|
43
|
+
end
|
44
|
+
|
45
|
+
def activity source
|
46
|
+
parse(source).last
|
47
|
+
end
|
48
|
+
|
49
|
+
def range source
|
50
|
+
res = parse("#{source} on X")
|
51
|
+
((res[1] - res[0]) * 24).hours
|
52
|
+
end
|
53
|
+
|
54
|
+
def hours source
|
55
|
+
res = parse("#{source} on X")
|
56
|
+
res[1].hour - res[0].hour
|
57
|
+
end
|
58
|
+
|
59
|
+
def minutes source
|
60
|
+
res = parse("#{source} on X")
|
61
|
+
res[1].min - res[0].min
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse source
|
65
|
+
InputParser.parse(source)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test/unit'
|
3
|
+
require File.dirname(__FILE__) + '/../lib/worked'
|
4
|
+
|
5
|
+
module WorkedTestHelper
|
6
|
+
def extract_tuple log
|
7
|
+
|
8
|
+
s_from, s_to, activity = /(\S*)\s+(\S*)\s+(.*)/.match(log).captures
|
9
|
+
|
10
|
+
from = DateTime.parse(s_from)
|
11
|
+
to = DateTime.parse(s_to)
|
12
|
+
|
13
|
+
[ ((to - from) * 24).hours, activity ]
|
14
|
+
end
|
15
|
+
end
|