"""Printers API routes.""" from flask import Blueprint, request from flask_jwt_extended import jwt_required from shopdb.extensions import db from shopdb.utils.responses import success_response, error_response, paginated_response, ErrorCodes from shopdb.utils.pagination import get_pagination_params, paginate_query from shopdb.core.models.machine import Machine, MachineType from shopdb.core.models.communication import Communication, CommunicationType from ..models import PrinterData from ..services import ZabbixService printers_bp = Blueprint('printers', __name__) @printers_bp.route('/', methods=['GET']) @jwt_required(optional=True) def list_printers(): """List all printers.""" page, per_page = get_pagination_params(request) # Get printer machine types printer_types = MachineType.query.filter_by(category='Printer').all() printer_type_ids = [pt.machinetypeid for pt in printer_types] query = Machine.query.filter( Machine.machinetypeid.in_(printer_type_ids), Machine.isactive == True ) # Filters if location_id := request.args.get('location', type=int): query = query.filter(Machine.locationid == location_id) if search := request.args.get('search'): query = query.filter( db.or_( Machine.machinenumber.ilike(f'%{search}%'), Machine.hostname.ilike(f'%{search}%'), Machine.alias.ilike(f'%{search}%') ) ) query = query.order_by(Machine.machinenumber) items, total = paginate_query(query, page, per_page) printers = [] for machine in items: printer_data = { 'machineid': machine.machineid, 'machinenumber': machine.machinenumber, 'hostname': machine.hostname, 'alias': machine.alias, 'serialnumber': machine.serialnumber, 'location': machine.location.locationname if machine.location else None, 'vendor': machine.vendor.vendor if machine.vendor else None, 'model': machine.model.modelnumber if machine.model else None, 'status': machine.status.status if machine.status else None, } # Add printer-specific data if machine.printerdata: pd = machine.printerdata printer_data['printerdata'] = { 'windowsname': pd.windowsname, 'sharename': pd.sharename, 'iscsf': pd.iscsf, 'pin': pd.pin, } # Get IP from communications primary_comm = next((c for c in machine.communications if c.isprimary), None) if not primary_comm and machine.communications: primary_comm = machine.communications[0] printer_data['ipaddress'] = primary_comm.ipaddress if primary_comm else None printers.append(printer_data) return paginated_response(printers, page, per_page, total) @printers_bp.route('/', methods=['GET']) @jwt_required(optional=True) def get_printer(machine_id: int): """Get a single printer with details.""" machine = Machine.query.get(machine_id) if not machine: return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404) data = machine.to_dict() data['machinetype'] = machine.machinetype.to_dict() if machine.machinetype else None data['vendor'] = machine.vendor.to_dict() if machine.vendor else None data['model'] = machine.model.to_dict() if machine.model else None data['location'] = machine.location.to_dict() if machine.location else None data['status'] = machine.status.to_dict() if machine.status else None data['communications'] = [c.to_dict() for c in machine.communications] # Add printer-specific data if machine.printerdata: pd = machine.printerdata data['printerdata'] = { 'id': pd.id, 'windowsname': pd.windowsname, 'sharename': pd.sharename, 'iscsf': pd.iscsf, 'installpath': pd.installpath, 'pin': pd.pin, } return success_response(data) @printers_bp.route('//printerdata', methods=['PUT']) @jwt_required() def update_printer_data(machine_id: int): """Update printer-specific data.""" machine = Machine.query.get(machine_id) if not machine: return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404) data = request.get_json() if not data: return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided') # Get or create printer data pd = machine.printerdata if not pd: pd = PrinterData(machineid=machine_id) db.session.add(pd) for key in ['windowsname', 'sharename', 'iscsf', 'installpath', 'pin']: if key in data: setattr(pd, key, data[key]) db.session.commit() return success_response({ 'id': pd.id, 'windowsname': pd.windowsname, 'sharename': pd.sharename, 'iscsf': pd.iscsf, 'installpath': pd.installpath, 'pin': pd.pin, }, message='Printer data updated') @printers_bp.route('//communication', methods=['PUT']) @jwt_required() def update_printer_communication(machine_id: int): """Update printer communication (IP address).""" machine = Machine.query.get(machine_id) if not machine: return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404) data = request.get_json() if not data: return error_response(ErrorCodes.VALIDATION_ERROR, 'No data provided') # Get or create IP communication type ip_comtype = CommunicationType.query.filter_by(comtype='IP').first() if not ip_comtype: ip_comtype = CommunicationType(comtype='IP', description='IP Network') db.session.add(ip_comtype) db.session.flush() # Find existing primary communication or create new one comm = next((c for c in machine.communications if c.isprimary), None) if not comm: comm = next((c for c in machine.communications if c.comtypeid == ip_comtype.comtypeid), None) if not comm: comm = Communication(machineid=machine_id, comtypeid=ip_comtype.comtypeid) db.session.add(comm) # Update fields if 'ipaddress' in data: comm.ipaddress = data['ipaddress'] if 'isprimary' in data: comm.isprimary = data['isprimary'] if 'macaddress' in data: comm.macaddress = data['macaddress'] db.session.commit() return success_response({ 'communicationid': comm.communicationid, 'ipaddress': comm.ipaddress, 'isprimary': comm.isprimary, }, message='Communication updated') @printers_bp.route('//supplies', methods=['GET']) @jwt_required(optional=True) def get_printer_supplies(machine_id: int): """Get supply levels from Zabbix (real-time lookup).""" machine = Machine.query.get(machine_id) if not machine: return error_response(ErrorCodes.NOT_FOUND, 'Printer not found', http_code=404) # Get IP address primary_comm = next((c for c in machine.communications if c.isprimary), None) if not primary_comm and machine.communications: primary_comm = machine.communications[0] if not primary_comm or not primary_comm.ipaddress: return error_response(ErrorCodes.VALIDATION_ERROR, 'Printer has no IP address') service = ZabbixService() if not service.isconfigured: return error_response(ErrorCodes.SERVICE_UNAVAILABLE, 'Zabbix not configured') supplies = service.getsuppliesbyip(primary_comm.ipaddress) return success_response({ 'ipaddress': primary_comm.ipaddress, 'supplies': supplies or [] }) @printers_bp.route('/dashboard/summary', methods=['GET']) @jwt_required(optional=True) def dashboard_summary(): """Get printer summary for dashboard.""" printer_types = MachineType.query.filter_by(category='Printer').all() printer_type_ids = [pt.machinetypeid for pt in printer_types] total = Machine.query.filter( Machine.machinetypeid.in_(printer_type_ids), Machine.isactive == True ).count() return success_response({ 'totalprinters': total, 'total': total, 'online': total, # Placeholder - would need Zabbix integration for real status 'lowsupplies': 0, 'criticalsupplies': 0 })