diff --git a/pixl_imaging/src/pixl_imaging/_orthanc.py b/pixl_imaging/src/pixl_imaging/_orthanc.py index c8cd7cec0..08132036d 100644 --- a/pixl_imaging/src/pixl_imaging/_orthanc.py +++ b/pixl_imaging/src/pixl_imaging/_orthanc.py @@ -64,9 +64,16 @@ async def get_local_study(self, study_id: str) -> Any: """Query local Orthanc instance for study.""" return await self._get(f"/studies/{study_id}") + async def get_local_study_statistics(self, study_id: str) -> Any: + """Query local Orthanc instance for study statistics.""" + return await self._get(f"/studies/{study_id}/statistics") + async def get_local_study_instances(self, study_id: str) -> Any: """Get the instances of a study.""" - return await self._get(f"/studies/{study_id}/instances") + return await self._get( + f"/studies/{study_id}/instances?short=true", + timeout=self.dicom_timeout, # this API call can sometimes take several minutes + ) async def query_remote(self, data: dict, modality: str) -> Optional[str]: """Query a particular modality, available from this node""" @@ -154,13 +161,15 @@ async def job_state(self, job_id: str) -> Any: # See: https://book.orthanc-server.com/users/advanced-rest.html#jobs-monitoring return await self._get(f"/jobs/{job_id}") - async def _get(self, path: str) -> Any: + async def _get(self, path: str, timeout: int | None = None) -> Any: + # Optionally override default http timeout + http_timeout = timeout or self.http_timeout async with ( aiohttp.ClientSession() as session, session.get( f"{self._url}{path}", auth=self._auth, - timeout=self.http_timeout, + timeout=http_timeout, ) as response, ): return await _deserialise(response) diff --git a/pixl_imaging/src/pixl_imaging/_processing.py b/pixl_imaging/src/pixl_imaging/_processing.py index c60230fa2..a1a92274d 100644 --- a/pixl_imaging/src/pixl_imaging/_processing.py +++ b/pixl_imaging/src/pixl_imaging/_processing.py @@ -283,15 +283,9 @@ async def _get_missing_instances( Return a list of missing instance UIDs (empty if none missing) """ - # First get all SOPInstanceUIDs for the study that are in Orthanc Raw - orthanc_raw_sop_instance_uids = [] - for resource in resources: - study_instances = await orthanc_raw.get_local_study_instances(study_id=resource) - orthanc_raw_sop_instance_uids.extend( - [instance["MainDicomTags"]["SOPInstanceUID"] for instance in study_instances] - ) + missing_instances: list[dict[str, str]] = [] - # Now query the VNA / PACS for the study instances + # First query the VNA / PACS for the study instances study_query_answers = await orthanc_raw.get_remote_query_answers(study_query_id) instances_queries_and_answers = [] for answer_id in study_query_answers: @@ -302,13 +296,25 @@ async def _get_missing_instances( instances_queries_and_answers.extend( [(instances_query_id, answer) for answer in instances_query_answers] ) + num_remote_instances = len(instances_queries_and_answers) - missing_instances: list[dict[str, str]] = [] + num_local_instances = 0 + for resource in resources: + study_statistics = await orthanc_raw.get_local_study_statistics(study_id=resource) + num_local_instances += int(study_statistics["CountInstances"]) - if len(instances_queries_and_answers) == len(orthanc_raw_sop_instance_uids): + if num_remote_instances == num_local_instances: logger.debug("No missing instances for study {}", study.message.study_uid) return missing_instances + # Get all SOPInstanceUIDs for the study that are in Orthanc Raw + orthanc_raw_sop_instance_uids = [] + for resource in resources: + study_instances = await orthanc_raw.get_local_study_instances(study_id=resource) + orthanc_raw_sop_instance_uids.extend( + [instance["MainDicomTags"]["0008,0018"] for instance in study_instances] + ) + # If the SOPInstanceUID is not in the list of instances in Orthanc Raw # retrieve the instance from the VNA / PACS query_tags = ["0020,000d", "0020,000e", "0008,0018"]