"""USB plugin API endpoints.""" from flask import Blueprint, request from flask_jwt_extended import jwt_required from datetime import datetime from shopdb.extensions import db from shopdb.core.models import Machine, MachineType, Vendor, Model from shopdb.utils.responses import ( success_response, error_response, paginated_response, ErrorCodes ) from shopdb.utils.pagination import get_pagination_params, paginate_query from ..models import USBCheckout usb_bp = Blueprint('usb', __name__) def get_usb_machinetype_id(): """Get the USB Device machine type ID dynamically.""" usb_type = MachineType.query.filter( MachineType.machinetype.ilike('%usb%') ).first() return usb_type.machinetypeid if usb_type else None @usb_bp.route('', methods=['GET']) @jwt_required() def list_usb_devices(): """ List all USB devices with checkout status. Query parameters: - page, per_page: Pagination - search: Search by serial number or alias - available: Filter to only available (not checked out) devices """ page, per_page = get_pagination_params(request) usb_type_id = get_usb_machinetype_id() if not usb_type_id: return success_response([]) # No USB type found # Get USB devices from machines table query = db.session.query(Machine).filter( Machine.machinetypeid == usb_type_id, Machine.isactive == True ) # Search filter if search := request.args.get('search'): query = query.filter( db.or_( Machine.serialnumber.ilike(f'%{search}%'), Machine.alias.ilike(f'%{search}%'), Machine.machinenumber.ilike(f'%{search}%') ) ) query = query.order_by(Machine.alias) items, total = paginate_query(query, page, per_page) # Build response with checkout status data = [] for device in items: # Check if currently checked out active_checkout = USBCheckout.query.filter_by( machineid=device.machineid, checkin_time=None ).first() item = { 'machineid': device.machineid, 'machinenumber': device.machinenumber, 'alias': device.alias, 'serialnumber': device.serialnumber, 'notes': device.notes, 'vendor_name': device.vendor.vendorname if device.vendor else None, 'model_name': device.model.modelnumber if device.model else None, 'is_checked_out': active_checkout is not None, 'current_checkout': active_checkout.to_dict() if active_checkout else None } data.append(item) # Filter by availability if requested if request.args.get('available', '').lower() == 'true': data = [d for d in data if not d['is_checked_out']] total = len(data) return paginated_response(data, page, per_page, total) @usb_bp.route('/', methods=['GET']) @jwt_required() def get_usb_device(device_id: int): """Get a single USB device with checkout history.""" device = Machine.query.filter_by( machineid=device_id, machinetypeid=get_usb_machinetype_id() ).first() if not device: return error_response( ErrorCodes.NOT_FOUND, f'USB device with ID {device_id} not found', http_code=404 ) # Get checkout history checkouts = USBCheckout.query.filter_by( machineid=device_id ).order_by(USBCheckout.checkout_time.desc()).limit(50).all() # Check current checkout active_checkout = next((c for c in checkouts if c.checkin_time is None), None) result = { 'machineid': device.machineid, 'machinenumber': device.machinenumber, 'alias': device.alias, 'serialnumber': device.serialnumber, 'notes': device.notes, 'vendor_name': device.vendor.vendorname if device.vendor else None, 'model_name': device.model.modelnumber if device.model else None, 'is_checked_out': active_checkout is not None, 'current_checkout': active_checkout.to_dict() if active_checkout else None, 'checkout_history': [c.to_dict() for c in checkouts] } return success_response(result) @usb_bp.route('//checkout', methods=['POST']) @jwt_required() def checkout_device(device_id: int): """Check out a USB device.""" device = Machine.query.filter_by( machineid=device_id, machinetypeid=get_usb_machinetype_id() ).first() if not device: return error_response( ErrorCodes.NOT_FOUND, f'USB device with ID {device_id} not found', http_code=404 ) # Check if already checked out active_checkout = USBCheckout.query.filter_by( machineid=device_id, checkin_time=None ).first() if active_checkout: return error_response( ErrorCodes.CONFLICT, f'Device is already checked out by {active_checkout.sso}', http_code=409 ) data = request.get_json() or {} if not data.get('sso'): return error_response(ErrorCodes.VALIDATION_ERROR, 'sso is required') checkout = USBCheckout( machineid=device_id, sso=data['sso'], checkout_name=data.get('name'), checkout_reason=data.get('reason'), checkout_time=datetime.utcnow() ) db.session.add(checkout) db.session.commit() return success_response(checkout.to_dict(), message='Device checked out', http_code=201) @usb_bp.route('//checkin', methods=['POST']) @jwt_required() def checkin_device(device_id: int): """Check in a USB device.""" device = Machine.query.filter_by( machineid=device_id, machinetypeid=get_usb_machinetype_id() ).first() if not device: return error_response( ErrorCodes.NOT_FOUND, f'USB device with ID {device_id} not found', http_code=404 ) # Find active checkout active_checkout = USBCheckout.query.filter_by( machineid=device_id, checkin_time=None ).first() if not active_checkout: return error_response( ErrorCodes.VALIDATION_ERROR, 'Device is not currently checked out', http_code=400 ) data = request.get_json() or {} active_checkout.checkin_time = datetime.utcnow() active_checkout.was_wiped = data.get('was_wiped', False) active_checkout.checkin_notes = data.get('notes') db.session.commit() return success_response(active_checkout.to_dict(), message='Device checked in') @usb_bp.route('//history', methods=['GET']) @jwt_required() def get_checkout_history(device_id: int): """Get checkout history for a USB device.""" page, per_page = get_pagination_params(request) query = USBCheckout.query.filter_by( machineid=device_id ).order_by(USBCheckout.checkout_time.desc()) items, total = paginate_query(query, page, per_page) data = [c.to_dict() for c in items] return paginated_response(data, page, per_page, total) @usb_bp.route('/checkouts', methods=['GET']) @jwt_required() def list_all_checkouts(): """List all checkouts (active and historical).""" page, per_page = get_pagination_params(request) query = db.session.query(USBCheckout).join( Machine, USBCheckout.machineid == Machine.machineid ) # Filter by active only if request.args.get('active', '').lower() == 'true': query = query.filter(USBCheckout.checkin_time == None) # Filter by user if sso := request.args.get('sso'): query = query.filter(USBCheckout.sso == sso) query = query.order_by(USBCheckout.checkout_time.desc()) items, total = paginate_query(query, page, per_page) # Include device info data = [] for checkout in items: device = Machine.query.get(checkout.machineid) item = checkout.to_dict() item['device'] = { 'machineid': device.machineid, 'alias': device.alias, 'serialnumber': device.serialnumber } if device else None data.append(item) return paginated_response(data, page, per_page, total)