diff --git a/src/containers/App/index.tsx b/src/containers/App/index.tsx index 39a8c680..c4bdef1f 100755 --- a/src/containers/App/index.tsx +++ b/src/containers/App/index.tsx @@ -76,7 +76,7 @@ function mapStateToProps(state: State) { isLoading: metadataStateBranch.selectors.getIsLoading(state), loadingText: metadataStateBranch.selectors.getLoadingText(state), selectedDataset: selectionStateBranch.selectors.getSelectedDataset(state), - datasets: metadataStateBranch.selectors.getDatasets(state), + datasets: metadataStateBranch.selectors.getDatasetsByNewest(state), }; } diff --git a/src/state/metadata/selectors.ts b/src/state/metadata/selectors.ts index 33ee9ddf..98acafe5 100755 --- a/src/state/metadata/selectors.ts +++ b/src/state/metadata/selectors.ts @@ -1,10 +1,8 @@ import { map, filter, sortBy, find } from "lodash"; import { createSelector } from "reselect"; -import { - MITOTIC_STAGE_KEY, - PROTEIN_NAME_KEY, -} from "../../constants"; +import { MITOTIC_STAGE_KEY, PROTEIN_NAME_KEY } from "../../constants"; +import { DatasetMetaData } from "../../constants/datasets"; import { State } from "../types"; import { @@ -28,16 +26,67 @@ export const getMeasuredFeaturesDefs = (state: State) => state.metadata.measured export const getFileInfo = (state: State) => state.metadata.cellFileInfo; export const getClusterData = (state: State) => state.metadata.clusterData; -export const getMeasuredFeatureArrays = createSelector([getPerCellDataForPlot], (dataForPlot: DataForPlot) => { - return dataForPlot.values; -}); +export const compareVersions = (versionA: string, versionB: string): number => { + const [majorA, minorA , patchA] = versionA.split("."); + const [majorB, minorB, patchB] = versionB.split("."); + // may not exist (or actually be 0), either way, set to zero for comparison + const minorANum = Number(minorA) || 0; + const minorBNum = Number(minorB) || 0; + const patchANum = Number(patchA) || 0; + const patchBNum = Number(patchB) || 0; + + if (majorA === majorB) { + // if the major versions are equal, check the minor and patch numbers + if (minorANum === minorBNum) { + // if minor versions are also equal, check patch number + return patchBNum - patchANum; + } else { + return minorBNum - minorANum; + } + } else { + return Number(majorB) - Number(majorA) + } -export const getLabelsPerCell = createSelector([getPerCellDataForPlot], (dataForPlot: DataForPlot) => { - return dataForPlot.labels; +}; + +export const getDatasetsByNewest = createSelector([getDatasets], (datasets) => { + return datasets.sort((a: DatasetMetaData, b: DatasetMetaData) => { + // TODO: We need to sort and group datasets by "megasets" + // and then order those megasets by "newness" + // plus we have plans to have different versions of the same + // dataset contained within one card, so this is a temporary + // sort function to get the newest datasets on top + if (a.name === b.name) { + // if it's the same dataset, return the newest version first + return compareVersions(a.version, b.version); + // otherwise sort by name + } else if (a.name > b.name) { + return -1; + } else if (a.name < b.name) { + return 1; + } else { + return 0; + } + }); }); -export const getSortedCellLineDefs = createSelector([getCellLineDefs], (cellLineDefs: CellLineDef[]): CellLineDef[] => - sortBy(cellLineDefs, [PROTEIN_NAME_KEY]) +export const getMeasuredFeatureArrays = createSelector( + [getPerCellDataForPlot], + (dataForPlot: DataForPlot) => { + return dataForPlot.values; + } +); + +export const getLabelsPerCell = createSelector( + [getPerCellDataForPlot], + (dataForPlot: DataForPlot) => { + return dataForPlot.labels; + } +); + +export const getSortedCellLineDefs = createSelector( + [getCellLineDefs], + (cellLineDefs: CellLineDef[]): CellLineDef[] => sortBy(cellLineDefs, [PROTEIN_NAME_KEY]) ); export const getProteinNames = createSelector( @@ -47,27 +96,40 @@ export const getProteinNames = createSelector( } ); -export const getMeasuredFeaturesKeys = createSelector([getMeasuredFeaturesDefs], (measuredFeatureDefs): string[] => { - return map(measuredFeatureDefs, "key"); -}); - -export const getCategoricalFeatureKeys = createSelector([getMeasuredFeaturesDefs], (measuredFeatureDefs): string[] => { - return map(filter(measuredFeatureDefs, "discrete"), "key"); -}); +export const getMeasuredFeaturesKeys = createSelector( + [getMeasuredFeaturesDefs], + (measuredFeatureDefs): string[] => { + return map(measuredFeatureDefs, "key"); + } +); -export const getProteinLabelsPerCell = createSelector([getLabelsPerCell], (labels: PerCellLabels): string[] => { - return labels[PROTEIN_NAME_KEY] || []; -}); +export const getCategoricalFeatureKeys = createSelector( + [getMeasuredFeaturesDefs], + (measuredFeatureDefs): string[] => { + return map(filter(measuredFeatureDefs, "discrete"), "key"); + } +); -export const getMitoticStageNames = createSelector([getMeasuredFeaturesDefs], (defs: MeasuredFeatureDef[]) => { - const mitoticFeature = find(defs, {key: MITOTIC_STAGE_KEY}); - if (mitoticFeature) { - return mitoticFeature.options; +export const getProteinLabelsPerCell = createSelector( + [getLabelsPerCell], + (labels: PerCellLabels): string[] => { + return labels[PROTEIN_NAME_KEY] || []; } - else return {}; -}); +); -export const getMitoticKeyPerCell = createSelector([getMeasuredFeatureArrays], (measuredFeatures: MappingOfMeasuredValuesArrays): number[] => { - return measuredFeatures[MITOTIC_STAGE_KEY] || []; -}) +export const getMitoticStageNames = createSelector( + [getMeasuredFeaturesDefs], + (defs: MeasuredFeatureDef[]) => { + const mitoticFeature = find(defs, { key: MITOTIC_STAGE_KEY }); + if (mitoticFeature) { + return mitoticFeature.options; + } else return {}; + } +); +export const getMitoticKeyPerCell = createSelector( + [getMeasuredFeatureArrays], + (measuredFeatures: MappingOfMeasuredValuesArrays): number[] => { + return measuredFeatures[MITOTIC_STAGE_KEY] || []; + } +); diff --git a/src/state/metadata/test/selectors.test.ts b/src/state/metadata/test/selectors.test.ts index 68333191..91d21b63 100644 --- a/src/state/metadata/test/selectors.test.ts +++ b/src/state/metadata/test/selectors.test.ts @@ -2,20 +2,23 @@ import { expect } from "chai"; import { mockState } from "../../test/mocks"; import { State } from "../../types"; -import { getMeasuredFeaturesKeys, getProteinNames } from "../selectors"; +import { compareVersions, getDatasetsByNewest, getMeasuredFeaturesKeys, getProteinNames } from "../selectors"; describe("Metadata branch selectors", () => { - describe("getMeasuredFeaturesKeys", () => { it("returns the keys of measured features data", () => { const state: State = { ...mockState, }; const result: string[] = getMeasuredFeaturesKeys(state); - expect(result).to.deep.equal(["apical-proximity", "cell-segmentation", "cellular-surface-area", "missing-data"]); + expect(result).to.deep.equal([ + "apical-proximity", + "cell-segmentation", + "cellular-surface-area", + "missing-data", + ]); }); }); - describe("getProteinNames", () => { it("returns names of the proteins in the dataset", () => { const state: State = { @@ -24,8 +27,113 @@ describe("Metadata branch selectors", () => { const result: string[] = getProteinNames(state).sort((a: string, b: string) => { return b > a ? 1 : -1; }); - expect(result).to.deep.equal(["Nucleophosmin", "Delta-actin", "Beta-catenin", "Beta-actin", "Alpha-actinin-1"]); + expect(result).to.deep.equal([ + "Nucleophosmin", + "Delta-actin", + "Beta-catenin", + "Beta-actin", + "Alpha-actinin-1", + ]); }); }); + describe("compareVersions", () => { + it("returns a negative if the major version of the first item is greater", () => { + const result = compareVersions("2021.1", "2020.1"); + const resultNoMinor = compareVersions("2021", "2020"); + + expect(result).to.be.lessThan(0); + expect(resultNoMinor).to.be.lessThan(0); + }) + it("returns a negative if the first item has a minor version and the second doesn't", () => { + const result = compareVersions("2021.1", "2021"); + + expect(result).to.be.lessThan(0); + }); + it("returns negative if the minor version of the first item is greater", () => { + const result = compareVersions("2020.2", "2020.1"); + const resultDoubleDigit = compareVersions("2020.10", "2020.1"); + + expect(result).to.be.lessThan(0); + expect(resultDoubleDigit).to.be.lessThan(0); + }); + it("returns negative if the patch version of the first item is greater", () => { + const result = compareVersions("2020.1.2", "2020.1.0"); + const resultDoubleDigit = compareVersions("2020.1.10", "2020.1.1"); + + expect(result).to.be.lessThan(0); + expect(resultDoubleDigit).to.be.lessThan(0); + }); + it("returns 0 if versions are the same", () => { + const resultMajor = compareVersions("2020", "2020"); + const resultMinor = compareVersions("2020.1", "2020.1"); + const resultPatch = compareVersions("2020.1.1", "2020.1.1"); + + expect(resultMajor).to.equal(0); + expect(resultMinor).to.equal(0); + expect(resultPatch).to.equal(0); + + }); + }) + + describe("getDatasetsByNewest", () => { + it("returns the dataset card data in the order of newest to oldest", () => { + const dataset1 = { + name: "name1", + version: "2020.1", + }; + const dataset2 = { + name: "name1", + version: "2021.1", + }; + const state: State = { + ...mockState, + metadata: { + datasets: [dataset1, dataset2], + }, + }; + const result: string[] = getDatasetsByNewest(state); + expect(result).to.deep.equal([dataset2, dataset1]); + }); + it("returns the dataset card data in the order of newest to oldest", () => { + const dataset1 = { + name: "name1", + version: "2021.1", + }; + const dataset2 = { + name: "name1", + version: "2021.2", + }; + const state: State = { + ...mockState, + metadata: { + datasets: [dataset1, dataset2], + }, + }; + const result: string[] = getDatasetsByNewest(state); + expect(result).to.deep.equal([dataset2, dataset1]); + }); + it("returns the dataset card data with datasets grouped by name", () => { + const newerName1 = { + name: "name1", + version: "2021.2", + }; + const dataset2 = { + name: "name2", + version: "2021.2", + }; + const olderName1 = { + name: "name1", + version: "2021.1", + }; + const state: State = { + ...mockState, + metadata: { + datasets: [newerName1, dataset2, olderName1], + }, + }; + const result: string[] = getDatasetsByNewest(state); + expect(result).to.deep.equal([dataset2, newerName1, olderName1]); + }); + }); });