Skip to content
This repository has been archived by the owner on Feb 22, 2020. It is now read-only.

#145 import image series #233

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion interface/backend/api/urls.py
Expand Up @@ -7,7 +7,10 @@
ImageMetadataApiView,
case_report,
update_candidate_location,
candidates_info
candidates_info,
image_series_registration,
case_available,
case_create
)
from django.conf.urls import (
include,
Expand All @@ -25,9 +28,12 @@
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^cases/available$', case_available, name='cases-available'),
url(r'^cases/create$', case_create, name='cases-create'),
url(r'^images/available$', ImageAvailableApiView.as_view(), name='images-available'),
url(r'^images/metadata$', ImageMetadataApiView.as_view(), name='images-metadata'),
url(r'^candidates-info$', candidates_info, name='candidates-info'),
url(r'^images/image_series_registration$', image_series_registration, name='images-registration'),
url(r'^candidates/(?P<candidate_id>\d+)/move$', update_candidate_location, name='update-candidate-location'),
]

Expand Down
39 changes: 39 additions & 0 deletions interface/backend/api/views.py
Expand Up @@ -234,3 +234,42 @@ def append_files_to_candidate(candidate):
if 'files' not in series:
# using `glob1` as it returns filenames without a directory path
series['files'] = glob.glob1(series['uri'], '*.dcm')


@api_view(['POST'])
def image_series_registration(request):
try:
uri = json.loads(request.body)['uri']
except Exception as e:
return Response({'response': "An error occurred: {}".format(e)}, 500)

image_series, created = ImageSeries.get_or_create(uri)
serialized = serializers.ImageSeriesSerializer(image_series, context={'request': request})
return Response(serialized.data)


@api_view(['POST'])
def case_available(request):
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no need to create a new method. cases/ will do just fine.

try:
uri = json.loads(request.body)['uri']
except Exception as e:
return Response({'response': "An error occurred: {}".format(e)}, 500)

image_series, created = ImageSeries.get_or_create(uri)
cases = Case.objects.filter(series=image_series)
# if case.count():
serialized = serializers.CaseSerializer(cases, context={'request': request}, many=True)
return Response(serialized.data)


@api_view(['POST'])
def case_create(request):
try:
uri = json.loads(request.body)['uri']
except Exception as e:
return Response({'response': "An error occurred: {}".format(e)}, 500)

image_series, created = ImageSeries.get_or_create(uri)
case = Case.objects.create(series=image_series)
serialized = serializers.CaseSerializer(case, context={'request': request})
return Response(serialized.data)
4 changes: 4 additions & 0 deletions interface/frontend/package.json
Expand Up @@ -107,6 +107,10 @@
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.6",
"vue-test-utils": "^1.0.0-beta.6",
"cornerstone-core": " 0.13.0",
"cornerstone-tools": " 0.10.0",
"jquery-slim": "^3.0.0",
"path-dirname": "^1.0.2",
"webpack": "^2.6.1",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
Expand Down
130 changes: 70 additions & 60 deletions interface/frontend/src/components/open-image/ImageSeries.vue
Expand Up @@ -8,78 +8,66 @@
Open imagery
</div>
<div class="card-block">
<template v-if="availableSeries.length">
<template v-if="selected">
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>key</th>
<th>value</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, key, index) in selected">
<td>{{ key }}</td>
<td><small>{{ item }}</small></td>
</tr>
</tbody>
</table>
<ul>
<li v-for="series in availableSeries" :key="series.series_instance_uid">
<a href="#" @click="selectSeries(series)">{{ series.series_instance_uid }}</a>
<span v-if="series == selected">&larr;</span>
<li v-for="el in cases" :key="el.created">
<a href="#" @click="selectCase(el)">{{ el.created }}</a>
<span v-if="el == selectedCase">&larr;</span>
</li>
</ul>
<button @click="createCase(selected)"
class="btn btn-primary float-left case-btn ml-1">
Create new case
</button>
<a href="#/report-and-export"
class="btn btn-primary float-left case-btn ml-1">
Start case
</a>
</template>
<template v-else>
<p class="card-text">No images imported.</p>
</template>
<button class="btn btn-warning float-right"
@click="showImport = !showImport"
>
Import
</button>
</div>
</div>
</div>
</div><!-- /row1 -->

<div class="row" v-show="showImport">
<div class="row">
<div class="col-md-12">
<div class="card card-outline-warning">
<div class="card-header">
Import image series
</div>
<div class="card-block left">
<tree-view class="item left" :model="directories"></tree-view>
<open-dicom class="right" :view="preview"></open-dicom>
<div class="card-block pull-left">
<tree-view class="item pull-left" :model="directories"></tree-view>
<open-dicom v-show="preview.paths.length" class="pull-right" :view="preview"></open-dicom>
</div>
</div>
</div>
</div>

<div class="row">
<div class="col-md-12">
<div class="card" v-if="selected">
<div class="card-header">
Selected image series
</div>
<div class="card-block">
<h3 class="card-title">{{ selected.patient_id }}</h3>

<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>key</th>
<th>value</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, key, index) in selected">
<td>{{ key }}</td>
<td><small>{{ item }}</small></td>
</tr>
</tbody>
</table>

<a href="#" class="btn btn-primary float-right">Start case</a>
</div>
</div>
</div>
</div><!-- /row2 -->

</div><!-- /container -->
</template>

<script>
import { EventBus } from '../../main.js'
import TreeView from './TreeView'
import OpenDicom from './OpenDICOM'
import dirname from 'path-dirname'

export default {
components: {
Expand All @@ -97,22 +85,35 @@
type: 'DICOM',
prefixCS: '://',
prefixUrl: '/api/images/metadata?dicom_location=/',
paths: [],
state: ''
paths: []
},
selected: null,
showImport: false
cases: [],
selectedCase: null,
selected: null
}
},
watch: {
selected: function (val) {
this.cases = []
this.fetchExistingCases(val)
},
'preview.paths': async function (val) {
if (val.length) {
const response = await this.$axios.post('api/images/image_series_registration', {
uri: dirname(this.preview.paths[0])
})
if (response.status === 200) this.selected = response.data
}
}
},
created () {
this.fetchData()
this.fetchAvailableImages()
},
mounted: function () {
EventBus.$on('dicom-selection', (context) => {
this.preview.paths = context.paths
this.preview.state = context.state
console.log(this.preview)
EventBus.$on('dicom-selection', (path) => {
this.preview.paths = path
console.log(this.selected)
})
},
methods: {
Expand All @@ -125,10 +126,28 @@
// TODO: handle error
})
},
async fetchExistingCases (series) {
const response = await this.$axios.post('/api/cases/available', {
uri: series.uri
})
this.cases = response.data
},
async createCase (series) {
console.log(series)
await this.$axios.post('/api/cases/create', {
uri: series.uri
})
const response = await this.fetchExistingCases(series)
this.cases = response.data
},
selectSeries (series) {
console.log(series.uri)
this.selected = series
},
selectCase (el) {
console.log(el.created)
this.selectedCase = el
},
fetchAvailableImages () {
this.$http.get('/api/images/available')
.then((response) => {
Expand All @@ -141,12 +160,3 @@
}
}
</script>

<style lang="scss" scoped>
.left {
float: left;
}
.right {
float: right;
}
</style>
6 changes: 2 additions & 4 deletions interface/frontend/src/components/open-image/TreeView.vue
Expand Up @@ -78,10 +78,8 @@
},
select: function (file) {
EventBus.$emit('dicom-selection',
{
paths: this.model.files.map((file) => { return file.path }),
state: file.path
})
this.model.files.map((file) => { return file.path })
)
}
}
}
Expand Down