Plugin reports render; USB check-in/out audit report; kiosk drive lists
This commit is contained in:
@@ -16,7 +16,7 @@ History rows are synthesized from usbcheckouts (each row = a check-out event and
|
||||
if returned, a check-in event).
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from shopdb.api import (
|
||||
db, success_response, error_response, ErrorCodes,
|
||||
@@ -452,3 +452,47 @@ def _kiosk_flag(key, default):
|
||||
if row is None or row.value is None or row.value == '':
|
||||
return default
|
||||
return str(row.value).strip().lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
|
||||
def audit_rows(days):
|
||||
"""Check-out / check-in events over a window (self-hosted mode).
|
||||
|
||||
One usbcheckouts row holds BOTH ends of a loan, so it expands into two
|
||||
events - the take and, when it has come back, the return. Reporting the row
|
||||
once would make a returned drive indistinguishable from one still out.
|
||||
|
||||
Rows only: the caller owns the CSV, the summary and the response shape, so
|
||||
both directory modes answer in exactly one voice.
|
||||
"""
|
||||
cutoff = None
|
||||
if days > 0:
|
||||
cutoff = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(days=days)
|
||||
|
||||
query = (db.session.query(USBCheckout, USBDevice)
|
||||
.join(USBDevice, USBDevice.usbdeviceid == USBCheckout.usbdeviceid))
|
||||
if cutoff is not None:
|
||||
# Either end of the loan can fall in the window: a drive taken before it
|
||||
# and returned inside it is a check-in the window has to show.
|
||||
query = query.filter(db.or_(USBCheckout.checkouttime >= cutoff,
|
||||
USBCheckout.checkintime >= cutoff))
|
||||
|
||||
rows = []
|
||||
for checkout, device in query.all():
|
||||
base = {
|
||||
'device_id': device.serialnumber,
|
||||
'device_desc': device.label or device.productname or '',
|
||||
'badge': checkout.sso or '',
|
||||
'name': checkout.checkoutname or '',
|
||||
'locker_location': device.storagelocation or '',
|
||||
}
|
||||
if checkout.checkouttime and (cutoff is None or checkout.checkouttime >= cutoff):
|
||||
rows.append(dict(base, timestamp=checkout.checkouttime.isoformat(),
|
||||
action='check-out'))
|
||||
if checkout.checkintime and (cutoff is None or checkout.checkintime >= cutoff):
|
||||
rows.append(dict(base, timestamp=checkout.checkintime.isoformat(),
|
||||
action='check-in'))
|
||||
|
||||
# Newest first, and a total order so two events in the same second do not
|
||||
# swap places between identical runs.
|
||||
rows.sort(key=lambda row: (row['timestamp'], row['device_id']), reverse=True)
|
||||
return rows
|
||||
|
||||
Reference in New Issue
Block a user