Skip to content
Merged
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
8 changes: 5 additions & 3 deletions src/virtualship/utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import warnings
from datetime import timedelta
from functools import lru_cache
from importlib.resources import files
Expand Down Expand Up @@ -89,11 +90,12 @@ def mfp_to_yaml(excel_file_path: str, yaml_output_path: str): # noqa: D417

extra_columns = actual_columns - expected_columns
if extra_columns:
print(
f"Warning: Found additional unexpected columns {list(extra_columns)}. "
warnings.warn(
f"Found additional unexpected columns {list(extra_columns)}. "
"Manually added columns have no effect. "
"If the MFP export format changed, please submit an issue: "
"https://github.com/OceanParcels/virtualship/issues."
"https://github.com/OceanParcels/virtualship/issues.",
stacklevel=2,
)

# Drop unexpected columns (optional, only if you want to ensure strict conformity)
Expand Down
92 changes: 47 additions & 45 deletions tests/test_mfp_to_yaml.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,54 @@
from unittest.mock import patch

import pandas as pd
import pytest

from virtualship.expedition.instrument_type import InstrumentType
from virtualship.expedition.schedule import Schedule
from virtualship.utils import mfp_to_yaml

# Sample correct MFP data
VALID_MFP_DATA = pd.DataFrame(
{
"Station Type": ["A", "B", "C"],
"Name": ["Station1", "Station2", "Station3"],
"Latitude": [30, 31, 32],
"Longitude": [-44, -45, -46],
"Instrument": ["CTD, DRIFTER", "ARGO_FLOAT", "XBT, CTD, DRIFTER"],
}
)

# Missing required columns
MISSING_HEADERS_DATA = pd.DataFrame(
{"Station Type": ["A"], "Name": ["Station1"], "Latitude": [10.5]}
)

# Extra unexpected columns
EXTRA_HEADERS_DATA = VALID_MFP_DATA.copy()
EXTRA_HEADERS_DATA["Unexpected Column"] = ["Extra1", "Extra2", "Extra3"]


@patch("pandas.read_excel", return_value=VALID_MFP_DATA)
def test_mfp_to_yaml_success(mock_read_excel, tmp_path):

def valid_mfp_data():
return pd.DataFrame(
{
"Station Type": ["A", "B", "C"],
"Name": ["Station1", "Station2", "Station3"],
"Latitude": [30, 31, 32],
"Longitude": [-44, -45, -46],
"Instrument": ["CTD, DRIFTER", "ARGO_FLOAT", "XBT, CTD, DRIFTER"],
}
)


@pytest.fixture
def valid_mfp_file(tmp_path):
path = tmp_path / "file.xlsx"
valid_mfp_data().to_excel(path, index=False)
yield path


@pytest.fixture
def missing_columns_mfp_file(tmp_path):
path = tmp_path / "file.xlsx"
valid_mfp_data().drop(columns=["Longitude", "Instrument"]).to_excel(
path, index=False
)
yield path


@pytest.fixture
def unexpected_header_mfp_file(tmp_path):
path = tmp_path / "file.xlsx"
df = valid_mfp_data()
df["Unexpected Column"] = ["Extra1", "Extra2", "Extra3"]
df.to_excel(path, index=False)
yield path


def test_mfp_to_yaml_success(valid_mfp_file, tmp_path):
"""Test that mfp_to_yaml correctly processes a valid MFP Excel file."""
yaml_output_path = tmp_path / "schedule.yaml"

# Run function (No need to mock open() for YAML, real file is created)
mfp_to_yaml("mock_file.xlsx", yaml_output_path)
mfp_to_yaml(valid_mfp_file, yaml_output_path)

# Ensure the YAML file was written
assert yaml_output_path.exists()
Expand All @@ -52,43 +66,31 @@ def test_mfp_to_yaml_success(mock_read_excel, tmp_path):
]


@patch("pandas.read_excel", return_value=MISSING_HEADERS_DATA)
def test_mfp_to_yaml_missing_headers(mock_read_excel, tmp_path):
def test_mfp_to_yaml_missing_headers(missing_columns_mfp_file, tmp_path):
"""Test that mfp_to_yaml raises an error when required columns are missing."""
yaml_output_path = tmp_path / "schedule.yaml"

with pytest.raises(
ValueError,
match="Error: Missing column 'Instrument'. Have you added this column after exporting from MFP?",
):
mfp_to_yaml("mock_file.xlsx", yaml_output_path)
mfp_to_yaml(missing_columns_mfp_file, yaml_output_path)


@patch("pandas.read_excel", return_value=EXTRA_HEADERS_DATA)
@patch("builtins.print") # Capture printed warnings
def test_mfp_to_yaml_extra_headers(mock_print, mock_read_excel, tmp_path):
def test_mfp_to_yaml_extra_headers(unexpected_header_mfp_file, tmp_path):
"""Test that mfp_to_yaml prints a warning when extra columns are found."""
yaml_output_path = tmp_path / "schedule.yaml"

# Run function
mfp_to_yaml("mock_file.xlsx", yaml_output_path)

# Ensure a warning message was printed
mock_print.assert_any_call(
"Warning: Found additional unexpected columns ['Unexpected Column']. "
"Manually added columns have no effect. "
"If the MFP export format changed, please submit an issue: "
"https://github.com/OceanParcels/virtualship/issues."
)
with pytest.warns(UserWarning, match="Found additional unexpected columns.*"):
mfp_to_yaml(unexpected_header_mfp_file, yaml_output_path)


@patch("pandas.read_excel", return_value=VALID_MFP_DATA)
def test_mfp_to_yaml_instrument_conversion(mock_read_excel, tmp_path):
def test_mfp_to_yaml_instrument_conversion(valid_mfp_file, tmp_path):
"""Test that instruments are correctly converted into InstrumentType enums."""
yaml_output_path = tmp_path / "schedule.yaml"

# Run function
mfp_to_yaml("mock_file.xlsx", yaml_output_path)
mfp_to_yaml(valid_mfp_file, yaml_output_path)

# Load the generated YAML
data = Schedule.from_yaml(yaml_output_path)
Expand Down