dev: 增加相似度检查
This commit is contained in:
@@ -9,7 +9,9 @@ columns_d = {
|
||||
"opt": Column("opt", DataType.TEXT),
|
||||
"url": Column("url", DataType.BLOB),
|
||||
"notes": Column("notes", DataType.BLOB),
|
||||
"path": Column("path", DataType.BLOB, nullable=False),
|
||||
"uuid": Column("uuid", DataType.TEXT, nullable=False),
|
||||
"filepath": Column("filepath", DataType.BLOB, nullable=False),
|
||||
"path": Column("path", DataType.BLOB),
|
||||
}
|
||||
|
||||
all_columns = [
|
||||
@@ -20,6 +22,8 @@ all_columns = [
|
||||
columns_d["opt"],
|
||||
columns_d["url"],
|
||||
columns_d["notes"],
|
||||
columns_d["uuid"],
|
||||
columns_d["filepath"],
|
||||
columns_d["path"],
|
||||
]
|
||||
|
||||
@@ -29,3 +33,9 @@ query_columns = [
|
||||
columns_d["username"],
|
||||
columns_d["url"],
|
||||
]
|
||||
|
||||
# 从数据库中读取 UUID 和 文件路径分析相似度
|
||||
sim_columns = [
|
||||
columns_d["uuid"],
|
||||
columns_d["filepath"],
|
||||
]
|
||||
|
||||
@@ -41,7 +41,9 @@ def read_kps_to_db(kps_file: str | PathLike[str], password: str,
|
||||
extract_otp(entry.otp),
|
||||
blob_fy(trim_str(entry.url)),
|
||||
blob_fy(entry.notes),
|
||||
blob_fy("::".join([kps_file] + entry.path[:-1])),
|
||||
str(entry.uuid),
|
||||
blob_fy(kps_file),
|
||||
blob_fy("::".join(entry.path[:-1])),
|
||||
])
|
||||
|
||||
sqh.insert_into(table_name, all_columns[1:], values)
|
||||
|
||||
@@ -8,7 +8,13 @@ from lib.kps_operations import read_kps_to_db
|
||||
|
||||
|
||||
class GbxKpsLogin(QtWidgets.QGroupBox):
|
||||
def __init__(self, path: str, sqh: Sqlite3Worker, config: dict, parent=None):
|
||||
def __init__(
|
||||
self,
|
||||
path: str,
|
||||
sqh: Sqlite3Worker,
|
||||
config: dict,
|
||||
parent: QtWidgets.QWidget = None
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.sqh = sqh
|
||||
self.config = config
|
||||
@@ -75,10 +81,12 @@ class GbxKpsLogin(QtWidgets.QGroupBox):
|
||||
|
||||
def on_pbn_load_clicked(self):
|
||||
try:
|
||||
read_kps_to_db(kps_file=self.lne_path.text(),
|
||||
password=self.lne_password.text(),
|
||||
table_name=self.config["table_name"],
|
||||
sqh=self.sqh)
|
||||
read_kps_to_db(
|
||||
kps_file=self.lne_path.text(),
|
||||
password=self.lne_password.text(),
|
||||
table_name=self.config["table_name"],
|
||||
sqh=self.sqh
|
||||
)
|
||||
except CredentialsError:
|
||||
QtWidgets.QMessageBox.critical(self, "密码错误",
|
||||
f"{self.lne_path.text()}\n密码错误")
|
||||
|
||||
@@ -3,6 +3,8 @@ from PySide6 import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .page_load import PageLoad
|
||||
from .page_query import PageQuery
|
||||
from .page_similar import PageSimilar
|
||||
|
||||
from .cmbx_styles import StyleComboBox
|
||||
from lib.Sqlite3Helper import Sqlite3Worker
|
||||
from lib.db_columns_def import all_columns
|
||||
@@ -10,7 +12,13 @@ from lib.config_utils import write_config
|
||||
|
||||
|
||||
class UiKpsUnifier(object):
|
||||
def __init__(self, default_db_path: str, config: dict, sqh: Sqlite3Worker, window: QtWidgets.QMainWindow):
|
||||
def __init__(
|
||||
self,
|
||||
default_db_path: str,
|
||||
config: dict,
|
||||
sqh: Sqlite3Worker,
|
||||
window: QtWidgets.QMainWindow
|
||||
):
|
||||
window.setWindowTitle('KeePassXC 多合一')
|
||||
self.cw = QtWidgets.QWidget(window)
|
||||
self.vly_m = QtWidgets.QVBoxLayout()
|
||||
@@ -26,7 +34,8 @@ class UiKpsUnifier(object):
|
||||
self.menu_view = self.menu_bar.addMenu("视图")
|
||||
self.act_load = QtGui.QAction("加载", self.cw)
|
||||
self.act_query = QtGui.QAction("查询", self.cw)
|
||||
self.menu_view.addActions([self.act_load, self.act_query])
|
||||
self.act_similar = QtGui.QAction("相似度", self.cw)
|
||||
self.menu_view.addActions([self.act_load, self.act_query, self.act_similar])
|
||||
|
||||
self.menu_help = self.menu_bar.addMenu("帮助")
|
||||
self.act_about = QtGui.QAction("关于", self.cw)
|
||||
@@ -51,6 +60,8 @@ class UiKpsUnifier(object):
|
||||
self.sw_m.addWidget(self.page_load)
|
||||
self.page_query = PageQuery(sqh, config, self.cw)
|
||||
self.sw_m.addWidget(self.page_query)
|
||||
self.page_similar = PageSimilar(sqh, config, self.cw)
|
||||
self.sw_m.addWidget(self.page_similar)
|
||||
|
||||
def update_sqh(self, sqh: Sqlite3Worker):
|
||||
self.page_load.update_sqh(sqh)
|
||||
@@ -71,6 +82,8 @@ class KpsUnifier(QtWidgets.QMainWindow):
|
||||
self.ui.act_open.triggered.connect(self.on_act_open_triggered)
|
||||
self.ui.act_load.triggered.connect(self.on_act_load_triggered)
|
||||
self.ui.act_query.triggered.connect(self.on_act_query_triggered)
|
||||
self.ui.act_similar.triggered.connect(self.on_act_similar_triggered)
|
||||
|
||||
self.ui.act_about.triggered.connect(self.on_act_about_triggered)
|
||||
self.ui.act_about_qt.triggered.connect(self.on_act_about_qt_triggered)
|
||||
|
||||
@@ -114,6 +127,9 @@ class KpsUnifier(QtWidgets.QMainWindow):
|
||||
def on_act_query_triggered(self):
|
||||
self.ui.sw_m.setCurrentIndex(1)
|
||||
|
||||
def on_act_similar_triggered(self):
|
||||
self.ui.sw_m.setCurrentIndex(2)
|
||||
|
||||
def on_act_about_triggered(self):
|
||||
QtWidgets.QMessageBox.about(
|
||||
self,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# coding: utf8
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
from .gbx_kps_login import GbxKpsLogin
|
||||
@@ -10,7 +9,12 @@ from lib.Sqlite3Helper import Sqlite3Worker
|
||||
|
||||
|
||||
class WgLoadKps(QtWidgets.QWidget):
|
||||
def __init__(self, sqh: Sqlite3Worker, config: dict, parent=None):
|
||||
def __init__(
|
||||
self,
|
||||
sqh: Sqlite3Worker,
|
||||
config: dict,
|
||||
parent: QtWidgets.QWidget = None
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.sqh = sqh
|
||||
self.config = config
|
||||
@@ -55,7 +59,12 @@ class WgLoadKps(QtWidgets.QWidget):
|
||||
|
||||
|
||||
class PageLoad(QtWidgets.QWidget):
|
||||
def __init__(self, sqh: Sqlite3Worker, config: dict, parent=None):
|
||||
def __init__(
|
||||
self,
|
||||
sqh: Sqlite3Worker,
|
||||
config: dict,
|
||||
parent: QtWidgets.QWidget = None
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.sqh = sqh
|
||||
self.config = config
|
||||
|
||||
92
src/page_similar.py
Normal file
92
src/page_similar.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# coding: utf8
|
||||
from itertools import combinations
|
||||
from uuid import UUID
|
||||
from PySide6 import QtWidgets, QtCore
|
||||
from PySide6.QtCore import QAbstractTableModel
|
||||
|
||||
from lib.Sqlite3Helper import Sqlite3Worker
|
||||
from lib.db_columns_def import sim_columns
|
||||
|
||||
|
||||
class SimilarDataModel(QAbstractTableModel):
|
||||
def __init__(self, similar_data: list[tuple], parent=None):
|
||||
super().__init__(parent)
|
||||
self.similar_data = similar_data
|
||||
self.headers = ["文件1", "文件2", "相似度"]
|
||||
|
||||
def rowCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.similar_data)
|
||||
|
||||
def columnCount(self, parent: QtCore.QModelIndex = ...):
|
||||
return len(self.headers)
|
||||
|
||||
def data(self, index: QtCore.QModelIndex, role: int = ...):
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return self.similar_data[index.row()][index.column()]
|
||||
if role == QtCore.Qt.ItemDataRole.TextAlignmentRole:
|
||||
if index.column() == 2:
|
||||
return QtCore.Qt.AlignmentFlag.AlignRight | QtCore.Qt.AlignmentFlag.AlignVCenter
|
||||
|
||||
def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = ...):
|
||||
if orientation == QtCore.Qt.Orientation.Horizontal:
|
||||
if role == QtCore.Qt.ItemDataRole.DisplayRole:
|
||||
return self.headers[section]
|
||||
|
||||
|
||||
class PageSimilar(QtWidgets.QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
sqh: Sqlite3Worker,
|
||||
config: dict,
|
||||
parent: QtWidgets.QWidget = None
|
||||
):
|
||||
super().__init__(parent)
|
||||
self.sqh = sqh
|
||||
self.config = config
|
||||
|
||||
self.hly_m = QtWidgets.QHBoxLayout()
|
||||
self.setLayout(self.hly_m)
|
||||
|
||||
self.vly_left = QtWidgets.QVBoxLayout()
|
||||
self.hly_m.addLayout(self.vly_left)
|
||||
|
||||
self.pbn_read_db = QtWidgets.QPushButton("读取数据库", self)
|
||||
self.pbn_read_db.setMinimumWidth(config["button_min_width"])
|
||||
self.vly_left.addWidget(self.pbn_read_db)
|
||||
|
||||
self.vly_left.addStretch(1)
|
||||
|
||||
self.tbv_m = QtWidgets.QTableView(self)
|
||||
self.hly_m.addWidget(self.tbv_m)
|
||||
|
||||
self.pbn_read_db.clicked.connect(self.on_pbn_read_db_clicked)
|
||||
|
||||
def on_pbn_read_db_clicked(self):
|
||||
_, results = self.sqh.select(self.config["table_name"], sim_columns)
|
||||
file_uuids: dict[str, list[UUID]] = {}
|
||||
for u, filepath in results:
|
||||
filepath = filepath.decode("utf8")
|
||||
if filepath not in file_uuids:
|
||||
file_uuids[filepath] = []
|
||||
file_uuids[filepath].append(u)
|
||||
|
||||
files = file_uuids.keys()
|
||||
if len(files) < 2:
|
||||
QtWidgets.QMessageBox.warning(self, "警告", "数据库中存在的文件数少于 2,无法检查相似度。")
|
||||
return
|
||||
|
||||
similar_data: list[tuple] = []
|
||||
|
||||
comb = combinations(files, 2)
|
||||
for i, j in list(comb):
|
||||
uuids_i = file_uuids[i]
|
||||
uuids_j = file_uuids[j]
|
||||
len_i = len(uuids_i)
|
||||
len_j = len(uuids_j)
|
||||
|
||||
set_inter = set(uuids_i).intersection(uuids_j)
|
||||
sim = (len(set_inter) * 2) / (len_i + len_j)
|
||||
similar_data.append((i, j, f"{sim * 100:.2f}"))
|
||||
|
||||
model = SimilarDataModel(similar_data, self)
|
||||
self.tbv_m.setModel(model)
|
||||
Reference in New Issue
Block a user