Merge pull request 'pds-bot' (#1) from pds-bot into master

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2025-08-06 08:33:10 +00:00

488
pds.py
View File

@@ -216,9 +216,6 @@ class WebAssistantScreen(GradientWidget):
# Chat bubble widget for chat interface # Chat bubble widget for chat interface
import queue
import threading
class ChatBubble(QWidget): class ChatBubble(QWidget):
def __init__(self, text, is_user=False): def __init__(self, text, is_user=False):
super().__init__() super().__init__()
@@ -242,11 +239,7 @@ class ChatBubble(QWidget):
bubble_label.setMaximumWidth(250) bubble_label.setMaximumWidth(250)
bubble_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) bubble_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
if self.is_user: layout.setAlignment(Qt.AlignmentFlag.AlignRight if self.is_user else Qt.AlignmentFlag.AlignLeft)
layout.setAlignment(Qt.AlignmentFlag.AlignRight)
else:
layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
layout.addWidget(bubble_label) layout.addWidget(bubble_label)
@@ -263,229 +256,260 @@ class ChatScrollArea(QScrollArea):
def add_bubble(self, text, is_user=False): def add_bubble(self, text, is_user=False):
bubble = ChatBubble(text, is_user=is_user) bubble = ChatBubble(text, is_user=is_user)
self.chat_layout.addWidget(bubble) self.chat_layout.addWidget(bubble)
# Scroll to bottom reliably
self.verticalScrollBar().setValue(self.verticalScrollBar().maximum()) self.verticalScrollBar().setValue(self.verticalScrollBar().maximum())
# class ProductionBotScreen(QWidget): class ProductionBotScreen(QWidget):
# def __init__(self, back_action=None): def __init__(self, back_action=None):
# super().__init__() super().__init__()
# self.back_action = back_action self.back_action = back_action
#
# self.chart_map = {} self.chart_map = {}
# self.plant_map = {} self.plant_map = {}
# self.awaiting_plants = False self.awaiting_plants = False
#
# self.init_tts() self.init_tts()
# self._setup_ui() self._setup_ui()
#
# QTimer.singleShot(500, self._show_greeting) QTimer.singleShot(500, self._show_greeting)
# QTimer.singleShot(1500, self.load_charts) QTimer.singleShot(1500, self.load_charts)
#
# self.tts_queue = queue.Queue() self.tts_queue = queue.Queue()
# self.tts_thread = threading.Thread(target=self.tts_worker, daemon=True) self.tts_thread = threading.Thread(target=self.tts_worker, daemon=True)
# self.tts_thread.start() self.tts_thread.start()
#
# def init_tts(self): def init_tts(self):
# self.tts_engine = pyttsx3.init() self.tts_engine = pyttsx3.init()
# self.tts_engine.setProperty('rate', 170) self.tts_engine.setProperty('rate', 170)
# # No voice filtering - use system default voice
# def _setup_ui(self):
# layout = QVBoxLayout(self) def tts_worker(self):
# layout.setContentsMargins(0, 0, 0, 0) while True:
# layout.setSpacing(0) text = self.tts_queue.get()
# if text is None:
# header = QWidget() break
# header.setFixedHeight(70) try:
# # Fix indentation here: self.tts_engine.say(text)
# header.setStyleSheet(""" self.tts_engine.runAndWait()
# background: qlineargradient(x1:0,y1:0,x2:1,y2:0, except Exception:
# stop:0 #B0E0E6, stop:1 #F3C2C2); pass
# color: #333; self.tts_queue.task_done()
# font-size: 22px;
# """) def speak(self, text):
# hlayout = QHBoxLayout(header) self.tts_queue.put(text)
# hlayout.setContentsMargins(10, 5, 10, 5)
# def closeEvent(self, event):
# logo = QLabel() self.tts_queue.put(None)
# pix = QPixmap("cri_logo.png") # Corrected filename self.tts_thread.join(timeout=2)
# if pix and not pix.isNull(): super().closeEvent(event)
# logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
# hlayout.addWidget(logo) def _setup_ui(self):
# layout = QVBoxLayout(self)
# title = QLabel("PRODUCTION BOT") layout.setContentsMargins(0, 0, 0, 0)
# title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;") layout.setSpacing(0)
# hlayout.addWidget(title)
# hlayout.addStretch(1) header = QWidget()
# header.setFixedHeight(70)
# self.back_btn = QPushButton("Back") header.setStyleSheet("""
# self.back_btn.setStyleSheet(""" background: qlineargradient(x1:0,y1:0,x2:1,y2:0,
# QPushButton { stop:0 #B0E0E6, stop:1 #F3C2C2);
# background: #C71585; color: #333;
# color: white; font-size: 22px;
# border-radius: 8px; """)
# padding: 8px 14px; hlayout = QHBoxLayout(header)
# font-weight: bold; hlayout.setContentsMargins(10, 5, 10, 5)
# }
# QPushButton:hover { logo = QLabel()
# background: #b95975; pix = QPixmap("cri_logo.png")
# } if pix and not pix.isNull():
# """) logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
# if self.back_action: hlayout.addWidget(logo)
# self.back_btn.clicked.connect(self.back_action)
# hlayout.addWidget(self.back_btn) title = QLabel("PRODUCTION BOT")
# title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
# self.refresh_btn = QPushButton("Refresh") hlayout.addWidget(title)
# self.refresh_btn.setStyleSheet("""
# QPushButton { hlayout.addStretch(1)
# background: #C71585;
# color: white; self.back_btn = QPushButton("Back")
# border-radius: 8px; self.back_btn.setStyleSheet("""
# padding: 8px 14px; QPushButton {
# font-weight: bold; background: #C71585;
# } color: white;
# QPushButton:hover { border-radius: 8px;
# background: #b95975; padding: 8px 14px;
# } font-weight: bold;
# """) }
# hlayout.addWidget(self.refresh_btn) QPushButton:hover {
# background: #b95975;
# layout.addWidget(header) }
# """)
# self.chat_area = ChatScrollArea() if self.back_action:
# self.chat_area.setStyleSheet(""" self.back_btn.clicked.connect(self.back_action)
# QScrollArea { hlayout.addWidget(self.back_btn)
# background: qlineargradient(x1:0,y1:0,x2:1,y2:1,
# stop:0 #b0e6e6, stop:1 #f3c2c2); self.refresh_btn = QPushButton("Refresh")
# } self.refresh_btn.setStyleSheet("""
# """) QPushButton {
# layout.addWidget(self.chat_area, stretch=1) background: #C71585;
# color: white;
# input_area = QWidget() border-radius: 8px;
# input_layout = QHBoxLayout(input_area) padding: 8px 14px;
# input_layout.setContentsMargins(10, 10, 10, 10) font-weight: bold;
# }
# self.user_input = QLineEdit() QPushButton:hover {
# self.user_input.setPlaceholderText("Type your message...") background: #b95975;
# self.user_input.setStyleSheet(""" }
# QLineEdit { """)
# font-size: 16px; hlayout.addWidget(self.refresh_btn)
# padding: 8px;
# border: 2px solid #db7093; layout.addWidget(header)
# border-radius: 4px;
# } self.chat_area = ChatScrollArea()
# QLineEdit:focus { self.chat_area.setStyleSheet("""
# border-color: #f3c2c2; QScrollArea {
# background: #fff0f0; background: qlineargradient(x1:0,y1:0,x2:1,y2:1,
# } stop:0 #b0e6e6, stop:1 #f3c2c2);
# """) }
# self.send_btn = QPushButton("Send") """)
# self.send_btn.setStyleSheet(""" layout.addWidget(self.chat_area, stretch=1)
# QPushButton {
# background: #006400; input_area = QWidget()
# color: white; input_layout = QHBoxLayout(input_area)
# font-weight: bold; input_layout.setContentsMargins(10, 10, 10, 10)
# padding: 8px 20px;
# border-radius: 8px; self.user_input = QLineEdit()
# } self.user_input.setPlaceholderText("Type your message...")
# QPushButton:hover { self.user_input.setStyleSheet("""
# background: #a2136a; QLineEdit {
# } font-size: 16px;
# """) padding: 8px;
# input_layout.addWidget(self.user_input) border: 2px solid #db7093;
# input_layout.addWidget(self.send_btn) border-radius: 4px;
# }
# layout.addWidget(input_area) QLineEdit:focus {
# border-color: #f3c2c2;
# self.send_btn.clicked.connect(self._on_send) background: #fff0f0;
# }
# def _on_send(self): """)
# msg = self.user_input.text().strip() self.send_btn = QPushButton("Send")
# if not msg: self.send_btn.setStyleSheet("""
# return QPushButton {
# self.chat_area.add_bubble(msg, True) background: #006400;
# self.user_input.clear() color: white;
# font-weight: bold;
# if msg.isdigit(): padding: 8px 20px;
# idx = int(msg) border-radius: 8px;
# if self.awaiting_plants: }
# plant = self.plant_map.get(idx) QPushButton:hover {
# if plant: background: #a2136a;
# self.plant_selected(plant) }
# else: """)
# self.chat_area.add_bubble("Invalid plant number. Please try again.", False) input_layout.addWidget(self.user_input)
# self.speak("Invalid plant number. Please try again.") input_layout.addWidget(self.send_btn)
# else:
# chart = self.chart_map.get(idx) layout.addWidget(input_area)
# if chart:
# self.chart_selected(chart) self.send_btn.clicked.connect(self._on_send)
# else:
# self.chat_area.add_bubble("Invalid chart number. Please try again.", False) def _show_greeting(self):
# self.speak("Invalid chart number. Please try again.") hour = QTime.currentTime().hour()
# else: greeting = "Good night"
# self.chat_area.add_bubble("Please enter a valid number.", False) if 5 <= hour < 12:
# self.speak("Please enter a valid number.") greeting = "Good morning"
# elif 12 <= hour < 17:
# def load_charts(self): greeting = "Good afternoon"
# url = "https://pds.iotsignin.com/api/get/modulechart-name/data" elif 17 <= hour < 21:
# headers = { greeting = "Good evening"
# "Authorization": "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE=", self.chat_area.add_bubble(f"{greeting}, User", False)
# "module-name": "Production DashBoard" self.speak(f"{greeting}, User")
# }
# def _on_send(self):
# try: msg = self.user_input.text().strip()
# response = requests.get(url, headers=headers, timeout=10) if not msg:
# response.raise_for_status() return
# data = response.json() self.chat_area.add_bubble(msg, True)
# if data.get("status_code") == "SUCCESS": self.user_input.clear()
# charts = data.get("status_description", [])
# self.chart_map = {i + 1: c for i, c in enumerate(charts)} if msg.isdigit():
# numbered = "\n".join(f"{num}. {name}" for num, name in self.chart_map.items()) idx = int(msg)
# self.chat_area.add_bubble("Please select a chart:\n\n" + numbered, False) if self.awaiting_plants:
# self.speak("Please select a chart.") plant = self.plant_map.get(idx)
# else: if plant:
# self.chat_area.add_bubble("Failed to load charts.", False) self.plant_selected(plant)
# self.speak("Failed to load charts.") else:
# except Exception as e: self.chat_area.add_bubble("Invalid plant number. Please try again.", False)
# self.chat_area.add_bubble(f"Error loading charts: {str(e)}", False) self.speak("Invalid plant number. Please try again.")
# self.speak("Error loading charts.") else:
# chart = self.chart_map.get(idx)
# def chart_selected(self, chart_name): if chart:
# self.chat_area.add_bubble(f"You selected: {chart_name}", False) self.chart_selected(chart)
# self.speak(f"You selected {chart_name}") else:
# self.awaiting_plants = True self.chat_area.add_bubble("Invalid chart number. Please try again.", False)
# QTimer.singleShot(500, self.load_plants) self.speak("Invalid chart number. Please try again.")
# else:
self.chat_area.add_bubble("Please enter a valid number.", False)
self.speak("Please enter a valid number.")
def load_charts(self):
url = "https://pds.iotsignin.com/api/get/modulechart-name/data"
headers = {
"Authorization": "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE=",
"module-name": "Production DashBoard"
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
if data.get("status_code") == "SUCCESS":
charts = data.get("status_description", [])
self.chart_map = {i + 1: c for i, c in enumerate(charts)}
numbered = "\n".join(f"{num}. {name}" for num, name in self.chart_map.items())
self.chat_area.add_bubble("Please select a chart:\n\n" + numbered, False)
self.speak("Please select a chart.")
else:
self.chat_area.add_bubble("Failed to load charts.", False)
self.speak("Failed to load charts.")
except Exception as e:
self.chat_area.add_bubble(f"Error loading charts: {str(e)}", False)
self.speak("Error loading charts.")
def chart_selected(self, chart_name):
self.chat_area.add_bubble(f"You selected: {chart_name}", False)
self.speak(f"You selected {chart_name}")
self.awaiting_plants = True
QTimer.singleShot(500, self.load_plants)
# def load_plants(self): # def load_plants(self):
# url = "https://pds.iotsignin.com/api/get/moduleplant-name/data" # Correct API URL for plants # url = "https://pds.iotsignin.com/api/get/moduleplant-name/data"
# headers = { # headers = {
# "Authorization": "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE=", # "Authorization": "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE=",
# "plant-name": "Plant List" # "plant-name": "Plant List"
# } # }
# try:
# try: response = requests.get(url, headers=headers, timeout=10)
# response = requests.get(url, headers=headers, timeout=10) response.raise_for_status()
# response.raise_for_status() data = response.json()
# data = response.json() if data.get("status_code") == "SUCCESS":
# if data.get("status_code") == "SUCCESS": plants = data.get("status_description", [])
# plants = data.get("status_description", []) self.plant_map = {i + 1: p for i, p in enumerate(plants)}
# self.plant_map = {i + 1: p for i, p in enumerate(plants)} numbered = "\n".join(f"{num}. {name}" for num, name in self.plant_map.items())
# numbered = "\n".join(f"{num}. {name}" for num, name in self.plant_map.items()) self.chat_area.add_bubble("Please select a plant:\n\n" + numbered, False)
# self.chat_area.add_bubble("Please select a plant:\n\n" + numbered, False) self.speak("Please select a plant.")
# self.speak("Please select a plant.") else:
# else: self.chat_area.add_bubble("Failed to load plants.", False)
# self.chat_area.add_bubble("Failed to load plants.", False) self.speak("Failed to load plants.")
# self.speak("Failed to load plants.") except Exception as e:
# except Exception as e: self.chat_area.add_bubble(f"Error loading plants: {str(e)}", False)
# self.chat_area.add_bubble(f"Error loading plants: {str(e)}", False) self.speak("Error loading plants.")
# self.speak("Error loading plants.")
#
# def plant_selected(self, plant_name):
# self.chat_area.add_bubble(f"You selected plant: {plant_name}", False)
# self.speak(f"You selected plant {plant_name}")
# self.awaiting_plants = False
# InvoiceBotScreen, independent UI and logic def plant_selected(self, plant_name):
self.chat_area.add_bubble(f"You selected plant: {plant_name}", False)
self.speak(f"You selected plant {plant_name}")
self.awaiting_plants = False
# InvoiceBotScreen is unchanged based on your last code snippet
class InvoiceBotScreen(QWidget): class InvoiceBotScreen(QWidget):
def __init__(self, back_action=None): def __init__(self, back_action=None):
super().__init__() super().__init__()
@@ -509,8 +533,8 @@ class InvoiceBotScreen(QWidget):
hlayout.setContentsMargins(10, 5, 10, 5) hlayout.setContentsMargins(10, 5, 10, 5)
logo = QLabel() logo = QLabel()
pix = QPixmap("cri_logo.png.png") pix = QPixmap("cri_logo.png")
if not pix.isNull(): if pix and not pix.isNull():
logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio)) logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
hlayout.addWidget(logo) hlayout.addWidget(logo)
@@ -595,17 +619,11 @@ class InvoiceBotScreen(QWidget):
layout.addWidget(input_widget) layout.addWidget(input_widget)
self.send_btn.clicked.connect(self._on_send) self.send_btn.clicked.connect(self._on_send)
self._show_greeting() self._show_greeting()
self.chat_area.add_bubble("Invoice bot activated. What invoice do you need?", is_user=False) self.chat_area.add_bubble("Invoice bot activated. What invoice do you need?", is_user=False)
def _on_send(self):
msg = self.user_input.text().strip()
if msg:
self.chat_area.add_bubble(msg, is_user=True)
self.user_input.clear()
self.chat_area.add_bubble("Processing your invoice request...", is_user=False)
def _show_greeting(self): def _show_greeting(self):
hour = QTime.currentTime().hour() hour = QTime.currentTime().hour()
greeting = "Good night" greeting = "Good night"
@@ -615,8 +633,14 @@ class InvoiceBotScreen(QWidget):
greeting = "Good afternoon" greeting = "Good afternoon"
elif 17 <= hour < 21: elif 17 <= hour < 21:
greeting = "Good evening" greeting = "Good evening"
self.chat_area.add_bubble(f"{greeting}, User", is_user=False) self.chat_area.add_bubble(f"{greeting}, User", False)
def _on_send(self):
msg = self.user_input.text().strip()
if msg:
self.chat_area.add_bubble(msg, True)
self.user_input.clear()
self.chat_area.add_bubble("Processing your invoice request...", False)
# Bot selector screen to choose Production or Invoice bots # Bot selector screen to choose Production or Invoice bots
class BotSelectorScreen(QWidget): class BotSelectorScreen(QWidget):