osm 0.0.26 → 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.
Files changed (46) hide show
  1. data/CHANGELOG.md +57 -0
  2. data/README.md +13 -7
  3. data/lib/osm.rb +47 -22
  4. data/lib/osm/activity.rb +52 -57
  5. data/lib/osm/api.rb +115 -1031
  6. data/lib/osm/api_access.rb +73 -36
  7. data/lib/osm/due_badges.rb +27 -12
  8. data/lib/osm/evening.rb +118 -55
  9. data/lib/osm/event.rb +275 -17
  10. data/lib/osm/flexi_record.rb +131 -0
  11. data/lib/osm/grouping.rb +37 -15
  12. data/lib/osm/member.rb +100 -41
  13. data/lib/osm/model.rb +95 -0
  14. data/lib/osm/register.rb +177 -0
  15. data/lib/osm/section.rb +163 -71
  16. data/lib/osm/term.rb +135 -21
  17. data/spec/osm/activity_spec.rb +7 -4
  18. data/spec/osm/api_access_spec.rb +44 -36
  19. data/spec/osm/api_spec.rb +32 -1147
  20. data/spec/osm/due_badges_spec.rb +8 -1
  21. data/spec/osm/evening_spec.rb +119 -54
  22. data/spec/osm/event_spec.rb +363 -13
  23. data/spec/osm/flexi_record_spec.rb +128 -0
  24. data/spec/osm/grouping_spec.rb +9 -5
  25. data/spec/osm/member_spec.rb +111 -36
  26. data/spec/osm/model_spec.rb +140 -0
  27. data/spec/osm/osm_spec.rb +7 -31
  28. data/spec/osm/register_spec.rb +103 -0
  29. data/spec/osm/section_spec.rb +208 -92
  30. data/spec/osm/term_spec.rb +164 -28
  31. data/spec/spec_helper.rb +22 -0
  32. data/version.rb +1 -1
  33. metadata +22 -29
  34. data/lib/osm/event_attendance.rb +0 -55
  35. data/lib/osm/flexi_record_data.rb +0 -51
  36. data/lib/osm/flexi_record_field.rb +0 -42
  37. data/lib/osm/register_data.rb +0 -64
  38. data/lib/osm/register_field.rb +0 -42
  39. data/lib/osm/role.rb +0 -133
  40. data/spec/osm/api_strangeness_spec.rb +0 -83
  41. data/spec/osm/event_attendance_spec.rb +0 -34
  42. data/spec/osm/flexi_record_data_spec.rb +0 -40
  43. data/spec/osm/flexi_record_field_spec.rb +0 -23
  44. data/spec/osm/register_data_spec.rb +0 -35
  45. data/spec/osm/register_field_spec.rb +0 -24
  46. data/spec/osm/role_spec.rb +0 -118
@@ -1,8 +1,6 @@
1
1
  module Osm
2
2
 
3
- class ApiAccess
4
- include ::ActiveAttr::MassAssignmentSecurity
5
- include ::ActiveAttr::Model
3
+ class ApiAccess < Osm::Model
6
4
 
7
5
  # @!attribute [rw] id
8
6
  # @return [Fixnum] the id for the API
@@ -20,52 +18,91 @@ module Osm
20
18
  validates_numericality_of :id, :only_integer=>true, :greater_than=>0
21
19
  validates_presence_of :name
22
20
 
23
- validates :permissions, :hash => {:key_type => Symbol, :value_in => [10, 20]}
21
+ validates :permissions, :hash => {:key_type => Symbol, :value_type => Array}
24
22
 
25
23
 
26
- # @!method initialize
27
- # Initialize a new Term
28
- # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
24
+ # Get API access details for a given section
25
+ # @param [Osm::Api] api The api to use to make the request
26
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the details for
27
+ # @!macro options_get
28
+ # @return [Array<Osm::ApiAccess>]
29
+ def self.get_all(api, section, options={})
30
+ section_id = section.to_i
31
+ cache_key = ['api_access', api.user_id, section_id]
29
32
 
33
+ if !options[:no_cache] && cache_exist?(api, cache_key)
34
+ return cache_read(api, cache_key)
35
+ end
30
36
 
31
- # Initialize a new ApiAccess from api data
32
- # @param [Hash] data the hash of data provided by the API
33
- def self.from_api(data)
34
- attributes = {}
35
- attributes[:id] = data['apiid'].to_i
36
- attributes[:name] = data['name']
37
- attributes[:permissions] = data['permissions'].is_a?(Hash) ? data['permissions'] : {}
38
-
39
- # Rubyfy permissions hash
40
- attributes[:permissions].keys.each do |key|
41
- attributes[:permissions][key] = attributes[:permissions][key].to_i
42
- attributes[:permissions][(key.to_sym rescue key) || key] = attributes[:permissions].delete(key) # Symbolize key
37
+ data = api.perform_query("users.php?action=getAPIAccess&sectionid=#{section_id}")
38
+
39
+ permissions_map = {
40
+ 10 => [:read],
41
+ 20 => [:read, :write],
42
+ }
43
+ result = Array.new
44
+ data['apis'].each do |item|
45
+ attributes = {}
46
+ attributes[:id] = item['apiid'].to_i
47
+ attributes[:name] = item['name']
48
+ attributes[:permissions] = item['permissions'].is_a?(Hash) ? item['permissions'] : {}
49
+
50
+ # Rubyify permissions hash
51
+ attributes[:permissions].keys.each do |old_key|
52
+ new_key = (old_key.to_sym rescue old_key) # Get symbol of the key
53
+ attributes[:permissions][new_key] = attributes[:permissions].delete(old_key) # Change the key
54
+ attributes[:permissions][new_key] = permissions_map[attributes[:permissions][new_key].to_i] # Translate permissions value
55
+ end
56
+ attributes[:permissions].freeze
57
+
58
+ this_item = new(attributes)
59
+ result.push this_item
60
+ cache_write(api, [*cache_key, this_item.id], this_item)
43
61
  end
44
- attributes[:permissions].freeze
62
+ cache_write(api, cache_key, result)
45
63
 
46
- return new(attributes)
64
+ return result
47
65
  end
48
66
 
49
- # Determine if this API has read access for the provided permission
50
- # @param [Symbol] key the permission being queried
51
- # @return [Boolean] if this API can read the passed permission
52
- def can_read?(key)
53
- return [20, 10].include?(permissions[key])
54
- end
55
67
 
56
- # Determine if this API has write access for the provided permission
57
- # @param [Symbol] key the permission being queried
58
- # @return [Boolean] if this API can write the passed permission
59
- def can_write?(key)
60
- return [20].include?(permissions[key])
68
+ # Get our API access details for a given section
69
+ # @param [Osm::Api] api The api to use to make the request
70
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the details for
71
+ # @!macro options_get
72
+ # @return [Osm::ApiAccess]
73
+ def self.get_ours(api, section, options={})
74
+ get(api, section, api.api_id, options)
61
75
  end
62
76
 
63
- # Determine if this API is the API being used to make requests
64
- # @return [Boolean] if this is the API being used
65
- def our_api?
66
- return id == Osm::Api.api_id.to_i
77
+
78
+ # Get API Access for a given API
79
+ # @param [Osm::Api] api The api to use to make the request
80
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the details for
81
+ # @param [Osm::Api] for_api The api (or its ID) to get access for
82
+ # @!macro options_get
83
+ # @return [Osm::ApiAccess]
84
+ def self.get(api, section, for_api, options={})
85
+ section_id = section.to_i
86
+ for_api_id = for_api.to_i
87
+ cache_key = ['api_access', api.user_id, section_id, for_api]
88
+
89
+ if !options[:no_cache] && cache_exist?(api, cache_key)
90
+ return cache_read(api, cache_key)
91
+ end
92
+
93
+ data = get_all(api, section_id, options)
94
+
95
+ data.each do |item|
96
+ return item if item.id == for_api_id
97
+ end
98
+ return nil
67
99
  end
68
100
 
101
+
102
+ # @!method initialize
103
+ # Initialize a new Term
104
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
105
+
69
106
  end # Class ApiAccess
70
107
 
71
108
  end # Module
@@ -1,8 +1,6 @@
1
1
  module Osm
2
2
 
3
- class DueBadges
4
- include ::ActiveAttr::MassAssignmentSecurity
5
- include ::ActiveAttr::Model
3
+ class DueBadges < Osm::Model
6
4
 
7
5
  # @!attribute [rw] descriptions
8
6
  # @return [Hash] descriptions for each of the badges
@@ -30,15 +28,25 @@ module Osm
30
28
  end
31
29
 
32
30
 
33
- # @!method initialize
34
- # Initialize a new Term
35
- # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
31
+ # Get due badges
32
+ # @param [Osm::Api] api The api to use to make the request
33
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the due badges for
34
+ # @param [Osm::Term, Fixnum, nil] term the term (or its ID) to get the due badges for, passing nil causes the current term to be used
35
+ # @!macro options_get
36
+ # @return [Osm::DueBadges]
37
+ def self.get(api, section, term=nil, options={})
38
+ section = Osm::Section.get(api, section, options) if section.is_a?(Fixnum)
39
+ term_id = term.nil? ? Osm::Term.get_current_term_for_section(api, section, options) : term.to_i
40
+ cache_key = ['due_badges', section.id, term_id]
36
41
 
42
+ if !options[:no_cache] && cache_exist?(api, cache_key) && get_user_permissions(api, section.id)[:badge].include?(:read)
43
+ return cache_read(api, cache_key)
44
+ end
37
45
 
38
- # Initialize a new DueBadges from api data
39
- # @param [Hash] data the hash of data provided by the API
40
- def self.from_api(data)
41
- data = {} unless data.is_a?(Hash) # OSM returns an empty array to represent no badges
46
+ data = api.perform_query("challenges.php?action=outstandingBadges&section=#{section.type}&sectionid=#{section.id}&termid=#{term_id}")
47
+
48
+
49
+ data = {} unless data.is_a?(Hash) # OSM/OGM returns an empty array to represent no badges
42
50
  pending_raw = data['pending'] || {}
43
51
  descriptions_raw = data['description'] || {}
44
52
 
@@ -52,16 +60,23 @@ module Osm
52
60
  name = "#{member['firstname']} #{member['lastname']}"
53
61
  description = descriptions_raw[key]['name'] + (descriptions_raw[key]['section'].eql?('staged') ? " (Level #{member['level']})" : '')
54
62
  description_key = key + (descriptions_raw[key]['section'].eql?('staged') ? "_#{member['level']}" : '_1')
55
-
56
63
  attributes[:descriptions][description_key] = description
57
64
  attributes[:by_member][name] ||= []
58
65
  attributes[:by_member][name].push(description_key)
59
66
  end
60
67
  end
61
68
 
62
- new(attributes)
69
+ due_badges = Osm::DueBadges.new(attributes)
70
+ cache_write(api, cache_key, due_badges)
71
+ return due_badges
63
72
  end
64
73
 
74
+
75
+ # @!method initialize
76
+ # Initialize a new Term
77
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
78
+
79
+
65
80
  # Check if there are no badges due
66
81
  # @return [Boolean]
67
82
  def empty?
@@ -1,11 +1,8 @@
1
1
  module Osm
2
2
 
3
- class Evening
3
+ class Evening < Osm::Model
4
4
  class Activity; end # Ensure the constant exists for the validators
5
5
 
6
- include ::ActiveAttr::MassAssignmentSecurity
7
- include ::ActiveAttr::Model
8
-
9
6
  # @!attribute [rw] id
10
7
  # @return [Fixnum] the id of the evening
11
8
  # @!attribute [rw] section_id
@@ -60,37 +57,103 @@ module Osm
60
57
  # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
61
58
 
62
59
 
63
- # Initialize a new Evening from api data
64
- # @param [Hash] data the hash of data provided by the API
65
- # @param activities an array of hashes to generate the list of ProgrammeActivity objects
66
- def self.from_api(data, activities)
67
- attributes = {}
68
- attributes[:id] = Osm::to_i_or_nil(data['eveningid'])
69
- attributes[:section_id] = Osm::to_i_or_nil(data['sectionid'])
70
- attributes[:title] = data['title'] || 'Unnamed meeting'
71
- attributes[:notes_for_parents] = data['notesforparents'] || ''
72
- attributes[:games] = data['games'] || ''
73
- attributes[:pre_notes] = data['prenotes'] || ''
74
- attributes[:post_notes] = data['postnotes'] || ''
75
- attributes[:leaders] = data['leaders'] || ''
76
- attributes[:start_time] = data['starttime'].nil? ? nil : data['starttime'][0..4]
77
- attributes[:finish_time] = data['endtime'].nil? ? nil : data['endtime'][0..4]
78
- attributes[:meeting_date] = Osm::parse_date(data['meetingdate'])
79
-
80
- attributes[:activities] = Array.new
81
- unless activities.nil?
82
- activities.each do |item|
83
- attributes[:activities].push Evening::Activity.from_api(item)
60
+ # Get the programme for a given term
61
+ # @param [Osm::Api] api The api to use to make the request
62
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the programme for
63
+ # @param [Osm::term, Fixnum, nil] term the term (or its ID) to get the programme for, passing nil causes the current term to be used
64
+ # @!macro options_get
65
+ # @return [Array<Osm::Evening>]
66
+ def self.get_programme(api, section, term, options={})
67
+ section_id = section.to_i
68
+ term_id = term.nil? ? Osm::Term.get_current_term_for_section(api, section).id : term.to_i
69
+ cache_key = ['programme', section_id, term_id]
70
+
71
+ if !options[:no_cache] && cache_exist?(api, cache_key) && get_user_permissions(api, section_id)[:programme].include?(:read)
72
+ return cache_read(api, cache_key)
73
+ end
74
+
75
+ data = api.perform_query("programme.php?action=getProgramme&sectionid=#{section_id}&termid=#{term_id}")
76
+
77
+ result = Array.new
78
+ data = {'items'=>[],'activities'=>{}} if data.is_a? Array
79
+ items = data['items'] || []
80
+ activities = data['activities'] || {}
81
+
82
+ items.each do |item|
83
+ attributes = {}
84
+ attributes[:id] = Osm::to_i_or_nil(item['eveningid'])
85
+ attributes[:section_id] = Osm::to_i_or_nil(item['sectionid'])
86
+ attributes[:title] = item['title'] || 'Unnamed meeting'
87
+ attributes[:notes_for_parents] = item['notesforparents'] || ''
88
+ attributes[:games] = item['games'] || ''
89
+ attributes[:pre_notes] = item['prenotes'] || ''
90
+ attributes[:post_notes] = item['postnotes'] || ''
91
+ attributes[:leaders] = item['leaders'] || ''
92
+ attributes[:start_time] = item['starttime'].nil? ? nil : item['starttime'][0..4]
93
+ attributes[:finish_time] = item['endtime'].nil? ? nil : item['endtime'][0..4]
94
+ attributes[:meeting_date] = Osm::parse_date(item['meetingdate'])
95
+
96
+ our_activities = activities[item['eveningid']]
97
+ attributes[:activities] = Array.new
98
+ unless our_activities.nil?
99
+ our_activities.each do |activity_data|
100
+ attributes[:activities].push Osm::Evening::Activity.new(
101
+ :activity_id => Osm::to_i_or_nil(activity_data['activityid']),
102
+ :title => activity_data['title'],
103
+ :notes => activity_data['notes'],
104
+ )
105
+ end
84
106
  end
107
+
108
+ result.push new(attributes)
85
109
  end
86
110
 
87
- new(attributes)
111
+ cache_write(api, cache_key, result)
112
+ return result
88
113
  end
89
114
 
90
- # Get the evening's data for use with the API
91
- # @return [Hash]
92
- def to_api
93
- {
115
+
116
+ # Create an evening in OSM
117
+ # @param [Osm::Api] api The api to use to make the request
118
+ # @param [Osm::Section, Fixnum] section or section_id to add the evening to
119
+ # @param [Date] meeting_date the date of the meeting
120
+ # @return [Boolean] if the operation suceeded or not
121
+ def self.create(api, section, meeting_date)
122
+ section_id = section.to_i
123
+ api_data = {
124
+ 'meetingdate' => meeting_date.strftime(Osm::OSM_DATE_FORMAT),
125
+ 'sectionid' => section_id,
126
+ 'activityid' => -1
127
+ }
128
+
129
+ data = api.perform_query("programme.php?action=addActivityToProgramme", api_data)
130
+
131
+ # The cached programmes for the section will be out of date - remove them
132
+ Osm::Term.get_for_section(api, section).each do |term|
133
+ cache_delete(api, ['programme', section_id, term.id])
134
+ end
135
+
136
+ return data.is_a?(Hash) && (data['result'] == 0)
137
+ end
138
+
139
+
140
+ # Update an evening in OSM
141
+ # @param [Osm::Api] api The api to use to make the request
142
+ # @return [Boolean] if the operation suceeded or not
143
+ def update(api)
144
+ raise ObjectIsInvalid, 'evening is invalid' unless valid?
145
+
146
+ activities_data = Array.new
147
+ activities.each do |activity|
148
+ this_activity = {
149
+ 'activityid' => activity.activity_id,
150
+ 'notes' => activity.notes,
151
+ }
152
+ activities_data.push this_activity
153
+ end
154
+ activities_data = ActiveSupport::JSON.encode(activities_data)
155
+
156
+ api_data = {
94
157
  'eveningid' => id,
95
158
  'sectionid' => section_id,
96
159
  'meetingdate' => meeting_date.strftime(Osm::OSM_DATE_FORMAT),
@@ -102,27 +165,39 @@ module Osm
102
165
  'postnotes' => post_notes,
103
166
  'games' => games,
104
167
  'leaders' => leaders,
105
- 'activity' => activities_for_to_api,
168
+ 'activity' => activities_data,
106
169
  }
170
+ response = api.perform_query("programme.php?action=editEvening", api_data)
171
+
172
+ # The cached programmes for the section will be out of date - remove them
173
+ Osm::Term.get_for_section(api, section_id).each do |term|
174
+ self.class.cache_delete(api, ['programme', section_id, term.id]) if term.contains_date?(meeting_date)
175
+ end
176
+
177
+ return response.is_a?(Hash) && (response['result'] == 0)
107
178
  end
108
179
 
109
180
 
110
- private
111
- # Get the JSON for the activities to pass to the OSM API
112
- # @return [String]
113
- def activities_for_to_api
114
- to_save = Array.new
115
- activities.each do |activity|
116
- this_activity = {
117
- 'activityid' => activity.activity_id,
118
- 'notes' => activity.notes,
119
- }
120
- to_save.push this_activity
181
+ # Get the badge requirements met on a specific evening
182
+ # @param [Osm::Api] api The api to use to make the request
183
+ # @!macro options_get
184
+ # @return [Array<Hash>] hashes ready to pass into the update_register method
185
+ def get_badge_requirements(api, options={})
186
+ section = Osm::Section.get(api, section_id)
187
+ cache_key = ['badge_requirements', section.id, id]
188
+
189
+ if !options[:no_cache] && self.class.cache_exist?(api, cache_key) && get_user_permissions(api, section_id)[:programme].include?(:read)
190
+ return self.class.cache_read(api, cache_key)
121
191
  end
122
- return ActiveSupport::JSON.encode(to_save)
192
+
193
+ data = api.perform_query("users.php?action=getActivityRequirements&date=#{meeting_date.strftime(Osm::OSM_DATE_FORMAT)}&sectionid=#{section.id}&section=#{section.type}")
194
+
195
+ self.class.cache_write(api, cache_key, data)
196
+ return data
123
197
  end
124
198
 
125
199
 
200
+ private
126
201
  class Activity
127
202
  include ::ActiveAttr::MassAssignmentSecurity
128
203
  include ::ActiveAttr::Model
@@ -143,22 +218,10 @@ module Osm
143
218
  validates_numericality_of :activity_id, :only_integer=>true, :greater_than=>0
144
219
  validates_presence_of :title
145
220
 
146
-
147
221
  # @!method initialize
148
222
  # Initialize a new Evening::Activity
149
223
  # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
150
224
 
151
-
152
- # Initialize a new Evening::Activity from api data
153
- # @param [Hash] data the hash of data provided by the API
154
- def self.from_api(data)
155
- new({
156
- :activity_id => Osm::to_i_or_nil(data['activityid']),
157
- :title => data['title'],
158
- :notes => data['notes'],
159
- })
160
- end
161
-
162
225
  end # Class Evening::Activity
163
226
 
164
227
  end # Class Evening
@@ -1,8 +1,6 @@
1
1
  module Osm
2
2
 
3
- class Event
4
- include ::ActiveAttr::MassAssignmentSecurity
5
- include ::ActiveAttr::Model
3
+ class Event < Osm::Model
6
4
 
7
5
  # @!attribute [rw] id
8
6
  # @return [Fixnum] the id for the event
@@ -22,6 +20,8 @@ module Osm
22
20
  # @return [String] notes about the event
23
21
  # @!attribute [rw] archived
24
22
  # @return [Boolean] if the event has been archived
23
+ # @!attribute [rw] fields
24
+ # @return [Hash] Keys are the field's id, values are the field names
25
25
 
26
26
  attribute :id, :type => Integer
27
27
  attribute :section_id, :type => Integer
@@ -32,34 +32,292 @@ module Osm
32
32
  attribute :location, :type => String, :default => ''
33
33
  attribute :notes, :type => String, :default => ''
34
34
  attribute :archived, :type => Boolean, :default => false
35
+ attribute :fields, :default => {}
35
36
 
36
- attr_accessible :id, :section_id, :name, :start, :finish, :cost, :location, :notes, :archived
37
+ attr_accessible :id, :section_id, :name, :start, :finish, :cost, :location, :notes, :archived, :fields
37
38
 
38
39
  validates_numericality_of :id, :only_integer=>true, :greater_than=>0, :allow_nil => true
39
40
  validates_numericality_of :section_id, :only_integer=>true, :greater_than=>0
40
41
  validates_presence_of :name
42
+ validates :fields, :hash => {:key_type => String, :value_type => String}
41
43
 
42
44
 
43
45
  # @!method initialize
44
46
  # Initialize a new Term
45
47
  # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
46
48
 
47
- # Initialize a new Event from api data
48
- # @param [Hash] data the hash of data provided by the API
49
- def self.from_api(data)
50
- new({
51
- :id => Osm::to_i_or_nil(data['eventid']),
52
- :section_id => Osm::to_i_or_nil(data['sectionid']),
53
- :name => data['name'],
54
- :start => Osm::make_datetime(data['startdate'], data['starttime']),
55
- :end => Osm::make_datetime(data['enddate'], data['endtime']),
56
- :cost => data['cost'],
57
- :location => data['location'],
58
- :notes => data['notes'],
59
- :archived => data['archived'].eql?('1'),
49
+
50
+ # Get events for a section
51
+ # @param [Osm::Api] api The api to use to make the request
52
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the events for
53
+ # @!macro options_get
54
+ # @option options [Boolean] :include_archived (optional) if true then archived activities will also be returned
55
+ # @return [Array<Osm::Event>]
56
+ def self.get_for_section(api, section, options={})
57
+ section_id = section.to_i
58
+ cache_key = ['events', section_id]
59
+ events = nil
60
+
61
+ if !options[:no_cache] && cache_exist?(api, cache_key) && get_user_permissions(api, section_id)[:events].include?(:read)
62
+ return cache_read(api, cache_key)
63
+ end
64
+
65
+ data = api.perform_query("events.php?action=getEvents&sectionid=#{section_id}&showArchived=true")
66
+
67
+ events = Array.new
68
+ unless data['items'].nil?
69
+ data['items'].each do |item|
70
+ event_id = Osm::to_i_or_nil(item['eventid'])
71
+ fields_data = api.perform_query("events.php?action=getEvent&sectionid=#{section_id}&eventid=#{event_id}")
72
+ fields = {}
73
+ ActiveSupport::JSON.decode(fields_data['config']).each do |field|
74
+ fields[field['id']] = field['name']
75
+ end
76
+
77
+ event = Osm::Event.new(
78
+ :id => event_id,
79
+ :section_id => Osm::to_i_or_nil(item['sectionid']),
80
+ :name => item['name'],
81
+ :start => Osm::make_datetime(item['startdate'], item['starttime']),
82
+ :finish => Osm::make_datetime(item['enddate'], item['endtime']),
83
+ :cost => item['cost'],
84
+ :location => item['location'],
85
+ :notes => item['notes'],
86
+ :archived => item['archived'].eql?('1'),
87
+ :fields => fields,
88
+ )
89
+ events.push event
90
+ cache_write(api, ['event', event.id], event)
91
+ end
92
+ end
93
+ cache_write(api, cache_key, events)
94
+
95
+ return events if options[:include_archived]
96
+ return events.reject do |event|
97
+ event.archived?
98
+ end
99
+ end
100
+
101
+ # Get an event
102
+ # @param [Osm::Api] api The api to use to make the request
103
+ # @param [Osm::Section, Fixnum] section the section (or its ID) to get the events for
104
+ # @param [Fixnum] event_id the id of the event to get
105
+ # @!macro options_get
106
+ # @option options [Boolean] :include_archived (optional) if true then archived activities will also be returned
107
+ # @return [Osm::Event, nil] the event (or nil if it couldn't be found
108
+ def self.get(api, section, event_id, options={})
109
+ section_id = section.to_i
110
+ cache_key = ['event', event_id]
111
+
112
+ if !options[:no_cache] && cache_exist?(api, cache_key) && get_user_permissions(api, section_id)[:events].include?(:read)
113
+ return cache_read(api, cache_key)
114
+ end
115
+
116
+ events = get_for_section(api, section, options)
117
+ return nil unless events.is_a? Array
118
+
119
+ events.each do |event|
120
+ return event if event.id == event_id
121
+ end
122
+
123
+ return nil
124
+ end
125
+
126
+
127
+ # Create an event in OSM
128
+ # @param [Osm::Api] api The api to use to make the request
129
+ # @return [Osm::Event, nil] the created event, nil if failed
130
+ def self.create(api, parameters)
131
+ event = new(parameters)
132
+ raise ObjectIsInvalid, 'event is invalid' unless event.valid?
133
+ raise Forbidden, 'you do not have permission to write to events for this section' unless get_user_permissions(api, event.section_id)[:events].include?(:write)
134
+
135
+ data = api.perform_query("events.php?action=addEvent&sectionid=#{event.section_id}", {
136
+ 'name' => event.name,
137
+ 'location' => event.location,
138
+ 'startdate' => event.start.strftime(Osm::OSM_DATE_FORMAT),
139
+ 'enddate' => event.finish.strftime(Osm::OSM_DATE_FORMAT),
140
+ 'cost' => event.cost,
141
+ 'notes' => event.notes,
142
+ 'starttime' => event.start.strftime(Osm::OSM_TIME_FORMAT),
143
+ 'endtime' => event.finish.strftime(Osm::OSM_TIME_FORMAT),
144
+ })
145
+
146
+ # The cached events for the section will be out of date - remove them
147
+ cache_delete(api, ['events', event.section_id])
148
+ cache_write(api, ['event', event.id], event)
149
+
150
+ if (data.is_a?(Hash) && data.has_key?('id'))
151
+ event.id = data['id'].to_i
152
+ return event
153
+ else
154
+ return nil
155
+ end
156
+ end
157
+
158
+ # Update event in OSM
159
+ # @param [Osm::Api] api The api to use to make the request
160
+ # @return [Boolean] wether the update succedded
161
+ def update(api)
162
+ raise Forbidden, 'you do not have permission to write to events for this section' unless self.class.get_user_permissions(api, section_id)[:events].include?(:write)
163
+
164
+ data = api.perform_query("events.php?action=addEvent&sectionid=#{section_id}", {
165
+ 'eventid' => id,
166
+ 'name' => name,
167
+ 'location' => location,
168
+ 'startdate' => start? ? start.strftime(Osm::OSM_DATE_FORMAT) : '',
169
+ 'enddate' => finish? ? finish.strftime(Osm::OSM_DATE_FORMAT) : '',
170
+ 'cost' => cost,
171
+ 'notes' => notes,
172
+ 'starttime' => start? ? start.strftime(Osm::OSM_TIME_FORMAT) : '',
173
+ 'endtime' => finish? ? finish.strftime(Osm::OSM_TIME_FORMAT) : '',
174
+ })
175
+
176
+ # The cached events for the section will be out of date - remove them
177
+ self.class.cache_delete(api, ['event', id])
178
+ self.class.cache_delete(api, ['events', section_id])
179
+
180
+ return data.is_a?(Hash) && (data['id'].to_i == id)
181
+ end
182
+
183
+ # Delete event from OSM
184
+ # @param [Osm::Api] api The api to use to make the request
185
+ # @return [Boolean] wether the delete succedded
186
+ def delete(api)
187
+ raise Forbidden, 'you do not have permission to write to events for this section' unless self.class.get_user_permissions(api, section_id)[:events].include?(:write)
188
+
189
+ data = api.perform_query("events.php?action=deleteEvent&sectionid=#{section_id}&eventid=#{id}")
190
+
191
+ # The cached events for the section will be out of date - remove them
192
+ self.class.cache_delete(api, ['events', section_id])
193
+ self.class.cache_delete(api, ['event', id])
194
+
195
+ return data.is_a?(Hash) ? data['ok'] : false
196
+ end
197
+
198
+
199
+ # Get event attendance
200
+ # @param [Osm::Api] api The api to use to make the request
201
+ # @param [Osm::Term, Fixnum, nil] term the term (or its ID) to get the members for, passing nil causes the current term to be used
202
+ # @!macro options_get
203
+ # @option options [Boolean] :include_archived (optional) if true then archived activities will also be returned
204
+ # @return [Array<Osm::Event::Attendance>]
205
+ def get_attendance(api, term=nil, options={})
206
+ term_id = term.nil? ? Osm::Term.get_current_term_for_section(api, section).id : term.to_i
207
+ cache_key = ['event_attendance', id]
208
+
209
+ if !options[:no_cache] && self.class.cache_exist?(api, cache_key) && self.class.get_user_permissions(api, section_id)[:events].include?(:read)
210
+ return self.class.cache_read(api, cache_key)
211
+ end
212
+
213
+ data = api.perform_query("events.php?action=getEventAttendance&eventid=#{id}&sectionid=#{section_id}&termid=#{term_id}")
214
+ data = data['items']
215
+
216
+ attendance = []
217
+ data.each_with_index do |item, index|
218
+ item.merge!({
219
+ 'dob' => item['dob'].nil? ? nil : Osm::parse_date(item['dob'], :ignore_epoch => true),
220
+ 'attending' => item['attending'].eql?('Yes'),
221
+ })
222
+
223
+ attendance.push Osm::Event::Attendance.new(
224
+ :event => self,
225
+ :member_id => Osm::to_i_or_nil(item['scoutid']),
226
+ :grouping_id => Osm::to_i_or_nil(item['patrolid'].eql?('') ? nil : item['patrolid']),
227
+ :fields => item.select { |key, value|
228
+ ['firstname', 'lastname', 'dob', 'attending'].include?(key) || key.to_s.match(/\Af_\d+\Z/)
229
+ },
230
+ :row => index,
231
+ )
232
+ end
233
+
234
+ self.class.cache_write(api, cache_key, attendance)
235
+ return attendance
236
+ end
237
+
238
+
239
+ # Add a field in OSM
240
+ # @param [Osm::Api] api The api to use to make the request
241
+ # @param [String] field_label the label for the field to add
242
+ # @return [Boolean] wether the update succedded
243
+ def add_field(api, label)
244
+ raise ArgumentIsInvalid, 'label is invalid' if label.blank?
245
+ raise Forbidden, 'you do not have permission to write to events for this section' unless self.class.get_user_permissions(api, section_id)[:events].include?(:write)
246
+
247
+ data = api.perform_query("events.php?action=addColumn&sectionid=#{section_id}&eventid=#{id}", {
248
+ 'columnName' => label
60
249
  })
250
+
251
+ # The cached events for the section will be out of date - remove them
252
+ self.class.cache_delete(api, ['events', section_id])
253
+ self.class.cache_delete(api, ['event', id])
254
+ self.class.cache_delete(api, ['event_attendance', id])
255
+
256
+ return data.is_a?(Hash) && (data['eventid'].to_i == id)
61
257
  end
62
258
 
259
+
260
+
261
+ class Attendance
262
+ include ::ActiveAttr::MassAssignmentSecurity
263
+ include ::ActiveAttr::Model
264
+
265
+ # @!attribute [rw] member_id
266
+ # @return [Fixnum] OSM id for the member
267
+ # @!attribute [rw] grouping__id
268
+ # @return [Fixnum] OSM id for the grouping the member is in
269
+ # @!attribute [rw] fields
270
+ # @return [Hash] Keys are the field's id, values are the field values
271
+ # @!attribute [rw] row
272
+ # @return [Fixnum] part of the OSM API
273
+
274
+ attribute :row, :type => Integer
275
+ attribute :member_id, :type => Integer
276
+ attribute :grouping_id, :type => Integer
277
+ attribute :fields, :default => {}
278
+ attribute :event
279
+
280
+ attr_accessible :member_id, :grouping_id, :fields, :row, :event
281
+
282
+ validates_numericality_of :row, :only_integer=>true, :greater_than_or_equal_to=>0
283
+ validates_numericality_of :member_id, :only_integer=>true, :greater_than=>0
284
+ validates_numericality_of :grouping_id, :only_integer=>true, :greater_than_or_equal_to=>-2
285
+ validates :fields, :hash => {:key_type => String}
286
+ validates_each :event do |record, attr, value|
287
+ record.event.valid?
288
+ end
289
+
290
+
291
+ # @!method initialize
292
+ # Initialize a new FlexiRecordData
293
+ # @param [Hash] attributes the hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
294
+
295
+
296
+ # Update event attendance
297
+ # @param [Osm::Api] api The api to use to make the request
298
+ # @param [String] field_id the id of the field to update (must be 'attending' or /\Af_\d+\Z/)
299
+ # @return [Boolean] if the operation suceeded or not
300
+ def update(api, field_id)
301
+ raise ArgumentIsInvalid, 'field_id is invalid' unless field_id.match(/\Af_\d+\Z/) || field_id.eql?('attending')
302
+ raise Forbidden, 'you do not have permission to write to events for this section' unless Osm::Model.get_user_permissions(api, event.section_id)[:events].include?(:write)
303
+
304
+ data = api.perform_query("events.php?action=updateScout", {
305
+ 'scoutid' => member_id,
306
+ 'column' => field_id,
307
+ 'value' => !field_id.eql?('attending') ? fields[field_id] : (fields['attending'] ? 'Yes' : 'No'),
308
+ 'sectionid' => event.section_id,
309
+ 'row' => row,
310
+ 'eventid' => event.id,
311
+ })
312
+
313
+ # The cached event attedance will be out of date
314
+ Osm::Model.cache_delete(api, ['event_attendance', event.id])
315
+
316
+ return data.is_a?(Hash)
317
+ end
318
+
319
+ end # Class Attendance
320
+
63
321
  end # Class Event
64
322
 
65
323
  end # Module