Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
derek
committed
Nov 30, 2018
1 parent
45347fd
commit 5553265
Showing
29 changed files
with
542 additions
and
98 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
DIANA CLI Applications | ||
================ | ||
|
||
Derek Merck | ||
<derek_merck@brown.edu> | ||
Rhode Island Hospital and Brown University | ||
Providence, RI | ||
|
||
## `diana-lookup.py` | ||
|
||
Wrapper command-line tool for a _Splunk_ query. | ||
|
||
``` | ||
> python3 diana-lookup.py --query "index=dose_report" -e "-1d" -l now -i my_splunk -s secrets.yml | ||
``` | ||
|
||
`secrets.yml` must have a section called "my_splunk" with keys suitable for creating | ||
an Splunk instance that can accept the query. | ||
|
||
|
||
## `diana-pull.py` | ||
|
||
Wrapper command-line tool for an _Orthanc Proxy_ retrieve from modality. | ||
|
||
``` | ||
> python3 pull-it.py -accession XYZ -series "thin * brain -p my_proxy -d my_pacs -s secrets.yml | ||
``` | ||
|
||
`secrets.yml` must have a section called "my_proxy" with keys suitable for creating | ||
an Orthanc instance that knows about the remote "my_pacs". | ||
|
||
|
||
## `diana-star.py` | ||
|
||
Wrapper command-line tool to stand up a DIANA distributed worker node for pipeline data processing. | ||
|
||
|
||
## `diana-watcher.py` | ||
|
||
Wrapper command-line tool to stand up a DIANA watcher daemon. Can be configured with environment vars for remote/embedded deployment, a yml/json routing file, or a directory of python routes. | ||
|
||
|
||
## `halibut.py` | ||
|
||
Wrapper for Halibut machine learning module (use weights in `tests`). | ||
|
||
|
||
## `dcm2im.py` | ||
|
||
Wrapper command-line tool to convert pixels from a DICOM format file or directory | ||
into a standard image format (png, jpg). | ||
|
||
``` | ||
> python dcm2py.py -i im000001.dcm | ||
``` | ||
|
||
## `index-it.py` | ||
|
||
Wrapper command-line tool for pre-index caching and restoring. | ||
|
||
``` | ||
$ python3 index-it.py --location /my_path --redis_service my_redis -s secrets.yml | ||
$ python3 index-it.py -l /my_path -r my_redis -s secrets.yml restore --an abcxyz123 -d orthanc | ||
``` | ||
|
||
`secrets.yml` must have a section called "my_redis" with keys suitable for creating | ||
a Redis instance. | ||
|
||
No python3 on a system that needs re-indexed? Docker to the rescue... | ||
|
||
``` | ||
$ docker run -v /orthanc/db:/orthanc/db -it derekmerck/diana /bin/bash | ||
# scp server:/secrets.yml . | ||
# python3 apps/cli/index-it.py -l /orthanc/db -r redis -s secrets.yml index -w orthanc | ||
``` | ||
|
||
## `monitor-dose.py` | ||
|
||
monitor-dose | ||
Merck, Summer 2018 | ||
|
||
Wrapper to configure and run a DoseReportHarvester daemon. | ||
|
||
``` | ||
$ python3 dose-monitor -q "gepacs" -j "dose_reports" | ||
``` | ||
|
||
|
||
|
||
License | ||
------------- | ||
|
||
[MIT](http://opensource.org/licenses/mit-license.html) |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,31 @@ | ||
DIANA CLI Applications | ||
================ | ||
# DIANA CLI | ||
|
||
Derek Merck | ||
<derek_merck@brown.edu> | ||
Rhode Island Hospital and Brown University | ||
Providence, RI | ||
|
||
## `diana-lookup.py` | ||
Derek Merck | ||
|
||
Wrapper command-line tool for a _Splunk_ query. | ||
## DICOM Analytics and Archive (DIANA) | ||
|
||
``` | ||
> python3 diana-lookup.py --query "index=dose_report" -e "-1d" -l now -i my_splunk -s secrets.yml | ||
``` | ||
1. `diana` -- Pythonic API for DICOM-related systems and data types | ||
2. `diana-cli` -- CLI wrapper for invoking tasks and daemons | ||
3. `diana-stack` -- Docker swarm definitions for DICOM-service stacks | ||
4. `diana-embedded` -- Balena compose definitions and images for embedded DICOM services | ||
5. `trialist` -- A Flack front end for a diana-stack supporting multiple image registries | ||
5. `radcatr` -- TCL UI for RADCAT report review | ||
|
||
`secrets.yml` must have a section called "my_splunk" with keys suitable for creating | ||
an Splunk instance that can accept the query. | ||
|
||
## The DIANA Command Line | ||
|
||
## `diana-pull.py` | ||
### Proxied Pull by Accession Number | ||
|
||
Wrapper command-line tool for an _Orthanc Proxy_ retrieve from modality. | ||
Batch: | ||
|
||
``` | ||
> python3 pull-it.py -accession XYZ -series "thin * brain -p my_proxy -d my_pacs -s secrets.yml | ||
``` | ||
$ `DIANA pull --accession_number 12345 --anonymize my_orthanc pacs` | ||
|
||
`secrets.yml` must have a section called "my_proxy" with keys suitable for creating | ||
an Orthanc instance that knows about the remote "my_pacs". | ||
Batch: | ||
|
||
$ `DIANA pull --file accesions.txt --anonymize my_orthanc pacs` | ||
|
||
## `diana-star.py` | ||
|
||
Wrapper command-line tool to stand up a DIANA distributed worker node for pipeline data processing. | ||
### Mock Scanner Daemon | ||
|
||
$ `DIANA mock --rate 10 my_orthanc` | ||
|
||
## `diana-watcher.py` | ||
|
||
Wrapper command-line tool to stand up a DIANA watcher daemon. Can be configured with environment vars for remote/embedded deployment, a yml/json routing file, or a directory of python routes. | ||
|
||
|
||
## `halibut.py` | ||
|
||
Wrapper for Halibut machine learning module (use weights in `tests`). | ||
|
||
|
||
## `dcm2im.py` | ||
|
||
Wrapper command-line tool to convert pixels from a DICOM format file or directory | ||
into a standard image format (png, jpg). | ||
|
||
``` | ||
> python dcm2py.py -i im000001.dcm | ||
``` | ||
|
||
## `index-it.py` | ||
|
||
Wrapper command-line tool for pre-index caching and restoring. | ||
|
||
``` | ||
$ python3 index-it.py --location /my_path --redis_service my_redis -s secrets.yml | ||
$ python3 index-it.py -l /my_path -r my_redis -s secrets.yml restore --an abcxyz123 -d orthanc | ||
``` | ||
|
||
`secrets.yml` must have a section called "my_redis" with keys suitable for creating | ||
a Redis instance. | ||
|
||
No python3 on a system that needs re-indexed? Docker to the rescue... | ||
|
||
``` | ||
$ docker run -v /orthanc/db:/orthanc/db -it derekmerck/diana /bin/bash | ||
# scp server:/secrets.yml . | ||
# python3 apps/cli/index-it.py -l /orthanc/db -r redis -s secrets.yml index -w orthanc | ||
``` | ||
|
||
## `monitor-dose.py` | ||
|
||
monitor-dose | ||
Merck, Summer 2018 | ||
|
||
Wrapper to configure and run a DoseReportHarvester daemon. | ||
|
||
``` | ||
$ python3 dose-monitor -q "gepacs" -j "dose_reports" | ||
``` | ||
|
||
|
||
|
||
License | ||
------------- | ||
|
||
[MIT](http://opensource.org/licenses/mit-license.html) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import click | ||
|
||
|
||
@click.command() | ||
@click.option('--count', default=1, help='Number of greetings.') | ||
@click.option('--name', prompt='Your name', help='The person to greet.') | ||
def hello(count, name): | ||
"""Greets NAME for a total of COUNT times.""" | ||
click.echo(hello.__doc__) | ||
|
||
for x in range(count): | ||
click.echo('Hello %s!' % name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import random, logging, time | ||
from datetime import datetime | ||
import click | ||
import attr | ||
from diana.apis import Orthanc | ||
from diana.mock import MockStudy | ||
|
||
@attr.s | ||
class MockScanner(object): | ||
seed = attr.ib( default=None ) | ||
name = attr.ib( type=str, default="Mock Scanner" ) | ||
modality = attr.ib( type=str, default="CT" ) | ||
rate = attr.ib( type=float, default=10, converter=float ) | ||
|
||
@seed.validator | ||
def set_seed(self, attribute, value): | ||
if value: | ||
random.seed(value) | ||
|
||
def gen_study(self): | ||
s = MockStudy(seed=self.seed, | ||
study_datetime=datetime.now(), | ||
station_name = self.name, | ||
modality=self.modality ) | ||
return s | ||
|
||
def run(self, dest: Orthanc): | ||
|
||
while True: | ||
|
||
logging.info("Generating mock study") | ||
s = self.gen_study() | ||
|
||
for d in s.dixels(): | ||
# logging.debug(d) | ||
d.gen_file() | ||
dest.put( d ) | ||
|
||
ave_delay = 60*60/self.rate | ||
this_delay = random.gauss(ave_delay, ave_delay*0.3) | ||
if this_delay < 0.1: | ||
this_delay = 0.1 | ||
logging.info("Waiting {} secs".format(this_delay)) | ||
time.sleep( this_delay ) | ||
|
||
|
||
@click.command() | ||
@click.argument('destination') | ||
@click.argument('rate') | ||
@click.pass_context | ||
def mock(ctx, destination, rate): | ||
"""Generate mock studies at RATE per hour and submit to DESTINATION""" | ||
click.echo(mock.__doc__) | ||
services = ctx.obj['SERVICES'] | ||
|
||
D = Orthanc(**services.get(destination)) | ||
M = MockScanner(name = "Diana Mock", rate = rate) | ||
M.run(dest=D) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import logging | ||
from hashlib import md5 | ||
import click | ||
import yaml | ||
from diana.apis import Orthanc, DicomFile | ||
from diana.utils.dicom import DicomLevel | ||
|
||
|
||
# TODO: Query should always include StudyDate, StudyTime, AccessionNumber | ||
# Without StudyDate/StudyTime it fails on readback | ||
|
||
@click.command() | ||
@click.argument('query') | ||
@click.argument('source') | ||
@click.option('--domain', help="Domain for proxied query") | ||
@click.option('-r', '--retrieve', default=False) | ||
@click.pass_context | ||
def ofind(ctx, query, source, domain, retrieve): | ||
"""Find items matching json QUERY in SOURCE Orthanc service {optionally with proxy DOMAIN}""" | ||
click.echo(ofind.__doc__) | ||
services = ctx.obj['SERVICES'] | ||
|
||
S = Orthanc(**services.get(source)) | ||
if isinstance(query, str): | ||
query = yaml.safe_load(query) | ||
click.echo(S.find(query, DicomLevel.STUDIES, domain, retrieve=retrieve)) | ||
|
||
|
||
@click.command() | ||
@click.option("--accession_number", required=False) | ||
@click.option("--worklist", type=click.File(), required=False) | ||
@click.argument('source') | ||
@click.argument('domain') | ||
@click.argument('destination', type=click.Path(), required=False) | ||
@click.option('-a', '--anonymize', default=False, is_flag=True) | ||
@click.pass_context | ||
def pull(ctx, accession_number, worklist, source, domain, destination, anonymize): | ||
"""Pull items matching QUERY from SOURCE Orthanc service with proxy DOMAIN""" | ||
click.echo(pull.__doc__) | ||
services = ctx.obj['SERVICES'] | ||
|
||
S = Orthanc(**services.get(source)) | ||
# ep.clear() | ||
click.echo(S) | ||
|
||
D = None | ||
if destination: | ||
D = DicomFile(location=destination) | ||
|
||
def get_by_accession_num(accession_num): | ||
|
||
# Only if it's a file destination... | ||
if destination: | ||
# If dest is DicomFile | ||
if D.check("{}.zip".format(md5(accession_num.encode('UTF8')).hexdigest())): | ||
click.echo("Skipping {}".format(accession_num)) | ||
return | ||
|
||
q = { | ||
"AccessionNumber": accession_num, | ||
"StudyDate": "", | ||
"StudyTime": "", | ||
"PatientName": "", | ||
"PatientBirthDate": "", | ||
"PatientSex": "" | ||
} | ||
dixels = S.find(q, DicomLevel.STUDIES, domain, retrieve=True) | ||
click.echo(dixels) | ||
|
||
for dixel in dixels: | ||
if anonymize: | ||
dixel = S.anonymize(dixel, remove=True) | ||
click.echo(dixel) | ||
|
||
if destination: | ||
# If dest is DicomFile | ||
dixel = S.get(dixel, view='archive') | ||
logging.debug(dixel.meta['AccessionNumber']) | ||
D.put(dixel, fn_from="AccessionNumber") | ||
S.remove(dixel) | ||
|
||
# If dest is peer or modality node | ||
# ep.send(dixel, peer_dest=destination) | ||
# ep.remove(dixel) | ||
|
||
if not worklist and not accession_number: | ||
logging.warning("No --accession_number or --worklist option provided. Nothing to do.") | ||
|
||
if accession_number: | ||
get_by_accession_num(accession_number) | ||
return | ||
|
||
if worklist: | ||
for accession_num in worklist.readlines(): | ||
# Get rid of trailing return | ||
get_by_accession_num(accession_num.rstrip()) | ||
|
Oops, something went wrong.