# system.kanoa.operators.getOLEDataByShift(userId, startDate, endDate, assetId, attributionMode)

Returns one row per rostered shift (sch.operatorShiftAssignment/shiftBlock) overlapping the range
for this user, or for every operator assigned to assetId when userId is None (assetId is then required -
there's no "every user's every shift" mode, and no login-session union like the other two OLE procs have,
since a shift bucket only exists where there's a schedule row)

---
### Parameters
**userId** `Integer`: Id of user, or None to return one row per operator's shift for the given assetId<br />
**startDate** `DateTime`: Start date<br />
**endDate** `DateTime`: End date<br />
**assetId** `Integer`: required when userId is None; optional filter otherwise<br />
**attributionMode** `String`: 'EvenSplit', 'FullCredit', or 'PrimaryOnly' - controls how counter output is split when the asset has a crew (multiple concurrent users). Default 'EvenSplit'.<br />

---
### Returns
**oleData** `pyDataset`<br />

---
### Example
```python
userId = None
startDate = system.date.parse("2025-11-01 00:00:00")
endDate = system.date.parse("2025-11-05 00:00:00")
assetId = 4
attributionMode = 'EvenSplit'
data = system.kanoa.operators.getOLEDataByShift(userId, startDate, endDate, assetId, attributionMode)
```
