view NewTests/ExtraMainDicomTags/test_extra_main_dicom_tags.py @ 691:79c06b1ed0a8

check storage access counts
author Alain Mazy <am@orthanc.team>
date Wed, 18 Sep 2024 14:54:47 +0200
parents 84b7444330a2
children 377ad9690c7a
line wrap: on
line source

import unittest
import time
import os
from helpers import OrthancTestCase, Helpers

from orthanc_api_client import OrthancApiClient, generate_test_dicom_file
from orthanc_tools import OrthancTestDbPopulator

import pathlib
import glob
here = pathlib.Path(__file__).parent.resolve()


class TestExtraMainDicomTags(OrthancTestCase):

    @classmethod
    def prepare(cls):
        print('-------------- preparing TestExtraMainDicomTags tests')

        cls.clear_storage(storage_name="ExtraMainDicomTags")

        config = {
                "ExtraMainDicomTags": {
                    "Instance" : [
                        "Rows",
                        "PerformedProtocolCodeSequence"
                    ],
                    "Series" : [
                        "RequestAttributesSequence"
                    ],
                    "Study": [
                    ],
                    "Patient": []
                },
                "OverwriteInstances": True,
                "DicomWeb" : {
                    "StudiesMetadata" : "MainDicomTags",
                    "SeriesMetadata": "MainDicomTags"
                },
                "StableAge": 1000  # we don't want to be disturbed by events when debugging
            }

        config_path = cls.generate_configuration(
            config_name="extra_main_dicom_tags",
            storage_name="ExtraMainDicomTags",
            config=config,
            plugins=Helpers.plugins
        )

        print('-------------- prepared ExtraMainDicomTags tests')
        if Helpers.break_after_preparation:
            print(f"++++ It is now time to start your Orthanc under tests with configuration file '{config_path}' +++++")
            input("Press Enter to continue")
        else:
            print('-------------- launching ExtraMainDicomTags tests')
            cls.launch_orthanc_under_tests(
                config_path=config_path,
                config_name="extra_main_dicom_tags",
                storage_name="ExtraMainDicomTags",
                plugins=Helpers.plugins
            )

        print('-------------- waiting for orthanc-under-tests to be available')
        cls.o.wait_started()
        
    def test_main_dicom_tags(self):

        # upload a study
        self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

        instance = self.o.get(endpoint="instances/4dc71dc0-6093b5f8-ca67aa8a-07b18ff5-95dbe3c8").json()

        self.assertIn("Rows", instance["MainDicomTags"])
        self.assertIn("PerformedProtocolCodeSequence", instance["MainDicomTags"])

    def test_main_dicom_tags_full(self):

        # upload a study
        self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

        instance = self.o.get(endpoint="instances/4dc71dc0-6093b5f8-ca67aa8a-07b18ff5-95dbe3c8?full").json()

        self.assertIn("0028,0010", instance["MainDicomTags"])
        self.assertIn("0040,0260", instance["MainDicomTags"])


    def test_main_reconstruct(self):

        # upload a study
        self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

        instance = self.o.get(endpoint="instances/4dc71dc0-6093b5f8-ca67aa8a-07b18ff5-95dbe3c8").json()

        self.assertIn("Rows", instance["MainDicomTags"])
        self.assertIn("PerformedProtocolCodeSequence", instance["MainDicomTags"])

        # reconstruct instance
        self.o.post(endpoint="instances/4dc71dc0-6093b5f8-ca67aa8a-07b18ff5-95dbe3c8/reconstruct", json={})
        instance = self.o.get(endpoint="instances/4dc71dc0-6093b5f8-ca67aa8a-07b18ff5-95dbe3c8").json()
        self.assertIn("Rows", instance["MainDicomTags"])
        self.assertIn("PerformedProtocolCodeSequence", instance["MainDicomTags"])

    def get_storage_access_count(self):
        mm = self.o.get_binary("/tools/metrics-prometheus").decode("utf-8")
        
        mm = [x.split(" ") for x in mm.split("\n")]

        count = 0
        for m in mm:
            if m[0] == 'orthanc_storage_cache_hit_count':
                # print(f"orthanc_storage_cache_hit_count = {m[1]}")
                count += int(m[1])
            if m[0] == 'orthanc_storage_cache_miss_count':
                # print(f"orthanc_storage_cache_miss_count = {m[1]}")
                count += int(m[1])

        print(f"storage access count = {count}")
        return count


    def test_tools_find(self):
        if self.o.is_orthanc_version_at_least(12, 5, 0):

            # upload a study
            self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

            # instance level, only extra main dicom tags from that level
            c = self.get_storage_access_count()
            r = self.o.post(
                endpoint="tools/find",
                json={
                    "Level": "Instances",
                    "Query": {
                        "PatientID": "5Yp0E"
                    },
                    "Expand": True,
                    "RequestedTags" : [
                        "Rows",                             # in the ExtraMainDicomTags at instance level
                        "PerformedProtocolCodeSequence"     # in the ExtraMainDicomTags at instance level
                    ]
                }
            )

            instances = r.json()
            self.assertEqual(1, len(instances))
            self.assertIn("Rows", instances[0]["RequestedTags"])
            self.assertIn("PerformedProtocolCodeSequence", instances[0]["RequestedTags"])
            self.assertEqual(c, self.get_storage_access_count()) # nothing should be read from disk

            # instance level, only extra main dicom tags from that level + a tag from disk
            c = self.get_storage_access_count()
            r = self.o.post(
                endpoint="tools/find",
                json={
                    "Level": "Instances",
                    "Query": {
                        "PatientID": "5Yp0E"
                    },
                    "Expand": True,
                    "RequestedTags" : [
                        "Rows",                             # in the ExtraMainDicomTags at instance level
                        "PerformedProtocolCodeSequence",    # in the ExtraMainDicomTags at instance level
                        "ReferencedStudySequence"           # "ReferencedStudySequence" is not stored in MainDicomTags !
                    ]
                }
            )

            instances = r.json()
            self.assertEqual(1, len(instances))
            self.assertIn("Rows", instances[0]["RequestedTags"])
            self.assertIn("PerformedProtocolCodeSequence", instances[0]["RequestedTags"])
            self.assertIn("ReferencedStudySequence", instances[0]["RequestedTags"])
            self.assertEqual(c + 1, self.get_storage_access_count())
            # TO test manually: ReferencedStudySequence 0008,1110 should be read from disk

            # instance level, extra main dicom tags from that level + a sequence from upper level
            c = self.get_storage_access_count()
            r = self.o.post(
                endpoint="tools/find",
                json={
                    "Level": "Instances",
                    "Query": {
                        "PatientID": "5Yp0E"
                    },
                    "Expand": True,
                    "RequestedTags" : [
                        "Rows",                             # in the ExtraMainDicomTags at instance level
                        "PerformedProtocolCodeSequence",    # in the ExtraMainDicomTags at instance level   0040,0260
                        "RequestAttributesSequence"         # in the ExtraMainDicomTags at series level     0040,0275
                    ]
                }
            )

            instances = r.json()
            self.assertEqual(1, len(instances))
            self.assertIn("Rows", instances[0]["RequestedTags"])
            self.assertIn("PerformedProtocolCodeSequence", instances[0]["RequestedTags"])
            self.assertIn("RequestAttributesSequence", instances[0]["RequestedTags"])  # note that, as of 1.12.5, Orthanc reads this from the disk !
            self.assertEqual(c, self.get_storage_access_count()) # nothing should be read from disk

            # series level, request a sequence
            c = self.get_storage_access_count()
            r = self.o.post(
                endpoint="tools/find",
                json={
                    "Level": "Series",
                    "Query": {
                        "PatientID": "5Yp0E"
                    },
                    "Expand": True,
                    "RequestedTags" : [
                        "RequestAttributesSequence"         # in the ExtraMainDicomTags at series level
                    ]
                }
            )

            series = r.json()
            self.assertEqual(1, len(series))
            self.assertIn("RequestAttributesSequence", series[0]["RequestedTags"])
            self.assertEqual(c, self.get_storage_access_count()) # nothing should be read from disk

            # series level, request a sequence + a tag from disk
            c = self.get_storage_access_count()
            r = self.o.post(
                endpoint="tools/find",
                json={
                    "Level": "Series",
                    "Query": {
                        "PatientID": "5Yp0E"
                    },
                    "Expand": True,
                    "RequestedTags" : [
                        "RequestAttributesSequence",        # in the ExtraMainDicomTags at series level
                        "ReferencedStudySequence"           # "ReferencedStudySequence" is not stored in MainDicomTags !
                    ]
                }
            )

            series = r.json()
            self.assertEqual(1, len(series))
            self.assertIn("RequestAttributesSequence", series[0]["RequestedTags"])
            self.assertIn("ReferencedStudySequence", series[0]["RequestedTags"])
            self.assertEqual(c + 1, self.get_storage_access_count())
            # TO test manually: ReferencedStudySequence 0008,1110 should be read from disk


    def test_dicom_web_metadata(self):

        # upload a study
        self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

        metadata = self.o.get(
            endpoint="dicom-web/studies/2.16.840.1.113669.632.20.1211.10000357775/metadata"
        ).json()

        self.assertEqual(1, len(metadata))
        self.assertIn("00280010", metadata[0])      # Rows
        self.assertNotIn("00280011", metadata[0])   # Columns should not be stored !
        self.assertIn("00400260", metadata[0])      # PerformedProtocolCodeSequence

    def test_storage_accesses_for_dicom_web(self):
        if self.o.is_orthanc_version_at_least(12, 5, 0):

            # upload a study
            self.o.upload_file(here / "../../Database/Brainix/Flair/IM-0001-0001.dcm")

            # study level, only tags that are in DB (note, since 1.12.5, TimezoneOffsetFromUTC is a standard MainDicomTags)
            c = self.get_storage_access_count()
            r = self.o.get_json("/dicom-web/studies?PatientID=5Yp0E")
            self.assertEqual(c, self.get_storage_access_count()) # nothing should be read from disk

            # series level, only tags that are in DB (note, since 1.12.5, TimezoneOffsetFromUTC is a standard MainDicomTags)
            c = self.get_storage_access_count()
            r = self.o.get_json("/dicom-web/series?PatientID=5Yp0E")
            self.assertEqual(c, self.get_storage_access_count()) # nothing should be read from disk