osm 1.2.14 → 1.2.15.dev
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.
- 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
|