diff --git a/client/__tests__/e2e/config.js b/client/__tests__/e2e/config.js
index d4748edd..80078f8b 100644
--- a/client/__tests__/e2e/config.js
+++ b/client/__tests__/e2e/config.js
@@ -1,9 +1,9 @@
-export const jest_env = process.env.JEST_ENV;
+export const jestEnv = process.env.JEST_ENV;
export const appPort = process.env.CXG_SERVER_PORT;
export const appUrlBase =
process.env.CXG_URL_BASE || `http://localhost:${appPort}`;
-export const DEV = jest_env === "dev";
-export const DEBUG = jest_env === "debug";
+export const DEV = jestEnv === "dev";
+export const DEBUG = jestEnv === "debug";
export const DATASET = "pbmc3k";
if (DEBUG) jest.setTimeout(100000);
diff --git a/client/__tests__/e2e/e2e.test.js b/client/__tests__/e2e/e2e.test.js
index 1710da9c..76e1b4d0 100644
--- a/client/__tests__/e2e/e2e.test.js
+++ b/client/__tests__/e2e/e2e.test.js
@@ -7,7 +7,10 @@ import { appUrlBase, DATASET } from "./config";
import { setupTestBrowser } from "./testBrowser";
import { datasets } from "./data";
-let browser, page, utils, cxgActions;
+let browser;
+let page;
+let utils;
+let cxgActions;
const data = datasets[DATASET];
beforeAll(async () => {
@@ -29,6 +32,17 @@ describe("did launch", () => {
);
expect(element).toBe(data.title);
});
+
+ test("terms of service, if they are there", async () => {
+ try {
+ await utils.clickOn("tos-cookies-accept", { timeout: 500 });
+ } catch {
+ console.warn("No terms of service footer detected.");
+ }
+ page.waitFor(50); // give the footer a chance to disappear
+ const result = await page.$("[data-testid='tos-cookies-accept']");
+ expect(result).toBeNull();
+ });
});
describe("metadata loads", () => {
@@ -119,7 +133,7 @@ describe("gene entry", () => {
testGenes
);
expect(allHistograms).toEqual(expect.arrayContaining(testGenes));
- expect(allHistograms.length).toEqual(testGenes.length);
+ expect(allHistograms).toHaveLength(testGenes.length);
});
});
@@ -133,7 +147,7 @@ describe("differential expression", () => {
expect(allHistograms).toEqual(
expect.arrayContaining(data.diffexp["gene-results"])
);
- expect(allHistograms.length).toEqual(data.diffexp["gene-results"].length);
+ expect(allHistograms).toHaveLength(data.diffexp["gene-results"].length);
});
});
diff --git a/client/__tests__/e2e/e2eAnnotations.test.js b/client/__tests__/e2e/e2eAnnotations.test.js
index 6da0c42e..f0041529 100644
--- a/client/__tests__/e2e/e2eAnnotations.test.js
+++ b/client/__tests__/e2e/e2eAnnotations.test.js
@@ -5,7 +5,10 @@ import { appUrlBase, DATASET } from "./config";
import { setupTestBrowser } from "./testBrowser";
import { datasets } from "./data";
-let browser, page, utils, actions;
+let browser;
+let page;
+let utils;
+let actions;
const data = datasets[DATASET];
beforeAll(async () => {
diff --git a/client/__tests__/e2e/feature.test.js b/client/__tests__/e2e/feature.test.js
index 0fed000a..9e7adc11 100644
--- a/client/__tests__/e2e/feature.test.js
+++ b/client/__tests__/e2e/feature.test.js
@@ -12,9 +12,13 @@ import { appUrlBase, DEBUG, DEV, DATASET } from "./config";
import { puppeteerUtils, cellxgeneActions } from "./puppeteerUtils";
import { datasets } from "./data";
-let browser, page, utils, cxgActions, spy;
+let browser;
+let page;
+let utils;
+let cxgActions;
+let spy;
const browserViewport = { width: 1280, height: 960 };
-let data = datasets[DATASET].features;
+const data = datasets[DATASET].features;
if (DEBUG) jest.setTimeout(100000);
if (DEV) jest.setTimeout(10000);
diff --git a/client/__tests__/e2e/puppeteerUtils.js b/client/__tests__/e2e/puppeteerUtils.js
index c72a865b..e7374f8b 100644
--- a/client/__tests__/e2e/puppeteerUtils.js
+++ b/client/__tests__/e2e/puppeteerUtils.js
@@ -44,8 +44,8 @@ export const puppeteerUtils = (page) => ({
},
async clickOn(testid, options = {}) {
- await this.waitByID(testid);
- const click = await page.click(`[data-testid='${testid}']`, options);
+ await this.waitByID(testid, options);
+ const click = await page.click(`[data-testid='${testid}']`);
await page.waitFor(50);
return click;
},
diff --git a/client/__tests__/e2e/testBrowser.js b/client/__tests__/e2e/testBrowser.js
index 8aa57bca..2931c17f 100644
--- a/client/__tests__/e2e/testBrowser.js
+++ b/client/__tests__/e2e/testBrowser.js
@@ -36,6 +36,10 @@ export async function setupTestBrowser() {
page.on("console", async (msg) => {
// If there is a console.error but an error is not thrown, this will ensure the test fails
if (msg.type() === "error") {
+ // TODO: chromium does not currently support the CSP directive on the
+ // line below, so we swallow this error. Remove this when the test
+ // suite uses a browser version that supports this directive.
+ if (msg.text() === "Unrecognized Content-Security-Policy directive 'require-trusted-types-for'.\n") return;
const errorMsgText = await Promise.all(
// TODO can we do this without internal properties?
msg.args().map((arg) => arg._remoteObject.description)
diff --git a/client/__tests__/util/promiseLimit.test.js b/client/__tests__/util/promiseLimit.test.js
index fc6832f6..659e2f23 100644
--- a/client/__tests__/util/promiseLimit.test.js
+++ b/client/__tests__/util/promiseLimit.test.js
@@ -1,4 +1,4 @@
-import { PromiseLimit } from "../../src/util/promiseLimit";
+import PromiseLimit from "../../src/util/promiseLimit";
import { range } from "../../src/util/range";
const delay = (t) => new Promise((resolve, reject) => setTimeout(resolve, t));
diff --git a/client/configuration/webpack/cspHashPlugin.js b/client/configuration/webpack/cspHashPlugin.js
index d56d2b32..99afd46f 100644
--- a/client/configuration/webpack/cspHashPlugin.js
+++ b/client/configuration/webpack/cspHashPlugin.js
@@ -54,7 +54,7 @@ class CspHashPlugin {
.createHash("sha256")
.update(str, "utf8")
.digest("base64");
- return "sha256-" + hash;
+ return `sha256-${hash}`;
}
}
diff --git a/client/src/actions/index.js b/client/src/actions/index.js
index 19ff498d..0bfffd51 100644
--- a/client/src/actions/index.js
+++ b/client/src/actions/index.js
@@ -7,7 +7,7 @@ import {
doBinaryRequest,
dispatchNetworkErrorMessageToUser,
} from "../util/actionHelpers";
-import { PromiseLimit } from "../util/promiseLimit";
+import PromiseLimit from "../util/promiseLimit";
import { requestReembed, reembedResetWorldToUniverse } from "./reembed";
import { loadUserColorConfig } from "../util/stateManager/colorHelpers";
diff --git a/client/src/components/autosave/filenameDialog.js b/client/src/components/autosave/filenameDialog.js
index e25792d1..c2f02e64 100644
--- a/client/src/components/autosave/filenameDialog.js
+++ b/client/src/components/autosave/filenameDialog.js
@@ -18,7 +18,7 @@ import {
saveInProgress: state.autosave?.saveInProgress ?? false,
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
error: state.autosave?.error,
- writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
+ writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
}))
class FilenameDialog extends React.Component {
constructor(props) {
diff --git a/client/src/components/autosave/index.js b/client/src/components/autosave/index.js
index bffd1740..d77dcc65 100644
--- a/client/src/components/autosave/index.js
+++ b/client/src/components/autosave/index.js
@@ -1,6 +1,5 @@
import React from "react";
import { connect } from "react-redux";
-import * as globals from "../../globals";
import actions from "../../actions";
import FilenameDialog from "./filenameDialog";
@@ -11,7 +10,7 @@ import FilenameDialog from "./filenameDialog";
saveInProgress: state.autosave?.saveInProgress ?? false,
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
error: state.autosave?.error,
- writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
+ writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
initialDataLoadComplete: state.autosave?.initialDataLoadComplete,
}))
class Autosave extends React.Component {
diff --git a/client/src/components/categorical/category/index.js b/client/src/components/categorical/category/index.js
index e3c6b239..dfd5e296 100644
--- a/client/src/components/categorical/category/index.js
+++ b/client/src/components/categorical/category/index.js
@@ -64,7 +64,7 @@ class Category extends React.Component {
} else if (categoryCount.selectedCatCount < categoryCount.totalCatCount) {
/* to be explicit... */
this.checkbox.indeterminate = true;
- this.setState({ isChecked: false });
+ this.setState({ isChecked: false }); // eslint-disable-line react/no-did-update-set-state
}
}
}
@@ -77,14 +77,15 @@ class Category extends React.Component {
});
};
- toggleAll() {
- const { dispatch, metadataField } = this.props;
- dispatch({
- type: "categorical metadata filter all of these",
- metadataField,
- });
- this.setState({ isChecked: true });
- }
+ handleCategoryClick = () => {
+ const { annotations, metadataField, onExpansionChange } = this.props;
+ const editingCategory =
+ annotations.isEditingCategoryName &&
+ annotations.categoryBeingEdited === metadataField;
+ if (!editingCategory) {
+ onExpansionChange(metadataField);
+ }
+ };
toggleNone() {
const { dispatch, metadataField } = this.props;
@@ -95,6 +96,15 @@ class Category extends React.Component {
this.setState({ isChecked: false });
}
+ toggleAll() {
+ const { dispatch, metadataField } = this.props;
+ dispatch({
+ type: "categorical metadata filter all of these",
+ metadataField,
+ });
+ this.setState({ isChecked: true });
+ }
+
handleToggleAllClick() {
const { isChecked } = this.state;
// || this.checkbox.indeterminate === false
@@ -115,6 +125,8 @@ class Category extends React.Component {
globals.categoryDisplayStringMaxLength
);
+ const checkboxID = `category-select-${metadataField}`;
+
return (
-
diff --git a/client/src/index.js b/client/src/index.js
index b4655afa..7f2de221 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,5 +1,4 @@
// jshint esversion: 6
-/* eslint-disable no-console */
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
diff --git a/client/src/reducers/colors.js b/client/src/reducers/colors.js
index aa10d077..a7c566fe 100644
--- a/client/src/reducers/colors.js
+++ b/client/src/reducers/colors.js
@@ -150,7 +150,7 @@ const ColorsReducer = (
case "annotation: delete label": {
const { world } = nextSharedState;
const { colorMode, colorAccessor } = state;
- const { metadataField, colors } = action;
+ const { metadataField } = action;
if (
colorMode !== "color by categorical metadata" ||
colorAccessor !== metadataField
diff --git a/client/src/reducers/layoutChoice.js b/client/src/reducers/layoutChoice.js
index 12486bc0..fa04f6f7 100644
--- a/client/src/reducers/layoutChoice.js
+++ b/client/src/reducers/layoutChoice.js
@@ -49,7 +49,7 @@ const LayoutChoice = (
}
case "reembed: add reembedding": {
- const name = action.schema.name;
+ const { name } = action.schema;
const available = Array.from(new Set(state.available).add(name));
return {
...state,
diff --git a/client/src/reducers/ontology.js b/client/src/reducers/ontology.js
index 504d26ff..828bc88a 100644
--- a/client/src/reducers/ontology.js
+++ b/client/src/reducers/ontology.js
@@ -9,11 +9,12 @@ const Ontology = (
) => {
switch (action.type) {
case "configuration load complete": {
- /* eslint-disable camelcase */
const enabled =
- action.config?.parameters?.annotations_cell_ontology_enabled ?? false;
- const terms = action.config?.parameters?.annotations_cell_ontology_terms;
- /* eslint-enable camelcase */
+ action.config?.parameters?.["annotations_cell_ontology_enabled"] ??
+ false;
+ const terms =
+ action.config?.parameters?.["annotations_cell_ontology_terms"];
+
const termSet = new Set(terms);
return {
...state,
diff --git a/client/src/reducers/world.js b/client/src/reducers/world.js
index 0e9685ad..6a62df26 100644
--- a/client/src/reducers/world.js
+++ b/client/src/reducers/world.js
@@ -41,8 +41,8 @@ const WorldReducer = (
const { dim } = action;
// we don't clip anything except for varData and obsAnnotations
- let unclipped = state.unclipped;
- if (dim == "varData" || dim == "obsAnnotations") {
+ let { unclipped } = state;
+ if (dim === "varData" || dim === "obsAnnotations") {
unclipped = {
...unclipped,
[dim]: universe[dim].clone(),
@@ -298,7 +298,7 @@ const WorldReducer = (
const { obsLayout: origObsLayout, schema: origSchema } = state;
const { embedding, schema: embeddingSchema } = action;
- const { dims, name } = embeddingSchema;
+ const { dims } = embeddingSchema;
let obsLayout = origObsLayout;
let schema = origSchema;
diff --git a/client/src/util/dataframe/labelIndex.js b/client/src/util/dataframe/labelIndex.js
index 0b01adda..79f69802 100644
--- a/client/src/util/dataframe/labelIndex.js
+++ b/client/src/util/dataframe/labelIndex.js
@@ -1,3 +1,4 @@
+/* eslint-disable max-classes-per-file */
/**
Label indexing - map a label to & from an integer offset. See Dataframe
for how this is used.
@@ -23,7 +24,6 @@ function extent(tarr) {
return [min, max];
}
-/* eslint-disable class-methods-use-this */
class IdentityInt32Index {
/*
identity/noop index, with small assumptions that labels are int32
@@ -41,11 +41,13 @@ class IdentityInt32Index {
return k;
}
+ // eslint-disable-next-line class-methods-use-this
getOffset(i) {
// label to offset
return i;
}
+ // eslint-disable-next-line class-methods-use-this
getLabel(i) {
// offset to label
return i;
@@ -93,9 +95,6 @@ class IdentityInt32Index {
return this.__promote(labelArray);
}
}
-/* eslint-enable class-methods-use-this */
-
-/* eslint-disable class-methods-use-this */
class DenseInt32Index {
/*
DenseInt32Index indexes integer labels, and uses Int32Array typed arrays
@@ -177,9 +176,7 @@ class DenseInt32Index {
return this.__promote(labelArray);
}
}
-/* eslint-enable class-methods-use-this */
-/* eslint-disable class-methods-use-this */
class KeyIndex {
/*
KeyIndex indexes arbitrary JS primitive types, and uses a Map()
@@ -223,6 +220,7 @@ class KeyIndex {
return this.rindex.length;
}
+ // eslint-disable-next-line class-methods-use-this
subsetLabels(labelArray) {
return new KeyIndex(labelArray);
}
diff --git a/client/src/util/promiseLimit.js b/client/src/util/promiseLimit.js
index ad502fbc..1b1731b3 100644
--- a/client/src/util/promiseLimit.js
+++ b/client/src/util/promiseLimit.js
@@ -13,7 +13,7 @@ return Promise.all([
plimit.add(() => fetch('/baz'))
])
*/
-export class PromiseLimit {
+export default class PromiseLimit {
constructor(maxConcurrency) {
this.queue = new Set();
this.maxConcurrency = maxConcurrency;
diff --git a/client/src/util/significantDigits.js b/client/src/util/significantDigits.js
index 6d3e005e..21fbf83f 100644
--- a/client/src/util/significantDigits.js
+++ b/client/src/util/significantDigits.js
@@ -5,6 +5,6 @@
export default (n) => {
return n
.toExponential()
- .replace(/e[\+\-0-9]*$/, "")
+ .replace(/e[+\-0-9]*$/, "")
.replace(/^0\.?0*|\./, "").length;
};
diff --git a/client/src/util/typedCrossfilter/crossfilter.js b/client/src/util/typedCrossfilter/crossfilter.js
index f328d3a8..3ee99386 100644
--- a/client/src/util/typedCrossfilter/crossfilter.js
+++ b/client/src/util/typedCrossfilter/crossfilter.js
@@ -1,3 +1,4 @@
+// eslint-disable-next-line max-classes-per-file
import PositiveIntervals from "./positiveIntervals";
import BitArray from "./bitArray";
import {
@@ -409,7 +410,7 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
this.index = makeSortIndex(array);
}
- /* eslint-disable class-methods-use-this */
+ // eslint-disable-next-line class-methods-use-this
_createValueArray(data, mapf, array) {
// create dimension value array
const len = data.length;
@@ -419,7 +420,6 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
}
return larray;
}
- /* eslint-enable class-methods-use-this */
select(spec) {
const { mode } = spec;
@@ -466,7 +466,7 @@ class ImmutableScalarDimension extends _ImmutableBaseDimension {
const ranges = [];
const r = [
lowerBoundIndirect(value, index, lo, 0, value.length),
- !!inclusive
+ inclusive
? upperBoundIndirect(value, index, hi, 0, value.length)
: lowerBoundIndirect(value, index, hi, 0, value.length),
];
@@ -514,11 +514,10 @@ class ImmutableEnumDimension extends ImmutableScalarDimension {
});
}
- /* eslint-disable class-methods-use-this */
+ // eslint-disable-next-line class-methods-use-this
selectRange() {
throw new Error("range selection unsupported on Enumerated dimension");
}
- /* eslint-enable class-methods-use-this */
}
class ImmutableSpatialDimension extends _ImmutableBaseDimension {
diff --git a/server/app/app.py b/server/app/app.py
index d22a6731..80aefc7f 100644
--- a/server/app/app.py
+++ b/server/app/app.py
@@ -105,7 +105,7 @@ def get_data_adaptor(dataset=None):
raise DatasetAccessError("Invalid dataset {dataset}")
if datapath is None:
- return common_rest.abort_and_log(HTTPStatus.BAD_REQUEST, f"Invalid dataset NONE", loglevel=logging.INFO)
+ return common_rest.abort_and_log(HTTPStatus.BAD_REQUEST, "Invalid dataset NONE", loglevel=logging.INFO)
cache_manager = current_app.matrix_data_cache_manager
return cache_manager.data_adaptor(datapath, config)
diff --git a/server/common/app_config.py b/server/common/app_config.py
index b228fee2..8e01419d 100644
--- a/server/common/app_config.py
+++ b/server/common/app_config.py
@@ -279,13 +279,13 @@ class AppConfig(object):
if self.server__csp_directives is not None:
for k, v in self.server__csp_directives.items():
if not isinstance(k, str):
- raise ConfigurationError(f"CSP directive names must be a string.")
+ raise ConfigurationError("CSP directive names must be a string.")
if isinstance(v, list):
for policy in v:
if not isinstance(policy, str):
- raise ConfigurationError(f"CSP directive value must be a string or list of strings.")
+ raise ConfigurationError("CSP directive value must be a string or list of strings.")
elif not isinstance(v, str):
- raise ConfigurationError(f"CSP directive value must be a string or list of strings.")
+ raise ConfigurationError("CSP directive value must be a string or list of strings.")
# scripts can be string (filename) or dict (attributes). Convert string to dict.
scripts = []
@@ -476,8 +476,8 @@ class AppConfig(object):
with self.matrix_data_cache_manager.data_adaptor(self.single_dataset__datapath, self) as data_adaptor:
if self.diffexp__enable and data_adaptor.parameters.get("diffexp_may_be_slow", False):
context["messagefn"](
- f"CAUTION: due to the size of your dataset, "
- f"running differential expression may take longer or fail."
+ "CAUTION: due to the size of your dataset, "
+ "running differential expression may take longer or fail."
)
max_workers = self.diffexp__alg_cxg__max_workers
diff --git a/server/data_anndata/anndata_adaptor.py b/server/data_anndata/anndata_adaptor.py
index fe7bda4f..60113b6d 100644
--- a/server/data_anndata/anndata_adaptor.py
+++ b/server/data_anndata/anndata_adaptor.py
@@ -183,13 +183,13 @@ class AnndataAdaptor(DataAdaptor):
def _validate_and_initialize(self):
if anndata_version_is_pre_070() and self.config.adaptor__anndata_adaptor__backed:
warnings.warn(
- f"Use of --backed mode with anndata versions older than 0.7 will have serious "
+ "Use of --backed mode with anndata versions older than 0.7 will have serious "
"performance issues. Please update to at least anndata 0.7 or later."
)
# var and obs column names must be unique
if not self.data.obs.columns.is_unique or not self.data.var.columns.is_unique:
- raise KeyError(f"All annotation column names must be unique.")
+ raise KeyError("All annotation column names must be unique.")
self._alias_annotation_names()
self._validate_data_types()
@@ -222,8 +222,8 @@ class AnndataAdaptor(DataAdaptor):
X0 = self.data.X[0, 0:1]
if sparse.isspmatrix(X0) and not sparse.isspmatrix_csc(X0):
warnings.warn(
- f"Anndata data matrix is sparse, but not a CSC (columnar) matrix. "
- f"Performance may be improved by using CSC."
+ "Anndata data matrix is sparse, but not a CSC (columnar) matrix. "
+ "Performance may be improved by using CSC."
)
if self.data.X.dtype != "float32":
warnings.warn(
@@ -295,7 +295,7 @@ class AnndataAdaptor(DataAdaptor):
valid_layouts.append(layout)
if len(valid_layouts) == 0:
- raise PrepareError(f"No valid layout data.")
+ raise PrepareError("No valid layout data.")
# cap layouts to MAX_LAYOUTS
return layouts[0:MAX_LAYOUTS]
diff --git a/server/data_common/data_adaptor.py b/server/data_common/data_adaptor.py
index 7aa5d452..cd542cc0 100644
--- a/server/data_common/data_adaptor.py
+++ b/server/data_common/data_adaptor.py
@@ -230,18 +230,19 @@ class DataAdaptor(metaclass=ABCMeta):
# all labels must have a name, which must be unique and not used in obs column names
if not labels_df.columns.is_unique:
- raise KeyError(f"All column names specified in user annotations must be unique.")
+ raise KeyError("All column names specified in user annotations must be unique.")
# the label index must be unique, and must have same values the anndata obs index
if not labels_df.index.is_unique:
- raise KeyError(f"All row index values specified in user annotations must be unique.")
+ raise KeyError("All row index values specified in user annotations must be unique.")
obs_columns = self.get_obs_columns()
duplicate_columns = list(set(labels_df.columns) & set(obs_columns))
if len(duplicate_columns) > 0:
raise KeyError(
- f"Labels file may not contain column names which overlap " f"with h5ad obs columns {duplicate_columns}"
+ "Labels file may not contain column names which overlap "
+ f"with h5ad obs columns {duplicate_columns}"
)
# labels must have same count as obs annotations
@@ -351,13 +352,13 @@ class DataAdaptor(metaclass=ABCMeta):
"""
embeddings = self.get_embedding_names() if fields is None or len(fields) == 0 else fields
layout_data = []
- with ServerTiming.time(f"layout.query"):
+ with ServerTiming.time("layout.query"):
for ename in embeddings:
embedding = self.get_embedding_array(ename, 2)
normalized_layout = DataAdaptor.normalize_embedding(embedding)
layout_data.append(pd.DataFrame(normalized_layout, columns=[f"{ename}_0", f"{ename}_1"]))
- with ServerTiming.time(f"layout.encode"):
+ with ServerTiming.time("layout.encode"):
if layout_data:
df = pd.concat(layout_data, axis=1, copy=False)
else:
diff --git a/server/data_common/matrix_loader.py b/server/data_common/matrix_loader.py
index 7ee0184a..fae95b5a 100644
--- a/server/data_common/matrix_loader.py
+++ b/server/data_common/matrix_loader.py
@@ -228,7 +228,7 @@ class MatrixDataLoader(object):
self.matrix_data_type = self.__matrix_data_type()
if not self.__matrix_data_type_allowed(app_config):
- raise DatasetAccessError(f"Dataset does not have an allowed type.")
+ raise DatasetAccessError("Dataset does not have an allowed type.")
if self.matrix_data_type == MatrixDataType.H5AD:
from server.data_anndata.anndata_adaptor import AnndataAdaptor
@@ -272,7 +272,7 @@ class MatrixDataLoader(object):
def pre_load_validation(self):
if self.matrix_data_type == MatrixDataType.UNKNOWN:
- raise DatasetAccessError(f"Dataset does not have a recognized type: .h5ad or .cxg")
+ raise DatasetAccessError("Dataset does not have a recognized type: .h5ad or .cxg")
self.matrix_type.pre_load_validation(self.location)
def file_size(self):
diff --git a/server/data_cxg/cxg_adaptor.py b/server/data_cxg/cxg_adaptor.py
index 3bab6987..accab96a 100644
--- a/server/data_cxg/cxg_adaptor.py
+++ b/server/data_cxg/cxg_adaptor.py
@@ -264,7 +264,7 @@ class CxgAdaptor(DataAdaptor):
# function to get the embedding
# this function to iterate through embeddings.
def get_embedding_names(self):
- with ServerTiming.time(f"layout.lsuri"):
+ with ServerTiming.time("layout.lsuri"):
pemb = self.get_path("emb")
embeddings = [os.path.basename(p) for (p, t) in self.lsuri(pemb) if t == "array"]
if len(embeddings) == 0:
@@ -311,7 +311,7 @@ class CxgAdaptor(DataAdaptor):
A = self.open_array(ax)
schema_hints = json.loads(A.meta["cxg_schema"]) if "cxg_schema" in A.meta else {}
if type(schema_hints) is not dict:
- raise TypeError(f"Array schema was malformed.")
+ raise TypeError("Array schema was malformed.")
cols = []
for attr in A.schema:
diff --git a/server/eb/app.py b/server/eb/app.py
index c493a9b9..2b6f3951 100644
--- a/server/eb/app.py
+++ b/server/eb/app.py
@@ -156,7 +156,7 @@ try:
dataroot = os.getenv("CXG_DATAROOT")
if dataroot:
- logging.info(f"Configuration from CXG_DATAROOT")
+ logging.info("Configuration from CXG_DATAROOT")
app_config.update(multi_dataset__dataroot=dataroot)
secret_name = os.getenv("CXG_AWS_SECRET_NAME")
@@ -171,7 +171,7 @@ try:
if not secret_region_name:
secret_region_name = discover_s3_region_name(config_file)
if not secret_region_name:
- logging.error(f"Could not determine the AWS Secret Manager region")
+ logging.error("Could not determine the AWS Secret Manager region")
sys.exit(1)
flask_secret_key = get_flask_secret_key(secret_region_name, secret_name)
@@ -188,7 +188,7 @@ try:
if not app_config.server__flask_secret_key:
logging.critical(
- f"flask_secret_key is not provided. Either set in config file, CXG_SECRET_KEY environment variable, "
+ "flask_secret_key is not provided. Either set in config file, CXG_SECRET_KEY environment variable, "
"or in AWS Secret Manager"
)
sys.exit(1)
diff --git a/server/eb/check_requirements.py b/server/eb/check_requirements.py
index 67d7ab8b..008afede 100644
--- a/server/eb/check_requirements.py
+++ b/server/eb/check_requirements.py
@@ -29,16 +29,12 @@ def check(expected, custom):
# cdict must only have exact requirements (==)
for cname, cspecs in cdict.items():
if len(cspecs) != 1 or cspecs[0][0] != "==":
- print(
- f"Error, spec must be an exact requirement {custom}: {cname} {str(cspecs)}"
- )
+ print(f"Error, spec must be an exact requirement {custom}: {cname} {str(cspecs)}")
okay = False
for ename, especs in edict.items():
if ename not in cdict:
- print(
- f"Error, missing requirement from {custom}: {ename} {str(especs)}"
- )
+ print(f"Error, missing requirement from {custom}: {ename} {str(especs)}")
okay = False
continue
@@ -46,9 +42,7 @@ def check(expected, custom):
for espec in especs:
rokay = check_version(cver, espec[0], Version(espec[1]))
if not rokay:
- print(
- f"Error, failed requirement from {custom}: {ename} {espec}, {cver}"
- )
+ print(f"Error, failed requirement from {custom}: {ename} {espec}, {cver}")
okay = False
if okay:
diff --git a/server/requirements.txt b/server/requirements.txt
index e538353c..61ead7b5 100644
--- a/server/requirements.txt
+++ b/server/requirements.txt
@@ -1,6 +1,6 @@
anndata>=0.6.20
boto3>=1.12.18
-click>=6.7
+click>=7.1.2
fastobo>=0.6.1
Flask>=1.0.2
Flask-Compress>=1.4.0
diff --git a/server/test/test_api.py b/server/test/test_api.py
index cd9a4329..319a5847 100644
--- a/server/test/test_api.py
+++ b/server/test/test_api.py
@@ -185,14 +185,14 @@ class EndPoints(object):
self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST)
def test_data_mimetype_error(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
header = {"Accept": "xxx"}
url = f"{self.URL_BASE}{endpoint}"
result = self.session.put(url, headers=header)
self.assertEqual(result.status_code, HTTPStatus.NOT_ACCEPTABLE)
def test_fbs_default(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
url = f"{self.URL_BASE}{endpoint}"
result = self.session.put(url)
self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST)
@@ -202,21 +202,21 @@ class EndPoints(object):
self.assertEqual(result.headers["Content-Type"], "application/octet-stream")
def test_data_put_fbs(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
url = f"{self.URL_BASE}{endpoint}"
header = {"Accept": "application/octet-stream"}
result = self.session.put(url, headers=header)
self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST)
def test_data_get_fbs(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
url = f"{self.URL_BASE}{endpoint}"
header = {"Accept": "application/octet-stream"}
result = self.session.get(url, headers=header)
self.assertEqual(result.status_code, HTTPStatus.BAD_REQUEST)
def test_data_put_filter_fbs(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
url = f"{self.URL_BASE}{endpoint}"
header = {"Accept": "application/octet-stream"}
filter = {"filter": {"var": {"index": [0, 1, 4]}}}
@@ -233,8 +233,8 @@ class EndPoints(object):
def test_data_get_filter_fbs(self):
index_col_name = self.schema["schema"]["annotations"]["var"]["index"]
+ endpoint = "data/var"
query = f"var:{index_col_name}=SIK1"
- endpoint = f"data/var"
url = f"{self.URL_BASE}{endpoint}?{query}"
header = {"Accept": "application/octet-stream"}
result = self.session.get(url, headers=header)
@@ -245,7 +245,7 @@ class EndPoints(object):
self.assertEqual(df["n_cols"], 1)
def test_data_put_single_var(self):
- endpoint = f"data/var"
+ endpoint = "data/var"
url = f"{self.URL_BASE}{endpoint}"
header = {"Accept": "application/octet-stream"}
index_col_name = self.schema["schema"]["annotations"]["var"]["index"]
@@ -306,7 +306,7 @@ class EndPointsAnnotations(EndPoints):
query = "annotation-collection-name=test_annotations"
url = f"{self.URL_BASE}{endpoint}?{query}"
n_rows = self.data.get_shape()[0]
- fbs = make_fbs({"cat_A": pd.Series(["label_A" for l in range(0, n_rows)], dtype="category")})
+ fbs = make_fbs({"cat_A": pd.Series(["label_A"] * n_rows, dtype="category")})
result = self.session.put(url, data=fbs)
self.assertEqual(result.status_code, HTTPStatus.OK)
self.assertEqual(result.headers["Content-Type"], "application/json")
diff --git a/server/test/test_writable_annotation.py b/server/test/test_writable_annotation.py
index 693bc373..d4cf15b4 100644
--- a/server/test/test_writable_annotation.py
+++ b/server/test/test_writable_annotation.py
@@ -27,7 +27,7 @@ class WritableAnnotationTest(unittest.TestCase):
def test_error_checks(self):
# verify that the expected errors are generated
n_rows = self.data.get_shape()[0]
- fbs_bad = make_fbs({"louvain": pd.Series(["undefined" for l in range(0, n_rows)], dtype="category")})
+ fbs_bad = make_fbs({"louvain": pd.Series(["undefined"] * n_rows, dtype="category")})
# ensure we catch attempt to overwrite non-writable data
with self.assertRaises(KeyError):
@@ -38,8 +38,8 @@ class WritableAnnotationTest(unittest.TestCase):
n_rows = self.data.get_shape()[0]
fbs = make_fbs(
{
- "cat_A": pd.Series(["label_A" for l in range(0, n_rows)], dtype="category"),
- "cat_B": pd.Series(["label_B" for l in range(0, n_rows)], dtype="category"),
+ "cat_A": pd.Series(["label_A"] * n_rows, dtype="category"),
+ "cat_B": pd.Series(["label_B"] * n_rows, dtype="category"),
}
)
res = self.annotation_put_fbs(fbs)
@@ -49,14 +49,14 @@ class WritableAnnotationTest(unittest.TestCase):
self.assertEqual(df.shape, (n_rows, 2))
self.assertEqual(set(df.columns), {"cat_A", "cat_B"})
self.assertTrue(self.data.original_obs_index.equals(df.index))
- self.assertTrue(np.all(df["cat_A"] == ["label_A" for l in range(0, n_rows)]))
- self.assertTrue(np.all(df["cat_B"] == ["label_B" for l in range(0, n_rows)]))
+ self.assertTrue(np.all(df["cat_A"] == ["label_A"] * n_rows))
+ self.assertTrue(np.all(df["cat_B"] == ["label_B"] * n_rows))
# verify complete overwrite on second attempt, AND rotation occurs
fbs = make_fbs(
{
- "cat_A": pd.Series(["label_A1" for l in range(0, n_rows)], dtype="category"),
- "cat_C": pd.Series(["label_C" for l in range(0, n_rows)], dtype="category"),
+ "cat_A": pd.Series(["label_A1"] * n_rows, dtype="category"),
+ "cat_C": pd.Series(["label_C"] * n_rows, dtype="category"),
}
)
res = self.annotation_put_fbs(fbs)
@@ -64,8 +64,8 @@ class WritableAnnotationTest(unittest.TestCase):
self.assertTrue(path.exists(self.annotations.output_file))
df = pd.read_csv(self.annotations.output_file, index_col=0, header=0, comment="#")
self.assertEqual(set(df.columns), {"cat_A", "cat_C"})
- self.assertTrue(np.all(df["cat_A"] == ["label_A1" for l in range(0, n_rows)]))
- self.assertTrue(np.all(df["cat_C"] == ["label_C" for l in range(0, n_rows)]))
+ self.assertTrue(np.all(df["cat_A"] == ["label_A1"] * n_rows))
+ self.assertTrue(np.all(df["cat_C"] == ["label_C"] * n_rows))
# rotation
name, ext = path.splitext(self.annotations.output_file)
@@ -79,8 +79,8 @@ class WritableAnnotationTest(unittest.TestCase):
n_rows = self.data.get_shape()[0]
fbs = make_fbs(
{
- "cat_A": pd.Series(["label_A" for l in range(0, n_rows)], dtype="category"),
- "cat_B": pd.Series(["label_B" for l in range(0, n_rows)], dtype="category"),
+ "cat_A": pd.Series(["label_A"] * n_rows, dtype="category"),
+ "cat_B": pd.Series(["label_B"] * n_rows, dtype="category"),
}
)
for i in range(0, 11):
@@ -100,8 +100,8 @@ class WritableAnnotationTest(unittest.TestCase):
n_rows = self.data.get_shape()[0]
fbs = make_fbs(
{
- "cat_A": pd.Series(["label_A" for l in range(0, n_rows)], dtype="category"),
- "cat_B": pd.Series(["label_B" for l in range(0, n_rows)], dtype="category"),
+ "cat_A": pd.Series(["label_A"] * n_rows, dtype="category"),
+ "cat_B": pd.Series(["label_B"] * n_rows, dtype="category"),
}
)
@@ -123,8 +123,8 @@ class WritableAnnotationTest(unittest.TestCase):
[obs_index_col_name, "n_genes", "percent_mito", "n_counts", "louvain", "cat_A", "cat_B"],
)
col_idx = annotations["col_idx"]
- self.assertEqual(annotations["columns"][col_idx.index("cat_A")], ["label_A" for l in range(0, n_rows)])
- self.assertEqual(annotations["columns"][col_idx.index("cat_B")], ["label_B" for l in range(0, n_rows)])
+ self.assertEqual(annotations["columns"][col_idx.index("cat_A")], ["label_A"] * n_rows)
+ self.assertEqual(annotations["columns"][col_idx.index("cat_B")], ["label_B"] * n_rows)
# verify the schema was updated
all_col_schema = {c["name"]: c for c in schema["annotations"]["obs"]["columns"]}