Skip to content

Commit

Permalink
Settings update (#7)
Browse files Browse the repository at this point in the history
* Settings update
- Changed order and grouped settings
- Changed config.md to new version & html for correct display
- Added settings help button for additional descriptions
- Added button to settings to view log file

---------

Co-authored-by: FileX <filex.stuff@proton.me>
Co-authored-by: Ren Tatsumoto <tatsu@autistici.org>
  • Loading branch information
3 people committed Apr 15, 2024
1 parent 4fcb751 commit bdc50f2
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 39 deletions.
10 changes: 9 additions & 1 deletion common.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
WINDOW_STATE_FILE_PATH = os.path.join(USER_FILES_DIR_PATH, "window_state.json")
CLOSE_ICON_PATH = os.path.join(IMG_DIR_PATH, "close.png")
PLAY_ICON_PATH = os.path.join(IMG_DIR_PATH, "play-button.svg")
CONFIG_MD_PATH = os.path.join(ADDON_DIR_PATH, "config.md")

for directory in (WEB_DIR_PATH, USER_FILES_DIR_PATH, IMG_DIR_PATH):
assert os.path.isdir(directory), f"Path to directory must be valid: {directory}"

for file in (CLOSE_ICON_PATH, PLAY_ICON_PATH):
for file in (CLOSE_ICON_PATH, PLAY_ICON_PATH, CONFIG_MD_PATH):
assert os.path.isfile(file), f"Path to file must be valid: {file}"


Expand Down Expand Up @@ -57,6 +58,13 @@ def write(self, msg: str) -> None:
def __call__(self, *args, **kwargs):
return self.write(*args, **kwargs)

def read(self) -> str:
try:
with open(DEBUG_LOG_FILE_PATH, encoding="utf-8") as lf:
return lf.read()
except FileNotFoundError:
return ""

def close(self):
if self._logfile and not self._logfile.closed:
self.write("closing debug log.")
Expand Down
54 changes: 38 additions & 16 deletions config.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
## Cross-Profile Search and Import
<h2>Cross-Profile Search and Import</h2>

**Anki needs to be restarted after changing the config.**
<p>
<i>Anki needs to be restarted after changing the config file directly.</i>
</p>

### List of options:
<h3>List of options</h3>

* `enable_debug_log` - print debug information to `stdout` and to a log file.
Log location: `~/.local/share/Anki2/cropro.log` (GNU systems).
* `max_displayed_notes` - how many search result to display
* `exported_tag` - tag that is added to cards in the other profile
so that you could easily find and delete them later.
* `hidden_fields` - contents of fields that contain these keywords won't be shown.
* `allow_empty_search` - Search notes even if the search field is emtpy. May be slow.
* `call_add_cards_hook` - Calls the `add_cards_did_add_note` hook as soon as a note
is imported through the main CroPro window.
For addon evaluation purposes.
<details>
<summary>General</summary>
<ul>
<li><code>max_displayed_notes</code> | how many search result to display on one page</li>
<li><code>hidden_fields</code> | contents of fields that contain these keywords won't be shown.</li>
<li><code>skip_duplicates</code> | Skips cards, which are already existent in the collection</li>
<li><code>copy_tags</code> | Adds the category in case of web search and tags in all cases as anki tags</li>
<li><code>search_online</code> | Toggle between local profile's and immersion kit's search</li>
<li><code>show_note_preview</code> | Toggles the preview on the right side when having a card selected</li>
</ul>
</details>

---
<details>
<summary>Local Search</summary>
<ul>
<li><code>allow_empty_search</code> | Search notes even if the search field is emtpy. Will show EVERY card you got (very slow)</li>
<li><code>copy_card_data</code> | Copies data like due date</li>
<li><code>exported_tag</code> | Tag added to other profile's cards when imported</li>
</ul>
</details>

If you enjoy this add-on, please consider supporting my work by
**[pledging your support on Patreon](https://www.patreon.com/tatsumoto_ren)**.
<details>
<summary>High level settings</summary>
<ul>
<li><code>timeout_seconds</code> | How many seconds should we try to find cards online before giving up</li>
<li><code>enable_debug_log</code> | print debug information to <code>stdout</code> and to a log file.<br/>
Location: <code>~/.local/share/Anki2/subsearch_debug.log</code> (GNU systems) or <code>%APPDATA%/Anki2/subsearch_debug.log</code> (Windows).</li>
<li><code>call_add_cards_hook</code> | Calls the <code>add_cards_did_add_note</code> hook as soon as a note is imported.<br/>
For addon evaluation purposes. (<a href="https://ankiweb.net/shared/info/1207537045">example</a>)</li>
</ul>
</details>

<p>If you enjoy this add-on, please consider supporting my work by
<b><a href="https://tatsumoto.neocities.org/blog/donating-to-tatsumoto.html">making a donation</a></b>.
Thank you so much!
</p>
7 changes: 3 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

class CroProConfig(AddonConfigManager):
@property
def tag_original_notes(self) -> Optional[str]:
def exported_tag(self) -> Optional[str]:
"""
Tag that is added to original notes (in the other profile)
to mark that they have been copied to the current profile.
"""
return self["exported_tag"].strip()

@tag_original_notes.setter
def tag_original_notes(self, new_value: str) -> None:
@exported_tag.setter
def exported_tag(self, new_value: str) -> None:
self["exported_tag"] = new_value.strip()

@property
Expand Down Expand Up @@ -107,5 +107,4 @@ def call_add_cards_hook(self) -> bool:
"""
return self["call_add_cards_hook"]


config = CroProConfig()
4 changes: 2 additions & 2 deletions edit_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ def create_window(self, other_note: Optional[Note] = None) -> NoteId:
if "tags" in other_note and config.copy_tags:
self.new_note.tags = [tag for tag in other_note.tags if tag != "leech"]

if config.tag_original_notes:
if config.exported_tag:
assert other_note.id, "Other note must be in the database."
other_note.add_tag(config.tag_original_notes)
other_note.add_tag(config.exported_tag)
other_note.flush()

if d := current_add_dialog():
Expand Down
2 changes: 1 addition & 1 deletion note_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def _construct_new_note(
return NoteCreateResult(new_note, NoteCreateStatus.connection_error)
else:
copy_media_files(new_note, other_note)
if tag := config.tag_original_notes:
if tag := config.exported_tag:
other_note.add_tag(tag)
other_note.flush()
if config.copy_card_data:
Expand Down
85 changes: 70 additions & 15 deletions settings_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html

from aqt.qt import *
from aqt.utils import restoreGeom, saveGeom, disable_help_button
from aqt.utils import restoreGeom, saveGeom, disable_help_button, showText
from aqt.webview import AnkiWebView

from .ajt_common.utils import ui_translate
from .ajt_common.about_menu import tweak_window
from .common import ADDON_NAME, DEBUG_LOG_FILE_PATH
from .ajt_common.utils import ui_translate
from .common import ADDON_NAME, DEBUG_LOG_FILE_PATH, LogDebug, CONFIG_MD_PATH
from .config import config
from .widgets.item_edit import ItemEditBox
from .widgets.utils import CroProSpinBox
Expand All @@ -23,6 +24,7 @@ def make_checkboxes() -> dict[str, QCheckBox]:
return d


BUT_HELP = QDialogButtonBox.StandardButton.Help
BUT_OK = QDialogButtonBox.StandardButton.Ok
BUT_CANCEL = QDialogButtonBox.StandardButton.Cancel

Expand All @@ -33,12 +35,14 @@ class CroProSettingsDialog(QDialog):
def __init__(self, *args, **kwargs) -> None:
QDialog.__init__(self, *args, **kwargs)
disable_help_button(self)
self.tab_view = QTabWidget()
self.checkboxes = make_checkboxes()
self.tag_edit = QLineEdit(config.tag_original_notes)
self.tag_edit = QLineEdit(config.exported_tag)
self.max_notes_edit = CroProSpinBox(min_val=10, max_val=10_000, step=50, value=config.max_displayed_notes)
self.hidden_fields = ItemEditBox("Hidden fields", initial_values=config.hidden_fields)
self.web_timeout_spinbox = CroProSpinBox(min_val=1, max_val=999, step=1, value=config.timeout_seconds)
self.button_box = QDialogButtonBox(BUT_OK | BUT_CANCEL)
self.button_box = QDialogButtonBox(BUT_HELP | BUT_OK | BUT_CANCEL)
self._create_tabs()
self._setup_ui()
self.connect_widgets()
self.add_tooltips()
Expand All @@ -53,25 +57,49 @@ def _setup_ui(self) -> None:

def _make_layout(self) -> QLayout:
layout = QVBoxLayout()
layout.addLayout(self._make_form())
layout.addStretch()
layout.addWidget(self.tab_view)
layout.addWidget(self.button_box)
return layout

def _make_form(self) -> QLayout:
layout = QFormLayout()
def _create_tabs(self) -> None:
self.tab_view.addTab(self._make_general_tab(), "General")
self.tab_view.addTab(self._make_local_tab(), "Local Search")
self.tab_view.addTab(self._make_hl_tab(), "High Level")

def _make_general_tab(self) -> QWidget:
widget = QWidget()
widget.setLayout(layout := QFormLayout())
layout.addRow("Max displayed notes", self.max_notes_edit)
layout.addRow("Tag original cards with", self.tag_edit)
layout.addRow("Web download timeout", self.web_timeout_spinbox)
layout.addRow(self.hidden_fields)
layout.addRow(self.checkboxes["skip_duplicates"])
layout.addRow(self.checkboxes["copy_tags"])
layout.addRow(self.checkboxes["preview_on_right_side"])
layout.addRow(self.checkboxes["search_the_web"])
return widget

def _make_local_tab(self) -> QWidget:
widget = QWidget()
widget.setLayout(layout := QFormLayout())
layout.addRow(self.checkboxes["allow_empty_search"])
layout.addRow(self.checkboxes["copy_card_data"])
layout.addRow("Tag original cards with", self.tag_edit)
return widget

for key, checkbox in self.checkboxes.items():
layout.addRow(checkbox)
return layout
def _make_hl_tab(self) -> QWidget:
widget = QWidget()
widget.setLayout(layout := QFormLayout())
layout.addRow("Web download timeout", self.web_timeout_spinbox)
layout.addRow(self.checkboxes["enable_debug_log"])
show_log_b = QPushButton("Show log")
qconnect(show_log_b.clicked, lambda: showText(LogDebug().read(), parent=self, copyBtn=True))
layout.addRow(show_log_b)
layout.addRow(self.checkboxes["call_add_cards_hook"])
return widget

def connect_widgets(self):
qconnect(self.button_box.accepted, self.accept)
qconnect(self.button_box.rejected, self.reject)
qconnect(self.button_box.helpRequested, self.show_help)

def add_tooltips(self) -> None:
self.tag_edit.setToolTip(
Expand Down Expand Up @@ -117,13 +145,40 @@ def add_tooltips(self) -> None:
"Instead of searching notes in a local profile,\n" "search the Internet instead."
)

def show_help(self):
help_win = QDialog(parent=self)
help_win.setWindowModality(Qt.WindowModality.NonModal)
help_win.setWindowTitle(f"{ADDON_NAME} - Settings Help")
help_win.setLayout(QVBoxLayout())

size_policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Minimum)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(help_win.sizePolicy().hasHeightForWidth())
help_win.setSizePolicy(size_policy)
help_win.setMinimumSize(240, 320)

webview = AnkiWebView(parent=help_win)
webview.setProperty("url", QUrl("about:blank"))
with open(CONFIG_MD_PATH) as c_help:
webview.stdHtml(c_help.read(), js=[])
webview.setMinimumSize(320, 480)
webview.disable_zoom()
help_win.layout().addWidget(webview)

button_box = QDialogButtonBox(BUT_OK)
help_win.layout().addWidget(button_box)
qconnect(button_box.accepted, help_win.accept)

help_win.exec()

def done(self, result: int) -> None:
saveGeom(self, self.name)
return super().done(result)

def accept(self) -> None:
config.max_displayed_notes = self.max_notes_edit.value()
config.tag_original_notes = self.tag_edit.text()
config.exported_tag = self.tag_edit.text()
config.hidden_fields = self.hidden_fields.values()
config.timeout_seconds = self.web_timeout_spinbox.value()
for key, checkbox in self.checkboxes.items():
Expand Down

0 comments on commit bdc50f2

Please sign in to comment.