osm 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
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