/
devsec
/
anothertap
Обзор
Документация
Войти
/
devsec
/
anothertap
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/gui/test_plan_editor_widget.py
372 строки
14 KB
devsec
init project
23 авг 2025, 21:01
23 авг 2025, 21:01
5b0e5af
Код
Авторство
О чём код?
""" Test Plan Editor Widget for creating and managing test plans with step management """ from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QSplitter, QTreeWidget, QTreeWidgetItem, QGroupBox, QFormLayout, QLineEdit, QComboBox, QTextEdit, QPushButton, QMessageBox, QDialog, QDialogButtonBox, QLabel, QTableWidget, QTableWidgetItem, QHeaderView, QCheckBox, QMenu, QListWidget, QListWidgetItem ) from PySide6.QtCore import Qt, Signal from PySide6.QtGui import QAction from models.test_plan import TestPlan, PlanStatus from models.test_step import TestStep, StepType, StepStatus from models.device import Device from services.test_plan_manager import TestPlanManager from services.device_manager import DeviceManager class TestPlanEditorWidget(QWidget): """Widget for editing test plans and managing test steps""" test_plan_selected = Signal(str) # plan_id def __init__(self, test_plan_manager: TestPlanManager, device_manager: DeviceManager): super().__init__() self.test_plan_manager = test_plan_manager self.device_manager = device_manager self.current_plan = None self.setup_ui() self.refresh_test_plans() def setup_ui(self): """Set up the user interface""" layout = QHBoxLayout(self) # Create splitter for resizable panels splitter = QSplitter(Qt.Horizontal) layout.addWidget(splitter) # Left panel - Test plan list self.setup_test_plan_list(splitter) # Right panel - Test plan details self.setup_test_plan_details(splitter) # Set splitter proportions splitter.setSizes([300, 700]) def setup_test_plan_list(self, parent): """Set up the test plan list panel""" list_widget = QWidget() list_layout = QVBoxLayout(list_widget) parent.addWidget(list_widget) # Header with buttons header_layout = QHBoxLayout() list_layout.addLayout(header_layout) header_layout.addWidget(QLabel("Test Plans")) header_layout.addStretch() self.new_plan_btn = QPushButton("New Plan") self.new_plan_btn.clicked.connect(self.new_test_plan) header_layout.addWidget(self.new_plan_btn) # Test plan list self.plan_list = QListWidget() self.plan_list.itemClicked.connect(self.on_plan_selected) list_layout.addWidget(self.plan_list) def setup_test_plan_details(self, parent): """Set up the test plan details panel""" details_widget = QWidget() details_layout = QVBoxLayout(details_widget) parent.addWidget(details_widget) # Plan info self.plan_info_group = QGroupBox("Test Plan Information") plan_info_layout = QFormLayout(self.plan_info_group) details_layout.addWidget(self.plan_info_group) self.plan_name_edit = QLineEdit() self.plan_name_edit.textChanged.connect(self.on_plan_info_changed) plan_info_layout.addRow("Name:", self.plan_name_edit) self.plan_description_edit = QTextEdit() self.plan_description_edit.setMaximumHeight(60) self.plan_description_edit.textChanged.connect(self.on_plan_info_changed) plan_info_layout.addRow("Description:", self.plan_description_edit) # Steps section steps_header_layout = QHBoxLayout() details_layout.addLayout(steps_header_layout) steps_header_layout.addWidget(QLabel("Test Steps")) steps_header_layout.addStretch() self.add_step_btn = QPushButton("Add Step") self.add_step_btn.clicked.connect(self.add_test_step) steps_header_layout.addWidget(self.add_step_btn) # Steps table self.steps_table = QTableWidget() self.steps_table.setColumnCount(5) self.steps_table.setHorizontalHeaderLabels(["Name", "Type", "Device", "Command", "Status"]) # Set column widths header = self.steps_table.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.Stretch) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(3, QHeaderView.Stretch) header.setSectionResizeMode(4, QHeaderView.ResizeToContents) self.steps_table.itemClicked.connect(self.on_step_selected) details_layout.addWidget(self.steps_table) # Action buttons action_layout = QHBoxLayout() details_layout.addLayout(action_layout) self.save_plan_btn = QPushButton("Save Plan") self.save_plan_btn.clicked.connect(self.save_test_plan) self.save_plan_btn.setEnabled(False) action_layout.addWidget(self.save_plan_btn) self.validate_plan_btn = QPushButton("Validate") self.validate_plan_btn.clicked.connect(self.validate_test_plan) action_layout.addWidget(self.validate_plan_btn) action_layout.addStretch() self.delete_plan_btn = QPushButton("Delete Plan") self.delete_plan_btn.clicked.connect(self.delete_test_plan) self.delete_plan_btn.setStyleSheet("color: red;") action_layout.addWidget(self.delete_plan_btn) def refresh_test_plans(self): """Refresh the test plan list""" self.plan_list.clear() plans = self.test_plan_manager.get_all_test_plans() for plan in plans: item = QListWidgetItem(plan.display_name) item.setData(Qt.UserRole, plan.id) self.plan_list.addItem(item) def new_test_plan(self): """Create a new test plan""" dialog = TestPlanDialog(self) if dialog.exec() == QDialog.Accepted: plan = dialog.get_test_plan() if self.test_plan_manager.create_test_plan(plan): self.refresh_test_plans() QMessageBox.information(self, "Success", "Test plan created successfully") else: QMessageBox.warning(self, "Error", "Failed to create test plan") def on_plan_selected(self, item): """Handle test plan selection""" if not item: return plan_id = item.data(Qt.UserRole) plan = self.test_plan_manager.get_test_plan(plan_id) if plan: self.current_plan = plan self.load_test_plan_details(plan) self.test_plan_selected.emit(plan.id) def load_test_plan_details(self, plan: TestPlan): """Load test plan details into the form""" self.plan_name_edit.setText(plan.name) self.plan_description_edit.setText(plan.description) # Load steps self.load_steps_into_table(plan.steps) self.save_plan_btn.setEnabled(False) def load_steps_into_table(self, steps: list): """Load steps into the table widget""" self.steps_table.setRowCount(len(steps)) for i, step in enumerate(steps): self.steps_table.setItem(i, 0, QTableWidgetItem(step.name)) self.steps_table.setItem(i, 1, QTableWidgetItem(step.step_type.value)) # Device name device = self.device_manager.get_device(step.device_id) device_name = device.display_name if device else "(No Device)" self.steps_table.setItem(i, 2, QTableWidgetItem(device_name)) self.steps_table.setItem(i, 3, QTableWidgetItem(step.scpi_command)) self.steps_table.setItem(i, 4, QTableWidgetItem(step.status.value)) # Store step ID in first column self.steps_table.item(i, 0).setData(Qt.UserRole, step.id) def on_plan_info_changed(self): """Handle test plan information changes""" if self.current_plan: self.save_plan_btn.setEnabled(True) def save_test_plan(self): """Save current test plan changes""" if not self.current_plan: return # Update plan from form self.current_plan.name = self.plan_name_edit.text() self.current_plan.description = self.plan_description_edit.toPlainText() # Save to manager if self.test_plan_manager.update_test_plan(self.current_plan): self.save_plan_btn.setEnabled(False) self.refresh_test_plans() QMessageBox.information(self, "Success", "Test plan saved successfully") else: QMessageBox.warning(self, "Error", "Failed to save test plan") def validate_test_plan(self): """Validate the current test plan""" if not self.current_plan: QMessageBox.information(self, "Info", "No test plan selected") return issues = self.current_plan.validate_plan() if issues: message = "Validation issues found:\\n\\n" + "\\n".join(f"• {issue}" for issue in issues) QMessageBox.warning(self, "Validation Failed", message) else: QMessageBox.information(self, "Validation Passed", "Test plan is valid and ready for execution") def delete_test_plan(self): """Delete current test plan""" if not self.current_plan: return reply = QMessageBox.question( self, "Confirm Delete", f"Are you sure you want to delete test plan '{self.current_plan.display_name}'?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) if reply == QMessageBox.Yes: if self.test_plan_manager.delete_test_plan(self.current_plan.id): self.refresh_test_plans() self.current_plan = None self.plan_name_edit.clear() self.plan_description_edit.clear() self.steps_table.setRowCount(0) else: QMessageBox.warning(self, "Error", "Failed to delete test plan") def add_test_step(self): """Add a new test step""" if not self.current_plan: QMessageBox.information(self, "Info", "Please select a test plan first") return dialog = TestStepDialog(self.device_manager, self) if dialog.exec() == QDialog.Accepted: step = dialog.get_test_step() self.current_plan.steps.append(step) self.load_steps_into_table(self.current_plan.steps) self.save_plan_btn.setEnabled(True) def on_step_selected(self, item): """Handle test step selection""" # Could show step details in a separate panel pass def get_current_test_plan(self): """Get the currently selected test plan""" return self.current_plan class TestPlanDialog(QDialog): """Dialog for creating test plans""" def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("New Test Plan") self.setModal(True) self.resize(400, 250) self.setup_ui() def setup_ui(self): layout = QVBoxLayout(self) form_layout = QFormLayout() layout.addLayout(form_layout) self.name_edit = QLineEdit() form_layout.addRow("Name:", self.name_edit) self.description_edit = QTextEdit() self.description_edit.setMaximumHeight(80) form_layout.addRow("Description:", self.description_edit) self.author_edit = QLineEdit() form_layout.addRow("Author:", self.author_edit) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) def get_test_plan(self) -> TestPlan: return TestPlan( name=self.name_edit.text(), description=self.description_edit.toPlainText(), author=self.author_edit.text() ) class TestStepDialog(QDialog): """Dialog for creating test steps""" def __init__(self, device_manager: DeviceManager, parent=None): super().__init__(parent) self.device_manager = device_manager self.setWindowTitle("New Test Step") self.setModal(True) self.resize(400, 300) self.setup_ui() def setup_ui(self): layout = QVBoxLayout(self) form_layout = QFormLayout() layout.addLayout(form_layout) self.name_edit = QLineEdit() form_layout.addRow("Name:", self.name_edit) self.description_edit = QTextEdit() self.description_edit.setMaximumHeight(60) form_layout.addRow("Description:", self.description_edit) self.step_type_combo = QComboBox() for step_type in StepType: self.step_type_combo.addItem(step_type.value) form_layout.addRow("Type:", self.step_type_combo) self.device_combo = QComboBox() self.device_combo.addItem("(No Device)", "") devices = self.device_manager.get_all_devices() for device in devices: self.device_combo.addItem(device.display_name, device.id) form_layout.addRow("Device:", self.device_combo) self.scpi_command_edit = QLineEdit() form_layout.addRow("SCPI Command:", self.scpi_command_edit) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(self.accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) def get_test_step(self) -> TestStep: return TestStep( name=self.name_edit.text(), description=self.description_edit.toPlainText(), step_type=StepType(self.step_type_combo.currentText()), device_id=self.device_combo.currentData(), scpi_command=self.scpi_command_edit.text() )