namelessjon-todoist 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.
@@ -0,0 +1,44 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rcov/rcovtask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "todoist"
10
+ s.summary = "a library for interacting with the Todoist public API"
11
+ s.email = "jonathan.stott@gmail.com"
12
+ s.homepage = "http://github.com/namelessjon/todoist"
13
+ s.description = "The todoist gem offers convinience methods and wrappers for the todoist list management service, easing retrival and parsing of the responses. It also offers a simple command-line client."
14
+ s.authors = ["Jonathan Stott"]
15
+ s.files = FileList["lib/**/*.rb", "spec/**/*.rb", 'Rakefile', "[A-Z]+"]
16
+ s.add_dependency('highline', '~> 1.5')
17
+ s.add_dependency('thor', '~> 0.9')
18
+ s.add_dependency('httparty', '~> 0.3')
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
22
+ end
23
+
24
+ Rake::TestTask.new do |t|
25
+ t.libs << 'lib'
26
+ t.pattern = 'spec/**/*_spec.rb'
27
+ t.verbose = true
28
+ end
29
+
30
+ Rake::RDocTask.new do |rdoc|
31
+ rdoc.rdoc_dir = 'rdoc'
32
+ rdoc.title = 'Todoist'
33
+ rdoc.options << '--line-numbers' << '--inline-source'
34
+ rdoc.rdoc_files.include('README*')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
37
+
38
+ Rcov::RcovTask.new do |t|
39
+ t.libs << "spec"
40
+ t.test_files = FileList['spec/**/*_spec.rb']
41
+ t.verbose = true
42
+ end
43
+
44
+ task :default => :rcov
@@ -0,0 +1,90 @@
1
+ require 'thor'
2
+ require 'highline'
3
+
4
+ HighLine.track_eof = false
5
+
6
+ dir = File.dirname(__FILE__)
7
+ require File.join(dir, '..', 'lib', 'todoist')
8
+
9
+ class TodoistCLI < Thor
10
+
11
+ desc "tasks", "list of projects with uncompleted tasks, and their tasks"
12
+ def tasks(name=nil)
13
+ setup_todoist_base
14
+
15
+ Todoist::Project.all.each do |project|
16
+ next if name and project.name !~ /#{name}/
17
+
18
+ if project.task_count > 0
19
+ print_project(project)
20
+ end
21
+ end
22
+ end
23
+
24
+ desc "complete", "Completes the task with the given content"
25
+ method_options :all => :boolean
26
+ def complete(content)
27
+ setup_todoist_base
28
+
29
+ @h = HighLine.new
30
+
31
+ to_complete = []
32
+ Todoist::Task.all.each do |task|
33
+ next if task.content !~ /#{content}/
34
+ to_complete << task
35
+ end
36
+
37
+ tasks = []
38
+ if to_complete.size > 1 and !options[:all]
39
+ @h.say("Choose tasks to delete:")
40
+ to_complete.each do |task|
41
+ if @h.agree("Delete #{task} (#{task.project})?")
42
+ tasks << task
43
+ end
44
+ end
45
+ else
46
+ tasks = to_complete
47
+ end
48
+
49
+ Todoist::Task.complete(tasks)
50
+ end
51
+
52
+ desc "overdue", "Fetch all overdue tasks"
53
+ def overdue
54
+ setup_todoist_base
55
+ Todoist::Task.overdue.each do |task|
56
+ puts task
57
+ end
58
+ end
59
+
60
+ protected
61
+ def print_project(project)
62
+ puts project
63
+ project.tasks.each do |task|
64
+ print " " * (task.indent.to_i - 1)
65
+ print (task.content =~ /\* /) ? '' : '- '
66
+ puts task
67
+ end
68
+ puts ""
69
+ end
70
+
71
+ def setup_todoist_base
72
+ if File.file?(File.expand_path('~/.todoistrc'))
73
+ require 'yaml'
74
+ conf = YAML.load_file(File.expand_path('~/.todoistrc'))
75
+ Todoist::Base.setup(conf['token'], conf['premium'])
76
+ else
77
+ puts <<-eos
78
+ You need to setup ~/.todoistrc, for example:
79
+ token: 91b1ce9e153fc5390ac3db4f0ded9f31a1a7ba23 # use your api key
80
+ premium: false # if this is true, ssl will be used.
81
+ eos
82
+ exit(1)
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ TodoistCLI.start
89
+
90
+ # vim: set ft=ruby:
@@ -0,0 +1,8 @@
1
+ module Todoist
2
+ end
3
+
4
+ dir = File.dirname(__FILE__)
5
+
6
+ require File.join(dir, 'todoist', 'base')
7
+ require File.join(dir, 'todoist', 'project')
8
+ require File.join(dir, 'todoist', 'task')
@@ -0,0 +1,38 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ module Todoist
4
+ ##
5
+ # The Todoist::Base class is responsible for making all queries to the
6
+ # todoist web API.
7
+ class Base
8
+ include HTTParty
9
+ format :json
10
+
11
+ ##
12
+ # Sets up the Todoist::Base class for making requests, setting the API key
13
+ # and if the account is a premium one or not.
14
+ def self.setup(token, premium = false)
15
+ self.default_params :token => token
16
+ @@premium = premium
17
+ set_base_uri
18
+ end
19
+
20
+ private
21
+ def self.set_base_uri
22
+ if premium?
23
+ base_uri 'https://todoist.com/API'
24
+ else
25
+ base_uri 'http://todoist.com/API'
26
+ end
27
+ end
28
+
29
+ def self.premium?
30
+ (@@premium) ? true : false
31
+ end
32
+
33
+ def id_array(ids)
34
+ ids.flatten.map {|i| i.to_i }.to_json
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,126 @@
1
+ module Todoist
2
+ ##
3
+ # Project
4
+ #
5
+ # A todoist project.
6
+ class Project
7
+ attr_reader :name, :id, :user_id, :color, :collapsed, :order, :indent
8
+
9
+ ##
10
+ # Get all projects
11
+ #
12
+ # Fetches all the user's todist projects.
13
+ #
14
+ # @return [Array] An Array of todoist project instances.
15
+ def self.all
16
+ projects = []
17
+ Base.get('/getProjects').each do |project|
18
+ projects << new_from_api_request(project)
19
+ end
20
+ projects
21
+ end
22
+
23
+ ##
24
+ # Get a project
25
+ #
26
+ # Fetches a todoist project
27
+ #
28
+ # @param [Integer,Todoist::Project] id The id, or project, to fetch
29
+ #
30
+ # @return [Todoist::Project] The fetched project.
31
+ def self.get(id)
32
+ new_from_api_request(get_project(id))
33
+ end
34
+
35
+ ##
36
+ # Create a new project
37
+ #
38
+ # Creates a new Todoist project.
39
+ #
40
+ # @param [String] name The name of the project
41
+ #
42
+ # @param [Hash] parameters The other parameters which make up the project.
43
+ #
44
+ # @return [Todoist::Project] The new todoist project.
45
+ def initialize(name, parameters={})
46
+ @name = name
47
+ @id = parameters['id']
48
+ @user_id = parameters['user_id']
49
+ @color = parameters['color']
50
+ @collapsed = parameters['collapsed']
51
+ @order = parameters['item_order']
52
+ @count = parameters['cache_count']
53
+ @indent = parameters['indent']
54
+ end
55
+
56
+
57
+ def to_s
58
+ "#{name}"
59
+ end
60
+
61
+ def to_i
62
+ id
63
+ end
64
+
65
+ def inspect
66
+ "<Project:#{name}:#{id}:#{task_count}:user_id=#{user_id} color='#{color}' collapsed=#{collapsed?} order=#{order} indent=#{indent}>"
67
+ end
68
+
69
+ def collapsed?
70
+ (collapsed == 1) ? true : false
71
+ end
72
+
73
+
74
+ ##
75
+ # The task count
76
+ #
77
+ # The number of tasks a project has, according to its cache_count when it was fetched
78
+ #
79
+ # @return [Integer] The number of tasks this project has
80
+ def task_count
81
+ @count
82
+ end
83
+
84
+ ##
85
+ # Get uncompleted tasks for the project
86
+ #
87
+ # @return [Array] An Array of Todoist::Tasks
88
+ def tasks
89
+ Task.uncompleted(self)
90
+ end
91
+
92
+ ##
93
+ # Get completed tasks for the project
94
+ #
95
+ # @return [Array] An Array of completed Todoist::Tasks
96
+ def completed_tasks
97
+ Task.completed(self)
98
+ end
99
+
100
+ ##
101
+ # Add task
102
+ #
103
+ # Adds a task to the project.
104
+ #
105
+ # @param [String] content The content of the new task
106
+ #
107
+ # @param [Hash] opts The options for the new task
108
+ #
109
+ # @return [Todoist::Task] The new task.
110
+ def add_task(content, opts={})
111
+ Task.new(content, self, opts).save
112
+ end
113
+
114
+ private
115
+
116
+ def self.new_from_api_request(project)
117
+ name = project.delete('name')
118
+ new(name, project)
119
+ end
120
+
121
+ def self.get_project(id)
122
+ Base.get('/getProject', :query => {:project_id => id.to_i })
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,252 @@
1
+ require 'time'
2
+ module Todoist
3
+ ##
4
+ # Todoist Task
5
+ #
6
+ # A todoist task.
7
+ class Task
8
+ attr_accessor :content, :priority, :task_details, :project_id
9
+ attr_reader :date, :id
10
+ ##
11
+ # Get tasks for a project
12
+ #
13
+ # Retrieves all uncompleted tasks from the todoist service for the project id.
14
+ #
15
+ # @param [Integer,Todoist::Project] project The project to get tasks for
16
+ #
17
+ # @return [Array] An array of todoist tasks.
18
+ def self.uncompleted(project)
19
+ make_tasks(Base.get('/getUncompletedItems', :query => { :project_id => project.to_i }))
20
+ end
21
+
22
+ ##
23
+ # Get completed tasks for project
24
+ #
25
+ # Retrieves completed tasks from the todoist service for the project id.
26
+ #
27
+ # @param [Integer,Todoist::Project] project The project to get tasks for
28
+ #
29
+ # @return [Array] An array of todoist tasks.
30
+ def self.completed(project)
31
+ make_tasks(Base.get('/getCompletedItems', :query => { :project_id => project.to_i }))
32
+ end
33
+
34
+ ##
35
+ # Complete tasks
36
+ #
37
+ # Completes a list of tasks
38
+ #
39
+ # @param [Array,Integer,Todist::Task] ids A list of tasks to complete.
40
+ def self.complete(*ids)
41
+ make_tasks(Base.get('/completeItems', :query => {:ids => id_array(ids)}))
42
+ end
43
+
44
+ ##
45
+ # Get Tasks by ID
46
+ #
47
+ # Retrives a list of tasks from the todoist service.
48
+ #
49
+ # @param [Array,Integer,Todist::Task] ids A list of tasks to retrieve
50
+ #
51
+ # @return [Array] An array of Todist::Tasks
52
+ def self.get(*ids)
53
+ make_tasks(Base.get('/getItemsById', :query => {:ids => id_array(ids)}))
54
+ end
55
+
56
+ ##
57
+ # Get all tasks
58
+ #
59
+ # Retrieves all the uncompleted tasks
60
+ #
61
+ # @return [Array] An array of Todist::Tasks
62
+ def self.all
63
+ query('viewall')['viewall']
64
+ end
65
+
66
+ ##
67
+ # Get overdue tasks
68
+ #
69
+ # Retrieves all the overdue tasks
70
+ #
71
+ # @return [Array] An array of overdue Todist::Tasks
72
+ def self.overdue
73
+ query('overdue')['overdue']
74
+ end
75
+
76
+
77
+ ##
78
+ # Query
79
+ #
80
+ # Use the task query API to get back several arrays of tasks.
81
+ #
82
+ # @param [String,Array] query A query or a list of queries to perform
83
+ #
84
+ # @return [Hash] a hash keyed on query, containing arrays of tasks.
85
+ #
86
+ # Allowed queries
87
+ # viewall:: All tasks
88
+ # overdue:: All overdue tasks
89
+ # p[123]:: All tasks of priority 1, 2 ,3
90
+ def self.query(*queries)
91
+ query = '["' + queries.flatten.map { |q| q.to_s }.join('","') + '"]'
92
+ results = {}
93
+ response = Base.get('/query', :query => { :queries => query })
94
+
95
+ response.each do |q|
96
+ if q['type'] == 'viewall'
97
+ tasks = []
98
+ q['data'].each do |stuff|
99
+ tasks << make_tasks(stuff['uncompleted'])
100
+ end
101
+ results[q['type']] = tasks.flatten
102
+ else
103
+ results[q['type']] = make_tasks(q['data'])
104
+ end
105
+ end
106
+ results
107
+ end
108
+
109
+ ##
110
+ # Create a new task
111
+ #
112
+ # @param [String] content The content of the new task.
113
+ #
114
+ # @param [Integer,Todoist::Project] project The project to create the new task in
115
+ #
116
+ # @param [Hash] opts Optional priority and due date for creation.
117
+ def self.create(content, project, opts={})
118
+ query = {:project_id => project.to_i, :content => content.to_s }
119
+ query['priority'] = opts.delete('priority') if opts.has_key?('priority')
120
+ query['date_string'] = opts.delete('date_string') if opts.has_key?('date_string')
121
+
122
+ new_from_api(Base.get('/addItem', :query => query))
123
+ end
124
+
125
+ ##
126
+ # Updates a task
127
+ #
128
+ # @param [String] content The new content of the task.
129
+ #
130
+ # @param [Integer,Todoist::Task] id The task to update
131
+ #
132
+ # @param [Hash] opts Optional priority and due date for creation.
133
+ def self.update(content, id, opts={})
134
+ query = {:id => project.to_i, :content => content.to_s }
135
+ query['priority'] = opts.delete('priority') if opts.has_key?('priority')
136
+ query['date_string'] = opts.delete('date_string') if opts.has_key?('date_string')
137
+
138
+ new_from_api(Base.get('/updateItem', :query => query))
139
+ end
140
+
141
+
142
+
143
+ def initialize(content, project_id, d={})
144
+ @content = content
145
+ @project_id = project_id.to_i
146
+ @date = d.delete('date') || d.delete('date_string')
147
+ @priority = d.delete('priority')
148
+ @id = d.delete('id')
149
+ @task_details = d
150
+ end
151
+
152
+ ##
153
+ # Is the task complete
154
+ def complete?
155
+ (@task_details['in_history'] == 1) ? true : false
156
+ end
157
+
158
+
159
+ ##
160
+ # Complete
161
+ #
162
+ # Completes the todoist task
163
+ def complete
164
+ self.class.complete(self) unless complete?
165
+ @task_details['in_history'] = 1
166
+ end
167
+
168
+ ##
169
+ # Is the task overdue?
170
+ def overdue?
171
+ return false unless due_date
172
+ Time.now > Time.parse(due_date)
173
+ end
174
+
175
+ ##
176
+ # Project
177
+ #
178
+ # Retreives the project for the task
179
+ def project
180
+ Project.get(project_id)
181
+ end
182
+
183
+ ##
184
+ # Saves the task
185
+ #
186
+ # Save the task, either creating a new task on the todoist service, or
187
+ # updating a previously retrieved task with new content, priority etc.
188
+ def save
189
+ opts = {}
190
+ opts['priority'] = priority if priority
191
+ opts['date_string'] = date if date
192
+ # if we don't have an id, then we can assume this is a new task.
193
+ unless (task_details.has_key?('id'))
194
+ result = self.class.create(self.content, self.project_id, opts)
195
+ else
196
+ result = self.class.update(self.content, self.id, opts)
197
+ end
198
+
199
+ self.content = result.content
200
+ self.project_id = result.project_id
201
+ self.task_details = result.task_details
202
+ self
203
+ end
204
+
205
+
206
+
207
+ def to_s
208
+ @content
209
+ end
210
+
211
+ def to_i
212
+ id
213
+ end
214
+
215
+ def inspect
216
+ "<Todoist::Task:#{content}:#{id}:#{project_id}:#{task_details.inspect}>"
217
+ end
218
+
219
+ def method_missing(*args, &block)
220
+ # the method name
221
+ m = args.shift
222
+ if @task_details.has_key?(m.to_s)
223
+ return @task_details[m.to_s]
224
+ else
225
+ raise NoMethodError, "undefined method `#{m}' for #{self.inspect}"
226
+ end
227
+ end
228
+
229
+ private
230
+
231
+ def self.new_from_api(task)
232
+ content = task.delete('content')
233
+ project_id = task.delete('project_id')
234
+ new(content, project_id, task)
235
+ end
236
+
237
+ def self.make_tasks(tasks)
238
+ new_tasks = []
239
+ tasks.each do |task|
240
+ new_tasks << new_from_api(task)
241
+ end
242
+ new_tasks
243
+ end
244
+
245
+ def self.id_array(*ids)
246
+ "[" + ids.flatten.map { |q| q.to_i }.join(",") + "]"
247
+ end
248
+
249
+
250
+ # {"due_date": null, "collapsed": 0, "labels": [], "is_dst": 0, "has_notifications": 0, "checked": 0, "indent": 1, "children": null, "content": "Finish this gem", "user_id": 34615, "mm_offset": 0, "in_history": 0, "id": 4152190, "priority": 4, "item_order": 1, "project_id": 528294, "chains": null, "date_string": ""}
251
+ end
252
+ end
@@ -0,0 +1,2 @@
1
+ require 'rubygems'
2
+ require 'bacon'
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'Todoist' do
4
+ it "is" do
5
+ true.should.equal true
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: namelessjon-todoist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Stott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-15 00:00:00 -08:00
13
+ default_executable: todoist
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: highline
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "1.5"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: thor
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: "0.9"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: httparty
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: "0.3"
44
+ version:
45
+ description: The todoist gem offers convinience methods and wrappers for the todoist list management service, easing retrival and parsing of the responses. It also offers a simple command-line client.
46
+ email: jonathan.stott@gmail.com
47
+ executables:
48
+ - todoist
49
+ extensions: []
50
+
51
+ extra_rdoc_files: []
52
+
53
+ files:
54
+ - lib/todoist/base.rb
55
+ - lib/todoist/project.rb
56
+ - lib/todoist/task.rb
57
+ - lib/todoist.rb
58
+ - spec/spec_helper.rb
59
+ - spec/todoist_spec.rb
60
+ - Rakefile
61
+ - bin/todoist
62
+ has_rdoc: true
63
+ homepage: http://github.com/namelessjon/todoist
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --inline-source
67
+ - --charset=UTF-8
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.2.0
86
+ signing_key:
87
+ specification_version: 2
88
+ summary: a library for interacting with the Todoist public API
89
+ test_files: []
90
+