Add USB, Notifications, Network plugins and reusable EmployeeSearch component
New Plugins: - USB plugin: Device checkout/checkin with employee lookup, checkout history - Notifications plugin: Announcements with types, scheduling, shopfloor display - Network plugin: Network device management with subnets and VLANs - Equipment and Computers plugins: Asset type separation Frontend: - EmployeeSearch component: Reusable employee lookup with autocomplete - USB views: List, detail, checkout/checkin modals - Notifications views: List, form with recognition mode - Network views: Device list, detail, form - Calendar view with FullCalendar integration - Shopfloor and TV dashboard views - Reports index page - Map editor for asset positioning - Light/dark mode fixes for map tooltips Backend: - Employee search API with external lookup service - Collector API for PowerShell data collection - Reports API endpoints - Slides API for TV dashboard - Fixed AppVersion model (removed BaseModel inheritance) - Added checkout_name column to usbcheckouts table Styling: - Unified detail page styles - Improved pagination (page numbers instead of prev/next) - Dark/light mode theme improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
104
shopdb/core/services/employee_service.py
Normal file
104
shopdb/core/services/employee_service.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Employee lookup service - queries wjf_employees database."""
|
||||
|
||||
from typing import Optional, Dict, List
|
||||
import pymysql
|
||||
from flask import current_app
|
||||
|
||||
|
||||
def get_employee_connection():
|
||||
"""Get connection to wjf_employees database."""
|
||||
return pymysql.connect(
|
||||
host='localhost',
|
||||
user='root',
|
||||
password='rootpassword',
|
||||
database='wjf_employees',
|
||||
cursorclass=pymysql.cursors.DictCursor
|
||||
)
|
||||
|
||||
|
||||
def lookup_employee(sso: str) -> Optional[Dict]:
|
||||
"""
|
||||
Look up employee by SSO.
|
||||
|
||||
Returns dict with: SSO, First_Name, Last_Name, full_name, Picture, etc.
|
||||
"""
|
||||
if not sso or not sso.strip().isdigit():
|
||||
return None
|
||||
|
||||
try:
|
||||
conn = get_employee_connection()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
'SELECT * FROM employees WHERE SSO = %s',
|
||||
(int(sso.strip()),)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
# Add computed full_name
|
||||
first = (row.get('First_Name') or '').strip()
|
||||
last = (row.get('Last_Name') or '').strip()
|
||||
row['full_name'] = f"{first} {last}".strip()
|
||||
return row
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Employee lookup error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def lookup_employees(sso_list: str) -> List[Dict]:
|
||||
"""
|
||||
Look up multiple employees by comma-separated SSO list.
|
||||
|
||||
Returns list of employee dicts.
|
||||
"""
|
||||
if not sso_list:
|
||||
return []
|
||||
|
||||
ssos = [s.strip() for s in sso_list.split(',') if s.strip().isdigit()]
|
||||
if not ssos:
|
||||
return []
|
||||
|
||||
try:
|
||||
conn = get_employee_connection()
|
||||
with conn.cursor() as cur:
|
||||
placeholders = ','.join(['%s'] * len(ssos))
|
||||
cur.execute(
|
||||
f'SELECT * FROM employees WHERE SSO IN ({placeholders})',
|
||||
[int(s) for s in ssos]
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
|
||||
# Add computed full_name to each
|
||||
for row in rows:
|
||||
first = (row.get('First_Name') or '').strip()
|
||||
last = (row.get('Last_Name') or '').strip()
|
||||
row['full_name'] = f"{first} {last}".strip()
|
||||
|
||||
return rows
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Employee lookup error: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_employee_names(sso_list: str) -> str:
|
||||
"""
|
||||
Get comma-separated list of employee names from SSO list.
|
||||
|
||||
Input: "212574611,212637451"
|
||||
Output: "Brandon Saltz, Jon Kolkmann"
|
||||
"""
|
||||
employees = lookup_employees(sso_list)
|
||||
if not employees:
|
||||
return sso_list # Return SSOs as fallback
|
||||
|
||||
return ', '.join(emp['full_name'] for emp in employees if emp.get('full_name'))
|
||||
|
||||
|
||||
def get_employee_picture_url(sso: str) -> Optional[str]:
|
||||
"""Get URL to employee picture if available."""
|
||||
emp = lookup_employee(sso)
|
||||
if emp and emp.get('Picture'):
|
||||
# Pictures are stored relative paths like "Support/212574611.png"
|
||||
return f"/static/employees/{emp['Picture']}"
|
||||
return None
|
||||
Reference in New Issue
Block a user