From 174add194efb4d57de0e0627a9bfc24e15a318fb Mon Sep 17 00:00:00 2001 From: Martin Bernstorff Date: Tue, 12 Dec 2023 16:10:37 +0000 Subject: [PATCH 1/3] feat: implement diffdeterminer Fixes #292 Add GeneralSyncer --- .../diff_determiner/base_diff_determiner.py | 29 +++++++++++++++++-- .../diff_determiner/test_diff_determiner.py | 10 +++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py 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..c69d6ad3 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,5 +1,5 @@ -from collections.abc import Sequence -from typing import Protocol +from collections.abc import Mapping, Sequence +from typing import Generic, Protocol, TypeVar from personal_mnemonic_medium.v2.domain.prompt_destination.base_prompt_destination import ( PromptDestinationCommand, @@ -7,6 +7,9 @@ from ..prompts.base_prompt import BasePrompt +T = TypeVar("T") +S = TypeVar("S") + class BaseSyncer(Protocol): def sync( @@ -17,6 +20,28 @@ def sync( ... +class GeneralSyncer(Generic[T, S]): + def __init__( + self, source: Mapping[str, T], destination: Mapping[str, 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 FakeDiffDeterminer(BaseSyncer): def sync( self, 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..0bae1068 --- /dev/null +++ b/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py @@ -0,0 +1,10 @@ +from .base_diff_determiner import GeneralSyncer + + +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"] From 9d708f2928af270c53e06f3ca285c8ff588f899e Mon Sep 17 00:00:00 2001 From: Martin Bernstorff Date: Tue, 12 Dec 2023 16:25:58 +0000 Subject: [PATCH 2/3] feat: add diffdeterminer --- .../diff_determiner/base_diff_determiner.py | 45 +++++++++++++++++-- .../diff_determiner/test_diff_determiner.py | 37 ++++++++++++++- .../v2/presentation/cli.py | 7 ++- 3 files changed, 82 insertions(+), 7 deletions(-) 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 c69d6ad3..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,17 +1,23 @@ 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], @@ -20,9 +26,9 @@ def sync( ... -class GeneralSyncer(Generic[T, S]): +class GeneralSyncer(Generic[K, T, S]): def __init__( - self, source: Mapping[str, T], destination: Mapping[str, S] + self, source: Mapping[K, T], destination: Mapping[K, S] ): self.source = source self.destination = destination @@ -42,7 +48,38 @@ def only_in_destination(self) -> Sequence[S]: ] -class FakeDiffDeterminer(BaseSyncer): +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 index 0bae1068..c902199d 100644 --- a/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py +++ b/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py @@ -1,4 +1,11 @@ -from .base_diff_determiner import GeneralSyncer +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(): @@ -8,3 +15,31 @@ def test_diff_determiner(): assert syncer.only_in_source() == [1] assert syncer.only_in_destination() == ["3"] + + +def test_prompt_diff_determiner(tmpdir: Path): + syncer = PromptDiffDeterminer( + tmp_read_dir=tmpdir, tmp_write_dir=tmpdir + ) + + 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=tmpdir, + tmp_read_dir=tmpdir, + ), + ] 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, ) From 37f3dd4542f4fe530491d1e72266a16633abf49c Mon Sep 17 00:00:00 2001 From: Martin Bernstorff Date: Tue, 12 Dec 2023 16:28:59 +0000 Subject: [PATCH 3/3] misc. --- .../v2/domain/diff_determiner/test_diff_determiner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 index c902199d..0dea7042 100644 --- a/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py +++ b/personal_mnemonic_medium/v2/domain/diff_determiner/test_diff_determiner.py @@ -17,9 +17,9 @@ def test_diff_determiner(): assert syncer.only_in_destination() == ["3"] -def test_prompt_diff_determiner(tmpdir: Path): +def test_prompt_diff_determiner(tmp_path: Path): syncer = PromptDiffDeterminer( - tmp_read_dir=tmpdir, tmp_write_dir=tmpdir + tmp_read_dir=tmp_path, tmp_write_dir=tmp_path ) source_prompts = [ @@ -39,7 +39,7 @@ def test_prompt_diff_determiner(tmpdir: Path): DeletePrompts([QAPrompt(question="c", answer="c")]), PushPrompts( [QAPrompt(question="a", answer="a")], - tmp_write_dir=tmpdir, - tmp_read_dir=tmpdir, + tmp_write_dir=tmp_path, + tmp_read_dir=tmp_path, ), ]