mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-15 12:47:56 +08:00
chore: add schema types (#2369)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
export const schema = {
|
||||
import { RawSchema } from "../../../../src/common/types/entities";
|
||||
|
||||
export const schema: { schema: RawSchema } = {
|
||||
schema: {
|
||||
annotations: {
|
||||
obs: {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -31,5 +31,5 @@
|
||||
"__tests__/**/*",
|
||||
"jest-puppeteer.config.js"
|
||||
],
|
||||
"exclude": ["__tests__/e2e/__snapshots__/**/*"]
|
||||
"exclude": ["node_modules", "__tests__/e2e/__snapshots__/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user