Implement projected time
This commit is contained in:
@@ -26,9 +26,7 @@ class CompletionsController < ApplicationController
|
|||||||
@completion = Completion.new(completion_params)
|
@completion = Completion.new(completion_params)
|
||||||
@completion.contest_id = @contest.id
|
@completion.contest_id = @contest.id
|
||||||
if @completion.save
|
if @completion.save
|
||||||
if @completion.display_time_from_start.present?
|
extend_completions!(@completion.contestant)
|
||||||
extend_completions!(@completion.contestant)
|
|
||||||
end
|
|
||||||
if @contestant && !params[:completion].key?(:message_id)
|
if @contestant && !params[:completion].key?(:message_id)
|
||||||
redirect_to edit_contest_contestant_path(@contest, @contestant), notice: t("completions.new.notice")
|
redirect_to edit_contest_contestant_path(@contest, @contestant), notice: t("completions.new.notice")
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -9,24 +9,38 @@ module CompletionsConcern
|
|||||||
end
|
end
|
||||||
|
|
||||||
def display_time(time)
|
def display_time(time)
|
||||||
|
if time == nil
|
||||||
|
return ""
|
||||||
|
end
|
||||||
h = time / 3600
|
h = time / 3600
|
||||||
m = (time % 3600) / 60
|
m = (time % 3600) / 60
|
||||||
s = (time % 3600) % 60
|
s = (time % 3600) % 60
|
||||||
if h > 0
|
if h > 0
|
||||||
return h.to_s + ":" + pad(m) + ":" + pad(s)
|
return h.to_s + ":" + pad(m) + ":" + pad(s)
|
||||||
elsif m > 0
|
|
||||||
return m.to_s + ":" + pad(s)
|
|
||||||
end
|
end
|
||||||
s.to_s
|
m.to_s + ":" + pad(s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def extend_completions!(contestant)
|
def extend_completions!(contestant)
|
||||||
current_time_from_start = 0
|
completions = contestant.completions
|
||||||
contestant.completions.order(:time_seconds).each do |completion|
|
puzzles = contestant.contest.puzzles
|
||||||
completion.update(display_time_from_start: display_time(completion.time_seconds),
|
if puzzles.length > 1
|
||||||
display_relative_time: display_time(completion.time_seconds - current_time_from_start))
|
current_time_from_start = 0
|
||||||
current_time_from_start = completion.time_seconds
|
completions.order(:time_seconds).each do |completion|
|
||||||
|
completion.update(display_time_from_start: display_time(completion.time_seconds),
|
||||||
|
display_relative_time: display_time(completion.time_seconds - current_time_from_start))
|
||||||
|
current_time_from_start = completion.time_seconds
|
||||||
|
end
|
||||||
|
contestant.update(display_time: display_time(current_time_from_start), time_seconds: current_time_from_start)
|
||||||
|
elsif puzzles.length == 1 && completions.length >= 1
|
||||||
|
if completions[0].remaining_pieces != nil
|
||||||
|
contestant.update(
|
||||||
|
display_time: "#{display_time(completions[0].time_seconds)} - #{puzzles[0].pieces - completions[0].remaining_pieces}p",
|
||||||
|
time_seconds: completions[0].projected_time
|
||||||
|
)
|
||||||
|
else
|
||||||
|
contestant.update(display_time: display_time(completions[0].time_seconds), time_seconds: completions[0].time_seconds)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
contestant.update(display_time: display_time(current_time_from_start), time_seconds: current_time_from_start)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ class ContestsController < ApplicationController
|
|||||||
contestant = Contestant.create(contest: @contest, name: @offline.name, offline: @offline)
|
contestant = Contestant.create(contest: @contest, name: @offline.name, offline: @offline)
|
||||||
Completion.create(contest: @contest,
|
Completion.create(contest: @contest,
|
||||||
contestant: contestant,
|
contestant: contestant,
|
||||||
|
offline: @offline,
|
||||||
puzzle: @contest.puzzles[0],
|
puzzle: @contest.puzzles[0],
|
||||||
completed: @offline.completed,
|
completed: @offline.completed,
|
||||||
display_time_from_start: dp,
|
display_time_from_start: dp,
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ module ContestsHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def display_time(time)
|
def display_time(time)
|
||||||
|
if time == nil
|
||||||
|
return ""
|
||||||
|
end
|
||||||
h = time / 3600
|
h = time / 3600
|
||||||
m = (time % 3600) / 60
|
m = (time % 3600) / 60
|
||||||
s = (time % 3600) % 60
|
s = (time % 3600) % 60
|
||||||
if h > 0
|
if h > 0
|
||||||
return h.to_s + ":" + pad(m) + ":" + pad(s)
|
return h.to_s + ":" + pad(m) + ":" + pad(s)
|
||||||
elsif m > 0
|
|
||||||
return m.to_s + ":" + pad(s)
|
|
||||||
end
|
end
|
||||||
"0:" + pad(s)
|
m.to_s + ":" + pad(s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
# display_relative_time :string
|
# display_relative_time :string
|
||||||
# display_time_from_start :string
|
# display_time_from_start :string
|
||||||
# missing_pieces :integer
|
# missing_pieces :integer
|
||||||
|
# projected_time :integer
|
||||||
# remaining_pieces :integer
|
# remaining_pieces :integer
|
||||||
# time_seconds :integer
|
# time_seconds :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
@@ -36,11 +37,12 @@ class Completion < ApplicationRecord
|
|||||||
belongs_to :puzzle
|
belongs_to :puzzle
|
||||||
belongs_to :message, optional: true
|
belongs_to :message, optional: true
|
||||||
|
|
||||||
before_save :add_time_seconds, if: -> { display_time_from_start.present? }
|
has_one :offline, dependent: :destroy
|
||||||
before_save :nullify_display_time
|
|
||||||
before_save :clean_pieces
|
|
||||||
|
|
||||||
validates :display_time_from_start, presence: true, format: { with: /\A(((\d\d|\d):\d\d|\d\d|\d):\d\d|\d\d|\d)\z/ }, if: -> { completed }
|
before_save :clean_pieces
|
||||||
|
before_save :compute_projected_time
|
||||||
|
|
||||||
|
validates :display_time_from_start, presence: true, format: { with: /\A(((\d\d|\d):\d\d|\d\d|\d):\d\d|\d\d|\d)\z/ }
|
||||||
validates :remaining_pieces, presence: true, if: -> { !completed }
|
validates :remaining_pieces, presence: true, if: -> { !completed }
|
||||||
validates :contestant_id, uniqueness: { scope: :puzzle }, if: -> { contest.puzzles.size == 1 }
|
validates :contestant_id, uniqueness: { scope: :puzzle }, if: -> { contest.puzzles.size == 1 }
|
||||||
validates :puzzle_id, uniqueness: { scope: :contestant }, if: -> { contest.puzzles.size > 1 }
|
validates :puzzle_id, uniqueness: { scope: :contestant }, if: -> { contest.puzzles.size > 1 }
|
||||||
@@ -53,21 +55,18 @@ class Completion < ApplicationRecord
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def nullify_display_time
|
|
||||||
if self.remaining_pieces
|
|
||||||
self.display_time_from_start = nil
|
|
||||||
self.display_relative_time = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_time_seconds
|
def add_time_seconds
|
||||||
arr = display_time_from_start.split(":")
|
if display_time_from_start.present?
|
||||||
if arr.size == 3
|
arr = display_time_from_start.split(":")
|
||||||
self.time_seconds = arr[0].to_i * 3600 + arr[1].to_i * 60 + arr[2].to_i
|
if arr.size == 3
|
||||||
elsif arr.size == 2
|
self.time_seconds = arr[0].to_i * 3600 + arr[1].to_i * 60 + arr[2].to_i
|
||||||
self.time_seconds = arr[0].to_i * 60 + arr[1].to_i
|
elsif arr.size == 2
|
||||||
elsif arr.size == 1
|
self.time_seconds = arr[0].to_i * 60 + arr[1].to_i
|
||||||
self.time_seconds = arr[0].to_i
|
elsif arr.size == 1
|
||||||
|
self.time_seconds = arr[0].to_i
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.time_seconds = 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -78,4 +77,16 @@ class Completion < ApplicationRecord
|
|||||||
self.missing_pieces = nil
|
self.missing_pieces = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compute_projected_time
|
||||||
|
add_time_seconds
|
||||||
|
if self.completed
|
||||||
|
self.projected_time = self.time_seconds
|
||||||
|
elsif self.offline.present?
|
||||||
|
assembled_time = self.time_seconds
|
||||||
|
assembled_pieces = self.puzzle.pieces - self.remaining_pieces
|
||||||
|
pieces_per_second = assembled_pieces.to_f / assembled_time.to_f
|
||||||
|
self.projected_time = assembled_time + Integer(self.remaining_pieces.to_f / pieces_per_second)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,14 +2,15 @@
|
|||||||
#
|
#
|
||||||
# Table name: contestants
|
# Table name: contestants
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# display_time :string
|
# display_time :string
|
||||||
# email :string
|
# email :string
|
||||||
# name :string
|
# name :string
|
||||||
# time_seconds :integer
|
# projected_time :string
|
||||||
# created_at :datetime not null
|
# time_seconds :integer
|
||||||
# updated_at :datetime not null
|
# created_at :datetime not null
|
||||||
# contest_id :integer not null
|
# updated_at :datetime not null
|
||||||
|
# contest_id :integer not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
@@ -22,7 +23,7 @@
|
|||||||
class Contestant < ApplicationRecord
|
class Contestant < ApplicationRecord
|
||||||
belongs_to :contest
|
belongs_to :contest
|
||||||
has_many :completions, dependent: :destroy
|
has_many :completions, dependent: :destroy
|
||||||
has_one :offline
|
has_one :offline, dependent: :destroy
|
||||||
has_and_belongs_to_many :categories
|
has_and_belongs_to_many :categories
|
||||||
|
|
||||||
before_validation :initialize_time_seconds_if_empty
|
before_validation :initialize_time_seconds_if_empty
|
||||||
|
|||||||
@@ -12,22 +12,26 @@
|
|||||||
# submitted :boolean
|
# submitted :boolean
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# completion_id :integer
|
||||||
# contest_id :integer not null
|
# contest_id :integer not null
|
||||||
# contestant_id :integer
|
# contestant_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_offlines_on_completion_id (completion_id)
|
||||||
# index_offlines_on_contest_id (contest_id)
|
# index_offlines_on_contest_id (contest_id)
|
||||||
# index_offlines_on_contestant_id (contestant_id)
|
# index_offlines_on_contestant_id (contestant_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# completion_id (completion_id => completions.id)
|
||||||
# contest_id (contest_id => contests.id)
|
# contest_id (contest_id => contests.id)
|
||||||
# contestant_id (contestant_id => contestants.id)
|
# contestant_id (contestant_id => contestants.id)
|
||||||
#
|
#
|
||||||
class Offline < ApplicationRecord
|
class Offline < ApplicationRecord
|
||||||
belongs_to :contest
|
belongs_to :contest
|
||||||
belongs_to :contestant, optional: true
|
belongs_to :contestant, optional: true
|
||||||
|
belongs_to :completion, optional: true
|
||||||
|
|
||||||
has_many_attached :images
|
has_many_attached :images
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
table.table.table-striped.table-hover
|
table.table.table-striped.table-hover
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
|
th scope="col"
|
||||||
|
= t("activerecord.attributes.completion.completed")
|
||||||
- if @contest.puzzles.size > 1
|
- if @contest.puzzles.size > 1
|
||||||
th scope="col"
|
th scope="col"
|
||||||
= t("activerecord.attributes.completion.display_time_from_start")
|
= t("activerecord.attributes.completion.display_time_from_start")
|
||||||
@@ -44,6 +46,8 @@
|
|||||||
- else
|
- else
|
||||||
th scope="col"
|
th scope="col"
|
||||||
= t("activerecord.attributes.completion.display_time")
|
= t("activerecord.attributes.completion.display_time")
|
||||||
|
th scope="col"
|
||||||
|
= t("activerecord.attributes.completion.projected_time")
|
||||||
th scope="col"
|
th scope="col"
|
||||||
= t("activerecord.attributes.completion.missing_pieces")
|
= t("activerecord.attributes.completion.missing_pieces")
|
||||||
th scope="col"
|
th scope="col"
|
||||||
@@ -54,10 +58,19 @@
|
|||||||
- @completions.each do |completion|
|
- @completions.each do |completion|
|
||||||
tr scope="row"
|
tr scope="row"
|
||||||
td
|
td
|
||||||
= completion.display_time_from_start
|
- if completion.completed
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-square" viewBox="0 0 16 16">
|
||||||
|
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
|
||||||
|
<path d="M10.97 4.97a.75.75 0 0 1 1.071 1.05l-3.992 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425z"/>
|
||||||
|
</svg>
|
||||||
|
td
|
||||||
|
= display_time(completion.time_seconds)
|
||||||
- if @contest.puzzles.size > 1
|
- if @contest.puzzles.size > 1
|
||||||
td
|
td
|
||||||
= completion.display_relative_time
|
= completion.display_relative_time
|
||||||
|
- else
|
||||||
|
td
|
||||||
|
= display_time(completion.projected_time)
|
||||||
td
|
td
|
||||||
= completion.missing_pieces
|
= completion.missing_pieces
|
||||||
td
|
td
|
||||||
|
|||||||
@@ -47,11 +47,11 @@ css:
|
|||||||
td
|
td
|
||||||
= contestant.completions.where(remaining_pieces: nil).length
|
= contestant.completions.where(remaining_pieces: nil).length
|
||||||
td style="position: relative"
|
td style="position: relative"
|
||||||
- if index > 0 && contestant.time_seconds > 0 && contestant.completions.where(remaining_pieces: nil).size > 0
|
- if index > 0 && contestant.time_seconds > 0 && contestant.completions.where(completed: true).size > 0
|
||||||
.relative-time style="position:absolute; margin: 1px 0 0 64px; font-size: 14px; color: grey"
|
.relative-time style="position:absolute; margin: 1px 0 0 100px; font-size: 14px; color: grey"
|
||||||
|> +
|
|> +
|
||||||
= display_time(contestant.time_seconds - @contestants[index - 1].time_seconds)
|
= display_time(contestant.time_seconds - @contestants[index - 1].time_seconds)
|
||||||
= contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? "#{contestant.completions.map{|completion| completion.puzzle.pieces}.sum - contestant.completions[-1].remaining_pieces}p" : contestant.display_time
|
= contestant.display_time
|
||||||
.col-1
|
.col-1
|
||||||
.col-5
|
.col-5
|
||||||
- @contest.puzzles.each do |puzzle|
|
- @contest.puzzles.each do |puzzle|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ en:
|
|||||||
display_relative_time: Time for this puzzle
|
display_relative_time: Time for this puzzle
|
||||||
puzzle: Puzzle
|
puzzle: Puzzle
|
||||||
missing_pieces: Missing pieces
|
missing_pieces: Missing pieces
|
||||||
|
projected_time: Projected time
|
||||||
remaining_pieces: Remaining pieces (not completed puzzle)
|
remaining_pieces: Remaining pieces (not completed puzzle)
|
||||||
contest:
|
contest:
|
||||||
lang: Language for the public scoreboard
|
lang: Language for the public scoreboard
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ fr:
|
|||||||
display_relative_time: Temps pour ce puzzle
|
display_relative_time: Temps pour ce puzzle
|
||||||
puzzle: Puzzle
|
puzzle: Puzzle
|
||||||
missing_pieces: Pièces manquantes
|
missing_pieces: Pièces manquantes
|
||||||
|
projected_time: Temps projeté
|
||||||
remaining_pieces: Pièces restantes (puzzle non fini)
|
remaining_pieces: Pièces restantes (puzzle non fini)
|
||||||
contest:
|
contest:
|
||||||
lang: Langue pour le classement public
|
lang: Langue pour le classement public
|
||||||
@@ -183,7 +184,7 @@ fr:
|
|||||||
notice: Participant.e modifié.e
|
notice: Participant.e modifié.e
|
||||||
not_finished: Non terminé
|
not_finished: Non terminé
|
||||||
offline_participation: Participation hors-ligne
|
offline_participation: Participation hors-ligne
|
||||||
start_time: Image de début
|
start_image: Image de début
|
||||||
title: Participant.e
|
title: Participant.e
|
||||||
team_title: Équipe
|
team_title: Équipe
|
||||||
finalize_import:
|
finalize_import:
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddProjectedTimeToCompletion < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_column :completions, :projected_time, :integer
|
||||||
|
end
|
||||||
|
end
|
||||||
5
db/migrate/20251114093213_add_completion_to_offline.rb
Normal file
5
db/migrate/20251114093213_add_completion_to_offline.rb
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
class AddCompletionToOffline < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_reference :offlines, :completion, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
||||||
7
db/schema.rb
generated
7
db/schema.rb
generated
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
ActiveRecord::Schema[8.0].define(version: 2025_11_14_093213) do
|
||||||
create_table "active_storage_attachments", force: :cascade do |t|
|
create_table "active_storage_attachments", force: :cascade do |t|
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.string "record_type", null: false
|
t.string "record_type", null: false
|
||||||
@@ -67,6 +67,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
|||||||
t.integer "remaining_pieces"
|
t.integer "remaining_pieces"
|
||||||
t.integer "missing_pieces"
|
t.integer "missing_pieces"
|
||||||
t.boolean "completed"
|
t.boolean "completed"
|
||||||
|
t.integer "projected_time"
|
||||||
t.index ["contest_id"], name: "index_completions_on_contest_id"
|
t.index ["contest_id"], name: "index_completions_on_contest_id"
|
||||||
t.index ["contestant_id"], name: "index_completions_on_contestant_id"
|
t.index ["contestant_id"], name: "index_completions_on_contestant_id"
|
||||||
t.index ["message_id"], name: "index_completions_on_message_id"
|
t.index ["message_id"], name: "index_completions_on_message_id"
|
||||||
@@ -81,6 +82,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
|||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "display_time"
|
t.string "display_time"
|
||||||
t.integer "time_seconds"
|
t.integer "time_seconds"
|
||||||
|
t.string "projected_time"
|
||||||
t.index ["contest_id"], name: "index_contestants_on_contest_id"
|
t.index ["contest_id"], name: "index_contestants_on_contest_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -141,6 +143,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
|||||||
t.integer "missing_pieces"
|
t.integer "missing_pieces"
|
||||||
t.integer "remaining_pieces"
|
t.integer "remaining_pieces"
|
||||||
t.boolean "submitted"
|
t.boolean "submitted"
|
||||||
|
t.integer "completion_id"
|
||||||
|
t.index ["completion_id"], name: "index_offlines_on_completion_id"
|
||||||
t.index ["contest_id"], name: "index_offlines_on_contest_id"
|
t.index ["contest_id"], name: "index_offlines_on_contest_id"
|
||||||
t.index ["contestant_id"], name: "index_offlines_on_contestant_id"
|
t.index ["contestant_id"], name: "index_offlines_on_contestant_id"
|
||||||
end
|
end
|
||||||
@@ -185,6 +189,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_14_085123) do
|
|||||||
add_foreign_key "contestants", "contests"
|
add_foreign_key "contestants", "contests"
|
||||||
add_foreign_key "contests", "users"
|
add_foreign_key "contests", "users"
|
||||||
add_foreign_key "messages", "contests"
|
add_foreign_key "messages", "contests"
|
||||||
|
add_foreign_key "offlines", "completions"
|
||||||
add_foreign_key "offlines", "contestants"
|
add_foreign_key "offlines", "contestants"
|
||||||
add_foreign_key "offlines", "contests"
|
add_foreign_key "offlines", "contests"
|
||||||
add_foreign_key "puzzles", "contests"
|
add_foreign_key "puzzles", "contests"
|
||||||
|
|||||||
@@ -12,16 +12,19 @@
|
|||||||
# submitted :boolean
|
# submitted :boolean
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# completion_id :integer
|
||||||
# contest_id :integer not null
|
# contest_id :integer not null
|
||||||
# contestant_id :integer
|
# contestant_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_offlines_on_completion_id (completion_id)
|
||||||
# index_offlines_on_contest_id (contest_id)
|
# index_offlines_on_contest_id (contest_id)
|
||||||
# index_offlines_on_contestant_id (contestant_id)
|
# index_offlines_on_contestant_id (contestant_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# completion_id (completion_id => completions.id)
|
||||||
# contest_id (contest_id => contests.id)
|
# contest_id (contest_id => contests.id)
|
||||||
# contestant_id (contestant_id => contestants.id)
|
# contestant_id (contestant_id => contestants.id)
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -12,16 +12,19 @@
|
|||||||
# submitted :boolean
|
# submitted :boolean
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
# completion_id :integer
|
||||||
# contest_id :integer not null
|
# contest_id :integer not null
|
||||||
# contestant_id :integer
|
# contestant_id :integer
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
# index_offlines_on_completion_id (completion_id)
|
||||||
# index_offlines_on_contest_id (contest_id)
|
# index_offlines_on_contest_id (contest_id)
|
||||||
# index_offlines_on_contestant_id (contestant_id)
|
# index_offlines_on_contestant_id (contestant_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# completion_id (completion_id => completions.id)
|
||||||
# contest_id (contest_id => contests.id)
|
# contest_id (contest_id => contests.id)
|
||||||
# contestant_id (contestant_id => contestants.id)
|
# contestant_id (contestant_id => contestants.id)
|
||||||
#
|
#
|
||||||
|
|||||||
Reference in New Issue
Block a user