diff --git a/shopdb/core/api/dashboard.py b/shopdb/core/api/dashboard.py index f93b874..52c48a4 100644 --- a/shopdb/core/api/dashboard.py +++ b/shopdb/core/api/dashboard.py @@ -4,74 +4,79 @@ from flask import Blueprint, current_app from flask_jwt_extended import jwt_required from shopdb.extensions import db -from shopdb.core.models import Machine, MachineType, MachineStatus +from shopdb.core.models import Asset, AssetType, AssetStatus from shopdb.utils.responses import success_response dashboard_bp = Blueprint('dashboard', __name__) +# Map asset type name -> dashboard category label +_TYPE_CATEGORY = { + 'equipment': 'Equipment', + 'computer': 'PC', + 'printer': 'Printer', + 'network_device': 'Network', +} + + +def _count_by_type(assettype): + return db.session.query(Asset).join(AssetType).filter( + Asset.isactive == True, + AssetType.assettype == assettype + ).count() + @dashboard_bp.route('/summary', methods=['GET']) @dashboard_bp.route('', methods=['GET']) @jwt_required(optional=True) def get_dashboard(): - """Get dashboard summary data.""" - # Count machines by category - equipment_count = db.session.query(Machine).join(MachineType).filter( - Machine.isactive == True, - MachineType.category == 'Equipment' - ).count() - - pc_count = db.session.query(Machine).join(MachineType).filter( - Machine.isactive == True, - MachineType.category == 'PC' - ).count() - - network_count = db.session.query(Machine).join(MachineType).filter( - Machine.isactive == True, - MachineType.category == 'Network' - ).count() + """Get dashboard summary data (asset-based).""" + equipment_count = _count_by_type('equipment') + pc_count = _count_by_type('computer') + network_count = _count_by_type('network_device') + printer_count = _count_by_type('printer') + total = equipment_count + pc_count + network_count + printer_count # Count by status status_counts = db.session.query( - MachineStatus.status, - db.func.count(Machine.machineid) + AssetStatus.status, + db.func.count(Asset.assetid) ).outerjoin( - Machine, - db.and_(Machine.statusid == MachineStatus.statusid, Machine.isactive == True) - ).group_by(MachineStatus.status).all() - - # Recent machines - recent_machines = Machine.query.filter_by(isactive=True).order_by( - Machine.createddate.desc() - ).limit(10).all() - - # Build status dict + Asset, + db.and_(Asset.statusid == AssetStatus.statusid, Asset.isactive == True) + ).group_by(AssetStatus.status).all() status_dict = {status: count for status, count in status_counts} + # Recent assets + recent = Asset.query.filter_by(isactive=True).order_by( + Asset.createddate.desc() + ).limit(10).all() + return success_response({ # Fields expected by frontend - 'totalmachines': equipment_count + pc_count + network_count, + 'totalmachines': total, 'totalequipment': equipment_count, 'totalpc': pc_count, 'totalnetwork': network_count, + 'totalprinter': printer_count, 'activemachines': status_dict.get('In Use', 0), 'inrepair': status_dict.get('In Repair', 0), - # Also include structured data + # Structured data 'counts': { 'equipment': equipment_count, 'pcs': pc_count, 'networkdevices': network_count, - 'total': equipment_count + pc_count + network_count + 'printers': printer_count, + 'total': total }, 'bystatus': status_dict, 'recent': [ { - 'machineid': m.machineid, - 'machinenumber': m.machinenumber, - 'machinetype': m.machinetype.machinetype if m.machinetype else None, - 'createddate': m.createddate.isoformat() + 'Z' if m.createddate else None + 'assetid': a.assetid, + 'assetnumber': a.assetnumber, + 'assettype': a.assettype.assettype if a.assettype else None, + 'createddate': a.createddate.isoformat() + 'Z' if a.createddate else None } - for m in recent_machines + for a in recent ] }) @@ -79,23 +84,23 @@ def get_dashboard(): @dashboard_bp.route('/stats', methods=['GET']) @jwt_required(optional=True) def get_stats(): - """Get detailed statistics.""" - # Machine type breakdown + """Get detailed statistics by asset type.""" type_counts = db.session.query( - MachineType.machinetype, - MachineType.category, - db.func.count(Machine.machineid) + AssetType.assettype, + db.func.count(Asset.assetid) ).outerjoin( - Machine, - db.and_(Machine.machinetypeid == MachineType.machinetypeid, Machine.isactive == True) - ).filter(MachineType.isactive == True).group_by( - MachineType.machinetypeid - ).all() + Asset, + db.and_(Asset.assettypeid == AssetType.assettypeid, Asset.isactive == True) + ).group_by(AssetType.assettypeid).all() return success_response({ 'bytype': [ - {'type': t, 'category': c, 'count': count} - for t, c, count in type_counts + { + 'type': t, + 'category': _TYPE_CATEGORY.get(t, t), + 'count': count + } + for t, count in type_counts ] }) @@ -142,7 +147,6 @@ def get_navigation(): def health_check(): """Health check endpoint (no auth required).""" try: - # Test database connection db.session.execute(db.text('SELECT 1')) db_status = 'healthy' except Exception as e: diff --git a/shopdb/core/api/reports.py b/shopdb/core/api/reports.py index 95d61ce..d7a0002 100644 --- a/shopdb/core/api/reports.py +++ b/shopdb/core/api/reports.py @@ -8,9 +8,10 @@ from flask_jwt_extended import jwt_required from shopdb.extensions import db from shopdb.core.models import ( - Asset, AssetType, AssetStatus, Machine, MachineStatus, - Application, KnowledgeBase, InstalledApp + Asset, AssetType, AssetStatus, + Application, KnowledgeBase ) +from plugins.computers.models import Computer, ComputerInstalledApp from shopdb.utils.responses import success_response, error_response, ErrorCodes reports_bp = Blueprint('reports', __name__) @@ -337,29 +338,33 @@ def software_compliance(): if app_filter and str(app.appid) != app_filter: continue - # Get all PCs - total_pcs = Machine.query.filter( - Machine.isactive == True, - Machine.pctypeid.isnot(None) - ).count() + # Get all PCs (computers) + total_pcs = Computer.query.join( + Asset, Asset.assetid == Computer.assetid + ).filter(Asset.isactive == True).count() # Get PCs with this app installed - installed_count = db.session.query(InstalledApp).join( - Machine, Machine.machineid == InstalledApp.machineid + installed_count = db.session.query(ComputerInstalledApp).join( + Computer, Computer.computerid == ComputerInstalledApp.computerid + ).join( + Asset, Asset.assetid == Computer.assetid ).filter( - InstalledApp.appid == app.appid, - Machine.isactive == True + ComputerInstalledApp.appid == app.appid, + ComputerInstalledApp.isactive == True, + Asset.isactive == True ).count() # Get list of non-compliant PCs - compliant_pc_ids = db.session.query(InstalledApp.machineid).filter( - InstalledApp.appid == app.appid + compliant_pc_ids = db.session.query(ComputerInstalledApp.computerid).filter( + ComputerInstalledApp.appid == app.appid, + ComputerInstalledApp.isactive == True ).subquery() - non_compliant_pcs = Machine.query.filter( - Machine.isactive == True, - Machine.pctypeid.isnot(None), - ~Machine.machineid.in_(compliant_pc_ids) + non_compliant_pcs = Computer.query.join( + Asset, Asset.assetid == Computer.assetid + ).filter( + Asset.isactive == True, + ~Computer.computerid.in_(compliant_pc_ids) ).limit(100).all() compliance_rate = (installed_count / total_pcs * 100) if total_pcs > 0 else 0 @@ -372,7 +377,8 @@ def software_compliance(): 'notinstalled': total_pcs - installed_count, 'compliancerate': round(compliance_rate, 1), 'noncompliantpcs': [ - {'machineid': pc.machineid, 'hostname': pc.hostname or pc.machinenumber} + {'computerid': pc.computerid, + 'hostname': pc.hostname or (pc.asset.assetnumber if pc.asset else None)} for pc in non_compliant_pcs ] }) @@ -534,23 +540,26 @@ def pc_relationships(): Query parameters: - format: 'json' (default) or 'csv' """ + # Asset relationships where a computer (source) relates to an equipment + # (target) - the asset-model equivalent of the legacy PC->machine links. sql = db.text(""" SELECT - eq.machinenumber AS machine_number, + eq.assetnumber AS machine_number, v.vendor AS vendor, mo.modelnumber AS model, - pc.machinenumber AS hostname, + COALESCE(cpc.hostname, pc.assetnumber) AS hostname, c.ipaddress AS ip - FROM machinerelationships mr - JOIN machines eq ON mr.parentmachineid = eq.machineid - JOIN machines pc ON mr.childmachineid = pc.machineid - LEFT JOIN communications c ON pc.machineid = c.machineid AND c.isprimary = 1 AND c.comtypeid = 1 - LEFT JOIN models mo ON eq.modelnumberid = mo.modelnumberid + FROM assetrelationships ar + JOIN assets pc ON ar.sourceassetid = pc.assetid + JOIN computers cpc ON cpc.assetid = pc.assetid + JOIN assets eq ON ar.targetassetid = eq.assetid + JOIN equipment eqx ON eqx.assetid = eq.assetid + LEFT JOIN communications c ON c.assetid = pc.assetid AND c.isprimary = 1 AND c.comtypeid = 1 + LEFT JOIN models mo ON eqx.modelnumberid = mo.modelnumberid LEFT JOIN vendors v ON mo.vendorid = v.vendorid - WHERE mr.isactive = 1 - AND pc.pctypeid IS NOT NULL - AND eq.machinenumber IS NOT NULL AND eq.machinenumber != '' - ORDER BY eq.machinenumber + WHERE ar.isactive = 1 + AND eq.assetnumber IS NOT NULL AND eq.assetnumber != '' + ORDER BY eq.assetnumber """) results = db.session.execute(sql).fetchall()