Class: VisitRawDataDirectory

Inherits:
Object
  • Object
show all
Defined in:
lib/metamri/visit_raw_data_directory.rb

Overview

Encapsulates a directory of data acquired during one participant visit. These are the raw data directories that are transfered directly from the scanners and archived in the raw data section of the vtrak filesystem. After initializing, the visit can be scanned to extract metadata for all of the images acquired during the visit. The scanning is done in a fairly naive manner: the visit directory is recursively walked and in each subdirectory any and all pfiles will be imported in addition to one single dicom if any exist. Thus, only a single dicom file among many in a scan session is used to retrieve information. checking the individual files for data integrity must be handled elsewhere if at all.

Constant Summary collapse

PREPROCESS_REPOSITORY_DIRECTORY =
'/Data/vtrak1/preprocessed/visits'
DATAPANDA_SERVER =
'https://adrcdev2.dom.wisc.edu'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(directory, scan_procedure_name = nil) ⇒ VisitRawDataDirectory

A new Visit instance needs to know the path to its raw data and scan_procedure name. The scan_procedure name must match a name in the database, if not a new scan_procedure entry will be inserted.

Raises:

  • (IOError)


70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/metamri/visit_raw_data_directory.rb', line 70

def initialize(directory, scan_procedure_name=nil)
  raise(IOError, "Visit directory not found: #{directory}") unless File.directory? File.expand_path(directory)
  @visit_directory = File.expand_path(directory)
  @working_directory = Dir.tmpdir
  @datasets = Array.new
  @timestamp = nil
  @rmr_number = nil
  @scan_procedure_name = scan_procedure_name.nil? ? get_scan_procedure_based_on_raw_directory : scan_procedure_name
  @db = nil
  @exam_number = nil
  initialize_log
end

Instance Attribute Details

#database_idObject

The id of the visit to be used when doing reverse-lookup in data panda.



59
60
61
# File 'lib/metamri/visit_raw_data_directory.rb', line 59

def database_id
  @database_id
end

#datasetsObject

An array of :RawImageDataset objects acquired during this visit.



43
44
45
# File 'lib/metamri/visit_raw_data_directory.rb', line 43

def datasets
  @datasets
end

#dbObject

Returns the value of attribute db.



55
56
57
# File 'lib/metamri/visit_raw_data_directory.rb', line 55

def db
  @db
end

#dicom_study_uidObject (readonly)

DICOM Study UID (Visit/Study Unique Identifier)



61
62
63
# File 'lib/metamri/visit_raw_data_directory.rb', line 61

def dicom_study_uid
  @dicom_study_uid
end

#exam_numberObject

scanner-defined study id / exam number



53
54
55
# File 'lib/metamri/visit_raw_data_directory.rb', line 53

def exam_number
  @exam_number
end

#rmr_numberObject

RMR number for this visit.



47
48
49
# File 'lib/metamri/visit_raw_data_directory.rb', line 47

def rmr_number
  @rmr_number
end

#scan_procedure_nameObject (readonly)

scan_procedure name



49
50
51
# File 'lib/metamri/visit_raw_data_directory.rb', line 49

def scan_procedure_name
  @scan_procedure_name
end

#scanidObject

Scan ID is the short name for the scan (tbiva018, tbiva018b)



57
58
59
# File 'lib/metamri/visit_raw_data_directory.rb', line 57

def scanid
  @scanid
end

#scanner_sourceObject

scanner source



51
52
53
# File 'lib/metamri/visit_raw_data_directory.rb', line 51

def scanner_source
  @scanner_source
end

#timestampObject

Timestamp for this visit, obtained from the first :RawImageDataset



45
46
47
# File 'lib/metamri/visit_raw_data_directory.rb', line 45

def timestamp
  @timestamp
end

#visit_directoryObject (readonly)

The absolute path of the visit directory, as a string.



41
42
43
# File 'lib/metamri/visit_raw_data_directory.rb', line 41

def visit_directory
  @visit_directory
end

Instance Method Details

#attributes_for_active_recordObject

use this to initialize Visit objects in the rails app



132
133
134
135
136
137
138
139
140
141
# File 'lib/metamri/visit_raw_data_directory.rb', line 132

def attributes_for_active_record
  { 
    :date => @timestamp.to_s, 
    :rmr => @rmr_number, 
    :path => @visit_directory, 
    :scanner_source => @scanner_source ||= get_scanner_source,
    :scan_number => @exam_number,
    :dicom_study_uid => @study_uid
  }
end

#db_insert!(db_file) ⇒ Object

Inserts each dataset in this visit into the specified database. The specifics of the database insert are handled by the #RawImageDataset class.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/metamri/visit_raw_data_directory.rb', line 145

def db_insert!(db_file)
  @db = SQLite3::Database.new(db_file)
  @db.results_as_hash = true
  @db.type_translation = true
  
  begin
    # checks scan_procedure in db, inserts if neccessary, returns id
    scan_procedure_id = fetch_or_insert_scan_procedure
    
    # insert or update visit as needed
    if visit_is_new? # this is a new visit
      visit_id = insert_new_visit(scan_procedure_id)    
    else # visit already exists in DB
      visit_id = get_existing_visit_id
      update_existing_visit(visit_id, scan_procedure_id)
    end
  
    # insert each dataset from the visit, also insert an entry in series descriptions table if necessary.
    @datasets.each do |dataset|
      update_series_descriptions_table(dataset.series_description)
      if dataset_is_new?(dataset)
        insert_new_dataset(dataset, visit_id)
      else # dataset is already in DB
        dataset_id = get_existing_dataset_id(dataset)
        update_existing_dataset(dataset, dataset_id)
      end
    end
  rescue Exception => e
    puts e.message
    puts e.backtrace
  ensure
    @db.close
    @db = nil
  end
end

#default_preprocess_directoryObject



181
182
183
# File 'lib/metamri/visit_raw_data_directory.rb', line 181

def default_preprocess_directory
  return File.join(PREPROCESS_REPOSITORY_DIRECTORY, scan_procedure_name, scanid)
end

#scan(options = {}) ⇒ Object

Recursively walks the filesystem inside the visit directory. At each subdirectory, any and all pfiles are scanned and imported in addition to one and only one dicom file. After scanning exception if no valid rmr is found in the datasets, be prepared to catch it.

Options

  • :ignore_patterns – Array of Regex’es. An array of Regular Expressions that will be used to skip heavy directories.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/metamri/visit_raw_data_directory.rb', line 92

def scan(options = {})
  flash "Scanning visit raw data directory #{@visit_directory}" if $LOG.level <= Logger::INFO
  default_options = {:ignore_patterns => []}
  options = default_options.merge(options)
  unless options[:ignore_patterns].empty?
    puts "Ignoring directories matching: #{options[:ignore_patterns].join(", ")}" if $LOG.level <= Logger::INFO
  end
  
  d = Pathname.new(@visit_directory)
  d.each_subdirectory do |dd|
    flash "ppppppppp #{dd}" if $LOG.level <= Logger::INFO
    begin
      matches = options[:ignore_patterns].collect {|pat| dd.to_s =~ pat ? dd : nil }.compact
      next unless matches.empty?  
      # if dd is P*.7.bz2 or P*.7 , check for P*.7.summary 
      dd.each_pfile_non_bz2  { |pf| @datasets << import_dataset(pf, dd);  @datasets.last.print_scan_status if $LOG.level == Logger::INFO }
      dd.each_pfile  { |pf|  # check for p*.7.summary
            @datasets << import_dataset(pf, dd); @datasets.last.print_scan_status if $LOG.level == Logger::INFO }
      dd.first_dicom { |fd| @datasets << import_dataset(fd, dd); @datasets.last.print_scan_status if $LOG.level == Logger::INFO }
     if (dd.to_s).include?("scan_archives") and (dd.to_s).include?("raw_data")
       dd.each_scanner_archive_summary { |sa|  @datasets << import_dataset(sa, dd);  @datasets.last.print_scan_status if $LOG.level == Logger::INFO }
     end
    rescue StandardError => e
      raise(e, "There was an error scaning dataset #{dd}: #{e}")
    end
  end
  
  unless @datasets.size == 0
    @timestamp = get_visit_timestamp
    @rmr_number = get_rmr_number
    @scanner_source = get_scanner_source
    @exam_number = get_exam_number
    @study_uid = get_study_uid unless dicom_datasets.empty?
    flash "Completed scanning #{@visit_directory}" if $LOG.level <= Logger::DEBUG
  else
    raise(IndexError, "No datasets could be scanned for directory #{@visit_directory}")
  end
end

#to_nifti!(output_directory = Dir.tmpdir) ⇒ Object

Walks through the dicom datasets in this Scan Visit directory and performs naive file conversion to nifti format, which is useful for basic quality checking. Accepts an output directory as an optional argument, defaults to the system temp directory. Returns an array of the created nifti files.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/metamri/visit_raw_data_directory.rb', line 190

def to_nifti!(output_directory = Dir.tmpdir)
  flash "Converting raw data directory #{@visit_directory} to Niftis in #{output_directory}"
  nifti_output_files = Array.new
  
  scan if @datasets.empty? 
      
  @datasets.each do |dataset|
    nifti_output_path = output_directory
    #v_basename =File.basename(dataset.directory).gsub(/-/,"").gsub(/_/,"").gsub(/\:/,"").gsub(/\//,"")
    #v_series_description = "."+dataset.series_description.gsub(/ /,"").gsub(/-/,"").gsub(/_/,"").gsub(/\:/,"").gsub(/\//,"")
    # 20171120 addition
    v_basename =File.basename(dataset.directory)
    v_series_description = "."+dataset.series_description
    v_series_description_full_replace = v_series_description
    # need to get the scan series numbers - take the v_basename/folder name -- replace all the series description stuff
    # end up with scan series number - add to the end of the series_description to get the nii file nam 
    if !v_basename.nil?
      v_basename = v_basename.gsub(/ /,"").gsub(/\-/,"").gsub(/\_/,"").gsub(/\(/,"").gsub(/\)/,"").gsub(/\=/,"").gsub(/\+/,"").gsub(/\'/,"").gsub(/\^/,"").gsub(/\,/,"").gsub(/\:/,"").gsub(/\*/,"star")
    end
    if !v_series_description_full_replace.nil?
        v_series_description_full_replace =  v_series_description_full_replace.gsub(/ /,"").gsub(/\-/,"").gsub(/\_/,"").gsub(/\(/,"").gsub(/\)/,"").gsub(/\=/,"").gsub(/\+/,"").gsub(/\'/,"").gsub(/\^/,"").gsub(/\,/,"").gsub(/\:/,"").gsub(/\*/,"star")
    end 

#puts "ccccc end v_basename="+v_basename
#puts "dddd end v_series_description_full_replace="+v_series_description_full_replace

    if v_basename.include? v_series_description
         # want the scan series number - e.g. 00001 at the end
         v_tmp_filename =  v_basename.gsub(v_series_description,"")
         nifti_filename = "#{scanid}_#{dataset.series_description.escape_filename}_#{v_tmp_filename}.nii"
    else
         nifti_filename = "#{scanid}_#{dataset.series_description.escape_filename}_#{File.basename(dataset.directory).escape_filename}.nii"
    end

    #nifti_filename = "#{scanid}_#{dataset.series_description.escape_filename}_#{File.basename(dataset.directory).escape_filename}.nii"

    Pathname.new(dataset.directory).all_dicoms do |dicom_files| 
      nifti_input_path = File.dirname(dicom_files.first)
      nifti_conversion_command, nifti_output_file = dataset.to_nifti!(nifti_output_path, nifti_filename, :input_directory => nifti_input_path, :append_modality_directory => true)
      nifti_output_files << nifti_output_file
    end
  end
  
  return nifti_output_files
end

#to_sObject



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/metamri/visit_raw_data_directory.rb', line 240

def to_s
  puts; @visit_directory.length.times { print "-" }; puts
  puts "#{@visit_directory}"
  puts "#{@rmr_number} - #{@timestamp.strftime('%F')} - #{@scanner_source} - #{@exam_number unless @exam_number.nil?}"
  puts
  puts RawImageDataset.to_table(@datasets)
  return
rescue NameError => e
  puts e
  if @datasets.first.class.to_s == "RawImageDatasetResource"
    @datasets = @datasets.map { |ds| ds.to_metamri_image_dataset }
  end
  
  # puts @datasets.first.class.to_s
  # puts @datasets
  
  # Header Line
  printf "\t%-15s %-30s [%s]\n", "Directory", "Series Description", "Files"
  
  # Dataset Lines
  @datasets.sort_by{|ds| [ds.timestamp, File.basename(ds.directory)] }.each do |dataset|
    printf "\t%-15s %-30s [%s]\n", File.basename(dataset.directory), dataset.series_description, dataset.file_count
  end
  
  # Reminder Line
  puts "(This would be much prettier if you hirb was installed (just type: gem install hirb)."
  
  return
end