Skip to content

Commit

Permalink
[BUILD-974] feat: Add config options for MH steps (#1076)
Browse files Browse the repository at this point in the history
* Split up MH options into step 1 and step 2

* Update _get_enabled_buttons_list

* Filter out resources based on config values

* Update tests

* Fix tests

* Update config option texts

* Refactor

* Add comment

* Use partially checked state on main checkbox

* ref: Remove blockSignals calls
  • Loading branch information
RisingOrange authored Jan 16, 2025
1 parent 883a999 commit 0f77455
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 72 deletions.
6 changes: 4 additions & 2 deletions ankihub/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"debug_level_logs": false,
"use_staging": false,
"ankihub_ai_chatbot": true,
"boards_and_beyond": true,
"first_aid_forward": true
"boards_and_beyond_step_1": true,
"boards_and_beyond_step_2": true,
"first_aid_forward_step_1": true,
"first_aid_forward_step_2": true
}
57 changes: 55 additions & 2 deletions ankihub/gui/config_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

from typing import cast

from aqt import qconnect
from aqt.qt import QCheckBox, Qt

from ..settings import config

_config_dialog_manager = None
Expand Down Expand Up @@ -56,8 +59,14 @@ def _general_tab(conf_window) -> None:
if config.get_feature_flags().get("mh_integration"):
tab.text("Sidebar", bold=True)
tab.checkbox("ankihub_ai_chatbot", "AnkiHub AI Chatbot")
tab.checkbox("boards_and_beyond", "Boards and Beyond")
tab.checkbox("first_aid_forward", "First Aid Forward")

add_nested_checkboxes(
tab, key_prefix="boards_and_beyond", description="Boards and Beyond"
)
add_nested_checkboxes(
tab, key_prefix="first_aid_forward", description="First Aid Forward"
)

tab.hseparator()
tab.space(8)

Expand All @@ -66,3 +75,47 @@ def _general_tab(conf_window) -> None:
tab.checkbox("debug_level_logs", "Verbose logs (restart required)")

tab.stretch()


def add_nested_checkboxes(config_layout, key_prefix: str, description: str) -> None:

from .ankiaddonconfig.window import ConfigLayout

config_layout = cast(ConfigLayout, config_layout)

main_checkbox = QCheckBox(description)
config_layout.addWidget(main_checkbox)

container_outer = config_layout.hcontainer()
container_outer.setContentsMargins(0, 2, 0, 2)

container_inner = container_outer.vcontainer()
container_inner.setContentsMargins(30, 0, 0, 0)

step_1_checkbox = container_inner.checkbox(
f"{key_prefix}_step_1", description="USMLE Step 1"
)
step_2_checkbox = container_inner.checkbox(
f"{key_prefix}_step_2", description="USMLE Step 2"
)

def update_main_checkbox() -> None:
checkboxes = [step_1_checkbox, step_2_checkbox]
checked_count = sum(checkbox.isChecked() for checkbox in checkboxes)

if checked_count == 0:
main_checkbox.setCheckState(Qt.CheckState.Unchecked)
elif checked_count == len(checkboxes):
main_checkbox.setCheckState(Qt.CheckState.Checked)
else:
main_checkbox.setCheckState(Qt.CheckState.PartiallyChecked)

def on_main_checkbox_clicked() -> None:
is_checked = main_checkbox.checkState() != Qt.CheckState.Unchecked
main_checkbox.setChecked(is_checked)
step_1_checkbox.setChecked(is_checked)
step_2_checkbox.setChecked(is_checked)

qconnect(step_1_checkbox.stateChanged, update_main_checkbox)
qconnect(step_2_checkbox.stateChanged, update_main_checkbox)
qconnect(main_checkbox.clicked, on_main_checkbox_clicked)
53 changes: 28 additions & 25 deletions ankihub/gui/reviewer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Modifies Anki's reviewer UI (aqt.reviewer)."""

import uuid
from dataclasses import dataclass
from enum import Enum
from textwrap import dedent
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
Expand All @@ -27,8 +26,8 @@
from ..db import ankihub_db
from ..gui.menu import AnkiHubLogin
from ..gui.webview import AuthenticationRequestInterceptor, CustomWebPage # noqa: F401
from ..main.utils import mh_tag_to_resource_title_and_slug
from ..settings import config, url_login, url_mh_integrations_preview
from ..main.utils import Resource, mh_tag_to_resource
from ..settings import config, url_login
from .js_message_handling import VIEW_NOTE_PYCMD, parse_js_message_kwargs
from .utils import get_ah_did_of_deck_or_ancestor_deck, using_qt5
from .web.templates import (
Expand Down Expand Up @@ -71,12 +70,6 @@ class ResourceType(Enum):
}


@dataclass(frozen=True)
class Resource:
title: str
url: str


class ReviewerSidebar:
def __init__(self, reviewer: Reviewer):
self.reviewer = reviewer
Expand Down Expand Up @@ -426,26 +419,21 @@ def _inject_ankihub_features_and_setup_sidebar(


def _get_enabled_buttons_list() -> List[str]:
buttons_map = {}
result = []

feature_flags = config.get_feature_flags()

if feature_flags.get("chatbot"):
buttons_map["ankihub_ai_chatbot"] = "chatbot"
if config.public_config.get("ankihub_ai_chatbot"):
result.append("chatbot")

if feature_flags.get("mh_integration"):
buttons_map.update(
{
"boards_and_beyond": "b&b",
"first_aid_forward": "fa4",
}
)
if _get_enabled_steps_for_resource_type(ResourceType.BOARDS_AND_BEYOND):
result.append("b&b")
if _get_enabled_steps_for_resource_type(ResourceType.FIRST_AID):
result.append("fa4")

return [
buttons_map[key]
for key, value in config.public_config.items()
if key in buttons_map and value
]
return result


def _related_ah_deck_has_note_embeddings(note: Note) -> bool:
Expand Down Expand Up @@ -554,14 +542,29 @@ def _show_resources_for_current_card(resource_type: ResourceType) -> None:
def _get_resources(tags: List[str], resource_type: ResourceType) -> List[Resource]:
resource_tags = _get_resource_tags(tags, resource_type)
result = {
Resource(title=title, url=url_mh_integrations_preview(slug))
resource
for tag in resource_tags
if (title_and_slug := mh_tag_to_resource_title_and_slug(tag))
for title, slug in [title_and_slug]
if (
(resource := mh_tag_to_resource(tag)).usmle_step
in _get_enabled_steps_for_resource_type(resource_type)
)
}
return list(sorted(result, key=lambda x: x.title))


def _get_enabled_steps_for_resource_type(resource_type: ResourceType) -> Set[int]:
resource_type_to_config_key_prefix = {
ResourceType.BOARDS_AND_BEYOND: "boards_and_beyond",
ResourceType.FIRST_AID: "first_aid_forward",
}
config_key_prefix = resource_type_to_config_key_prefix[resource_type]
return {
step
for step in [1, 2]
if config.public_config.get(f"{config_key_prefix}_step_{step}")
}


def _get_resource_tags(tags: List[str], resource_type: ResourceType) -> Set[str]:
"""Get all (v12) tags matching a specific resource type."""
search_pattern = f"v12::{RESOURCE_TYPE_TO_TAG_PART[resource_type]}".lower()
Expand Down
13 changes: 11 additions & 2 deletions ankihub/main/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import time
from collections import defaultdict
from concurrent.futures import Future
from dataclasses import dataclass
from pathlib import Path
from textwrap import dedent
from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Set, Tuple
Expand All @@ -24,6 +25,7 @@
ANKIHUB_NOTE_TYPE_FIELD_NAME,
ANKIHUB_NOTE_TYPE_MODIFICATION_STRING,
ANKIHUB_TEMPLATE_END_COMMENT,
url_mh_integrations_preview,
url_view_note,
)

Expand Down Expand Up @@ -659,7 +661,14 @@ def collection_schema() -> int:
return aqt.mw.col.db.scalar("select scm from col")


def mh_tag_to_resource_title_and_slug(tag: str) -> Optional[Tuple[str, str]]:
@dataclass(frozen=True)
class Resource:
title: str
url: str
usmle_step: int


def mh_tag_to_resource(tag: str) -> Optional[Resource]:
"""Converts a McGrawHill tag to a title and URL for the MH resource preview.
Example:
Expand Down Expand Up @@ -695,4 +704,4 @@ def mh_tag_to_resource_title_and_slug(tag: str) -> Optional[Tuple[str, str]]:
# We want to ignore any tags that don't match the expected format
return None

return title, slug
return Resource(title=title, url=url_mh_integrations_preview(slug), usmle_step=step)
8 changes: 8 additions & 0 deletions ankihub/public_config_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ def migrate_public_config() -> None:
if "ankihub_url" in addon_config:
addon_config.pop("ankihub_url")
aqt.mw.addonManager.writeConfig(__name__, addon_config)

if "boards_and_beyond" in addon_config:
addon_config.pop("boards_and_beyond")
aqt.mw.addonManager.writeConfig(__name__, addon_config)

if "first_aid_forward" in addon_config:
addon_config.pop("first_aid_forward")
aqt.mw.addonManager.writeConfig(__name__, addon_config)
Loading

0 comments on commit 0f77455

Please sign in to comment.