Skip to content

Commit

Permalink
Implement text structure import endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
bkis committed Oct 10, 2023
1 parent b648151 commit bb852a0
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 6 deletions.
4 changes: 2 additions & 2 deletions Tekst-API/tekst/models/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class NodeDefinition(ModelBase):
str,
StringConstraints(min_length=1, max_length=256),
]
children: list["NodeDefinition"] | None = None
nodes: list["NodeDefinition"] | None = None


class TextStructureDefinition(ModelBase):
model_config = ConfigDict(extra="allow")
structure: list[NodeDefinition] = []
nodes: list[NodeDefinition] = []
95 changes: 91 additions & 4 deletions Tekst-API/tekst/routers/texts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@

from copy import deepcopy
from pathlib import Path as SysPath
from typing import Annotated, List

from beanie import PydanticObjectId
from beanie.operators import Or, Set, Unset
from fastapi import APIRouter, Body, Depends, HTTPException, Path, status
from fastapi import (
APIRouter,
Body,
Depends,
File,
HTTPException,
Path,
UploadFile,
status,
)
from fastapi.responses import FileResponse

from tekst.auth import OptionalUserDep, SuperuserDep
Expand Down Expand Up @@ -111,10 +121,10 @@ async def download_structure_template(
node = deepcopy(dummy_node)
node.label = node.label.format(text.levels[n][0]["label"])
if curr_node_def is None:
structure_def.structure.append(node)
structure_def.nodes.append(node)
else:
curr_node_def.children = []
curr_node_def.children.append(node)
curr_node_def.nodes = []
curr_node_def.nodes.append(node)
curr_node_def = node
# validate template
try:
Expand All @@ -139,6 +149,83 @@ async def download_structure_template(
)


@router.post("/{id}/structure", status_code=status.HTTP_201_CREATED)
async def upload_structure_definition(
su: SuperuserDep,
text_id: Annotated[PydanticObjectId, Path(alias="id")],
file: Annotated[
UploadFile, File(description="JSON file containing the text's structure")
],
) -> None:
"""
Upload the structure definition for a text to apply as a structure of nodes
"""
# test upload file MIME type
if not file.content_type.lower() == "application/json":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid file MIME type (must be 'application/json')",
)
# find text
text = await TextDocument.get(text_id)
if not text:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Could not find text with ID {text_id}",
)
# does text already have structure nodes defined?
if await NodeDocument.find_one(NodeDocument.text_id == text_id).exists():
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="This text already has structure nodes",
)
# validate structure definition
try:
structure_def = TextStructureDefinition.model_validate_json(await file.read())
except Exception:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid structure definition",
)
# import nodes depth-first
nodes = [
dict(
level=0,
position=i,
parent_id=None,
**structure_def.nodes[i].model_dump(by_alias=False),
)
for i in range(len(structure_def.nodes))
]
structure_def = None
positions = [0] * len(text.levels)
positions[0] += len(nodes) - 1
while nodes:
node = nodes.pop(0)
node["id"] = (
await NodeDocument(
text_id=text_id,
label=node["label"],
level=node["level"],
position=node["position"],
parent_id=node["parent_id"],
).create()
).id
children_level = node["level"] + 1
if children_level >= len(text.levels):
continue
for child in node.get("nodes", []):
nodes.append(
dict(
level=children_level,
parent_id=node["id"],
position=positions[children_level],
**child,
)
)
positions[children_level] += 1


@router.post(
"/{id}/level/{index}", response_model=TextRead, status_code=status.HTTP_200_OK
)
Expand Down

0 comments on commit bb852a0

Please sign in to comment.