From c4902d85d54b108c789e2e013837486a0875c8bd Mon Sep 17 00:00:00 2001 From: sto Date: Thu, 15 May 2025 08:57:25 +0200 Subject: [PATCH] Messages to completions conversion --- app/controllers/completions_controller.rb | 8 ++--- app/controllers/contests_controller.rb | 2 +- app/controllers/messages_controller.rb | 32 ++++++++++++++++--- app/models/completion.rb | 14 ++++++-- app/models/message.rb | 3 ++ app/policies/contest_policy.rb | 4 +++ app/views/completions/_form.html.slim | 16 ++++++++-- app/views/contests/show.html.slim | 8 +++-- config/locales/en.yml | 12 +++++++ config/locales/fr.yml | 12 +++++++ config/routes.rb | 4 ++- .../20250515061619_add_author_to_message.rb | 12 +++++++ ...50515062154_add_display_time_to_message.rb | 12 +++++++ db/schema.rb | 4 ++- spec/factories/messages.rb | 2 ++ spec/models/message_spec.rb | 2 ++ 16 files changed, 127 insertions(+), 20 deletions(-) create mode 100644 db/migrate/20250515061619_add_author_to_message.rb create mode 100644 db/migrate/20250515062154_add_display_time_to_message.rb diff --git a/app/controllers/completions_controller.rb b/app/controllers/completions_controller.rb index 6281620..0123eba 100644 --- a/app/controllers/completions_controller.rb +++ b/app/controllers/completions_controller.rb @@ -27,8 +27,6 @@ class CompletionsController < ApplicationController extend_completions!(@completion.contestant) redirect_to contest_path(@contest) else - logger = Logger.new(STDOUT) - logger.info(@completion.errors) render :new, status: :unprocessable_entity end end @@ -36,9 +34,7 @@ class CompletionsController < ApplicationController def update authorize @contest - if params[:contestant_id] - @completion.contestant_id = params[:contestant_id] - end + @completion.contestant_id = params[:contestant_id] if params[:contestant_id] if @completion.update(completion_params) extend_completions!(@completion.contestant) redirect_to @contest @@ -74,6 +70,6 @@ class CompletionsController < ApplicationController end def completion_params - params.expect(completion: [ :time_seconds, :contestant_id, :puzzle_id ]) + params.expect(completion: [ :display_time_from_start, :contestant_id, :puzzle_id ]) end end diff --git a/app/controllers/contests_controller.rb b/app/controllers/contests_controller.rb index 3f44330..c26ea17 100644 --- a/app/controllers/contests_controller.rb +++ b/app/controllers/contests_controller.rb @@ -17,7 +17,7 @@ class ContestsController < ApplicationController @action_path = edit_contest_path(@contest) @contestants = @contest.contestants.order(:name) @puzzles = @contest.puzzles.order(:id) - @messages = @contest.messages.order(:id) + @messages = @contest.messages.order(:time_seconds) set_badges end diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 5469ef7..81142d2 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -1,8 +1,15 @@ class MessagesController < ApplicationController + include CompletionsConcern + skip_before_action :verify_authenticity_token, only: %i[ create ] - before_action :set_contest, only: %i[ destroy ] - before_action :set_message, only: %i[ destroy ] + before_action :set_contest, only: %i[ convert destroy ] + before_action :set_message, only: %i[ convert destroy ] + before_action :set_data, only: %i[ convert ] + + def self.local_prefixes + super + [ "completions" ] + end def create allow_unauthenticated_access @@ -10,7 +17,8 @@ class MessagesController < ApplicationController @message_params = message_params @contest = Contest.find_by_token_for(:token, params[:token]) - @message = Message.new(text: params[:text], time_seconds: params[:time_seconds], contest: @contest) + @message = Message.new(text: params[:text], time_seconds: params[:time_seconds], + display_time: display_time(params[:time_seconds]), contest: @contest) if @contest && @message.save respond_to do |format| format.json { render json: {}, status: 200 } @@ -22,6 +30,15 @@ class MessagesController < ApplicationController end end + def convert + authorize @contest + + @completion = Completion.new() + @completion.display_time_from_start = @message.display_time + + render "completions/new" + end + def destroy authorize @contest @@ -36,10 +53,15 @@ class MessagesController < ApplicationController end def set_message - @message = Message.find(params[:id]) + @message = Message.find(params[:message_id]) + end + + def set_data + @contestants = @contest.contestants + @puzzles = @contest.puzzles end def message_params - params.expect(message: [ :text, :time_seconds, :token ]) + params.expect(message: [ :author, :text, :time_seconds, :token ]) end end diff --git a/app/models/completion.rb b/app/models/completion.rb index bced86e..b9f7dd2 100644 --- a/app/models/completion.rb +++ b/app/models/completion.rb @@ -29,7 +29,17 @@ class Completion < ApplicationRecord belongs_to :contestant belongs_to :puzzle - validates :time_seconds, presence: true - validates_numericality_of :time_seconds + before_save :add_time_seconds + + validates :display_time_from_start, presence: true, format: { with: /\A((\d\d|\d):\d\d|\d\d|\d):\d\d\z/ } validates :puzzle_id, uniqueness: { scope: :contestant } + + def add_time_seconds + arr = display_time_from_start.split(":") + if arr.size == 3 + self.time_seconds = arr[0].to_i * 3600 + arr[1].to_i * 60 + arr[2].to_i + elsif arr.size == 2 + self.time_seconds = arr[0].to_i * 60 + arr[1].to_i + end + end end diff --git a/app/models/message.rb b/app/models/message.rb index 42187a1..db17498 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -3,6 +3,8 @@ # Table name: messages # # id :integer not null, primary key +# author :string +# display_time :string # text :string not null # time_seconds :integer not null # created_at :datetime not null @@ -20,5 +22,6 @@ class Message < ApplicationRecord belongs_to :contest + validates :author, presence: true validates :text, presence: true end diff --git a/app/policies/contest_policy.rb b/app/policies/contest_policy.rb index c71a77c..d309f97 100644 --- a/app/policies/contest_policy.rb +++ b/app/policies/contest_policy.rb @@ -15,6 +15,10 @@ class ContestPolicy < ApplicationPolicy true end + def convert? + record.user.id == user.id || user.admin? + end + def edit? record.user.id == user.id || user.admin? end diff --git a/app/views/completions/_form.html.slim b/app/views/completions/_form.html.slim index 4957469..cffe685 100644 --- a/app/views/completions/_form.html.slim +++ b/app/views/completions/_form.html.slim @@ -1,9 +1,21 @@ = form_with model: completion, url: url, method: method do |form| + - if @message + .row.mb-3 + .col + h4 = t("messages.singular").capitalize + .alert.alert-secondary + b + = @message.author + br + = @message.text + .row + .col + h4 = t("completions.singular").capitalize .row.mb-3 .col .form-floating - = form.text_field :time_seconds, autocomplete: "off", class: "form-control" - = form.label :time_seconds, class: "required" + = form.text_field :display_time_from_start, autocomplete: "off", class: "form-control" + = form.label :display_time_from_start, class: "required" .row.mb-3 .col .form-floating diff --git a/app/views/contests/show.html.slim b/app/views/contests/show.html.slim index b0d35d7..6e4b7ab 100644 --- a/app/views/contests/show.html.slim +++ b/app/views/contests/show.html.slim @@ -51,17 +51,21 @@ tr th scope="col" | Time + th scope="col" + | Author th scope="col" | Text tbody - @messages.each do |message| tr.align-middle scope="row" td - = message.time_seconds + = message.display_time + td + = message.author td = message.text td - a.btn.btn-sm.btn-secondary href="" style="white-space: nowrap;" + a.btn.btn-sm.btn-secondary href=contest_message_convert_path(@contest, message) style="white-space: nowrap;" | Add completion td = link_to "Delete", contest_message_path(@contest, message), data: { turbo_method: :delete }, class: "btn btn-sm btn-danger" diff --git a/config/locales/en.yml b/config/locales/en.yml index 3b14284..d4dd454 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -41,11 +41,20 @@ en: email_address: "Email address" lang: "Language" password: "New password" + errors: + models: + completion: + attributes: + display_time_from_start: + invalid: "Allowed formats: xx:xx:xx, x:xx:xx, xx:xx, x:xx" + puzzle_id: + taken: "This contestant has already completed this puzzle" completions: edit: title: "Edit completion" new: title: "New completion" + singular: "completion" contests: edit: title: "Edit contest settings" @@ -80,7 +89,10 @@ en: create: "Create" save: "Save" messages: + convert: + title: "Convert message into completion" plural: "messages" + singular: "message" nav: users: "Users" home: "Home" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index b206f75..1b284a3 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -12,11 +12,20 @@ fr: email_address: "Adresse email" lang: "Langue de l'interface" password: "Nouveau mot de passe" + errors: + models: + completion: + attributes: + display_time_from_start: + invalid: "Formats autorisés: xx:xx:xx, x:xx:xx, xx:xx, x:xx" + puzzle_id: + taken: "Ce.tte participant.e a déjà complété ce puzzle" completions: edit: title: "Modifier la complétion" new: title: "Nouvelle complétion" + singular: "complétion" contests: edit: title: "Paramètres du concours" @@ -51,7 +60,10 @@ fr: create: "Créer" save: "Modifier" messages: + convert: + title: "Conversion d'un message en complétion" plural: "messages" + singular: "message" nav: users: "Utilisateur.ices" home: "Accueil" diff --git a/config/routes.rb b/config/routes.rb index 451aecc..11cd9ff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,9 @@ Rails.application.routes.draw do resources :completions resources :contestants resources :puzzles - resources :messages + resources :messages, only: :destroy do + get "convert", to: "messages#convert" + end end resources :passwords, param: :token resource :session diff --git a/db/migrate/20250515061619_add_author_to_message.rb b/db/migrate/20250515061619_add_author_to_message.rb new file mode 100644 index 0000000..03004cb --- /dev/null +++ b/db/migrate/20250515061619_add_author_to_message.rb @@ -0,0 +1,12 @@ +class AddAuthorToMessage < ActiveRecord::Migration[8.0] + def change + add_column :messages, :author, :string + + Message.find_each do |message| + message.author = "Unknown" + message.save + end + + change_column_null :messages, :author, true + end +end diff --git a/db/migrate/20250515062154_add_display_time_to_message.rb b/db/migrate/20250515062154_add_display_time_to_message.rb new file mode 100644 index 0000000..739a8ac --- /dev/null +++ b/db/migrate/20250515062154_add_display_time_to_message.rb @@ -0,0 +1,12 @@ +class AddDisplayTimeToMessage < ActiveRecord::Migration[8.0] + def change + add_column :messages, :display_time, :string + + Message.find_each do |message| + message.display_time = "12:30" + message.save + end + + change_column_null :messages, :display_time, true + end +end diff --git a/db/schema.rb b/db/schema.rb index 385afd1..d91116d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_05_11_173749) do +ActiveRecord::Schema[8.0].define(version: 2025_05_15_062154) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -92,6 +92,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_11_173749) do t.string "text", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "author" + t.string "display_time" t.index ["contest_id"], name: "index_messages_on_contest_id" end diff --git a/spec/factories/messages.rb b/spec/factories/messages.rb index d3436db..4ad1192 100644 --- a/spec/factories/messages.rb +++ b/spec/factories/messages.rb @@ -3,6 +3,8 @@ # Table name: messages # # id :integer not null, primary key +# author :string +# display_time :string # text :string not null # time_seconds :integer not null # created_at :datetime not null diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index 9157728..cdabb8f 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -3,6 +3,8 @@ # Table name: messages # # id :integer not null, primary key +# author :string +# display_time :string # text :string not null # time_seconds :integer not null # created_at :datetime not null