Skip to content
Merged
42 changes: 42 additions & 0 deletions src/murfey/instrument_server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,48 @@ def register_processing_parameters(
return {"success": True}


@router.get("/instruments/{instrument_name}/sessions/{session_id}/possible_otf_dirs")
def get_possible_otf_dirs(
instrument_name: str,
session_id: MurfeySessionID,
) -> list[File]:
"""
Looks under the configured reference file directory for all top-level directories,
and returns them as a list of Pydantic models.
"""
# Get the machine config from the backend server
url_path = url_path_for(
"session_control.router",
"machine_info_by_instrument",
instrument_name=sanitise_nonpath(instrument_name),
)
machine_config: dict[str, Any] = requests.get(
f"{_get_murfey_url()}{url_path}",
headers={"Authorization": f"Bearer {tokens[session_id]}"},
).json()

# Look for OTF directories under the specified directory
candidates: list[File] = []
otf_dir = machine_config.get("gain_reference_directory", None)
if otf_dir is not None:
for item in secure_path(Path(otf_dir), keep_spaces=True).glob("*"):
if item.is_dir():
dir_stats = item.stat()
candidates.append(
File(
name=item.name,
description="",
size=dir_stats.st_size / 1e6,
timestamp=datetime.fromtimestamp(dir_stats.st_mtime),
full_path=str(item),
)
)
candidates.sort(key=lambda x: x.timestamp, reverse=True)
else:
logger.error(f"No OTF directory was configured for {instrument_name}")
return candidates


@router.get(
"/instruments/{instrument_name}/sessions/{session_id}/possible_gain_references"
)
Expand Down
31 changes: 31 additions & 0 deletions src/murfey/server/api/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,37 @@ async def check_instrument_server(instrument_name: MurfeyInstrumentName):
return data


@router.get("/instruments/{instrument_name}/sessions/{session_id}/possible_otf_dirs")
async def get_possible_otf_dirs(
instrument_name: MurfeyInstrumentName,
session_id: MurfeySessionID,
) -> list[File]:
"""
Submits a GET request to the client-side instrument server to get all top-level
directories found under the configured OTF files directory.
"""
data: list[File] = []
machine_config = get_machine_config(instrument_name=instrument_name)[
instrument_name
]
if machine_config.instrument_server_url:
async with lock:
token = instrument_server_tokens[session_id]["access_token"]
async with aiohttp.ClientSession() as clientsession:
url_path = url_path_for(
"api.router",
"get_possible_otf_dirs",
instrument_name=sanitise(instrument_name),
session_id=session_id,
)
async with clientsession.get(
f"{machine_config.instrument_server_url}{url_path}",
headers={"Authorization": f"Bearer {token}"},
) as resp:
data = await resp.json()
return data


@router.get(
"/instruments/{instrument_name}/sessions/{session_id}/possible_gain_references"
)
Expand Down
2 changes: 1 addition & 1 deletion src/murfey/util/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class MachineConfig(BaseModel): # type: ignore
"files": [],
}
mkdir_chmod: int = 0o2750
gain_directory_name: str = "processing"

# Rsync setup
rsync_url: str = ""
Expand All @@ -85,7 +86,6 @@ class MachineConfig(BaseModel): # type: ignore
# General processing setup
processing_enabled: bool = True
process_by_default: bool = True
gain_directory_name: str = "processing"
process_multiple_datasets: bool = True
processed_directory_name: str = "processed"
processed_extra_directory: str = ""
Expand Down
18 changes: 18 additions & 0 deletions src/murfey/util/route_manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ murfey.instrument_server.api.router:
type: int
methods:
- POST
- path: /instruments/{instrument_name}/sessions/{session_id}/possible_otf_dirs
function: get_possible_otf_dirs
path_params:
- name: instrument_name
type: str
- name: session_id
type: int
methods:
- GET
- path: /instruments/{instrument_name}/sessions/{session_id}/possible_gain_references
function: get_possible_gain_references
path_params:
Expand Down Expand Up @@ -543,6 +552,15 @@ murfey.server.api.instrument.router:
type: str
methods:
- GET
- path: /instrument_server/instruments/{instrument_name}/sessions/{session_id}/possible_otf_dirs
function: get_possible_otf_dirs
path_params:
- name: instrument_name
type: str
- name: session_id
type: int
methods:
- GET
- path: /instrument_server/instruments/{instrument_name}/sessions/{session_id}/possible_gain_references
function: get_possible_gain_references
path_params:
Expand Down
101 changes: 85 additions & 16 deletions tests/instrument_server/test_api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from pathlib import Path
from typing import Optional
from unittest.mock import ANY, MagicMock, patch
Expand All @@ -15,6 +16,7 @@
)
from murfey.util import posix_path
from murfey.util.api import url_path_for
from murfey.util.config import MachineConfig


def set_up_test_client(session_id: Optional[int] = None):
Expand All @@ -30,17 +32,17 @@ def set_up_test_client(session_id: Optional[int] = None):
return TestClient(client_app)


test_get_murfey_url_params_matrix = (
# Server URL to use
("default",),
("0.0.0.0:8000",),
("murfey_server",),
("http://murfey_server:8000",),
("http://murfey_server:8080/api",),
@pytest.mark.parametrize(
"test_params",
(
# Server URL to use
("default",),
("0.0.0.0:8000",),
("murfey_server",),
("http://murfey_server:8000",),
("http://murfey_server:8080/api",),
),
)


@pytest.mark.parametrize("test_params", test_get_murfey_url_params_matrix)
def test_get_murfey_url(
test_params: tuple[str],
mock_client_configuration, # From conftest.py
Expand Down Expand Up @@ -101,15 +103,82 @@ def test_check_multigrid_controller_status(mocker: MockerFixture):
}


test_upload_gain_reference_params_matrix = (
# Rsync URL settings
("http://1.1.1.1",), # When rsync_url is provided
("",), # When rsync_url is blank
(None,), # When rsync_url not provided
@pytest.mark.parametrize(
"has_gain_reference_directory",
(True, False),
)
def test_get_possible_otf_dirs(
mocker: MockerFixture,
has_gain_reference_directory: bool,
tmp_path: Path,
):
session_id = 1

# Set up client-side OTF files
otf_dir = tmp_path / "otfs" / "otfs-123456"
otf_dir.mkdir(parents=True, exist_ok=True)
for stem in ("far_red.tiff", "red.tiff", "green.tiff", "blue.tiff"):
file = otf_dir / stem
file.touch(exist_ok=True)

# Mock the stored tokens
mocker.patch("murfey.instrument_server.api.tokens", {session_id: "dummy"})

# Mock the stored Murfey URL
mocker.patch(
"murfey.instrument_server.api.murfey_server_url",
MagicMock(url="dummy"),
)

# Mock the GET request
mock_machine_config = json.loads(
MachineConfig(
gain_reference_directory=otf_dir.parent
if has_gain_reference_directory
else None
).model_dump_json()
)
mock_response = MagicMock()
mock_response.json.return_value = mock_machine_config
mocker.patch(
"murfey.instrument_server.api.requests.get", return_value=mock_response
)

# Set up the test client
client_server = set_up_test_client(session_id=session_id)
url_path = url_path_for(
"api.router",
"get_possible_otf_dirs",
instrument_name="sim",
session_id=session_id,
)
response = client_server.get(url_path)

# Check that the result is as expected
assert response.json() == (
[
{
"name": str(otf_dir.name),
"description": "",
"size": ANY,
"timestamp": ANY,
"full_path": str(otf_dir),
}
]
if has_gain_reference_directory
else []
)


@pytest.mark.parametrize("test_params", test_upload_gain_reference_params_matrix)
@pytest.mark.parametrize(
"test_params",
(
# Rsync URL settings
("http://1.1.1.1",), # When rsync_url is provided
("",), # When rsync_url is blank
(None,), # When rsync_url not provided
),
)
def test_upload_gain_reference(
mocker: MockerFixture,
test_params: tuple[Optional[str]],
Expand Down
Loading