Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
833b134
Add dagster run_id to dag result
schote Aug 25, 2025
4b7116e
Add dagster UI modal to view DAG task
schote Aug 25, 2025
678209a
Remove @mui/material theme
schote Aug 25, 2025
edcf18b
Replace item status "DELETED" with "ERROR"
schote Aug 25, 2025
e1c12a5
Updated device and task status handling (acquisition task)
schote Aug 25, 2025
0bf1833
Updated workflow manager and dag task status handling
schote Aug 25, 2025
250d88b
Visual improvements for exam items and task status
schote Aug 25, 2025
7b877cf
Fix frontend linting issues
schote Aug 25, 2025
cb292ff
Fixed linter finding (backend)
schote Aug 25, 2025
dc66cd2
Added default user role
schote Aug 26, 2025
7115ee0
Added device status model
schote Aug 26, 2025
fde4a35
Various fixes
schote Aug 26, 2025
bfb6f5d
Added status bar with device status and service connections
schote Aug 26, 2025
eef6673
Removed obsolete connection status page
schote Aug 26, 2025
e1e8122
Fixed linter findings frontend
schote Aug 26, 2025
aa03321
Fixed backend linter findings
schote Aug 26, 2025
3ad678d
Added option to upload xml header with sequence
schote Aug 26, 2025
cb954c5
Statusbar fixes
schote Aug 26, 2025
81cd990
Use atexit in device example usage
schote Aug 26, 2025
5d6199d
Frontend sequence and header upload
schote Aug 26, 2025
f44eb44
Updated package.json
schote Aug 26, 2025
7eca01e
Frontend fixed linter findings
schote Aug 26, 2025
b171907
Fixed linter findings
schote Aug 26, 2025
807d7d1
Added license file reference to services
schote Aug 28, 2025
c2ce6ea
Updated dagster package structure, added readme
schote Aug 28, 2025
ee4f061
Updated to modern pyproject.toml
schote Aug 28, 2025
dc2c540
Include license files correctly
schote Aug 28, 2025
6d67741
Use recommended dagster project structure and package
schote Aug 28, 2025
ec0b4c0
Updated reco job
schote Aug 28, 2025
817b656
Fixed result directory in workflow manager
schote Aug 28, 2025
02c42bb
Added data lake directory to .env
schote Aug 29, 2025
5aeede4
Updated dagster endpoint in nginx
schote Aug 29, 2025
ad93ce9
Fixed render on task state change in image Ids hook (dicom viewer)
schote Aug 29, 2025
82bcf9d
Fixed render on task state change in file Ids hook (raw data viewer)
schote Aug 29, 2025
2c622d3
Updated viewer inputs in acquisition view
schote Aug 29, 2025
6bb673a
Removed unused import
schote Aug 29, 2025
e14b336
Replaces relative license paths by copy in subpackages
schote Aug 29, 2025
e60a313
Added script to synchronize license files
schote Aug 29, 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
15 changes: 12 additions & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# Container Images
## change the value from "ghcr.io/brain-link/scanhub/scanhub-base:latest" to "scanhub-base" if the local scanhub-base image shall be used
#SCANHUB_BASE_IMAGE=scanhub-base
# BASE IMAGE CONFIGURATION

# Change the value from "ghcr.io/brain-link/scanhub/scanhub-base:latest" to "scanhub-base"
# if the local scanhub-base image shall be used for the service containers

# SCANHUB_BASE_IMAGE=scanhub-base
SCANHUB_BASE_IMAGE=ghcr.io/brain-link/scanhub/scanhub-base:latest


# Configuration of data lake directory
# The data lake contains all the acquisition, reconstruction and processing data

DATA_LAKE_DIR="./data_lake"
45 changes: 25 additions & 20 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
x-dagster-common: &dagster_common
image: scanhub-dagster:latest
image: scanhub-orchestration-engine:latest
pull_policy: never # Don't pull the image, use the local build
build:
context: ./services/dagster
context: ./services/orchestration-engine
args:
BASE_IMG: ${SCANHUB_BASE_IMAGE}
environment:
Expand All @@ -11,12 +11,16 @@ x-dagster-common: &dagster_common
volumes:
# Persistent storage for Dagster instance data (run logs, schedules, sensors, etc.)
- dagster_storage:/opt/dagster/dagster_home
# Mount dagster.yaml config file into the container
- ./services/dagster/dagster.yaml:/opt/dagster/dagster_home/dagster.yaml
# Mount the Dagster application source code (repository, jobs, ops, etc.)
- ./services/dagster:/opt/dagster/app

# For development
# Mount dagster.yaml and workspace.yaml configuration files into the dagster home directory
- ./services/orchestration-engine/dagster.yaml:/opt/dagster/dagster_home/dagster.yaml
- ./services/orchestration-engine/workspace.yaml:/opt/dagster/dagster_home/workspace.yaml
# Mount the Dagster application source code (repository, jobs, ops, etc.) - only for development!
- ./services/orchestration-engine:/opt/dagster/app

# Shared data lake between services
- ./data_lake:/data
- ${DATA_LAKE_DIR}:/data

services:
# SERVICES
Expand All @@ -29,8 +33,7 @@ services:
- BASE_IMG=${SCANHUB_BASE_IMAGE}
volumes:
- ./services/workflow-manager/app/:/app/
# - data_lake:/data
- ./data_lake:/data
- ${DATA_LAKE_DIR}:/data
environment:
DATA_LAKE_DIRECTORY: /data
healthcheck:
Expand All @@ -42,7 +45,7 @@ services:
depends_on:
scanhub-database:
condition: service_healthy
dagster-dagit:
dagster-webserver:
condition: service_healthy
dagster-daemon:
condition: service_healthy
Expand All @@ -55,7 +58,7 @@ services:
- BASE_IMG=${SCANHUB_BASE_IMAGE}
volumes:
- ./services/device-manager/app/:/app/
- ./data_lake:/data
- ${DATA_LAKE_DIR}:/data
environment:
DATA_LAKE_DIRECTORY: /data
healthcheck:
Expand All @@ -80,7 +83,7 @@ services:
- BASE_IMG=${SCANHUB_BASE_IMAGE}
volumes:
- ./services/exam-manager/app/:/app/
- ./data_lake:/data
- ${DATA_LAKE_DIR}:/data
healthcheck:
# TODO: host.docker.internal is only working with docker desktop, must be replaced in product
test: ["CMD", "curl", "http://exam-manager:8000/api/v1/exam/health/readiness"]
Expand Down Expand Up @@ -168,6 +171,8 @@ services:
container_name: db-scanhub
image: postgres:15.2-bullseye
restart: always # unless-stopped
ports:
- 5432:5432 # For development only
volumes:
- postgres_data_scanhub:/var/lib/postgresql/data/
environment:
Expand Down Expand Up @@ -260,10 +265,10 @@ services:
condition: service_healthy
user-login-manager:
condition: service_healthy
dagster-dagit:
condition: service_healthy
scanhub-ui:
dagster-webserver:
condition: service_healthy
# scanhub-ui:
# condition: service_healthy
extra_hosts: # somehow needed to find host.docker.internal under linux
- 'host.docker.internal:host-gateway' # for the access to host.docker.internal to work the firewall of the host needs to allow incoming connections
secrets:
Expand All @@ -289,12 +294,12 @@ services:
start_period: 30s

# DAGSTER
dagster-dagit:
dagster-webserver:
<<: *dagster_common
container_name: dagster-dagit
command: ["dagster-webserver", "-h", "0.0.0.0", "-p", "3000", "--path-prefix", "/dagit"]
container_name: dagster-webserver
command: ["dagster-webserver", "-h", "0.0.0.0", "-p", "3000", "--path-prefix", "/dagster", "-w", "/opt/dagster/app/workspace.yaml"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/dagit/server-status"]
test: ["CMD", "curl", "-f", "http://localhost:3000/dagster/server-status"]
interval: 10s
timeout: 5s
retries: 5
Expand All @@ -306,7 +311,7 @@ services:
<<: *dagster_common
container_name: dagster-daemon
user: "0:0"
entrypoint: ["dagster-daemon", "run"]
entrypoint: ["dagster-daemon", "run", "-w", "/opt/dagster/app/workspace.yaml"]
healthcheck:
test: ["CMD", "pgrep", "-f", "dagster-daemon"]
interval: 10s
Expand Down
8 changes: 4 additions & 4 deletions infrastructure/nginx_config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ server {
# --------------------------------------
# Dagster Dagit UI (already WS-ready)
# --------------------------------------
location /dagit/ {
proxy_pass http://dagster-dagit:3000/dagit/;
location /dagster/ {
proxy_pass http://dagster-webserver:3000/dagster/;
proxy_http_version 1.1;

# WebSocket support
Expand All @@ -119,10 +119,10 @@ server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Prefix /dagit;
proxy_set_header X-Forwarded-Prefix /dagster;

# Optional: rewrite trailing slash
rewrite ^/dagit$ /dagit/ permanent;
rewrite ^/dagster$ /dagster/ permanent;
}

# --------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion scanhub-ui/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"camelcase": "warn",
"spaced-comment": "error",
"quotes": ["error", "single"],
"no-duplicate-imports": "off"
"no-duplicate-imports": "off",
"@typescript-eslint/no-explicit-any": "off"
},
"settings": {
"import/resolver": {
Expand Down
4 changes: 2 additions & 2 deletions scanhub-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint ./src/**/*.tsx",
"lint+fix": "eslint --fix './src/**/*.tsx'",
"lint": "eslint ./src --ext .ts,.tsx,.js,.jsx",
"lint+fix": "eslint ./src --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write './src/**/*.tsx' --config ./.prettierrc",
"generate-all-clients": "./src/openapi/generate-all-clients.sh",
"ts-unused-exports": "ts-unused-exports tsconfig.json --excludePathsFromReport=rc/generated-client"
Expand Down
10 changes: 2 additions & 8 deletions scanhub-ui/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,8 @@ import {
Experimental_CssVarsProvider as MaterialCssVarsProvider,
extendTheme as materialExtendTheme,
} from '@mui/material/styles'
const materialTheme = materialExtendTheme()

import LoginContext from './LoginContext'

import App from './pages/App'
import Login from './pages/LoginView'
import PatientListView from './pages/PatientListView'
Expand All @@ -28,8 +26,8 @@ import TemplatesView from './pages/TemplatesView'
import UserManagementView from './pages/UserManagementView'
import SequenceView from './pages/SequenceView'
import DeviceView from './pages/DeviceView'
import ConnectionStatus from './components/ConnectionStatus'

const materialTheme = materialExtendTheme()

export function RouteConfiguration() {
const navigate = useNavigate()
Expand All @@ -49,10 +47,7 @@ export function RouteConfiguration() {
'--Transition-duration': '0.4s', // set to `none` to disable transition
'--Sidebar-width': '360px',
'--Navigation-height': '60px',
// '--Navigation-height': '64px', // set height of navigation bar
// '--PatientView-toolbarHeight': '54px',
// '--PatientView-drawerWidth': '300px',
// '--PatientView-recordWidth': '300px',
'--Status-height': '22px',
},
}}
/>
Expand All @@ -65,7 +60,6 @@ export function RouteConfiguration() {
<Route path='/devices' element={<DeviceView />} />
<Route path='/sequences' element={<SequenceView />} />
<Route path='/users' element={<UserManagementView />} />
<Route path='/connections' element={<ConnectionStatus buttonOrPage='page' />} />
</Route>

<Route
Expand Down
57 changes: 35 additions & 22 deletions scanhub-ui/src/components/AccordionWithMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,47 @@ import React from 'react';
import Stack from '@mui/joy/Stack';
import IconButton from '@mui/joy/IconButton';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import Tooltip from '@mui/joy/Tooltip'

import { AccordionWithMenuInterface } from '../interfaces/components.interface';


export default function AccordionWithMenu(props: AccordionWithMenuInterface) {

const [expanded, setExpanded] = React.useState(false);
const [expanded, setExpanded] = React.useState(false);

return (
<Stack direction='column' width='100%'>
<Stack direction='row' alignItems='center' justifyContent='space-between'>
{props.accordionSummary}
<Stack direction='row' alignItems='center'>
{props.accordionMenu}
<IconButton
onClick={() => setExpanded(!expanded)}
>
{expanded ? <ExpandMoreIcon /> : <KeyboardArrowLeftIcon />}
</IconButton>
</Stack>
</Stack>
<Stack
direction='column'
sx={{ paddingLeft: 3 }}
return (
<Stack direction='column' width='100%'>
<Stack direction='row' alignItems='center'>
<Tooltip
placement='right'
variant='outlined'
describeChild={false}
arrow
title={ props.toolTipContent }
modifiers={[
{ name: 'offset', options: { offset: [0, 64] } }, // skidding=8 (down), distance=20 (further right)
]}
>
<Stack direction='row' alignItems='center' flexGrow={1}>
<IconButton
onClick={() => setExpanded(!expanded)}
>
{expanded ? props.children : undefined}
</Stack>
</Stack>
)
{expanded ? <ExpandMoreIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
{ props.accordionSummary }
</Stack>
</Tooltip>
{ props.accordionMenu }
</Stack>
<Stack
direction='column'
sx={{ paddingLeft: 2 }}
>
{expanded ? props.children : undefined}
</Stack>

</Stack>
)
}
16 changes: 7 additions & 9 deletions scanhub-ui/src/components/AcquisitionControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
import PlayCircleIcon from '@mui/icons-material/PlayCircle'
import StopCircleIcon from '@mui/icons-material/StopCircle';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import Box from '@mui/joy/Box'
import IconButton from '@mui/joy/IconButton'
import LinearProgress from '@mui/joy/LinearProgress'
Expand Down Expand Up @@ -45,17 +44,13 @@ function AcquisitionControl({ itemSelection, openConfirmModal }: {
},
})

let actionIcon = <PlayCircleIcon />
if (itemSelection.status == ItemStatus.Started) actionIcon = <StopCircleIcon />
if (itemSelection.status == ItemStatus.Finished) actionIcon = <CheckCircleIcon />

return (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<IconButton
size='sm'
variant='plain'
color={'neutral'}
disabled={processTaskMutation.isPending}
disabled={processTaskMutation.isPending || !(itemSelection.type == 'DAG' || itemSelection.type == 'ACQUISITION')}
onClick={() => {
openConfirmModal(() => {
// By now, only tasks can be executed
Expand All @@ -72,7 +67,7 @@ function AcquisitionControl({ itemSelection, openConfirmModal }: {
})
}}
>
{actionIcon}
{ itemSelection.status == ItemStatus.Started ? <StopCircleIcon /> : <PlayCircleIcon /> }
</IconButton>

<Stack direction='column' sx={{ flex: 1 }}>
Expand All @@ -84,9 +79,12 @@ function AcquisitionControl({ itemSelection, openConfirmModal }: {
</Typography>
<Typography level='body-xs'>{'ID: ' + itemSelection.itemId}</Typography>
<LinearProgress
determinate value={itemSelection.progress}
determinate={itemSelection.progress !== undefined && itemSelection.progress > 0}
value={itemSelection.progress !== undefined && itemSelection.progress > 0 ? itemSelection.progress : (
itemSelection.status === ItemStatus.Inprogress ? 25 : 0
)
}
sx={{marginTop: 1}}
color={itemSelection.status == ItemStatus.Finished ? 'success' : 'primary'}
/>
</Stack>
</Box>
Expand Down
Loading
Loading