in source/app/pages/documents/view.js [95:467]
function Document({ currentPageNumber, dispatch, id, document, pageTitle, searchQuery, track }) {
// TODO: Ensure id corresponds to a valid resource, otherwise 404
// e.g. /documents/export and /documents/view should fail
const isDocumentFetched = !!document.textractResponse && !!document.comprehendMedicalResponse && !!document.comprehendResponse
const { status } = useFetchDocument(dispatch, id, isDocumentFetched)
const pageCount = getDocumentPageCount(document)
const { documentName, documentURL, searchablePdfURL } = document
// Reset currentPageNumber on mount
useEffect(() => {
dispatch(setCurrentPageNumber(1))
}, [dispatch])
useEffect(() => {
return () => {
dispatch(clearRedactions(id))
}
}, [dispatch, id])
// Set search results data
const wordsMatchingSearch = useMemo(() => {
return getPageWordsBySearch(document, currentPageNumber, searchQuery)
}, [document, currentPageNumber, searchQuery])
const docData = useMemo(() => {
const pairs = getDocumentKeyValuePairs(document)
const tables = getDocumentTables(document)
const lines = getDocumentLines(document)
const entities = getDocumentEntityPairs(document, COMPREHEND_SERVICE)
const medicalEntities = getDocumentEntityPairs(document, COMPREHEND_MEDICAL_SERVICE)
return { pairs, tables, lines, entities , medicalEntities }
// eslint-disable-next-line
}, [document, document.textractResponse ,document.medicalComprehendResponse, document.comprehendResponse])
// Set the paged content for each tab
const pageData = useMemo(() => {
const lines = getPageLines(document, currentPageNumber)
const pairs = docData.pairs.filter(d => d.pageNumber === currentPageNumber)
const tables = docData.tables.filter(d => d.pageNumber === currentPageNumber)
const entities = docData.entities.filter(d => d.pageNumber === currentPageNumber)
const medicalEntities = docData.medicalEntities.filter(d => d.pageNumber === currentPageNumber)
return { lines, pairs, tables , entities , medicalEntities }
// eslint-disable-next-line
}, [document, document.textractResponse,document.comprehendMedicalResponse, currentPageNumber, docData.pairs, docData.entities , docData.medicalEntities , docData.tables])
const [tab, selectTab] = useState('search')
const [trackTab, selectTrack] = useState('search')
const downloadKV = useCallback(async () => {
const { resultDirectory } = document
const url = await Storage.get(`${resultDirectory}/textract/page-${currentPageNumber}-forms.csv`, {
expires: 300,
})
window.open(url)
}, [currentPageNumber, document])
const downloadEntities = useCallback(async () => {
const { resultDirectory } = document
const url = await Storage.get(`${resultDirectory}/comprehend/comprehendEntities.json`, {
expires: 300,
})
window.open(url)
}, [ document])
const downloadMedicalEntities = useCallback(async () => {
const { resultDirectory } = document
const url = await Storage.get(`${resultDirectory}/comprehend/comprehendMedicalEntities.json`, {
expires: 300,
})
window.open(url)
}, [ document])
const downloadMedicalICD10Ontologies = useCallback(async () => {
const { resultDirectory } = document
const url = await Storage.get(`${resultDirectory}/comprehend/comprehendMedicalICD10.json`, {
expires: 300,
})
window.open(url)
}, [ document])
const redactMatches = useCallback(async () => {
dispatch(addRedactions(id, currentPageNumber, wordsMatchingSearch))
dispatch(setDocumentSearchQuery(''))
}, [currentPageNumber, dispatch, id, wordsMatchingSearch])
const redact = useCallback(
async (bbox, pageNumber = currentPageNumber) => {
dispatch(addRedactions(id, pageNumber, [bbox]))
},
[currentPageNumber, dispatch, id]
)
const highlightEntities = useCallback(
async (bbox, pageNumber = currentPageNumber) => {
dispatch(addHighlights(id, pageNumber, bbox))
},
[currentPageNumber, dispatch, id]
)
const clearReds = useCallback(() => {
dispatch(clearRedactions(id))
}, [dispatch, id])
const redactAllValues = useCallback(
async (bbox, pageNumber = currentPageNumber) => {
dispatch(addRedactions(id, currentPageNumber, pageData.pairs.map(p => p.valueBoundingBox)))
}, [currentPageNumber, dispatch, id, pageData.pairs])
const redactEntityMatches = useCallback(async (pageNumber ,bboxlist) => {
dispatch(addRedactions(id, pageNumber,bboxlist.map(p => p)))
}, [currentPageNumber, dispatch, id])
const contentRef = useRef()
const downloadRedacted = useCallback(async () => {
const theThing = contentRef.current.querySelector('canvas,img')
const cnv = window.document.createElement('canvas')
// TODO the resolution is just based on the viewport for pdfs. It shouldn't be.
cnv.width = theThing.naturalWidth || theThing.width
cnv.height = theThing.naturalHeight || theThing.height
const ctx = cnv.getContext('2d')
ctx.drawImage(theThing, 0, 0)
ctx.fillStyle = '#000'
const x = val => val * cnv.width
const y = val => val * cnv.height
const margin = 2
Object.values(document.redactions[currentPageNumber]).forEach(red => {
ctx.fillRect(
x(red.Left) - margin,
y(red.Top) - margin,
x(red.Width) + 2 * margin,
y(red.Height) + 2 * margin
)
})
cnv.toBlob(blob => {
const a = window.document.createElement('a')
a.href = URL.createObjectURL(blob)
a.target = '_blank'
a.style.display = 'none'
a.download = document.objectName
.split('/')
.pop()
.replace(/\.[^.]+$/, '-REDACTED.png')
window.document.body.appendChild(a)
a.click()
}, 'image/png')
}, [currentPageNumber, document.objectName, document.redactions])
const pagePairsAsMarks = useMemo(() => {
return pageData.pairs.reduce((acc, { id, keyBoundingBox, valueBoundingBox }) => {
return [
...acc,
{ ...keyBoundingBox, id, type: 'key' },
{ ...valueBoundingBox, id, type: 'value' },
]
}, [])
}, [pageData.pairs])
const pageLinesAsMarks = useMemo(() => {
return pageData.lines.map(({ id, boundingBox }) => {
return {
id,
...boundingBox,
}
})
}, [pageData.lines])
const [highlightedKv, setHighlightedKv] = useState(null)
useEffect(() => {
if (highlightedKv) {
setTimeout(() => {
setHighlightedKv(null)
}, 10)
}
}, [highlightedKv])
const switchPage = useCallback(
pageNumber => {
dispatch(setCurrentPageNumber(pageNumber))
},
[dispatch]
)
const setHighlightedLine = useCallback(() => {}, [])
return (
<div className={css.document}>
{status === 'pending' && <Loading />}
{status === 'success' && (
<>
<div className={css.tabWrapper}>
<Tabs
isTrackTab={false}
selected={tab}
track={track}
onSelectTab={selectTab}
items={[
{ id: 'search', title: 'Preview' },
{ id: 'text', title: 'Raw Text' },
{ id: 'kv', title: `Key-Value Pairs` },
{ id: 'tables', title: `Tables` },
{ id: 'entities', title: `Entities` },
{ id: 'medical_entities', title: `Medical Entities` },
]}
/>
{track === 'redaction' &&
document.redactions &&
Object.keys(document.redactions).length ? (
<div className={css.downloadButtons}>
<Button inverted onClick={clearReds}>
Clear Redaction
</Button>
<Button className={css.downloadRedacted} onClick={downloadRedacted}>
⬇ Redacted Doc
</Button>
</div>
) : null}
<div>
<Tabs
isTrackTab={true}
selected={trackTab}
track={track}
onSelectTab={selectTrack}
items={[
{ id: 'searchTrack', title: 'Discovery'},
{ id: 'complianceTrack', title: 'Compliance'},
{ id: 'workflowTrack', title: 'Workflow Automation'}
]}
/>
</div>
</div>
<div className={cs(css.searchBarWrapper, tab === 'search' && css.visible)}>
<DocumentSearchBar className={css.searchBar} placeholder="Search current document…" />
{track === 'redaction' ? <Button onClick={redactMatches}>Redact matches</Button> : null}
</div>
<div className={css.content} ref={contentRef}>
<DocumentViewer
className={cs(
css.tabSourceViewer,
tab === 'kv' && css.withKv,
tab === 'entities' && css.withEv,
tab === 'medical_entities' && css.withEv,
tab === 'text' && css.withText
)}
document={document}
pageCount={pageCount}
redactions={(document.redactions || {})[currentPageNumber]}
marks={
tab === 'search'
? wordsMatchingSearch
: tab === 'text'
? pageLinesAsMarks
: tab === 'kv'
? pagePairsAsMarks
: tab === 'entities'
? (document.highlights || [])
: tab === 'medical_entities'
? (document.highlights || [])
: []
}
tables={tab === 'tables' && pageData.tables}
highlightedMark={highlightedKv}
/>
<div
className={cs(
css.sidebar,
(tab === 'kv' || tab === 'text' || tab === 'entities' || tab ==='medical_entities' || tab === 'search' ||tab === 'text'||tab === 'tables') && css.visible
)}
>
<KeyValueList
kvPairs={docData.pairs}
pageCount={pageCount}
currentPageNumber={currentPageNumber}
showRedaction={track === 'redaction'}
onHighlight={setHighlightedKv}
onSwitchPage={switchPage}
onRedact={redact}
onRedactAll={redactAllValues}
onDownload={downloadKV}
visible={tab === 'kv'}
/>
<DocumentPreview
document={document}
pageCount={pageCount}
visible={tab === 'search'}
track = {track}
/>
<RawTextLines
lines={docData.lines}
pageCount={pageCount}
currentPageNumber={currentPageNumber}
onHighlight={setHighlightedLine}
onSwitchPage={switchPage}
visible={tab === 'text'}
/>
<EntitiesCheckbox
entities={docData.entities}
pageCount={pageCount}
currentPageNumber={currentPageNumber}
showRedaction={track === 'redaction'}
onHighlight={highlightEntities}
onSwitchPage={switchPage}
onRedact={redactEntityMatches}
onRedactAll={redactAllValues}
onDownload={downloadKV}
visible={tab === 'entities'}
comprehendService={COMPREHEND_SERVICE}
onDownloadPrimary = {downloadEntities}
onDownloadSecondary = {null}
document = {document}
/>
<EntitiesCheckbox
entities={docData.medicalEntities}
pageCount={pageCount}
currentPageNumber={currentPageNumber}
showRedaction={track === 'redaction'}
onHighlight={highlightEntities}
onSwitchPage={switchPage}
onRedact={redactEntityMatches}
onRedactAll={redactAllValues}
onDownloadPrimary={downloadMedicalEntities}
onDownloadSecondary = {downloadMedicalICD10Ontologies}
visible={tab === 'medical_entities'}
comprehendService={COMPREHEND_MEDICAL_SERVICE}
document = {document}
/>
<TableResults
tables={docData.tables}
pageCount={pageCount}
currentPageNumber={currentPageNumber}
onSwitchPage={switchPage}
visible={tab === 'tables'}
document = {document}
/>
</div>
</div>
</>
)}
</div>
)
}