diff --git a/personal_mnemonic_medium/v2/domain/diff_determiner/base_diff_determiner.py b/personal_mnemonic_medium/v2/domain/diff_determiner/base_diff_determiner.py index 558a9338..62ff37fe 100644 --- a/personal_mnemonic_medium/v2/domain/diff_determiner/base_diff_determiner.py +++ b/personal_mnemonic_medium/v2/domain/diff_determiner/base_diff_determiner.py @@ -1,14 +1,23 @@ -from collections.abc import Sequence -from typing import Protocol +from collections.abc import Mapping, Sequence +from pathlib import Path +from typing import Generic, Protocol, TypeVar from personal_mnemonic_medium.v2.domain.prompt_destination.base_prompt_destination import ( PromptDestinationCommand, ) +from ..prompt_destination.destination_commands import ( + DeletePrompts, + PushPrompts, +) from ..prompts.base_prompt import BasePrompt +K = TypeVar("K") +T = TypeVar("T") +S = TypeVar("S") + -class BaseSyncer(Protocol): +class BaseDiffDeterminer(Protocol): def sync( self, source_prompts: Sequence[BasePrompt], @@ -17,7 +26,60 @@ def sync( ... -class FakeDiffDeterminer(BaseSyncer): +class GeneralSyncer(Generic[K, T, S]): + def __init__( + self, source: Mapping[K, T], destination: Mapping[K, S] + ): + self.source = source + self.destination = destination + + def only_in_source(self) -> Sequence[T]: + return [ + value + for key, value in self.source.items() + if key not in self.destination + ] + + def only_in_destination(self) -> Sequence[S]: + return [ + value + for key, value in self.destination.items() + if key not in self.source + ] + + +class PromptDiffDeterminer(BaseDiffDeterminer): + def __init__(self, tmp_read_dir: Path, tmp_write_dir: Path): + # TODO: https://github.com/MartinBernstorff/personal-mnemonic-medium/issues/308 refactor: get rid of tmp_read_dir and tmp_write_dir + self.tmp_read_dir = tmp_read_dir + self.tmp_read_dir.mkdir(exist_ok=True) + + self.tmp_write_dir = tmp_write_dir + self.tmp_write_dir.mkdir(exist_ok=True) + + def sync( + self, + source_prompts: Sequence[BasePrompt], + destination_prompts: Sequence[BasePrompt], + ) -> Sequence[PromptDestinationCommand]: + syncer = GeneralSyncer( + source={prompt.uid: prompt for prompt in source_prompts}, + destination={ + prompt.uid: prompt for prompt in destination_prompts + }, + ) + + return [ + DeletePrompts(syncer.only_in_destination()), + PushPrompts( + syncer.only_in_source(), + tmp_write_dir=self.tmp_write_dir, + tmp_read_dir=self.tmp_read_dir, + ), + ] + + +class FakeDiffDeterminer(BaseDiffDeterminer): def sync( self, source_prompts: Sequence[BasePrompt], diff --git a/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py b/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py new file mode 100644 index 00000000..0dea7042 --- /dev/null +++ b/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py @@ -0,0 +1,45 @@ +from pathlib import Path + +from ..prompt_destination.destination_commands import ( + DeletePrompts, + PushPrompts, +) +from ..prompts.qa_prompt import QAPrompt +from .base_diff_determiner import GeneralSyncer, PromptDiffDeterminer + + +def test_diff_determiner(): + syncer = GeneralSyncer( + source={"a": 1, "b": 2}, destination={"b": "2", "c": "3"} + ) + + assert syncer.only_in_source() == [1] + assert syncer.only_in_destination() == ["3"] + + +def test_prompt_diff_determiner(tmp_path: Path): + syncer = PromptDiffDeterminer( + tmp_read_dir=tmp_path, tmp_write_dir=tmp_path + ) + + source_prompts = [ + QAPrompt(question="a", answer="a"), + QAPrompt(question="b", answer="b"), + ] + destination_prompts = [ + QAPrompt(question="b", answer="b"), + QAPrompt(question="c", answer="c"), + ] + + diff = syncer.sync( + source_prompts=source_prompts, + destination_prompts=destination_prompts, + ) + assert diff == [ + DeletePrompts([QAPrompt(question="c", answer="c")]), + PushPrompts( + [QAPrompt(question="a", answer="a")], + tmp_write_dir=tmp_path, + tmp_read_dir=tmp_path, + ), + ] diff --git a/personal_mnemonic_medium/v2/presentation/cli.py b/personal_mnemonic_medium/v2/presentation/cli.py index 633c17b9..386002c3 100644 --- a/personal_mnemonic_medium/v2/presentation/cli.py +++ b/personal_mnemonic_medium/v2/presentation/cli.py @@ -15,7 +15,7 @@ from ...main import get_env from ..data_access.ankiconnect_gateway import AnkiConnectGateway from ..domain.diff_determiner.base_diff_determiner import ( - FakeDiffDeterminer, + PromptDiffDeterminer, ) from ..domain.prompt_destination.anki_connect.ankiconnect_destination import ( AnkiConnectDestination, @@ -29,6 +29,7 @@ def _sync_deck(deck_name: str): + # TODO: https://github.com/MartinBernstorff/personal-mnemonic-medium/issues/309 feat: use markdown promptsource source_prompts = FakePromptSource().get_prompts() destination = AnkiConnectDestination( @@ -41,7 +42,9 @@ def _sync_deck(deck_name: str): ) destination_prompts = destination.get_all_prompts() - update_commands = FakeDiffDeterminer().sync( + update_commands = PromptDiffDeterminer( + tmp_write_dir=Path("/tmp"), tmp_read_dir=Path("/tmp") + ).sync( source_prompts=source_prompts, destination_prompts=destination_prompts, )