jumbotron 0.1.1
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 +7 -0
- data/MIT-LICENSE +21 -0
- data/README.md +5 -0
- data/app/adapters/jumbotron/adapters/base.rb +136 -0
- data/app/adapters/jumbotron/adapters/cadence.rb +42 -0
- data/app/adapters/jumbotron/adapters/discovery_definition.rb +7 -0
- data/app/adapters/jumbotron/adapters/espn/nfl.rb +169 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/acquisition_scope.rb +29 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/competition_odds.rb +185 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/lifecycle.rb +30 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/line_acquisition_scope.rb +26 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/progress.rb +54 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/scoreboard.rb +146 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/season_phase.rb +33 -0
- data/app/adapters/jumbotron/adapters/espn/nfl_helpers/teams.rb +35 -0
- data/app/adapters/jumbotron/adapters/line_acquisition.rb +19 -0
- data/app/adapters/jumbotron/adapters/operation.rb +23 -0
- data/app/adapters/jumbotron/adapters/policy_definition.rb +14 -0
- data/app/adapters/jumbotron/adapters/registry.rb +78 -0
- data/app/adapters/jumbotron/adapters.rb +11 -0
- data/app/boot/jumbotron/boot/enqueue_discoveries.rb +41 -0
- data/app/clients/jumbotron/clients/espn/competition_odds/get.rb +26 -0
- data/app/clients/jumbotron/clients/espn/competition_odds/get_input.rb +16 -0
- data/app/clients/jumbotron/clients/espn/scoreboard/get.rb +18 -0
- data/app/clients/jumbotron/clients/espn/scoreboard/get_input.rb +17 -0
- data/app/clients/jumbotron/clients/espn/teams/get.rb +18 -0
- data/app/clients/jumbotron/clients/espn/teams/get_input.rb +15 -0
- data/app/clients/jumbotron/clients/espn.rb +44 -0
- data/app/clients/jumbotron/clients.rb +8 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition.rb +79 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/draw_odds.rb +31 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/get.rb +66 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/item.rb +60 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/listed_provider.rb +31 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/market_window.rb +31 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/point_spread.rb +27 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/price.rb +35 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/side_quote.rb +35 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/team_odds.rb +40 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/competitor.rb +49 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/event.rb +81 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/scoreboard/get.rb +88 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/season.rb +27 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/status.rb +69 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/support.rb +61 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/team.rb +42 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/teams/get.rb +76 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/venue.rb +34 -0
- data/app/deserializers/jumbotron/deserializers/clients/espn/week.rb +23 -0
- data/app/deserializers/jumbotron/deserializers/public/game.rb +26 -0
- data/app/deserializers/jumbotron/deserializers/public/game_ids.rb +28 -0
- data/app/deserializers/jumbotron/deserializers/public/includes.rb +32 -0
- data/app/deserializers/jumbotron/deserializers/public/schedule.rb +151 -0
- data/app/deserializers/jumbotron/deserializers/public/schedule_groups.rb +91 -0
- data/app/errors/jumbotron/error.rb +6 -0
- data/app/errors/jumbotron/errors/adapters.rb +143 -0
- data/app/errors/jumbotron/errors/canonical.rb +67 -0
- data/app/errors/jumbotron/errors/public.rb +27 -0
- data/app/errors/jumbotron/invalid_request_error.rb +6 -0
- data/app/errors/jumbotron/not_found_error.rb +6 -0
- data/app/errors/jumbotron/unsupported_capability_error.rb +6 -0
- data/app/jobs/jumbotron/application_job.rb +6 -0
- data/app/jobs/jumbotron/discovery_job.rb +14 -0
- data/app/jobs/jumbotron/game_update_job.rb +12 -0
- data/app/jobs/jumbotron/line_update_job.rb +12 -0
- data/app/models/jumbotron/application_record.rb +8 -0
- data/app/models/jumbotron/bookmaker.rb +11 -0
- data/app/models/jumbotron/canonical.rb +128 -0
- data/app/models/jumbotron/game.rb +92 -0
- data/app/models/jumbotron/game_participant.rb +10 -0
- data/app/models/jumbotron/historical_change.rb +9 -0
- data/app/models/jumbotron/league.rb +12 -0
- data/app/models/jumbotron/line_observation.rb +18 -0
- data/app/models/jumbotron/observation_batch.rb +14 -0
- data/app/models/jumbotron/provider_identity.rb +32 -0
- data/app/models/jumbotron/public/translate_outcome.rb +57 -0
- data/app/models/jumbotron/public.rb +88 -0
- data/app/models/jumbotron/schedule_group.rb +22 -0
- data/app/models/jumbotron/season.rb +13 -0
- data/app/models/jumbotron/season_phase.rb +12 -0
- data/app/models/jumbotron/sport.rb +10 -0
- data/app/models/jumbotron/team.rb +11 -0
- data/app/models/jumbotron/venue.rb +10 -0
- data/app/providers/jumbotron/providers/espn/cooldown.rb +88 -0
- data/app/providers/jumbotron/providers/espn/cooldown_active_error.rb +13 -0
- data/app/providers/jumbotron/providers/espn/provider_failure.rb +40 -0
- data/app/providers/jumbotron/providers/espn/resources/competition_odds.rb +29 -0
- data/app/providers/jumbotron/providers/espn/resources/scoreboard.rb +90 -0
- data/app/providers/jumbotron/providers/espn/resources/teams.rb +19 -0
- data/app/scheduling/jumbotron/scheduling/backends/sidekiq_cron.rb +70 -0
- data/app/scheduling/jumbotron/scheduling/backends/solid_queue.rb +92 -0
- data/app/scheduling/jumbotron/scheduling/clock.rb +61 -0
- data/app/scheduling/jumbotron/scheduling/desired_schedules.rb +51 -0
- data/app/scheduling/jumbotron/scheduling/detect_integration.rb +45 -0
- data/app/scheduling/jumbotron/scheduling/materializer.rb +13 -0
- data/app/scheduling/jumbotron/scheduling/owned.rb +19 -0
- data/app/scheduling/jumbotron/scheduling/schedule_definition.rb +7 -0
- data/app/serializers/jumbotron/serializers/clients/espn/competition_odds/get.rb +17 -0
- data/app/serializers/jumbotron/serializers/clients/espn/scoreboard/get.rb +22 -0
- data/app/serializers/jumbotron/serializers/clients/espn/teams/get.rb +20 -0
- data/app/services/jumbotron/services/adapters/build_acquisition_scopes.rb +37 -0
- data/app/services/jumbotron/services/adapters/build_line_acquisition_scopes.rb +39 -0
- data/app/services/jumbotron/services/adapters/ensure_league.rb +29 -0
- data/app/services/jumbotron/services/adapters/execute_endpoint.rb +49 -0
- data/app/services/jumbotron/services/adapters/execute_line_acquisition_scope.rb +75 -0
- data/app/services/jumbotron/services/adapters/execute_synchronization_scope.rb +64 -0
- data/app/services/jumbotron/services/adapters/resolve_discovery.rb +53 -0
- data/app/services/jumbotron/services/adapters/resolve_game_update_policy.rb +74 -0
- data/app/services/jumbotron/services/adapters/resolve_line_update_policy.rb +74 -0
- data/app/services/jumbotron/services/adapters/select_eligible_games.rb +41 -0
- data/app/services/jumbotron/services/adapters/validate_sync_request.rb +51 -0
- data/app/services/jumbotron/services/canonical/attach_provider_identities.rb +59 -0
- data/app/services/jumbotron/services/canonical/derive_consensus_for_games.rb +18 -0
- data/app/services/jumbotron/services/canonical/derive_consensus_line.rb +70 -0
- data/app/services/jumbotron/services/canonical/ensure_bookmaker.rb +70 -0
- data/app/services/jumbotron/services/canonical/ensure_schedule_group.rb +40 -0
- data/app/services/jumbotron/services/canonical/ensure_season.rb +22 -0
- data/app/services/jumbotron/services/canonical/ensure_season_phase.rb +22 -0
- data/app/services/jumbotron/services/canonical/persist_line_observations.rb +145 -0
- data/app/services/jumbotron/services/canonical/record_observation_evidence.rb +51 -0
- data/app/services/jumbotron/services/canonical/resolve_current_lines.rb +21 -0
- data/app/services/jumbotron/services/canonical/resolve_current_lines_for_games.rb +69 -0
- data/app/services/jumbotron/services/canonical/upsert_game.rb +186 -0
- data/app/services/jumbotron/services/canonical/upsert_game_participant.rb +88 -0
- data/app/services/jumbotron/services/canonical/upsert_team.rb +90 -0
- data/app/services/jumbotron/services/canonical/upsert_venue.rb +92 -0
- data/app/services/jumbotron/services/public/assemble_public_game.rb +131 -0
- data/app/services/jumbotron/services/public/assemble_public_schedule.rb +63 -0
- data/app/services/jumbotron/services/public/derive_consensus_for_games.rb +21 -0
- data/app/services/jumbotron/services/public/load_games_by_ids.rb +18 -0
- data/app/services/jumbotron/services/public/load_included_lines.rb +47 -0
- data/app/services/jumbotron/services/public/map_public_consensus_lines.rb +44 -0
- data/app/services/jumbotron/services/public/map_public_current_lines.rb +27 -0
- data/app/services/jumbotron/services/public/read_game.rb +32 -0
- data/app/services/jumbotron/services/public/read_schedule.rb +116 -0
- data/app/services/jumbotron/services/public/read_schedule_groups.rb +90 -0
- data/app/services/jumbotron/services/public/resolve_current_lines_for_games.rb +21 -0
- data/app/synchronization/jumbotron/synchronization/lease.rb +127 -0
- data/app/workflows/jumbotron/workflows/execute_discovery_workflow.rb +131 -0
- data/app/workflows/jumbotron/workflows/execute_game_update_policy_workflow.rb +159 -0
- data/app/workflows/jumbotron/workflows/execute_line_update_policy_workflow.rb +156 -0
- data/app/workflows/jumbotron/workflows/public_read.rb +29 -0
- data/app/workflows/jumbotron/workflows/read_consensus_workflow.rb +37 -0
- data/app/workflows/jumbotron/workflows/read_current_lines_workflow.rb +27 -0
- data/app/workflows/jumbotron/workflows/read_game_workflow.rb +34 -0
- data/app/workflows/jumbotron/workflows/read_schedule_groups_workflow.rb +21 -0
- data/app/workflows/jumbotron/workflows/read_schedule_workflow.rb +48 -0
- data/app/workflows/jumbotron/workflows/synchronize_adapter_workflow.rb +183 -0
- data/db/migrate/20260812000001_create_jumbotron_foundational_catalog.rb +68 -0
- data/db/migrate/20260812000002_create_jumbotron_game_graph.rb +70 -0
- data/db/migrate/20260812000003_add_freshness_to_jumbotron_provider_mutated_entities.rb +31 -0
- data/db/migrate/20260812000004_create_jumbotron_observation_evidence.rb +34 -0
- data/db/migrate/20260813000001_create_jumbotron_schedule_groups.rb +24 -0
- data/db/migrate/20260814000001_create_jumbotron_line_observations.rb +38 -0
- data/db/migrate/20260820000001_add_progress_to_jumbotron_games.rb +14 -0
- data/lib/jumbotron/client.rb +40 -0
- data/lib/jumbotron/engine.rb +32 -0
- data/lib/jumbotron/version.rb +5 -0
- data/lib/jumbotron.rb +13 -0
- data/lib/tasks/jumbotron_schedules.rake +12 -0
- metadata +239 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
class Competition
|
|
8
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
9
|
+
field :id, type: Support.types.string, required: true
|
|
10
|
+
field :uid, type: Support.types.string, nullable: true
|
|
11
|
+
field :date, type: Support.types.string, nullable: true
|
|
12
|
+
field :start_date, type: Support.types.string, nullable: true
|
|
13
|
+
field :neutral_site, type: Support.types.boolean, nullable: true
|
|
14
|
+
field :status, type: Status::Result, required: true
|
|
15
|
+
field :venue, type: Venue::Result, nullable: true
|
|
16
|
+
field :competitors, type: Support.types.array(Competitor::Result), required: true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.call(payload)
|
|
20
|
+
Support.ensure_hash!(payload, label: "competition")
|
|
21
|
+
|
|
22
|
+
Result.build!(
|
|
23
|
+
id: coerce_id(Support.payload.fetch(payload, "id")),
|
|
24
|
+
uid: Support.payload.fetch(payload, "uid"),
|
|
25
|
+
date: Support.payload.fetch(payload, "date"),
|
|
26
|
+
start_date: Support.payload.fetch(payload, "startDate"),
|
|
27
|
+
neutral_site: Support.payload.fetch(payload, "neutralSite"),
|
|
28
|
+
status: Support.nest("status", Support.payload.fetch(payload, "status")) { |raw| Status.call(raw) },
|
|
29
|
+
venue: Support.nest("venue", Support.payload.fetch(payload, "venue")) { |raw| Venue.call(raw) },
|
|
30
|
+
competitors: map_competitors(Support.payload.fetch(payload, "competitors"))
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.coerce_id(raw)
|
|
35
|
+
return raw if raw.equal?(Support.missing) || raw.nil? || raw.is_a?(String)
|
|
36
|
+
|
|
37
|
+
raw.to_s
|
|
38
|
+
end
|
|
39
|
+
private_class_method :coerce_id
|
|
40
|
+
|
|
41
|
+
def self.map_competitors(raw)
|
|
42
|
+
if raw.equal?(Support.missing)
|
|
43
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
44
|
+
message: "competitors is required",
|
|
45
|
+
details: {
|
|
46
|
+
path: "competitors",
|
|
47
|
+
expected: "Array",
|
|
48
|
+
actual: "missing",
|
|
49
|
+
rule: "required",
|
|
50
|
+
messages: ["competitors is required"]
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unless raw.is_a?(Array)
|
|
56
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
57
|
+
message: "competitors must be an Array",
|
|
58
|
+
details: {
|
|
59
|
+
path: "competitors",
|
|
60
|
+
expected: "Array",
|
|
61
|
+
actual: raw.class.name,
|
|
62
|
+
rule: "type",
|
|
63
|
+
messages: ["competitors must be an Array"]
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
raw.each_with_index.map do |item, index|
|
|
69
|
+
Competitor.call(item)
|
|
70
|
+
rescue CommandTower::Clients::Errors::DeserializationError => e
|
|
71
|
+
raise CommandTower::Deserializers::Clients::Errors.prefix(e, "competitors[#{index}]")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
private_class_method :map_competitors
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class DrawOdds
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :money_line, type: Support.types.float, nullable: true
|
|
11
|
+
field :summary, type: Support.types.string, nullable: true
|
|
12
|
+
field :value, type: Support.types.float, nullable: true
|
|
13
|
+
field :handicap, type: Support.types.float, nullable: true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.call(payload)
|
|
17
|
+
Support.ensure_hash!(payload, label: "draw_odds")
|
|
18
|
+
|
|
19
|
+
Result.build!(
|
|
20
|
+
money_line: Support.payload.fetch(payload, "moneyLine"),
|
|
21
|
+
summary: Support.payload.fetch(payload, "summary"),
|
|
22
|
+
value: Support.payload.fetch(payload, "value"),
|
|
23
|
+
handicap: Support.payload.fetch(payload, "handicap")
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class Get
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :count, type: Support.types.integer, nullable: true
|
|
11
|
+
field :page_index, type: Support.types.integer, nullable: true
|
|
12
|
+
field :page_size, type: Support.types.integer, nullable: true
|
|
13
|
+
field :page_count, type: Support.types.integer, nullable: true
|
|
14
|
+
field :items, type: Support.types.array(Item::Result), required: true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.call(payload)
|
|
18
|
+
Support.ensure_hash!(payload, label: "competition_odds")
|
|
19
|
+
items_raw = Support.payload.fetch(payload, "items")
|
|
20
|
+
|
|
21
|
+
if items_raw.equal?(Support.missing)
|
|
22
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
23
|
+
message: "items is required",
|
|
24
|
+
details: {
|
|
25
|
+
path: "items",
|
|
26
|
+
expected: "Array",
|
|
27
|
+
actual: "missing",
|
|
28
|
+
rule: "required",
|
|
29
|
+
messages: ["items is required"]
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless items_raw.is_a?(Array)
|
|
35
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
36
|
+
message: "items must be an Array",
|
|
37
|
+
details: {
|
|
38
|
+
path: "items",
|
|
39
|
+
expected: "Array",
|
|
40
|
+
actual: items_raw.class.name,
|
|
41
|
+
rule: "type",
|
|
42
|
+
messages: ["items must be an Array"]
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
items = items_raw.each_with_index.map do |item, index|
|
|
48
|
+
Item.call(item)
|
|
49
|
+
rescue CommandTower::Clients::Errors::DeserializationError => e
|
|
50
|
+
raise CommandTower::Deserializers::Clients::Errors.prefix(e, "items[#{index}]")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Result.build!(
|
|
54
|
+
count: Support.whole_number(Support.payload.fetch(payload, "count")),
|
|
55
|
+
page_index: Support.whole_number(Support.payload.fetch(payload, "pageIndex")),
|
|
56
|
+
page_size: Support.whole_number(Support.payload.fetch(payload, "pageSize")),
|
|
57
|
+
page_count: Support.whole_number(Support.payload.fetch(payload, "pageCount")),
|
|
58
|
+
items: items
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class Item
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :ref, type: Support.types.string, nullable: true
|
|
11
|
+
field :details, type: Support.types.string, nullable: true
|
|
12
|
+
field :spread, type: Support.types.float, nullable: true
|
|
13
|
+
field :over_under, type: Support.types.float, nullable: true
|
|
14
|
+
field :over_odds, type: Support.types.float, nullable: true
|
|
15
|
+
field :under_odds, type: Support.types.float, nullable: true
|
|
16
|
+
field :listed_provider, type: ListedProvider::Result, required: true
|
|
17
|
+
field :home_team_odds, type: TeamOdds::Result, nullable: true
|
|
18
|
+
field :away_team_odds, type: TeamOdds::Result, nullable: true
|
|
19
|
+
field :draw_odds, type: DrawOdds::Result, nullable: true
|
|
20
|
+
field :open, type: MarketWindow::Result, nullable: true
|
|
21
|
+
field :current, type: MarketWindow::Result, nullable: true
|
|
22
|
+
field :close, type: MarketWindow::Result, nullable: true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.call(payload)
|
|
26
|
+
Support.ensure_hash!(payload, label: "competition_odds_item")
|
|
27
|
+
|
|
28
|
+
Result.build!(
|
|
29
|
+
ref: Support.payload.fetch(payload, "$ref"),
|
|
30
|
+
details: Support.payload.fetch(payload, "details"),
|
|
31
|
+
spread: Support.payload.fetch(payload, "spread"),
|
|
32
|
+
over_under: Support.payload.fetch(payload, "overUnder"),
|
|
33
|
+
over_odds: Support.payload.fetch(payload, "overOdds"),
|
|
34
|
+
under_odds: Support.payload.fetch(payload, "underOdds"),
|
|
35
|
+
listed_provider: Support.nest("provider", Support.payload.fetch(payload, "provider")) do |raw|
|
|
36
|
+
ListedProvider.call(raw)
|
|
37
|
+
end,
|
|
38
|
+
home_team_odds: Support.nest("homeTeamOdds", Support.payload.fetch(payload, "homeTeamOdds")) do |raw|
|
|
39
|
+
TeamOdds.call(raw)
|
|
40
|
+
end,
|
|
41
|
+
away_team_odds: Support.nest("awayTeamOdds", Support.payload.fetch(payload, "awayTeamOdds")) do |raw|
|
|
42
|
+
TeamOdds.call(raw)
|
|
43
|
+
end,
|
|
44
|
+
draw_odds: Support.nest("drawOdds", Support.payload.fetch(payload, "drawOdds")) do |raw|
|
|
45
|
+
DrawOdds.call(raw)
|
|
46
|
+
end,
|
|
47
|
+
open: Support.nest("open", Support.payload.fetch(payload, "open")) { |raw| MarketWindow.call(raw) },
|
|
48
|
+
current: Support.nest(
|
|
49
|
+
"current",
|
|
50
|
+
Support.payload.fetch(payload, "current")
|
|
51
|
+
) { |raw| MarketWindow.call(raw) },
|
|
52
|
+
close: Support.nest("close", Support.payload.fetch(payload, "close")) { |raw| MarketWindow.call(raw) }
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/app/deserializers/jumbotron/deserializers/clients/espn/competition_odds/listed_provider.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class ListedProvider
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :id, type: Support.types.string, required: true
|
|
11
|
+
field :name, type: Support.types.string, required: true
|
|
12
|
+
field :priority, type: Support.types.integer, nullable: true
|
|
13
|
+
field :ref, type: Support.types.string, nullable: true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.call(payload)
|
|
17
|
+
Support.ensure_hash!(payload, label: "listed_provider")
|
|
18
|
+
|
|
19
|
+
Result.build!(
|
|
20
|
+
id: Support.coerce_id(Support.payload.fetch(payload, "id")),
|
|
21
|
+
name: Support.payload.fetch(payload, "name"),
|
|
22
|
+
priority: Support.whole_number(Support.payload.fetch(payload, "priority")),
|
|
23
|
+
ref: Support.payload.fetch(payload, "$ref")
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class MarketWindow
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :over, type: Price::Result, nullable: true
|
|
11
|
+
field :under, type: Price::Result, nullable: true
|
|
12
|
+
field :total, type: PointSpread::Result, nullable: true
|
|
13
|
+
field :draw, type: Price::Result, nullable: true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.call(payload)
|
|
17
|
+
Support.ensure_hash!(payload, label: "market_window")
|
|
18
|
+
|
|
19
|
+
Result.build!(
|
|
20
|
+
over: Support.nest("over", Support.payload.fetch(payload, "over")) { |raw| Price.call(raw) },
|
|
21
|
+
under: Support.nest("under", Support.payload.fetch(payload, "under")) { |raw| Price.call(raw) },
|
|
22
|
+
total: Support.nest("total", Support.payload.fetch(payload, "total")) { |raw| PointSpread.call(raw) },
|
|
23
|
+
draw: Support.nest("draw", Support.payload.fetch(payload, "draw")) { |raw| Price.call(raw) }
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class PointSpread
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :american, type: Support.types.string, nullable: true
|
|
11
|
+
field :alternate_display_value, type: Support.types.string, nullable: true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.call(payload)
|
|
15
|
+
Support.ensure_hash!(payload, label: "point_spread")
|
|
16
|
+
|
|
17
|
+
Result.build!(
|
|
18
|
+
american: Support.payload.fetch(payload, "american"),
|
|
19
|
+
alternate_display_value: Support.payload.fetch(payload, "alternateDisplayValue")
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class Price
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :value, type: Support.types.float, nullable: true
|
|
11
|
+
field :display_value, type: Support.types.string, nullable: true
|
|
12
|
+
field :alternate_display_value, type: Support.types.string, nullable: true
|
|
13
|
+
field :decimal, type: Support.types.float, nullable: true
|
|
14
|
+
field :fraction, type: Support.types.string, nullable: true
|
|
15
|
+
field :american, type: Support.types.string, nullable: true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.call(payload)
|
|
19
|
+
Support.ensure_hash!(payload, label: "price")
|
|
20
|
+
|
|
21
|
+
Result.build!(
|
|
22
|
+
value: Support.payload.fetch(payload, "value"),
|
|
23
|
+
display_value: Support.payload.fetch(payload, "displayValue"),
|
|
24
|
+
alternate_display_value: Support.payload.fetch(payload, "alternateDisplayValue"),
|
|
25
|
+
decimal: Support.payload.fetch(payload, "decimal"),
|
|
26
|
+
fraction: Support.payload.fetch(payload, "fraction"),
|
|
27
|
+
american: Support.payload.fetch(payload, "american")
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class SideQuote
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :favorite, type: Support.types.boolean, nullable: true
|
|
11
|
+
field :point_spread, type: PointSpread::Result, nullable: true
|
|
12
|
+
field :spread, type: Price::Result, nullable: true
|
|
13
|
+
field :money_line, type: Price::Result, nullable: true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.call(payload)
|
|
17
|
+
Support.ensure_hash!(payload, label: "side_quote")
|
|
18
|
+
|
|
19
|
+
Result.build!(
|
|
20
|
+
favorite: Support.payload.fetch(payload, "favorite"),
|
|
21
|
+
point_spread: Support.nest("pointSpread", Support.payload.fetch(payload, "pointSpread")) do |raw|
|
|
22
|
+
PointSpread.call(raw)
|
|
23
|
+
end,
|
|
24
|
+
spread: Support.nest("spread", Support.payload.fetch(payload, "spread")) { |raw| Price.call(raw) },
|
|
25
|
+
money_line: Support.nest("moneyLine", Support.payload.fetch(payload, "moneyLine")) do |raw|
|
|
26
|
+
Price.call(raw)
|
|
27
|
+
end
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module CompetitionOdds
|
|
8
|
+
class TeamOdds
|
|
9
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :favorite, type: Support.types.boolean, nullable: true
|
|
11
|
+
field :underdog, type: Support.types.boolean, nullable: true
|
|
12
|
+
field :money_line, type: Support.types.float, nullable: true
|
|
13
|
+
field :spread_odds, type: Support.types.float, nullable: true
|
|
14
|
+
field :open, type: SideQuote::Result, nullable: true
|
|
15
|
+
field :current, type: SideQuote::Result, nullable: true
|
|
16
|
+
field :close, type: SideQuote::Result, nullable: true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.call(payload)
|
|
20
|
+
Support.ensure_hash!(payload, label: "team_odds")
|
|
21
|
+
|
|
22
|
+
Result.build!(
|
|
23
|
+
favorite: Support.payload.fetch(payload, "favorite"),
|
|
24
|
+
underdog: Support.payload.fetch(payload, "underdog"),
|
|
25
|
+
money_line: Support.payload.fetch(payload, "moneyLine"),
|
|
26
|
+
spread_odds: Support.payload.fetch(payload, "spreadOdds"),
|
|
27
|
+
open: Support.nest("open", Support.payload.fetch(payload, "open")) { |raw| SideQuote.call(raw) },
|
|
28
|
+
current: Support.nest(
|
|
29
|
+
"current",
|
|
30
|
+
Support.payload.fetch(payload, "current")
|
|
31
|
+
) { |raw| SideQuote.call(raw) },
|
|
32
|
+
close: Support.nest("close", Support.payload.fetch(payload, "close")) { |raw| SideQuote.call(raw) }
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
class Competitor
|
|
8
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
9
|
+
field :id, type: Support.types.string, required: true
|
|
10
|
+
field :uid, type: Support.types.string, nullable: true
|
|
11
|
+
field :home_away, type: Support.types.string, required: true
|
|
12
|
+
field :score, type: Support.types.string, nullable: true
|
|
13
|
+
field :winner, type: Support.types.boolean, nullable: true
|
|
14
|
+
field :order, type: Support.types.integer, nullable: true
|
|
15
|
+
field :team, type: Team::Result, required: true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.call(payload)
|
|
19
|
+
Support.ensure_hash!(payload, label: "competitor")
|
|
20
|
+
|
|
21
|
+
Result.build!(
|
|
22
|
+
id: coerce_id(Support.payload.fetch(payload, "id")),
|
|
23
|
+
uid: Support.payload.fetch(payload, "uid"),
|
|
24
|
+
home_away: Support.payload.fetch(payload, "homeAway"),
|
|
25
|
+
score: coerce_score(Support.payload.fetch(payload, "score")),
|
|
26
|
+
winner: Support.payload.fetch(payload, "winner"),
|
|
27
|
+
order: Support.payload.fetch(payload, "order"),
|
|
28
|
+
team: Support.nest("team", Support.payload.fetch(payload, "team")) { |raw| Team.call(raw) }
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.coerce_id(raw)
|
|
33
|
+
return raw if raw.equal?(Support.missing) || raw.nil? || raw.is_a?(String)
|
|
34
|
+
|
|
35
|
+
raw.to_s
|
|
36
|
+
end
|
|
37
|
+
private_class_method :coerce_id
|
|
38
|
+
|
|
39
|
+
def self.coerce_score(raw)
|
|
40
|
+
return raw if raw.equal?(Support.missing) || raw.nil? || raw.is_a?(String)
|
|
41
|
+
|
|
42
|
+
raw.to_s
|
|
43
|
+
end
|
|
44
|
+
private_class_method :coerce_score
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
class Event
|
|
8
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
9
|
+
field :id, type: Support.types.string, required: true
|
|
10
|
+
field :uid, type: Support.types.string, nullable: true
|
|
11
|
+
field :date, type: Support.types.string, required: true
|
|
12
|
+
field :name, type: Support.types.string, required: true
|
|
13
|
+
field :short_name, type: Support.types.string, nullable: true
|
|
14
|
+
field :season, type: Season::Result, nullable: true
|
|
15
|
+
field :week, type: Week::Result, nullable: true
|
|
16
|
+
field :status, type: Status::Result, nullable: true
|
|
17
|
+
field :competitions, type: Support.types.array(Competition::Result), required: true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.call(payload)
|
|
21
|
+
Support.ensure_hash!(payload, label: "event")
|
|
22
|
+
|
|
23
|
+
Result.build!(
|
|
24
|
+
id: coerce_id(Support.payload.fetch(payload, "id")),
|
|
25
|
+
uid: Support.payload.fetch(payload, "uid"),
|
|
26
|
+
date: Support.payload.fetch(payload, "date"),
|
|
27
|
+
name: Support.payload.fetch(payload, "name"),
|
|
28
|
+
short_name: Support.payload.fetch(payload, "shortName"),
|
|
29
|
+
season: Support.nest("season", Support.payload.fetch(payload, "season")) { |raw| Season.call(raw) },
|
|
30
|
+
week: Support.nest("week", Support.payload.fetch(payload, "week")) { |raw| Week.call(raw) },
|
|
31
|
+
status: Support.nest("status", Support.payload.fetch(payload, "status")) { |raw| Status.call(raw) },
|
|
32
|
+
competitions: map_competitions(Support.payload.fetch(payload, "competitions"))
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.coerce_id(raw)
|
|
37
|
+
return raw if raw.equal?(Support.missing) || raw.nil? || raw.is_a?(String)
|
|
38
|
+
|
|
39
|
+
raw.to_s
|
|
40
|
+
end
|
|
41
|
+
private_class_method :coerce_id
|
|
42
|
+
|
|
43
|
+
def self.map_competitions(raw)
|
|
44
|
+
if raw.equal?(Support.missing)
|
|
45
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
46
|
+
message: "competitions is required",
|
|
47
|
+
details: {
|
|
48
|
+
path: "competitions",
|
|
49
|
+
expected: "Array",
|
|
50
|
+
actual: "missing",
|
|
51
|
+
rule: "required",
|
|
52
|
+
messages: ["competitions is required"]
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
unless raw.is_a?(Array)
|
|
58
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
59
|
+
message: "competitions must be an Array",
|
|
60
|
+
details: {
|
|
61
|
+
path: "competitions",
|
|
62
|
+
expected: "Array",
|
|
63
|
+
actual: raw.class.name,
|
|
64
|
+
rule: "type",
|
|
65
|
+
messages: ["competitions must be an Array"]
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
raw.each_with_index.map do |item, index|
|
|
71
|
+
Competition.call(item)
|
|
72
|
+
rescue CommandTower::Clients::Errors::DeserializationError => e
|
|
73
|
+
raise CommandTower::Deserializers::Clients::Errors.prefix(e, "competitions[#{index}]")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
private_class_method :map_competitions
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jumbotron
|
|
4
|
+
module Deserializers
|
|
5
|
+
module Clients
|
|
6
|
+
module Espn
|
|
7
|
+
module Scoreboard
|
|
8
|
+
class Get
|
|
9
|
+
class CalendarPhase < CommandTower::Deserializers::Clients::Result
|
|
10
|
+
field :season_type, type: Support.types.integer, required: true
|
|
11
|
+
field :weeks, type: Support.types.array(Support.types.integer), required: true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Result < CommandTower::Deserializers::Clients::Result
|
|
15
|
+
field :events, type: Support.types.array(Event::Result), required: true
|
|
16
|
+
field :season_calendar, type: Support.types.array(CalendarPhase), nullable: true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.call(payload)
|
|
20
|
+
Support.ensure_hash!(payload, label: "scoreboard")
|
|
21
|
+
events_raw = Support.payload.fetch(payload, "events")
|
|
22
|
+
|
|
23
|
+
if events_raw.equal?(Support.missing)
|
|
24
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
25
|
+
message: "events is required",
|
|
26
|
+
details: {
|
|
27
|
+
path: "events",
|
|
28
|
+
expected: "Array",
|
|
29
|
+
actual: "missing",
|
|
30
|
+
rule: "required",
|
|
31
|
+
messages: ["events is required"]
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
unless events_raw.is_a?(Array)
|
|
37
|
+
raise CommandTower::Clients::Errors::DeserializationError.new(
|
|
38
|
+
message: "events must be an Array",
|
|
39
|
+
details: {
|
|
40
|
+
path: "events",
|
|
41
|
+
expected: "Array",
|
|
42
|
+
actual: events_raw.class.name,
|
|
43
|
+
rule: "type",
|
|
44
|
+
messages: ["events must be an Array"]
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
events = events_raw.each_with_index.map do |item, index|
|
|
50
|
+
Event.call(item)
|
|
51
|
+
rescue CommandTower::Clients::Errors::DeserializationError => e
|
|
52
|
+
raise CommandTower::Deserializers::Clients::Errors.prefix(e, "events[#{index}]")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Result.build!(events: events, season_calendar: parse_season_calendar(payload))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.parse_season_calendar(payload)
|
|
59
|
+
leagues = payload["leagues"]
|
|
60
|
+
return [] unless leagues.is_a?(Array) && leagues.first.is_a?(Hash)
|
|
61
|
+
|
|
62
|
+
calendar = leagues.first["calendar"]
|
|
63
|
+
return [] unless calendar.is_a?(Array)
|
|
64
|
+
|
|
65
|
+
calendar.filter_map do |entry|
|
|
66
|
+
next unless entry.is_a?(Hash)
|
|
67
|
+
|
|
68
|
+
season_type = entry["value"].to_i
|
|
69
|
+
next if season_type <= 0 || season_type == 4
|
|
70
|
+
|
|
71
|
+
weeks = Array(entry["entries"]).filter_map do |week|
|
|
72
|
+
next unless week.is_a?(Hash)
|
|
73
|
+
|
|
74
|
+
number = week["value"].to_i
|
|
75
|
+
number if number.positive?
|
|
76
|
+
end.uniq
|
|
77
|
+
next if weeks.empty?
|
|
78
|
+
|
|
79
|
+
CalendarPhase.build!(season_type: season_type, weeks: weeks)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
private_class_method :parse_season_calendar
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|