calendar-assistant 0.9.0 → 0.14.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/README.md +44 -22
  4. data/Rakefile +37 -6
  5. data/bin/calendar-assistant +3 -1
  6. data/lib/calendar_assistant.rb +14 -16
  7. data/lib/calendar_assistant/available_block.rb +1 -1
  8. data/lib/calendar_assistant/calendar_assistant.rb +21 -25
  9. data/lib/calendar_assistant/cli.rb +10 -10
  10. data/lib/calendar_assistant/cli/authorizer.rb +14 -14
  11. data/lib/calendar_assistant/cli/command_service.rb +2 -2
  12. data/lib/calendar_assistant/cli/commands.rb +47 -40
  13. data/lib/calendar_assistant/cli/config.rb +16 -17
  14. data/lib/calendar_assistant/cli/event_presenter.rb +2 -2
  15. data/lib/calendar_assistant/cli/event_set_presenter.rb +3 -3
  16. data/lib/calendar_assistant/cli/helpers.rb +7 -8
  17. data/lib/calendar_assistant/cli/linter_event_presenter.rb +3 -3
  18. data/lib/calendar_assistant/cli/linter_event_set_presenter.rb +4 -4
  19. data/lib/calendar_assistant/cli/printer.rb +15 -15
  20. data/lib/calendar_assistant/config.rb +11 -11
  21. data/lib/calendar_assistant/config/token_store.rb +4 -4
  22. data/lib/calendar_assistant/date_helpers.rb +1 -2
  23. data/lib/calendar_assistant/event.rb +54 -50
  24. data/lib/calendar_assistant/event_repository.rb +14 -15
  25. data/lib/calendar_assistant/event_repository_factory.rb +1 -1
  26. data/lib/calendar_assistant/event_set.rb +14 -14
  27. data/lib/calendar_assistant/extensions/google_apis_extensions.rb +1 -1
  28. data/lib/calendar_assistant/extensions/launchy_extensions.rb +7 -4
  29. data/lib/calendar_assistant/has_duration.rb +4 -5
  30. data/lib/calendar_assistant/lint_event_repository.rb +3 -3
  31. data/lib/calendar_assistant/location_config_validator.rb +3 -3
  32. data/lib/calendar_assistant/location_event_repository.rb +7 -8
  33. data/lib/calendar_assistant/predicate_collection.rb +1 -2
  34. data/lib/calendar_assistant/scheduler.rb +4 -5
  35. data/lib/calendar_assistant/string_helpers.rb +1 -1
  36. data/lib/calendar_assistant/version.rb +1 -1
  37. metadata +38 -30
@@ -3,9 +3,10 @@ class CalendarAssistant
3
3
  class Config
4
4
  autoload :TokenStore, "calendar_assistant/config/token_store"
5
5
 
6
- class NoTokensAuthorized < CalendarAssistant::BaseException;
6
+ class NoTokensAuthorized < CalendarAssistant::BaseException
7
7
  end
8
- class AccessingHashAsScalar < CalendarAssistant::BaseException;
8
+
9
+ class AccessingHashAsScalar < CalendarAssistant::BaseException
9
10
  end
10
11
 
11
12
  module Keys
@@ -57,16 +58,15 @@ class CalendarAssistant
57
58
 
58
59
  attr_reader :user_config, :options, :defaults
59
60
 
60
- def initialize options: {},
61
+ def initialize(options: {},
61
62
  user_config: {},
62
- defaults: DEFAULT_SETTINGS
63
-
63
+ defaults: DEFAULT_SETTINGS)
64
64
  @defaults = defaults
65
65
  @options = options
66
66
  @user_config = user_config
67
67
  end
68
68
 
69
- def in_env &block
69
+ def in_env(&block)
70
70
  # this is totally not thread-safe
71
71
  orig_b_o_d = BusinessTime::Config.beginning_of_workday
72
72
  orig_e_o_d = BusinessTime::Config.end_of_workday
@@ -98,7 +98,7 @@ class CalendarAssistant
98
98
  end
99
99
  end
100
100
 
101
- def get keypath
101
+ def get(keypath)
102
102
  rval = Config.find_in_hash(user_config, keypath)
103
103
 
104
104
  if rval.is_a?(Hash)
@@ -108,7 +108,7 @@ class CalendarAssistant
108
108
  rval
109
109
  end
110
110
 
111
- def set keypath, value
111
+ def set(keypath, value)
112
112
  Config.set_in_hash user_config, keypath, value
113
113
  end
114
114
 
@@ -116,7 +116,7 @@ class CalendarAssistant
116
116
  # note that, despite the name, this method returns both options
117
117
  # and settings
118
118
  #
119
- def setting setting_name
119
+ def setting(setting_name)
120
120
  context = Config.find_in_hash(options, Keys::Options::CONTEXT)
121
121
  Config.find_in_hash(options, setting_name) ||
122
122
  Config.find_in_hash(user_config, [Keys::SETTINGS, context, setting_name]) ||
@@ -190,14 +190,14 @@ class CalendarAssistant
190
190
  a
191
191
  end
192
192
 
193
- def self.find_in_hash hash, keypath
193
+ def self.find_in_hash(hash, keypath)
194
194
  split_keypath(keypath).inject(hash) do |current_val, key|
195
195
  break unless current_val.has_key?(key)
196
196
  current_val[key]
197
197
  end
198
198
  end
199
199
 
200
- def self.set_in_hash hash, keypath, new_value
200
+ def self.set_in_hash(hash, keypath, new_value)
201
201
  *path_parts, key = *split_keypath(keypath)
202
202
 
203
203
  current_hash = path_parts.inject(hash) do |current_val, path|
@@ -3,20 +3,20 @@ class CalendarAssistant
3
3
  class TokenStore
4
4
  attr_reader :config
5
5
 
6
- def initialize config
6
+ def initialize(config)
7
7
  @config = config
8
8
  end
9
9
 
10
- def delete id
10
+ def delete(id)
11
11
  config.tokens.delete(id)
12
12
  config.persist!
13
13
  end
14
14
 
15
- def load id
15
+ def load(id)
16
16
  config.tokens[id]
17
17
  end
18
18
 
19
- def store id, token
19
+ def store(id, token)
20
20
  config.tokens[id] = token
21
21
  config.persist!
22
22
  end
@@ -1,6 +1,6 @@
1
1
  class CalendarAssistant
2
2
  module DateHelpers
3
- def self.cast_dates attributes
3
+ def self.cast_dates(attributes)
4
4
  attributes.each_with_object({}) do |(key, value), object|
5
5
  if value.is_a?(Time) || value.is_a?(DateTime)
6
6
  object[key] = Google::Apis::CalendarV3::EventDateTime.new(date_time: value)
@@ -13,4 +13,3 @@ class CalendarAssistant
13
13
  end
14
14
  end
15
15
  end
16
-
@@ -29,49 +29,49 @@ class CalendarAssistant
29
29
  end
30
30
 
31
31
  PREDICATES = {
32
- "response": %I[
33
- accepted?
34
- declined?
35
- awaiting?
36
- tentative?
37
- ],
38
- "temporal": %I[
39
- all_day?
40
- past?
41
- current?
42
- future?
43
- ],
44
- "visibility": %I[
45
- private?
46
- public?
47
- explicitly_visible?
48
- visible_guestlist?
49
- ],
50
- "attributes": %I[
51
- location_event?
52
- self?
53
- one_on_one?
54
- busy?
55
- commitment?
56
- recurring?
57
- abandoned?
58
- anyone_can_add_self?
59
- attendees_omitted?
60
- end_time_unspecified?
61
- guests_can_invite_others?
62
- guests_can_modify?
63
- guests_can_see_other_guests?
64
- private_copy?
65
- locked?
66
- needs_action?
67
- ]
32
+ "response": %I[
33
+ accepted?
34
+ declined?
35
+ awaiting?
36
+ tentative?
37
+ ],
38
+ "temporal": %I[
39
+ all_day?
40
+ past?
41
+ current?
42
+ future?
43
+ ],
44
+ "visibility": %I[
45
+ private?
46
+ public?
47
+ explicitly_visible?
48
+ visible_guestlist?
49
+ ],
50
+ "attributes": %I[
51
+ location_event?
52
+ self?
53
+ one_on_one?
54
+ busy?
55
+ commitment?
56
+ recurring?
57
+ abandoned?
58
+ anyone_can_add_self?
59
+ attendees_omitted?
60
+ end_time_unspecified?
61
+ guests_can_invite_others?
62
+ guests_can_modify?
63
+ guests_can_see_other_guests?
64
+ private_copy?
65
+ locked?
66
+ needs_action?
67
+ ],
68
68
  }
69
69
 
70
70
  #
71
71
  # class methods
72
72
  #
73
73
 
74
- def self.location_event_prefix config
74
+ def self.location_event_prefix(config)
75
75
  icon = config[CalendarAssistant::Config::Keys::Settings::LOCATION_ICON]
76
76
  if nickname = config[CalendarAssistant::Config::Keys::Settings::NICKNAME]
77
77
  return "#{icon} #{nickname} @ "
@@ -87,13 +87,13 @@ class CalendarAssistant
87
87
  @config = config
88
88
  end
89
89
 
90
- def update **args
90
+ def update(**args)
91
91
  update!(**args)
92
92
  self
93
93
  end
94
94
 
95
95
  def location_event?
96
- !! summary.try(:starts_with?, Event.location_event_prefix(@config))
96
+ !!summary.try(:starts_with?, Event.location_event_prefix(@config))
97
97
  end
98
98
 
99
99
  def accepted?
@@ -167,15 +167,15 @@ class CalendarAssistant
167
167
 
168
168
  def other_human_attendees
169
169
  return nil if attendees.nil?
170
- attendees.select { |a| ! a.resource && ! a.self }
170
+ attendees.select { |a| !a.resource && !a.self }
171
171
  end
172
172
 
173
173
  def human_attendees
174
174
  return nil if attendees.nil?
175
- attendees.select { |a| ! a.resource }
175
+ attendees.select { |a| !a.resource }
176
176
  end
177
177
 
178
- def attendee id
178
+ def attendee(id)
179
179
  return nil if attendees.nil?
180
180
  attendees.find do |attendee|
181
181
  attendee.email == id
@@ -192,15 +192,19 @@ class CalendarAssistant
192
192
 
193
193
  def av_uri
194
194
  @av_uri ||= begin
195
- description_link = CalendarAssistant::StringHelpers.find_uri_for_domain(description, "zoom.us")
196
- return description_link if description_link
195
+ if conference_data && conference_data.conference_solution.name == "Zoom Meeting"
196
+ return conference_data.entry_points.detect{|d| d.entry_point_type == "video" }.uri
197
+ end
197
198
 
198
- location_link = CalendarAssistant::StringHelpers.find_uri_for_domain(location, "zoom.us")
199
- return location_link if location_link
199
+ description_link = CalendarAssistant::StringHelpers.find_uri_for_domain(description, "zoom.us")
200
+ return description_link if description_link
200
201
 
201
- return hangout_link if hangout_link
202
- nil
203
- end
202
+ location_link = CalendarAssistant::StringHelpers.find_uri_for_domain(location, "zoom.us")
203
+ return location_link if location_link
204
+
205
+ return hangout_link if hangout_link
206
+ nil
207
+ end
204
208
  end
205
209
  end
206
- end
210
+ end
@@ -1,6 +1,6 @@
1
1
  class CalendarAssistant
2
2
  class EventRepository
3
- class CalendarNotFoundException < CalendarAssistant::BaseException;
3
+ class CalendarNotFoundException < CalendarAssistant::BaseException
4
4
  end
5
5
 
6
6
  attr_reader :calendar, :calendar_id, :config
@@ -15,45 +15,44 @@ class CalendarAssistant
15
15
  raise
16
16
  end
17
17
 
18
- def in_tz &block
18
+ def in_tz(&block)
19
19
  CalendarAssistant.in_tz calendar.time_zone do
20
20
  yield
21
21
  end
22
22
  end
23
23
 
24
- def find time_range, predicates: {}
24
+ def find(time_range, predicates: {})
25
25
  events = @service.list_events(@calendar_id,
26
- time_min: time_range.first.iso8601,
27
- time_max: time_range.last.iso8601,
28
- order_by: "startTime",
29
- single_events: true,
30
- max_results: 2000,
31
- )
26
+ time_min: time_range.first.iso8601,
27
+ time_max: time_range.last.iso8601,
28
+ order_by: "startTime",
29
+ single_events: true,
30
+ max_results: 2000)
32
31
  events = events.items.map { |e| CalendarAssistant::Event.new(e, config: config) }
33
32
 
34
33
  events = filter_by_predicates(events, predicates) unless predicates.empty?
35
34
  CalendarAssistant::EventSet.new self, events
36
35
  end
37
36
 
38
- def new event_attributes
39
- event = Google::Apis::CalendarV3::Event.new DateHelpers.cast_dates(event_attributes)
37
+ def new(event_attributes)
38
+ event = Google::Apis::CalendarV3::Event.new(**DateHelpers.cast_dates(event_attributes))
40
39
  event.visibility ||= config.event_visibility
41
40
  CalendarAssistant::Event.new(event, config: config)
42
41
  end
43
42
 
44
- def create event_attributes
43
+ def create(event_attributes)
45
44
  new(event_attributes).tap do |event|
46
45
  @service.insert_event @calendar_id, event.__getobj__
47
46
  end
48
47
  end
49
48
 
50
- def delete event
51
- @service.delete_event @calendar_id, event.id
49
+ def delete(event)
50
+ @service.delete_event @calendar_id, event.id
52
51
  event
53
52
  end
54
53
 
55
54
  def update(event, attributes)
56
- event.update! DateHelpers.cast_dates(attributes)
55
+ event.update!(**DateHelpers.cast_dates(attributes))
57
56
  updated_event = @service.update_event @calendar_id, event.id, event
58
57
  CalendarAssistant::Event.new(updated_event, config: config)
59
58
  end
@@ -1,6 +1,6 @@
1
1
  class CalendarAssistant
2
2
  class EventRepositoryFactory
3
- def self.new_event_repository service, calendar_id, config: CalendarAssistant::Config.new, type: :base
3
+ def self.new_event_repository(service, calendar_id, config: CalendarAssistant::Config.new, type: :base)
4
4
  klass = case type
5
5
  when :location
6
6
  LocationEventRepository
@@ -7,7 +7,7 @@ class CalendarAssistant
7
7
  # - it could be a bare Event
8
8
  #
9
9
  class EventSet
10
- def self.new event_repository, events=nil
10
+ def self.new(event_repository, events = nil)
11
11
  if events.is_a?(EventSet::Hash)
12
12
  return EventSet::Hash.new event_repository, events.try(:events)
13
13
  end
@@ -23,17 +23,17 @@ class CalendarAssistant
23
23
  class Base
24
24
  attr_reader :event_repository, :events
25
25
 
26
- def initialize event_repository, events
26
+ def initialize(event_repository, events)
27
27
  @event_repository = event_repository
28
28
  @events = events
29
29
  end
30
30
 
31
- def == rhs
31
+ def ==(rhs)
32
32
  return false unless rhs.is_a?(self.class)
33
33
  self.event_repository == rhs.event_repository && self.events == rhs.events
34
34
  end
35
35
 
36
- def new new_events
36
+ def new(new_events)
37
37
  EventSet.new self.event_repository, new_events
38
38
  end
39
39
 
@@ -45,28 +45,28 @@ class CalendarAssistant
45
45
  end
46
46
 
47
47
  class Hash < EventSet::Base
48
- def ensure_keys keys, only: false
48
+ def ensure_keys(keys, only: false)
49
49
  keys.each do |key|
50
50
  events[key] = [] unless events.has_key?(key)
51
51
  end
52
52
  if only
53
53
  events.keys.each do |key|
54
- if ! keys.include? key
54
+ if !keys.include? key
55
55
  events.delete(key)
56
56
  end
57
57
  end
58
58
  end
59
59
  end
60
60
 
61
- def [] key
61
+ def [](key)
62
62
  events[key] ||= []
63
63
  end
64
64
 
65
- def []= key, value
65
+ def []=(key, value)
66
66
  events[key] = value
67
67
  end
68
68
 
69
- def available_blocks length: 1
69
+ def available_blocks(length: 1)
70
70
  event_repository.in_tz do
71
71
  dates = events.keys.sort
72
72
 
@@ -91,7 +91,7 @@ class CalendarAssistant
91
91
  avail_time[date] << AvailableBlock.new(start: start_time, end: e.start_time)
92
92
  end
93
93
  start_time = [e.end_time, start_time].max
94
- break if ! start_time.during_business_hours?
94
+ break if !start_time.during_business_hours?
95
95
  end
96
96
 
97
97
  if HasDuration.duration_in_seconds(start_time, end_time) >= length
@@ -105,18 +105,18 @@ class CalendarAssistant
105
105
  end
106
106
  end
107
107
 
108
- def intersection other, length: 1
108
+ def intersection(other, length: 1)
109
109
  set = new({})
110
110
  set.ensure_keys(events.keys + other.events.keys)
111
111
  set.events.keys.each do |date|
112
112
  events[date].each do |event_a|
113
113
  other.events[date].each do |event_b|
114
114
  if event_a.contains?(event_b.start_time) ||
115
- event_a.contains?(event_b.end_time-1) ||
115
+ event_a.contains?(event_b.end_time - 1) ||
116
116
  event_b.contains?(event_a.start_time) ||
117
- event_b.contains?(event_a.end_time-1)
117
+ event_b.contains?(event_a.end_time - 1)
118
118
  start_time = [event_a.start_time, event_b.start_time].max
119
- end_time = [event_a.end_time, event_b.end_time ].min
119
+ end_time = [event_a.end_time, event_b.end_time].min
120
120
  if HasDuration.duration_in_seconds(start_time, end_time) >= length
121
121
  set.events[date] << AvailableBlock.new(start: start_time, end: end_time)
122
122
  end
@@ -21,7 +21,7 @@ class Google::Apis::CalendarV3::EventDateTime
21
21
  date_time.strftime "%Y-%m-%d %H:%M"
22
22
  end
23
23
 
24
- def == rhs
24
+ def ==(rhs)
25
25
  if date
26
26
  return to_date == rhs.to_date
27
27
  end
@@ -12,9 +12,9 @@ require "launchy"
12
12
  #
13
13
  class CalendarAssistant
14
14
  class ZoomLaunchy < Launchy::Application::Browser
15
- ZOOM_URI_REGEXP = %r(https?://\w+.zoom.us/j/(\d+))
15
+ ZOOM_URI_REGEXP = %r(https?://\w+.zoom.us/j/(\d+)(\?(.*))?)
16
16
 
17
- def self.handles? uri
17
+ def self.handles?(uri)
18
18
  return true if ZOOM_URI_REGEXP.match(uri)
19
19
  end
20
20
 
@@ -26,13 +26,16 @@ class CalendarAssistant
26
26
  [find_executable("xdg-open")]
27
27
  end
28
28
 
29
- def open uri, options={}
29
+ def open(uri, options = {})
30
30
  command = host_os_family.app_list(self).compact.first
31
31
  if command.nil?
32
32
  super uri, options
33
33
  else
34
- confno = ZOOM_URI_REGEXP.match(uri)[1]
34
+ matches = ZOOM_URI_REGEXP.match(uri)
35
+ confno = matches[1]
36
+ params = matches[3]
35
37
  url = "zoommtg://zoom.us/join?confno=#{confno}"
38
+ url += "&#{params}" if params
36
39
  run command, [url]
37
40
  end
38
41
  end