Question about Python script modifying series from Plug-In.

I’ve been experimenting a bit with a script to renumber the series in a study based upon the time and date for the series, such that they are in chronological order. It is mostly for development purposes currently. I am running into an issue whereby there are some Postgres DB errors when I run the modifications asynchronously and Orthanc appears to just ‘hang’ when I run the modifications synchronously.

Part of the script is like below, where the sortedByTime array has a list of series with the uuid and datetime, sorted by time. It does seem to work if I set Asynchronous to True because it queues up a bunch of jobs, but there are a number of error messages in the log from Postgres like could not serialize access due, The transaction might succeed if retried, etc. I am allowing multiple Postgres connections/4 and lock is false in my dev environment. When I set Asynchronous to False Orthanc just freezes and I don’t really see any error messages, but when I restart it appears to execute the first request that it got hung on. Is there a problem with calling the modify path Synchronously in a Plug-in ? I maybe try limiting my PostGres connections to 1 also to see if that eliminates errors in the Jobs method.

i = 1
for series in sortedByTime:
command = {
“Replace”: {
“SeriesNumber”: “%s” % i
},
“KeepSource”:False,
“Asynchronous”:False vs True
}
modify = json.loads(orthanc.RestApiPost(‘/series/%s/modify’ % series[‘uuid’],json.dumps(command)))
modify[‘datetime’] = series[‘datetime’]
results[series[‘uuid’]] = modify
i+=1

logging.info(“STUDY|RENUMBER_SEQUENCES|” + json.dumps(results))
return results

/sds

Hi,

I think that it’s just the verbose level of that message that is wrong.It says that a transaction has failed and it should be retried but it is actually retried by the plugin. Can you confirm that your modifications are applied correctly despite the error messages ?

Best regards,

Alain

Alain,

Thank you for the response.

I’ve never seen that “could not serialize access due, The transaction might succeed if retried, etc” type of message in the Postgres logs before. But, you are right, when I run the script with “Asynchronous”:True it throws those messages, but it does execute and renumber the series. So, probably just normal ? i might need to play around with these also.

ORTHANC__POSTGRESQL__INDEX_CONNECTIONS_COUNT=4
ORTHANC__POSTGRESQL__MAXIMUM_CONNECTION_RETRIES=8
ORTHANC__POSTGRESQL__LOCK=“false”

However, when I set Async to False, it “hangs”. i.e. it prints out the sorted series like:

[{
“uuid”: “356f06ca-ab99f9fd-365c3180-a36c65b6-595357de”,
“datetime”: “20220407103330”
}, {
“uuid”: “c7038e54-fbe30535-2653c424-6dec764f-a6b6bb2f”,
“datetime”: “20220407103427”
}, {
“uuid”: “7a7ff157-db2aa43c-3a01bf18-e7ffff0f-02d060b5”,
“datetime”: “20220407104155”
}, {
“uuid”: “4eceaeab-87a66966-91bf32b1-3599fca9-8a56a67c”,
“datetime”: “20220407104756”
}, {
“uuid”: “cf7f6bc5-e5e75550-094e15ea-11158d1d-5a05df63”,
“datetime”: “20220407105422”
}]

but it just ‘hangs’ there, whereas with Async it seems to work. Must have something to do with the callback or function call. Is better to run it as jobs anyways.

I could provide the entire script, but basically like:

API request from PHP app or curl:

echo $this->GuzzleAPI(‘POST’, “renumber_series”,‘{“uuid”:"’.$uuid.‘"}’);

or: curl -k -d ‘{“uuid”:"uuid for study}’ http://localhost:8042/renumber_series

/renumber_series route called from my API or curl to the Python Script

def RenumberSeries(output, uri, **request):

if request[‘method’] != ‘POST’:
output.SendMethodNotAllowed(‘POST’)
else:
response = dict()
uuid = json.loads(request[‘body’])[‘uuid’]
response[‘status’] = renumber_sequences(uuid)
output.AnswerBuffer(json.dumps(response, indent = 3), ‘application/json’)

orthanc.RegisterRestCallback('/renumber_series’, RenumberSeries)

function in my Python Script:

def renumber_sequences(study_uuid):

study = json.loads(orthanc.RestApiGet(‘/studies/%s’ % study_uuid))
if ‘Series’ in study:
studySeries = study[‘Series’]
sortedByTime = []
results = dict()
for series in studySeries:
seriesData = json.loads(orthanc.RestApiGet(‘/series/%s’ % series))
item = {
‘uuid’:series,
‘datetime’:seriesData[‘MainDicomTags’][‘SeriesDate’] + seriesData[‘MainDicomTags’][‘SeriesTime’]
}

sortedByTime.append(item)
sortedByTime.sort(key = lambda i: i[‘datetime’]);
orthanc.LogWarning(‘Resorted Series for Study: ’ + study_uuid + ’ Series Dump: \n\n’ + json.dumps(sortedByTime))

KeepSource set to False deletes the originals and uses Orthanc’s UID generator, good and bad really.

In the future Orthanc will allow specifying one’s own root ID.

i = 1
for series in sortedByTime:
command = {
“Replace”: {
“SeriesNumber”: “%s” % i
},
“KeepSource”:False,
“Asynchronous”:True
}
modify = orthanc.RestApiPost(‘/series/%s/modify’ % series[‘uuid’],json.dumps(command))

modify[‘datetime’] = series[‘datetime’]

results[series[‘uuid’]] = json.loads(modify)
i+=1

logging.info(“STUDY|RENUMBER_SEQUENCES|” + json.dumps(results))
return results
else:
return ‘error’ # No series in the study.