Skip to content

Commit 7912b11

Browse files
authored
Merge pull request #201 from MetaCell/release/1.1.0
update main
2 parents 51ad206 + c469476 commit 7912b11

18 files changed

Lines changed: 670 additions & 249 deletions

File tree

applications/virtual-fly-brain/Dockerfile

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@ ARG CLOUDHARNESS_FLASK
33
FROM ${CLOUDHARNESS_FLASK}
44

55
ARG DOMAIN
6+
ARG BE_DOMAIN
67

78
ENV MODULE_NAME=virtual_fly_brain
89
ENV WORKERS=2
910
ENV PORT=8080
1011
ENV BUILDDIR=/app
1112

13+
# Update ImageMagick to fix critical security vulnerabilities
14+
# CVE-2025-57807, CVE-2025-55298, CVE-2025-55154, CVE-2025-55212, CVE-2025-57803
15+
RUN apt-get update && apt-get upgrade -y imagemagick
16+
17+
# Remove bluez if present (CVE-2023-44431 - no fix available, not needed for this app)
18+
RUN apt-get remove -y bluez bluez-obexd libbluetooth3 || true
19+
1220
RUN apt-get update && apt-get install -y \
1321
git \
1422
build-essential \
@@ -43,14 +51,20 @@ ENV MESA_GL_VERSION_OVERRIDE=3.3
4351

4452
COPY frontend ${BUILDDIR}
4553
WORKDIR ${BUILDDIR}
46-
ENV VFB_DOMAIN=https://${DOMAIN}
4754
RUN yarn install
48-
RUN yarn run build
55+
# Debug: Print all environment variables and the VFB_DOMAIN value
56+
RUN echo "=== Environment Check ===" && \
57+
echo "BE_DOMAIN ARG: ${BE_DOMAIN}" && \
58+
echo "Full VFB_DOMAIN value: https://${BE_DOMAIN}" && \
59+
export VFB_DOMAIN="https://${BE_DOMAIN}" && \
60+
echo "Exported VFB_DOMAIN: $VFB_DOMAIN" && \
61+
echo "========================"
62+
# Pass VFB_DOMAIN to Vite build process explicitly
63+
RUN export VFB_DOMAIN="https://${BE_DOMAIN}" && yarn run build
4964

5065
WORKDIR /usr/src/app
5166
COPY backend/requirements.txt /usr/src/app/
5267
RUN pip3 install --no-cache-dir -r requirements.txt
53-
RUN echo 'test s'
5468
COPY backend/ /usr/src/app
5569
RUN pip3 install -e .
5670

applications/virtual-fly-brain/backend/requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4"
44
# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug
55
# we must peg werkzeug versions below to fix connexion
66
# https://github.com/zalando/connexion/pull/1044
7-
Werkzeug == 2.3.7;
7+
Werkzeug >= 3.0.3;
88
swagger-ui-bundle >= 0.0.2
99
python_dateutil >= 2.6.0
1010
setuptools >= 21.0.0
11-
Flask == 2.2.2
11+
Flask >= 2.3.2
1212
psycopg2-binary
13-
gunicorn==20.1.0
14-
flask_cors==3.0.10
13+
gunicorn >= 22.0.0
14+
flask_cors >= 4.0.1
1515
dataclasses_json >= 0.5.7
1616
dacite >= 1.6.0
1717
pandas >= 1.0.0
1818
numpy >= 1.19.0
19-
vfbquery==0.3.4
19+
vfbquery==0.5.1

applications/virtual-fly-brain/backend/setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from setuptools import setup, find_packages
55

66
NAME = "virtual_fly_brain"
7-
VERSION = "1.0.0"
7+
VERSION = "1.1.0"
88

99
# To install the library, run the following
1010
#
@@ -37,4 +37,3 @@
3737
virtual_fly_brain
3838
"""
3939
)
40-

applications/virtual-fly-brain/backend/tests/test_main.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ def test_get_instances(self):
2121
self.assertEqual(response.status_code, 200)
2222
# # Parse the JSON response
2323
data = json.loads(response.data)
24-
self.assertIsInstance(data, list)
24+
self.assertIsInstance(data, dict)
25+
self.assertIn('count', data)
26+
self.assertIn('headers', data)
27+
self.assertIn('rows', data)
28+
self.assertIsInstance(data['rows'], list)
2529

2630
def test_get_term_info(self):
2731
"""Test the /get_term_info route."""

applications/virtual-fly-brain/frontend/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "frontend",
33
"private": true,
4-
"version": "0.0.0",
4+
"version": "1.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
@@ -41,7 +41,7 @@
4141
"@types/react": "^18.2.0",
4242
"@types/react-dom": "^18.2.1",
4343
"@types/react-redux": "^7.1.24",
44-
"axios": "^0.27.2",
44+
"axios": "^1.7.0",
4545
"bloodhound-js": "1.2.3",
4646
"create-react-class": "^15.7.0",
4747
"file-saver": "^2.0.5",
@@ -85,5 +85,9 @@
8585
"less": "^3.10.3",
8686
"less-vars-to-js": "^1.3.0",
8787
"vite": "^7.0.4"
88+
},
89+
"resolutions": {
90+
"node-fetch": "^2.6.7",
91+
"cross-spawn": "^7.0.6"
8892
}
8993
}

applications/virtual-fly-brain/frontend/src/components/StackViewer.jsx

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -152,36 +152,69 @@ const VFBStackViewer = (props) => {
152152
const config = useMemo(() => {
153153
let result = {
154154
serverUrl: 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi',
155-
templateId: 'NOTSET'
155+
templateId: 'NOTSET',
156+
templateDomainIds: [],
157+
templateDomainNames: [],
158+
templateDomainTypeIds: [],
159+
subDomains: []
156160
};
157-
158-
data?.forEach( stackViewerData => {
161+
162+
data?.forEach(stackViewerData => {
159163
if (stackViewerData?.metadata?.IsTemplate) {
160-
let keys = Object.keys(stackViewerData.metadata?.Images);
161-
result = stackViewerData.metadata?.Images[keys[0]]?.[0];
162-
result.serverUrl = 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi';
163-
if ( stackViewerData?.metadata?.Domains ){
164-
keys = Object.keys(stackViewerData?.metadata?.Domains);
164+
const imageKeys = Object.keys(stackViewerData.metadata?.Images || {});
165+
if (!imageKeys.length) {
166+
return;
167+
}
168+
169+
const imageMeta =
170+
stackViewerData.metadata?.Images[imageKeys[0]]?.[0];
171+
if (!imageMeta) {
172+
return;
165173
}
166-
let ids = [parseInt(keys[keys?.length - 1]) + 1], labels = [parseInt(keys[keys?.length - 1]) + 1], classID = [parseInt(keys[keys?.length - 1]) + 1];
167-
keys?.forEach( key => {
168-
ids[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.id);
169-
labels[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.type_label);
170-
classID[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.type_id);
171-
})
172-
let voxels = [];
173-
if (result?.voxel != undefined) {
174-
voxelSizeRef.current.x = Number(result.voxel.X || 0.622088);
175-
voxelSizeRef.current.y = Number(result.voxel.Y || 0.622088);
176-
voxelSizeRef.current.z = Number(result.voxel.Z || 0.622088);
177-
voxels = [voxelSizeRef.current.x, voxelSizeRef.current.y, voxelSizeRef.current.z];
174+
175+
const domains = stackViewerData.metadata?.Domains || {};
176+
const domainKeys = Object.keys(domains);
177+
const maxIndex = domainKeys.reduce(
178+
(acc, k) => Math.max(acc, Number(k) || 0),
179+
0
180+
);
181+
182+
const ids = new Array(maxIndex + 1);
183+
const labels = new Array(maxIndex + 1);
184+
const classIDs = new Array(maxIndex + 1);
185+
186+
domainKeys.forEach(key => {
187+
const idx = Number(key);
188+
const d = domains[key];
189+
ids[idx] = d?.id;
190+
labels[idx] = d?.type_label;
191+
classIDs[idx] = d?.type_id;
192+
});
193+
194+
const voxels = [];
195+
if (imageMeta?.voxel) {
196+
voxelSizeRef.current.x = Number(imageMeta.voxel.X || 0.622088);
197+
voxelSizeRef.current.y = Number(imageMeta.voxel.Y || 0.622088);
198+
voxelSizeRef.current.z = Number(imageMeta.voxel.Z || 0.622088);
199+
voxels.push(
200+
voxelSizeRef.current.x,
201+
voxelSizeRef.current.y,
202+
voxelSizeRef.current.z
203+
);
178204
}
179205

180-
let subDomains = [voxels, ids, labels, classID]
181-
result.subDomains = subDomains;
206+
result = {
207+
...imageMeta,
208+
serverUrl: 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi',
209+
templateId: stackViewerData.metadata?.Id,
210+
templateDomainIds: ids,
211+
templateDomainNames: labels,
212+
templateDomainTypeIds: classIDs,
213+
subDomains: [voxels, ids, labels, classIDs]
214+
};
182215
}
183216
});
184-
217+
185218
return result;
186219
}, [data]);
187220

applications/virtual-fly-brain/frontend/src/components/StackViewerComponent.jsx

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -981,16 +981,14 @@ const rgbToHex = (color) => {
981981
createImages: async function () {
982982
if (this.state.stack.length > 0) {
983983
var i, x, y, w, h, d, offX, offY, t, image, Xpos, Ypos, XboundMax, YboundMax, XboundMin, YboundMin;
984-
/*
985-
* move through tiles
986-
* console.log('Creating slice view...');
987-
*/
984+
988985
this.state.visibleTiles = [];
989-
w = Math.ceil(((this.state.imageX / 10.0) * this.state.scl) / this.state.tileX);
990-
h = Math.ceil(((this.state.imageY / 10.0) * this.state.scl) / this.state.tileY);
991-
// console.log('Tile grid is ' + w.toString() + ' wide by ' + h.toString() + ' high');
986+
987+
w = Math.ceil(this.state.imageX / this.state.tileX);
988+
h = Math.ceil(this.state.imageY / this.state.tileY);
992989
this.state.numTiles = w * h;
993-
990+
// console.log('Tile grid is ' + w.toString() + ' wide by ' + h.toString() + ' high');
991+
994992
for (t = 0; t < w * h; t++) {
995993
x = 0;
996994
y = 0;
@@ -1683,12 +1681,23 @@ const StackViewerComponent = () => createClass({
16831681
}
16841682

16851683
// detect available wheel event
1686-
support = "onwheel" in document.createElement("div") ? "wheel" // Modern browsers support "wheel"
1687-
: document.onmousewheel !== undefined ? "mousewheel" // Webkit and IE support at least "mousewheel"
1688-
: "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
1689-
this?.addWheelListener(document.getElementById(this.props.data.id + 'displayArea'), (e) => {
1684+
support = "onwheel" in document.createElement("div")
1685+
? "wheel" // Modern browsers
1686+
: document.onmousewheel !== undefined
1687+
? "mousewheel" // Webkit / IE
1688+
: "DOMMouseScroll"; // Older Firefox
1689+
1690+
const displayElem =
1691+
document.getElementById((this.props.data && this.props.data.id) + 'displayArea') ||
1692+
document.getElementById('slice-viewer');
1693+
1694+
if (displayElem) {
1695+
this.addWheelListener(displayElem, (e) => {
16901696
this.onWheelEvent(e);
16911697
});
1698+
} else {
1699+
console.warn('StackViewer: wheel listener target not found for slice viewer');
1700+
}
16921701

16931702
if (this.props.data && this.props.data != null && this.props.data.instances && this.props.data.instances != null) {
16941703
this.setState(this.handleInstances(this.props.data.instances));
@@ -1732,24 +1741,30 @@ const StackViewerComponent = () => createClass({
17321741
newState.voxelY = Number(this.props.config.subDomains[0][1] || 0.622088);
17331742
newState.voxelZ = Number(this.props.config.subDomains[0][2] || 0.622088);
17341743
}
1735-
if (this.props.config && this.props.config != null) {
1736-
if (this.props.config.subDomains && this.props.config.subDomains != null && this.props.config.subDomains.length) {
1737-
if (this.props.config.subDomains.length > 0 && this.props.config.subDomains[0] && this.props.config.subDomains[0].length && this.props.config.subDomains[0].length > 2) {
1738-
newState.voxelX = Number(this.props.config.subDomains[0][0] || 0.622088);
1739-
newState.voxelY = Number(this.props.config.subDomains[0][1] || 0.622088);
1740-
newState.voxelZ = Number(this.props.config.subDomains[0][2] || 0.622088);
1741-
}
1742-
if (this.props.config.subDomains.length > 3 && this.props.config.subDomains[1] != null) {
1743-
newState.tempName = this.props.config.subDomains[2];
1744-
newState.tempId = this.props.config.subDomains[1];
1745-
newState.tempType = this.props.config.subDomains[3];
1746-
// FIXME : Add extra subdomain to match previous configuration
1747-
// if (this.props.config.subDomains[4] && this.props.config.subDomains[4].length && this.props.config.subDomains[4].length > 0) {
1748-
// newState.fxp = JSON.parse(this.props.config.subDomains[4][0]);
1749-
// }
1750-
}
1744+
if (this.props.config) {
1745+
const {
1746+
subDomains = [],
1747+
templateDomainIds,
1748+
templateDomainNames,
1749+
templateDomainTypeIds
1750+
} = this.props.config;
1751+
1752+
if (subDomains.length > 0 && subDomains[0] && subDomains[0].length > 2) {
1753+
newState.voxelX = Number(subDomains[0][0] || 0.622088);
1754+
newState.voxelY = Number(subDomains[0][1] || 0.622088);
1755+
newState.voxelZ = Number(subDomains[0][2] || 0.622088);
17511756
}
1752-
}
1757+
1758+
const templateIds = templateDomainIds || subDomains[1];
1759+
const templateNames = templateDomainNames || subDomains[2];
1760+
const templateTypes = templateDomainTypeIds || subDomains[3];
1761+
1762+
if (templateIds && templateNames && templateTypes) {
1763+
newState.tempId = templateIds;
1764+
newState.tempName = templateNames;
1765+
newState.tempType = templateTypes;
1766+
}
1767+
}
17531768
for (instance in instances) {
17541769
try {
17551770
if ((instances[instance].wrappedObj.id != undefined) && (instances[instance].parent != null) ){
@@ -1792,7 +1807,7 @@ const StackViewerComponent = () => createClass({
17921807

17931808
componentWillUnmount: function () {
17941809
this._isMounted = false;
1795-
return true;
1810+
return true;
17961811
},
17971812
/**
17981813
* Event handler for clicking zoom in. Increments the zoom level

0 commit comments

Comments
 (0)