studia/jezyki-skryptowe/lista8/gui.py
2024-06-14 16:53:58 +02:00

250 lines
7.5 KiB
Python

from datetime import datetime, timedelta
import sys
from PyQt6.QtWidgets import (
QApplication,
QWidget,
QVBoxLayout,
QDateEdit,
QGridLayout,
QHBoxLayout,
QPushButton,
QLabel,
QListWidget,
QListWidgetItem,
QFrame,
QFileDialog,
QMessageBox
)
from parser import ApacheLogEntry
DATA_ROLE = 1001
LINES_PER_PAGE = 100
LABELS = [
"Host",
"Date",
"Time",
"Timezone",
"Status code",
"Method",
"Path",
"Size",
]
class LogViewer(QWidget):
def __init__(self):
self.value_labels = {}
super().__init__()
self.init()
self.current_page = 0
self.total_pages = 0
self.file_lines = []
def init(self):
self.setWindowTitle("Log Viewer")
self.setGeometry(100, 100, 900, 500)
mainLayout = QVBoxLayout()
topLayout = QHBoxLayout()
self.pathLabel = QLabel("Select working file...")
self.openButton = QPushButton("Open")
self.openButton.clicked.connect(self.open_file_dialog)
topLayout.addWidget(self.pathLabel)
topLayout.addWidget(self.openButton)
filterLayout = QHBoxLayout()
self.fromDateEdit = QDateEdit()
self.fromDateEdit.setDate(datetime.now() - timedelta(days=7))
self.toDateEdit = QDateEdit()
self.toDateEdit.setDate(datetime.now())
self.filterButton = QPushButton("Filter")
self.filterButton.clicked.connect(self.apply_date_filter)
filterLayout.addWidget(QLabel("From"))
filterLayout.addWidget(self.fromDateEdit)
filterLayout.addWidget(QLabel("To"))
filterLayout.addWidget(self.toDateEdit)
filterLayout.addWidget(self.filterButton)
middleLayout = QHBoxLayout()
self.logListWidget = QListWidget()
self.logListWidget.itemClicked.connect(self.select_log)
middleLayout.addWidget(self.logListWidget, 2)
self.detailsFrame = QFrame()
detailsLayout = QVBoxLayout(self.detailsFrame)
self.create_details_widget(detailsLayout)
middleLayout.addWidget(self.detailsFrame, 2)
bottomLayout = QHBoxLayout()
self.prevButton = QPushButton("Previous")
self.nextButton = QPushButton("Next")
self.prevButton.clicked.connect(self.show_previous_page)
self.nextButton.clicked.connect(self.show_next_page)
self.pageLabel = QLabel("")
bottomLayout.addWidget(self.prevButton)
bottomLayout.addStretch()
bottomLayout.addWidget(self.pageLabel)
bottomLayout.addStretch()
bottomLayout.addWidget(self.nextButton)
mainLayout.addLayout(topLayout)
mainLayout.addLayout(filterLayout)
mainLayout.addLayout(middleLayout)
mainLayout.addLayout(bottomLayout)
self.setLayout(mainLayout)
def reset_ui(self):
self.pathLabel.setText("Select working file...")
self.prevButton.setEnabled(False)
self.nextButton.setEnabled(True)
self.pageLabel.setText("")
self.logListWidget.clear()
for label in LABELS:
self.value_labels[label].setText("")
def apply_date_filter(self):
from_date = self.fromDateEdit.date().toPyDate()
to_date = self.toDateEdit.date().toPyDate()
if from_date > to_date:
self.show_error("Date 'From' must not be after date 'To'")
self.filtered_lines = []
for line in self.file_lines:
log = ApacheLogEntry.from_log(line)
if not log:
self.show_parse_error(line)
continue
date = datetime.strptime(log.timestamp.strftime("%Y-%m-%d"), "%Y-%m-%d").date()
if from_date <= date:
if date <= to_date:
self.filtered_lines.append(line)
else:
break
self.total_pages = (len(self.filtered_lines) - 1) // LINES_PER_PAGE + 1
self.current_page = 0
self.update_list()
def select_log(self, item):
log = item.data(DATA_ROLE)
self.value_labels["Host"].setText(log.host)
self.value_labels["Date"].setText(log.timestamp.strftime("%Y-%m-%d"))
self.value_labels["Time"].setText(log.timestamp.strftime("%H:%M:%S"))
self.value_labels["Timezone"].setText(log.timestamp.strftime("%z"))
self.value_labels["Status code"].setText(str(log.status_code))
self.value_labels["Method"].setText(log.method)
self.value_labels["Path"].setText(log.path)
self.value_labels["Size"].setText(str(log.bytes_sent or "-"))
def create_details_widget(self, layout):
gridLayout = QGridLayout()
for index, label in enumerate(LABELS):
labelWidget = QLabel(f"{label}:")
valueLabel = QLabel()
gridLayout.addWidget(labelWidget, index, 0)
gridLayout.addWidget(valueLabel, index, 1)
self.value_labels[label] = valueLabel
layout.addLayout(gridLayout)
def open_file_dialog(self):
fileName, _ = QFileDialog.getOpenFileName(
self,
"Open Log File",
"",
"All Files (*)",
)
if fileName:
self.pathLabel.setText(fileName)
self.load_log_file(fileName)
def load_log_file(self, file_path):
self.file_lines = []
self.reset_ui()
try:
with open(file_path, "r") as file:
self.file_lines = file.readlines()
self.filtered_lines = self.file_lines
except:
self.show_error(f"could not open file {file_path}")
return
self.total_pages = (len(self.file_lines) - 1) // LINES_PER_PAGE + 1
if self.total_pages == 1:
self.nextButton.setEnabled(False)
self.current_page = 0
self.update_list()
def update_list(self):
self.logListWidget.clear()
start_index = self.current_page * LINES_PER_PAGE
end_index = start_index + LINES_PER_PAGE
self.pageLabel.setText(f"{self.current_page + 1} / {max(1, self.total_pages)}")
for line in self.filtered_lines[start_index:end_index]:
parsed_data = ApacheLogEntry.from_log(line.strip())
if not parsed_data:
self.show_parse_error(line)
continue
item = QListWidgetItem(line.strip())
item.setData(DATA_ROLE, parsed_data)
self.logListWidget.addItem(item)
def show_next_page(self):
if self.current_page < self.total_pages - 1:
self.current_page += 1
self.update_list()
self.prevButton.setEnabled(True)
if self.current_page == self.total_pages - 1:
self.nextButton.setEnabled(False)
def show_previous_page(self):
if self.current_page > 0:
self.current_page -= 1
self.update_list()
self.nextButton.setEnabled(True)
if self.current_page == 0:
self.prevButton.setEnabled(False)
def show_error(self, text):
message_box = QMessageBox()
message_box.setIcon(QMessageBox.Icon.Critical)
message_box.setText("Error")
message_box.setWindowTitle("Error")
message_box.setInformativeText(text)
message_box.exec()
def show_parse_error(self, line):
self.show_error(f"Failed to parse data from line: {line}, ignoring.")
def main():
app = QApplication(sys.argv)
ex = LogViewer()
ex.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()