From d2a089d057706f888d814c4ac2d66a50900f48dc Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Mon, 3 Jul 2017 10:09:48 +0200 Subject: [PATCH 01/33] Add structured errors to schema --- app/models/error_template.rb | 3 ++ app/models/error_template_attribute.rb | 3 ++ app/models/structured_error.rb | 4 +++ app/models/structured_error_attribute.rb | 4 +++ .../20170703075832_create_error_templates.rb | 11 +++++++ ...075959_create_error_template_attributes.rb | 11 +++++++ ...20170703080205_create_structured_errors.rb | 10 ++++++ ...0355_create_structured_error_attributes.rb | 11 +++++++ db/schema.rb | 33 ++++++++++++++++++- test/factories/error_template_attributes.rb | 7 ++++ test/factories/error_templates.rb | 7 ++++ test/factories/structured_error_attributes.rb | 7 ++++ test/factories/structured_errors.rb | 6 ++++ test/models/error_template_attribute_test.rb | 7 ++++ test/models/error_template_test.rb | 7 ++++ .../models/structured_error_attribute_test.rb | 7 ++++ test/models/structured_error_test.rb | 7 ++++ 17 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 app/models/error_template.rb create mode 100644 app/models/error_template_attribute.rb create mode 100644 app/models/structured_error.rb create mode 100644 app/models/structured_error_attribute.rb create mode 100644 db/migrate/20170703075832_create_error_templates.rb create mode 100644 db/migrate/20170703075959_create_error_template_attributes.rb create mode 100644 db/migrate/20170703080205_create_structured_errors.rb create mode 100644 db/migrate/20170703080355_create_structured_error_attributes.rb create mode 100644 test/factories/error_template_attributes.rb create mode 100644 test/factories/error_templates.rb create mode 100644 test/factories/structured_error_attributes.rb create mode 100644 test/factories/structured_errors.rb create mode 100644 test/models/error_template_attribute_test.rb create mode 100644 test/models/error_template_test.rb create mode 100644 test/models/structured_error_attribute_test.rb create mode 100644 test/models/structured_error_test.rb diff --git a/app/models/error_template.rb b/app/models/error_template.rb new file mode 100644 index 00000000..9b441d51 --- /dev/null +++ b/app/models/error_template.rb @@ -0,0 +1,3 @@ +class ErrorTemplate < ActiveRecord::Base + belongs_to :execution_environment +end diff --git a/app/models/error_template_attribute.rb b/app/models/error_template_attribute.rb new file mode 100644 index 00000000..361b727b --- /dev/null +++ b/app/models/error_template_attribute.rb @@ -0,0 +1,3 @@ +class ErrorTemplateAttribute < ActiveRecord::Base + belongs_to :error_template +end diff --git a/app/models/structured_error.rb b/app/models/structured_error.rb new file mode 100644 index 00000000..46f40423 --- /dev/null +++ b/app/models/structured_error.rb @@ -0,0 +1,4 @@ +class StructuredError < ActiveRecord::Base + belongs_to :error_template + belongs_to :file, class_name: 'CodeOcean::File' +end diff --git a/app/models/structured_error_attribute.rb b/app/models/structured_error_attribute.rb new file mode 100644 index 00000000..335a5901 --- /dev/null +++ b/app/models/structured_error_attribute.rb @@ -0,0 +1,4 @@ +class StructuredErrorAttribute < ActiveRecord::Base + belongs_to :structured_error + belongs_to :error_template_attribute +end diff --git a/db/migrate/20170703075832_create_error_templates.rb b/db/migrate/20170703075832_create_error_templates.rb new file mode 100644 index 00000000..6f442842 --- /dev/null +++ b/db/migrate/20170703075832_create_error_templates.rb @@ -0,0 +1,11 @@ +class CreateErrorTemplates < ActiveRecord::Migration + def change + create_table :error_templates do |t| + t.belongs_to :execution_environment + t.string :name + t.string :signature + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170703075959_create_error_template_attributes.rb b/db/migrate/20170703075959_create_error_template_attributes.rb new file mode 100644 index 00000000..3503fcac --- /dev/null +++ b/db/migrate/20170703075959_create_error_template_attributes.rb @@ -0,0 +1,11 @@ +class CreateErrorTemplateAttributes < ActiveRecord::Migration + def change + create_table :error_template_attributes do |t| + t.belongs_to :error_template + t.string :key + t.string :regex + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170703080205_create_structured_errors.rb b/db/migrate/20170703080205_create_structured_errors.rb new file mode 100644 index 00000000..560649b4 --- /dev/null +++ b/db/migrate/20170703080205_create_structured_errors.rb @@ -0,0 +1,10 @@ +class CreateStructuredErrors < ActiveRecord::Migration + def change + create_table :structured_errors do |t| + t.references :error_template + t.belongs_to :file + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20170703080355_create_structured_error_attributes.rb b/db/migrate/20170703080355_create_structured_error_attributes.rb new file mode 100644 index 00000000..aa9ee04e --- /dev/null +++ b/db/migrate/20170703080355_create_structured_error_attributes.rb @@ -0,0 +1,11 @@ +class CreateStructuredErrorAttributes < ActiveRecord::Migration + def change + create_table :structured_error_attributes do |t| + t.belongs_to :structured_error + t.references :error_template_attribute + t.string :value + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 81693c1d..6e12f864 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170608141612) do +ActiveRecord::Schema.define(version: 20170703080355) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -47,6 +47,22 @@ ActiveRecord::Schema.define(version: 20170608141612) do t.string "oauth_secret", limit: 255 end + create_table "error_template_attributes", force: :cascade do |t| + t.integer "error_template_id" + t.string "key" + t.string "regex" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "error_templates", force: :cascade do |t| + t.integer "execution_environment_id" + t.string "name" + t.string "signature" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "errors", force: :cascade do |t| t.integer "execution_environment_id" t.text "message" @@ -268,6 +284,21 @@ ActiveRecord::Schema.define(version: 20170608141612) do t.datetime "updated_at" end + create_table "structured_error_attributes", force: :cascade do |t| + t.integer "structured_error_id" + t.integer "error_template_attribute_id" + t.string "value" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "structured_errors", force: :cascade do |t| + t.integer "error_template_id" + t.integer "file_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "submissions", force: :cascade do |t| t.integer "exercise_id" t.float "score" diff --git a/test/factories/error_template_attributes.rb b/test/factories/error_template_attributes.rb new file mode 100644 index 00000000..24adb856 --- /dev/null +++ b/test/factories/error_template_attributes.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :error_template_attribute do + error_template nil + key "MyString" + regex "MyString" + end +end diff --git a/test/factories/error_templates.rb b/test/factories/error_templates.rb new file mode 100644 index 00000000..2abf68c9 --- /dev/null +++ b/test/factories/error_templates.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :error_template do + execution_environment nil + name "MyString" + signature "MyString" + end +end diff --git a/test/factories/structured_error_attributes.rb b/test/factories/structured_error_attributes.rb new file mode 100644 index 00000000..7485967c --- /dev/null +++ b/test/factories/structured_error_attributes.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :structured_error_attribute do + structured_error nil + error_template_attribute nil + value "MyString" + end +end diff --git a/test/factories/structured_errors.rb b/test/factories/structured_errors.rb new file mode 100644 index 00000000..4a87cec1 --- /dev/null +++ b/test/factories/structured_errors.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :structured_error do + error_template nil + file nil + end +end diff --git a/test/models/error_template_attribute_test.rb b/test/models/error_template_attribute_test.rb new file mode 100644 index 00000000..187ae1b7 --- /dev/null +++ b/test/models/error_template_attribute_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ErrorTemplateAttributeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/error_template_test.rb b/test/models/error_template_test.rb new file mode 100644 index 00000000..538dc19a --- /dev/null +++ b/test/models/error_template_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ErrorTemplateTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/structured_error_attribute_test.rb b/test/models/structured_error_attribute_test.rb new file mode 100644 index 00000000..1dcb316b --- /dev/null +++ b/test/models/structured_error_attribute_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class StructuredErrorAttributeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/structured_error_test.rb b/test/models/structured_error_test.rb new file mode 100644 index 00000000..28b03689 --- /dev/null +++ b/test/models/structured_error_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class StructuredErrorTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 872611bff64e624c5105fb526d16248297f0cd92 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Tue, 11 Jul 2017 19:22:12 +0200 Subject: [PATCH 02/33] Update schema to reflect re-usable error_template_attributes, descriptions, hints, and sorting --- app/models/error_template.rb | 1 + app/models/error_template_attribute.rb | 2 +- app/models/execution_environment.rb | 1 + ...add_description_and_hint_to_error_template.rb | 9 +++++++++ ..._template_attribute_relationship_to_n_to_m.rb | 6 ++++++ db/schema.rb | 16 ++++++++++++---- 6 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20170711170456_add_description_and_hint_to_error_template.rb create mode 100644 db/migrate/20170711170928_change_error_template_attribute_relationship_to_n_to_m.rb diff --git a/app/models/error_template.rb b/app/models/error_template.rb index 9b441d51..dc497d48 100644 --- a/app/models/error_template.rb +++ b/app/models/error_template.rb @@ -1,3 +1,4 @@ class ErrorTemplate < ActiveRecord::Base belongs_to :execution_environment + has_and_belongs_to_many :error_template_attributes end diff --git a/app/models/error_template_attribute.rb b/app/models/error_template_attribute.rb index 361b727b..15229b33 100644 --- a/app/models/error_template_attribute.rb +++ b/app/models/error_template_attribute.rb @@ -1,3 +1,3 @@ class ErrorTemplateAttribute < ActiveRecord::Base - belongs_to :error_template + has_and_belongs_to_many :error_template end diff --git a/app/models/execution_environment.rb b/app/models/execution_environment.rb index 3a4efdde..74177173 100644 --- a/app/models/execution_environment.rb +++ b/app/models/execution_environment.rb @@ -11,6 +11,7 @@ class ExecutionEnvironment < ActiveRecord::Base has_many :exercises belongs_to :file_type has_many :hints + has_many :error_templates scope :with_exercises, -> { where('id IN (SELECT execution_environment_id FROM exercises)') } diff --git a/db/migrate/20170711170456_add_description_and_hint_to_error_template.rb b/db/migrate/20170711170456_add_description_and_hint_to_error_template.rb new file mode 100644 index 00000000..62cbee95 --- /dev/null +++ b/db/migrate/20170711170456_add_description_and_hint_to_error_template.rb @@ -0,0 +1,9 @@ +class AddDescriptionAndHintToErrorTemplate < ActiveRecord::Migration + def change + add_column :error_templates, :description, :text + add_column :error_templates, :hint, :text + + add_column :error_template_attributes, :description, :text + add_column :error_template_attributes, :important, :boolean + end +end diff --git a/db/migrate/20170711170928_change_error_template_attribute_relationship_to_n_to_m.rb b/db/migrate/20170711170928_change_error_template_attribute_relationship_to_n_to_m.rb new file mode 100644 index 00000000..d10fbda2 --- /dev/null +++ b/db/migrate/20170711170928_change_error_template_attribute_relationship_to_n_to_m.rb @@ -0,0 +1,6 @@ +class ChangeErrorTemplateAttributeRelationshipToNToM < ActiveRecord::Migration + def change + remove_belongs_to :error_template_attributes, :error_template + create_join_table :error_templates, :error_template_attributes + end +end diff --git a/db/schema.rb b/db/schema.rb index 6e12f864..137d251d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170703080355) do +ActiveRecord::Schema.define(version: 20170711170928) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -48,11 +48,17 @@ ActiveRecord::Schema.define(version: 20170703080355) do end create_table "error_template_attributes", force: :cascade do |t| - t.integer "error_template_id" t.string "key" t.string "regex" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "description" + t.boolean "important" + end + + create_table "error_template_attributes_templates", id: false, force: :cascade do |t| + t.integer "error_template_id", null: false + t.integer "error_template_attribute_id", null: false end create_table "error_templates", force: :cascade do |t| @@ -61,6 +67,8 @@ ActiveRecord::Schema.define(version: 20170703080355) do t.string "signature" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.text "description" + t.text "hint" end create_table "errors", force: :cascade do |t| From 4d684a7a05e24911abbab4ca5468b97be96738ba Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 09:52:33 +0200 Subject: [PATCH 03/33] Extract structured errors on run and submit --- app/controllers/concerns/submission_scoring.rb | 8 ++++++++ app/controllers/submissions_controller.rb | 16 +++++++++++++++- app/models/structured_error.rb | 8 ++++++++ app/models/structured_error_attribute.rb | 5 +++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/app/controllers/concerns/submission_scoring.rb b/app/controllers/concerns/submission_scoring.rb index 16f1f061..9c577aca 100644 --- a/app/controllers/concerns/submission_scoring.rb +++ b/app/controllers/concerns/submission_scoring.rb @@ -9,6 +9,14 @@ module SubmissionScoring assessment = assessor.assess(output) passed = ((assessment[:passed] == assessment[:count]) and (assessment[:score] > 0)) testrun_output = passed ? nil : output[:stderr] + if !testrun_output.blank? + submission.exercise.execution_environment.error_templates.each do |template| + pattern = Regexp.new(template.signature).freeze + if pattern.match(testrun_output) + StructuredError.create_from_template(template, testrun_output) + end + end + end Testrun.new(submission: submission, file: file, passed: passed, output: testrun_output).save output.merge!(assessment) output.merge!(filename: file.name_with_extension, message: feedback_message(file, output[:score]), weight: file.weight) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 9b37fde0..ae111823 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -6,7 +6,7 @@ class SubmissionsController < ApplicationController include SubmissionScoring include Tubesock::Hijack - before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :show, :statistics, :stop, :test] + before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :extract_errors, :show, :statistics, :stop, :test] before_action :set_docker_client, only: [:run, :test] before_action :set_files, only: [:download, :download_file, :render_file, :show] before_action :set_file, only: [:download_file, :render_file] @@ -187,6 +187,9 @@ class SubmissionsController < ApplicationController end def kill_socket(tubesock) + # search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb) + extract_errors + # save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb) save_run_output @@ -195,6 +198,17 @@ class SubmissionsController < ApplicationController tubesock.close end + def extract_errors + if !@message_buffer.blank? + @submission.exercise.execution_environment.error_templates.each do |template| + pattern = Regexp.new(template.signature).freeze + if pattern.match?(@message_buffer) + StructuredError.create_from_template(template, @message_buffer) + end + end + end + end + def handle_message(message, tubesock, container) @message_buffer ||= "" # Handle special commands first diff --git a/app/models/structured_error.rb b/app/models/structured_error.rb index 46f40423..2f03ec54 100644 --- a/app/models/structured_error.rb +++ b/app/models/structured_error.rb @@ -1,4 +1,12 @@ class StructuredError < ActiveRecord::Base belongs_to :error_template belongs_to :file, class_name: 'CodeOcean::File' + + def self.create_from_template(template, message_buffer) + instance = self.create(error_template: template) + template.error_template_attributes.each do |attribute| + StructuredErrorAttribute.create_from_template(attribute, instance, message_buffer) + end + instance + end end diff --git a/app/models/structured_error_attribute.rb b/app/models/structured_error_attribute.rb index 335a5901..b9967719 100644 --- a/app/models/structured_error_attribute.rb +++ b/app/models/structured_error_attribute.rb @@ -1,4 +1,9 @@ class StructuredErrorAttribute < ActiveRecord::Base belongs_to :structured_error belongs_to :error_template_attribute + + def self.create_from_template(attribute, structured_error, message_buffer) + value = message_buffer.match(attribute.regex).captures[0] + self.create(structured_error: structured_error, error_template_attribute: attribute, value: value) + end end From 4d38195c99bbc292a9513d0e58ef5b610dfa0d73 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 10:11:52 +0200 Subject: [PATCH 04/33] Scaffold error_templates UI --- .../stylesheets/error_templates.css.scss | 3 + app/assets/stylesheets/scaffolds.css.scss | 69 +++++++++++++++ app/controllers/error_templates_controller.rb | 86 +++++++++++++++++++ app/helpers/error_templates_helper.rb | 2 + app/policies/error_template_policy.rb | 3 + app/views/error_templates/_form.html.erb | 17 ++++ app/views/error_templates/edit.html.erb | 6 ++ app/views/error_templates/index.html.erb | 25 ++++++ app/views/error_templates/index.json.jbuilder | 4 + app/views/error_templates/new.html.erb | 5 ++ app/views/error_templates/show.html.erb | 4 + app/views/error_templates/show.json.jbuilder | 1 + config/routes.rb | 1 + .../error_templates_controller_test.rb | 49 +++++++++++ 14 files changed, 275 insertions(+) create mode 100644 app/assets/stylesheets/error_templates.css.scss create mode 100644 app/assets/stylesheets/scaffolds.css.scss create mode 100644 app/controllers/error_templates_controller.rb create mode 100644 app/helpers/error_templates_helper.rb create mode 100644 app/policies/error_template_policy.rb create mode 100644 app/views/error_templates/_form.html.erb create mode 100644 app/views/error_templates/edit.html.erb create mode 100644 app/views/error_templates/index.html.erb create mode 100644 app/views/error_templates/index.json.jbuilder create mode 100644 app/views/error_templates/new.html.erb create mode 100644 app/views/error_templates/show.html.erb create mode 100644 app/views/error_templates/show.json.jbuilder create mode 100644 test/controllers/error_templates_controller_test.rb diff --git a/app/assets/stylesheets/error_templates.css.scss b/app/assets/stylesheets/error_templates.css.scss new file mode 100644 index 00000000..bbd8b05e --- /dev/null +++ b/app/assets/stylesheets/error_templates.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the error_templates controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss new file mode 100644 index 00000000..6ec6a8ff --- /dev/null +++ b/app/assets/stylesheets/scaffolds.css.scss @@ -0,0 +1,69 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + &:visited { + color: #666; + } + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + ul li { + font-size: 12px; + list-style: square; + } +} diff --git a/app/controllers/error_templates_controller.rb b/app/controllers/error_templates_controller.rb new file mode 100644 index 00000000..1007eeaf --- /dev/null +++ b/app/controllers/error_templates_controller.rb @@ -0,0 +1,86 @@ +class ErrorTemplatesController < ApplicationController + before_action :set_error_template, only: [:show, :edit, :update, :destroy] + + def authorize! + authorize(@error_templates || @error_template) + end + private :authorize! + + # GET /error_templates + # GET /error_templates.json + def index + @error_templates = ErrorTemplate.all + authorize! + end + + # GET /error_templates/1 + # GET /error_templates/1.json + def show + authorize! + end + + # GET /error_templates/new + def new + @error_template = ErrorTemplate.new + authorize! + end + + # GET /error_templates/1/edit + def edit + authorize! + end + + # POST /error_templates + # POST /error_templates.json + def create + authorize! + @error_template = ErrorTemplate.new(error_template_params) + + respond_to do |format| + if @error_template.save + format.html { redirect_to @error_template, notice: 'Error template was successfully created.' } + format.json { render :show, status: :created, location: @error_template } + else + format.html { render :new } + format.json { render json: @error_template.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /error_templates/1 + # PATCH/PUT /error_templates/1.json + def update + authorize! + respond_to do |format| + if @error_template.update(error_template_params) + format.html { redirect_to @error_template, notice: 'Error template was successfully updated.' } + format.json { render :show, status: :ok, location: @error_template } + else + format.html { render :edit } + format.json { render json: @error_template.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /error_templates/1 + # DELETE /error_templates/1.json + def destroy + authorize! + @error_template.destroy + respond_to do |format| + format.html { redirect_to error_templates_url, notice: 'Error template was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_error_template + @error_template = ErrorTemplate.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def error_template_params + params.fetch(:error_template, {}) + end +end diff --git a/app/helpers/error_templates_helper.rb b/app/helpers/error_templates_helper.rb new file mode 100644 index 00000000..f42cf302 --- /dev/null +++ b/app/helpers/error_templates_helper.rb @@ -0,0 +1,2 @@ +module ErrorTemplatesHelper +end diff --git a/app/policies/error_template_policy.rb b/app/policies/error_template_policy.rb new file mode 100644 index 00000000..30bdbd24 --- /dev/null +++ b/app/policies/error_template_policy.rb @@ -0,0 +1,3 @@ +class ErrorTemplatePolicy < AdminOnlyPolicy + +end diff --git a/app/views/error_templates/_form.html.erb b/app/views/error_templates/_form.html.erb new file mode 100644 index 00000000..17371914 --- /dev/null +++ b/app/views/error_templates/_form.html.erb @@ -0,0 +1,17 @@ +<%= form_for(@error_template) do |f| %> + <% if @error_template.errors.any? %> +
+

<%= pluralize(@error_template.errors.count, "error") %> prohibited this error_template from being saved:

+ +
    + <% @error_template.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%= f.submit %> +
+<% end %> diff --git a/app/views/error_templates/edit.html.erb b/app/views/error_templates/edit.html.erb new file mode 100644 index 00000000..0d2919e5 --- /dev/null +++ b/app/views/error_templates/edit.html.erb @@ -0,0 +1,6 @@ +

Editing Error Template

+ +<%= render 'form' %> + +<%= link_to 'Show', @error_template %> | +<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/index.html.erb b/app/views/error_templates/index.html.erb new file mode 100644 index 00000000..65829aeb --- /dev/null +++ b/app/views/error_templates/index.html.erb @@ -0,0 +1,25 @@ +

<%= notice %>

+ +

Listing Error Templates

+ + + + + + + + + + <% @error_templates.each do |error_template| %> + + + + + + <% end %> + +
<%= link_to 'Show', error_template %><%= link_to 'Edit', edit_error_template_path(error_template) %><%= link_to 'Destroy', error_template, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%= link_to 'New Error template', new_error_template_path %> diff --git a/app/views/error_templates/index.json.jbuilder b/app/views/error_templates/index.json.jbuilder new file mode 100644 index 00000000..ce85b2be --- /dev/null +++ b/app/views/error_templates/index.json.jbuilder @@ -0,0 +1,4 @@ +json.array!(@error_templates) do |error_template| + json.extract! error_template, :id + json.url error_template_url(error_template, format: :json) +end diff --git a/app/views/error_templates/new.html.erb b/app/views/error_templates/new.html.erb new file mode 100644 index 00000000..6b4acd06 --- /dev/null +++ b/app/views/error_templates/new.html.erb @@ -0,0 +1,5 @@ +

New Error Template

+ +<%= render 'form' %> + +<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/show.html.erb b/app/views/error_templates/show.html.erb new file mode 100644 index 00000000..9debad9b --- /dev/null +++ b/app/views/error_templates/show.html.erb @@ -0,0 +1,4 @@ +

<%= notice %>

+ +<%= link_to 'Edit', edit_error_template_path(@error_template) %> | +<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/show.json.jbuilder b/app/views/error_templates/show.json.jbuilder new file mode 100644 index 00000000..1cbaff55 --- /dev/null +++ b/app/views/error_templates/show.json.jbuilder @@ -0,0 +1 @@ +json.extract! @error_template, :id, :created_at, :updated_at diff --git a/config/routes.rb b/config/routes.rb index d340a204..ce5be3ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do + resources :error_templates resources :file_templates do collection do get 'by_file_type/:file_type_id', as: :by_file_type, action: :by_file_type diff --git a/test/controllers/error_templates_controller_test.rb b/test/controllers/error_templates_controller_test.rb new file mode 100644 index 00000000..452ad134 --- /dev/null +++ b/test/controllers/error_templates_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ErrorTemplatesControllerTest < ActionController::TestCase + setup do + @error_template = error_templates(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:error_templates) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create error_template" do + assert_difference('ErrorTemplate.count') do + post :create, error_template: { } + end + + assert_redirected_to error_template_path(assigns(:error_template)) + end + + test "should show error_template" do + get :show, id: @error_template + assert_response :success + end + + test "should get edit" do + get :edit, id: @error_template + assert_response :success + end + + test "should update error_template" do + patch :update, id: @error_template, error_template: { } + assert_redirected_to error_template_path(assigns(:error_template)) + end + + test "should destroy error_template" do + assert_difference('ErrorTemplate.count', -1) do + delete :destroy, id: @error_template + end + + assert_redirected_to error_templates_path + end +end From 9eadc3a4db3b68175f207c1dc86301c5e3ab8205 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 11:04:29 +0200 Subject: [PATCH 05/33] Make error template UI usable --- .../stylesheets/error_templates.css.scss | 3 --- app/controllers/error_templates_controller.rb | 6 ++--- app/views/error_templates/_form.html.erb | 17 ------------- app/views/error_templates/_form.html.slim | 19 ++++++++++++++ app/views/error_templates/edit.html.erb | 6 ----- app/views/error_templates/edit.html.slim | 3 +++ app/views/error_templates/index.html.erb | 25 ------------------- app/views/error_templates/index.html.slim | 22 ++++++++++++++++ app/views/error_templates/index.json.jbuilder | 4 --- app/views/error_templates/new.html.erb | 5 ---- app/views/error_templates/new.html.slim | 3 +++ app/views/error_templates/show.html.erb | 4 --- app/views/error_templates/show.html.slim | 10 ++++++++ app/views/error_templates/show.json.jbuilder | 1 - config/locales/de.yml | 9 ++++++- config/locales/en.yml | 8 ++++++ 16 files changed, 76 insertions(+), 69 deletions(-) delete mode 100644 app/assets/stylesheets/error_templates.css.scss delete mode 100644 app/views/error_templates/_form.html.erb create mode 100644 app/views/error_templates/_form.html.slim delete mode 100644 app/views/error_templates/edit.html.erb create mode 100644 app/views/error_templates/edit.html.slim delete mode 100644 app/views/error_templates/index.html.erb create mode 100644 app/views/error_templates/index.html.slim delete mode 100644 app/views/error_templates/index.json.jbuilder delete mode 100644 app/views/error_templates/new.html.erb create mode 100644 app/views/error_templates/new.html.slim delete mode 100644 app/views/error_templates/show.html.erb create mode 100644 app/views/error_templates/show.html.slim delete mode 100644 app/views/error_templates/show.json.jbuilder diff --git a/app/assets/stylesheets/error_templates.css.scss b/app/assets/stylesheets/error_templates.css.scss deleted file mode 100644 index bbd8b05e..00000000 --- a/app/assets/stylesheets/error_templates.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the error_templates controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/error_templates_controller.rb b/app/controllers/error_templates_controller.rb index 1007eeaf..6bf9a4f2 100644 --- a/app/controllers/error_templates_controller.rb +++ b/app/controllers/error_templates_controller.rb @@ -9,7 +9,7 @@ class ErrorTemplatesController < ApplicationController # GET /error_templates # GET /error_templates.json def index - @error_templates = ErrorTemplate.all + @error_templates = ErrorTemplate.all.order(:execution_environment_id, :name).paginate(page: params[:page]) authorize! end @@ -33,8 +33,8 @@ class ErrorTemplatesController < ApplicationController # POST /error_templates # POST /error_templates.json def create - authorize! @error_template = ErrorTemplate.new(error_template_params) + authorize! respond_to do |format| if @error_template.save @@ -81,6 +81,6 @@ class ErrorTemplatesController < ApplicationController # Never trust parameters from the scary internet, only allow the white list through. def error_template_params - params.fetch(:error_template, {}) + params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint) end end diff --git a/app/views/error_templates/_form.html.erb b/app/views/error_templates/_form.html.erb deleted file mode 100644 index 17371914..00000000 --- a/app/views/error_templates/_form.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -<%= form_for(@error_template) do |f| %> - <% if @error_template.errors.any? %> -
-

<%= pluralize(@error_template.errors.count, "error") %> prohibited this error_template from being saved:

- -
    - <% @error_template.errors.full_messages.each do |message| %> -
  • <%= message %>
  • - <% end %> -
-
- <% end %> - -
- <%= f.submit %> -
-<% end %> diff --git a/app/views/error_templates/_form.html.slim b/app/views/error_templates/_form.html.slim new file mode 100644 index 00000000..f9363155 --- /dev/null +++ b/app/views/error_templates/_form.html.slim @@ -0,0 +1,19 @@ += form_for(@error_template) do |f| + = render('shared/form_errors', object: @error_template) + .form-group + = f.label(:name) + = f.text_field(:name, class: 'form-control', required: true) + .form-group + = f.label(:execution_environment_id) + = f.collection_select(:execution_environment_id, ExecutionEnvironment.all.order(:name), :id, :name, {include_blank: false}, class: 'form-control') + .form-group + = f.label(:signature) + = f.text_field(:signature, class: 'form-control') + .help-block == t('error_templates.hints.signature') + .form-group + = f.label(:description) + = f.text_field(:description, class: 'form-control') + .form-group + = f.label(:hint) + = f.text_field(:hint, class: 'form-control') + .actions = render('shared/submit_button', f: f, object: @error_template) diff --git a/app/views/error_templates/edit.html.erb b/app/views/error_templates/edit.html.erb deleted file mode 100644 index 0d2919e5..00000000 --- a/app/views/error_templates/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Editing Error Template

- -<%= render 'form' %> - -<%= link_to 'Show', @error_template %> | -<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/edit.html.slim b/app/views/error_templates/edit.html.slim new file mode 100644 index 00000000..2d12b363 --- /dev/null +++ b/app/views/error_templates/edit.html.slim @@ -0,0 +1,3 @@ +h1 = @error_template + += render('form') diff --git a/app/views/error_templates/index.html.erb b/app/views/error_templates/index.html.erb deleted file mode 100644 index 65829aeb..00000000 --- a/app/views/error_templates/index.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -

<%= notice %>

- -

Listing Error Templates

- - - - - - - - - - <% @error_templates.each do |error_template| %> - - - - - - <% end %> - -
<%= link_to 'Show', error_template %><%= link_to 'Edit', edit_error_template_path(error_template) %><%= link_to 'Destroy', error_template, method: :delete, data: { confirm: 'Are you sure?' } %>
- -
- -<%= link_to 'New Error template', new_error_template_path %> diff --git a/app/views/error_templates/index.html.slim b/app/views/error_templates/index.html.slim new file mode 100644 index 00000000..1b028614 --- /dev/null +++ b/app/views/error_templates/index.html.slim @@ -0,0 +1,22 @@ +h1 = ErrorTemplate.model_name.human(count: 2) + +.table-responsive + table.sortable.table + thead + tr + th = t('activerecord.attributes.error_template.name') + th = t('activerecord.attributes.error_template.description') + th = t('activerecord.attributes.exercise.execution_environment') + th colspan=5 = t('shared.actions') + tbody + - @error_templates.each do |error_template| + tr + td = error_template.name + td = error_template.description + td = link_to(error_template.execution_environment) + td = link_to(t('shared.show'), error_template) + td = link_to(t('shared.edit'), edit_error_template_path(error_template)) + td = link_to(t('shared.destroy'), error_template, data: {confirm: t('shared.confirm_destroy')}, method: :delete) + += render('shared/pagination', collection: @error_templates) +p = render('shared/new_button', model: ErrorTemplate) diff --git a/app/views/error_templates/index.json.jbuilder b/app/views/error_templates/index.json.jbuilder deleted file mode 100644 index ce85b2be..00000000 --- a/app/views/error_templates/index.json.jbuilder +++ /dev/null @@ -1,4 +0,0 @@ -json.array!(@error_templates) do |error_template| - json.extract! error_template, :id - json.url error_template_url(error_template, format: :json) -end diff --git a/app/views/error_templates/new.html.erb b/app/views/error_templates/new.html.erb deleted file mode 100644 index 6b4acd06..00000000 --- a/app/views/error_templates/new.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

New Error Template

- -<%= render 'form' %> - -<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/new.html.slim b/app/views/error_templates/new.html.slim new file mode 100644 index 00000000..eeecadc8 --- /dev/null +++ b/app/views/error_templates/new.html.slim @@ -0,0 +1,3 @@ +h1 = t('shared.new_model', model: ErrorTemplate.model_name.human) + += render('form') diff --git a/app/views/error_templates/show.html.erb b/app/views/error_templates/show.html.erb deleted file mode 100644 index 9debad9b..00000000 --- a/app/views/error_templates/show.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

<%= notice %>

- -<%= link_to 'Edit', edit_error_template_path(@error_template) %> | -<%= link_to 'Back', error_templates_path %> diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim new file mode 100644 index 00000000..21843b14 --- /dev/null +++ b/app/views/error_templates/show.html.slim @@ -0,0 +1,10 @@ +h1 + = @error_template + = render('shared/edit_button', object: @error_template) + += row(label: 'error_template.name', value: @error_template.name) += row(label: 'exercise.execution_environment', value: link_to(@error_template.execution_environment)) +- [:signature, :description, :hint].each do |attribute| + = row(label: "error_template.#{attribute}", value: @error_template.send(attribute)) + +//todo: list attributes \ No newline at end of file diff --git a/app/views/error_templates/show.json.jbuilder b/app/views/error_templates/show.json.jbuilder deleted file mode 100644 index 1cbaff55..00000000 --- a/app/views/error_templates/show.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.extract! @error_template, :id, :created_at, :updated_at diff --git a/config/locales/de.yml b/config/locales/de.yml index d0aef588..d37992c9 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -109,6 +109,11 @@ de: name: "Name" file_type: "Dateityp" content: "Code" + error_template: + name: Name + signature: Regulärer Ausdruck + description: Beschreibung + hint: Hinweis models: code_harbor_link: one: CodeHarbor-Link @@ -580,4 +585,6 @@ de: estimated_time_20_to_30: "zwischen 20 und 30 Minuten" estimated_time_more_30: "mehr als 30 Minuten" working_time: "Geschätze Bearbeitungszeit für diese Aufgabe:" - + error_templates: + hints: + signature: "Ein regulärer Ausdruck in Ruby-Syntax und ohne führende und schließende \"/\"" diff --git a/config/locales/en.yml b/config/locales/en.yml index 9a334697..17427a5f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -130,6 +130,11 @@ en: name: "Name" file_type: "File Type" content: "Content" + error_template: + name: Name + signature: Signature Regular Expression + description: Description + hint: Hint models: code_harbor_link: one: CodeHarbor Link @@ -601,3 +606,6 @@ en: estimated_time_20_to_30: "between 20 and 30 minutes" estimated_time_more_30: "more than 30 minutes" working_time: "Estimated time working on this exercise:" + error_templates: + hints: + signature: "A regular expression in Ruby syntax without leading and trailing \"/\"" From fdee3c3efc9629a80f590a7a6f63f721c230e34f Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 11:11:32 +0200 Subject: [PATCH 06/33] Fix string in heading and breadcrumbs --- app/models/error_template.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/error_template.rb b/app/models/error_template.rb index dc497d48..be4a3279 100644 --- a/app/models/error_template.rb +++ b/app/models/error_template.rb @@ -1,4 +1,8 @@ class ErrorTemplate < ActiveRecord::Base belongs_to :execution_environment has_and_belongs_to_many :error_template_attributes + + def to_s + "#{id} [#{name}]" + end end From 2f759c03a9dd2b466fc37b0cd0880593f5e65e3a Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 11:23:51 +0200 Subject: [PATCH 07/33] List error template attributes --- app/views/error_templates/show.html.slim | 23 ++++++++++++++++++++++- config/locales/de.yml | 6 ++++++ config/locales/en.yml | 6 ++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index 21843b14..373aa615 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -7,4 +7,25 @@ h1 - [:signature, :description, :hint].each do |attribute| = row(label: "error_template.#{attribute}", value: @error_template.send(attribute)) -//todo: list attributes \ No newline at end of file +h3 + = t 'error_templates.attributes' + +.table-responsive + table.sortable.table + thead + tr + th + th = t('activerecord.attributes.error_template_attributes.key') + th = t('activerecord.attributes.error_template_attributes.description') + th = t('activerecord.attributes.error_template_attributes.regex') + tbody + - @error_template.error_template_attributes.order(:important).each do |attribute| + tr + td + - if attribute.important + span class="fa fa-star" aria-hidden="true" + - else + span class="fa fa-star-o" aria-hidden="true" + td = attribute.key + td = attribute.description + td = attribute.regex diff --git a/config/locales/de.yml b/config/locales/de.yml index d37992c9..ab4f835d 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -114,6 +114,11 @@ de: signature: Regulärer Ausdruck description: Beschreibung hint: Hinweis + error_template_attributes: + important: "Wichtig" + key: "Name" + description: "Beschreibung" + regex: "Regulärer Ausdruck" models: code_harbor_link: one: CodeHarbor-Link @@ -588,3 +593,4 @@ de: error_templates: hints: signature: "Ein regulärer Ausdruck in Ruby-Syntax und ohne führende und schließende \"/\"" + attributes: "Attribute" diff --git a/config/locales/en.yml b/config/locales/en.yml index 17427a5f..cd61a066 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -135,6 +135,11 @@ en: signature: Signature Regular Expression description: Description hint: Hint + error_template_attributes: + important: "Important" + key: "Identifier" + description: "Description" + regex: "Regular Expression" models: code_harbor_link: one: CodeHarbor Link @@ -609,3 +614,4 @@ en: error_templates: hints: signature: "A regular expression in Ruby syntax without leading and trailing \"/\"" + attributes: "Attributes" From 4b87d960baaf359b8d8806b8eca32b9c2e5578d8 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 11:47:47 +0200 Subject: [PATCH 08/33] Remove scaffold.css --- app/assets/stylesheets/scaffolds.css.scss | 69 ----------------------- 1 file changed, 69 deletions(-) delete mode 100644 app/assets/stylesheets/scaffolds.css.scss diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss deleted file mode 100644 index 6ec6a8ff..00000000 --- a/app/assets/stylesheets/scaffolds.css.scss +++ /dev/null @@ -1,69 +0,0 @@ -body { - background-color: #fff; - color: #333; - font-family: verdana, arial, helvetica, sans-serif; - font-size: 13px; - line-height: 18px; -} - -p, ol, ul, td { - font-family: verdana, arial, helvetica, sans-serif; - font-size: 13px; - line-height: 18px; -} - -pre { - background-color: #eee; - padding: 10px; - font-size: 11px; -} - -a { - color: #000; - &:visited { - color: #666; - } - &:hover { - color: #fff; - background-color: #000; - } -} - -div { - &.field, &.actions { - margin-bottom: 10px; - } -} - -#notice { - color: green; -} - -.field_with_errors { - padding: 2px; - background-color: red; - display: table; -} - -#error_explanation { - width: 450px; - border: 2px solid red; - padding: 7px; - padding-bottom: 0; - margin-bottom: 20px; - background-color: #f0f0f0; - h2 { - text-align: left; - font-weight: bold; - padding: 5px 5px 5px 15px; - font-size: 12px; - margin: -7px; - margin-bottom: 0px; - background-color: #c00; - color: #fff; - } - ul li { - font-size: 12px; - list-style: square; - } -} From 0c8c8562f57b410457cac1449e8cca2516bccc0b Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 11:58:18 +0200 Subject: [PATCH 09/33] Scaffold error template attributes --- .../error_template_attributes_controller.rb | 86 +++++++++++++++++++ .../error_template_attributes_helper.rb | 2 + app/models/error_template_attribute.rb | 4 + .../error_template_attribute_policy.rb | 3 + .../error_template_attributes/_form.html.slim | 16 ++++ .../error_template_attributes/edit.html.slim | 3 + .../error_template_attributes/index.html.slim | 28 ++++++ .../error_template_attributes/new.html.slim | 3 + .../error_template_attributes/show.html.slim | 8 ++ app/views/error_templates/show.html.slim | 6 +- config/locales/de.yml | 2 +- config/locales/en.yml | 2 +- config/routes.rb | 1 + ...ror_template_attributes_controller_test.rb | 49 +++++++++++ 14 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 app/controllers/error_template_attributes_controller.rb create mode 100644 app/helpers/error_template_attributes_helper.rb create mode 100644 app/policies/error_template_attribute_policy.rb create mode 100644 app/views/error_template_attributes/_form.html.slim create mode 100644 app/views/error_template_attributes/edit.html.slim create mode 100644 app/views/error_template_attributes/index.html.slim create mode 100644 app/views/error_template_attributes/new.html.slim create mode 100644 app/views/error_template_attributes/show.html.slim create mode 100644 test/controllers/error_template_attributes_controller_test.rb diff --git a/app/controllers/error_template_attributes_controller.rb b/app/controllers/error_template_attributes_controller.rb new file mode 100644 index 00000000..dc2da30e --- /dev/null +++ b/app/controllers/error_template_attributes_controller.rb @@ -0,0 +1,86 @@ +class ErrorTemplateAttributesController < ApplicationController + before_action :set_error_template_attribute, only: [:show, :edit, :update, :destroy] + + def authorize! + authorize(@error_template_attributes || @error_template_attribute) + end + private :authorize! + + # GET /error_template_attributes + # GET /error_template_attributes.json + def index + @error_template_attributes = ErrorTemplateAttribute.all.order(:id).paginate(page: params[:page]) + authorize! + end + + # GET /error_template_attributes/1 + # GET /error_template_attributes/1.json + def show + authorize! + end + + # GET /error_template_attributes/new + def new + @error_template_attribute = ErrorTemplateAttribute.new + authorize! + end + + # GET /error_template_attributes/1/edit + def edit + authorize! + end + + # POST /error_template_attributes + # POST /error_template_attributes.json + def create + @error_template_attribute = ErrorTemplateAttribute.new(error_template_attribute_params) + authorize! + + respond_to do |format| + if @error_template_attribute.save + format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully created.' } + format.json { render :show, status: :created, location: @error_template_attribute } + else + format.html { render :new } + format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /error_template_attributes/1 + # PATCH/PUT /error_template_attributes/1.json + def update + authorize! + respond_to do |format| + if @error_template_attribute.update(error_template_attribute_params) + format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully updated.' } + format.json { render :show, status: :ok, location: @error_template_attribute } + else + format.html { render :edit } + format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /error_template_attributes/1 + # DELETE /error_template_attributes/1.json + def destroy + authorize! + @error_template_attribute.destroy + respond_to do |format| + format.html { redirect_to error_template_attributes_url, notice: 'Error template attribute was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_error_template_attribute + @error_template_attribute = ErrorTemplateAttribute.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def error_template_attribute_params + params.fetch(:error_template_attribute, {}) + end +end diff --git a/app/helpers/error_template_attributes_helper.rb b/app/helpers/error_template_attributes_helper.rb new file mode 100644 index 00000000..9046f407 --- /dev/null +++ b/app/helpers/error_template_attributes_helper.rb @@ -0,0 +1,2 @@ +module ErrorTemplateAttributesHelper +end diff --git a/app/models/error_template_attribute.rb b/app/models/error_template_attribute.rb index 15229b33..844c1019 100644 --- a/app/models/error_template_attribute.rb +++ b/app/models/error_template_attribute.rb @@ -1,3 +1,7 @@ class ErrorTemplateAttribute < ActiveRecord::Base has_and_belongs_to_many :error_template + + def to_s + "#{id} [#{key}]" + end end diff --git a/app/policies/error_template_attribute_policy.rb b/app/policies/error_template_attribute_policy.rb new file mode 100644 index 00000000..eed0896d --- /dev/null +++ b/app/policies/error_template_attribute_policy.rb @@ -0,0 +1,3 @@ +class ErrorTemplateAttributePolicy < AdminOnlyPolicy + +end diff --git a/app/views/error_template_attributes/_form.html.slim b/app/views/error_template_attributes/_form.html.slim new file mode 100644 index 00000000..4fc28b02 --- /dev/null +++ b/app/views/error_template_attributes/_form.html.slim @@ -0,0 +1,16 @@ += form_for(@error_template_attribute) do |f| + = render('shared/form_errors', object: @error_template_attribute) + .form-group + = f.label(:key) + = f.text_field(:key, class: 'form-control', required: true) + .form-group + = f.label(:description) + = f.text_field(:description, class: 'form-control') + .form-group + = f.label(:regex) + = f.text_field(:regex, class: 'form-control', required: true) + .help-block == t('error_templates.hints.signature') + .form-group + = f.check_box(:important) + = t('activerecord.attributes.error_template_attribute.important') + .actions = render('shared/submit_button', f: f, object: @error_template_attribute) diff --git a/app/views/error_template_attributes/edit.html.slim b/app/views/error_template_attributes/edit.html.slim new file mode 100644 index 00000000..f3279442 --- /dev/null +++ b/app/views/error_template_attributes/edit.html.slim @@ -0,0 +1,3 @@ +h1 = @error_template_attribute + += render('form') diff --git a/app/views/error_template_attributes/index.html.slim b/app/views/error_template_attributes/index.html.slim new file mode 100644 index 00000000..81d5cac9 --- /dev/null +++ b/app/views/error_template_attributes/index.html.slim @@ -0,0 +1,28 @@ +h1 = ErrorTemplateAttribute.model_name.human(count: 2) + +.table-responsive + table.sortable.table + thead + tr + th + th = t('activerecord.attributes.error_template_attribute.key') + th = t('activerecord.attributes.error_template_attribute.description') + th = t('activerecord.attributes.error_template_attribute.regex') + th colspan=5 = t('shared.actions') + tbody + - @error_template_attributes.each do |error_template_attribute| + tr + td + - if error_template_attribute.important + span class="fa fa-star" aria-hidden="true" + - else + span class="fa fa-star-o" aria-hidden="true" + td = error_template_attribute.key + td = error_template_attribute.description + td = error_template_attribute.regex + td = link_to(t('shared.show'), error_template_attribute) + td = link_to(t('shared.edit'), edit_error_template_attribute_path(error_template_attribute)) + td = link_to(t('shared.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete) + += render('shared/pagination', collection: @error_template_attributes) +p = render('shared/new_button', model: ErrorTemplateAttribute) diff --git a/app/views/error_template_attributes/new.html.slim b/app/views/error_template_attributes/new.html.slim new file mode 100644 index 00000000..12c719f9 --- /dev/null +++ b/app/views/error_template_attributes/new.html.slim @@ -0,0 +1,3 @@ +h1 = t('shared.new_model', model: ErrorTemplateAttribute.model_name.human) + += render('form') diff --git a/app/views/error_template_attributes/show.html.slim b/app/views/error_template_attributes/show.html.slim new file mode 100644 index 00000000..2bdd01ca --- /dev/null +++ b/app/views/error_template_attributes/show.html.slim @@ -0,0 +1,8 @@ +h1 + = @error_template_attribute + = render('shared/edit_button', object: @error_template_attribute) + +- [:key, :description, :regex, :important].each do |attribute| + = row(label: "error_template_attribute.#{attribute}", value: @error_template_attribute.send(attribute)) + +// todo: used by diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index 373aa615..885c3788 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -15,9 +15,9 @@ h3 thead tr th - th = t('activerecord.attributes.error_template_attributes.key') - th = t('activerecord.attributes.error_template_attributes.description') - th = t('activerecord.attributes.error_template_attributes.regex') + th = t('activerecord.attributes.error_template_attribute.key') + th = t('activerecord.attributes.error_template_attribute.description') + th = t('activerecord.attributes.error_template_attribute.regex') tbody - @error_template.error_template_attributes.order(:important).each do |attribute| tr diff --git a/config/locales/de.yml b/config/locales/de.yml index ab4f835d..6aca7d64 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -114,7 +114,7 @@ de: signature: Regulärer Ausdruck description: Beschreibung hint: Hinweis - error_template_attributes: + error_template_attribute: important: "Wichtig" key: "Name" description: "Beschreibung" diff --git a/config/locales/en.yml b/config/locales/en.yml index cd61a066..34d8894f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -135,7 +135,7 @@ en: signature: Signature Regular Expression description: Description hint: Hint - error_template_attributes: + error_template_attribute: important: "Important" key: "Identifier" description: "Description" diff --git a/config/routes.rb b/config/routes.rb index ce5be3ae..c614a50c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do + resources :error_template_attributes resources :error_templates resources :file_templates do collection do diff --git a/test/controllers/error_template_attributes_controller_test.rb b/test/controllers/error_template_attributes_controller_test.rb new file mode 100644 index 00000000..7dd008ec --- /dev/null +++ b/test/controllers/error_template_attributes_controller_test.rb @@ -0,0 +1,49 @@ +require 'test_helper' + +class ErrorTemplateAttributesControllerTest < ActionController::TestCase + setup do + @error_template_attribute = error_template_attributes(:one) + end + + test "should get index" do + get :index + assert_response :success + assert_not_nil assigns(:error_template_attributes) + end + + test "should get new" do + get :new + assert_response :success + end + + test "should create error_template_attribute" do + assert_difference('ErrorTemplateAttribute.count') do + post :create, error_template_attribute: { } + end + + assert_redirected_to error_template_attribute_path(assigns(:error_template_attribute)) + end + + test "should show error_template_attribute" do + get :show, id: @error_template_attribute + assert_response :success + end + + test "should get edit" do + get :edit, id: @error_template_attribute + assert_response :success + end + + test "should update error_template_attribute" do + patch :update, id: @error_template_attribute, error_template_attribute: { } + assert_redirected_to error_template_attribute_path(assigns(:error_template_attribute)) + end + + test "should destroy error_template_attribute" do + assert_difference('ErrorTemplateAttribute.count', -1) do + delete :destroy, id: @error_template_attribute + end + + assert_redirected_to error_template_attributes_path + end +end From c0160ae4515e3314e442746b0c5087b59311eea8 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 12:06:19 +0200 Subject: [PATCH 10/33] Make table more concise --- app/views/error_templates/index.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/error_templates/index.html.slim b/app/views/error_templates/index.html.slim index 1b028614..f44b3f67 100644 --- a/app/views/error_templates/index.html.slim +++ b/app/views/error_templates/index.html.slim @@ -7,7 +7,7 @@ h1 = ErrorTemplate.model_name.human(count: 2) th = t('activerecord.attributes.error_template.name') th = t('activerecord.attributes.error_template.description') th = t('activerecord.attributes.exercise.execution_environment') - th colspan=5 = t('shared.actions') + th colspan=3 = t('shared.actions') tbody - @error_templates.each do |error_template| tr From 2cc1a7478ccb78244a597086b7344cd31a39212e Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 12:06:40 +0200 Subject: [PATCH 11/33] Add actions to attribute list --- app/views/error_templates/show.html.slim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index 885c3788..cc262171 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -18,6 +18,7 @@ h3 th = t('activerecord.attributes.error_template_attribute.key') th = t('activerecord.attributes.error_template_attribute.description') th = t('activerecord.attributes.error_template_attribute.regex') + th colspan=3 = t('shared.actions') tbody - @error_template.error_template_attributes.order(:important).each do |attribute| tr @@ -29,3 +30,5 @@ h3 td = attribute.key td = attribute.description td = attribute.regex + td = link_to(t('shared.show'), attribute) + td = "Remove" From 24fd142d3c723cc4f4ef46012a02c00bebd1c127 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 15:15:19 +0200 Subject: [PATCH 12/33] Add localization and plural forms for error templates and error template attributes --- config/locales/de.yml | 6 ++++++ config/locales/en.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/config/locales/de.yml b/config/locales/de.yml index 6aca7d64..edb5610f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -129,6 +129,12 @@ de: error: one: Fehler other: Fehler + error_template: + one: Fehlertemplate + other: Fehlertemplates + error_template_attribute: + one: Fehlertemplatettribut + other: Fehlertemplatettribute execution_environment: one: Ausführungsumgebung other: Ausführungsumgebungen diff --git a/config/locales/en.yml b/config/locales/en.yml index 34d8894f..7cfe4c48 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -150,6 +150,12 @@ en: error: one: Error other: Errors + error_template: + one: Error Template + other: Error Templates + error_template_attribute: + one: Error Template Attribute + other: Error Template Attributes execution_environment: one: Execution Environment other: Execution Environments From 28605fbe9b0f7c9f6772213cb5fb6492af0ebf3b Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 21:25:18 +0200 Subject: [PATCH 13/33] Support adding and removing attributes of error templates --- app/assets/javascripts/error_templates.js | 18 +++++++++++++++++ app/assets/stylesheets/error_templates.scss | 9 +++++++++ app/controllers/error_templates_controller.rb | 20 ++++++++++++++++++- app/policies/error_template_policy.rb | 6 ++++++ app/views/error_templates/show.html.slim | 8 +++++++- config/locales/de.yml | 1 + config/locales/en.yml | 1 + config/routes.rb | 7 ++++++- 8 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/error_templates.js create mode 100644 app/assets/stylesheets/error_templates.scss diff --git a/app/assets/javascripts/error_templates.js b/app/assets/javascripts/error_templates.js new file mode 100644 index 00000000..1b9b5234 --- /dev/null +++ b/app/assets/javascripts/error_templates.js @@ -0,0 +1,18 @@ +$(function() { + if ($.isController('error_templates')) { + $('#add-attribute').find('button').on('click', function () { + $.ajax(location + '/attribute.json', { + method: 'POST', + data: { + _method: 'PUT', + dataType: 'json', + error_template_attribute_id: $('#add-attribute').find('select').val() + } + }).success(function () { + location.reload(); + }).error(function (error) { + $.flash.danger({text: error.statusText}); + }); + }); + } +}); \ No newline at end of file diff --git a/app/assets/stylesheets/error_templates.scss b/app/assets/stylesheets/error_templates.scss new file mode 100644 index 00000000..29b2f96c --- /dev/null +++ b/app/assets/stylesheets/error_templates.scss @@ -0,0 +1,9 @@ +#add-attribute { + display: flex; + max-width: 400px; + margin-top: 30px; + + button { + margin-left: 10px; + } +} \ No newline at end of file diff --git a/app/controllers/error_templates_controller.rb b/app/controllers/error_templates_controller.rb index 6bf9a4f2..2632abf9 100644 --- a/app/controllers/error_templates_controller.rb +++ b/app/controllers/error_templates_controller.rb @@ -1,5 +1,5 @@ class ErrorTemplatesController < ApplicationController - before_action :set_error_template, only: [:show, :edit, :update, :destroy] + before_action :set_error_template, only: [:show, :edit, :update, :destroy, :add_attribute, :remove_attribute] def authorize! authorize(@error_templates || @error_template) @@ -73,6 +73,24 @@ class ErrorTemplatesController < ApplicationController end end + def add_attribute + authorize! + @error_template.error_template_attributes << ErrorTemplateAttribute.find(params['error_template_attribute_id']) + respond_to do |format| + format.html { redirect_to @error_template } + format.json { head :no_content } + end + end + + def remove_attribute + authorize! + @error_template.error_template_attributes.delete(ErrorTemplateAttribute.find(params['error_template_attribute_id'])) + respond_to do |format| + format.html { redirect_to @error_template } + format.json { head :no_content } + end + end + private # Use callbacks to share common setup or constraints between actions. def set_error_template diff --git a/app/policies/error_template_policy.rb b/app/policies/error_template_policy.rb index 30bdbd24..908be08e 100644 --- a/app/policies/error_template_policy.rb +++ b/app/policies/error_template_policy.rb @@ -1,3 +1,9 @@ class ErrorTemplatePolicy < AdminOnlyPolicy + def add_attribute? + admin? + end + def remove_attribute? + admin? + end end diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index cc262171..5b513e6c 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -31,4 +31,10 @@ h3 td = attribute.description td = attribute.regex td = link_to(t('shared.show'), attribute) - td = "Remove" + td = link_to(t('shared.destroy'), attribute_error_template_url(:error_template_attribute_id => attribute.id), :method => :delete) + +#add-attribute + = collection_select({}, :error_template_attribute_id, + ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order(:important, :key), + :id, :key, {include_blank: false}, class: '') + button.btn.btn-default = t('error_templates.add_attribute') diff --git a/config/locales/de.yml b/config/locales/de.yml index edb5610f..c2414a16 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -600,3 +600,4 @@ de: hints: signature: "Ein regulärer Ausdruck in Ruby-Syntax und ohne führende und schließende \"/\"" attributes: "Attribute" + add_attribute: "Attribut hinzufügen" diff --git a/config/locales/en.yml b/config/locales/en.yml index 7cfe4c48..d76774d2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -621,3 +621,4 @@ en: hints: signature: "A regular expression in Ruby syntax without leading and trailing \"/\"" attributes: "Attributes" + add_attribute: "Add attribute" diff --git a/config/routes.rb b/config/routes.rb index c614a50c..24a9671d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,12 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do resources :error_template_attributes - resources :error_templates + resources :error_templates do + member do + put 'attribute', to: 'error_templates#add_attribute' + delete 'attribute', to: 'error_templates#remove_attribute' + end + end resources :file_templates do collection do get 'by_file_type/:file_type_id', as: :by_file_type, action: :by_file_type From 45614c5a8cf4631a18d5aaa02e52c877c04b4bbf Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 21:37:43 +0200 Subject: [PATCH 14/33] Add error template views to navigation --- app/views/application/_navigation.html.slim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/application/_navigation.html.slim b/app/views/application/_navigation.html.slim index b9663d3f..7b9e0a33 100644 --- a/app/views/application/_navigation.html.slim +++ b/app/views/application/_navigation.html.slim @@ -8,7 +8,8 @@ - if current_user.admin? li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path) li.divider - - models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser].sort_by { |model| model.model_name.human(count: 2) } + - models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, + ErrorTemplate, ErrorTemplateAttribute, InternalUser].sort_by { |model| model.model_name.human(count: 2) } - models.each do |model| - if policy(model).index? li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path")) From a9068ef1b7c7a28a46063d8b25fcd46190b8745c Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 21:37:57 +0200 Subject: [PATCH 15/33] Fix error template attribute creation --- app/controllers/error_template_attributes_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/error_template_attributes_controller.rb b/app/controllers/error_template_attributes_controller.rb index dc2da30e..e5e0d486 100644 --- a/app/controllers/error_template_attributes_controller.rb +++ b/app/controllers/error_template_attributes_controller.rb @@ -81,6 +81,6 @@ class ErrorTemplateAttributesController < ApplicationController # Never trust parameters from the scary internet, only allow the white list through. def error_template_attribute_params - params.fetch(:error_template_attribute, {}) + params[:error_template_attribute].permit(:key, :description, :regex, :important) end end From 0fabed4c1eb4490081f4cd66c18177ac570134c5 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 21:43:49 +0200 Subject: [PATCH 16/33] Fix sorting by important attributes --- app/views/error_templates/show.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index 5b513e6c..574e3d88 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -35,6 +35,6 @@ h3 #add-attribute = collection_select({}, :error_template_attribute_id, - ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order(:important, :key), + ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key), :id, :key, {include_blank: false}, class: '') button.btn.btn-default = t('error_templates.add_attribute') From 7ba245920c6c1b5c72a66a3dc7c6ae16492c13f5 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 12 Jul 2017 21:46:39 +0200 Subject: [PATCH 17/33] Use the same sorting everywhere --- app/controllers/error_template_attributes_controller.rb | 2 +- app/views/error_templates/show.html.slim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/error_template_attributes_controller.rb b/app/controllers/error_template_attributes_controller.rb index e5e0d486..05de2772 100644 --- a/app/controllers/error_template_attributes_controller.rb +++ b/app/controllers/error_template_attributes_controller.rb @@ -9,7 +9,7 @@ class ErrorTemplateAttributesController < ApplicationController # GET /error_template_attributes # GET /error_template_attributes.json def index - @error_template_attributes = ErrorTemplateAttribute.all.order(:id).paginate(page: params[:page]) + @error_template_attributes = ErrorTemplateAttribute.all.order('important DESC', :key, :id).paginate(page: params[:page]) authorize! end diff --git a/app/views/error_templates/show.html.slim b/app/views/error_templates/show.html.slim index 574e3d88..9936ef7f 100644 --- a/app/views/error_templates/show.html.slim +++ b/app/views/error_templates/show.html.slim @@ -20,7 +20,7 @@ h3 th = t('activerecord.attributes.error_template_attribute.regex') th colspan=3 = t('shared.actions') tbody - - @error_template.error_template_attributes.order(:important).each do |attribute| + - @error_template.error_template_attributes.order('important DESC', :key).each do |attribute| tr td - if attribute.important From 5d6158f95a2d63062fcdf89e20eec619316fd457 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 19 Jul 2017 15:46:19 +0200 Subject: [PATCH 18/33] Fix structured error creation if attributes don't match; write match status to database --- app/models/structured_error_attribute.rb | 12 ++++++++++-- ...133351_add_match_to_structured_error_attribute.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20170719133351_add_match_to_structured_error_attribute.rb diff --git a/app/models/structured_error_attribute.rb b/app/models/structured_error_attribute.rb index b9967719..6eb17fc4 100644 --- a/app/models/structured_error_attribute.rb +++ b/app/models/structured_error_attribute.rb @@ -3,7 +3,15 @@ class StructuredErrorAttribute < ActiveRecord::Base belongs_to :error_template_attribute def self.create_from_template(attribute, structured_error, message_buffer) - value = message_buffer.match(attribute.regex).captures[0] - self.create(structured_error: structured_error, error_template_attribute: attribute, value: value) + match = false + value = nil + result = message_buffer.match(attribute.regex) + if result != nil + match = true + if result.captures.size > 0 + value = result.captures[0] + end + end + self.create(structured_error: structured_error, error_template_attribute: attribute, value: value, match: match) end end diff --git a/db/migrate/20170719133351_add_match_to_structured_error_attribute.rb b/db/migrate/20170719133351_add_match_to_structured_error_attribute.rb new file mode 100644 index 00000000..7ec3ccc0 --- /dev/null +++ b/db/migrate/20170719133351_add_match_to_structured_error_attribute.rb @@ -0,0 +1,5 @@ +class AddMatchToStructuredErrorAttribute < ActiveRecord::Migration + def change + add_column :structured_error_attributes, :match, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 137d251d..62a776f9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170711170928) do +ActiveRecord::Schema.define(version: 20170719133351) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -298,6 +298,7 @@ ActiveRecord::Schema.define(version: 20170711170928) do t.string "value" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.boolean "match" end create_table "structured_errors", force: :cascade do |t| From 280b4dbe0c00bd0716b6eafc60537bb299a80dd1 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 23 Aug 2017 15:37:48 +0200 Subject: [PATCH 19/33] Fix question mark bug --- app/controllers/submissions_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index ae111823..5a256b17 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -202,7 +202,7 @@ class SubmissionsController < ApplicationController if !@message_buffer.blank? @submission.exercise.execution_environment.error_templates.each do |template| pattern = Regexp.new(template.signature).freeze - if pattern.match?(@message_buffer) + if pattern.match(@message_buffer) StructuredError.create_from_template(template, @message_buffer) end end From ddeab8c34feb9c1333959ffad40f7258bd98b995 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Mon, 16 Oct 2017 13:12:46 +0200 Subject: [PATCH 20/33] Remove auto-generated tests --- .../exercise_collections_controller_test.rb | 14 -------------- test/controllers/subscription_controller_test.rb | 7 ------- test/factories/subscriptions.rb | 7 ------- test/models/subscription_test.rb | 7 ------- 4 files changed, 35 deletions(-) delete mode 100644 test/controllers/exercise_collections_controller_test.rb delete mode 100644 test/controllers/subscription_controller_test.rb delete mode 100644 test/factories/subscriptions.rb delete mode 100644 test/models/subscription_test.rb diff --git a/test/controllers/exercise_collections_controller_test.rb b/test/controllers/exercise_collections_controller_test.rb deleted file mode 100644 index 699c9271..00000000 --- a/test/controllers/exercise_collections_controller_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'test_helper' - -class ExerciseCollectionsControllerTest < ActionController::TestCase - test "should get index" do - get :index - assert_response :success - end - - test "should get show" do - get :show - assert_response :success - end - -end diff --git a/test/controllers/subscription_controller_test.rb b/test/controllers/subscription_controller_test.rb deleted file mode 100644 index 8dde0e19..00000000 --- a/test/controllers/subscription_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class SubscriptionControllerTest < ActionController::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/factories/subscriptions.rb b/test/factories/subscriptions.rb deleted file mode 100644 index 11c5a67a..00000000 --- a/test/factories/subscriptions.rb +++ /dev/null @@ -1,7 +0,0 @@ -FactoryGirl.define do - factory :subscription do - user nil - request_for_comments nil - type "" - end -end diff --git a/test/models/subscription_test.rb b/test/models/subscription_test.rb deleted file mode 100644 index a045d1ea..00000000 --- a/test/models/subscription_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class SubscriptionTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end From 686d56bbd6e73ca907d9c981de11cd061723196f Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Mon, 16 Oct 2017 13:20:40 +0200 Subject: [PATCH 21/33] Add rspec persistence file to config to allow for re-running only failed tests locally --- spec/spec_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0ea8706a..1fa8ecda 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -82,4 +82,6 @@ RSpec.configure do |config| # a real object. This is generally recommended. mocks.verify_partial_doubles = true end + + config.example_status_persistence_file_path = 'tmp/rspec_persistence_file.txt' end From a00adbce256ce89c494d56074290ffab9b0b5d9e Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Mon, 16 Oct 2017 14:02:40 +0200 Subject: [PATCH 22/33] Move *_url spec to controller, because the subscription model does not handle urls anymore --- .../submissions_controller_spec.rb | 21 +++++++++++++++++++ spec/models/submission_spec.rb | 15 ------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/spec/controllers/submissions_controller_spec.rb b/spec/controllers/submissions_controller_spec.rb index 3670645b..d1e489ff 100644 --- a/spec/controllers/submissions_controller_spec.rb +++ b/spec/controllers/submissions_controller_spec.rb @@ -183,6 +183,27 @@ describe SubmissionsController do expect_template(:show) end + describe 'GET #show.json' do + before(:each) { get :show, id: submission.id, format: :json } + expect_assigns(submission: :submission) + expect_status(200) + + [:render, :run, :test].each do |action| + describe "##{action}_url" do + let(:url) { response.body.send(:"#{action}_url") } + + it "starts like the #{action} path" do + filename = File.basename(__FILE__) + expect(url).to start_with(Rails.application.routes.url_helpers.send(:"#{action}_submission_path", submission, filename).sub(filename, '')) + end + + it 'ends with a placeholder' do + expect(url).to end_with(Submission::FILENAME_URL_PLACEHOLDER) + end + end + end + end + describe 'GET #score' do let(:request) { proc { get :score, id: submission.id } } before(:each) { request.call } diff --git a/spec/models/submission_spec.rb b/spec/models/submission_spec.rb index 3c297ca4..75f0cf2a 100644 --- a/spec/models/submission_spec.rb +++ b/spec/models/submission_spec.rb @@ -16,21 +16,6 @@ describe Submission do expect(described_class.create.errors[:user_type]).to be_present end - [:render, :run, :test].each do |action| - describe "##{action}_url" do - let(:url) { submission.send(:"#{action}_url") } - - it "starts like the #{action} path" do - filename = File.basename(__FILE__) - expect(url).to start_with(Rails.application.routes.url_helpers.send(:"#{action}_submission_path", submission, filename).sub(filename, '')) - end - - it 'ends with a placeholder' do - expect(url).to end_with(Submission::FILENAME_URL_PLACEHOLDER) - end - end - end - describe '#main_file' do let(:submission) { FactoryGirl.create(:submission) } From ffe4f65628b3cf0b5c4e913898173488e806e4c9 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 18 Oct 2017 22:05:07 +0200 Subject: [PATCH 23/33] Adapt lti_spec to current functionality --- spec/concerns/lti_spec.rb | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/spec/concerns/lti_spec.rb b/spec/concerns/lti_spec.rb index c03ef9a5..a7028224 100644 --- a/spec/concerns/lti_spec.rb +++ b/spec/concerns/lti_spec.rb @@ -25,31 +25,23 @@ describe Lti do describe '#external_user_name' do let(:first_name) { 'Jane' } - let(:full_name) { 'John Doe' } let(:last_name) { 'Doe' } + let(:full_name) { 'John Doe' } let(:provider) { double } + let(:provider_full) { double(:lis_person_name_full => full_name) } context 'when a full name is provided' do it 'returns the full name' do - expect(provider).to receive(:lis_person_name_full).twice.and_return(full_name) - expect(controller.send(:external_user_name, provider)).to eq(full_name) - end - end - - context 'when first and last name are provided' do - it 'returns the concatenated names' do - expect(provider).to receive(:lis_person_name_full) - expect(provider).to receive(:lis_person_name_given).twice.and_return(first_name) - expect(provider).to receive(:lis_person_name_family).twice.and_return(last_name) - expect(controller.send(:external_user_name, provider)).to eq("#{first_name} #{last_name}") + expect(provider_full).to receive(:lis_person_name_full).twice.and_return(full_name) + expect(controller.send(:external_user_name, provider_full)).to eq(full_name) end end context 'when only partial information is provided' do it 'returns the first available name' do expect(provider).to receive(:lis_person_name_full) - expect(provider).to receive(:lis_person_name_given).twice.and_return(first_name) - expect(provider).to receive(:lis_person_name_family) + expect(provider).to receive(:lis_person_name_given).and_return(first_name) + expect(provider).not_to receive(:lis_person_name_family) expect(controller.send(:external_user_name, provider)).to eq(first_name) end end @@ -122,6 +114,7 @@ describe Lti do context 'when grading is not supported' do it 'returns a corresponding status' do + skip('todo') expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:outcome_service?).and_return(false) expect(controller.send(:send_score, submission.exercise_id, score, submission.user_id)[:status]).to eq('unsupported') end @@ -140,10 +133,12 @@ describe Lti do end it 'sends the score' do + skip('todo') controller.send(:send_score, submission.exercise_id, score, submission.user_id) end it 'returns code, message, and status' do + skip('todo') result = controller.send(:send_score, submission.exercise_id, score, submission.user_id) expect(result[:code]).to eq(response.response_code) expect(result[:message]).to eq(response.body) From 6d28f427d89eb124f2ee65bcb79fd4da2c21e880 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 25 Oct 2017 10:12:29 +0200 Subject: [PATCH 24/33] Add Ralf's comment to skipped tests --- spec/concerns/lti_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/concerns/lti_spec.rb b/spec/concerns/lti_spec.rb index a7028224..32d740aa 100644 --- a/spec/concerns/lti_spec.rb +++ b/spec/concerns/lti_spec.rb @@ -114,7 +114,7 @@ describe Lti do context 'when grading is not supported' do it 'returns a corresponding status' do - skip('todo') + skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked') expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:outcome_service?).and_return(false) expect(controller.send(:send_score, submission.exercise_id, score, submission.user_id)[:status]).to eq('unsupported') end @@ -133,12 +133,12 @@ describe Lti do end it 'sends the score' do - skip('todo') + skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked') controller.send(:send_score, submission.exercise_id, score, submission.user_id) end it 'returns code, message, and status' do - skip('todo') + skip('ralf: this does not work, since send_score pulls data from the database, which then returns an empty array. On this is called .first, which returns nil and lets the test fail. Before Toms changes, this was taken from the session, which could be mocked') result = controller.send(:send_score, submission.exercise_id, score, submission.user_id) expect(result[:code]).to eq(response.response_code) expect(result[:message]).to eq(response.body) From 01aad0a4a69ddbf9915213c08ca64a75f894a5be Mon Sep 17 00:00:00 2001 From: Thomas Hille Date: Wed, 25 Oct 2017 11:28:27 +0200 Subject: [PATCH 25/33] fixed problem in ProxyExercise that caused: ActiveRecord::AssociationTypeMismatch (Exercise(#51937940) expected, got TrueClass(#7943420)): --- app/models/proxy_exercise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/proxy_exercise.rb b/app/models/proxy_exercise.rb index 5922e062..82fad35f 100644 --- a/app/models/proxy_exercise.rb +++ b/app/models/proxy_exercise.rb @@ -36,8 +36,8 @@ class ProxyExercise < ActiveRecord::Base Rails.logger.debug("retrieved assigned exercise for user #{user.id}: Exercise #{assigned_user_proxy_exercise.exercise}" ) assigned_user_proxy_exercise.exercise else + Rails.logger.debug("find new matching exercise for user #{user.id}" ) matching_exercise = - Rails.logger.debug("find new matching exercise for user #{user.id}" ) begin find_matching_exercise(user) rescue => e #fallback From 87f280089d76efaf79132fdf91daff572dc43220 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 25 Oct 2017 12:07:16 +0200 Subject: [PATCH 26/33] Fix return value of logger being assigned to exercise --- app/models/proxy_exercise.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/proxy_exercise.rb b/app/models/proxy_exercise.rb index 5922e062..5bdff20d 100644 --- a/app/models/proxy_exercise.rb +++ b/app/models/proxy_exercise.rb @@ -37,8 +37,8 @@ class ProxyExercise < ActiveRecord::Base assigned_user_proxy_exercise.exercise else matching_exercise = - Rails.logger.debug("find new matching exercise for user #{user.id}" ) begin + Rails.logger.debug("find new matching exercise for user #{user.id}" ) find_matching_exercise(user) rescue => e #fallback Rails.logger.error("finding matching exercise failed. Fall back to random exercise! Error: #{$!}" ) @@ -85,8 +85,7 @@ class ProxyExercise < ActiveRecord::Base @reason[:reason] = "easiest exercise in pool. empty potential exercises" select_easiest_exercise(exercises) else - recommended_exercise = select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises) - recommended_exercise + select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises) end end @@ -238,4 +237,4 @@ class ProxyExercise < ActiveRecord::Base exercises.order(:expected_difficulty).first end -end \ No newline at end of file +end From 0bade2c2e7376eb3f8987ece69e1cf683039276d Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 25 Oct 2017 14:05:10 +0200 Subject: [PATCH 27/33] Fix recommending too difficult questions if user has too low level --- app/models/proxy_exercise.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/proxy_exercise.rb b/app/models/proxy_exercise.rb index 5bdff20d..612fbc59 100644 --- a/app/models/proxy_exercise.rb +++ b/app/models/proxy_exercise.rb @@ -72,7 +72,7 @@ class ProxyExercise < ActiveRecord::Base # find exercises potential_recommended_exercises = [] - exercises.where("expected_difficulty > 1").each do |ex| + exercises.where("expected_difficulty >= 1").each do |ex| ## find exercises which have only tags the user has already seen if (ex.tags - tags_user_has_seen).empty? potential_recommended_exercises << ex From 13e33bf977f073578515fce648899ebbac5f0895 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 26 Oct 2017 15:42:06 +0200 Subject: [PATCH 28/33] Add optional logging for tests --- config/environments/test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/environments/test.rb b/config/environments/test.rb index 1c19f08b..a5c78bf9 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -39,4 +39,8 @@ Rails.application.configure do # Raises error for missing translations # config.action_view.raise_on_missing_translations = true + + #config.logger = Logger.new(STDOUT) + # Set log level + #config.log_level = :DEBUG end From 14a135a0c9e7e05326c2af564ae508772470d5b3 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 26 Oct 2017 15:42:20 +0200 Subject: [PATCH 29/33] Add explanatory comment to config --- spec/spec_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1fa8ecda..ca9f9a63 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -83,5 +83,6 @@ RSpec.configure do |config| mocks.verify_partial_doubles = true end + # Save test results to persistence file to enable usage of --next-failure flag in local testing/debugging config.example_status_persistence_file_path = 'tmp/rspec_persistence_file.txt' end From 34e96e40beedfbd2d531ad973d2c66052b59b1c6 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 26 Oct 2017 15:43:14 +0200 Subject: [PATCH 30/33] Fix submissions controller test json response --- spec/controllers/submissions_controller_spec.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/controllers/submissions_controller_spec.rb b/spec/controllers/submissions_controller_spec.rb index d1e489ff..d9ee5316 100644 --- a/spec/controllers/submissions_controller_spec.rb +++ b/spec/controllers/submissions_controller_spec.rb @@ -184,13 +184,17 @@ describe SubmissionsController do end describe 'GET #show.json' do + # Render views requested in controller tests in order to get json responses + # https://github.com/rails/jbuilder/issues/32 + render_views + before(:each) { get :show, id: submission.id, format: :json } expect_assigns(submission: :submission) expect_status(200) [:render, :run, :test].each do |action| describe "##{action}_url" do - let(:url) { response.body.send(:"#{action}_url") } + let(:url) { JSON.parse(response.body).with_indifferent_access.fetch("#{action}_url") } it "starts like the #{action} path" do filename = File.basename(__FILE__) From 0fd993c1cd94371ac054df0475eadbe5863a7e06 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 26 Oct 2017 15:48:21 +0200 Subject: [PATCH 31/33] Move submission url attributes to controller test --- spec/controllers/submissions_controller_spec.rb | 10 ++++++++++ spec/models/submission_spec.rb | 10 ---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/controllers/submissions_controller_spec.rb b/spec/controllers/submissions_controller_spec.rb index d9ee5316..98f36cb2 100644 --- a/spec/controllers/submissions_controller_spec.rb +++ b/spec/controllers/submissions_controller_spec.rb @@ -206,6 +206,16 @@ describe SubmissionsController do end end end + + [:score, :stop].each do |action| + describe "##{action}_url" do + let(:url) { JSON.parse(response.body).with_indifferent_access.fetch("#{action}_url") } + + it "corresponds to the #{action} path" do + expect(url).to eq(Rails.application.routes.url_helpers.send(:"#{action}_submission_path", submission)) + end + end + end end describe 'GET #score' do diff --git a/spec/models/submission_spec.rb b/spec/models/submission_spec.rb index 75f0cf2a..64f4e49e 100644 --- a/spec/models/submission_spec.rb +++ b/spec/models/submission_spec.rb @@ -63,16 +63,6 @@ describe Submission do end end - [:score, :stop].each do |action| - describe "##{action}_url" do - let(:url) { submission.send(:"#{action}_url") } - - it "corresponds to the #{action} path" do - expect(url).to eq(Rails.application.routes.url_helpers.send(:"#{action}_submission_path", submission)) - end - end - end - describe '#siblings' do let(:siblings) { described_class.find_by(user: user).siblings } let(:user) { FactoryGirl.create(:external_user) } From 04baf6c5d55612b3d95864ce7fc98b410db1a97c Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 26 Oct 2017 16:14:40 +0200 Subject: [PATCH 32/33] Make paths explicit to fix tests --- app/views/exercises/_editor.html.slim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 4291028f..e692b748 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -3,7 +3,7 @@ - consumer_id = @current_user.respond_to?(:external_id) ? @current_user.consumer_id : '' #'tests' #(@current_user.uuid.present? ? @current_user.uuid : '') - show_break_interventions = @show_break_interventions || "false" - show_rfc_interventions = @show_rfc_interventions || "false" -#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path data-intervention-save-url=intervention_exercise_path data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path +#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files) div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id ) div id='frames' class='editor-col' @@ -24,4 +24,4 @@ = render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent') -= render('shared/modal', id: 'break-intervention-modal', title: t('exercises.implement.break_intervention.title'), template: 'interventions/_break_intervention_modal') \ No newline at end of file += render('shared/modal', id: 'break-intervention-modal', title: t('exercises.implement.break_intervention.title'), template: 'interventions/_break_intervention_modal') From 08c715470806b19ac0ddfe3b1141cf132b23df8e Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 1 Nov 2017 10:14:03 +0100 Subject: [PATCH 33/33] removed duplicate logging statement --- app/models/proxy_exercise.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/proxy_exercise.rb b/app/models/proxy_exercise.rb index 3e5d740c..9c84e3ad 100644 --- a/app/models/proxy_exercise.rb +++ b/app/models/proxy_exercise.rb @@ -39,7 +39,6 @@ class ProxyExercise < ActiveRecord::Base Rails.logger.debug("find new matching exercise for user #{user.id}" ) matching_exercise = begin - Rails.logger.debug("find new matching exercise for user #{user.id}" ) find_matching_exercise(user) rescue => e #fallback Rails.logger.error("finding matching exercise failed. Fall back to random exercise! Error: #{$!}" )