Skip to content

Commit

Permalink
Implement node structure template download
Browse files Browse the repository at this point in the history
  • Loading branch information
bkis committed Oct 9, 2023
1 parent 9345290 commit b648151
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 82 deletions.
97 changes: 53 additions & 44 deletions Tekst-API/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,59 @@
}
}
},
"/texts/{id}/template": {
"get": {
"tags": [
"texts"
],
"summary": "Download structure template",
"description": "Download the structure template for a text to help compose a structure\ndefinition that can later be uploaded to the server",
"operationId": "downloadStructureTemplate",
"security": [
{
"APIKeyCookie": []
},
{
"OAuth2PasswordBearer": []
}
],
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"example": "5eb7cf5a86d9755df3a6c593",
"title": "Id"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"404": {
"description": "Not found"
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/texts/{id}/level/{index}": {
"post": {
"tags": [
Expand Down Expand Up @@ -3260,21 +3313,6 @@
"minLength": 1,
"title": "Label",
"description": "Label for identifying this text node in level context"
},
"meta": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"title": "Meta",
"description": "Arbitrary metadata"
}
},
"type": "object",
Expand Down Expand Up @@ -3331,21 +3369,6 @@
"minLength": 1,
"title": "Label",
"description": "Label for identifying this text node in level context"
},
"meta": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"title": "Meta",
"description": "Arbitrary metadata"
}
},
"type": "object",
Expand Down Expand Up @@ -3388,20 +3411,6 @@
"label": {
"type": "string",
"title": "Label"
},
"meta": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"title": "Meta"
}
},
"type": "object",
Expand Down
11 changes: 11 additions & 0 deletions Tekst-API/tekst/dependencies.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from collections.abc import Iterator
from tempfile import TemporaryDirectory

from fastapi import Depends
from motor.motor_asyncio import AsyncIOMotorDatabase as Database

Expand All @@ -17,3 +20,11 @@ def get_db(
db_client: Database = Depends(get_db_client), cfg: TekstConfig = Depends(get_cfg)
) -> Database:
return db_client[cfg.db_name]


async def get_temp_dir() -> Iterator[str]:
dir = TemporaryDirectory()
try:
yield dir.name
finally:
del dir
18 changes: 18 additions & 0 deletions Tekst-API/tekst/models/exchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Annotated

from pydantic import ConfigDict, StringConstraints

from tekst.models.common import ModelBase


class NodeDefinition(ModelBase):
label: Annotated[
str,
StringConstraints(min_length=1, max_length=256),
]
children: list["NodeDefinition"] | None = None


class TextStructureDefinition(ModelBase):
model_config = ConfigDict(extra="allow")
structure: list[NodeDefinition] = []
62 changes: 61 additions & 1 deletion Tekst-API/tekst/routers/texts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
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, HTTPException, Path, status
from fastapi import APIRouter, Body, Depends, HTTPException, Path, status
from fastapi.responses import FileResponse

from tekst.auth import OptionalUserDep, SuperuserDep
from tekst.dependencies import get_temp_dir
from tekst.models.exchange import NodeDefinition, TextStructureDefinition
from tekst.models.layer import LayerBaseDocument
from tekst.models.text import (
NodeDocument,
Expand Down Expand Up @@ -79,6 +84,61 @@ async def create_text(su: SuperuserDep, text: TextCreate) -> TextRead:
# return text


@router.get("/{id}/template", status_code=status.HTTP_200_OK)
async def download_structure_template(
su: SuperuserDep,
text_id: Annotated[PydanticObjectId, Path(alias="id")],
temp_dir_name: str = Depends(get_temp_dir),
) -> FileResponse:
"""
Download the structure template for a text to help compose a structure
definition that can later be uploaded to the server
"""
# 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}",
)
# create template for text
structure_def: TextStructureDefinition = TextStructureDefinition()
curr_node_def: NodeDefinition | None = None
dummy_node = NodeDefinition(
label="Label for the first node on level '{}' (required!)",
)
for n in range(len(text.levels)):
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)
else:
curr_node_def.children = []
curr_node_def.children.append(node)
curr_node_def = node
# validate template
try:
TextStructureDefinition.model_validate(structure_def)
except Exception:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error creating template",
)
# allocate and write temporary file
temp_file_name = f"{text.slug}_structure_template.json"
temp_file_path = SysPath(temp_dir_name) / temp_file_name
with open(temp_file_path, "w") as f:
f.write(
structure_def.model_dump_json(indent=2, by_alias=True, exclude_none=True)
)
# return structure template file
return FileResponse(
path=temp_file_path,
media_type="application/octet-stream",
filename=temp_file_name,
)


@router.post(
"/{id}/level/{index}", response_model=TextRead, status_code=status.HTTP_200_OK
)
Expand Down
3 changes: 3 additions & 0 deletions Tekst-Web/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export const optionsPresets = {
},
};

export const getFullUrl = (path: string, query?: Record<string, any>) =>
apiUrl + path + (query ? '?' + queryString.stringify(query, { arrayFormat: 'none' }) : '');

// export components types for use throughout codebase
export type UserCreate = components['schemas']['UserCreate'];
export type UserRead = components['schemas']['UserRead'];
Expand Down
Loading

0 comments on commit b648151

Please sign in to comment.