Skip to content

Commit

Permalink
refactor: update loaders to support error logs (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
Santiago Miranda authored Nov 15, 2022
1 parent d3c41f8 commit 02adad5
Show file tree
Hide file tree
Showing 21 changed files with 419 additions and 184 deletions.
9 changes: 4 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"@lukeed/uuid": "^2.0.0",
"@popperjs/core": "^2.11.6",
"@tanstack/react-table": "^8.5.22",
"biologic-converter": "^0.4.0",
"biologic-converter": "^0.5.0",
"cheminfo-types": "^1.4.0",
"d3-scale-chromatic": "^3.0.0",
"filelist-utils": "^1.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app-data/DataState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { assert } from '../utils/assert';

import type { IRMeasurement } from './IRMeasurement';
import type { MeasurementBase } from './MeasurementBase';
import type { ParserLog } from './loaders/utility/parserLog';

export interface DataState {
measurements: Measurements;
Expand Down Expand Up @@ -63,6 +64,7 @@ export const kindsLabel: Record<MeasurementKind, string> = {

export type Loader = (
fileCollection: FileCollection,
logs?: ParserLog[],
) => Promise<Partial<Measurements>>;

export function mergeMeasurements(
Expand Down
2 changes: 1 addition & 1 deletion src/app-data/__tests__/getGCMSMeasurement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const filteredFileCollection = fileCollection.filter(
);

test('getGCMSMeasurement', async () => {
const measurements = await loadMeasurements(filteredFileCollection, {
const { measurements } = await loadMeasurements(filteredFileCollection, {
loaders: [cdfLoader],
});
const result = measurements.gclcms?.entries;
Expand Down
2 changes: 1 addition & 1 deletion src/app-data/__tests__/getHPLCMeasurement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const filteredFileCollection = fileCollection.filter(
);

test('getHPLCMeasurement', async () => {
const measurements = await loadMeasurements(filteredFileCollection, {
const { measurements } = await loadMeasurements(filteredFileCollection, {
loaders: [cdfLoader],
});
const result = measurements.gclc?.entries || [];
Expand Down
2 changes: 1 addition & 1 deletion src/app-data/__tests__/getIRMeasurement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export async function getIRMeasurement() {
(file) => file.name === 'ir.jdx',
);

const measurements = await loadMeasurements(filteredFileCollection, {
const { measurements } = await loadMeasurements(filteredFileCollection, {
loaders,
enhancers,
});
Expand Down
2 changes: 1 addition & 1 deletion src/app-data/__tests__/getIVMeasurement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ const loaders = [biologicLoader];

async function getIVMeasurement() {
const fileCollection = await getTestFileCollection('biologic');
const measurements = await loadMeasurements(fileCollection, { loaders });
const { measurements } = await loadMeasurements(fileCollection, { loaders });
return measurements.iv || { entries: [] };
}
4 changes: 2 additions & 2 deletions src/app-data/__tests__/loadMeasurements.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const enhancers = {
test('loadMeasurements function', async () => {
const fileCollection = await getTestFileCollection();

const measurements = await loadMeasurements(fileCollection, {
const { measurements } = await loadMeasurements(fileCollection, {
loaders,
enhancers,
});

expect(Object.keys(measurements)).toStrictEqual(['nmr', 'ir', 'uv', 'raman']);
const { ir, raman, uv } = measurements;
expect(Object.keys(measurements)).toStrictEqual(['nmr', 'ir', 'uv', 'raman']);
expect(ir?.entries).toHaveLength(2);

expect(ir?.entries[0].peaks).toHaveLength(18);
Expand Down
11 changes: 7 additions & 4 deletions src/app-data/loadMeasurements.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import type { FileCollection } from 'filelist-utils';

import { Loader, Measurements, mergeMeasurements } from './DataState';
import { Measurements, Loader, mergeMeasurements } from './DataState';
import { enhance } from './enhancers/enhance';
import type { ParserLog } from './loaders/utility/parserLog';

interface LoadOptions {
loaders?: Loader[];
enhancers?;
logger?: boolean;
}

export async function loadMeasurements(
fileCollection: FileCollection,
options: LoadOptions = {},
) {
const measurements: Partial<Measurements> = {};
const { loaders = [], enhancers = {} } = options;
let logs: ParserLog[] = [];
const { loaders = [], enhancers = {}, logger = true } = options;
for (const loader of loaders) {
const loaderData = await loader(fileCollection);
const loaderData = await loader(fileCollection, logger ? logs : undefined);
enhance(loaderData, enhancers);
mergeMeasurements(measurements, loaderData);
}
return measurements;
return { measurements, logs };
}
110 changes: 65 additions & 45 deletions src/app-data/loaders/biologicLoader.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,72 @@
import { v4 } from '@lukeed/uuid';
import { convert } from 'biologic-converter';
import { parseMPR, parseMPT } from 'biologic-converter';
import type { MeasurementVariable } from 'cheminfo-types';
import type { FileCollection } from 'filelist-utils';

import type { Measurements } from '../DataState';
import type { MeasurementBase } from '../MeasurementBase';

import { getMeasurementInfoFromFile } from './utility/getMeasurementInfoFromFile';
import { createLogEntry, ParserLog } from './utility/parserLog';

/* the MeasurementBase has got a data key,
and inside a variable key, compatible with this type */
type MeasurementDataVariable = Record<string, MeasurementVariable>;
/**
* Loader for biologic files, using biologic-converter
* @param fileCollection
* @returns iv-MeasurementBase for each file in the collection
*
* @param fileCollection - the dragged file/files
* @param logs - pass to append errors to the logs, it mutates the external array.
* @returns - new measurements object to be merged
*/
export async function biologicLoader(fileCollection: FileCollection) {
export async function biologicLoader(
fileCollection: FileCollection,
logs?: ParserLog[],
): Promise<Partial<Measurements>> {
const measurements: Partial<Measurements> = {};
const entries: MeasurementBase[] = [];
const results = await convert(fileCollection);
for (let { dir, mpr, mpt } of results) {
const prepare: Partial<MeasurementBase> = {
id: v4(),
filename: '',
path: dir,
info: {},
title: '',
};
if (mpr !== undefined) {
prepare.meta = { ...mpr.settings.variables };
// puts the "useful" variables at x and y for default plot.
const variables = preferredXY(prepare.meta.technique, mpr.data.variables);
prepare.data = [{ variables }];
} else if (mpt !== undefined) {
prepare.meta = { ...mpt.settings?.variables };
if (mpt.data?.variables) {

for (const file of fileCollection.files) {
try {
if (/\.mpr$/i.test(file.name)) {
const mpr = parseMPR(await file.arrayBuffer());
const info = getMeasurementInfoFromFile(file);
const meta = mpr.settings.variables;
// puts the "useful" variables at x and y for default plot.
const variables = preferredXY(
prepare.meta?.technique,
mpt.data.variables,
);
prepare.data = [{ variables }];
const variables = preferredXY(meta.technique, mpr.data.variables);
const result: MeasurementBase = {
title: `Experiment: ${meta.technique}`,
...info,
meta,
data: [{ variables }],
};
entries.push(result);
} else if (/\.mpt$/i.test(file.name)) {
const { data, settings } = parseMPT(await file.arrayBuffer());
const info = getMeasurementInfoFromFile(file);
if (data?.variables) {
// puts the "useful" variables at x and y for default plot.
const metaData = settings?.variables;
const variables = preferredXY(metaData?.technique, data.variables);
const result: MeasurementBase = {
title: metaData.technique
? `Experiment: ${metaData.technique}`
: file.name,
...info,
meta: metaData || {},
data: [{ variables }],
};
entries.push(result);
}
}
} catch (error) {
//send error to UI to show to User ?
if (error instanceof Error) {
if (logs) {
logs.push(createLogEntry({ error, relativePath: file.relativePath }));
} else {
throw error;
}
}
}
if (!prepare.data) {
// no data ? skip this file
// need a way to dispatch an error with the filename and a custom
// message
continue;
}
//will notify if the key is not a valid kind
entries.push(prepare as MeasurementBase);
}
measurements.iv = { entries };
return measurements;
Expand All @@ -73,17 +90,15 @@ function preferredXY(

switch (technique) {
case 'CA': {
//Chronoamperometry or Chronocoulometry
variables = setDefault(variables, 'time', 'x');
// I am not sure how to find out whether it is Chronoamperometry or Chronocoulometry
// (same experiment just changes what user is interested in.)
//Chronoamperometry or Chronocoulometry (how distinguish?)
variables = setDefault(variables, 'I', 'y');
variables = setDefault(variables, 'time', 'x');
break;
}
case 'CP': {
//Chronopotentiometry
variables = setDefault(variables, '<Ewe>', 'y');
variables = setDefault(variables, 'time', 'x');
variables = setDefault(variables, '<I>', 'y');
break;
}
case 'CV': {
Expand All @@ -92,8 +107,13 @@ function preferredXY(
variables = setDefault(variables, 'Ewe', 'x');
break;
}
case 'GCPL': {
variables = setDefault(variables, 'Ewe', 'y');
variables = setDefault(variables, 'time', 'x');
break;
}
default:
return variables;
break;
}
return variables;
}
Expand All @@ -110,8 +130,8 @@ function setDefault(
label: string,
storeAt: 'x' | 'y',
) {
for (let varum in variables) {
// find the key that has the label (or skip if not found)
for (const varum in variables) {
// find the key with `label` (or skip if not found)
if (
variables[varum].label === label && //if not already at the right key
varum !== storeAt
Expand All @@ -122,7 +142,7 @@ function setDefault(
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete variables[varum];
} else {
// if the storeAt is already occupied, we swap the two
// if the storeAt is already occupied, swap the two
const originalX = variables[storeAt];
const newX = variables[varum];
variables[storeAt] = newX;
Expand Down
Loading

0 comments on commit 02adad5

Please sign in to comment.