Skip to content

Commit

Permalink
Merge pull request #26 from piiq/feature/pypi
Browse files Browse the repository at this point in the history
Publish to PyPI under "thedicomsort" name
  • Loading branch information
pieper authored Feb 3, 2023
2 parents e8908a9 + c22e8ba commit 94cae7a
Show file tree
Hide file tree
Showing 5 changed files with 1,426 additions and 28 deletions.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,23 @@ dicomsort returns with a count for both DICOM files organized and non-DICOM (or
It aborts with an error if it is to overwrite any existing file.


Usage
-----
Installation
------------

```bash
pip install thedicomsort
```
% dicomsort.py --help


```bash
% dicomsort --help
dicomsort [options...] sourceDir targetDir/<patterns>

where [options...] can be:
[-z,--compressTargets] - create a .zip file in the target directory
[-d,--deleteSource] - remove source files/directories after sorting
[-f,--forceDelete] - remove source without confirmation
[-k,--keepGoing] - report but ignore dupicate target files
[-k,--keepGoing] - report but ignore duplicate target files
[-v,--verbose] - print diagnostics while processing
[-s,--symlink] - create a symlink to dicom files in sourceDir instead of copying them
[-t,--test] - run the built in self test (requires internet)
Expand All @@ -45,7 +50,7 @@ dicomsort [options...] sourceDir targetDir/<patterns>
names based on the dicom tags in the file.

If patterns are not specified, the following default is used:

%PatientName-%Modality%StudyID-%StudyDescription-%StudyDate/%SeriesNumber_%SeriesDescription-%InstanceNumber.dcm

Example 1:
Expand All @@ -60,13 +65,7 @@ Example 2:

find DicomSourceDir/ | grep "IMA$" | dicomsort -s "" DicomTargetDir

would scan DicomSourceDir for file pathnames ending in IMA and create an
would scan DicomSourceDir for file path names ending in IMA and create an
output directory DicomTargetDir. The folder structure will be created using
the default pattern with symbolic links to the source dicom data files.
```

Requires
========
Python 2.x or 3.x

pydicom
34 changes: 18 additions & 16 deletions dicomsort.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
See the License.txt file or http://slicer.org for full text.
"""


# {{{ packages and logging utilities

# standard python includes
import sys, os, traceback
import sys
import os
import traceback
import shutil
import time
import tempfile
import urllib
import urllib.request
import zipfile

# special public packages
Expand Down Expand Up @@ -97,7 +97,7 @@ def setOptions(self,options):
complaining if require options are missing, and filling in
optional options with default values if not specified"""
for option in self.requiredOptions:
if not option in options:
if option not in options:
return False
for option in self.defaultOptions:
if option not in options:
Expand All @@ -113,7 +113,7 @@ def setOptions(self,options):

def safeFileName(self,fileName):
"""Remove any potentially dangerous or confusing characters from
the file name by mapping them to reasonable subsitutes"""
the file name by mapping them to reasonable substitutes"""
underscores = r"""+`~!@#$%^&*(){}[]/=\|<>,.":' """
safeName = ""
for c in fileName:
Expand Down Expand Up @@ -242,7 +242,7 @@ def renameFile(self,file):
return False

# check for valid path - abort program to avoid overwrite
path = self.pathFromDatasetPattern(ds, safe=(not sorter.options['unsafe']))
path = self.pathFromDatasetPattern(ds, safe=(not self.options['unsafe']))
if os.path.exists(path):
print('\nSource file: %s' % file)
print('Target file: %s' % path)
Expand All @@ -267,9 +267,9 @@ def renameFile(self,file):
if self.options['verbose']:
print("Copied %s, to %s" % (file,path))
except (IOError, os.error) as why:
print( "Dicom file copy/symlink IO error on output pathname >%s< Exception >%s<" % (path,str(why)) )
print( "Dicom file copy/symlink IO error on output pathname >%s< Exception >%s<" % (path,str(why)) )
if self.options['deleteSource'] or self.options['forceDelete']:
print ("Halting execution on IO error because delteSource or forceDelete options could cause data loss.")
print ("Halting execution on IO error because deleteSource or forceDelete options could cause data loss.")
sys.exit(1)

# keep track of files and new directories
Expand Down Expand Up @@ -343,7 +343,7 @@ def downloadFileIfNeeded(self,url,destination,expectedSize):
self.downloadPercent = 0
print('Requesting download of %s from %s...\n' % (destination, url))
try:
urllib.urlretrieve(url, destination, self.downloadReportHook)
urllib.request.urlretrieve(url, destination, self.downloadReportHook)
print('Download finished')
except IOError as e:
print('Download failed: %s' % e)
Expand All @@ -363,7 +363,7 @@ def usage():
[-z,--compressTargets] - create a .zip file in the target directory
[-d,--deleteSource] - remove source files/directories after sorting
[-f,--forceDelete] - remove source without confirmation
[-k,--keepGoing] - report but ignore dupicate target files
[-k,--keepGoing] - report but ignore duplicate target files
[-v,--verbose] - print diagnostics while processing
[-s,--symlink] - create a symlink to dicom files in sourceDir instead of copying them
[-t,--test] - run the built in self test (requires internet)
Expand Down Expand Up @@ -391,7 +391,7 @@ def usage():
find DicomSourceDir/ | grep "IMA$" | dicomsort -s "" DicomTargetDir
would scan DicomSourceDir for file pathnames ending in IMA and create an
would scan DicomSourceDir for file path names ending in IMA and create an
output directory DicomTargetDir. The folder structure will be created using
the default pattern with symbolic links to the source dicom data files.
"""
Expand Down Expand Up @@ -432,7 +432,7 @@ def selfTest(sorter):
targetDir = os.path.join(tempfile.tempdir, 'dicomsort-output')
if os.path.exists(targetDir):
shutil.rmtree(targetDir)
targetPattern = targetDir + '/%PatientName/%StudyDesciption-%StudyDate/%SeriesDescription-%SeriesNumber-%InstanceNumber.dcm'
targetPattern = targetDir + '/%PatientName/%StudyDescription-%StudyDate/%SeriesDescription-%SeriesNumber-%InstanceNumber.dcm'
options = sorter.options
options['sourceDir'] = dataDir
options['targetPattern'] = targetPattern
Expand Down Expand Up @@ -472,7 +472,7 @@ def parseArgs(sorter,args):
print ("Source directory does not exist: %s" % options['sourceDir'])
sys.exit(1)
if options['symlink'] and (options['compressTargets'] or options['deleteSource'] or options['forceDelete']):
print ("symlink option is not compatible with compressTargets, delteSource, or forceDelete options")
print ("symlink option is not compatible with compressTargets, deleteSource, or forceDelete options")
sys.exit(1)

def confirmDelete(sorter):
Expand All @@ -484,8 +484,7 @@ def confirmDelete(sorter):
return True
return False


if __name__ == '__main__':
def main():
sorter = DICOMSorter()
try:
parseArgs(sorter,sys.argv[1:])
Expand Down Expand Up @@ -515,6 +514,9 @@ def confirmDelete(sorter):
traceback.print_exc()
os._exit(1)

if __name__ == '__main__':
main()

# }}}

# vim:set sr et ts=4 sw=4 ft=python fenc=utf-8: // See Vim, :help 'modeline
Expand Down
Loading

0 comments on commit 94cae7a

Please sign in to comment.