From 2293751fe25682032d3b8404756e1227184bbcae Mon Sep 17 00:00:00 2001 From: david Date: Tue, 12 Nov 2024 22:43:59 +0100 Subject: [PATCH] Add screenshots to elements --- Gemfile | 1 + Gemfile.lock | 14 +++ app/controllers/elements_controller.rb | 2 +- app/models/element.rb | 4 + app/views/elements/_element.html.erb | 17 ++- app/views/elements/_element.odt.erb | 3 + app/views/elements/_form.html.erb | 7 -- app/views/elements/_form.html.slim | 9 ++ app/views/elements/update.turbo_stream.slim | 3 +- app/views/exports/show.html.slim | 2 + bin/jobs | 6 + config/database.yml | 18 ++- config/environments/development.rb | 4 +- config/environments/production.rb | 4 +- config/puma.rb | 1 + config/queue.yml | 18 +++ config/recurring.yml | 10 ++ db/queue_schema.rb | 129 ++++++++++++++++++++ 18 files changed, 232 insertions(+), 20 deletions(-) delete mode 100644 app/views/elements/_form.html.erb create mode 100644 app/views/elements/_form.html.slim create mode 100755 bin/jobs create mode 100644 config/queue.yml create mode 100644 config/recurring.yml create mode 100644 db/queue_schema.rb diff --git a/Gemfile b/Gemfile index 4819425..7268a2b 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,7 @@ gem "prawn-markup" gem "prawn-rails" gem "sablon" gem "slim" +gem "solid_queue" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/Gemfile.lock b/Gemfile.lock index be0da77..c9d597d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,12 +116,17 @@ GEM reline (>= 0.3.8) drb (2.2.1) erubi (1.13.0) + et-orbi (1.2.11) + tzinfo ffi (1.17.0-aarch64-linux-gnu) ffi (1.17.0-arm-linux-gnu) ffi (1.17.0-arm64-darwin) ffi (1.17.0-x86-linux-gnu) ffi (1.17.0-x86_64-darwin) ffi (1.17.0-x86_64-linux-gnu) + fugit (1.11.1) + et-orbi (~> 1, >= 1.2.11) + raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) hpricot (0.8.6) @@ -226,6 +231,7 @@ GEM public_suffix (6.0.0) puma (6.4.2) nio4r (~> 2.0) + raabro (1.4.0) racc (1.8.0) rack (3.1.7) rack-session (2.0.0) @@ -330,6 +336,13 @@ GEM slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) + solid_queue (1.0.1) + activejob (>= 7.1) + activerecord (>= 7.1) + concurrent-ruby (>= 1.3.1) + fugit (~> 1.11.0) + railties (>= 7.1) + thor (~> 1.3.1) sorbet-runtime (0.5.11625) sqlite3 (2.2.0-aarch64-linux-gnu) sqlite3 (2.2.0-arm-linux-gnu) @@ -409,6 +422,7 @@ DEPENDENCIES sablon selenium-webdriver slim + solid_queue sqlite3 (>= 2.1) stimulus-rails turbo-rails diff --git a/app/controllers/elements_controller.rb b/app/controllers/elements_controller.rb index 152c904..abfd931 100644 --- a/app/controllers/elements_controller.rb +++ b/app/controllers/elements_controller.rb @@ -87,6 +87,6 @@ class ElementsController < ApplicationController # Only allow a list of trusted parameters through. def element_params - params.require(:element).permit(:page_id, :title, :description, :position) + params.require(:element).permit(:page_id, :title, :description, :position, :screenshot) end end diff --git a/app/models/element.rb b/app/models/element.rb index b46e492..e0b7ddf 100644 --- a/app/models/element.rb +++ b/app/models/element.rb @@ -11,6 +11,10 @@ class Element < ApplicationRecord before_validation :set_position before_update :update_positions, if: :position_changed? + has_one_attached :screenshot do |attachable| + attachable.variant :thumbnail, resize_to_limit: [ 200, 200 ] + end + # Calculate actual conformity level: # - if a success_criterion has result :failed -> the confirmity_level # of that success_criterion is not reached. diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index 346648f..a465f6c 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -17,11 +17,18 @@ <% end %> - <% if element.description %> -
- <%= element.description %> -
- <% end %> + +
+ <% if element.description %> +
+ <%= element.description %> +
+ <% end %> + <%= safe_display(element.screenshot) do %> + <%= image_tag(_1.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> + <% end %> +
+

diff --git a/app/views/elements/_element.odt.erb b/app/views/elements/_element.odt.erb index bf1ae9e..fac3639 100644 --- a/app/views/elements/_element.odt.erb +++ b/app/views/elements/_element.odt.erb @@ -7,6 +7,9 @@ <%= element.description_html %> + <%= image_tag(element.screenshot.preview(:thumbnail)) %> + <%= image_tag(element.screenshot) %> + <% element.success_criteria.each do |sc| %> <%= render sc %> <% end %> diff --git a/app/views/elements/_form.html.erb b/app/views/elements/_form.html.erb deleted file mode 100644 index 1795bfc..0000000 --- a/app/views/elements/_form.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= bootstrap_form_with(model: element.persisted? ? element : [:page, element], class: "mb-3") do |form| %> - <%= form.hidden_field :page_id %> - <%= form.text_field :title %> - <%= form.rich_text_area :description %> - <%= form.submit class: "btn btn-primary" %> - <%= link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") %> -<% end %> diff --git a/app/views/elements/_form.html.slim b/app/views/elements/_form.html.slim new file mode 100644 index 0000000..4feb80d --- /dev/null +++ b/app/views/elements/_form.html.slim @@ -0,0 +1,9 @@ += bootstrap_form_with(model: element.persisted? ? element : [:page, element], class: "mb-3") do |form| + = form.hidden_field :page_id + = form.text_field :title + = form.rich_text_area :description + = form.file_field :screenshot + - if element.persisted? + = safe_display(element.screenshot) { tag.div(link_to(_1.filename.to_s, _1), class: "mb-3") } + = form.submit class: "btn btn-primary" + = link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") diff --git a/app/views/elements/update.turbo_stream.slim b/app/views/elements/update.turbo_stream.slim index e6b478c..64c6489 100644 --- a/app/views/elements/update.turbo_stream.slim +++ b/app/views/elements/update.turbo_stream.slim @@ -4,4 +4,5 @@ / - element.success_criteria.each do |sc| / = turbo_stream.update dom_id(sc, :title), "#{sc.page.position}.#{sc.element.position}.#{sc.position} #{sc.title}" - element.success_criteria.each do |sc| - = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" \ No newline at end of file + = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" + = turbo_stream.replace dom_id(element, :frame), element \ No newline at end of file diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 2a00726..2f28bea 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -30,6 +30,8 @@ h2 Testbericht h3 = "#{page.position} #{page.path}" - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| h4 = "#{element.number} #{element.title}" + = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} + = element.description - element.success_criteria.select{ _1.failed? }.each do |sc| h5 = "#{sc.number} #{sc.title}" - if sc.test_comment? diff --git a/bin/jobs b/bin/jobs new file mode 100755 index 0000000..dcf59f3 --- /dev/null +++ b/bin/jobs @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require_relative "../config/environment" +require "solid_queue/cli" + +SolidQueue::Cli.start(ARGV) diff --git a/config/database.yml b/config/database.yml index 56d378d..843b079 100644 --- a/config/database.yml +++ b/config/database.yml @@ -11,8 +11,13 @@ default: &default default_transaction_mode: IMMEDIATE development: - <<: *default - database: storage/development.sqlite3 + primary: + <<: *default + database: storage/development.sqlite3 + queue: + <<: *default + database: storage/development_queue.sqlite3 + migrations_paths: db/queue_migrate # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -22,5 +27,10 @@ test: database: storage/test.sqlite3 production: - <<: *default - database: storage/production.sqlite3 + primary: + <<: *default + database: storage/production.sqlite3 + queue: + <<: *default + database: storage/production_queue.sqlite3 + migrations_paths: db/queue_migrate diff --git a/config/environments/development.rb b/config/environments/development.rb index c6145f2..a0f127e 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -60,6 +60,8 @@ Rails.application.configure do # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + config.active_job.queue_adapter = :solid_queue + config.solid_queue.connects_to = { database: { writing: :queue } } # Suppress logger output for asset requests. config.assets.quiet = true @@ -67,7 +69,7 @@ Rails.application.configure do # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true - # Annotate rendered view with file names. + # Annotate rendered view with file nactive_jobames. config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. diff --git a/config/environments/production.rb b/config/environments/production.rb index b20131b..399551b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -73,7 +73,9 @@ Rails.application.configure do # config.cache_store = :mem_cache_store # Use a real queuing backend for Active Job (and separate queues per environment). - # config.active_job.queue_adapter = :resque + config.active_job.queue_adapter = :solid_queue + config.solid_queue.connects_to = { database: { writing: :queue } } + # config.active_job.queue_name_prefix = "a11yist_production" # Disable caching for Action Mailer templates even if Action Controller diff --git a/config/puma.rb b/config/puma.rb index 60e1b9c..5968b47 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -28,6 +28,7 @@ port ENV.fetch("PORT", 3000) # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart +plugin :solid_queue # Only use a pidfile when requested pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/config/queue.yml b/config/queue.yml new file mode 100644 index 0000000..9eace59 --- /dev/null +++ b/config/queue.yml @@ -0,0 +1,18 @@ +default: &default + dispatchers: + - polling_interval: 1 + batch_size: 500 + workers: + - queues: "*" + threads: 3 + processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> + polling_interval: 0.1 + +development: + <<: *default + +test: + <<: *default + +production: + <<: *default diff --git a/config/recurring.yml b/config/recurring.yml new file mode 100644 index 0000000..8486884 --- /dev/null +++ b/config/recurring.yml @@ -0,0 +1,10 @@ +# production: +# cleanup_session: +# class: CleanSoftDeletedRecordsJob +# queue: background +# args: [ 1000, { batch_size: 500 } ] +# schedule: every hour +# periodic_command: +# command: "SoftDeletedRecord.due.delete_all" +# priority: 2 +# schedule: at 5am every day diff --git a/db/queue_schema.rb b/db/queue_schema.rb new file mode 100644 index 0000000..85194b6 --- /dev/null +++ b/db/queue_schema.rb @@ -0,0 +1,129 @@ +ActiveRecord::Schema[7.1].define(version: 1) do + create_table "solid_queue_blocked_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.string "concurrency_key", null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release" + t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + end + + create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.bigint "process_id" + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + end + + create_table "solid_queue_failed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.text "error" + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true + end + + create_table "solid_queue_jobs", force: :cascade do |t| + t.string "queue_name", null: false + t.string "class_name", null: false + t.text "arguments" + t.integer "priority", default: 0, null: false + t.string "active_job_id" + t.datetime "scheduled_at" + t.datetime "finished_at" + t.string "concurrency_key" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id" + t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name" + t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at" + t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering" + t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting" + end + + create_table "solid_queue_pauses", force: :cascade do |t| + t.string "queue_name", null: false + t.datetime "created_at", null: false + t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true + end + + create_table "solid_queue_processes", force: :cascade do |t| + t.string "kind", null: false + t.datetime "last_heartbeat_at", null: false + t.bigint "supervisor_id" + t.integer "pid", null: false + t.string "hostname" + t.text "metadata" + t.datetime "created_at", null: false + t.string "name", null: false + t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id" + end + + create_table "solid_queue_ready_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all" + t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue" + end + + create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "task_key", null: false + t.datetime "run_at", null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + end + + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.string "key", null: false + t.string "schedule", null: false + t.string "command", limit: 2048 + t.string "class_name" + t.text "arguments" + t.string "queue_name" + t.integer "priority", default: 0 + t.boolean "static", default: true, null: false + t.text "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static" + end + + create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "scheduled_at", null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all" + end + + create_table "solid_queue_semaphores", force: :cascade do |t| + t.string "key", null: false + t.integer "value", default: 1, null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at" + t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value" + t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true + end + + add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade +end