Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add GET /documents/{id}/status #76

Merged
merged 4 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions dewy-client/dewy_client/api/default/get_document_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
from http import HTTPStatus
from typing import Any, Dict, Optional, Union

import httpx

from ... import errors
from ...client import AuthenticatedClient, Client
from ...models.document_status import DocumentStatus
from ...models.http_validation_error import HTTPValidationError
from ...types import Response


def _get_kwargs(
id: int,
) -> Dict[str, Any]:
_kwargs: Dict[str, Any] = {
"method": "get",
"url": f"/api/documents/{id}/status",
}

return _kwargs


def _parse_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Optional[Union[DocumentStatus, HTTPValidationError]]:
if response.status_code == HTTPStatus.OK:
response_200 = DocumentStatus.from_dict(response.json())

return response_200
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
response_422 = HTTPValidationError.from_dict(response.json())

return response_422
if client.raise_on_unexpected_status:
raise errors.UnexpectedStatus(response.status_code, response.content)
else:
return None


def _build_response(
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
) -> Response[Union[DocumentStatus, HTTPValidationError]]:
return Response(
status_code=HTTPStatus(response.status_code),
content=response.content,
headers=response.headers,
parsed=_parse_response(client=client, response=response),
)


def sync_detailed(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[DocumentStatus, HTTPValidationError]]:
"""Get Document Status

Args:
id (int): The document ID.

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Response[Union[DocumentStatus, HTTPValidationError]]
"""

kwargs = _get_kwargs(
id=id,
)

response = client.get_httpx_client().request(
**kwargs,
)

return _build_response(client=client, response=response)


def sync(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[DocumentStatus, HTTPValidationError]]:
"""Get Document Status

Args:
id (int): The document ID.

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Union[DocumentStatus, HTTPValidationError]
"""

return sync_detailed(
id=id,
client=client,
).parsed


async def asyncio_detailed(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Response[Union[DocumentStatus, HTTPValidationError]]:
"""Get Document Status

Args:
id (int): The document ID.

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Response[Union[DocumentStatus, HTTPValidationError]]
"""

kwargs = _get_kwargs(
id=id,
)

response = await client.get_async_httpx_client().request(**kwargs)

return _build_response(client=client, response=response)


async def asyncio(
id: int,
*,
client: Union[AuthenticatedClient, Client],
) -> Optional[Union[DocumentStatus, HTTPValidationError]]:
"""Get Document Status

Args:
id (int): The document ID.

Raises:
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
httpx.TimeoutException: If the request takes longer than Client.timeout.

Returns:
Union[DocumentStatus, HTTPValidationError]
"""

return (
await asyncio_detailed(
id=id,
client=client,
)
).parsed
2 changes: 2 additions & 0 deletions dewy-client/dewy_client/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .collection_create import CollectionCreate
from .distance_metric import DistanceMetric
from .document import Document
from .document_status import DocumentStatus
from .http_validation_error import HTTPValidationError
from .image_chunk import ImageChunk
from .image_result import ImageResult
Expand All @@ -21,6 +22,7 @@
"CollectionCreate",
"DistanceMetric",
"Document",
"DocumentStatus",
"HTTPValidationError",
"ImageChunk",
"ImageResult",
Expand Down
2 changes: 1 addition & 1 deletion dewy-client/dewy_client/models/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@_attrs_define
class Document:
"""Schema for documents in the SQL DB.
"""Model for documents in Dewy.

Attributes:
collection_id (int):
Expand Down
90 changes: 90 additions & 0 deletions dewy-client/dewy_client/models/document_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from typing import Any, Dict, List, Type, TypeVar, Union, cast

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..models.ingest_state import IngestState
from ..types import UNSET, Unset

T = TypeVar("T", bound="DocumentStatus")


@_attrs_define
class DocumentStatus:
"""Model for document status.

Attributes:
id (int):
ingest_state (IngestState):
ingest_error (Union[None, Unset, str]):
"""

id: int
ingest_state: IngestState
ingest_error: Union[None, Unset, str] = UNSET
additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

def to_dict(self) -> Dict[str, Any]:
id = self.id

ingest_state = self.ingest_state.value

ingest_error: Union[None, Unset, str]
if isinstance(self.ingest_error, Unset):
ingest_error = UNSET
else:
ingest_error = self.ingest_error

field_dict: Dict[str, Any] = {}
field_dict.update(self.additional_properties)
field_dict.update(
{
"id": id,
"ingest_state": ingest_state,
}
)
if ingest_error is not UNSET:
field_dict["ingest_error"] = ingest_error

return field_dict

@classmethod
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
d = src_dict.copy()
id = d.pop("id")

ingest_state = IngestState(d.pop("ingest_state"))

def _parse_ingest_error(data: object) -> Union[None, Unset, str]:
if data is None:
return data
if isinstance(data, Unset):
return data
return cast(Union[None, Unset, str], data)

ingest_error = _parse_ingest_error(d.pop("ingest_error", UNSET))

document_status = cls(
id=id,
ingest_state=ingest_state,
ingest_error=ingest_error,
)

document_status.additional_properties = d
return document_status

@property
def additional_keys(self) -> List[str]:
return list(self.additional_properties.keys())

def __getitem__(self, key: str) -> Any:
return self.additional_properties[key]

def __setitem__(self, key: str, value: Any) -> None:
self.additional_properties[key] = value

def __delitem__(self, key: str) -> None:
del self.additional_properties[key]

def __contains__(self, key: str) -> bool:
return key in self.additional_properties
2 changes: 1 addition & 1 deletion dewy/common/db_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ async def apply_migrations(
applied += 1

if applied_migrations:
logger.warn("Unrecognized migrations applied: {}", applied_migrations)
logger.warning("Unrecognized migrations applied: {}", applied_migrations)

logger.info(
"Migrations complete. {} total, {} newly applied",
Expand Down
8 changes: 7 additions & 1 deletion dewy/document/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class IngestState(Enum):


class Document(BaseModel):
"""Schema for documents in the SQL DB."""
"""Model for documents in Dewy."""

id: Optional[int] = None
collection_id: int
Expand All @@ -41,3 +41,9 @@ class Document(BaseModel):

ingest_state: Optional[IngestState] = None
ingest_error: Optional[str] = None

class DocumentStatus(BaseModel):
"""Model for document status."""
id: int
ingest_state: IngestState
ingest_error: Optional[str] = None
18 changes: 17 additions & 1 deletion dewy/document/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dewy.common.db import PgConnectionDep, PgPoolDep
from dewy.document.models import Document

from .models import AddDocumentRequest
from .models import AddDocumentRequest, DocumentStatus, IngestState

router = APIRouter(prefix="/documents")

Expand Down Expand Up @@ -125,3 +125,19 @@ async def get_document(conn: PgConnectionDep, id: PathDocumentId) -> Document:
id,
)
return Document.model_validate(dict(result))

@router.get("/{id}/status")
async def get_document_status(conn: PgConnectionDep, id: PathDocumentId) -> DocumentStatus:
result = await conn.fetchrow(
"""
SELECT ingest_state, ingest_error
FROM document
WHERE id = $1
""",
id
)
return DocumentStatus(
id=id,
ingest_state = result["ingest_state"],
ingest_error = result["ingest_error"]
)
2 changes: 1 addition & 1 deletion dewy/migrations/0001_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ CREATE TABLE document(
-- The state of the most recent ingestion of this document.
-- TODO: Should we have a separate `ingestion` table and associate
-- many ingestions with each document ID?
ingest_state ingest_state,
ingest_state ingest_state NOT NULL,
-- The error (if any) resulting from the most recent ingestion.
ingest_error VARCHAR,

Expand Down
46 changes: 45 additions & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,32 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/documents/{id}/status:
get:
summary: Get Document Status
operationId: getDocumentStatus
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: The document ID.
title: Id
description: The document ID.
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/DocumentStatus'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
/api/chunks/:
get:
summary: List Chunks
Expand Down Expand Up @@ -400,7 +426,25 @@ components:
- collection_id
- url
title: Document
description: Schema for documents in the SQL DB.
description: Model for documents in Dewy.
DocumentStatus:
properties:
id:
type: integer
title: Id
ingest_state:
$ref: '#/components/schemas/IngestState'
ingest_error:
anyOf:
- type: string
- type: 'null'
title: Ingest Error
type: object
required:
- id
- ingest_state
title: DocumentStatus
description: Model for document status.
HTTPValidationError:
properties:
detail:
Expand Down
Loading
Loading