Skip to content

Commit 6832466

Browse files
authored
Instrument PDF generation and make fields optional (#513)
* Instrument PDFs and relax optional fields * Fix full-suite order dependencies * Address PDF and unit review feedback * Align unit PDF inspection ordering * Verify unit PDF inspection reuse
1 parent 3f86352 commit 6832466

43 files changed

Lines changed: 722 additions & 180 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/controllers/inspections_controller.rb

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ def filtered_inspections_query_without_order = current_user.inspections
354354
def no_index = response.set_header("X-Robots-Tag", "noindex,nofollow")
355355

356356
def set_inspection
357-
@inspection = Inspection
357+
inspection_query = Inspection
358358
.includes(
359359
:user, :inspector_company,
360360
*Inspection::ALL_ASSESSMENT_TYPES.keys,
@@ -363,7 +363,19 @@ def set_inspection
363363
photo_2_attachment: :blob,
364364
photo_3_attachment: :blob
365365
)
366-
.find_by(id: params[:id]&.upcase)
366+
inspection_id = params[:id]&.upcase
367+
368+
@inspection = if request.format.pdf?
369+
PdfPerformance.measure(
370+
:record_load,
371+
pdf_type: :inspection,
372+
record_id: inspection_id
373+
) do
374+
inspection_query.find_by(id: inspection_id)
375+
end
376+
else
377+
inspection_query.find_by(id: inspection_id)
378+
end
367379

368380
head :not_found unless @inspection
369381
end
@@ -432,14 +444,26 @@ def handle_failed_update
432444
end
433445

434446
def send_inspection_pdf
435-
result = PdfCacheService.fetch_or_generate_inspection_pdf(
436-
@inspection,
437-
debug_enabled: admin_debug_enabled?,
438-
debug_queries: debug_sql_queries
439-
)
440-
@inspection.update(pdf_last_accessed_at: Time.current)
447+
PdfPerformance.measure(
448+
:total,
449+
pdf_type: :inspection,
450+
record_id: @inspection.id
451+
) do
452+
result = PdfCacheService.fetch_or_generate_inspection_pdf(
453+
@inspection,
454+
debug_enabled: admin_debug_enabled?,
455+
debug_queries: debug_sql_queries
456+
)
457+
PdfPerformance.measure(
458+
:access_tracking,
459+
pdf_type: :inspection,
460+
record_id: @inspection.id
461+
) do
462+
@inspection.update(pdf_last_accessed_at: Time.current)
463+
end
441464

442-
handle_pdf_response(result, pdf_filename)
465+
handle_pdf_response(result, pdf_filename)
466+
end
443467
end
444468

445469
def send_inspection_qr_code

app/controllers/units_controller.rb

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,22 @@ def normalize_unit_id(raw_id)
196196

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

199+
sig { void }
199200
def set_unit
200-
@unit = Unit.includes(photo_attachment: :blob)
201-
.find_by(id: params[:id].upcase)
201+
unit_id = params[:id].upcase
202+
unit_query = Unit.includes(photo_attachment: :blob)
203+
204+
@unit = if request.format.pdf?
205+
PdfPerformance.measure(
206+
:record_load,
207+
pdf_type: :unit,
208+
record_id: unit_id
209+
) do
210+
unit_query.find_by(id: unit_id)
211+
end
212+
else
213+
unit_query.find_by(id: unit_id)
214+
end
202215

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

222235
def send_unit_pdf
223-
# Unit already has photo loaded from set_unit
224-
result = PdfCacheService.fetch_or_generate_unit_pdf(
225-
@unit,
226-
debug_enabled: admin_debug_enabled?,
227-
debug_queries: debug_sql_queries
228-
)
229-
230-
handle_pdf_response(result, pdf_filename)
236+
PdfPerformance.measure(
237+
:total,
238+
pdf_type: :unit,
239+
record_id: @unit.id
240+
) do
241+
result = PdfCacheService.fetch_or_generate_unit_pdf(
242+
@unit,
243+
debug_enabled: admin_debug_enabled?,
244+
debug_queries: debug_sql_queries
245+
)
246+
247+
handle_pdf_response(result, pdf_filename)
248+
end
231249
end
232250

233251
def send_unit_qr_code

app/models/assessment_schema.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def initialize(raw)
6161
sig { returns(T::Boolean) }
6262
def required? = !!attributes[:required]
6363

64+
sig { returns(T::Boolean) }
65+
def optional_for_completion? = attributes[:required] == false
66+
6467
sig { returns(T::Boolean) }
6568
def numeric? = NUMERIC_PARTIALS.include?(partial)
6669

@@ -163,6 +166,14 @@ def add_not_applicable_fields
163166
fields.select(&:add_not_applicable?).map(&:name)
164167
end
165168

169+
sig { returns(T::Array[Symbol]) }
170+
def completion_optional_fields
171+
@completion_optional_fields ||= fields
172+
.select(&:optional_for_completion?)
173+
.flat_map { [it.name, *it.composite_fields] }
174+
.freeze
175+
end
176+
166177
# Returns a new schema with the named fields removed from each fieldset.
167178
# Used for permission-driven filtering (e.g. hiding admin-only fields).
168179
sig { params(field_names: Symbol).returns(AssessmentSchema) }

app/models/concerns/assessment_completion.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ def complete?
1919

2020
sig { returns(T::Array[Symbol]) }
2121
def incomplete_fields
22-
(self.class.column_name_syms - SYSTEM_FIELDS)
22+
fields = self.class.column_name_syms - SYSTEM_FIELDS
23+
fields -= self.class.assessment_schema.completion_optional_fields
24+
25+
fields
2326
.reject { |f| f.end_with?("_comment") }
2427
.select { |f| field_is_incomplete?(f) }
2528
.reject { |f| field_allows_nil_when_na?(f) }

app/models/inspection.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,11 @@ def invalidate_pdf_cache
474474

475475
sig { void }
476476
def invalidate_unit_pdf_cache
477+
changed_attrs = saved_changes.keys
478+
ignorable_attrs = ["pdf_last_accessed_at", "updated_at"]
479+
480+
return if (changed_attrs - ignorable_attrs).empty?
481+
477482
PdfCacheService.invalidate_unit_cache(unit) if unit
478483
end
479484
end

app/models/unit.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
# Indexes
2222
#
2323
# index_units_on_is_seed (is_seed)
24-
# index_units_on_manufacturer_and_serial (manufacturer,serial) UNIQUE
2524
# index_units_on_serial_and_user_id (serial,user_id) UNIQUE
2625
# index_units_on_unit_type (unit_type)
2726
# index_units_on_user_id (user_id)
@@ -67,8 +66,7 @@ class Unit < ApplicationRecord
6766
before_destroy :check_complete_inspections
6867
before_destroy :destroy_draft_inspections
6968

70-
# All fields are required for Units
71-
validates :name, :serial, :description, :manufacturer, presence: true
69+
validates :description, :name, :serial, presence: true
7270
validates :serial, uniqueness: {scope: [:user_id]}
7371
validate :badge_id_valid, on: :create, if: -> { unit_badges_enabled? }
7472

app/services/pdf_cache_service.rb

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ class << self
1717
.returns(CacheResult)
1818
end
1919
def fetch_or_generate_inspection_pdf(inspection, **options)
20-
# Never cache incomplete inspections
21-
unless caching_enabled? && inspection.complete?
22-
return generate_pdf_result(inspection, :inspection, **options)
20+
if caching_enabled? && inspection.complete?
21+
fetch_or_generate(inspection, :inspection, **options)
22+
else
23+
generate_pdf_result(inspection, :inspection, **options)
2324
end
24-
25-
fetch_or_generate(inspection, :inspection, **options)
2625
end
2726

2827
sig { params(unit: Unit, options: T.untyped).returns(CacheResult) }
@@ -52,8 +51,14 @@ def invalidate_unit_cache(unit)
5251
).returns(CacheResult)
5352
end
5453
def fetch_or_generate(record, type, **options)
55-
valid_cache = record.cached_pdf.attached? &&
56-
cached_pdf_valid?(record.cached_pdf, record)
54+
valid_cache = PdfPerformance.measure(
55+
:cache_lookup,
56+
pdf_type: type,
57+
record_id: record.id
58+
) do
59+
record.cached_pdf.attached? &&
60+
cached_pdf_valid?(record.cached_pdf, record)
61+
end
5762

5863
if valid_cache
5964
Rails.logger.info "PDF cache hit for #{type} #{record.id}"
@@ -79,7 +84,13 @@ def fetch_or_generate(record, type, **options)
7984
end
8085
def generate_and_cache(record, type, **options)
8186
result = generate_pdf_result(record, type, **options)
82-
store_cached_pdf(record, result.data)
87+
PdfPerformance.measure(
88+
:cache_store,
89+
pdf_type: type,
90+
record_id: record.id
91+
) do
92+
store_cached_pdf(record, result.data)
93+
end
8394
result
8495
end
8596

@@ -91,14 +102,28 @@ def generate_and_cache(record, type, **options)
91102
).returns(CacheResult)
92103
end
93104
def generate_pdf_result(record, type, **options)
94-
pdf_document = case type
95-
when :inspection
96-
PdfGeneratorService.generate_inspection_report(record, **options)
97-
when :unit
98-
PdfGeneratorService.generate_unit_report(record, **options)
105+
pdf_document = PdfPerformance.measure(
106+
:document_build,
107+
pdf_type: type,
108+
record_id: record.id
109+
) do
110+
case type
111+
when :inspection
112+
PdfGeneratorService.generate_inspection_report(record, **options)
113+
when :unit
114+
PdfGeneratorService.generate_unit_report(record, **options)
115+
end
116+
end
117+
118+
pdf_data = PdfPerformance.measure(
119+
:document_render,
120+
pdf_type: type,
121+
record_id: record.id
122+
) do
123+
pdf_document.render
99124
end
100125

101-
CacheResult.new(type: :pdf_data, data: pdf_document.render)
126+
CacheResult.new(type: :pdf_data, data: pdf_data)
102127
end
103128

104129
sig { params(record: T.any(Inspection, Unit)).void }

0 commit comments

Comments
 (0)