Processors¶
Every processor subclasses AbstractProcessor and implements two methods:
_compute()— returns apandas.DataFramewith one row per output unit.nwbize(nwb)— populates anNWBFilewith the same data (optional).
compute() wraps _compute() and stamps provenance (packaging_version,
data_contract_version, dataset_version, processor) into df.attrs.
AbstractProcessor¶
AbstractProcessor
¶
Bases: ABC
Source code in src/aind_behavior_vr_foraging_packaging/_base.py
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
output_name
property
¶
Canonical name used as the parquet filename stem.
Returns __output_name__ if defined on the class, otherwise a
snake_case of the class name (e.g. LicksProcessor → licks_processor).
provenance
cached
property
¶
Provenance snapshot for this processor's dataset.
Cached so that :meth:compute and version-check code in subclasses
share a single :class:~aind_behavior_vr_foraging_packaging._provenance.PackagingProvenance
instance rather than rebuilding it on every call.
strict_parsing
property
¶
Whether known data anomalies raise instead of being logged and worked around.
The flag covers only anomalies a processor explicitly checks for and can name, and only where a degraded-but-meaningful output exists. The convention is::
if <specific condition detected>:
msg = "<what was violated>"
if self.strict_parsing:
raise DatasetProcessorError(msg)
logger.warning("%s; <what is used instead>.", msg)
It does not gate general exceptions. Never write
except Exception: ... if self.strict_parsing: raise — with the flag off (the
default) that swallows real bugs, API drift and corrupt files as though they were
data quirks, dropping the processor's output while the run still reports success.
Catch only the exception types that signal an expected condition, narrowly —
e.g. except (KeyError, FileNotFoundError) for a stream a given schema version
does not declare — and let everything else propagate.
Failures that leave nothing meaningful to emit (e.g. absent treadmill calibration, without which position cannot be computed at all) should raise unconditionally rather than consult this flag: there is no degraded output to fall back to.
Isolating one failure from the rest of a run is the caller's job, not the flag's.
:func:~aind_behavior_vr_foraging_packaging.pipeline.batch.process_sessions
catches whatever a processor raises, so a single bad session or processor never
aborts a batch.
compute()
¶
Return the processor's output DataFrame with provenance metadata in attrs.
Calls :meth:_compute, then stamps df.attrs with the session-level
provenance keys from :class:~aind_behavior_vr_foraging_packaging._provenance.PackagingProvenance
plus a processor-specific processor key (this class's name).
Attrs already set by _compute (e.g. sampling_rate_hz from
:class:SniffingProcessor) are preserved via setdefault.
Source code in src/aind_behavior_vr_foraging_packaging/_base.py
nwbize(nwb_file)
¶
Write this processor's output to nwb_file and return it.
Default implementation is a no-op. Override in subclasses that have
an NWB representation. May call compute() internally; the two
methods are intentionally independent (no shared state).
That independence costs a second full _compute() per session when
both outputs are written (--write-nwb). Processors for which that
is expensive decorate _compute with
:func:~aind_behavior_vr_foraging_packaging._base.cached_frame, which
removes the recomputation while preserving the no-shared-state
guarantee — every call still hands back its own copy.
Source code in src/aind_behavior_vr_foraging_packaging/_base.py
PackagingProvenance¶
PackagingProvenance
¶
Bases: BaseModel
Immutable provenance snapshot for one packaging run.
All version strings are validated as semver-compatible on construction, so any call site is guaranteed a well-formed object.
Attributes¶
packaging_version:
Version of this package (aind-behavior-vr-foraging-packaging).
data_contract_version:
Version of aind-behavior-vr-foraging (the behavioural schema library).
dataset_version:
Version recorded in the session's tasklogic_input.json.
Source code in src/aind_behavior_vr_foraging_packaging/_provenance.py
dataset_semver
property
¶
Dataset version as a parsed :class:semver.Version.
data_contract_semver
property
¶
Data-contract (parser) version as a parsed :class:semver.Version.
build(dataset)
classmethod
¶
Construct a :class:PackagingProvenance from a live dataset.
Source code in src/aind_behavior_vr_foraging_packaging/_provenance.py
SiteTableProcessor¶
SiteTableProcessor
¶
Bases: AbstractProcessor
Source code in src/aind_behavior_vr_foraging_packaging/processing/_site_table.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 | |
process_to_sites()
¶
Processes sites, patches, and blocks from the dataset and merges them. Returns a DataFrame with merged information.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_site_table.py
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | |
nwbize(nwb_file)
¶
Add sites to nwb_file from compute() output.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_site_table.py
PositionAndVelocityProcessor¶
PositionAndVelocityProcessor
¶
Bases: AbstractProcessor
Source code in src/aind_behavior_vr_foraging_packaging/processing/_position_and_velocity.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
nwbize(nwb_file)
¶
Add a single position_velocity DynamicTable to nwb_file.
The table has three columns: timestamp (harp time, seconds), position (cm) and
velocity (cm/s).
Source code in src/aind_behavior_vr_foraging_packaging/processing/_position_and_velocity.py
compute_position_and_velocity(dataset, *, downsample_to_hz)
¶
Computes position and velocity from treadmill encoder data
Source code in src/aind_behavior_vr_foraging_packaging/processing/_position_and_velocity.py
compute_position_and_velocity_from_treadmill(dataset, rig_config)
staticmethod
¶
Compute position and velocity from treadmill encoder data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
Dataset
|
A contraqctor Dataset providing access to HarpTreadmill data. |
required |
rig_config
|
dict
|
Rig configuration dict. Must contain a
|
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with |
DataFrame
|
indexed by harp timestamp (seconds). |
Source code in src/aind_behavior_vr_foraging_packaging/processing/_position_and_velocity.py
LicksProcessor¶
LicksProcessor
¶
Bases: AbstractProcessor
Source code in src/aind_behavior_vr_foraging_packaging/processing/_licks.py
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | |
nwbize(nwb_file)
¶
Add lick TimeSeries to nwb_file.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_licks.py
SniffingProcessor¶
SniffingProcessor
¶
Bases: AbstractProcessor
Source code in src/aind_behavior_vr_foraging_packaging/processing/_sniffing.py
nwbize(nwb_file)
¶
Add sniffing TimeSeries to nwb_file.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_sniffing.py
compute_sniff_signal(dataset)
¶
Computes the filtered breathing/sniff signal from the sniff detector raw voltage.
The raw voltage is resampled onto a uniform time grid and then passed
through a 0.2-20 Hz band-pass filter to isolate the breathing band, as
done in contraqctor.qc.harp.sniff_detector. The grid spacing is set
by the processor's resampling_frequency_hz when provided, otherwise
it defaults to the median sampling rate of the raw voltage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
Dataset
|
A contraqctor Dataset providing access to the
|
required |
Returns:
| Type | Description |
|---|---|
Series
|
A tuple of the filtered sniff signal (indexed by harp timestamp in |
float
|
seconds) and the sampling frequency (Hz) used for resampling. |
Source code in src/aind_behavior_vr_foraging_packaging/processing/_sniffing.py
SoftwareEventsProcessor¶
SoftwareEventsProcessor
¶
Bases: AbstractProcessor
Collects all SoftwareEvents streams into a single tall DataFrame.
Each row is one event, with columns:
event_name(str): the stream's short name, e.g."ActiveSite".-
data(str): JSON-serialized event payload. The structure varies by event type (polymorphic). Parse back with::df["data"].apply(json.loads) # or flatten a specific event type: active_sites = df[df["event_name"] == "ActiveSite"] pd.json_normalize(active_sites["data"].apply(json.loads).tolist())
Rows are sorted by harp timestamp (the DataFrame index, named "timestamp").
Source code in src/aind_behavior_vr_foraging_packaging/processing/_software_events.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
nwbize(nwb_file)
¶
Add each SoftwareEvents stream as a separate DynamicTable acquisition.
The NWB representation keeps one table per event type (mirroring the
original stream structure) rather than the single tall table produced by
compute(). The data column is JSON-serialized to remain
compatible with NWB's string dtypes.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_software_events.py
EventsProcessor¶
EventsProcessor
¶
Bases: AbstractProcessor
Collects derived/computed events into a single tall table, alongside SoftwareEventsProcessor's raw streams.
Each row is one event, with columns event_name (str) and data (the event's payload),
indexed by timestamp (harp seconds). Unlike SoftwareEventsProcessor, sources here are
computed from one or more underlying streams rather than being a straight passthrough.
To add a new event source: write a @staticmethod(dataset) -> pd.DataFrame returning a
data-column frame indexed by timestamp, and append it to _EVENT_SOURCES.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_events.py
nwbize(nwb_file)
¶
Add the derived events to nwb_file as an EventsTable.
The tall compute() frame maps one-to-one onto the table: the index becomes the required
timestamp column, and event_name/data become columns. data is JSON-serialized
to stay within NWB's string dtypes. No table is added when there are no derived events.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_events.py
SessionMetadataProcessor¶
SessionMetadataProcessor
¶
Bases: AbstractProcessor
Produces a single-row DataFrame of session-level metadata.
session_id is always the session directory's name; the stream's own
session_name field is ignored. subject and date come from the
contraqctor Behavior/InputSchemas/Session stream, with no fallback.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_session_metadata.py
Legacy processors¶
These are selected automatically when the dataset version is < 0.6.0.
LegacySiteTableProcessor
¶
Bases: SiteTableProcessor
SiteTableProcessor for VR foraging datasets with schema version < 0.6.0.
Key differences from SiteTableProcessor:
- Block stream is optional; falls back to ActivePatch stream when absent.
- Olfactometer always exposes 3 odor channels (channel 3 is the carrier).
- OdorSpecification uses legacy format: {"index": int, "concentration": float}.
- Choice cue is read from HarpBehavior.PwmStart with dynamic port detection
(PwmDO1 in v0.3, PwmDO2 in v0.4+; port is inferred from which channel is active).
- PatchStateAtReward is reconstructed from split PatchReward.json files when absent.
- A site interval may contain several water deliveries, because manual (experimenter)
water was not logged separately until 0.6.0 added ForceGiveReward. The first
delivery at or after the choice tone is taken as the earned one; see
:meth:_first_after_choice.
- HarpTreadmill is optional; friction defaults to 0 when absent.
- IsStopped and velocity streams are absent; last_stop_ fields are always None.
Note on NWB table naming: re-ingested legacy datasets use the current full-path naming convention for DynamicTables (e.g., "Behavior.HarpBehavior.PwmStart"), not the legacy stripped names (e.g., "HarpBehavior.PwmStart").
Source code in src/aind_behavior_vr_foraging_packaging/processing/_legacy_site_table.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | |
LegacyPositionAndVelocityProcessor
¶
Bases: PositionAndVelocityProcessor
PositionAndVelocityProcessor for VR foraging datasets with schema version < 0.6.0.
The only difference from PositionAndVelocityProcessor is support for v0.3
datasets where no HarpTreadmill device exists. In those sessions the encoder
was wired to HarpBehavior.AnalogData.Encoder (already-differential counts at
1 kHz) and rig calibration lives under treadmill.settings rather than
harp_treadmill.calibration.
For v0.4+ datasets this processor is identical to PositionAndVelocityProcessor: it uses HarpTreadmill.SensorData via the parent static method unchanged.
Source code in src/aind_behavior_vr_foraging_packaging/processing/_legacy_position_and_velocity.py
compute_position_and_velocity_from_analog_data(dataset, rig_config)
staticmethod
¶
Compute position and velocity from HarpBehavior.AnalogData (v0.3 datasets).
AnalogData.Encoder contains already-differential counts timestamped per read. Velocity and position are computed the same way as the parent's treadmill path: displacement / actual dt, using the harp timestamps directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
Dataset
|
Dataset providing access to HarpBehavior data. |
required |
rig_config
|
dict
|
Rig config dict. Supports the |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame with |
DataFrame
|
indexed by harp timestamp (seconds). |