I am running a process of extracting codes from pages in a PDF file. The problem is that when I hit the cancel button. The process does not get canceled and keeps reading codes from the file. I am using abortController to abort the process.
Here is function scanCodes:
export const scanCodes = async ({
setLogs,
setProgress,
pdfPath,
patientsFolder,
cropCords,
pdf,
abortControllerSignal,
sortToPatients,
}) =>
new Promise(async (resolve, reject) => {
const pageCodes = [];
const newFiles = [];
const pageLength = await configService.get('page-length');
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.STARTING'));
if (!model) {
wasmBinary = await loadWasmBinary();
engine = await createOCREngine({ wasmBinary });
const modelPath = app.isPackaged
? path.join(process.resourcesPath, 'public/lang-data/eng.traineddata')
: path.resolve('public/lang-data/eng.traineddata');
model = await fs.promises.readFile(modelPath);
await engine.loadModel(model);
await engine.setVariable('tessedit_pageseg_mode', '7');
await engine.setVariable('tessedit_char_whitelist', ' 0123456789');
}
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.SCANNING'));
await Promise.all(
Array.from({ length: pdf._pdfInfo.numPages }).map(async (x, i) => {
const cropCordsConfig = await configService.get('ocr-position');
const cords = {
x: cropCordsConfig?.x || cropCords.x,
y: cropCordsConfig?.y || cropCords.y,
width: cropCordsConfig?.width || cropCords.width,
height: cropCordsConfig?.height || cropCords.height,
};
const pageImage = await pdfService.readAsImageData(pdf, i + 1, cords);
await engine.loadImage(pageImage);
const text = engine.getText();
pageCodes.push({
code: text.replace(/\n/gi, ''),
pageIndex: i,
});
setProgress(parseInt((pageCodes.length / pdf._pdfInfo.numPages) * 100));
})
);
setProgress(0);
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.ANALYZING'));
const pagesWithInvalidCodeFormat = [];
const invalidCustIdFiles = [];
const validCustIdFiles = [];
const custId = await configService.get('custId');
pageCodes.forEach((c, i) => {
const validity = validateCode(c.code, pageLength);
if (validity === null) {
// Do not replace with ===
if (parseCode(c.code, pageLength).custId == custId) {
validCustIdFiles.push(c);
} else {
invalidCustIdFiles.push(c);
}
} else {
pagesWithInvalidCodeFormat.push({ ...c, error: validity });
}
return validity === null;
});
let docSorted = _.groupBy(invalidCustIdFiles, (item) => parseCode(item.code, pageLength).patientId);
docSorted = Object.keys(docSorted).map((key) => docSorted[key]);
const invalidDocuments = await groupByAsync(
docSorted.flat(),
async (item) => `Docs-CN-${parseCode(item.code, pageLength).custId}`
);
const documents = await groupByAsync(validCustIdFiles, async (item) =>
documentId(parseCode(item.code, pageLength))
);
const missingPageNumbers = {};
for (let docId of Object.keys(documents)) {
const docPages = documents[docId];
const docTotalPages = parseCode(docPages[0].code, pageLength).totalPages;
missingPageNumbers[docId] = [...new Array(parseInt(docTotalPages)).keys()]
// Do not replace with ===
.filter((i) => !docPages.find((p) => parseCode(p.code, pageLength).pageNo == i + 1))
.map((p) => p + 1);
setProgress(parseInt((Object.keys(documents).indexOf(docId) / Object.keys(documents).length) * 100));
}
setProgress(0);
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.CREATING'));
const docPromises = Object.keys(documents).map(async (docId) => {
if (abortControllerSignal && abortControllerSignal.aborted) {
setLogs('ready');
setProgress(0);
return;
}
if (missingPageNumbers[docId].length) return;
const docPages = documents[docId];
// create pdf file
const newExtractedPdf = await pdfService.extractPagesToPdf({
file: await fs.promises.readFile(pdfPath),
pages: docPages
.sort(
(a, b) => parseInt(parseCode(a.code, pageLength).pageNo) - parseInt(parseCode(b.code, pageLength).pageNo)
)
.map((p) => ({ originalPageNumber: parseInt(p.pageIndex) + 1 })),
});
const newFileName = `${docId}.pdf`;
const newFilePath = path.join(patientsFolder, newFileName);
let targetDir = newFilePath;
if (sortToPatients) {
const fileName = path.basename(newFileName);
const splitFileName = fileName.split('-');
const patientID = splitFileName[1];
targetDir = path.join(patientsFolder, patientID);
if (!fse.existsSync(targetDir)) {
fse.mkdirs(targetDir);
}
targetDir = path.join(targetDir, path.basename(newFileName));
}
await fs.promises.writeFile(targetDir, newExtractedPdf).catch((err) => {
alert(err.message ? err.message : err.toString());
});
newFiles.push(newFilePath);
setProgress(100);
});
await Promise.all(docPromises);
if (invalidDocuments.length) {
setProgress(0);
setLogs('Moving files to SplitScan...');
}
const splitScanPath = await configService.get('splitscan-path');
const invalidDocPromises = Object.keys(invalidDocuments).map(async (docId) => {
if (abortControllerSignal && abortControllerSignal.aborted) {
setLogs(i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.SCAN_STATE.READY'));
setProgress(0);
return;
}
const docPages = invalidDocuments[docId];
// create pdf file
const newExtractedPdf = await pdfService.extractPagesToPdf({
file: await fs.promises.readFile(pdfPath),
pages: docPages.map((p) => ({ originalPageNumber: parseInt(p.pageIndex) + 1 })),
});
const newFileName = `${docId}.pdf`;
const newFilePath = path.join(splitScanPath, newFileName);
await fs.promises.writeFile(newFilePath, newExtractedPdf).catch((err) => {
alert(err.message ? err.message : err.toString());
});
setProgress(100);
});
await Promise.all(invalidDocPromises);
const pagesOfDocumentsWithMissingPages = Object.entries(missingPageNumbers)
.filter(([_key, value]) => value.length)
.map(([doc, _pages]) => documents[doc])
.map((doc) => Object.fromEntries(doc.map(({ pageIndex, code }) => [pageIndex, code])))
.reduce((a, b) => ({ ...a, ...b }), {});
const pagesHavingErrors = Object.fromEntries(
pagesWithInvalidCodeFormat.map(({ pageIndex, code }) => [pageIndex, code])
);
const pagesExcludedFromResult = { ...pagesOfDocumentsWithMissingPages, ...pagesHavingErrors };
let errorsFilePath = null;
if (Object.keys(pagesExcludedFromResult).length) {
// save error pages
errorsFilePath = path.join(path.dirname(pdfPath), `${path.basename(pdfPath, path.extname(pdfPath))}_errors.pdf`);
const pagesToBeExcluded = Object.keys(pagesExcludedFromResult).sort((a, b) => parseInt(a) - parseInt(b));
const errorPdfFileContents = await pdfService
.extractPagesToPdf({
file: await fs.promises.readFile(pdfPath),
pages: pagesToBeExcluded.map((pNo) => ({ originalPageNumber: parseInt(pNo) + 1 })),
})
.catch((err) => {
alert('could not extract error pages !');
console.log(err);
});
const errorsFileMappingPath = getMappingFile(errorsFilePath);
await fs.promises.writeFile(errorsFilePath, errorPdfFileContents).catch(reject);
await fs.promises
.writeFile(
errorsFileMappingPath,
JSON.stringify(
pagesToBeExcluded.map((p) => pagesExcludedFromResult[p]),
1
)
)
.catch(reject);
}
setProgress(100);
resolve([
Object.fromEntries(pagesWithInvalidCodeFormat.map((i) => [`#${i.pageIndex + 1} | '${i.code}'`, i.error])),
Object.fromEntries(Object.entries(missingPageNumbers).filter(([_key, value]) => value.length)),
newFiles,
errorsFilePath,
]);
});
The process was aborting previously when I was using a simple for loop instead of Promise.all and 'async map calls`
Here is the Button Code:
<ActionBarButton className="SS-C-code-scan-action-bar_run" onClick={() => abortController?.abort() || undefined}> {i18n.t('MAIN_CONTENT.CODESCAN.SUBHEADER.BUTTON.CANCEL')} </ActionBarButton>
I have tried different solution tried to shift abortControllerSignal if statement to different locations out of the Promise.all. Then I declared a function that sets the cancelToken value to true But the true value was only accessible when the Promise was resolved. Any help would be appreciated