osm 0.0.1.alpha

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/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ # Because this is a gem, ignore Gemfile.lock:
2
+
3
+ Gemfile.lock
4
+
5
+ # And because this is Ruby, ignore the following
6
+ # (source: https://github.com/github/gitignore/blob/master/Ruby.gitignore):
7
+
8
+ *.gem
9
+ *.rbc
10
+ .bundle
11
+ .config
12
+ coverage
13
+ InstalledFiles
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
21
+
22
+ # YARD artifacts
23
+ .yardoc
24
+ _yardoc
25
+ doc/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ branches:
5
+ only:
6
+ - master
7
+ - staging
8
+ script: rake ci:travis
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Version 0.0.1
2
+
3
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in osm.gemspec
4
+ gemspec
data/LICENSE.rdoc ADDED
@@ -0,0 +1,39 @@
1
+ ==Copyright
2
+ Copyright (c) 2011-2012, Robert Gauld. All rights reserved.
3
+
4
+
5
+ ==License
6
+ This code can be used under the BSD License (reproduced below).
7
+ Commiters to the project give Robert Gauld the right to redistribute their commits
8
+ under the BSD license. Should the project be re-licensed and the commiter can not be
9
+ contacted either by the email address accompanying their commits or the project's community
10
+ then Robert Gauld shall have the right to re-license their commits on their behalf.
11
+
12
+ For the purposes of this project the community will be taken to include:
13
+
14
+ - GitHub (make sure you can be contacted through the user name making the commit)
15
+ - The Online Scout Manager forum
16
+
17
+
18
+ ===BSD License
19
+ Redistribution and use in source and binary forms, with or without modification,
20
+ are permitted provided that the following conditions are met:
21
+
22
+ - Redistributions of source code must retain the above copyright notice,
23
+ this list of conditions and the following disclaimer.
24
+ - Redistributions in binary form must reproduce the above copyright notice,
25
+ this list of conditions and the following disclaimer in the documentation and/or
26
+ other materials provided with the distribution.
27
+ - Neither the name of the OSM Extender nor the names of its contributors
28
+ may be used to endorse or promote products derived from this software
29
+ without specific prior written permission.
30
+
31
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
32
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
34
+ SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
36
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
38
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ ##Build State
2
+ This project uses continuous integration to help ensure that a quality product is delivered.
3
+ Travis CI monitors two branches (versions) of the code - Master (which is what gets released)
4
+ and Staging (which is what is currently being debugged ready for moving to master).
5
+
6
+ Master [![Build Status](https://secure.travis-ci.org/robertgauld/osm.png?branch=master)](http://travis-ci.org/robertgauld/osm)
7
+
8
+ Staging [![Build Status](https://secure.travis-ci.org/robertgauld/osm.png?branch=staging)](http://travis-ci.org/robertgauld/osm)
9
+
10
+
11
+ ## OSM
12
+
13
+ Use the [Online Scout Manager](https://www.onlinescoutmanager.co.uk) API.
14
+
15
+
16
+ ## Installation
17
+
18
+ Add to your Gemfile and run the `bundle` command to install it.
19
+
20
+ ```ruby
21
+ gem 'osm'
22
+ ```
23
+
24
+ **Requires Ruby 1.9.2 or later.**
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
7
+
8
+
9
+ namespace :ci do
10
+ desc "Run the Travis CI tests"
11
+ task :travis do
12
+ Rake::Task[:spec].invoke
13
+ end
14
+ end
data/lib/osm.rb ADDED
@@ -0,0 +1,71 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'version')
2
+ Dir[File.join(File.dirname(__FILE__) , 'osm', '*.rb')].each {|file| require file }
3
+
4
+ require 'date'
5
+
6
+
7
+ module Osm
8
+
9
+ class Error < Exception; end
10
+ class ConnectionError < Error; end
11
+
12
+
13
+ private
14
+ def self.make_array_of_symbols(array)
15
+ array.each_with_index do |item, index|
16
+ array[index] = item.to_sym
17
+ end
18
+ end
19
+
20
+ def self.find_current_term_id(api, section_id, data={})
21
+ terms = api.get_terms(data)
22
+
23
+ # Return the term we are currently in
24
+ unless terms.nil?
25
+ terms.each do |term|
26
+ return term.id if (term.section_id == section_id) && term.current?
27
+ end
28
+ end
29
+
30
+ raise Error.new('There is no current term for the section.')
31
+ end
32
+
33
+ def self.make_datetime(date, time)
34
+ date = nil if date.nil? || date.empty?
35
+ time = nil if time.nil? || time.empty?
36
+ if (!date.nil? && !time.nil?)
37
+ begin
38
+ return DateTime.strptime((date + ' ' + time), '%Y-%m-%d %H:%M:%S')
39
+ rescue ArgumentError
40
+ return nil
41
+ end
42
+ elsif !date.nil?
43
+ begin
44
+ return DateTime.strptime(date, '%Y-%m-%d')
45
+ rescue ArgumentError
46
+ return nil
47
+ end
48
+ else
49
+ return nil
50
+ end
51
+ end
52
+
53
+ def self.parse_date(date)
54
+ return nil if date.nil?
55
+ begin
56
+ return Date.strptime(date, '%Y-%m-%d')
57
+ rescue ArgumentError
58
+ return nil
59
+ end
60
+ end
61
+
62
+ def self.to_i_or_nil(item)
63
+ return nil if item.nil?
64
+ begin
65
+ return item.to_i
66
+ rescue
67
+ return nil
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,45 @@
1
+ module Osm
2
+
3
+ class Activity
4
+
5
+ attr_reader :id, :version, :group_id, :user_id, :title, :description, :resources, :instructions, :running_time, :location, :shared, :rating, :editable, :deletable, :used, :versions, :sections, :tags, :files, :badges
6
+
7
+ # Initialize a new Activity using the hash returned by the API call
8
+ # @param data the hash of data for the object returned by the API
9
+ def initialize(data)
10
+ @id = data['details']['activityid'].to_i
11
+ @version = data['details']['version'].to_i
12
+ @group_id = data['details']['groupid'].to_i
13
+ @user_id = data['details']['userid'].to_i
14
+ @title = data['details']['title']
15
+ @description = data['details']['description']
16
+ @resources = data['details']['resources']
17
+ @instructions = data['details']['instructions']
18
+ @running_time = data['details']['runningtime'].to_i
19
+ @location = data['details']['location'].to_sym
20
+ @shared = data['details']['shared'].to_i
21
+ @rating = data['details']['rating'].to_i
22
+ @editable = data['editable']
23
+ @deletable = data['deletable']
24
+ @used = data['used'].to_i
25
+ @versions = data['versions']
26
+ @sections = Osm::make_array_of_symbols(data['sections'] || [])
27
+ @tags = data['tags'] || []
28
+ @files = data['files'] || []
29
+ @badges = data['badges'] || []
30
+
31
+ # Clean versions hashes
32
+ @versions.each do |version|
33
+ version.keys.each do |key|
34
+ version[(key.to_sym rescue key) || key] = version.delete(key)
35
+ end
36
+ version[:value] = version[:value].to_i
37
+ version[:user_id] = version[:userid].to_i
38
+ version.delete(:userid)
39
+ version[:selected] = (version[:selected] == 'selected')
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
data/lib/osm/api.rb ADDED
@@ -0,0 +1,688 @@
1
+ module Osm
2
+
3
+ class Api
4
+
5
+ @@default_cache_ttl = 30 * 60 # The default caching time for responses from OSM (in seconds)
6
+ # Some things will only be cached for half this time
7
+ # Whereas others will be cached for twice this time
8
+ # Most items however will be cached for this time
9
+
10
+ @@user_access = Hash.new
11
+
12
+ # Initialize a new API connection
13
+ # If passing user details then both must be passed
14
+ # @param userid (optional) osm userid of the user to act as
15
+ # @param secret (optional) osm secret of the user to act as
16
+ # @param site (optional) wether to use OSM (:scout) or OGM (:guide), defaults to the value set for the class
17
+ def initialize(userid=nil, secret=nil, site=@@api_site)
18
+ raise ArgumentError, 'You must pass a secret if you are passing a userid' if secret.nil? && !userid.nil?
19
+ raise ArgumentError, 'You must pass a userid if you are passing a secret' if userid.nil? && !secret.nil?
20
+ raise ArgumentError, 'site is invalid, if passed it should be either :scout or :guide' unless [:scout, :guide].include?(site)
21
+
22
+ @base_url = 'https://www.onlinescoutmanager.co.uk' if site == :scout
23
+ @base_url = 'http://www.onlineguidemanager.co.uk' if site == :guide
24
+ set_user(userid, secret)
25
+ end
26
+
27
+ # Configure the API options used by all instances of the class
28
+ # @param options - a hash containing the following keys:
29
+ # * :api_id - the apiid given to you for using the OSM id
30
+ # * :api_token - the token which goes with the above api
31
+ # * :api_name - the name displayed in the External Access tab of OSM
32
+ # * :api_site - wether to use OSM (if :scout) or OGM (if :guide)
33
+ # * :default_cache_ttl (optional, default = 30 minutes) - The default TTL value for the cache, note that some items are cached for twice this time and others are cached for half this time.
34
+ def self.configure(options)
35
+ raise ArgumentError, ':api_id does not exist in options hash' if options[:api_id].nil?
36
+ raise ArgumentError, ':api_token does not exist in options hash' if options[:api_token].nil?
37
+ raise ArgumentError, ':api_name does not exist in options hash' if options[:api_name].nil?
38
+ raise ArgumentError, ':api_site does not exist in options hash or is invalid, this should be set to either :scout or :guide' unless [:scout, :guide].include?(options[:api_site])
39
+ raise ArgumentError, ':default_cache_ttl must be greater than 0' unless (options[:default_cache_ttl].nil? || options[:default_cache_ttl].to_i > 0)
40
+
41
+ @@api_id = options[:api_id]
42
+ @@api_token = options[:api_token]
43
+ @@api_name = options[:api_name]
44
+ @@api_site = options[:api_site]
45
+ @@default_cache_ttl = options[:default_cache_ttl].to_i unless options[:default_cache_ttl].nil?
46
+ end
47
+
48
+ # Get the API ID used in this class
49
+ # @returns the API ID
50
+ def self.api_id
51
+ return @@api_id
52
+ end
53
+
54
+ # Get the API name displayed in the External Access tab of OSM
55
+ # @returns the API ID
56
+ def self.api_name
57
+ return @@api_name
58
+ end
59
+
60
+ # Get the userid and secret to be able to act as a certain user on the OSM system
61
+ # Also set's the 'current user'
62
+ # @param email the login email address of the user on OSM
63
+ # @param password the login password of the user on OSM
64
+ # @returns hash containing the following keys:
65
+ # * 'userid' - the userid to use in future requests
66
+ # * 'secret' - the secret to use in future requests
67
+ def authorize(email, password)
68
+ api_data = {
69
+ 'email' => email,
70
+ 'password' => password,
71
+ }
72
+ data = perform_query('users.php?action=authorise', api_data)
73
+ set_user(data['userid'], data['secret'])
74
+ return data
75
+ end
76
+
77
+ # Get the user's roles
78
+ # @param options (optional) a hash which may contain the following keys:
79
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
80
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
81
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
82
+ # * 'secret' (optional) the OSM secret belonging to the above user
83
+ # @returns an array of Osm::Role objects
84
+ def get_roles(options={})
85
+ api_data = options[:api_data] || {}
86
+
87
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-roles-#{api_data[:userid] || @userid}")
88
+ return Rails.cache.read("OSMAPI-roles-#{api_data[:userid] || @userid}")
89
+ end
90
+
91
+ data = perform_query('api.php?action=getUserRoles', api_data)
92
+
93
+ result = Array.new
94
+ data.each do |item|
95
+ role = Osm::Role.new(item)
96
+ result.push role
97
+ Rails.cache.write("OSMAPI-section-#{role.section.id}", role.section, :expires_in => @@default_cache_ttl*2)
98
+ self.user_can_access :section, role.section.id, api_data
99
+ end
100
+ Rails.cache.write("OSMAPI-roles-#{api_data[:userid] || @userid}", result, :expires_in => @@default_cache_ttl*2)
101
+
102
+ return result
103
+ end
104
+
105
+ # Get the user's notepads
106
+ # @param options (optional) a hash which may contain the following keys:
107
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
108
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
109
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
110
+ # * 'secret' (optional) the OSM secret belonging to the above user
111
+ # @returns a hash (keys are section IDs, values are a string)
112
+ def get_notepads(options={})
113
+ api_data = options[:api_data] || {}
114
+
115
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-notepads-#{api_data[:userid] || @userid}")
116
+ return Rails.cache.read("OSMAPI-notepads-#{api_data[:userid] || @userid}")
117
+ end
118
+
119
+ notepads = perform_query('api.php?action=getNotepads', api_data)
120
+ return {} unless notepads.is_a?(Hash)
121
+
122
+ data = {}
123
+ notepads.each do |key, value|
124
+ data[key.to_i] = value
125
+ Rails.cache.write("OSMAPI-notepad-#{key}", value, :expires_in => @@default_cache_ttl*2)
126
+ end
127
+
128
+ Rails.cache.write("OSMAPI-notepads-#{api_data[:userid] || @userid}", data, :expires_in => @@default_cache_ttl*2)
129
+ return data
130
+ end
131
+
132
+ # Get the notepad for a specified section
133
+ # @param section_id the section id of the required section
134
+ # @param options (optional) a hash which may contain the following keys:
135
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
136
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
137
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
138
+ # * 'secret' (optional) the OSM secret belonging to the above user
139
+ # @returns nil if an error occured or the user does not have access to that section
140
+ # @returns a string otherwise
141
+ def get_notepad(section_id, options={})
142
+ api_data = options[:api_data] || {}
143
+
144
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-notepad-#{section_id}") && self.user_can_access?(:section, section_id, api_data)
145
+ return Rails.cache.read("OSMAPI-notepad-#{section_id}")
146
+ end
147
+
148
+ notepads = get_notepads(options)
149
+ return nil unless notepads.is_a? Hash
150
+
151
+ notepads.each_key do |key|
152
+ return notepads[key] if key == section_id
153
+ end
154
+
155
+ return nil
156
+ end
157
+
158
+ # Get the section (and its configuration)
159
+ # @param section_id the section id of the required section
160
+ # @param options (optional) a hash which may contain the following keys:
161
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
162
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
163
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
164
+ # * 'secret' (optional) the OSM secret belonging to the above user
165
+ # @returns nil if an error occured or the user does not have access to that section
166
+ # @returns an Osm::SectionConfig object otherwise
167
+ def get_section(section_id, options={})
168
+ api_data = options[:api_data] || {}
169
+
170
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-section-#{section_id}") && self.user_can_access?(:section, section_id, api_data)
171
+ return Rails.cache.read("OSMAPI-section-#{section_id}")
172
+ end
173
+
174
+ roles = get_roles(options)
175
+ return nil unless roles.is_a? Array
176
+
177
+ roles.each do |role|
178
+ return role.section if role.section.id == section_id
179
+ end
180
+
181
+ return nil
182
+ end
183
+
184
+ # Get the groupings (e.g. patrols, sixes, lodges) for a given section
185
+ # @param section_id the section to get the programme for
186
+ # @param options (optional) a hash which may contain the following keys:
187
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
188
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
189
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
190
+ # * 'secret' (optional) the OSM secret belonging to the above user
191
+ # @returns an array of Osm::Patrol objects
192
+ def get_groupings(section_id, options={})
193
+ api_data = options[:api_data] || {}
194
+
195
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-groupings-#{section_id}") && self.user_can_access?(:section, section_id, api_data)
196
+ return Rails.cache.read("OSMAPI-groupings-#{section_id}")
197
+ end
198
+
199
+ data = perform_query("users.php?action=getPatrols&sectionid=#{section_id}", api_data)
200
+
201
+ result = Array.new
202
+ data['patrols'].each do |item|
203
+ grouping = Osm::Grouping.new(item)
204
+ result.push grouping
205
+ Rails.cache.write("OSMAPI-grouping-#{grouping.id}", grouping, :expires_in => @@default_cache_ttl*2)
206
+ self.user_can_access :grouping, grouping.id, api_data
207
+ end
208
+ Rails.cache.write("OSMAPI-groupings-#{section_id}", result, :expires_in => @@default_cache_ttl*2)
209
+
210
+ return result
211
+ end
212
+
213
+ # Get the terms that the OSM user can access
214
+ # @param options (optional) a hash which may contain the following keys:
215
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
216
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
217
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
218
+ # * 'secret' (optional) the OSM secret belonging to the above user
219
+ # @returns an array of Osm::Term objects
220
+ def get_terms(options={})
221
+ api_data = options[:api_data] || {}
222
+
223
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-terms-#{api_data[:userid] || @userid}")
224
+ return Rails.cache.read("OSMAPI-terms-#{api_data[:userid] || @userid}")
225
+ end
226
+
227
+ data = perform_query('api.php?action=getTerms', api_data)
228
+
229
+ result = Array.new
230
+ data.each_key do |key|
231
+ data[key].each do |item|
232
+ term = Osm::Term.new(item)
233
+ result.push term
234
+ Rails.cache.write("OSMAPI-term-#{term.id}", term, :expires_in => @@default_cache_ttl*2)
235
+ self.user_can_access :term, term.id, api_data
236
+ end
237
+ end
238
+
239
+ Rails.cache.write("OSMAPI-terms-#{api_data[:userid] || @userid}", result, :expires_in => @@default_cache_ttl*2)
240
+ return result
241
+ end
242
+
243
+ # Get a term
244
+ # @param term_id the id of the required term
245
+ # @param options (optional) a hash which may contain the following keys:
246
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
247
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
248
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
249
+ # * 'secret' (optional) the OSM secret belonging to the above user
250
+ # @returns nil if an error occured or the user does not have access to that term
251
+ # @returns an Osm::Term object otherwise
252
+ def get_term(term_id, options={})
253
+ api_data = options[:api_data] || {}
254
+
255
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-term-#{term_id}") && self.user_can_access?(:term, term_id, api_data)
256
+ return Rails.cache.read("OSMAPI-term-#{term_id}")
257
+ end
258
+
259
+ terms = get_terms(options)
260
+ return nil unless terms.is_a? Array
261
+
262
+ terms.each do |term|
263
+ return term if term.id == term_id
264
+ end
265
+
266
+ return nil
267
+ end
268
+
269
+ # Get the programme for a given term
270
+ # @param sectionid the section to get the programme for
271
+ # @param termid the term to get the programme for
272
+ # @param options (optional) a hash which may contain the following keys:
273
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
274
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
275
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
276
+ # * 'secret' (optional) the OSM secret belonging to the above user
277
+ # @returns an array of Osm::ProgrammeItem objects
278
+ def get_programme(section_id, term_id, options={})
279
+ api_data = options[:api_data] || {}
280
+
281
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-programme-#{section_id}-#{term_id}") && self.user_can_access?(:programme, section_id, api_data)
282
+ return Rails.cache.read("OSMAPI-programme-#{section_id}-#{term_id}")
283
+ end
284
+
285
+ data = perform_query("programme.php?action=getProgramme&sectionid=#{section_id}&termid=#{term_id}", api_data)
286
+
287
+ result = Array.new
288
+ data = {'items'=>[],'activities'=>{}} if data.is_a? Array
289
+ self.user_can_access(:programme, section_id, api_data) unless data.is_a? Array
290
+ items = data['items'] || []
291
+ activities = data['activities'] || {}
292
+
293
+ items.each do |item|
294
+ programme_item = Osm::ProgrammeItem.new(item, activities[item['eveningid']])
295
+ result.push programme_item
296
+ programme_item.activities.each do |activity|
297
+ self.user_can_access :activity, activity.activity_id, api_data
298
+ end
299
+ end
300
+
301
+ Rails.cache.write("OSMAPI-programme-#{section_id}-#{term_id}", result, :expires_in => @@default_cache_ttl)
302
+ return result
303
+ end
304
+
305
+ # Get activity details
306
+ # @param activity_id the activity ID
307
+ # @param version (optional) the version of the activity to retreive
308
+ # @param options (optional) a hash which may contain the following keys:
309
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
310
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
311
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
312
+ # * 'secret' (optional) the OSM secret belonging to the above user
313
+ # @returns an Osm::Activity object
314
+ def get_activity(activity_id, version=nil, options={})
315
+ api_data = options[:api_data] || {}
316
+
317
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-activity-#{activity_id}-#{version}") && self.user_can_access?(:activity, activity_id, api_data)
318
+ return Rails.cache.read("OSMAPI-activity-#{activity_id}-#{version}")
319
+ end
320
+
321
+ data = nil
322
+ if version.nil?
323
+ data = perform_query("programme.php?action=getActivity&id=#{activity_id}", api_data)
324
+ else
325
+ data = perform_query("programme.php?action=getActivity&id=#{activity_id}&version=#{version}", api_data)
326
+ end
327
+
328
+ activity = Osm::Activity.new(data)
329
+ Rails.cache.write("OSMAPI-activity-#{activity_id}-#{nil}", activity, :expires_in => @@default_cache_ttl*2) if version.nil?
330
+ Rails.cache.write("OSMAPI-activity-#{activity_id}-#{activity.version}", activity, :expires_in => @@default_cache_ttl/2)
331
+ self.user_can_access :activity, activity.id, api_data
332
+
333
+ return activity
334
+ end
335
+
336
+ # Get member details
337
+ # @section_id the section to get details for
338
+ # @term_id (optional) the term to get details for, if it is omitted then the current term is used
339
+ # @param options (optional) a hash which may contain the following keys:
340
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
341
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
342
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
343
+ # * 'secret' (optional) the OSM secret belonging to the above user
344
+ # @returns an array of Osm::Member objects
345
+ def get_members(section_id, term_id=nil, options={})
346
+ api_data = options[:api_data] || {}
347
+ term_id = Osm::find_current_term_id(self, section_id, api_data) if term_id.nil?
348
+
349
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-members-#{section_id}-#{term_id}") && self.user_can_access?(:member, section_id, api_data)
350
+ return Rails.cache.read("OSMAPI-members-#{section_id}-#{term_id}")
351
+ end
352
+
353
+ data = perform_query("users.php?action=getUserDetails&sectionid=#{section_id}&termid=#{term_id}", api_data)
354
+
355
+ result = Array.new
356
+ data['items'].each do |item|
357
+ result.push Osm::Member.new(item)
358
+ end
359
+ self.user_can_access :member, section_id, api_data
360
+ Rails.cache.write("OSMAPI-members-#{section_id}-#{term_id}", result, :expires_in => @@default_cache_ttl)
361
+
362
+ return result
363
+ end
364
+
365
+ # Get API access details for a given section
366
+ # @param section_id the section to get details for
367
+ # @param options (optional) a hash which may contain the following keys:
368
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
369
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
370
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
371
+ # * 'secret' (optional) the OSM secret belonging to the above user
372
+ # @returns an array of Osm::ApiAccess objects
373
+ def get_api_access(section_id, options={})
374
+ api_data = options[:api_data] || {}
375
+
376
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-api_access-#{api_data['userid'] || @userid}-#{section_id}")
377
+ return Rails.cache.read("OSMAPI-api_access-#{api_data['userid'] || @userid}-#{section_id}")
378
+ end
379
+
380
+ data = perform_query("users.php?action=getAPIAccess&sectionid=#{section_id}", api_data)
381
+
382
+ result = Array.new
383
+ data['apis'].each do |item|
384
+ this_item = Osm::ApiAccess.new(item)
385
+ result.push this_item
386
+ self.user_can_access(:programme, section_id, api_data) if this_item.can_read?(:programme)
387
+ self.user_can_access(:member, section_id, api_data) if this_item.can_read?(:member)
388
+ self.user_can_access(:badge, section_id, api_data) if this_item.can_read?(:badge)
389
+ Rails.cache.write("OSMAPI-api_access-#{api_data['userid'] || @userid}-#{section_id}-#{this_item.id}", this_item, :expires_in => @@default_cache_ttl*2)
390
+ end
391
+
392
+ return result
393
+ end
394
+
395
+ # Get our API access details for a given section
396
+ # @param section_id the section to get details for
397
+ # @param options (optional) a hash which may contain the following keys:
398
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
399
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
400
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
401
+ # * 'secret' (optional) the OSM secret belonging to the above user
402
+ # @returns an Osm::ApiAccess objects
403
+ def get_our_api_access(section_id, options={})
404
+ api_data = options[:api_data] || {}
405
+
406
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-api_access-#{api_data['userid'] || @userid}-#{section_id}-#{Osm::Api.api_id}")
407
+ return Rails.cache.read("OSMAPI-api_access-#{api_data['userid'] || @userid}-#{section_id}-#{Osm::Api.api_id}")
408
+ end
409
+
410
+ data = get_api_access(section_id, options)
411
+ found = nil
412
+ data.each do |item|
413
+ found = item if item.our_api?
414
+ end
415
+
416
+ return found
417
+ end
418
+
419
+ # Get events
420
+ # @section_id the section to get details for
421
+ # @param options (optional) a hash which may contain the following keys:
422
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
423
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
424
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
425
+ # * 'secret' (optional) the OSM secret belonging to the above user
426
+ # @returns an array of Osm::Event objects
427
+ def get_events(section_id, options={})
428
+ api_data = options[:api_data] || {}
429
+
430
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-events-#{section_id}") && self.user_can_access?(:programme, section_id, api_data)
431
+ return Rails.cache.read("OSMAPI-events-#{section_id}")
432
+ end
433
+
434
+ data = perform_query("events.php?action=getEvents&sectionid=#{section_id}", api_data)
435
+
436
+ result = Array.new
437
+ unless data['items'].nil?
438
+ data['items'].each do |item|
439
+ result.push Osm::Event.new(item)
440
+ end
441
+ end
442
+ self.user_can_access :programme, section_id, api_data
443
+ Rails.cache.write("OSMAPI-events-#{section_id}", result, :expires_in => @@default_cache_ttl)
444
+
445
+ return result
446
+ end
447
+
448
+ # Get due badges
449
+ # @section_id the section to get details for
450
+ # @param options (optional) a hash which may contain the following keys:
451
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
452
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
453
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
454
+ # * 'secret' (optional) the OSM secret belonging to the above user
455
+ # @returns an Osm::DueBadges object
456
+ def get_due_badges(section_id, term_id=nil, options={})
457
+ api_data = options[:api_data] || {}
458
+ term_id = Osm::find_current_term_id(self, section_id, api_data) if term_id.nil?
459
+
460
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-due_badges-#{section_id}-#{term_id}") && self.user_can_access?(:badge, section_id, api_data)
461
+ return Rails.cache.read("OSMAPI-due_badges-#{section_id}-#{term_id}")
462
+ end
463
+
464
+ section_type = get_section(section_id, api_data).type.to_s
465
+ data = perform_query("challenges.php?action=outstandingBadges&section=#{section_type}&sectionid=#{section_id}&termid=#{term_id}", api_data)
466
+
467
+ data = Osm::DueBadges.new(data)
468
+ self.user_can_access :badge, section_id, api_data
469
+ Rails.cache.write("OSMAPI-due_badges-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl*2)
470
+
471
+ return data
472
+ end
473
+
474
+ # Get register structure
475
+ # @section_id the section to get details for
476
+ # @param options (optional) a hash which may contain the following keys:
477
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
478
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
479
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
480
+ # * 'secret' (optional) the OSM secret belonging to the above user
481
+ # @returns an array of hashes representing the rows of the register
482
+ def get_register_structure(section_id, term_id=nil, options={})
483
+ api_data = options[:api_data] || {}
484
+ term_id = Osm::find_current_term_id(self, section_id, api_data) if term_id.nil?
485
+
486
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-register_structure-#{section_id}-#{term_id}") && self.user_can_access?(:register, section_id, api_data)
487
+ return Rails.cache.read("OSMAPI-register_structure-#{section_id}-#{term_id}")
488
+ end
489
+
490
+ data = perform_query("users.php?action=registerStructure&sectionid=#{section_id}&termid=#{term_id}", api_data)
491
+
492
+ data.each do |item|
493
+ item.symbolize_keys!
494
+ item[:rows].each do |row|
495
+ row.symbolize_keys!
496
+ end
497
+ end
498
+ self.user_can_access :register, section_id, api_data
499
+ Rails.cache.write("OSMAPI-register_structure-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl/2)
500
+
501
+ return data
502
+ end
503
+
504
+ # Get register
505
+ # @section_id the section to get details for
506
+ # @param options (optional) a hash which may contain the following keys:
507
+ # * :no_cache - if true then the data will be retreived from OSM not the cache
508
+ # * :api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
509
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
510
+ # * 'secret' (optional) the OSM secret belonging to the above user
511
+ # @returns an array of hashes representing the attendance of each member
512
+ def get_register(section_id, term_id=nil, options={})
513
+ api_data = options[:api_data] || {}
514
+ term_id = Osm::find_current_term_id(self, section_id, api_data) if term_id.nil?
515
+
516
+ if !options[:no_cache] && Rails.cache.exist?("OSMAPI-register-#{section_id}-#{term_id}") && self.user_can_access?(:register, section_id, api_data)
517
+ return Rails.cache.read("OSMAPI-register-#{section_id}-#{term_id}")
518
+ end
519
+
520
+ data = perform_query("users.php?action=register&sectionid=#{section_id}&termid=#{term_id}", api_data)
521
+
522
+ data = data['items']
523
+ data.each do |item|
524
+ item.symbolize_keys!
525
+ item[:scoutid] = item[:scoutid].to_i
526
+ item[:sectionid] = item[:sectionid].to_i
527
+ item[:patrolid] = item[:patrolid].to_i
528
+ end
529
+ self.user_can_access :register, section_id, api_data
530
+ Rails.cache.write("OSMAPI-register-#{section_id}-#{term_id}", data, :expires_in => @@default_cache_ttl/2)
531
+ return data
532
+ end
533
+
534
+ # Create an evening in OSM
535
+ # @param section_id the id of the section to add the term to
536
+ # @param meeting_date the date of the meeting
537
+ # @param api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
538
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
539
+ # * 'secret' (optional) the OSM secret belonging to the above user
540
+ # @returns a boolean representing if the operation suceeded or not
541
+ def create_evening(section_id, meeting_date, api_data={})
542
+ section_id = section_id.to_i
543
+ evening_api_data = {
544
+ 'meetingdate' => meeting_date.strftime('%Y-%m-%d'),
545
+ 'sectionid' => section_id,
546
+ 'activityid' => -1
547
+ }
548
+
549
+ data = perform_query("programme.php?action=addActivityToProgramme", api_data.merge(evening_api_data))
550
+
551
+ # The cached programmes for the section will be out of date - remove them
552
+ get_terms(api_data).each do |term|
553
+ Rails.cache.delete("OSMAPI-programme-#{term.section_id}-#{term.id}") if term.section_id == section_id
554
+ end
555
+
556
+ return data.is_a?(Hash) && (data['result'] == 0)
557
+ end
558
+
559
+ # Update an evening in OSM
560
+ # @param programme_item is the Osm::ProgrammeItem object to update
561
+ # @param api_data (optional) a hash containing information to be sent to the server, it may contain the following keys:
562
+ # * 'userid' (optional) the OSM userid to make the request as, this will override one provided using the set_user method
563
+ # * 'secret' (optional) the OSM secret belonging to the above user
564
+ # @returns a boolean representing if the operation suceeded or not
565
+ def update_evening(programme_item, api_data={})
566
+ response = perform_query("programme.php?action=editEvening", api_data.merge({
567
+ 'eveningid' => programme_item.evening_id,
568
+ 'sectionid' => programme_item.section_id,
569
+ 'meetingdate' => programme_item.meeting_date.try(:strftime, '%Y-%m-%d'),
570
+ 'starttime' => programme_item.start_time,
571
+ 'endtime' => programme_item.end_time,
572
+ 'title' => programme_item.title,
573
+ 'notesforparents' => programme_item.notes_for_parents,
574
+ 'prenotes' => programme_item.pre_notes,
575
+ 'postnotes' => programme_item.post_notes,
576
+ 'games' => programme_item.games,
577
+ 'leaders' => programme_item.leaders,
578
+ 'activity' => programme_item.activities_for_saving,
579
+ 'googlecalendar' => programme_item.google_calendar || '',
580
+ }))
581
+
582
+ # The cached programmes for the section will be out of date - remove them
583
+ get_terms(api_data).each do |term|
584
+ Rails.cache.delete("OSMAPI-programme-#{term.section_id}-#{term.id}") if term.section_id == programme_item.section_id
585
+ end
586
+
587
+ return response.is_a?(Hash) && (response['result'] == 0)
588
+ end
589
+
590
+
591
+ protected
592
+ # Set access permission for the current user on a resource stored in the cache
593
+ # @param resource_type a symbol representing the resource type (:section, :grouping, :term, :activity, :programme, :member, :badge, :register)
594
+ # @param resource_id the id of the resource being checked
595
+ # @param api_data the data hash used in accessing the api
596
+ # @param permission (optional, default true) wether the user can access the resource
597
+ def user_can_access(resource_type, resource_id, api_data, permission=true)
598
+ user = (api_data['userid'] || @userid).to_i
599
+ resource_id = resource_id.to_i
600
+ resource_type = resource_type.to_sym
601
+
602
+ @@user_access[user] = {} if @@user_access[user].nil?
603
+ @@user_access[user][resource_type] = {} if @@user_access[user][resource_type].nil?
604
+
605
+ @@user_access[user][resource_type][resource_id] = permission
606
+ end
607
+
608
+ # Get access permission for the current user on a resource stored in the cache
609
+ # @param resource_type a symbol representing the resource type (:section, :grouping, :term, :activity, :programme, :member, :badge, :register)
610
+ # @param resource_id the id of the resource being checked
611
+ # @param api_data the data hash used in accessing the api
612
+ # @returns true if the user can access the resource
613
+ # @returns false if the user can not access the resource
614
+ # @returns nil if the combination of user and resource has not been seen
615
+ def user_can_access?(resource_type, resource_id, api_data)
616
+ user = (api_data['userid'] || @userid).to_i
617
+ resource_id = resource_id.to_i
618
+ resource_type = resource_type.to_sym
619
+
620
+ return nil if @@user_access[user].nil?
621
+ return nil if @@user_access[user][resource_type].nil?
622
+ return @@user_access[user][resource_type][resource_id]
623
+ end
624
+
625
+
626
+ private
627
+ # Set the OSM user to make future requests as
628
+ # @param userid the OSM userid to use (get this using the authorize method)
629
+ # @param secret the OSM secret to use (get this using the authorize method)
630
+ def set_user(userid, secret)
631
+ @userid = userid
632
+ @secret = secret
633
+ end
634
+
635
+ # Make the query to the OSM API
636
+ # @param url the script on the remote server to invoke
637
+ # @param api_data (optional) a hash containing the values to be sent to the server
638
+ # @returns the parsed JSON returned by OSM
639
+ def perform_query(url, api_data={})
640
+ api_data['apiid'] = @@api_id
641
+ api_data['token'] = @@api_token
642
+
643
+ if api_data['userid'].nil? && api_data['secret'].nil?
644
+ unless @userid.nil? || @secret.nil?
645
+ api_data['userid'] = @userid
646
+ api_data['secret'] = @secret
647
+ end
648
+ end
649
+
650
+ if Rails.env.development?
651
+ puts "Making OSM API request to #{url}"
652
+ puts api_data.to_s
653
+ end
654
+
655
+ begin
656
+ result = HTTParty.post("#{@base_url}/#{url}", {:body => api_data})
657
+ rescue SocketError, TimeoutError, OpenSSL::SSL::SSLError
658
+ raise ConnectionError.new('A problem occured on the internet.')
659
+ end
660
+ raise ConnectionError.new("HTTP Status code was #{result.response.code}") if !result.response.code.eql?('200')
661
+
662
+ if Rails.env.development?
663
+ puts "Result from OSM request to #{url}"
664
+ puts result.response.body
665
+ end
666
+
667
+ raise Error.new(result.response.body) unless looks_like_json?(result.response.body)
668
+ decoded = ActiveSupport::JSON.decode(result.response.body)
669
+ osm_error = get_osm_error(decoded)
670
+ raise Error.new(osm_error) if osm_error
671
+ return decoded
672
+ end
673
+
674
+ def looks_like_json?(text)
675
+ (['[', '{'].include?(text[0]))
676
+ end
677
+
678
+ def get_osm_error(data)
679
+ return false unless data.is_a?(Hash)
680
+ to_return = data['error'] || data['err'] || false
681
+ to_return = false if to_return.blank?
682
+ puts "OSM API ERROR: #{to_return}" if Rails.env.development? && to_return
683
+ return to_return
684
+ end
685
+
686
+ end
687
+
688
+ end