Skip to content

Commit 399a7b0

Browse files
authored
Merge pull request #108 from vkt1414/feat-add-metadata-for-series-not-in-latest-index-for-idc
feat: enable downloading data from previous idc data versions
2 parents 26e6d13 + 01dc5ab commit 399a7b0

3 files changed

Lines changed: 117 additions & 28 deletions

File tree

idc_index/index.py

Lines changed: 115 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ def __init__(self):
6363
file_path = idc_index_data.IDC_INDEX_PARQUET_FILEPATH
6464
logger.debug(f"Reading index file v{idc_index_data.__version__}")
6565
self.index = pd.read_parquet(file_path)
66+
67+
self.previous_versions_index_path = (
68+
idc_index_data.PRIOR_VERSIONS_INDEX_PARQUET_FILEPATH
69+
)
70+
6671
# self.index = self.index.astype(str).replace("nan", "")
6772
self.index["series_size_MB"] = self.index["series_size_MB"].astype(float)
6873
self.collection_summary = self.index.groupby("collection_id").agg(
@@ -75,6 +80,11 @@ def __init__(self):
7580
"installed": True,
7681
"url": None,
7782
},
83+
"previous_versions_index": {
84+
"description": "index containing one row per DICOM series from all previous IDC versions that are not in current version.",
85+
"installed": True,
86+
"url": None,
87+
},
7888
"sm_index": {
7989
"description": "DICOM Slide Microscopy series-level index.",
8090
"installed": False,
@@ -617,34 +627,44 @@ def _validate_update_manifest_and_get_download_size(
617627
# create a copy of the index
618628
index_df_copy = self.index
619629

630+
# use default hierarchy
631+
if dirTemplate is not None:
632+
hierarchy = self._generate_sql_concat_for_building_directory(
633+
dirTemplate=dirTemplate, downloadDir=downloadDir
634+
)
635+
else:
636+
hierarchy = "NULL"
637+
620638
# Extract s3 url and crdc_series_uuid from the manifest copy commands
621639
# Next, extract crdc_series_uuid from aws_series_url in the index and
622640
# try to verify if every series in the manifest is present in the index
623641

624642
# TODO: need to remove the assumption that manifest commands will have 'cp'
625643
# and need to parse S3 URL directly
626644
# ruff: noqa
627-
sql = """
645+
sql = f"""
628646
PRAGMA disable_progress_bar;
629647
WITH
630648
index_temp AS (
631649
SELECT
632650
seriesInstanceUID,
633651
series_aws_url,
634652
series_size_MB,
635-
REGEXP_EXTRACT(series_aws_url, '(?:.*?\\/){3}([^\\/?#]+)', 1) index_crdc_series_uuid
653+
{hierarchy} AS path,
654+
REGEXP_EXTRACT(series_aws_url, '(?:.*?\\/){{3}}([^\\/?#]+)', 1) index_crdc_series_uuid
636655
FROM
637656
index_df_copy),
638657
manifest_temp AS (
639658
SELECT
640659
manifest_cp_cmd,
641-
REGEXP_EXTRACT(manifest_cp_cmd, '(?:.*?\\/){3}([^\\/?#]+)', 1) AS manifest_crdc_series_uuid,
660+
REGEXP_EXTRACT(manifest_cp_cmd, '(?:.*?\\/){{3}}([^\\/?#]+)', 1) AS manifest_crdc_series_uuid,
642661
REGEXP_REPLACE(regexp_replace(manifest_cp_cmd, 'cp ', ''), '\\s[^\\s]*$', '') AS s3_url,
643662
FROM
644663
manifest_df )
645664
SELECT
646665
seriesInstanceuid,
647666
s3_url,
667+
path,
648668
series_size_MB,
649669
index_crdc_series_uuid is not NULL as crdc_series_uuid_match,
650670
s3_url==series_aws_url AS s3_url_match,
@@ -679,6 +699,75 @@ def _validate_update_manifest_and_get_download_size(
679699
)
680700
logger.error("\n" + "\n".join(missing_manifest_cp_cmds.tolist()))
681701

702+
logger.debug(
703+
"Checking if the requested data is available in other idc versions "
704+
)
705+
missing_series_sql = f"""
706+
PRAGMA disable_progress_bar;
707+
WITH
708+
combined_index AS
709+
(SELECT
710+
*,
711+
{hierarchy} AS path,
712+
FROM
713+
index_df_copy
714+
union by name
715+
SELECT
716+
*,
717+
{hierarchy} AS path,
718+
FROM
719+
'{self.previous_versions_index_path}' pvip
720+
721+
),
722+
index_temp AS (
723+
SELECT
724+
seriesInstanceUID,
725+
series_aws_url,
726+
series_size_MB,
727+
path,
728+
REGEXP_EXTRACT(series_aws_url, '(?:.*?\\/){{3}}([^\\/?#]+)', 1) index_crdc_series_uuid
729+
FROM
730+
combined_index),
731+
manifest_temp AS (
732+
SELECT
733+
manifest_cp_cmd,
734+
REGEXP_EXTRACT(manifest_cp_cmd, '(?:.*?\\/){{3}}([^\\/?#]+)', 1) AS manifest_crdc_series_uuid,
735+
REGEXP_REPLACE(regexp_replace(manifest_cp_cmd, 'cp ', ''), '\\s[^\\s]*$', '') AS s3_url,
736+
FROM
737+
manifest_df )
738+
SELECT
739+
seriesInstanceuid,
740+
s3_url,
741+
path,
742+
series_size_MB,
743+
index_crdc_series_uuid is not NULL as crdc_series_uuid_match,
744+
TRIM(s3_url) = TRIM(series_aws_url) AS s3_url_match,
745+
manifest_temp.manifest_cp_cmd,
746+
CASE
747+
WHEN TRIM(s3_url) = TRIM(series_aws_url) THEN 'aws'
748+
ELSE
749+
'unknown'
750+
END
751+
AS endpoint
752+
FROM
753+
manifest_temp
754+
LEFT JOIN
755+
index_temp
756+
ON
757+
index_temp.index_crdc_series_uuid = manifest_temp.manifest_crdc_series_uuid
758+
"""
759+
merged_df = duckdb.query(missing_series_sql).df()
760+
if not all(merged_df["crdc_series_uuid_match"]):
761+
missing_manifest_cp_cmds = merged_df.loc[
762+
~merged_df["crdc_series_uuid_match"], "manifest_cp_cmd"
763+
]
764+
logger.error(
765+
"The following manifest copy commands are not recognized as referencing any associated series in any release of IDC.\n"
766+
"This means either these commands are invalid. Please submit an issue on https://github.com/ImagingDataCommons/idc-index/issues \n"
767+
"The corresponding files could not be downloaded.\n"
768+
)
769+
logger.error("\n" + "\n".join(missing_manifest_cp_cmds.tolist()))
770+
682771
if validate_manifest:
683772
# Check if there is more than one endpoint
684773
if len(merged_df["endpoint"].unique()) > 1:
@@ -728,29 +817,29 @@ def _validate_update_manifest_and_get_download_size(
728817
total_size = merged_df["series_size_MB"].sum()
729818
total_size = round(total_size, 2)
730819

731-
if dirTemplate is not None:
732-
hierarchy = self._generate_sql_concat_for_building_directory(
733-
dirTemplate=dirTemplate, downloadDir=downloadDir
734-
)
735-
sql = f"""
736-
WITH temp as
737-
(
738-
SELECT
739-
seriesInstanceUID,
740-
s3_url
741-
FROM
742-
merged_df
743-
)
744-
SELECT
745-
s3_url,
746-
{hierarchy} as path
747-
FROM
748-
temp
749-
JOIN
750-
index using (seriesInstanceUID)
751-
"""
752-
logger.debug(f"About to run this query:\n{sql}")
753-
merged_df = self.sql_query(sql)
820+
# if dirTemplate is not None:
821+
# hierarchy = self._generate_sql_concat_for_building_directory(
822+
# dirTemplate=dirTemplate, downloadDir=downloadDir
823+
# )
824+
# sql = f"""
825+
# WITH temp as
826+
# (
827+
# SELECT
828+
# seriesInstanceUID,
829+
# s3_url
830+
# FROM
831+
# merged_df
832+
# )
833+
# SELECT
834+
# s3_url,
835+
# {hierarchy} as path
836+
# FROM
837+
# temp
838+
# JOIN
839+
# index using (seriesInstanceUID)
840+
# """
841+
# logger.debug(f"About to run this query:\n{sql}")
842+
# merged_df = self.sql_query(sql)
754843
# Write a temporary manifest file
755844
with tempfile.NamedTemporaryFile(mode="w", delete=False) as temp_manifest_file:
756845
if use_s5cmd_sync and len(os.listdir(downloadDir)) != 0:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ dynamic = ["version"]
3434
dependencies = [
3535
"click",
3636
'duckdb>=0.10.0',
37-
"idc-index-data==18.1.0",
37+
"idc-index-data==18.2.0",
3838
"packaging",
3939
"pandas<2.2",
4040
"psutil",

tests/idcindex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def test_prior_version_manifest(self):
481481
# is fully resolved, the manifest below should not be empty, and this test should be updated
482482
# with count equal to 5
483483
with open(temp_manifest_file) as file:
484-
assert len(file.readlines()) == 0
484+
assert len(file.readlines()) == 5
485485

486486
def test_list_indices(self):
487487
i = IDCClient()

0 commit comments

Comments
 (0)