synchronize documents generation
This commit is contained in:
+5
-2
@@ -31,10 +31,13 @@ Metrics/MethodLength:
|
|||||||
- 'db/migrate/**'
|
- 'db/migrate/**'
|
||||||
|
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 10
|
Max: 15
|
||||||
|
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 10
|
Max: 15
|
||||||
|
|
||||||
|
Layout/LineLength:
|
||||||
|
AllowedPatterns: ['\A\s*#']
|
||||||
|
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 35
|
Max: 35
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ class SendSubmissionEmailController < ApplicationController
|
|||||||
Submitter.find_by!(slug: params[:submitter_slug])
|
Submitter.find_by!(slug: params[:submitter_slug])
|
||||||
end
|
end
|
||||||
|
|
||||||
Submissions::GenerateResultAttachments.call(@submitter) if @submitter.documents.blank?
|
|
||||||
|
|
||||||
SubmitterMailer.documents_copy_email(@submitter).deliver_later!
|
SubmitterMailer.documents_copy_email(@submitter).deliver_later!
|
||||||
|
|
||||||
respond_to do |f|
|
respond_to do |f|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class StartFormController < ApplicationController
|
|||||||
|
|
||||||
def update
|
def update
|
||||||
@submitter = Submitter.where(submission: @template.submissions.where(deleted_at: nil))
|
@submitter = Submitter.where(submission: @template.submissions.where(deleted_at: nil))
|
||||||
.find_or_initialize_by(**submitter_params)
|
.find_or_initialize_by(email: submitter_params[:email])
|
||||||
|
|
||||||
if @submitter.completed_at?
|
if @submitter.completed_at?
|
||||||
redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email])
|
redirect_to start_form_completed_path(@template.slug, email: submitter_params[:email])
|
||||||
@@ -25,7 +25,7 @@ class StartFormController < ApplicationController
|
|||||||
ua: request.user_agent
|
ua: request.user_agent
|
||||||
)
|
)
|
||||||
|
|
||||||
@submitter.build_submission(template: @template)
|
@submitter.submission ||= Submission.new(template: @template)
|
||||||
|
|
||||||
if @submitter.save
|
if @submitter.save
|
||||||
redirect_to submit_form_path(@submitter.slug)
|
redirect_to submit_form_path(@submitter.slug)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class SubmissionsDownloadController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
submitter = Submitter.find_by(slug: params[:submitter_slug])
|
submitter = Submitter.find_by(slug: params[:submitter_slug])
|
||||||
|
|
||||||
Submissions::GenerateResultAttachments.call(submitter) if submitter.documents.blank?
|
Submissions::EnsureResultGenerated.call(submitter)
|
||||||
|
|
||||||
urls =
|
urls =
|
||||||
Submitters.select_attachments_for_download(submitter).map do |attachment|
|
Submitters.select_attachments_for_download(submitter).map do |attachment|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class SubmitFormController < ApplicationController
|
|||||||
submitter.save!
|
submitter.save!
|
||||||
|
|
||||||
if submitter.completed_at?
|
if submitter.completed_at?
|
||||||
|
GenerateSubmitterResultAttachmentsJob.perform_later(submitter)
|
||||||
|
|
||||||
submitter.submission.template.account.users.active.each do |user|
|
submitter.submission.template.account.users.active.each do |user|
|
||||||
SubmitterMailer.completed_email(submitter, user).deliver_later!
|
SubmitterMailer.completed_email(submitter, user).deliver_later!
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ApplicationJob < ActiveJob::Base
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GenerateSubmitterResultAttachmentsJob < ApplicationJob
|
||||||
|
def perform(submitter)
|
||||||
|
Submissions::EnsureResultGenerated.call(submitter)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -21,6 +21,9 @@ class SubmitterMailer < ApplicationMailer
|
|||||||
|
|
||||||
def documents_copy_email(submitter)
|
def documents_copy_email(submitter)
|
||||||
@submitter = submitter
|
@submitter = submitter
|
||||||
|
|
||||||
|
Submissions::EnsureResultGenerated.call(@submitter)
|
||||||
|
|
||||||
@documents = Submitters.select_attachments_for_download(submitter)
|
@documents = Submitters.select_attachments_for_download(submitter)
|
||||||
|
|
||||||
@documents.each do |attachment|
|
@documents.each do |attachment|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: document_generation_events
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# event_name :string not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# submitter_id :bigint not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_document_generation_events_on_submitter_id (submitter_id)
|
||||||
|
# index_document_generation_events_on_submitter_id_and_event_name (submitter_id,event_name) UNIQUE WHERE ((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[]))
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (submitter_id => submitters.id)
|
||||||
|
#
|
||||||
|
class DocumentGenerationEvent < ApplicationRecord
|
||||||
|
belongs_to :submitter
|
||||||
|
|
||||||
|
enum :event_name, {
|
||||||
|
complete: 'complete',
|
||||||
|
fail: 'fail',
|
||||||
|
start: 'start',
|
||||||
|
retry: 'retry'
|
||||||
|
}, scope: false
|
||||||
|
end
|
||||||
@@ -39,6 +39,8 @@ class Submitter < ApplicationRecord
|
|||||||
has_many_attached :documents
|
has_many_attached :documents
|
||||||
has_many_attached :attachments
|
has_many_attached :attachments
|
||||||
|
|
||||||
|
has_many :document_generation_events, dependent: :destroy
|
||||||
|
|
||||||
def status
|
def status
|
||||||
if completed_at?
|
if completed_at?
|
||||||
'completed'
|
'completed'
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
|
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<%= f.button button_title(title: 'Start'), class: 'base-button' %>
|
<%= f.button button_title(title: 'Start', disabled_with: 'Starting'), class: 'base-button' %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateDocumentGenerationEvents < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
create_table :document_generation_events do |t|
|
||||||
|
t.references :submitter, null: false, foreign_key: true, index: true
|
||||||
|
t.string :event_name, null: false
|
||||||
|
|
||||||
|
t.index %i[submitter_id event_name], unique: true, where: "event_name IN ('start', 'complete')"
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
+11
-1
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2023_06_12_182744) do
|
ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
@@ -52,6 +52,15 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_12_182744) do
|
|||||||
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "document_generation_events", force: :cascade do |t|
|
||||||
|
t.bigint "submitter_id", null: false
|
||||||
|
t.string "event_name", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["submitter_id", "event_name"], name: "index_document_generation_events_on_submitter_id_and_event_name", unique: true, where: "((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[]))"
|
||||||
|
t.index ["submitter_id"], name: "index_document_generation_events_on_submitter_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "encrypted_configs", force: :cascade do |t|
|
create_table "encrypted_configs", force: :cascade do |t|
|
||||||
t.bigint "account_id", null: false
|
t.bigint "account_id", null: false
|
||||||
t.string "key", null: false
|
t.string "key", null: false
|
||||||
@@ -135,6 +144,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_12_182744) do
|
|||||||
|
|
||||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "document_generation_events", "submitters"
|
||||||
add_foreign_key "encrypted_configs", "accounts"
|
add_foreign_key "encrypted_configs", "accounts"
|
||||||
add_foreign_key "submissions", "templates"
|
add_foreign_key "submissions", "templates"
|
||||||
add_foreign_key "submissions", "users", column: "created_by_user_id"
|
add_foreign_key "submissions", "users", column: "created_by_user_id"
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Submissions
|
||||||
|
module EnsureResultGenerated
|
||||||
|
WAIT_FOR_RETRY = 2.seconds
|
||||||
|
CHECK_EVENT_INTERVAL = 1.second
|
||||||
|
CHECK_COMPLETE_TIMEOUT = 20.seconds
|
||||||
|
|
||||||
|
WaitForCompleteTimeout = Class.new(StandardError)
|
||||||
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def call(submitter)
|
||||||
|
return submitter.documents if submitter.document_generation_events.complete.exists?
|
||||||
|
|
||||||
|
events =
|
||||||
|
DocumentGenerationEvent.uncached do
|
||||||
|
DocumentGenerationEvent.where(submitter:).order(:created_at).to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
if events.present? && events.last.event_name.in?(%w[start retry])
|
||||||
|
wait_for_complete_or_fail(submitter)
|
||||||
|
else
|
||||||
|
submitter.document_generation_events.create!(event_name: events.present? ? :retry : :start)
|
||||||
|
|
||||||
|
GenerateResultAttachments.call(submitter)
|
||||||
|
|
||||||
|
submitter.document_generation_events.create!(event_name: :complete)
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::RecordNotUnique
|
||||||
|
sleep WAIT_FOR_RETRY
|
||||||
|
|
||||||
|
retry
|
||||||
|
rescue StandardError
|
||||||
|
submitter.document_generation_events.create!(event_name: :fail)
|
||||||
|
|
||||||
|
raise
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_for_complete_or_fail(submitter)
|
||||||
|
total_wait_time = 0
|
||||||
|
|
||||||
|
loop do
|
||||||
|
sleep CHECK_EVENT_INTERVAL
|
||||||
|
total_wait_time += CHECK_EVENT_INTERVAL
|
||||||
|
|
||||||
|
last_event =
|
||||||
|
DocumentGenerationEvent.uncached do
|
||||||
|
DocumentGenerationEvent.where(submitter:).order(:created_at).last
|
||||||
|
end
|
||||||
|
|
||||||
|
break last_event if last_event.event_name.in?(%w[complete fail])
|
||||||
|
|
||||||
|
raise WaitForCompleteTimeout if total_wait_time > CHECK_COMPLETE_TIMEOUT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -209,9 +209,13 @@ module Submissions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build_pdfs_index(submitter)
|
def build_pdfs_index(submitter)
|
||||||
latest_submitter = submitter.submission.submitters
|
latest_submitter =
|
||||||
.select { |e| e.id != submitter.id && e.completed_at? }
|
submitter.submission.submitters
|
||||||
.max_by(&:completed_at)
|
.select(&:completed_at?)
|
||||||
|
.select { |e| e.id != submitter.id && e.completed_at <= submitter.completed_at }
|
||||||
|
.max_by(&:completed_at)
|
||||||
|
|
||||||
|
Submissions::EnsureResultGenerated.call(latest_submitter) if latest_submitter
|
||||||
|
|
||||||
documents = latest_submitter&.documents&.preload(:blob).to_a.presence
|
documents = latest_submitter&.documents&.preload(:blob).to_a.presence
|
||||||
documents ||= submitter.submission.template.documents.preload(:blob)
|
documents ||= submitter.submission.template.documents.preload(:blob)
|
||||||
|
|||||||
Reference in New Issue
Block a user