From 90f403d3ffc85b8867a20239b30684a6693542af Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sun, 26 Jan 2025 15:07:45 +0100 Subject: [PATCH 1/8] Preserve CSS below ankihub end comment --- ankihub/main/importing.py | 8 ++- ankihub/main/utils.py | 101 ++++++++++++++++++++++---------------- ankihub/settings.py | 9 +++- 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/ankihub/main/importing.py b/ankihub/main/importing.py index 3b63f701d..b6da87bc7 100644 --- a/ankihub/main/importing.py +++ b/ankihub/main/importing.py @@ -46,7 +46,7 @@ get_unique_ankihub_deck_name, is_tag_in_list, lowest_level_common_ancestor_did, - note_type_with_updated_templates, + note_type_with_updated_templates_and_css, truncated_list, ) @@ -782,13 +782,11 @@ def _update_templates_and_css( ) ) - updated_note_type = note_type_with_updated_templates( + updated_note_type = note_type_with_updated_templates_and_css( old_note_type=local_note_type, new_note_type=remote_note_type, - use_new_templates=use_new_templates_and_css, + use_new_templates_and_css=use_new_templates_and_css, ) - if use_new_templates_and_css: - updated_note_type["css"] = remote_note_type["css"] aqt.mw.col.models.update_dict(updated_note_type) diff --git a/ankihub/main/utils.py b/ankihub/main/utils.py index 999b98745..ef7910817 100644 --- a/ankihub/main/utils.py +++ b/ankihub/main/utils.py @@ -22,9 +22,10 @@ from ..settings import ( ANKI_INT_VERSION, ANKI_VERSION_23_10_00, + ANKIHUB_CSS_END_COMMENT, + ANKIHUB_HTML_END_COMMENT, ANKIHUB_NOTE_TYPE_FIELD_NAME, ANKIHUB_NOTE_TYPE_MODIFICATION_STRING, - ANKIHUB_TEMPLATE_END_COMMENT, url_mh_integrations_preview, url_view_note, ) @@ -35,8 +36,11 @@ # Pattern for the AnkiHub end comment in card templates. # The end comment is used to allow users to add their own content below it without it being overwritten # when the template is updated. -ANKIHUB_END_COMMENT_PATTERN = re.compile( - rf"{ANKIHUB_TEMPLATE_END_COMMENT}(?P[\w\W]*)" +ANKIHUB_HTML_END_COMMENT_PATTERN = re.compile( + rf"{re.escape(ANKIHUB_HTML_END_COMMENT)}(?P[\w\W]*)" +) +ANKIHUB_CSS_END_COMMENT_PATTERN = re.compile( + rf"{re.escape(ANKIHUB_CSS_END_COMMENT)}(?P[\w\W]*)" ) # decks @@ -429,11 +433,11 @@ def _add_ankihub_end_comment_to_template(template: Dict) -> None: overwritten when the note type is updated.""" for key in ["qfmt", "afmt"]: cur_side = template[key] - if re.search(ANKIHUB_TEMPLATE_END_COMMENT, cur_side): + if re.search(re.escape(ANKIHUB_HTML_END_COMMENT), cur_side): continue template[key] = ( - template[key].rstrip("\n ") + "\n\n" + ANKIHUB_TEMPLATE_END_COMMENT + "\n\n" + template[key].rstrip("\n ") + "\n\n" + ANKIHUB_HTML_END_COMMENT + "\n\n" ) LOGGER.info( "Added ANKIHUB_TEMPLATE_END_COMMENT to template.", @@ -442,22 +446,24 @@ def _add_ankihub_end_comment_to_template(template: Dict) -> None: ) -def note_type_with_updated_templates( - old_note_type: NotetypeDict, new_note_type: NotetypeDict, use_new_templates: bool +def note_type_with_updated_templates_and_css( + old_note_type: NotetypeDict, + new_note_type: NotetypeDict, + use_new_templates_and_css: bool, ) -> NotetypeDict: - """Returns the new note type with modifications applied to the card templates. - The new templates are used as the base if use_new_templates is True. + """Returns the new note type with modifications applied to the card templates and css. + The new templates and css are used as the base if use_new_templates_and_css is True. The modifications are as follows: - The View on AnkiHub button is added to the back side of each template. - - Contents below the AnkiHub end comments are preserved when the template is updated. + - Contents below the AnkiHub end comments are preserved when the template/css is updated. Args: old_note_type (NotetypeDict): The old note tpye. The contents below the AnkiHub end comments is preserved when the template is updated. new_note_type (NotetypeDict): The new note type. - use_new_templates (bool): If True, the templates from the new_note_type are used as the base for - the updated templates, otherwise the old templates are used. Then this function just refreshes + use_new_templates_and_css (bool): If True, the templates/css from the new_note_type are used as the base for + the updated templates/css, otherwise the old templates/css are used. Then this function just refreshes the modifications or adds them if they are not present. Returns: @@ -468,66 +474,75 @@ def note_type_with_updated_templates( for new_template, old_template in zip( new_note_type["tmpls"], old_note_type["tmpls"] ): - if use_new_templates: + if use_new_templates_and_css: updated_template = copy.deepcopy(new_template) else: updated_template = copy.deepcopy(old_template) - # Update template sides for template_side_name in ["qfmt", "afmt"]: - updated_template[template_side_name] = _updated_template_side( - new_template_side=new_template[template_side_name], - old_template_side=old_template[template_side_name], - template_side_name=template_side_name, - use_new_template=use_new_templates, + updated_template[template_side_name] = _upated_note_type_content( + new_content=new_template[template_side_name], + old_content=old_template[template_side_name], + add_view_on_ankihub_snippet=template_side_name == "afmt", + use_new_content=use_new_templates_and_css, + content_type="html", ) updated_templates.append(updated_template) result = copy.deepcopy(old_note_type) result["tmpls"] = updated_templates + + result["css"] = _upated_note_type_content( + new_content=new_note_type["css"], + old_content=old_note_type["css"], + add_view_on_ankihub_snippet=False, + use_new_content=use_new_templates_and_css, + content_type="css", + ) + return result -def _updated_template_side( - new_template_side: str, - old_template_side: str, - template_side_name: str, - use_new_template: bool, +def _upated_note_type_content( + new_content: str, + old_content: str, + add_view_on_ankihub_snippet: bool, + use_new_content: bool, + content_type: str, ) -> str: """ Args: - template_side_name (str): The name of the template side ("qfmt" or "afmt"). - use_new_template (bool): If True, the new template side is used as the base for - the updated template side, otherwise the old template side is used. + use_new_contnet (bool): If True, the new content is used as the base for + the updated content, otherwise the old content is used. Returns: - str: The updated template side with the AnkiHub modifications applied. + str: The updated content with the AnkiHub modifications applied. """ - m = re.search(ANKIHUB_END_COMMENT_PATTERN, old_template_side) - html_to_migrate = m.group("html_to_migrate") if m else "" + if content_type == "html": + end_comment = ANKIHUB_HTML_END_COMMENT + end_comment_pattern = ANKIHUB_HTML_END_COMMENT_PATTERN + else: + end_comment = ANKIHUB_CSS_END_COMMENT + end_comment_pattern = ANKIHUB_CSS_END_COMMENT_PATTERN + + m = re.search(end_comment_pattern, old_content) + text_to_migrate = m.group("text_to_migrate") if m else "" # Choose the base for the result - if use_new_template: - result = new_template_side + if use_new_content: + result = new_content else: - result = old_template_side + result = old_content # Remove end comment and content below it from the template side. # It will be added back below. - result = re.sub(ANKIHUB_END_COMMENT_PATTERN, "", result) + result = re.sub(end_comment_pattern, "", result) - if template_side_name == "afmt": - # The view on AnkiHub button is added to the back side of the template. + if add_view_on_ankihub_snippet: result = _template_side_with_view_on_ankihub_snippet(result) # Add the AnkiHub end comment and the content below it back to the template side. - return ( - result.rstrip("\n ") - + "\n\n" - + ANKIHUB_TEMPLATE_END_COMMENT - + "\n" - + html_to_migrate - ) + return result.rstrip("\n ") + "\n\n" + end_comment + "\n" + text_to_migrate # ... undo modifications diff --git a/ankihub/settings.py b/ankihub/settings.py index daea48f71..8a3b29e81 100644 --- a/ankihub/settings.py +++ b/ankihub/settings.py @@ -876,13 +876,20 @@ def _send_logs_to_datadog(self, records: List[logging.LogRecord]) -> None: ANKIHUB_NOTE_TYPE_FIELD_NAME = "ankihub_id" ANKIHUB_NOTE_TYPE_MODIFICATION_STRING = "ANKIHUB MODFICATIONS" -ANKIHUB_TEMPLATE_END_COMMENT = ( +ANKIHUB_HTML_END_COMMENT = ( "" ) +ANKIHUB_CSS_END_COMMENT = ( + "/*\n" + "ANKIHUB_END\n" + "Text below this comment will not be modified by AnkiHub or AnKing add-ons.\n" + "Do not edit or remove this comment if you want to protect the content below.\n" + "*/" +) ADDON_PACKAGE = __name__.split(".")[0] ICONS_PATH = ADDON_PATH / "gui/icons" From 3910473c50b197a55b55ae4518f34c902ce346e2 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sun, 26 Jan 2025 15:08:36 +0100 Subject: [PATCH 2/8] Update tests --- ...emplates.test_basic.css.False.approved.txt | 7 ++++ ...Templates.test_basic.css.True.approved.txt | 7 ++++ ..._from_old_note_type.css.False.approved.txt | 9 ++++ ...t_from_old_note_type.css.True.approved.txt | 9 ++++ tests/addon/test_integration.py | 10 ++--- tests/addon/test_unit.py | 42 +++++++++++++------ 6 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.False.approved.txt create mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.True.approved.txt create mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt create mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.False.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.False.approved.txt new file mode 100644 index 000000000..1cbb94572 --- /dev/null +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.False.approved.txt @@ -0,0 +1,7 @@ +old content + +/* +ANKIHUB_END +Text below this comment will not be modified by AnkiHub or AnKing add-ons. +Do not edit or remove this comment if you want to protect the content below. +*/ diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.True.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.True.approved.txt new file mode 100644 index 000000000..3a7009f0e --- /dev/null +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.css.True.approved.txt @@ -0,0 +1,7 @@ +new content + +/* +ANKIHUB_END +Text below this comment will not be modified by AnkiHub or AnKing add-ons. +Do not edit or remove this comment if you want to protect the content below. +*/ diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt new file mode 100644 index 000000000..f7e9d08ed --- /dev/null +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt @@ -0,0 +1,9 @@ +old css + +/* +ANKIHUB_END +Text below this comment will not be modified by AnkiHub or AnKing add-ons. +Do not edit or remove this comment if you want to protect the content below. +*/ + +content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt new file mode 100644 index 000000000..e2339ac00 --- /dev/null +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt @@ -0,0 +1,9 @@ +new content + +/* +ANKIHUB_END +Text below this comment will not be modified by AnkiHub or AnKing add-ons. +Do not edit or remove this comment if you want to protect the content below. +*/ + +content to migrate diff --git a/tests/addon/test_integration.py b/tests/addon/test_integration.py index 4141abb25..71c43ad10 100644 --- a/tests/addon/test_integration.py +++ b/tests/addon/test_integration.py @@ -223,9 +223,9 @@ note_type_contains_field, ) from ankihub.settings import ( + ANKIHUB_HTML_END_COMMENT, ANKIHUB_NOTE_TYPE_FIELD_NAME, ANKIHUB_NOTE_TYPE_MODIFICATION_STRING, - ANKIHUB_TEMPLATE_END_COMMENT, AnkiHubCommands, BehaviorOnRemoteNoteDeleted, DeckConfig, @@ -2066,16 +2066,16 @@ def test_note_type_templates_and_css_are_updated( assert new_qfmt in updated_qfmt else: assert new_qfmt not in updated_qfmt - assert ANKIHUB_TEMPLATE_END_COMMENT in updated_qfmt + assert ANKIHUB_HTML_END_COMMENT in updated_qfmt updated_afmt = updated_note_type["tmpls"][0]["afmt"] if expected_template_and_css_updated: assert new_afmt in updated_afmt - assert updated_note_type["css"] == new_css + assert new_css in updated_note_type["css"] else: assert new_afmt not in updated_afmt - assert updated_note_type["css"] == old_css - assert ANKIHUB_TEMPLATE_END_COMMENT in updated_afmt + assert new_css not in updated_note_type["css"] + assert ANKIHUB_HTML_END_COMMENT in updated_afmt # This is only on the back template (afmt) assert ANKIHUB_NOTE_TYPE_MODIFICATION_STRING in updated_afmt diff --git a/tests/addon/test_unit.py b/tests/addon/test_unit.py index cf099846b..57e7a3594 100644 --- a/tests/addon/test_unit.py +++ b/tests/addon/test_unit.py @@ -151,11 +151,12 @@ lowest_level_common_ancestor_deck_name, mh_tag_to_resource, mids_of_notes, - note_type_with_updated_templates, + note_type_with_updated_templates_and_css, retain_nids_with_ah_note_type, ) from ankihub.settings import ( - ANKIHUB_TEMPLATE_END_COMMENT, + ANKIHUB_CSS_END_COMMENT, + ANKIHUB_HTML_END_COMMENT, ANKIWEB_ID, DatadogLogHandler, config, @@ -2918,18 +2919,20 @@ class TestNoteTypeWithUpdatedTemplates: def test_basic(self, use_new_templates: bool): old_note_type_content = "old content" old_note_type = { - "tmpls": [{"qfmt": old_note_type_content, "afmt": old_note_type_content}] + "tmpls": [{"qfmt": old_note_type_content, "afmt": old_note_type_content}], + "css": old_note_type_content, } new_note_type_content = "new content" new_note_type = { - "tmpls": [{"qfmt": new_note_type_content, "afmt": new_note_type_content}] + "tmpls": [{"qfmt": new_note_type_content, "afmt": new_note_type_content}], + "css": new_note_type_content, } - updated_note_type = note_type_with_updated_templates( + updated_note_type = note_type_with_updated_templates_and_css( old_note_type=old_note_type, new_note_type=new_note_type, - use_new_templates=use_new_templates, + use_new_templates_and_css=use_new_templates, ) assert len(updated_note_type["tmpls"]) == 1 template = updated_note_type["tmpls"][0] @@ -2942,26 +2945,37 @@ def test_basic(self, use_new_templates: bool): template["afmt"], options=NamerFactory.with_parameters("afmt", use_new_templates), ) + verify( + updated_note_type["css"], + options=NamerFactory.with_parameters("css", use_new_templates), + ) @pytest.mark.parametrize("use_new_templates", [True, False]) def test_with_migrating_content_from_old_note_type(self, use_new_templates: bool): content_to_migrate = "content to migrate" - old_note_type_content = ( - f"old content\n{ANKIHUB_TEMPLATE_END_COMMENT}\n{content_to_migrate}" + old_note_type_html_content = ( + f"old content\n{ANKIHUB_HTML_END_COMMENT}\n{content_to_migrate}" + ) + old_note_type_css_content = ( + f"old css\n{ANKIHUB_CSS_END_COMMENT}\n{content_to_migrate}" ) old_note_type = { - "tmpls": [{"qfmt": old_note_type_content, "afmt": old_note_type_content}] + "tmpls": [ + {"qfmt": old_note_type_html_content, "afmt": old_note_type_html_content} + ], + "css": old_note_type_css_content, } new_note_type_content = "new content" new_note_type = { - "tmpls": [{"qfmt": new_note_type_content, "afmt": new_note_type_content}] + "tmpls": [{"qfmt": new_note_type_content, "afmt": new_note_type_content}], + "css": new_note_type_content, } - updated_note_type = note_type_with_updated_templates( + updated_note_type = note_type_with_updated_templates_and_css( old_note_type=old_note_type, new_note_type=new_note_type, - use_new_templates=use_new_templates, + use_new_templates_and_css=use_new_templates, ) assert len(updated_note_type["tmpls"]) == 1 template = updated_note_type["tmpls"][0] @@ -2974,6 +2988,10 @@ def test_with_migrating_content_from_old_note_type(self, use_new_templates: bool template["afmt"], options=NamerFactory.with_parameters("afmt", use_new_templates), ) + verify( + updated_note_type["css"], + options=NamerFactory.with_parameters("css", use_new_templates), + ) def test_get_daily_review_data_since_last_sync(mocker, anki_session_with_addon_data): From 23c263d5da4a758bf26f436ff1c7cc0e4be91ffb Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sun, 26 Jan 2025 15:45:05 +0100 Subject: [PATCH 3/8] Use note_type_with_updated_templates_and_css during deck creation, refactor --- ankihub/main/deck_creation.py | 6 +-- ankihub/main/importing.py | 3 +- ankihub/main/utils.py | 94 ++++++++++----------------------- tests/addon/test_integration.py | 8 +-- tests/addon/test_unit.py | 6 +-- tests/fixtures.py | 4 +- 6 files changed, 39 insertions(+), 82 deletions(-) diff --git a/ankihub/main/deck_creation.py b/ankihub/main/deck_creation.py index 1e4ba23d5..d899e9601 100644 --- a/ankihub/main/deck_creation.py +++ b/ankihub/main/deck_creation.py @@ -3,7 +3,6 @@ import re import typing import uuid -from copy import deepcopy from dataclasses import dataclass from typing import Dict, List @@ -23,7 +22,7 @@ change_note_types_of_notes, create_backup, get_note_types_in_deck, - modify_note_type, + modified_note_type, ) @@ -88,8 +87,7 @@ def _create_note_types_for_deck(deck_id: DeckId) -> Dict[NotetypeId, NotetypeId] result: Dict[NotetypeId, NotetypeId] = {} model_ids = get_note_types_in_deck(deck_id) for mid in model_ids: - new_model = deepcopy(aqt.mw.col.models.get(mid)) - modify_note_type(new_model) + new_model = modified_note_type(aqt.mw.col.models.get(mid)) name_without_modifications = _note_type_name_without_ankihub_modifications( new_model["name"] ) diff --git a/ankihub/main/importing.py b/ankihub/main/importing.py index b6da87bc7..f0171f154 100644 --- a/ankihub/main/importing.py +++ b/ankihub/main/importing.py @@ -784,8 +784,7 @@ def _update_templates_and_css( updated_note_type = note_type_with_updated_templates_and_css( old_note_type=local_note_type, - new_note_type=remote_note_type, - use_new_templates_and_css=use_new_templates_and_css, + new_note_type=(remote_note_type if use_new_templates_and_css else None), ) aqt.mw.col.models.update_dict(updated_note_type) diff --git a/ankihub/main/utils.py b/ankihub/main/utils.py index ef7910817..a2eb06277 100644 --- a/ankihub/main/utils.py +++ b/ankihub/main/utils.py @@ -302,26 +302,26 @@ def get_anki_nid_to_mid_dict(nids: Collection[NoteId]) -> Dict[NoteId, NotetypeI ) -def modify_note_type(note_type: NotetypeDict) -> None: - """Adds the AnkiHub ID Field to the Note Type and modifies the card templates.""" - LOGGER.info( - "Modifying note type...", - note_type_name=note_type["name"], - note_type_id=note_type["id"], - ) +def modified_note_type(note_type: NotetypeDict) -> NotetypeDict: + """Returns a modified version of the note type with the AnkiHub field added and + the card templates updated.""" + note_type = copy.deepcopy(note_type) modify_fields(note_type) - templates = note_type["tmpls"] - for template in templates: - modify_template(template) + return note_type_with_updated_templates_and_css( + old_note_type=note_type, + new_note_type=None, + ) def modify_note_type_templates(note_type_ids: Iterable[NotetypeId]) -> None: for mid in note_type_ids: note_type = aqt.mw.col.models.get(mid) - for template in note_type["tmpls"]: - modify_template(template) + note_type = note_type_with_updated_templates_and_css( + old_note_type=note_type, + new_note_type=None, + ) aqt.mw.col.models.update_dict(note_type) @@ -341,12 +341,6 @@ def modify_fields(note_type: Dict) -> None: note_type["flds"].append(ankihub_field) -def modify_template(template: Dict) -> None: - # the order is important here, the end comment must be added last - template["afmt"] = _template_side_with_view_on_ankihub_snippet(template["afmt"]) - _add_ankihub_end_comment_to_template(template) - - def _template_side_with_view_on_ankihub_snippet(template_side: str) -> str: """Return template html with the AnkiHub view note snippet added to it.""" snippet = dedent( @@ -427,32 +421,12 @@ def _template_side_with_view_on_ankihub_snippet(template_side: str) -> str: ) -def _add_ankihub_end_comment_to_template(template: Dict) -> None: - """Add the AnkiHub end comment to the template if it is not already present. - The purpose of the AnkiHub end comment is to allow users to add their own content below it without it being - overwritten when the note type is updated.""" - for key in ["qfmt", "afmt"]: - cur_side = template[key] - if re.search(re.escape(ANKIHUB_HTML_END_COMMENT), cur_side): - continue - - template[key] = ( - template[key].rstrip("\n ") + "\n\n" + ANKIHUB_HTML_END_COMMENT + "\n\n" - ) - LOGGER.info( - "Added ANKIHUB_TEMPLATE_END_COMMENT to template.", - template=template["name"], - key=key, - ) - - def note_type_with_updated_templates_and_css( old_note_type: NotetypeDict, - new_note_type: NotetypeDict, - use_new_templates_and_css: bool, + new_note_type: Optional[NotetypeDict], ) -> NotetypeDict: """Returns the new note type with modifications applied to the card templates and css. - The new templates and css are used as the base if use_new_templates_and_css is True. + The new templates and css are used as the base if they are provided. The modifications are as follows: - The View on AnkiHub button is added to the back side of each template. @@ -461,30 +435,30 @@ def note_type_with_updated_templates_and_css( Args: old_note_type (NotetypeDict): The old note tpye. The contents below the AnkiHub end comments is preserved when the template is updated. - new_note_type (NotetypeDict): The new note type. - use_new_templates_and_css (bool): If True, the templates/css from the new_note_type are used as the base for - the updated templates/css, otherwise the old templates/css are used. Then this function just refreshes - the modifications or adds them if they are not present. + new_note_type (Optional[NotetypeDict]): The new note type. If provided, the templates and css are updated + based on this. Returns: NotetypeDict: The updated note type. """ updated_templates = [] - for new_template, old_template in zip( - new_note_type["tmpls"], old_note_type["tmpls"] - ): - if use_new_templates_and_css: + for template_idx, old_template in enumerate(old_note_type["tmpls"]): + if new_note_type is not None: + new_template = new_note_type["tmpls"][template_idx] updated_template = copy.deepcopy(new_template) else: updated_template = copy.deepcopy(old_template) for template_side_name in ["qfmt", "afmt"]: updated_template[template_side_name] = _upated_note_type_content( - new_content=new_template[template_side_name], old_content=old_template[template_side_name], + new_content=( + new_template[template_side_name] + if new_note_type is not None + else None + ), add_view_on_ankihub_snippet=template_side_name == "afmt", - use_new_content=use_new_templates_and_css, content_type="html", ) updated_templates.append(updated_template) @@ -493,10 +467,9 @@ def note_type_with_updated_templates_and_css( result["tmpls"] = updated_templates result["css"] = _upated_note_type_content( - new_content=new_note_type["css"], old_content=old_note_type["css"], + new_content=new_note_type["css"] if new_note_type is not None else None, add_view_on_ankihub_snippet=False, - use_new_content=use_new_templates_and_css, content_type="css", ) @@ -504,20 +477,12 @@ def note_type_with_updated_templates_and_css( def _upated_note_type_content( - new_content: str, old_content: str, add_view_on_ankihub_snippet: bool, - use_new_content: bool, content_type: str, + new_content: Optional[str], ) -> str: - """ - Args: - use_new_contnet (bool): If True, the new content is used as the base for - the updated content, otherwise the old content is used. - - Returns: - str: The updated content with the AnkiHub modifications applied. - """ + """Returns the updated content with the AnkiHub modifications applied.""" if content_type == "html": end_comment = ANKIHUB_HTML_END_COMMENT end_comment_pattern = ANKIHUB_HTML_END_COMMENT_PATTERN @@ -529,10 +494,7 @@ def _upated_note_type_content( text_to_migrate = m.group("text_to_migrate") if m else "" # Choose the base for the result - if use_new_content: - result = new_content - else: - result = old_content + result = new_content if new_content is not None else old_content # Remove end comment and content below it from the template side. # It will be added back below. diff --git a/tests/addon/test_integration.py b/tests/addon/test_integration.py index 71c43ad10..4ee9cf1c6 100644 --- a/tests/addon/test_integration.py +++ b/tests/addon/test_integration.py @@ -184,7 +184,7 @@ FlashCardSelectorDialog, ) from ankihub.gui.suggestion_dialog import SuggestionDialog -from ankihub.main.deck_creation import create_ankihub_deck, modify_note_type +from ankihub.main.deck_creation import create_ankihub_deck, modified_note_type from ankihub.main.deck_unsubscribtion import uninstall_deck from ankihub.main.exporting import to_note_data from ankihub.main.importing import ( @@ -880,7 +880,7 @@ def test_modify_note_type(anki_session_with_addon_data: AnkiSession): note_type = anki_session.mw.col.models.by_name("Basic") original_note_type = copy.deepcopy(note_type) original_note_template = original_note_type["tmpls"][0]["afmt"] - modify_note_type(note_type) + note_type = modified_note_type(note_type) modified_template = note_type["tmpls"][0]["afmt"] # # TODO Make more precise assertions. assert ANKIHUB_NOTE_TYPE_FIELD_NAME in modified_template @@ -1651,12 +1651,12 @@ def test_adjust_note_types(anki_session_with_addon_data: AnkiSession): ankihub_basic_1 = copy.deepcopy(mw.col.models.by_name("Basic")) ankihub_basic_1["id"] = 1 ankihub_basic_1["name"] = "AnkiHub Basic 1" - modify_note_type(ankihub_basic_1) + ankihub_basic_1 = modified_note_type(ankihub_basic_1) # for testing updating existing note type ankihub_basic_2 = copy.deepcopy(mw.col.models.by_name("Basic")) ankihub_basic_2["name"] = "AnkiHub Basic 2" - modify_note_type(ankihub_basic_2) + ankihub_basic_2 = modified_note_type(ankihub_basic_2) # ... save the note type ankihub_basic_2["id"] = 0 changes = mw.col.models.add_dict(ankihub_basic_2) diff --git a/tests/addon/test_unit.py b/tests/addon/test_unit.py index 57e7a3594..7535530fc 100644 --- a/tests/addon/test_unit.py +++ b/tests/addon/test_unit.py @@ -2931,8 +2931,7 @@ def test_basic(self, use_new_templates: bool): updated_note_type = note_type_with_updated_templates_and_css( old_note_type=old_note_type, - new_note_type=new_note_type, - use_new_templates_and_css=use_new_templates, + new_note_type=new_note_type if use_new_templates else None, ) assert len(updated_note_type["tmpls"]) == 1 template = updated_note_type["tmpls"][0] @@ -2974,8 +2973,7 @@ def test_with_migrating_content_from_old_note_type(self, use_new_templates: bool updated_note_type = note_type_with_updated_templates_and_css( old_note_type=old_note_type, - new_note_type=new_note_type, - use_new_templates_and_css=use_new_templates, + new_note_type=new_note_type if use_new_templates else None, ) assert len(updated_note_type["tmpls"]) == 1 template = updated_note_type["tmpls"][0] diff --git a/tests/fixtures.py b/tests/fixtures.py index b17882c27..3532e9054 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -30,7 +30,7 @@ from ankihub.gui.media_sync import _AnkiHubMediaSync from ankihub.gui.suggestion_dialog import SuggestionMetadata from ankihub.main.importing import AnkiHubImporter -from ankihub.main.utils import modify_note_type +from ankihub.main.utils import modified_note_type from ankihub.settings import BehaviorOnRemoteNoteDeleted, DeckConfig, config @@ -84,7 +84,7 @@ def create_or_get_ah_version_of_note_type( if model := mw.col.models.by_name(note_type["name"]): return model - modify_note_type(note_type) + note_type = modified_note_type(note_type) mw.col.models.add_dict(note_type) return mw.col.models.by_name(note_type["name"]) From d0a538d364197f3a440fa6efe6ed85dc8b5f08cd Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sun, 26 Jan 2025 15:48:59 +0100 Subject: [PATCH 4/8] Refactor --- ankihub/main/utils.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ankihub/main/utils.py b/ankihub/main/utils.py index a2eb06277..885d16433 100644 --- a/ankihub/main/utils.py +++ b/ankihub/main/utils.py @@ -307,7 +307,7 @@ def modified_note_type(note_type: NotetypeDict) -> NotetypeDict: the card templates updated.""" note_type = copy.deepcopy(note_type) - modify_fields(note_type) + _modify_fields(note_type) return note_type_with_updated_templates_and_css( old_note_type=note_type, @@ -315,17 +315,7 @@ def modified_note_type(note_type: NotetypeDict) -> NotetypeDict: ) -def modify_note_type_templates(note_type_ids: Iterable[NotetypeId]) -> None: - for mid in note_type_ids: - note_type = aqt.mw.col.models.get(mid) - note_type = note_type_with_updated_templates_and_css( - old_note_type=note_type, - new_note_type=None, - ) - aqt.mw.col.models.update_dict(note_type) - - -def modify_fields(note_type: Dict) -> None: +def _modify_fields(note_type: Dict) -> None: fields = note_type["flds"] field_names = [field["name"] for field in fields] if settings.ANKIHUB_NOTE_TYPE_FIELD_NAME in field_names: @@ -341,6 +331,16 @@ def modify_fields(note_type: Dict) -> None: note_type["flds"].append(ankihub_field) +def modify_note_type_templates(note_type_ids: Iterable[NotetypeId]) -> None: + for mid in note_type_ids: + note_type = aqt.mw.col.models.get(mid) + note_type = note_type_with_updated_templates_and_css( + old_note_type=note_type, + new_note_type=None, + ) + aqt.mw.col.models.update_dict(note_type) + + def _template_side_with_view_on_ankihub_snippet(template_side: str) -> str: """Return template html with the AnkiHub view note snippet added to it.""" snippet = dedent( @@ -478,9 +478,9 @@ def note_type_with_updated_templates_and_css( def _upated_note_type_content( old_content: str, + new_content: Optional[str], add_view_on_ankihub_snippet: bool, content_type: str, - new_content: Optional[str], ) -> str: """Returns the updated content with the AnkiHub modifications applied.""" if content_type == "html": From 6fcc02d0eab5bb4b8c7979f9e8782a5659cff1c9 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Sun, 26 Jan 2025 15:57:59 +0100 Subject: [PATCH 5/8] Refactor, update comments --- ankihub/main/importing.py | 2 +- ankihub/main/utils.py | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ankihub/main/importing.py b/ankihub/main/importing.py index f0171f154..eb7f7f466 100644 --- a/ankihub/main/importing.py +++ b/ankihub/main/importing.py @@ -784,7 +784,7 @@ def _update_templates_and_css( updated_note_type = note_type_with_updated_templates_and_css( old_note_type=local_note_type, - new_note_type=(remote_note_type if use_new_templates_and_css else None), + new_note_type=remote_note_type if use_new_templates_and_css else None, ) aqt.mw.col.models.update_dict(updated_note_type) diff --git a/ankihub/main/utils.py b/ankihub/main/utils.py index 885d16433..8897810e2 100644 --- a/ankihub/main/utils.py +++ b/ankihub/main/utils.py @@ -425,16 +425,15 @@ def note_type_with_updated_templates_and_css( old_note_type: NotetypeDict, new_note_type: Optional[NotetypeDict], ) -> NotetypeDict: - """Returns the new note type with modifications applied to the card templates and css. - The new templates and css are used as the base if they are provided. + """Returns the updated note type with modifications applied to the card templates and css. + The templates and css of the new note type are used as the base if it is provided. The modifications are as follows: - The View on AnkiHub button is added to the back side of each template. - Contents below the AnkiHub end comments are preserved when the template/css is updated. Args: - old_note_type (NotetypeDict): The old note tpye. The contents below the AnkiHub end comments is preserved - when the template is updated. + old_note_type (NotetypeDict): The old note tpye. The contents below the AnkiHub end comments are preserved. new_note_type (Optional[NotetypeDict]): The new note type. If provided, the templates and css are updated based on this. @@ -451,7 +450,7 @@ def note_type_with_updated_templates_and_css( updated_template = copy.deepcopy(old_template) for template_side_name in ["qfmt", "afmt"]: - updated_template[template_side_name] = _upated_note_type_content( + updated_template[template_side_name] = _updated_note_type_content( old_content=old_template[template_side_name], new_content=( new_template[template_side_name] @@ -466,7 +465,7 @@ def note_type_with_updated_templates_and_css( result = copy.deepcopy(old_note_type) result["tmpls"] = updated_templates - result["css"] = _upated_note_type_content( + result["css"] = _updated_note_type_content( old_content=old_note_type["css"], new_content=new_note_type["css"] if new_note_type is not None else None, add_view_on_ankihub_snippet=False, @@ -476,13 +475,20 @@ def note_type_with_updated_templates_and_css( return result -def _upated_note_type_content( +def _updated_note_type_content( old_content: str, new_content: Optional[str], add_view_on_ankihub_snippet: bool, content_type: str, ) -> str: - """Returns the updated content with the AnkiHub modifications applied.""" + """Returns updated content with preserved content below ankihub end comment. + + Args: + old_content: Original content to preserve custom additions from + new_content: New base content to use, or None to use old_content + add_view_on_ankihub_snippet: Whether to add AnkiHub view button + content_type: Either "html" or "css" to determine comment style + """ if content_type == "html": end_comment = ANKIHUB_HTML_END_COMMENT end_comment_pattern = ANKIHUB_HTML_END_COMMENT_PATTERN @@ -496,14 +502,14 @@ def _upated_note_type_content( # Choose the base for the result result = new_content if new_content is not None else old_content - # Remove end comment and content below it from the template side. + # Remove end comment and content below it. # It will be added back below. result = re.sub(end_comment_pattern, "", result) if add_view_on_ankihub_snippet: result = _template_side_with_view_on_ankihub_snippet(result) - # Add the AnkiHub end comment and the content below it back to the template side. + # Add the AnkiHub end comment and the content below it back. return result.rstrip("\n ") + "\n\n" + end_comment + "\n" + text_to_migrate From 57690bd714bacf773b9308ef265a566c5de0a33e Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Tue, 28 Jan 2025 17:03:00 +0100 Subject: [PATCH 6/8] fix: Strip text to migrate --- ankihub/main/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ankihub/main/utils.py b/ankihub/main/utils.py index 8897810e2..072298da3 100644 --- a/ankihub/main/utils.py +++ b/ankihub/main/utils.py @@ -510,7 +510,13 @@ def _updated_note_type_content( result = _template_side_with_view_on_ankihub_snippet(result) # Add the AnkiHub end comment and the content below it back. - return result.rstrip("\n ") + "\n\n" + end_comment + "\n" + text_to_migrate + return ( + result.rstrip("\n ") + + "\n\n" + + end_comment + + "\n" + + text_to_migrate.strip("\n ") + ) # ... undo modifications From ab192b3f158d84ccaa429a03b85a584174be17dd Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Tue, 28 Jan 2025 17:05:39 +0100 Subject: [PATCH 7/8] Update tests --- ..._migrating_content_from_old_note_type.afmt.False.approved.txt | 1 - ...h_migrating_content_from_old_note_type.afmt.True.approved.txt | 1 - ...h_migrating_content_from_old_note_type.css.False.approved.txt | 1 - ...th_migrating_content_from_old_note_type.css.True.approved.txt | 1 - ..._migrating_content_from_old_note_type.qfmt.False.approved.txt | 1 - ...h_migrating_content_from_old_note_type.qfmt.True.approved.txt | 1 - 6 files changed, 6 deletions(-) diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.False.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.False.approved.txt index f9c236f9a..b647df0c4 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.False.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.False.approved.txt @@ -62,5 +62,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. --> - content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.True.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.True.approved.txt index a2db625a0..f6c8e212c 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.True.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.True.approved.txt @@ -62,5 +62,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. --> - content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt index f7e9d08ed..1a6966d1a 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.False.approved.txt @@ -5,5 +5,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. */ - content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt index e2339ac00..df37bc329 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.css.True.approved.txt @@ -5,5 +5,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. */ - content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.False.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.False.approved.txt index 5bf479027..55fc4b3db 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.False.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.False.approved.txt @@ -5,5 +5,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. --> - content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.True.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.True.approved.txt index 8d3ccfc5d..8bfc5db82 100644 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.True.approved.txt +++ b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.True.approved.txt @@ -5,5 +5,4 @@ ANKIHUB_END Text below this comment will not be modified by AnkiHub or AnKing add-ons. Do not edit or remove this comment if you want to protect the content below. --> - content to migrate From d53e5d0c05d8f0aab8ffb2b6c2ac4067e0556622 Mon Sep 17 00:00:00 2001 From: RisingOrange Date: Tue, 28 Jan 2025 17:09:00 +0100 Subject: [PATCH 8/8] Delete not needed files --- ...atedTemplates.test_basic.afmt.approved.txt | 64 ------------------ ...atedTemplates.test_basic.qfmt.approved.txt | 7 -- ...ntent_from_old_note_type.afmt.approved.txt | 66 ------------------- ...ntent_from_old_note_type.qfmt.approved.txt | 9 --- 4 files changed, 146 deletions(-) delete mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.afmt.approved.txt delete mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.qfmt.approved.txt delete mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.approved.txt delete mode 100644 tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.approved.txt diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.afmt.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.afmt.approved.txt deleted file mode 100644 index af32eb130..000000000 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.afmt.approved.txt +++ /dev/null @@ -1,64 +0,0 @@ -new content - - -{{#ankihub_id}} - - View Note on AnkiHub - - - - - - -{{/ankihub_id}} - - - diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.qfmt.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.qfmt.approved.txt deleted file mode 100644 index 21872bc57..000000000 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_basic.qfmt.approved.txt +++ /dev/null @@ -1,7 +0,0 @@ -new content - - diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.approved.txt deleted file mode 100644 index a2db625a0..000000000 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.afmt.approved.txt +++ /dev/null @@ -1,66 +0,0 @@ -new content - - -{{#ankihub_id}} - - View Note on AnkiHub - - - - - - -{{/ankihub_id}} - - - - -content to migrate diff --git a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.approved.txt b/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.approved.txt deleted file mode 100644 index 8d3ccfc5d..000000000 --- a/tests/addon/approval_test_files/TestNoteTypeWithUpdatedTemplates.test_with_migrating_content_from_old_note_type.qfmt.approved.txt +++ /dev/null @@ -1,9 +0,0 @@ -new content - - - -content to migrate