diff --git a/app/models/exercise.rb b/app/models/exercise.rb index 74e8a477..4c20fca6 100644 --- a/app/models/exercise.rb +++ b/app/models/exercise.rb @@ -24,6 +24,8 @@ class Exercise < ApplicationRecord has_many :tags, through: :exercise_tags accepts_nested_attributes_for :exercise_tags has_many :user_exercise_feedbacks + has_many :exercise_tips + has_many :tips, through: :exercise_tips has_many :external_users, source: :user, source_type: 'ExternalUser', through: :submissions has_many :internal_users, source: :user, source_type: 'InternalUser', through: :submissions diff --git a/app/models/exercise_tip.rb b/app/models/exercise_tip.rb new file mode 100644 index 00000000..98cc0318 --- /dev/null +++ b/app/models/exercise_tip.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class ExerciseTip < ApplicationRecord + belongs_to :exercise + belongs_to :tip + belongs_to :parent_exercise_tip, class_name: 'ExerciseTip', optional: true + attr_accessor :children + + # Ensure no parent tip is set if current tip has rank == 1 + validates :rank, exclusion: {in: [1]}, if: :parent_exercise_tip_id? + + validate :tip_chain?, if: :parent_exercise_tip_id? + + def tip_chain? + # Ensure each referenced parent exercise tip is set for this exercise + errors.add :parent_exercise_tip, I18n.t('activerecord.errors.messages.together', attribute: I18n.t('activerecord.attributes.exercise_tip.tip')) unless ExerciseTip.exists?(exercise: exercise, tip: parent_exercise_tip) + end +end diff --git a/app/models/tip.rb b/app/models/tip.rb new file mode 100644 index 00000000..5fb82d39 --- /dev/null +++ b/app/models/tip.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Tip < ApplicationRecord + include Creation + + has_many :exercise_tips + has_many :exercises, through: :exercise_tips + belongs_to :file_type, optional: true + validates_presence_of :file_type, if: :example? + validate :content? + + def content? + errors.add :description, I18n.t('activerecord.errors.messages.at_least', attribute: I18n.t('activerecord.attributes.tip.example')) unless [description?, example?].include?(true) + end + + def to_s + if title? + "#{I18n.t('activerecord.models.tip.one')}: #{title} (#{id})" + else + "#{I18n.t('activerecord.models.tip.one')} #{id}" + end + end +end diff --git a/config/locales/de.yml b/config/locales/de.yml index 5f4f60a0..28690b79 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -121,6 +121,10 @@ de: name: Name usage: Verwendet difficulty: Anteil an der Aufgabe + tip: + title: Titel + description: Beschreibung + example: Beispiel file_template: name: "Name" file_type: "Dateityp" @@ -215,6 +219,7 @@ de: errors: messages: together: 'muss zusammen mit %{attribute} definiert werden' + at_least: 'oder %{attribute} muss definiert sein' models: exercise: at_most_one_main_file: dürfen höchstens eine Hauptdatei enthalten diff --git a/config/locales/en.yml b/config/locales/en.yml index 5d7bd43d..65c68950 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -121,6 +121,10 @@ en: name: Name usage: Used difficulty: Share on the Exercise + tip: + title: title + description: description + example: example file_template: name: "Name" file_type: "File Type" @@ -215,6 +219,7 @@ en: errors: messages: together: 'has to be set along with %{attribute}' + at_least: 'or %{attribute} must be defined' models: exercise: at_most_one_main_file: must include at most one main file diff --git a/db/migrate/20201007104221_create_tips.rb b/db/migrate/20201007104221_create_tips.rb new file mode 100644 index 00000000..cde0695c --- /dev/null +++ b/db/migrate/20201007104221_create_tips.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class CreateTips < ActiveRecord::Migration[5.2] + def change + create_table :tips do |t| + t.string :title + t.text :description + t.text :example + t.references :file_type, foreign_key: true + t.references :user, polymorphic: true, null: false + t.timestamps + end + + create_table :exercise_tips do |t| + t.references :exercise, null: false + t.references :tip, null: false + t.integer :rank, null: false + t.references :parent_exercise_tip, foreign_key: {to_table: :exercise_tips} + t.index %i[exercise_id tip_id rank], unique: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index bbc6bc60..8e5ccd1b 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.define(version: 2020_05_06_093054) do +ActiveRecord::Schema.define(version: 2020_10_07_104221) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -137,6 +137,17 @@ ActiveRecord::Schema.define(version: 2020_05_06_093054) do t.integer "factor", default: 1 end + create_table "exercise_tips", force: :cascade do |t| + t.bigint "exercise_id", null: false + t.bigint "tip_id", null: false + t.integer "rank", null: false + t.bigint "parent_exercise_tip_id" + t.index ["exercise_id", "tip_id", "rank"], name: "index_exercise_tips_on_exercise_id_and_tip_id_and_rank", unique: true + t.index ["exercise_id"], name: "index_exercise_tips_on_exercise_id" + t.index ["parent_exercise_tip_id"], name: "index_exercise_tips_on_parent_exercise_tip_id" + t.index ["tip_id"], name: "index_exercise_tips_on_tip_id" + end + create_table "exercises", id: :serial, force: :cascade do |t| t.text "description" t.integer "execution_environment_id" @@ -389,6 +400,19 @@ ActiveRecord::Schema.define(version: 2020_05_06_093054) do t.index ["submission_id"], name: "index_testruns_on_submission_id" end + create_table "tips", force: :cascade do |t| + t.string "title" + t.text "description" + t.text "example" + t.bigint "file_type_id" + t.string "user_type", null: false + t.bigint "user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["file_type_id"], name: "index_tips_on_file_type_id" + t.index ["user_type", "user_id"], name: "index_tips_on_user_type_and_user_id" + end + create_table "user_exercise_feedbacks", id: :serial, force: :cascade do |t| t.integer "exercise_id", null: false t.integer "user_id", null: false @@ -427,4 +451,5 @@ ActiveRecord::Schema.define(version: 2020_05_06_093054) do add_foreign_key "request_for_comments", "submissions", name: "request_for_comments_submissions_id_fk" add_foreign_key "submissions", "study_groups" + add_foreign_key "tips", "file_types" end