From 5c3ed10a266a68a35bb530b794448b8ff84264d8 Mon Sep 17 00:00:00 2001 From: Ramon Wenger Date: Thu, 22 Feb 2024 15:18:51 +0100 Subject: [PATCH] Add server implementation and unit test for chapter highlight --- server/books/schema/nodes/chapter.py | 5 +++ server/notes/models.py | 3 ++ server/notes/tests/test_highlights.py | 45 ++++++++++++++++++++++- server/schema.graphql | 51 ++++++++++++++------------- 4 files changed, 78 insertions(+), 26 deletions(-) diff --git a/server/books/schema/nodes/chapter.py b/server/books/schema/nodes/chapter.py index 8669c1d7..5669afc4 100644 --- a/server/books/schema/nodes/chapter.py +++ b/server/books/schema/nodes/chapter.py @@ -13,6 +13,7 @@ class ChapterNode(DjangoObjectType): title_hidden_for = graphene.List("users.schema.SchoolClassNode") description_hidden_for = graphene.List("users.schema.SchoolClassNode") path = graphene.String() + highlights = graphene.List("notes.schema.HighlightNode") class Meta: model = Chapter @@ -72,3 +73,7 @@ class ChapterNode(DjangoObjectType): def resolve_path(root: Chapter, info, **kwargs): module = root.get_parent() return f"module/{module.slug}#{root.graphql_id}" + + @staticmethod + def resolve_highlights(root: Chapter, info, **kwargs): + return root.highlights.filter(user=info.context.user) diff --git a/server/notes/models.py b/server/notes/models.py index a75309ee..3cdeeb6f 100644 --- a/server/notes/models.py +++ b/server/notes/models.py @@ -65,3 +65,6 @@ class Highlight(models.Model): text = models.TextField() note = models.OneToOneField(Note, null=True, on_delete=models.SET_NULL) color = models.CharField(max_length=50) + + def __str__(self) -> str: + return self.text diff --git a/server/notes/tests/test_highlights.py b/server/notes/tests/test_highlights.py index 6c0ef487..8d5f3dc9 100644 --- a/server/notes/tests/test_highlights.py +++ b/server/notes/tests/test_highlights.py @@ -1,5 +1,5 @@ import pytest -from books.factories import ContentBlockFactory, ModuleFactory +from books.factories import ChapterFactory, ContentBlockFactory, ModuleFactory from books.models.contentblock import ContentBlock from graphql_relay import to_global_id @@ -110,6 +110,12 @@ module_query = ( highlights { ...HighlightLegacyParts } + chapters { + id + highlights { + ...HighlightLegacyParts + } + } } } """ @@ -217,3 +223,40 @@ class TestAddHighlight: assert module.get("id") == mid assert module.get("highlights")[0].get("color") == "alpha" assert module.get("highlights")[0].get("page").get("id") == mid + + def test_add_highlight_in_chapter(self, teacher, get_client): + module = ModuleFactory() + chapter = ChapterFactory(parent=module) + client = get_client(teacher) + mid = to_global_id("ModuleNode", module.id) + cid = to_global_id("ChapterNode", chapter.id) + result = client.execute( + add_highlight_mutation, + variables={ + "input": { + "highlight": { + "page": cid, + "paragraphIndex": 0, + "text": "Hallo", + "startPosition": 0, + "selectionLength": 10, + "color": "alpha", + } + } + }, + ) + assert result.errors is None + + highlight = result.data.get("addHighlight").get("highlight") + assert highlight.get("text") == "Hallo" + assert highlight.get("page").get("id") == cid + + client = get_client(teacher) + result = client.execute(module_query, variables={"id": mid}) + assert result.errors is None + module = result.data.get("module") + logger.debug(module) + chapter = module.get("chapters")[0] + assert chapter.get("id") == cid + assert chapter.get("highlights")[0].get("color") == "alpha" + assert chapter.get("highlights")[0].get("page").get("id") == cid diff --git a/server/schema.graphql b/server/schema.graphql index a4acb3b2..e1641cac 100644 --- a/server/schema.graphql +++ b/server/schema.graphql @@ -567,22 +567,25 @@ type ChapterNode implements Node & ChapterInterface { bookmark: ChapterBookmarkNode contentBlocks: [ContentBlockNode] path: String + highlights: [HighlightNode] } -type InstrumentBookmarkNode implements Node { +type HighlightNode implements Node { """The ID of the object""" id: ID! user: PrivateUserNode! + page: HighlightableNode + contentIndex: Int + contentUuid: UUID + paragraphIndex: Int! + startPosition: Int! + selectionLength: Int! + text: String! note: NoteNode - uuid: UUID - instrument: InstrumentNode! + color: String! } -""" -Leverages the internal Python implementation of UUID (uuid.UUID) to provide native UUID objects -in fields, resolvers and input. -""" -scalar UUID +union HighlightableNode = ContentBlockNode | InstrumentNode | ModuleNode | ChapterNode type InstrumentNode implements Node { """Der Seitentitel, der öffentlich angezeigt werden soll""" @@ -603,6 +606,21 @@ type InstrumentNode implements Node { highlights: [HighlightNode] } +type InstrumentBookmarkNode implements Node { + """The ID of the object""" + id: ID! + user: PrivateUserNode! + note: NoteNode + uuid: UUID + instrument: InstrumentNode! +} + +""" +Leverages the internal Python implementation of UUID (uuid.UUID) to provide native UUID objects +in fields, resolvers and input. +""" +scalar UUID + type InstrumentTypeNode implements Node { """The ID of the object""" id: ID! @@ -620,23 +638,6 @@ type InstrumentCategoryNode implements Node { types: [InstrumentTypeNode] } -type HighlightNode implements Node { - """The ID of the object""" - id: ID! - user: PrivateUserNode! - page: HighlightableNode - contentIndex: Int - contentUuid: UUID - paragraphIndex: Int! - startPosition: Int! - selectionLength: Int! - text: String! - note: NoteNode - color: String! -} - -union HighlightableNode = ContentBlockNode | InstrumentNode | ModuleNode | ChapterNode - type SnapshotObjectiveGroupNode implements Node { """The ID of the object""" id: ID!