Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions app/controllers/inspections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ def filtered_inspections_query_without_order = current_user.inspections
def no_index = response.set_header("X-Robots-Tag", "noindex,nofollow")

def set_inspection
@inspection = Inspection
inspection_query = Inspection
.includes(
:user, :inspector_company,
*Inspection::ALL_ASSESSMENT_TYPES.keys,
Expand All @@ -363,7 +363,19 @@ def set_inspection
photo_2_attachment: :blob,
photo_3_attachment: :blob
)
.find_by(id: params[:id]&.upcase)
inspection_id = params[:id]&.upcase

@inspection = if request.format.pdf?
PdfPerformance.measure(
:record_load,
pdf_type: :inspection,
record_id: inspection_id
) do
inspection_query.find_by(id: inspection_id)
end
else
inspection_query.find_by(id: inspection_id)
end

head :not_found unless @inspection
end
Expand Down Expand Up @@ -432,14 +444,26 @@ def handle_failed_update
end

def send_inspection_pdf
result = PdfCacheService.fetch_or_generate_inspection_pdf(
@inspection,
debug_enabled: admin_debug_enabled?,
debug_queries: debug_sql_queries
)
@inspection.update(pdf_last_accessed_at: Time.current)
PdfPerformance.measure(
:total,
pdf_type: :inspection,
record_id: @inspection.id
) do
result = PdfCacheService.fetch_or_generate_inspection_pdf(
@inspection,
debug_enabled: admin_debug_enabled?,
debug_queries: debug_sql_queries
)
PdfPerformance.measure(
:access_tracking,
pdf_type: :inspection,
record_id: @inspection.id
) do
@inspection.update(pdf_last_accessed_at: Time.current)
end

handle_pdf_response(result, pdf_filename)
handle_pdf_response(result, pdf_filename)
end
end

def send_inspection_qr_code
Expand Down
38 changes: 28 additions & 10 deletions app/controllers/units_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,22 @@ def normalize_unit_id(raw_id)

def no_index = response.set_header("X-Robots-Tag", "noindex,nofollow")

sig { void }
def set_unit
@unit = Unit.includes(photo_attachment: :blob)
.find_by(id: params[:id].upcase)
unit_id = params[:id].upcase
unit_query = Unit.includes(photo_attachment: :blob)

@unit = if request.format.pdf?
PdfPerformance.measure(
:record_load,
pdf_type: :unit,
record_id: unit_id
) do
unit_query.find_by(id: unit_id)
end
else
unit_query.find_by(id: unit_id)
end

unless @unit
# Always return 404 for non-existent resources regardless of login status
Expand All @@ -220,14 +233,19 @@ def check_assessments_enabled
end

def send_unit_pdf
# Unit already has photo loaded from set_unit
result = PdfCacheService.fetch_or_generate_unit_pdf(
@unit,
debug_enabled: admin_debug_enabled?,
debug_queries: debug_sql_queries
)

handle_pdf_response(result, pdf_filename)
PdfPerformance.measure(
:total,
pdf_type: :unit,
record_id: @unit.id
) do
result = PdfCacheService.fetch_or_generate_unit_pdf(
@unit,
debug_enabled: admin_debug_enabled?,
debug_queries: debug_sql_queries
)

handle_pdf_response(result, pdf_filename)
end
end

def send_unit_qr_code
Expand Down
11 changes: 11 additions & 0 deletions app/models/assessment_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ def initialize(raw)
sig { returns(T::Boolean) }
def required? = !!attributes[:required]

sig { returns(T::Boolean) }
def optional_for_completion? = attributes[:required] == false

sig { returns(T::Boolean) }
def numeric? = NUMERIC_PARTIALS.include?(partial)

Expand Down Expand Up @@ -163,6 +166,14 @@ def add_not_applicable_fields
fields.select(&:add_not_applicable?).map(&:name)
end

sig { returns(T::Array[Symbol]) }
def completion_optional_fields
@completion_optional_fields ||= fields
.select(&:optional_for_completion?)
.flat_map { [it.name, *it.composite_fields] }
.freeze
end

# Returns a new schema with the named fields removed from each fieldset.
# Used for permission-driven filtering (e.g. hiding admin-only fields).
sig { params(field_names: Symbol).returns(AssessmentSchema) }
Expand Down
5 changes: 4 additions & 1 deletion app/models/concerns/assessment_completion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ def complete?

sig { returns(T::Array[Symbol]) }
def incomplete_fields
(self.class.column_name_syms - SYSTEM_FIELDS)
fields = self.class.column_name_syms - SYSTEM_FIELDS
fields -= self.class.assessment_schema.completion_optional_fields

fields
.reject { |f| f.end_with?("_comment") }
.select { |f| field_is_incomplete?(f) }
.reject { |f| field_allows_nil_when_na?(f) }
Expand Down
5 changes: 5 additions & 0 deletions app/models/inspection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,11 @@ def invalidate_pdf_cache

sig { void }
def invalidate_unit_pdf_cache
changed_attrs = saved_changes.keys
ignorable_attrs = ["pdf_last_accessed_at", "updated_at"]

return if (changed_attrs - ignorable_attrs).empty?

PdfCacheService.invalidate_unit_cache(unit) if unit
end
end
3 changes: 1 addition & 2 deletions app/models/unit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ class Unit < ApplicationRecord
before_destroy :check_complete_inspections
before_destroy :destroy_draft_inspections

# All fields are required for Units
validates :name, :serial, :description, :manufacturer, presence: true
validates :description, :name, :serial, presence: true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
validates :serial, uniqueness: {scope: [:user_id]}
validate :badge_id_valid, on: :create, if: -> { unit_badges_enabled? }

Expand Down
53 changes: 39 additions & 14 deletions app/services/pdf_cache_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ class << self
.returns(CacheResult)
end
def fetch_or_generate_inspection_pdf(inspection, **options)
# Never cache incomplete inspections
unless caching_enabled? && inspection.complete?
return generate_pdf_result(inspection, :inspection, **options)
if caching_enabled? && inspection.complete?
fetch_or_generate(inspection, :inspection, **options)
else
generate_pdf_result(inspection, :inspection, **options)
end

fetch_or_generate(inspection, :inspection, **options)
end

sig { params(unit: Unit, options: T.untyped).returns(CacheResult) }
Expand Down Expand Up @@ -52,8 +51,14 @@ def invalidate_unit_cache(unit)
).returns(CacheResult)
end
def fetch_or_generate(record, type, **options)
valid_cache = record.cached_pdf.attached? &&
cached_pdf_valid?(record.cached_pdf, record)
valid_cache = PdfPerformance.measure(
:cache_lookup,
pdf_type: type,
record_id: record.id
) do
record.cached_pdf.attached? &&
cached_pdf_valid?(record.cached_pdf, record)
end

if valid_cache
Rails.logger.info "PDF cache hit for #{type} #{record.id}"
Expand All @@ -79,7 +84,13 @@ def fetch_or_generate(record, type, **options)
end
def generate_and_cache(record, type, **options)
result = generate_pdf_result(record, type, **options)
store_cached_pdf(record, result.data)
PdfPerformance.measure(
:cache_store,
pdf_type: type,
record_id: record.id
) do
store_cached_pdf(record, result.data)
end
result
end

Expand All @@ -91,14 +102,28 @@ def generate_and_cache(record, type, **options)
).returns(CacheResult)
end
def generate_pdf_result(record, type, **options)
pdf_document = case type
when :inspection
PdfGeneratorService.generate_inspection_report(record, **options)
when :unit
PdfGeneratorService.generate_unit_report(record, **options)
pdf_document = PdfPerformance.measure(
:document_build,
pdf_type: type,
record_id: record.id
) do
case type
when :inspection
PdfGeneratorService.generate_inspection_report(record, **options)
when :unit
PdfGeneratorService.generate_unit_report(record, **options)
end
end

pdf_data = PdfPerformance.measure(
:document_render,
pdf_type: type,
record_id: record.id
) do
pdf_document.render
end

CacheResult.new(type: :pdf_data, data: pdf_document.render)
CacheResult.new(type: :pdf_data, data: pdf_data)
end

sig { params(record: T.any(Inspection, Unit)).void }
Expand Down
Loading
Loading