spree_cm_commissioner 2.8.2.pre.pre.2 → 2.8.2
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/.gitignore +0 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/homepage_section_controller.rb +1 -4
- data/app/controllers/spree/admin/taxons_controller_decorator.rb +0 -19
- data/app/controllers/spree/api/v2/storefront/homepage_sections_controller.rb +0 -1
- data/app/controllers/spree/api/v2/tenant/base_controller.rb +0 -4
- data/app/controllers/spree/api/v2/tenant/homepage_sections_controller.rb +0 -1
- data/app/controllers/spree/api/v2/tenant/products_controller.rb +1 -1
- data/app/controllers/spree/api/v2/tenant/taxons_controller.rb +1 -1
- data/app/controllers/spree_cm_commissioner/admin/products_controller_decorator.rb +0 -19
- data/app/finders/spree_cm_commissioner/events/find_matches.rb +0 -1
- data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +0 -2
- data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +1 -2
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -2
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +0 -10
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
- data/app/models/spree_cm_commissioner/product_decorator.rb +0 -39
- data/app/models/spree_cm_commissioner/role_decorator.rb +1 -4
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +0 -15
- data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +1 -10
- data/app/models/spree_cm_commissioner/tenant.rb +0 -9
- data/app/models/spree_cm_commissioner/user_decorator.rb +0 -5
- data/app/models/spree_cm_commissioner/variant_options.rb +0 -4
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +0 -4
- data/app/serializers/spree/v2/storefront/homepage_section_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/product_serializer_decorator.rb +1 -1
- data/app/serializers/spree/v2/storefront/role_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb +1 -2
- data/app/serializers/spree/v2/tenant/homepage_section_serializer.rb +1 -1
- data/app/serializers/spree/v2/tenant/role_serializer.rb +1 -1
- data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +0 -12
- data/app/views/spree/admin/homepage_section/_form.html.erb +0 -5
- data/config/initializers/spree_permitted_attributes.rb +0 -8
- data/config/locales/en.yml +0 -14
- data/config/locales/km.yml +0 -10
- data/config/routes.rb +0 -26
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +1 -7
- data/spree_cm_commissioner.gemspec +1 -1
- metadata +4 -116
- data/app/controllers/spree/api/v2/storefront/preview_products_controller.rb +0 -48
- data/app/controllers/spree/api/v2/storefront/preview_sections_controller.rb +0 -27
- data/app/controllers/spree/api/v2/storefront/preview_taxons_controller.rb +0 -18
- data/app/controllers/spree/api/v2/storefront/products_controller_decorator.rb +0 -15
- data/app/controllers/spree/api/v2/storefront/taxons_controller_decorator.rb +0 -15
- data/app/controllers/spree/api/v2/tenant/free_vote_claims_controller.rb +0 -37
- data/app/controllers/spree/api/v2/tenant/preview_products_controller.rb +0 -47
- data/app/controllers/spree/api/v2/tenant/preview_sections_controller.rb +0 -26
- data/app/controllers/spree/api/v2/tenant/preview_shows_controller.rb +0 -19
- data/app/controllers/spree/api/v2/tenant/preview_taxons_controller.rb +0 -19
- data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +0 -52
- data/app/controllers/spree/api/v2/tenant/show_elimination_sessions_controller.rb +0 -57
- data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +0 -49
- data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +0 -36
- data/app/controllers/spree/api/v2/tenant/shows_controller.rb +0 -34
- data/app/controllers/spree/api/v2/tenant/votes_controller.rb +0 -94
- data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +0 -40
- data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +0 -41
- data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +0 -31
- data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +0 -9
- data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +0 -10
- data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +0 -36
- data/app/models/spree_cm_commissioner/preview_role.rb +0 -8
- data/app/models/spree_cm_commissioner/role_user_decorator.rb +0 -8
- data/app/models/spree_cm_commissioner/show.rb +0 -159
- data/app/models/spree_cm_commissioner/show_contestant.rb +0 -39
- data/app/models/spree_cm_commissioner/show_contestant_image.rb +0 -11
- data/app/models/spree_cm_commissioner/show_contestant_video.rb +0 -15
- data/app/models/spree_cm_commissioner/show_episode.rb +0 -135
- data/app/models/spree_cm_commissioner/show_person.rb +0 -15
- data/app/models/spree_cm_commissioner/show_person_assignment.rb +0 -20
- data/app/models/spree_cm_commissioner/show_person_image.rb +0 -11
- data/app/models/spree_cm_commissioner/vote.rb +0 -16
- data/app/models/spree_cm_commissioner/vote_fraud_event.rb +0 -19
- data/app/models/spree_cm_commissioner/voting_contestant.rb +0 -46
- data/app/models/spree_cm_commissioner/voting_credit.rb +0 -72
- data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +0 -55
- data/app/models/spree_cm_commissioner/voting_session.rb +0 -223
- data/app/models/spree_cm_commissioner/voting_session_stat.rb +0 -8
- data/app/overrides/spree/admin/products/_form/preview_checkbox.html.erb.deface +0 -9
- data/app/overrides/spree/admin/taxons/_form/preview_checkbox.html.erb.deface +0 -7
- data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +0 -21
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +0 -17
- data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +0 -16
- data/app/serializers/spree/v2/tenant/show_person_serializer.rb +0 -13
- data/app/serializers/spree/v2/tenant/show_serializer.rb +0 -26
- data/app/serializers/spree/v2/tenant/video_serializer.rb +0 -9
- data/app/serializers/spree/v2/tenant/vote_serializer.rb +0 -14
- data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +0 -22
- data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +0 -10
- data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +0 -14
- data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +0 -18
- data/app/services/spree_cm_commissioner/fraud_check.rb +0 -279
- data/app/services/spree_cm_commissioner/show_contestants/normalize_video_highlights.rb +0 -57
- data/app/services/spree_cm_commissioner/url_embed/youtube_embed.rb +0 -44
- data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +0 -43
- data/app/services/spree_cm_commissioner/vote_counters/base.rb +0 -31
- data/app/services/spree_cm_commissioner/vote_counters/increment.rb +0 -44
- data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +0 -68
- data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +0 -70
- data/app/services/spree_cm_commissioner/vote_counters/snapshot_to_db.rb +0 -113
- data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +0 -68
- data/app/services/spree_cm_commissioner/vote_package/create.rb +0 -145
- data/app/services/spree_cm_commissioner/vote_package/update.rb +0 -91
- data/app/services/spree_cm_commissioner/vote_processor.rb +0 -144
- data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +0 -334
- data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +0 -32
- data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +0 -106
- data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +0 -77
- data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +0 -119
- data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +0 -35
- data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +0 -87
- data/app/services/spree_cm_commissioner/voting_leaderboards/calculate_score.rb +0 -74
- data/app/services/spree_cm_commissioner/voting_sessions/finalize.rb +0 -66
- data/db/migrate/20260309230148_create_cm_show_people.rb +0 -14
- data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +0 -16
- data/db/migrate/20260310082711_create_cm_show_contestants.rb +0 -28
- data/db/migrate/20260310082720_create_cm_voting_sessions.rb +0 -21
- data/db/migrate/20260310082721_create_cm_voting_contestants.rb +0 -23
- data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +0 -9
- data/db/migrate/20260310082735_add_type_to_spree_products.rb +0 -6
- data/db/migrate/20260310082749_create_cm_voting_credits.rb +0 -27
- data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +0 -27
- data/db/migrate/20260330160000_create_cm_votes.rb +0 -25
- data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +0 -7
- data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +0 -6
- data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +0 -12
- data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +0 -5
- data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +0 -6
- data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +0 -11
- data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +0 -9
- data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +0 -25
- data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +0 -23
- data/db/migrate/20260423000001_add_preview_to_taxons_products_and_sections.rb +0 -11
- data/db/migrate/20260423000002_create_preview_roles.rb +0 -24
- data/db/migrate/20260515120000_add_public_metadata_to_cm_voting_sessions.rb +0 -5
- data/db/migrate/20260518090920_add_unique_voter_count_to_voting_contestants.rb +0 -5
- data/db/migrate/20260518094322_create_cm_voting_session_stats.rb +0 -17
- data/db/migrate/20260520000001_add_scoring_model_to_cm_voting_sessions.rb +0 -5
- data/db/migrate/20260520000001_optimize_cm_votes_indexes.rb +0 -22
- data/db/migrate/20260525042257_add_vote_number_to_cm_voting_contestants.rb +0 -18
- data/db/migrate/20260527035430_add_confirmed_rank_to_cm_voting_contestants.rb +0 -5
- data/db/migrate/20260527062005_add_eliminated_at_to_cm_show_contestants.rb +0 -5
- data/docs/sql/jsonb_query_guide.md +0 -57
- data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +0 -12
- data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +0 -120
- data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +0 -37
- data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +0 -28
- data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +0 -11
- data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +0 -11
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class Show < Spree::Taxon
|
|
3
|
-
include SpreeCmCommissioner::StoreMetadata
|
|
4
|
-
|
|
5
|
-
has_many :voting_credits, class_name: 'SpreeCmCommissioner::VotingCredit', as: :votable, dependent: :destroy
|
|
6
|
-
belongs_to :show, class_name: 'SpreeCmCommissioner::Show', foreign_key: :parent_id, optional: true
|
|
7
|
-
has_many :seasons, class_name: 'SpreeCmCommissioner::Show', foreign_key: :parent_id, inverse_of: :parent
|
|
8
|
-
has_many :show_contestants, class_name: 'SpreeCmCommissioner::ShowContestant', inverse_of: :show, dependent: :destroy
|
|
9
|
-
has_many :show_people_assignments, class_name: 'SpreeCmCommissioner::ShowPersonAssignment', dependent: :destroy
|
|
10
|
-
has_many :show_people, through: :show_people_assignments, source: :show_person
|
|
11
|
-
has_many :episodes, class_name: 'SpreeCmCommissioner::ShowEpisode', foreign_key: :event_id
|
|
12
|
-
has_many :voting_sessions, through: :episodes, class_name: 'SpreeCmCommissioner::VotingSession'
|
|
13
|
-
has_one :current_episode, -> { current_or_next_upcoming }, class_name: 'SpreeCmCommissioner::ShowEpisode', foreign_key: :event_id
|
|
14
|
-
has_one :current_voting_session, through: :current_episode
|
|
15
|
-
|
|
16
|
-
before_validation { self.type = self.class.name }
|
|
17
|
-
before_validation { self.kind = :event }
|
|
18
|
-
before_validation :assign_taxonomy_and_parent, on: :create
|
|
19
|
-
|
|
20
|
-
store_private_metadata :season_number, :integer
|
|
21
|
-
store_private_metadata :season_year, :integer
|
|
22
|
-
|
|
23
|
-
# Define standard voting configuration keys
|
|
24
|
-
store_accessor :voting_config,
|
|
25
|
-
:free_vote_limit,
|
|
26
|
-
:free_vote_limit_type,
|
|
27
|
-
:paid_vote_enabled,
|
|
28
|
-
:max_paid_votes_per_session,
|
|
29
|
-
:sms_vote_enabled,
|
|
30
|
-
:sms_vote_cost_usd,
|
|
31
|
-
:operator_vote_enabled,
|
|
32
|
-
:votes_weight,
|
|
33
|
-
:voters_weight
|
|
34
|
-
|
|
35
|
-
# voting_credit_scope determines how purchased vote credits are grouped (scoped).
|
|
36
|
-
# Changing this after credits have been allocated will cause inconsistencies:
|
|
37
|
-
# - existing credits are scoped to the old type (e.g. ShowEpisode),
|
|
38
|
-
# but new purchases will create credits under the new type (e.g. Show),
|
|
39
|
-
# leading to split balances that cannot be merged.
|
|
40
|
-
# UI rule: once voting_credit_scope is set and any VotingCredit exists for this show, the field must be read-only — do not allow changes.
|
|
41
|
-
# Note: VoteCredit of user should base on CREDIT_SCOPES
|
|
42
|
-
# - if scope is Show, User's credits should be only 1 per show, and can be used for any episode under the show.
|
|
43
|
-
# - if scope is ShowEpisode, User's credits should be N per show (N is the number of episodes), and can be only used for specific episode.
|
|
44
|
-
# - if scope is VotingSession, User's credits should be N per show (N is the number of voting sessions), and can be only used for specific voting session. # rubocop:disable Layout/LineLength
|
|
45
|
-
CREDIT_SCOPES = {
|
|
46
|
-
'Show' => { free_vote_limit_type: 'per_show' }.freeze,
|
|
47
|
-
'ShowEpisode' => { free_vote_limit_type: 'per_episode' }.freeze,
|
|
48
|
-
'VotingSession' => { free_vote_limit_type: 'per_voting_session' }.freeze
|
|
49
|
-
}.freeze
|
|
50
|
-
|
|
51
|
-
def self.polymorphic_name
|
|
52
|
-
name
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
validate :voting_credit_scope_immutable, if: :voting_credit_scope_changed?
|
|
56
|
-
|
|
57
|
-
def credit_votable_type
|
|
58
|
-
voting_credit_scope = self[:voting_credit_scope] || 'ShowEpisode'
|
|
59
|
-
|
|
60
|
-
type = CREDIT_SCOPES.key?(voting_credit_scope) ? voting_credit_scope : 'ShowEpisode'
|
|
61
|
-
"SpreeCmCommissioner::#{type}"
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def free_vote_idempotency_key(user_id:, votable_id: nil)
|
|
65
|
-
case free_vote_limit_type
|
|
66
|
-
when 'per_episode' then "free_claim::episode::#{votable_id}::user::#{user_id}"
|
|
67
|
-
when 'per_voting_session' then "free_claim::voting_session::#{votable_id}::user::#{user_id}"
|
|
68
|
-
when 'per_show' then "free_claim::show::#{id}::user::#{user_id}"
|
|
69
|
-
else raise ArgumentError, "Invalid free_vote_limit_type: #{free_vote_limit_type}"
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Define standard fraud configuration keys
|
|
74
|
-
store_accessor :fraud_config,
|
|
75
|
-
:max_votes_per_minute_per_user,
|
|
76
|
-
:max_votes_per_minute_per_ip,
|
|
77
|
-
:max_accounts_per_device,
|
|
78
|
-
:block_vpn
|
|
79
|
-
|
|
80
|
-
def show?
|
|
81
|
-
depth == 1
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def season?
|
|
85
|
-
depth == 2
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def display_name
|
|
89
|
-
if season? && parent.present?
|
|
90
|
-
"#{parent.name} #{name}"
|
|
91
|
-
else
|
|
92
|
-
name
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def contestants
|
|
97
|
-
show_contestants
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def effective_voting_config
|
|
101
|
-
config = parse_json_config(voting_config)
|
|
102
|
-
return config if show?
|
|
103
|
-
|
|
104
|
-
config.reverse_merge(parent&.effective_voting_config || {})
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def effective_fraud_config
|
|
108
|
-
config = parse_json_config(fraud_config)
|
|
109
|
-
return config if show?
|
|
110
|
-
|
|
111
|
-
config.reverse_merge(parent&.effective_fraud_config || {})
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def build_next_season(attributes = {})
|
|
115
|
-
last_season = seasons.order(created_at: :desc).first
|
|
116
|
-
|
|
117
|
-
default_number = (last_season&.season_number || 0) + 1
|
|
118
|
-
default_year = last_season&.season_year || Time.current.year
|
|
119
|
-
|
|
120
|
-
seasons.new(
|
|
121
|
-
{
|
|
122
|
-
season_number: default_number,
|
|
123
|
-
season_year: default_year
|
|
124
|
-
}.merge(attributes)
|
|
125
|
-
)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
private
|
|
129
|
-
|
|
130
|
-
def assign_taxonomy_and_parent
|
|
131
|
-
if parent.present?
|
|
132
|
-
self.taxonomy ||= parent.taxonomy
|
|
133
|
-
elsif taxonomy_id.blank?
|
|
134
|
-
self.taxonomy = Spree::Taxonomy.shows
|
|
135
|
-
self.parent = taxonomy.root
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def voting_credit_scope_immutable
|
|
140
|
-
show_has_credits = SpreeCmCommissioner::VotingCredit.exists?(votable_type: 'SpreeCmCommissioner::Show', votable_id: id)
|
|
141
|
-
episode_has_credits = SpreeCmCommissioner::VotingCredit.exists?(votable_type: 'SpreeCmCommissioner::ShowEpisode',
|
|
142
|
-
votable_id: episodes.select(:id)
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
return unless show_has_credits || episode_has_credits
|
|
146
|
-
|
|
147
|
-
errors.add(:voting_credit_scope, 'cannot be changed after voting credits have been allocated')
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def parse_json_config(value)
|
|
151
|
-
return {} if value.nil? || value == ''
|
|
152
|
-
return value if value.is_a?(Hash)
|
|
153
|
-
|
|
154
|
-
JSON.parse(value)
|
|
155
|
-
rescue JSON::ParserError
|
|
156
|
-
{}
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class ShowContestant < SpreeCmCommissioner::Base
|
|
3
|
-
belongs_to :show, class_name: 'SpreeCmCommissioner::Show', optional: false
|
|
4
|
-
belongs_to :assigned_show_person, class_name: 'SpreeCmCommissioner::ShowPerson', optional: true
|
|
5
|
-
belongs_to :user, class_name: 'Spree::User', optional: true
|
|
6
|
-
has_many :show_contestant_images, lambda {
|
|
7
|
-
order(:position)
|
|
8
|
-
}, as: :viewable, class_name: 'SpreeCmCommissioner::ShowContestantImage', dependent: :destroy
|
|
9
|
-
|
|
10
|
-
has_many :show_contestant_videos, lambda {
|
|
11
|
-
order(:position)
|
|
12
|
-
}, as: :viewable, class_name: 'SpreeCmCommissioner::ShowContestantVideo', dependent: :destroy
|
|
13
|
-
|
|
14
|
-
has_one :show_person_images, -> { order(:position) }, as: :viewable, class_name: 'SpreeCmCommissioner::ShowPersonImage', dependent: :destroy
|
|
15
|
-
|
|
16
|
-
has_many :voting_contestants, class_name: 'SpreeCmCommissioner::VotingContestant', dependent: :destroy
|
|
17
|
-
has_one :current_voting_session, through: :show
|
|
18
|
-
|
|
19
|
-
enum status: { active: 0, eliminated: 1, advanced: 2, winner: 3, withdrawn: 4 }
|
|
20
|
-
enum gender: { :other => 0, :male => 1, :female => 2 }
|
|
21
|
-
|
|
22
|
-
enum category: { boys: 0, girls: 1, groups: 2, overs: 3 }
|
|
23
|
-
|
|
24
|
-
# Define standard Social Link Keys
|
|
25
|
-
store_accessor :social_links, :facebook, :instagram, :twitter, :linkedin, :youtube, :tiktok, :telegram
|
|
26
|
-
|
|
27
|
-
validates :name, presence: true
|
|
28
|
-
validates :contestant_number, uniqueness: { scope: :show_id }, allow_nil: true
|
|
29
|
-
|
|
30
|
-
scope :ordered, -> { order(:position, :id) }
|
|
31
|
-
|
|
32
|
-
def eliminate
|
|
33
|
-
self.status = :eliminated
|
|
34
|
-
self.eliminated_at = Time.current
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
self.whitelisted_ransackable_attributes = %w[name contestant_number status gender category origin]
|
|
38
|
-
end
|
|
39
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class ShowContestantVideo < Video
|
|
3
|
-
validate :validate_attachment_file_size
|
|
4
|
-
|
|
5
|
-
private
|
|
6
|
-
|
|
7
|
-
def validate_attachment_file_size
|
|
8
|
-
return unless attachment.attached?
|
|
9
|
-
|
|
10
|
-
return unless attachment.blob.byte_size > 5.megabytes
|
|
11
|
-
|
|
12
|
-
errors.add(:attachment, 'must be less than 5 MB')
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class ShowEpisode < Spree::Product
|
|
3
|
-
self.inheritance_column = :_type_disabled
|
|
4
|
-
|
|
5
|
-
include SpreeCmCommissioner::StoreMetadata
|
|
6
|
-
|
|
7
|
-
belongs_to :season, class_name: 'SpreeCmCommissioner::Show', foreign_key: :event_id, optional: false
|
|
8
|
-
has_one :show, through: :season
|
|
9
|
-
|
|
10
|
-
has_many :voting_credits, class_name: 'SpreeCmCommissioner::VotingCredit', as: :votable, dependent: :destroy
|
|
11
|
-
has_many :voting_sessions, class_name: 'SpreeCmCommissioner::VotingSession', foreign_key: :episode_id, dependent: :destroy
|
|
12
|
-
has_many :contestants, through: :season, source: :show_contestants
|
|
13
|
-
|
|
14
|
-
has_one :current_voting_session, -> { current_or_next_upcoming }, class_name: 'SpreeCmCommissioner::VotingSession', foreign_key: :episode_id
|
|
15
|
-
|
|
16
|
-
validates :ends_at, presence: true, if: -> { product_type == 'show_episode' }
|
|
17
|
-
|
|
18
|
-
before_validation { self.type = self.class.name }
|
|
19
|
-
|
|
20
|
-
store_private_metadata :episode_number, :integer
|
|
21
|
-
store_private_metadata :scheduled_at, :datetime
|
|
22
|
-
store_private_metadata :ends_at, :datetime
|
|
23
|
-
store_private_metadata :aired_at, :datetime
|
|
24
|
-
|
|
25
|
-
store_public_metadata :votable_type, :string
|
|
26
|
-
store_public_metadata :votable_id, :integer
|
|
27
|
-
|
|
28
|
-
# Uses the database clock (CURRENT_TIMESTAMP) for consistency and to avoid application server clock drift.
|
|
29
|
-
scope :current, lambda {
|
|
30
|
-
where("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'scheduled_at' AS timestamp) <= CURRENT_TIMESTAMP")
|
|
31
|
-
.where("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'ends_at' AS timestamp) >= CURRENT_TIMESTAMP")
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
scope :upcoming, lambda {
|
|
35
|
-
where("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'scheduled_at' AS timestamp) > CURRENT_TIMESTAMP")
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# Orders episodes by their episode_number stored in private_metadata JSON.
|
|
39
|
-
# e.g. [Episode#3 (ep_num=1), Episode#7 (ep_num=2), Episode#1 (ep_num=3)]
|
|
40
|
-
scope :ordered_by_episode_number, lambda {
|
|
41
|
-
reorder(Arel.sql("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'episode_number' AS INTEGER) ASC"))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
def self.current_or_next_upcoming
|
|
45
|
-
current.or(upcoming)
|
|
46
|
-
.reorder(
|
|
47
|
-
Arel.sql("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'scheduled_at' AS timestamp) ASC"),
|
|
48
|
-
Arel.sql("CAST((spree_products.private_metadata #>> '{}')::jsonb ->> 'episode_number' AS integer) ASC")
|
|
49
|
-
)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def current?
|
|
53
|
-
(scheduled_at.present? && scheduled_at <= Time.zone.now) &&
|
|
54
|
-
(ends_at >= Time.zone.now)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def upcoming?
|
|
58
|
-
scheduled_at.present? && scheduled_at > Time.zone.now
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Returns the episode that follows this one within the same show, ordered by episode_number.
|
|
62
|
-
# e.g. show has [ep1, ep2, ep3]; self=ep2 → returns ep3; self=ep3 → nil
|
|
63
|
-
def next_in_season
|
|
64
|
-
eps = season.episodes.where(product_type: :show_episode).ordered_by_episode_number.to_a
|
|
65
|
-
current_index = eps.index { |ep| ep.id == id }
|
|
66
|
-
eps[current_index + 1] if current_index && current_index < eps.length - 1
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# True when this episode is the first in the season by episode_number.
|
|
70
|
-
# e.g. season has [ep1, ep2]; self=ep1 → true; self=ep2 → false
|
|
71
|
-
def first_in_season?
|
|
72
|
-
season.episodes.ordered_by_episode_number.first&.id == id
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Returns the first voting session (by position) of the next episode in the show.
|
|
76
|
-
# e.g. next episode has sessions [pos:1 "Prelim", pos:2 "Final"] → returns "Prelim" session
|
|
77
|
-
# Returns nil when there is no next episode or the next episode has no sessions.
|
|
78
|
-
def next_episode_first_session
|
|
79
|
-
next_in_season&.voting_sessions&.order(:position)&.first
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.model_name
|
|
83
|
-
Spree::Product.model_name
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def self.polymorphic_name
|
|
87
|
-
name
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def votable_type
|
|
91
|
-
public_metadata['votable_type']
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def votable_id
|
|
95
|
-
public_metadata['votable_id']
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
private
|
|
99
|
-
|
|
100
|
-
# Price is optional for real show episodes, but remains required for other
|
|
101
|
-
# product types that also use this model (e.g. vote_package).
|
|
102
|
-
def requires_price?
|
|
103
|
-
product_type.to_s != 'show_episode'
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Skip master price validation for show_episode records so they can be
|
|
107
|
-
# created without product/master price.
|
|
108
|
-
def validate_master
|
|
109
|
-
return if product_type.to_s == 'show_episode'
|
|
110
|
-
|
|
111
|
-
super
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Persist master variant without running price validation only for
|
|
115
|
-
# show_episode records.
|
|
116
|
-
def save_master
|
|
117
|
-
if product_type.to_s == 'show_episode' && master_updated?
|
|
118
|
-
master.save(validate: false)
|
|
119
|
-
@nested_changes = true
|
|
120
|
-
return
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
super
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Override. This is to ensure that the slug generator uses the correct scope
|
|
127
|
-
# when generating slugs for ShowEpisode records.
|
|
128
|
-
def scope_for_slug_generator
|
|
129
|
-
scope = self.class.base_class.unscoped
|
|
130
|
-
scope = scope.friendly unless scope.respond_to?(:exists_by_friendly_id?)
|
|
131
|
-
primary_key_name = self.class.primary_key
|
|
132
|
-
scope.where(self.class.base_class.arel_table[primary_key_name].not_eq(send(primary_key_name)))
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class ShowPerson < SpreeCmCommissioner::Base
|
|
3
|
-
belongs_to :user, class_name: 'Spree::User', optional: true
|
|
4
|
-
belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant', optional: true
|
|
5
|
-
has_many :show_person_images, -> { order(:position) }, as: :viewable, class_name: 'SpreeCmCommissioner::ShowPersonImage', dependent: :destroy
|
|
6
|
-
has_many :assignments, class_name: 'SpreeCmCommissioner::ShowPersonAssignment', dependent: :destroy
|
|
7
|
-
has_many :shows, through: :assignments, source: :show
|
|
8
|
-
has_many :assigned_contestants, class_name: 'SpreeCmCommissioner::ShowContestant', foreign_key: :assigned_show_person_id, dependent: :nullify
|
|
9
|
-
|
|
10
|
-
validates :name, presence: true
|
|
11
|
-
|
|
12
|
-
# Define standard Social Link Keys
|
|
13
|
-
store_accessor :social_links, :facebook, :instagram, :twitter, :linkedin, :youtube, :tiktok, :telegram
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class ShowPersonAssignment < SpreeCmCommissioner::Base
|
|
3
|
-
self.table_name = 'cm_show_people_assignments'
|
|
4
|
-
|
|
5
|
-
acts_as_list column: :position
|
|
6
|
-
|
|
7
|
-
belongs_to :show_person, class_name: 'SpreeCmCommissioner::ShowPerson', optional: false
|
|
8
|
-
belongs_to :show, class_name: 'Spree::Taxon', optional: false
|
|
9
|
-
|
|
10
|
-
enum role: { coach: 0, judge: 1, host: 2, mentor: 3 }
|
|
11
|
-
|
|
12
|
-
validates :role, presence: true
|
|
13
|
-
|
|
14
|
-
has_many :assigned_contestants,
|
|
15
|
-
-> (assignment) { where(show_id: assignment.show_id) },
|
|
16
|
-
class_name: 'SpreeCmCommissioner::ShowContestant',
|
|
17
|
-
primary_key: :show_person_id,
|
|
18
|
-
foreign_key: :assigned_show_person_id
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class Vote < SpreeCmCommissioner::Base
|
|
3
|
-
belongs_to :voting_session, class_name: 'SpreeCmCommissioner::VotingSession', optional: false
|
|
4
|
-
belongs_to :contestant, class_name: 'SpreeCmCommissioner::VotingContestant', optional: false
|
|
5
|
-
belongs_to :user, class_name: 'Spree::User', optional: true
|
|
6
|
-
belongs_to :vote_credit, class_name: 'SpreeCmCommissioner::VotingCredit', optional: true
|
|
7
|
-
belongs_to :vendor, class_name: 'Spree::Vendor', optional: true
|
|
8
|
-
|
|
9
|
-
enum channel: { web: 0, mobile: 1, admin: 2 }
|
|
10
|
-
|
|
11
|
-
validates :tenant_id, :vendor_id, :channel, :quantity, presence: true
|
|
12
|
-
validates :quantity, numericality: { greater_than: 0 }
|
|
13
|
-
|
|
14
|
-
scope :for_session, -> (session) { where(voting_session: session) }
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class VoteFraudEvent < SpreeCmCommissioner::Base
|
|
3
|
-
belongs_to :voting_session, class_name: 'SpreeCmCommissioner::VotingSession'
|
|
4
|
-
belongs_to :user, class_name: 'Spree::User', optional: true
|
|
5
|
-
belongs_to :vote, class_name: 'SpreeCmCommissioner::Vote', optional: true
|
|
6
|
-
belongs_to :resolved_by, class_name: 'Spree::User', optional: true
|
|
7
|
-
|
|
8
|
-
enum fraud_type: {
|
|
9
|
-
rate_limit: 0,
|
|
10
|
-
device_duplicate: 1,
|
|
11
|
-
ip_cluster: 2,
|
|
12
|
-
anomaly: 3,
|
|
13
|
-
new_account: 4,
|
|
14
|
-
manual_flag: 5
|
|
15
|
-
}
|
|
16
|
-
enum severity: { low: 0, medium: 1, high: 2, critical: 3 }
|
|
17
|
-
enum action_taken: { allowed: 0, blocked: 1, flagged: 2, invalidated: 3 }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class VotingContestant < SpreeCmCommissioner::Base
|
|
3
|
-
has_paper_trail
|
|
4
|
-
enum eliminated_via: { public_vote: 0, judges_deadlock: 1, coach_decision: 2, instant_save_lost: 3 }
|
|
5
|
-
belongs_to :show, class_name: 'Spree::Taxon', optional: false
|
|
6
|
-
belongs_to :voting_session, class_name: 'SpreeCmCommissioner::VotingSession', optional: false
|
|
7
|
-
belongs_to :show_contestant, class_name: 'SpreeCmCommissioner::ShowContestant', optional: false
|
|
8
|
-
belongs_to :advanced_to, polymorphic: true, optional: true
|
|
9
|
-
belongs_to :advanced_from, polymorphic: true, optional: true
|
|
10
|
-
has_many :votes, class_name: 'SpreeCmCommissioner::Vote', foreign_key: :contestant_id, dependent: :restrict_with_error
|
|
11
|
-
has_many :show_contestant_images, through: :show_contestant
|
|
12
|
-
has_many :show_contestant_videos, through: :show_contestant
|
|
13
|
-
|
|
14
|
-
self.whitelisted_ransackable_attributes = %w[eliminated special_rules]
|
|
15
|
-
self.whitelisted_ransackable_associations = %w[show_contestant]
|
|
16
|
-
|
|
17
|
-
ransacker :special_rule_type do
|
|
18
|
-
Arel.sql("cm_voting_contestants.special_rules ->> 'type'")
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
validates :eliminated_via, presence: true, if: :eliminated?
|
|
22
|
-
|
|
23
|
-
# Uniqueness validation: vote_number must be unique per contestant per voting_session
|
|
24
|
-
# Scope: [show_contestant_id, voting_session_id]
|
|
25
|
-
#
|
|
26
|
-
# | contestant_id | session_id | vote_number | Status | Reason |
|
|
27
|
-
# |---------------|-----------|-------------|--------|---------------------------|
|
|
28
|
-
# | 1 | 1 | 1 | ✓ OK | First record |
|
|
29
|
-
# | 1 | 1 | 1 | ✗ FAIL | Duplicate in same scope |
|
|
30
|
-
# | 1 | 1 | 2 | ✓ OK | Can update/change number |
|
|
31
|
-
# | 2 | 1 | 1 | ✓ OK | Different contestant |
|
|
32
|
-
# | 1 | 2 | 1 | ✓ OK | Different session |
|
|
33
|
-
validates :vote_number, uniqueness: { scope: %i[show_contestant_id voting_session_id], allow_nil: true }
|
|
34
|
-
|
|
35
|
-
delegate :name, :contestant_number, :category, :gender, :eliminated_at, to: :show_contestant, allow_nil: true
|
|
36
|
-
delegate :can_vote?, to: :voting_session, allow_nil: true
|
|
37
|
-
|
|
38
|
-
def special_rule_type=(value)
|
|
39
|
-
self.special_rules = value.present? ? { 'type' => value } : {}
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def special_rule_type
|
|
43
|
-
(special_rules || {})['type']
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class VotingCredit < SpreeCmCommissioner::Base
|
|
3
|
-
enum category: { purchase: 0, promo: 1, bonus: 2, admin_grant: 3 }
|
|
4
|
-
enum credit_type: { expiring: 0, non_expiring: 1 }
|
|
5
|
-
|
|
6
|
-
belongs_to :user, class_name: 'Spree::User', optional: false
|
|
7
|
-
belongs_to :created_by, class_name: 'Spree::User', optional: true
|
|
8
|
-
belongs_to :votable, polymorphic: true, optional: true
|
|
9
|
-
|
|
10
|
-
has_many :voting_credit_transactions, class_name: 'SpreeCmCommissioner::VotingCreditTransaction',
|
|
11
|
-
foreign_key: :vote_credit_id, dependent: :destroy
|
|
12
|
-
has_many :votes, class_name: 'SpreeCmCommissioner::Vote',
|
|
13
|
-
foreign_key: :vote_credit_id, dependent: :destroy
|
|
14
|
-
|
|
15
|
-
scope :active, -> { where(deleted_at: nil) }
|
|
16
|
-
scope :eligible, lambda { |voting_session|
|
|
17
|
-
active.where(
|
|
18
|
-
'(votable_type = ? AND votable_id = ?) OR ' \
|
|
19
|
-
'(votable_type = ? AND votable_id = ?) OR ' \
|
|
20
|
-
'(votable_type = ? AND votable_id = ?)',
|
|
21
|
-
'SpreeCmCommissioner::VotingSession', voting_session.id,
|
|
22
|
-
'SpreeCmCommissioner::ShowEpisode', voting_session.episode_id,
|
|
23
|
-
'SpreeCmCommissioner::Show', voting_session.episode.event_id
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
before_validation :set_default_votes, on: :create
|
|
28
|
-
before_create :set_created_by
|
|
29
|
-
|
|
30
|
-
validates :free_votes_amount, numericality: { greater_than_or_equal_to: 0 }
|
|
31
|
-
validates :free_votes_used, numericality: { greater_than_or_equal_to: 0 }
|
|
32
|
-
validates :paid_votes_amount, numericality: { greater_than_or_equal_to: 0 }
|
|
33
|
-
validates :paid_votes_used, numericality: { greater_than_or_equal_to: 0 }
|
|
34
|
-
|
|
35
|
-
def free_votes_remaining
|
|
36
|
-
free_votes_amount - free_votes_used
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def paid_votes_remaining
|
|
40
|
-
paid_votes_amount - paid_votes_used
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def available_votes
|
|
44
|
-
free_votes_remaining + paid_votes_remaining
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def can_use?(requested_amount)
|
|
48
|
-
available_votes >= requested_amount
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def soft_delete
|
|
52
|
-
update(deleted_at: Time.current)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def restore
|
|
56
|
-
update(deleted_at: nil)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def set_created_by
|
|
62
|
-
self.created_by ||= user
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def set_default_votes
|
|
66
|
-
self.free_votes_amount = 0 if free_votes_amount.blank?
|
|
67
|
-
self.free_votes_used = 0 if free_votes_used.blank?
|
|
68
|
-
self.paid_votes_amount = 0 if paid_votes_amount.blank?
|
|
69
|
-
self.paid_votes_used = 0 if paid_votes_used.blank?
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
module SpreeCmCommissioner
|
|
2
|
-
class VotingCreditTransaction < SpreeCmCommissioner::Base
|
|
3
|
-
enum action: {
|
|
4
|
-
purchase: 0,
|
|
5
|
-
canceled: 1,
|
|
6
|
-
use: 2,
|
|
7
|
-
authorize: 3,
|
|
8
|
-
release: 4,
|
|
9
|
-
free_claim: 5
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
attr_accessor :free_claim_votable_id
|
|
13
|
-
|
|
14
|
-
belongs_to :vote_credit, class_name: 'SpreeCmCommissioner::VotingCredit', optional: false
|
|
15
|
-
belongs_to :episode, class_name: 'SpreeCmCommissioner::ShowEpisode', optional: true
|
|
16
|
-
belongs_to :originator, polymorphic: true, optional: true
|
|
17
|
-
|
|
18
|
-
scope :by_action, -> (actions) { where(action: actions) }
|
|
19
|
-
|
|
20
|
-
before_validation :set_idempotency_key, if: -> { idempotency_key.nil? }
|
|
21
|
-
|
|
22
|
-
validates :amount, presence: true
|
|
23
|
-
validates :action, presence: true
|
|
24
|
-
validates :user_total_amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
25
|
-
validates :idempotency_key, uniqueness: true, allow_nil: true
|
|
26
|
-
|
|
27
|
-
def soft_delete
|
|
28
|
-
update(deleted_at: Time.current)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def restore
|
|
32
|
-
update(deleted_at: nil)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def set_idempotency_key
|
|
38
|
-
self.idempotency_key =
|
|
39
|
-
case action
|
|
40
|
-
when 'purchase', 'canceled' then order_scoped_key
|
|
41
|
-
when 'free_claim' then free_claim_key
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def order_scoped_key
|
|
46
|
-
"#{originator.number}::#{action}" if originator.respond_to?(:number)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def free_claim_key
|
|
50
|
-
return unless originator.respond_to?(:free_vote_idempotency_key) && vote_credit&.user_id
|
|
51
|
-
|
|
52
|
-
originator.free_vote_idempotency_key(user_id: vote_credit.user_id, votable_id: free_claim_votable_id)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|