orthanc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fdfad2aeeae25d4ff088160e28a91ea86b64fa01
4
+ data.tar.gz: d87077c820484914845e550b003562b5cb19b6b7
5
+ SHA512:
6
+ metadata.gz: dbe6ececc0c65bc59f7da9f6dc76ebc06227dab0cad6b2af44cb7f259a742f225d9e6d91dc1daee4d45cc3bbaa76c10de30186e093976d0bb69cb4bf8133eeb3
7
+ data.tar.gz: 7f5ac39bbe99b695cce5c3099c954144bbaf12914fd46f9463bc337881d072bf8a34f8d040739903c48c91395975fe8ac2faf1ae1fc1b4578202862ecc50627a
@@ -0,0 +1,12 @@
1
+ lib/.DS_Store
2
+ lib/orthanc/.DS_Store
3
+ /.bundle/
4
+ /.yardoc
5
+ /Gemfile.lock
6
+ /_yardoc/
7
+ /coverage/
8
+ /doc/
9
+ /pkg/
10
+ /spec/reports/
11
+ /tmp/
12
+ *.gem
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in orthanc.gemspec
4
+ gemspec
@@ -0,0 +1,52 @@
1
+ # Orthanc-ruby
2
+
3
+ ##### A Ruby implementation of the [Orthanc](http://orthanc-server.com) DICOM server v0.8.6 REST API
4
+
5
+ ##### Extremely Alpha!! Not ready for production. Anything may change, including resource nesting and naming schemes.
6
+
7
+ (This is my first API client gem, experienced help or advice would be most appreciated) :)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'orthanc'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install orthanc
24
+
25
+ ## Usage
26
+ The gem tries to follow the Orthanc API naming scheme as closely as possible, converting methods and response items to snake case to make the experience more ruby-like.
27
+
28
+ api=Orthanc::Client.new("localhost", "8042")
29
+ api.all_patients
30
+ api.all_patients.first
31
+ api.system.database_version => 5
32
+ api.statistics.count_studies => 14
33
+
34
+ You get the picture.
35
+
36
+ ## Development
37
+
38
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
39
+
40
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
41
+
42
+ ## Contributing
43
+
44
+ 1. Fork it ( https://github.com/[my-github-username]/orthanc/fork )
45
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
46
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
47
+ 4. Push to the branch (`git push origin my-new-feature`)
48
+ 5. Create a new Pull Request
49
+
50
+ ## License
51
+
52
+ MIT License
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "orthanc"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,9 @@
1
+ require "orthanc/version"
2
+
3
+ require 'rest-client'
4
+ require 'recursive-open-struct'
5
+ require 'plissken'
6
+ require 'orthanc/client'
7
+
8
+ module Orthanc
9
+ end
@@ -0,0 +1,72 @@
1
+ require 'orthanc/tools'
2
+ require 'orthanc/plugins'
3
+ require 'orthanc/modalities'
4
+ require 'orthanc/peers'
5
+ require 'orthanc/patients'
6
+ require 'orthanc/studies'
7
+ require 'orthanc/series'
8
+ require 'orthanc/instances'
9
+
10
+ module Orthanc
11
+ class Client
12
+
13
+ attr_accessor :base_uri
14
+
15
+ def initialize(host,port = 8042)
16
+ self.base_uri = RestClient::Resource.new("http://#{host}:#{port}")
17
+ end
18
+
19
+ # ------------- General -------------
20
+ def system
21
+ objectify(base_uri["system"].get)
22
+ end
23
+
24
+ def statistics
25
+ objectify(base_uri["statistics"].get)
26
+ end
27
+
28
+ def changes(params = {}) # "last", "limit" and "since" arguments
29
+ objectify(base_uri["changes"].get({params: params}))
30
+ end
31
+
32
+ def delete_changes(params = {}) # "last", "limit" and "since" arguments
33
+ objectify(base_uri["changes"].delete({params: params}))
34
+ end
35
+
36
+ def exports(params = {}) # "last", "limit" and "since" arguments
37
+ objectify(base_uri["exports"].get({params: params}))
38
+ end
39
+
40
+ def delete_exports(params = {}) # "last", "limit" and "since" arguments
41
+ objectify(base_uri["exports"].delete({params: params}))
42
+ end
43
+
44
+
45
+ private
46
+
47
+ def bool_to_num(bool)
48
+ return 0 if bool == false
49
+ return 1 if bool == true
50
+ end
51
+
52
+ def num_to_bool(num)
53
+ return false if num == "0"
54
+ return true if num == "1"
55
+ end
56
+
57
+ def lowkey(h)
58
+ Hash[h.map{|k,v| v.class == Array ? [k,v.map{|r| f r}.to_a] : [k.downcase,v]}]
59
+ end
60
+
61
+ def objectify(response)
62
+ if JSON.parse(response).class == Array
63
+ return JSON.parse(response)
64
+ elsif JSON.parse(response).class == Hash
65
+ return RecursiveOpenStruct.new(JSON.parse(response).to_snake_keys, recurse_over_arrays: true )
66
+ else
67
+ return response
68
+ end
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,223 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Instances -------------
4
+
5
+ # GET /instances
6
+ def all_instances
7
+ objectify(base_uri["instances"].get)
8
+ end
9
+
10
+ # POST /instances
11
+ def instances(dicom_file) # POST = Add the new DICOM file given in the POST body
12
+ objectify(base_uri["instances"].post(dicom_file))
13
+ end
14
+
15
+ # GET /instances/{id}
16
+ def instance(id)
17
+ objectify(base_uri["instances/#{id}"].get)
18
+ end
19
+
20
+ # DELETE /instances/{id}
21
+ def delete_instance(id)
22
+ objectify(base_uri["instances/#{id}"].delete)
23
+ end
24
+
25
+ # POST /instances/{id}/anonymize
26
+ def anonymize_instance(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
27
+ objectify(base_uri["instances/#{id}/anonymize"].post(payload))
28
+ end
29
+
30
+ # GET /instances/{id}/content/
31
+ def instance_content(id) # List the first-level DICOM tags
32
+ objectify(base_uri["instances/#{id}/frames"].get)
33
+ end
34
+
35
+ # GET /instances/{id}/content/{group}-{element}
36
+ def instance_content_tag(id, group, element) # Raw access to the value of DICOM tags (comprising the padding character)
37
+ objectify(base_uri["instances/#{id}/content/#{group}-#{element}"].get)
38
+ end
39
+
40
+ # GET /instances/{id}/content/{group}-{element}/{index}/...
41
+ def instance_content_sequence(id, group, element, index) # Raw access to the content of DICOM sequences
42
+ objectify(base_uri["instances/#{id}/content/#{group}-#{element}/#{index}"].get)
43
+ end
44
+
45
+ # POST /instances/{id}/export
46
+ def instance_export(id) # Write the DICOM file in the filesystem where Orthanc is running
47
+ objectify(base_uri["instances/#{id}/export"].post)
48
+ end
49
+
50
+ # GET /instances/{id}/file
51
+ def instance_file(id)
52
+ objectify(base_uri["instances/#{id}/file"].get)
53
+ end
54
+
55
+ # GET /instances/{id}/frames
56
+ def instance_frames(id) # Instance frames array
57
+ objectify(base_uri["instances/#{id}/frames"].get)
58
+ end
59
+
60
+ # GET /instances/{id}/frames/{frameNumber}/image-int16
61
+ def instance_frame_image_int16(id, frame_number) # Truncated image to the [-32768;32767] range
62
+ objectify(base_uri["instances/#{id}/frames/#{frame_number}/image-int16"].get)
63
+ end
64
+
65
+ # GET /instances/{id}/frames/{frameNumber}/image-uint16
66
+ def instance_frame_image_uint16(id, frame_number) # Truncated image to the [0;65535] range
67
+ objectify(base_uri["instances/#{id}/frames/#{frame_number}/image-uint16"].get)
68
+ end
69
+
70
+ # GET /instances/{id}/frames/{frameNumber}/image-uint8
71
+ def instance_frame_image_uint8(id, frame_number) # Truncated image to the [0;255] range
72
+ objectify(base_uri["instances/#{id}/frames/#{frame_number}/image-uint8"].get)
73
+ end
74
+
75
+ # GET /instances/{id}/frames/{frameNumber}/matlab
76
+ def instance_frame_matlab(id, frame_number) # a = eval(urlread('http://localhost:8042/instances/.../matlab'))
77
+ objectify(base_uri["instances/#{id}/frames/#{frame_number}/matlab"].get)
78
+ end
79
+
80
+ # GET /instances/{id}/frames/{frameNumber}/preview
81
+ def instance_preview(id, frame_number) # Rescaled image (so that all the range [0;255] is used)
82
+ objectify(base_uri["instances/#{id}/frames/#{frame_number}/preview"].get)
83
+ end
84
+
85
+ # GET /instances/{id}/image-int16
86
+ def instance_image_int16(id) # Truncated image to the [-32768;32767] range
87
+ objectify(base_uri["instances/#{id}/image-int16"].get)
88
+ end
89
+
90
+ # GET /instances/{id}/image-uint16
91
+ def instance_image_uint16(id) # Truncated image to the [0;65535] range
92
+ objectify(base_uri["instances/#{id}/image-uint16"].get)
93
+ end
94
+
95
+ # GET /instances/{id}/image-uint8
96
+ def instance_image_uint8(id) # Truncated image to the [0;255] range
97
+ objectify(base_uri["instances/#{id}/image-uint8"].get)
98
+ end
99
+
100
+ # GET /instances/{id}/matlab
101
+ def instance_matlab(id) # a = eval(urlread('http://localhost:8042/instances/.../matlab'))
102
+ objectify(base_uri["instances/#{id}/matlab"].get)
103
+ end
104
+
105
+ # POST /instances/{id}/modify
106
+ def modify_instance(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
107
+ objectify(base_uri["instances/#{id}/modify"].post(payload))
108
+ end
109
+
110
+ # GET /instances/{id}/module
111
+ def instance_module(id)
112
+ objectify(base_uri["instances/#{id}/module"].get)
113
+ end
114
+
115
+ # GET /instances/{id}/patient
116
+ def instance_patient(id)
117
+ objectify(base_uri["instances/#{id}/patient"].get)
118
+ end
119
+
120
+ # GET /instances/{id}/preview
121
+ def instance_preview(id) # Rescaled image (so that all the range [0;255] is used)
122
+ objectify(base_uri["instances/#{id}/preview"].get)
123
+ end
124
+
125
+ # GET /instances/{id}/series
126
+ def instance_series(id)
127
+ objectify(base_uri["instances/#{id}/series"].get)
128
+ end
129
+
130
+ # GET /instances/{id}/simplified-tags
131
+ def instance_shared_tags(id) # "?simplify" argument to simplify output
132
+ objectify(base_uri["instances/#{id}/shared-tags"].get)
133
+ end
134
+
135
+ # GET /instances/{id}/statistics
136
+ def instance_statistics(id)
137
+ objectify(base_uri["instances/#{id}/statistics"].get)
138
+ end
139
+
140
+ # GET /instances/{id}/study
141
+ def instance_study(id)
142
+ objectify(base_uri["instances/#{id}/study"].get)
143
+ end
144
+
145
+ # GET /instances/{id}/tags
146
+ def instance_tags(id) # TODO: "?simplify" argument to simplify output (same as "simplified-tags")
147
+ objectify(base_uri["instances/#{id}/tags"].get)
148
+ end
149
+
150
+ # TODO: Polymorphic resourceType resources. Repetitive. must refactor
151
+
152
+ # GET /{resourceType}/{id}/attachments
153
+ def instance_attachments(id)
154
+ objectify(base_uri["instances/#{id}/attachments"].get)
155
+ end
156
+
157
+ # DELETE /{resourceType}/{id}/attachments/{name}
158
+ def delete_instance_attachment(id, name)
159
+ objectify(base_uri["instances/#{id}/attachments/#{name}"].delete)
160
+ end
161
+
162
+ # PUT /{resourceType}/{id}/attachments/{name}
163
+ def instance_attachment(id, name, payload = {})
164
+ objectify(base_uri["instances/#{id}/attachments/#{name}"].put(payload))
165
+ end
166
+
167
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-data
168
+ def instance_attachment_compressed_data(id, name)
169
+ objectify(base_uri["instances/#{id}/attachments/#{name}/compressed-data"].get)
170
+ end
171
+
172
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-md5
173
+ def instance_attachment_compressed_md5(id, name)
174
+ objectify(base_uri["instances/#{id}/attachments/#{name}/compressed-md5"].get)
175
+ end
176
+
177
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-size
178
+ def instance_attachment_compressed_size(id, name)
179
+ objectify(base_uri["instances/#{id}/attachments/#{name}/compressed-size"].get)
180
+ end
181
+
182
+ # GET /{resourceType}/{id}/attachments/{name}/data
183
+ def instance_attachment_data(id, name)
184
+ objectify(base_uri["instances/#{id}/attachments/#{name}/data"].get)
185
+ end
186
+
187
+ # GET /{resourceType}/{id}/attachments/{name}/md5
188
+ def instance_attachment_md5(id, name)
189
+ objectify(base_uri["instances/#{id}/attachments/#{name}/md5"].get)
190
+ end
191
+
192
+ # GET /{resourceType}/{id}/attachments/{name}/size
193
+ def instance_attachment_size(id, name)
194
+ objectify(base_uri["instances/#{id}/attachments/#{name}/size"].get)
195
+ end
196
+
197
+ # POST /{resourceType}/{id}/attachments/{name}/verify-md5
198
+ def instance_attachment_verify_md5(id, name)
199
+ objectify(base_uri["instances/#{id}/attachments/#{name}/verify-md5"].get)
200
+ end
201
+
202
+ # GET /{resourceType}/{id}/metadata
203
+ def instance_all_metadata(id)
204
+ objectify(base_uri["instances/#{id}/metadata"].get)
205
+ end
206
+
207
+ # GET /{resourceType}/{id}/metadata/{name}
208
+ def instance_metadata(id, name)
209
+ objectify(base_uri["instances/#{id}/metadata/#{name}"].get)
210
+ end
211
+
212
+ # DELETE /{resourceType}/{id}/metadata/{name}
213
+ def instance_delete_metadata(id, name)
214
+ objectify(base_uri["instances/#{id}/metadata/#{name}"].delete)
215
+ end
216
+
217
+ # PUT /{resourceType}/{id}/metadata/{name}
218
+ def instance_update_metadata(id, name, payload = {})
219
+ objectify(base_uri["instances/#{id}/metadata/#{name}"].put(payload))
220
+ end
221
+
222
+ end
223
+ end
@@ -0,0 +1,55 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Modalities -------------
4
+ # GET /modalities
5
+ def modalities
6
+ objectify(base_uri["modalities"].get)
7
+ end
8
+
9
+ # GET /modalities/{dicom}
10
+ def modality(dicom)
11
+ objectify(base_uri["modalities/#{dicom}"].get)
12
+ end
13
+
14
+ # DELETE /modalities/{dicom}
15
+ def delete_modality(dicom)
16
+ objectify(base_uri["modalities/#{dicom}"].delete)
17
+ end
18
+
19
+ # PUT /modalities/{dicom}
20
+ def modify_modality(dicom)
21
+ objectify(base_uri["modalities/#{dicom}"].put)
22
+ end
23
+
24
+ # POST /modalities/{dicom}/echo
25
+ def modality_echo(dicom, payload = {}) # C-Echo SCU
26
+ objectify(base_uri["modalities/#{dicom}/echo"].post(payload))
27
+ end
28
+
29
+ # POST /modalities/{dicom}/find
30
+ def modality_find(dicom, payload = {})
31
+ objectify(base_uri["modalities/#{dicom}/find"].post(payload))
32
+ end
33
+
34
+ # POST /modalities/{dicom}/find-patient
35
+ def modality_find_patient(dicom, payload = {})
36
+ objectify(base_uri["modalities/#{dicom}/find-patient"].post(payload))
37
+ end
38
+
39
+ # POST /modalities/{dicom}/find-series
40
+ def modality_find_series(dicom, payload = {})
41
+ objectify(base_uri["modalities/#{dicom}/find-series"].post(payload))
42
+ end
43
+
44
+ # POST /modalities/{dicom}/find-study
45
+ def modality_find_study(dicom, payload = {})
46
+ objectify(base_uri["modalities/#{dicom}/find-study"].post(payload))
47
+ end
48
+
49
+ # POST /modalities/{dicom}/store
50
+ def modality_store(dicom, payload = {}) # POST body = UUID series, UUID instance, or raw DICOM file
51
+ objectify(base_uri["modalities/#{dicom}/store"].post(payload))
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,154 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Patients -------------
4
+ # GET /patients
5
+ def all_patients
6
+ objectify(base_uri["patients"].get)
7
+ end
8
+
9
+ # GET /patients/{id}
10
+ def patient(id)
11
+ objectify(base_uri["patients/#{id}"].get)
12
+ end
13
+
14
+ # DELETE /patients/{id}
15
+ def delete_patient(id)
16
+ objectify(base_uri["patients/#{id}"].delete)
17
+ end
18
+
19
+ # POST /patients/{id}/anonymize
20
+ def anonymize_patient(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
21
+ objectify(base_uri["patients/#{id}/anonymize"].post(payload))
22
+ end
23
+
24
+ # GET /patients/{id}/archive
25
+ def archive_patient(id) # Create ZIP
26
+ base_uri["patients/#{id}/archive"].get # CAREFUL! Returns the whole zipfile
27
+ end
28
+
29
+ # GET /patients/{id}/instances
30
+ def patient_instances(id) # Retrieve all the instances of this patient in a single REST call
31
+ objectify(base_uri["patients/#{id}/instances"].get)
32
+ end
33
+
34
+ # GET /patients/{id}/media
35
+ def patient_media(id) # Create a ZIP archive for media storage with DICOMDIR
36
+ base_uri["patients/#{id}/media"].get # CAREFUL! Returns the whole zipfile
37
+ end
38
+
39
+ # POST /patients/{id}/modify
40
+ def modify_patient(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
41
+ objectify(base_uri["patients/#{id}/modify"].post(payload))
42
+ end
43
+
44
+ # GET /patients/{id}/module
45
+ def patient_module(id)
46
+ objectify(base_uri["patients/#{id}/module"].get)
47
+ end
48
+
49
+ # GET /patients/{id}/protected
50
+ def is_patient_protected?(id) # Protection against recycling: "0" means unprotected, "1" protected
51
+ num_to_bool(base_uri["patients/#{id}/protected"].get)
52
+ end
53
+
54
+ # PUT /patients/{id}/protected
55
+ def set_patient_protected(id, boolstatus = true) # Protection against recycling: "0" means unprotected, "1" protected
56
+ status = bool_to_num(boolstatus)
57
+ base_uri["patients/#{id}/protected"].put("#{status}")
58
+ return nil # API returns ""
59
+ end
60
+
61
+ # GET /patients/{id}/series
62
+ def patient_series(id) # Retrieve all the series of this patient in a single REST call
63
+ objectify(base_uri["patients/#{id}/series"].get)
64
+ end
65
+
66
+ # GET /patients/{id}/shared-tags
67
+ def patient_shared_tags(id) # "?simplify" argument to simplify output
68
+ objectify(base_uri["patients/#{id}/shared-tags"].get)
69
+ end
70
+
71
+ # GET /patients/{id}/statistics
72
+ def patient_statistics(id)
73
+ objectify(base_uri["patients/#{id}/statistics"].get)
74
+ end
75
+
76
+ # GET /patients/{id}/studies
77
+ def patient_studies(id) # Retrieve all the studies of this patient in a single REST call
78
+ objectify(base_uri["patients/#{id}/studies"].get)
79
+ end
80
+
81
+ # TODO: Polymorphic resourceType resources. Repetitive. must refactor
82
+
83
+ # GET /{resourceType}/{id}/attachments
84
+ def patient_attachments(id)
85
+ objectify(base_uri["patients/#{id}/attachments"].get)
86
+ end
87
+
88
+ # DELETE /{resourceType}/{id}/attachments/{name}
89
+ def delete_patient_attachment(id, name)
90
+ objectify(base_uri["patients/#{id}/attachments/#{name}"].delete)
91
+ end
92
+
93
+ # PUT /{resourceType}/{id}/attachments/{name}
94
+ def patient_attachment(id, name, payload = {})
95
+ objectify(base_uri["patients/#{id}/attachments/#{name}"].put(payload))
96
+ end
97
+
98
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-data
99
+ def patient_attachment_compressed_data(id, name)
100
+ objectify(base_uri["patients/#{id}/attachments/#{name}/compressed-data"].get)
101
+ end
102
+
103
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-md5
104
+ def patient_attachment_compressed_md5(id, name)
105
+ objectify(base_uri["patients/#{id}/attachments/#{name}/compressed-md5"].get)
106
+ end
107
+
108
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-size
109
+ def patient_attachment_compressed_size(id, name)
110
+ objectify(base_uri["patients/#{id}/attachments/#{name}/compressed-size"].get)
111
+ end
112
+
113
+ # GET /{resourceType}/{id}/attachments/{name}/data
114
+ def patient_attachment_data(id, name)
115
+ objectify(base_uri["patients/#{id}/attachments/#{name}/data"].get)
116
+ end
117
+
118
+ # GET /{resourceType}/{id}/attachments/{name}/md5
119
+ def patient_attachment_md5(id, name)
120
+ objectify(base_uri["patients/#{id}/attachments/#{name}/md5"].get)
121
+ end
122
+
123
+ # GET /{resourceType}/{id}/attachments/{name}/size
124
+ def patient_attachment_size(id, name)
125
+ objectify(base_uri["patients/#{id}/attachments/#{name}/size"].get)
126
+ end
127
+
128
+ # POST /{resourceType}/{id}/attachments/{name}/verify-md5
129
+ def patient_attachment_verify_md5(id, name)
130
+ objectify(base_uri["patients/#{id}/attachments/#{name}/verify-md5"].get)
131
+ end
132
+
133
+ # GET /{resourceType}/{id}/metadata
134
+ def patient_all_metadata(id)
135
+ objectify(base_uri["patients/#{id}/metadata"].get)
136
+ end
137
+
138
+ # GET /{resourceType}/{id}/metadata/{name}
139
+ def patient_metadata(id, name)
140
+ objectify(base_uri["patients/#{id}/metadata/#{name}"].get)
141
+ end
142
+
143
+ # DELETE /{resourceType}/{id}/metadata/{name}
144
+ def patient_delete_metadata(id, name)
145
+ objectify(base_uri["patients/#{id}/metadata/#{name}"].delete)
146
+ end
147
+
148
+ # PUT /{resourceType}/{id}/metadata/{name}
149
+ def patient_update_metadata(id, name, payload = {})
150
+ objectify(base_uri["patients/#{id}/metadata/#{name}"].put(payload))
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,30 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Peers -------------
4
+ # GET /peers
5
+ def peers # Get the list of all the registered plugins
6
+ objectify(base_uri["peers"].get)
7
+ end
8
+
9
+ # GET /peers/{peer}
10
+ def peer(peer)
11
+ objectify(base_uri["peers/#{peer}"].get)
12
+ end
13
+
14
+ # DELETE /peers/{peer}
15
+ def delete_peer(peer)
16
+ objectify(base_uri["peers/#{peer}"].delete)
17
+ end
18
+
19
+ # PUT /peers/{peer}
20
+ def modify_peer(peer, payload = {})
21
+ objectify(base_uri["peers/#{peer}"].put(payload))
22
+ end
23
+
24
+ # GET /peers/{peer}/store
25
+ def peer_store(peer, payload = {}) # POST body = UUID series, UUID instance, or raw DICOM file
26
+ objectify(base_uri["peers/#{peer}/store"].post(payload))
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Plugins -------------
4
+ # GET /plugins
5
+ def plugins # Get the list of all the registered plugins
6
+ objectify(base_uri["plugins"].get)
7
+ end
8
+
9
+ # GET /plugins/explorer.js
10
+ def plugins_explorerjs # Get the JavaScript code that is injected by plugins into Orthanc Explorer
11
+ objectify(base_uri["plugins/explorer.js"].get)
12
+ end
13
+
14
+ # GET /plugins/{id}
15
+ def plugin(id) # Get information about some plugin
16
+ objectify(base_uri["plugins/#{id}"].get)
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,142 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Series -------------
4
+ # GET /series
5
+ def all_series # Darn DICOM, why did you have to call them something that's spelled the same singular and plural?
6
+ objectify(base_uri["series"].get)
7
+ end
8
+
9
+ # GET /series/{id}
10
+ def series(id)
11
+ objectify(base_uri["series/#{id}"].get)
12
+ end
13
+
14
+ # DELETE /series/{id}
15
+ def delete_series(id)
16
+ objectify(base_uri["series/#{id}"].delete)
17
+ end
18
+
19
+ # POST /series/{id}/anonymize
20
+ def anonymize_series(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
21
+ objectify(base_uri["series/#{id}/anonymize"].post(payload))
22
+ end
23
+
24
+ # GET /series/{id}/archive
25
+ def archive_series(id) # Create ZIP
26
+ base_uri["series/#{id}/archive"].get # CAREFUL! Returns the whole zipfile
27
+ end
28
+
29
+ # GET /series/{id}/instances
30
+ def series_instances(id) # Retrieve all the instances of this patient in a single REST call
31
+ objectify(base_uri["series/#{id}/instances"].get)
32
+ end
33
+
34
+ # GET /series/{id}/media
35
+ def series_media(id) # Create a ZIP archive for media storage with DICOMDIR
36
+ base_uri["series/#{id}/media"].get # CAREFUL! Returns the whole zipfile
37
+ end
38
+
39
+ # POST /series/{id}/modify
40
+ def modify_series(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
41
+ objectify(base_uri["series/#{id}/modify"].post(payload))
42
+ end
43
+
44
+ # GET /series/{id}/module
45
+ def series_module(id)
46
+ objectify(base_uri["series/#{id}/module"].get)
47
+ end
48
+
49
+ # GET /series/{id}/patient
50
+ def series_patient(id)
51
+ objectify(base_uri["series/#{id}/patient"].get)
52
+ end
53
+
54
+ # GET /series/{id}/shared-tags
55
+ def series_shared_tags(id) # "?simplify" argument to simplify output
56
+ objectify(base_uri["series/#{id}/shared-tags"].get)
57
+ end
58
+
59
+ # GET /series/{id}/statistics
60
+ def series_statistics(id)
61
+ objectify(base_uri["series/#{id}/statistics"].get)
62
+ end
63
+
64
+ # GET /series/{id}/study
65
+ def series_study(id)
66
+ objectify(base_uri["series/#{id}/study"].get)
67
+ end
68
+
69
+ # TODO: Polymorphic resourceType resources. Repetitive. must refactor
70
+
71
+ # GET /{resourceType}/{id}/attachments
72
+ def series_attachments(id)
73
+ objectify(base_uri["series/#{id}/attachments"].get)
74
+ end
75
+
76
+ # DELETE /{resourceType}/{id}/attachments/{name}
77
+ def delete_series_attachment(id, name)
78
+ objectify(base_uri["series/#{id}/attachments/#{name}"].delete)
79
+ end
80
+
81
+ # PUT /{resourceType}/{id}/attachments/{name}
82
+ def series_attachment(id, name, payload = {})
83
+ objectify(base_uri["series/#{id}/attachments/#{name}"].put(payload))
84
+ end
85
+
86
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-data
87
+ def series_attachment_compressed_data(id, name)
88
+ objectify(base_uri["series/#{id}/attachments/#{name}/compressed-data"].get)
89
+ end
90
+
91
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-md5
92
+ def series_attachment_compressed_md5(id, name)
93
+ objectify(base_uri["series/#{id}/attachments/#{name}/compressed-md5"].get)
94
+ end
95
+
96
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-size
97
+ def series_attachment_compressed_size(id, name)
98
+ objectify(base_uri["series/#{id}/attachments/#{name}/compressed-size"].get)
99
+ end
100
+
101
+ # GET /{resourceType}/{id}/attachments/{name}/data
102
+ def series_attachment_data(id, name)
103
+ objectify(base_uri["series/#{id}/attachments/#{name}/data"].get)
104
+ end
105
+
106
+ # GET /{resourceType}/{id}/attachments/{name}/md5
107
+ def series_attachment_md5(id, name)
108
+ objectify(base_uri["series/#{id}/attachments/#{name}/md5"].get)
109
+ end
110
+
111
+ # GET /{resourceType}/{id}/attachments/{name}/size
112
+ def series_attachment_size(id, name)
113
+ objectify(base_uri["series/#{id}/attachments/#{name}/size"].get)
114
+ end
115
+
116
+ # POST /{resourceType}/{id}/attachments/{name}/verify-md5
117
+ def series_attachment_verify_md5(id, name)
118
+ objectify(base_uri["series/#{id}/attachments/#{name}/verify-md5"].get)
119
+ end
120
+
121
+ # GET /{resourceType}/{id}/metadata
122
+ def series_all_metadata(id)
123
+ objectify(base_uri["series/#{id}/metadata"].get)
124
+ end
125
+
126
+ # GET /{resourceType}/{id}/metadata/{name}
127
+ def series_metadata(id, name)
128
+ objectify(base_uri["series/#{id}/metadata/#{name}"].get)
129
+ end
130
+
131
+ # DELETE /{resourceType}/{id}/metadata/{name}
132
+ def series_delete_metadata(id, name)
133
+ objectify(base_uri["series/#{id}/metadata/#{name}"].delete)
134
+ end
135
+
136
+ # GET /{resourceType}/{id}/metadata/{name}
137
+ def series_update_metadata(id, name, payload = {})
138
+ objectify(base_uri["series/#{id}/metadata/#{name}"].put(payload))
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,147 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Studies -------------
4
+ # GET /studies
5
+ def all_studies
6
+ objectify(base_uri["studies"].get)
7
+ end
8
+
9
+ # GET /studies/{id}
10
+ def study(id)
11
+ objectify(base_uri["studies/#{id}"].get)
12
+ end
13
+
14
+ # DELETE /studies/{id}
15
+ def delete_study(id)
16
+ objectify(base_uri["studies/#{id}"].delete)
17
+ end
18
+
19
+ # POST /studies/{id}/anonymize
20
+ def anonymize_study(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
21
+ objectify(base_uri["studies/#{id}/anonymize"].post(payload))
22
+ end
23
+
24
+ # GET /studies/{id}/archive
25
+ def archive_study(id) # Create ZIP
26
+ base_uri["studies/#{id}/archive"].get # CAREFUL! Returns the whole zipfile
27
+ end
28
+
29
+ # GET /studies/{id}/instances
30
+ def study_instances(id) # Retrieve all the instances of this patient in a single REST call
31
+ objectify(base_uri["studies/#{id}/instances"].get)
32
+ end
33
+
34
+ # GET /studies/{id}/media
35
+ def study_media(id) # Create a ZIP archive for media storage with DICOMDIR
36
+ base_uri["studies/#{id}/media"].get # CAREFUL! Returns the whole zipfile
37
+ end
38
+
39
+ # POST /studies/{id}/modify
40
+ def modify_study(id, payload = {}) # https://code.google.com/p/orthanc/wiki/Anonymization
41
+ objectify(base_uri["studies/#{id}/modify"].post(payload))
42
+ end
43
+
44
+ # GET /studies/{id}/module
45
+ def study_module(id)
46
+ objectify(base_uri["studies/#{id}/module"].get)
47
+ end
48
+
49
+ # GET /studies/{id}/module-patient
50
+ def study_module_patient(id)
51
+ objectify(base_uri["studies/#{id}/module-patient"].get)
52
+ end
53
+
54
+ # GET /studies/{id}/patient
55
+ def study_patient(id)
56
+ objectify(base_uri["studies/#{id}/patient"].get)
57
+ end
58
+
59
+ # GET /studies/{id}/series
60
+ def study_series(id) # Retrieve all the series of this patient in a single REST call
61
+ objectify(base_uri["studies/#{id}/series"].get)
62
+ end
63
+
64
+ # GET /studies/{id}/shared-tags
65
+ def study_shared_tags(id) # "?simplify" argument to simplify output
66
+ objectify(base_uri["studies/#{id}/shared-tags"].get)
67
+ end
68
+
69
+ # GET /studies/{id}/statistics
70
+ def study_statistics(id)
71
+ objectify(base_uri["studies/#{id}/statistics"].get)
72
+ end
73
+
74
+ # TODO: Polymorphic resourceType resources. Repetitive. must refactor
75
+
76
+ # GET /{resourceType}/{id}/attachments
77
+ def study_attachments(id)
78
+ objectify(base_uri["studies/#{id}/attachments"].get)
79
+ end
80
+
81
+ # DELETE /{resourceType}/{id}/attachments/{name}
82
+ def delete_study_attachment(id, name)
83
+ objectify(base_uri["studies/#{id}/attachments/#{name}"].delete)
84
+ end
85
+
86
+ # PUT /{resourceType}/{id}/attachments/{name}
87
+ def study_attachment(id, name, payload = {})
88
+ objectify(base_uri["studies/#{id}/attachments/#{name}"].put(payload))
89
+ end
90
+
91
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-data
92
+ def study_attachment_compressed_data(id, name)
93
+ objectify(base_uri["studies/#{id}/attachments/#{name}/compressed-data"].get)
94
+ end
95
+
96
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-md5
97
+ def study_attachment_compressed_md5(id, name)
98
+ objectify(base_uri["studies/#{id}/attachments/#{name}/compressed-md5"].get)
99
+ end
100
+
101
+ # GET /{resourceType}/{id}/attachments/{name}/compressed-size
102
+ def study_attachment_compressed_size(id, name)
103
+ objectify(base_uri["studies/#{id}/attachments/#{name}/compressed-size"].get)
104
+ end
105
+
106
+ # GET /{resourceType}/{id}/attachments/{name}/data
107
+ def study_attachment_data(id, name)
108
+ objectify(base_uri["studies/#{id}/attachments/#{name}/data"].get)
109
+ end
110
+
111
+ # GET /{resourceType}/{id}/attachments/{name}/md5
112
+ def study_attachment_md5(id, name)
113
+ objectify(base_uri["studies/#{id}/attachments/#{name}/md5"].get)
114
+ end
115
+
116
+ # GET /{resourceType}/{id}/attachments/{name}/size
117
+ def study_attachment_size(id, name)
118
+ objectify(base_uri["studies/#{id}/attachments/#{name}/size"].get)
119
+ end
120
+
121
+ # POST /{resourceType}/{id}/attachments/{name}/verify-md5
122
+ def study_attachment_verify_md5(id, name)
123
+ objectify(base_uri["studies/#{id}/attachments/#{name}/verify-md5"].get)
124
+ end
125
+
126
+ # GET /{resourceType}/{id}/metadata
127
+ def study_all_metadata(id)
128
+ objectify(base_uri["studies/#{id}/metadata"].get)
129
+ end
130
+
131
+ # GET /{resourceType}/{id}/metadata/{name}
132
+ def study_metadata(id, name)
133
+ objectify(base_uri["studies/#{id}/metadata/#{name}"].get)
134
+ end
135
+
136
+ # DELETE /{resourceType}/{id}/metadata/{name}
137
+ def study_delete_metadata(id, name)
138
+ objectify(base_uri["studies/#{id}/metadata/#{name}"].delete)
139
+ end
140
+
141
+ # PUT /{resourceType}/{id}/metadata/{name}
142
+ def study_update_metadata(id, name, payload = {})
143
+ objectify(base_uri["studies/#{id}/metadata/#{name}"].put(payload))
144
+ end
145
+
146
+ end
147
+ end
@@ -0,0 +1,41 @@
1
+ module Orthanc
2
+ class Client
3
+ # ------------- Tools -------------
4
+
5
+ # POST /tools/create-dicom
6
+ def tools_create_dicom(payload = {}) # Create and store a new DICOM instance (experimental)
7
+ objectify(base_uri["tools/create-dicom"].post(payload))
8
+ end
9
+
10
+ # GET /tools/dicom-conformance
11
+ def tools_dicom_conformance # DICOM conformance statement of this version of Orthanc
12
+ base_uri["tools/dicom-conformance"].get
13
+ end
14
+
15
+ # POST /tools/execute-script
16
+ def tools_execute_script(payload = {}) # Execute the Lua script in the POST body (experimental)
17
+ objectify(base_uri["tools/execute-script"].post(payload))
18
+ end
19
+
20
+ # GET /tools/generate-uid
21
+ def tools_generate_uid(level) # "level" argument among "patient", "study", "series" and "instance"
22
+ objectify(base_uri["tools/generate-uid"].get({params: {level: level}}))
23
+ end
24
+
25
+ # POST /tools/lookup
26
+ def tools_lookup(payload = {}) # Map DICOM UIDs to Orthanc identifiers
27
+ objectify(base_uri["tools/lookup"].post(payload))
28
+ end
29
+
30
+ # GET /tools/now
31
+ def tools_now # Returns the current datetime in the ISO 8601 format
32
+ base_uri["tools/now"].get
33
+ end
34
+
35
+ # POST /tools/reset
36
+ def tools_reset(payload = {}) # Hot restart of Orthanc, the configuration file will be read again
37
+ objectify(base_uri["tools/reset"].post(payload))
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Orthanc
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'orthanc/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "orthanc"
8
+ spec.version = Orthanc::VERSION
9
+ spec.authors = ["Simon Rascovsky"]
10
+ spec.email = ["simonmd@gmail.com"]
11
+
12
+ spec.summary = "Client for the Orthanc DICOM server API"
13
+ spec.description = "Client for the Orthanc DICOM server REST API"
14
+ spec.homepage = "https://github.com/simonmd/orthanc-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+
25
+
26
+ spec.add_dependency 'rest-client', "~> 1.8"
27
+ spec.add_dependency 'recursive-open-struct', "~> 0.6.3"
28
+ spec.add_dependency 'plissken', "~> 0.2.0"
29
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: orthanc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Simon Rascovsky
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-04-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.9'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.9'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rest-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: recursive-open-struct
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.6.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.6.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: plissken
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
83
+ description: Client for the Orthanc DICOM server REST API
84
+ email:
85
+ - simonmd@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - Gemfile
93
+ - README.md
94
+ - Rakefile
95
+ - bin/console
96
+ - bin/setup
97
+ - lib/orthanc.rb
98
+ - lib/orthanc/client.rb
99
+ - lib/orthanc/instances.rb
100
+ - lib/orthanc/modalities.rb
101
+ - lib/orthanc/patients.rb
102
+ - lib/orthanc/peers.rb
103
+ - lib/orthanc/plugins.rb
104
+ - lib/orthanc/series.rb
105
+ - lib/orthanc/studies.rb
106
+ - lib/orthanc/tools.rb
107
+ - lib/orthanc/version.rb
108
+ - orthanc.gemspec
109
+ homepage: https://github.com/simonmd/orthanc-ruby
110
+ licenses:
111
+ - MIT
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 2.4.6
130
+ signing_key:
131
+ specification_version: 4
132
+ summary: Client for the Orthanc DICOM server API
133
+ test_files: []