From fc19064cfc681d9ba2e2d0eec958ac76294b4398 Mon Sep 17 00:00:00 2001 From: Joss Moffatt Date: Sun, 12 Nov 2023 22:10:32 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20tagging=20commands=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :memo: Adding tag and untag documentation * :sparkles: Adding tag and untag methods * :rotating_light: Linting * :bookmark: Bumping version * :memo: Updating documentation * :memo: Updating documentation * :memo: Updating documentation --- README.md | 4 ++- bookshelf/__init__.py | 2 +- bookshelf/__main__.py | 22 +++++++++++++++ bookshelf/models.py | 13 ++++++++- docs/astro.config.mjs | 2 ++ docs/src/content/docs/reference/tag.mdx | 24 ++++++++++++++++ docs/src/content/docs/reference/untag.mdx | 24 ++++++++++++++++ .../docs/reference/use-bookshelf-cli.mdx | 21 ++++++++------ test/test_models.py | 28 +++++++++++++++++++ 9 files changed, 128 insertions(+), 12 deletions(-) create mode 100644 docs/src/content/docs/reference/tag.mdx create mode 100644 docs/src/content/docs/reference/untag.mdx diff --git a/README.md b/README.md index af6ba71..c1de7ba 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The base command for the `bookshelf` CLI. | Name | Description | |--------------------------------------------------------------------------|--------------------------------------------------------------| -| cancel | Cancel the current chapter of a story on your bookshelf | +| cancel | Cancel the current chapter of a story on your bookshelf | | create | Create a new story for your bookshelf | | finish | Finish writing a story on your bookshelf | | info | Displays the information for a given story on your bookshelf | @@ -71,6 +71,8 @@ The base command for the `bookshelf` CLI. | rm | Remove a story from your bookshelf | | start | Start a new chapter for a story on your bookshelf | | stop | Stop the current chapter of a story on your bookshelf | +| tag | Add a new tag a story on your bookshelf | +| untag | Remove a tag from a story on your bookshelf | ## 🤝 Contributing diff --git a/bookshelf/__init__.py b/bookshelf/__init__.py index e19434e..6a9beea 100644 --- a/bookshelf/__init__.py +++ b/bookshelf/__init__.py @@ -1 +1 @@ -__version__ = "0.3.3" +__version__ = "0.4.0" diff --git a/bookshelf/__main__.py b/bookshelf/__main__.py index 91db036..44b7b96 100644 --- a/bookshelf/__main__.py +++ b/bookshelf/__main__.py @@ -179,3 +179,25 @@ def cancel_chapter(story_name): bookshelf_console.render_story_panel(story) except KeyboardInterrupt: bookshelf_console.print(f'❌ Current chapter in \'{story_name}\' has been cancelled!') + + +@bookshelf.command(name='tag') +@click.argument('story_name', type=story_type) +@click.argument('tag', type=str) +def tag_story(story_name: str, tag: str): + """Add a tag to a story on your bookshelf""" + story = bookshelf_storage.load_story(story_name) + story.add_tag(tag) + bookshelf_console.print(f'🏷️ The tag \'{tag}\' has been added to \'{story_name}\'!') + bookshelf_storage.save_story(story) + + +@bookshelf.command(name='untag') +@click.argument('story_name', type=story_type) +@click.argument('tag', type=str) +def untag_story(story_name: str, tag: str): + """Remove a tag from a story on your bookshelf""" + story = bookshelf_storage.load_story(story_name) + story.remove_tag(tag) + bookshelf_console.print(f'🏷️ The tag \'{tag}\' has been removed from \'{story_name}\'!') + bookshelf_storage.save_story(story) diff --git a/bookshelf/models.py b/bookshelf/models.py index 27a75c2..3678102 100644 --- a/bookshelf/models.py +++ b/bookshelf/models.py @@ -77,6 +77,17 @@ def finish_story(self) -> None: self.finish_chapter() self.end_date = self.get_last_chapter().end_time + def _has_tag(self, tag: str) -> bool: + return tag in self.tags + + def add_tag(self, tag: str) -> None: + if not self._has_tag(tag): + self.tags.append(tag) + + def remove_tag(self, tag: str) -> None: + if self._has_tag(tag): + self.tags.remove(tag) + def is_finished(self) -> bool: return self.end_date is not None @@ -96,7 +107,7 @@ def from_json(cls, data: dict) -> 'Story': end_date = datetime.fromisoformat(data['end_date']) if data['end_date'] else None tags = data['tags'] chapters = [Chapter.from_json(chapter_data) for chapter_data in data['chapters']] - return cls(name, start_date, end_date, chapters, tags) + return cls(name, start_date, end_date, chapters.copy(), tags.copy()) def in_progress(self) -> bool: return self.get_last_chapter().in_progress() if len(self.chapters) > 0 else False diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index 784f5d2..2cd84de 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -34,6 +34,8 @@ export default defineConfig({ { label: 'bookshelf rm', link: '/reference/rm/' }, { label: 'bookshelf start', link: '/reference/start/' }, { label: 'bookshelf stop', link: '/reference/stop/' }, + { label: 'bookshelf tag', link: '/reference/tag/' }, + { label: 'bookshelf untag', link: '/reference/untag/' }, ] }, ], diff --git a/docs/src/content/docs/reference/tag.mdx b/docs/src/content/docs/reference/tag.mdx new file mode 100644 index 0000000..1a74212 --- /dev/null +++ b/docs/src/content/docs/reference/tag.mdx @@ -0,0 +1,24 @@ +--- +title: bookshelf tag +description: Add a new tag a story on your bookshelf +--- + +### Usage + +```bash +bookshelf tag [OPTIONS] STORY_NAME +``` + +### Description + +Use `bookshelf tag` to add a new tag to a story on your bookshelf. + +### Options + +| Name | Shorthand | Default | Description| +|------- | --------- | ------- | -----------| +| | | | | + +### Examples + +#### Add a new tag to a story diff --git a/docs/src/content/docs/reference/untag.mdx b/docs/src/content/docs/reference/untag.mdx new file mode 100644 index 0000000..0764564 --- /dev/null +++ b/docs/src/content/docs/reference/untag.mdx @@ -0,0 +1,24 @@ +--- +title: bookshelf untag +description: Remove a tag from a story on your bookshelf +--- + +### Usage + +```bash +bookshelf tag [OPTIONS] STORY_NAME +``` + +### Description + +Use `bookshelf tag` to remove a tag from a story on your bookshelf. + +### Options + +| Name | Shorthand | Default | Description| +|------- | --------- | ------- | -----------| +| | | | | + +### Examples + +#### Remove a tag from a story diff --git a/docs/src/content/docs/reference/use-bookshelf-cli.mdx b/docs/src/content/docs/reference/use-bookshelf-cli.mdx index 18a9171..75d807a 100644 --- a/docs/src/content/docs/reference/use-bookshelf-cli.mdx +++ b/docs/src/content/docs/reference/use-bookshelf-cli.mdx @@ -14,15 +14,18 @@ The base command for the `bookshelf` CLI. ### Subcommands -| Name | Description | -| ----------- | ----------- | -| create | Create a new story for your bookshelf | -| finish | Finish writing a story on your bookshelf | -| info | Displays the information for a given story on your bookshelf | -| ls | List all the current stories on your bookshelf | -| rm | Remove a story from your bookshelf | -| start | Start a new chapter for a story on your bookshelf | -| stop | Stop the current chapter of a story on your bookshelf | +| Name | Description | +| ----------- | ----------- | +| cancel | Cancel the current chapter of a story on your bookshelf | +| create | Create a new story for your bookshelf | +| finish | Finish writing a story on your bookshelf | +| info | Displays the information for a given story on your bookshelf | +| ls | List all the current stories on your bookshelf | +| rm | Remove a story from your bookshelf | +| start | Start a new chapter for a story on your bookshelf | +| stop | Stop the current chapter of a story on your bookshelf | +| tag | Add a new tag a story on your bookshelf | +| untag | Remove a tag from a story on your bookshelf | diff --git a/test/test_models.py b/test/test_models.py index b75d297..2e650f0 100644 --- a/test/test_models.py +++ b/test/test_models.py @@ -48,6 +48,34 @@ def test_story_to_dict(sample_story): assert_that(story_dict).is_equal_to(STORY_JSON) +def test_add_new_tag_adds_tag(): + story = Story.from_json(STORY_JSON) + new_tag = 'new_tag' + story.add_tag(new_tag) + assert_that(story.tags).contains_only('tag1', 'tag2', new_tag) + + +def test_add_duplicate_tag_does_not_add_tag(): + story = Story.from_json(STORY_JSON) + existing_tag = 'tag1' + story.add_tag(existing_tag) + assert_that(story.tags).contains_only('tag1', 'tag2') + + +def test_remove_existing_tag_removes_tag(): + story = Story.from_json(STORY_JSON) + existing_tag = 'tag1' + story.remove_tag(existing_tag) + assert_that(story.tags).contains_only('tag2') + + +def test_remove_non_existing_tag_does_not_remove_tag(): + story = Story.from_json(STORY_JSON) + non_existing_tag = 'non_existing_tag' + story.remove_tag(non_existing_tag) + assert_that(story.tags).contains_only('tag1', 'tag2') + + def test_story_from_dict(): story = Story.from_json(STORY_JSON) assert story.name == STORY_NAME