From c546eb85dce6b3685c9fa3eb8be1652e785a8207 Mon Sep 17 00:00:00 2001 From: johnpincock Date: Sun, 16 Aug 2020 16:15:22 +0100 Subject: [PATCH 01/33] update fix - thanks andrewsanchez https://forums.ankiweb.net/t/special-fields-error-on-2-1-30/2203/13 --- Specialfields21/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index b929983..03ed49f 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -250,6 +250,7 @@ def _mid(self, srcMid): # copy styling changes over if newer if updateNoteType or (updateNoteType is None and srcModel["mod"] > dstModel["mod"]): model = srcModel.copy() + model["mod"] = max(srcModel["mod"], dstModel["mod"]) model["id"] = mid model["usn"] = self.col.usn() self.dst.models.update(model) From 8f3b26e80d1c4cf774a94de57a4a16e14ed8cdcb Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Wed, 25 Nov 2020 18:08:58 +0100 Subject: [PATCH 02/33] Add note type mapping functionality --- Specialfields21/__init__.py | 3 +- Specialfields21/note_type_mapping.py | 70 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Specialfields21/note_type_mapping.py diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 03ed49f..29e3e8b 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -6,6 +6,7 @@ from . import dialog from .config import getUserOption +from .note_type_mapping import create_mapping_on_field_name_equality # ######################################################### # @@ -288,7 +289,7 @@ def _did(self, did: int): self._did(idInSrc) # if target is a filtered deck, we'll need a new deck name deck = self.dst.decks.byName(name) - + is_new = not bool(deck) if deck and deck["dyn"]: diff --git a/Specialfields21/note_type_mapping.py b/Specialfields21/note_type_mapping.py new file mode 100644 index 0000000..7961ce0 --- /dev/null +++ b/Specialfields21/note_type_mapping.py @@ -0,0 +1,70 @@ +from typing import Optional, Dict +from abc import ABC + +from anki.models import NoteType +from aqt.utils import showText +from aqt import mw + + +class NoteTypeMapping(ABC): + def map_card_type(tmpl_id: int) -> Optional[int]: + pass + + def map_field(fld_id: int) -> Optional[int]: + pass + + +class FieldMapping(NoteTypeMapping): + def __init__( + self, + tmpl_amount: int, + fld_mappings: Dict[int, Optional[int]], + ): + self.tmpl_amount = tmpl_amount + self.fld_mappings = fld_mappings + + def map_card_type(self, tmpl_id: int) -> Optional[int]: + return tmpl_id if tmpl_id < self.tmpl_amount else None + + def map_field(self, fld_id: int) -> Optional[int]: + return self.fld_mappings[fld_id] if fld_id in self.fld_mappings else fld_id + + +def get_template_name(tmpl: Dict[str, any]) -> str: + return tmpl["name"] + + +def get_field_name(fld: Dict[str, any]) -> str: + return fld["name"] + + +def templates_match(model: NoteType, other_model: NoteType) -> bool: + if len(model["tmpls"]) != len(other_model["tmpls"]): + return False + + template_names = map(get_template_name, model["tmpls"]) + + for idx, name in enumerate(template_names): + if name != get_template_name(other_model["tmpls"][idx]): + return False + + return True + + +def create_mapping_on_field_name_equality(src_model: NoteType, dst_model: NoteType) -> Optional[NoteTypeMapping]: + if not templates_match(src_model, dst_model): + return None + + src_fields = list(map(get_field_name, src_model["flds"])) + dst_fields = list(map(get_field_name, dst_model["flds"])) + + field_mappings = {} + + for index, field_name in enumerate(src_fields): + try: + index_in_dst = dst_fields.index(field_name) + field_mappings[index] = index_in_dst + except ValueError: + field_mappings[index] = None + + return FieldMapping(len(src_model["tmpls"]), field_mappings) From 6599c6e4d4932247cfad6158322073edce4a14b9 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Wed, 25 Nov 2020 19:44:02 +0100 Subject: [PATCH 03/33] Use note type mapping functionality in _importNoteTypes --- Specialfields21/__init__.py | 34 +++++++++++++++++++++++++-- Specialfields21/note_type_mapping.py | 35 ++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 29e3e8b..b52814b 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -1,6 +1,7 @@ from anki.importing import Anki2Importer from anki.lang import _ from anki.utils import json +from anki.hooks import schema_will_change from aqt import mw from aqt.utils import showWarning @@ -14,6 +15,7 @@ # # ######################################################### +NID = 0 GUID = 1 MID = 2 MOD = 3 @@ -66,6 +68,10 @@ def newImportNotes(self) -> None: midCheck.append(str(i["id"])) ######################################################################## + ######### note type mapping + schema_will_change.remove(mw.onSchemaMod) + ######### /note type mapping + for note in self.src.db.execute("select * from notes"): total += 1 # turn the db result into a mutable list @@ -90,6 +96,7 @@ def newImportNotes(self) -> None: if self.allowUpdate: oldNid, oldMod, oldMid = self._notes[note[GUID]] # will update if incoming note more recent + if oldMod < note[MOD] or (not getUserOptionSpecial("update only if newer", True)): # safe if note types identical if oldMid == note[MID]: @@ -100,13 +107,36 @@ def newImportNotes(self) -> None: update.append(note) dirty.append(note[0]) else: - dupesIgnored.append(note) - self._ignoredGuids[note[GUID]] = True + ######### note type mapping + old_model = self.dst.models.get(oldMid) + target_model = self.dst.models.get(note[MID]) + + mapping = create_mapping_on_field_name_equality(old_model, target_model) + + if mapping: + self.dst.models.change( + old_model, + [note[NID]], + target_model, + mapping.get_field_map(), + mapping.get_card_type_map(), + ) + + update.append(note) + + ######### /note type mapping + else: + dupesIgnored.append(note) + self._ignoredGuids[note[GUID]] = True else: dupesIdentical.append(note) self.log.append(_("Notes found in file: %d") % total) + ######### note type mapping + schema_will_change.append(mw.onSchemaMod) + ######### /note type mapping + for note in update: oldnote = mw.col.getNote(note[0]) newTags = [t for t in note[5].replace('\u3000', ' ').split(" ") if t] diff --git a/Specialfields21/note_type_mapping.py b/Specialfields21/note_type_mapping.py index 7961ce0..aa847e4 100644 --- a/Specialfields21/note_type_mapping.py +++ b/Specialfields21/note_type_mapping.py @@ -7,25 +7,46 @@ class NoteTypeMapping(ABC): - def map_card_type(tmpl_id: int) -> Optional[int]: + destination: NoteType + + def get_card_type_map(self) -> Dict[int, Optional[int]]: + pass + + def map_card_type(self, tmpl_id: int) -> Optional[int]: pass - def map_field(fld_id: int) -> Optional[int]: + def get_field_map(self) -> Dict[int, Optional[int]]: + pass + + def map_field(self, fld_id: int) -> Optional[int]: pass class FieldMapping(NoteTypeMapping): def __init__( self, + to_model: NoteType, tmpl_amount: int, fld_mappings: Dict[int, Optional[int]], ): + self.destination = to_model self.tmpl_amount = tmpl_amount self.fld_mappings = fld_mappings + def get_card_type_map(self) -> Dict[int, Optional[int]]: + result = {} + + for i in range(self.tmpl_amount): + result[i] = i + + return result + def map_card_type(self, tmpl_id: int) -> Optional[int]: return tmpl_id if tmpl_id < self.tmpl_amount else None + def get_field_map(self) -> Dict[int, Optional[int]]: + return self.fld_mappings + def map_field(self, fld_id: int) -> Optional[int]: return self.fld_mappings[fld_id] if fld_id in self.fld_mappings else fld_id @@ -51,12 +72,12 @@ def templates_match(model: NoteType, other_model: NoteType) -> bool: return True -def create_mapping_on_field_name_equality(src_model: NoteType, dst_model: NoteType) -> Optional[NoteTypeMapping]: - if not templates_match(src_model, dst_model): +def create_mapping_on_field_name_equality(from_model: NoteType, to_model: NoteType) -> Optional[NoteTypeMapping]: + if not templates_match(from_model, to_model): return None - src_fields = list(map(get_field_name, src_model["flds"])) - dst_fields = list(map(get_field_name, dst_model["flds"])) + src_fields = list(map(get_field_name, from_model["flds"])) + dst_fields = list(map(get_field_name, to_model["flds"])) field_mappings = {} @@ -67,4 +88,4 @@ def create_mapping_on_field_name_equality(src_model: NoteType, dst_model: NoteTy except ValueError: field_mappings[index] = None - return FieldMapping(len(src_model["tmpls"]), field_mappings) + return FieldMapping(to_model, len(from_model["tmpls"]), field_mappings) From 38354ed72a790a555358f77788a1e0f2d425ded1 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Wed, 25 Nov 2020 19:57:06 +0100 Subject: [PATCH 04/33] Refactoring and Whitespace --- Specialfields21/__init__.py | 3 +++ Specialfields21/note_type_mapping.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index b52814b..6000bc3 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -295,6 +295,7 @@ def _mid(self, srcMid): Anki2Importer._mid = _mid + def _did(self, did: int): "Given did in src col, return local id." # already converted? @@ -343,4 +344,6 @@ def _did(self, did: int): # add to deck map and return self._decks[did] = newid return newid + + Anki2Importer._did = _did diff --git a/Specialfields21/note_type_mapping.py b/Specialfields21/note_type_mapping.py index aa847e4..9d42fa4 100644 --- a/Specialfields21/note_type_mapping.py +++ b/Specialfields21/note_type_mapping.py @@ -37,7 +37,7 @@ def get_card_type_map(self) -> Dict[int, Optional[int]]: result = {} for i in range(self.tmpl_amount): - result[i] = i + result[i] = self.map_card_type(i) return result From ed4fda537a00fecea4210e96f618fcc0bd52ef72 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Fri, 27 Nov 2020 01:37:26 +0100 Subject: [PATCH 05/33] Reformat + One use note type mapping if UpdateNoteTypeStyling is checked --- Specialfields21/__init__.py | 45 ++++++++++++++++++---------- Specialfields21/note_type_mapping.py | 6 ++-- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 6000bc3..d3b4335 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -64,7 +64,9 @@ def newImportNotes(self) -> None: for i in a: fields = i["flds"] for n in fields: - if n['name'] in getUserOptionSpecial("Special field", []) or getUserOptionSpecial("All fields are special", False): + if n["name"] in getUserOptionSpecial( + "Special field", [] + ) or getUserOptionSpecial("All fields are special", False): midCheck.append(str(i["id"])) ######################################################################## @@ -97,7 +99,9 @@ def newImportNotes(self) -> None: oldNid, oldMod, oldMid = self._notes[note[GUID]] # will update if incoming note more recent - if oldMod < note[MOD] or (not getUserOptionSpecial("update only if newer", True)): + if oldMod < note[MOD] or ( + not getUserOptionSpecial("update only if newer", True) + ): # safe if note types identical if oldMid == note[MID]: # incoming note should use existing id @@ -108,12 +112,16 @@ def newImportNotes(self) -> None: dirty.append(note[0]) else: ######### note type mapping + updateNoteType = getUserOptionSpecial("update note styling") + old_model = self.dst.models.get(oldMid) target_model = self.dst.models.get(note[MID]) - mapping = create_mapping_on_field_name_equality(old_model, target_model) + mapping = create_mapping_on_field_name_equality( + old_model, target_model + ) - if mapping: + if updateNoteType and mapping: self.dst.models.change( old_model, [note[NID]], @@ -139,7 +147,7 @@ def newImportNotes(self) -> None: for note in update: oldnote = mw.col.getNote(note[0]) - newTags = [t for t in note[5].replace('\u3000', ' ').split(" ") if t] + newTags = [t for t in note[5].replace("\u3000", " ").split(" ") if t] for tag in oldnote.tags: for i in newTags: if i.lower() == tag.lower(): @@ -153,7 +161,7 @@ def newImportNotes(self) -> None: model = mw.col.models.get(mid) specialFields = getUserOptionSpecial("Special field", []) if getUserOptionSpecial("All fields are special", False): - specialFields = [fld['name'] for fld in model['flds']] + specialFields = [fld["name"] for fld in model["flds"]] # if this note belongs to a model with "Special Field" trow = list(note) for i in specialFields: @@ -167,19 +175,20 @@ def newImportNotes(self) -> None: # valueLocal = mw.col.getNote(note[0]).values() # splitRow[indexOfField] = valueLocal[indexOfField] - finalrow = '' + finalrow = "" count = 0 for a in splitRow: if count == fieldOrd: finalrow += str(fields[fieldOrd]) + "\x1f" else: - finalrow += a+"\x1f" + finalrow += a + "\x1f" count = count + 1 def rreplace(s, old, new, occurrence): li = s.rsplit(old, occurrence) return new.join(li) - finarow = rreplace(finalrow, """\x1f""", '', 1) + + finarow = rreplace(finalrow, """\x1f""", "", 1) note[6] = str(finarow) # if note[0] == 1558556384609: #FOR TROUBLE SHOOTING ! Change to the card.id you are uncertain about @@ -193,15 +202,17 @@ def rreplace(s, old, new, occurrence): if dupesIgnored: self.log.append( _("Notes that could not be imported as note type has changed: %d") - % len(dupesIgnored)) + % len(dupesIgnored) + ) if update: - self.log.append( - _("Notes updated, as file had newer version: %d") % len(update)) + self.log.append(_("Notes updated, as file had newer version: %d") % len(update)) if add: self.log.append(_("Notes added from file: %d") % len(add)) if dupesIdentical: - self.log.append(_("Notes skipped, as they're already in your collection: %d") % - len(dupesIdentical)) + self.log.append( + _("Notes skipped, as they're already in your collection: %d") + % len(dupesIdentical) + ) self.log.append("") @@ -238,7 +249,7 @@ def rreplace(s, old, new, occurrence): for importedDid, importedDeck in ((d["id"], d) for d in self.src.decks.all()): localDid = self._did(importedDid) localDeck = self.dst.decks.get(localDid) - localDeck['desc'] = importedDeck['desc'] + localDeck["desc"] = importedDeck["desc"] self.dst.decks.save(localDeck) @@ -279,7 +290,9 @@ def _mid(self, srcMid): dstScm = self.dst.models.scmhash(dstModel) if srcScm == dstScm: # copy styling changes over if newer - if updateNoteType or (updateNoteType is None and srcModel["mod"] > dstModel["mod"]): + if updateNoteType or ( + updateNoteType is None and srcModel["mod"] > dstModel["mod"] + ): model = srcModel.copy() model["mod"] = max(srcModel["mod"], dstModel["mod"]) model["id"] = mid diff --git a/Specialfields21/note_type_mapping.py b/Specialfields21/note_type_mapping.py index 9d42fa4..b0ac1cb 100644 --- a/Specialfields21/note_type_mapping.py +++ b/Specialfields21/note_type_mapping.py @@ -34,7 +34,7 @@ def __init__( self.fld_mappings = fld_mappings def get_card_type_map(self) -> Dict[int, Optional[int]]: - result = {} + result = {} for i in range(self.tmpl_amount): result[i] = self.map_card_type(i) @@ -72,7 +72,9 @@ def templates_match(model: NoteType, other_model: NoteType) -> bool: return True -def create_mapping_on_field_name_equality(from_model: NoteType, to_model: NoteType) -> Optional[NoteTypeMapping]: +def create_mapping_on_field_name_equality( + from_model: NoteType, to_model: NoteType +) -> Optional[NoteTypeMapping]: if not templates_match(from_model, to_model): return None From 3144d72ed362c18b0e8f02b362c34794a801f3c5 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 30 Nov 2020 20:43:38 +0100 Subject: [PATCH 06/33] Move the check for special fields after calling _uniquifyNotes _uniquifyNotes can create new models in the collection, if they exist in the deck to import, but not in the local collection --- Specialfields21/__init__.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index d3b4335..5f5004f 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -57,18 +57,6 @@ def newImportNotes(self) -> None: dupesIdentical = [] dupesIgnored = [] total = 0 - ######################################################################## - # check if any models with special field exist - midCheck = [] - a = mw.col.models.all() - for i in a: - fields = i["flds"] - for n in fields: - if n["name"] in getUserOptionSpecial( - "Special field", [] - ) or getUserOptionSpecial("All fields are special", False): - midCheck.append(str(i["id"])) - ######################################################################## ######### note type mapping schema_will_change.remove(mw.onSchemaMod) @@ -145,6 +133,19 @@ def newImportNotes(self) -> None: schema_will_change.append(mw.onSchemaMod) ######### /note type mapping + ######################################################################## + # check if any models with special field exist + midCheck = [] + a = mw.col.models.all() + for i in a: + fields = i["flds"] + for n in fields: + if n["name"] in getUserOptionSpecial( + "Special field", [] + ) or getUserOptionSpecial("All fields are special", False): + midCheck.append(str(i["id"])) + ######################################################################## + for note in update: oldnote = mw.col.getNote(note[0]) newTags = [t for t in note[5].replace("\u3000", " ").split(" ") if t] From b3997c395dc5de7f0079d2f2e5c5bbad44cc1388 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 11 Jan 2021 16:10:41 +0100 Subject: [PATCH 07/33] Correctly update media references when importing with a note type mapping --- Specialfields21/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 5f5004f..4e811a3 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -118,7 +118,11 @@ def newImportNotes(self) -> None: mapping.get_card_type_map(), ) + note[0] = oldNid + note[4] = usn + note[6] = self._mungeMedia(note[MID], note[6]) update.append(note) + dirty.append(note[0]) ######### /note type mapping else: From 2001a723ccc9f4fe33674beba2d3c067d8690fce Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 18 Jan 2021 20:55:16 +0100 Subject: [PATCH 08/33] Introduce keep tags feature * It will preserve tags which include %%keep%%. --- Specialfields21/__init__.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 4e811a3..3fcb625 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -161,6 +161,21 @@ def newImportNotes(self) -> None: newTags = set(newTags) togetherTags = " %s " % " ".join(newTags) + + ######### KEEP tags + keepTags = [t for t in note[5].replace("\u3000", " ").split(" ") if t] + for tag in oldnote.tags: + for i in keepTags: + if i.lower() == tag.lower(): + tag = i + + if "%%keep%%" in tag: + keepTags.append(tag) + + keepTags = set(keepTags) + keepTagsTogether = " %s " % " ".join(keepTags) + ######### /KEEP tags + mid = str(note[2]) if mid in midCheck: model = mw.col.models.get(mid) @@ -201,6 +216,8 @@ def rreplace(s, old, new, occurrence): pass if getUserOptionSpecial("Combine tagging", False): note[5] = togetherTags + else: + note[5] = keepTagsTogether self.log.append(_("Notes found in file: %d") % total) From fe07e4d4f76f1c3f5594424fc74eaa567f70ec15 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Mon, 18 Jan 2021 16:35:38 -0700 Subject: [PATCH 09/33] Update manifest with package ID and version --- Specialfields21/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index f55bdba..42be047 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -1,8 +1,8 @@ { "name": "Special Fields", - "package": "Special Fields", + "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "1.0", + "version": "5.0", "conflicts": [] } From c7d7f1b1bca2e5aabcf1e9eecf8cd1678fba4c10 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Mon, 18 Jan 2021 19:17:49 -0700 Subject: [PATCH 10/33] Update dialog.py --- Specialfields21/dialog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index 5f3e474..de66930 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -83,6 +83,7 @@ def setupOptions(self): self.b2 = QCheckBox("Combine tagging", self) self.form._2.addWidget(self.b2) self.b2.setChecked(combTaging) + self.b2.setToolTip('
If this is unchecked, all tags except those containing %%keep%% will be updated
') self.b3 = QCheckBox("Update deck description", self) self.form._2.addWidget(self.b3) From 0aafc4bca784d82ece94a8476126ae9293e5d1a1 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Tue, 19 Jan 2021 15:33:07 -0700 Subject: [PATCH 11/33] Make the keep tag text identifier at top just to keep it slightly modifiable for those that know what they're doing --- Specialfields21/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 3fcb625..21fe5d1 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -13,6 +13,8 @@ # # See this video for how to use this add-on: https://youtu.be/cg-tQ6Ut0IQ # +KEEPTAGTEXT = "%%keep%%" +# # ######################################################### NID = 0 @@ -169,7 +171,7 @@ def newImportNotes(self) -> None: if i.lower() == tag.lower(): tag = i - if "%%keep%%" in tag: + if KEEPTAGTEXT in tag: keepTags.append(tag) keepTags = set(keepTags) From da68794c2139c5f0aa93b67b3485d6340c4b1655 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Wed, 20 Jan 2021 06:21:57 -0700 Subject: [PATCH 12/33] Add QLineEdit --- Specialfields21/__init__.py | 8 +++---- Specialfields21/config.json | 2 ++ Specialfields21/dialog.py | 43 ++++++++++++++++++++++++++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 21fe5d1..11f9afa 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -6,6 +6,7 @@ from aqt.utils import showWarning from . import dialog +from .dialog import returnTagsText from .config import getUserOption from .note_type_mapping import create_mapping_on_field_name_equality @@ -13,8 +14,6 @@ # # See this video for how to use this add-on: https://youtu.be/cg-tQ6Ut0IQ # -KEEPTAGTEXT = "%%keep%%" -# # ######################################################### NID = 0 @@ -171,8 +170,9 @@ def newImportNotes(self) -> None: if i.lower() == tag.lower(): tag = i - if KEEPTAGTEXT in tag: - keepTags.append(tag) + for item in returnTagsText(): + if item in tag: + keepTags.append(tag) keepTags = set(keepTags) keepTagsTogether = " %s " % " ".join(keepTags) diff --git a/Specialfields21/config.json b/Specialfields21/config.json index 5bf91ce..f8dfd8f 100644 --- a/Specialfields21/config.json +++ b/Specialfields21/config.json @@ -2,6 +2,7 @@ {"current config": { "All fields are special": true, "Combine tagging": true, + "Protected tags": ["%%keep%%"], "Special field": ["Lecture Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], "update deck description": false, @@ -11,6 +12,7 @@ "user default config": { "All fields are special": true, "Combine tagging": true, + "Protected tags": ["%%keep%%"], "Special field": ["Lecture Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], "update deck description": false, diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index de66930..bdf720f 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -20,6 +20,8 @@ fullconfig = getUserOption() configs = getUserOption("configs") +KEEPTAGTEXT = configs["current config"]["Protected tags"] + addon = __name__.split(".")[0] @@ -48,6 +50,7 @@ def __init__(self, mw, fields, ord=0, parent=None): self.form.fieldList.setCurrentRow(0) self.setupOptions() + self.getTagsText() # self.form.buttonBox.button(QRadioButton("Upload Collection", self)) # self.upload_but.clicked.connect(self.uploadBut) @@ -76,6 +79,8 @@ def setupOptions(self): updateStyle = configs["current config"]["update note styling"] upOnlyIfNewer = configs["current config"]["update only if newer"] + global KEEPTAGTEXT + self.b1 = QCheckBox("All fields are special", self) self.form._2.addWidget(self.b1) self.b1.setChecked(allSpecial) @@ -83,7 +88,7 @@ def setupOptions(self): self.b2 = QCheckBox("Combine tagging", self) self.form._2.addWidget(self.b2) self.b2.setChecked(combTaging) - self.b2.setToolTip('
If this is unchecked, all tags except those containing %%keep%% will be updated
') + self.b2.setToolTip(f'
If this is unchecked, all tags except those containing "{KEEPTAGTEXT}" will be updated
') self.b3 = QCheckBox("Update deck description", self) self.form._2.addWidget(self.b3) @@ -109,6 +114,18 @@ def setupOptions(self): self.b9 = QPushButton("Restore Defaults", self) self.form._2.addWidget(self.b9) + + self.l1 = QLabel("
Protected Tags:
", self) + self.l1.setAlignment(Qt.AlignRight) + self.form._2.addWidget(self.l1) + + self.t1 = QLineEdit(self) + KEEPTAGSTRING = ' '.join(str(elem) for elem in KEEPTAGTEXT) + self.t1.setText(KEEPTAGSTRING) + self.form._2.addWidget(self.t1) + self.t1.textChanged.connect(self.getTagsText) + + self.b1.clicked.connect(self.b1_press) self.b2.clicked.connect(self.b2_press) self.b3.clicked.connect(self.b3_press) @@ -118,7 +135,19 @@ def setupOptions(self): self.b7.clicked.connect(self.updatePresetConfig) self.b8.clicked.connect(self.importPresetConfig) self.b9.clicked.connect(self.restoreConfig) # change this class - + + def getTagsText(self): + global KEEPTAGTEXT + val = self.t1.text() + KEEPTAGTEXT = val.split(" ") + configs["current config"]["Protected tags"] = KEEPTAGTEXT + mw.addonManager.writeConfig(__name__, fullconfig) + #showInfo("done") + + def returnTagsText(self): + getTagsText() + return KEEPTAGTEXT + def fillFields(self): self.currentIdx = None self.form.fieldList.clear() @@ -173,12 +202,15 @@ def restoreConfig(self): updateDesc = configs["current config"]["update deck description"] updateStyle = configs["current config"]["update note styling"] upOnlyIfNewer = configs["current config"]["update only if newer"] + global KEEPTAGTEXT + KEEPTAGTEXT = configs["current config"]["Protected tags"] self.b1.setChecked(allSpecial) self.b2.setChecked(combTaging) self.b3.setChecked(updateDesc) self.b4.setChecked(updateStyle) self.b5.setChecked(upOnlyIfNewer) + #self.t1.setText(KEEPTAGTEXT) showInfo("Settings Restored") self.close() onFieldsExecute() @@ -193,12 +225,15 @@ def setConfig(self): updateDesc = configs["current config"]["update deck description"] updateStyle = configs["current config"]["update note styling"] upOnlyIfNewer = configs["current config"]["update only if newer"] + global KEEPTAGTEXT + KEEPTAGTEXT = configs["current config"]["Protected tags"] self.b1.setChecked(allSpecial) self.b2.setChecked(combTaging) self.b3.setChecked(updateDesc) self.b4.setChecked(updateStyle) self.b5.setChecked(upOnlyIfNewer) + #self.t1.setText(KEEPTAGTEXT) showInfo("Settings Restored") self.close() onFieldsExecute() @@ -311,7 +346,9 @@ def onHelp(self): #openHelp("fields") webbrowser.open('https://youtu.be/cg-tQ6Ut0IQ') - +def returnTagsText(): + global KEEPTAGTEXT + return KEEPTAGTEXT def onFields(self): # Use existing FieldDialog as template for UI. From 5fe374df6e6b3805b996011f04f57823484c04c7 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Wed, 20 Jan 2021 16:47:32 -0700 Subject: [PATCH 13/33] Make Qlineedit longer --- Specialfields21/__init__.py | 2 +- Specialfields21/dialog.py | 4 ++-- Specialfields21/manifest.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 11f9afa..77f53c6 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -171,7 +171,7 @@ def newImportNotes(self) -> None: tag = i for item in returnTagsText(): - if item in tag: + if item.lower() in tag: keepTags.append(tag) keepTags = set(keepTags) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index bdf720f..d1f1ad4 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -88,7 +88,6 @@ def setupOptions(self): self.b2 = QCheckBox("Combine tagging", self) self.form._2.addWidget(self.b2) self.b2.setChecked(combTaging) - self.b2.setToolTip(f'
If this is unchecked, all tags except those containing "{KEEPTAGTEXT}" will be updated
') self.b3 = QCheckBox("Update deck description", self) self.form._2.addWidget(self.b3) @@ -118,11 +117,12 @@ def setupOptions(self): self.l1 = QLabel("
Protected Tags:
", self) self.l1.setAlignment(Qt.AlignRight) self.form._2.addWidget(self.l1) + self.l1.setToolTip(f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space)
') self.t1 = QLineEdit(self) KEEPTAGSTRING = ' '.join(str(elem) for elem in KEEPTAGTEXT) self.t1.setText(KEEPTAGSTRING) - self.form._2.addWidget(self.t1) + self.form._2.addWidget(self.t1, 7, 1, 1, 2) self.t1.textChanged.connect(self.getTagsText) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index 42be047..9334d35 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.0", + "version": "5.1", "conflicts": [] } From 5d6fd2028d57ab2d31576d92be5c01518dae83dc Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Wed, 20 Jan 2021 18:18:57 -0700 Subject: [PATCH 14/33] Small stability checks by Henrik --- Specialfields21/__init__.py | 2 +- Specialfields21/dialog.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 77f53c6..11f9afa 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -171,7 +171,7 @@ def newImportNotes(self) -> None: tag = i for item in returnTagsText(): - if item.lower() in tag: + if item in tag: keepTags.append(tag) keepTags = set(keepTags) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index d1f1ad4..cedfab2 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -20,7 +20,7 @@ fullconfig = getUserOption() configs = getUserOption("configs") -KEEPTAGTEXT = configs["current config"]["Protected tags"] +KEEPTAGTEXT = configs["current config"]["Protected tags"] if "Protected tags" in configs["current config"] else "%%keep%%" addon = __name__.split(".")[0] @@ -139,7 +139,7 @@ def setupOptions(self): def getTagsText(self): global KEEPTAGTEXT val = self.t1.text() - KEEPTAGTEXT = val.split(" ") + KEEPTAGTEXT = [v for v in val.split(" ") if v] configs["current config"]["Protected tags"] = KEEPTAGTEXT mw.addonManager.writeConfig(__name__, fullconfig) #showInfo("done") From 8be1bbf43a115ce00623856b36ecdcb63a1e890d Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Wed, 20 Jan 2021 18:42:10 -0700 Subject: [PATCH 15/33] Set config defaults --- Specialfields21/dialog.py | 9 +++++++-- Specialfields21/manifest.json | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index cedfab2..5c5273e 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -20,7 +20,12 @@ fullconfig = getUserOption() configs = getUserOption("configs") -KEEPTAGTEXT = configs["current config"]["Protected tags"] if "Protected tags" in configs["current config"] else "%%keep%%" +if "Protected tags" not in configs["current config"]: + configs["current config"]["Protected tags"] = ["%%keep%%"] + configs["user default config"]["Protected tags"] = ["%%keep%%"] + mw.addonManager.writeConfig(__name__, fullconfig) + +KEEPTAGTEXT = configs["current config"]["Protected tags"] addon = __name__.split(".")[0] @@ -117,7 +122,7 @@ def setupOptions(self): self.l1 = QLabel("
Protected Tags:
", self) self.l1.setAlignment(Qt.AlignRight) self.form._2.addWidget(self.l1) - self.l1.setToolTip(f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space)
') + self.l1.setToolTip(f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space - case sensitive!)
') self.t1 = QLineEdit(self) KEEPTAGSTRING = ' '.join(str(elem) for elem in KEEPTAGTEXT) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index 9334d35..42be047 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.1", + "version": "5.0", "conflicts": [] } From 2dd765895da03e6eccd3f93e2a7368bf51b9c473 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Mon, 8 Feb 2021 18:46:19 -0700 Subject: [PATCH 16/33] Update special fields video link --- Specialfields21/dialog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index 5c5273e..a7d9df6 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -11,7 +11,7 @@ # ######################################################### # -# See this video for how to use this add-on: https://youtu.be/cg-tQ6Ut0IQ +# See this video for how to use this add-on: https://youtu.be/TTHpODHBk3U # # ######################################################### @@ -349,7 +349,7 @@ def saveField(self): def onHelp(self): #openHelp("fields") - webbrowser.open('https://youtu.be/cg-tQ6Ut0IQ') + webbrowser.open('https://youtu.be/TTHpODHBk3U') def returnTagsText(): global KEEPTAGTEXT From 15322322cf131a24010d0fffabb246022156fb33 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Mon, 8 Feb 2021 18:46:34 -0700 Subject: [PATCH 17/33] Update manifest.json --- Specialfields21/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index 42be047..9334d35 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.0", + "version": "5.1", "conflicts": [] } From eb3d1fbdfb095592acac2bc3af4f40bc6bd95b9e Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Sat, 13 Feb 2021 12:43:41 -0700 Subject: [PATCH 18/33] Keep marked and leech tags when updating tags --- Specialfields21/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 11f9afa..c2fa8ba 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -173,6 +173,9 @@ def newImportNotes(self) -> None: for item in returnTagsText(): if item in tag: keepTags.append(tag) + + if "marked" in tag or "leech" in tag: + keepTags.append(tag) keepTags = set(keepTags) keepTagsTogether = " %s " % " ".join(keepTags) From 6f334f6111f1f017fddf5937e383c893e3ed35c6 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Sat, 13 Feb 2021 15:47:24 -0700 Subject: [PATCH 19/33] Update version --- Specialfields21/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index 9334d35..ef8854e 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.1", + "version": "5.2", "conflicts": [] } From ce1871945b5664edd49aa87866c74083f7cccdae Mon Sep 17 00:00:00 2001 From: bluegreenmagick Date: Sun, 20 Jun 2021 23:26:18 +0900 Subject: [PATCH 20/33] specify widget positions on the grid --- Specialfields21/dialog.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index a7d9df6..6dcc9cd 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -87,47 +87,47 @@ def setupOptions(self): global KEEPTAGTEXT self.b1 = QCheckBox("All fields are special", self) - self.form._2.addWidget(self.b1) + self.form._2.addWidget(self.b1, 0, 0) self.b1.setChecked(allSpecial) self.b2 = QCheckBox("Combine tagging", self) - self.form._2.addWidget(self.b2) + self.form._2.addWidget(self.b2, 0, 1) self.b2.setChecked(combTaging) self.b3 = QCheckBox("Update deck description", self) - self.form._2.addWidget(self.b3) + self.form._2.addWidget(self.b3, 0, 2) self.b3.setChecked(updateDesc) self.b4 = QCheckBox("Update note styling", self) - self.form._2.addWidget(self.b4) + self.form._2.addWidget(self.b4, 1, 0) self.b4.setChecked(updateStyle) self.b5 = QCheckBox("Update only if newer", self) - self.form._2.addWidget(self.b5) + self.form._2.addWidget(self.b5, 1, 1) self.b5.setChecked(upOnlyIfNewer) self.b6 = QPushButton("Set Defaults", self) - self.form._2.addWidget(self.b6) + self.form._2.addWidget(self.b6, 1, 2) self.b7 = QPushButton("'Update' Settings", self) - self.form._2.addWidget(self.b7) + self.form._2.addWidget(self.b7, 2, 0) self.b8 = QPushButton("'Import Tags' Settings", self) - self.form._2.addWidget(self.b8) + self.form._2.addWidget(self.b8, 2, 1) self.b9 = QPushButton("Restore Defaults", self) - self.form._2.addWidget(self.b9) + self.form._2.addWidget(self.b9, 2, 2) self.l1 = QLabel("
Protected Tags:
", self) self.l1.setAlignment(Qt.AlignRight) - self.form._2.addWidget(self.l1) + self.form._2.addWidget(self.l1, 3, 0) self.l1.setToolTip(f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space - case sensitive!)
') self.t1 = QLineEdit(self) KEEPTAGSTRING = ' '.join(str(elem) for elem in KEEPTAGTEXT) self.t1.setText(KEEPTAGSTRING) - self.form._2.addWidget(self.t1, 7, 1, 1, 2) + self.form._2.addWidget(self.t1, 3, 1, 1, 2) self.t1.textChanged.connect(self.getTagsText) From 9435d74e1c968e12e85b58f4bf6e2f42c148efa0 Mon Sep 17 00:00:00 2001 From: bluegreenmagick Date: Sun, 20 Jun 2021 23:26:32 +0900 Subject: [PATCH 21/33] programatically remove initial widgets from grid --- Specialfields21/dialog.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index 6dcc9cd..77503c4 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -54,23 +54,19 @@ def __init__(self, mw, fields, ord=0, parent=None): self.setupSignals() self.form.fieldList.setCurrentRow(0) - self.setupOptions() - self.getTagsText() - # self.form.buttonBox.button(QRadioButton("Upload Collection", self)) - # self.upload_but.clicked.connect(self.uploadBut) - # removing irrelevant stuff from general "fields.ui" template - # self.form._2.setParent(None) - self.form.rtl.setParent(None) - self.form.fontFamily.setParent(None) - self.form.fontSize.setParent(None) - self.form.sticky.setParent(None) - self.form.label_18.setParent(None) - self.form.fontFamily.setParent(None) + for r in reversed(range(self.form._2.count())): + # reversed because removing item afrom start shifts the other items forward + item = self.form._2.itemAt(r) + item.widget().setParent(None) self.form.fieldRename.setParent(None) self.form.fieldPosition.setParent(None) self.form.label_5.setParent(None) - self.form.sortField.setParent(None) + + self.setupOptions() + self.getTagsText() + + self.resize(500, 300) self.exec_() From 3110964edb4d90d863bf0c448b9b3880f8020ddb Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Sun, 20 Jun 2021 15:27:48 -0600 Subject: [PATCH 22/33] Update version --- Specialfields21/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index ef8854e..b19d4de 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.2", + "version": "5.3", "conflicts": [] } From 800284f5635f62b3cb7ea8d1cf343708b1e88af6 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Fri, 31 Dec 2021 14:50:42 +0100 Subject: [PATCH 23/33] Update for Anki 2.1.50 --- Specialfields21/__init__.py | 9 ++-- Specialfields21/config.py | 3 -- Specialfields21/dialog.py | 67 +++++++++++++--------------- Specialfields21/note_type_mapping.py | 4 +- 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index c2fa8ba..ef8df65 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -1,13 +1,10 @@ +from anki.hooks import schema_will_change from anki.importing import Anki2Importer from anki.lang import _ -from anki.utils import json -from anki.hooks import schema_will_change from aqt import mw -from aqt.utils import showWarning -from . import dialog -from .dialog import returnTagsText from .config import getUserOption +from .dialog import returnTagsText from .note_type_mapping import create_mapping_on_field_name_equality # ######################################################### @@ -173,7 +170,7 @@ def newImportNotes(self) -> None: for item in returnTagsText(): if item in tag: keepTags.append(tag) - + if "marked" in tag or "leech" in tag: keepTags.append(tag) diff --git a/Specialfields21/config.py b/Specialfields21/config.py index 3257899..4e86f69 100644 --- a/Specialfields21/config.py +++ b/Specialfields21/config.py @@ -1,7 +1,4 @@ -import sys - from aqt import mw -from aqt.utils import showWarning userOption = None diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index 77503c4..b1fab6e 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -1,13 +1,13 @@ import copy import webbrowser + import aqt from anki.consts import * -from anki.utils import json from aqt import mw from aqt.qt import * -from aqt.utils import askUser, getOnlyText, openHelp, showInfo, showWarning +from aqt.utils import getOnlyText, showInfo, showWarning -from .config import getDefaultConfig, getUserOption, writeConfig +from .config import getUserOption # ######################################################### # @@ -16,7 +16,6 @@ # ######################################################### - fullconfig = getUserOption() configs = getUserOption("configs") @@ -25,15 +24,14 @@ configs["user default config"]["Protected tags"] = ["%%keep%%"] mw.addonManager.writeConfig(__name__, fullconfig) -KEEPTAGTEXT = configs["current config"]["Protected tags"] +KEEPTAGTEXT = configs["current config"]["Protected tags"] addon = __name__.split(".")[0] class FieldDialog(QDialog): - def __init__(self, mw, fields, ord=0, parent=None): - QDialog.__init__(self, parent or mw) # , Qt.Window) + QDialog.__init__(self, parent or mw) # , Qt.WindowType.Window) self.specialFields = fields self.mw = aqt.mw @@ -61,15 +59,18 @@ def __init__(self, mw, fields, ord=0, parent=None): item.widget().setParent(None) self.form.fieldRename.setParent(None) self.form.fieldPosition.setParent(None) - self.form.label_5.setParent(None) + + try: + self.form.label_5.setParent(None) + except AttributeError: + pass # Do nothing, there is no label_5 in Anki >= 2.1.50 self.setupOptions() self.getTagsText() - self.resize(500, 300) - self.exec_() + self.exec() ########################################################################## def setupOptions(self): @@ -114,18 +115,18 @@ def setupOptions(self): self.b9 = QPushButton("Restore Defaults", self) self.form._2.addWidget(self.b9, 2, 2) - self.l1 = QLabel("
Protected Tags:
", self) self.l1.setAlignment(Qt.AlignRight) self.form._2.addWidget(self.l1, 3, 0) - self.l1.setToolTip(f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space - case sensitive!)
') + self.l1.setToolTip( + f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space - case sensitive!)
' + ) self.t1 = QLineEdit(self) - KEEPTAGSTRING = ' '.join(str(elem) for elem in KEEPTAGTEXT) + KEEPTAGSTRING = " ".join(str(elem) for elem in KEEPTAGTEXT) self.t1.setText(KEEPTAGSTRING) self.form._2.addWidget(self.t1, 3, 1, 1, 2) self.t1.textChanged.connect(self.getTagsText) - self.b1.clicked.connect(self.b1_press) self.b2.clicked.connect(self.b2_press) @@ -136,19 +137,19 @@ def setupOptions(self): self.b7.clicked.connect(self.updatePresetConfig) self.b8.clicked.connect(self.importPresetConfig) self.b9.clicked.connect(self.restoreConfig) # change this class - + def getTagsText(self): - global KEEPTAGTEXT + global KEEPTAGTEXT val = self.t1.text() KEEPTAGTEXT = [v for v in val.split(" ") if v] configs["current config"]["Protected tags"] = KEEPTAGTEXT mw.addonManager.writeConfig(__name__, fullconfig) - #showInfo("done") - + # showInfo("done") + def returnTagsText(self): - getTagsText() + self.getTagsText() return KEEPTAGTEXT - + def fillFields(self): self.currentIdx = None self.form.fieldList.clear() @@ -156,8 +157,7 @@ def fillFields(self): fields = configs["current config"]["Special field"] for c, f in enumerate(fields): - self.form.fieldList.addItem("{}: {}".format(c+1, f - )) + self.form.fieldList.addItem("{}: {}".format(c + 1, f)) def setupSignals(self): f = self.form @@ -192,9 +192,7 @@ def b5_press(self): mw.addonManager.writeConfig(__name__, fullconfig) def restoreConfig(self): - addon = __name__.split(".")[0] - configs["current config"] = copy.deepcopy( - configs["user default config"]) + configs["current config"] = copy.deepcopy(configs["user default config"]) mw.addonManager.writeConfig(__name__, fullconfig) @@ -211,14 +209,13 @@ def restoreConfig(self): self.b3.setChecked(updateDesc) self.b4.setChecked(updateStyle) self.b5.setChecked(upOnlyIfNewer) - #self.t1.setText(KEEPTAGTEXT) + # self.t1.setText(KEEPTAGTEXT) showInfo("Settings Restored") self.close() onFieldsExecute() def setConfig(self): - configs["user default config"] = copy.deepcopy( - configs["current config"]) + configs["user default config"] = copy.deepcopy(configs["current config"]) mw.addonManager.writeConfig(__name__, fullconfig) allSpecial = configs["current config"]["All fields are special"] @@ -234,7 +231,7 @@ def setConfig(self): self.b3.setChecked(updateDesc) self.b4.setChecked(updateStyle) self.b5.setChecked(upOnlyIfNewer) - #self.t1.setText(KEEPTAGTEXT) + # self.t1.setText(KEEPTAGTEXT) showInfo("Settings Restored") self.close() onFieldsExecute() @@ -270,7 +267,7 @@ def importPresetConfig(self): showInfo("Settings applied for importing tags") def updatePresetConfig(self): - addon = __name__.split(".")[0] + # addon = __name__.split(".")[0] # mw.addonManager.writeAddonMeta(addon, conf) configs["current config"]["All fields are special"] = False @@ -324,7 +321,7 @@ def onAdd(self): self.fillFields() fields = configs["current config"]["Special field"] self.specialFields = fields - self.form.fieldList.setCurrentRow(len(self.specialFields)-1) + self.form.fieldList.setCurrentRow(len(self.specialFields) - 1) mw.addonManager.writeConfig(__name__, fullconfig) def onDelete(self): @@ -339,18 +336,18 @@ def saveField(self): if self.currentIdx is None: return - fields = configs["current config"]["Special field"] - fields = self.specialFields mw.addonManager.writeConfig(__name__, fullconfig) def onHelp(self): - #openHelp("fields") - webbrowser.open('https://youtu.be/TTHpODHBk3U') + # openHelp("fields") + webbrowser.open("https://youtu.be/TTHpODHBk3U") + def returnTagsText(): global KEEPTAGTEXT return KEEPTAGTEXT + def onFields(self): # Use existing FieldDialog as template for UI. fields = configs["current config"]["Special field"] diff --git a/Specialfields21/note_type_mapping.py b/Specialfields21/note_type_mapping.py index b0ac1cb..57eb496 100644 --- a/Specialfields21/note_type_mapping.py +++ b/Specialfields21/note_type_mapping.py @@ -1,9 +1,7 @@ -from typing import Optional, Dict from abc import ABC +from typing import Dict, Optional from anki.models import NoteType -from aqt.utils import showText -from aqt import mw class NoteTypeMapping(ABC): From 2a69c3a5a9b9eead9390046e33ea0b983cbaec87 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Fri, 31 Dec 2021 14:59:09 +0100 Subject: [PATCH 24/33] Add old intTime definition --- Specialfields21/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index ef8df65..761035d 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -1,3 +1,5 @@ +import time + from anki.hooks import schema_will_change from anki.importing import Anki2Importer from anki.lang import _ @@ -383,4 +385,10 @@ def _did(self, did: int): return newid +def intTime(scale: int = 1) -> int: + # copied from aqt.utils of Anki versions < 2.1.50 + "The time in integer seconds. Pass scale=1000 to get milliseconds." + return int(time.time() * scale) + + Anki2Importer._did = _did From 5a3540fb241c394fd271406354444b3a36a860b2 Mon Sep 17 00:00:00 2001 From: AnKingMed <57678116+AnKingMed@users.noreply.github.com> Date: Sun, 16 Jan 2022 14:05:50 -0700 Subject: [PATCH 25/33] Update version --- Specialfields21/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Specialfields21/manifest.json b/Specialfields21/manifest.json index b19d4de..6fb039d 100644 --- a/Specialfields21/manifest.json +++ b/Specialfields21/manifest.json @@ -3,6 +3,6 @@ "package": "1102281552", "ankiweb_id": "1102281552", "author": "John Pincock", - "version": "5.3", + "version": "5.4", "conflicts": [] } From a8c623335842be60742bac7907e916300c356d13 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Wed, 23 Feb 2022 15:55:45 +0100 Subject: [PATCH 26/33] Fix Passes invalid note ids to `models.change()` #30 --- Specialfields21/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 761035d..12d539e 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -2,6 +2,7 @@ from anki.hooks import schema_will_change from anki.importing import Anki2Importer +from anki.importing.anki2 import Anki2Importer from anki.lang import _ from aqt import mw @@ -33,7 +34,7 @@ def getUserOptionSpecial(key=None, default=None): return default -def newImportNotes(self) -> None: +def newImportNotes(self: Anki2Importer) -> None: # build guid -> (id,mod,mid) hash & map of existing note ids self._notes = {} existing = {} @@ -112,7 +113,7 @@ def newImportNotes(self) -> None: if updateNoteType and mapping: self.dst.models.change( old_model, - [note[NID]], + [oldNid], target_model, mapping.get_field_map(), mapping.get_card_type_map(), From 0096d2189cb5eb3134214ececb12e420ac3b4be4 Mon Sep 17 00:00:00 2001 From: Abdo Date: Thu, 17 Nov 2022 19:09:00 +0300 Subject: [PATCH 27/33] Disable the new APKG importer --- Specialfields21/__init__.py | 7 +++++++ Specialfields21/new_importer.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 Specialfields21/new_importer.py diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index 12d539e..a8479a6 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -5,6 +5,7 @@ from anki.importing.anki2 import Anki2Importer from anki.lang import _ from aqt import mw +from aqt import appVersion from .config import getUserOption from .dialog import returnTagsText @@ -393,3 +394,9 @@ def intTime(scale: int = 1) -> int: Anki2Importer._did = _did + +anki_version = tuple(int(p) for p in appVersion.split(".")) +if anki_version >= (2, 1, 54): + from .new_importer import patch_new_importer + + patch_new_importer() diff --git a/Specialfields21/new_importer.py b/Specialfields21/new_importer.py new file mode 100644 index 0000000..cb9f83f --- /dev/null +++ b/Specialfields21/new_importer.py @@ -0,0 +1,12 @@ +from aqt.import_export.importing import ApkgImporter +from aqt.importing import importFile +from aqt.main import AnkiQt + +# NOTE: This just disables the new APKG importer to keep the add-on working when the new import/export handling is enabled + +def do_import(mw: AnkiQt, path: str) -> None: + importFile(mw, path) + + +def patch_new_importer() -> None: + ApkgImporter.do_import = do_import From 0a882af22b2d6b889c8c4bd9534f63cee31251f7 Mon Sep 17 00:00:00 2001 From: Abdo Date: Tue, 31 Oct 2023 13:23:52 +0300 Subject: [PATCH 28/33] Use scoped Qt enums --- Specialfields21/dialog.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index b1fab6e..4ae9809 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -42,11 +42,11 @@ def __init__(self, mw, fields, ord=0, parent=None): self.form = aqt.forms.fields.Ui_Dialog() self.form.setupUi(self) self.setWindowTitle(_("Special Fields")) - self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False) - if self.form.buttonBox.button(QDialogButtonBox.Close): - self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False) + self.form.buttonBox.button(QDialogButtonBox.StandardButton.Help).setAutoDefault(False) + if self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close): + self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close).setAutoDefault(False) else: - self.form.buttonBox.button(QDialogButtonBox.Close) + self.form.buttonBox.button(QDialogButtonBox.StandardButton.Close) self.currentIdx = None self.fillFields() self.setupSignals() @@ -116,7 +116,7 @@ def setupOptions(self): self.form._2.addWidget(self.b9, 2, 2) self.l1 = QLabel("
Protected Tags:
", self) - self.l1.setAlignment(Qt.AlignRight) + self.l1.setAlignment(Qt.AlignmentFlag.AlignRight) self.form._2.addWidget(self.l1, 3, 0) self.l1.setToolTip( f'
When updating, all tags except those containing these phrases will be updated (separate multiple terms by a space - case sensitive!)
' From eb9d31d0545c1e9b1533ff01f1b5ac38012b8c5c Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 16 Mar 2024 21:11:42 +0300 Subject: [PATCH 29/33] Stop disabling new APKG importer --- Specialfields21/__init__.py | 7 ------- Specialfields21/new_importer.py | 12 ------------ 2 files changed, 19 deletions(-) delete mode 100644 Specialfields21/new_importer.py diff --git a/Specialfields21/__init__.py b/Specialfields21/__init__.py index a8479a6..12d539e 100644 --- a/Specialfields21/__init__.py +++ b/Specialfields21/__init__.py @@ -5,7 +5,6 @@ from anki.importing.anki2 import Anki2Importer from anki.lang import _ from aqt import mw -from aqt import appVersion from .config import getUserOption from .dialog import returnTagsText @@ -394,9 +393,3 @@ def intTime(scale: int = 1) -> int: Anki2Importer._did = _did - -anki_version = tuple(int(p) for p in appVersion.split(".")) -if anki_version >= (2, 1, 54): - from .new_importer import patch_new_importer - - patch_new_importer() diff --git a/Specialfields21/new_importer.py b/Specialfields21/new_importer.py deleted file mode 100644 index cb9f83f..0000000 --- a/Specialfields21/new_importer.py +++ /dev/null @@ -1,12 +0,0 @@ -from aqt.import_export.importing import ApkgImporter -from aqt.importing import importFile -from aqt.main import AnkiQt - -# NOTE: This just disables the new APKG importer to keep the add-on working when the new import/export handling is enabled - -def do_import(mw: AnkiQt, path: str) -> None: - importFile(mw, path) - - -def patch_new_importer() -> None: - ApkgImporter.do_import = do_import From dd9ea1a42659a89da6d2f4321016a3d89fb54e53 Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 16 Mar 2024 21:29:53 +0300 Subject: [PATCH 30/33] Offer to enable legacy importer/exporter --- Specialfields21/dialog.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index 4ae9809..c526542 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -5,7 +5,8 @@ from anki.consts import * from aqt import mw from aqt.qt import * -from aqt.utils import getOnlyText, showInfo, showWarning +from aqt.utils import getOnlyText, showInfo, showWarning, askUser +from anki.utils import pointVersion from .config import getUserOption @@ -349,6 +350,10 @@ def returnTagsText(): def onFields(self): + if pointVersion() >= 55 and not mw.pm.legacy_import_export(): + yes = askUser('Special Fields doesn\'t work without enabling the "Legacy import/export handling" preference. Do you want to enable it?') + if yes: + mw.pm.set_legacy_import_export(True) # Use existing FieldDialog as template for UI. fields = configs["current config"]["Special field"] FieldDialog(mw, fields, parent=self) From 357d14aab14ed38d52d8d2e9e72aebccf1a1ab88 Mon Sep 17 00:00:00 2001 From: Abdo Date: Mon, 18 Mar 2024 19:13:03 +0300 Subject: [PATCH 31/33] Add sentence about importing new files --- Specialfields21/dialog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index c526542..ce3e358 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -351,7 +351,9 @@ def returnTagsText(): def onFields(self): if pointVersion() >= 55 and not mw.pm.legacy_import_export(): - yes = askUser('Special Fields doesn\'t work without enabling the "Legacy import/export handling" preference. Do you want to enable it?') + yes = askUser( + 'Special Fields doesn\'t work without enabling the "Legacy import/export handling" preference. Do you want to enable it? This may make it so you cannot import newer files. If you experience errors, turn this setting off temporarily' + ) if yes: mw.pm.set_legacy_import_export(True) # Use existing FieldDialog as template for UI. From e1d41c2e5810938a2946756c6076b398017def69 Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 26 Oct 2024 15:43:50 +0300 Subject: [PATCH 32/33] Prompt to do a legacy import dynamically https://github.com/ankitects/anki/issues/3493 --- Specialfields21/dialog.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/Specialfields21/dialog.py b/Specialfields21/dialog.py index ce3e358..91e73b3 100644 --- a/Specialfields21/dialog.py +++ b/Specialfields21/dialog.py @@ -2,11 +2,13 @@ import webbrowser import aqt +import aqt.importing from anki.consts import * +from anki.hooks import wrap +from anki.utils import pointVersion from aqt import mw from aqt.qt import * -from aqt.utils import getOnlyText, showInfo, showWarning, askUser -from anki.utils import pointVersion +from aqt.utils import askUser, getOnlyText, showInfo, showWarning from .config import getUserOption @@ -350,12 +352,6 @@ def returnTagsText(): def onFields(self): - if pointVersion() >= 55 and not mw.pm.legacy_import_export(): - yes = askUser( - 'Special Fields doesn\'t work without enabling the "Legacy import/export handling" preference. Do you want to enable it? This may make it so you cannot import newer files. If you experience errors, turn this setting off temporarily' - ) - if yes: - mw.pm.set_legacy_import_export(True) # Use existing FieldDialog as template for UI. fields = configs["current config"]["Special field"] FieldDialog(mw, fields, parent=self) @@ -364,9 +360,27 @@ def onFields(self): def onFieldsExecute(): onFields(mw) +def wants_legacy_import(): + return askUser('Import using the Special Fields add-on?') + +def prompt_for_file_then_import_override(mw, _old): + if wants_legacy_import(): + aqt.importing.onImport(mw) + else: + _old(mw) + +def import_file_override(mw, path, _old): + if wants_legacy_import(): + aqt.importing.importFile(mw, path) + else: + _old(mw, path) mw.addonManager.setConfigAction(__name__, onFieldsExecute) action = QAction("Special Fields", mw) action.setShortcut(QKeySequence("Ctrl+shift+s")) action.triggered.connect(onFields) mw.form.menuTools.addAction(action) +if pointVersion() >= 55: + aqt.main.prompt_for_file_then_import = wrap(aqt.main.prompt_for_file_then_import, prompt_for_file_then_import_override, "around") + # for drag & drop + aqt.main.import_file = wrap(aqt.main.import_file, import_file_override, "around") From 65943c5f27e7260bc71f754d869fc376ff256fb2 Mon Sep 17 00:00:00 2001 From: Abdo Date: Tue, 25 Feb 2025 14:47:39 +0300 Subject: [PATCH 33/33] Add 'Personal Notes' field --- Specialfields21/config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Specialfields21/config.json b/Specialfields21/config.json index f8dfd8f..6ca0be7 100644 --- a/Specialfields21/config.json +++ b/Specialfields21/config.json @@ -4,7 +4,7 @@ "Combine tagging": true, "Protected tags": ["%%keep%%"], "Special field": - ["Lecture Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], + ["Lecture Notes", "Personal Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], "update deck description": false, "update note styling": false, "update only if newer": false @@ -14,7 +14,7 @@ "Combine tagging": true, "Protected tags": ["%%keep%%"], "Special field": - ["Lecture Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], + ["Lecture Notes", "Personal Notes", "Missed Questions", "Pathoma", "Boards and Beyond"], "update deck description": false, "update note styling": false, "update only if newer": false