taskwarrior-calendar 1.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4381f3210f9a478f0b1ad0453d7e84c42f3dd8b2b55a5afc17d0ce8c21fdf938
4
+ data.tar.gz: 6609c61b9c93d0a0fd8d2fdffcc11d3334ec55297c0ee5004d9e18e64a0e0a1e
5
+ SHA512:
6
+ metadata.gz: 80f387b7fae053e80079eee0e7d36f17bb6260f9de2d55dde84cd010a9e72aacd8c2193cf67a0e4c46a6063b836c75c7f91fa54238826f37d194eb28bcf2b7bc
7
+ data.tar.gz: 785ff03b8ce423694a906964a07a97d0dac57f79961cd9ee4cdc48021d5a5a4780383911d9d1b2e89d65d5e6368a7855d9d7608ed5f730e93dba85352f263e33
data/bin/task-ical ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'taskwarrior/calendar/app'
5
+
6
+ Taskwarrior::Calendar::App::Command.run
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clamp'
4
+ require 'pastel'
5
+ require 'tty-pager'
6
+
7
+ require_relative '../version'
8
+ require_relative '../../calendar'
9
+
10
+ class Taskwarrior
11
+ class Calendar
12
+ module App
13
+ class BaseCommand < Clamp::Command
14
+ option ['-m', '--man'], :flag, 'show manpage' do
15
+ manpage = <<~MANPAGE
16
+ Name:
17
+ task-ical - Taskwarrior tasks to ical converter
18
+
19
+ #{help}
20
+ Description:
21
+ The due timestamp, optionally scheduled timestamp and
22
+ description of a task are used to create a calendar event.
23
+
24
+ Authors:
25
+ Tobias Schäfer <github@blackox.org>
26
+
27
+ Copyright and License
28
+ This software is copyright (c) 2022 by Tobias Schäfer.
29
+
30
+ This package is free software; you can redistribute it and/or
31
+ modify it under the terms of the "MIT License".
32
+ MANPAGE
33
+ TTY::Pager.page(manpage)
34
+
35
+ exit 0
36
+ end
37
+
38
+ option ['-v', '--version'], :flag, 'show version' do
39
+ puts "task-ical #{Taskwarrior::Calendar::VERSION}"
40
+ exit 0
41
+ end
42
+
43
+ def bailout(message)
44
+ puts Pastel.new.red.bold(message)
45
+ exit 1
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ class Taskwarrior
6
+ class Calendar
7
+ module App
8
+ class ConvertCommand < Taskwarrior::Calendar::App::BaseCommand
9
+ option '--[no-]alarm', :flag, 'add alarm to event', default: true
10
+ option '--filter', 'FILTER', 'tasks filter', default: 'status:pending'
11
+ option '--filename', 'FILE', 'ics output file'
12
+
13
+ def execute
14
+ calendar = Taskwarrior::Calendar.new(filter:, alarm: alarm?)
15
+ return calendar.publish(filename:) if !filename.nil?
16
+
17
+ puts calendar.to_ical
18
+ rescue StandardError => e
19
+ bailout(e.cause || e.message)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'app/base'
4
+ require_relative 'app/convert'
5
+
6
+ class Taskwarrior
7
+ class Calendar
8
+ module App
9
+ class Command < Taskwarrior::Calendar::App::BaseCommand
10
+ self.default_subcommand = 'convert'
11
+
12
+ subcommand 'convert', 'Convert tasks to events', Taskwarrior::Calendar::App::ConvertCommand
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'icalendar'
4
+ require 'icalendar/tzinfo'
5
+
6
+ class Taskwarrior
7
+ class Calendar
8
+ module Event
9
+ def add_event(event, task, type, alarm)
10
+ date = Icalendar::Values::DateTime.new(
11
+ tz_info.utc_to_local(DateTime.parse(task[type])),
12
+ 'tzid' => tz_name
13
+ )
14
+ event.dtstart = event.dtend = date
15
+
16
+ event.summary = "#{type.capitalize}: #{task['description']}"
17
+ event.ip_class = 'PRIVATE'
18
+
19
+ add_event_alarm(event, task, type) if alarm
20
+ end
21
+
22
+ private
23
+
24
+ def add_event_alarm(event, task, type)
25
+ event.alarm do |a|
26
+ a.action = 'DISPLAY'
27
+ a.summary = "#{type.capitalize}: #{task['description']}"
28
+ a.trigger = '-PT15M'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tzinfo'
4
+
5
+ class Taskwarrior
6
+ class Calendar
7
+ module Timezone
8
+ def tz_name
9
+ @tz_name ||= ENV['TZ'] || begin
10
+ File.read('/etc/timezone').strip
11
+ rescue Errno::ENOENT
12
+ 'UTC'
13
+ end
14
+ end
15
+
16
+ def tz_info
17
+ @tz_info ||= TZInfo::Timezone.get(tz_name)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Taskwarrior
4
+ class Calendar
5
+ VERSION = '1.0.1'
6
+ end
7
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'icalendar'
5
+ require 'icalendar/tzinfo'
6
+ require 'json'
7
+ require 'open3'
8
+
9
+ require_relative 'calendar/timezone'
10
+ require_relative 'calendar/event'
11
+
12
+ class Taskwarrior
13
+ ##
14
+ # Convert taskwarrior tasks to calendar events.
15
+ #
16
+ # calendar = Taskwarrior::Calendar.new(filter: 'status:pending', alarm: false)
17
+ # calendar.publish(filename: '~/tasks.ics')
18
+ #
19
+ # Task *due* and optional *scheduled* dates if available are exported as
20
+ # calendar events.
21
+ #
22
+ # By default tasks are filtered by +status:pending+ and all events are
23
+ # appointed with an alarm *15* *minutes* *before* the event date. This can be
24
+ # configured with the filter and alarm options. For further filter details see
25
+ # https://taskwarrior.org/docs/filter. All events are exported in the
26
+ # timezone of the system. This can be configured with the +TZ+ environment
27
+ # variable.
28
+ class Calendar
29
+ include Timezone
30
+ include Event
31
+
32
+ ##
33
+ # Initialize a new taskwarrior calendar object. Tasks are filtered by
34
+ # +status:pending+ and all calendar events are appointed with an alarm.
35
+ # This can be configured with the +filter+ and +alarm+ options.
36
+ #
37
+ # On failure an error is raised.
38
+ def initialize(filter: 'status:pending', alarm: true)
39
+ tasks(filter:)
40
+ calendar(alarm:)
41
+ end
42
+
43
+ ##
44
+ # Return the Icalendar::Calendar object.
45
+ def to_object
46
+ @calendar
47
+ end
48
+
49
+ ##
50
+ # Return the calendar as ICS string.
51
+ def to_ical
52
+ @calendar.to_ical
53
+ end
54
+
55
+ ##
56
+ # Publish calendar ICS to a file, default +taskwarrior.ics+. This can be
57
+ # configured with the +filename+ option.
58
+ def publish(filename: 'taskwarrior.ics')
59
+ File.write(filename, to_ical)
60
+ end
61
+
62
+ private
63
+
64
+ def tasks(filter:)
65
+ @tasks = Open3.popen3("task #{filter} export") do |_, stdout, stderr, process|
66
+ raise IOError, "taskwarrior: #{stderr.read}" if process.value.exitstatus != 0
67
+
68
+ JSON.parse(stdout.read)
69
+ end
70
+ end
71
+
72
+ def calendar(alarm:)
73
+ @calendar = Icalendar::Calendar.new
74
+ @calendar.add_timezone(tz_info.ical_timezone(DateTime.now))
75
+
76
+ @tasks.each do |task|
77
+ %w[due scheduled].each do |type|
78
+ next if !task.key?(type) || task[type].empty?
79
+
80
+ @calendar.event do |event|
81
+ add_event(event, task, type, alarm)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: taskwarrior-calendar
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Schäfer
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-03-16 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: clamp
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 1.3.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: icalendar
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 2.9.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 2.9.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: logger
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: ostruct
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: pastel
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 0.8.0
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.8.0
96
+ - !ruby/object:Gem::Dependency
97
+ name: tty-pager
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.14.0
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 0.14.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: tzinfo
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 2.0.6
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 2.0.6
124
+ - !ruby/object:Gem::Dependency
125
+ name: tzinfo-data
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 1.2023.3
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 1.2023.3
138
+ description: |
139
+ Convert taskwarrior tasks to calendar events.
140
+
141
+ The due timestamp, optionally scheduled timestamp and description of a
142
+ task are used to create a calendar event.
143
+ email:
144
+ - github@blackox.org
145
+ executables:
146
+ - task-ical
147
+ extensions: []
148
+ extra_rdoc_files: []
149
+ files:
150
+ - bin/task-ical
151
+ - lib/taskwarrior/calendar.rb
152
+ - lib/taskwarrior/calendar/app.rb
153
+ - lib/taskwarrior/calendar/app/base.rb
154
+ - lib/taskwarrior/calendar/app/convert.rb
155
+ - lib/taskwarrior/calendar/event.rb
156
+ - lib/taskwarrior/calendar/timezone.rb
157
+ - lib/taskwarrior/calendar/version.rb
158
+ homepage: https://github.com/tschaefer/taskwarrior-calendar
159
+ licenses:
160
+ - MIT
161
+ metadata:
162
+ rubygems_mfa_required: 'true'
163
+ source_code_uri: https://github.com/tschaefer/taskwarrior-calendar
164
+ bug_tracker_uri: https://github.com/tschaefer/taskwarrior-calendar/issues
165
+ post_install_message: All your tasks are belong to us!
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: 3.1.1
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubygems_version: 3.6.6
181
+ specification_version: 4
182
+ summary: Convert taskwarrior tasks to calendar events.
183
+ test_files: []