Skip to content

Commit 42c316c

Browse files
authored
Whiteboard fetch improvements (#556)
* prefetch whiteboard data to reduce number of requests * added memo ids to several dats objects * updated api * fixed missing memo_ids * added new test and fixed deletion cascade
1 parent a48c4ff commit 42c316c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+654
-73
lines changed

backend/src/api/endpoints/bbox_annotation.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,11 @@ def delete_by_id(
119119
) -> BBoxAnnotationRead:
120120
authz_user.assert_in_same_project_as(Crud.BBOX_ANNOTATION, bbox_id)
121121

122-
db_obj = crud_bbox_anno.remove(db=db, id=bbox_id)
123-
return BBoxAnnotationRead.model_validate(db_obj)
122+
db_obj = crud_bbox_anno.read(db=db, id=bbox_id)
123+
bbox_read = BBoxAnnotationRead.model_validate(db_obj)
124+
125+
crud_bbox_anno.remove(db=db, id=bbox_id)
126+
return bbox_read
124127

125128

126129
@router.delete(

backend/src/api/endpoints/code.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,8 @@ def delete_by_id(
7878
) -> CodeRead:
7979
authz_user.assert_in_same_project_as(Crud.CODE, code_id)
8080

81-
db_obj = crud_code.remove(db=db, id=code_id)
82-
return CodeRead.model_validate(db_obj)
81+
db_obj = crud_code.read(db=db, id=code_id)
82+
code_read = CodeRead.model_validate(db_obj)
83+
84+
crud_code.remove(db=db, id=code_id)
85+
return code_read

backend/src/api/endpoints/document_tag.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,11 @@ def delete_by_id(
220220
) -> DocumentTagRead:
221221
authz_user.assert_in_same_project_as(Crud.DOCUMENT_TAG, tag_id)
222222

223-
db_obj = crud_document_tag.remove(db=db, id=tag_id)
224-
return DocumentTagRead.model_validate(db_obj)
223+
db_obj = crud_document_tag.read(db=db, id=tag_id)
224+
tag_read = DocumentTagRead.model_validate(db_obj)
225+
226+
crud_document_tag.remove(db=db, id=tag_id)
227+
return tag_read
225228

226229

227230
@router.get(

backend/src/api/endpoints/sentence_annotation.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,11 @@ def delete_by_id(
155155
) -> SentenceAnnotationRead:
156156
authz_user.assert_in_same_project_as(Crud.SENTENCE_ANNOTATION, sentence_anno_id)
157157

158-
db_obj = crud_sentence_anno.remove(db=db, id=sentence_anno_id)
159-
return SentenceAnnotationRead.model_validate(db_obj)
158+
db_obj = crud_sentence_anno.read(db=db, id=sentence_anno_id)
159+
sentence_anno_read = SentenceAnnotationRead.model_validate(db_obj)
160+
161+
crud_sentence_anno.remove(db=db, id=sentence_anno_id)
162+
return sentence_anno_read
160163

161164

162165
@router.delete(

backend/src/api/endpoints/span_annotation.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,11 @@ def delete_by_id(
157157
) -> SpanAnnotationDeleted:
158158
authz_user.assert_in_same_project_as(Crud.SPAN_ANNOTATION, span_id)
159159

160-
db_obj = crud_span_anno.remove(db=db, id=span_id)
161-
return SpanAnnotationDeleted.model_validate(db_obj)
160+
db_obj = crud_span_anno.read(db=db, id=span_id)
161+
anno_read = SpanAnnotationDeleted.model_validate(db_obj)
162+
163+
crud_span_anno.remove(db=db, id=span_id)
164+
return anno_read
162165

163166

164167
@router.delete(

backend/src/api/endpoints/whiteboard.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from app.core.data.dto.whiteboard import (
77
WhiteboardCreate,
88
WhiteboardCreateIntern,
9+
WhiteboardData,
910
WhiteboardRead,
1011
WhiteboardUpdate,
1112
WhiteboardUpdateIntern,
@@ -60,6 +61,22 @@ def get_by_id(
6061
return WhiteboardRead.model_validate(db_obj)
6162

6263

64+
@router.get(
65+
"/data/{whiteboard_id}",
66+
response_model=WhiteboardData,
67+
summary="Returns the Whiteboard with the given ID if it exists",
68+
)
69+
def get_data_by_id(
70+
*,
71+
db: Session = Depends(get_db_session),
72+
whiteboard_id: int,
73+
authz_user: AuthzUser = Depends(),
74+
) -> WhiteboardData:
75+
authz_user.assert_in_same_project_as(Crud.WHITEBOARD, whiteboard_id)
76+
77+
return crud_whiteboard.read_data(db=db, id=whiteboard_id)
78+
79+
6380
@router.get(
6481
"/project/{project_id}",
6582
response_model=List[WhiteboardRead],

backend/src/app/core/data/crud/whiteboard.py

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
1-
from typing import List
1+
from typing import Dict, List
22

33
from sqlalchemy.orm import Session
44

5+
from app.core.data.crud.bbox_annotation import crud_bbox_anno
56
from app.core.data.crud.crud_base import CRUDBase
7+
from app.core.data.crud.memo import crud_memo
8+
from app.core.data.crud.project import crud_project
9+
from app.core.data.crud.sentence_annotation import crud_sentence_anno
10+
from app.core.data.crud.source_document import crud_sdoc
11+
from app.core.data.crud.span_annotation import crud_span_anno
12+
from app.core.data.dto.bbox_annotation import BBoxAnnotationRead
13+
from app.core.data.dto.code import CodeRead
14+
from app.core.data.dto.document_tag import DocumentTagRead
15+
from app.core.data.dto.memo import MemoRead
16+
from app.core.data.dto.sentence_annotation import SentenceAnnotationRead
17+
from app.core.data.dto.source_document import SourceDocumentRead
18+
from app.core.data.dto.span_annotation import SpanAnnotationRead
619
from app.core.data.dto.whiteboard import (
20+
BBoxAnnotationNodeData,
21+
MemoNodeData,
22+
SdocNodeData,
23+
SentenceAnnotationNodeData,
24+
SpanAnnotationNodeData,
725
WhiteboardCreateIntern,
26+
WhiteboardData,
27+
WhiteboardNode,
28+
WhiteboardNodeType,
29+
WhiteboardRead,
830
WhiteboardUpdateIntern,
931
)
1032
from app.core.data.orm.whiteboard import WhiteboardORM
@@ -36,6 +58,98 @@ def read_by_project(self, db: Session, *, project_id: int) -> List[WhiteboardORM
3658
)
3759
return db_obj
3860

61+
def read_data(self, db: Session, *, id: int) -> WhiteboardData:
62+
db_obj = self.read(db, id=id)
63+
whiteboard = WhiteboardRead.model_validate(db_obj)
64+
65+
# group nodes by their type
66+
whiteboard_nodes: Dict[str, List[WhiteboardNode]] = {
67+
node_type.value: [] for node_type in WhiteboardNodeType
68+
}
69+
for node in whiteboard.content.nodes:
70+
if node.type in whiteboard_nodes:
71+
whiteboard_nodes[node.type].append(node)
72+
73+
# fetch all data by type
74+
result = WhiteboardData(
75+
sdocs=[],
76+
codes=[],
77+
tags=[],
78+
span_annotations=[],
79+
sent_annotations=[],
80+
bbox_annotations=[],
81+
memos=[],
82+
)
83+
for node_type, nodes in whiteboard_nodes.items():
84+
match node_type:
85+
case WhiteboardNodeType.SPAN_ANNOTATION:
86+
span_ids = [
87+
SpanAnnotationNodeData.model_validate(
88+
node.data
89+
).spanAnnotationId
90+
for node in nodes
91+
]
92+
result.span_annotations = [
93+
SpanAnnotationRead.model_validate(sa)
94+
for sa in crud_span_anno.read_by_ids(db=db, ids=span_ids)
95+
]
96+
case WhiteboardNodeType.SENTENCE_ANNOTATION:
97+
sent_ids = [
98+
SentenceAnnotationNodeData.model_validate(
99+
node.data
100+
).sentenceAnnotationId
101+
for node in nodes
102+
]
103+
result.sent_annotations = [
104+
SentenceAnnotationRead.model_validate(sa)
105+
for sa in crud_sentence_anno.read_by_ids(db=db, ids=sent_ids)
106+
]
107+
case WhiteboardNodeType.BBOX_ANNOTATION:
108+
bbox_ids = [
109+
BBoxAnnotationNodeData.model_validate(
110+
node.data
111+
).bboxAnnotationId
112+
for node in nodes
113+
]
114+
result.bbox_annotations = [
115+
BBoxAnnotationRead.model_validate(b)
116+
for b in crud_bbox_anno.read_by_ids(db=db, ids=bbox_ids)
117+
]
118+
case WhiteboardNodeType.MEMO:
119+
memo_ids = [
120+
MemoNodeData.model_validate(node.data).memoId for node in nodes
121+
]
122+
result.memos = [
123+
MemoRead.model_validate(m)
124+
for m in crud_memo.read_by_ids(db=db, ids=memo_ids)
125+
]
126+
case WhiteboardNodeType.SDOC:
127+
sdoc_ids = [
128+
SdocNodeData.model_validate(node.data).sdocId for node in nodes
129+
]
130+
result.sdocs = [
131+
SourceDocumentRead.model_validate(s)
132+
for s in crud_sdoc.read_by_ids(db=db, ids=sdoc_ids)
133+
]
134+
case WhiteboardNodeType.CODE:
135+
if len(nodes) > 0:
136+
result.codes = [
137+
CodeRead.model_validate(c)
138+
for c in crud_project.read(
139+
db=db, id=whiteboard.project_id
140+
).codes
141+
]
142+
case WhiteboardNodeType.TAG:
143+
if len(nodes) > 0:
144+
result.tags = [
145+
DocumentTagRead.model_validate(c)
146+
for c in crud_project.read(
147+
db=db, id=whiteboard.project_id
148+
).document_tags
149+
]
150+
151+
return result
152+
39153
def duplicate_by_id(
40154
self, db: Session, *, whiteboard_id: int, user_id: int
41155
) -> WhiteboardORM:

backend/src/app/core/data/dto/bbox_annotation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
from typing import List
23

34
from pydantic import BaseModel, ConfigDict, Field
45

@@ -46,4 +47,5 @@ class BBoxAnnotationRead(BBoxAnnotationBaseDTO):
4647
sdoc_id: int = Field(description="SourceDocument the BBoxAnnotation refers to")
4748
created: datetime = Field(description="Created timestamp of the BBoxAnnotation")
4849
updated: datetime = Field(description="Updated timestamp of the BBoxAnnotation")
50+
memo_ids: List[int] = Field(description="Memo IDs attached to the BBoxAnnotation")
4951
model_config = ConfigDict(from_attributes=True)

backend/src/app/core/data/dto/code.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime
2-
from typing import Optional
2+
from typing import List, Optional
33

44
from pydantic import BaseModel, ConfigDict, Field
55

@@ -48,4 +48,5 @@ class CodeRead(CodeBaseDTO):
4848
created: datetime = Field(description="Created timestamp of the Code")
4949
updated: datetime = Field(description="Updated timestamp of the Code")
5050
is_system: bool = Field(description="Is the Code a system code")
51+
memo_ids: List[int] = Field(description="Memo IDs attached to the Code")
5152
model_config = ConfigDict(from_attributes=True)

backend/src/app/core/data/dto/document_tag.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class DocumentTagRead(DocumentTagBaseDTO):
4343
project_id: int = Field(description="Project the DocumentTag belongs to")
4444
created: datetime = Field(description="Created timestamp of the DocumentTag")
4545
updated: datetime = Field(description="Updated timestamp of the DocumentTag")
46+
memo_ids: List[int] = Field(description="Memo IDs attached to the DocumentTag")
4647
model_config = ConfigDict(from_attributes=True)
4748

4849

0 commit comments

Comments
 (0)