spree_cm_commissioner 2.8.3.pre.pre4 → 2.8.3.pre.pre5
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 +4 -4
- data/.github/workflows/test_and_build_gem.yml +2 -1
- data/.gitignore +2 -0
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/api/v2/tenant/episodes_controller.rb +38 -0
- data/app/controllers/spree/api/v2/tenant/shows_controller.rb +3 -1
- data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +4 -1
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +3 -1
- data/app/models/spree_cm_commissioner/voting_contestant.rb +28 -0
- data/app/models/spree_cm_commissioner/voting_session.rb +1 -1
- data/app/serializers/spree/v2/tenant/campaign_serializer.rb +4 -0
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +2 -0
- data/app/serializers/spree/v2/tenant/show_serializer.rb +2 -0
- data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +4 -3
- data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +3 -1
- data/app/services/spree_cm_commissioner/advertisements/sorted_advertisements.rb +61 -0
- data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +5 -4
- data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +11 -3
- data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +33 -3
- data/config/locales/en.yml +5 -0
- data/config/locales/km.yml +10 -0
- data/config/routes.rb +1 -0
- data/db/migrate/20260608000000_add_display_to_cm_voting_sessions.rb +5 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +5 -3
- data/app/services/spree_cm_commissioner/advertisements/weight_validator.rb +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 145ade12d325f15a3f1963b4fd75e56093cbb182e1f3ac115d0101c0c6cad285
|
|
4
|
+
data.tar.gz: e47239cbfa8a359ce7e0b0ed2f73e5270876f679d3047a2d88be89493683004a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '06134389566fe49ae2dfe58a65919130cc57dcf43694d33af480d2930d517cfbba1ba97c7714f0b011cdb063a9a7d1611dc205fdcce1cf7f77fd4618c6d9ffd9'
|
|
7
|
+
data.tar.gz: 60d6491f501f2baa87fb971bee6a906f3180425d9f72917462adc2d266c0f3302a44bf532c39fe9885b3b965d7c94fdae9b158d0c7bc9263879b2d2e43197e23
|
|
@@ -164,7 +164,8 @@ jobs:
|
|
|
164
164
|
env:
|
|
165
165
|
DATABASE_URL: postgres://myuser:mypassword@localhost:5432/test_db
|
|
166
166
|
run: |
|
|
167
|
-
|
|
167
|
+
# Using --no-threads to bypass a known Ruby 3.2.0 GC segmentation fault in CI.
|
|
168
|
+
bundle exec brakeman --no-threads --no-exit-on-warn
|
|
168
169
|
|
|
169
170
|
test:
|
|
170
171
|
needs: [setup]
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class EpisodesController < BaseController
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def model_class
|
|
9
|
+
SpreeCmCommissioner::ShowEpisode
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def resource
|
|
13
|
+
@resource ||= scope.find(params[:id])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def scope
|
|
17
|
+
current_season.episodes.includes(:voting_sessions)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def current_season
|
|
21
|
+
@current_season ||= SpreeCmCommissioner::Show
|
|
22
|
+
.where(vendor_id: @tenant.vendors.select(:id))
|
|
23
|
+
.where.not(parent_id: nil)
|
|
24
|
+
.find_by!(slug: params[:show_id])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def resource_serializer
|
|
28
|
+
Spree::V2::Tenant::ShowEpisodeSerializer
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def collection_serializer
|
|
32
|
+
Spree::V2::Tenant::ShowEpisodeSerializer
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -20,7 +20,9 @@ module Spree
|
|
|
20
20
|
return model_class.none if vendor_ids.blank?
|
|
21
21
|
|
|
22
22
|
model_class.where(vendor_id: vendor_ids, preview: false)
|
|
23
|
-
.includes(:parent, :app_banner,
|
|
23
|
+
.includes(:parent, :app_banner, show_people: :show_person_images,
|
|
24
|
+
episodes: :voting_sessions, seasons: { episodes: :voting_sessions }
|
|
25
|
+
)
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# override
|
|
@@ -9,7 +9,10 @@ module Spree
|
|
|
9
9
|
def collection
|
|
10
10
|
@collection ||= @voting_session
|
|
11
11
|
.voting_contestants
|
|
12
|
-
.includes(
|
|
12
|
+
.includes(:voting_session, :show_contestant,
|
|
13
|
+
:show_contestant_images, :show_contestant_videos,
|
|
14
|
+
:advanced_to, :advanced_from
|
|
15
|
+
)
|
|
13
16
|
.order(:id)
|
|
14
17
|
end
|
|
15
18
|
|
|
@@ -86,7 +86,9 @@ module SpreeCmCommissioner
|
|
|
86
86
|
base.scope :events, -> { where(taxonomy_id: Spree::Taxonomy.events.id, depth: 1) }
|
|
87
87
|
base.scope :campaigns, -> { where(taxonomy_id: Spree::Taxonomy.ads.id, depth: 1) }
|
|
88
88
|
|
|
89
|
-
base.has_many :advertisements,
|
|
89
|
+
base.has_many :advertisements, lambda {
|
|
90
|
+
advertisement.where(deleted_at: nil)
|
|
91
|
+
}, through: :classifications, class_name: 'Spree::Product', source: :product
|
|
90
92
|
base.has_many :agencies, class_name: 'SpreeCmCommissioner::Agency', foreign_key: :agency_category_id
|
|
91
93
|
base.has_many :preview_roles, class_name: 'SpreeCmCommissioner::PreviewRole', as: :previewable
|
|
92
94
|
|
|
@@ -19,6 +19,8 @@ module SpreeCmCommissioner
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
before_destroy :prevent_if_last_contestant_in_parent_session
|
|
22
|
+
before_destroy :prevent_if_advanced_from_session
|
|
23
|
+
before_destroy :prevent_if_pre_vote_session
|
|
22
24
|
|
|
23
25
|
validates :eliminated_via, presence: true, if: :eliminated?
|
|
24
26
|
|
|
@@ -45,6 +47,14 @@ module SpreeCmCommissioner
|
|
|
45
47
|
(special_rules || {})['type']
|
|
46
48
|
end
|
|
47
49
|
|
|
50
|
+
def advanced_from_name
|
|
51
|
+
advanced_from&.name
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def advanced_to_name
|
|
55
|
+
advanced_to&.name
|
|
56
|
+
end
|
|
57
|
+
|
|
48
58
|
private
|
|
49
59
|
|
|
50
60
|
# Prevent removing the last contestant from a session that is referenced as a parent
|
|
@@ -57,5 +67,23 @@ module SpreeCmCommissioner
|
|
|
57
67
|
errors.add(:base, :last_contestant_in_parent_session, session_name: voting_session.name)
|
|
58
68
|
throw :abort
|
|
59
69
|
end
|
|
70
|
+
|
|
71
|
+
def prevent_if_pre_vote_session
|
|
72
|
+
return unless voting_session.pre_vote?
|
|
73
|
+
|
|
74
|
+
errors.add(:base, :pre_vote_session)
|
|
75
|
+
throw :abort
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Prevent direct destroy when this record was advanced here from a previous session.
|
|
79
|
+
# Destroying it directly would leave the source session's advanced_to pointer dangling.
|
|
80
|
+
# The correct removal path is to clear advanced_to on the source VotingContestant,
|
|
81
|
+
# which triggers the Advancer service to clean up this record atomically.
|
|
82
|
+
def prevent_if_advanced_from_session
|
|
83
|
+
return if advanced_from_id.blank?
|
|
84
|
+
|
|
85
|
+
errors.add(:base, :advanced_from_session)
|
|
86
|
+
throw :abort
|
|
87
|
+
end
|
|
60
88
|
end
|
|
61
89
|
end
|
|
@@ -119,7 +119,7 @@ module SpreeCmCommissioner
|
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def can_vote?
|
|
122
|
-
enabled? && Time.zone.now.between?(opens_at, closes_at)
|
|
122
|
+
enabled? && Time.zone.now.between?(opens_at, closes_at) && !manual_advance?
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
# Returns the next advanceable session within the same episode (by position).
|
|
@@ -6,6 +6,10 @@ module Spree
|
|
|
6
6
|
|
|
7
7
|
attributes :name, :from_date, :to_date
|
|
8
8
|
|
|
9
|
+
attribute :sorted_ads_ids do |campaign|
|
|
10
|
+
SpreeCmCommissioner::Advertisements::SortedAdvertisements.call(campaign: campaign).value
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
has_many :advertisements, serializer: Spree::V2::Tenant::AdvertisementSerializer
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -9,6 +9,8 @@ module Spree
|
|
|
9
9
|
|
|
10
10
|
attribute :name, &:display_name
|
|
11
11
|
|
|
12
|
+
has_many :show_people, serializer: Spree::V2::Tenant::ShowPersonSerializer
|
|
13
|
+
has_many :episodes, serializer: Spree::V2::Tenant::ShowEpisodeSerializer
|
|
12
14
|
has_many :voting_sessions, serializer: Spree::V2::Tenant::VotingSessionSerializer
|
|
13
15
|
has_one :current_episode, serializer: Spree::V2::Tenant::ShowEpisodeSerializer
|
|
14
16
|
has_one :current_voting_session, serializer: Spree::V2::Tenant::VotingSessionSerializer
|
|
@@ -4,9 +4,9 @@ module Spree
|
|
|
4
4
|
class VotingContestantSerializer < BaseSerializer
|
|
5
5
|
set_type :voting_contestant
|
|
6
6
|
|
|
7
|
-
attributes :
|
|
8
|
-
:
|
|
9
|
-
:created_at, :updated_at, :
|
|
7
|
+
attributes :show_contestant_id, :name, :contestant_number, :vote_number, :category, :gender,
|
|
8
|
+
:eliminated, :eliminated_via, :vote_count, :unique_voter_count, :special_rules,
|
|
9
|
+
:created_at, :updated_at, :advanced_from_name, :advanced_to_name, :confirmed_rank
|
|
10
10
|
|
|
11
11
|
attribute :voting_session_id, &:voting_session_id
|
|
12
12
|
|
|
@@ -16,6 +16,7 @@ module Spree
|
|
|
16
16
|
|
|
17
17
|
has_many :show_contestant_images, serializer: ::Spree::V2::Tenant::AssetSerializer
|
|
18
18
|
has_many :show_contestant_videos, serializer: ::Spree::V2::Tenant::VideoSerializer
|
|
19
|
+
belongs_to :voting_session, serializer: ::Spree::V2::Tenant::VotingSessionSerializer
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
@@ -4,7 +4,7 @@ module Spree
|
|
|
4
4
|
class VotingSessionSerializer < BaseSerializer
|
|
5
5
|
attributes :opens_at, :name, :closes_at, :status, :session_type, :live_stream_enabled,
|
|
6
6
|
:live_stream_thumbnail, :live_stream_title, :live_stream_description,
|
|
7
|
-
:position, :fraud_config
|
|
7
|
+
:position, :fraud_config, :display, :public_metadata
|
|
8
8
|
|
|
9
9
|
attribute :live_stream_url, &:embedded_live_stream_url
|
|
10
10
|
attribute :can_vote, &:can_vote?
|
|
@@ -12,6 +12,8 @@ module Spree
|
|
|
12
12
|
belongs_to :episode, serializer: Spree::V2::Tenant::ShowEpisodeSerializer
|
|
13
13
|
belongs_to :show, serializer: Spree::V2::Tenant::ShowSerializer
|
|
14
14
|
|
|
15
|
+
has_many :voting_contestants, serializer: ::Spree::V2::Tenant::VotingContestantSerializer
|
|
16
|
+
|
|
15
17
|
has_many :eliminated_voting_contestants, serializer: ::Spree::V2::Tenant::VotingContestantSerializer
|
|
16
18
|
end
|
|
17
19
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Builds a weighted round-robin slot sequence for one ad rotation cycle.
|
|
2
|
+
#
|
|
3
|
+
# Ads are interleaved so the same ad never repeats until all others have appeared.
|
|
4
|
+
# Each round emits one slot per ad in descending weight order. Higher weight =
|
|
5
|
+
# appears first in every round + appears in more rounds = displayed more often.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# result = SpreeCmCommissioner::Advertisements::SortedAdvertisements.call(campaign: campaign)
|
|
9
|
+
#
|
|
10
|
+
# result.success? # => true
|
|
11
|
+
# result.value # => [469, 470, 468, 469, 470, 468, 469, 470, ...]
|
|
12
|
+
#
|
|
13
|
+
# Example: Bachuss weight=5, Feranado weight=5, Ganzberg weight=4 (14 slots total)
|
|
14
|
+
# Round 1: Bachuss, Feranado, Ganzberg
|
|
15
|
+
# Round 2: Bachuss, Feranado, Ganzberg
|
|
16
|
+
# Round 3: Bachuss, Feranado, Ganzberg
|
|
17
|
+
# Round 4: Bachuss, Feranado, Ganzberg
|
|
18
|
+
# Round 5: Bachuss, Feranado ← Ganzberg exhausted
|
|
19
|
+
#
|
|
20
|
+
# Bachuss → 5/14 ≈ 36% of opens
|
|
21
|
+
# Feranado → 5/14 ≈ 36% of opens
|
|
22
|
+
# Ganzberg → 4/14 ≈ 28% of opens
|
|
23
|
+
module SpreeCmCommissioner
|
|
24
|
+
module Advertisements
|
|
25
|
+
class SortedAdvertisements
|
|
26
|
+
prepend ::Spree::ServiceModule::Base
|
|
27
|
+
|
|
28
|
+
def call(campaign:)
|
|
29
|
+
return success([]) unless campaign
|
|
30
|
+
|
|
31
|
+
ads = campaign.advertisements.to_a
|
|
32
|
+
return success([]) if ads.empty?
|
|
33
|
+
|
|
34
|
+
success(weighted_round_robin(ads))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Interleaves ads so no ID repeats until all others have appeared.
|
|
40
|
+
# Each round emits one slot per ad (highest weight first); ads with
|
|
41
|
+
# remaining quota continue into the next round until all slots are filled.
|
|
42
|
+
def weighted_round_robin(ads)
|
|
43
|
+
pool = ads
|
|
44
|
+
.map { |ad| { id: ad.id, remaining: ad.advertise_weight.to_i } }
|
|
45
|
+
.sort_by { |entry| -entry[:remaining] }
|
|
46
|
+
|
|
47
|
+
slots = []
|
|
48
|
+
until pool.all? { |entry| entry[:remaining].zero? }
|
|
49
|
+
pool.each do |entry|
|
|
50
|
+
next if entry[:remaining].zero?
|
|
51
|
+
|
|
52
|
+
slots << entry[:id]
|
|
53
|
+
entry[:remaining] -= 1
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
slots
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -190,8 +190,9 @@ module SpreeCmCommissioner
|
|
|
190
190
|
# e.g. vc_b.vote_count = 5 → return false
|
|
191
191
|
return false if linked.vote_count.to_i.positive?
|
|
192
192
|
|
|
193
|
-
#
|
|
194
|
-
#
|
|
193
|
+
# Clear back-reference before destroy so prevent_if_advanced_from_session doesn't abort.
|
|
194
|
+
# The Advancer manages both sides atomically, so the source pointer is cleaned up by the caller.
|
|
195
|
+
linked.update!(advanced_from_type: nil, advanced_from_id: nil)
|
|
195
196
|
linked.destroy!
|
|
196
197
|
true
|
|
197
198
|
end
|
|
@@ -235,8 +236,8 @@ module SpreeCmCommissioner
|
|
|
235
236
|
end
|
|
236
237
|
end
|
|
237
238
|
|
|
238
|
-
#
|
|
239
|
-
|
|
239
|
+
# Clear back-reference before destroy so prevent_if_advanced_from_session doesn't abort.
|
|
240
|
+
linked_row.update!(advanced_from_type: nil, advanced_from_id: nil)
|
|
240
241
|
linked_row.destroy!
|
|
241
242
|
end
|
|
242
243
|
|
|
@@ -21,9 +21,17 @@ module SpreeCmCommissioner
|
|
|
21
21
|
target_ids = @show_contestant_ids.uniq
|
|
22
22
|
return if target_ids.empty?
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
@
|
|
26
|
-
|
|
24
|
+
ApplicationRecord.transaction do
|
|
25
|
+
@episode.season.show_contestants.where(id: target_ids).find_each do |show_contestant|
|
|
26
|
+
if @voting_session.save_vote? && !show_contestant.eliminated?
|
|
27
|
+
raise I18n.t('spree_cm_commissioner.voting_contestants.assigner.not_eliminated',
|
|
28
|
+
name: show_contestant.name, status: show_contestant.status
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
@voting_session.voting_contestants.find_or_create_by!(show_contestant: show_contestant) do |voting_contestant|
|
|
33
|
+
voting_contestant.show_id = @voting_session.show_id
|
|
34
|
+
end
|
|
27
35
|
end
|
|
28
36
|
end
|
|
29
37
|
end
|
|
@@ -3,10 +3,18 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class Finalize
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
|
-
def call(voting_session:)
|
|
6
|
+
def call(voting_session:, save_advance_to_session_id: nil)
|
|
7
|
+
@save_advance_to_session_id = save_advance_to_session_id
|
|
8
|
+
|
|
7
9
|
ApplicationRecord.transaction do
|
|
8
10
|
validate_confirmed_ranks!(voting_session)
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
if voting_session.save_vote?
|
|
13
|
+
restore_and_advance_saved_contestants(voting_session)
|
|
14
|
+
elsif next_session?(voting_session)
|
|
15
|
+
advance_confirmed_winners(voting_session)
|
|
16
|
+
end
|
|
17
|
+
|
|
10
18
|
voting_session.update!(status: :finalized)
|
|
11
19
|
success(voting_session: voting_session)
|
|
12
20
|
end
|
|
@@ -17,7 +25,7 @@ module SpreeCmCommissioner
|
|
|
17
25
|
private
|
|
18
26
|
|
|
19
27
|
def validate_confirmed_ranks!(voting_session)
|
|
20
|
-
if next_session?(voting_session)
|
|
28
|
+
if voting_session.save_vote? || next_session?(voting_session)
|
|
21
29
|
raise I18n.t('voting.errors.no_winners_confirmed') if voting_session.voting_contestants.where.not(confirmed_rank: nil).none?
|
|
22
30
|
else
|
|
23
31
|
unranked = voting_session.voting_contestants.where(eliminated: false, confirmed_rank: nil)
|
|
@@ -25,6 +33,27 @@ module SpreeCmCommissioner
|
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
35
|
|
|
36
|
+
def restore_and_advance_saved_contestants(voting_session)
|
|
37
|
+
target_session = SpreeCmCommissioner::VotingSession.find_by(id: @save_advance_to_session_id)
|
|
38
|
+
raise I18n.t('voting.errors.save_advance_to_session_required') if target_session.nil?
|
|
39
|
+
|
|
40
|
+
voting_session.voting_contestants.each do |vc|
|
|
41
|
+
if vc.confirmed_rank.present?
|
|
42
|
+
vc.show_contestant.update!(status: :active)
|
|
43
|
+
SpreeCmCommissioner::VotingContestants::Advancer.call(
|
|
44
|
+
voting_contestant: vc,
|
|
45
|
+
attributes: { advanced_to: target_session }
|
|
46
|
+
)
|
|
47
|
+
else
|
|
48
|
+
SpreeCmCommissioner::VotingContestants::Advancer.call(
|
|
49
|
+
voting_contestant: vc,
|
|
50
|
+
attributes: { eliminated: true, eliminated_via: :instant_save_lost, advanced_to_type: nil, advanced_to_id: nil }
|
|
51
|
+
)
|
|
52
|
+
# ShowContestant#status already :eliminated — no change needed
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
28
57
|
# Re-syncs advancement on every call — handles first finalize and re-finalize.
|
|
29
58
|
# Contestants without a confirmed_rank get their advanced_to cleared.
|
|
30
59
|
def advance_confirmed_winners(voting_session)
|
|
@@ -48,6 +77,7 @@ module SpreeCmCommissioner
|
|
|
48
77
|
voting_contestant: voting_contestant,
|
|
49
78
|
attributes: { eliminated: true, eliminated_via: :public_vote, advanced_to_type: nil, advanced_to_id: nil }
|
|
50
79
|
)
|
|
80
|
+
voting_contestant.show_contestant.update!(status: :eliminated)
|
|
51
81
|
end
|
|
52
82
|
end
|
|
53
83
|
end
|
data/config/locales/en.yml
CHANGED
|
@@ -557,6 +557,8 @@ en:
|
|
|
557
557
|
attributes:
|
|
558
558
|
base:
|
|
559
559
|
last_contestant_in_parent_session: 'cannot remove the last contestant from "%{session_name}" because child sessions depend on it'
|
|
560
|
+
advanced_from_session: 'was advanced from a previous session and cannot be removed directly. Clear the advancement in the source session instead.'
|
|
561
|
+
pre_vote_session: 'cannot be removed from a pre-vote session.'
|
|
560
562
|
spree_cm_commissioner/tenant:
|
|
561
563
|
attributes:
|
|
562
564
|
host:
|
|
@@ -777,6 +779,9 @@ en:
|
|
|
777
779
|
verification_failed: "Human verification failed"
|
|
778
780
|
service_unavailable: "Verification service is temporarily unavailable"
|
|
779
781
|
spree_cm_commissioner:
|
|
782
|
+
voting_contestants:
|
|
783
|
+
assigner:
|
|
784
|
+
not_eliminated: "Only eliminated contestants can be added to a Save Contestants session. %{name} is %{status}."
|
|
780
785
|
voting_leaderboards:
|
|
781
786
|
combined_result:
|
|
782
787
|
pre_vote_not_finalized: "Cannot combine results: Pre-Vote sessions not yet finalized: %{names}"
|
data/config/locales/km.yml
CHANGED
|
@@ -419,6 +419,12 @@ km:
|
|
|
419
419
|
must_not_exceed_parent: "មិនអាចក្រោយ closes_at របស់ session មេ"
|
|
420
420
|
parent_id:
|
|
421
421
|
must_have_contestants: '"%{name}" ត្រូវតែមានអ្នកចូលប្រកួត'
|
|
422
|
+
spree_cm_commissioner/voting_contestant:
|
|
423
|
+
attributes:
|
|
424
|
+
base:
|
|
425
|
+
last_contestant_in_parent_session: 'មិនអាចដកអ្នកចូលប្រកួតចុងក្រោយពី "%{session_name}" ព្រោះ session កូនពឹងផ្អែកលើវា'
|
|
426
|
+
advanced_from_session: 'ត្រូវបានដំឡើងពី session មុន ហើយមិនអាចដកចេញដោយផ្ទាល់បានទេ។ សូមលុបការដំឡើងនៅក្នុង session ប្រភពជំនួសវិញ។'
|
|
427
|
+
pre_vote_session: 'មិនអាចដកចេញពី pre-vote session បានទេ។'
|
|
422
428
|
spree_cm_commissioner/tenant:
|
|
423
429
|
attributes:
|
|
424
430
|
host:
|
|
@@ -612,6 +618,10 @@ km:
|
|
|
612
618
|
token_missing: "សញ្ញាណផ្ទៀងផ្ទាត់គឺបាត់"
|
|
613
619
|
verification_failed: "ការផ្ទៀងផ្ទាត់បានបរាជ័យ"
|
|
614
620
|
service_unavailable: "សេវាកម្មពេលនេះមិនមានការផ្ទៀងផ្ទាត់"
|
|
621
|
+
spree_cm_commissioner:
|
|
622
|
+
voting_contestants:
|
|
623
|
+
assigner:
|
|
624
|
+
not_eliminated: "តែអ្នកចូលប្រកួតដែលត្រូវបានលុបចោលប៉ុណ្ណោះអាចបន្ថែមទៅ Save Contestants session បាន។ %{name} មានស្ថានភាព %{status}។"
|
|
615
625
|
voting:
|
|
616
626
|
errors:
|
|
617
627
|
no_winners_confirmed: "មិនមានអ្នកឈ្នះត្រូវបានបញ្ជាក់"
|
data/config/routes.rb
CHANGED
|
@@ -589,6 +589,7 @@ Spree::Core::Engine.add_routes do
|
|
|
589
589
|
resources :waiting_room_sessions, only: :create
|
|
590
590
|
|
|
591
591
|
resources :shows, only: [:show] do
|
|
592
|
+
resources :episodes, only: %i[index show]
|
|
592
593
|
resources :people, controller: :show_people, only: %i[index] do
|
|
593
594
|
resources :assignments, controller: :show_person_assignments, only: %i[index]
|
|
594
595
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_cm_commissioner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.8.3.pre.
|
|
4
|
+
version: 2.8.3.pre.pre5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: spree
|
|
@@ -1045,6 +1045,7 @@ files:
|
|
|
1045
1045
|
- app/controllers/spree/api/v2/tenant/cms_pages_controller.rb
|
|
1046
1046
|
- app/controllers/spree/api/v2/tenant/customer_notifications_controller.rb
|
|
1047
1047
|
- app/controllers/spree/api/v2/tenant/dynamic_field_options_controller.rb
|
|
1048
|
+
- app/controllers/spree/api/v2/tenant/episodes_controller.rb
|
|
1048
1049
|
- app/controllers/spree/api/v2/tenant/free_vote_claims_controller.rb
|
|
1049
1050
|
- app/controllers/spree/api/v2/tenant/guests_controller.rb
|
|
1050
1051
|
- app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb
|
|
@@ -2102,7 +2103,7 @@ files:
|
|
|
2102
2103
|
- app/services/sms_adapter/base.rb
|
|
2103
2104
|
- app/services/sms_adapter/plasgate.rb
|
|
2104
2105
|
- app/services/sms_adapter/twillio.rb
|
|
2105
|
-
- app/services/spree_cm_commissioner/advertisements/
|
|
2106
|
+
- app/services/spree_cm_commissioner/advertisements/sorted_advertisements.rb
|
|
2106
2107
|
- app/services/spree_cm_commissioner/aes_encryption_service.rb
|
|
2107
2108
|
- app/services/spree_cm_commissioner/agency_categories/create.rb
|
|
2108
2109
|
- app/services/spree_cm_commissioner/agency_users/add.rb
|
|
@@ -3282,6 +3283,7 @@ files:
|
|
|
3282
3283
|
- db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb
|
|
3283
3284
|
- db/migrate/20260603063652_add_parent_to_voting_sessions.rb
|
|
3284
3285
|
- db/migrate/20260603090000_add_session_type_to_cm_voting_sessions.rb
|
|
3286
|
+
- db/migrate/20260608000000_add_display_to_cm_voting_sessions.rb
|
|
3285
3287
|
- docker-compose.yml
|
|
3286
3288
|
- docs/api/scoped-access-token-endpoints.md
|
|
3287
3289
|
- docs/option_types/attr_types.md
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# Validates that adding or updating an advertisement keeps the campaign total within 100.
|
|
2
|
-
#
|
|
3
|
-
# Usage:
|
|
4
|
-
# # on create
|
|
5
|
-
# result = SpreeCmCommissioner::Advertisements::WeightValidator.call(
|
|
6
|
-
# campaign: campaign,
|
|
7
|
-
# new_weight: 40
|
|
8
|
-
# )
|
|
9
|
-
#
|
|
10
|
-
# # on update (exclude current ad from total)
|
|
11
|
-
# result = SpreeCmCommissioner::Advertisements::WeightValidator.call(
|
|
12
|
-
# campaign: campaign,
|
|
13
|
-
# new_weight: 40,
|
|
14
|
-
# exclude_id: advertisement.id
|
|
15
|
-
# )
|
|
16
|
-
#
|
|
17
|
-
# result.success? # => true
|
|
18
|
-
# result.error.value # => "Campaign weights must not exceed 100, got 130"
|
|
19
|
-
#
|
|
20
|
-
# Rules:
|
|
21
|
-
# - Skip validation if campaign is nil
|
|
22
|
-
# - All other ads in the campaign + new_weight must not exceed 100
|
|
23
|
-
module SpreeCmCommissioner
|
|
24
|
-
module Advertisements
|
|
25
|
-
class WeightValidator
|
|
26
|
-
prepend ::Spree::ServiceModule::Base
|
|
27
|
-
|
|
28
|
-
def call(campaign:, new_weight:, exclude_id: nil)
|
|
29
|
-
return success(campaign) unless campaign
|
|
30
|
-
|
|
31
|
-
scope = campaign.advertisements
|
|
32
|
-
scope = scope.where.not(id: exclude_id) if exclude_id
|
|
33
|
-
|
|
34
|
-
existing_total = scope.sum { |ad| ad.advertise_weight.to_i }
|
|
35
|
-
total = existing_total + new_weight.to_i
|
|
36
|
-
|
|
37
|
-
return success(campaign) if total <= 100
|
|
38
|
-
|
|
39
|
-
failure(nil, "Campaign weights must not exceed 100, got #{total}")
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|