-
Notifications
You must be signed in to change notification settings - Fork 19
/
document.rb
272 lines (230 loc) · 7.33 KB
/
document.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# frozen_string_literal: true
class Document < CaseflowRecord
has_many :annotations
has_many :document_views
has_many :documents_tags
has_many :tags, through: :documents_tags
has_paper_trail only: [:description,
:category_case_summary,
:category_medical,
:category_other,
:category_procedural],
on: [:update, :destroy], save_changes: false
self.inheritance_column = nil
S3_BUCKET_NAME = "documents"
# Document types are defined in the following file in
# caseflow commons: /app/models/caseflow/document_types.rb
# some of these names are confusing and are overriden
# in the following table.
TYPES_OVERRIDE = {
"73" => "NOD",
"95" => "SOC",
"97" => "SSOC",
"178" => "Form 8",
"179" => "Form 9",
"713" => "NOD",
"856" => "NOD",
"857" => "Form 9"
}.freeze
ALT_TYPES = {
"Appeals - Notice of Disagreement (NOD)" => "NOD",
"Appeals - Statement of the Case (SOC)" => "SOC",
"Appeals - Substantive Appeal to Board of Veterans' Appeals" => "Form 9",
"Appeals - Supplemental Statement of the Case (SSOC)" => "SSOC"
}.freeze
CASE_SUMMARY_TYPES = [
"NOD",
"Notice of Disagreement",
"SOC",
"Statement of Case (SOC)",
"Form 9",
"VA 9 Appeal to Board of Appeals",
"Form 8",
"VA 8 Certification of Appeal",
"BVA Decision",
"SSOC",
"Supplemental Statement of Case (SSOC)",
"DD 214 Certified Original - Certificate of Release or Discharge From Active Duty",
"Rating Decision - Codesheet",
"Rating Decision - Narrative",
"VA 21-526EZ, Fully Developed Claim (Compensation)",
"VA 21-527EZ, Fully Developed Claim (Pension)",
"VA 21-22 Appointment of Veterans Serv. Org. as Claimant Rep",
"Power of Attorney (incl. VA 21-22, VA 22a)",
"Hearing Transcript",
"RAMP Opt-in Election"
].freeze
DECISION_TYPES = ["BVA Decision", "Remand BVA or CAVC"].freeze
FUZZY_MATCH_DAYS = 4.days.freeze
attr_accessor :efolder_id, :alt_types, :filename, :vacols_date
def type?(type)
(self.type == type) || (alt_types || []).include?(type)
end
def receipt_date
received_at&.to_date
end
def match_vbms_document_from(vbms_documents)
match_vbms_document_using(vbms_documents) { |doc| doc.receipt_date == vacols_date }
end
def fuzzy_match_vbms_document_from(vbms_documents)
match_vbms_document_using(vbms_documents) { |doc| fuzzy_date_match?(doc) }
end
# If a document was created with a vacols_date and merged with a matching vbms
# document with a receipt_date, then the document is considered to be "matching"
def matching?
!!(received_at && vacols_date)
end
def self.type_from_vbms_type(vbms_type)
TYPES_OVERRIDE[vbms_type] ||
Caseflow::DocumentTypes::TYPES[vbms_type.to_i] ||
:other
end
def self.from_efolder(hash, file_number)
new(efolder_id: hash["id"],
type: type_from_vbms_type(hash["type_id"]),
received_at: hash["received_at"],
vbms_document_id: hash["external_document_id"] || hash["version_id"],
series_id: hash["series_id"],
upload_date: hash["upload_date"],
file_number: file_number)
end
def self.from_vbms_document(vbms_document, file_number)
new(type: type_from_vbms_type(vbms_document.doc_type),
alt_types: (vbms_document.alt_doc_types || []).map { |type| ALT_TYPES[type] },
received_at: vbms_document.received_at,
upload_date: vbms_document.upload_date,
vbms_document_id: vbms_document.document_id,
filename: vbms_document.filename,
file_number: file_number)
end
def self.type_id(type)
TYPES_OVERRIDE.key(type) ||
Caseflow::DocumentTypes::TYPES.key(type)
end
# Currently three levels of caching. Try to serve content
# from memory, then look to S3 if it's not in memory, and
# if it's not in S3 grab it from VBMS
# Log where we get the file from for now for easy verification
# of S3 integration.
def fetch_and_cache_document_from_vbms
@content = vbms.fetch_document_file(self)
S3Service.store_file(S3_BUCKET_NAME + "/" + file_name, @content)
Rails.logger.info("File #{vbms_document_id} fetched from VBMS")
@content
end
def fetch_content
content = S3Service.fetch_content(S3_BUCKET_NAME + "/" + file_name)
content && Rails.logger.info("File #{vbms_document_id} fetched from S3")
content || fetch_and_cache_document_from_vbms
end
def content
@content ||= fetch_content
end
def serve
File.binwrite(default_path, content)
default_path
end
def file_name
vbms_document_id.to_s
end
def default_path
File.join(Rails.root, "tmp", "pdfs", file_name)
end
def serializable_hash(options = {})
super({
methods: [
:vbms_document_id,
:content_url,
:type,
:received_at,
:upload_date,
:filename,
:category_procedural,
:category_medical,
:category_other,
:category_case_summary,
:serialized_vacols_date,
:serialized_receipt_date,
:matching?
]
}.update(options))
end
def to_hash
serializable_hash
end
def merge_into(document)
document.assign_attributes(
efolder_id: efolder_id,
type: type,
alt_types: alt_types,
received_at: received_at,
upload_date: upload_date,
filename: filename,
vbms_document_id: vbms_document_id,
series_id: series_id
)
document
end
def category_case_summary
CASE_SUMMARY_TYPES.include?(type) || (received_at && received_at >= 30.days.ago)
end
def serialized_vacols_date
serialize_date(vacols_date)
end
def serialized_receipt_date
serialize_date(receipt_date)
end
def content_url
if reader_with_efolder_api?
ExternalApi::EfolderService.efolder_content_url(vbms_document_id.tr("{}", ""))
else
"/document/#{id}/pdf"
end
end
def copy_metadata_from_document(source_document)
source_document.annotations.map do |annotation|
annotation.dup.tap do |a|
a.document_id = id
a.save!
end
end
source_document.documents_tags.map do |tag|
tag.dup.tap do |t|
t.document_id = id
t.save!
end
end
update(
category_procedural: source_document.category_procedural,
category_medical: source_document.category_medical,
category_other: source_document.category_other,
previous_document_version_id: source_document.id
)
end
private
def reader_with_efolder_api?
EFolderService == ExternalApi::EfolderService &&
RequestStore.store[:application] == "reader"
end
def match_vbms_document_using(vbms_documents)
match = vbms_documents.detect do |doc|
yield(doc) && doc.type?(type)
end
match ? merge_with(match) : self
end
# Because VBMS does not allow the receipt date to be set after the upload date,
# we allow it to be up to 4 days before the VACOLS date in some scenarios. In
# these scenarios we "fuzzy match" the VBMS and VACOLS dates.
def fuzzy_date_match?(vbms_document)
((vacols_date - FUZZY_MATCH_DAYS)..vacols_date).cover?(vbms_document.receipt_date)
end
def serialize_date(date)
date ? date.to_formatted_s(:short_date) : ""
end
def merge_with(document)
document.merge_into(self)
end
def vbms
VBMSService
end
end