2021-05-08 06:32:15 +02:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
import html
|
2021-10-03 10:59:42 +02:00
|
|
|
from typing import Any
|
2021-05-08 06:32:15 +02:00
|
|
|
|
|
|
|
import anki
|
|
|
|
import anki.find
|
|
|
|
import aqt
|
|
|
|
from anki.collection import SearchNode
|
2021-05-08 08:56:51 +02:00
|
|
|
from anki.notes import NoteId
|
2021-05-08 06:32:15 +02:00
|
|
|
from aqt.qt import *
|
|
|
|
|
2021-05-08 08:56:51 +02:00
|
|
|
from ..operations import QueryOp
|
|
|
|
from ..operations.tag import add_tags_to_notes
|
2021-05-08 06:32:15 +02:00
|
|
|
from ..utils import (
|
|
|
|
disable_help_button,
|
|
|
|
restore_combo_history,
|
|
|
|
restore_combo_index_for_session,
|
|
|
|
restoreGeom,
|
|
|
|
save_combo_history,
|
|
|
|
save_combo_index_for_session,
|
|
|
|
saveGeom,
|
|
|
|
tr,
|
|
|
|
)
|
|
|
|
from . import Browser
|
|
|
|
|
|
|
|
|
|
|
|
class FindDuplicatesDialog(QDialog):
|
|
|
|
def __init__(self, browser: Browser, mw: aqt.AnkiQt):
|
|
|
|
super().__init__(parent=browser)
|
|
|
|
self.browser = browser
|
|
|
|
self.mw = mw
|
|
|
|
self.mw.garbage_collect_on_dialog_finish(self)
|
2021-05-08 08:56:51 +02:00
|
|
|
self.form = form = aqt.forms.finddupes.Ui_Dialog()
|
2021-05-08 06:32:15 +02:00
|
|
|
form.setupUi(self)
|
|
|
|
restoreGeom(self, "findDupes")
|
|
|
|
disable_help_button(self)
|
|
|
|
searchHistory = restore_combo_history(form.search, "findDupesFind")
|
|
|
|
|
|
|
|
fields = sorted(
|
|
|
|
anki.find.fieldNames(self.mw.col, downcase=False), key=lambda x: x.lower()
|
|
|
|
)
|
|
|
|
form.fields.addItems(fields)
|
|
|
|
restore_combo_index_for_session(form.fields, fields, "findDupesFields")
|
2021-10-03 10:59:42 +02:00
|
|
|
self._dupesButton: QPushButton | None = None
|
2021-10-07 04:49:41 +02:00
|
|
|
self._dupes: list[tuple[str, list[NoteId]]] = []
|
2021-05-08 06:32:15 +02:00
|
|
|
|
|
|
|
# links
|
|
|
|
form.webView.set_title("find duplicates")
|
2021-05-08 08:56:51 +02:00
|
|
|
form.webView.set_bridge_command(self._on_duplicate_clicked, context=self)
|
2021-05-08 06:32:15 +02:00
|
|
|
form.webView.stdHtml("", context=self)
|
|
|
|
|
2021-05-08 08:56:51 +02:00
|
|
|
def on_finished(code: Any) -> None:
|
2021-05-08 06:32:15 +02:00
|
|
|
saveGeom(self, "findDupes")
|
|
|
|
|
2021-05-08 08:56:51 +02:00
|
|
|
qconnect(self.finished, on_finished)
|
2021-05-08 06:32:15 +02:00
|
|
|
|
2021-05-08 08:56:51 +02:00
|
|
|
def on_click() -> None:
|
2021-05-08 06:32:15 +02:00
|
|
|
search_text = save_combo_history(
|
|
|
|
form.search, searchHistory, "findDupesFind"
|
|
|
|
)
|
|
|
|
save_combo_index_for_session(form.fields, "findDupesFields")
|
|
|
|
field = fields[form.fields.currentIndex()]
|
2021-05-08 08:56:51 +02:00
|
|
|
QueryOp(
|
|
|
|
parent=self.browser,
|
2021-06-27 07:12:22 +02:00
|
|
|
op=lambda col: col.find_dupes(field, search_text),
|
2021-05-08 08:56:51 +02:00
|
|
|
success=self.show_duplicates_report,
|
|
|
|
).run_in_background()
|
2021-05-08 06:32:15 +02:00
|
|
|
|
|
|
|
search = form.buttonBox.addButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
tr.actions_search(), QDialogButtonBox.ButtonRole.ActionRole
|
2021-05-08 06:32:15 +02:00
|
|
|
)
|
2021-05-08 08:56:51 +02:00
|
|
|
qconnect(search.clicked, on_click)
|
2021-05-08 06:32:15 +02:00
|
|
|
self.show()
|
|
|
|
|
2021-10-03 10:59:42 +02:00
|
|
|
def show_duplicates_report(self, dupes: list[tuple[str, list[NoteId]]]) -> None:
|
2021-10-07 04:49:41 +02:00
|
|
|
self._dupes = dupes
|
2021-05-08 06:32:15 +02:00
|
|
|
if not self._dupesButton:
|
2021-05-08 08:56:51 +02:00
|
|
|
self._dupesButton = b = self.form.buttonBox.addButton(
|
2021-10-05 05:53:01 +02:00
|
|
|
tr.browsing_tag_duplicates(), QDialogButtonBox.ButtonRole.ActionRole
|
2021-05-08 06:32:15 +02:00
|
|
|
)
|
2021-10-07 04:49:41 +02:00
|
|
|
qconnect(b.clicked, self._tag_duplicates)
|
2021-05-08 08:56:51 +02:00
|
|
|
text = ""
|
|
|
|
groups = len(dupes)
|
|
|
|
notes = sum(len(r[1]) for r in dupes)
|
2021-05-08 06:32:15 +02:00
|
|
|
part1 = tr.browsing_group(count=groups)
|
|
|
|
part2 = tr.browsing_note_count(count=notes)
|
2021-05-08 08:56:51 +02:00
|
|
|
text += tr.browsing_found_as_across_bs(part=part1, whole=part2)
|
|
|
|
text += "<p><ol>"
|
|
|
|
for val, nids in dupes:
|
|
|
|
text += (
|
2021-05-08 06:32:15 +02:00
|
|
|
"""<li><a href=# onclick="pycmd('%s');return false;">%s</a>: %s</a>"""
|
|
|
|
% (
|
|
|
|
html.escape(
|
|
|
|
self.mw.col.build_search_string(
|
|
|
|
SearchNode(nids=SearchNode.IdList(ids=nids))
|
|
|
|
)
|
|
|
|
),
|
|
|
|
tr.browsing_note_count(count=len(nids)),
|
|
|
|
html.escape(val),
|
|
|
|
)
|
|
|
|
)
|
2021-05-08 08:56:51 +02:00
|
|
|
text += "</ol>"
|
|
|
|
self.form.webView.stdHtml(text, context=self)
|
2021-05-08 06:32:15 +02:00
|
|
|
|
2021-10-07 04:49:41 +02:00
|
|
|
def _tag_duplicates(self) -> None:
|
|
|
|
if not self._dupes:
|
2021-05-08 06:32:15 +02:00
|
|
|
return
|
2021-05-08 08:56:51 +02:00
|
|
|
|
|
|
|
note_ids = set()
|
2021-10-07 04:49:41 +02:00
|
|
|
for _, nids in self._dupes:
|
2021-05-08 08:56:51 +02:00
|
|
|
note_ids.update(nids)
|
|
|
|
|
|
|
|
add_tags_to_notes(
|
|
|
|
parent=self,
|
|
|
|
note_ids=list(note_ids),
|
|
|
|
space_separated_tags=tr.browsing_duplicate(),
|
|
|
|
).run_in_background()
|
|
|
|
|
|
|
|
def _on_duplicate_clicked(self, link: str) -> None:
|
2021-05-08 06:32:15 +02:00
|
|
|
self.browser.search_for(link)
|
|
|
|
self.browser.onNote()
|