chore: add schema types (#2369)

This commit is contained in:
Timmy Huang
2021-08-04 13:16:18 -07:00
committed by GitHub
parent 95ce39f2e9
commit 26de334274
8 changed files with 157 additions and 82 deletions

View File

@@ -1,4 +1,6 @@
export const schema = {
import { RawSchema } from "../../../../src/common/types/entities";
export const schema: { schema: RawSchema } = {
schema: {
annotations: {
obs: {

View File

@@ -5,6 +5,7 @@ import zip from "lodash.zip";
import _ from "lodash";
import { flatbuffers } from "flatbuffers";
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.
@@ -29,7 +30,7 @@ const aConfigResponse = {
},
};
const aSchemaResponse = {
const aSchemaResponse: { schema: RawSchema } = {
schema: {
dataframe: {
nObs,
@@ -40,28 +41,30 @@ const aSchemaResponse = {
obs: {
index: "name",
columns: [
{ name: "name", type: "string" },
{ name: "field1", type: "int32" },
{ name: "field2", type: "float32" },
{ name: "field3", type: "boolean" },
{ name: "name", type: "string", writable: false },
{ name: "field1", type: "int32", writable: false },
{ name: "field2", type: "float32", writable: false },
{ name: "field3", type: "boolean", writable: false },
{
name: "field4",
type: "categorical",
categories: field4Categories,
writable: false,
},
],
},
var: {
index: "name",
columns: [
{ name: "name", type: "string" },
{ name: "fieldA", type: "int32" },
{ name: "fieldB", type: "float32" },
{ name: "fieldC", type: "boolean" },
{ name: "name", type: "string", writable: false },
{ name: "fieldA", type: "int32", writable: false },
{ name: "fieldB", type: "float32", writable: false },
{ name: "fieldC", type: "boolean", writable: false },
{
name: "fieldD",
type: "categorical",
categories: fieldDCategories,
writable: false,
},
],
},

View File

@@ -12,9 +12,10 @@
"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",
"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",
"test": "jest --testPathIgnorePatterns e2e"
"test": "jest --testPathIgnorePatterns e2e",
"type-check": "tsc --noEmit"
},
"engineStrict": true,
"engines": {

View File

@@ -1 +1,60 @@
// 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;
}

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(arr: any) {
export default function fromEntries<T = unknown>(
arr: [string | number, T][]
): { [key: string]: T } {
/*
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
been released in the Chrome stable channel.
*/
const obj = {};
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
const obj: { [key: string]: T } = {};
for (let i = 0; i < arr.length; i += 1) {
obj[arr[i][0]] = arr[i][1];
}
return obj;
}

View File

@@ -3,6 +3,8 @@ Helper functions for user-editable annotations state management.
See also reducers/annotations.js
*/
import { Schema } from "../../common/types/entities";
/*
There are a number of state constraints assumed throughout the
application:
@@ -16,34 +18,36 @@ application:
In addition, the current state management only allows for
categorical annotations to be writable.
*/
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function isCategoricalAnnotation(schema, name) {
/*
export function isCategoricalAnnotation(
schema: Schema,
name: string
): boolean | undefined {
/*
we treat any string, categorical or boolean as a categorical.
Return true/false/undefined (for unkonwn fields)
*/
const colSchema = schema.annotations.obsByName[name];
if (colSchema === undefined) return undefined;
const { type } = colSchema;
return type === "string" || type === "boolean" || type === "categorical";
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function isContinuousAnnotation(schema, name) {
/*
Return true/false/undefined
*/
export function isContinuousAnnotation(
schema: Schema,
name: string
): boolean | undefined {
const colSchema = schema.annotations.obsByName[name];
if (colSchema === undefined) return undefined;
const { type } = colSchema;
return !(type === "string" || type === "boolean" || type === "categorical");
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
function _isUserAnnotation(schema, name) {
function _isUserAnnotation(schema: Schema, name: string): boolean {
return schema.annotations.obsByName[name]?.writable || false;
}
@@ -72,9 +76,8 @@ export function allHaveLabelByMask(df, colName, label, mask) {
}
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) {
export function annotationNameIsErroneous(name: string): boolean | string {
/*
Validate the name - return:
* 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;
}

View File

@@ -8,6 +8,12 @@ import cloneDeep from "lodash.clonedeep";
import fromEntries from "../fromEntries";
import catLabelSort from "../catLabelSort";
import {
RawSchema,
Schema,
LayoutColumn,
AnnotationColumn,
} from "../../common/types/entities";
/*
System wide schema assumptions:
@@ -15,31 +21,25 @@ System wide schema assumptions:
- 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: any) {
export function indexEntireSchema(schema: RawSchema): Schema {
/* Index schema for ease of use */
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: any) => [v.name, v]) ?? []
(schema as Schema).annotations.obsByName = fromEntries(
schema.annotations?.obs?.columns?.map((v) => [v.name, v]) || []
);
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: any) => [v.name, v]) ?? []
(schema as Schema).annotations.varByName = fromEntries(
schema.annotations?.var?.columns?.map((v) => [v.name, v]) || []
);
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: any) => [v.name, v]) ?? []
(schema as Schema).layout.obsByName = fromEntries(
schema.layout?.obs?.map((v) => [v.name, v]) || []
);
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: any) => [v.name, v]) ?? []
(schema as Schema).layout.varByName = fromEntries(
schema.layout?.var?.map((v) => [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: any) {
function _copyObsAnno(schema: Schema): Schema {
/* redux copy conventions - WARNING, only for modifying obs annotations */
return {
...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: any) {
function _copyObsLayout(schema: Schema): Schema {
return {
...schema,
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: any) {
function _reindexObsAnno(schema: Schema): Schema {
/* reindex obs annotations ONLY */
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: any) => [v.name, v])
schema.annotations.obs.columns.map((v) => [v.name, v])
);
return schema;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _reindexObsLayout(schema: any) {
function _reindexObsLayout(schema: Schema) {
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: any) => [v.name, v])
schema.layout.obs.map((v) => [v.name, v])
);
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: any, name: any) {
export function removeObsAnnoColumn(schema: Schema, name: string): Schema {
const newSchema = _copyObsAnno(schema);
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: any) => v.name !== name
(v) => v.name !== name
);
return _reindexObsAnno(newSchema);
}
// @ts-expect-error ts-migrate(6133) FIXME: 'name' is declared but its value is never read.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function addObsAnnoColumn(schema: any, name: any, defn: any) {
export function addObsAnnoColumn(
schema: Schema,
_: string,
defn: AnnotationColumn
): Schema {
const newSchema = _copyObsAnno(schema);
newSchema.annotations.obs.columns.push(defn);
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(schema: any, name: any, category: any) {
export function removeObsAnnoCategory(
schema: Schema,
name: string,
category: string
): Schema {
/* remove a category from a categorical annotation */
const categories = schema.annotations.obsByName[name]?.categories;
if (!categories)
if (!categories) {
throw new Error("column does not exist or is not categorical");
}
const idx = categories.indexOf(category);
if (idx === -1) throw new Error("category does not exist");
const newSchema = _reindexObsAnno(_copyObsAnno(schema));
/* 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;
}
@@ -129,26 +133,27 @@ export function addObsAnnoCategory(schema: any, name: any, category: any) {
/* add category, retaining presentation sort order */
const catAnno = newSchema.annotations.obsByName[name];
catAnno.categories = catLabelSort(catAnno.writable, [
...catAnno.categories,
...(catAnno.categories || []),
category,
]);
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: any, layout: any) {
export function addObsLayout(schema: Schema, layout: LayoutColumn): Schema {
/* add or replace a layout */
const newSchema = _copyObsLayout(schema);
newSchema.layout.obs.push(layout);
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: any, name: any) {
export function removeObsLayout(schema: Schema, name: string): Schema {
/* remove a layout */
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);
}

View File

@@ -31,5 +31,5 @@
"__tests__/**/*",
"jest-puppeteer.config.js"
],
"exclude": ["__tests__/e2e/__snapshots__/**/*"]
"exclude": ["node_modules", "__tests__/e2e/__snapshots__/**/*"]
}