Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0914d2c0e |
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<solid android:color="#880E4F" />
|
|
||||||
<corners android:radius="16dp" />
|
|
||||||
</shape>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 131 KiB |
3
download
3
download
@@ -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"
|
|
||||||
68
login.qss.py
68
login.qss.py
@@ -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;
|
|
||||||
}
|
|
||||||
484
rppds.py
Normal file
484
rppds.py
Normal file
@@ -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()
|
||||||
1217
sfgprintingnew.py
Normal file
1217
sfgprintingnew.py
Normal file
File diff suppressed because it is too large
Load Diff
15
styles.py
15
styles.py
@@ -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 ""
|
|
||||||
915
test.py
915
test.py
@@ -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"<b>HTTP:</b> {response.status_code}")
|
|
||||||
self.chat_area.append(f"<b>Bot Module:</b> {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"<b>Bot:</b> 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"<b>Bot:</b> Please select a {label_map[item]}:<br>" + "<br>".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"<b>Bot:</b> ✅ You selected: <b>{self.selected_chart}</b>")
|
|
||||||
self.fetch_and_display_plants()
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Please select a number from 1 to {len(self.charts)}.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Bot:</b> ✅ You selected: <b>{self.selected_plant}</b>")
|
|
||||||
|
|
||||||
# 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"<b>Bot:</b> ❌ Please select a number from 1 to {len(self.plants)}.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Bot:</b> ✅ You selected: <b>{self.selected_line}</b>")
|
|
||||||
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("<b>Bot:</b> Please enter the production order number:")
|
|
||||||
else:
|
|
||||||
self.fetch_and_display_filters()
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Please select a number from 1 to {len(self.lines)}.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ Please enter a valid production order number (numbers only).")
|
|
||||||
else:
|
|
||||||
self.selected_order = order
|
|
||||||
self.awaiting_order_input = False
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ✅ Production order set: <b>{self.selected_order}</b>")
|
|
||||||
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"<b>Bot:</b> ✅ You selected invoice type: <b>{self.selected_invoice_type}</b>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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(
|
|
||||||
"<b>Bot:</b> ❌ Unrecognized invoice chart selected."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.chat_area.append(
|
|
||||||
f"<b>Bot:</b> ❌ Please select a number from 1 to {len(self.invoice_types)}."
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.chat_area.append(
|
|
||||||
f"<b>Bot:</b> ❌ 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"<b>Bot:</b> ✅ You selected filter: <b>{self.selected_filter}</b>")
|
|
||||||
# Show current date/time
|
|
||||||
now = QDateTime.currentDateTime()
|
|
||||||
fmt_date = now.toString("dddd, MMMM d, yyyy, hh:mm AP")
|
|
||||||
self.chat_area.append(f"<b>Current date:</b> {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"<b>Bot:</b> ❌ Please select a number from 1 to {len(self.filters)}.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ No plants found.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Error: {data.get('status_description')}")
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ No lines found for the selected plant.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Error: {data.get('status_description')}")
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ No date filters found for your selection.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ API Error: {data.get('status_description', 'Unknown error')}")
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Bot:</b> 📊 <b>Production Counts (All Lines):</b>")
|
|
||||||
for ln, count in result.items():
|
|
||||||
self.chat_area.append(f"{ln}: {count}")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> 📈 <b>Production Count:</b> {result}")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Error getting production count: {data.get('status_description', 'Unknown')}")
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Raw API response:</b><br><pre>{response.text}</pre>")
|
|
||||||
|
|
||||||
# 🔒 Handle empty API response
|
|
||||||
if not response.text.strip():
|
|
||||||
print("[DEBUG] ❌ Empty response received.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ 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"<b>Bot:</b> ❌ 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"<b>📊 Production Hourly Count ({self.selected_filter})</b><br><b>Current date:</b> {now}<br><br>")
|
|
||||||
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} ➤ <b>{val}</b><br>"
|
|
||||||
self.chat_area.append(f"<b>{label}:</b><br>{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"<b>Bot:</b> ❌ API returned status <code>{req_status}</code>")
|
|
||||||
|
|
||||||
# 🔒 Specific error handling
|
|
||||||
except requests.exceptions.HTTPError as errh:
|
|
||||||
print("[DEBUG] ❌ HTTP Error:", errh)
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ HTTP Error: {errh}")
|
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
print("[DEBUG] ❌ Connection Error.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ Connection error.")
|
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
print("[DEBUG] ❌ Timeout Error.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ Request timed out.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DEBUG] ❌ General Error: {e}")
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Raw API response:</b><br><pre>{response.text}</pre>")
|
|
||||||
if response.status_code != 200:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ HTTP error {response.status_code}")
|
|
||||||
return
|
|
||||||
if not response.text.strip():
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ No response from API.")
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
data = response.json()
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Production Line Stop Counts</b><br><b>Current date:</b> {current_date}<br><br>"
|
|
||||||
for label, value in zip(labels, values):
|
|
||||||
message += f"<b>{label}:</b> {value}<br>"
|
|
||||||
self.chat_area.append(message)
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ No data found for this selection.")
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Raw API response:</b><br><pre>{response.text}</pre>")
|
|
||||||
|
|
||||||
if not response.text.strip():
|
|
||||||
print("[DEBUG] ❌ Empty API response received.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ 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"<b>Bot:</b> ❌ 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"<b>Production Order Count ({self.selected_filter})</b><br><b>Current date:</b> {now}<br><br>")
|
|
||||||
datasets = data.get("datasets", [])
|
|
||||||
|
|
||||||
for dataset in datasets:
|
|
||||||
label = dataset.get("label", "Data")
|
|
||||||
self.chat_area.append(f"<b>{label}:</b>")
|
|
||||||
# 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}: <b>{count}</b><br>")
|
|
||||||
# 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}: <b>{value}</b><br>")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"Data: <b>{dataset.get('data')}</b>")
|
|
||||||
print("[DEBUG] ✅ Production order count handled/displayed.")
|
|
||||||
else:
|
|
||||||
print(f"[DEBUG] ❌ API status_code not SUCCESS: {status}")
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ API returned status <code>{status}</code>")
|
|
||||||
|
|
||||||
except requests.exceptions.HTTPError as errh:
|
|
||||||
print("[DEBUG] ❌ HTTP Error:", errh)
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ HTTP Error: {errh}")
|
|
||||||
|
|
||||||
except requests.exceptions.ConnectionError:
|
|
||||||
print("[DEBUG] ❌ Connection Error.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ Connection error.")
|
|
||||||
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
print("[DEBUG] ❌ Timeout Error.")
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ Request timed out.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[DEBUG] ❌ General Error: {e}")
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ No invoice types found.")
|
|
||||||
else:
|
|
||||||
error_msg = data.get("status_description", "Unknown error")
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Failed to get invoice types: {error_msg}")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ HTTP Error {response.status_code} while fetching invoice types.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Error getting invoice types: <code>{str(e)}</code>")
|
|
||||||
|
|
||||||
# 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"<b>Bot:</b> ❌ 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("<b>Bot:</b> ❌ No invoice filters found.")
|
|
||||||
else:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ API Error: {data.get('status_description', 'Unknown error')}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Exception when fetching invoice filters: <code>{str(e)}</code>")
|
|
||||||
|
|
||||||
# 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"<b>📡 Raw API Response:</b><br><pre>{response.text}</pre>")
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Bot:</b> ❌ 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"<b>Current date:</b> {fmt_date}<br>")
|
|
||||||
|
|
||||||
# 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"<b>{label}:</b><br>")
|
|
||||||
for lbl, val in zip(labels, values):
|
|
||||||
self.chat_area.append(f"{lbl}: <b>{val}</b><br>")
|
|
||||||
self.chat_area.append("<br>") # 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"<b>{label}:</b><br>")
|
|
||||||
for lbl, val in zip(labels, values):
|
|
||||||
self.chat_area.append(f"{lbl}: <b>{val}</b><br>")
|
|
||||||
else:
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ No data found in API response.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Exception fetching invoice count: <code>{str(e)}</code>")
|
|
||||||
|
|
||||||
|
|
||||||
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"<b>📡 Raw API Response:</b><br><pre>{response.text}</pre>")
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ 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"<b>Bot:</b> ❌ 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"<b>Current date:</b> {fmt_date}<br>")
|
|
||||||
|
|
||||||
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"<b>{label}:</b><br>")
|
|
||||||
for lbl, val in zip(labels, values):
|
|
||||||
self.chat_area.append(f"{lbl}: <b>{val}</b><br>")
|
|
||||||
self.chat_area.append("<br>")
|
|
||||||
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"<b>{label}:</b><br>")
|
|
||||||
for lbl, val in zip(labels, values):
|
|
||||||
self.chat_area.append(f"{lbl}: <b>{val}</b><br>")
|
|
||||||
else:
|
|
||||||
self.chat_area.append("<b>Bot:</b> ❌ No invoice quantity data found.")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.chat_area.append(f"<b>Bot:</b> ❌ Exception fetching invoice quantity: <code>{str(e)}</code>")
|
|
||||||
|
|
||||||
|
|
||||||
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()
|
|
||||||
Reference in New Issue
Block a user