Commit 17c1c6d2 authored by Duggan, John's avatar Duggan, John
Browse files

Add flag for picking files out of user directories

parent d150ad49
Loading
Loading
Loading
Loading
+39 −18
Original line number Diff line number Diff line
@@ -54,23 +54,17 @@ INSTRUMENTS = {
}


def get_facilities() -> List[str]:
    return list(INSTRUMENTS.keys())


def get_instruments(facility: str) -> List[str]:
    return list(INSTRUMENTS.get(facility, {}).keys())


class DataSelectorState(BaseModel, validate_assignment=True):
    """Selection state for identifying datafiles."""

    facility: str = Field(default="", title="Facility")
    instrument: str = Field(default="", title="Instrument")
    experiment: str = Field(default="", title="Experiment")
    user_directory: str = Field(default="", title="User Directory")
    directory: str = Field(default="")
    extensions: List[str] = Field(default=[])
    prefix: str = Field(default="")
    show_user_directories: bool = Field(default=False)

    @field_validator("experiment", mode="after")
    @classmethod
@@ -81,11 +75,11 @@ class DataSelectorState(BaseModel, validate_assignment=True):

    @model_validator(mode="after")
    def validate_state(self) -> Self:
        valid_facilities = get_facilities()
        valid_facilities = self.get_facilities()
        if self.facility and self.facility not in valid_facilities:
            warn(f"Facility '{self.facility}' could not be found. Valid options: {valid_facilities}", stacklevel=1)

        valid_instruments = get_instruments(self.facility)
        valid_instruments = self.get_instruments()
        if self.instrument and self.instrument not in valid_instruments:
            warn(
                (
@@ -98,25 +92,37 @@ class DataSelectorState(BaseModel, validate_assignment=True):

        return self

    def get_facilities(self) -> List[str]:
        facilities = list(INSTRUMENTS.keys())
        if self.show_user_directories:
            facilities.append("User Directory")
        return facilities

    def get_instruments(self) -> List[str]:
        return list(INSTRUMENTS.get(self.facility, {}).keys())


class DataSelectorModel:
    """Manages file system interactions for the DataSelector widget."""

    def __init__(self, facility: str, instrument: str, extensions: List[str], prefix: str) -> None:
    def __init__(
        self, facility: str, instrument: str, extensions: List[str], prefix: str, show_user_directories: bool
    ) -> None:
        self.state = DataSelectorState()
        self.state.facility = facility
        self.state.instrument = instrument
        self.state.extensions = extensions
        self.state.prefix = prefix
        self.state.show_user_directories = show_user_directories

    def get_facilities(self) -> List[str]:
        return sorted(get_facilities())
        return sorted(self.state.get_facilities())

    def get_instrument_dir(self) -> str:
        return INSTRUMENTS.get(self.state.facility, {}).get(self.state.instrument, "")

    def get_instruments(self) -> List[str]:
        return sorted(get_instruments(self.state.facility))
        return sorted(self.state.get_instruments())

    def get_experiments(self) -> List[str]:
        experiments = []
@@ -142,17 +148,32 @@ class DataSelectorModel:

        return sorted_dirs

    def get_directories(self) -> List[Any]:
    def get_experiment_directory_path(self) -> Optional[Path]:
        if not self.state.experiment:
            return None

        return Path("/") / self.state.facility / self.get_instrument_dir() / self.state.experiment

    def get_user_directory_path(self) -> Optional[Path]:
        if not self.state.user_directory:
            return None

        return Path("/SNS/users") / self.state.user_directory

    def get_directories(self) -> List[str]:
        if self.state.facility == "User Directory":
            base_path = self.get_user_directory_path()
        else:
            base_path = self.get_experiment_directory_path()

        if not base_path:
            return []

        directories = []

        experiment_path = Path("/") / self.state.facility / self.get_instrument_dir() / self.state.experiment
        try:
            for dirpath, _, _ in os.walk(experiment_path):
            for dirpath, _, _ in os.walk(base_path):
                # Get the relative path from the start path
                path_parts = os.path.relpath(dirpath, experiment_path).split(os.sep)
                path_parts = os.path.relpath(dirpath, base_path).split(os.sep)

                # Only create a new entry for top-level directories
                if len(path_parts) == 1 and path_parts[0] != ".":  # This indicates a top-level directory
+20 −3
Original line number Diff line number Diff line
"""View Implementation for DataSelector."""

from typing import Any, List, Optional, cast
from warnings import warn

from trame.app import get_server
from trame.widgets import client, html
@@ -27,6 +28,7 @@ class DataSelector(vuetify.VDataTableVirtual):
        extensions: Optional[List[str]] = None,
        prefix: str = "",
        select_strategy: str = "all",
        show_user_directories: bool = False,
        **kwargs: Any,
    ) -> None:
        """Constructor for DataSelector.
@@ -48,6 +50,9 @@ class DataSelector(vuetify.VDataTableVirtual):
        select_strategy : str, optional
            The selection strategy to pass to the `VDataTable component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VDataTable>`__.
            If unset, the `all` strategy will be used.
        show_user_directories : bool, optional
            Whether or not to allow users to select data files from user directories. Ignored if the facility parameter
            is set.
        **kwargs
            All other arguments will be passed to the underlying
            `VDataTable component <https://trame.readthedocs.io/en/latest/trame.widgets.vuetify3.html#trame.widgets.vuetify3.VDataTable>`_.
@@ -64,10 +69,15 @@ class DataSelector(vuetify.VDataTableVirtual):
        else:
            self._label = None

        if facility and show_user_directories:
            warn("show_user_directories will be ignored since the facility parameter is set.", stacklevel=1)

        self._v_model = v_model
        self._v_model_name_in_state = v_model.split(".")[0]
        self._extensions = extensions if extensions is not None else []
        self._prefix = prefix
        self._select_strategy = select_strategy
        self._show_user_directories = show_user_directories

        self._state_name = f"nova__dataselector_{self._next_id}_state"
        self._facilities_name = f"nova__dataselector_{self._next_id}_facilities"
@@ -76,7 +86,7 @@ class DataSelector(vuetify.VDataTableVirtual):
        self._directories_name = f"nova__dataselector_{self._next_id}_directories"
        self._datafiles_name = f"nova__dataselector_{self._next_id}_datafiles"

        self._flush_state = f"flushState('{self._v_model.split('.')[0]}');"
        self._flush_state = f"flushState('{self._v_model_name_in_state}');"
        self._reset_state = client.JSEval(exec=f"{self._v_model} = []; {self._flush_state}").exec

        self.create_model(facility, instrument)
@@ -96,14 +106,19 @@ class DataSelector(vuetify.VDataTableVirtual):
                if instrument == "":
                    columns -= 1
                    InputField(
                        v_model=f"{self._state_name}.instrument", items=(self._instruments_name,), type="autocomplete"
                        v_if=f"{self._state_name}.facility !== 'User Directory'",
                        v_model=f"{self._state_name}.instrument",
                        items=(self._instruments_name,),
                        type="autocomplete",
                    )
                InputField(
                    v_if=f"{self._state_name}.facility !== 'User Directory'",
                    v_model=f"{self._state_name}.experiment",
                    column_span=columns,
                    items=(self._experiments_name,),
                    type="autocomplete",
                )
                InputField(v_else=True, v_model=f"{self._state_name}.user_directory", column_span=2)

            with GridLayout(columns=2, classes="flex-1-0 h-0", valign="start"):
                if not self._prefix:
@@ -155,7 +170,9 @@ class DataSelector(vuetify.VDataTableVirtual):
                    )

    def create_model(self, facility: str, instrument: str) -> None:
        self._model = DataSelectorModel(facility, instrument, self._extensions, self._prefix)
        self._model = DataSelectorModel(
            facility, instrument, self._extensions, self._prefix, self._show_user_directories
        )

    def create_viewmodel(self) -> None:
        server = get_server(None, client_type="vue3")