Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Aide app #8

Open
wants to merge 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3397726
Begin refactoring of MRS for AIDE SDK
Feb 28, 2022
30aef81
refined requirements and more conversions
Mar 1, 2022
ec360b6
Philips tests passing
Mar 9, 2022
9f4c442
all tests passing and adds pacs archive funcitonality
Mar 10, 2022
e8d9b25
enable context return
Mar 11, 2022
2519f36
enable dockerfile entrypoint
Mar 11, 2022
fedc3cd
remove logs
Mar 11, 2022
cccc25d
update to use newer pydicom as aide-sdk IO
Mar 17, 2022
69c3377
Update MRSTask.py
Mar 17, 2022
3cd3874
update aide-sdk version
Mar 21, 2022
28674d3
add main.py to image
Mar 21, 2022
d92a16e
add path to manifest file
Mar 21, 2022
becf9e9
addresses bugs and refactors logging to use log.warn
Mar 21, 2022
8791122
Update MRSTask.py
Mar 22, 2022
4dc9ab3
use fixed version of suspect
Mar 30, 2022
c52448e
Update Dockerfile
Mar 30, 2022
abd04eb
simplify actions to use Dockerfile
Mar 30, 2022
9bea619
pass built docker image between jobs as artifact
Mar 30, 2022
fb4c04a
changes gh action back to single job for efficiency
Mar 30, 2022
630dd82
Update test_development_mrs.yml
Mar 30, 2022
eb6e4ec
Update test_development_mrs.yml
Mar 30, 2022
4295dbf
move dockerfile contents out of root
Mar 30, 2022
6f3729c
use slim python
Mar 30, 2022
fb8ca4d
add lint job and add cov packages to req
Mar 30, 2022
4fc2ff4
Update test_development_mrs.yml
Mar 30, 2022
53c0e16
Update test_development_mrs.yml
Mar 30, 2022
15c4732
Update MRSNormalChart.py
Mar 30, 2022
0968bf3
auto change version number from manifest
Mar 30, 2022
c1bc556
Update requirements.txt
Mar 30, 2022
6f89cc2
update coverage comment reporting
Mar 30, 2022
9df0197
adds coverage and test badges to readme
Mar 30, 2022
a63f47a
fix some PEP8 things
Mar 30, 2022
271638d
update requirements.txt
Apr 5, 2022
e8ed2bc
improve MRS logging and exception handling
Aug 26, 2022
620ff14
update manifest path in dockerfile fails on non-mrs
Aug 26, 2022
4af7d1d
improve logging for MRS
Sep 9, 2022
353288e
Update manifest.json
Sep 13, 2022
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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/tests/
/documents/
/.github/
/venv/
*.log*
78 changes: 19 additions & 59 deletions .github/workflows/test_development_mrs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,75 +8,35 @@ on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.6
build-and-test:

- name: setup virtual environment
run: |
python3.6 -m venv env
source ./env/bin/activate
echo "VIRTUAL ENV:" $VIRTUAL_ENV
python3.6 -m pip install --upgrade pip

- name: Install system requirements
run: |
source ./env/bin/activate
sudo apt-get update
sudo apt-get install default-libmysqlclient-dev poppler-utils wget dcmtk gnuplot libcairo2-dev libtiff5-dev libjpeg62 zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk ghostscript -y
gs --version
sudo sed -i '/disable ghostscript format types/,+6d' /etc/ImageMagick-6/policy.xml
# https://stackoverflow.com/questions/52998331/imagemagick-security-policy-pdf-blocking-conversion
runs-on: ubuntu-latest

- name: install python packages
run: |
source ./env/bin/activate
python3.6 -m pip install -r requirements.txt
python3.6 -m pip install git+https://${{ secrets.dicomserver_uname }}:${{ secrets.dicomserver_pw }}@bitbucket.org/gsttmri/[email protected]
steps:
- name: checkout
uses: actions/checkout@v2

- name: GA fixes # https://github.com/actions/virtual-environments/issues/4799
run: |
sudo apt-get update
sudo apt-get remove mysql* && sudo apt-get install -y mysql-server libmysqlclient-dev
- name: Build and tag image
run: docker build -t mrs-tarquin:latest .

- name: Install Tarquin
run: |
source ./env/bin/activate
mkdir ../tarquin_folder && wget -O tarquin.tar.gz https://sourceforge.net/projects/tarquin/files/TARQUIN_4.3.11/TARQUIN_Linux_4.3.11.tar.gz/download && tar -xzf tarquin.tar.gz -C ../tarquin_folder && rm tarquin.tar.gz
mv ../tarquin_folder/TARQUIN_Linux_4.3.11_RC/tarquin /usr/local/bin/tarquin && ln -s /usr/bin/gnuplot /usr/local/bin/gnuplot
- name: Setup flake8 annotations
uses: rbialon/flake8-annotations@v1

- name: print env info
- name: Lint with flake8
run: |
python3 -c "import sys; print('python3 sys.prefix: ' + sys.prefix)"
python3 -c "import sys; print('python3 sys.base_prefix: ' + sys.base_prefix)"
source ./env/bin/activate
python3 -c "import sys; print('post activate python3 sys.prefix' + sys.prefix)"
python3 -c "import sys; print('post activate python3 sys.base_prefix: ' + sys.base_prefix)"
# stop the build if there are Python syntax errors or undefined names
docker run --mount type=bind,source=$(pwd),target=/mrs mrs-tarquin flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
docker run --mount type=bind,source=$(pwd),target=/mrs mrs-tarquin flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

- name: Run MRS tests
run: |
source ./env/bin/activate
pwd
python3.6 -m pytest --continue-on-collection-errors tests/
set -o pipefail
pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered tests/ | tee pytest-coverage.txt ; echo $?
- name: run tests
run: docker run --mount type=bind,source=$(pwd),target=/mrs mrs-tarquin pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=mrs --continue-on-collection-errors tests/ | tee pytest-coverage.txt ; echo $?

- name: Pytest coverage
- name: report coverage
id: coverageComment
uses: MishaKav/pytest-coverage-comment@main
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
pytest-coverage-path: pytest-coverage.txt
junitxml-path: pytest.xml
97 changes: 27 additions & 70 deletions .github/workflows/test_production_mrs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,84 +8,41 @@ on:
- "main"
- "release/*"


# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.6

- name: setup virtual environment
run: |
python3.6 -m venv env
source ./env/bin/activate
echo "VIRTUAL ENV:" $VIRTUAL_ENV
python3.6 -m pip install --upgrade pip
build-and-test:

- name: Install system requirements
run: |
source ./env/bin/activate
sudo apt-get update
sudo apt-get install default-libmysqlclient-dev poppler-utils wget dcmtk gnuplot libcairo2-dev libtiff5-dev libjpeg62 zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk ghostscript -y
gs --version
sudo sed -i '/disable ghostscript format types/,+6d' /etc/ImageMagick-6/policy.xml
# https://stackoverflow.com/questions/52998331/imagemagick-security-policy-pdf-blocking-conversion

- name: install python packages
run: |
source ./env/bin/activate
python3.6 -m pip install -r requirements.txt
python3.6 -m pip install git+https://${{ secrets.dicomserver_uname }}:${{ secrets.dicomserver_pw }}@bitbucket.org/gsttmri/[email protected]

- name: GA fixes # https://github.com/actions/virtual-environments/issues/4799
run: |
sudo apt-get update
sudo apt-get remove mysql* && sudo apt-get install -y mysql-server libmysqlclient-dev
runs-on: ubuntu-latest

- name: Install Tarquin
run: |
source ./env/bin/activate
mkdir ../tarquin_folder && wget -O tarquin.tar.gz https://sourceforge.net/projects/tarquin/files/TARQUIN_4.3.11/TARQUIN_Linux_4.3.11.tar.gz/download && tar -xzf tarquin.tar.gz -C ../tarquin_folder && rm tarquin.tar.gz
mv ../tarquin_folder/TARQUIN_Linux_4.3.11_RC/tarquin /usr/local/bin/tarquin && ln -s /usr/bin/gnuplot /usr/local/bin/gnuplot
steps:
- name: checkout
uses: actions/checkout@v2

- name: print env info
run: |
python3 -c "import sys; print('python3 sys.prefix: ' + sys.prefix)"
python3 -c "import sys; print('python3 sys.base_prefix: ' + sys.base_prefix)"
source ./env/bin/activate
python3 -c "import sys; print('post activate python3 sys.prefix' + sys.prefix)"
python3 -c "import sys; print('post activate python3 sys.base_prefix: ' + sys.base_prefix)"
- name: Build and tag image
run: docker build -t mrs-tarquin:latest .

- name: Run MRS tests
run: |
source ./env/bin/activate
pwd
python3.6 -m pytest --continue-on-collection-errors tests/
- name: run tests
run: docker run --mount type=bind,source=$(pwd),target=/mrs mrs-tarquin pytest --junitxml=pytest.xml --cov-report=term-missing:skip-covered --cov=mrs --continue-on-collection-errors tests/ | tee pytest-coverage.txt ; echo $?

# - name: Update coverage Badge
# if: github.ref == 'refs/heads/main' # only on main
# uses: schneegans/[email protected]
# with:
# auth: ${{ secrets.PYTEST_COVERAGE_COMMENT }}
# gistID: ba102d5f3e592fcd50451c2eff8a803d
# filename: mlops_pytest-coverage-comment.json
# label: Test coverage
# message: ${{ steps.coverageComment.outputs.coverage }}
# color: ${{ steps.coverageComment.outputs.color }}
# namedLogo: python
# logo: https://img.shields.io/badge/coverage-{{ steps.coverageComment.outputs.coverage }}-{{ steps.coverageComment.outputs.color }}.svg
- name: Pytest coverage comment
id: coverageComment
uses: MishaKav/pytest-coverage-comment@main
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml

- name: Update coverage Badge
if: github.ref == 'refs/heads/main' # only on main
uses: schneegans/[email protected]
with:
auth: ${{ secrets.PYTEST_COVERAGE_COMMENT }}
gistID: e1aa11345e5c62d9ea100a83a0f4bfc9
filename: MRS_cov_badge.json
label: Test coverage
message: ${{ steps.coverageComment.outputs.coverage }}
color: ${{ steps.coverageComment.outputs.color }}
namedLogo: python
logo: https://img.shields.io/badge/coverage-{{ steps.coverageComment.outputs.coverage }}-{{ steps.coverageComment.outputs.color }}.svg
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,9 @@ dmypy.json

# Pyre type checker
.pyre/
.idea
.idea
pytest-coverage.txt
pytest.xml

.idea/
.pytest_cache/
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM python:3.9-slim

# Install tarquin
RUN apt-get update -y && apt-get install -y gnuplot ghostscript git wget imagemagick
RUN mkdir ../tarquin_folder
RUN wget -O tarquin.tar.gz https://sourceforge.net/projects/tarquin/files/TARQUIN_4.3.11/TARQUIN_Linux_4.3.11.tar.gz/download
RUN tar -xzf tarquin.tar.gz -C ../tarquin_folder
RUN rm tarquin.tar.gz
RUN mv ../tarquin_folder/TARQUIN_Linux_4.3.11_RC/tarquin /usr/local/bin/tarquin
RUN ln -s /usr/bin/gnuplot /usr/local/bin/gnuplot

COPY requirements.txt mrs/requirements.txt
RUN pip install --upgrade pip
RUN pip install -r mrs/requirements.txt

RUN sed -i '/disable ghostscript format types/,+6d' /etc/ImageMagick-6/policy.xml
COPY mrs/ mrs/mrs/
COPY config/ mrs/config/
COPY main.py mrs/main.py

WORKDIR /mrs

ENV MANIFEST_PATH=config/manifest.json

CMD ["python", "main.py"]
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
<!-- PROJECT HEADING -->
<br />
<p align="center">
<a href="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy">
<img src="https://raw.githubusercontent.com/GSTT-CSC/gstt-csc.github.io/main/assets/transparent-CSC-logo-cropped.png" alt="Logo" width="50%">
</a>
<h1 align="center">MR Spectroscopy</h1>
<p align="center">
<br />
<a href="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy"><strong>Explore the docs »</strong></a>
<br />
<br />
<a href="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy">View repo</a>
·
<a href="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy/issues">Report Bug</a>
·
<a href="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy/issues">Request Feature</a>
</p>
<p align="center">
<img src="https://github.com/GSTT-CSC/0018-ds-mr-spectroscopy/workflows/test_production_mrs.yml/badge.svg?branch=main">
<img src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/laurencejackson/e1aa11345e5c62d9ea100a83a0f4bfc9/MRS_cov_badge.json">
</p>

# MR Spectroscopy

Clinical MRS at the end of a standard MRI examination obtains functional information in addition to anatomical information.
Expand Down
22 changes: 22 additions & 0 deletions config/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import configparser
import os
import shutil
import json

manifest = json.load(open('config/manifest.json'))

APP_DATA_DIR = '/mrs_app_data'
CONFIG_DIR = os.path.dirname(os.path.realpath(__file__)) # same dir as this file
VERSION = manifest['model_version']

if os.path.exists(APP_DATA_DIR):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here in context of AIDE?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks for the APP_DATA_DIR inside the container the application runs in. The image does not have this folder so it will always create a new one.

shutil.rmtree(APP_DATA_DIR)
os.makedirs(APP_DATA_DIR, exist_ok=True)

# Create configparser reader objects
SETTINGS = configparser.ConfigParser()

# This makes a list of config files for both server and local files
config_files = list(os.path.join(CONFIG_DIR, x) for x in os.listdir(CONFIG_DIR) if x.endswith('cfg'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are config files read in AIDE?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to above, the dicomserver config lives entirely within the container where the app runs. There is no configuration that extends beyond the container, other than the few bits in the manifest file.

# Read config files
SETTINGS.read(config_files)
7 changes: 7 additions & 0 deletions config/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"model_description": "Magnetic Resonance Spectroscopy using Tarquin",
"predicate": "DICOM_Modality == 'MR' AND DICOM_Manufacturer == 'Philips Medical Systems' OR DICOM_Manufacturer == 'Siemens' OR DICOM_Manufacturer == 'SIEMENS' OR DICOM_Manufacturer == 'siemens'",
"mode": "QA",
"model_name": "mrs-tarquin",
"model_version": "0.1.2"
}
19 changes: 19 additions & 0 deletions config/mrs.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[mrs]
qa_email_list = [email protected],[email protected],[email protected]
clinical_email_list = [email protected]
job_ref_acq_time_diff_thresh = 10
stat_thresh = 1.5
gnuplot = /usr/local/bin/gnuplot

[philips_qa_tarquin_params]
start_pnt = 10
end_pnt = 1000
ref = 4.85
ref_signals = 1h_naa
int_basis = braino
w_conc = 55556
w_att = 0.7

[email]
nhs_user=dsfsa
nhs_pass=dsc
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from aide_sdk.application import AideApplication
from mrs.MRSOperator import MRSOperator

if __name__ == "__main__":
AideApplication.start(operator=MRSOperator())
33 changes: 33 additions & 0 deletions mrs/MRSOperator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from aide_sdk.inference.aideoperator import AideOperator
from aide_sdk.model.operatorcontext import OperatorContext

from mrs.dicom.study import Study
from mrs.processing.MRSTask import MRSTask
from mrs.tools.tools import check_valid_mrs
from aide_sdk.logger.logger import LogManager
logger = LogManager.get_logger()


class MRSOperator(AideOperator):
"""
MRSOperator class

This class is used to perform MRS processing, and then create a report and archive it to PACS.
"""

def process(self, context: OperatorContext) -> OperatorContext:
"""
Process the MRS task.
:param context:
:return: OperatorContext
"""
mrs_study = Study(study_dir=context.origin.file_path)

if check_valid_mrs(mrs_study):
logger.info(f'Study {mrs_study} valid for MRS')
mrs_task = MRSTask(mrs_study, context)
context = mrs_task.process()
else:
context.set_failure("Data is not valid for MRS")

return context
1 change: 1 addition & 0 deletions mrs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VERSION = '0.1.1'
Empty file added mrs/dicom/__init__.py
Empty file.
Loading