Mercurial > hg > orthanc-book
changeset 703:a589668768d7
moving python samples in separate files (2)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 11 Jun 2021 10:07:12 +0200 |
parents | 6e02cd89eb6a |
children | ba2403ebd4b7 |
files | Sphinx/source/plugins/python.rst Sphinx/source/plugins/python/accessing-new-instance.py Sphinx/source/plugins/python/autorouting-1.py Sphinx/source/plugins/python/autorouting-2.py Sphinx/source/plugins/python/changes-deadlock-3.0.py Sphinx/source/plugins/python/extending-rest-api.py Sphinx/source/plugins/python/inspect-api.py Sphinx/source/plugins/python/listening-changes.py Sphinx/source/plugins/python/pil.py Sphinx/source/plugins/python/pydicom.py |
diffstat | 10 files changed, 167 insertions(+), 171 deletions(-) [+] |
line wrap: on
line diff
--- a/Sphinx/source/plugins/python.rst Fri Jun 11 09:38:15 2021 +0200 +++ b/Sphinx/source/plugins/python.rst Fri Jun 11 10:07:12 2021 +0200 @@ -215,21 +215,11 @@ Extending the REST API ...................... -.. highlight:: python - Here is a basic Python script that registers two new routes in the -REST API:: +REST API: - import orthanc - import pprint - - def OnRest(output, uri, **request): - pprint.pprint(request) - print('Accessing uri: %s' % uri) - output.AnswerBuffer('ok\n', 'text/plain') - - orthanc.RegisterRestCallback('/(to)(t)o', OnRest) - orthanc.RegisterRestCallback('/tata', OnRest) +.. literalinclude:: python/extending-rest-api.py + :language: python .. highlight:: json @@ -255,27 +245,10 @@ Listening to changes .................... -.. highlight:: python - -This sample uploads a DICOM file as soon as Orthanc is started:: +This sample uploads a DICOM file as soon as Orthanc is started: - import orthanc - - def OnChange(changeType, level, resource): - if changeType == orthanc.ChangeType.ORTHANC_STARTED: - print('Started') - - with open('/tmp/sample.dcm', 'rb') as f: - orthanc.RestApiPost('/instances', f.read()) - - elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: - print('Stopped') - - elif changeType == orthanc.ChangeType.NEW_INSTANCE: - print('A new instance was uploaded: %s' % resource) - - orthanc.RegisterOnChangeCallback(OnChange) - +.. literalinclude:: python/listening-changes.py + :language: python .. warning:: @@ -289,22 +262,10 @@ these calls in a separate thread, passing the pending events to be processed through a message queue. Here is the template of a possible solution to postpone such deadlocks as much as possible by relying on -the multithreading primitives of Python:: - - import orthanc - import threading +the multithreading primitives of Python: - def OnChange(changeType, level, resource): - # One can safely invoke the "orthanc" module in this function - orthanc.LogWarning("Hello world") - - def _OnChange(changeType, level, resource): - # Invoke the actual "OnChange()" function in a separate thread - t = threading.Timer(0, function = OnChange, args = (changeType, level, resource)) - t.start() - - orthanc.RegisterOnChangeCallback(_OnChange) - +.. literalinclude:: python/changes-deadlock-3.0.py + :language: python Beware that **this workaround is imperfect** and deadlocks have been observed even if using it! Make sure to upgrade your plugin to solve @@ -316,31 +277,8 @@ Accessing the content of a new instance ....................................... -.. highlight:: python - -:: - - import orthanc - import json - import pprint - - def OnStoredInstance(dicom, instanceId): - print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % ( - instanceId, dicom.GetInstanceSize(), - dicom.GetInstanceMetadata('TransferSyntax'), - dicom.GetInstanceMetadata('SopClassUid'))) - - # Print the origin information - if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL: - print('This instance was received through the DICOM protocol') - elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API: - print('This instance was received through the REST API') - - # Print the DICOM tags - pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson())) - - orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance) - +.. literalinclude:: python/accessing-new-instance.py + :language: python .. warning:: Your callback function will be called synchronously with @@ -357,34 +295,16 @@ Calling pydicom ............... -.. highlight:: python - Here is a sample Python plugin that registers a REST callback to dump the content of the dataset of one given DICOM instance stored in -Orthanc, using `pydicom <https://pydicom.github.io/>`__:: - - import io - import orthanc - import pydicom +Orthanc, using `pydicom <https://pydicom.github.io/>`__: - def DecodeInstance(output, uri, **request): - if request['method'] == 'GET': - # Retrieve the instance ID from the regular expression (*) - instanceId = request['groups'][0] - # Get the content of the DICOM file - f = orthanc.GetDicomForInstance(instanceId) - # Parse it using pydicom - dicom = pydicom.dcmread(io.BytesIO(f)) - # Return a string representation the dataset to the caller - output.AnswerBuffer(str(dicom), 'text/plain') - else: - output.SendMethodNotAllowed('GET') - - orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) +.. literalinclude:: python/pydicom.py + :language: python .. highlight:: bash -This can be called as follows:: +This callback can be called as follows:: $ curl http://localhost:8042/pydicom/19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5 @@ -392,68 +312,25 @@ Auto-routing studies .................... -.. highlight:: python - Here is a sample Python plugin that routes any :ref:`stable study <stable-resources>` to a modality named ``samples`` (as declared in the -``DicomModalities`` configuration option):: +``DicomModalities`` configuration option): - import orthanc - - def OnChange(changeType, level, resourceId): - if changeType == orthanc.ChangeType.STABLE_STUDY: - print('Stable study: %s' % resourceId) - orthanc.RestApiPost('/modalities/sample/store', resourceId) - - orthanc.RegisterOnChangeCallback(OnChange) - +.. literalinclude:: python/autorouting-1.py + :language: python Note that, if you want to use an orthanc plugin to transfer the study, -you should use the ``RestApiPostAfterPlugins()`` method:: - - import orthanc +you should use the ``RestApiPostAfterPlugins()`` method: - def OnChange(changeType, level, resourceId): - if changeType == orthanc.ChangeType.STABLE_STUDY: - print('Stable study: %s' % resourceId) - orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId) - - orthanc.RegisterOnChangeCallback(OnChange) - +.. literalinclude:: python/autorouting-2.py + :language: python + Rendering a thumbnail using PIL/Pillow ...................................... -.. highlight:: python - -:: - - from PIL import Image - import io - import orthanc - - def DecodeInstance(output, uri, **request): - if request['method'] == 'GET': - # Retrieve the instance ID from the regular expression (*) - instanceId = request['groups'][0] - - # Render the instance, then open it in Python using PIL/Pillow - png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId) - image = Image.open(io.BytesIO(png)) - - # Downsize the image as a 64x64 thumbnail - image.thumbnail((64, 64), Image.ANTIALIAS) - - # Save the thumbnail as JPEG, then send the buffer to the caller - jpeg = io.BytesIO() - image.save(jpeg, format = "JPEG", quality = 80) - jpeg.seek(0) - output.AnswerBuffer(jpeg.read(), 'text/plain') - - else: - output.SendMethodNotAllowed('GET') - - orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*) +.. literalinclude:: python/pil.py + :language: python .. _python-introspection: @@ -461,34 +338,14 @@ Inspecting the available API ............................ -.. highlight:: python - Thanks to Python's introspection primitives, it is possible to inspect the API of the ``orthanc`` module in order to dump all the available -features:: - - import inspect - import numbers - import orthanc - - # Loop over the members of the "orthanc" module - for (name, obj) in inspect.getmembers(orthanc): - if inspect.isroutine(obj): - print('Function %s():\n Documentation: %s\n' % (name, inspect.getdoc(obj))) +features: - elif inspect.isclass(obj): - print('Class %s:\n Documentation: %s' % (name, inspect.getdoc(obj))) +.. literalinclude:: python/inspect-api.py + :language: python - # Loop over the members of the class - for (subname, subobj) in inspect.getmembers(obj): - if isinstance(subobj, numbers.Number): - print(' - Enumeration value %s: %s' % (subname, subobj)) - elif (not subname.startswith('_') and - inspect.ismethoddescriptor(subobj)): - print(' - Method %s(): %s' % (subname, inspect.getdoc(subobj))) - print('') - - + .. _python-scheduler: Scheduling a task for periodic execution
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/accessing-new-instance.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,20 @@ +import orthanc +import json +import pprint + +def OnStoredInstance(dicom, instanceId): + print('Received instance %s of size %d (transfer syntax %s, SOP class UID %s)' % ( + instanceId, dicom.GetInstanceSize(), + dicom.GetInstanceMetadata('TransferSyntax'), + dicom.GetInstanceMetadata('SopClassUid'))) + + # Print the origin information + if dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.DICOM_PROTOCOL: + print('This instance was received through the DICOM protocol') + elif dicom.GetInstanceOrigin() == orthanc.InstanceOrigin.REST_API: + print('This instance was received through the REST API') + + # Print the DICOM tags + pprint.pprint(json.loads(dicom.GetInstanceSimplifiedJson())) + +orthanc.RegisterOnStoredInstanceCallback(OnStoredInstance)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/autorouting-1.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,8 @@ +import orthanc + +def OnChange(changeType, level, resourceId): + if changeType == orthanc.ChangeType.STABLE_STUDY: + print('Stable study: %s' % resourceId) + orthanc.RestApiPost('/modalities/sample/store', resourceId) + +orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/autorouting-2.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,8 @@ +import orthanc + +def OnChange(changeType, level, resourceId): + if changeType == orthanc.ChangeType.STABLE_STUDY: + print('Stable study: %s' % resourceId) + orthanc.RestApiPostAfterPlugins('/dicom-web/servers/sample/store', resourceId) + +orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/changes-deadlock-3.0.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,13 @@ +import orthanc +import threading + +def OnChange(changeType, level, resource): + # One can safely invoke the "orthanc" module in this function + orthanc.LogWarning("Hello world") + +def _OnChange(changeType, level, resource): + # Invoke the actual "OnChange()" function in a separate thread + t = threading.Timer(0, function = OnChange, args = (changeType, level, resource)) + t.start() + +orthanc.RegisterOnChangeCallback(_OnChange)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/extending-rest-api.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,10 @@ +import orthanc +import pprint + +def OnRest(output, uri, **request): + pprint.pprint(request) + print('Accessing uri: %s' % uri) + output.AnswerBuffer('ok\n', 'text/plain') + +orthanc.RegisterRestCallback('/(to)(t)o', OnRest) +orthanc.RegisterRestCallback('/tata', OnRest)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/inspect-api.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,20 @@ +import inspect +import numbers +import orthanc + +# Loop over the members of the "orthanc" module +for (name, obj) in inspect.getmembers(orthanc): + if inspect.isroutine(obj): + print('Function %s():\n Documentation: %s\n' % (name, inspect.getdoc(obj))) + + elif inspect.isclass(obj): + print('Class %s:\n Documentation: %s' % (name, inspect.getdoc(obj))) + + # Loop over the members of the class + for (subname, subobj) in inspect.getmembers(obj): + if isinstance(subobj, numbers.Number): + print(' - Enumeration value %s: %s' % (subname, subobj)) + elif (not subname.startswith('_') and + inspect.ismethoddescriptor(subobj)): + print(' - Method %s(): %s' % (subname, inspect.getdoc(subobj))) + print('')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/listening-changes.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,16 @@ +import orthanc + +def OnChange(changeType, level, resource): + if changeType == orthanc.ChangeType.ORTHANC_STARTED: + print('Started') + + with open('/tmp/sample.dcm', 'rb') as f: + orthanc.RestApiPost('/instances', f.read()) + + elif changeType == orthanc.ChangeType.ORTHANC_STOPPED: + print('Stopped') + + elif changeType == orthanc.ChangeType.NEW_INSTANCE: + print('A new instance was uploaded: %s' % resource) + +orthanc.RegisterOnChangeCallback(OnChange)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/pil.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,26 @@ +from PIL import Image +import io +import orthanc + +def DecodeInstance(output, uri, **request): + if request['method'] == 'GET': + # Retrieve the instance ID from the regular expression (*) + instanceId = request['groups'][0] + + # Render the instance, then open it in Python using PIL/Pillow + png = orthanc.RestApiGet('/instances/%s/rendered' % instanceId) + image = Image.open(io.BytesIO(png)) + + # Downsize the image as a 64x64 thumbnail + image.thumbnail((64, 64), Image.ANTIALIAS) + + # Save the thumbnail as JPEG, then send the buffer to the caller + jpeg = io.BytesIO() + image.save(jpeg, format = "JPEG", quality = 80) + jpeg.seek(0) + output.AnswerBuffer(jpeg.read(), 'text/plain') + + else: + output.SendMethodNotAllowed('GET') + +orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sphinx/source/plugins/python/pydicom.py Fri Jun 11 10:07:12 2021 +0200 @@ -0,0 +1,18 @@ +import io +import orthanc +import pydicom + +def DecodeInstance(output, uri, **request): + if request['method'] == 'GET': + # Retrieve the instance ID from the regular expression (*) + instanceId = request['groups'][0] + # Get the content of the DICOM file + f = orthanc.GetDicomForInstance(instanceId) + # Parse it using pydicom + dicom = pydicom.dcmread(io.BytesIO(f)) + # Return a string representation the dataset to the caller + output.AnswerBuffer(str(dicom), 'text/plain') + else: + output.SendMethodNotAllowed('GET') + +orthanc.RegisterRestCallback('/pydicom/(.*)', DecodeInstance) # (*)