Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions aidrin/compute/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"""

from dataclasses import asdict, is_dataclass
from typing import Any, Callable, Dict, Optional
from typing import Any, Callable, Dict, List, Optional

from aidrin.compute import client
from aidrin.compute.profiles import RemoteTarget
Expand Down Expand Up @@ -138,11 +138,16 @@ def summarize_dataset(
file_path: str,
file_type: Optional[str] = None,
max_features: Optional[int] = None,
selected_keys: Optional[List[str]] = None,
) -> Dict[str, Any]:
return self._call(
"summarize",
{"file_path": file_path, "file_type": file_type, "max_features": max_features},
)
payload: Dict[str, Any] = {
"file_path": file_path,
"file_type": file_type,
"max_features": max_features,
}
if selected_keys:
payload["selected_keys"] = selected_keys
return self._call("summarize", payload)

def run_data_quality(
self,
Expand All @@ -151,17 +156,18 @@ def run_data_quality(
file_name: Optional[str] = None,
verbose: bool = False,
strip_visualizations: bool = True,
selected_keys: Optional[List[str]] = None,
) -> Dict[str, Any]:
return self._call(
"data_quality",
{
"file_path": file_path,
"file_type": file_type,
"file_name": file_name,
"verbose": verbose,
"strip_visualizations": strip_visualizations,
},
)
payload: Dict[str, Any] = {
"file_path": file_path,
"file_type": file_type,
"file_name": file_name,
"verbose": verbose,
"strip_visualizations": strip_visualizations,
}
if selected_keys:
payload["selected_keys"] = selected_keys
return self._call("data_quality", payload)

def run_batch_metrics(
self,
Expand Down
18 changes: 16 additions & 2 deletions aidrin/file_handling/file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
from aidrin.file_handling.readers.json_reader import jsonReader
from aidrin.file_handling.readers.npz_reader import npzReader
from aidrin.file_handling.readers.parquet_reader import parquetReader
from aidrin.file_handling.readers.zarr_reader import zarrReader

# Notes:
# To add support for new file types:
# - Add a new subclass of BaseFileReader with a .read() method
# (and optionally .parse(), .filter()) to 'file_readers'.
# - Register the class in READER_MAP.
# - Add a display name and extension to SUPPORTED_FILE_TYPES for the front end.
# - Formats that are CLI/library/Globus-only (e.g. Zarr directories) go in
# GLOBUS_FILE_TYPES but not SUPPORTED_FILE_TYPES (local upload dropdown).

# Reader Map. Used to create file type specific parsing
READER_MAP = {
Expand All @@ -24,10 +27,11 @@
".json": jsonReader,
".h5": hdf5Reader,
".parquet": parquetReader,
".zarr": zarrReader,
# Add additional file types here
}

# Supported file types. Read on front end to create select features.
# Supported file types. Read on front end to create local upload select features.
SUPPORTED_FILE_TYPES = [
(".csv", "CSV"),
(".xls, .xlsb, .xlsx, .xlsm", "Excel"),
Expand All @@ -39,6 +43,15 @@
# (file_type,file_type_name)
]

# Globus (and other path-based surfaces) may include directory-shaped formats
# that are not offered in the local browser upload dropdown.
GLOBUS_FILE_TYPES = SUPPORTED_FILE_TYPES + [
(".zarr", "Zarr"),
]

# File types that accept selected_keys for multi-array / multi-dataset selection.
_SELECTION_FILE_TYPES = {".h5", ".zarr"}

# logger config
file_upload_time_log = logging.getLogger("file_upload")

Expand Down Expand Up @@ -204,6 +217,7 @@ def read_file(file_info, columns=None):
-file_path: str, relative or absolute path of the file.
-file_name: str, file name.
-file_type: str, file format. Passed from front end select value.
- optional selected_keys (4th element) for HDF5/Zarr path selection.
columns: list of str, optional
When given, only these columns are returned.
Returns
Expand Down Expand Up @@ -250,7 +264,7 @@ def read_file(file_info, columns=None):

# Slow path: parse the source once.
reader_cls = READER_MAP[file_type]
if file_type == ".h5":
if file_type in _SELECTION_FILE_TYPES:
df = reader_cls(
file_path, file_upload_time_log, selected_keys=selected_keys
).read()
Expand Down
17 changes: 17 additions & 0 deletions aidrin/file_handling/readers/root_reader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from aidrin.file_handling.readers.structured import (
INVENTORY_UNSUPPORTED,
InventoryResult,
StructuredFileReader,
make_inventory,
)


class rootReader(StructuredFileReader):
"""ROOT file reader (scaffold). v1 will support TTrees only via uproot."""

def inventory(self) -> InventoryResult:
return make_inventory(INVENTORY_UNSUPPORTED)

def read(self):
self.logger.warning("ROOT read is not implemented yet.")
return None
81 changes: 81 additions & 0 deletions aidrin/file_handling/readers/structured.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Shared contract for structured file readers (HDF5, Zarr, ROOT).

Structured formats may hold many arrays or tables under paths. Readers classify
the on-disk layout via ``inventory()`` before ``read()`` loads tabular data.

Inventory ``type`` values
-------------------------
empty
No readable datasets or arrays.
single_dataset
One dataset; auto-read without a picker.
multi_dataset
Incompatible or grouped layout; caller must select paths.
legacy
Internal auto-read path for compatible multi-array layouts (and HDF5
PyTables stores). Never surface this label in API or UI responses.
unsupported
Reader stub or format not implemented yet. Never treat as auto-read.
"""

from __future__ import annotations

from typing import Any, TypedDict

from aidrin.file_handling.readers.base_reader import BaseFileReader

INVENTORY_EMPTY = "empty"
INVENTORY_SINGLE = "single_dataset"
INVENTORY_MULTI = "multi_dataset"
INVENTORY_LEGACY = "legacy"
INVENTORY_UNSUPPORTED = "unsupported"

# Types that may be shown to users when driving picker or error messages.
USER_FACING_INVENTORY_TYPES = frozenset(
{INVENTORY_EMPTY, INVENTORY_SINGLE, INVENTORY_MULTI}
)


class DatasetEntry(TypedDict):
path: str
shape: tuple[int, ...]
ndim: int
dtype: str
size: int


class PickerGroup(TypedDict, total=False):
name: str
paths: list[str]
prefix: str


class InventoryResult(TypedDict):
type: str
datasets: list[DatasetEntry]
groups: list[dict[str, Any]]


def make_inventory(
inv_type: str,
datasets: list[DatasetEntry] | None = None,
groups: list[dict[str, Any]] | None = None,
) -> InventoryResult:
"""Build a normalized inventory dict."""
return {
"type": inv_type,
"datasets": datasets or [],
"groups": groups or [],
}


class StructuredFileReader(BaseFileReader):
"""Base for hierarchical array/tree formats with inventory and metadata hooks."""

def inventory(self) -> InventoryResult:
return make_inventory(INVENTORY_UNSUPPORTED)

def get_metadata(self) -> dict[str, Any]:
"""Embedded file metadata for FAIR assessment (``.zattrs``, ROOT headers, etc.)."""
return {}
Loading
Loading