Asynchronous Jobs, e.g. reconstruct with Python Plug-in and output.AnswerBuffer()

I have a Python script that is part of a Python Plug-in script for Orthanc. One of the methods there does some post-processing of a study after the technician marks the study as “Complete” / CM in the RIS system.

In some instances, I will want to:

  1. Modify the Study:

modified = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/modify’, json.dumps(command))

and possibly reconstruct the study after modification because it seems like that is necessary to update the tags. It doesn’t seem to do that automatically after modification, so:

  1. Reconstruct:

reconstruct = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/reconstruct’,‘{“Asynchronous”:true}’)

That all seems to work, but the call to do so is made via the REST API since I want to pass in the Technicians ID and Initials as well. With the current setup it takes maybe 20-30 seconds to get a response back from the Python script for a study with 1000 images and when there are several modifications and a reconstruction. Not that the reconstruct in Asynchronous.

The end of the script that handles that stuff looks like this. Note that the output.AnswerBuffer() is last. When I look at the terminal / log output from the Docker Orthanc Container I can see that the job starts running around the time that I get my response back and finishes some number of seconds after that depending upon how many images are processed. Just wondering if there is a way to somehow speed up the REST response from around the 20 seconds to something shorter than that.

#. . . . In Method to PostProcess
modified = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/modify’, json.dumps(command))
response[‘modify’] = json.loads(modified)
reconstruct = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/reconstruct’,‘{“Asynchronous”:true}’)
print(‘Mark CM’)

reconstruct returns nothing

ChangeOrderStatus(accession, ‘CM’)

else:
print(“Uploaded Instance has no MWL for the AccessionNumber”)

output.AnswerBuffer(json.dumps(response), ‘application/json’)

An example response is:

{
“status”: “OK”,
“uuid”: “1d4940e4-9d2dc87f-7809bba5-d6ec83bd-f5e9353d”,
“accession”: “CMACC00000006”,
“modify”: {
“ID”: “35493735-d7a5-42fd-853f-e0b6d2e95905”,
“Path”: “/jobs/35493735-d7a5-42fd-853f-e0b6d2e95905”
},
“DBupdate”: “Status Was already CM ?”
}

And Orthanc Explorer Job log:

  • CompletionTime: Mon Aug 02 2021 07:04:31 GMT-0500 (Central Daylight Time)
    CreationTime: Mon Aug 02 2021 07:02:20 GMT-0500 (Central Daylight Time)
    EffectiveRuntime: 131.243
    ErrorCode: 0
    ErrorDescription: Success

    ErrorDetails:
    ID: 35493735-d7a5-42fd-853f-e0b6d2e95905
    Priority: 0
    Progress: 100
    State: Success
    Type: ResourceModification

Hi Stephen,

Actually, the /reconstruct route does not handle the “Asynchronous” option and it does not create a job so it always executes synchronously.

It might be interesting for you to offload the /reconstruct to another slave process (https://book.orthanc-server.com/plugins/python.html#slave-processes-and-the-orthanc-module)

HTH

Alain

Alain,

First, thank you for the reply. I take it that modify runs Asynchronously by default then (i.e. it creates a job). What about the following below ? Delete the reconstruction from the script. The modification job probably had not even started by the time the reconstruction was invoked, and the response was delayed because it did run though. I’ll just get back the same response above, but probably faster since the reconstruction does not have to run synchronously. Then I need to capture the job completion either within the Python script itself (e.g. OnJobComplete event, if there is one, or via some polling mechanism within the RIS using the Job ID to trigger the reconstruction. I actually did that manually and that does what I want. The modified tags are now part of the Main Study tags for the study (.e.g. StudyDescription, OperatorName, ImageComments, PregnancyStatus . . . ., basically a bunch that are not handled by the MWL with the modality itself.)

Correct me if I am wrong, but it looks like it is necessary to perform a reconstruction after a modification for a Study. The tags are not updated for the study unless you explicitly do so.

**I don’t really care how long it takes to run the Reconstruction, up to a limit. So on the RIS via the REST API, what happens if I get back the initial response and fire off a reconstruction via the API ? I presume that I have no way of knowing if the job status (**queued, in progress, completed, failed, etc.) unless I kept polling until it failed or completed, or somehow captured a job complete event. I guess I could also specify the “Priority” in the modification POST (? neg number is highest ?).

#. . . . In Method to PostProcess
modified = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/modify’, json.dumps(command))
response[‘modify’] = json.loads(modified)
print(‘Mark CM’)

remove the reconstruct = orthanc.RestApiPost(‘/studies/’ + uuid + ‘/reconstruct’)

ChangeOrderStatus(accession, ‘CM’)

else:
print(“Uploaded Instance has no MWL for the AccessionNumber”)
output.AnswerBuffer(json.dumps(response), ‘application/json’)

May have found sort of what I am looking for: https://hg.orthanc-server.com/orthanc-tests/file/Orthanc-1.9.6/Tests/Toolbox.py

https://hg.orthanc-server.com/orthanc-tests/file/Orthanc-1.9.6/Tests/Toolbox.py, although I’ll need to adapt that to run within my own Python Plugin. The job ID is in the response[‘modify’] property, but you are right, if I call that within the Python Plug-in I need to have it run as a slave to sort of run asynchronously. The sleep time could probably 1 second intervals. That is all assuming that reconstruction does not just occur automatically when you modify tags at the study level.

def WaitJobDone(orthanc, job):
while True:
s = DoGet(orthanc, ‘/jobs/%s’ % job) [‘State’]
if s == ‘Success’:
return True
elif s == ‘Failure’:
return False
time.sleep(0.01)

Alain,

I added this method to my Python script that I am using as a Plug-in for Orthanc, which I call after the modifcation is queud in the modification. With a small test study having only about 10 images it takes just a few seconds really to do the modification and the reconstruction, and the recon is working as desired now. We have an MR unit that might have 1000 and possibly even up to 2000 instances in some studies, so I presume it’ll take a quite a bit longer, for some so I’ll test it out with a larger study and see how it performs. If too long, I’ll follow your suggestion and try to delegate it to a slave process. The little thing below is basically a modified version of what is in the Unit Tests to work in an Python Plug-in and to handle that specific task.

Thanks. I actually have a similar process that I might have to circle around back about later. I do have a GitHub repo that I have some of these things on, so I’ll try to update that periodically as I move along. I might actually just return the JobStatus to the parent process instead of True or False so I can capture the details of that also, or just log it somewhere.

def Reconstruct(JobID, StudyID):

while True:

JobStatus = json.loads(orthanc.RestApiGet(‘/jobs/%s’ % JobID))
s = JobStatus[‘State’]
if s == ‘Success’:
print(JobStatus)
reconstruct = orthanc.RestApiPost(‘/studies/%s/reconstruct’ % StudyID,‘{“Asynchronous”:true}’)
return True
elif s == ‘Failure’:
return False
time.sleep(1.0)