calendar-assistant 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +280 -202
- data/Rakefile +9 -1
- data/lib/calendar_assistant.rb +35 -187
- data/lib/calendar_assistant/authorizer.rb +0 -3
- data/lib/calendar_assistant/calendar_assistant.rb +122 -0
- data/lib/calendar_assistant/cli.rb +84 -54
- data/lib/calendar_assistant/cli_helpers.rb +101 -38
- data/lib/calendar_assistant/config.rb +71 -12
- data/lib/calendar_assistant/date_helpers.rb +16 -0
- data/lib/calendar_assistant/event.rb +124 -14
- data/lib/calendar_assistant/event_repository.rb +30 -19
- data/lib/calendar_assistant/event_repository_factory.rb +7 -0
- data/lib/calendar_assistant/event_set.rb +124 -0
- data/lib/calendar_assistant/extensions/{event_date_time_extensions.rb → google_apis_extensions.rb} +5 -6
- data/lib/calendar_assistant/local_service.rb +82 -0
- data/lib/calendar_assistant/scheduler.rb +45 -0
- data/lib/calendar_assistant/string_helpers.rb +0 -2
- data/lib/calendar_assistant/version.rb +1 -1
- metadata +9 -4
- data/lib/calendar_assistant/extensions/event_extensions.rb +0 -71
data/Rakefile
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
#require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
require "concourse"
|
4
|
+
require "license_finder"
|
4
5
|
|
5
6
|
Concourse.new("calendar-assistant").create_tasks!
|
6
7
|
|
7
8
|
RSpec::Core::RakeTask.new(:spec)
|
8
9
|
|
9
|
-
|
10
|
+
desc "Ensure all dependencies meet license requirements."
|
11
|
+
task :license_finder do
|
12
|
+
LicenseFinder::CLI::Main.start(["report"])
|
13
|
+
LicenseFinder::CLI::Main.start([])
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run specs and license finder"
|
17
|
+
task :default => [:spec, :license_finder]
|
data/lib/calendar_assistant.rb
CHANGED
@@ -1,192 +1,40 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
#
|
2
|
+
# stdlib
|
3
|
+
#
|
4
|
+
autoload :YAML, "yaml"
|
5
|
+
autoload :URI, "uri"
|
6
|
+
autoload :Set, "set"
|
7
|
+
autoload :FileUtils, "fileutils"
|
8
|
+
|
9
|
+
#
|
10
|
+
# gem dependencies
|
11
|
+
#
|
12
|
+
autoload :BusinessTime, "business_time"
|
13
|
+
autoload :Chronic, "chronic"
|
14
|
+
autoload :ChronicDuration, "chronic_duration"
|
15
|
+
autoload :Google, "calendar_assistant/extensions/google_apis_extensions"
|
16
|
+
autoload :Launchy, "launchy"
|
17
|
+
autoload :TOML, "toml"
|
18
|
+
autoload :Thor, "thor"
|
19
|
+
require "calendar_assistant/extensions/rainbow_extensions" # Rainbow() doesn't trigger autoload
|
20
|
+
|
21
|
+
#
|
22
|
+
# CalendarAssistant and associated classes
|
23
|
+
#
|
24
|
+
require "calendar_assistant/calendar_assistant"
|
10
25
|
|
11
26
|
class CalendarAssistant
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def self.authorize profile_name
|
25
|
-
config = CalendarAssistant::Config.new
|
26
|
-
Authorizer.new(profile_name, config.token_store).authorize
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.date_range_cast time_range
|
30
|
-
time_range.first.to_date..(time_range.last + 1.day).to_date
|
31
|
-
end
|
32
|
-
|
33
|
-
def initialize config=CalendarAssistant::Config.new, event_repository: nil
|
34
|
-
@config = config
|
35
|
-
@service = Authorizer.new(config.profile_name, config.token_store).service
|
36
|
-
@calendar = service.get_calendar DEFAULT_CALENDAR_ID
|
37
|
-
@event_repository = event_repository || EventRepository.new(@service, DEFAULT_CALENDAR_ID)
|
38
|
-
end
|
39
|
-
|
40
|
-
def find_events time_range
|
41
|
-
@event_repository.find(time_range)
|
42
|
-
end
|
43
|
-
|
44
|
-
def availability time_range
|
45
|
-
length = ChronicDuration.parse(config.setting(Config::Keys::Settings::MEETING_LENGTH))
|
46
|
-
|
47
|
-
start_of_day = Chronic.parse(config.setting(Config::Keys::Settings::START_OF_DAY))
|
48
|
-
start_of_day = start_of_day - start_of_day.beginning_of_day
|
49
|
-
|
50
|
-
end_of_day = Chronic.parse(config.setting(Config::Keys::Settings::END_OF_DAY))
|
51
|
-
end_of_day = end_of_day - end_of_day.beginning_of_day
|
52
|
-
|
53
|
-
events = find_events time_range
|
54
|
-
date_range = time_range.first.to_date .. time_range.last.to_date
|
55
|
-
|
56
|
-
# find relevant events and map them into dates
|
57
|
-
dates_events = date_range.inject({}) { |de, date| de[date] = [] ; de }
|
58
|
-
events.each do |event|
|
59
|
-
if event.accepted?
|
60
|
-
event_date = event.start.to_date!
|
61
|
-
dates_events[event_date] ||= []
|
62
|
-
dates_events[event_date] << event
|
63
|
-
end
|
64
|
-
dates_events
|
65
|
-
end
|
66
|
-
|
67
|
-
# iterate over the days finding free chunks of time
|
68
|
-
avail_time = date_range.inject({}) do |avail_time, date|
|
69
|
-
avail_time[date] ||= []
|
70
|
-
date_events = dates_events[date]
|
71
|
-
|
72
|
-
start_time = date.to_time + start_of_day
|
73
|
-
end_time = date.to_time + end_of_day
|
74
|
-
|
75
|
-
date_events.each do |e|
|
76
|
-
if (e.start.date_time.to_time - start_time) >= length
|
77
|
-
avail_time[date] << CalendarAssistant.available_block(start_time.to_datetime, e.start.date_time)
|
78
|
-
end
|
79
|
-
start_time = e.end.date_time.to_time
|
80
|
-
break if start_time >= end_time
|
81
|
-
end
|
82
|
-
|
83
|
-
if end_time - start_time >= length
|
84
|
-
avail_time[date] << CalendarAssistant.available_block(start_time.to_datetime, end_time.to_datetime)
|
85
|
-
end
|
86
|
-
|
87
|
-
avail_time
|
88
|
-
end
|
89
|
-
|
90
|
-
avail_time
|
91
|
-
end
|
92
|
-
|
93
|
-
def find_location_events time_range
|
94
|
-
@event_repository.find(time_range).select { |e| e.location_event? }
|
95
|
-
end
|
96
|
-
|
97
|
-
def create_location_event time_range, location
|
98
|
-
# find pre-existing events that overlap
|
99
|
-
existing_events = find_location_events time_range
|
100
|
-
|
101
|
-
# augment event end date appropriately
|
102
|
-
range = CalendarAssistant.date_range_cast time_range
|
103
|
-
|
104
|
-
deleted_events = []
|
105
|
-
modified_events = []
|
106
|
-
|
107
|
-
event = @event_repository.create(transparency: GCal::Event::Transparency::TRANSPARENT, start: range.first, end: range.last , summary: "#{EMOJI_WORLDMAP} #{location}")
|
108
|
-
|
109
|
-
existing_events.each do |existing_event|
|
110
|
-
if existing_event.start.date >= event.start.date && existing_event.end.date <= event.end.date
|
111
|
-
@event_repository.delete existing_event
|
112
|
-
deleted_events << existing_event
|
113
|
-
elsif existing_event.start.date <= event.end.date && existing_event.end.date > event.end.date
|
114
|
-
@event_repository.update existing_event, start: range.last
|
115
|
-
modified_events << existing_event
|
116
|
-
elsif existing_event.start.date < event.start.date && existing_event.end.date >= event.start.date
|
117
|
-
@event_repository.update existing_event, end: range.first
|
118
|
-
modified_events << existing_event
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
response = {created: [event]}
|
123
|
-
response[:deleted] = deleted_events unless deleted_events.empty?
|
124
|
-
response[:modified] = modified_events unless modified_events.empty?
|
125
|
-
response
|
126
|
-
end
|
127
|
-
|
128
|
-
def event_description event
|
129
|
-
s = sprintf("%-25.25s", event_date_description(event))
|
130
|
-
|
131
|
-
date_ansi_codes = []
|
132
|
-
date_ansi_codes << :bright if event.current?
|
133
|
-
date_ansi_codes << :faint if event.past?
|
134
|
-
s = date_ansi_codes.inject(Rainbow(s)) { |text, ansi| text.send ansi }
|
135
|
-
|
136
|
-
s += Rainbow(sprintf(" | %s", event.view_summary)).bold
|
137
|
-
|
138
|
-
attributes = []
|
139
|
-
unless event.private?
|
140
|
-
attributes << "recurring" if event.recurring_event_id
|
141
|
-
attributes << "not-busy" unless event.busy?
|
142
|
-
attributes << "self" if event.human_attendees.nil? && event.visibility != "private"
|
143
|
-
attributes << "1:1" if event.one_on_one?
|
144
|
-
end
|
145
|
-
|
146
|
-
attributes << event.visibility if event.explicit_visibility?
|
147
|
-
|
148
|
-
s += Rainbow(sprintf(" (%s)", attributes.to_a.sort.join(", "))).italic unless attributes.empty?
|
149
|
-
|
150
|
-
s = Rainbow(Rainbow.uncolor(s)).faint.strike if event.declined?
|
151
|
-
|
152
|
-
s
|
153
|
-
end
|
154
|
-
|
155
|
-
def event_date_description event
|
156
|
-
if event.all_day?
|
157
|
-
start_date = event.start.to_date
|
158
|
-
end_date = event.end.to_date
|
159
|
-
if (end_date - start_date) <= 1
|
160
|
-
event.start.to_s
|
161
|
-
else
|
162
|
-
sprintf("%s - %s", start_date, end_date - 1.day)
|
163
|
-
end
|
164
|
-
else
|
165
|
-
if event.start.date_time.to_date == event.end.date_time.to_date
|
166
|
-
sprintf("%s - %s", event.start.date_time.strftime("%Y-%m-%d %H:%M"), event.end.date_time.strftime("%H:%M"))
|
167
|
-
else
|
168
|
-
sprintf("%s - %s", event.start.date_time.strftime("%Y-%m-%d %H:%M"), event.end.date_time.strftime("%Y-%m-%d %H:%M"))
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
private
|
174
|
-
|
175
|
-
def self.available_block start_time, end_time
|
176
|
-
Google::Apis::CalendarV3::Event.new(
|
177
|
-
start: Google::Apis::CalendarV3::EventDateTime.new(date_time: start_time),
|
178
|
-
end: Google::Apis::CalendarV3::EventDateTime.new(date_time: end_time),
|
179
|
-
summary: "available"
|
180
|
-
)
|
181
|
-
end
|
27
|
+
autoload :VERSION, "calendar_assistant/version"
|
28
|
+
autoload :Config, "calendar_assistant/config"
|
29
|
+
autoload :Authorizer, "calendar_assistant/authorizer"
|
30
|
+
autoload :StringHelpers, "calendar_assistant/string_helpers"
|
31
|
+
autoload :DateHelpers, "calendar_assistant/date_helpers"
|
32
|
+
autoload :Event, "calendar_assistant/event"
|
33
|
+
autoload :EventRepository, "calendar_assistant/event_repository"
|
34
|
+
autoload :EventRepositoryFactory, "calendar_assistant/event_repository_factory"
|
35
|
+
autoload :EventSet, "calendar_assistant/event_set"
|
36
|
+
autoload :Scheduler, "calendar_assistant/scheduler"
|
37
|
+
autoload :LocalService, "calendar_assistant/local_service"
|
182
38
|
end
|
183
39
|
|
184
|
-
require "calendar_assistant/config"
|
185
|
-
require "calendar_assistant/authorizer"
|
186
40
|
require "calendar_assistant/cli"
|
187
|
-
require "calendar_assistant/string_helpers"
|
188
|
-
require "calendar_assistant/extensions/event_date_time_extensions"
|
189
|
-
require "calendar_assistant/extensions/event_extensions"
|
190
|
-
require "calendar_assistant/event"
|
191
|
-
require "calendar_assistant/event_repository"
|
192
|
-
require "calendar_assistant/extensions/rainbow_extensions"
|
@@ -17,9 +17,6 @@
|
|
17
17
|
# See the License for the specific language governing permissions and
|
18
18
|
# limitations under the License.
|
19
19
|
|
20
|
-
require 'googleauth'
|
21
|
-
require 'rainbow'
|
22
|
-
|
23
20
|
class CalendarAssistant
|
24
21
|
class Authorizer
|
25
22
|
class NoCredentials < CalendarAssistant::BaseException ; end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
class CalendarAssistant
|
3
|
+
class BaseException < RuntimeError ; end
|
4
|
+
|
5
|
+
EMOJI_WORLDMAP = "🗺" # U+1F5FA WORLD MAP
|
6
|
+
EMOJI_PLANE = "🛪" # U+1F6EA NORTHEAST-POINTING AIRPLANE
|
7
|
+
EMOJI_1_1 = "👫" # MAN AND WOMAN HOLDING HANDS
|
8
|
+
|
9
|
+
attr_reader :service, :calendar, :config
|
10
|
+
|
11
|
+
def self.authorize profile_name
|
12
|
+
config = CalendarAssistant::Config.new
|
13
|
+
Authorizer.new(profile_name, config.token_store).authorize
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.date_range_cast time_range
|
17
|
+
time_range.first.to_date..(time_range.last + 1.day).to_date
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.in_tz time_zone, &block
|
21
|
+
# this is totally not thread-safe
|
22
|
+
orig_time_tz = Time.zone
|
23
|
+
orig_env_tz = ENV['TZ']
|
24
|
+
begin
|
25
|
+
unless time_zone.nil?
|
26
|
+
Time.zone = time_zone
|
27
|
+
ENV['TZ'] = time_zone
|
28
|
+
end
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
Time.zone = orig_time_tz
|
32
|
+
ENV['TZ'] = orig_env_tz
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def initialize config=Config.new,
|
38
|
+
event_repository_factory: EventRepositoryFactory
|
39
|
+
@config = config
|
40
|
+
|
41
|
+
if filename = config.setting(Config::Keys::Options::LOCAL_STORE)
|
42
|
+
@service = CalendarAssistant::LocalService.new(file: filename)
|
43
|
+
else
|
44
|
+
@service = Authorizer.new(config.profile_name, config.token_store).service
|
45
|
+
end
|
46
|
+
@calendar = service.get_calendar Config::DEFAULT_CALENDAR_ID
|
47
|
+
@event_repository_factory = event_repository_factory
|
48
|
+
@event_repositories = {} # calendar_id → event_repository
|
49
|
+
end
|
50
|
+
|
51
|
+
def in_env &block
|
52
|
+
# this is totally not thread-safe
|
53
|
+
config.in_env do
|
54
|
+
in_tz do
|
55
|
+
yield
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def in_tz &block
|
61
|
+
CalendarAssistant.in_tz calendar.time_zone do
|
62
|
+
yield
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_events time_range
|
67
|
+
calendar_ids = config.attendees
|
68
|
+
if calendar_ids.length > 1
|
69
|
+
raise "CalendarAssistant#find_events only supports one person (for now)"
|
70
|
+
end
|
71
|
+
event_repository(calendar_ids.first).find(time_range)
|
72
|
+
end
|
73
|
+
|
74
|
+
def availability time_range
|
75
|
+
calendar_ids = config.attendees
|
76
|
+
ers = calendar_ids.map do |calendar_id|
|
77
|
+
event_repository calendar_id
|
78
|
+
end
|
79
|
+
Scheduler.new(self, ers).available_blocks(time_range)
|
80
|
+
end
|
81
|
+
|
82
|
+
def find_location_events time_range
|
83
|
+
event_set = event_repository.find(time_range)
|
84
|
+
event_set.new event_set.events.select { |e| e.location_event? }
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_location_event time_range, location
|
88
|
+
# find pre-existing events that overlap
|
89
|
+
existing_event_set = find_location_events time_range
|
90
|
+
|
91
|
+
# augment event end date appropriately
|
92
|
+
range = CalendarAssistant.date_range_cast time_range
|
93
|
+
|
94
|
+
deleted_events = []
|
95
|
+
modified_events = []
|
96
|
+
|
97
|
+
event = event_repository.create(transparency: CalendarAssistant::Event::Transparency::TRANSPARENT, start: range.first, end: range.last , summary: "#{EMOJI_WORLDMAP} #{location}")
|
98
|
+
|
99
|
+
existing_event_set.events.each do |existing_event|
|
100
|
+
if existing_event.start_date >= event.start_date && existing_event.end_date <= event.end_date
|
101
|
+
event_repository.delete existing_event
|
102
|
+
deleted_events << existing_event
|
103
|
+
elsif existing_event.start_date <= event.end_date && existing_event.end_date > event.end_date
|
104
|
+
event_repository.update existing_event, start: range.last
|
105
|
+
modified_events << existing_event
|
106
|
+
elsif existing_event.start_date < event.start_date && existing_event.end_date >= event.start_date
|
107
|
+
event_repository.update existing_event, end: range.first
|
108
|
+
modified_events << existing_event
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
response = {created: [event]}
|
113
|
+
response[:deleted] = deleted_events unless deleted_events.empty?
|
114
|
+
response[:modified] = modified_events unless modified_events.empty?
|
115
|
+
|
116
|
+
existing_event_set.new response
|
117
|
+
end
|
118
|
+
|
119
|
+
def event_repository calendar_id=Config::DEFAULT_CALENDAR_ID
|
120
|
+
@event_repositories[calendar_id] ||= @event_repository_factory.new_event_repository(@service, calendar_id)
|
121
|
+
end
|
122
|
+
end
|
@@ -1,17 +1,25 @@
|
|
1
|
-
require "thor"
|
2
|
-
require "chronic"
|
3
|
-
require "chronic_duration"
|
4
|
-
require "launchy"
|
5
|
-
|
6
1
|
require "calendar_assistant/cli_helpers"
|
7
2
|
|
8
3
|
class CalendarAssistant
|
9
4
|
class CLI < Thor
|
10
|
-
def self.
|
11
|
-
option
|
5
|
+
def self.will_create_a_service
|
6
|
+
option CalendarAssistant::Config::Keys::Settings::PROFILE,
|
12
7
|
type: :string,
|
13
8
|
desc: "the profile you'd like to use (if different from default)",
|
14
9
|
aliases: ["-p"]
|
10
|
+
|
11
|
+
option CalendarAssistant::Config::Keys::Options::LOCAL_STORE,
|
12
|
+
type: :string,
|
13
|
+
banner: "FILENAME",
|
14
|
+
desc: "Load events from a local file instead of Google Calendar"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.has_attendees
|
18
|
+
option CalendarAssistant::Config::Keys::Options::ATTENDEES,
|
19
|
+
type: :string,
|
20
|
+
banner: "ATTENDEE1[,ATTENDEE2[,...]]",
|
21
|
+
desc: "[default 'me'] people (email IDs) to whom this command will be applied",
|
22
|
+
aliases: ["-a"]
|
15
23
|
end
|
16
24
|
|
17
25
|
default_config = CalendarAssistant::Config.new options: options # used in option descriptions
|
@@ -19,22 +27,25 @@ class CalendarAssistant
|
|
19
27
|
class_option :help,
|
20
28
|
type: :boolean,
|
21
29
|
aliases: ["-h", "-?"]
|
22
|
-
class_option
|
30
|
+
class_option CalendarAssistant::Config::Keys::Options::DEBUG,
|
23
31
|
type: :boolean,
|
24
32
|
desc: "how dare you suggest there are bugs"
|
25
33
|
|
26
34
|
|
35
|
+
desc "version",
|
36
|
+
"Display the version of calendar-assistant"
|
37
|
+
def version
|
38
|
+
return if handle_help_args
|
39
|
+
out.puts CalendarAssistant::VERSION
|
40
|
+
end
|
41
|
+
|
42
|
+
|
27
43
|
desc "config",
|
28
44
|
"Dump your configuration parameters (merge of defaults and overrides from #{CalendarAssistant::Config::CONFIG_FILE_PATH})"
|
29
45
|
def config
|
30
46
|
return if handle_help_args
|
31
|
-
|
32
|
-
|
33
|
-
setting_names = CalendarAssistant::Config::Keys::Settings.constants.map { |k| CalendarAssistant::Config::Keys::Settings.const_get k }
|
34
|
-
setting_names.each do |key|
|
35
|
-
settings[key] = config.setting key
|
36
|
-
end
|
37
|
-
puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
47
|
+
settings = CalendarAssistant::Config.new.settings
|
48
|
+
out.puts TOML::Generator.new({CalendarAssistant::Config::Keys::SETTINGS => settings}).body
|
38
49
|
end
|
39
50
|
|
40
51
|
|
@@ -50,11 +61,12 @@ class CalendarAssistant
|
|
50
61
|
file to `#{CalendarAssistant::Authorizer::CREDENTIALS_PATH}`
|
51
62
|
EOD
|
52
63
|
def setup
|
53
|
-
|
64
|
+
# TODO ugh see #34 for advice on how to clean this up
|
65
|
+
return if handle_help_args
|
54
66
|
if File.exist? CalendarAssistant::Authorizer::CREDENTIALS_PATH
|
55
67
|
out.puts sprintf("Credentials already exist in %s",
|
56
68
|
CalendarAssistant::Authorizer::CREDENTIALS_PATH)
|
57
|
-
|
69
|
+
return
|
58
70
|
end
|
59
71
|
|
60
72
|
out.launch "https://developers.google.com/calendar/quickstart/ruby"
|
@@ -92,8 +104,10 @@ class CalendarAssistant
|
|
92
104
|
In order for this to work, you'll need to have set up your API client
|
93
105
|
credentials. Run `calendar-assistant help setup` for instructions.
|
94
106
|
EOD
|
95
|
-
def authorize profile_name
|
107
|
+
def authorize profile_name=nil
|
96
108
|
return if handle_help_args
|
109
|
+
return help! if profile_name.nil?
|
110
|
+
|
97
111
|
CalendarAssistant.authorize profile_name
|
98
112
|
puts "\nYou're authorized!\n\n"
|
99
113
|
end
|
@@ -101,64 +115,70 @@ class CalendarAssistant
|
|
101
115
|
|
102
116
|
desc "show [DATE | DATERANGE | TIMERANGE]",
|
103
117
|
"Show your events for a date or range of dates (default 'today')"
|
104
|
-
option
|
118
|
+
option CalendarAssistant::Config::Keys::Options::COMMITMENTS,
|
105
119
|
type: :boolean,
|
106
120
|
desc: "only show events that you've accepted with another person",
|
107
121
|
aliases: ["-c"]
|
108
|
-
|
122
|
+
will_create_a_service
|
123
|
+
has_attendees
|
109
124
|
def show datespec="today"
|
110
125
|
return if handle_help_args
|
111
|
-
config = CalendarAssistant::Config.new
|
126
|
+
config = CalendarAssistant::Config.new(options: options)
|
112
127
|
ca = CalendarAssistant.new config
|
113
|
-
|
114
|
-
|
128
|
+
ca.in_env do
|
129
|
+
event_set = ca.find_events CLIHelpers.parse_datespec(datespec)
|
130
|
+
out.print_events ca, event_set
|
131
|
+
end
|
115
132
|
end
|
116
133
|
|
117
134
|
|
118
135
|
desc "join [TIME]",
|
119
136
|
"Open the URL for a video call attached to your meeting at time TIME (default 'now')"
|
120
|
-
option
|
137
|
+
option CalendarAssistant::Config::Keys::Options::JOIN,
|
121
138
|
type: :boolean, default: true,
|
122
139
|
desc: "launch a browser to join the video call URL"
|
123
|
-
|
140
|
+
will_create_a_service
|
124
141
|
def join timespec="now"
|
125
142
|
return if handle_help_args
|
126
|
-
|
127
|
-
ca
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
143
|
+
ca = CalendarAssistant.new CalendarAssistant::Config.new(options: options)
|
144
|
+
ca.in_env do
|
145
|
+
event_set, url = CLIHelpers.find_av_uri ca, timespec
|
146
|
+
if ! event_set.empty?
|
147
|
+
out.print_events ca, event_set
|
148
|
+
out.puts url
|
149
|
+
out.launch url if options[CalendarAssistant::Config::Keys::Options::JOIN]
|
150
|
+
else
|
151
|
+
out.puts "Could not find a meeting '#{timespec}' with a video call to join."
|
134
152
|
end
|
135
|
-
else
|
136
|
-
CLIHelpers::Out.new.puts "Could not find a meeting '#{timespec}' with a video call to join."
|
137
153
|
end
|
138
154
|
end
|
139
155
|
|
140
156
|
|
141
157
|
desc "location [DATE | DATERANGE]",
|
142
158
|
"Show your location for a date or range of dates (default 'today')"
|
143
|
-
|
159
|
+
will_create_a_service
|
144
160
|
def location datespec="today"
|
145
161
|
return if handle_help_args
|
146
|
-
|
147
|
-
ca
|
148
|
-
|
149
|
-
|
162
|
+
ca = CalendarAssistant.new CalendarAssistant::Config.new(options: options)
|
163
|
+
ca.in_env do
|
164
|
+
event_set = ca.find_location_events CLIHelpers.parse_datespec(datespec)
|
165
|
+
out.print_events ca, event_set
|
166
|
+
end
|
150
167
|
end
|
151
168
|
|
152
169
|
|
153
170
|
desc "location-set LOCATION [DATE | DATERANGE]",
|
154
171
|
"Set your location to LOCATION for a date or range of dates (default 'today')"
|
155
|
-
|
156
|
-
def location_set location, datespec="today"
|
172
|
+
will_create_a_service
|
173
|
+
def location_set location=nil, datespec="today"
|
157
174
|
return if handle_help_args
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
175
|
+
return help! if location.nil?
|
176
|
+
|
177
|
+
ca = CalendarAssistant.new CalendarAssistant::Config.new(options: options)
|
178
|
+
ca.in_env do
|
179
|
+
event_set = ca.create_location_event CLIHelpers.parse_datespec(datespec), location
|
180
|
+
out.print_events ca, event_set
|
181
|
+
end
|
162
182
|
end
|
163
183
|
|
164
184
|
|
@@ -173,29 +193,39 @@ class CalendarAssistant
|
|
173
193
|
option CalendarAssistant::Config::Keys::Settings::START_OF_DAY,
|
174
194
|
type: :string,
|
175
195
|
banner: "TIME",
|
176
|
-
desc: sprintf("[default %s] find chunks of available time after TIME (which is a
|
196
|
+
desc: sprintf("[default %s] find chunks of available time after TIME (which is a BusinessTime string like '9am' or '14:30')",
|
177
197
|
default_config.setting(CalendarAssistant::Config::Keys::Settings::START_OF_DAY)),
|
178
198
|
aliases: ["-s"]
|
179
199
|
option CalendarAssistant::Config::Keys::Settings::END_OF_DAY,
|
180
200
|
type: :string,
|
181
201
|
banner: "TIME",
|
182
|
-
desc: sprintf("[default %s] find chunks of available time before TIME (which is a
|
202
|
+
desc: sprintf("[default %s] find chunks of available time before TIME (which is a BusinessTime string like '9am' or '14:30')",
|
183
203
|
default_config.setting(CalendarAssistant::Config::Keys::Settings::END_OF_DAY)),
|
184
204
|
aliases: ["-e"]
|
185
|
-
|
205
|
+
has_attendees
|
206
|
+
will_create_a_service
|
186
207
|
def availability datespec="today"
|
187
208
|
return if handle_help_args
|
188
|
-
|
189
|
-
ca
|
190
|
-
|
191
|
-
|
209
|
+
ca = CalendarAssistant.new CalendarAssistant::Config.new(options: options)
|
210
|
+
ca.in_env do
|
211
|
+
event_set = ca.availability CLIHelpers.parse_datespec(datespec)
|
212
|
+
out.print_available_blocks ca, event_set
|
213
|
+
end
|
192
214
|
end
|
193
215
|
|
194
216
|
private
|
195
217
|
|
218
|
+
def out
|
219
|
+
@out ||= CLIHelpers::Out.new
|
220
|
+
end
|
221
|
+
|
222
|
+
def help!
|
223
|
+
help(current_command_chain.first)
|
224
|
+
end
|
225
|
+
|
196
226
|
def handle_help_args
|
197
227
|
if options[:help]
|
198
|
-
help
|
228
|
+
help!
|
199
229
|
return true
|
200
230
|
end
|
201
231
|
end
|