Merge branch 'main' into colinmegill/#632

This commit is contained in:
Colin Megill
2021-08-04 14:43:09 -07:00
8 changed files with 157 additions and 82 deletions
@@ -1,4 +1,6 @@
export const schema = { import { RawSchema } from "../../../../src/common/types/entities";
export const schema: { schema: RawSchema } = {
schema: { schema: {
annotations: { annotations: {
obs: { obs: {
@@ -5,6 +5,7 @@ import zip from "lodash.zip";
import _ from "lodash"; import _ from "lodash";
import { flatbuffers } from "flatbuffers"; import { flatbuffers } from "flatbuffers";
import { NetEncoding } from "../../../src/util/stateManager/matrix_generated"; import { NetEncoding } from "../../../src/util/stateManager/matrix_generated";
import { RawSchema } from "../../../src/common/types/entities";
/* /*
test data mocking REST 0.2 API responses. Used in several tests. test data mocking REST 0.2 API responses. Used in several tests.
@@ -29,7 +30,7 @@ const aConfigResponse = {
}, },
}; };
const aSchemaResponse = { const aSchemaResponse: { schema: RawSchema } = {
schema: { schema: {
dataframe: { dataframe: {
nObs, nObs,
@@ -40,28 +41,30 @@ const aSchemaResponse = {
obs: { obs: {
index: "name", index: "name",
columns: [ columns: [
{ name: "name", type: "string" }, { name: "name", type: "string", writable: false },
{ name: "field1", type: "int32" }, { name: "field1", type: "int32", writable: false },
{ name: "field2", type: "float32" }, { name: "field2", type: "float32", writable: false },
{ name: "field3", type: "boolean" }, { name: "field3", type: "boolean", writable: false },
{ {
name: "field4", name: "field4",
type: "categorical", type: "categorical",
categories: field4Categories, categories: field4Categories,
writable: false,
}, },
], ],
}, },
var: { var: {
index: "name", index: "name",
columns: [ columns: [
{ name: "name", type: "string" }, { name: "name", type: "string", writable: false },
{ name: "fieldA", type: "int32" }, { name: "fieldA", type: "int32", writable: false },
{ name: "fieldB", type: "float32" }, { name: "fieldB", type: "float32", writable: false },
{ name: "fieldC", type: "boolean" }, { name: "fieldC", type: "boolean", writable: false },
{ {
name: "fieldD", name: "fieldD",
type: "categorical", type: "categorical",
categories: fieldDCategories, categories: fieldDCategories,
writable: false,
}, },
], ],
}, },
+3 -2
View File
@@ -12,9 +12,10 @@
"e2e-annotations": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2eAnnotations.test.ts", "e2e-annotations": "jest --config __tests__/e2e/e2eJestConfig.json e2e/e2eAnnotations.test.ts",
"e2e-prod": "CXG_URL_BASE='https://cellxgene.cziscience.com/d/pbmc3k.cxg/' jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.ts", "e2e-prod": "CXG_URL_BASE='https://cellxgene.cziscience.com/d/pbmc3k.cxg/' jest --config __tests__/e2e/e2eJestConfig.json e2e/e2e.test.ts",
"fmt": "eslint --fix src __tests__", "fmt": "eslint --fix src __tests__",
"lint": "eslint --fix src __tests__", "lint": "eslint src __tests__ & npm run type-check",
"prod": "npm run build -- configuration/webpack/webpack.config.prod.js", "prod": "npm run build -- configuration/webpack/webpack.config.prod.js",
"test": "jest --testPathIgnorePatterns e2e" "test": "jest --testPathIgnorePatterns e2e",
"type-check": "tsc --noEmit"
}, },
"engineStrict": true, "engineStrict": true,
"engines": { "engines": {
+59
View File
@@ -1 +1,60 @@
// If a globally shared type or interface doesn't have a clear owner, put it here // If a globally shared type or interface doesn't have a clear owner, put it here
export type Category = number | string | boolean;
export interface AnnotationColumn {
categories?: Category[];
name: string;
type: "string" | "float32" | "int32" | "categorical" | "boolean";
writable: boolean;
}
interface DataFrame {
nObs: number;
nVar: number;
// TODO(thuang): Not sure what other types are available
type: "float32";
}
export interface LayoutColumn {
dims: string[];
name: string;
// TODO(thuang): Not sure what other types are available
type: "float32";
}
interface RawLayout {
obs: LayoutColumn[];
var?: LayoutColumn[];
}
interface RawAnnotations {
obs: {
columns: AnnotationColumn[];
index: string;
};
var: {
columns: AnnotationColumn[];
index: string;
};
}
export interface RawSchema {
annotations: RawAnnotations;
dataframe: DataFrame;
layout: RawLayout;
}
interface Annotations extends RawAnnotations {
obsByName: { [name: string]: AnnotationColumn };
varByName: { [name: string]: AnnotationColumn };
}
interface Layout extends RawLayout {
obsByName: { [name: string]: LayoutColumn };
varByName: { [name: string]: LayoutColumn };
}
export interface Schema extends RawSchema {
annotations: Annotations;
layout: Layout;
}
+8 -6
View File
@@ -1,15 +1,17 @@
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export default function fromEntries<T = unknown>(
export default function fromEntries(arr: any) { arr: [string | number, T][]
): { [key: string]: T } {
/* /*
Similar to Object.fromEntries, but only handles array. Similar to Object.fromEntries, but only handles array.
This could be replaced with the standard fucnction once it This could be replaced with the standard function once it
is widely available. As of 3/20/2019, it has not yet is widely available. As of 3/20/2019, it has not yet
been released in the Chrome stable channel. been released in the Chrome stable channel.
*/ */
const obj = {}; const obj: { [key: string]: T } = {};
for (let i = 0, l = arr.length; i < l; i += 1) {
// @ts-expect-error ts-migrate(7053) FIXME: Element implicitly has an 'any' type because expre... Remove this comment to see the full error message for (let i = 0; i < arr.length; i += 1) {
obj[arr[i][0]] = arr[i][1]; obj[arr[i][0]] = arr[i][1];
} }
return obj; return obj;
} }
@@ -3,6 +3,8 @@ Helper functions for user-editable annotations state management.
See also reducers/annotations.js See also reducers/annotations.js
*/ */
import { Schema } from "../../common/types/entities";
/* /*
There are a number of state constraints assumed throughout the There are a number of state constraints assumed throughout the
application: application:
@@ -16,34 +18,36 @@ application:
In addition, the current state management only allows for In addition, the current state management only allows for
categorical annotations to be writable. categorical annotations to be writable.
*/ */
export function isCategoricalAnnotation(
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type. schema: Schema,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS. name: string
export function isCategoricalAnnotation(schema, name) { ): boolean | undefined {
/* /*
we treat any string, categorical or boolean as a categorical. we treat any string, categorical or boolean as a categorical.
Return true/false/undefined (for unkonwn fields)
*/ */
const colSchema = schema.annotations.obsByName[name]; const colSchema = schema.annotations.obsByName[name];
if (colSchema === undefined) return undefined; if (colSchema === undefined) return undefined;
const { type } = colSchema; const { type } = colSchema;
return type === "string" || type === "boolean" || type === "categorical"; return type === "string" || type === "boolean" || type === "categorical";
} }
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type. export function isContinuousAnnotation(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS. schema: Schema,
export function isContinuousAnnotation(schema, name) { name: string
/* ): boolean | undefined {
Return true/false/undefined
*/
const colSchema = schema.annotations.obsByName[name]; const colSchema = schema.annotations.obsByName[name];
if (colSchema === undefined) return undefined; if (colSchema === undefined) return undefined;
const { type } = colSchema; const { type } = colSchema;
return !(type === "string" || type === "boolean" || type === "categorical"); return !(type === "string" || type === "boolean" || type === "categorical");
} }
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type. function _isUserAnnotation(schema: Schema, name: string): boolean {
function _isUserAnnotation(schema, name) {
return schema.annotations.obsByName[name]?.writable || false; return schema.annotations.obsByName[name]?.writable || false;
} }
@@ -72,9 +76,8 @@ export function allHaveLabelByMask(df, colName, label, mask) {
} }
const legalCharacters = /^(\w|[ .()-])+$/; const legalCharacters = /^(\w|[ .()-])+$/;
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'name' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS. export function annotationNameIsErroneous(name: string): boolean | string {
export function annotationNameIsErroneous(name) {
/* /*
Validate the name - return: Validate the name - return:
* false - a valid name * false - a valid name
@@ -101,6 +104,6 @@ export function annotationNameIsErroneous(name) {
} }
} }
/* all is well! Indicte not erroneous with a false */ /* all is well! Indicate not erroneous with a false */
return false; return false;
} }
+50 -45
View File
@@ -8,6 +8,12 @@ import cloneDeep from "lodash.clonedeep";
import fromEntries from "../fromEntries"; import fromEntries from "../fromEntries";
import catLabelSort from "../catLabelSort"; import catLabelSort from "../catLabelSort";
import {
RawSchema,
Schema,
LayoutColumn,
AnnotationColumn,
} from "../../common/types/entities";
/* /*
System wide schema assumptions: System wide schema assumptions:
@@ -15,31 +21,25 @@ System wide schema assumptions:
- schema will be internally self-consistent (eg, index matches columns) - schema will be internally self-consistent (eg, index matches columns)
*/ */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function indexEntireSchema(schema: RawSchema): Schema {
export function indexEntireSchema(schema: any) {
/* Index schema for ease of use */ /* Index schema for ease of use */
schema.annotations.obsByName = fromEntries( (schema as Schema).annotations.obsByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.annotations?.obs?.columns?.map((v) => [v.name, v]) || []
schema.annotations?.obs?.columns?.map((v: any) => [v.name, v]) ?? []
); );
schema.annotations.varByName = fromEntries( (schema as Schema).annotations.varByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.annotations?.var?.columns?.map((v) => [v.name, v]) || []
schema.annotations?.var?.columns?.map((v: any) => [v.name, v]) ?? []
); );
schema.layout.obsByName = fromEntries( (schema as Schema).layout.obsByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.layout?.obs?.map((v) => [v.name, v]) || []
schema.layout?.obs?.map((v: any) => [v.name, v]) ?? []
); );
schema.layout.varByName = fromEntries( (schema as Schema).layout.varByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.layout?.var?.map((v) => [v.name, v]) || []
schema.layout?.var?.map((v: any) => [v.name, v]) ?? []
); );
return schema; return schema as Schema;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function _copyObsAnno(schema: Schema): Schema {
function _copyObsAnno(schema: any) {
/* redux copy conventions - WARNING, only for modifying obs annotations */ /* redux copy conventions - WARNING, only for modifying obs annotations */
return { return {
...schema, ...schema,
@@ -50,8 +50,7 @@ function _copyObsAnno(schema: any) {
}; };
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function _copyObsLayout(schema: Schema): Schema {
function _copyObsLayout(schema: any) {
return { return {
...schema, ...schema,
layout: { layout: {
@@ -61,57 +60,62 @@ function _copyObsLayout(schema: any) {
}; };
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function _reindexObsAnno(schema: Schema): Schema {
function _reindexObsAnno(schema: any) {
/* reindex obs annotations ONLY */ /* reindex obs annotations ONLY */
schema.annotations.obsByName = fromEntries( schema.annotations.obsByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.annotations.obs.columns.map((v) => [v.name, v])
schema.annotations.obs.columns.map((v: any) => [v.name, v])
); );
return schema; return schema;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. function _reindexObsLayout(schema: Schema) {
function _reindexObsLayout(schema: any) {
schema.layout.obsByName = fromEntries( schema.layout.obsByName = fromEntries(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. schema.layout.obs.map((v) => [v.name, v])
schema.layout.obs.map((v: any) => [v.name, v])
); );
return schema; return schema;
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function removeObsAnnoColumn(schema: Schema, name: string): Schema {
export function removeObsAnnoColumn(schema: any, name: any) {
const newSchema = _copyObsAnno(schema); const newSchema = _copyObsAnno(schema);
newSchema.annotations.obs.columns = schema.annotations.obs.columns.filter( newSchema.annotations.obs.columns = schema.annotations.obs.columns.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS. (v) => v.name !== name
(v: any) => v.name !== name
); );
return _reindexObsAnno(newSchema); return _reindexObsAnno(newSchema);
} }
// @ts-expect-error ts-migrate(6133) FIXME: 'name' is declared but its value is never read. export function addObsAnnoColumn(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. schema: Schema,
export function addObsAnnoColumn(schema: any, name: any, defn: any) { _: string,
defn: AnnotationColumn
): Schema {
const newSchema = _copyObsAnno(schema); const newSchema = _copyObsAnno(schema);
newSchema.annotations.obs.columns.push(defn); newSchema.annotations.obs.columns.push(defn);
return _reindexObsAnno(newSchema); return _reindexObsAnno(newSchema);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function removeObsAnnoCategory(
export function removeObsAnnoCategory(schema: any, name: any, category: any) { schema: Schema,
name: string,
category: string
): Schema {
/* remove a category from a categorical annotation */ /* remove a category from a categorical annotation */
const categories = schema.annotations.obsByName[name]?.categories; const categories = schema.annotations.obsByName[name]?.categories;
if (!categories)
if (!categories) {
throw new Error("column does not exist or is not categorical"); throw new Error("column does not exist or is not categorical");
}
const idx = categories.indexOf(category); const idx = categories.indexOf(category);
if (idx === -1) throw new Error("category does not exist"); if (idx === -1) throw new Error("category does not exist");
const newSchema = _reindexObsAnno(_copyObsAnno(schema)); const newSchema = _reindexObsAnno(_copyObsAnno(schema));
/* remove category. Do not need to resort as this can't change presentation order */ /* remove category. Do not need to resort as this can't change presentation order */
newSchema.annotations.obsByName[name].categories.splice(idx, 1); newSchema.annotations.obsByName[name].categories?.splice(idx, 1);
return newSchema; return newSchema;
} }
@@ -129,26 +133,27 @@ export function addObsAnnoCategory(schema: any, name: any, category: any) {
/* add category, retaining presentation sort order */ /* add category, retaining presentation sort order */
const catAnno = newSchema.annotations.obsByName[name]; const catAnno = newSchema.annotations.obsByName[name];
catAnno.categories = catLabelSort(catAnno.writable, [ catAnno.categories = catLabelSort(catAnno.writable, [
...catAnno.categories, ...(catAnno.categories || []),
category, category,
]); ]);
return newSchema; return newSchema;
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function addObsLayout(schema: Schema, layout: LayoutColumn): Schema {
export function addObsLayout(schema: any, layout: any) {
/* add or replace a layout */ /* add or replace a layout */
const newSchema = _copyObsLayout(schema); const newSchema = _copyObsLayout(schema);
newSchema.layout.obs.push(layout); newSchema.layout.obs.push(layout);
return _reindexObsLayout(newSchema); return _reindexObsLayout(newSchema);
} }
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS. export function removeObsLayout(schema: Schema, name: string): Schema {
export function removeObsLayout(schema: any, name: any) {
/* remove a layout */ /* remove a layout */
const newSchema = _copyObsLayout(schema); const newSchema = _copyObsLayout(schema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
newSchema.layout.obs = schema.layout.obs.filter((v: any) => v.name !== name); newSchema.layout.obs = schema.layout.obs.filter((v) => v.name !== name);
return _reindexObsLayout(newSchema); return _reindexObsLayout(newSchema);
} }
+1 -1
View File
@@ -31,5 +31,5 @@
"__tests__/**/*", "__tests__/**/*",
"jest-puppeteer.config.js" "jest-puppeteer.config.js"
], ],
"exclude": ["__tests__/e2e/__snapshots__/**/*"] "exclude": ["node_modules", "__tests__/e2e/__snapshots__/**/*"]
} }