Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a2d4e27
Merge pull request #195 from MetaCell/main
ddelpiano Sep 15, 2025
1075d6d
pointin backend to robbie server
ddelpiano Oct 2, 2025
72ecc0f
fixing imagemagick vulnerabilities
ddelpiano Oct 2, 2025
10ff348
fixing axios, npm fetch and flask issues
ddelpiano Oct 2, 2025
4de0334
upgrade imagemagick
ddelpiano Oct 2, 2025
8a6a84e
upgrade for more vulnerabilities
ddelpiano Oct 2, 2025
d178abc
more fixes
ddelpiano Oct 2, 2025
8ce01dc
fixing BE_DOMAIN
ddelpiano Oct 2, 2025
cd6f3bf
update yarn lock
ddelpiano Oct 2, 2025
5210fb9
fixing BE url
ddelpiano Oct 20, 2025
678891b
be domain var added to pipeline
ddelpiano Oct 20, 2025
fd12ea7
fixing encoding issue
ddelpiano Oct 29, 2025
e8c7f4b
only 5 last recent searches + encoding issue fixed
ddelpiano Oct 29, 2025
c6a3945
hiding temporarily the query button (to be implemented)
ddelpiano Oct 29, 2025
93e5de2
Merge pull request #196 from MetaCell/feature/last_sprint
ddelpiano Oct 29, 2025
c19bd1a
fix: address encoding issue in recent searches functionality
jrmartin Nov 14, 2025
6285595
#vfb-223 Stack viewer not cleaning up after resizing image and #VFB-2…
jrmartin Nov 15, 2025
5fd8645
refactor: remove unnecessary comments in StackViewer and StackViewerC…
jrmartin Nov 15, 2025
497e171
refactor: simplify URL construction in TerminfoSlider and remove unne…
jrmartin Nov 15, 2025
4259e50
refactor: optimize position calculations and improve instance loading…
jrmartin Nov 17, 2025
eb5af8f
refactor: adjust position calculations and instance loading logic in …
jrmartin Nov 17, 2025
2674b2c
refactor: reorganize instance loading logic for improved clarity and …
jrmartin Nov 17, 2025
0d33244
refactor: clean up event listener management in StackViewerComponent …
jrmartin Nov 17, 2025
dafda57
refactor: remove unused event listener cleanup and enhance focus targ…
jrmartin Nov 17, 2025
74a3444
moving to vfbquery 0.5.1 and changes required for the results coming …
ddelpiano Dec 1, 2025
6d9b30b
Merge pull request #197 from MetaCell/feature/circuit-browser-fixes
ddelpiano Dec 2, 2025
d564b4e
fixing failing test
ddelpiano Dec 2, 2025
d5575fb
Merge branch 'development' of https://github.com/metacell/virtual-fly…
ddelpiano Dec 2, 2025
cacd8ed
fixing review highlights
ddelpiano Dec 2, 2025
4ce9640
check for images not available in results
ddelpiano Dec 2, 2025
13f6cc9
fixing loader behaviour
ddelpiano Dec 2, 2025
0d5e3b0
Merge pull request #198 from MetaCell/feature/new_vfbquery
ddelpiano Dec 2, 2025
c469476
bumping version up
ddelpiano Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions applications/virtual-fly-brain/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ ARG CLOUDHARNESS_FLASK
FROM ${CLOUDHARNESS_FLASK}

ARG DOMAIN
ARG BE_DOMAIN

ENV MODULE_NAME=virtual_fly_brain
ENV WORKERS=2
ENV PORT=8080
ENV BUILDDIR=/app

# Update ImageMagick to fix critical security vulnerabilities
# CVE-2025-57807, CVE-2025-55298, CVE-2025-55154, CVE-2025-55212, CVE-2025-57803
RUN apt-get update && apt-get upgrade -y imagemagick

# Remove bluez if present (CVE-2023-44431 - no fix available, not needed for this app)
RUN apt-get remove -y bluez bluez-obexd libbluetooth3 || true

RUN apt-get update && apt-get install -y \
git \
build-essential \
Expand Down Expand Up @@ -43,14 +51,20 @@ ENV MESA_GL_VERSION_OVERRIDE=3.3

COPY frontend ${BUILDDIR}
WORKDIR ${BUILDDIR}
ENV VFB_DOMAIN=https://${DOMAIN}
RUN yarn install
RUN yarn run build
# Debug: Print all environment variables and the VFB_DOMAIN value
RUN echo "=== Environment Check ===" && \
echo "BE_DOMAIN ARG: ${BE_DOMAIN}" && \
echo "Full VFB_DOMAIN value: https://${BE_DOMAIN}" && \
export VFB_DOMAIN="https://${BE_DOMAIN}" && \
echo "Exported VFB_DOMAIN: $VFB_DOMAIN" && \
echo "========================"
# Pass VFB_DOMAIN to Vite build process explicitly
RUN export VFB_DOMAIN="https://${BE_DOMAIN}" && yarn run build

WORKDIR /usr/src/app
COPY backend/requirements.txt /usr/src/app/
RUN pip3 install --no-cache-dir -r requirements.txt
RUN echo 'test s'
COPY backend/ /usr/src/app
RUN pip3 install -e .

Expand Down
10 changes: 5 additions & 5 deletions applications/virtual-fly-brain/backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4"
# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug
# we must peg werkzeug versions below to fix connexion
# https://github.com/zalando/connexion/pull/1044
Werkzeug == 2.3.7;
Werkzeug >= 3.0.3;
swagger-ui-bundle >= 0.0.2
python_dateutil >= 2.6.0
setuptools >= 21.0.0
Flask == 2.2.2
Flask >= 2.3.2
psycopg2-binary
gunicorn==20.1.0
flask_cors==3.0.10
gunicorn >= 22.0.0
flask_cors >= 4.0.1
dataclasses_json >= 0.5.7
dacite >= 1.6.0
pandas >= 1.0.0
numpy >= 1.19.0
vfbquery==0.3.4
vfbquery==0.5.1
3 changes: 1 addition & 2 deletions applications/virtual-fly-brain/backend/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from setuptools import setup, find_packages

NAME = "virtual_fly_brain"
VERSION = "1.0.0"
VERSION = "1.1.0"

# To install the library, run the following
#
Expand Down Expand Up @@ -37,4 +37,3 @@
virtual_fly_brain
"""
)

6 changes: 5 additions & 1 deletion applications/virtual-fly-brain/backend/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ def test_get_instances(self):
self.assertEqual(response.status_code, 200)
# # Parse the JSON response
data = json.loads(response.data)
self.assertIsInstance(data, list)
self.assertIsInstance(data, dict)
self.assertIn('count', data)
self.assertIn('headers', data)
self.assertIn('rows', data)
self.assertIsInstance(data['rows'], list)

def test_get_term_info(self):
"""Test the /get_term_info route."""
Expand Down
8 changes: 6 additions & 2 deletions applications/virtual-fly-brain/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"version": "1.1.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -41,7 +41,7 @@
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"@types/react-redux": "^7.1.24",
"axios": "^0.27.2",
"axios": "^1.7.0",
"bloodhound-js": "1.2.3",
"create-react-class": "^15.7.0",
"file-saver": "^2.0.5",
Expand Down Expand Up @@ -85,5 +85,9 @@
"less": "^3.10.3",
"less-vars-to-js": "^1.3.0",
"vite": "^7.0.4"
},
"resolutions": {
"node-fetch": "^2.6.7",
"cross-spawn": "^7.0.6"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,36 +152,69 @@ const VFBStackViewer = (props) => {
const config = useMemo(() => {
let result = {
serverUrl: 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi',
templateId: 'NOTSET'
templateId: 'NOTSET',
templateDomainIds: [],
templateDomainNames: [],
templateDomainTypeIds: [],
subDomains: []
};
data?.forEach( stackViewerData => {

data?.forEach(stackViewerData => {
if (stackViewerData?.metadata?.IsTemplate) {
let keys = Object.keys(stackViewerData.metadata?.Images);
result = stackViewerData.metadata?.Images[keys[0]]?.[0];
result.serverUrl = 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi';
if ( stackViewerData?.metadata?.Domains ){
keys = Object.keys(stackViewerData?.metadata?.Domains);
const imageKeys = Object.keys(stackViewerData.metadata?.Images || {});
if (!imageKeys.length) {
return;
}

const imageMeta =
stackViewerData.metadata?.Images[imageKeys[0]]?.[0];
if (!imageMeta) {
return;
}
let ids = [parseInt(keys[keys?.length - 1]) + 1], labels = [parseInt(keys[keys?.length - 1]) + 1], classID = [parseInt(keys[keys?.length - 1]) + 1];
keys?.forEach( key => {
ids[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.id);
labels[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.type_label);
classID[parseInt(key)] = (stackViewerData?.metadata?.Domains?.[key]?.type_id);
})
let voxels = [];
if (result?.voxel != undefined) {
voxelSizeRef.current.x = Number(result.voxel.X || 0.622088);
voxelSizeRef.current.y = Number(result.voxel.Y || 0.622088);
voxelSizeRef.current.z = Number(result.voxel.Z || 0.622088);
voxels = [voxelSizeRef.current.x, voxelSizeRef.current.y, voxelSizeRef.current.z];

const domains = stackViewerData.metadata?.Domains || {};
const domainKeys = Object.keys(domains);
const maxIndex = domainKeys.reduce(
(acc, k) => Math.max(acc, Number(k) || 0),
0
);

const ids = new Array(maxIndex + 1);
const labels = new Array(maxIndex + 1);
const classIDs = new Array(maxIndex + 1);

domainKeys.forEach(key => {
const idx = Number(key);
const d = domains[key];
ids[idx] = d?.id;
labels[idx] = d?.type_label;
classIDs[idx] = d?.type_id;
});

const voxels = [];
if (imageMeta?.voxel) {
voxelSizeRef.current.x = Number(imageMeta.voxel.X || 0.622088);
voxelSizeRef.current.y = Number(imageMeta.voxel.Y || 0.622088);
voxelSizeRef.current.z = Number(imageMeta.voxel.Z || 0.622088);
voxels.push(
voxelSizeRef.current.x,
voxelSizeRef.current.y,
voxelSizeRef.current.z
);
}

let subDomains = [voxels, ids, labels, classID]
result.subDomains = subDomains;
result = {
...imageMeta,
serverUrl: 'http://www.virtualflybrain.org/fcgi/wlziipsrv.fcgi',
templateId: stackViewerData.metadata?.Id,
templateDomainIds: ids,
templateDomainNames: labels,
templateDomainTypeIds: classIDs,
subDomains: [voxels, ids, labels, classIDs]
};
}
});

return result;
}, [data]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -981,16 +981,14 @@ const rgbToHex = (color) => {
createImages: async function () {
if (this.state.stack.length > 0) {
var i, x, y, w, h, d, offX, offY, t, image, Xpos, Ypos, XboundMax, YboundMax, XboundMin, YboundMin;
/*
* move through tiles
* console.log('Creating slice view...');
*/

this.state.visibleTiles = [];
w = Math.ceil(((this.state.imageX / 10.0) * this.state.scl) / this.state.tileX);
h = Math.ceil(((this.state.imageY / 10.0) * this.state.scl) / this.state.tileY);
// console.log('Tile grid is ' + w.toString() + ' wide by ' + h.toString() + ' high');

w = Math.ceil(this.state.imageX / this.state.tileX);
h = Math.ceil(this.state.imageY / this.state.tileY);
this.state.numTiles = w * h;

// console.log('Tile grid is ' + w.toString() + ' wide by ' + h.toString() + ' high');

for (t = 0; t < w * h; t++) {
x = 0;
y = 0;
Expand Down Expand Up @@ -1683,12 +1681,23 @@ const StackViewerComponent = () => createClass({
}

// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" // Modern browsers support "wheel"
: document.onmousewheel !== undefined ? "mousewheel" // Webkit and IE support at least "mousewheel"
: "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
this?.addWheelListener(document.getElementById(this.props.data.id + 'displayArea'), (e) => {
support = "onwheel" in document.createElement("div")
? "wheel" // Modern browsers
: document.onmousewheel !== undefined
? "mousewheel" // Webkit / IE
: "DOMMouseScroll"; // Older Firefox

const displayElem =
document.getElementById((this.props.data && this.props.data.id) + 'displayArea') ||
document.getElementById('slice-viewer');

if (displayElem) {
this.addWheelListener(displayElem, (e) => {
this.onWheelEvent(e);
});
} else {
console.warn('StackViewer: wheel listener target not found for slice viewer');
}

if (this.props.data && this.props.data != null && this.props.data.instances && this.props.data.instances != null) {
this.setState(this.handleInstances(this.props.data.instances));
Expand Down Expand Up @@ -1732,24 +1741,30 @@ const StackViewerComponent = () => createClass({
newState.voxelY = Number(this.props.config.subDomains[0][1] || 0.622088);
newState.voxelZ = Number(this.props.config.subDomains[0][2] || 0.622088);
}
if (this.props.config && this.props.config != null) {
if (this.props.config.subDomains && this.props.config.subDomains != null && this.props.config.subDomains.length) {
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) {
newState.voxelX = Number(this.props.config.subDomains[0][0] || 0.622088);
newState.voxelY = Number(this.props.config.subDomains[0][1] || 0.622088);
newState.voxelZ = Number(this.props.config.subDomains[0][2] || 0.622088);
}
if (this.props.config.subDomains.length > 3 && this.props.config.subDomains[1] != null) {
newState.tempName = this.props.config.subDomains[2];
newState.tempId = this.props.config.subDomains[1];
newState.tempType = this.props.config.subDomains[3];
// FIXME : Add extra subdomain to match previous configuration
// if (this.props.config.subDomains[4] && this.props.config.subDomains[4].length && this.props.config.subDomains[4].length > 0) {
// newState.fxp = JSON.parse(this.props.config.subDomains[4][0]);
// }
}
if (this.props.config) {
const {
subDomains = [],
templateDomainIds,
templateDomainNames,
templateDomainTypeIds
} = this.props.config;

if (subDomains.length > 0 && subDomains[0] && subDomains[0].length > 2) {
newState.voxelX = Number(subDomains[0][0] || 0.622088);
newState.voxelY = Number(subDomains[0][1] || 0.622088);
newState.voxelZ = Number(subDomains[0][2] || 0.622088);
}
}

const templateIds = templateDomainIds || subDomains[1];
const templateNames = templateDomainNames || subDomains[2];
const templateTypes = templateDomainTypeIds || subDomains[3];

if (templateIds && templateNames && templateTypes) {
newState.tempId = templateIds;
newState.tempName = templateNames;
newState.tempType = templateTypes;
}
}
for (instance in instances) {
try {
if ((instances[instance].wrappedObj.id != undefined) && (instances[instance].parent != null) ){
Expand Down Expand Up @@ -1792,7 +1807,7 @@ const StackViewerComponent = () => createClass({

componentWillUnmount: function () {
this._isMounted = false;
return true;
return true;
},
/**
* Event handler for clicking zoom in. Increments the zoom level
Expand Down
Loading