redpomo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/redpomo +5 -0
- data/lib/redpomo/cli.rb +72 -0
- data/lib/redpomo/config.rb +50 -0
- data/lib/redpomo/entries_printer.rb +33 -0
- data/lib/redpomo/entry.rb +49 -0
- data/lib/redpomo/file_cache.rb +40 -0
- data/lib/redpomo/fuzzy_converter.rb +68 -0
- data/lib/redpomo/issue.rb +24 -0
- data/lib/redpomo/null_cache.rb +9 -0
- data/lib/redpomo/numeric_ext.rb +29 -0
- data/lib/redpomo/puller.rb +35 -0
- data/lib/redpomo/pusher.rb +59 -0
- data/lib/redpomo/task.rb +88 -0
- data/lib/redpomo/task_list.rb +50 -0
- data/lib/redpomo/tracker.rb +108 -0
- data/lib/redpomo/version.rb +3 -0
- data/lib/redpomo.rb +15 -0
- data/redpomo.gemspec +31 -0
- data/spec/file_cache_spec.rb +17 -0
- data/spec/fixtures/cassettes/cli_close.yml +51 -0
- data/spec/fixtures/cassettes/cli_pull.yml +1222 -0
- data/spec/fixtures/cassettes/cli_push.yml +109 -0
- data/spec/fixtures/cassettes/close_issue.yml +50 -0
- data/spec/fixtures/cassettes/issues.yml +449 -0
- data/spec/fixtures/close_results.txt +2 -0
- data/spec/fixtures/printer_output.txt +16 -0
- data/spec/fixtures/pull_results.txt +20 -0
- data/spec/fixtures/timelog.csv +6 -0
- data/spec/lib/redpomo/cli_spec.rb +101 -0
- data/spec/lib/redpomo/fuzzy_converter_spec.rb +65 -0
- data/spec/lib/redpomo/task_spec.rb +39 -0
- data/spec/lib/redpomo/tracker_spec.rb +38 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/capture.rb +13 -0
- metadata +235 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'todo-txt/list'
|
2
|
+
require 'redpomo/task'
|
3
|
+
|
4
|
+
module Redpomo
|
5
|
+
class TaskList < Array
|
6
|
+
|
7
|
+
def self.find(task_number)
|
8
|
+
list = TaskList.new(Config.todo_path)
|
9
|
+
list.find(task_number)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.pull_from_trackers!
|
13
|
+
list = TaskList.new(Config.todo_path)
|
14
|
+
list.pull_from_trackers!
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(path)
|
18
|
+
@path = path
|
19
|
+
File.read(path).split("\n").each do |line|
|
20
|
+
push Task.new(self, line)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(task_number)
|
25
|
+
slice(task_number.to_i - 1)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove!(task)
|
29
|
+
delete(task)
|
30
|
+
write!
|
31
|
+
end
|
32
|
+
|
33
|
+
def pull_from_trackers!
|
34
|
+
issue_tasks = Tracker.all.map(&:issues).flatten.map(&:to_task)
|
35
|
+
delete_if do |task|
|
36
|
+
task.tracker.present?
|
37
|
+
end
|
38
|
+
self << issue_tasks
|
39
|
+
self.flatten!
|
40
|
+
write!
|
41
|
+
end
|
42
|
+
|
43
|
+
def write!
|
44
|
+
File.open(@path, 'w') do |file|
|
45
|
+
file.write map(&:orig).join("\n") + "\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'active_support/core_ext/hash'
|
2
|
+
require 'redpomo/issue'
|
3
|
+
require 'redpomo/config'
|
4
|
+
|
5
|
+
module Redpomo
|
6
|
+
class Tracker
|
7
|
+
|
8
|
+
def self.find(name)
|
9
|
+
if data = Config.trackers_data[name.to_sym]
|
10
|
+
Tracker.new(name.to_sym, data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.all
|
15
|
+
Config.trackers_data.map do |name, data|
|
16
|
+
Tracker.new(name.to_sym, data)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :name, :base_url
|
21
|
+
|
22
|
+
def initialize(name, options)
|
23
|
+
options.symbolize_keys!
|
24
|
+
@name = name
|
25
|
+
@base_url = options[:url]
|
26
|
+
@api_key = options[:token]
|
27
|
+
@default_project = options[:default_project]
|
28
|
+
@closed_status_id = options[:closed_status].to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def issues
|
32
|
+
data = get("/issues", assigned_to_id: current_user_id, status_id: "open")
|
33
|
+
data["issues"].map do |issue|
|
34
|
+
issue["project"] = project_identifier_for(issue["project"]["id"])
|
35
|
+
Issue.new(self, issue)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def push_entry!(entry)
|
40
|
+
task = entry.to_task
|
41
|
+
time_entry = {}
|
42
|
+
|
43
|
+
if issue = task.issue
|
44
|
+
time_entry[:issue_id] = issue
|
45
|
+
elsif project = task.project
|
46
|
+
time_entry[:project_id] = project
|
47
|
+
else
|
48
|
+
time_entry[:project_id] = @default_project
|
49
|
+
end
|
50
|
+
|
51
|
+
time_entry[:spent_on] = entry.datetime
|
52
|
+
time_entry[:hours] = entry.duration / 3600.0
|
53
|
+
time_entry[:comments] = task.text
|
54
|
+
|
55
|
+
post("/time_entries", time_entry: time_entry)
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_issue!(id, message = nil)
|
59
|
+
issue = { status_id: @closed_status_id }
|
60
|
+
issue[:notes] = message if message.present?
|
61
|
+
put("/issues/#{id}", issue: issue)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def project_identifier_for(project_id)
|
67
|
+
Config.cache.get("#{@name}:#{project_id}:identifier") do
|
68
|
+
data = get("/projects/#{project_id}")
|
69
|
+
data["project"]["identifier"]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def current_user_id
|
74
|
+
get("/users/current")["user"]["id"]
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(url, params = {})
|
78
|
+
request(:get, url, params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def post(url, data)
|
82
|
+
request(:post, url, body: data)
|
83
|
+
end
|
84
|
+
|
85
|
+
def put(url, data)
|
86
|
+
request(:put, url, body: data)
|
87
|
+
end
|
88
|
+
|
89
|
+
def request(type, url, params = {})
|
90
|
+
require 'rest_client'
|
91
|
+
require 'json'
|
92
|
+
args = []
|
93
|
+
args << @base_url + url + ".json"
|
94
|
+
args << params.delete(:body).to_json unless type == :get
|
95
|
+
args << {
|
96
|
+
accept: :json,
|
97
|
+
content_type: :json,
|
98
|
+
params: params.merge(key: @api_key)
|
99
|
+
}
|
100
|
+
parse RestClient.send(type, *args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse(json)
|
104
|
+
json && json.length >= 2 ? JSON.parse(json) : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/lib/redpomo.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# require "active_support/core_ext"
|
2
|
+
# require "rest-client"
|
3
|
+
# require "todo-txt"
|
4
|
+
# require "yaml"
|
5
|
+
# require "json"
|
6
|
+
# require "csv"
|
7
|
+
# require "launchy"
|
8
|
+
# require "applescript"
|
9
|
+
|
10
|
+
require "redpomo/version"
|
11
|
+
# require "redpomo/ext"
|
12
|
+
# require "redpomo/cli"
|
13
|
+
|
14
|
+
module Redpomo
|
15
|
+
end
|
data/redpomo.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/redpomo/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Stefano Verna"]
|
6
|
+
gem.email = ["stefano.verna@welaika.com"]
|
7
|
+
gem.description = %q{A nice little gem that integrates Redmine, Todo.txt and Pomodoro.app}
|
8
|
+
gem.summary = %q{A nice little gem that integrates Redmine, Todo.txt and Pomodoro.app}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "redpomo"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Redpomo::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "activesupport"
|
19
|
+
gem.add_dependency "thor"
|
20
|
+
gem.add_dependency "todo-txt"
|
21
|
+
gem.add_dependency "rest-client"
|
22
|
+
gem.add_dependency "launchy"
|
23
|
+
gem.add_dependency "applescript"
|
24
|
+
gem.add_dependency "terminal-table"
|
25
|
+
|
26
|
+
gem.add_development_dependency "rspec"
|
27
|
+
gem.add_development_dependency "vcr"
|
28
|
+
gem.add_development_dependency "webmock"
|
29
|
+
gem.add_development_dependency "mocha"
|
30
|
+
gem.add_development_dependency "simplecov"
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'redpomo/file_cache'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe Redpomo::FileCache do
|
6
|
+
|
7
|
+
describe "#get" do
|
8
|
+
it "executes the block on cache miss" do
|
9
|
+
Redpomo::FileCache.instance.cache_path = Tempfile.new('cache').path
|
10
|
+
counter = Redpomo::FileCache.get("foobar") { 5 }
|
11
|
+
counter.should == 5
|
12
|
+
counter = Redpomo::FileCache.get("foobar") { 10 }
|
13
|
+
counter.should == 5
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: https://project.cantierecreativo.net/issues/838.json?key=CANTIERE_TOKEN
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ! '{"issue":{"status_id":5}}'
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/json
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip, deflate
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Content-Length:
|
17
|
+
- '25'
|
18
|
+
User-Agent:
|
19
|
+
- Ruby
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Sat, 05 May 2012 12:50:41 GMT
|
27
|
+
Server:
|
28
|
+
- Apache/2.2.9 (Debian) DAV/2 PHP/5.2.6-1+lenny10 with Suhosin-Patch mod_python/3.3.1
|
29
|
+
Python/2.5.2 mod_ssl/2.2.9 OpenSSL/0.9.8g Phusion_Passenger/3.0.0
|
30
|
+
X-Powered-By:
|
31
|
+
- Phusion Passenger (mod_rails/mod_rack) 3.0.0
|
32
|
+
X-Runtime:
|
33
|
+
- '2868'
|
34
|
+
Cache-Control:
|
35
|
+
- no-cache
|
36
|
+
Set-Cookie:
|
37
|
+
- _redmine_session=BAh7BzoPc2Vzc2lvbl9pZCIlYTRjNzRjYzVmMDllNDJmYTNlMzU0NmEwMWQwYTEwYzUiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7BjoLbm90aWNlIhlNb2RpZmljYSBlZmZldHR1YXRhLgY6CkB1c2VkewY7B0Y%3D--1337564c1bf1f5b34c2a505039c279d6f7074532;
|
38
|
+
path=/; HttpOnly
|
39
|
+
- autologin=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
|
40
|
+
Content-Length:
|
41
|
+
- '1'
|
42
|
+
Status:
|
43
|
+
- '200'
|
44
|
+
Content-Type:
|
45
|
+
- application/json; charset=utf-8
|
46
|
+
body:
|
47
|
+
encoding: US-ASCII
|
48
|
+
string: ! ' '
|
49
|
+
http_version:
|
50
|
+
recorded_at: Sat, 05 May 2012 12:50:46 GMT
|
51
|
+
recorded_with: VCR 2.0.1
|