Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/__tests__/units/actions/lcms.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import hplcMsTicPosJcamp from "../../fixtures/lc_ms_jcamp_tic_pos";
import hplcMsTicNegJcamp from "../../fixtures/lc_ms_jcamp_tic_neg";
import hplcMsUvvisJcamp from "../../fixtures/lc_ms_jcamp_uvvis";
import lcMsMzChemstationJcamp from "../../fixtures/lc_ms_jcamp_mz_chemstation";
import msJcamp from "../../fixtures/ms_jcamp";

const hasSpectrumData = (entity: any) => {
const data = entity?.spectra?.[0]?.data?.[0];
Expand Down Expand Up @@ -170,6 +171,20 @@ describe('LCMS ExtractJcamp', () => {
const pages = extractPages(entity);
expect(new Set(pages).size).toBeGreaterThan(1);
});

it('Extract a plain MS jcamp keeps the MS layout instead of LC/MS', () => {
const entity: any = ExtractJcamp(msJcamp);
expect(entity.layout).toEqual(LIST_LAYOUT.MS);
});

it('Extract Chemstation MZ still resolves LC/MS when it also carries generic chem-spectra markers', () => {
const injected = lcMsMzChemstationJcamp.replace(
'##DATA TYPE=MASS SPECTRUM',
'##DATA TYPE=MASS SPECTRUM\n##$CSCATEGORY=SPECTRUM\n##$CSSCANAUTOTARGET=1',
);
const entity: any = ExtractJcamp(injected);
expect(entity.layout).toEqual(LIST_LAYOUT.LC_MS);
});
});

describe('LCMS grouping', () => {
Expand Down
19 changes: 14 additions & 5 deletions src/features/lc-ms/parsing/chemstation.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,9 @@ export const isChemstationLcms = (source, jcamp) => {
const scanMode = String(info.SCAN_MODE || info.SCANMODE || '').toUpperCase();
const type = String(info.TYPE || '').toUpperCase();
const software = String(info.SOFTWARE || '').toUpperCase();
const csCategory = jcamp?.info?.$CSCATEGORY;
const categories = Array.isArray(csCategory)
? csCategory.map((c) => String(c).toUpperCase())
: [];
const categories = []
.concat(jcamp?.info?.$CSCATEGORY || [])
.map((c) => String(c).toUpperCase());

const hasPolarityCategory = categories.some(
(c) => c.includes('POSITIVE') || c.includes('NEGATIVE') || c.includes('NEUTRAL'),
Expand Down Expand Up @@ -123,7 +122,7 @@ export const isChemstationLcms = (source, jcamp) => {

if (
hasMassSpectrumRootDataType
&& (hasMassSpectrumDataType || hasScanModeHint || hasTypeHint || hasSoftwareHint)
&& (hasScanModeHint || hasTypeHint || hasSoftwareHint || hasTicOrUvvisCategory)
) {
return true;
}
Expand All @@ -134,5 +133,15 @@ export const isChemstationLcms = (source, jcamp) => {
return true;
}

// A multi-page MASS SPECTRUM root with no other chromatographic signal is ambiguous:
// both a genuine Chemstation m/z export and a plain multi-page MS NTUPLES export look
// like this. The only field that reliably tells them apart is this units triplet, which
// is unique to the plain export - so it only vetoes this weak fallback, never the
// stronger positive evidence handled above.
if (hasMassSpectrumRootDataType && hasMultipleSpectra && hasPageMetadata) {
const hasMsUnitsTriplet = /##UNITS\s*=\s*M\/Z,\s*RELATIVE ABUNDANCE,\s*SECONDS/i.test(source);
return !hasMsUnitsTriplet;
}

return false;
};