diff --git a/Production Display.py b/Production Display.py
deleted file mode 100644
index 8879be5..0000000
--- a/Production Display.py
+++ /dev/null
@@ -1,1182 +0,0 @@
-import os
-import sys
-import requests
-import base64
-import platform
-
-from PyQt6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton,
- QToolButton, QSizePolicy, QStackedWidget, QSpacerItem, QTextEdit, QScrollArea,
- QMessageBox, QHBoxLayout, QTabWidget, QFileDialog, QComboBox, QDialog
-)
-from PyQt6.QtCore import Qt, QEvent, QTime, pyqtSlot, QTimer, QUrl, QObject, QDateTime
-from PyQt6.QtGui import QPalette, QColor, QPixmap, QFont
-from PyQt6.QtWebChannel import QWebChannel
-from PyQt6.QtWebEngineWidgets import QWebEngineView
-
-
-# Placeholder new screens to navigate on button click
-class JSBridge(QObject):
- @pyqtSlot(str, str, str)
- def saveFile(self, base64_data, filename, source):
- try:
- if base64_data.startswith("data:"):
- base64_data = base64_data.split(",", 1)[1]
- data = base64.b64decode(base64_data)
-
- path, _ = QFileDialog.getSaveFileName(None, f"Save {source.title()} File", filename, "All Files (*)")
- if path:
- with open(path, 'wb') as f:
- f.write(data)
- QMessageBox.information(None, f"{source.title()} Download", f"File saved to:\n{path}")
- if platform.system() == "Windows":
- os.startfile(path)
- elif platform.system() == "Darwin":
- os.system(f"open '{path}'")
- else:
- os.system(f"xdg-open '{path}'")
-
- except Exception as e:
- QMessageBox.critical(None, "Save Failed", str(e))
-
-
-class ProductionDisplayScreen(QWidget):
- def __init__(self, stacked, url="https://pds.iotsignin.com/admin"):
- super().__init__()
- self.stacked = stacked
- self.url = url
- self.setObjectName("ProductionDisplayScreen")
- self.init_ui()
-
- def init_ui(self):
- self.channel = QWebChannel()
- self.js_bridge = JSBridge()
- self.channel.registerObject("pyBridge", self.js_bridge)
-
- # Header
- header = QWidget()
- hl = QHBoxLayout(header)
- hl.setContentsMargins(8, 8, 8, 8)
- header.setStyleSheet("background-color: #e6f2ff;")
-
- # Logo
- logo = QLabel()
- pix = QPixmap(
- r"C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_logo.png.png")
- logo.setPixmap(
- pix.scaled(90, 70, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
- hl.addWidget(logo)
- hl.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
-
- # Title
- title = QLabel("CRI DIGITAL MANUFACTURING SOLUTIONS")
- title.setAlignment(Qt.AlignmentFlag.AlignCenter)
- title.setStyleSheet("font-weight:bold; font-size:28px;")
- hl.addWidget(title)
- hl.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum))
-
- # Back button
- self.btn_back = QPushButton("Back")
- self.btn_back.setStyleSheet("""
- QPushButton {
- background-color: #c2185b;
- color: white;
- border: none;
- padding: 6px 12px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #34495E;
- }
- QPushButton:pressed {
- background-color: #1F2D3D;
- }
- """)
- self.btn_back.clicked.connect(self.go_back)
- hl.addWidget(self.btn_back)
-
- # Navigation buttons
- btn_style = """
- QPushButton {
- background-color: #c2185b;
- color: white;
- border: none;
- padding: 6px 12px;
- border-radius: 4px;
- }
- QPushButton:hover {
- background-color: #34495E;
- }
- QPushButton:pressed {
- background-color: #1F2D3D;
- }
- """
- self.btn_prev = QPushButton("Previous")
- self.btn_refresh = QPushButton("Refresh")
- self.btn_next = QPushButton("Next")
- self.btn_full = QPushButton("Full Screen")
- self.btn_dup = QPushButton("Duplicate")
- self.btn_del = QPushButton("Delete")
- self.tab_dropdown = QComboBox()
-
- for b in [self.btn_prev, self.btn_refresh, self.btn_next, self.btn_full, self.btn_dup, self.btn_del]:
- b.setStyleSheet(btn_style)
- hl.addWidget(b)
- hl.addWidget(self.tab_dropdown)
-
- # Tabbed WebView
- self.tabs = QTabWidget()
- self.tabs.setTabsClosable(False)
- self.add_tab(self.url)
-
- self.tabs.setStyleSheet("""
- QTabWidget::pane { background-color: #ffe6e6; }
- QTabBar::tab {
- background: #f8d7da;
- padding: 6px;
- border-radius: 4px;
- }
- QTabBar::tab:selected {
- background: #e6e6fa;
- font-weight: bold;
- }
- """)
-
- # Connections
- self.btn_prev.clicked.connect(lambda: self.notify("Going to previous page...", self.go_prev))
- self.btn_refresh.clicked.connect(lambda: self.notify("Refreshing...", self.go_refresh))
- self.btn_next.clicked.connect(lambda: self.notify("Going to next page...", self.go_next))
- self.btn_full.clicked.connect(self.toggle_fullscreen)
- self.btn_dup.clicked.connect(self.duplicate_tab)
- self.btn_del.clicked.connect(self.delete_tab)
- self.tab_dropdown.currentIndexChanged.connect(self.switch_tab)
-
- # Layout
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.addWidget(header)
- layout.addWidget(self.tabs)
-
- def add_tab(self, url):
- webview = QWebEngineView()
- webview.page().setWebChannel(self.channel)
- index = self.tabs.addTab(webview, "Loading…")
- self.tab_dropdown.addItem("Loading…")
- self.tabs.setCurrentIndex(index)
- self.tab_dropdown.setCurrentIndex(index)
-
- webview.page().titleChanged.connect(lambda title, w=webview: self.update_tab_title(w, title))
- webview.page().loadFinished.connect(lambda ok, w=webview: self.inject_js(w) if ok else None)
- webview.load(QUrl(url))
-
- def update_tab_title(self, webview, title):
- i = self.tabs.indexOf(webview)
- if i != -1:
- self.tabs.setTabText(i, title or "New Tab")
- self.tab_dropdown.setItemText(i, title or "New Tab")
-
- def notify(self, message, callback):
- dlg = QDialog(self)
- dlg.setWindowFlags(Qt.WindowType.FramelessWindowHint)
- dlg.setStyleSheet("""
- QDialog { background-color: #34495E; border: 2px solid #2C3E50; border-radius: 10px; }
- QLabel { color: white; padding: 15px; font-size: 14px; }
- """)
- lbl = QLabel(message)
- lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
- v = QVBoxLayout(dlg)
- v.addWidget(lbl)
- dlg.resize(250, 80)
- dlg.move(self.geometry().center().x() - 125, self.geometry().center().y() - 40)
- dlg.show()
- QTimer.singleShot(1000, lambda: (dlg.close(), callback()))
-
- def current_webview(self):
- return self.tabs.currentWidget()
-
- def go_prev(self):
- view = self.current_webview()
- if view and view.history().canGoBack():
- view.back()
-
- def go_refresh(self):
- view = self.current_webview()
- if view: view.reload()
-
- def go_next(self):
- view = self.current_webview()
- if view and view.history().canGoForward():
- view.forward()
-
- def toggle_fullscreen(self):
- self.setWindowState(self.windowState() ^ Qt.WindowState.WindowFullScreen)
-
- def duplicate_tab(self):
- view = self.current_webview()
- if view:
- self.add_tab(view.url().toString())
-
- def delete_tab(self):
- if self.tabs.count() > 1:
- index = self.tabs.currentIndex()
- self.tabs.removeTab(index)
- self.tab_dropdown.removeItem(index)
-
- def switch_tab(self, index):
- self.tabs.setCurrentIndex(index)
-
- def go_back(self):
- for i in range(self.stacked.count()):
- widget = self.stacked.widget(i)
- if isinstance(widget, SelectorScreen):
- self.stacked.setCurrentWidget(widget)
- return
-
- def inject_js(self, webview):
- js_code = """
- (function () {
- if (window._fileDownloadInjected) return;
- window._fileDownloadInjected = true;
-
- let _downloadInProgress = false;
-
- function getFileUrlAndType(el) {
- let href = el.getAttribute("href") || el.getAttribute("data-url") || "";
-
- if (!href) {
- const inner = el.querySelector("a[href]");
- if (inner) href = inner.getAttribute("href") || "";
- }
-
- let filename = "download.csv";
- if (href) {
- const parts = href.split('/');
- filename = parts[parts.length - 1].split('?')[0] || filename;
- }
-
- const isExport = href.includes("/exports") || el.textContent.toLowerCase().includes("export");
- const source = isExport ? "export" : "import";
-
- return { href, filename, source };
- }
-
- function startDownload(e, href, filename, source) {
- if (_downloadInProgress) return;
- _downloadInProgress = true;
-
- e.preventDefault();
-
- fetch(href, { credentials: 'include' })
- .then(res => {
- if (!res.ok) throw new Error("Failed: " + res.status);
- return res.blob();
- })
- .then(blob => {
- const reader = new FileReader();
- reader.onloadend = () => {
- const base64 = reader.result;
- if (window.pyBridge && window.pyBridge.saveFile) {
- window.pyBridge.saveFile(base64, filename, source);
- } else {
- alert("❌ pyBridge not available");
- }
- _downloadInProgress = false;
- };
- reader.readAsDataURL(blob);
- })
- .catch(err => {
- alert("❌ Download error: " + err.message);
- _downloadInProgress = false;
- });
- }
-
- document.addEventListener("click", function (e) {
- const el = e.target.closest("a,button");
- if (!el) return;
-
- const { href, filename, source } = getFileUrlAndType(el);
-
- if (href && (href.endsWith(".csv") || href.endsWith(".xlsx") || href.includes("/exports") || href.startsWith("blob:"))) {
- startDownload(e, href, filename, source);
- }
- }, true);
-
- const origCreateObjectURL = URL.createObjectURL;
- URL.createObjectURL = function (blob) {
- if (_downloadInProgress) return origCreateObjectURL(blob);
- _downloadInProgress = true;
-
- try {
- const reader = new FileReader();
- reader.onloadend = function () {
- const base64 = reader.result;
- if (window.pyBridge && window.pyBridge.saveFile) {
- window.pyBridge.saveFile(base64, "blob_file.csv", "import");
- }
- _downloadInProgress = false;
- };
- reader.readAsDataURL(blob);
- } catch (e) {
- console.error("Blob hook failed", e);
- _downloadInProgress = false;
- }
-
- return origCreateObjectURL(blob);
- };
-
- function waitForBridge() {
- if (typeof QWebChannel === 'undefined') return setTimeout(waitForBridge, 100);
- new QWebChannel(qt.webChannelTransport, function (channel) {
- window.pyBridge = channel.objects.pyBridge;
- });
- }
- waitForBridge();
- })();
- """
-
- webview.page().runJavaScript(f"""
- var script = document.createElement('script');
- script.src = 'qrc:///qtwebchannel/qwebchannel.js';
- script.onload = function() {{
- {js_code}
- }};
- document.head.appendChild(script);
- """)
-
-
-class BotScreen(QWidget):
- def __init__(self, stacked, module_name=""):
- super().__init__()
- self.stacked = stacked
- self.module_name = module_name
- self.charts = []
- self.plants = []
- self.lines = []
- self.filters = []
-
- self.awaiting_chart_choice = False
- self.awaiting_plant_choice = False
- self.awaiting_line_choice = False
- self.awaiting_order_input = False
- self.awaiting_filter_choice = False
-
- self.selected_chart = None
- self.selected_plant = None
- self.selected_line = None
- self.selected_order = None
- self.selected_filter = None
-
- self.init_ui()
-
- if self.module_name:
- self.load_chart_data()
-
- def set_module_name(self, name):
- self.module_name = name
- self.clear_chat()
- self.load_chart_data()
-
- def init_ui(self):
- main_layout = QVBoxLayout()
- top_bar = QHBoxLayout()
- logo = QLabel()
- logo.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_logo.png.png'
- ).scaled(40, 40, Qt.AspectRatioMode.KeepAspectRatio))
- top_bar.addWidget(logo)
-
- title = QLabel("CRI DIGITAL MANUFACTURING SOLUTIONS \n CRI BOT ASSISTANT")
- title.setStyleSheet("font-size: 16px; font-weight: bold; color: black; padding: 10px; border-radius: 8px;")
- top_bar.addWidget(title)
-
- top_bar.addStretch()
- back_btn = QPushButton("Back")
- back_btn.clicked.connect(self.go_back)
- top_bar.addWidget(back_btn)
-
- refresh_btn = QPushButton("Refresh")
- refresh_btn.clicked.connect(self.load_chart_data)
- top_bar.addWidget(refresh_btn)
- main_layout.addLayout(top_bar)
-
- self.chart_info = QLabel("Waiting for chart data...")
- self.chart_info.setWordWrap(True)
- self.chart_info.setStyleSheet(
- "background: white; color: black; font-size: 15px; padding: 14px; border-radius: 10px")
- main_layout.addWidget(self.chart_info)
-
- self.chat_area = QTextEdit()
- self.chat_area.setReadOnly(True)
- self.chat_area.setStyleSheet("background-color: white; color: black; padding: 10px; font-size: 14px;")
- main_layout.addWidget(self.chat_area)
-
- input_layout = QHBoxLayout()
-
- self.user_input = QLineEdit()
- self.user_input.setPlaceholderText("Type a number to select...")
- input_layout.addWidget(self.user_input)
-
- send_btn = QPushButton("Send")
- send_btn.setStyleSheet(
- "padding: 8px; background: #2e7d32; color: white; border-radius: 8px;")
- send_btn.clicked.connect(self.send_message)
- input_layout.addWidget(send_btn)
- main_layout.addLayout(input_layout)
-
- self.setLayout(main_layout)
-
- def load_chart_data(self):
- self.chart_info.setText("Loading chart data...")
- 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": self.module_name
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- status = data.get("status_code", "UNKNOWN")
- desc = data.get("status_description", [])
- self.chart_info.setText(f"API Status: {status}")
- self.chat_area.append(f"HTTP: {response.status_code}")
- self.chat_area.append(f"Bot Module: {self.module_name}")
- if isinstance(desc, list) and desc:
- self.charts = desc
- else:
- self.charts = [
- "Production Line Count",
- "Production Hourly Count",
- "Production Line Stop Count",
- "Production Order Count"
- ]
-
- self.awaiting_chart_choice = True
- self.show_choice_menu("chart", self.charts)
- except Exception as e:
- self.chart_info.setText("Failed to load chart data.")
- self.chat_area.append(f"Bot: Error: {e}")
-
- def show_choice_menu(self, item, items_list):
- label_map = {
- "chart": "chart",
- "plant": "plant",
- "line": "line",
- "order": "production order",
- "filter": "date filter"
- }
-
- lines = [f"{i + 1}. {name}" for i, name in enumerate(items_list)]
- menu_txt = f"Bot: Please select a {label_map[item]}:
" + "
".join(lines)
- self.chat_area.append(menu_txt)
-
- def send_message(self):
- msg = self.user_input.text().strip()
- self.user_input.clear()
-
- if self.awaiting_chart_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.charts):
- self.selected_chart = self.charts[idx]
- self.awaiting_chart_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_chart}")
- self.fetch_and_display_plants()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.charts)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1-{len(self.charts)}).")
- return
-
- if self.awaiting_plant_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.plants):
- self.selected_plant = self.plants[idx]
- self.awaiting_plant_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_plant}")
- self.fetch_and_display_lines()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.plants)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1-{len(self.plants)}).")
- return
-
- if self.awaiting_line_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.lines):
- self.selected_line = self.lines[idx]
- self.awaiting_line_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_line}")
- chart_raw = self.selected_chart
- chart_normalized = chart_raw.strip().lower() if chart_raw else ""
-
- if chart_normalized == "production order count":
- print(f"[DEBUG] self.selected_chart (raw): '{self.selected_chart}'")
- self.awaiting_order_input = True
- self.chat_area.append("Bot: Please enter the production order number:")
- else:
- self.fetch_and_display_filters()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.lines)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1-{len(self.lines)}).")
- return
-
- if hasattr(self, 'awaiting_order_input') and self.awaiting_order_input:
- order = msg.strip()
- if not order.isdigit():
- self.chat_area.append("Bot: ❌ Please enter a valid production order number (numbers only).")
- else:
- self.selected_order = order
- self.awaiting_order_input = False
- self.chat_area.append(f"Bot: ✅ Production order set: {self.selected_order}")
- self.fetch_and_display_filters()
- return
-
- if self.awaiting_filter_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.filters):
- self.selected_filter = self.filters[idx]
- self.awaiting_filter_choice = False
- self.chat_area.append(f"Bot: ✅ You selected filter: {self.selected_filter}")
- now = QDateTime.currentDateTime()
- fmt_date = now.toString("dddd, MMMM d, yyyy, hh:mm AP")
- self.chat_area.append(f"Current date: {fmt_date}")
-
- selected = self.selected_chart.strip().lower()
- if selected == "production hourly count":
- self.fetch_and_display_hourly_count()
- elif selected == "production line stop count":
- self.fetch_and_display_production_line_stop_count()
- elif selected == "production order count":
- self.fetch_and_display_production_order_count()
- else:
- self.fetch_and_display_production_count()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.filters)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a valid number (1–{len(self.filters)}).")
- return
-
- def fetch_and_display_plants(self):
- url = 'https://pds.iotsignin.com/api/get/moduleplant-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=",
- "plant-name": "Plant List"
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- plants = data.get("status_description", [])
- if plants:
- self.plants = plants
- self.awaiting_plant_choice = True
- self.show_choice_menu("plant", plants)
- else:
- self.chat_area.append("Bot: ❌ No plants found.")
- else:
- self.chat_area.append(f"Bot: ❌ Error: {data.get('status_description')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load plants: {e}")
-
- def fetch_and_display_lines(self):
- url = 'https://pds.iotsignin.com/api/get/module-plantline-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=",
- "plant-name": self.selected_plant,
- "chart-name": self.selected_chart
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- lines = data.get("status_description", [])
- if lines:
- self.lines = lines
- self.awaiting_line_choice = True
- self.show_choice_menu("line", lines)
- else:
- self.chat_area.append("Bot: ❌ No lines found for the selected plant.")
- else:
- self.chat_area.append(f"Bot: ❌ Error: {data.get('status_description')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load lines: {e}")
-
- def fetch_and_display_filters(self):
- url = 'https://pds.iotsignin.com/api/get/module-line-filter-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=",
- "line-name": self.selected_line
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- filters = data.get("status_description", [])
- if filters:
- self.filters = filters
- self.awaiting_filter_choice = True
- self.show_choice_menu("filter", filters)
- else:
- self.chat_area.append("Bot: ❌ No date filters found for the selected line.")
- else:
- self.chat_area.append(f"Bot: ❌ Error: {data.get('status_description')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load date filters: {e}")
-
- def fetch_and_display_production_count(self):
- url = 'https://pds.iotsignin.com/api/get/module-filter-value/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": self.module_name,
- "chart-name": self.selected_chart,
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "filter-name": self.selected_filter
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- result = data.get("status_description")
- if isinstance(result, dict):
- self.chat_area.append(f"Bot: 📊 Production Counts (All Lines):")
- for ln, count in result.items():
- self.chat_area.append(f"{ln}: {count}")
- else:
- self.chat_area.append(f"Bot: 📈 Production Count: {result}")
- else:
- self.chat_area.append(
- f"Bot: ❌ Error getting production count: {data.get('status_description', 'Unknown')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load production count: {e}")
-
- API_TOKEN = "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE="
-
- def fetch_and_display_hourly_count(self):
- url = "https://pds.iotsignin.com/api/get/module-filter-value/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=",
- "Accept": "application/json",
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
- "module-name": "Production Dashboard",
- "chart-name": "Production Hourly Count",
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "filter-name": self.selected_filter
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- print("📡 [DEBUG] HTTP Status:", response.status_code)
- print("📡 [DEBUG] Raw API Response:\n", response.text)
- print("-" * 50)
-
- self.chat_area.append(f"Raw API response:
{response.text}")
-
- if not response.text.strip():
- print("[DEBUG] ❌ Empty response received.")
- self.chat_area.append("Bot: ❌ API returned no data.")
- return
-
- try:
- data = response.json()
- except Exception as json_err:
- print(f"[DEBUG] ❌ JSON Parse Error: {json_err}")
- self.chat_area.append(f"Bot: ❌ Failed to parse data: {json_err}")
- return
-
- if data.get("status_code") == "SUCCESS":
- now = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- self.chat_area.append(
- f"📊 Production Hourly Count ({self.selected_filter})
Current date: {now}
")
- for dataset in data.get("datasets", []):
- label = dataset.get("label", "Data")
- rows = ""
- for lbl, val in zip(data.get("labels", []), dataset.get("data", [])):
- rows += f"{lbl} ➤ {val}
"
- self.chat_area.append(f"{label}:
{rows}")
- print("[DEBUG] ✅ Hourly count displayed successfully.")
- else:
- req_status = data.get("status_code")
- print(f"[DEBUG] ❌ API returned status {req_status}")
- self.chat_area.append(f"Bot: ❌ API returned status {req_status}")
-
- except requests.exceptions.HTTPError as errh:
- print("[DEBUG] ❌ HTTP Error:", errh)
- self.chat_area.append(f"Bot: ❌ HTTP Error: {errh}")
-
- except requests.exceptions.ConnectionError:
- print("[DEBUG] ❌ Connection Error.")
- self.chat_area.append("Bot: ❌ Connection error.")
-
- except requests.exceptions.Timeout:
- print("[DEBUG] ❌ Timeout Error.")
- self.chat_area.append("Bot: ❌ Request timed out.")
-
- except Exception as e:
- print(f"[DEBUG] ❌ General Error: {e}")
- self.chat_area.append(f"Bot: ❌ Unexpected Error: {e}")
-
- def fetch_and_display_production_line_stop_count(self):
- url = "https://pds.iotsignin.com/api/get/module-production-linestop/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",
- "chart-name": "Production Line Stop",
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "filter-name": self.selected_filter
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- self.chat_area.append(f"Raw API response:
{response.text}")
- if response.status_code != 200:
- self.chat_area.append(f"Bot: ❌ HTTP error {response.status_code}")
- return
- if not response.text.strip():
- self.chat_area.append(f"Bot: ❌ No response from API.")
- return
- try:
- data = response.json()
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to parse API response: {e}")
- return
- current_date = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- if data.get("status_code") == "SUCCESS":
- labels = data.get("labels", [])
- values = data.get("data", [])
- message = f"Production Line Stop Counts
Current date: {current_date}
"
- for label, value in zip(labels, values):
- message += f"{label}: {value}
"
- self.chat_area.append(message)
- else:
- self.chat_area.append(f"Bot: ❌ No data found for this selection.")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Error fetching report: {e}")
-
- def fetch_and_display_production_order_count(self):
- url = "https://pds.iotsignin.com/api/get/module-production-order/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",
- "chart-name": "Production Order Count",
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "production-order": self.selected_order,
- "filter-name": self.selected_filter
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- print("📡 [DEBUG] HTTP Status:", response.status_code)
- print("📡 [DEBUG] Raw API Response:\n", response.text)
- print("-" * 50)
-
- self.chat_area.append(f"Raw API response:
{response.text}")
-
- if not response.text.strip():
- print("[DEBUG] ❌ Empty API response received.")
- self.chat_area.append("Bot: ❌ API returned no data.")
- return
-
- try:
- data = response.json()
- except Exception as json_err:
- print(f"[DEBUG] ❌ JSON Parse Error: {json_err}")
- self.chat_area.append(f"Bot: ❌ Failed to parse data: {json_err}")
- return
-
- status = data.get("status_code")
- if status == "SUCCESS":
- now = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- self.chat_area.append(
- f"Production Order Count ({self.selected_filter})
Current date: {now}
")
- datasets = data.get("datasets", [])
-
- for dataset in datasets:
- label = dataset.get("label", "Data")
- self.chat_area.append(f"{label}:")
- if isinstance(dataset.get("data"), dict):
- for timeslot, count in dataset.get("data", {}).items():
- self.chat_area.append(f"{timeslot}: {count}
")
- elif isinstance(dataset.get("data"), list):
- labels = data.get("labels", [])
- for idx, value in enumerate(dataset.get("data")):
- label_text = labels[idx] if idx < len(labels) else f"Data {idx + 1}"
- self.chat_area.append(f"{label_text}: {value}
")
- else:
- self.chat_area.append(f"Data: {dataset.get('data')}")
- print("[DEBUG] ✅ Production order count handled/displayed.")
- else:
- print(f"[DEBUG] ❌ API status_code not SUCCESS: {status}")
- self.chat_area.append(f"Bot: ❌ API returned status {status}")
-
- except requests.exceptions.HTTPError as errh:
- print("[DEBUG] ❌ HTTP Error:", errh)
- self.chat_area.append(f"Bot: ❌ HTTP Error: {errh}")
-
- except requests.exceptions.ConnectionError:
- print("[DEBUG] ❌ Connection Error.")
- self.chat_area.append("Bot: ❌ Connection error.")
-
- except requests.exceptions.Timeout:
- print("[DEBUG] ❌ Timeout Error.")
- self.chat_area.append("Bot: ❌ Request timed out.")
-
- except Exception as e:
- print(f"[DEBUG] ❌ General Error: {e}")
- self.chat_area.append(f"Bot: ❌ Unexpected Error: {e}")
-
- def clear_chat(self):
- self.chat_area.clear()
-
- def go_back(self):
- for i in range(self.stacked.count()):
- widget = self.stacked.widget(i)
- if isinstance(widget, SelectorScreen):
- self.stacked.setCurrentWidget(widget)
- return
-
-
-class SelectorScreen(QWidget):
- def __init__(self, stacked, username=None):
- super().__init__()
- self.stacked = stacked
- self.username = username or "User"
- self.init_ui()
-
- def init_ui(self):
- # Set light blue background
- palette = self.palette()
- palette.setColor(QPalette.ColorRole.Window, QColor("#ADD8E6"))
- self.setPalette(palette)
- self.setAutoFillBackground(True)
-
- # Set Times New Roman font for entire screen
- self.setFont(QFont("Times New Roman"))
-
- self.layout = QVBoxLayout()
- self.layout.setContentsMargins(40, 30, 40, 30)
- self.layout.setSpacing(15)
- self.layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
-
- # CRI red logo at top
- self.cri_logo = QLabel()
- cri_logo_path = r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_red_logo.png'
- self.cri_logo.setPixmap(QPixmap(cri_logo_path).scaledToWidth(150, Qt.TransformationMode.SmoothTransformation))
- self.cri_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(self.cri_logo)
-
- # Title
- title_label = QLabel("SELECTOR SCREEN")
- title_label.setFont(QFont("Times New Roman", 18, QFont.Weight.Bold))
- title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(title_label)
-
- # Welcome username label
- welcome_label = QLabel(f"Welcome, {self.username}!")
- welcome_label.setFont(QFont("Times New Roman", 14))
- welcome_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(welcome_label)
-
- # Instruction label
- instruction_label = QLabel("Choose menu to use")
- instruction_label.setFont(QFont("Times New Roman", 12))
- instruction_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(instruction_label)
-
- # Production Display Section
- prod_display_title = QLabel("PRODUCTION DISPLAY WEBSITE")
- prod_display_title.setFont(QFont("Times New Roman", 14, QFont.Weight.Bold))
- prod_display_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(prod_display_title)
-
- self.production_display_btn = QPushButton("Production Display Website")
- self.production_display_btn.setFont(QFont("Times New Roman", 12))
- self.production_display_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
- self.production_display_btn.setStyleSheet(
- "padding: 10px; background-color: #c2185b; color: white; border-radius: 8px;")
- self.production_display_btn.clicked.connect(self.open_production_display)
- self.layout.addWidget(self.production_display_btn)
-
- # Dashboards Section
- dashboards_title = QLabel("DASHBOARDS")
- dashboards_title.setFont(QFont("Times New Roman", 14, QFont.Weight.Bold))
- dashboards_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(dashboards_title)
-
- self.production_dashboard_btn = QPushButton("Production Dashboard")
- self.production_dashboard_btn.setFont(QFont("Times New Roman", 12))
- self.production_dashboard_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
- self.production_dashboard_btn.setStyleSheet(
- "padding: 10px; background-color: #c2185b; color: white; border-radius: 8px;")
- self.production_dashboard_btn.clicked.connect(lambda: self.open_dashboard("Production Dashboard"))
- self.layout.addWidget(self.production_dashboard_btn)
-
- self.invoice_dashboard_btn = QPushButton("Invoice Dashboard")
- self.invoice_dashboard_btn.setFont(QFont("Times New Roman", 12))
- self.invoice_dashboard_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
- self.invoice_dashboard_btn.setStyleSheet(
- "padding: 10px; background-color: #c2185b; color: white; border-radius: 8px;")
- self.invoice_dashboard_btn.clicked.connect(lambda: self.open_dashboard("Invoice Dashboard"))
- self.layout.addWidget(self.invoice_dashboard_btn)
-
- # Spacer to push the logo down
- self.layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding))
-
- # CRI banner logo at bottom
- self.cri_banner_logo = QLabel()
- banner_logo_path = r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_farm_banner.jpg'
- self.cri_banner_logo.setPixmap(
- QPixmap(banner_logo_path).scaledToWidth(350, Qt.TransformationMode.SmoothTransformation))
- self.cri_banner_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.layout.addWidget(self.cri_banner_logo)
-
- self.setLayout(self.layout)
-
- def open_production_display(self):
- # Check if screen already exists
- for i in range(self.stacked.count()):
- widget = self.stacked.widget(i)
- if isinstance(widget, ProductionDisplayScreen):
- self.stacked.setCurrentWidget(widget)
- return
-
- # Otherwise, add new screen
- display_screen = ProductionDisplayScreen(self.stacked)
- self.stacked.addWidget(display_screen)
- self.stacked.setCurrentWidget(display_screen)
-
- def open_dashboard(self, dashboard_type):
- # Check if screen already exists
- for i in range(self.stacked.count()):
- widget = self.stacked.widget(i)
- if isinstance(widget, BotScreen) and widget.module_name == dashboard_type:
- self.stacked.setCurrentWidget(widget)
- return
-
- # Otherwise, create new screen
- bot_screen = BotScreen(self.stacked, dashboard_type)
- self.stacked.addWidget(bot_screen)
- self.stacked.setCurrentWidget(bot_screen)
-
- def show_error(self, msg):
- error = QLabel(f"❌ {msg}")
- error.setStyleSheet("color: red;")
- self.layout.addWidget(error)
-
-
-class LoginScreen(QWidget):
- def __init__(self, stacked):
- super().__init__()
- self.stacked = stacked
- self.init_ui()
-
- def init_ui(self):
- self.showMaximized()
-
- # Light blue background
- palette = self.palette()
- palette.setColor(QPalette.ColorRole.Window, QColor("#ADD8E6"))
- self.setPalette(palette)
- self.setAutoFillBackground(True)
-
- layout = QVBoxLayout()
- layout.setContentsMargins(40, 30, 40, 30)
- layout.setSpacing(20)
- layout.setAlignment(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter)
-
- # Logo
- logo = QLabel()
- logo.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_red_logo.png'
- ).scaled(150, 150, Qt.AspectRatioMode.KeepAspectRatio))
- logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(logo)
-
- # Title
- title_label = QLabel("CRI DIGITAL MANUFACTURING \n SOLUTIONS")
- title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- title_label.setStyleSheet("""
- font-size: 18px;
- font-weight: bold;
- color: #002244;
- margin-bottom: 10px;
- """)
- layout.addWidget(title_label)
-
- # Username QLineEdit
- self.username = QLineEdit()
- self.username.setPlaceholderText("Username (max 25 chars)")
- self.username.setMaxLength(25)
- self.username.setStyleSheet(self.input_style())
- layout.addWidget(self.username)
-
- # Password QLineEdit
- self.password = QLineEdit()
- self.password.setPlaceholderText("Password (max 25 chars)")
- self.password.setMaxLength(25)
- self.password.setEchoMode(QLineEdit.EchoMode.Password)
- self.password.setStyleSheet(self.input_style())
-
- # Create eye toggle button with emoji inside password field
- self.eye_button = QToolButton(self.password)
- self.eye_button.setText("🙈") # Initially hidden password
- self.eye_button.setCursor(Qt.CursorShape.PointingHandCursor)
- self.eye_button.setStyleSheet("border: none; background: transparent; font-size: 18px;")
- self.eye_button.setToolTip("Show/Hide password")
- self.eye_button.clicked.connect(self.toggle_password_visibility)
-
- # Adjust size and margins in QLineEdit for the eye button
- frame_width = self.password.style().pixelMetric(self.password.style().PixelMetric.PM_DefaultFrameWidth)
- button_size = 24
- self.eye_button.setFixedSize(button_size, button_size)
- self.password.setTextMargins(0, 0, button_size + frame_width, 0)
-
- # Position the eye button correctly
- self.position_eye_button()
-
- # Reposition on resize or text change
- self.password.textChanged.connect(self.position_eye_button)
- self.password.installEventFilter(self)
-
- layout.addWidget(self.password)
-
- # Connect Enter key navigation
- self.username.returnPressed.connect(self.password.setFocus)
- self.password.returnPressed.connect(self.do_login)
-
- # Login button smaller style
- login_btn = QPushButton("Login")
- login_btn.setStyleSheet("""
- QPushButton {
- background-color: #E91E63;
- color: white;
- font-weight: bold;
- padding: 6px 15px;
- border-radius: 12px;
- font-size: 14px;
- max-width: 120px;
- }
- QPushButton:hover {
- background-color: #C2185B;
- }
- QPushButton:pressed {
- background-color: #AD1457;
- }
- """)
- login_btn.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
- login_btn.clicked.connect(self.do_login)
- layout.addWidget(login_btn, alignment=Qt.AlignmentFlag.AlignCenter)
-
- # Secondary image/banner
- logo2 = QLabel()
- logo2.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_farm_banner.jpg'
- ).scaled(350, 350, Qt.AspectRatioMode.KeepAspectRatio))
- logo2.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(logo2)
-
- self.setLayout(layout)
-
- def position_eye_button(self):
- frame_width = self.password.style().pixelMetric(self.password.style().PixelMetric.PM_DefaultFrameWidth)
- button_size = self.eye_button.width()
- self.eye_button.move(
- self.password.rect().right() - button_size - frame_width,
- (self.password.rect().bottom() - button_size + 1) // 2
- )
-
- def eventFilter(self, obj, event):
- if obj == self.password and event.type() == QEvent.Type.Resize:
- self.position_eye_button()
- return super().eventFilter(obj, event)
-
- def toggle_password_visibility(self):
- if self.password.echoMode() == QLineEdit.EchoMode.Password:
- self.password.setEchoMode(QLineEdit.EchoMode.Normal)
- self.eye_button.setText("👁️")
- else:
- self.password.setEchoMode(QLineEdit.EchoMode.Password)
- self.eye_button.setText("🙈")
-
- def input_style(self):
- return """
- QLineEdit {
- background-color: white;
- border: 2px solid #ccc;
- border-radius: 15px;
- padding: 8px 12px;
- color: black;
- font-size: 14px;
- min-width: 300px;
- }
- QLineEdit:focus {
- border: 2px solid purple;
- }
- """
-
- def do_login(self):
- username = self.username.text().strip()
- password = self.password.text().strip()
-
- if not username or not password:
- QMessageBox.critical(self, "Input Error", "Please enter both username and password.")
- return
-
- url = "https://pds.iotsignin.com/api/testing/user/get-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=",
- "User-Name": username,
- "User-Pass": password
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
- response.raise_for_status()
- data = response.json()
-
- if "roles" in data:
- roles = data["roles"]
- roles_str = ', '.join(roles)
- info_msg = f"Login successful!"
- result = QMessageBox.information(self, "Success", info_msg, QMessageBox.StandardButton.Ok)
- if result == QMessageBox.StandardButton.Ok:
- selector_screen = SelectorScreen(self.stacked, username)
- self.stacked.addWidget(selector_screen)
- self.stacked.setCurrentWidget(selector_screen)
-
- elif data.get("status_code") == "ERROR":
- description = data.get("status_description", "Unknown error from server.")
- QMessageBox.critical(self, "Login Error", description)
- else:
- QMessageBox.critical(self, "Login Error", "Unexpected response from server.")
-
- except requests.exceptions.RequestException as e:
- QMessageBox.critical(self, "Network Error", f"Failed to connect to server:\n{str(e)}")
-
-
-def main():
- app = QApplication(sys.argv)
- stacked = QStackedWidget()
-
- login_screen = LoginScreen(stacked)
- stacked.addWidget(login_screen)
-
- stacked.setFixedSize(1200, 800)
- stacked.show()
-
- sys.exit(app.exec())
-
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/bg_user_message.xml b/bg_user_message.xml
deleted file mode 100644
index b3a9cb3..0000000
--- a/bg_user_message.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/bot.png b/bot.png
deleted file mode 100644
index 8c46573..0000000
Binary files a/bot.png and /dev/null differ
diff --git a/cri_farm_banner.jpg b/cri_farm_banner.jpg
deleted file mode 100644
index 9b7969d..0000000
Binary files a/cri_farm_banner.jpg and /dev/null differ
diff --git a/download b/download
deleted file mode 100644
index e75c8d3..0000000
--- a/download
+++ /dev/null
@@ -1,3 +0,0 @@
-NO,NAME,"CREATED AT","UPDATED AT"
-1,"C.R.I. Pumps Private Limited","2025-04-21 16:10:04","2025-04-21 16:10:04"
-2,"Tormac Pumps Private Limited","2025-04-21 16:10:04","2025-06-16 19:58:01"
diff --git a/eye.png b/eye.png
deleted file mode 100644
index 548cdc7..0000000
Binary files a/eye.png and /dev/null differ
diff --git a/login.qss.py b/login.qss.py
deleted file mode 100644
index 41cd0f1..0000000
--- a/login.qss.py
+++ /dev/null
@@ -1,68 +0,0 @@
-from PyQt6.QtWidgets import QWidget
-
-QWidget#LoginWindow {
-background: qlineargradient(
- x1: 0, y1: 0, x2: 0, y2: 1,
- stop: 0 #fd8e53, /* warm pink-orange */
- stop: 1 #111748 /* deep blue */
- );
-}
-
-/* Icon or title label—for centered white icon/text */
-QLabel {
- color: white;
-}
-
-/* Username and Password input boxes */
-QLineEdit {
- background: rgba(255, 255, 255, 0.6);
- border: 1px solid rgba(255, 255, 255, 0.8);
- border-radius: 8px;
- padding: 8px 12px;
- color: #333;
-}
-
-/* Placeholder text styling */
-QLineEdit::placeholder {
- color: rgba(51, 51, 51, 0.6);
-}
-
-/* Login button styling */
-QPushButton#loginButton {
- font-size: 16px;
- font-weight: bold;
- color: white;
- background: qlineargradient(
- x1: 0, y1: 0, x2: 1, y2: 0,
- stop: 0 #ff416c, /* pink-red */
- stop: 1 #ff4b2b /* orange-red */
- );
- border: none;
- border-radius: 12px;
- padding: 12px;
-}
-
-/* Hover state for the login button */
-QPushButton#loginButton:hover {
- background: qlineargradient(
- x1: 0, y1: 0, x2: 1, y2: 0,
- stop: 0 #ff5a7a,
- stop: 1 #ff6a4b
- );
-}
-
-/* Checkbox and link area styling */
-QCheckBox, QLabel {
- color: rgba(255, 255, 255, 0.8);
- font-size: 12px;
-}
-
-/* Rich-text link color */
-QLabel[href] {
- color: rgba(255, 255, 255, 0.7);
-}
-
-/* Remove focus outline on QLineEdit and button when clicked */
-QLineEdit:focus, QPushButton:focus {
- outline: none;
-}
diff --git a/pds.py b/pds.py
deleted file mode 100644
index 083dad5..0000000
--- a/pds.py
+++ /dev/null
@@ -1,1033 +0,0 @@
-import sys
-import os
-import base64
-import requests
-import pyttsx3
-import queue
-import threading
-from PyQt6.QtCore import Qt, QUrl, QObject, pyqtSlot, QTimer, QTime
-from PyQt6.QtGui import QIcon, QPixmap, QAction, QFont, QColor, QLinearGradient, QBrush, QPainter, QPalette
-from PyQt6.QtWidgets import (
- QApplication, QWidget, QLabel, QLineEdit, QPushButton, QMessageBox,
- QVBoxLayout, QHBoxLayout, QStackedWidget, QComboBox, QFileDialog,
- QTabWidget, QFrame, QScrollArea, QSizePolicy
-)
-from PyQt6.QtWebEngineWidgets import QWebEngineView
-from PyQt6.QtWebChannel import QWebChannel
-from threading import Thread
-
-
-# Gradient widget base class for backgrounds
-class GradientWidget(QWidget):
- def __init__(self, parent=None):
- super().__init__(parent)
- self.setAutoFillBackground(True)
-
- def paintEvent(self, event):
- painter = QPainter(self)
- gradient = QLinearGradient(0, 0, 0, self.height())
- gradient.setColorAt(0, QColor("#B0E0E6"))
- gradient.setColorAt(1, QColor("#F3C2C2"))
- painter.fillRect(self.rect(), QBrush(gradient))
-
-
-# JSBridge for interacting with web content (file save)
-class JSBridge(QObject):
- @pyqtSlot(str, str, str)
- def saveFile(self, base64_data, filename, source):
- if base64_data.startswith("data:"):
- base64_data = base64_data.split(",", 1)[1]
- data = base64.b64decode(base64_data)
- path, _ = QFileDialog.getSaveFileName(None, f"Save {source.title()} File", filename)
- if path:
- with open(path, "wb") as f:
- f.write(data)
- QMessageBox.information(None, f"{source.title()} Download", f"Saved to:\n{path}")
- if sys.platform.startswith("win"):
- os.startfile(path)
- elif sys.platform.startswith("darwin"):
- os.system(f"open '{path}'")
- else:
- os.system(f"xdg-open '{path}'")
-
-
-# WebAssistantScreen with tabbed browser and credential injection
-class WebAssistantScreen(GradientWidget):
- def __init__(self, email, password, go_back):
- super().__init__()
- self.email = email
- self.password = password
- self.go_back = go_back
- self._inject_attempts = 0
- self._max_inject_attempts = 5
- self._setup_ui()
-
- def _setup_ui(self):
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
-
- toolbar = QWidget(self)
- toolbar.setFixedHeight(60)
- toolbar.setStyleSheet("""
- background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
- stop:0 #B0E0E6, stop:1 #F3C2C2);
- """)
- hl = QHBoxLayout(toolbar)
- hl.setContentsMargins(10, 5, 10, 5)
- hl.setSpacing(10)
-
- logo = QLabel()
- pix = QPixmap("cri_logo.png.png")
- if not pix.isNull():
- logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
- hl.addWidget(logo, alignment=Qt.AlignmentFlag.AlignVCenter)
-
- title = QLabel(" CRI DIGITAL MANUFACTURING SOLUTIONS - PRODUCTION DISPLAY")
- title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
- hl.addWidget(title, alignment=Qt.AlignmentFlag.AlignVCenter)
- hl.addStretch(1)
-
- self.btn_back = QPushButton("Back")
- self.btn_prev = QPushButton("Previous")
- self.btn_refresh = QPushButton("Refresh")
- self.btn_next = QPushButton("Next")
- self.btn_dup = QPushButton("Duplicate")
- self.btn_del = QPushButton("Delete")
- self.spinner = QComboBox()
-
- button_style = """
- QPushButton {
- background: #C71585;
- color: white;
- border: none;
- border-radius: 8px;
- padding: 10px 20px;
- font-size: 16px;
- font-weight: bold;
- }
- QPushButton:hover {
- background: #b95470;
- }
- """
- for w in (self.btn_back, self.btn_prev, self.btn_refresh,
- self.btn_next, self.btn_dup, self.btn_del):
- w.setStyleSheet(button_style)
- hl.addWidget(w, alignment=Qt.AlignmentFlag.AlignVCenter)
-
- self.spinner.setStyleSheet("""
- QComboBox {
- padding: 6px;
- border: 1px solid #aaa;
- border-radius: 6px;
- background-color: #ffffff;
- }
- """)
- hl.addWidget(self.spinner, alignment=Qt.AlignmentFlag.AlignVCenter)
-
- layout.addWidget(toolbar)
-
- self.tabs = QTabWidget(self)
- self.tabs.setTabsClosable(False)
- layout.addWidget(self.tabs, stretch=1)
-
- self.channel = QWebChannel()
- self.channel.registerObject("pyBridge", JSBridge())
-
- self.add_tab("https://pds.iotsignin.com/admin")
-
- self.btn_back.clicked.connect(self.go_back)
- self.btn_prev.clicked.connect(lambda: self.tabs.currentWidget().back())
- self.btn_refresh.clicked.connect(lambda: self.tabs.currentWidget().reload())
- self.btn_next.clicked.connect(lambda: self.tabs.currentWidget().forward())
- self.btn_dup.clicked.connect(self.duplicate_tab)
- self.btn_del.clicked.connect(self.delete_tab)
- self.spinner.currentIndexChanged.connect(self.switch_tab)
-
- def add_tab(self, url):
- view = QWebEngineView()
- view.page().setWebChannel(self.channel)
- view.load(QUrl(url))
- index = self.tabs.addTab(view, "Loading…")
- self.spinner.addItem(f"Tab {index + 1}")
- view.titleChanged.connect(lambda t, v=view: self.tabs.setTabText(self.tabs.indexOf(v), t))
- view.loadFinished.connect(self.on_page_loaded)
-
- def on_page_loaded(self, ok):
- if ok:
- self._inject_attempts = 0
- self.inject_credentials()
- QTimer.singleShot(1000, self.inject_credentials)
- QTimer.singleShot(3000, self.inject_credentials)
-
- def inject_credentials(self):
- if self._inject_attempts >= self._max_inject_attempts:
- return
- self._inject_attempts += 1
- js_code = f"""
- (function() {{
- function setField(field, value) {{
- if (!field) return false;
- field.focus();
- field.value = value;
- field.dispatchEvent(new Event('input', {{ bubbles: true }}));
- field.dispatchEvent(new Event('change', {{ bubbles: true }}));
- return true;
- }}
- var emailField = document.querySelector('input[type="email"], input#email, input[name="email"], input[data-testid="email"]');
- var pwdField = document.querySelector('input[type="password"], input#password, input[name="password"], input[data-testid="password"]');
- var emailSet = setField(emailField, "{self.email}");
- var passwordSet = setField(pwdField, "{self.password}");
- return {{
- emailSet: emailSet,
- passwordSet: passwordSet
- }};
- }})();
- """
- current_index = self.tabs.currentIndex()
- if current_index != -1:
- view = self.tabs.widget(current_index)
- view.page().runJavaScript(js_code, self.handle_injection_result)
-
- def handle_injection_result(self, result):
- if result and (not result['emailSet'] or not result['passwordSet']):
- if self._inject_attempts < self._max_inject_attempts:
- QTimer.singleShot(1000, self.inject_credentials)
-
- def duplicate_tab(self):
- current = self.current_tab()
- if current:
- url = current.url().toString()
- self.add_tab(url)
- self.tabs.setCurrentIndex(self.tabs.count() - 1)
-
- def delete_tab(self):
- if self.tabs.count() > 1:
- idx = self.tabs.currentIndex()
- self.tabs.removeTab(idx)
- self.spinner.removeItem(idx)
-
- def switch_tab(self, i):
- if 0 <= i < self.tabs.count():
- self.tabs.setCurrentIndex(i)
-
- def current_tab(self):
- return self.tabs.currentWidget()
-
-
-# Chat bubble widget for chat interface
-class ChatBubble(QWidget):
- def __init__(self, text, is_user=False):
- super().__init__()
- self.text = text
- self.is_user = is_user
- self.init_ui()
-
- def init_ui(self):
- layout = QVBoxLayout(self)
- layout.setContentsMargins(10, 0, 10, 0)
-
- bubble_label = QLabel(self.text)
- bubble_label.setWordWrap(True)
- bubble_label.setFont(QFont("Segoe UI", 12))
- bubble_label.setStyleSheet(f"""
- background-color: {"#B4DBFA" if self.is_user else "#2E3A59"};
- color: {"black" if self.is_user else "white"};
- padding: 10px 14px;
- border-radius: 16px;
- """)
- bubble_label.setMaximumWidth(250)
- bubble_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
-
- layout.setAlignment(Qt.AlignmentFlag.AlignRight if self.is_user else Qt.AlignmentFlag.AlignLeft)
- layout.addWidget(bubble_label)
-
-
-class ChatScrollArea(QScrollArea):
- def __init__(self):
- super().__init__()
- self.setWidgetResizable(True)
- self.chat_content = QWidget()
- self.chat_layout = QVBoxLayout(self.chat_content)
- self.chat_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
- self.chat_layout.setSpacing(8)
- self.setWidget(self.chat_content)
-
- def add_bubble(self, text, is_user=False):
- bubble = ChatBubble(text, is_user=is_user)
- self.chat_layout.addWidget(bubble)
- self.verticalScrollBar().setValue(self.verticalScrollBar().maximum())
-
-
-class ProductionBotScreen(QWidget):
- def __init__(self, back_action=None):
- super().__init__()
- self.back_action = back_action
-
- self.chart_map = {}
- self.plant_map = {}
- self.awaiting_plants = False
-
- self.init_tts()
- self._setup_ui()
-
- QTimer.singleShot(500, self._show_greeting)
- QTimer.singleShot(1500, self.load_charts)
-
- self.tts_queue = queue.Queue()
- self.tts_thread = threading.Thread(target=self.tts_worker, daemon=True)
- self.tts_thread.start()
-
- def init_tts(self):
- self.tts_engine = pyttsx3.init()
- self.tts_engine.setProperty('rate', 170)
- # No voice filtering - use system default voice
-
- def tts_worker(self):
- while True:
- text = self.tts_queue.get()
- if text is None:
- break
- try:
- self.tts_engine.say(text)
- self.tts_engine.runAndWait()
- except Exception:
- pass
- self.tts_queue.task_done()
-
- def speak(self, text):
- self.tts_queue.put(text)
-
- def closeEvent(self, event):
- self.tts_queue.put(None)
- self.tts_thread.join(timeout=2)
- super().closeEvent(event)
-
- def _setup_ui(self):
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
-
- header = QWidget()
- header.setFixedHeight(70)
- header.setStyleSheet("""
- background: qlineargradient(x1:0,y1:0,x2:1,y2:0,
- stop:0 #B0E0E6, stop:1 #F3C2C2);
- color: #333;
- font-size: 22px;
- """)
- hlayout = QHBoxLayout(header)
- hlayout.setContentsMargins(10, 5, 10, 5)
-
- logo = QLabel()
- pix = QPixmap("cri_logo.png")
- if pix and not pix.isNull():
- logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
- hlayout.addWidget(logo)
-
- title = QLabel("PRODUCTION BOT")
- title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
- hlayout.addWidget(title)
-
- hlayout.addStretch(1)
-
- self.back_btn = QPushButton("Back")
- self.back_btn.setStyleSheet("""
- QPushButton {
- background: #C71585;
- color: white;
- border-radius: 8px;
- padding: 8px 14px;
- font-weight: bold;
- }
- QPushButton:hover {
- background: #b95975;
- }
- """)
- if self.back_action:
- self.back_btn.clicked.connect(self.back_action)
- hlayout.addWidget(self.back_btn)
-
- self.refresh_btn = QPushButton("Refresh")
- self.refresh_btn.setStyleSheet("""
- QPushButton {
- background: #C71585;
- color: white;
- border-radius: 8px;
- padding: 8px 14px;
- font-weight: bold;
- }
- QPushButton:hover {
- background: #b95975;
- }
- """)
- hlayout.addWidget(self.refresh_btn)
-
- layout.addWidget(header)
-
- self.chat_area = ChatScrollArea()
- self.chat_area.setStyleSheet("""
- QScrollArea {
- background: qlineargradient(x1:0,y1:0,x2:1,y2:1,
- stop:0 #b0e6e6, stop:1 #f3c2c2);
- }
- """)
- layout.addWidget(self.chat_area, stretch=1)
-
- input_area = QWidget()
- input_layout = QHBoxLayout(input_area)
- input_layout.setContentsMargins(10, 10, 10, 10)
-
- self.user_input = QLineEdit()
- self.user_input.setPlaceholderText("Type your message...")
- self.user_input.setStyleSheet("""
- QLineEdit {
- font-size: 16px;
- padding: 8px;
- border: 2px solid #db7093;
- border-radius: 4px;
- }
- QLineEdit:focus {
- border-color: #f3c2c2;
- background: #fff0f0;
- }
- """)
- self.send_btn = QPushButton("Send")
- self.send_btn.setStyleSheet("""
- QPushButton {
- background: #006400;
- color: white;
- font-weight: bold;
- padding: 8px 20px;
- border-radius: 8px;
- }
- QPushButton:hover {
- background: #a2136a;
- }
- """)
- input_layout.addWidget(self.user_input)
- input_layout.addWidget(self.send_btn)
-
- layout.addWidget(input_area)
-
- self.send_btn.clicked.connect(self._on_send)
-
- def _show_greeting(self):
- hour = QTime.currentTime().hour()
- greeting = "Good night"
- if 5 <= hour < 12:
- greeting = "Good morning"
- elif 12 <= hour < 17:
- greeting = "Good afternoon"
- elif 17 <= hour < 21:
- greeting = "Good evening"
- self.chat_area.add_bubble(f"{greeting}, User", False)
- self.speak(f"{greeting}, User")
-
- def _on_send(self):
- msg = self.user_input.text().strip()
- if not msg:
- return
- self.chat_area.add_bubble(msg, True)
- self.user_input.clear()
-
- if msg.isdigit():
- idx = int(msg)
- if self.awaiting_plants:
- plant = self.plant_map.get(idx)
- if plant:
- self.plant_selected(plant)
- else:
- self.chat_area.add_bubble("Invalid plant number. Please try again.", False)
- self.speak("Invalid plant number. Please try again.")
- else:
- chart = self.chart_map.get(idx)
- if chart:
- self.chart_selected(chart)
- else:
- self.chat_area.add_bubble("Invalid chart number. Please try again.", False)
- 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):
- # url = "https://pds.iotsignin.com/api/get/moduleplant-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=",
- # "plant-name": "Plant List"
- # }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- response.raise_for_status()
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- plants = data.get("status_description", [])
- 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())
- self.chat_area.add_bubble("Please select a plant:\n\n" + numbered, False)
- self.speak("Please select a plant.")
- else:
- self.chat_area.add_bubble("Failed to load plants.", False)
- self.speak("Failed to load plants.")
- except Exception as e:
- self.chat_area.add_bubble(f"Error loading plants: {str(e)}", False)
- 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 is unchanged based on your last code snippet
-class InvoiceBotScreen(QWidget):
- def __init__(self, back_action=None):
- super().__init__()
- self.back_action = back_action
- self._setup_ui()
-
- def _setup_ui(self):
- layout = QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
-
- self.header = QWidget()
- self.header.setFixedHeight(70)
- self.header.setStyleSheet("""
- background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
- stop:0 #B0E0E6, stop:1 #F3C2C2);
- color: #333;
- font-size: 22px;
- """)
- hlayout = QHBoxLayout(self.header)
- hlayout.setContentsMargins(10, 5, 10, 5)
-
- logo = QLabel()
- pix = QPixmap("cri_logo.png")
- if pix and not pix.isNull():
- logo.setPixmap(pix.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio))
- hlayout.addWidget(logo)
-
- title = QLabel("INVOICE BOT")
- title.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
- hlayout.addWidget(title)
-
- hlayout.addStretch(1)
-
- self.back_btn = QPushButton("Back")
- self.refresh_btn = QPushButton("Refresh")
- for btn in [self.back_btn, self.refresh_btn]:
- btn.setStyleSheet("""
- QPushButton {
- background: #C71585;
- color: white;
- border-radius: 8px;
- padding: 8px 14px;
- font-weight: bold;
- }
- QPushButton:hover {
- background: #f2f2f2;
- }
- """)
- if self.back_action:
- self.back_btn.clicked.connect(self.back_action)
-
- hlayout.addWidget(self.back_btn)
- hlayout.addWidget(self.refresh_btn)
-
- layout.addWidget(self.header)
-
- self.chat_area = ChatScrollArea()
- self.chat_area.setStyleSheet("""
- QScrollArea {
- background: qlineargradient(
- x1: 0, y1: 0,
- x2: 1, y2: 1,
- stop: 0 #b3e0e6,
- stop: 1 #f3c2c2
- );
- }
- """)
- layout.addWidget(self.chat_area, stretch=1)
-
- input_widget = QWidget()
- input_layout = QHBoxLayout(input_widget)
- input_layout.setContentsMargins(10, 10, 10, 10)
-
- self.user_input = QLineEdit()
- self.user_input.setPlaceholderText("Type your message...")
- self.send_btn = QPushButton("Send")
-
- self.user_input.setStyleSheet("""
- QLineEdit {
- font-size: 16px;
- padding: 8px;
- border: 2px solid #ccc;
- border-radius: 4px;
- border-color: #DB7093;
- }
- QLineEdit:focus {
- border: 2px solid #f3c2c2;
- background-color: #fff0f0;
- }
- """)
- self.send_btn.setStyleSheet("""
- QPushButton {
- background-color: #006400;
- color: white;
- font-weight: bold;
- padding: 8px 20px;
- border-radius: 8px;
- }
- QPushButton:hover {
- background-color: #a2136a;
- }
- """)
-
- input_layout.addWidget(self.user_input)
- input_layout.addWidget(self.send_btn)
- layout.addWidget(input_widget)
-
- self.send_btn.clicked.connect(self._on_send)
-
- self._show_greeting()
-
- self.chat_area.add_bubble("Invoice bot activated. What invoice do you need?", is_user=False)
-
- def _show_greeting(self):
- hour = QTime.currentTime().hour()
- greeting = "Good night"
- if 5 <= hour < 12:
- greeting = "Good morning"
- elif 12 <= hour < 17:
- greeting = "Good afternoon"
- elif 17 <= hour < 21:
- greeting = "Good evening"
- 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
-class BotSelectorScreen(QWidget):
- def __init__(self, navigate_to_dashboard):
- super().__init__()
- self.navigate_to_dashboard = navigate_to_dashboard
- self._init_ui()
- self.engine = pyttsx3.init()
- QTimer.singleShot(500, self._speak_welcome_message)
-
- def _init_ui(self):
- # Gradient background for widget
- gradient = QLinearGradient(0, 0, 0, self.height() or 400)
- gradient.setColorAt(0, QColor("#B0E0E6"))
- gradient.setColorAt(1, QColor("#F3C2C2"))
- palette = QPalette()
- palette.setBrush(QPalette.ColorRole.Window, QBrush(gradient))
- self.setPalette(palette)
- self.setAutoFillBackground(True)
-
- layout = QVBoxLayout(self)
- layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.setSpacing(20)
-
- logo = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
- pix = QPixmap("cri_logo.png.png")
- if not pix.isNull():
- pix = pix.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio)
- logo.setPixmap(pix)
- else:
- logo.setText("CRI Logo")
- logo.setStyleSheet("font-size: 36px; font-weight: bold; color: #333;")
- layout.addWidget(logo)
-
- welcome_label = QLabel("Welcome to CRI PUMPS", alignment=Qt.AlignmentFlag.AlignCenter)
- welcome_label.setStyleSheet("font-size: 36px; font-weight: bold; color: #333;")
- layout.addWidget(welcome_label)
-
- menu_label = QLabel("Choose menu to use", alignment=Qt.AlignmentFlag.AlignCenter)
- menu_label.setStyleSheet("font-size: 32px; color: #333;")
- layout.addWidget(menu_label)
-
- button_container = QFrame()
- button_layout = QVBoxLayout(button_container)
- button_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
- button_layout.setSpacing(40)
-
- button_style = """
- QPushButton {
- background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
- stop:0 #88CFF1, stop:1 #F3C2C2);
- border: 2px solid #D36C84;
- border-radius: 15px;
- padding: 25px;
- font-size: 24px;
- font-weight: bold;
- color: #333;
- min-width: 400px;
- min-height: 100px;
- }
- QPushButton:hover {
- background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
- stop:0 #AEDFF7, stop:1 #F5D3D3);
- border: 3px solid #b95470;
- }
- """
-
- prod_btn = QPushButton("Production Dashboard")
- prod_btn.setStyleSheet(button_style)
- prod_btn.clicked.connect(lambda: self.navigate_to_dashboard("Production"))
- button_layout.addWidget(prod_btn, alignment=Qt.AlignmentFlag.AlignCenter)
-
- invoice_btn = QPushButton("Invoice Dashboard")
- invoice_btn.setStyleSheet(button_style)
- invoice_btn.clicked.connect(lambda: self.navigate_to_dashboard("Invoice"))
- button_layout.addWidget(invoice_btn, alignment=Qt.AlignmentFlag.AlignCenter)
-
- layout.addWidget(button_container, alignment=Qt.AlignmentFlag.AlignCenter)
- layout.addStretch(1)
-
- def _speak_welcome_message(self):
- self.engine.say("Welcome to C R I Pumps. Click the button to see the production count.")
- self.engine.runAndWait()
-
-
-# SelectorScreen (your unchanged code)
-class SelectorScreen(GradientWidget):
- def __init__(self, switch_to_login, switch_to_web, switch_to_bot):
- super().__init__()
- self.switch_to_login = switch_to_login
- self.switch_to_web = switch_to_web
- self.switch_to_bot = switch_to_bot
- self.email = ""
- self.password = ""
- self._init_ui()
-
- def _init_ui(self):
- layout = QVBoxLayout(self)
- layout.setContentsMargins(20, 20, 20, 20)
- layout.setSpacing(0)
-
- logo_top = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
- pix = QPixmap("cri_logo.png.png")
- if not pix.isNull():
- pix = pix.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
- logo_top.setPixmap(pix)
- else:
- logo_top.setText("CRI Logo")
- logo_top.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
- layout.addWidget(logo_top)
-
- layout.addSpacing(40)
-
- title = QLabel("Welcome to CRI PUMPS", alignment=Qt.AlignmentFlag.AlignCenter)
- title.setStyleSheet("""
- font-size: 36px;
- font-weight: bold;
- color: #333;
- text-shadow: 1px 1px 2px rgba(255,255,255,0.8);
- """)
- layout.addWidget(title)
-
- layout.addSpacing(30)
-
- subtitle = QLabel("PRODUCTION DISPLAY SOFTWARE", alignment=Qt.AlignmentFlag.AlignCenter)
- subtitle.setStyleSheet("""
- font-size: 28px;
- font-weight: bold;
- color: #333;
- font-style: italic;
- text-shadow: 1px 1px 2px rgba(255,255,255,0.8);
- """)
- layout.addWidget(subtitle)
-
- layout.addSpacing(30)
-
- subtitle2 = QLabel("Choose menu to use", alignment=Qt.AlignmentFlag.AlignCenter)
- subtitle2.setStyleSheet("""
- font-size: 28px;
- color: #333;
- text-shadow: 1px 1px 2px rgba(255,255,255,0.8);
- """)
- layout.addWidget(subtitle2)
-
- layout.addSpacing(40)
-
- logo_mid = QLabel(alignment=Qt.AlignmentFlag.AlignCenter)
- pix2 = QPixmap("cri_farm_banner.jpg")
- if not pix2.isNull():
- pix2 = pix2.scaled(500, 300, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
- logo_mid.setPixmap(pix2)
- else:
- logo_mid.setText("CRI Banner")
- logo_mid.setStyleSheet("font-size: 24px; color: #333;")
- layout.addWidget(logo_mid)
-
- layout.addStretch(1)
-
- bottom = QWidget(self)
- bottom.setStyleSheet("background: transparent;")
- hb = QHBoxLayout(bottom)
- hb.setContentsMargins(0, 20, 0, 20)
- hb.setSpacing(80)
-
- button_style = """
- QPushButton {
- background: #F3C2C2;
- border: none;
- font-size: 20px;
- border-radius: 10px;
- padding: 10px;
- min-width: 120px;
- min-height: 60px;
- }
- QPushButton:hover {
- background: #E8B1B1;
- }
- """
-
- btn_logout = QPushButton("Logout")
- btn_logout.setStyleSheet(button_style + "font-size: 24px;")
- btn_logout.clicked.connect(self.switch_to_login)
- hb.addWidget(btn_logout, alignment=Qt.AlignmentFlag.AlignCenter)
-
- btn_web = QPushButton()
- pix_web = QPixmap("web.png")
- if not pix_web.isNull():
- pix_web = pix_web.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio)
- btn_web.setIcon(QIcon(pix_web))
- btn_web.setIconSize(pix_web.size())
- else:
- btn_web.setText("Web")
- btn_web.setStyleSheet(button_style)
- btn_web.clicked.connect(self.switch_to_web)
- hb.addWidget(btn_web, alignment=Qt.AlignmentFlag.AlignCenter)
-
- btn_bot = QPushButton()
- pix_bot = QPixmap("bot.png")
- if not pix_bot.isNull():
- pix_bot = pix_bot.scaled(60, 60, Qt.AspectRatioMode.KeepAspectRatio)
- btn_bot.setIcon(QIcon(pix_bot))
- btn_bot.setIconSize(pix_bot.size())
- else:
- btn_bot.setText("Bot")
- btn_bot.setStyleSheet(button_style)
- btn_bot.clicked.connect(self.switch_to_bot)
- hb.addWidget(btn_bot, alignment=Qt.AlignmentFlag.AlignCenter)
-
- layout.addWidget(bottom)
-
-
-# LoginWindow with full UI & login logic
-class LoginWindow(GradientWidget):
- API_URL = "https://pds.iotsignin.com/api/testing/user/get-data"
- AUTH_TOKEN = "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE="
-
- def __init__(self, stack):
- super().__init__()
- self.stack = stack
- self._init_ui()
-
- def _init_ui(self):
- layout = QVBoxLayout(self)
- layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.setSpacing(40)
-
- logo = QLabel()
- pix = QPixmap("cri_logo.png.png")
- if not pix.isNull():
- pix = pix.scaled(150, 150, Qt.AspectRatioMode.KeepAspectRatio)
- logo.setPixmap(pix)
- else:
- logo.setText("CRI Logo")
- logo.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
- layout.addWidget(logo, alignment=Qt.AlignmentFlag.AlignCenter)
-
- self.email_input = QLineEdit()
- self.email_input.setPlaceholderText("USER NAME")
- self.email_input.setFixedWidth(400)
- self.email_input.setStyleSheet("""
- background: rgba(255,255,255,0.7);
- border: 2px solid #F3C2C2;
- border-radius: 10px;
- padding: 12px;
- font-size: 20px;
- color: #333;
- """)
- layout.addWidget(self.email_input, alignment=Qt.AlignmentFlag.AlignCenter)
-
- self.password_input = QLineEdit()
- self.password_input.setPlaceholderText("PASSWORD")
- self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
- self.password_input.setFixedWidth(400)
- self.password_input.setStyleSheet("""
- background: rgba(255,255,255,0.7);
- border: 2px solid #F3C2C2;
- border-radius: 10px;
- padding: 12px;
- font-size: 20px;
- color: #333;
- """)
- layout.addWidget(self.password_input, alignment=Qt.AlignmentFlag.AlignCenter)
-
- eye_action = QAction(QIcon("eye_closed.png"), "", self.password_input)
- eye_action.setCheckable(True)
- self.password_input.addAction(eye_action, QLineEdit.ActionPosition.TrailingPosition)
-
- def toggle_password_visibility(checked):
- self.password_input.setEchoMode(
- QLineEdit.EchoMode.Normal if checked else QLineEdit.EchoMode.Password
- )
- eye_action.setIcon(QIcon("eye.png") if checked else QIcon("eye_closed.png"))
-
- eye_action.toggled.connect(toggle_password_visibility)
-
- self.login_btn = QPushButton("LOGIN")
- self.login_btn.setFixedWidth(300)
- self.login_btn.setStyleSheet("""
- QPushButton {
- font-size: 22px;
- font-weight: bold;
- color: white;
- background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
- stop:0 #C71585, stop:1 #DB7093);
- border: none;
- border-radius: 14px;
- padding: 16px;
- }
- QPushButton:hover {
- background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
- stop:0 #D87093, stop:1 #FF69B4);
- }
- """)
- layout.addWidget(self.login_btn, alignment=Qt.AlignmentFlag.AlignCenter)
-
- self.email_input.returnPressed.connect(self.password_input.setFocus)
- self.password_input.returnPressed.connect(self.login_btn.setFocus)
- self.login_btn.clicked.connect(self.perform_login)
-
- def perform_login(self):
- email_input = self.email_input.text().strip()
- password = self.password_input.text().strip()
- if not email_input or not password:
- QMessageBox.warning(self, "Error", "Please enter email and password")
- return
-
- headers = {
- "Authorization": self.AUTH_TOKEN,
- "User-Name": email_input,
- "User-Pass": password
- }
-
- try:
- resp = requests.get(self.API_URL, headers=headers, timeout=10)
- resp.raise_for_status()
- data = resp.json()
- except requests.exceptions.RequestException as e:
- QMessageBox.critical(self, "Network Error", f"Failed to connect:\n{e}")
- return
- except ValueError as e:
- QMessageBox.critical(self, "Response Error", f"Invalid response:\n{e}")
- return
-
- if data.get("status_code") == "ERROR":
- desc = data.get("status_description", "Login failed")
- QMessageBox.warning(self, "Login Failed", desc)
- else:
- user_email = data.get("email", email_input)
- QMessageBox.information(self, "Success", "Login successful!")
- selector = self.stack.widget(1)
- selector.email = user_email
- selector.password = password
- self.stack.setCurrentIndex(1)
-
-
-if __name__ == "__main__":
- app = QApplication(sys.argv)
- stack = QStackedWidget()
-
- # Navigation helper - remove extra widgets beyond base screens
- def remove_extras_and_go(index):
- while stack.count() > 2:
- widget = stack.widget(2)
- stack.removeWidget(widget)
- widget.deleteLater()
- stack.setCurrentIndex(index)
-
- def show_login():
- remove_extras_and_go(0)
-
- def show_selector():
- remove_extras_and_go(1)
-
- def show_web():
- selector = stack.widget(1)
- if hasattr(selector, "email") and selector.email:
- remove_extras_and_go(1)
- web_screen = WebAssistantScreen(
- email=selector.email, password=selector.password, go_back=show_selector
- )
- stack.addWidget(web_screen)
- stack.setCurrentIndex(stack.count() - 1)
-
- def show_bot_selector():
- remove_extras_and_go(1)
- bot_selector = BotSelectorScreen(navigate_to_dashboard)
- stack.addWidget(bot_selector)
- stack.setCurrentIndex(stack.count() - 1)
-
- def navigate_to_dashboard(dashboard_type):
- remove_extras_and_go(1)
- if dashboard_type == "Production":
- screen = ProductionBotScreen(back_action=show_bot_selector)
- elif dashboard_type == "Invoice":
- screen = InvoiceBotScreen(back_action=show_bot_selector)
- else:
- return
- stack.addWidget(screen)
- stack.setCurrentIndex(stack.count() - 1)
-
- login_screen = LoginWindow(stack)
- selector_screen = SelectorScreen(show_login, show_web, show_bot_selector)
-
- stack.addWidget(login_screen) # Index 0
- stack.addWidget(selector_screen) # Index 1
-
- stack.setWindowTitle("Production Display")
- stack.setCurrentIndex(0)
- stack.showMaximized()
- sys.exit(app.exec())
diff --git a/rppds.py b/rppds.py
new file mode 100644
index 0000000..1fa2eed
--- /dev/null
+++ b/rppds.py
@@ -0,0 +1,484 @@
+#!/usr/bin/env python3
+# main.py
+import sys
+import os
+import json
+import threading
+import queue
+import subprocess
+from pathlib import Path
+
+from PyQt5.QtWidgets import (
+ QApplication, QWidget, QStackedWidget, QVBoxLayout, QPushButton, QLabel,
+ QLineEdit, QTextEdit, QHBoxLayout, QFileDialog, QMessageBox
+)
+from PyQt5.QtCore import Qt, pyqtSignal, QObject, QThread
+from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEngineDownloadItem
+
+# VOSK imports (make sure vosk and sounddevice installed)
+try:
+ from vosk import Model, KaldiRecognizer
+ import sounddevice as sd
+except Exception as e:
+ Model = None
+ KaldiRecognizer = None
+ sd = None
+ print("Vosk/sounddevice not available:", e)
+
+# ---------- Helpers & Workers ----------
+
+class WorkerSignals(QObject):
+ finished = pyqtSignal()
+ error = pyqtSignal(str)
+ result = pyqtSignal(object)
+ progress = pyqtSignal(int)
+
+class GenericWorker(QThread):
+ """Simple generic worker that runs a function in a QThread."""
+ result_ready = pyqtSignal(object)
+ error = pyqtSignal(str)
+
+ def __init__(self, fn, *args, **kwargs):
+ super().__init__()
+ self.fn = fn
+ self.args = args
+ self.kwargs = kwargs
+
+ def run(self):
+ try:
+ res = self.fn(*self.args, **self.kwargs)
+ self.result_ready.emit(res)
+ except Exception as e:
+ self.error.emit(str(e))
+
+# ---------- Vosk Voice Worker ----------
+class VoskWorker(QThread):
+ text_ready = pyqtSignal(str)
+ error = pyqtSignal(str)
+
+ def __init__(self, model_path, device=None, samplerate=16000, parent=None):
+ super().__init__(parent)
+ self.model_path = model_path
+ self.device = device
+ self.samplerate = samplerate
+ self._running = True
+
+ def stop(self):
+ self._running = False
+
+ def run(self):
+ if Model is None or sd is None:
+ self.error.emit("Vosk or sounddevice not installed")
+ return
+ if not Path(self.model_path).exists():
+ self.error.emit(f"Vosk model not found at {self.model_path}")
+ return
+
+ model = Model(self.model_path)
+ rec = KaldiRecognizer(model, self.samplerate)
+ try:
+ with sd.RawInputStream(samplerate=self.samplerate, blocksize=8000, dtype='int16',
+ channels=1, device=self.device) as stream:
+ while self._running:
+ data = stream.read(4000)[0]
+ if rec.AcceptWaveform(data):
+ text = rec.Result()
+ parsed = json.loads(text).get("text", "")
+ if parsed:
+ self.text_ready.emit(parsed)
+ else:
+ # partial = rec.PartialResult()
+ pass
+ except Exception as e:
+ self.error.emit(str(e))
+
+# ---------- TTS helper ----------
+def speak_text(text):
+ # Try pyttsx3 first (if installed); else fallback to espeak
+ try:
+ import pyttsx3
+ engine = pyttsx3.init()
+ engine.say(text)
+ engine.runAndWait()
+ except Exception:
+ # fallback: use espeak command-line
+ try:
+ subprocess.run(['espeak', text], check=False)
+ except Exception as e:
+ print("TTS failed:", e)
+
+# ---------- Screens ----------
+class LoginWindow(GradientWidget):
+ API_URL = "https://pds1.iotsignin.com/api/testing/user/get-data"
+ AUTH_TOKEN = "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE="
+
+ def __init__(self, stack):
+ super().__init__()
+ self.stack = stack
+ self._init_ui()
+
+ def _init_ui(self):
+ layout = QVBoxLayout(self)
+ layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ layout.setSpacing(40)
+
+ logo = QLabel()
+ pix = QPixmap("cri_logo.png.png")
+ if not pix.isNull():
+ pix = pix.scaled(150, 150, Qt.AspectRatioMode.KeepAspectRatio)
+ logo.setPixmap(pix)
+ else:
+ logo.setText("CRI Logo")
+ logo.setStyleSheet("font-size: 24px; font-weight: bold; color: #333;")
+ layout.addWidget(logo, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.email_input = QLineEdit()
+ self.email_input.setPlaceholderText("USER NAME")
+ self.email_input.setFixedWidth(400)
+ self.email_input.setStyleSheet("""
+ background: rgba(255,255,255,0.7);
+ border: 2px solid #F3C2C2;
+ border-radius: 10px;
+ padding: 12px;
+ font-size: 20px;
+ color: #333;
+ """)
+ layout.addWidget(self.email_input, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.password_input = QLineEdit()
+ self.password_input.setPlaceholderText("PASSWORD")
+ self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
+ self.password_input.setFixedWidth(400)
+ self.password_input.setStyleSheet("""
+ background: rgba(255,255,255,0.7);
+ border: 2px solid #F3C2C2;
+ border-radius: 10px;
+ padding: 12px;
+ font-size: 20px;
+ color: #333;
+ """)
+ layout.addWidget(self.password_input, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ eye_action = QAction(QIcon("eye_closed.png"), "", self.password_input)
+ eye_action.setCheckable(True)
+ self.password_input.addAction(eye_action, QLineEdit.ActionPosition.TrailingPosition)
+
+ def toggle_password_visibility(checked):
+ self.password_input.setEchoMode(
+ QLineEdit.EchoMode.Normal if checked else QLineEdit.EchoMode.Password
+ )
+ eye_action.setIcon(QIcon("eye.png") if checked else QIcon("eye_closed.png"))
+
+ eye_action.toggled.connect(toggle_password_visibility)
+
+ self.login_btn = QPushButton("LOGIN")
+ self.login_btn.setFixedWidth(300)
+ self.login_btn.setStyleSheet("""
+ QPushButton {
+ font-size: 22px;
+ font-weight: bold;
+ color: white;
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+ stop:0 #C71585, stop:1 #DB7093);
+ border: none;
+ border-radius: 14px;
+ padding: 16px;
+ }
+ QPushButton:hover {
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
+ stop:0 #D87093, stop:1 #FF69B4);
+ }
+ """)
+ layout.addWidget(self.login_btn, alignment=Qt.AlignmentFlag.AlignCenter)
+
+ self.email_input.returnPressed.connect(self.password_input.setFocus)
+ self.password_input.returnPressed.connect(self.login_btn.setFocus)
+ self.login_btn.clicked.connect(self.perform_login)
+
+ def perform_login(self):
+ email_input = self.email_input.text().strip()
+ password = self.password_input.text().strip()
+ if not email_input or not password:
+ QMessageBox.warning(self, "Error", "Please enter email and password")
+ return
+
+ headers = {
+ "Authorization": self.AUTH_TOKEN,
+ "User-Name": email_input,
+ "User-Pass": password
+ }
+
+ try:
+ resp = requests.get(self.API_URL, headers=headers, timeout=10)
+ resp.raise_for_status()
+ data = resp.json()
+ except requests.exceptions.RequestException as e:
+ QMessageBox.critical(self, "Network Error", f"Failed to connect:\n{e}")
+ return
+ except ValueError as e:
+ QMessageBox.critical(self, "Response Error", f"Invalid response:\n{e}")
+ return
+
+ if data.get("status_code") == "ERROR":
+ desc = data.get("status_description", "Login failed")
+ QMessageBox.warning(self, "Login Failed", desc)
+ else:
+ user_email = data.get("email", email_input)
+ QMessageBox.information(self, "Success", "Login successful!")
+ selector = self.stack.widget(1)
+ selector.email = user_email
+ selector.password = password
+ self.stack.setCurrentIndex(1)
+
+class SelectorScreen(QWidget):
+ select_bot = pyqtSignal(str)
+
+ def __init__(self):
+ super().__init__()
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout()
+ layout.addWidget(QLabel("Selector Screen - choose action"))
+ btn_web = QPushButton("Open WebAssistant")
+ btn_prod = QPushButton("Production Bot")
+ btn_invoice = QPushButton("Invoice Bot")
+ btn_web.clicked.connect(lambda: self.select_bot.emit("webassistant"))
+ btn_prod.clicked.connect(lambda: self.select_bot.emit("production"))
+ btn_invoice.clicked.connect(lambda: self.select_bot.emit("invoice"))
+ layout.addWidget(btn_web)
+ layout.addWidget(btn_prod)
+ layout.addWidget(btn_invoice)
+ self.setLayout(layout)
+
+class WebAssistantScreen(QWidget):
+ # navigation signals
+ go_back = pyqtSignal()
+
+ def __init__(self, download_folder=None, model_path=None):
+ super().__init__()
+ self.download_folder = download_folder or str(Path.home() / "Downloads")
+ self.model_path = model_path
+ self.vosk_worker = None
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout()
+ header = QHBoxLayout()
+ back_btn = QPushButton("Back")
+ back_btn.clicked.connect(lambda: self.go_back.emit())
+ self.url_edit = QLineEdit("https://www.example.com")
+ go_btn = QPushButton("Go")
+ go_btn.clicked.connect(self._load_url)
+ header.addWidget(back_btn)
+ header.addWidget(self.url_edit)
+ header.addWidget(go_btn)
+ layout.addLayout(header)
+
+ # Web view
+ self.webview = QWebEngineView()
+ profile = QWebEngineProfile.defaultProfile()
+ profile.downloadRequested.connect(self._on_download_requested)
+ self.webview.setUrl(self.url_edit.text())
+ layout.addWidget(self.webview)
+
+ # Voice area
+ vbox = QHBoxLayout()
+ self.voice_out = QPushButton("Speak Selected Text")
+ self.voice_out.clicked.connect(self._speak_selected)
+ self.voice_in = QPushButton("Start Voice Input")
+ self.voice_in.setCheckable(True)
+ self.voice_in.clicked.connect(self._toggle_voice)
+ vbox.addWidget(self.voice_in)
+ vbox.addWidget(self.voice_out)
+ layout.addLayout(vbox)
+
+ # log
+ self.log = QTextEdit()
+ self.log.setReadOnly(True)
+ layout.addWidget(self.log)
+
+ self.setLayout(layout)
+
+ def _load_url(self):
+ url = self.url_edit.text().strip()
+ if not url.startswith("http"):
+ url = "http://" + url
+ self.webview.setUrl(url)
+
+ def _on_download_requested(self, download_item: QWebEngineDownloadItem):
+ # This handles downloads initiated by the web page
+ suggested = download_item.downloadFileName()
+ dest, _ = QFileDialog.getSaveFileName(self, "Save File", str(Path(self.download_folder) / suggested))
+ if not dest:
+ download_item.cancel()
+ return
+ download_item.setPath(dest)
+ download_item.accept()
+ download_item.finished.connect(lambda: self.log.append(f"Downloaded: {dest}"))
+
+ def _speak_selected(self):
+ # get selected text from webview (async)
+ def got_selection(text):
+ if not text:
+ self.log.append("No text selected")
+ return
+ self.log.append("TTS: " + text)
+ threading.Thread(target=speak_text, args=(text,), daemon=True).start()
+ # run JS to get selection
+ self.webview.page().runJavaScript("window.getSelection().toString();", got_selection)
+
+ def _toggle_voice(self, checked):
+ if checked:
+ self.voice_in.setText("Stop Voice Input")
+ self.log.append("Starting voice input...")
+ self.vosk_worker = VoskWorker(self.model_path or "models/vosk-model-small-en-us-0.15")
+ self.vosk_worker.text_ready.connect(self._on_voice_text)
+ self.vosk_worker.error.connect(self._on_voice_error)
+ self.vosk_worker.start()
+ else:
+ self.voice_in.setText("Start Voice Input")
+ if self.vosk_worker:
+ self.vosk_worker.stop()
+ self.vosk_worker = None
+ self.log.append("Stopped voice input")
+
+ def _on_voice_text(self, text):
+ self.log.append(f"Voice: {text}")
+ # You can send the recognized text to the web page or your bot API
+ # Example: inject into active text field
+ js = f"""
+ (function() {{
+ var el = document.activeElement;
+ if (el && ('value' in el)) {{
+ el.value = el.value + " {text}";
+ }} else {{
+ console.log("No active element");
+ }}
+ }})();
+ """
+ self.webview.page().runJavaScript(js)
+
+ def _on_voice_error(self, err):
+ self.log.append("Voice error: " + str(err))
+
+class BotBaseScreen(QWidget):
+ go_back = pyqtSignal()
+
+ def __init__(self, bot_name="bot"):
+ super().__init__()
+ self.bot_name = bot_name
+ self._build_ui()
+
+ def _build_ui(self):
+ layout = QVBoxLayout()
+ header = QHBoxLayout()
+ back_btn = QPushButton("Back")
+ back_btn.clicked.connect(lambda: self.go_back.emit())
+ header.addWidget(QLabel(f"{self.bot_name.capitalize()}"))
+ header.addWidget(back_btn)
+ layout.addLayout(header)
+
+ self.log = QTextEdit()
+ self.log.setReadOnly(True)
+ layout.addWidget(self.log)
+
+ # Example controls for sending API calls
+ self.send_btn = QPushButton("Run Bot Task (API call)")
+ self.send_btn.clicked.connect(self._run_task)
+ layout.addWidget(self.send_btn)
+
+ self.setLayout(layout)
+
+ def _run_task(self):
+ self.log.append(f"Starting {self.bot_name} job...")
+ # placeholder for heavy work - use GenericWorker to avoid blocking UI
+ def fake_job():
+ import time
+ time.sleep(2)
+ return {"status": "ok", "bot": self.bot_name}
+ worker = GenericWorker(fake_job)
+ worker.result_ready.connect(lambda r: self.log.append(str(r)))
+ worker.error.connect(lambda e: self.log.append("Error: " + e))
+ worker.start()
+
+class ProductionBotScreen(BotBaseScreen):
+ def __init__(self):
+ super().__init__(bot_name="production")
+
+class InvoiceBotScreen(BotBaseScreen):
+ def __init__(self):
+ super().__init__(bot_name="invoice")
+
+# ---------- Application Controller ----------
+class AppController(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Pi Assistant App")
+ self.resize(1024, 720)
+ layout = QVBoxLayout()
+ self.stack = QStackedWidget()
+ layout.addWidget(self.stack)
+ self.setLayout(layout)
+
+ # screens
+ self.login = LoginScreen()
+ self.selector = SelectorScreen()
+ self.webassistant = WebAssistantScreen(model_path="models/vosk-model-small-en-us-0.15")
+ self.production = ProductionBotScreen()
+ self.invoice = InvoiceBotScreen()
+
+ # add to stack
+ self.stack.addWidget(self.login) # idx 0
+ self.stack.addWidget(self.selector) # idx 1
+ self.stack.addWidget(self.webassistant) # idx 2
+ self.stack.addWidget(self.production) # idx 3
+ self.stack.addWidget(self.invoice) # idx 4
+
+ # wire signals
+ self.login.login_success.connect(self.on_login)
+ self.selector.select_bot.connect(self.on_select)
+ self.webassistant.go_back.connect(self.show_selector)
+ self.production.go_back.connect(self.show_selector)
+ self.invoice.go_back.connect(self.show_selector)
+
+ self.show_login()
+
+ def show_login(self):
+ self.stack.setCurrentWidget(self.login)
+
+ def show_selector(self):
+ self.stack.setCurrentWidget(self.selector)
+
+ def show_webassistant(self):
+ self.stack.setCurrentWidget(self.webassistant)
+
+ def show_production(self):
+ self.stack.setCurrentWidget(self.production)
+
+ def show_invoice(self):
+ self.stack.setCurrentWidget(self.invoice)
+
+ def on_login(self, user_info):
+ # Called after login: you can check roles and direct user
+ print("Logged in:", user_info)
+ self.show_selector()
+
+ def on_select(self, name):
+ if name == "webassistant":
+ self.show_webassistant()
+ elif name == "production":
+ self.show_production()
+ elif name == "invoice":
+ self.show_invoice()
+
+# ---------- main ----------
+def main():
+ app = QApplication(sys.argv)
+ # ensure WebEngine resources are loaded properly
+ controller = AppController()
+ controller.show()
+ sys.exit(app.exec_())
+
+if __name__ == "__main__":
+ main()
diff --git a/sample.py b/sample.py
deleted file mode 100644
index e69de29..0000000
diff --git a/sfgprintingnew.py b/sfgprintingnew.py
new file mode 100644
index 0000000..2185c6a
--- /dev/null
+++ b/sfgprintingnew.py
@@ -0,0 +1,1217 @@
+import sys
+import sqlite3
+import os
+from PyQt5.QtWidgets import (
+ QApplication, QWidget, QLabel, QLineEdit, QPushButton,
+ QVBoxLayout, QHBoxLayout, QMessageBox, QStackedWidget, QTableWidget, QHeaderView, QTableWidgetItem, QTextEdit,
+ QAbstractScrollArea, QScrollArea, QSizePolicy,QFormLayout
+)
+from PyQt5.QtGui import QFont, QColor, QPalette, QPixmap, QBrush
+from PyQt5.QtCore import Qt
+from PyQt5.QtCore import QEvent
+from db_setup import init_db, get_connection
+
+# Initialize DB once (creates tables if missing)
+init_db()
+
+# Global references
+global_stacked_widget = None
+global_main_screen = None
+keyboard_window = None
+
+def insert_text(widget, text):
+ if isinstance(widget, (QLineEdit, QTextEdit)):
+ widget.insert(text)
+
+def backspace_text(widget):
+ if isinstance(widget, QLineEdit):
+ current_text = widget.text()
+ widget.setText(current_text[:-1])
+ elif isinstance(widget, QTextEdit):
+ cursor = widget.textCursor()
+ cursor.deletePreviousChar()
+ widget.setTextCursor(cursor)
+
+def clear_text(widget):
+ if isinstance(widget, QLineEdit):
+ widget.clear()
+ elif isinstance(widget, QTextEdit):
+ widget.clear()
+
+def show_keyboard():
+ global keyboard_window
+
+ # Close any existing keyboard window
+ if keyboard_window is not None:
+ keyboard_window.close()
+
+ keyboard_window = QWidget()
+ keyboard_window.setWindowTitle("Numeric Keyboard")
+ keyboard_window.setFixedSize(270, 360)
+ layout = QVBoxLayout()
+
+ # Button layout
+ buttons = [
+ ["1", "2", "3"],
+ ["4", "5", "6"],
+ ["7", "8", "9"],
+ ["0"],
+ ["Back", "Clear"]
+ ]
+
+ for row in buttons:
+ row_layout = QHBoxLayout()
+ for key in row:
+ btn = QPushButton(key)
+ btn.setFixedSize(80, 60)
+ btn.setFont(QFont("Arial", 14))
+
+ # Connect button actions
+ if key == "Back":
+ btn.clicked.connect(lambda _, w=QApplication.focusWidget(): backspace_text(w))
+ elif key == "Clear":
+ btn.clicked.connect(lambda _, w=QApplication.focusWidget(): clear_text(w))
+ else:
+ btn.clicked.connect(lambda _, k=key, w=QApplication.focusWidget(): insert_text(w, k))
+
+ row_layout.addWidget(btn)
+ layout.addLayout(row_layout)
+
+ keyboard_window.setLayout(layout)
+ keyboard_window.setWindowModality(Qt.NonModal)
+ keyboard_window.setWindowFlag(Qt.WindowStaysOnTopHint)
+ keyboard_window.show()
+
+
+# Navigation functions
+def go_home():
+ if global_stacked_widget and global_stacked_widget.currentIndex() != 1:
+ global_stacked_widget.setCurrentIndex(1)
+
+
+def refresh_screen():
+ if global_stacked_widget:
+ current_index = global_stacked_widget.currentIndex()
+ current_widget = global_stacked_widget.currentWidget()
+ current_class = type(current_widget)
+
+ new_widget = current_class() # Recreate the same screen
+ global_stacked_widget.removeWidget(current_widget)
+ global_stacked_widget.insertWidget(current_index, new_widget)
+ global_stacked_widget.setCurrentIndex(current_index)
+
+ # Optional: delete the old widget to free memory
+ current_widget.deleteLater()
+
+def go_login():
+ if global_stacked_widget:
+ global_stacked_widget.setCurrentIndex(0)
+
+def open_operator_identity():
+ global global_stacked_widget
+ operator_screen = OperatorIdentityScreen()
+ global_stacked_widget.addWidget(operator_screen)
+ global_stacked_widget.setCurrentWidget(operator_screen)
+
+def open_checklist_screen(self):
+ global global_stacked_widget
+ checklist_screen = ChecklistScreen()
+ global_stacked_widget.addWidget(checklist_screen)
+ global_stacked_widget.setCurrentWidget(checklist_screen)
+
+def go_first_off():
+ global global_stacked_widget
+ first_off_screen = FirstOff()
+ global_stacked_widget.addWidget(first_off_screen)
+ global_stacked_widget.setCurrentWidget(first_off_screen)
+
+
+def go_final_validation():
+ global global_stacked_widget
+ final_validation_screen = FinalValidation()
+ global_stacked_widget.addWidget(final_validation_screen)
+ global_stacked_widget.setCurrentWidget(final_validation_screen)
+
+# ---------------- Login Screen ---------------- #
+class LoginScreen(QWidget):
+ def __init__(self, stacked_widget):
+ super().__init__()
+ self.stacked_widget = stacked_widget
+
+ self.init_ui()
+ def init_ui(self):
+ self.setWindowTitle("Login Screen")
+ palette = QPalette()
+ palette.setColor(QPalette.Window, QColor("#E6F7FF"))
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+
+ title = QLabel("WELCOME TO CRI PUMPS")
+ title.setFont(QFont("Times New Roman", 28, QFont.Bold))
+ title.setStyleSheet("color: darkblue;")
+ title.setAlignment(Qt.AlignCenter)
+
+ logo = QLabel()
+ logo.setAlignment(Qt.AlignCenter)
+ logo_path = "/home/cri/myproject/venv/cri_logo_red.png"
+ if os.path.exists(logo_path):
+ pixmap = QPixmap(logo_path)
+ if not pixmap.isNull():
+ logo.setPixmap(pixmap.scaledToWidth(150, Qt.SmoothTransformation))
+ else:
+ logo.setText("Failed to load image.")
+ else:
+ logo.setText("Logo not found.")
+
+ self.username_label = QLabel("Username:")
+ self.username_label.setFont(QFont("Times New Roman", 14, QFont.Bold))
+ self.username_edit = QLineEdit()
+ self.username_edit.setFont(QFont("Times New Roman", 12))
+ self.username_edit.setFixedHeight(40)
+ self.username_edit.setFixedWidth(300)
+ self.username_edit.setPlaceholderText("Enter username")
+ self.username_edit.returnPressed.connect(self.focus_password)
+
+ user_row = QHBoxLayout()
+ user_row.addStretch()
+ user_row.addWidget(self.username_label)
+ user_row.addWidget(self.username_edit)
+ user_row.addStretch()
+
+ self.password_label = QLabel("Password:")
+ self.password_label.setFont(QFont("Times New Roman", 14, QFont.Bold))
+ self.password_edit = QLineEdit()
+ self.password_edit.setFont(QFont("Times New Roman", 12))
+ self.password_edit.setFixedHeight(40)
+ self.password_edit.setFixedWidth(300)
+ self.password_edit.setEchoMode(QLineEdit.Password)
+ self.password_edit.setPlaceholderText("Enter password")
+ self.password_edit.returnPressed.connect(self.login)
+
+ self.toggle_button = QPushButton("👁")
+ self.toggle_button.setFixedWidth(40)
+ self.toggle_button.setCheckable(True)
+ self.toggle_button.toggled.connect(self.toggle_password)
+
+ pass_row = QHBoxLayout()
+ pass_row.addStretch()
+ pass_row.addWidget(self.password_label)
+ pass_layout = QHBoxLayout()
+ pass_layout.addWidget(self.password_edit)
+ pass_layout.addWidget(self.toggle_button)
+ pass_row.addLayout(pass_layout)
+ pass_row.addStretch()
+
+ self.login_button = QPushButton("LOGIN")
+ self.login_button.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ self.login_button.setFixedWidth(200)
+ self.login_button.setStyleSheet("""
+ QPushButton {
+ background-color: darkblue;
+ color: white;
+ padding: 10px;
+ border-radius: 6px;
+ }
+""")
+ self.login_button.clicked.connect(self.login)
+
+ login_btn_row = QHBoxLayout()
+ login_btn_row.addStretch()
+ login_btn_row.addWidget(self.login_button)
+ login_btn_row.addStretch()
+
+# Second logo below login button
+ footer_logo = QLabel()
+ footer_logo.setAlignment(Qt.AlignCenter)
+ footer_logo_path = "/home/cri/myproject/venv/cri_farm_banner.jpg"
+ if os.path.exists(footer_logo_path):
+ pixmap2 = QPixmap(footer_logo_path)
+ if not pixmap2.isNull():
+ footer_logo.setPixmap(pixmap2.scaledToWidth(500, Qt.SmoothTransformation))
+ else:
+ footer_logo.setText("Failed to load image.")
+ else:
+ footer_logo.setText("Footer logo not found.")
+
+ # Main layout
+ layout = QVBoxLayout()
+ layout.addSpacing(50)
+ layout.addWidget(title)
+ layout.addSpacing(30)
+ layout.addWidget(logo) # Top logo
+ layout.addSpacing(40)
+ layout.addLayout(user_row)
+ layout.addSpacing(20)
+ layout.addLayout(pass_row)
+ layout.addSpacing(40)
+ layout.addLayout(login_btn_row)
+ layout.addSpacing(30)
+ layout.addWidget(footer_logo) # New footer logo
+ layout.addStretch()
+
+ self.setLayout(layout)
+
+ def toggle_password(self, checked):
+ self.password_edit.setEchoMode(QLineEdit.Normal if checked else QLineEdit.Password)
+
+ def focus_password(self):
+ self.password_edit.setFocus()
+
+ def login(self):
+ username = self.username_edit.text()
+ password = self.password_edit.text()
+
+ if not username or not password:
+ QMessageBox.warning(self, "Error", "Enter both username and password.")
+ return
+
+ # ✅ Use the same connection from db_setup.py
+ conn = get_connection()
+ cursor = conn.cursor()
+ cursor.execute(
+ "SELECT * FROM user_details WHERE user_name=? AND user_pass=?",
+ (username, password)
+ )
+ result = cursor.fetchone()
+ conn.close()
+
+ if result:
+ self.stacked_widget.setCurrentIndex(1)
+ else:
+ QMessageBox.warning(self, "Error", "Invalid credentials.")
+
+# ---------------- Main Screen ---------------- #
+
+class MainScreen(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Production Display")
+ self.showFullScreen()
+
+ # Set entire background to light blue
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#ADD8E6"))
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+
+ # Initialize widgets
+ self.table = QTableWidget(5, 3) # Example table
+ self.operator_identity_btn = QPushButton("Operator Identity")
+ self.btn_serial_print = QPushButton("Serial Print")
+ self.btn_part_validation = QPushButton("Part Validation")
+ self.btn_checklist = QPushButton("Checklist")
+ self.btn_couth_marking = QPushButton("Couth Marking")
+ self.prod_order_input = QLineEdit()
+ self.serial_input = QLineEdit()
+
+ self.init_ui()
+ # Helper for focusing next input
+ def make_focus_function(self, index):
+ def focus_next():
+ if index < len(self.input_fields):
+ self.input_fields[index].setFocus()
+ return focus_next
+
+ def init_ui(self):
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#E6F7FF"))
+ self.setPalette(palette)
+
+ # Title label
+ self.title_label = QLabel("WORK ORDER VALIDATION")
+ self.title_label.setFont(QFont("Times New Roman", 20, QFont.Bold))
+ self.title_label.setStyleSheet("color: white;")
+ self.title_label.setAlignment(Qt.AlignCenter)
+
+ # Logo
+ logo_label = QLabel()
+ pixmap = QPixmap("/home/cri/myproject/cri_logo_red.png")
+ pixmap = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ logo_label.setPixmap(pixmap)
+ logo_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+
+ # Title layout (logo + title text)
+ title_layout = QHBoxLayout()
+ title_layout.setContentsMargins(10, 5, 10, 5)
+ title_layout.addWidget(logo_label)
+ title_layout.addSpacing(20)
+ title_layout.addWidget(self.title_label, stretch=1)
+ title_layout.addStretch()
+
+ # Header container with background color
+ header_widget = QWidget()
+ header_widget.setLayout(title_layout)
+ header_widget.setStyleSheet("background-color: #003366;") # Dark blue background
+
+
+ # Disable widgets that should start inactive
+ self.table.setDisabled(True)
+ self.operator_identity_btn.setDisabled(True)
+ self.other_buttons = [
+ self.btn_serial_print, self.btn_part_validation,
+ self.btn_checklist, self.btn_couth_marking
+ ]
+ for btn in self.other_buttons:
+ btn.setDisabled(True)
+
+ self.prod_order_input.setEnabled(True)
+ self.serial_input.setEnabled(True)
+
+ self.prod_order_input.editingFinished.connect(lambda: print("Work order details fetch"))
+ self.serial_input.editingFinished.connect(lambda: print("Serial details fetch"))
+
+ # Input Fields
+ label_names = [
+ "Production Order Number", "Item Description", "Current Serial Number",
+ "Operator Number", "Completed Quantity", "Sub Assembly Serial Number",
+ "Sub Assembly Item Description", "Previous Serial Number",
+ "Total Quantity", "Remaining Quantity"
+ ]
+ font_label = QFont("Times New Roman", 14, QFont.Bold)
+ font_input = QFont("Times New Roman", 12)
+ #field_width, field_height = 350, 50
+
+ self.input_fields = []
+ left_layout, right_layout = QVBoxLayout(), QVBoxLayout()
+
+ for i, name in enumerate(label_names):
+ sub_layout = left_layout if i < 5 else right_layout
+ field_box = QVBoxLayout()
+
+ label = QLabel(name)
+ label.setFont(font_label)
+
+ input_box = QLineEdit()
+ input_box.setFont(font_input)
+ input_box.setMinimumHeight(45)
+ input_box.setStyleSheet("""
+ QLineEdit {
+ background-color: #ADD8E6;
+ padding: 6px;
+ border-radius: 6px;
+ }
+ """)
+ # ✅ Auto expand horizontally with screen
+ input_box.setSizePolicy(QPushButton().sizePolicy().Expanding,
+ QPushButton().sizePolicy().Fixed)
+
+ if i == 0:
+ toggle_button = QPushButton("Serial OFF")
+ toggle_button.setCheckable(True)
+ toggle_button.setFont(QFont("Times New Roman", 10, QFont.Bold))
+ toggle_button.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B;
+ color: white;
+ padding: 6px;
+ border-radius: 6px;
+ }
+ QPushButton:checked {
+ background-color: #FF6347;
+ color: white;
+ }
+ """)
+ toggle_button.clicked.connect(self.handle_serial_toggle)
+ self.serial_toggle_button = toggle_button
+
+ toggle_layout = QHBoxLayout()
+ toggle_layout.addWidget(input_box, stretch=3)
+ toggle_layout.addWidget(toggle_button, stretch=1)
+ field_box.addWidget(label)
+ field_box.addLayout(toggle_layout)
+ else:
+ if i < 9:
+ input_box.returnPressed.connect(self.make_focus_function(i + 1))
+ field_box.addWidget(label)
+ field_box.addWidget(input_box)
+
+ sub_layout.addLayout(field_box)
+ # ✅ Add spacing between each field block
+ sub_layout.addSpacing(15)
+ self.input_fields.append(input_box)
+
+ input_layout = QHBoxLayout()
+ input_layout.addStretch()
+ input_layout.addLayout(left_layout, stretch=1)
+ input_layout.addSpacing(60)
+ input_layout.addLayout(right_layout, stretch=1)
+ input_layout.addStretch()
+
+ # Right navigation buttons
+ right_buttons = QVBoxLayout()
+ button_actions = {
+ "Operator Mapping": open_operator_identity,
+ "Product Check List": open_checklist_screen,
+ "Process Check List": go_first_off,
+ "Part Validation": go_final_validation
+ }
+
+ for name, handler in button_actions.items():
+ btn = QPushButton(name)
+ btn.setFont(QFont("Times New Roman", 11, QFont.Bold))
+ btn.setFixedSize(180, 45)
+ btn.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B;
+ color: white;
+ padding: 6px;
+ border-radius: 6px;
+ }
+ QPushButton:hover {
+ background-color: #0000CD;
+ }
+ QPushButton:pressed {
+ background-color: #000066;
+ }
+ """)
+ btn.clicked.connect(handler)
+ right_buttons.addWidget(btn)
+ right_buttons.addSpacing(15)
+ right_buttons.addStretch()
+ # ✅ Extra OK / NOT OK buttons below right buttons
+ ok_btn = QPushButton("OK")
+ ok_btn.setStyleSheet("""
+ QPushButton {
+ background-color: darkgreen;
+ color: white;
+ border-radius: 8px;
+ padding: 5px 10px;
+ }
+ QPushButton:pressed {
+ background-color: green;
+ }
+ """)
+ ok_btn.setFont(QFont("Times New Roman", 9))
+ ok_btn.setFixedWidth(100)
+
+ not_ok_btn = QPushButton("NOT OK")
+ not_ok_btn.setStyleSheet("""
+ QPushButton {
+ background-color: darkred;
+ color: white;
+ border-radius: 8px;
+ padding: 5px 10px;
+ }
+ QPushButton:pressed {
+ background-color: red;
+ }
+ """)
+ not_ok_btn.setFont(QFont("Times New Roman", 9))
+ not_ok_btn.setFixedWidth(100)
+
+ # Place them in a horizontal layout
+ ok_notok_row = QHBoxLayout()
+ ok_notok_row.addStretch()
+ ok_notok_row.addWidget(ok_btn)
+ ok_notok_row.addSpacing(10)
+ ok_notok_row.addWidget(not_ok_btn)
+ ok_notok_row.addStretch()
+
+ # Add to right side buttons layout
+ right_buttons.addSpacing(20)
+ right_buttons.addLayout(ok_notok_row)
+
+ # Bottom navigation
+ bottom_buttons = QHBoxLayout()
+ btn_data = [
+ ("REFRESH", refresh_screen),
+ ("LOGIN", go_login)
+ ]
+ for txt, handler in btn_data:
+ btn = QPushButton(txt)
+ btn.setFont(QFont("Times New Roman", 11, QFont.Bold))
+ btn.setFixedSize(150, 45)
+ btn.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B;
+ color: white;
+ font-weight: bold;
+ border-radius: 6px;
+ }
+ QPushButton:pressed {
+ background-color: #000066;
+ }
+ """)
+ btn.clicked.connect(handler)
+ bottom_buttons.addStretch()
+ bottom_buttons.addWidget(btn)
+ bottom_buttons.addStretch()
+
+ # Final layout
+ content = QHBoxLayout()
+ content.addLayout(input_layout, stretch=3)
+ content.addLayout(right_buttons, stretch=1)
+
+ main_layout = QVBoxLayout()
+ main_layout.addWidget(header_widget)
+ main_layout.addSpacing(30)
+ main_layout.addLayout(content)
+ main_layout.addStretch()
+ main_layout.addLayout(bottom_buttons)
+
+ self.setLayout(main_layout)
+
+ def handle_serial_toggle(self):
+ if self.serial_toggle_button.isChecked():
+ self.serial_toggle_button.setText("Serial ON")
+ self.title_label.setText("SERIAL NUMBER VALIDATION")
+ else:
+ self.serial_toggle_button.setText("Serial OFF")
+ self.title_label.setText("WORK ORDER VALIDATION")
+
+
+# ------------------------------ Operator Identity Screen ------------------#
+
+class OperatorIdentityScreen(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Operator Identity")
+ self.showFullScreen()
+ self.inputs = []
+ # Set entire background to light blue
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#ADD8E6")) # Light Blue
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+ self.init_ui()
+ def init_ui(self):
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#E6F7FF"))
+ self.setPalette(palette)
+
+ # Header: Logo + Title
+ logo_label = QLabel()
+ pixmap = QPixmap("/home/cri/myproject/cri_logo_red.png")
+ pixmap = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ logo_label.setPixmap(pixmap)
+ logo_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+
+ title_label = QLabel("OPERATOR IDENTITY")
+ title_label.setFont(QFont("Times New Roman", 20, QFont.Bold))
+ title_label.setStyleSheet("color: white;")
+ title_label.setAlignment(Qt.AlignCenter) # ✅ Centered title
+
+ header_layout = QHBoxLayout()
+ header_layout.setContentsMargins(10, 10, 10, 10)
+ header_layout.addWidget(logo_label)
+ header_layout.addStretch()
+ header_layout.addWidget(title_label)
+ header_layout.addStretch()
+
+ header_widget = QWidget()
+ header_widget.setLayout(header_layout)
+ header_widget.setStyleSheet("background-color: #003366;") # Dark Blue background
+
+ # Input Fields
+ left_layout = QVBoxLayout()
+ right_layout = QVBoxLayout()
+
+ for i in range(10):
+ label = QLabel(f"Operator ID {i + 1}")
+ label.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ label.setStyleSheet("color: black;")
+
+ line_edit = QLineEdit()
+ line_edit.setFont(QFont("Times New Roman", 11))
+ line_edit.setFixedHeight(50)
+ line_edit.setFixedWidth(800)
+ line_edit.setStyleSheet("background-color: #ADD8E6;")
+ line_edit.textChanged.connect(self.check_fields)
+
+ if i < 9:
+ line_edit.returnPressed.connect(self.make_focus_function(i + 1))
+
+ self.inputs.append(line_edit)
+
+ clear_btn = QPushButton("CLEAR")
+ clear_btn.setFixedSize(70, 25)
+ clear_btn.setStyleSheet("""
+ QPushButton {
+ background-color: red;
+ color: white;
+ border: 1px solid black;
+ }
+ QPushButton:pressed {
+ background-color: darkred;
+ }
+""")
+ clear_btn.setAutoFillBackground(True)
+ clear_btn.clicked.connect(line_edit.clear)
+
+ entry_layout = QVBoxLayout()
+ entry_layout.addWidget(label)
+ entry_layout.addWidget(line_edit)
+ entry_layout.addWidget(clear_btn)
+
+ # Add space below each operator entry
+ entry_layout.addSpacing(20) # 👈 Equal space between entries
+ if i < 5:
+ left_layout.addLayout(entry_layout)
+ else:
+ right_layout.addLayout(entry_layout)
+
+ input_area = QHBoxLayout()
+ input_area.addLayout(left_layout)
+ input_area.addSpacing(40)
+ input_area.addLayout(right_layout)
+
+ # Submit Button
+ self.submit_btn = QPushButton("SUBMIT")
+ self.submit_btn.setFont(QFont("Times New Roman", 10, QFont.Bold))
+ self.submit_btn.setStyleSheet("""
+ QPushButton {
+ background-color: #008000; /* Dark green hex code */
+ color: white;
+ border: 1px solid black;
+ font-weight: bold;
+ padding: 8px;
+ }
+ QPushButton:pressed {
+ background-color: #006400; /* Darker green on press */
+ }
+""")
+ self.submit_btn.setAutoFillBackground(True)
+ self.submit_btn.setEnabled(False)
+ self.submit_btn.clicked.connect(self.submit_operator_ids)
+
+ # Bottom Navigation Buttons
+ bottom_buttons = QHBoxLayout()
+ button_data = [
+ ("HOME", "lightblue", go_home),
+ ("REFRESH", "lightblue", refresh_screen)
+ ]
+ for text, color, handler in button_data:
+ btn = QPushButton(text)
+ btn.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ btn.setFixedSize(150, 50)
+ btn.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B; /* Dark Blue hex code */
+ color: white;
+ border: 1px solid black;
+ font-weight: bold;
+ }
+ QPushButton:pressed {
+ background-color: #000066; /* Darker blue on press */
+ }
+""")
+ btn.setAutoFillBackground(True)
+ btn.clicked.connect(handler)
+ bottom_buttons.addWidget(btn)
+ self.submit_btn.setFixedSize(150, 50)
+ bottom_buttons.addWidget(self.submit_btn)
+
+ layout = QVBoxLayout()
+ layout.addWidget(header_widget)
+ layout.addSpacing(20)
+ layout.addLayout(input_area)
+ layout.addStretch()
+ layout.addSpacing(20)
+ layout.addLayout(bottom_buttons)
+ self.setLayout(layout)
+
+ def make_focus_function(self, index):
+ def focus_next():
+ if index < len(self.inputs):
+ self.inputs[index].setFocus()
+
+ return focus_next
+
+ def check_fields(self):
+ """Enable submit button if any field has text"""
+ self.submit_btn.setEnabled(any(inp.text().strip() for inp in self.inputs))
+
+ def submit_operator_ids(self):
+ operator_ids = [inp.text().strip() if inp.text().strip() else None for inp in self.inputs]
+ if not any(operator_ids):
+ QMessageBox.warning(self, "Input Error", "Please fill at least one Operator ID before submitting.")
+ return
+
+ try:
+ conn = sqlite3.connect("sfg printing.db")
+ cursor = conn.cursor()
+ cursor.execute('''
+ INSERT INTO operator_details (
+ operator1_id, operator2_id, operator3_id, operator4_id, operator5_id,
+ operator6_id, operator7_id, operator8_id, operator9_id, operator10_id
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ''', operator_ids)
+ conn.commit()
+ conn.close()
+ QMessageBox.information(self, "Success", "Operator IDs saved successfully.")
+ for inp in self.inputs:
+ inp.clear()
+ self.submit_btn.setEnabled(False)
+ except Exception as e:
+ QMessageBox.critical(self, "Database Error", f"Failed to insert data:\n{e}")
+
+
+# -----------------------------------Check List Screen---------------------------#
+
+class ChecklistScreen(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Checklist Screen")
+ self.resize(1000, 700)
+
+ # Set background
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#ADD8E6"))
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+
+ layout = QVBoxLayout(self)
+
+ # ---------------- Header ----------------
+ header_widget = QWidget()
+ header_widget.setStyleSheet("background-color: #003366;")
+ header_layout = QHBoxLayout(header_widget)
+ header_layout.setContentsMargins(10, 10, 10, 10)
+
+ logo_label = QLabel()
+ pixmap = QPixmap("/home/cri/myproject/cri_logo_red.png")
+ pixmap = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ logo_label.setPixmap(pixmap)
+ logo_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+
+ title_label = QLabel("PRODUCT CHECK LIST")
+ title_label.setFont(QFont("Times New Roman", 24, QFont.Bold))
+ title_label.setStyleSheet("color: white;")
+ title_label.setAlignment(Qt.AlignCenter)
+
+ header_layout.addWidget(logo_label)
+ header_layout.addSpacing(20)
+ header_layout.addWidget(title_label, stretch=1)
+ header_layout.addStretch()
+ layout.addWidget(header_widget)
+
+ # ---------------- Inputs ----------------
+ input_layout = QHBoxLayout()
+ self.work_order = self.create_labeled_input("Workorder Number")
+ self.serial_number = self.create_labeled_input("Serial Number")
+ self.operator_plant = self.create_labeled_input("Operator Number")
+
+ input_layout.addLayout(self.work_order['layout'])
+ input_layout.addLayout(self.serial_number['layout'])
+ input_layout.addLayout(self.operator_plant['layout'])
+ layout.addLayout(input_layout)
+
+ # ---------------- Table ----------------
+ self.table = QTableWidget(1, 3)
+ self.table.setHorizontalHeaderLabels(["Dimension Name", "Dimension Value", "Entered Value"])
+ self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+ self.table.verticalHeader().setVisible(False)
+ self.table.installEventFilter(self)
+
+ # Header style
+ self.table.setStyleSheet("""
+ QHeaderView::section {
+ background-color: #003366;
+ color: white;
+ font-weight: bold;
+ padding: 6px;
+ border: 1px solid #CCCCCC;
+ }
+ """)
+ self.apply_row_colors()
+ layout.addWidget(self.table)
+
+ # ---------------- Buttons ----------------
+ button_layout = QHBoxLayout()
+ button_layout.setSpacing(40)
+ button_layout.addStretch()
+
+ button_style = """
+ QPushButton {
+ background-color: #00008B; /* Dark Blue */
+ color: white; /* White text */
+ font-weight: bold;
+ border: 1px solid black;
+ }
+ QPushButton:pressed {
+ background-color: #000066; /* Darker blue on press */
+ }
+"""
+
+ submit_button = QPushButton("SUBMIT")
+ submit_button.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ submit_button.setFixedSize(150, 50)
+ submit_button.setStyleSheet(button_style)
+ submit_button.clicked.connect(self.submit_data)
+ button_layout.addWidget(submit_button)
+
+ back_button = QPushButton("BACK")
+ back_button.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ back_button.setFixedSize(150, 50)
+ back_button.setStyleSheet(button_style)
+ back_button.clicked.connect(go_home)
+ button_layout.addWidget(back_button)
+
+ button_layout.addStretch()
+ layout.addLayout(button_layout)
+
+ # ---------------- Helpers ----------------
+ def create_labeled_input(self, label_text):
+ layout = QVBoxLayout()
+ label = QLabel(label_text)
+ label.setFont(QFont("Times New Roman", 12))
+ line_edit = QLineEdit()
+ line_edit.setFixedWidth(200)
+ layout.addWidget(label)
+ layout.addWidget(line_edit)
+ return {"layout": layout, "edit": line_edit}
+
+ def add_row(self):
+ row = self.table.rowCount()
+ self.table.insertRow(row)
+ for col in range(self.table.columnCount()):
+ item = QTableWidgetItem("")
+ item.setForeground(QBrush(QColor("black")))
+ color = QColor("#d8f3dc") if row % 2 == 0 else QColor("#d0f0ff")
+ item.setBackground(QBrush(color))
+ self.table.setItem(row, col, item)
+
+ def apply_row_colors(self):
+ for row in range(self.table.rowCount()):
+ for col in range(self.table.columnCount()):
+ item = self.table.item(row, col)
+ if item:
+ color = QColor("#d8f3dc") if row % 2 == 0 else QColor("#d0f0ff")
+ item.setBackground(QBrush(color))
+
+ def eventFilter(self, source, event):
+ if source == self.table and event.type() == QEvent.KeyPress:
+ row = self.table.currentRow()
+ col = self.table.currentColumn()
+ if event.key() in (Qt.Key_Return, Qt.Key_Enter):
+ # Move to next column or add new row
+ if col < self.table.columnCount() - 1:
+ self.table.setCurrentCell(row, col + 1)
+ else:
+ self.add_row()
+ self.table.setCurrentCell(self.table.rowCount() - 1, 0)
+ return True
+ elif event.key() == Qt.Key_Right:
+ # Move right in same row
+ if col < self.table.columnCount() - 1:
+ self.table.setCurrentCell(row, col + 1)
+ return True
+ return super().eventFilter(source, event)
+
+ def submit_data(self):
+ data_rows = []
+ for row in range(self.table.rowCount()):
+ row_data = []
+ for col in range(self.table.columnCount()):
+ item = self.table.item(row, col)
+ row_data.append(item.text().strip() if item else "")
+ if any(row_data):
+ data_rows.append(row_data)
+
+ if not data_rows:
+ QMessageBox.warning(self, "Empty Table", "Please enter at least one row of dimension data.")
+ return
+
+ try:
+ with sqlite3.connect("/home/cri/myproject/sfgprinting.db") as conn:
+ cursor = conn.cursor()
+ for row in data_rows:
+ name = row[0] if len(row) > 0 else ""
+ value = row[1] if len(row) > 1 else ""
+ entervalue = row[2] if len(row) > 2 else ""
+ cursor.execute("""
+ INSERT INTO check_list_details (
+ Dimension_Name, Dimension_Value, Entered_Value
+ ) VALUES (?, ?, ?)
+ """, (name, value, entervalue))
+ conn.commit()
+
+ QMessageBox.information(self, "Success", "Checklist data saved successfully.")
+ self.clear_fields()
+
+ except sqlite3.Error as e:
+ QMessageBox.critical(self, "Database Error", f"SQLite error occurred:\n{e}")
+ except Exception as e:
+ QMessageBox.critical(self, "Error", f"Unexpected error occurred:\n{e}")
+
+ def clear_fields(self):
+ self.work_order['edit'].clear()
+ self.serial_number['edit'].clear()
+ self.operator_plant['edit'].clear()
+ self.table.setRowCount(0) # Clear all rows
+ def go_back(self):
+ self.stacked_widget.setCurrentIndex(3)
+
+# -------------------------first off screen---------------------------------------#
+class FirstOff(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("First Off")
+ self.resize(1400, 750)
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#ADD8E6"))
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+ main_layout = QVBoxLayout(self)
+
+ # Header
+ header_widget = QWidget()
+ header_widget.setStyleSheet("background-color: #003366;")
+ header_layout = QHBoxLayout(header_widget)
+ header_layout.setContentsMargins(10, 10, 10, 10)
+
+ logo_label = QLabel()
+ pixmap = QPixmap("/home/cri/myproject/cri_logo_red.png")
+ pixmap = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ logo_label.setPixmap(pixmap)
+ logo_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+
+ title_label = QLabel("PROCESS CHECK LIST")
+ title_label.setFont(QFont("Times New Roman", 24, QFont.Bold))
+ title_label.setStyleSheet("color: white;")
+ title_label.setAlignment(Qt.AlignCenter)
+
+ header_layout.addWidget(logo_label)
+ header_layout.addSpacing(20)
+ header_layout.addWidget(title_label, stretch=1)
+ header_layout.addStretch()
+
+ main_layout.addWidget(header_widget)
+
+ # Table
+ self.table = QTableWidget(0, 3)
+ self.table.setHorizontalHeaderLabels(["Dimension Name", "Dimension Value", "Enter Value"])
+ self.table.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+ self.table.setStyleSheet("""
+ QTableWidget {
+ background-color: white;
+ gridline-color: black;
+ }
+ QHeaderView::section {
+ background-color: #00008B;
+ color: white;
+ font-weight: bold;
+ padding: 4px;
+ border: 1px solid black;
+ }
+ """)
+
+ header = self.table.horizontalHeader()
+ header.setSectionResizeMode(QHeaderView.Stretch)
+ header.setHighlightSections(False)
+
+ main_layout.addWidget(self.table, stretch=1)
+
+ # Add first row
+ self.add_row()
+
+ # Install event filter to capture Enter key
+ self.table.installEventFilter(self)
+
+ # Back Button
+ back_btn = QPushButton("BACK")
+ back_btn.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ back_btn.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B; /* Dark Blue */
+ color: white;
+ border: 1px solid black;
+ padding: 10px 20px;
+ font-weight: bold;
+ }
+ QPushButton:pressed {
+ background-color: #000066; /* Darker blue on press */
+ }
+ """)
+ back_btn.clicked.connect(go_home)
+ main_layout.addWidget(back_btn, alignment=Qt.AlignCenter)
+
+ def add_row(self):
+ row = self.table.rowCount()
+ self.table.insertRow(row)
+ for col in range(self.table.columnCount()):
+ item = QTableWidgetItem()
+ bg_color = "white" if row % 2 == 0 else "#E6F0FF"
+ item.setBackground(QBrush(QColor(bg_color)))
+ self.table.setItem(row, col, item)
+
+ def eventFilter(self, obj, event):
+ if obj == self.table and event.type() == QEvent.KeyPress:
+ if event.key() in (Qt.Key_Return, Qt.Key_Enter):
+ row = self.table.currentRow()
+ col = self.table.currentColumn()
+
+ # Move to next column
+ if col < self.table.columnCount() - 1:
+ self.table.setCurrentCell(row, col + 1)
+ else:
+ # Last column, move to next row
+ if row == self.table.rowCount() - 1:
+ self.add_row()
+ self.table.setCurrentCell(row + 1, 0)
+ return True
+ return super().eventFilter(obj, event)
+
+ def go_back(self):
+ self.stacked_widget.setCurrentIndex(4)
+# -------------------------final validation screen----------------------------------#
+class FinalValidation(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle("Final Validation")
+ self.resize(1400, 750)
+
+ # Background
+ palette = self.palette()
+ palette.setColor(QPalette.Window, QColor("#ADD8E6"))
+ self.setAutoFillBackground(True)
+ self.setPalette(palette)
+ main_layout = QVBoxLayout(self)
+
+ # ================= Header =================
+ header_widget = QWidget()
+ header_widget.setStyleSheet("background-color: #003366;")
+ header_layout = QHBoxLayout(header_widget)
+ header_layout.setContentsMargins(10, 10, 10, 10)
+
+ logo_label = QLabel()
+ pixmap = QPixmap("/home/cri/myproject/cri_logo_red.png")
+ pixmap = pixmap.scaled(60, 60, Qt.KeepAspectRatio, Qt.SmoothTransformation)
+ logo_label.setPixmap(pixmap)
+ logo_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
+
+ title_label = QLabel("PART VALIDATION")
+ title_label.setFont(QFont("Times New Roman", 24, QFont.Bold))
+ title_label.setStyleSheet("color: white;")
+ title_label.setAlignment(Qt.AlignCenter)
+
+ header_layout.addWidget(logo_label)
+ header_layout.addSpacing(20)
+ header_layout.addWidget(title_label, stretch=1)
+ header_layout.addStretch()
+ main_layout.addWidget(header_widget)
+
+ # Top Info Layout
+ info_layout = QHBoxLayout()
+
+ self.work_order_input = QLineEdit()
+ self.serial_input = QLineEdit()
+ self.operator_input = QLineEdit()
+
+ light_blue_style = "background-color: #E6F0FF;"
+ self.work_order_input.setStyleSheet(light_blue_style)
+ self.serial_input.setStyleSheet(light_blue_style)
+ self.operator_input.setStyleSheet(light_blue_style)
+
+ info_layout.addWidget(self._labeled_field("Work Order Number:", self.work_order_input))
+ info_layout.addWidget(self._labeled_field("Serial Number:", self.serial_input))
+ info_layout.addWidget(self._labeled_field("Operator Plant:", self.operator_input))
+ main_layout.addLayout(info_layout)
+
+ # ================= Table =================
+ self.table = QTableWidget(0, 3)
+ self.table.setHorizontalHeaderLabels(["Dimension Name", "Dimension Value", "Enter Value"])
+ self.table.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
+ self.table.setStyleSheet("""
+ QTableWidget {
+ background-color: white;
+ gridline-color: black;
+ }
+ QHeaderView::section {
+ background-color: #00008B;
+ color: white;
+ font-weight: bold;
+ padding: 4px;
+ border: 1px solid black;
+ }
+ """)
+
+ header = self.table.horizontalHeader()
+ header.setSectionResizeMode(QHeaderView.Stretch)
+ header.setHighlightSections(False)
+
+ main_layout.addWidget(self.table, stretch=1)
+
+ # Add first row
+ self.add_row()
+
+ # Install event filter for Enter key
+ self.table.installEventFilter(self)
+
+ # ================= Back Button =================
+ back_btn = QPushButton("BACK")
+ back_btn.setFont(QFont("Times New Roman", 12, QFont.Bold))
+ back_btn.setStyleSheet("""
+ QPushButton {
+ background-color: #00008B;
+ color: white;
+ border: 1px solid black;
+ padding: 10px 20px;
+ font-weight: bold;
+ }
+ QPushButton:pressed {
+ background-color: #000066;
+ }
+ """)
+ back_btn.clicked.connect(go_home)
+ main_layout.addWidget(back_btn, alignment=Qt.AlignCenter)
+
+ def _labeled_field(self, label_text, line_edit):
+ container = QWidget()
+ layout = QHBoxLayout(container)
+ layout.setContentsMargins(0, 0, 0, 0)
+
+ label = QLabel(label_text)
+ label.setFont(QFont("Times New Roman", 11))
+ label.setFixedWidth(160)
+
+ line_edit.setFixedWidth(300)
+ line_edit.setFixedHeight(30)
+ line_edit.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+
+ layout.addWidget(label)
+ layout.addWidget(line_edit)
+ return container
+
+ def add_row(self):
+ row = self.table.rowCount()
+ self.table.insertRow(row)
+ for col in range(self.table.columnCount()):
+ item = QTableWidgetItem()
+ bg_color = "white" if row % 2 == 0 else "#E6F0FF"
+ item.setBackground(QBrush(QColor(bg_color)))
+ self.table.setItem(row, col, item)
+
+ def eventFilter(self, obj, event):
+ if obj == self.table and event.type() == QEvent.KeyPress:
+ if event.key() in (Qt.Key_Return, Qt.Key_Enter):
+ row = self.table.currentRow()
+ col = self.table.currentColumn()
+
+ # Move to next column
+ if col < self.table.columnCount() - 1:
+ self.table.setCurrentCell(row, col + 1)
+ else:
+ # Last column → move to next row
+ if row == self.table.rowCount() - 1:
+ self.add_row()
+ self.table.setCurrentCell(row + 1, 0)
+ return True
+ return super().eventFilter(obj, event)
+
+ def go_back(self):
+ self.stacked_widget.setCurrentIndex(5)
+# ---------------- Application Entry ---------------- #
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+ stacked_widget = QStackedWidget()
+ login_screen = LoginScreen(stacked_widget)
+ main_screen = MainScreen()
+ stacked_widget.addWidget(login_screen)
+ stacked_widget.addWidget(main_screen)
+ stacked_widget.setCurrentIndex(0)
+ global_stacked_widget = stacked_widget
+ global_main_screen = main_screen
+ stacked_widget.show()
+ sys.exit(app.exec_())
+
diff --git a/styles.py b/styles.py
deleted file mode 100644
index 5b3d6e4..0000000
--- a/styles.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import os
-
-def load_login_qss():
- """
- Load and return the contents of 'login.qss',
- located in the same directory as this script.
- """
- base = os.path.dirname(os.path.abspath(__file__))
- path = os.path.join(base, "login.qss")
- try:
- with open(path, encoding="utf-8") as f:
- return f.read()
- except OSError as e:
- print(f"Error loading login.qss: {e}")
- return ""
diff --git a/test.py b/test.py
deleted file mode 100644
index 3a1daf9..0000000
--- a/test.py
+++ /dev/null
@@ -1,915 +0,0 @@
-
-import sys
-import requests
-
-from PyQt6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton,
- QHBoxLayout, QStackedWidget, QCheckBox, QTextEdit
-)
-from PyQt6.QtGui import QPixmap, QFont
-from PyQt6.QtCore import Qt, QDateTime, QDate
-
-# --- Login Screen ---
-class LoginScreen(QWidget):
- def __init__(self, stacked):
- super().__init__()
- self.stacked = stacked
- self.init_ui()
-
- def init_ui(self):
- layout = QVBoxLayout()
- logo = QLabel()
- logo.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_logo.png.png'
- ).scaled(100, 100, Qt.AspectRatioMode.KeepAspectRatio))
- logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(logo)
-
- title_label = QLabel("Login Screen")
- title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #002244; margin-bottom: 10px;")
- layout.addWidget(title_label)
-
- self.username = QLineEdit()
- self.username.setPlaceholderText("Username")
- layout.addWidget(self.username)
- pw_layout = QHBoxLayout()
-
- self.password = QLineEdit()
- self.password.setPlaceholderText("Password")
- self.password.setEchoMode(QLineEdit.EchoMode.Password)
- pw_layout.addWidget(self.password)
-
- self.eye_toggle = QCheckBox("Show")
- self.eye_toggle.toggled.connect(lambda checked: self.password.setEchoMode(
- QLineEdit.EchoMode.Normal if checked else QLineEdit.EchoMode.Password))
- pw_layout.addWidget(self.eye_toggle)
- layout.addLayout(pw_layout)
-
- self.username.returnPressed.connect(self.password.setFocus)
- self.password.returnPressed.connect(self.do_login)
- login_btn = QPushButton("Login")
- login_btn.clicked.connect(self.do_login)
- layout.addWidget(login_btn)
-
- logo2 = QLabel()
- logo2.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_farm_banner.jpg'
- ).scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))
- logo2.setAlignment(Qt.AlignmentFlag.AlignCenter)
- layout.addWidget(logo2)
- self.setLayout(layout)
-
- def do_login(self):
- if self.username.text() and self.password.text():
- self.stacked.setCurrentIndex(1)
-
-# --- Module Selector ---
-class SelectorScreen(QWidget):
-
- def __init__(self, stacked, bot_screen):
- super().__init__()
- self.stacked = stacked
- self.bot_screen = bot_screen
- self.init_ui()
- self.load_module_list()
-
- def init_ui(self):
- self.layout = QVBoxLayout()
- title_label = QLabel("Selector Screen")
- title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- title_label.setStyleSheet("font-size: 18px; font-weight: bold; color: #002244;")
- self.layout.addWidget(title_label)
-
- date_label = QLabel(QDate.currentDate().toString("dddd, MMMM d, yyyy"))
- date_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- date_label.setFont(QFont("Arial", 14, QFont.Weight.Bold))
- self.layout.addWidget(date_label)
-
- self.button_container = QVBoxLayout()
- self.layout.addLayout(self.button_container)
- self.setLayout(self.layout)
-
- def load_module_list(self):
- url = 'https://pds.iotsignin.com/api/get/module-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": "Module List"
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- if response.status_code == 200:
- data = response.json()
- for module in data.get("status_description", []):
- self.add_module_button(module)
- else:
- self.show_error(f"Failed to load modules: {response.status_code}")
- except Exception as e:
- self.show_error(str(e))
-
- def add_module_button(self, module_name):
- btn = QPushButton(module_name)
- btn.setStyleSheet(
- "padding: 10px; background-color: #c2185b; color: white; font-size: 14px; border-radius: 8px;")
- btn.clicked.connect(lambda _, name=module_name: self.goto_bot_screen(name))
- self.button_container.addWidget(btn)
-
- def show_error(self, msg):
- error = QLabel(f"❌ {msg}")
- error.setStyleSheet("color: red;")
- self.layout.addWidget(error)
-
- def goto_bot_screen(self, module_name):
- self.bot_screen.set_module_name(module_name)
- self.stacked.setCurrentIndex(2)
-
-# --- BotScreen: full chat-driven selection ---
-class BotScreen(QWidget):
-
- def __init__(self, stacked):
- super().__init__()
- self.stacked = stacked
- self.module_name = ""
- self.charts = []
- self.plants = []
- self.lines = []
- self.filters = []
- self.invoice_types = []
-
- self.awaiting_chart_choice = False
- self.awaiting_plant_choice = False
- self.awaiting_line_choice = False
- self.awaiting_order_input = False
- self.awaiting_filter_choice = False
- self.awaiting_invoice_choice = False
-
- self.selected_chart = None
- self.selected_plant = None
- self.selected_line = None
- self.selected_order = None
- self.selected_filter = None
- self.selected_invoice_type = None
- self.init_ui()
-
- def set_module_name(self, name):
- self.module_name = name
- self.clear_chat()
- self.load_chart_data()
-
- def init_ui(self):
- main_layout = QVBoxLayout()
- top_bar = QHBoxLayout()
- logo = QLabel()
- logo.setPixmap(QPixmap(
- r'C:\Users\Administrator\Downloads\sfg printing\backup from airline device\SRIMATHI\cri_logo.png.png'
- ).scaled(40, 40, Qt.AspectRatioMode.KeepAspectRatio))
- top_bar.addWidget(logo)
-
- title = QLabel("CRI DIGITAL MANUFACTURING SOLUTIONS \n CRI BOT ASSISTANT")
- title.setStyleSheet("font-size: 16px; font-weight: bold; color: black; padding: 10px; border-radius: 8px;")
- top_bar.addWidget(title)
-
- top_bar.addStretch()
- back_btn = QPushButton("Back")
- back_btn.clicked.connect(self.go_back)
- top_bar.addWidget(back_btn)
-
- refresh_btn = QPushButton("Refresh")
- refresh_btn.clicked.connect(self.load_chart_data)
- top_bar.addWidget(refresh_btn)
- main_layout.addLayout(top_bar)
-
- self.chart_info = QLabel("Waiting for chart data...")
- self.chart_info.setWordWrap(True)
- self.chart_info.setStyleSheet(
- "background: white; color: black; font-size: 15px; padding: 14px; border-radius: 10px")
- main_layout.addWidget(self.chart_info)
-
- self.chat_area = QTextEdit()
- self.chat_area.setReadOnly(True)
- self.chat_area.setStyleSheet("background-color: white; color: black; padding: 10px; font-size: 14px;")
- main_layout.addWidget(self.chat_area)
- input_layout = QHBoxLayout()
-
- self.user_input = QLineEdit()
- self.user_input.setPlaceholderText("Type a number to select...")
- input_layout.addWidget(self.user_input)
- send_btn = QPushButton("Send")
- send_btn.setStyleSheet(
- "padding: 8px; background: #2e7d32; color: white; border-radius: 8px;")
- send_btn.clicked.connect(self.send_message)
- input_layout.addWidget(send_btn)
- main_layout.addLayout(input_layout)
- self.setLayout(main_layout)
-
- def load_chart_data(self):
- self.chart_info.setText("Loading chart data...")
- 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": self.module_name
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- status = data.get("status_code", "UNKNOWN")
- desc = data.get("status_description", [])
- self.chart_info.setText(f"API Status: {status}")
- self.chat_area.append(f"HTTP: {response.status_code}")
- self.chat_area.append(f"Bot Module: {self.module_name}")
- if isinstance(desc, list) and desc:
- self.charts = desc
- else:
- self.charts = [
- "Production Line Count",
- "Production Hourly Count",
- "Production Line Stop Count",
- "Production Order Count"
- ]
- self.awaiting_chart_choice = True
- self.show_choice_menu("chart", self.charts)
- except Exception as e:
- self.chart_info.setText("Failed to load chart data.")
- self.chat_area.append(f"Bot: Error: {e}")
-
- def show_choice_menu(self, item, items_list):
- label_map = {
- "chart": "chart",
- "plant": "plant",
- "line": "line",
- "order": "production order",
- "filter": "date filter",
- "invoice": "invoice type",
- }
- # Ensure name extraction works for both simple str and dict/list cases.
- lines = [
- f"{i + 1}. {item_name['name'] if isinstance(item_name, dict) and 'name' in item_name else str(item_name)}"
- for i, item_name in enumerate(items_list)
- ]
- menu_txt = f"Bot: Please select a {label_map[item]}:
" + "
".join(lines)
- self.chat_area.append(menu_txt)
-
- def send_message(self):
- msg = self.user_input.text().strip()
- self.user_input.clear()
- # --- Chart selection ---
- if self.awaiting_chart_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.charts):
- self.selected_chart = self.charts[idx]
- self.awaiting_chart_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_chart}")
- self.fetch_and_display_plants()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.charts)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1-{len(self.charts)}).")
- return
-
- # --- Plant selection ---
- if self.awaiting_plant_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.plants):
- self.selected_plant = self.plants[idx]
- self.awaiting_plant_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_plant}")
-
- # Normalize chart name and check for invoice / invoice quantity charts explicitly
- selected_chart_name = (self.selected_chart or "").strip().lower()
-
- if selected_chart_name == "invoice":
- # Fetch invoice types for regular invoice dashboard
- self.fetch_and_display_invoice_types()
- elif selected_chart_name == "invoice quantity":
- # For invoice quantity, also fetch invoice types first (if needed)
- self.fetch_and_display_invoice_types()
- # Alternatively: if invoice type is fixed or selection is not needed, you can
- # proceed to date filter directly by calling
- # self.fetch_and_display_invoice_filters() here if your flow allows
- else:
- # For other charts, fetch lines normally
- self.fetch_and_display_lines()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.plants)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1–{len(self.plants)}).")
- return
-
- # --- Line selection ---
- if self.awaiting_line_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.lines):
- self.selected_line = self.lines[idx]
- self.awaiting_line_choice = False
- self.chat_area.append(f"Bot: ✅ You selected: {self.selected_line}")
- chart_raw = self.selected_chart
- chart_normalized = chart_raw.strip().lower() if chart_raw else ""
-
- # 🚩 Only for Chart 4 (Production Order Count)
- if chart_normalized == "production order count":
- print(f"[DEBUG] self.selected_chart (raw): '{self.selected_chart}'")
- self.awaiting_order_input = True # Set state for next prompt
- self.chat_area.append("Bot: Please enter the production order number:")
- else:
- self.fetch_and_display_filters()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.lines)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a number (1-{len(self.lines)}).")
- return
-
- # --- Production order entry (Chart 4 only) ---
- if hasattr(self, 'awaiting_order_input') and self.awaiting_order_input:
- order = msg.strip()
- if not order.isdigit():
- self.chat_area.append("Bot: ❌ Please enter a valid production order number (numbers only).")
- else:
- self.selected_order = order
- self.awaiting_order_input = False
- self.chat_area.append(f"Bot: ✅ Production order set: {self.selected_order}")
- self.fetch_and_display_filters()
- return
- #-------if MODULE 2, chart 2 then this part will execute here---------
-
- if self.awaiting_invoice_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.invoice_types):
- self.selected_invoice_type = self.invoice_types[idx]
- self.awaiting_invoice_choice = False
- self.chat_area.append(
- f"Bot: ✅ You selected invoice type: {self.selected_invoice_type}"
- )
-
- # Normalize selected chart to distinguish between Invoice and Invoice Quantity dashboards
- selected_chart_name = (self.selected_chart or "").strip().lower()
-
- if selected_chart_name == "invoice":
- # For "Invoice" chart, fetch invoice filters (date filters)
- self.fetch_and_display_invoice_filters()
- elif selected_chart_name == "invoice quantity":
- # For "Invoice Quantity" chart, fetch invoice filters as well
- self.fetch_and_display_invoice_filters()
- else:
- # If future charts, handle accordingly or fallback
- self.chat_area.append(
- "Bot: ❌ Unrecognized invoice chart selected."
- )
- else:
- self.chat_area.append(
- f"Bot: ❌ Please select a number from 1 to {len(self.invoice_types)}."
- )
- else:
- self.chat_area.append(
- f"Bot: ❌ Please enter a valid number (1–{len(self.invoice_types)})."
- )
- return
-
- # --- Date filter selection ---
- if self.awaiting_filter_choice:
- if msg.isdigit():
- idx = int(msg) - 1
- if 0 <= idx < len(self.filters):
- self.selected_filter = self.filters[idx]
- self.awaiting_filter_choice = False
- self.chat_area.append(f"Bot: ✅ You selected filter: {self.selected_filter}")
- # Show current date/time
- now = QDateTime.currentDateTime()
- fmt_date = now.toString("dddd, MMMM d, yyyy, hh:mm AP")
- self.chat_area.append(f"Current date: {fmt_date}")
-
- selected = (self.selected_chart or "").strip().lower()
- if "invoice" in selected:
- # NEW: Handle invoice dashboard: no line name needed
- self.fetch_and_display_invoice_production_count()
- elif selected == "production hourly count":
- self.fetch_and_display_hourly_count()
- elif selected == "production line stop count":
- self.fetch_and_display_production_line_stop_count()
- elif selected == "production order count":
- self.fetch_and_display_production_order_count()
- elif selected == "invoice":
- self.fetch_and_display_invoice_production_count()
- else:
- self.fetch_and_display_production_count()
- else:
- self.chat_area.append(f"Bot: ❌ Please select a number from 1 to {len(self.filters)}.")
- else:
- self.chat_area.append(f"Bot: ❌ Please enter a valid number (1–{len(self.filters)}).")
- return
-
- # handle user input
- def fetch_and_display_plants(self):
- url = 'https://pds.iotsignin.com/api/get/moduleplant-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=",
- "plant-name": "Plant List"
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- plants = data.get("status_description", [])
- if plants:
- self.plants = plants
- self.awaiting_plant_choice = True
- self.show_choice_menu("plant", plants)
- else:
- self.chat_area.append("Bot: ❌ No plants found.")
- else:
- self.chat_area.append(f"Bot: ❌ Error: {data.get('status_description')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load plants: {e}")
-
-
- def fetch_and_display_lines(self):
- url = 'https://pds.iotsignin.com/api/get/module-plantline-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=",
- "plant-name": self.selected_plant,
- "chart-name": self.selected_chart
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- lines = data.get("status_description", [])
- if lines:
- self.lines = lines
- self.awaiting_line_choice = True
- self.show_choice_menu("line", lines)
- else:
- self.chat_area.append("Bot: ❌ No lines found for the selected plant.")
- else:
- self.chat_area.append(f"Bot: ❌ Error: {data.get('status_description')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load lines: {e}")
-
- def fetch_and_display_filters(self):
- selected_chart_lower = (self.selected_chart or "").strip().lower()
-
- # URL remains the same unless invoice filters come from a different path
- url = 'https://pds.iotsignin.com/api/get/module-line-filter-name/data'
-
- # Base headers
- headers = {
- "Authorization": self.API_TOKEN
- }
-
- # Check if it’s an invoice chart by partial match
- if "invoice" in selected_chart_lower:
- # INVOICE FLOW: Skip line name, use invoice-type
- url = 'https://pds.iotsignin.com/api/get/module-invoice-filter-name/data'
- headers.update({
- "module-name": self.module_name,
- "chart-name": self.selected_chart,
- "plant-name": self.selected_plant,
- "invoice-type": self.selected_invoice_type
- })
- else:
- # PRODUCTION FLOW: Use line name as before
- headers.update({
- "line-name": self.selected_line
- })
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- filters = data.get("status_description", [])
- if filters:
- self.filters = filters
- self.awaiting_filter_choice = True
- self.show_choice_menu("filter", filters)
- self.fetch_and_display_invoice_production_count()
- else:
- self.chat_area.append("Bot: ❌ No date filters found for your selection.")
- else:
- self.chat_area.append(f"Bot: ❌ API Error: {data.get('status_description', 'Unknown error')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load date filters: {e}")
-
- def fetch_and_display_production_count(self):
- url = 'https://pds.iotsignin.com/api/get/module-filter-value/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": self.module_name,
- "chart-name": self.selected_chart,
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "filter-name": self.selected_filter
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- result = data.get("status_description")
- if isinstance(result, dict):
- self.chat_area.append(f"Bot: 📊 Production Counts (All Lines):")
- for ln, count in result.items():
- self.chat_area.append(f"{ln}: {count}")
- else:
- self.chat_area.append(f"Bot: 📈 Production Count: {result}")
- else:
- self.chat_area.append(f"Bot: ❌ Error getting production count: {data.get('status_description', 'Unknown')}")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to load production count: {e}")
-
- API_TOKEN = "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE="
-
-#CHART -2 PRODUCTION HOURLY COUNT
- def fetch_and_display_hourly_count(self):
- url = "https://pds.iotsignin.com/api/get/module-filter-value/data"
-
- # 👇 Pass everything only via HEADERS, not params
- headers = {
- "Authorization": "Bearer sb-eba140ab-74bb-44a4-8d92-70a636940def!b1182|it-rt-dev-cri-stjllphr!b68:616d8991-307b-4ab1-be37-7894a8c6db9d$0p0fE2I7w1Ve23-lVSKQF0ka3mKrTVcKPJYELr-i4nE=",
- "Accept": "application/json",
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
- "module-name": "Production Dashboard",
- "chart-name": "Production Hourly Count", # ✅ Chart name as header
- "plant-name": self.selected_plant, # ✅ From previous input
- "line-name": self.selected_line, # ✅ From previous input
- "filter-name": self.selected_filter # ✅ e.g., "Today", "Yesterday"
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- # ✅ Print raw HTTP response to console
- print("📡 [DEBUG] HTTP Status:", response.status_code)
- print("📡 [DEBUG] Raw API Response:\n", response.text)
- print("-" * 50) # Divider in logs
-
- # ✅ Display raw response in chat area
- self.chat_area.append(f"Raw API response:
{response.text}")
-
- # 🔒 Handle empty API response
- if not response.text.strip():
- print("[DEBUG] ❌ Empty response received.")
- self.chat_area.append("Bot: ❌ API returned no data.")
- return
-
- # 🔒 Try parsing JSON
- try:
- data = response.json()
- except Exception as json_err:
- print(f"[DEBUG] ❌ JSON Parse Error: {json_err}")
- self.chat_area.append(f"Bot: ❌ Failed to parse data: {json_err}")
- return
-
- if data.get("status_code") == "SUCCESS":
- now = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- self.chat_area.append(
- f"📊 Production Hourly Count ({self.selected_filter})
Current date: {now}
")
- for dataset in data.get("datasets", []):
- label = dataset.get("label", "Data")
- rows = ""
- for lbl, val in zip(data.get("labels", []), dataset.get("data", [])):
- rows += f"{lbl} ➤ {val}
"
- self.chat_area.append(f"{label}:
{rows}")
- print("[DEBUG] ✅ Hourly count displayed successfully.")
- else:
- req_status = data.get("status_code")
- print(f"[DEBUG] ❌ API returned status {req_status}")
- self.chat_area.append(f"Bot: ❌ API returned status {req_status}")
-
- # 🔒 Specific error handling
- except requests.exceptions.HTTPError as errh:
- print("[DEBUG] ❌ HTTP Error:", errh)
- self.chat_area.append(f"Bot: ❌ HTTP Error: {errh}")
-
- except requests.exceptions.ConnectionError:
- print("[DEBUG] ❌ Connection Error.")
- self.chat_area.append("Bot: ❌ Connection error.")
-
- except requests.exceptions.Timeout:
- print("[DEBUG] ❌ Timeout Error.")
- self.chat_area.append("Bot: ❌ Request timed out.")
-
- except Exception as e:
- print(f"[DEBUG] ❌ General Error: {e}")
- self.chat_area.append(f"Bot: ❌ Unexpected Error: {e}")
-
-
- # PRODUCTION LINE STOP DATA
- def fetch_and_display_production_line_stop_count(self):
- # You must have these set by prior steps:
- # self.selected_plant, self.selected_line, self.selected_filter
- url = "https://pds.iotsignin.com/api/get/module-production-linestop/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",
- "chart-name": "Production Line Stop",
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "filter-name": self.selected_filter # Example: "Today"
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
- self.chat_area.append(f"Raw API response:
{response.text}")
- if response.status_code != 200:
- self.chat_area.append(f"Bot: ❌ HTTP error {response.status_code}")
- return
- if not response.text.strip():
- self.chat_area.append(f"Bot: ❌ No response from API.")
- return
- try:
- data = response.json()
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Failed to parse API response: {e}")
- return
- current_date = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- if data.get("status_code") == "SUCCESS":
- labels = data.get("labels", [])
- values = data.get("data", [])
- message = f"Production Line Stop Counts
Current date: {current_date}
"
- for label, value in zip(labels, values):
- message += f"{label}: {value}
"
- self.chat_area.append(message)
- else:
- self.chat_area.append(f"Bot: ❌ No data found for this selection.")
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Error fetching report: {e}")
-
- # CHART-4 PRODUCTION ORDER COUNT
- def fetch_and_display_production_order_count(self):
- url = "https://pds.iotsignin.com/api/get/module-production-order/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",
- "chart-name": "Production Order Count",
- "plant-name": self.selected_plant,
- "line-name": self.selected_line,
- "production-order": self.selected_order,
- "filter-name": self.selected_filter
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- # Print raw API response in Python console for debugging
- print("📡 [DEBUG] HTTP Status:", response.status_code)
- print("📡 [DEBUG] Raw API Response:\n", response.text)
- print("-" * 50)
-
- # Display raw API response in chat for traceability
- self.chat_area.append(f"Raw API response:
{response.text}")
-
- if not response.text.strip():
- print("[DEBUG] ❌ Empty API response received.")
- self.chat_area.append("Bot: ❌ API returned no data.")
- return
-
- try:
- data = response.json()
- except Exception as json_err:
- print(f"[DEBUG] ❌ JSON Parse Error: {json_err}")
- self.chat_area.append(f"Bot: ❌ Failed to parse data: {json_err}")
- return
-
- status = data.get("status_code")
- if status == "SUCCESS":
- now = QDateTime.currentDateTime().toString("dddd, MMMM d, yyyy, h:mm AP t")
- self.chat_area.append(
- f"Production Order Count ({self.selected_filter})
Current date: {now}
")
- datasets = data.get("datasets", [])
-
- for dataset in datasets:
- label = dataset.get("label", "Data")
- self.chat_area.append(f"{label}:")
- # Handle 'Today' and 'Yesterday' (dict)
- if isinstance(dataset.get("data"), dict):
- for timeslot, count in dataset.get("data", {}).items():
- self.chat_area.append(f"{timeslot}: {count}
")
- # Handle 'This Week', 'This Month' (list)
- elif isinstance(dataset.get("data"), list):
- labels = data.get("labels", [])
- for idx, value in enumerate(dataset.get("data")):
- label_text = labels[idx] if idx < len(labels) else f"Data {idx + 1}"
- self.chat_area.append(f"{label_text}: {value}
")
- else:
- self.chat_area.append(f"Data: {dataset.get('data')}")
- print("[DEBUG] ✅ Production order count handled/displayed.")
- else:
- print(f"[DEBUG] ❌ API status_code not SUCCESS: {status}")
- self.chat_area.append(f"Bot: ❌ API returned status {status}")
-
- except requests.exceptions.HTTPError as errh:
- print("[DEBUG] ❌ HTTP Error:", errh)
- self.chat_area.append(f"Bot: ❌ HTTP Error: {errh}")
-
- except requests.exceptions.ConnectionError:
- print("[DEBUG] ❌ Connection Error.")
- self.chat_area.append("Bot: ❌ Connection error.")
-
- except requests.exceptions.Timeout:
- print("[DEBUG] ❌ Timeout Error.")
- self.chat_area.append("Bot: ❌ Request timed out.")
-
- except Exception as e:
- print(f"[DEBUG] ❌ General Error: {e}")
- self.chat_area.append(f"Bot: ❌ Unexpected Error: {e}")
-
- def clear_chat(self):
- self.chat_area.clear()
- def go_back(self):
- self.stacked.setCurrentIndex(1)
-
-
- # MODULE -2 INVOICE DASHBOARD
- #CHART-1 INVOICE
-
- #to fetch the invoice types
-
- def fetch_and_display_invoice_types(self):
- url = "https://pds.iotsignin.com/api/get/module-invoice-type/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=",
- "invoice-type-list": "Invoice Type List"
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- if response.status_code == 200:
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- self.invoice_types = data.get("status_description", [])
- if self.invoice_types:
- self.awaiting_invoice_choice = True
- self.show_choice_menu("invoice", self.invoice_types)
- else:
- self.chat_area.append("Bot: ❌ No invoice types found.")
- else:
- error_msg = data.get("status_description", "Unknown error")
- self.chat_area.append(f"Bot: ❌ Failed to get invoice types: {error_msg}")
- else:
- self.chat_area.append(f"Bot: ❌ HTTP Error {response.status_code} while fetching invoice types.")
-
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Error getting invoice types: {str(e)}")
-
-# To fetch date filter
-
- def fetch_and_display_invoice_filters(self):
- url = "http://pds.iotsignin.com/api/get/module-invoice-filter/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=",
- "filter-name": "Filter List"
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
- if response.status_code != 200:
- self.chat_area.append(f"Bot: ❌ HTTP Error {response.status_code} when fetching filters.")
- return
-
- data = response.json()
- if data.get("status_code") == "SUCCESS":
- filters = data.get("status_description", [])
- if filters:
- self.filters = filters
- self.awaiting_filter_choice = True
- self.show_choice_menu("filter", filters)
- else:
- self.chat_area.append("Bot: ❌ No invoice filters found.")
- else:
- self.chat_area.append(f"Bot: ❌ API Error: {data.get('status_description', 'Unknown error')}")
-
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Exception when fetching invoice filters: {str(e)}")
-
- # to fetch production count
- def fetch_and_display_invoice_production_count(self):
- url = "https://pds.iotsignin.com/api/get/module-invoice-count/data"
- headers = {
- "Authorization": self.API_TOKEN,
- "plant-name": self.selected_plant,
- "invoice-name": self.selected_invoice_type,
- "filter-name": self.selected_filter
- }
-
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- # Show raw API response for debugging
- self.chat_area.append(f"📡 Raw API Response:
{response.text}")
-
- if response.status_code != 200:
- self.chat_area.append(f"Bot: ❌ HTTP Error {response.status_code} fetching invoice count.")
- return
-
- data = response.json()
- status = data.get("status_code")
- if status != "SUCCESS":
- error_msg = data.get("status_description", "Unknown error")
- self.chat_area.append(f"Bot: ❌ API Error: {error_msg}")
- return
-
- labels = data.get("labels", [])
- datasets = data.get("datasets", [])
-
- # Show current date/time
- now = QDateTime.currentDateTime()
- fmt_date = now.toString("dddd, MMMM d, yyyy, h:mm AP")
- self.chat_area.append(f"Current date: {fmt_date}
")
-
- # Multiple datasets means 'All Invoice' selection
- if len(datasets) > 1:
- for dataset in datasets:
- label = dataset.get("label", "Dataset")
- values = dataset.get("data", [])
- self.chat_area.append(f"{label}:
")
- for lbl, val in zip(labels, values):
- self.chat_area.append(f"{lbl}: {val}
")
- self.chat_area.append("
") # spacing between datasets
- # Single dataset for specific invoice type
- elif len(datasets) == 1:
- dataset = datasets[0]
- label = dataset.get("label", "Invoices")
- values = dataset.get("data", [])
- self.chat_area.append(f"{label}:
")
- for lbl, val in zip(labels, values):
- self.chat_area.append(f"{lbl}: {val}
")
- else:
- self.chat_area.append("Bot: ❌ No data found in API response.")
-
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Exception fetching invoice count: {str(e)}")
-
-
- def fetch_and_display_invoice_quantity(self):
- url = "https://pds.iotsignin.com/api/get/module-invoice-quantity/data"
- headers = {
- "Authorization": self.API_TOKEN,
- "plant-name": self.selected_plant,
- "invoice-name": self.selected_invoice_type,
- "filter-name": self.selected_filter
- }
- try:
- response = requests.get(url, headers=headers, timeout=10)
-
- # Show raw response for debugging
- self.chat_area.append(f"📡 Raw API Response:
{response.text}")
-
- if response.status_code != 200:
- self.chat_area.append(f"Bot: ❌ HTTP Error {response.status_code} fetching invoice quantity.")
- return
-
- data = response.json()
- if data.get("status_code") != "SUCCESS":
- error_msg = data.get("status_description", "Unknown error")
- self.chat_area.append(f"Bot: ❌ API Error: {error_msg}")
- return
-
- labels = data.get("labels", [])
- datasets = data.get("datasets", [])
-
- # Display current date/time in desired format
- now = QDateTime.currentDateTime()
- fmt_date = now.toString("dddd, MMMM d, yyyy, h:mm AP")
- self.chat_area.append(f"Current date: {fmt_date}
")
-
- if len(datasets) > 1:
- # Multiple datasets (e.g. All Invoice)
- for dataset in datasets:
- label = dataset.get("label", "Data")
- values = dataset.get("data", [])
- self.chat_area.append(f"{label}:
")
- for lbl, val in zip(labels, values):
- self.chat_area.append(f"{lbl}: {val}
")
- self.chat_area.append("
")
- elif len(datasets) == 1:
- # Single dataset (single invoice type)
- dataset = datasets[0]
- label = dataset.get("label", "Invoice Quantity")
- values = dataset.get("data", [])
- self.chat_area.append(f"{label}:
")
- for lbl, val in zip(labels, values):
- self.chat_area.append(f"{lbl}: {val}
")
- else:
- self.chat_area.append("Bot: ❌ No invoice quantity data found.")
-
- except Exception as e:
- self.chat_area.append(f"Bot: ❌ Exception fetching invoice quantity: {str(e)}")
-
-
-def main():
- app = QApplication(sys.argv)
- stacked = QStackedWidget()
- bot_screen = BotScreen(stacked)
- selector_screen = SelectorScreen(stacked, bot_screen)
- stacked.addWidget(LoginScreen(stacked))
- stacked.addWidget(selector_screen)
- stacked.addWidget(bot_screen)
- stacked.setFixedSize(420, 640)
- stacked.show() ,
-
-
- sys.exit(app.exec())
-
-if __name__ == '__main__':
- main()
diff --git a/web.png b/web.png
deleted file mode 100644
index 270c296..0000000
Binary files a/web.png and /dev/null differ