Skip to content

Commit 38d5198

Browse files
Merge pull request #409 from AllenNeuralDynamics/add-replace-flag-for-instruments
Add replace flag for instruments
2 parents 3795a31 + 857040d commit 38d5198

2 files changed

Lines changed: 64 additions & 13 deletions

File tree

src/aind_metadata_mapper/fip/make_instrument.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@
3636
from aind_data_schema.components.devices import Computer
3737
from aind_data_schema_models.modalities import Modality
3838

39-
from aind_metadata_mapper.utils import check_instrument_id, prompt_for_string, save_instrument
39+
from aind_metadata_mapper.utils import (
40+
check_existing_instrument,
41+
check_instrument_id,
42+
prompt_for_string,
43+
save_instrument,
44+
)
4045

4146
logger = logging.getLogger(__name__)
4247

@@ -449,9 +454,25 @@ def main(
449454
input_func=input_func,
450455
)
451456

457+
# Check if instrument with same ID and date already exists
458+
modification_date_str = instrument_model.modification_date.isoformat()
459+
record_exists = check_existing_instrument(instrument_id, modification_date_str)
460+
replace = False
461+
if record_exists:
462+
logger.info(
463+
f"An instrument with ID '{instrument_id}' and modification_date '{modification_date_str}' already exists."
464+
)
465+
response = input_func("Do you want to overwrite the existing record? [y/N]: ").strip().lower()
466+
if response in ("y", "yes"):
467+
replace = True
468+
logger.info("Will overwrite existing record.")
469+
else:
470+
logger.info("Cancelled. Not overwriting existing record.")
471+
sys.exit(0)
472+
452473
# Save instrument (includes round-trip validation)
453474
try:
454-
save_instrument(instrument_model)
475+
save_instrument(instrument_model, replace=replace)
455476
except ValueError as e:
456477
logger.error(f"Failed - {e}")
457478
sys.exit(1)

src/aind_metadata_mapper/utils.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
API_BASE_URL = "http://aind-metadata-service/api/v2/instrument"
1414

1515

16-
def get_instrument(instrument_id: str, modification_date: Optional[str] = None) -> Optional[dict]: # pragma: no cover
16+
def get_instrument(
17+
instrument_id: str, modification_date: Optional[str] = None, suppress_warning: bool = False
18+
) -> Optional[dict]: # pragma: no cover
1719
"""Get instrument.
1820
1921
Gets the latest record by default, or a specific record if modification_date is provided.
@@ -24,6 +26,8 @@ def get_instrument(instrument_id: str, modification_date: Optional[str] = None)
2426
Instrument identifier.
2527
modification_date : Optional[str]
2628
Optional modification date (YYYY-MM-DD format). If None, returns latest record.
29+
suppress_warning : bool
30+
If True, suppress warning when modification_date not found.
2731
2832
Returns
2933
-------
@@ -48,21 +52,22 @@ def get_instrument(instrument_id: str, modification_date: Optional[str] = None)
4852
for record in matching_records:
4953
if record.get("modification_date") == modification_date:
5054
return record
51-
# No matching date found - log available dates
52-
available_dates = sorted(
53-
set(r.get("modification_date") for r in matching_records if r.get("modification_date"))
54-
)
55-
logger.warning(
56-
f"No record found for instrument_id '{instrument_id}' with modification_date '{modification_date}'. "
57-
f"Available dates: {available_dates}"
58-
)
55+
# No matching date found - log available dates (unless suppressed)
56+
if not suppress_warning:
57+
available_dates = sorted(
58+
set(r.get("modification_date") for r in matching_records if r.get("modification_date"))
59+
)
60+
logger.warning(
61+
f"No record found for instrument_id '{instrument_id}' with modification_date '{modification_date}'. "
62+
f"Available dates: {available_dates}"
63+
)
5964
return None
6065
else:
6166
# Return latest record
6267
return sorted(matching_records, key=lambda record: record["modification_date"])[-1]
6368

6469

65-
def save_instrument(instrument_model: instrument.Instrument) -> None: # pragma: no cover
70+
def save_instrument(instrument_model: instrument.Instrument, replace: bool = False) -> None: # pragma: no cover
6671
"""Save instrument and validate round-trip.
6772
6873
Saves the instrument, then retrieves it back and verifies that what we get back
@@ -72,6 +77,8 @@ def save_instrument(instrument_model: instrument.Instrument) -> None: # pragma:
7277
----------
7378
instrument_model : instrument.Instrument
7479
Instrument to POST.
80+
replace : bool
81+
If True, overwrite existing record with same instrument_id and modification_date.
7582
7683
Raises
7784
------
@@ -83,7 +90,8 @@ def save_instrument(instrument_model: instrument.Instrument) -> None: # pragma:
8390
# Use model_dump_json() and parse to ensure dates are properly serialized
8491
source_dict = json.loads(instrument_model.model_dump_json())
8592
logger.info(f"POSTing instrument to {API_BASE_URL}")
86-
response = requests.post(API_BASE_URL, json=source_dict)
93+
params = {"replace": "true"} if replace else {}
94+
response = requests.post(API_BASE_URL, json=source_dict, params=params)
8795
# POST 400 is always an error (e.g., "Record already exists")
8896
if response.status_code == 400:
8997
error_msg = response.json().get("message", response.text)
@@ -108,6 +116,28 @@ def save_instrument(instrument_model: instrument.Instrument) -> None: # pragma:
108116
raise ValueError("Round-trip test failed: Source and read-back instruments differ")
109117

110118

119+
def check_existing_instrument(
120+
instrument_id: str,
121+
modification_date: str,
122+
) -> bool: # pragma: no cover
123+
"""Check if an instrument with the same ID and modification_date already exists.
124+
125+
Parameters
126+
----------
127+
instrument_id : str
128+
Instrument identifier.
129+
modification_date : str
130+
Modification date (YYYY-MM-DD format).
131+
132+
Returns
133+
-------
134+
bool
135+
True if a record with the same instrument_id and modification_date exists.
136+
"""
137+
existing = get_instrument(instrument_id, modification_date=modification_date, suppress_warning=True)
138+
return existing is not None
139+
140+
111141
def check_instrument_id(
112142
instrument_id: str,
113143
skip_confirmation: bool = False,

0 commit comments

Comments
 (0)