Skip to content

Commit

Permalink
Flesh-out README, fix search filter bug
Browse files Browse the repository at this point in the history
If there were no selected items in the note field tree, then the
selected_items attribute was not getting updated and the the
on_update_callback was not getting fired, and therefore, the selected
items were not actually being updated in subsequent views.

Fill in readme with usage details.
  • Loading branch information
cofinley committed Nov 17, 2019
1 parent c44c250 commit a47138e
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 31 deletions.
163 changes: 162 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,164 @@
# Find Missing Words in Anki

An Anki add-on for searching through your collection to find and create missing words.
An Anki add-on for searching through your collection to find gaps in your vocabulary and quickly create notes to fill in those gaps.

This is a similar flow to LingQ or ReadLang. But instead of starting your vocab from scratch, it uses your existing Anki collection to find which words you don't know.

## Usage

### Search

![](https://i.imgur.com/dPQW4wc.png)

1. Open the "Find Missing Words" addon from the Anki Tools menu
1. Optionally filter your search
- By deck and/or by models and their fields
- Check the box next to the filter to enable it, disable to leave search open
- This filter will determine if the words in the text are considered 'known' or 'missing.'
- **Note:** including the fields in the search is much more accurate than leaving it blank (all fields).
1. Enter text into the text area
1. Click "Search"

---

Example of model/field filter. I usually add words to these two note types/fields:

![](https://i.imgur.com/KDPYhLp.png)

Search with populated info:

![](https://i.imgur.com/b4JCuvZ.png)

### Word Select

![](https://i.imgur.com/ThY9QJ2.png)

Upon search, you are presented with a window that shows the text, with words not found in your query colored green.

#### Known Words

These will not be colored green, but can be clicked on to view the current notes that contain the word.

1. Click on word in left pane
1. In right pane, view the list of notes that contain the word
- Note: these will only be notes that contain the word _and_ fall within the filter parameters.
1. Click on a note entry in the list
1. Use the editor to make any adjustments, if necessary

#### New Words

1. Click on a green word bubble that holds a new word
![](https://i.imgur.com/8lLGJ02.png)
1. In the right pane, notice there are two options
1. Ignore the word (marks the word as known, removes green highlighting)
2. Create a note based on a note preset

##### Ignoring

Ignoring a word is handy when you are just starting to use the addon and many little, insignificant words (articles, stop words, proper nouns, words you know by heart) are shown in green. You don't need cards for them, so you can ignore them and they will cease to show up as green from now on.

##### Create Note from Note Preset

Now for the fun part: actually creating notes for missing words.

When you first start out, you won't have any **note creation presets**. A note creation preset is a mapping that tells the extension what you'd like to do with a new word. This includes the location (deck and model/field) as well as the surrounding sentences for context (more on that later).

To create a preset:

1. Click the plus ('+') button
- This brings up the config (more on that later, too)
![](https://i.imgur.com/LYSk5dT.png)
1. On the "Note Creation" tab of the config, click on "Add" at the bottom of the list pane
![](https://i.imgur.com/ipm1Ze2.png)
1. Choose a name for your preset
- E.g. "Fill in the Blank," "New Word Form," "Word Order"
1. Choose a destination for the word
1. Note type (the model, e.g. "Basic")
2. Word destination Field (e.g. "Front")
1. Optionally use the surrounding sentence(s) for context
1. Save, you will be taken back to the Note Creation view
1. Click on the new Note Creation Preset button (e.g. "Fill in the Blank")
![](https://i.imgur.com/vkdiUUq.png)
1. View the pre-populated fields, fill in any extra info for your note
![](https://i.imgur.com/XdfBXbv.png)

Upon adding a note, the word that was selected (and any duplicates) will be marked as "known" and cease to be highlighted in the word selection view.

![](https://i.imgur.com/eGgdbVc.png)

Clicking on the same word again will bring up the (new) note(s) associated with it. The regular note editor will be used instead of the "Add Note Editor" (notice not "Add" or "Cancel" buttons at the bottom).

![](https://i.imgur.com/1MUVoiS.png)

###### Sentences

For a note creation preset, you can include the surrounding sentences for helpful context.

To add sentences:

1. Enable the sentence box by checking the box next to "Sentences"
1. Pick a field (based on the "Note Type" model fields from above)
1. Pick a sentence configuration type
1. Whole sentence
1. Word blanked out (word -> "__")
1. Word removed (word -> "")
1. Word clozed (reuse cloze for all occurrences)
- "{{c1::word}} ... {{c1::Word}} ... {{c1:: word}}"
- All word occurrences revealed on card flip
1. Word clozed (separate cloze for each occurrence)
- "{{c1::word}} ... {{c2::Word}} ... {{c3:: word}}"
- Helpful for creating multiple cards
1. Optionally add more sentence configurations
- E.g. one for whole sentence in the "whole sentence" field and another for a "sentence with word blanked out" field
![](https://i.imgur.com/KO8Vpo1.png)

### Config

The config has two parts:
1. Search configuration
2. Note creation presets

We've just discussed #2 above, so here's some info on search configuration:

#### Search Configuration

![](https://i.imgur.com/6mly1qu.png)

This tab of the config lets you set defaults to save time by not having to re-enter the search filters.

##### Filters

Here, you can set a default deck and note/field combination for future searches. The steps are the same as the above Search section. If you use the same deck and fields for your words in Anki, these are for you.


##### Ignored Words

In this text box, you can enter the words you don't want showing up as 'new' in the word select step.

The words are separated by new lines; and common punctuation is ignored by default. When you ignore words in the Word Select view, they will save to your config and be sorted for you to see here.

**Before and after ignoring a word**:

Clicking "Ignore" in the top right in the Note Creation pane:

![](https://i.imgur.com/JrmgkZP.png)

Seeing the word ignored in the config:

![](https://i.imgur.com/873jwLL.png)

### Saving and Cancelling

Clicking "Cancel" will revert the config to the state/preferences it had when you opened it.
Clicking "Save All" will save any changes on both tabs that were made since opening the config.

## Contributing

- See docs/DEVELOPMENT.md for setup
- See other files in docs/ for understanding some of the data structures
- Fill out issues and PRs accordingly
- For issues, make sure to include stack trace(s)

---

Enjoy!
14 changes: 14 additions & 0 deletions designer/config_sentence_preset.ui
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Field</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
Expand All @@ -27,6 +34,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sentence_type_combo_box">
<item>
Expand Down
5 changes: 2 additions & 3 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ UI files are used to speed up visual development and reduce extra code in the bu
# Could be QDialog or QWidget
class MyComponent(QDialog):
def __init__(self, parent=None):
super().__init__()
self.parent = parent or mw
super().__init__(parent)

# Check the compiled UI file's class to call on the next line
# Could be Ui_Form or Ui_Dialog
self.form = my_component_form.Ui_Form()
self.form = my_component_form.Ui_Dialog()
self.form.setupUi(self)

def foo(self):
Expand Down
4 changes: 2 additions & 2 deletions src/find_missing_words/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def addon_reloader_after():


def initialize_addon():
from .gui.options import initialize_menu_item, initialize_config_window
from .gui.options import initialize_menu_item, initialize_config_menu_item
initialize_menu_item()
initialize_config_window()
initialize_config_menu_item()


initialize_addon()
2 changes: 1 addition & 1 deletion src/find_missing_words/gui/config/sentence_preset.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def render_sentence_destination(self):
choice=choice,
callback=self.update_sentence_destination,
parent=self)
self.layout().insertWidget(0, self.sentence_destination_chooser)
self.layout().insertWidget(1, self.sentence_destination_chooser)
if "sentence_destination" not in self.sentence_preset:
self.update_sentence_destination(choice)

Expand Down
2 changes: 1 addition & 1 deletion src/find_missing_words/gui/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def invoke_config_window():
config.exec_()


def initialize_config_window():
def initialize_config_menu_item():
"""
Add option for addon's config in Anki
:return:
Expand Down
20 changes: 5 additions & 15 deletions src/find_missing_words/gui/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ def render_note_field_chooser(self):
self.form.filter_note_fields_hbox.addWidget(self.note_field_chooser)

def update_note_fields(self):
self.note_field_selected_items = self.note_field_chooser.selected_items
self.update_init_search()
if hasattr(self, "note_field_chooser"):
self.note_field_selected_items = self.note_field_chooser.selected_items
self.update_init_search()

def toggle_deck_selection(self):
self.deck_selection_enabled = not self.deck_selection_enabled
Expand All @@ -91,27 +92,16 @@ def toggle_note_field_selection(self):
self.note_field_chooser.setEnabled(self.note_field_selection_enabled)
self.update_init_search()

########################
####### Non-UI #######
########################

# def copy_preview(self):
# data = self.get_final_search("[Word]")
# command = 'echo ' + data.strip() + '| clip'
# os.system(command)

def update_init_search(self):
deck_name = self.deck_selection_enabled and (self.deck_name or self.deck_chooser.deckName())
note_fields = self.note_field_selection_enabled and self.note_field_selected_items

self.init_query = ""
self.init_query += self.search_formatter(True, "deck", deck_name) if deck_name else ""
if self.note_field_selection_enabled:
note_type_selected = [mod['name'] for mod in note_fields] if note_fields else ""
fields_selected = list(
{fields['name'] for mod in note_fields for fields in mod['fields']} if note_fields else "")

self.init_query = ""
self.init_query += self.search_formatter(True, "deck", deck_name) if deck_name else ""
if self.note_field_selection_enabled:
self.init_query += self.search_multiple_terms_formatter("note", note_type_selected)
self.init_query += self.search_multiple_fields_formatter(fields_selected, self.REPLACEMENT_STRING)
else:
Expand Down
3 changes: 1 addition & 2 deletions src/find_missing_words/gui/search_results/note_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def __init__(self, word_model, text, deck_name, note_fields, parent=None):
self.note_fields = note_fields
self.note_ids = []
self.current_word = ""
self.sentences = None
self.mw = mw
self.editor = None
self.last_list_item = None
Expand All @@ -41,9 +40,9 @@ def __init__(self, word_model, text, deck_name, note_fields, parent=None):
self.form.note_list_widget.itemClicked.connect(self.display_note_editor)
self.form.ignore_button.clicked.connect(self.ignore_word)
self.setup_note_widgets()
self.render_word_select()

addHook("load_word", self.load_word)
self.render_word_select()

def setup_note_widgets(self):
"""
Expand Down
11 changes: 5 additions & 6 deletions src/find_missing_words/gui/utils/note_field_chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ def set_selected_items(self, selected_note_field_items):

def invoke_note_field_tree(self):
ret = NoteFieldTree(self.note_field_items, self)
if ret.selected_items:
self.note_field_items = ret.all_items
self.update_btn(ret.selected_items)
self.note_field_items = ret.all_items
self.update_btn(ret.selected_items)

def update_btn(self, selected_items=None):
"""
Expand All @@ -73,10 +72,10 @@ def update_btn(self, selected_items=None):
else:
self.btn_text = formatted_selected_items
self.setToolTip(formatted_selected_items)
self.selected_items = selected_items
if self.on_update_callback:
self.on_update_callback()
self.selected_items = selected_items
self.setText(self.btn_text)
if self.on_update_callback:
self.on_update_callback()

@staticmethod
def format_btn_text(items):
Expand Down

0 comments on commit a47138e

Please sign in to comment.