teambox-things-sync 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ .bundle
2
+ *~
3
+ *.cache
4
+ *.log
5
+ *.pid
6
+ .DS_Store
7
+ .rvmrc
8
+ pkg/*
9
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+ gem 'teambox-client', "0.2.0"
4
+ gem 'things-client', "0.2.4"
5
+
6
+ group :development do
7
+ gem 'rspec', '>=2.0.0.beta.20'
8
+ end
@@ -0,0 +1,32 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ crack (0.1.6)
5
+ diff-lcs (1.1.2)
6
+ hashie (0.2.2)
7
+ httparty (0.5.2)
8
+ crack (= 0.1.6)
9
+ rb-appscript (0.5.3)
10
+ rspec (2.0.0.beta.20)
11
+ rspec-core (= 2.0.0.beta.20)
12
+ rspec-expectations (= 2.0.0.beta.20)
13
+ rspec-mocks (= 2.0.0.beta.20)
14
+ rspec-core (2.0.0.beta.20)
15
+ rspec-expectations (2.0.0.beta.20)
16
+ diff-lcs (>= 1.1.2)
17
+ rspec-mocks (2.0.0.beta.20)
18
+ teambox-client (0.2.0)
19
+ hashie (~> 0.2.0)
20
+ httparty (~> 0.5.0)
21
+ yajl-ruby (~> 0.7.0)
22
+ things-client (0.2.4)
23
+ rb-appscript (>= 0.5.3)
24
+ yajl-ruby (0.7.7)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ rspec (>= 2.0.0.beta.20)
31
+ teambox-client (= 0.2.0)
32
+ things-client (= 0.2.4)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Arkadiusz Holko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+
2
+ **teambox-things-sync** lets you sync your Things todos to and from Teambox Collaboration Software.
3
+
4
+ It fetches todos from remote teambox app (project, task name, due date, task list name) and updates completed ones in both ways. Project (named like the one in teambox) will be created in Things if necessary. By default only tasks assigned to you are imported.
5
+
6
+ Installation
7
+ -------------------------------------------------------------------------------
8
+
9
+ For now you can install only from source:
10
+
11
+ git clone git://github.com/fastred/teambox-things-sync.git
12
+ cd teambox-things-sync
13
+ bundle install
14
+
15
+ Then create a file $HOME/.teambox with these values:
16
+
17
+ username: your_teambox_username_or_email
18
+ password: password
19
+ site_url: http://your-teambox-app.com (optional, default set to http://teambox.com)
20
+
21
+
22
+ Usage
23
+ -------------------------------------------------------------------------------
24
+
25
+ Now you can run:
26
+
27
+ ruby sync.rb
28
+
29
+ You should see output with names of imported tasks.
30
+
31
+ If it worked you can add it to your crontab:
32
+
33
+ 1,31 * * * * cd /path/to/teambox-things-sync/ && ruby sync.rb
34
+
35
+ Legal
36
+ -------------------------------------------------------------------------------
37
+
38
+ Things is a trademark of Cultured Code GmbH & Co. KG.
@@ -0,0 +1,16 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |gemspec|
5
+ gemspec.name = "teambox-things-sync"
6
+ gemspec.summary = "Simple ruby app for Teambox and Things.app syncing"
7
+ gemspec.email = "fastred@fastred.org"
8
+ gemspec.homepage = "http://github.com/fastred/teambox-things-sync"
9
+ gemspec.authors = ["Arkadiusz Holko"]
10
+ gemspec.add_development_dependency "rspec", ">=2.0.0.beta.20"
11
+ gemspec.add_dependency "teambox-client", "0.2.0"
12
+ gemspec.add_dependency "things-client", "0.2.4"
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: gem install jeweler"
16
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
4
+
5
+ require 'teambox-things-sync'
6
+ require 'teambox-things-sync/command'
@@ -0,0 +1,15 @@
1
+ require 'teambox-client'
2
+ require 'things'
3
+ require 'time'
4
+ require 'timeout'
5
+
6
+ module TeamboxThingsSync
7
+ autoload :Base, File.dirname(__FILE__) + '/teambox-things-sync/base'
8
+ autoload :ConfigStore, File.dirname(__FILE__) + '/teambox-things-sync/config_store'
9
+ autoload :TaskListCache, File.dirname(__FILE__) + '/teambox-things-sync/task_list_cache'
10
+ end
11
+
12
+ module Things
13
+ additional_properties = %{properties :tag_names, :due_date}
14
+ Todo.module_eval(additional_properties)
15
+ end
@@ -0,0 +1,146 @@
1
+ module TeamboxThingsSync
2
+ class Base
3
+
4
+ attr_reader :config
5
+
6
+ def initialize
7
+ parse_config_file
8
+ httpauth = Teambox::HTTPAuth.new(@config['username'], @config['password'],
9
+ :api_endpoint => api_url(@config['teambox_url']))
10
+ @client = Teambox::Base.new(httpauth)
11
+ end
12
+
13
+ def synchronise
14
+ Timeout::timeout(@config['timeout_limit']) {
15
+ begin
16
+ projects = @client.projects
17
+ rescue
18
+ abort "Cannot fetch project list, check your internet connection."
19
+ end
20
+
21
+ projects.each do |project|
22
+ log "Started working with #{project.name}"
23
+ begin
24
+ mark_as_done_at_remote(project)
25
+
26
+ person_id = find_person_id(project.permalink)
27
+ fetch_tasks_from_remote(project, person_id)
28
+ rescue
29
+ abort "Something went wrong while updating.\n" + $!
30
+ end
31
+ end
32
+ }
33
+ end
34
+
35
+
36
+ def self.find_or_create_project_in_things(project_name)
37
+ Things::Project.find(project_name) or Things::Project.create(:name => project_name)
38
+ end
39
+
40
+ def self.find_or_create_todo_in_things(todo_name)
41
+ Things::Todo.find(todo_name) or Things::Todo.create(:name => todo_name)
42
+ end
43
+
44
+ def task_url(project_permalink, task_list_id, task_id)
45
+ "#{@config['teambox_url']}/projects/#{project_permalink}/"+
46
+ "task_lists/#{task_list_id}/tasks/#{task_id}"
47
+ end
48
+
49
+ def api_url(url)
50
+ url.gsub("http://", "")
51
+ end
52
+
53
+ def is_task_open?(status)
54
+ [0,1].include? status
55
+ end
56
+
57
+ private
58
+
59
+ # update tasks marked as done in Things
60
+ def mark_as_done_at_remote(project)
61
+ @client.tasks.each do |task|
62
+ things_todo = Things::Todo.find(task.name)
63
+ if !things_todo.nil? && things_todo.completed? && is_task_open?(task.status)
64
+ # API isn't great right now, so we update task as resolved
65
+ # and add the new comment to it
66
+ @client.update_project_task(project.permalink, task.id, {:status => 3})
67
+ @client.create_project_task_comment(project.permalink, task.id,
68
+ {:status => 3,
69
+ :body => "Sent from my Things.app"})
70
+ log "#{task.name} at remote set as done"
71
+ end
72
+ end
73
+ end
74
+
75
+ # fetches current task data from remote
76
+ def fetch_tasks_from_remote(project, person_id)
77
+ task_list_cache = TaskListCache.new(@client, project.permalink)
78
+ @client.tasks.each do |task|
79
+ # grab only tasks assigned to current user
80
+ if task.assigned_id == person_id
81
+ things_todo = Base.find_or_create_todo_in_things(task.name)
82
+ # update only those that aren't completed in both local and remote
83
+ if is_task_open?(task.status) || !things_todo.completed?
84
+ things_todo.project = Base.find_or_create_project_in_things(project.name)
85
+ things_todo.notes = "Don't edit this field!\n" +
86
+ task_url(project.permalink, task.task_list_id, task.id)
87
+
88
+ unless task.due_on.nil?
89
+ things_todo.due_date = Time.parse(task.due_on)
90
+ end
91
+
92
+ # data from remote has higher priority
93
+ unless is_task_open?(task.status)
94
+ things_todo.complete
95
+ else
96
+ things_todo.open
97
+ end
98
+
99
+ things_todo.tag_names = task_list_cache[task.task_list_id]
100
+ things_todo.save
101
+ log "\"#{task.name}\" has been saved in Things.app"
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def upload_tasks_to_remote(project)
108
+ # maybe add task from things to teambox app (in future)
109
+ # things_project_tasks = things_project.todos
110
+ # things_project_tasks.each do |todo|
111
+ # if !all_tasks.any?{|t| t.name == todo.name}
112
+ # end
113
+ # end
114
+ end
115
+
116
+ # finds person_id in given project
117
+ def find_person_id(project_permalink)
118
+ project_people = @client.project_people(project_permalink)
119
+ project_people.find { |person| person.user_id == @client.account.id }.id
120
+ end
121
+
122
+ def log(text)
123
+ puts text if @config['output_log']
124
+ end
125
+
126
+
127
+ def parse_config_file
128
+ @config = ConfigStore.new("#{ENV['HOME']}/.teambox")
129
+ @config['teambox_url'] ||= "http://teambox.com"
130
+ @config['output_log'] ||= true
131
+ @config['timeout_limit'] ||= 40
132
+ check_correctness_of_config
133
+ end
134
+
135
+ def check_correctness_of_config
136
+ if @config['username'].nil? || @config['password'].nil?
137
+ abort "'username' and/or 'password' not found in " +
138
+ "#{ENV['HOME']}/.teambox file. See README."
139
+ end
140
+
141
+ unless @config['timeout_limit'].integer?
142
+ abort "timeout_limit should be a number (integer)"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1 @@
1
+ TeamboxThingsSync::Base.new.synchronise
@@ -0,0 +1,42 @@
1
+ # borrowed from http://github.com/teambox/teambox-ruby-client/blob/master/examples/helpers/config_store.rb
2
+ module TeamboxThingsSync
3
+ class ConfigStore
4
+
5
+ attr_reader :file
6
+
7
+ def initialize(file)
8
+ @file = file
9
+ end
10
+
11
+ def load
12
+ @config ||= YAML::load(open(file))
13
+ self
14
+ end
15
+
16
+ def [](key)
17
+ load
18
+ @config[key]
19
+ end
20
+
21
+ def []=(key, value)
22
+ @config[key] = value
23
+ end
24
+
25
+ def delete(*keys)
26
+ keys.each { |key| @config.delete(key) }
27
+ save
28
+ self
29
+ end
30
+
31
+ def update(c={})
32
+ @config.merge!(c)
33
+ save
34
+ self
35
+ end
36
+
37
+ def save
38
+ File.open(file, 'w') { |f| f.write(YAML.dump(@config)) }
39
+ self
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ module TeamboxThingsSync
2
+ class TaskListCache
3
+
4
+ def initialize(client, project_name)
5
+ @data = {}
6
+ @client = client
7
+ @project_name = project_name
8
+ end
9
+
10
+ def [](task_list_id)
11
+ if @data[task_list_id].nil?
12
+ @data[task_list_id] = @client.project_task_list(@project_name, task_list_id).name
13
+ end
14
+ @data[task_list_id]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require "bundler/setup"
3
+ require 'teambox-client'
4
+ require 'things'
5
+ require 'time'
6
+ require 'timeout'
7
+ require 'rspec'
8
+ Dir["#{File.dirname(__FILE__)}/../lib/*.rb"].each {|f| require f}
@@ -0,0 +1,46 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "TeamboxThingsSync::Base" do
4
+ describe "#task_url" do
5
+ it "should return correct url to teambox.com' task" do
6
+ sync = TeamboxThingsSync::Base.new
7
+ sync.task_url("proj", "1", "2").should ==
8
+ "http://teambox.com/projects/proj/task_lists/1/tasks/2"
9
+ end
10
+
11
+ it "should return correct url to task if teambox url changed" do
12
+ sync = TeamboxThingsSync::Base.new
13
+ sync.config["teambox_url"] = "http://testteambox.com"
14
+ sync.task_url("proj", "1", "2").should ==
15
+ "http://testteambox.com/projects/proj/task_lists/1/tasks/2"
16
+ end
17
+ end
18
+
19
+ describe "#.find_or_create_project_in_things" do
20
+
21
+ it "should find project by name in Things" do
22
+ Things::Project.should_receive(:find).with("testowy").and_return(true)
23
+ TeamboxThingsSync::Base.find_or_create_project_in_things("testowy")
24
+ end
25
+
26
+ it "should create new project in Things if it doesn't exist" do
27
+ Things::Project.should_receive(:find).with("testowy").and_return(false)
28
+ Things::Project.should_receive(:create).with(:name => "testowy").and_return(true)
29
+ TeamboxThingsSync::Base.find_or_create_project_in_things("testowy")
30
+ end
31
+ end
32
+
33
+ describe "#.find_or_create_todo_in_things" do
34
+
35
+ it "should find todo by name in Things" do
36
+ Things::Todo.should_receive(:find).with("testowe").and_return(true)
37
+ TeamboxThingsSync::Base.find_or_create_todo_in_things("testowe")
38
+ end
39
+
40
+ it "should create new todo in Things if it doesn't exist" do
41
+ Things::Todo.should_receive(:find).with("testowe").and_return(false)
42
+ Things::Todo.should_receive(:create).with(:name => "testowe").and_return(true)
43
+ TeamboxThingsSync::Base.find_or_create_todo_in_things("testowe")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "TeamboxThingsSync::TaskListCache" do
4
+ pending
5
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe TeamboxThingsSync do
4
+ pending
5
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teambox-things-sync
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Arkadiusz Holko
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-15 00:00:00 +02:00
18
+ default_executable: teambox-things-sync
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 0
30
+ - 0
31
+ - beta
32
+ - 20
33
+ version: 2.0.0.beta.20
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: teambox-client
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 2
46
+ - 0
47
+ version: 0.2.0
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: things-client
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ - 2
60
+ - 4
61
+ version: 0.2.4
62
+ type: :runtime
63
+ version_requirements: *id003
64
+ description:
65
+ email: fastred@fastred.org
66
+ executables:
67
+ - teambox-things-sync
68
+ extensions: []
69
+
70
+ extra_rdoc_files:
71
+ - LICENSE
72
+ - README.md
73
+ files:
74
+ - .gitignore
75
+ - Gemfile
76
+ - Gemfile.lock
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - VERSION
81
+ - bin/teambox-things-sync
82
+ - lib/teambox-things-sync.rb
83
+ - lib/teambox-things-sync/base.rb
84
+ - lib/teambox-things-sync/command.rb
85
+ - lib/teambox-things-sync/config_store.rb
86
+ - lib/teambox-things-sync/task_list_cache.rb
87
+ - spec/spec_helper.rb
88
+ - spec/teambox-things-sync/base_spec.rb
89
+ - spec/teambox-things-sync/task_list_cache_spec.rb
90
+ - spec/teambox-things-sync_spec.rb
91
+ has_rdoc: true
92
+ homepage: http://github.com/fastred/teambox-things-sync
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options:
97
+ - --charset=UTF-8
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ requirements: []
115
+
116
+ rubyforge_project:
117
+ rubygems_version: 1.3.6
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Simple ruby app for Teambox and Things.app syncing
121
+ test_files:
122
+ - spec/spec_helper.rb
123
+ - spec/teambox-things-sync/base_spec.rb
124
+ - spec/teambox-things-sync/task_list_cache_spec.rb
125
+ - spec/teambox-things-sync_spec.rb