Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-39517: Add getExpRecord butler utility #49

Merged
merged 2 commits into from
Jun 21, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 41 additions & 0 deletions python/lsst/summit/utils/butlerUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"getDayObsSeqNumFromExposureId",
"removeDataProduct",
"getLatissOnSkyDataIds",
"getExpRecord",
]

_LATISS_DEFAULT_COLLECTIONS = ['LATISS/raw/all', 'LATISS/calib', "LATISS/runs/quickLook"]
Expand Down Expand Up @@ -765,3 +766,43 @@ def isOnSky(expRecord):
return filledIds
else:
return [updateDataIdOrDataCord(dataId, detector=0) for dataId in dataIds]


def getExpRecord(butler, instrument, expId=None, dayObs=None, seqNum=None):
"""Get the exposure record for a given exposure ID or dayObs+seqNum.

Parameters
----------
butler : `lsst.daf.butler.Butler`
The butler.
expId : `int`
The exposure ID.
instrument : `str`
The instrument name, e.g. 'LSSTCam'.

Returns
-------
expRecord : `lsst.daf.butler.DimensionRecord`
The exposure record.
"""
if expId is None and (dayObs is None or seqNum is None):
raise ValueError('Must supply either expId or (dayObs AND seqNum)')

where = "instrument=inst" # Note you can't use =instrument as bind-strings can't clash with dimensions
bind = {'inst': instrument}
if expId:
where += ' AND exposure.id=expId'
bind.update({'expId': expId})
if dayObs and seqNum:
where += ' AND exposure.day_obs=dayObs AND exposure.seq_num=seqNum'
bind.update({'dayObs': dayObs, 'seqNum': seqNum})

expRecords = butler.registry.queryDimensionRecords("exposure",
where=where,
bind=bind,
datasets='raw')
expRecords = list(set(expRecords)) # must call set as this may contain many duplicates
if len(expRecords) != 1:
raise RuntimeError(f'Failed to find unique exposure record for {instrument=} with'
f' {expId=}, {dayObs=}, {seqNum=}, got {len(expRecords)} records')
return expRecords[0]
22 changes: 22 additions & 0 deletions tests/test_butlerUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
_assureDict,
getLatissDefaultCollections,
RECENT_DAY,
getExpRecord,
)
from lsst.summit.utils.butlerUtils import removeDataProduct # noqa: F401
import lsst.daf.butler as dafButler
Expand Down Expand Up @@ -392,6 +393,27 @@ def test_updateDataId(self):
self.assertTrue('detector' in dataId)
self.assertEqual(dataId['detector'], 321)

def test_getExpRecord(self):
expId = self.expIdOnly['exposure']
dayObs = self.dayObsSeqNumIdOnly['day_obs']
seqNum = self.dayObsSeqNumIdOnly['seq_num']

recordByExpId = getExpRecord(self.butler, 'LATISS', expId=expId)
self.assertIsInstance(recordByExpId, dafButler.dimensions.DimensionRecord)

recordByDayObsSeqNum = getExpRecord(self.butler, 'LATISS', dayObs=dayObs, seqNum=seqNum)
self.assertIsInstance(recordByDayObsSeqNum, dafButler.dimensions.DimensionRecord)
self.assertEqual(recordByExpId, recordByDayObsSeqNum)

with self.assertRaises(ValueError):
# because we need dayObs too, so immediate raise due to bad args
_ = getExpRecord(self.butler, 'LATISS', seqNum=seqNum)

with self.assertRaises(RuntimeError):
# (dayObs, seqNum) no longer matches the expId, so there are no
# results, which is a RuntimeError
_ = getExpRecord(self.butler, 'LATISS', expId=expId, dayObs=dayObs, seqNum=seqNum+1)


class ButlerInitTestCase(lsst.utils.tests.TestCase):
"""Separately test whether we can make a butler with the env var set
Expand Down