diff --git a/app/scripts/nmr-cli/package-lock.json b/app/scripts/nmr-cli/package-lock.json index df27a3e..188c9f4 100644 --- a/app/scripts/nmr-cli/package-lock.json +++ b/app/scripts/nmr-cli/package-lock.json @@ -13,6 +13,7 @@ "@zakodium/nmrium-core": "^0.6.5", "@zakodium/nmrium-core-plugins": "^0.6.39", "axios": "^1.13.5", + "fifo-logger": "^2.0.1", "file-collection": "^6.6.0", "json-stream-stringify": "^3.1.6", "lodash.merge": "^4.6.2", @@ -1559,4 +1560,4 @@ } } } -} +} \ No newline at end of file diff --git a/app/scripts/nmr-cli/package.json b/app/scripts/nmr-cli/package.json index 3f4c9d0..c1cda08 100644 --- a/app/scripts/nmr-cli/package.json +++ b/app/scripts/nmr-cli/package.json @@ -19,6 +19,7 @@ "@zakodium/nmrium-core": "^0.6.5", "@zakodium/nmrium-core-plugins": "^0.6.39", "axios": "^1.13.5", + "fifo-logger": "^2.0.1", "file-collection": "^6.6.0", "json-stream-stringify": "^3.1.6", "lodash.merge": "^4.6.2", @@ -36,4 +37,4 @@ "ts-node": "^10.9.2", "typescript": "^5.9.3" } -} +} \ No newline at end of file diff --git a/app/scripts/nmr-cli/src/parse/prase-spectra.ts b/app/scripts/nmr-cli/src/parse/prase-spectra.ts index d560e75..7701a06 100644 --- a/app/scripts/nmr-cli/src/parse/prase-spectra.ts +++ b/app/scripts/nmr-cli/src/parse/prase-spectra.ts @@ -13,19 +13,25 @@ import { Filters1DManager, Filters2DManager } from 'nmr-processing' import yargs from 'yargs' import { createWriteStream } from 'fs' import { JsonStreamStringify } from 'json-stream-stringify'; +import { FifoLogger } from 'fifo-logger' type RequiredKey = Omit & Required>; +function toMessage(e: unknown): string { + return e instanceof Error ? e.message : String(e) +} + + const parsingOptions: ParsingOptions = { onLoadProcessing: { autoProcessing: true }, selector: { general: { dataSelection: 'preferFT' } }, experimentalFeatures: true, }; + interface Snapshot { id: string; image: string | null; - error: string | null; } const core = init() @@ -41,7 +47,7 @@ async function launchBrowser() { return playwright.firefox.launch(); } -async function captureSpectraViewAsBase64(nmriumState: Partial): Promise { +async function captureSpectraViewAsBase64(nmriumState: Partial, logger: FifoLogger): Promise { const { data: { spectra } = { spectra: [] }, version } = nmriumState; if (!spectra?.length) return []; @@ -77,15 +83,10 @@ async function captureSpectraViewAsBase64(nmriumState: Partial): Pr await page.locator('text=Loading').waitFor({ state: 'hidden' }); const snapshot = await page.locator('#nmrSVG .container').screenshot(); - snapshots.push({ id: spectrum.id, image: snapshot.toString('base64'), error: null }); + snapshots.push({ id: spectrum.id, image: snapshot.toString('base64') }); } catch (e) { - snapshots.push({ - id: spectrum.id, - image: null, - error: e instanceof Error ? e.message : String(e), - }); - + logger.error({ id: spectrum.id, stage: 'snapshot', details: toMessage(e) }, 'Failed to capture snapshot for spectrum with id: ' + spectrum.id); // browser crashed — close and recreate for next spectrum await browser.close().catch(() => { }); browser = await launchBrowser(); @@ -104,23 +105,41 @@ interface ProcessSpectraOptions { autoDetection: boolean; autoProcessing: boolean; } -function processSpectra(data: NmriumData, options: ProcessSpectraOptions) { +function processSpectra(data: NmriumData, options: ProcessSpectraOptions, logger: FifoLogger) { const { autoDetection = false, autoProcessing = false } = options for (let index = 0; index < data.spectra.length; index++) { const inputSpectrum = data.spectra[index] const is2D = isSpectrum2D(inputSpectrum); - const spectrum = is2D ? initiateDatum2D(inputSpectrum) : initiateDatum1D(inputSpectrum); + let spectrum = null; + try { + + spectrum = is2D ? initiateDatum2D(inputSpectrum) : initiateDatum1D(inputSpectrum); + } catch (e) { + logger.error({ id: inputSpectrum.id, stage: 'parsing', details: toMessage(e) }, 'Failed to parse spectrum with id: ' + inputSpectrum.id); + continue; + } if (autoProcessing) { - isSpectrum2D(spectrum) ? Filters2DManager.reapplyFilters(spectrum) : Filters1DManager.reapplyFilters(spectrum) + try { + + isSpectrum2D(spectrum) ? Filters2DManager.reapplyFilters(spectrum) : Filters1DManager.reapplyFilters(spectrum) + } catch (e) { + logger.error({ id: inputSpectrum.id, stage: 'processing', details: toMessage(e) }, 'Failed to process spectrum with id: ' + inputSpectrum.id); + } } if (autoDetection && spectrum.info.isFt) { - isSpectrum2D(spectrum) ? detectZones(spectrum) : detectRanges(spectrum); + try { + isSpectrum2D(spectrum) ? detectZones(spectrum) : detectRanges(spectrum); + } catch (e) { + logger.error({ id: inputSpectrum.id, stage: 'detection', details: toMessage(e) }, 'Failed to detect spectrum peaks with id: ' + inputSpectrum.id); + } } + if (!spectrum) continue; + data.spectra[index] = spectrum; } @@ -143,16 +162,17 @@ function outputResult(result: any, outputPath?: string) { async function processAndSerialize( nmriumState: Partial, - options: FileOptionsArgs + options: FileOptionsArgs, + logger: FifoLogger ) { const { s: enableSnapshot = false, p: autoProcessing = false, d: autoDetection = false, o, r } = options; if (nmriumState.data) { - processSpectra(nmriumState.data, { autoDetection, autoProcessing }); + processSpectra(nmriumState.data, { autoDetection, autoProcessing }, logger); } const images: Snapshot[] = enableSnapshot - ? await captureSpectraViewAsBase64(nmriumState) + ? await captureSpectraViewAsBase64(nmriumState, logger) : []; const { data, version } = core.serializeNmriumState( @@ -169,11 +189,11 @@ async function processAndSerialize( spectra[i] = { ...spectra[i], info, meta } } } - - outputResult({ data, version, images }, o); + const errors = logger.getLogs({ minLevel: 'error' }) + outputResult({ data, version, images, errors }, o); } -async function loadSpectrumFromURL(options: RequiredKey) { +async function loadSpectrumFromURL(options: RequiredKey, logger: FifoLogger) { const { u: url } = options; const { pathname: relativePath, origin: baseURL } = new URL(url) @@ -186,13 +206,14 @@ async function loadSpectrumFromURL(options: RequiredKey) { baseURL, } - const [nmriumState] = await core.readFromWebSource(source, parsingOptions); - processAndSerialize(nmriumState, options) + const [nmriumState] = await core.readFromWebSource(source, { ...parsingOptions, logger }); + + processAndSerialize(nmriumState, options, logger) } -async function loadSpectrumFromFilePath(options: RequiredKey) { +async function loadSpectrumFromFilePath(options: RequiredKey, logger: FifoLogger) { const { dir: path } = options; const dirPath = isAbsolute(path) ? path : join(process.cwd(), path) @@ -203,25 +224,26 @@ async function loadSpectrumFromFilePath(options: RequiredKey ) { + const logger = new FifoLogger(); const { u, dir } = argv; // Handle parsing the spectra file logic based on argv options if (u) { - loadSpectrumFromURL({ u, ...argv }); + loadSpectrumFromURL({ u, ...argv }, logger); } if (dir) { - loadSpectrumFromFilePath({ dir, ...argv }); + loadSpectrumFromFilePath({ dir, ...argv }, logger); }