zzamboni-things2thl 0.5.0 → 0.7.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/ChangeLog CHANGED
@@ -1,6 +1,67 @@
1
+ 2009-05-21 Diego Zamboni <diego@zzamboni.org>
2
+
3
+ * VERSION, lib/Things2THL.rb: Version bump to 0.7.0
4
+
5
+ 2009-05-21 Diego Zamboni <diego@zzamboni.org>
6
+
7
+ * README, bin/things2thl, lib/Things2THL.rb: Added a new mode of
8
+ operation --projects-areas-as-lists (-B). As the name implies, in
9
+ this mode both projects and areas from Things are stored as lists in
10
+ THL. This means that projects are NOT nested inside areas, as is the
11
+ case in the other two modes. Instead, the lists for areas will
12
+ contain only single tasks that were inside the area in Things. If --projects-top-level and --areas-top-level are not given, all the
13
+ lists will be created in the main folders group. Otherwise, they
14
+ will appear within the corresponding folder.
15
+
16
+ 2009-05-21 Diego Zamboni <diego@zzamboni.org>
17
+
18
+ * bin/things2thl, lib/Things2THL.rb: Added --areas-top-level option
19
+ to specify a folder in which imported Areas should be created.
20
+
21
+ 2009-05-20 Diego Zamboni <diego@zzamboni.org>
22
+
23
+ * README: Updated README
24
+
25
+ 2009-05-20 Diego Zamboni <diego@zzamboni.org>
26
+
27
+ * things2thl.gemspec: Regenerated gemspec for version 0.6.0
28
+
29
+ 2009-05-20 Diego Zamboni <diego@zzamboni.org>
30
+
31
+ * ChangeLog, VERSION, lib/Things2THL.rb: Version bump to 0.6.0
32
+
33
+ 2009-05-20 Diego Zamboni <diego@zzamboni.org>
34
+
35
+ * Rakefile, lib/Things2THL.rb: Vastly improved conversion of the
36
+ notes field. The HTML stored in the Things notes is parsed and
37
+ rendered as text, including all the appropriate URLs and links. It
38
+ does not look exactly as the original, but is quite usable.
39
+
1
40
  2009-05-19 Diego Zamboni <diego@zzamboni.org>
2
41
 
3
- * lib/Things2THL.rb: Version bump to 0.5.0
42
+ * README: Updated to state limitation of transferring repeating
43
+ tasks.
44
+
45
+ 2009-05-19 Diego Zamboni <diego@zzamboni.org>
46
+
47
+ * lib/Things2THL.rb: Make Scheduled and Logbook a list instead of a
48
+ folder when --projects-as-tasks. Someday stays always as a folder,
49
+ because it can contain areas (suspended areas)
50
+
51
+ 2009-05-19 Diego Zamboni <diego@zzamboni.org>
52
+
53
+ * lib/Things2THL.rb: Transfer activation_date as start_date so that
54
+ scheduled (one-time) tasks are set appropriately. Still need to
55
+ figure out how to identify repeating tasks.
56
+
57
+ 2009-05-19 Diego Zamboni <diego@zzamboni.org>
58
+
59
+ * things2thl.gemspec: Regenerated gemspec for version 0.5.0
60
+
61
+ 2009-05-19 Diego Zamboni <diego@zzamboni.org>
62
+
63
+ * ChangeLog, README, VERSION, lib/Things2THL.rb: Version bump to
64
+ 0.5.0
4
65
 
5
66
  2009-05-19 Diego Zamboni <diego@zzamboni.org>
6
67
 
data/README CHANGED
@@ -1,4 +1,4 @@
1
- Things2THL
1
+ Things2THL 0.7.0
2
2
  http://zzamboni.github.com/things2thl/
3
3
 
4
4
  Conversion program to transfer data from Things
@@ -17,9 +17,10 @@ support.
17
17
  Then install things2thl by running:
18
18
  $ sudo gem install zzamboni-things2thl --source http://gems.github.com/
19
19
 
20
- Note: things2thl need the rb-appscript library from
21
- http://appscript.sourceforge.net/rb-appscript/install.html. If you
22
- don't have it already, it will be automatically installed by gem.
20
+ Note: things2thl need the rb-appscript
21
+ (http://appscript.sourceforge.net/rb-appscript/) and hpricot
22
+ (http://wiki.github.com/why/hpricot) . If you don't have them already,
23
+ they will be automatically installed by gem.
23
24
 
24
25
 
25
26
 
@@ -31,6 +32,9 @@ Usage: things2thl <mode of operation> [options]
31
32
  Modes of operation (required):
32
33
  -L, --projects-as-lists Convert projects in Things to lists in THL
33
34
  -T, --projects-as-tasks Convert projects in Things to tasks in THL
35
+ -B, --projects-areas-as-tasks Convert both projects and areas in Things
36
+ to lists in THL. This implies that
37
+ projects are not nested inside areas.
34
38
 
35
39
  Options:
36
40
  --[no-]areas Transfer areas from Things (default: yes)
@@ -53,6 +57,8 @@ Options:
53
57
  'Projects' list is always created, but
54
58
  this option can be used to specify
55
59
  its name.
60
+ --areas-top-level FOLDER The named folder will be created to
61
+ contain all areas.
56
62
  -c, --completed Transfer also completed/canceled tasks
57
63
  and projects (default: no)
58
64
  --[no-]archive-completed If transferring completed/canceled tasks,
@@ -67,15 +73,9 @@ Options you should seldom need:
67
73
  --thl THLAPP Location of the The Hit List application
68
74
  (default: /Applications/The Hit List.app)
69
75
 
70
-
71
76
  Functionality still missing:
72
77
  ---------------------------
73
78
 
74
- - Handle rich-text notes (with attachments, links, etc.) properly
75
-
76
- Plan: not sure yet. Need to investigate how notes are stored in
77
- THL (Things uses an XML format)
78
-
79
79
  - Handling delegation ("People" feature in Things)
80
80
 
81
81
  Not sure how to transfer this to THL. Ideas are welcome.
@@ -89,9 +89,9 @@ Known issues:
89
89
  completed/canceled tasks, they will all appear in your "completed
90
90
  today" view.
91
91
 
92
- - Tasks in the Things "Scheduled" focus are transferred, but the
93
- scheduling itself is not transferred, because this information is
94
- not accessible through AS from either Things not THL.
92
+ - One-time tasks in the Things "Scheduled" focus are transferred, but
93
+ repeating tasks are not. The Things Applescript interface does not
94
+ provide access to repeating tasks.
95
95
 
96
96
  - The format of time estimate tags is fixed for the moment. May add
97
- customization if I get any requests for it.
97
+ customization if I get any requests for it.
data/Rakefile CHANGED
@@ -11,6 +11,7 @@ begin
11
11
  gemspec.description = "Library and command-line tool for migrating Things data to The Hit List"
12
12
  gemspec.authors = ["Diego Zamboni"]
13
13
  gemspec.add_dependency('rb-appscript', '>=0.5.1')
14
+ gemspec.add_dependency('hpricot', '>=0.6')
14
15
  end
15
16
  rescue LoadError
16
17
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.7.0
data/bin/things2thl CHANGED
@@ -21,6 +21,10 @@ opts = OptionParser.new do |opts|
21
21
  "Convert projects in Things to lists in THL" ) { options.structure = :projects_as_lists }
22
22
  opts.on("-T", "--projects-as-tasks",
23
23
  "Convert projects in Things to tasks in THL" ) { options.structure = :projects_as_tasks }
24
+ opts.on("-B", "--projects-areas-as-tasks",
25
+ "Convert both projects and areas in Things",
26
+ " to lists in THL. This implies that",
27
+ " projects are not nested inside areas.") { options.structure = :projects_areas_as_lists }
24
28
 
25
29
  opts.separator ''
26
30
  opts.separator("Options:")
@@ -53,6 +57,11 @@ opts = OptionParser.new do |opts|
53
57
  " its name.") do |projfolder|
54
58
  options.projectsfolder = projfolder
55
59
  end
60
+ opts.on("--areas-top-level FOLDER",
61
+ "The named folder will be created to",
62
+ " contain all areas.") do |areafolder|
63
+ options.areasfolder = areafolder
64
+ end
56
65
 
57
66
  opts.on("-c", "--completed",
58
67
  "Transfer also completed/canceled tasks",
data/lib/Things2THL.rb CHANGED
@@ -5,13 +5,14 @@ require "ostruct"
5
5
  require 'time'
6
6
  begin; require 'rubygems'; rescue LoadError; end
7
7
  require 'appscript'; include Appscript
8
+ require 'hpricot'
8
9
 
9
10
  ######################################################################
10
11
 
11
12
  module Things2THL
12
13
  module Version
13
14
  MAJOR = 0
14
- MINOR = 5
15
+ MINOR = 7
15
16
  PATCH = 0
16
17
 
17
18
  STRING = [MAJOR, MINOR, PATCH].join(".")
@@ -64,13 +65,14 @@ module Things2THL
64
65
  :due_date => :due_date,
65
66
  :completion_date => :completed_date,
66
67
  :cancellation_date => :canceled_date,
68
+ :activation_date => :start_date,
67
69
  :status => {
68
70
  :completed => :completed,
69
71
  :canceled => :canceled,
70
72
  },
71
- :notes => :notes,
72
73
  },
73
74
  Proc.new {|node,prop,obj|
75
+ obj.add_notes(node,prop)
74
76
  obj.fix_completed_canceled(node, prop)
75
77
  obj.archive_completed(prop)
76
78
  obj.process_tags(node, prop, true, true)
@@ -90,13 +92,14 @@ module Things2THL
90
92
  :due_date => :due_date,
91
93
  :completion_date => :completed_date,
92
94
  :cancellation_date => :canceled_date,
95
+ :activation_date => :start_date,
93
96
  :status => {
94
97
  :completed => :completed,
95
98
  :canceled => :canceled,
96
99
  },
97
- :notes => :notes,
98
100
  },
99
101
  Proc.new {|node,prop,obj|
102
+ obj.add_notes(node,prop)
100
103
  obj.fix_completed_canceled(node, prop)
101
104
  obj.archive_completed(prop)
102
105
  obj.process_tags(node, prop, false, true)
@@ -109,19 +112,57 @@ module Things2THL
109
112
  :due_date => :due_date,
110
113
  :completion_date => :completed_date,
111
114
  :cancellation_date => :canceled_date,
115
+ :activation_date => :start_date,
112
116
  :status => {
113
117
  :completed => :completed,
114
118
  :canceled => :canceled,
115
119
  },
116
- :notes => :notes,
117
120
  },
118
121
  Proc.new {|node,prop,obj|
122
+ obj.add_notes(node,prop)
119
123
  obj.fix_completed_canceled(node, prop)
120
124
  obj.archive_completed(prop)
121
125
  obj.process_tags(node, prop, false, true)
122
126
  obj.check_today(node, prop)
123
127
  }
124
128
  ]
129
+ },
130
+ :projects_areas_as_lists => {
131
+ :area => [:list,
132
+ {
133
+ :name => :name,
134
+ }],
135
+ :project => [:list,
136
+ {
137
+ :name => :name,
138
+ :creation_date => :created_date,
139
+ },
140
+ Proc.new {|node,prop,obj|
141
+ obj.add_list_notes(node,prop)
142
+ obj.add_project_duedate(node,prop)
143
+ }
144
+ ],
145
+ :selected_to_do => [:task,
146
+ {
147
+ :name => :title,
148
+ :creation_date => :created_date,
149
+ :due_date => :due_date,
150
+ :completion_date => :completed_date,
151
+ :cancellation_date => :canceled_date,
152
+ :activation_date => :start_date,
153
+ :status => {
154
+ :completed => :completed,
155
+ :canceled => :canceled,
156
+ },
157
+ },
158
+ Proc.new {|node,prop,obj|
159
+ obj.add_notes(node,prop)
160
+ obj.fix_completed_canceled(node, prop)
161
+ obj.archive_completed(prop)
162
+ obj.process_tags(node, prop, true, true)
163
+ obj.check_today(node, prop)
164
+ }
165
+ ]
125
166
  }
126
167
  }
127
168
 
@@ -259,7 +300,13 @@ module Things2THL
259
300
  # Get the type of the THL node that corresponds to the given Things node,
260
301
  # depending on the options specified
261
302
  def thl_node_type(node)
262
- Constants::STRUCTURES[options.structure][node.type][0]
303
+ case node
304
+ when Symbol
305
+ type=node
306
+ else
307
+ type=node.type
308
+ end
309
+ Constants::STRUCTURES[options.structure][type][0]
263
310
  end
264
311
 
265
312
  # Get the name/title for a THL node.
@@ -370,10 +417,12 @@ module Things2THL
370
417
  nil
371
418
  when 'Inbox', 'Next'
372
419
  find_or_create(:list, focusname, top_level_node)
373
- when 'Scheduled', 'Someday', 'Logbook'
420
+ when 'Scheduled', 'Logbook'
421
+ find_or_create((thl_node_type(:project) == :task) ? :list : :folder, focusname, top_level_node)
422
+ when 'Someday'
374
423
  find_or_create(:folder, focusname, top_level_node)
375
424
  when 'Projects'
376
- if options.structure == :projects_as_tasks
425
+ if thl_node_type(:project) == :task
377
426
  find_or_create(:list, options.projectsfolder || 'Projects', top_level_node)
378
427
  else
379
428
  if options.projectsfolder
@@ -393,10 +442,12 @@ module Things2THL
393
442
  thl.inbox.get
394
443
  when 'Next'
395
444
  top_level_node
396
- when 'Scheduled', 'Someday', 'Logbook'
445
+ when 'Scheduled', 'Logbook'
446
+ find_or_create((thl_node_type(:project) == :task) ? :list : :folder, focusname, top_level_node)
447
+ when 'Someday'
397
448
  find_or_create(:folder, focusname, top_level_node)
398
449
  when 'Projects'
399
- if options.structure == :projects_as_tasks
450
+ if thl_node_type(:project) == :task
400
451
  find_or_create(:list, options.projectsfolder || 'Projects', top_level_node)
401
452
  else
402
453
  if options.projectsfolder
@@ -426,7 +477,11 @@ module Things2THL
426
477
  if node.suspended
427
478
  return top_level_for_focus('Someday')
428
479
  else
429
- return top_level_node
480
+ if options.areasfolder
481
+ return find_or_create(:folder, options.areasfolder || 'Areas', top_level_node)
482
+ else
483
+ return top_level_node
484
+ end
430
485
  end
431
486
  end
432
487
 
@@ -467,7 +522,6 @@ module Things2THL
467
522
  # we need to find or create an auxiliary list to contain it.
468
523
  def container_for(node)
469
524
  # If its top-level container is nil, it means we need to skip this node
470
- # unless it's an area, areas don't have a focus
471
525
  tlcontainer=top_level_for_node(node)
472
526
  return nil unless tlcontainer
473
527
 
@@ -476,7 +530,7 @@ module Things2THL
476
530
  when :area
477
531
  tlcontainer
478
532
  when :project
479
- if options.areas && node.area?
533
+ if options.areas && (options.structure != :projects_areas_as_lists) && node.area?
480
534
  get_cached_or_process(node.area)
481
535
  else
482
536
  tlcontainer
@@ -702,6 +756,30 @@ module Things2THL
702
756
  prop[:__newnodes__].push(newnode)
703
757
  end
704
758
 
759
+ def hextostring(hexstr)
760
+ [hexstr.delete(" ")].pack("H*")
761
+ end
762
+
763
+ def aliastostring(node)
764
+ hextostring((node/'alias').inner_text)
765
+ end
766
+
767
+ # Process Things notes before adding them to THL.
768
+ def convert_notes(notes)
769
+ return unless notes
770
+ # First, parse the notes as XML and extract the contents of the <note> tag
771
+ note_html = (Hpricot.XML(notes)/'/note').inner_html
772
+ # Then parse the HTML
773
+ html = Hpricot(note_html)
774
+ # Then swap any <alias> tags with their unencoded content
775
+ # TODO: this is a hack - it would be much better to understand the binary structure of the <alias> tag.
776
+ while html.at('alias')
777
+ html.at('alias').swap( aliastostring(html).gsub(/^.*\000\022\000.(.*?)\000.*$/m, 'file:///\1') )
778
+ end
779
+ # Finally return the HTML in "plain text" format, which shows the links in brackets
780
+ html.to_plain_text
781
+ end
782
+
705
783
  # Add a new task containing project notes when the project is a THL list,
706
784
  # since THL lists cannot have notes
707
785
  def add_list_notes(node, prop)
@@ -713,7 +791,7 @@ module Things2THL
713
791
  if node.notes? && new_node_type == :list
714
792
  newnode = {
715
793
  :new => :task,
716
- :with_properties => { :title => "Notes for '#{prop[:name]}'", :notes => node.notes }}
794
+ :with_properties => { :title => "Notes for '#{prop[:name]}'", :notes => convert_notes(node.notes) }}
717
795
  # Mark as completed if the project is completed
718
796
  if node.status == :completed || node.status == :canceled
719
797
  newnode[:with_properties][:completed] = true
@@ -727,6 +805,11 @@ module Things2THL
727
805
  end
728
806
  end
729
807
 
808
+ # Transfer processed notes
809
+ def add_notes(node, prop)
810
+ prop[:notes] = convert_notes(node.notes) if node.notes?
811
+ end
812
+
730
813
  # When projects are lists, if the project has a due date, we add a bogus task to it
731
814
  # to represent its due date, since lists in THL cannot have due dates.
732
815
  def add_project_duedate(node, prop)
@@ -755,6 +838,7 @@ module Things2THL
755
838
  options.quiet = false
756
839
  options.archivecompleted = true
757
840
  options.projectsfolder = nil
841
+ options.areasfolder = nil
758
842
  options.contexttagsregex = '^@'
759
843
  options.timetagsregex = '^(\d+)(min|sec|hr)$'
760
844
  options.timetags = false
@@ -765,3 +849,13 @@ module Things2THL
765
849
  Converter.new(opt_struct, things_db, thl_location)
766
850
  end
767
851
  end
852
+
853
+ ######################################################################
854
+ # For debugging and testing
855
+
856
+ if $0 == __FILE__
857
+ c=Things2THL.new
858
+ th=c.things
859
+ thl=c.thl
860
+ true
861
+ end
data/things2thl.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{things2thl}
5
- s.version = "0.5.0"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Diego Zamboni"]
9
- s.date = %q{2009-05-19}
9
+ s.date = %q{2009-05-21}
10
10
  s.default_executable = %q{things2thl}
11
11
  s.description = %q{Library and command-line tool for migrating Things data to The Hit List}
12
12
  s.email = %q{diego@zzamboni.org}
@@ -38,10 +38,13 @@ Gem::Specification.new do |s|
38
38
 
39
39
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
40
40
  s.add_runtime_dependency(%q<rb-appscript>, [">= 0.5.1"])
41
+ s.add_runtime_dependency(%q<hpricot>, [">= 0.6"])
41
42
  else
42
43
  s.add_dependency(%q<rb-appscript>, [">= 0.5.1"])
44
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
43
45
  end
44
46
  else
45
47
  s.add_dependency(%q<rb-appscript>, [">= 0.5.1"])
48
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
46
49
  end
47
50
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zzamboni-things2thl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Zamboni
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-19 00:00:00 -07:00
12
+ date: 2009-05-21 00:00:00 -07:00
13
13
  default_executable: things2thl
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.5.1
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hpricot
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0.6"
34
+ version:
25
35
  description: Library and command-line tool for migrating Things data to The Hit List
26
36
  email: diego@zzamboni.org
27
37
  executables: