Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Field history more features #102

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
__pycache__/
# Anki
src/*/meta.json
src/*/config.json
# Build files
build
src/*/forms*
Expand Down
13 changes: 4 additions & 9 deletions src/browser_refresh/browser_refresh.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
(e.g. to show newly added cards since last search)

Copyright: (c) Glutanimate 2016-2017 <https://glutanimate.com/>
2018 Arthur Milchior <[email protected]> (porting to 2.1)
License: GNU AGPLv3 or later <https://www.gnu.org/licenses/agpl.html>
"""

# Do not modify the following line
from __future__ import unicode_literals
from anki import version as anki_version
anki21 = anki_version.startswith("2.1.")
from .config import getConfig
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant to file the changes for browser refresh as a separate PR?


######## USER CONFIGURATION START ########

Expand All @@ -41,15 +39,12 @@
from aqt.browser import Browser
from anki.hooks import addHook
def debug(t):
#print(t)
print(t)
pass

def refreshView(self):
debug("Calling refreshView()")
if anki21:
self.onSearchActivated()
else:
self.onSearch(reset=True)
self.onSearchActivated()
if SORTING_COLUMN:
try:
col_index = self.model.activeCols.index(SORTING_COLUMN)
Expand All @@ -62,7 +57,7 @@ def setupMenu(self):
menu = self.form.menuEdit
menu.addSeparator()
a = menu.addAction('Refresh View')
a.setShortcut(QKeySequence("CTRL+F5" if anki21 else "F5"))
a.setShortcut(QKeySequence(getConfig("ShortCut","CTRL+F5")))
a.triggered.connect(self.refreshView)

Browser.refreshView = refreshView
Expand Down
1 change: 1 addition & 0 deletions src/browser_refresh/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"ShortCut": "CTRL+F5"}
25 changes: 25 additions & 0 deletions src/browser_refresh/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from aqt import mw

options = None
def readIfRequired():
global options
if options is None:
options = mw.addonManager.getConfig(__name__) or dict()

def newConf(config):
global options
options = None

def getConfig(s = None, default = None):
"""Get the dictionnary of objects. If a name is given, return the
object with this name if it exists.

reads if required."""

readIfRequired()
if s is None:
return options
else:
return options.get(s, default)

mw.addonManager.setConfigUpdatedAction(__name__,newConf)
15 changes: 15 additions & 0 deletions src/editor_field_history/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"limit search to deck": true,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I can see the advantage of including spaces in config keys for readability, I still prefer going old school json and using camelCase config keys (whitespace in keys always makes me nervous. might just be because I started programming with bash...)

"number of note to consider in history": 100,
"historyWindowShortcut": "Ctrl+Alt+H",
"predefinedWindowShortcut": "Ctrl+Alt+P",
"fieldRestoreShortcut": "Alt+Z",
"partialRestoreShortcut": "Alt+Shift+Z",
"fullRestoreShortcut": "Ctrl+Alt+Shift+Z",
"partialRestoreFields": [],
"predefinedFields":{
"Example of name of field": [
"Example of default value",
"other example of default value"
]}
}
30 changes: 30 additions & 0 deletions src/editor_field_history/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from aqt import mw
import sys

userOption = None

def getUserOption(key = None, default = None):
#print(f"getUserOption(key = {key}, default = {default})")
global userOption
if userOption is None:
userOption = mw.addonManager.getConfig(__name__)
#debug("userOption read from the file and is {userOption}")
if key is None:
#debug("return {userOption}")
return userOption
if key in userOption:
#debug("key in userOption. Returning {userOption[key]}")
return userOption[key]
else:
#debug("key not in userOption. Returning default.")
return default

def writeConfig():
mw.addonManager.writeConfig(__name__,userOption)

def update(_):
global userOption, fromName
userOption = None
fromName = None

mw.addonManager.setConfigUpdatedAction(__name__,update)
130 changes: 84 additions & 46 deletions src/editor_field_history/editor_field_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Anki's built-in add-on configuration menu

history_window_shortcut = "Ctrl+Alt+H"
predefined_window_shortcut = "Ctrl+Alt+P"
field_restore_shortcut = "Alt+Z"
partial_restore_shortcut = "Alt+Shift+Z"
full_restore_shortcut = "Ctrl+Alt+Shift+Z"
Expand All @@ -37,15 +38,16 @@

from anki import version
ANKI21 = version.startswith("2.1")
from .config import getConfig
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently throws an error because config.py is using getUserOption



if ANKI21:
config = mw.addonManager.getConfig(__name__)
history_window_shortcut = config["historyWindowShortcut"]
field_restore_shortcut = config["fieldRestoreShortcut"]
partial_restore_shortcut = config["partialRestoreShortcut"]
full_restore_shortcut = config["fullRestoreShortcut"]
partial_restore_fields = config["partialRestoreFields"]
history_window_shortcut = getConfig("historyWindowShortcut", "Ctrl+Alt+H")
predefined_window_shortcut = getConfig("predefinedWindowShortcut", "Ctrl+Alt+P")
field_restore_shortcut = getConfig("fieldRestoreShortcut", "Alt+Z")
partial_restore_shortcut = getConfig("partialRestoreShortcut", "Alt+Shift+Z")
full_restore_shortcut = getConfig("fullRestoreShortcut", "Ctrl+Alt+Shift+Z")
partial_restore_fields = getConfig("partialRestoreFields", [])

# Ctrl+Alt+H is a global hotkey on macOS
# Hacky solution for anki21. A platform-specific config.json would be
Expand Down Expand Up @@ -76,17 +78,38 @@ def showCompleter(self):
self.completer.complete()


def myGetField(parent, question, last_val, **kwargs):
edit = CustomTextEdit(parent, last_val)
ret = getText(question, parent, edit=edit, **kwargs)
return ret
def myGetField(parent, question, potential_vals, **kwargs):
striped_vals = {}
keys = []
for val in potential_vals:
if not val.strip():
continue
striped_val = stripHTML(val)
striped_vals[striped_val] = val
keys.append(striped_val)
edit = CustomTextEdit(parent, potential_vals)
(text, ret) = getText(question, parent, edit=edit, **kwargs)
return striped_vals.get(text, False)


def predefinedRestore(self, model, fld):
field = model['flds'][fld]['name']
keys = getConfig("predefinedFields", dict()).get(field, [])
if not keys:
tooltip("No predefined values for this field. Edit the configuration.")
return False

txt = "Set field to:"
text = myGetField(self.parentWindow,
txt, keys, title="Predefined fields")
if text:
self.note[field] = text

def historyRestore(self, mode, results, model, fld):
field = model['flds'][fld]['name']
last_val = {}
last_vals = {}
keys = []
for nid in results[:100]:
for nid in results[:getConfig("number of note to consider in history", 100)]:
oldNote = self.note.col.getNote(nid)
if field in oldNote:
html = oldNote[field]
Expand All @@ -99,18 +122,17 @@ def historyRestore(self, mode, results, model, fld):
text = stripHTML(html)
else:
text = None
if text and text not in last_val:
if text and text not in last_vals:
keys.append(text)
last_val[text] = html
if not last_val:
last_vals[text] = html
if not last_vals:
tooltip("No prior entries for this field found.")
return False
txt = "Set field to:"
(text, ret) = myGetField(self.parentWindow,
text = myGetField(self.parentWindow,
txt, keys, title="Field History")
if not ret or not text.strip() or text not in last_val:
return False
self.note[field] = last_val[text]
if text:
self.note[field] = text


def quickRestore(self, mode, results, model, fld):
Expand Down Expand Up @@ -149,29 +171,40 @@ def restoreEditorFields(self, mode):

# Gather note info
fld = self.currentField
if fld is None and mode in ("history", "field"):
if fld is None and mode in ("history", "field", "predefined"):
# only necessary on anki20
tooltip("Please select a field whose last entry you want to restore.")
return False
did = self.parentWindow.deckChooser.selectedId()
deck = self.mw.col.decks.nameOrNone(did)
model = self.note.model()

# Perform search
if deck:
query = "deck:'%s'" % (deck)
results = self.note.col.findNotes(query)
if not results:
tooltip("Could not find any past notes in current deck.<br>"
"If you just imported a deck you might have to restart Anki.")
return False
results.sort(reverse=True)

# Get user selection
if mode == "history":
ret = historyRestore(self, mode, results, model, fld)
# for predefined, don't compute deck info
if mode == "predefined":
ret = predefinedRestore(self, model, fld)
if ret is False:
return False
else:
ret = quickRestore(self, mode, results, model, fld)
# Perform search
if getConfig("limit search to deck", True) and hasattr(self.parentWindow, "deckChooser"):
did = self.parentWindow.deckChooser.selectedId()
deck = self.mw.col.decks.nameOrNone(did)
where = "deck"
if deck:
query = "deck:'%s'" % (deck)
else:
query = ""
where = "collection"
results = self.note.col.findNotes(query)
if not results:
tooltip(f"Could not find any past notes in current {where}.<br>"
"If you just imported a deck you might have to restart Anki.")
return False
results.sort(reverse=True)

# Get user selection
if mode == "history":
ret = historyRestore(self, mode, results, model, fld)
else:
ret = quickRestore(self, mode, results, model, fld)
if ret is False:
return False

Expand All @@ -186,6 +219,10 @@ def restoreEditorFields(self, mode):
# Assign hotkeys

def onSetupButtons20(editor):
t = QShortcut(QKeySequence(history_window_shortcut), editor.parentWindow)
t.activated.connect(lambda: editor.restoreEditorFields("history"))
t = QShortcut(QKeySequence(predefined_window_shortcut), editor.parentWindow)
t.activated.connect(lambda: editor.restoreEditorFields("predefined"))
if not isinstance(editor.parentWindow, AddCards):
return # only enable in add cards dialog
t = QShortcut(QKeySequence(full_restore_shortcut), editor.parentWindow)
Expand All @@ -194,23 +231,24 @@ def onSetupButtons20(editor):
t.activated.connect(lambda: editor.restoreEditorFields("partial"))
t = QShortcut(QKeySequence(field_restore_shortcut), editor.parentWindow)
t.activated.connect(lambda: editor.restoreEditorFields("field"))
t = QShortcut(QKeySequence(history_window_shortcut), editor.parentWindow)
t.activated.connect(lambda: editor.restoreEditorFields("history"))


def onSetupShortcuts21(cuts, editor):
if not isinstance(editor.parentWindow, AddCards):
return # only enable in AddCards dialog
added_shortcuts = [
(full_restore_shortcut,
lambda: editor.restoreEditorFields("full"), True),
(partial_restore_shortcut,
lambda: editor.restoreEditorFields("partial"), True),
(field_restore_shortcut,
lambda: editor.restoreEditorFields("field")),
(history_window_shortcut,
lambda: editor.restoreEditorFields("history")),
(predefined_window_shortcut,
lambda: editor.restoreEditorFields("predefined")),
]
if isinstance(editor.parentWindow, AddCards):
added_shortcuts += [
(full_restore_shortcut,
lambda: editor.restoreEditorFields("full"), True),
(partial_restore_shortcut,
lambda: editor.restoreEditorFields("partial"), True),
(field_restore_shortcut,
lambda: editor.restoreEditorFields("field")),
]
cuts.extend(added_shortcuts)


Expand Down