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