dcm4chee 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +45 -0
- data/Rakefile +9 -0
- data/app/controllers/dcm4chee/api/v1/application_entities_controller.rb +178 -0
- data/app/controllers/dcm4chee/api/v1/base_controller.rb +15 -0
- data/app/controllers/dcm4chee/api/v1/dicom_objects_controller.rb +58 -0
- data/app/controllers/dcm4chee/api/v1/file_systems_controller.rb +37 -0
- data/app/controllers/dcm4chee/api/v1/instances_controller.rb +68 -0
- data/app/controllers/dcm4chee/api/v1/modalities_controller.rb +29 -0
- data/app/controllers/dcm4chee/api/v1/patients_controller.rb +77 -0
- data/app/controllers/dcm4chee/api/v1/series_controller.rb +69 -0
- data/app/controllers/dcm4chee/api/v1/source_aets_controller.rb +29 -0
- data/app/controllers/dcm4chee/api/v1/studies_controller.rb +69 -0
- data/app/controllers/dcm4chee/api/v1/trashed_instances_controller.rb +82 -0
- data/app/controllers/dcm4chee/api/v1/trashed_patients_controller.rb +84 -0
- data/app/controllers/dcm4chee/api/v1/trashed_series_controller.rb +81 -0
- data/app/controllers/dcm4chee/api/v1/trashed_studies_controller.rb +81 -0
- data/app/controllers/dcm4chee/api/v1/trashes_controller.rb +23 -0
- data/app/controllers/dcm4chee/application_controller.rb +4 -0
- data/app/models/dcm4chee/application_entity.rb +129 -0
- data/app/models/dcm4chee/dicom_file.rb +62 -0
- data/app/models/dcm4chee/file_system.rb +95 -0
- data/app/models/dcm4chee/instance.rb +69 -0
- data/app/models/dcm4chee/modality.rb +17 -0
- data/app/models/dcm4chee/patient.rb +42 -0
- data/app/models/dcm4chee/series.rb +78 -0
- data/app/models/dcm4chee/source_aet.rb +17 -0
- data/app/models/dcm4chee/study.rb +60 -0
- data/app/models/dcm4chee/trashed_dicom_file.rb +58 -0
- data/app/models/dcm4chee/trashed_instance.rb +56 -0
- data/app/models/dcm4chee/trashed_patient.rb +40 -0
- data/app/models/dcm4chee/trashed_series.rb +50 -0
- data/app/models/dcm4chee/trashed_study.rb +40 -0
- data/config/routes.rb +27 -0
- data/lib/dcm4chee.rb +58 -0
- data/lib/dcm4chee/api_constraints.rb +12 -0
- data/lib/dcm4chee/dicom_object_manager.rb +48 -0
- data/lib/dcm4chee/engine.rb +5 -0
- data/lib/dcm4chee/models/has_dicom_object.rb +71 -0
- data/lib/dcm4chee/services/application_entity_service.rb +113 -0
- data/lib/dcm4chee/services/content_edit_service.rb +37 -0
- data/lib/dcm4chee/services/file_system_management.rb +16 -0
- data/lib/dcm4chee/services/mbean.rb +11 -0
- data/lib/dcm4chee/services/move_scu_service.rb +54 -0
- data/lib/dcm4chee/version.rb +3 -0
- data/lib/tasks/dcm4chee_tasks.rake +4 -0
- metadata +241 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
module Api
|
4
|
+
module V1
|
5
|
+
class TrashedStudiesController < BaseController
|
6
|
+
respond_to :json
|
7
|
+
|
8
|
+
# Search for studies from the trash. Supported querying
|
9
|
+
# conditions:
|
10
|
+
# trashed_patient_id
|
11
|
+
#
|
12
|
+
# Check {DataMapper::Searcher::ClassMethods} for supported
|
13
|
+
# querying operators.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # Request
|
17
|
+
# GET /api/trashed_studies?q[trashed_patient_id]=... HTTP/1.1
|
18
|
+
# Accept: application/vnd.menglifang.org; version=1
|
19
|
+
#
|
20
|
+
# # Response
|
21
|
+
# HTTP/1.1 200 OK
|
22
|
+
# {
|
23
|
+
# "trashed_studies": [{
|
24
|
+
# "id": ...,
|
25
|
+
# "trashed_patient_id": ...,
|
26
|
+
# "study_iuid": ...,
|
27
|
+
# "accession_no": ...,
|
28
|
+
# "dcm_elements": [{
|
29
|
+
# "name": ...,
|
30
|
+
# "value": ...,
|
31
|
+
# "tag": ...,
|
32
|
+
# "value_representation": ...,
|
33
|
+
# "length": ...
|
34
|
+
# }, ...]
|
35
|
+
# }, ...]
|
36
|
+
# }
|
37
|
+
def index
|
38
|
+
studies = TrashedStudy.search(params[:q])
|
39
|
+
|
40
|
+
respond_with trashed_studies: studies
|
41
|
+
end
|
42
|
+
|
43
|
+
# Move a study to the trash including the related series, instances and files.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# # Request
|
47
|
+
# POST /api/trashed_studies HTTP/1.1
|
48
|
+
# Accept: application/vnd.menglifang.org; version=1
|
49
|
+
# Content-Type: application/json
|
50
|
+
#
|
51
|
+
# { "study_id": ... }
|
52
|
+
#
|
53
|
+
# # Response
|
54
|
+
# HTTP/1.1 201 Created
|
55
|
+
def create
|
56
|
+
study = Study.get!(params[:study_id])
|
57
|
+
study.move_to_trash
|
58
|
+
|
59
|
+
head :created
|
60
|
+
end
|
61
|
+
|
62
|
+
# Delete a study from the trash.
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# # Request
|
66
|
+
# DELETE /api/trashed_studies/... HTTP/1.1
|
67
|
+
# Accept: application/vnd.menglifang.org; version=1
|
68
|
+
#
|
69
|
+
# # Response
|
70
|
+
# HTTP/1.1 200 OK
|
71
|
+
def destroy
|
72
|
+
study = TrashedStudy.get!(params[:id])
|
73
|
+
study.remove_from_trash
|
74
|
+
|
75
|
+
head :ok
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
module Api
|
4
|
+
module V1
|
5
|
+
class TrashesController < BaseController
|
6
|
+
# Clean up the trash.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # Request
|
10
|
+
# DELETE /api/trash HTTP/1.1
|
11
|
+
# Accept: application/vnd.menglifang.org; version=1
|
12
|
+
#
|
13
|
+
# # Response
|
14
|
+
# HTTP/1.1 200 OK
|
15
|
+
def destroy
|
16
|
+
Dcm4chee.content_edit_service.empty_trash
|
17
|
+
|
18
|
+
head :ok
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
class ApplicationEntity
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
storage_names[Dcm4chee.config.repository_name] = 'ae'
|
7
|
+
|
8
|
+
# @return [Integer] primary key
|
9
|
+
property :id, Serial, field: 'pk'
|
10
|
+
|
11
|
+
# @return [String] name
|
12
|
+
property :title, String, field: 'aet'
|
13
|
+
|
14
|
+
# @return [String] hostname or ip address
|
15
|
+
property :host, String, field: 'hostname'
|
16
|
+
|
17
|
+
# @return [Integer] port
|
18
|
+
property :port, Integer, field: 'port'
|
19
|
+
|
20
|
+
# @return [String] cipher_suites, available values:SSL_RSA_WITH_NULL_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA
|
21
|
+
# Multi-suites should be specified in comma-separated format.
|
22
|
+
property :cipher_suites, String, field: 'cipher_suites'
|
23
|
+
|
24
|
+
# @return [String] issuer of the patient id
|
25
|
+
property :patient_id_issuer, String, field: 'pat_id_issuer'
|
26
|
+
|
27
|
+
# @return [String] issuer of the accession number
|
28
|
+
property :accession_number_issuer, String, field: 'acc_no_issuer'
|
29
|
+
|
30
|
+
# @return [String] username
|
31
|
+
property :username, String, field: 'user_id'
|
32
|
+
|
33
|
+
# @return [String] password
|
34
|
+
property :password, String, field: 'passwd'
|
35
|
+
|
36
|
+
# @return [String] group of the file system
|
37
|
+
property :fs_group, String, field: 'fs_group_id', default: 'ONLINE_STORAGE'
|
38
|
+
|
39
|
+
# @return [String] group of the application entity
|
40
|
+
property :group, String, field: 'ae_group'
|
41
|
+
|
42
|
+
# @return [String] description
|
43
|
+
property :description, String, field: 'ae_desc'
|
44
|
+
|
45
|
+
# @return [String] url of WADO
|
46
|
+
property :wado_url, String, field: 'wado_url'
|
47
|
+
|
48
|
+
# @return [String] station name
|
49
|
+
property :station_name, String, field: 'station_name'
|
50
|
+
|
51
|
+
# @return [String] institution
|
52
|
+
property :institution, String, field: 'institution'
|
53
|
+
|
54
|
+
# @return [String] department
|
55
|
+
property :department, String, field: 'department'
|
56
|
+
|
57
|
+
# @return [String] installed or not
|
58
|
+
property :installed, String, field: 'installed'
|
59
|
+
|
60
|
+
# Update an application entity through AEService of dcm4chee.
|
61
|
+
#
|
62
|
+
# @param [Hash] attrs Attributes of the application entity
|
63
|
+
def update_by_service(attrs = {})
|
64
|
+
params = [
|
65
|
+
id,
|
66
|
+
attrs[:title] || title,
|
67
|
+
attrs[:host] || host,
|
68
|
+
attrs[:port] || port,
|
69
|
+
attrs[:cipher_suites] || cipher_suites,
|
70
|
+
attrs[:patient_id_issuer] || patient_id_issuer,
|
71
|
+
attrs[:accession_number_issuer] || accession_number_issuer,
|
72
|
+
attrs[:username] || username,
|
73
|
+
attrs[:password] || password,
|
74
|
+
attrs[:fs_group] || fs_group,
|
75
|
+
attrs[:group] || group,
|
76
|
+
attrs[:description] || description,
|
77
|
+
attrs[:wado_url] || wado_url,
|
78
|
+
attrs[:station_name] || station_name,
|
79
|
+
attrs[:institution] || institution,
|
80
|
+
attrs[:department] || department,
|
81
|
+
attrs[:installed] || installed,
|
82
|
+
true
|
83
|
+
]
|
84
|
+
Dcm4chee.application_entity_service.update_ae(params)
|
85
|
+
reload
|
86
|
+
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Delete an application entity through AEService of dcm4chee
|
91
|
+
def destroy_by_service
|
92
|
+
Dcm4chee.application_entity_service.remove_ae(title)
|
93
|
+
end
|
94
|
+
|
95
|
+
class << self
|
96
|
+
# Create an application entity through AEService of dcm4chee.
|
97
|
+
#
|
98
|
+
# @param [Hash] attrs Attributes of the new application entity
|
99
|
+
def create_by_service(attrs = {})
|
100
|
+
params = [
|
101
|
+
attrs[:title],
|
102
|
+
attrs[:host],
|
103
|
+
attrs[:port],
|
104
|
+
attrs[:cipher_suites],
|
105
|
+
attrs[:patient_id_issuer],
|
106
|
+
attrs[:accession_number_issuer],
|
107
|
+
attrs[:username],
|
108
|
+
attrs[:password],
|
109
|
+
attrs[:fs_group],
|
110
|
+
attrs[:group],
|
111
|
+
attrs[:description],
|
112
|
+
attrs[:wado_url],
|
113
|
+
attrs[:station_name],
|
114
|
+
attrs[:institution],
|
115
|
+
attrs[:department],
|
116
|
+
!!(attrs[:installed]),
|
117
|
+
true
|
118
|
+
]
|
119
|
+
Dcm4chee.application_entity_service.add_ae(params)
|
120
|
+
|
121
|
+
first(title: attrs[:title])
|
122
|
+
end
|
123
|
+
|
124
|
+
def repository(name = nil, &block)
|
125
|
+
super(Dcm4chee.config.repository_name, &block)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
class DicomFile
|
4
|
+
include DataMapper::Resource
|
5
|
+
include HasDicomObject
|
6
|
+
|
7
|
+
storage_names[Dcm4chee.config.repository_name] = 'files'
|
8
|
+
|
9
|
+
# @return [Integer] primary key
|
10
|
+
property :id, Serial, field: 'pk'
|
11
|
+
|
12
|
+
# @return [Integer] foreign key of {Instance}
|
13
|
+
property :instance_id, Integer, field: 'instance_fk'
|
14
|
+
|
15
|
+
# @return [Integer] foreign key of {FileSystem}
|
16
|
+
property :file_system_id, Integer, field: 'filesystem_fk'
|
17
|
+
|
18
|
+
# @return [String] path
|
19
|
+
property :path, Text, field: 'filepath'
|
20
|
+
|
21
|
+
# @return [String] DICOM transfer syntax UID(0002,0010)
|
22
|
+
property :transfer_syntax_uid, Text, field: 'file_tsuid'
|
23
|
+
|
24
|
+
# @return [String] MD5 of the file
|
25
|
+
property :md5, String, field: 'file_md5'
|
26
|
+
|
27
|
+
# @return [Integer] file size
|
28
|
+
property :size, Integer, field: 'file_size'
|
29
|
+
|
30
|
+
# Available values:
|
31
|
+
# -4: QUERY_HSM_FAILED
|
32
|
+
# -3: MD5_CHECK_FAILED
|
33
|
+
# -2: VERIFY_COMPRESS_FAILED
|
34
|
+
# -1: COMPRESS_FAILED
|
35
|
+
# 0: DEFAULT
|
36
|
+
# 1: TO_ARCHIVE
|
37
|
+
# 2: ARCHIVED
|
38
|
+
# 3: COMPRESSING
|
39
|
+
#
|
40
|
+
# @return [Integer] file status
|
41
|
+
property :availability, Enum[-4, -3, -2, -1, 0, 1, 2, 3], field: 'file_status'
|
42
|
+
|
43
|
+
# @return [DateTime] last MD5 checking time of the file
|
44
|
+
property :md5_checked_at, DateTime, field: 'md5_check_time'
|
45
|
+
|
46
|
+
# @return [DateTime] created time
|
47
|
+
property :created_at, DateTime, field: 'created_time'
|
48
|
+
|
49
|
+
belongs_to :instance, 'Dcm4chee::Instance'
|
50
|
+
belongs_to :file_system, 'Dcm4chee::FileSystem'
|
51
|
+
|
52
|
+
# Load the dicom object
|
53
|
+
# @return [DICOM::DObject] DICOM object
|
54
|
+
def dcm
|
55
|
+
@dcm ||= DICOM::DObject.read(File.join(file_system.path, path))
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.repository(name = nil, &block)
|
59
|
+
super(Dcm4chee.config.repository_name, &block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
class FileSystem
|
4
|
+
include DataMapper::Resource
|
5
|
+
|
6
|
+
storage_names[Dcm4chee.config.repository_name] = 'filesystem'
|
7
|
+
|
8
|
+
# @return [Integer] primary key
|
9
|
+
property :id, Serial, field: 'pk'
|
10
|
+
|
11
|
+
# @return [String] path of the directory
|
12
|
+
property :path, String, field: 'dirpath'
|
13
|
+
|
14
|
+
# @return [Integer] type of the file system
|
15
|
+
# 0: ONLINE
|
16
|
+
# 1: NEARLINE
|
17
|
+
# 2: OFFLINE
|
18
|
+
property :availability, Integer, field: 'availability'
|
19
|
+
|
20
|
+
has n, :dicom_files, 'Dcm4chee::DicomFile'
|
21
|
+
has n, :trashed_dicom_files, 'Dcm4chee::TrashedDicomFile'
|
22
|
+
|
23
|
+
# Check whether the path is relative or absolute. The default path of the relative one is `#{Dcm4chee.config.server_home}/server/default`
|
24
|
+
def path
|
25
|
+
p = self[:path]
|
26
|
+
|
27
|
+
return p if p.start_with?('/')
|
28
|
+
|
29
|
+
raise 'Home of dcm4chee must be set' unless Dcm4chee.config.server_home
|
30
|
+
|
31
|
+
File.join(Dcm4chee.config.server_home, 'server/default', p)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Integer] Total space of the file system(in bytes)
|
35
|
+
def total_space
|
36
|
+
status.block_size * status.blocks
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Integer] Available space of the file system(in bytes)
|
40
|
+
def free_space
|
41
|
+
status.block_size * status.blocks_available
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Integer] Used space of the file system(in bytes)
|
45
|
+
def used_space
|
46
|
+
total_space - free_space
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Integer] Minimum free space of the file system. If the free space is under the limit, dcm4chee will use the next one instead.
|
50
|
+
def min_free_space
|
51
|
+
@min_free_space ||= Dcm4chee.file_system_management.minimum_free_disk_space_bytes
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Integer] Expected space per day(in bytes)
|
55
|
+
def expected_space_per_day
|
56
|
+
@expected_space_per_day ||= Dcm4chee.file_system_management.expected_data_volume_per_day_bytes
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Integer] Remaining days of the file system
|
60
|
+
def remaining_days
|
61
|
+
@remaining_days ||= (free_space - min_free_space) / expected_space_per_day
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Hash] Serialized data
|
65
|
+
def as_json(opts = {})
|
66
|
+
opts[:exclude] ||= []
|
67
|
+
opts[:exclude] << :availability
|
68
|
+
|
69
|
+
opts[:methods] ||= []
|
70
|
+
opts[:methods].concat([:path, :total_space, :free_space,
|
71
|
+
:used_space, :min_free_space,
|
72
|
+
:expected_space_per_day, :remaining_days])
|
73
|
+
|
74
|
+
super(opts)
|
75
|
+
end
|
76
|
+
|
77
|
+
class << self
|
78
|
+
# @return [DataMapper::Collection] All online file systems
|
79
|
+
def online
|
80
|
+
all(availability: 0)
|
81
|
+
end
|
82
|
+
|
83
|
+
def repository(name = nil, &block)
|
84
|
+
super(Dcm4chee.config.repository_name, &block)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# @return [Sys::Filesystem] Status of the file system
|
91
|
+
def status
|
92
|
+
@status ||= Sys::Filesystem.stat(path)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
module Dcm4chee
|
3
|
+
class Instance
|
4
|
+
include DataMapper::Resource
|
5
|
+
include DataMapper::Searcher
|
6
|
+
|
7
|
+
include HasDicomObject
|
8
|
+
|
9
|
+
storage_names[Dcm4chee.config.repository_name] = 'instance'
|
10
|
+
|
11
|
+
# @return [Integer] primary key
|
12
|
+
property :id, Serial, field: 'pk'
|
13
|
+
|
14
|
+
# @return [Integer] foreign key of {Series}
|
15
|
+
property :series_id, Integer, field: 'series_fk'
|
16
|
+
|
17
|
+
# @return [DateTime] created time
|
18
|
+
property :created_at, DateTime, field: 'created_time'
|
19
|
+
|
20
|
+
# @return [String] DICOM Instance NO(0020,0013)
|
21
|
+
property :instance_no, String, field: 'inst_no'
|
22
|
+
|
23
|
+
# @return [String] DICOM SOP Instance UID(0008,0018)
|
24
|
+
property :sop_iuid, Text, field: 'sop_iuid'
|
25
|
+
|
26
|
+
# @return [String] DICOM SOP Class UID(0008,0016)
|
27
|
+
property :sop_cuid, Text, field: 'sop_cuid'
|
28
|
+
|
29
|
+
# DICOM Instance Availability(0008,0056)
|
30
|
+
# 0: ONLINE
|
31
|
+
# 1: NEARLINE
|
32
|
+
# 2: OFFLINE
|
33
|
+
# 3: UNAVAILABLE
|
34
|
+
#
|
35
|
+
# @return [Integer] DICOM Integer Availability
|
36
|
+
property :availability, Enum[0, 1, 2, 3], field: 'availability'
|
37
|
+
|
38
|
+
# TODO Added description
|
39
|
+
|
40
|
+
dicom_field 'inst_attrs'
|
41
|
+
|
42
|
+
belongs_to :series, 'Dcm4chee::Series'
|
43
|
+
has n, :dicom_files, 'Dcm4chee::DicomFile'
|
44
|
+
|
45
|
+
def study_iuid
|
46
|
+
series.study.study_iuid
|
47
|
+
end
|
48
|
+
|
49
|
+
def series_iuid
|
50
|
+
series.series_iuid
|
51
|
+
end
|
52
|
+
|
53
|
+
def move_to_trash
|
54
|
+
Dcm4chee.content_edit_service.move_instance_to_trash(id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def as_json(opts = {})
|
58
|
+
opts[:methods] ||= []
|
59
|
+
opts[:methods] << :series_iuid
|
60
|
+
opts[:methods] << :study_iuid
|
61
|
+
|
62
|
+
super(opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.repository(name = nil, &block)
|
66
|
+
super(Dcm4chee.config.repository_name, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|