Couple of question regarding Python Plug-ins

  1. Can you define more than one Python script in the config.json file or just 1 ? If just 1, do you then have to put all of your python code in 1 script. It looks like you can register multiple paths in a single file, so that must be possible, but it would be nice to have separate scripts.

  2. Not sure this is worthwhile, but I am working on a python script to create MWL files. I aleady have another method to do that, but I still need to put the file on the Orthanc server where it belongs. It would be nice to be able to do all of that through a REST API call. I tested on OS X, and it at least creates a .txt file from a template by POSTING the placeholder values using a new registered callback path. That all seems to work. It saves the .txt file in the same directory as the python script, so I’ll have to figure out how to write it to the WorklistsDatabase folder instead.

I got some sort of ASCII char violation or error at first, but the “Zapped Gremlins” with my editor and that took care of that. Must have had some stray characters.

with open(filename, ‘w’) as filehandle: just write to the same dir as the script.

There is something as SO, but not sure how to use that:

import sys,os sys.path.append(os.path.realpath(‘…’)) ???

  1. Can you call / invoke dcmtk commands (or even other shell scripts) via a Python Plug-in script ? Is there a “dump2dcm” feature built into Orthanc ? Orthanc MWL reference. It sounds like you maybe can ? Execute Shell Commands with Python

Thanks.

Progress:

I have this at the end of the script.

with open(filename + “.txt”, ‘w’) as filehandle:

set the new output channel

sys.stdout = filehandle
for line in mwl:
print(line)

restore the old output channel

sys.stdout = original
stream = os.popen('/usr/local/Cellar/dcmtk/3.6.5/bin/dump2dcm ’ + filename + ".txt " + filename + “.wl” )
output = stream.read()
output
response = {
“message”:"MWL File Written: " + filename,
“errors”: 0
}
output.AnswerBuffer(json.dumps(response, indent = 3), ‘application/json’)

That creates a .txt file like this:

Dicom-File-Format

Dicom-Meta-Information-Header

Used TransferSyntax: Little Endian Explicit

(0002,0000) UL 202 # 4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00 # 2, 1 FileMetaInformationVersion
(0002,0002) UI [MediaStorageSOPClassUID] # MediaStorageSOPClassUID 1.2.276.0.7230010.3.1.0.1 vs 1.2.840.10008.3.1.2.3.3 for Easote
(0002,0003) UI [1.2.276.0.7230010.3.1.4.2831176407.11154.1448031138.805061] # 58, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianExplicit # 20, 1 TransferSyntaxUID
(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.0] # 28, 1 ImplementationClassUID
(0002,0013) SH [OFFIS_DCMTK_360] # 16, 1 ImplementationVersionName

Dicom-Data-Set

Used TransferSyntax: Little Endian Explicit

(0008,0005) CS [ISO_IR 100] # SpecificCharacterSet, 16 Bytes Uppercase and #s _ and space
(0008,0050) SH [AccessionNumber] # AccessionNumber
(0008,0060) CS [Modality] # Modality
(0010,0010) PN [PatientName] # PatientName
(0010,0020) LO [PatientID] # PatientID
(0010,0030) DA [PatientBirthDate] # PatientBirthDate
(0010,0040) CS [PatientSex] # PatientSex
(0010,2000) LO [MedicalAlerts] # MedicalAlerts
(0010,2110) LO [Allergies] # Allergies
(0010,21B0) LT [AdditionalPatientHistory] # AdditionalPatientHistory
(0020,000d) UI [StudyInstanceUID] # StudyInstanceUID
(0032,1032) PN [RequestingPhysician] # RequestingPhysician
(0032,1060) LO [RequestedProcedureDescription] # RequestedProcedureDescription
(0040,0001) AE [ScheduleStationAETitle] # ScheduleStationAETitle
(0040,0002) DA [ScheduledProcedureStepStartDate] # ScheduledProcedureStepStartDate
(0040,0003) TM [ScheduledProcedureStepStartTime] # ScheduledProcedureStepStartTime
(0040,1001) SH [RequestedProcedureID] # RequestedProcedureID
(0040,1003) SH [RequestedProcedurePriority] # RequestedProcedurePriority

and it also creates a .wl file.

When I dcmdump that file I get:

Dicom-File-Format

Dicom-Meta-Information-Header

Used TransferSyntax: Little Endian Explicit

(0002,0000) UL 194 # 4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\01 # 2, 1 FileMetaInformationVersion
(0002,0002) UI [1.2.276.0.7230010.3.1.0.1] # 26, 1 MediaStorageSOPClassUID
(0002,0003) UI [1.2.276.0.7230010.3.1.4.0.29343.1597711468.412721] # 50, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianExplicit # 20, 1 TransferSyntaxUID
(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.5] # 28, 1 ImplementationClassUID
(0002,0013) SH [OFFIS_DCMTK_365] # 16, 1 ImplementationVersionName

Dicom-Data-Set

Used TransferSyntax: Little Endian Explicit

with nothing after the Little Endian Explicit.

I do get some warning and errors in the Orthanc log:

W: output transfer syntax unknown, assuming --write-xfer-little
E0817 19:38:34.912922 PluginsManager.cpp:164] Error in the REST callback, traceback:
<type ‘exceptions.AttributeError’>
‘str’ object has no attribute ‘AnswerBuffer’

So I guess you can execute dcmtk commands from Python ? Can we just use the dcmtk that comes bundled with the Orthanc Source, or use the system package ?

If I comment out:

output = stream.read()

output

then when I dcmdump the .wl file it almost matches the original .txt template.

I see Unknown Version of MetaHeader detected: 0x0000, supported: 0x0001 in the log, and the header from the .txt is:

Dicom-File-Format

Dicom-Meta-Information-Header

Used TransferSyntax: Little Endian Explicit

(0002,0000) UL 202 # 4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00 # 2, 1 FileMetaInformationVersion
(0002,0002) UI [MediaStorageSOPClassUID] # MediaStorageSOPClassUID 1.2.276.0.7230010.3.1.0.1 vs 1.2.840.10008.3.1.2.3.3 for Easote
(0002,0003) UI [1.2.276.0.7230010.3.1.4.2831176407.11154.1448031138.805061] # 58, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianExplicit # 20, 1 TransferSyntaxUID
(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.0] # 28, 1 ImplementationClassUID
(0002,0013) SH [OFFIS_DCMTK_360] # 16, 1 ImplementationVersionName

vs. from the dcmdump of the generated .wl file.

Dicom-File-Format

Dicom-Meta-Information-Header

Used TransferSyntax: Little Endian Explicit

(0002,0000) UL 200 # 4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\00 # 2, 1 FileMetaInformationVersion
(0002,0002) UI [MediaStorageSOPClassUID] # 24, 1 MediaStorageSOPClassUID
(0002,0003) UI [1.2.276.0.7230010.3.1.4.2831176407.11154.1448031138.805061] # 58, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianExplicit # 20, 1 TransferSyntaxUID
(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.5] # 28, 1 ImplementationClassUID
(0002,0013) SH [OFFIS_DCMTK_365] # 16, 1 ImplementationVersionName

Thanks.

Might have fixed it. You must have escape and escape character in a string ?

.py script.

mwl.append(“(0002,0001) OB 00\01 # 2, 1 FileMetaInformationVersion”)

generated .txt template

(0002,0000) UL 202 # 4, 1 FileMetaInformationGroupLength

(0002,0001) OB 00\01 # 2, 1 FileMetaInformationVersion

dcmdumped .wl file:

(0002,0000) UL 200 # 4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\01 # 2, 1 FileMetaInformationVersion

1- No, just 1. You’ll have to create one main script that includes other Python files.

2- Check out the “/tools/create-dicom” route in the REST API, for which many integration tests are available:
https://hg.orthanc-server.com/orthanc-tests/file/Orthanc-1.7.2/Tests/Tests.py#l1473

https://book.orthanc-server.com/users/advanced-rest.html#attaching-pdf-file-as-dicom-series

3- Check out the standard functions “subprocess.check_output()” or “subprocess.check_call()” from Python.

Thank you for that information. The Unit Test file is a nice reference.

On UBUNTU, Orthanc says it is using Python 2.7.17, and on OS X 2.7.16 (Orthanc version: mainline (20200804T203159)). I might have slightly different version or Orthanc running and different version of Python. OS X is actually using 3.x from the CLI, so that is messed up on my system. Does Orthanc use an embedded version of Python as opposed to the system Python version? If it uses the system version can we use pip to install any additional modules and then just import them into scripts that we write for Orthanc. I’ve been unable to use pydicom on the Mac, probably because it has multiple versions of Python that are not linked appropriately.

I have already used /tools/create-dicom to attach a PDF file to a study. (Now sure what other MIME types that supports, presumably as base64). That works fine, although I’ve been doing that through a remote API call with an intermediate step that routes the file to Orthanc. That actually works fine, and I’m using wkhtmltopdf on the Mac or UBUNTU (PHP wrapper) to create the document from HTML, and then attach it via the REST API. There is apparently a Python wrapper for the same ? wkhtmltopdf python wrapper. Can I just install with pip and then use it in Python script for Orthanc if I wish ? I’d rather integrate the PDF file creation from HTML also into a Python script for Orthanc, using pdfkit.

Finally, the /system path gives something like this. It would be useful to read additional informaiton regarding the configuration, specifically the WorklistsDatabase folder. Is there a way to get that information.

{
“ApiVersion” : 7,
“DatabaseBackendPlugin” : “/home/sscotti/Desktop/orthanc_plugins/libOrthancPostgreSQLIndex.so.3.2”,
“DatabaseVersion” : 6,
“DicomAet” : “CML_ORTHANC”,
“DicomPort” : 4242,
“HttpPort” : 8042,
“IsHttpServerSecure” : true,
“Name” : “UbuntuOrthanc”,
“PluginsEnabled” : true,
“StorageAreaPlugin” : null,
“Version” : “mainline”
}

Just an FYI. Making more progress. Might be useful to some others. I have to update things on Github, but here are some screen shots.

Main thing I am tyring to figure out is how to get the path to the Worklists database in the config file from a Python script. It would be useful to have a method to get almost any parameter from the Config File via a REST call or Python script.

apitool.pdf (171 KB)

pagination.pdf (104 KB)

apitool2.pdf (159 KB)

Does Orthanc use an embedded version of Python as opposed to the system Python version?

No.

If it uses the system version can we use pip to install any additional modules and then just import them into scripts that we write for Orthanc. I’ve been unable to use pydicom on the Mac, probably because it has multiple versions of Python that are not linked appropriately.

Indeed, as can be read in the CMakeLists.txt:
https://hg.orthanc-server.com/orthanc-python/file/47/CMakeLists.txt#l15

=> Change “3.6” by what you need, and recompile from scratch.

It would be useful to read additional informaiton regarding the configuration, specifically the WorklistsDatabase folder. Is there a way to get that information.

Python plugin:

import json

import orthanc

def OnRest(output, uri, **request):
config = json.loads(orthanc.GetConfiguration())
path = config[‘Worklists’][‘Database’]
output.AnswerBuffer(path, ‘text/plain’)

orthanc.RegisterRestCallback(‘/worklist-path’, OnRest)

Thanks !! Should have plenty to work with for awhile.

Sébastien,

Thank you much for your help. Ran into an annoying problem regarding one of my python scripts. I’m using pdfkit as a wrapper for wkhtmltopdf. I also sometimes run Orthanc in the foreground from the CLI using something like:

/path/to/orthanc /path/to/config/config.json --verbose

and using an init.d like:

/etc/init.d/orthanc (start|stop|restart)

Orthanc starts up either way, and the logs show all plug-ins loading, and the Explorer and most of my API also work fine.

There is some sort of issue with the python wrapper for wkhtmltopdf though. It works when I start Orthanc from the CLI, but it does not when I use the init.d script. I’m running as root for the init.d and sudo user from the CLI.

The relevent code in the Python script is: config = pdfkit.configuration(wkhtmltopdf=bytes(“/usr/local/bin/wkhtmltopdf”, ‘utf8’))

Like I said, from the CLI, no errors. I do have a thing to catch Exceptions in the script, and this is what it returns when I startup using the init.d script.

“No wkhtmltopdf executable found: "b’'"\nIf this file exists please check that this process can read it. Otherwise please install wkhtmltopdf - https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf

My ENV path has:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

So it seems that it can’t find the executable or otherwise does not have permissions when I start up using the init.d script.
The permissions for the executable are 755 and the owner is root.

This question is related to the administration of GNU/Linux systems, and is thus unrelated to Orthanc.

Please ask your system administrator, and ideally post her answer on this forum.