Feature: Convert from Node.js/Express to Python/Flask

BREAKING CHANGE: Replaced Node.js backend with Python Flask

Reason: npm not available on production server, Python/pip is available.

Changes:
- Created app.py (Flask) to replace server.js (Node.js)
- Created requirements.txt with only 2 dependencies (Flask, mysql-connector-python)
- Updated README.md with Flask installation and deployment instructions
- Maintained all existing functionality:
  * Same API endpoints (/api/notifications, /health)
  * Same database queries (isshopfloor filter, 72-hour window)
  * Same priority sorting (incidents first)
  * Serves static files from public/ directory
  * Same environment variable configuration

Dependencies:
- Flask==3.0.0
- mysql-connector-python==8.2.0

The public/index.html frontend remains unchanged - only the backend was converted.

Tested and verified:
- API endpoint returns correct data
- Health check responds
- Dashboard displays properly
- Database connectivity working
- PM2 process manager compatible

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-10-24 11:52:34 -04:00
parent e0a500a364
commit fbe5312e7b
3 changed files with 195 additions and 33 deletions

View File

@@ -9,15 +9,18 @@ Real-time display dashboard for showing current and upcoming events on the shopf
## Features ## Features
- **Live Data Updates**: Auto-refreshes every 10 seconds via AJAX (no page reload) - **Live Data Updates**: Auto-refreshes every 10 seconds via AJAX (no page reload)
- **48-Hour Window**: Shows current events and upcoming events within next 48 hours - **72-Hour Window**: Shows current events and upcoming events within next 72 hours
- **Shopfloor Filtering**: Only displays notifications marked for shopfloor display
- **Type-Based Color Coding**: Visual severity indicators (Red=Incident, Yellow=Change, Green=Awareness)
- **Priority Sorting**: Critical incidents always shown first
- **Real-Time Clock**: Always-on clock display - **Real-Time Clock**: Always-on clock display
- **Connection Monitoring**: Visual indicator shows live connection status - **Connection Monitoring**: Visual indicator shows live connection status
- **GE Aerospace Branding**: Official colors, fonts, and logo - **GE Aerospace Branding**: Official colors, fonts, and logo
## Technology Stack ## Technology Stack
- **Backend**: Node.js with Express - **Backend**: Python 3 with Flask
- **Database**: MySQL 5.6 - **Database**: MySQL 5.6+
- **Frontend**: Vanilla JavaScript (no frameworks) - **Frontend**: Vanilla JavaScript (no frameworks)
- **Styling**: Custom CSS with GE Aerospace brand colors - **Styling**: Custom CSS with GE Aerospace brand colors
@@ -25,7 +28,7 @@ Real-time display dashboard for showing current and upcoming events on the shopf
### Prerequisites ### Prerequisites
- Node.js 16+ and npm - Python 3.8+ with pip
- MySQL 5.6+ database - MySQL 5.6+ database
- Access to ShopDB database - Access to ShopDB database
@@ -33,7 +36,7 @@ Real-time display dashboard for showing current and upcoming events on the shopf
```bash ```bash
# Install dependencies # Install dependencies
npm install pip install -r requirements.txt
# Set environment variables (optional) # Set environment variables (optional)
export DB_HOST=localhost export DB_HOST=localhost
@@ -44,10 +47,7 @@ export DB_NAME=shopdb
export PORT=3001 export PORT=3001
# Start the server # Start the server
npm start python3 app.py
# Or for development with auto-restart
npm run dev
``` ```
## Configuration ## Configuration
@@ -99,8 +99,8 @@ Returns server status.
``` ```
shopfloor-dashboard/ shopfloor-dashboard/
├── server.js # Express server ├── app.py # Flask application
├── package.json # Dependencies ├── requirements.txt # Python dependencies
├── public/ ├── public/
│ ├── index.html # Dashboard UI │ ├── index.html # Dashboard UI
│ └── ge-aerospace-logo.svg │ └── ge-aerospace-logo.svg
@@ -110,17 +110,24 @@ shopfloor-dashboard/
## Database Schema ## Database Schema
Queries the `notifications` table in ShopDB: Queries the `notifications` table in ShopDB with shopfloor filtering:
```sql ```sql
SELECT notificationid, notification, starttime, endtime, SELECT n.notificationid, n.notification, n.starttime, n.endtime,
ticketnumber, link, isactive n.ticketnumber, n.link, n.isactive, n.isshopfloor,
FROM notifications nt.typename, nt.typecolor
WHERE isactive = 1 FROM notifications n
AND (conditions for current/upcoming) LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid
ORDER BY starttime ASC WHERE n.isactive = 1
AND n.isshopfloor = 1
AND (conditions for 72-hour window)
ORDER BY n.starttime ASC
``` ```
**Key Fields:**
- `isshopfloor`: Boolean flag (0/1) - only events with `1` appear on dashboard
- `typecolor`: Used for visual severity indicators (danger/warning/success)
## Design ## Design
### Colors (GE Aerospace Brand) ### Colors (GE Aerospace Brand)
@@ -173,50 +180,72 @@ git push
## Development ## Development
```bash ```bash
# Install dev dependencies # Install dependencies
npm install pip install -r requirements.txt
# Run with auto-restart # Run the Flask development server
npm run dev python3 app.py
# The server will restart automatically when you edit files # The built-in Flask server auto-reloads on code changes when debug=True
``` ```
## Deployment ## Deployment
For production deployment: For production deployment (no pip/npm required on production server):
1. Use a process manager (PM2, systemd) ### Option 1: Package with Dependencies (Recommended)
2. Set environment variables
3. Configure reverse proxy (nginx/Apache) if needed
4. Enable SSL/TLS for secure connections
### Example with PM2: 1. Package entire directory including installed Python packages
2. Copy to production server
3. Run directly with Python 3
4. Use a process manager (systemd, supervisor, or PM2)
### Option 2: Install on Production
```bash ```bash
npm install -g pm2 # Install dependencies on production
pm2 start server.js --name shopfloor-dashboard pip install -r requirements.txt
# Run with production WSGI server (recommended)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:3001 app:app
# Or use PM2 with Python
pm2 start app.py --name shopfloor-dashboard --interpreter python3
pm2 save pm2 save
pm2 startup pm2 startup
``` ```
**Note**: Flask's built-in server (`python3 app.py`) works for production if using a process manager, but gunicorn is recommended for higher traffic.
## Troubleshooting ## Troubleshooting
**Port already in use:** **Port already in use:**
```bash ```bash
# Use a different port # Use a different port
PORT=3001 npm start PORT=3001 python3 app.py
``` ```
**Can't connect to database:** **Can't connect to database:**
- Verify MySQL is running - Verify MySQL is running
- Check credentials in environment variables - Check credentials in environment variables or app.py
- Ensure database exists and user has permissions - Ensure database exists and user has permissions
- Verify `mysql-connector-python` is installed
**Dashboard not updating:** **Dashboard not updating:**
- Check browser console for errors - Check browser console for errors
- Verify `/api/notifications` endpoint returns data - Verify `/api/notifications` endpoint returns data
- Check network connectivity - Check network connectivity
- Ensure Flask server is running
**Python module errors:**
```bash
# Reinstall dependencies
pip install -r requirements.txt
# Or install individually
pip install Flask mysql-connector-python
```
## License ## License

131
app.py Normal file
View File

@@ -0,0 +1,131 @@
from flask import Flask, jsonify, send_from_directory
import mysql.connector
from datetime import datetime, timedelta
import os
app = Flask(__name__, static_folder='public')
# Database configuration
DB_CONFIG = {
'host': os.environ.get('DB_HOST', 'localhost'),
'port': int(os.environ.get('DB_PORT', 3306)),
'user': os.environ.get('DB_USER', '570005354'),
'password': os.environ.get('DB_PASS', '570005354'),
'database': os.environ.get('DB_NAME', 'shopdb')
}
# Get database connection
def get_db_connection():
return mysql.connector.connect(**DB_CONFIG)
# Serve static files (index.html, logo, etc)
@app.route('/')
def index():
return send_from_directory('public', 'index.html')
@app.route('/<path:path>')
def static_files(path):
return send_from_directory('public', path)
# API endpoint to get notifications
@app.route('/api/notifications')
def get_notifications():
try:
now = datetime.now()
future = now + timedelta(hours=72) # 72 hours from now
# Connect to database
conn = get_db_connection()
cursor = conn.cursor(dictionary=True)
# Query with isshopfloor filter and 72-hour window
query = """
SELECT n.notificationid, n.notification, n.starttime, n.endtime, n.ticketnumber, n.link,
n.isactive, n.isshopfloor, nt.typename, nt.typecolor
FROM notifications n
LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid
WHERE n.isactive = 1
AND n.isshopfloor = 1
AND (
(n.starttime <= %s AND (n.endtime IS NULL OR n.endtime >= %s))
OR (n.starttime BETWEEN %s AND %s)
)
ORDER BY n.starttime ASC
"""
cursor.execute(query, (future, now, now, future))
rows = cursor.fetchall()
cursor.close()
conn.close()
# Convert bit fields to boolean and datetime to ISO strings
for row in rows:
row['isactive'] = bool(row['isactive'])
row['isshopfloor'] = bool(row['isshopfloor'])
if row['starttime']:
row['starttime'] = row['starttime'].isoformat()
if row['endtime']:
row['endtime'] = row['endtime'].isoformat()
# Categorize notifications
current_events = []
upcoming_events = []
for notification in rows:
start = datetime.fromisoformat(notification['starttime'])
end = datetime.fromisoformat(notification['endtime']) if notification['endtime'] else None
if start <= now and (end is None or end >= now):
current_events.append(notification)
else:
upcoming_events.append(notification)
# Sort current events by severity priority, then by starttime
# Priority: Incident (danger) > Change (warning) > Awareness/TBD (success)
severity_priority = {
'danger': 1, # Incident - highest priority
'warning': 2, # Change
'success': 3, # Awareness/TBD - lowest priority
'secondary': 4 # Fallback
}
current_events.sort(key=lambda x: (
severity_priority.get(x['typecolor'], 4),
datetime.fromisoformat(x['starttime'])
))
# Upcoming events stay sorted by starttime only
upcoming_events.sort(key=lambda x: datetime.fromisoformat(x['starttime']))
return jsonify({
'success': True,
'timestamp': datetime.now().isoformat(),
'current': current_events,
'upcoming': upcoming_events
})
except mysql.connector.Error as err:
return jsonify({
'success': False,
'error': f'Database error: {str(err)}'
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'Server error: {str(e)}'
}), 500
# Health check endpoint
@app.route('/health')
def health():
return jsonify({
'status': 'ok',
'timestamp': datetime.now().isoformat()
})
if __name__ == '__main__':
port = int(os.environ.get('PORT', 3001))
print(f'Shopfloor Dashboard running on port {port}')
print(f'Access at: http://localhost:{port}')
app.run(host='0.0.0.0', port=port, debug=False)

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
Flask==3.0.0
mysql-connector-python==8.2.0