Class: EVSSClaimDocument

Inherits:
Common::Base show all
Includes:
ActiveModel::Validations, ActiveModel::Validations::Callbacks, SentryLogging
Defined in:
app/models/evss_claim_document.rb

Constant Summary collapse

DOCUMENT_TYPES =

rubocop:disable Layout/LineLength

{
  '1489' => 'Hypertension Rapid Ready For Decision Summary PDF',
  'L014' => 'Birth Certificate',
  'L015' => 'Buddy/Lay Statement',
  'L018' => 'Civilian Police Reports',
  'L023' => 'Other Correspondence',
  'L029' => 'Copy of a DD214',
  'L033' => 'Death Certificate',
  'L034' => 'Military Personnel Record',
  'L037' => 'Divorce Decree',
  'L048' => 'Medical Treatment Record - Government Facility',
  'L049' => 'Medical Treatment Record - Non-Government Facility',
  'L051' => 'Marriage Certificate',
  'L070' => 'Photographs',
  'L102' => 'VA Form 21-2680 - Examination for Housebound Status or Permanent Need for Regular Aid & Attendance',
  'L107' => 'VA Form 21-4142 - Authorization To Disclose Information',
  'L115' => 'VA Form 21-4192 - Request for Employment Information in Connection with Claim for Disability',
  'L117' => 'VA Form 21-4502 - Application for Automobile or Other Conveyance and Adaptive Equipment Under 38 U.S.C. 3901-3904',
  'L133' => 'VA Form 21-674 - Request for Approval of School Attendance',
  'L139' => 'VA Form 21-686c - Declaration of Status of Dependents',
  'L149' => 'VA Form 21-8940 - Veterans Application for Increased Compensation Based on Un-employability',
  'L159' => 'VA Form 26-4555 - Application in Acquiring Specially Adapted Housing or Special Home Adaptation Grant',
  'L222' => 'VA Form 21-0779 - Request for Nursing Home Information in Connection with Claim for Aid & Attendance',
  'L228' => 'VA Form 21-0781 - Statement in Support of Claim for PTSD',
  'L229' => 'VA Form 21-0781a - Statement in Support of Claim for PTSD Secondary to Personal Assault',
  'L418' => 'Court papers / documents',
  'L450' => 'STR - Dental - Photocopy',
  'L451' => 'STR - Medical - Photocopy',
  'L478' => 'Medical Treatment Records - Furnished by SSA',
  'L702' => 'Disability Benefits Questionnaire (DBQ)',
  'L703' => 'Goldmann Perimetry Chart/Field Of Vision Chart',
  'L827' => 'VA Form 21-4142a - General Release for Medical Provider Information',
  'L1489' => 'Automated Review Summary Document'
}.freeze
EVSS_TEXT_ENCODING =

rubocop:enable Layout/LineLength

'ascii'
MINIMUM_ENCODING_CONFIDENCE =

EVSS only accepts text files written in ASCII

0.5

Instance Attribute Summary

Attributes inherited from Common::Base

#errors_hash, #metadata

Instance Method Summary collapse

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Methods inherited from Common::Base

#changed, #changed?, #changes, default_sort, filterable_attributes, #initialize, max_per_page, per_page, sortable_attributes

Constructor Details

This class inherits a constructor from Common::Base

Instance Method Details

#==(other) ⇒ Object



73
74
75
# File 'app/models/evss_claim_document.rb', line 73

def ==(other)
  attributes == other.attributes
end

#content_type_matches_extension?Boolean (private)

Returns:

  • (Boolean)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/models/evss_claim_document.rb', line 90

def content_type_matches_extension?
  return unless file_obj

  true_mime_type = MimeMagic.by_magic(File.open(file_obj.tempfile.path)).to_s

  # MimeMagic cannot always determine the mime_type and will sometimes
  # return ''. In those cases it makes sense to fall back to the content_type
  # as passed in when the request is made
  true_mime_type = file_obj.content_type if true_mime_type.empty?

  assumed_mime_type = MimeMagic.by_extension(extension).to_s

  errors.add(:base, I18n.t('errors.messages.uploads.content_type_mismatch')) if true_mime_type != assumed_mime_type
end

#convert_to_unlocked_pdfObject (private)



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'app/models/evss_claim_document.rb', line 115

def convert_to_unlocked_pdf
  return unless file_name.match?(/\.pdf$/i) && password.present?

  pdftk = PdfForms.new(Settings.binaries.pdftk)
  tempfile_without_pass = Tempfile.new(['decrypted_evss_claim_document', '.pdf'])

  begin
    pdftk.call_pdftk(file_obj.tempfile.path,
                     'input_pw', password,
                     'output', tempfile_without_pass.path)
  rescue PdfForms::PdftkError => e
    file_regex = %r{/(?:\w+/)*[\w-]+\.pdf\b}
    password_regex = /(input_pw).*?(output)/
    sanitized_message = e.message.gsub(file_regex, '[FILTERED FILENAME]').gsub(password_regex, '\1 [FILTERED] \2')
    log_message_to_sentry(sanitized_message, 'warn')
    errors.add(:base, I18n.t('errors.messages.uploads.pdf.incorrect_password'))
  end

  @password = nil

  file_obj.tempfile.unlink
  file_obj.tempfile = tempfile_without_pass
end

#descriptionObject



65
66
67
# File 'app/models/evss_claim_document.rb', line 65

def description
  DOCUMENT_TYPES[document_type]
end

#extensionObject (private)



105
106
107
108
109
# File 'app/models/evss_claim_document.rb', line 105

def extension
  # Using file_name instead of file_path because the temp path doesn't include
  # an extension
  File.extname(file_name).downcase[1..] # Remove the leading dot
end

#known_document_type?Boolean (private)

Returns:

  • (Boolean)


111
112
113
# File 'app/models/evss_claim_document.rb', line 111

def known_document_type?
  errors.add(:base, I18n.t('errors.messages.uploads.document_type_unknown')) unless description
end

#normalize_file_nameObject (private)



166
167
168
169
170
171
# File 'app/models/evss_claim_document.rb', line 166

def normalize_file_name
  return if !file_name || file_name.frozen?

  # remove all but the last "."  in the file name
  file_name.gsub!(/[.](?=.*[.])/, '')
end

#normalize_textObject (private)



154
155
156
157
158
159
160
161
162
163
164
# File 'app/models/evss_claim_document.rb', line 154

def normalize_text
  return unless file_name.match?(/\.txt$/i) && file_obj

  text = file_obj.read
  text = text.encode(EVSS_TEXT_ENCODING)
  file_obj.tempfile = Tempfile.new(encoding: EVSS_TEXT_ENCODING)
  file_obj.tempfile.write text
  file_obj.tempfile.rewind
rescue Encoding::UndefinedConversionError
  errors.add(:base, I18n.t('errors.messages.uploads.ascii_encoded'))
end

#to_serializable_hashObject



77
78
79
80
# File 'app/models/evss_claim_document.rb', line 77

def to_serializable_hash
  # file_obj is not suitable for serialization
  to_hash.tap { |h| h.delete :file_obj }
end

#tracked_item_id=(num) ⇒ Object

The front-end URLencodes a nil tracked_item_id as the string ‘null’



83
84
85
86
# File 'app/models/evss_claim_document.rb', line 83

def tracked_item_id=(num)
  num = nil if num == 'null'
  super num
end

#unencrypted_pdf?Boolean (private)

Returns:

  • (Boolean)


139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'app/models/evss_claim_document.rb', line 139

def unencrypted_pdf?
  return unless file_name.match?(/\.pdf$/i) && file_obj

   = PdfInfo::Metadata.read(file_obj.tempfile)
  errors.add(:base, I18n.t('errors.messages.uploads.encrypted')) if .encrypted?
  file_obj.tempfile.rewind
rescue PdfInfo::MetadataReadError => e
  log_exception_to_sentry(e, nil, nil, 'warn')
  if e.message.include?('Incorrect password')
    errors.add(:base, I18n.t('errors.messages.uploads.pdf.locked'))
  else
    errors.add(:base, I18n.t('errors.messages.uploads.malformed_pdf'))
  end
end

#uploader_idsObject



69
70
71
# File 'app/models/evss_claim_document.rb', line 69

def uploader_ids
  [tracked_item_id, uuid]
end