mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-11 02:00:55 +08:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76a39fcf92 | ||
|
|
5eb3021752 | ||
|
|
7c7203bdfe | ||
|
|
5ac256fc8b | ||
|
|
2b86f8e600 | ||
|
|
0fddcd042a | ||
|
|
eb1dc8944f |
@@ -69,10 +69,21 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: make pydist install-dist dev-env-server
|
run: make pydist install-dist dev-env-server
|
||||||
- name: Unit tests
|
- name: Unit tests
|
||||||
run: |
|
run: make unit-test-server unit-test-client
|
||||||
make unit-test-server unit-test-client
|
- name: Generate server coverage XML
|
||||||
bash <(curl -s https://codecov.io/bash) -y .codecov.yml -k server -cF server,python,unitTest
|
run: coverage xml -o server/coverage.xml
|
||||||
cd client && ./node_modules/codecov/bin/codecov --yml=../.codecov.yml --root=../ --gcov-root=../ -C -F frontend,javascript,unitTest
|
- name: Upload server coverage
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
flags: server,python,unitTest
|
||||||
|
files: ./server/coverage.xml
|
||||||
|
fail_ci_if_error: false
|
||||||
|
- name: Upload client coverage
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
flags: frontend,javascript,unitTest
|
||||||
|
files: ./client/coverage/lcov.info
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
smoke-tests:
|
smoke-tests:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
@@ -102,9 +113,7 @@ jobs:
|
|||||||
pip install setuptools
|
pip install setuptools
|
||||||
make pydist install-dist
|
make pydist install-dist
|
||||||
- name: Smoke tests (without annotations feature)
|
- name: Smoke tests (without annotations feature)
|
||||||
run: |
|
run: cd client && make smoke-test
|
||||||
cd client && make smoke-test
|
|
||||||
./node_modules/codecov/bin/codecov --yml=../.codecov.yml --root=../ --gcov-root=../ -C -F frontend,javascript,smokeTest
|
|
||||||
|
|
||||||
# TODO: reinstate: https://github.com/chanzuckerberg/cellxgene/issues/2544
|
# TODO: reinstate: https://github.com/chanzuckerberg/cellxgene/issues/2544
|
||||||
# smoke-tests-annotations:
|
# smoke-tests-annotations:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2017-2023 Chan Zuckerberg Initiative
|
Copyright (c) 2017-2026 Chan Zuckerberg Initiative
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
@@ -82,6 +82,13 @@ extension could be directly contributed, which would make it available for a wid
|
|||||||
|
|
||||||
See the [CELLxGENE extensions](https://github.com/chanzuckerberg/cellxgene-documentation/blob/main/community-extensions.md) section of our documentation for examples of community use and CELLxGENE extensions.
|
See the [CELLxGENE extensions](https://github.com/chanzuckerberg/cellxgene-documentation/blob/main/community-extensions.md) section of our documentation for examples of community use and CELLxGENE extensions.
|
||||||
|
|
||||||
|
### Trademarks
|
||||||
|
|
||||||
|
CZ CELLXGENE, CZ CELLXGENE DISCOVER, and CZ CELLXGENE ANNOTATE are trademarks of the Chan Zuckerberg Initiative. All rights reserved.
|
||||||
|
|
||||||
|
Use, reuse, modification, and re-distribution of the source code in this repository is subject to the terms of the applicable open source [license](LICENSE.txt). However, that license does not grant permission to use the trademarks without separate, express permission from the Chan Zuckerberg Initiative.
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
If you believe you have found a security issue, we would appreciate notification. Please send email to <security@chanzuckerberg.com>.
|
If you believe you have found a security issue, we would appreciate notification. Please send email to <security@chanzuckerberg.com>.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Reporting Security Issues
|
||||||
|
|
||||||
|
If you believe you have found a security issue, please responsibly disclose by contacting us at [security@chanzuckerberg.com](mailto:security@chanzuckerberg.com).
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as ENV_DEFAULT from "Code/cellxgene/environment.default.json";
|
import * as ENV_DEFAULT from "../../../environment.default.json";
|
||||||
|
|
||||||
export const jestEnv = process.env.JEST_ENV || ENV_DEFAULT.JEST_ENV;
|
export const jestEnv = process.env.JEST_ENV || ENV_DEFAULT.JEST_ENV;
|
||||||
export const appUrlBase =
|
export const appUrlBase =
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { setDefaultOptions } from "expect-puppeteer";
|
import { setDefaultOptions } from "expect-puppeteer";
|
||||||
import * as ENV_DEFAULT from "Code/cellxgene/environment.default.json";
|
|
||||||
import { isDebug, isDev } from "./config";
|
import { isDebug, isDev } from "./config";
|
||||||
|
import * as ENV_DEFAULT from "../../../environment.default.json";
|
||||||
|
|
||||||
// (thuang): This is the max time a test can take to run.
|
// (thuang): This is the max time a test can take to run.
|
||||||
// Since when debugging, we run slowMo and !headless, this means
|
// Since when debugging, we run slowMo and !headless, this means
|
||||||
|
|||||||
@@ -82,14 +82,13 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
|
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
|
||||||
// "extends": ["plugin:jest/recommended"]
|
// "extends": ["plugin:jest/recommended"]
|
||||||
plugins: ["jest", "no-floating-promise"],
|
plugins: ["jest"],
|
||||||
rules: {
|
rules: {
|
||||||
"jest/no-disabled-tests": "warn",
|
"jest/no-disabled-tests": "warn",
|
||||||
"jest/no-focused-tests": "error",
|
"jest/no-focused-tests": "error",
|
||||||
"jest/no-identical-title": "error",
|
"jest/no-identical-title": "error",
|
||||||
"jest/prefer-to-have-length": "warn",
|
"jest/prefer-to-have-length": "warn",
|
||||||
"jest/valid-expect": "error",
|
"jest/valid-expect": "error",
|
||||||
"no-floating-promise/no-floating-promise": 2,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Generated
+632
-953
File diff suppressed because it is too large
Load Diff
+4
-6
@@ -92,7 +92,7 @@
|
|||||||
"@babel/preset-react": "^7.13.13",
|
"@babel/preset-react": "^7.13.13",
|
||||||
"@babel/register": "^7.13.16",
|
"@babel/register": "^7.13.16",
|
||||||
"@babel/runtime": "^7.13.16",
|
"@babel/runtime": "^7.13.16",
|
||||||
"@blueprintjs/eslint-plugin": "^6.1.4",
|
"@blueprintjs/eslint-plugin": "^0.3.0",
|
||||||
"@sentry/webpack-plugin": "^1.15.0",
|
"@sentry/webpack-plugin": "^1.15.0",
|
||||||
"babel-jest": "^26.1.0",
|
"babel-jest": "^26.1.0",
|
||||||
"babel-loader": "^8.1.0",
|
"babel-loader": "^8.1.0",
|
||||||
@@ -101,19 +101,17 @@
|
|||||||
"cheerio": "^1.0.0-rc.6",
|
"cheerio": "^1.0.0-rc.6",
|
||||||
"clean-css": "^5.1.2",
|
"clean-css": "^5.1.2",
|
||||||
"clean-webpack-plugin": "^4.0.0-alpha.0",
|
"clean-webpack-plugin": "^4.0.0-alpha.0",
|
||||||
"codecov": "^3.7.1",
|
|
||||||
"css-loader": "^5.2.4",
|
"css-loader": "^5.2.4",
|
||||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^7.24.0",
|
||||||
"eslint-config-airbnb": "^19.0.4",
|
"eslint-config-airbnb": "^18.2.0",
|
||||||
"eslint-config-prettier": "^8.2.0",
|
"eslint-config-prettier": "^8.2.0",
|
||||||
"eslint-plugin-compat": "^4.2.0",
|
"eslint-plugin-compat": "^4.2.0",
|
||||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||||
"eslint-plugin-filenames": "^1.3.2",
|
"eslint-plugin-filenames": "^1.3.2",
|
||||||
"eslint-plugin-import": "^2.24.2",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"eslint-plugin-jest": "^28.8.3",
|
"eslint-plugin-jest": "^24.3.5",
|
||||||
"eslint-plugin-jsx-a11y": "^6.3.1",
|
"eslint-plugin-jsx-a11y": "^6.3.1",
|
||||||
"eslint-plugin-no-floating-promise": "^2.0.0",
|
|
||||||
"eslint-plugin-react": "^7.23.2",
|
"eslint-plugin-react": "^7.23.2",
|
||||||
"eslint-plugin-react-hooks": "^4.0.8",
|
"eslint-plugin-react-hooks": "^4.0.8",
|
||||||
"expect-puppeteer": "^5.0.0",
|
"expect-puppeteer": "^5.0.0",
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
|
|
||||||
function ErrorLoading({ displayName, zebra }) {
|
const ErrorLoading = ({ displayName, zebra }) => (
|
||||||
return <div
|
<div
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: zebra ? globals.lightestGrey : "white",
|
backgroundColor: zebra ? globals.lightestGrey : "white",
|
||||||
fontStyle: "italic",
|
fontStyle: "italic",
|
||||||
@@ -10,6 +10,6 @@ function ErrorLoading({ displayName, zebra }) {
|
|||||||
>
|
>
|
||||||
<span>{`Failure loading ${displayName}`}</span>
|
<span>{`Failure loading ${displayName}`}</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
);
|
||||||
|
|
||||||
export default ErrorLoading;
|
export default ErrorLoading;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as d3 from "d3";
|
|||||||
import maybeScientific from "../../util/maybeScientific";
|
import maybeScientific from "../../util/maybeScientific";
|
||||||
import clamp from "../../util/clamp";
|
import clamp from "../../util/clamp";
|
||||||
|
|
||||||
function Histogram({
|
const Histogram = ({
|
||||||
field,
|
field,
|
||||||
fieldForId,
|
fieldForId,
|
||||||
display,
|
display,
|
||||||
@@ -18,7 +18,7 @@ function Histogram({
|
|||||||
isColorBy,
|
isColorBy,
|
||||||
selectionRange,
|
selectionRange,
|
||||||
mini,
|
mini,
|
||||||
}) {
|
}) => {
|
||||||
const svgRef = useRef(null);
|
const svgRef = useRef(null);
|
||||||
const [brush, setBrush] = useState(null);
|
const [brush, setBrush] = useState(null);
|
||||||
|
|
||||||
@@ -186,6 +186,6 @@ function Histogram({
|
|||||||
ref={svgRef}
|
ref={svgRef}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Histogram;
|
export default Histogram;
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ import { Button } from "@blueprintjs/core";
|
|||||||
|
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
|
|
||||||
function StillLoading({ zebra, displayName }) {
|
const StillLoading = ({ zebra, displayName }) =>
|
||||||
return <div
|
/*
|
||||||
|
Render a loading indicator for the field.
|
||||||
|
*/
|
||||||
|
(
|
||||||
|
<div
|
||||||
data-testclass="gene-loading-spinner"
|
data-testclass="gene-loading-spinner"
|
||||||
style={{
|
style={{
|
||||||
padding: globals.leftSidebarSectionPadding,
|
padding: globals.leftSidebarSectionPadding,
|
||||||
@@ -33,6 +37,7 @@ function StillLoading({ zebra, displayName }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)
|
||||||
|
;
|
||||||
|
|
||||||
export default StillLoading;
|
export default StillLoading;
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class Category extends React.PureComponent {
|
|||||||
const { metadataField, annotations, obsCrossfilter } = this.props;
|
const { metadataField, annotations, obsCrossfilter } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AnnoDialog
|
<AnnoDialog
|
||||||
isActive={
|
isActive={
|
||||||
annotations.isAddingNewLabel &&
|
annotations.isAddingNewLabel &&
|
||||||
@@ -104,6 +105,7 @@ class Category extends React.PureComponent {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
|||||||
const { metadataField, annotations } = this.props;
|
const { metadataField, annotations } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AnnoDialog
|
<AnnoDialog
|
||||||
isActive={
|
isActive={
|
||||||
annotations.isEditingCategoryName &&
|
annotations.isEditingCategoryName &&
|
||||||
@@ -140,6 +141,7 @@ class AnnoDialogEditCategoryName extends React.PureComponent {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,8 +288,11 @@ class Category extends React.PureComponent {
|
|||||||
|
|
||||||
export default Category;
|
export default Category;
|
||||||
|
|
||||||
function StillLoading({ metadataField, checkboxID }) {
|
const StillLoading = ({ metadataField, checkboxID }) => (
|
||||||
return <div
|
/*
|
||||||
|
We are still loading this category, so render a "busy" signal.
|
||||||
|
*/
|
||||||
|
<div
|
||||||
style={{
|
style={{
|
||||||
maxWidth: globals.maxControlsWidth,
|
maxWidth: globals.maxControlsWidth,
|
||||||
}}
|
}}
|
||||||
@@ -332,8 +335,8 @@ function StillLoading({ metadataField, checkboxID }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
);
|
||||||
function ErrorLoading({ metadataField, error }) {
|
const ErrorLoading = ({ metadataField, error }) => {
|
||||||
console.error(error); // log error to console as it is unexpected.
|
console.error(error); // log error to console as it is unexpected.
|
||||||
return (
|
return (
|
||||||
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
<div style={{ marginBottom: 10, marginTop: 4 }}>
|
||||||
@@ -349,7 +352,7 @@ function ErrorLoading({ metadataField, error }) {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const CategoryHeader = React.memo(
|
const CategoryHeader = React.memo(
|
||||||
({
|
({
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ class CategoryValue extends React.Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate = (nextProps, nextState) => {
|
||||||
/*
|
/*
|
||||||
Checks to see if at least one of the following changed:
|
Checks to see if at least one of the following changed:
|
||||||
* world state
|
* world state
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ const loadAllEmbeddingCounts = async ({ annoMatrix, available }) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
function EmbeddingChoices({ onChange, annoMatrix, layoutChoice }) {
|
const EmbeddingChoices = ({ onChange, annoMatrix, layoutChoice }) => {
|
||||||
const { available } = layoutChoice;
|
const { available } = layoutChoice;
|
||||||
const { data, error, isPending } = useAsync({
|
const { data, error, isPending } = useAsync({
|
||||||
promiseFn: loadAllEmbeddingCounts,
|
promiseFn: loadAllEmbeddingCounts,
|
||||||
@@ -149,4 +149,4 @@ function EmbeddingChoices({ onChange, annoMatrix, layoutChoice }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import icon from "../../images/icon.png";
|
import icon from "../../images/icon.png";
|
||||||
|
|
||||||
function Logo(props) {
|
const Logo = (props) => {
|
||||||
const { size } = props;
|
const { size } = props;
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
@@ -11,6 +11,6 @@ function Logo(props) {
|
|||||||
alt="CELLxGENE Annotate Logo"
|
alt="CELLxGENE Annotate Logo"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Logo;
|
export default Logo;
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class AddGeneToGenesetDialogue extends React.PureComponent {
|
|||||||
const { genesToAdd } = this.state;
|
const { genesToAdd } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AnnoDialog
|
<AnnoDialog
|
||||||
isActive={genesetsUI.isAddingGenesToGeneset === geneset}
|
isActive={genesetsUI.isAddingGenesToGeneset === geneset}
|
||||||
inputProps={{ "data-testid": `${geneset}:create-label-dialog` }}
|
inputProps={{ "data-testid": `${geneset}:create-label-dialog` }}
|
||||||
@@ -80,6 +81,7 @@ class AddGeneToGenesetDialogue extends React.PureComponent {
|
|||||||
handleSubmit={this.handleAddGeneToGeneSet}
|
handleSubmit={this.handleAddGeneToGeneSet}
|
||||||
handleCancel={this.disableAddGeneMode}
|
handleCancel={this.disableAddGeneMode}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ class CreateGenesetDialogue extends React.PureComponent {
|
|||||||
const { genesetsUI, genesets } = this.props;
|
const { genesetsUI, genesets } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Dialog
|
<Dialog
|
||||||
icon="tag"
|
icon="tag"
|
||||||
title="Create gene set"
|
title="Create gene set"
|
||||||
@@ -209,6 +210,7 @@ class CreateGenesetDialogue extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class RenameGeneset extends React.PureComponent {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<AnnoDialog
|
<AnnoDialog
|
||||||
isActive={genesetsUI.isEditingGenesetName === originalGenesetName}
|
isActive={genesetsUI.isEditingGenesetName === originalGenesetName}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
@@ -160,6 +161,7 @@ class RenameGeneset extends React.PureComponent {
|
|||||||
handleSubmit={this.renameGeneset}
|
handleSubmit={this.renameGeneset}
|
||||||
handleCancel={this.disableEditGenesetNameMode}
|
handleCancel={this.disableEditGenesetNameMode}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -935,7 +935,7 @@ class Graph extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ErrorLoading({ displayName, error, width, height }) {
|
const ErrorLoading = ({ displayName, error, width, height }) => {
|
||||||
console.log(error); // log to console as this is an unepected error
|
console.log(error); // log to console as this is an unepected error
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -949,10 +949,14 @@ function ErrorLoading({ displayName, error, width, height }) {
|
|||||||
<span>{`Failure loading ${displayName}`}</span>
|
<span>{`Failure loading ${displayName}`}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function StillLoading({ displayName, width, height }) {
|
const StillLoading = ({ displayName, width, height }) =>
|
||||||
return <div
|
/*
|
||||||
|
Render a busy/loading indicator
|
||||||
|
*/
|
||||||
|
(
|
||||||
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
@@ -972,6 +976,7 @@ function StillLoading({ displayName, width, height }) {
|
|||||||
<span style={{ fontStyle: "italic" }}>Loading {displayName}</span>
|
<span style={{ fontStyle: "italic" }}>Loading {displayName}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
)
|
||||||
|
;
|
||||||
|
|
||||||
export default Graph;
|
export default Graph;
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class CentroidLabels extends PureComponent {
|
|||||||
dilatedValue={dilatedValue}
|
dilatedValue={dilatedValue}
|
||||||
coords={coords}
|
coords={coords}
|
||||||
inverseTransform={inverseTransform}
|
inverseTransform={inverseTransform}
|
||||||
opactity={selected ? 1 : deselectOpacity}
|
opacity={selected ? 1 : deselectOpacity}
|
||||||
colorAccessor={colorAccessor}
|
colorAccessor={colorAccessor}
|
||||||
displayLabel={displayLabel}
|
displayLabel={displayLabel}
|
||||||
onMouseEnter={this.handleMouseEnter}
|
onMouseEnter={this.handleMouseEnter}
|
||||||
@@ -167,7 +167,7 @@ class CentroidLabels extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Label({
|
const Label = ({
|
||||||
label,
|
label,
|
||||||
dilatedValue,
|
dilatedValue,
|
||||||
coords,
|
coords,
|
||||||
@@ -177,7 +177,7 @@ function Label({
|
|||||||
displayLabel,
|
displayLabel,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseOut,
|
onMouseOut,
|
||||||
}) {
|
}) => {
|
||||||
/*
|
/*
|
||||||
Render a label at a given coordinate.
|
Render a label at a given coordinate.
|
||||||
*/
|
*/
|
||||||
@@ -205,7 +205,7 @@ function Label({
|
|||||||
fontWeight,
|
fontWeight,
|
||||||
fill: "black",
|
fill: "black",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
opacity: { opacity },
|
opacity,
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => onMouseEnter(e, colorAccessor, label)}
|
onMouseEnter={(e) => onMouseEnter(e, colorAccessor, label)}
|
||||||
onMouseOut={(e) => onMouseOut(e, colorAccessor, label)}
|
onMouseOut={(e) => onMouseOut(e, colorAccessor, label)}
|
||||||
@@ -215,4 +215,4 @@ function Label({
|
|||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export default class LabelInput extends React.PureComponent {
|
|||||||
};
|
};
|
||||||
const { queryResults } = this.state;
|
const { queryResults } = this.state;
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Suggest
|
<Suggest
|
||||||
fill
|
fill
|
||||||
inputValueRenderer={(i) => i.target}
|
inputValueRenderer={(i) => i.target}
|
||||||
@@ -171,6 +172,7 @@ export default class LabelInput extends React.PureComponent {
|
|||||||
inputProps={inputProps}
|
inputProps={inputProps}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ export default class MiniHistogram extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount = () => {
|
||||||
this.drawHistogram();
|
this.drawHistogram();
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate = (prevProps) => {
|
||||||
const { obsOrVarContinuousFieldDisplayName, bins } = this.props;
|
const { obsOrVarContinuousFieldDisplayName, bins } = this.props;
|
||||||
if (
|
if (
|
||||||
prevProps.obsOrVarContinuousFieldDisplayName !==
|
prevProps.obsOrVarContinuousFieldDisplayName !==
|
||||||
|
|||||||
@@ -42,12 +42,12 @@ export default class MiniStackedBar extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate = (prevProps) => {
|
||||||
const { occupancy } = this.props;
|
const { occupancy } = this.props;
|
||||||
if (occupancy !== prevProps.occupancy) this.drawStacks();
|
if (occupancy !== prevProps.occupancy) this.drawStacks();
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount = () => {
|
||||||
this.drawStacks();
|
this.drawStacks();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const SECOND_HALF_INNER_STYLE = {
|
|||||||
right: 0,
|
right: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(props) {
|
export default (props) => {
|
||||||
const { children, isGenesetDescription, tooltipAddendum = "" } = props;
|
const { children, isGenesetDescription, tooltipAddendum = "" } = props;
|
||||||
// Truncate only support a single child with a text child
|
// Truncate only support a single child with a text child
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Colors } from "@blueprintjs/core";
|
import { Colors } from "@blueprintjs/core";
|
||||||
import ENV_DEFAULT from "Code/cellxgene/environment.default.json";
|
|
||||||
import { dispatchNetworkErrorMessageToUser } from "./util/actionHelpers";
|
import { dispatchNetworkErrorMessageToUser } from "./util/actionHelpers";
|
||||||
|
import ENV_DEFAULT from "../../environment.default.json";
|
||||||
|
|
||||||
/* overflow category values are created using this string */
|
/* overflow category values are created using this string */
|
||||||
export const overflowCategoryLabel = ": all other labels";
|
export const overflowCategoryLabel = ": all other labels";
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ def prepare(
|
|||||||
raise click.FileError(data, hint="not a valid file or path")
|
raise click.FileError(data, hint="not a valid file or path")
|
||||||
|
|
||||||
if not set_obs_names == "":
|
if not set_obs_names == "":
|
||||||
if set_obs_names not in adata.obs_keys():
|
if set_obs_names not in list(adata.obs.keys()):
|
||||||
raise click.UsageError(f"obs {set_obs_names} not found, options are: {adata.obs_keys()}")
|
raise click.UsageError(f"obs {set_obs_names} not found, options are: {list(adata.obs.keys())}")
|
||||||
adata.obs_names = adata.obs[set_obs_names]
|
adata.obs_names = adata.obs[set_obs_names]
|
||||||
if not set_var_names == "":
|
if not set_var_names == "":
|
||||||
if set_var_names not in adata.var_keys():
|
if set_var_names not in list(adata.var.keys()):
|
||||||
raise click.UsageError(f"var {set_var_names} not found, options are: {adata.var_keys()}")
|
raise click.UsageError(f"var {set_var_names} not found, options are: {list(adata.var.keys())}")
|
||||||
adata.var_names = adata.var[set_var_names]
|
adata.var_names = adata.var[set_var_names]
|
||||||
if make_obs_names_unique:
|
if make_obs_names_unique:
|
||||||
adata.obs.index = make_index_unique(adata.obs.index)
|
adata.obs.index = make_index_unique(adata.obs.index)
|
||||||
|
|||||||
@@ -228,6 +228,6 @@ def convert_anndata_category_colors_to_cxg_category_colors(data):
|
|||||||
|
|
||||||
# create the cellxgene color entry for this category
|
# create the cellxgene color entry for this category
|
||||||
cxg_colors[category_name] = dict(
|
cxg_colors[category_name] = dict(
|
||||||
zip(data.obs[category_name].cat.categories, [convert_color_to_hex_format(c) for c in data.uns[uns_key]])
|
zip(data.obs[category_name].astype('category').cat.categories, [convert_color_to_hex_format(c) for c in data.uns[uns_key]])
|
||||||
)
|
)
|
||||||
return cxg_colors
|
return cxg_colors
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ def corpora_get_versions_from_anndata(adata):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# per Corpora AnnData spec, this is a corpora file if the following is true
|
# per Corpora AnnData spec, this is a corpora file if the following is true
|
||||||
if "version" not in adata.uns_keys():
|
if "version" not in list(adata.uns.keys()):
|
||||||
return None
|
return None
|
||||||
version = adata.uns["version"]
|
version = adata.uns["version"]
|
||||||
if not isinstance(version, collections.abc.Mapping) or "corpora_schema_version" not in version:
|
if not isinstance(version, collections.abc.Mapping) or "corpora_schema_version" not in version:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import warnings
|
import warnings
|
||||||
|
import importlib.metadata
|
||||||
|
|
||||||
import anndata
|
import anndata
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -16,7 +17,7 @@ from server.common.utils.type_conversion_utils import get_schema_type_hint_of_ar
|
|||||||
from server.data_common.data_adaptor import DataAdaptor
|
from server.data_common.data_adaptor import DataAdaptor
|
||||||
from server.common.fbs.matrix import encode_matrix_fbs
|
from server.common.fbs.matrix import encode_matrix_fbs
|
||||||
|
|
||||||
anndata_version = version.parse(str(anndata.__version__)).release
|
anndata_version = version.parse(str(importlib.metadata.version('anndata'))).release
|
||||||
|
|
||||||
|
|
||||||
def anndata_version_is_pre_070():
|
def anndata_version_is_pre_070():
|
||||||
@@ -63,7 +64,7 @@ class AnndataAdaptor(DataAdaptor):
|
|||||||
return "cellxgene anndata adaptor version"
|
return "cellxgene anndata adaptor version"
|
||||||
|
|
||||||
def get_library_versions(self):
|
def get_library_versions(self):
|
||||||
return dict(anndata=str(anndata.__version__))
|
return dict(anndata=str(importlib.metadata.version('anndata')))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_unique_column_name(df, col_name_prefix):
|
def _create_unique_column_name(df, col_name_prefix):
|
||||||
@@ -173,13 +174,24 @@ class AnndataAdaptor(DataAdaptor):
|
|||||||
)
|
)
|
||||||
except MemoryError:
|
except MemoryError:
|
||||||
raise DatasetAccessError("Out of memory - file is too large for available memory.")
|
raise DatasetAccessError("Out of memory - file is too large for available memory.")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
|
error_msg = str(e)
|
||||||
|
|
||||||
|
# IMPROVEMENT: Broadly catch ANY version incompatibility
|
||||||
|
if "No read method registered" in error_msg and "IOSpec" in error_msg:
|
||||||
|
message = (
|
||||||
|
"Error loading file: This H5AD file uses a newer internal format that "
|
||||||
|
"your version of 'anndata' cannot read.\n"
|
||||||
|
f"The specific error was: {error_msg}\n"
|
||||||
|
"Please upgrade anndata in your environment (pip install --upgrade anndata)."
|
||||||
|
)
|
||||||
|
else:
|
||||||
message = (
|
message = (
|
||||||
"File not found or is inaccessible. File must be an .h5ad object. "
|
"File not found or is inaccessible. File must be an .h5ad object. "
|
||||||
"Please check your input and try again."
|
"Please check your input and try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.server_config.app__verbose:
|
if self.server_config.app__verbose:
|
||||||
message += f"\n{traceback.format_exc()}"
|
message += f"\n{traceback.format_exc()}"
|
||||||
raise DatasetAccessError(message)
|
raise DatasetAccessError(message)
|
||||||
@@ -302,11 +314,11 @@ class AnndataAdaptor(DataAdaptor):
|
|||||||
layouts = self.dataset_config.embeddings__names
|
layouts = self.dataset_config.embeddings__names
|
||||||
|
|
||||||
if layouts is None or len(layouts) == 0:
|
if layouts is None or len(layouts) == 0:
|
||||||
layouts = [key[2:] for key in self.data.obsm_keys() if type(key) is str and key.startswith("X_")]
|
layouts = [key[2:] for key in list(self.data.obsm.keys()) if type(key) is str and key.startswith("X_")]
|
||||||
|
|
||||||
# remove invalid layouts
|
# remove invalid layouts
|
||||||
valid_layouts = []
|
valid_layouts = []
|
||||||
obsm_keys = self.data.obsm_keys()
|
obsm_keys = list(self.data.obsm.keys())
|
||||||
for layout in layouts:
|
for layout in layouts:
|
||||||
layout_name = f"X_{layout}"
|
layout_name = f"X_{layout}"
|
||||||
if layout_name not in obsm_keys:
|
if layout_name not in obsm_keys:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
black
|
black
|
||||||
bumpversion>=0.5
|
bumpversion>=0.5
|
||||||
codecov>=2.0.15
|
coverage>=5.0
|
||||||
parameterized>=0.7.0
|
parameterized>=0.7.0
|
||||||
pytest>=3.6.3
|
pytest>=3.6.3
|
||||||
python-jose>=3.2.0
|
python-jose>=3.2.0
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ Test the anndata adaptor using the pbmc3k data set.
|
|||||||
(f"{FIXTURES_ROOT}/pbmc3k-CSC-gz.h5ad", True, "normal"),
|
(f"{FIXTURES_ROOT}/pbmc3k-CSC-gz.h5ad", True, "normal"),
|
||||||
(f"{FIXTURES_ROOT}/pbmc3k-CSR-gz.h5ad", True, "normal"),
|
(f"{FIXTURES_ROOT}/pbmc3k-CSR-gz.h5ad", True, "normal"),
|
||||||
(f"{FIXTURES_ROOT}/pbmc3k_64.h5ad", False, "auto"), # 64 bit conversion tests
|
(f"{FIXTURES_ROOT}/pbmc3k_64.h5ad", False, "auto"), # 64 bit conversion tests
|
||||||
(f"{FIXTURES_ROOT}/pbmc3k_16.h5ad", False, "auto"), # 16 bit conversion tests
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class AdaptorTest(unittest.TestCase):
|
class AdaptorTest(unittest.TestCase):
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
from parameterized import parameterized_class
|
|
||||||
|
|
||||||
from server.common.errors import DatasetAccessError
|
|
||||||
from test import FIXTURES_ROOT
|
|
||||||
from test.unit import app_config
|
|
||||||
|
|
||||||
|
|
||||||
@parameterized_class(
|
|
||||||
("data_locator", "backed", "X_approximate_distribution"),
|
|
||||||
[
|
|
||||||
(f"{FIXTURES_ROOT}/pbmc3k_16.h5ad", True, "auto"), # 16 bit conversion tests
|
|
||||||
],
|
|
||||||
)
|
|
||||||
class AdaptorLoadErrorTest(unittest.TestCase):
|
|
||||||
def test_float16_backed_raises_err(self):
|
|
||||||
with self.assertRaises(DatasetAccessError):
|
|
||||||
config = app_config(
|
|
||||||
self.data_locator,
|
|
||||||
backed=self.backed,
|
|
||||||
extra_dataset_config=dict(X_approximate_distribution=self.X_approximate_distribution),
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user