osm 1.2.14 → 1.2.15.dev
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG.md +32 -0
- data/lib/osm/activity.rb +66 -35
- data/lib/osm/api.rb +7 -5
- data/lib/osm/badge.rb +371 -182
- data/lib/osm/badges.rb +28 -20
- data/lib/osm/event.rb +63 -55
- data/lib/osm/meeting.rb +56 -43
- data/lib/osm/member.rb +2 -2
- data/lib/osm/model.rb +1 -1
- data/spec/osm/activity_spec.rb +33 -17
- data/spec/osm/api_spec.rb +1 -1
- data/spec/osm/badge_spec.rb +562 -642
- data/spec/osm/badges_spec.rb +92 -51
- data/spec/osm/event_spec.rb +95 -75
- data/spec/osm/meeting_spec.rb +53 -50
- data/spec/osm/member_spec.rb +10 -0
- data/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTc4ZGUzMmFjNjdhZjNhY2M0MzdjNGJhODk4MGJhOTY1NjM2YmJlMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZmIyMGUyZDVlNzU5MmFkNjUwNTJiNGJiODJjZDc1MDZjZDJhMWMzYg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDViMmI1NDdhZThkMDcxYzc4Nzk1ZDZiNjM5MDJiMThhNDA0ZWU3YjQyNjdk
|
10
|
+
YTJlYjYyZTU1ZDczMWJlMmY5Yjc3NmZhNWIzZjIwNDllZWIwM2Q3MDEwNzZj
|
11
|
+
ODg4NjI5YzFmOWI0N2NiOGQ1ODg0ZmRlYmJkMWI1ZDAwMGNkMzQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZWIxMTU3NmVmMmNkOWI2MTc0OGJjNjA3MGRiODY2YTZhMTExN2UyMTdkOWY0
|
14
|
+
ZWNjNTdhZDE5MGE3MTc1MzBhZDAxYWJlMjdiNTQ1MGZiMDZiZDNkMDRjOTVk
|
15
|
+
NmFmODIyMGExOWI5ZTg0NGQ1NTg4ZTE5NzMwNjYzMTY5ZWIyZjY=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
## Version 1.2.15
|
2
|
+
|
3
|
+
* Add support for census and giftaid link generation for members
|
4
|
+
* Add :osm_staging as a site to point the gem at. Really only useful for gem development.
|
5
|
+
* Update to match OSM's new badge system:
|
6
|
+
* Retrieving badge stock
|
7
|
+
* Updating badge stock
|
8
|
+
* Fetching badge data
|
9
|
+
* Fetching due badges (added stock\_levels attribute, since OSM now gives it)
|
10
|
+
* Fetching & updating badge links for Events
|
11
|
+
* Fetching & updating badge links for Activities
|
12
|
+
* Fetching & updating badge links for Meetings
|
13
|
+
* Summary now returns all started/completed/awarded badges (it can't filter by type) so can only be called from Osm::Badge
|
14
|
+
* Osm::Badge
|
15
|
+
* osm\_key, osm\_long\_key and competion criteria attributes are gone
|
16
|
+
* id, version, identifier, group\_name, latest, user\_id, levels, sharing attributes added
|
17
|
+
* OSM Changed how completion criteria are retrieved (this gem now uses a special peice of OSM's API just for this purpose) so:
|
18
|
+
* Osm::Badge attributes sections\_needed, total\_needed and needed\_from\_section are gone
|
19
|
+
* Osm::Badge::Data
|
20
|
+
* mark\_awarded method now only marks as awarded (the optional mark\_as parameter is gone)
|
21
|
+
* mark\_not\_awarded method added
|
22
|
+
* mark\_due and mark\_not\_due methods added
|
23
|
+
* completed attribute renamed to due
|
24
|
+
* sections_gained method renamed to modules_gained, now returns an array of letters
|
25
|
+
* gained_in_sections renamed to gained_in_modules
|
26
|
+
* Osm::Event::BadgeLink
|
27
|
+
* Attributes added: badge\_name, badge\_id, badge\_version, requirement\_id
|
28
|
+
* Attributes removed: badge\_label, badge\_key, requirement\_key
|
29
|
+
* Attributes untouched: badge\_type, badge\_section, data, requirement\_label
|
30
|
+
* Osm::Activity::Badge attributes now match Osm::Event::BadgeLink
|
31
|
+
* Osm::Meeting::BadgeLink attributes now match Osm::Event::badgeLink
|
32
|
+
|
1
33
|
## Version 1.2.14
|
2
34
|
|
3
35
|
* Fix fetching sections when user doesn't have access to any
|
data/lib/osm/activity.rb
CHANGED
@@ -156,12 +156,14 @@ module Osm
|
|
156
156
|
end
|
157
157
|
(data['badges'].is_a?(Array) ? data['badges'] : []).each do |badge_data|
|
158
158
|
attributes[:badges].push Badge.new(
|
159
|
-
:
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
159
|
+
:badge_type => badge_data['badgetype'].to_sym,
|
160
|
+
:badge_section => badge_data['section'].to_sym,
|
161
|
+
:badge_name => badge_data['badgeLongName'],
|
162
|
+
:badge_id => Osm::to_i_or_nil(badge_data['badge_id']),
|
163
|
+
:badge_version => Osm::to_i_or_nil(badge_data['badge_version']),
|
164
|
+
:requirement_id => Osm::to_i_or_nil(badge_data['column_id']),
|
165
|
+
:requirement_label => badge_data['columnnameLongName'],
|
166
|
+
:data => badge_data['data'],
|
165
167
|
)
|
166
168
|
end
|
167
169
|
(data['versions'].is_a?(Array) ? data['versions'] : []).each do |version_data|
|
@@ -241,7 +243,23 @@ module Osm
|
|
241
243
|
'location' => location,
|
242
244
|
'sections' => sections.to_json,
|
243
245
|
'tags' => tags.to_json,
|
244
|
-
'links' => badges.map{ |b|
|
246
|
+
'links' => badges.map{ |b|
|
247
|
+
{
|
248
|
+
'badge_id' => b.badge_id.to_s,
|
249
|
+
'badge_version' => b.badge_version.to_s,
|
250
|
+
'column_id' => b.requirement_id.to_s,
|
251
|
+
'badge' => nil,
|
252
|
+
'badgeLongName' => b.badge_name,
|
253
|
+
'columnname' => nil,
|
254
|
+
'columnnameLongName' => b.requirement_label,
|
255
|
+
'data' => b.data,
|
256
|
+
'section' => b.badge_section,
|
257
|
+
'sectionLongName' => nil,
|
258
|
+
'sections' => sections.map{ |s| s.to_s },
|
259
|
+
'badgetype' => b.badge_type.to_s,
|
260
|
+
'badgetypeLongName' => nil,
|
261
|
+
}
|
262
|
+
}.to_json,
|
245
263
|
'shared' => shared,
|
246
264
|
'sectionid' => section.to_i,
|
247
265
|
'secretEdit' => secret_update,
|
@@ -309,43 +327,56 @@ module Osm
|
|
309
327
|
include ActiveModel::MassAssignmentSecurity if ActiveModel::VERSION::MAJOR < 4
|
310
328
|
include ActiveAttr::Model
|
311
329
|
|
312
|
-
# @!attribute [rw]
|
313
|
-
# @return [Fixnum] the activity being done
|
314
|
-
# @!attribute [rw] section_type
|
315
|
-
# @return [Symbol] the section the badge 'belongs' to
|
316
|
-
# @!attribute [rw] type
|
330
|
+
# @!attribute [rw] badge_type
|
317
331
|
# @return [Symbol] the type of badge
|
318
|
-
# @!attribute [rw]
|
319
|
-
# @return [
|
320
|
-
# @!attribute [rw]
|
321
|
-
# @return [String]
|
322
|
-
# @!attribute [rw]
|
323
|
-
# @return [String]
|
324
|
-
|
325
|
-
|
326
|
-
attribute
|
327
|
-
|
328
|
-
attribute
|
329
|
-
|
330
|
-
attribute
|
332
|
+
# @!attribute [rw] badge_section
|
333
|
+
# @return [Symbol] the section type that the badge belongs to
|
334
|
+
# @!attribute [rw] requirement_label
|
335
|
+
# @return [String] human firendly requirement label
|
336
|
+
# @!attribute [rw] data
|
337
|
+
# @return [String] what to put in the column when the badge records are updated
|
338
|
+
# @!attribute [rw] badge_name
|
339
|
+
# @return [String] the badge's name
|
340
|
+
# @!attribute [rw] badge_id
|
341
|
+
# @return [Fixnum] the badge's ID in OSM
|
342
|
+
# @!attribute [rw] badge_version
|
343
|
+
# @return [Fixnum] the version of the badge
|
344
|
+
# @!attribute [rw] requirement_id
|
345
|
+
# @return [Fixnum] the requirement's ID in OSM
|
346
|
+
|
347
|
+
attribute :badge_type, :type => Object
|
348
|
+
attribute :badge_section, :type => Object
|
349
|
+
attribute :requirement_label, :type => String
|
350
|
+
attribute :data, :type => String
|
351
|
+
attribute :badge_name, :type => String
|
352
|
+
attribute :badge_id, :type => Integer
|
353
|
+
attribute :badge_version, :type => Integer
|
354
|
+
attribute :requirement_id, :type => Integer
|
331
355
|
|
332
356
|
if ActiveModel::VERSION::MAJOR < 4
|
333
|
-
attr_accessible :
|
357
|
+
attr_accessible :badge_type, :badge_section, :requirement_label, :data, :badge_name, :badge_id, :badge_version, :requirement_id
|
334
358
|
end
|
335
359
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
record.errors.add(attr, 'must be a Symbol') unless value.is_a?(Symbol)
|
343
|
-
end
|
360
|
+
validates_presence_of :badge_name
|
361
|
+
validates_inclusion_of :badge_section, :in => [:beavers, :cubs, :scouts, :explorers, :staged]
|
362
|
+
validates_inclusion_of :badge_type, :in => [:core, :staged, :activity, :challenge]
|
363
|
+
validates_numericality_of :badge_id, :only_integer=>true, :greater_than=>0
|
364
|
+
validates_numericality_of :badge_version, :only_integer=>true, :greater_than_or_equal_to=>0
|
365
|
+
validates_numericality_of :requirement_id, :only_integer=>true, :greater_than=>0, :allow_nil=>true
|
344
366
|
|
345
367
|
# @!method initialize
|
346
|
-
# Initialize a new
|
368
|
+
# Initialize a new Meeting::Activity
|
347
369
|
# @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
|
348
370
|
|
371
|
+
# Compare BadgeLink based on section, type, badge_name, requirement_label, data
|
372
|
+
def <=>(another)
|
373
|
+
[:badge_section, :badge_type, :badge_name, :requirement_label].each do |attribute|
|
374
|
+
result = self.try(:data) <=> another.try(:data)
|
375
|
+
return result unless result == 0
|
376
|
+
end
|
377
|
+
return self.try(:data) <=> another.try(:data)
|
378
|
+
end
|
379
|
+
|
349
380
|
end # Class Activity::Badge
|
350
381
|
|
351
382
|
class Version
|
data/lib/osm/api.rb
CHANGED
@@ -12,6 +12,7 @@ module Osm
|
|
12
12
|
BASE_URLS = {
|
13
13
|
:osm => 'https://www.onlinescoutmanager.co.uk',
|
14
14
|
:ogm => 'http://www.onlineguidemanager.co.uk',
|
15
|
+
:osm_staging => 'http://staging.onlinescoutmanager.co.uk'
|
15
16
|
}
|
16
17
|
|
17
18
|
|
@@ -29,9 +30,9 @@ module Osm
|
|
29
30
|
# @option options [Boolean] :debug if true debugging info is output (optional, default = false)
|
30
31
|
# @return nil
|
31
32
|
def self.configure(options)
|
32
|
-
raise ArgumentError, ':default_site does not exist in options hash or is invalid, this should be set to either :osm or :ogm' unless [:osm, :ogm].include?(options[:default_site])
|
33
|
-
raise ArgumentError,
|
34
|
-
[:osm, :ogm].each do |api_key|
|
33
|
+
raise ArgumentError, ':default_site does not exist in options hash or is invalid, this should be set to either :osm or :ogm' unless [:osm, :ogm, :osm_staging].include?(options[:default_site])
|
34
|
+
raise ArgumentError, ":#{options[:default_site]} does not exist in options hash" if options[options[:default_site]].nil?
|
35
|
+
[:osm, :ogm, :osm_staging].each do |api_key|
|
35
36
|
if options[api_key]
|
36
37
|
api_data = options[api_key]
|
37
38
|
raise ArgumentError, ":#{api_key} must be a Hash" unless api_data.is_a?(Hash)
|
@@ -46,6 +47,7 @@ module Osm
|
|
46
47
|
@@api_details = {
|
47
48
|
:osm => (options[:osm] || {}),
|
48
49
|
:ogm => (options[:ogm] || {}),
|
50
|
+
:osm_staging => (options[:osm_staging] || {})
|
49
51
|
}
|
50
52
|
nil
|
51
53
|
end
|
@@ -71,7 +73,7 @@ module Osm
|
|
71
73
|
def initialize(user_id, secret, site=@@site)
|
72
74
|
raise ArgumentError, 'You must pass a secret (get this by using the authorize method)' if secret.nil?
|
73
75
|
raise ArgumentError, 'You must pass a user_id (get this by using the authorize method)' if user_id.nil?
|
74
|
-
raise ArgumentError, 'site is invalid, if passed it should be either :osm or :ogm, if not passed then you forgot to run Api.configure' unless [:osm, :ogm].include?(site)
|
76
|
+
raise ArgumentError, 'site is invalid, if passed it should be either :osm or :ogm, if not passed then you forgot to run Api.configure' unless [:osm, :ogm, :osm_staging].include?(site)
|
75
77
|
|
76
78
|
@site = site
|
77
79
|
set_user(user_id, secret)
|
@@ -209,7 +211,7 @@ module Osm
|
|
209
211
|
# @raise [Osm::Error] If an error was returned by OSM
|
210
212
|
# @raise [Osm::ConnectionError] If an error occured connecting to OSM
|
211
213
|
def self.perform_query(site, url, api_data={})
|
212
|
-
raise ArgumentError, 'site is invalid, this should be set to either :osm or :ogm' unless [:osm, :ogm].include?(site)
|
214
|
+
raise ArgumentError, 'site is invalid, this should be set to either :osm or :ogm' unless [:osm, :ogm, :osm_staging].include?(site)
|
213
215
|
|
214
216
|
data = api_data.merge({
|
215
217
|
'apiid' => @@api_details[site][:id],
|
data/lib/osm/badge.rb
CHANGED
@@ -7,47 +7,58 @@ module Osm
|
|
7
7
|
# @return [String] the name of the badge
|
8
8
|
# @!attribute [rw] requirement_notes
|
9
9
|
# @return [String] a description of the badge
|
10
|
-
# @!attribute [rw] osm_key
|
11
|
-
# @return [String] the key for the badge in OSM
|
12
|
-
# @!attribute [rw] osm_long_key
|
13
|
-
# @return [String] the long key for the badge in osm (used for getting stock)
|
14
|
-
# @!attribute [rw] sections_needed
|
15
|
-
# @return [Fixnum]
|
16
|
-
# @!attribute [rw] total_needed
|
17
|
-
# @return [Fixnum]
|
18
|
-
# @!attribute [rw] needed_from_section
|
19
|
-
# @return [Hash]
|
20
10
|
# @!attribute [rw] requirements
|
21
|
-
# @return [Array<Osm::Badge::Requirement>]
|
11
|
+
# @return [Array<Osm::Badge::Requirement>] the requirements of the badge
|
12
|
+
# @!attribute [rw] id
|
13
|
+
# @return [Fixnum] the badge's id in OSM
|
14
|
+
# @!attribute [rw] version
|
15
|
+
# @return [Fixnum] the version of the badge
|
16
|
+
# @!attribute [rw] identifier
|
17
|
+
# @return [String] the identifier used by OSM for this badge & version
|
18
|
+
# @!attribute [rw] group_name
|
19
|
+
# @return [String] what group (if any) this badge belongs to (eg Core, Partnership), used only for display sorting
|
20
|
+
# @!attribute [rw] latest
|
21
|
+
# @return [Boolean] whether this is the latest version of the badge
|
22
|
+
# @!attribute [rw] sharing
|
23
|
+
# @return [Symbol] the sharing status of this badge (:draft, :private, :optin, :default_locked, :optin_locked)
|
24
|
+
# @!attribute [rw] user_id
|
25
|
+
# @return [Fixnum] the OSM user who created this (version of the) badge
|
26
|
+
# @!attribute [rw] levels
|
27
|
+
# @return [Array<Fixnum>, nil] the levels available, nil if it's a single level badge
|
22
28
|
|
23
29
|
attribute :name, :type => String
|
24
30
|
attribute :requirement_notes, :type => String
|
25
|
-
attribute :osm_key, :type => String
|
26
|
-
attribute :osm_long_key, :type => String
|
27
|
-
attribute :sections_needed, :type => Integer
|
28
|
-
attribute :total_needed, :type => Integer
|
29
|
-
attribute :needed_from_section, :type => Object
|
30
31
|
attribute :requirements, :type => Object
|
32
|
+
attribute :id, :type => Integer
|
33
|
+
attribute :version, :type => Integer
|
34
|
+
attribute :identifier, :type => String
|
35
|
+
attribute :group_name, :type => String
|
36
|
+
attribute :latest, :type => Boolean
|
37
|
+
attribute :sharing, :type => Object
|
38
|
+
attribute :user_id, :type => Integer
|
39
|
+
attribute :levels, :type => Object
|
40
|
+
attribute :completion_criteria, :type => Object
|
31
41
|
|
32
42
|
if ActiveModel::VERSION::MAJOR < 4
|
33
|
-
attr_accessible :name, :requirement_notes, :
|
43
|
+
attr_accessible :name, :requirement_notes, :requirements, :id, :version, :identifier, :group_name, :latest, :sharing, :user_id, :levels, :completion_criteria
|
34
44
|
end
|
35
45
|
|
36
|
-
validates_numericality_of :sections_needed, :only_integer=>true, :greater_than_or_equal_to=>-1
|
37
|
-
validates_numericality_of :total_needed, :only_integer=>true, :greater_than_or_equal_to=>-1
|
38
46
|
validates_presence_of :name
|
39
47
|
validates_presence_of :requirement_notes
|
40
|
-
validates_presence_of :
|
41
|
-
validates_presence_of :
|
42
|
-
|
48
|
+
validates_presence_of :id
|
49
|
+
validates_presence_of :version
|
50
|
+
validates_presence_of :identifier
|
51
|
+
validates_inclusion_of :sharing, :in => [:draft, :private, :optin, :optin_locked, :default_locked]
|
52
|
+
validates_presence_of :user_id
|
43
53
|
validates :requirements, :array_of => {:item_type => Osm::Badge::Requirement, :item_valid => true}
|
54
|
+
validates_inclusion_of :latest, :in => [true, false]
|
55
|
+
validates :levels, :array_of => {:item_type => Fixnum}, :allow_nil => true
|
44
56
|
|
45
57
|
|
46
58
|
# @!method initialize
|
47
59
|
# Initialize a new Badge
|
48
60
|
# @param [Hash] attributes The hash of attributes (see attributes for descriptions, use Symbol of attribute name as the key)
|
49
61
|
|
50
|
-
|
51
62
|
# Get badges
|
52
63
|
# @param [Osm::Api] api The api to use to make the request
|
53
64
|
# @param [Osm::Section, Fixnum, #to_i] section The section (or its ID) to get the due badges for
|
@@ -67,24 +78,45 @@ module Osm
|
|
67
78
|
|
68
79
|
term_id = Osm::Term.get_current_term_for_section(api, section, options).to_i
|
69
80
|
badges = []
|
81
|
+
badge_sharing_map = {
|
82
|
+
'draft' => :draft,
|
83
|
+
'private' => :private,
|
84
|
+
'optin' => :optin,
|
85
|
+
'optin-locked' => :optin_locked,
|
86
|
+
'default-locked' => :default_locked
|
87
|
+
}
|
70
88
|
|
71
|
-
data = api.perform_query("
|
89
|
+
data = api.perform_query("ext/badges/records/?action=getBadgeStructureByType§ion=#{section_type}&type_id=#{type_id}&term_id=#{term_id}§ion_id=#{section.id}")
|
72
90
|
badge_order = data["badgeOrder"].to_s.split(',')
|
73
91
|
structures = data["structure"] || {}
|
74
92
|
details = data["details"] || {}
|
93
|
+
|
75
94
|
badge_order.each do |b|
|
76
95
|
structure = structures[b]
|
77
96
|
detail = details[b]
|
78
97
|
config = ActiveSupport::JSON.decode(detail['config'] || '{}')
|
79
98
|
|
80
99
|
badge = new(
|
100
|
+
:id => detail['badge_id'],
|
101
|
+
:version => detail['badge_version'],
|
102
|
+
:identifier => detail['badge_identifier'],
|
81
103
|
:name => detail['name'],
|
82
104
|
:requirement_notes => detail['description'],
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
105
|
+
:group_name => detail['group_name'],
|
106
|
+
:latest => detail['latest'].to_i.eql?(1),
|
107
|
+
:sharing => badge_sharing_map[detail['sharing']],
|
108
|
+
:user_id => Osm.to_i_or_nil(detail['userid']),
|
109
|
+
:levels => config['levelslist'],
|
110
|
+
:completion_criteria => {
|
111
|
+
:min_modules_required => config['numModulesRequired'].to_i,
|
112
|
+
:fields_required => (config['columnsRequired'] || []).map{ |i| {id: Osm.to_i_or_nil(i['id']), min: i['min'].to_i} },
|
113
|
+
:badges_required => (config['badgesRequired'] || []).map{ |i| {id: Osm.to_i_or_nil(i['id']), version: i['version'].to_i} },
|
114
|
+
:min_requirements_completed => config['minRequirementsCompleted'].to_i,
|
115
|
+
:requires => config['requires'],
|
116
|
+
:add_columns_to_module => Osm.to_i_or_nil(config['addcolumns']),
|
117
|
+
:levels_column => Osm.to_i_or_nil(config['levels_column_id']),
|
118
|
+
:show_letters => !!config['shownumbers'],
|
119
|
+
},
|
88
120
|
)
|
89
121
|
|
90
122
|
requirements = []
|
@@ -93,11 +125,13 @@ module Osm
|
|
93
125
|
:badge => badge,
|
94
126
|
:name => r['name'],
|
95
127
|
:description => r['tooltip'],
|
96
|
-
:
|
97
|
-
:
|
128
|
+
:module_letter => r['module'],
|
129
|
+
:field => Osm::to_i_or_nil(r['field']),
|
130
|
+
:editable => r['editable'].to_s.eql?('true'),
|
98
131
|
)
|
99
132
|
end
|
100
133
|
badge.requirements = requirements
|
134
|
+
badge.completion_criteria[:modules] = module_completion_data(api, badge, options)
|
101
135
|
|
102
136
|
badges.push badge
|
103
137
|
end
|
@@ -113,26 +147,41 @@ module Osm
|
|
113
147
|
# @!macro options_get
|
114
148
|
# @return [Array<Hash>]
|
115
149
|
def self.get_summary_for_section(api, section, term=nil, options={})
|
116
|
-
raise Error, 'This method must be called on one of the subclasses
|
150
|
+
raise Error, 'This method must NOT be called on one of the subclasses(CoreBadge, ChallengeBadge, StagedBadge or ActivityBadge)' unless type.nil?
|
117
151
|
require_ability_to(api, :read, :badge, section, options)
|
118
152
|
section = Osm::Section.get(api, section, options) unless section.is_a?(Osm::Section)
|
119
153
|
term_id = (term.nil? ? Osm::Term.get_current_term_for_section(api, section, options) : term).to_i
|
120
|
-
cache_key = ['badge-summary', section.id, term_id
|
154
|
+
cache_key = ['badge-summary', section.id, term_id]
|
121
155
|
|
122
156
|
if !options[:no_cache] && Osm::Model.cache_exist?(api, cache_key)
|
123
157
|
return cache_read(api, cache_key)
|
124
158
|
end
|
125
159
|
|
126
160
|
summary = []
|
127
|
-
data = api.perform_query("
|
161
|
+
data = api.perform_query("ext/badges/records/summary/?action=get&mode=verbose§ion=#{section.type}§ionid=#{section.id}&termid=#{term_id}")
|
128
162
|
data['items'].each do |item|
|
129
163
|
new_item = {
|
130
164
|
:first_name => item['firstname'],
|
131
165
|
:last_name => item['lastname'],
|
166
|
+
:name => "#{item['firstname']} #{item['lastname']}",
|
167
|
+
:member_id => Osm.to_i_or_nil(item['scout_id']),
|
132
168
|
}
|
133
|
-
|
134
|
-
|
169
|
+
|
170
|
+
badge_data = Hash[item.to_a.select{ |k,v| !!k.match(/\d+_\d+/) }]
|
171
|
+
badge_data.each do |badge_identifier, status|
|
172
|
+
if status.is_a?(String)
|
173
|
+
# Possible statuses: 'Started', 'Due', 'Awarded', 'Due Lvl ?' & 'Awarded Lvl ?'
|
174
|
+
case status[0]
|
175
|
+
when 'S'
|
176
|
+
new_item[badge_identifier] = :started
|
177
|
+
when 'D'
|
178
|
+
new_item[badge_identifier] = :due
|
179
|
+
when 'A'
|
180
|
+
new_item[badge_identifier] = :awarded
|
181
|
+
end
|
182
|
+
end
|
135
183
|
end
|
184
|
+
|
136
185
|
summary.push new_item
|
137
186
|
end
|
138
187
|
|
@@ -151,23 +200,24 @@ module Osm
|
|
151
200
|
Osm::Model.require_ability_to(api, :read, :badge, section, options)
|
152
201
|
section = Osm::Section.get(api, section, options) unless section.is_a?(Osm::Section)
|
153
202
|
term_id = (term.nil? ? Osm::Term.get_current_term_for_section(api, section, options) : term).to_i
|
154
|
-
cache_key = ['badge_data', section.id, term_id,
|
203
|
+
cache_key = ['badge_data', section.id, term_id, id, version]
|
155
204
|
|
156
205
|
if !options[:no_cache] && cache_exist?(api, cache_key)
|
157
206
|
return cache_read(api, cache_key)
|
158
207
|
end
|
159
208
|
|
160
209
|
datas = []
|
161
|
-
data = api.perform_query("
|
210
|
+
data = api.perform_query("ext/badges/records/?action=getBadgeRecords&term_id=#{term_id}§ion=#{section.type}&badge_id=#{id}§ion_id=#{section.id}&badge_version=#{version}")
|
211
|
+
|
162
212
|
data['items'].each do |d|
|
163
213
|
datas.push Osm::Badge::Data.new(
|
164
214
|
:member_id => d['scoutid'],
|
165
215
|
:first_name => d['firstname'],
|
166
216
|
:last_name => d['lastname'],
|
167
|
-
:
|
217
|
+
:due => d['completed'].to_i,
|
168
218
|
:awarded => d['awarded'].to_i,
|
169
219
|
:awarded_date => Osm.parse_date(d['awardeddate']),
|
170
|
-
:requirements => d.
|
220
|
+
:requirements => Hash[d.map{ |k,v| [k.to_i, v] }].except(0),
|
171
221
|
:section_id => section.id,
|
172
222
|
:badge => self,
|
173
223
|
)
|
@@ -177,14 +227,94 @@ module Osm
|
|
177
227
|
return datas
|
178
228
|
end
|
179
229
|
|
180
|
-
|
230
|
+
|
231
|
+
def has_levels?
|
232
|
+
!levels.nil?
|
233
|
+
end
|
234
|
+
|
235
|
+
def module_map
|
236
|
+
@module_map ||= Hash[
|
237
|
+
*completion_criteria[:modules].map{ |m|
|
238
|
+
[m[:module_id], m[:module_letter], m[:module_letter], m[:module_id]]
|
239
|
+
}.flatten
|
240
|
+
].except('z')
|
241
|
+
end
|
242
|
+
|
243
|
+
def needed_per_module
|
244
|
+
@needed_per_module ||= Hash[*completion_criteria[:modules].map{ |m|
|
245
|
+
[m[:module_id], m[:min_required], m[:module_letter], m[:min_required]]
|
246
|
+
}.flatten].except('z')
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
# Compare Badge based on name then id then version (desc)
|
181
251
|
def <=>(another)
|
182
252
|
result = self.name <=> another.try(:name)
|
183
|
-
result = self.
|
253
|
+
result = self.id <=> another.try(:id) if result == 0
|
254
|
+
result = another.try(:version) <=> self.version if result == 0
|
184
255
|
return result
|
185
256
|
end
|
186
257
|
|
187
258
|
|
259
|
+
private
|
260
|
+
# return an array of hashes representing the modules of the badge
|
261
|
+
def self.module_completion_data(api, badge, options={})
|
262
|
+
fetched_this_time = @module_completion_data.nil? # Flag to ensure we only get the data once (at most) per invocation
|
263
|
+
@module_completion_data = get_module_completion_data(api, options) if fetched_this_time
|
264
|
+
|
265
|
+
if @module_completion_data[badge.id].nil? && !fetched_this_time
|
266
|
+
@module_completion_data = fetch_from_osm
|
267
|
+
fetched_this_time = true
|
268
|
+
end
|
269
|
+
data = @module_completion_data[badge.id]
|
270
|
+
raise ArgumentError, "That badge does't exist (bad ID)." if data.nil?
|
271
|
+
|
272
|
+
if data[badge.version].nil? && !fetched_this_time
|
273
|
+
@module_completion_data = fetch_from_osm
|
274
|
+
data = @module_completion_data[badge.id]
|
275
|
+
fetched_this_time = true
|
276
|
+
end
|
277
|
+
data = data[badge.version]
|
278
|
+
raise ArgumentError, "That badge does't exist (bad version)." if data.nil?
|
279
|
+
return data
|
280
|
+
end
|
281
|
+
|
282
|
+
# Return a 2 dimensional hash/array (badge ID, badge version) of hashes representing the modules
|
283
|
+
def self.get_module_completion_data(api, options={})
|
284
|
+
cache_key = ['badge_module_completion_data']
|
285
|
+
if !options[:no_cache] && cache_exist?(api, cache_key)
|
286
|
+
return cache_read(api, cache_key)
|
287
|
+
end
|
288
|
+
|
289
|
+
osm_data = api.perform_query('ext/badges/records/?action=_getModuleDetails')
|
290
|
+
osm_data = (osm_data || {})['items'] || []
|
291
|
+
osm_data.map! do |i|
|
292
|
+
{
|
293
|
+
badge_id: Osm.to_i_or_nil(i['badge_id']),
|
294
|
+
badge_version: Osm.to_i_or_nil(i['badge_version']),
|
295
|
+
module_id: Osm.to_i_or_nil(i['module_id']),
|
296
|
+
module_letter: i['module_letter'],
|
297
|
+
min_required: i['num_required'].to_i,
|
298
|
+
custom_columns: i['custom_columns'].to_i,
|
299
|
+
completed_into_column: i['completed_into_column_id'].to_i.eql?(0) ? nil : i['completed_into_column_id'].to_i,
|
300
|
+
numeric_into_column: i['numeric_into_column_id'].to_i.eql?(0) ? nil : i['numeric_into_column_id'].to_i,
|
301
|
+
add_column_id_to_numeric: i['add_column_id_to_numeric'].to_i.eql?(0) ? nil : i['add_column_id_to_numeric'].to_i,
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
data = {}
|
306
|
+
osm_data.each do |i|
|
307
|
+
id, version = i.values_at(:badge_id, :badge_version)
|
308
|
+
data[id] ||= []
|
309
|
+
data[id][version] ||= []
|
310
|
+
data[id][version].push i
|
311
|
+
end
|
312
|
+
|
313
|
+
cache_write(api, cache_key, data, {expires_in: 864000}) # Expire in 24 hours as this data changes really slowly
|
314
|
+
return data
|
315
|
+
end
|
316
|
+
|
317
|
+
public
|
188
318
|
def self.type
|
189
319
|
nil
|
190
320
|
end
|
@@ -212,22 +342,24 @@ module Osm
|
|
212
342
|
# @!attribute [rw] description
|
213
343
|
# @return [String] a description of the badge
|
214
344
|
# @!attribute [rw] field
|
215
|
-
# @return [
|
345
|
+
# @return [Fixnum] the field for the requirement (passed to OSM)
|
216
346
|
# @!attribute [rw] editable
|
217
347
|
# @return [Boolean]
|
218
348
|
|
219
349
|
attribute :badge, :type => Object
|
220
350
|
attribute :name, :type => String
|
221
351
|
attribute :description, :type => String
|
222
|
-
attribute :
|
352
|
+
attribute :module_letter, :type => String
|
353
|
+
attribute :field, :type => Integer
|
223
354
|
attribute :editable, :type => Boolean
|
224
355
|
|
225
356
|
if ActiveModel::VERSION::MAJOR < 4
|
226
|
-
attr_accessible :name, :description, :field, :editable, :badge
|
357
|
+
attr_accessible :name, :description, :field, :editable, :badge, :module_letter
|
227
358
|
end
|
228
359
|
|
229
360
|
validates_presence_of :name
|
230
361
|
validates_presence_of :description
|
362
|
+
validates_presence_of :module_letter
|
231
363
|
validates_presence_of :field
|
232
364
|
validates_presence_of :badge
|
233
365
|
validates_inclusion_of :editable, :in => [true, false]
|
@@ -244,7 +376,7 @@ module Osm
|
|
244
376
|
end
|
245
377
|
|
246
378
|
def inspect
|
247
|
-
Osm.inspect_instance(self, options={:replace_with => {'badge' => :
|
379
|
+
Osm.inspect_instance(self, options={:replace_with => {'badge' => :identifier}})
|
248
380
|
end
|
249
381
|
|
250
382
|
end # Class Requirement
|
@@ -257,8 +389,8 @@ module Osm
|
|
257
389
|
# @return [Fixnum] the member's first name
|
258
390
|
# @!attribute [rw] last_name
|
259
391
|
# @return [Fixnum] Ithe member's last name
|
260
|
-
# @!attribute [rw]
|
261
|
-
# @return [Fixnum] whether this badge
|
392
|
+
# @!attribute [rw] due
|
393
|
+
# @return [Fixnum] whether this badge is due according to OSM, number indicates stage if appropriate
|
262
394
|
# @!attribute [rw] awarded
|
263
395
|
# @return [Date] the last stage awarded
|
264
396
|
# @!attribute [rw] awarded_date
|
@@ -273,7 +405,7 @@ module Osm
|
|
273
405
|
attribute :member_id, :type => Integer
|
274
406
|
attribute :first_name, :type => String
|
275
407
|
attribute :last_name, :type => String
|
276
|
-
attribute :
|
408
|
+
attribute :due, :type => Integer, :default => 0
|
277
409
|
attribute :awarded, :type => Integer, :default => 0
|
278
410
|
attribute :awarded_date, :type => Date, :default => nil
|
279
411
|
attribute :requirements, :type => Object, :default => DirtyHashy.new
|
@@ -281,23 +413,18 @@ module Osm
|
|
281
413
|
attribute :badge, :type => Object
|
282
414
|
|
283
415
|
if ActiveModel::VERSION::MAJOR < 4
|
284
|
-
attr_accessible :member_id, :first_name, :last_name, :
|
416
|
+
attr_accessible :member_id, :first_name, :last_name, :due, :awarded, :awarded_date, :requirements, :section_id, :badge
|
285
417
|
end
|
286
418
|
|
287
419
|
validates_presence_of :badge
|
288
420
|
validates_presence_of :first_name
|
289
421
|
validates_presence_of :last_name
|
290
|
-
validates_numericality_of :
|
422
|
+
validates_numericality_of :due, :only_integer=>true, :greater_than_or_equal_to=>0
|
291
423
|
validates_numericality_of :awarded, :only_integer=>true, :greater_than_or_equal_to=>0
|
292
424
|
validates_numericality_of :member_id, :only_integer=>true, :greater_than=>0
|
293
425
|
validates_numericality_of :section_id, :only_integer=>true, :greater_than=>0
|
294
|
-
validates :requirements, :hash => {:key_type =>
|
426
|
+
validates :requirements, :hash => {:key_type => Fixnum, :value_type => String}
|
295
427
|
|
296
|
-
STAGES = {
|
297
|
-
'nightsaway' => [1, 2, 3, 4, 5, 10, 15, 20, 35, 50, 75, 100, 125, 150, 175, 200],
|
298
|
-
'hikes' => [1, 2, 5, 10, 15, 20, 35, 50],
|
299
|
-
'timeonthewater' => [1, 2, 5, 10, 15, 20, 35, 50],
|
300
|
-
}
|
301
428
|
|
302
429
|
# @!method initialize
|
303
430
|
# Initialize a new Badge
|
@@ -316,169 +443,180 @@ module Osm
|
|
316
443
|
# @return [Fixnum] the total number of requirements considered gained
|
317
444
|
def total_gained
|
318
445
|
count = 0
|
319
|
-
requirements.each do |
|
320
|
-
next unless
|
446
|
+
requirements.each do |field_id, data|
|
447
|
+
next unless requirement_met?(data)
|
321
448
|
count += 1
|
322
449
|
end
|
323
450
|
return count
|
324
451
|
end
|
325
452
|
|
326
|
-
# Get the
|
327
|
-
# @return [
|
328
|
-
def
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
next if gained
|
335
|
-
|
453
|
+
# Get the number of modules gained
|
454
|
+
# @return [Fixnum]
|
455
|
+
def modules_gained
|
456
|
+
needed = badge.needed_per_module
|
457
|
+
modules = []
|
458
|
+
|
459
|
+
gained_in_modules.each do |module_id, gained|
|
460
|
+
next unless module_id.is_a?(Fixnum)
|
461
|
+
next if gained < needed[module_id]
|
462
|
+
module_letter = badge.module_map[module_id]
|
463
|
+
modules.push module_letter unless module_letter >= 'y'
|
336
464
|
end
|
337
|
-
|
465
|
+
|
466
|
+
return modules
|
338
467
|
end
|
339
468
|
|
340
|
-
# Get the number of requirements gained in each
|
469
|
+
# Get the number of requirements gained in each module
|
341
470
|
# @return [Hash]
|
342
|
-
def
|
471
|
+
def gained_in_modules
|
343
472
|
count = {}
|
344
|
-
requirements.each do |
|
345
|
-
|
346
|
-
unless
|
347
|
-
|
348
|
-
next unless reguiremet_met?(data)
|
349
|
-
count[field] += 1
|
350
|
-
else
|
351
|
-
# A total 'section'
|
352
|
-
count['y'] = data.to_i
|
353
|
-
end
|
473
|
+
badge.requirements.each do |requirement|
|
474
|
+
count[requirement.module_letter] ||= 0
|
475
|
+
next unless requirement_met?(requirements[requirement.field])
|
476
|
+
count[requirement.module_letter] += 1
|
354
477
|
end
|
355
|
-
|
356
|
-
end
|
357
|
-
|
358
|
-
# Check if this badge is due (according data retrieved from OSM)
|
359
|
-
# @return [Boolean] whether the badge is due to the member
|
360
|
-
def due?
|
361
|
-
completed > awarded
|
478
|
+
Hash[*count.map{ |k,v| [badge.module_map[k], v, k, v] }.flatten]
|
362
479
|
end
|
363
480
|
|
364
481
|
|
365
482
|
# Check if this badge has been earnt
|
366
|
-
# @return [Boolean] whether the badge
|
483
|
+
# @return [Boolean] whether the badge has been earnt (ignores other badge's and their requirements which might be needed)
|
367
484
|
def earnt?
|
368
|
-
if badge.
|
369
|
-
return
|
370
|
-
end
|
371
|
-
return false if (completed.eql?(1) && awarded.eql?(1))
|
372
|
-
return true if (completed.eql?(1) && awarded.eql?(0))
|
373
|
-
if badge.sections_needed == -1 # require all sections
|
374
|
-
return (sections_gained == badge.needed_from_section.keys.size)
|
485
|
+
if badge.has_levels?
|
486
|
+
return earnt > awarded
|
375
487
|
else
|
376
|
-
return
|
488
|
+
return false if (due.eql?(1) && awarded.eql?(1))
|
489
|
+
return true if (due.eql?(1) && awarded.eql?(0))
|
490
|
+
|
491
|
+
criteria = badge.completion_criteria
|
492
|
+
earnt = true
|
493
|
+
if criteria[:min_modules_required] > 0
|
494
|
+
earnt &= (modules_gained.size >= criteria[:min_modules_required])
|
495
|
+
end
|
496
|
+
if criteria[:min_requirements_completed] > 0
|
497
|
+
earnt &= (total_gained >= criteria[:min_requirements_completed])
|
498
|
+
end
|
499
|
+
if criteria[:requires]
|
500
|
+
# [['a'], ['b', 'c']] = a and (b or c)
|
501
|
+
requires = criteria[:requires].clone
|
502
|
+
modules = modules_gained
|
503
|
+
requires.map!{ |a| a.map{ |b| modules.include?(b) } } # Replace letters with true/false
|
504
|
+
requires.map!{ |a| a.include?(true) } # Replace each combination with true/false
|
505
|
+
earnt &= !requires.include?(false) # Only earnt if all combinations are met
|
506
|
+
end
|
507
|
+
criteria[:badges_required].each do |b|
|
508
|
+
# {:id => ###, :version => #}
|
509
|
+
#TODO
|
510
|
+
end
|
511
|
+
criteria[:fields_required].each do |c|
|
512
|
+
# {:id => ###, :min => #}
|
513
|
+
#TODO
|
514
|
+
end
|
515
|
+
return earnt
|
377
516
|
end
|
378
517
|
end
|
379
518
|
|
519
|
+
|
380
520
|
# Get what stage which has most recently been earnt
|
381
521
|
# (using #earnt? will tell you if it's still due (not yet awarded))
|
382
522
|
# @return [Fixnum] the stage which has most recently been due
|
383
523
|
def earnt
|
384
|
-
unless badge.
|
524
|
+
unless badge.has_levels?
|
385
525
|
return earnt? ? 1 : 0
|
386
526
|
end
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
return
|
527
|
+
|
528
|
+
levels_column = badge.completion_criteria[:levels_column_id]
|
529
|
+
unless badge.completion_criteria[:show_letters] # It's a hikes, nights type badge
|
530
|
+
badge.levels.reverse_each do |level|
|
531
|
+
return level if requirements[levels_column].to_i >= level
|
392
532
|
end
|
393
|
-
else
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
end
|
533
|
+
else # It's an activity type badge
|
534
|
+
modules = modules_gained
|
535
|
+
letters = ('a'..'z').to_a
|
536
|
+
(awarded..badge.levels.last).reverse_each do |level|
|
537
|
+
return level if modules.include?(letters[level - 1])
|
399
538
|
end
|
400
539
|
end
|
401
540
|
return 0
|
402
541
|
end
|
403
542
|
|
543
|
+
|
404
544
|
# Check if this badge has been started
|
405
545
|
# @return [Boolean] whether the badge has been started by the member (always false if the badge has been completed)
|
406
546
|
def started?
|
407
|
-
|
408
|
-
|
547
|
+
if badge.has_levels?
|
548
|
+
return (started > due)
|
549
|
+
end
|
550
|
+
return false if due?
|
409
551
|
requirements.each do |key, value|
|
410
|
-
|
411
|
-
when 'a'
|
412
|
-
return true if reguiremet_met?(value)
|
413
|
-
when 'y'
|
414
|
-
return true if (value.to_i > 0)
|
415
|
-
end
|
552
|
+
return true if requirement_met?(value)
|
416
553
|
end
|
417
554
|
return false
|
418
555
|
end
|
419
556
|
|
557
|
+
|
420
558
|
# Get which stage has been started
|
421
559
|
# @return [Fixnum] which stage of the badge has been started by the member (lowest)
|
422
560
|
def started
|
423
|
-
unless badge.
|
561
|
+
unless badge.has_levels?
|
424
562
|
return started? ? 1 : 0
|
563
|
+
end
|
564
|
+
unless badge.completion_criteria[:show_letters]
|
565
|
+
# Nights, Hikes or Water
|
566
|
+
done = requirements[badge.completion_criteria[:levels_column_id]].to_i
|
567
|
+
levels = badge.levels # e.g. [0,1,2,3,4,5,10]
|
568
|
+
return 0 if levels.include?(done) # Has achieved a level (and not started next )
|
569
|
+
return 0 if done >= levels[-1] # No more levels to do
|
570
|
+
(1..(levels.size-1)).to_a.reverse_each do |i| # indexes from last to 2nd
|
571
|
+
this_level = levels[i]
|
572
|
+
previous_level = levels[i-1]
|
573
|
+
return this_level if (done < this_level && done > previous_level) # this_level has been started (and not finished)
|
574
|
+
end
|
575
|
+
return 0 # No reason we should ever get here
|
425
576
|
else
|
426
|
-
#
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
return stages[index]
|
435
|
-
end
|
436
|
-
end
|
437
|
-
else
|
438
|
-
# 'Normal' staged badge
|
439
|
-
return 0 if completed == 5 || awarded == 5 # No more stages can be started
|
440
|
-
start_group = 'abcde'[completed] # Requirements use the group letter to denote stage
|
441
|
-
started = 'z'
|
442
|
-
requirements.each do |key, value|
|
443
|
-
next if key[0] < start_group # This stage is marked as completed
|
444
|
-
next if key[0] > started # This stage is after the stage currently started
|
445
|
-
started = key[0] unless value.blank? || value.to_s[0].downcase.eql?('x')
|
577
|
+
# 'Normal' staged
|
578
|
+
letters = ('a'..'z').to_a
|
579
|
+
top_level = badge.levels[-1]
|
580
|
+
return 0 if due == top_level || awarded == top_level # No more levels to do
|
581
|
+
((due + 1)..top_level).reverse_each do |level|
|
582
|
+
badge.requirements.each do |requirement|
|
583
|
+
next unless requirement.module_letter.eql?(letters[level - 1]) # Not interested in other levels
|
584
|
+
return level if requirement_met?(requirements[requirement.field])
|
446
585
|
end
|
447
|
-
return started.eql?('z') ? 0 : 'abcde'.index(started)+1
|
448
586
|
end
|
449
|
-
return 0
|
587
|
+
return 0 # No levels started
|
450
588
|
end
|
451
589
|
end
|
452
590
|
|
591
|
+
|
453
592
|
# Mark the badge as awarded in OSM
|
454
593
|
# @param [Osm::Api] api The api to use to make the request
|
455
594
|
# @param [Date] date The date to mark the badge as awarded
|
456
|
-
# @param [Fixnum] level The level of the badge to award (1 for non-staged badges)
|
457
|
-
# @param [Symbol] mark_as :awarded or :due
|
595
|
+
# @param [Fixnum] level The level of the badge to award (1 for non-staged badges), setting the level to 0 unawards the badge
|
458
596
|
# @return [Boolean] whether the data was updated in OSM
|
459
|
-
def mark_awarded(api, date=Date.today, level=
|
597
|
+
def mark_awarded(api, date=Date.today, level=due)
|
460
598
|
raise ArgumentError, 'date is not a Date' unless date.is_a?(Date)
|
461
|
-
raise ArgumentError, 'mark_as is not an allowed value, use :awarded or :du' unless [:awarded, :due].include?(mark_as)
|
462
599
|
raise ArgumentError, 'level can not be negative' if level < 0
|
463
600
|
section = Osm::Section.get(api, section_id)
|
464
601
|
require_ability_to(api, :write, :badge, section)
|
465
602
|
|
466
603
|
date_formatted = date.strftime(Osm::OSM_DATE_FORMAT)
|
467
|
-
|
468
|
-
|
469
|
-
'
|
604
|
+
entries = [{
|
605
|
+
'badge_id' => badge.id.to_s,
|
606
|
+
'badge_version' => badge.version.to_s,
|
607
|
+
'scout_id' => member_id.to_s,
|
608
|
+
'level' => level.to_s
|
609
|
+
}]
|
610
|
+
|
611
|
+
result = api.perform_query("ext/badges/records/?action=awardBadge", {
|
612
|
+
'date' => date_formatted,
|
470
613
|
'sectionid' => section_id,
|
471
|
-
'
|
472
|
-
'chal' => badge.osm_key,
|
473
|
-
'type' => badge.type,
|
474
|
-
'stagedLevel' => level,
|
475
|
-
'due' => mark_as,
|
614
|
+
'entries' => entries.to_json
|
476
615
|
})
|
477
|
-
updated = result.is_a?(
|
478
|
-
result[
|
479
|
-
(result[
|
480
|
-
(result[
|
481
|
-
(result[0]['awardeddate'] == date_formatted)
|
616
|
+
updated = result.is_a?(Hash) &&
|
617
|
+
(result['scoutid'].to_i == member_id) &&
|
618
|
+
(result['awarded'].to_i == level) &&
|
619
|
+
(result['awardeddate'] == date_formatted)
|
482
620
|
|
483
621
|
if updated
|
484
622
|
awarded = level
|
@@ -487,12 +625,41 @@ module Osm
|
|
487
625
|
return updated
|
488
626
|
end
|
489
627
|
|
628
|
+
# Mark the badge as not awarded in OSM
|
629
|
+
# @param [Osm::Api] api The api to use to make the request
|
630
|
+
# @return [Boolean] whether the data was updated in OSM
|
631
|
+
def mark_not_awarded(api)
|
632
|
+
mark_awarded(api, Date.today, 0)
|
633
|
+
end
|
634
|
+
|
635
|
+
|
490
636
|
# Mark the badge as due in OSM
|
491
637
|
# @param [Osm::Api] api The api to use to make the request
|
492
|
-
# @param [Fixnum] level The level of the badge to
|
638
|
+
# @param [Fixnum] level The level of the badge to award (1 for non-staged badges), setting the level to 0 unawards the badge
|
639
|
+
# @return [Boolean] whether the data was updated in OSM
|
640
|
+
def mark_due(api, level=completed)
|
641
|
+
raise ArgumentError, 'level can not be negative' if level < 0
|
642
|
+
section = Osm::Section.get(api, section_id)
|
643
|
+
require_ability_to(api, :write, :badge, section)
|
644
|
+
|
645
|
+
result = api.perform_query("ext/badges/records/?action=overrideCompletion", {
|
646
|
+
'section_id' => section.id,
|
647
|
+
'badge_id' => badge.id,
|
648
|
+
'badge_version' => badge.version,
|
649
|
+
'scoutid' => member_id,
|
650
|
+
'level' => level
|
651
|
+
})
|
652
|
+
updated = result.is_a?(Hash) &&
|
653
|
+
(result['scoutid'].to_i == member_id) &&
|
654
|
+
(result['completed'].to_i == level)
|
655
|
+
return updated
|
656
|
+
end
|
657
|
+
|
658
|
+
# Mark the badge as not due in OSM
|
659
|
+
# @param [Osm::Api] api The api to use to make the request
|
493
660
|
# @return [Boolean] whether the data was updated in OSM
|
494
|
-
def
|
495
|
-
|
661
|
+
def mark_not_due(api)
|
662
|
+
mark_due(api, 0)
|
496
663
|
end
|
497
664
|
|
498
665
|
# Update data in OSM
|
@@ -504,37 +671,47 @@ module Osm
|
|
504
671
|
section = Osm::Section.get(api, section_id)
|
505
672
|
require_ability_to(api, :write, :badge, section)
|
506
673
|
|
507
|
-
|
508
|
-
|
674
|
+
# Update requirements that changed
|
675
|
+
requirements_updated = true
|
676
|
+
editable_fields = badge.requirements.select{ |r| r.editable }.map{ |r| r.field }
|
509
677
|
requirements.changes.each do |field, (was,now)|
|
510
678
|
if editable_fields.include?(field)
|
511
|
-
result = api.perform_query("
|
512
|
-
'
|
513
|
-
'
|
514
|
-
'
|
515
|
-
'
|
516
|
-
'
|
517
|
-
'
|
679
|
+
result = api.perform_query("ext/badges/records/?action=updateSingleRecord", {
|
680
|
+
'scoutid' => member_id,
|
681
|
+
'section_id' => section_id,
|
682
|
+
'badge_id' => badge.id,
|
683
|
+
'badge_version' => badge.version,
|
684
|
+
'field' => field,
|
685
|
+
'value' => now
|
518
686
|
})
|
519
|
-
|
520
|
-
(result['
|
521
|
-
(result[field] == now)
|
687
|
+
requirements_updated = false unless result.is_a?(Hash) &&
|
688
|
+
(result['scoutid'].to_i == member_id) &&
|
689
|
+
(result[field.to_s] == now)
|
522
690
|
end
|
523
691
|
end
|
524
692
|
|
525
|
-
if
|
693
|
+
if requirements_updated
|
526
694
|
requirements.clean_up!
|
527
695
|
end
|
528
696
|
|
697
|
+
# Update due if it changed
|
698
|
+
due_updated = true
|
699
|
+
if changed_attributes.include?('due')
|
700
|
+
due_updated = mark_due(api, due)
|
701
|
+
end
|
702
|
+
|
703
|
+
# Update awarded if it changed
|
704
|
+
awarded_updated = true
|
529
705
|
if changed_attributes.include?('awarded') || changed_attributes.include?('awarded_date')
|
530
|
-
|
531
|
-
reset_changed_attributes
|
532
|
-
else
|
533
|
-
updated = false
|
534
|
-
end
|
706
|
+
awarded_updated = mark_awarded(api, awarded_date, awarded)
|
535
707
|
end
|
536
708
|
|
537
|
-
|
709
|
+
# reset changed attributes if everything was updated ok
|
710
|
+
if due_updated && awarded_updated
|
711
|
+
reset_changed_attributes
|
712
|
+
end
|
713
|
+
|
714
|
+
return requirements_updated && due_updated && awarded_updated
|
538
715
|
end
|
539
716
|
|
540
717
|
# Compare Badge::Data based on badge, section_id then member_id
|
@@ -546,11 +723,11 @@ module Osm
|
|
546
723
|
end
|
547
724
|
|
548
725
|
def inspect
|
549
|
-
Osm.inspect_instance(self, options={:replace_with => {'badge' => :
|
726
|
+
Osm.inspect_instance(self, options={:replace_with => {'badge' => :name}})
|
550
727
|
end
|
551
728
|
|
552
729
|
private
|
553
|
-
def
|
730
|
+
def requirement_met?(data)
|
554
731
|
return false if data == 0
|
555
732
|
!(data.blank? || data.to_s[0].downcase.eql?('x'))
|
556
733
|
end
|
@@ -565,6 +742,9 @@ module Osm
|
|
565
742
|
def self.type
|
566
743
|
:core
|
567
744
|
end
|
745
|
+
def self.type_id
|
746
|
+
4
|
747
|
+
end
|
568
748
|
end # Class CoreBadge
|
569
749
|
|
570
750
|
class ChallengeBadge < Osm::Badge
|
@@ -572,6 +752,9 @@ module Osm
|
|
572
752
|
def self.type
|
573
753
|
:challenge
|
574
754
|
end
|
755
|
+
def self.type_id
|
756
|
+
1
|
757
|
+
end
|
575
758
|
end # Class ChallengeBadge
|
576
759
|
|
577
760
|
class StagedBadge < Osm::Badge
|
@@ -579,6 +762,9 @@ module Osm
|
|
579
762
|
def self.type
|
580
763
|
:staged
|
581
764
|
end
|
765
|
+
def self.type_id
|
766
|
+
3
|
767
|
+
end
|
582
768
|
end # Class StagedBadge
|
583
769
|
|
584
770
|
class ActivityBadge < Osm::Badge
|
@@ -586,6 +772,9 @@ module Osm
|
|
586
772
|
def self.type
|
587
773
|
:activity
|
588
774
|
end
|
775
|
+
def self.type_id
|
776
|
+
2
|
777
|
+
end
|
589
778
|
def self.subscription_required
|
590
779
|
:silver
|
591
780
|
end
|