quivr/backend/routes/knowledge_routes.py
Pascal Gula d893ec7d97
Fixes string formatting when logging knowledge table (#1691)
# Description

```
======================================================================
backend-core  | --- Logging error ---
backend-core  | Traceback (most recent call last):
backend-core  |   File "/usr/local/lib/python3.11/logging/__init__.py", line 1110, in emit
backend-core  |     msg = self.format(record)
backend-core  |           ^^^^^^^^^^^^^^^^^^^
backend-core  |   File "/usr/local/lib/python3.11/logging/__init__.py", line 953, in format
backend-core  |     return fmt.format(record)
backend-core  |            ^^^^^^^^^^^^^^^^^^
backend-core  |   File "/usr/local/lib/python3.11/logging/__init__.py", line 687, in format
backend-core  |     record.message = record.getMessage()
backend-core  |                      ^^^^^^^^^^^^^^^^^^^
backend-core  |   File "/usr/local/lib/python3.11/logging/__init__.py", line 377, in getMessage
backend-core  |     msg = msg % self.args
backend-core  |           ~~~~^~~~~~~~~~~
backend-core  | TypeError: not all arguments converted during string formatting
backend-core  | Call stack:
backend-core  |   File "<string>", line 1, in <module>
backend-core  |   File "/usr/local/lib/python3.11/multiprocessing/spawn.py", line 122, in spawn_main
backend-core  |     exitcode = _main(fd, parent_sentinel)
backend-core  |   File "/usr/local/lib/python3.11/multiprocessing/spawn.py", line 135, in _main
backend-core  |     return self._bootstrap(parent_sentinel)
backend-core  |   File "/usr/local/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
backend-core  |     self.run()
backend-core  |   File "/usr/local/lib/python3.11/multiprocessing/process.py", line 108, in run
backend-core  |     self._target(*self._args, **self._kwargs)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/_subprocess.py", line 76, in subprocess_started
backend-core  |     target(sockets=sockets)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/server.py", line 61, in run
backend-core  |     return asyncio.run(self.serve(sockets=sockets))
backend-core  |   File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run
backend-core  |     return runner.run(main)
backend-core  |   File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
backend-core  |     return self._loop.run_until_complete(task)
backend-core  |   File "/usr/local/lib/python3.11/asyncio/base_events.py", line 640, in run_until_complete
backend-core  |     self.run_forever()
backend-core  |   File "/usr/local/lib/python3.11/asyncio/base_events.py", line 607, in run_forever
backend-core  |     self._run_once()
backend-core  |   File "/usr/local/lib/python3.11/asyncio/base_events.py", line 1922, in _run_once
backend-core  |     handle._run()
backend-core  |   File "/usr/local/lib/python3.11/asyncio/events.py", line 80, in _run
backend-core  |     self._context.run(self._callback, *self._args)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/protocols/http/h11_impl.py", line 428, in run_asgi
backend-core  |     result = await app(  # type: ignore[func-returns-value]
backend-core  |   File "/usr/local/lib/python3.11/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
backend-core  |     return await self.app(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/fastapi/applications.py", line 276, in __call__
backend-core  |     await super().__call__(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/applications.py", line 122, in __call__
backend-core  |     await self.middleware_stack(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/errors.py", line 162, in __call__
backend-core  |     await self.app(scope, receive, _send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 91, in __call__
backend-core  |     await self.simple_response(scope, receive, send, request_headers=headers)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/cors.py", line 146, in simple_response
backend-core  |     await self.app(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/middleware/exceptions.py", line 68, in __call__
backend-core  |     await self.app(scope, receive, sender)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
backend-core  |     await self.app(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 718, in __call__
backend-core  |     await route.handle(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 276, in handle
backend-core  |     await self.app(scope, receive, send)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 66, in app
backend-core  |     response = await func(request)
backend-core  |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 237, in app
backend-core  |     raw_response = await run_endpoint_function(
backend-core  |   File "/usr/local/lib/python3.11/site-packages/fastapi/routing.py", line 163, in run_endpoint_function
backend-core  |     return await dependant.call(**values)
backend-core  |   File "/code/routes/knowledge_routes.py", line 37, in list_knowledge_in_brain_endpoint
backend-core  |     logger.info("List of knowledge from knowledge table", knowledges)
backend-core  | Message: 'List of knowledge from knowledge table'
backend-core  | Arguments: ([Knowledge(id=UUID('b5ae6a25-1e8e-4fb5-a0c1-0039a8318ec3'), brain_id=UUID('7af64537-61cb-48e1-b3d1-c700d748b373'), file_name='Cradle to Cradle Criteria for the built environmen.pdf', url=None, extension='.pdf'), Knowledge(id=UUID('efb7ad5a-a13e-46f3-a580-4c574710ad15'), brain_id=UUID('7af64537-61cb-48e1-b3d1-c700d748b373'), file_name='GEG_2023_gebaeudeenergiegesetz_kompakt_und_praktisch.pdf', url=None, extension='.pdf'), Knowledge(id=UUID('9442f898-0fbe-46d2-b6ae-a726d62717bd'), brain_id=UUID('7af64537-61cb-48e1-b3d1-c700d748b373'), file_name='dgnb-kriterienkatalog-gebaeude-neubau-version-2023-auflage-2.pdf', url=None, extension='.pdf')],)
======================================================================
```

## Checklist before requesting a review

Please delete options that are not relevant.

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented hard-to-understand areas
- [x] I have ideally added tests that prove my fix is effective or that
my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged
2023-11-23 17:25:57 +01:00

103 lines
3.1 KiB
Python

from uuid import UUID
from fastapi import APIRouter, Depends, Query
from logger import get_logger
from middlewares.auth import AuthBearer, get_current_user
from models import Brain
from modules.user.entity.user_identity import UserIdentity
from repository.files.delete_file import delete_file_from_storage
from repository.files.generate_file_signed_url import generate_file_signed_url
from repository.knowledge.get_all_knowledge import get_all_knowledge
from repository.knowledge.get_knowledge import get_knowledge
from repository.knowledge.remove_knowledge import remove_knowledge
from routes.authorizations.brain_authorization import (
RoleEnum,
has_brain_authorization,
validate_brain_authorization,
)
knowledge_router = APIRouter()
logger = get_logger(__name__)
@knowledge_router.get(
"/knowledge", dependencies=[Depends(AuthBearer())], tags=["Knowledge"]
)
async def list_knowledge_in_brain_endpoint(
brain_id: UUID = Query(..., description="The ID of the brain"),
current_user: UserIdentity = Depends(get_current_user),
):
"""
Retrieve and list all the knowledge in a brain.
"""
validate_brain_authorization(brain_id=brain_id, user_id=current_user.id)
knowledges = get_all_knowledge(brain_id)
logger.info(f"List of knowledge from knowledge table: {knowledges}")
return {"knowledges": knowledges}
@knowledge_router.delete(
"/knowledge/{knowledge_id}",
dependencies=[
Depends(AuthBearer()),
Depends(has_brain_authorization(RoleEnum.Owner)),
],
tags=["Knowledge"],
)
async def delete_endpoint(
knowledge_id: UUID,
current_user: UserIdentity = Depends(get_current_user),
brain_id: UUID = Query(..., description="The ID of the brain"),
):
"""
Delete a specific knowledge from a brain.
"""
validate_brain_authorization(brain_id=brain_id, user_id=current_user.id)
brain = Brain(id=brain_id)
knowledge = get_knowledge(knowledge_id)
file_name = knowledge.file_name if knowledge.file_name else knowledge.url
remove_knowledge(knowledge_id)
if knowledge.file_name:
delete_file_from_storage(f"{brain_id}/{knowledge.file_name}")
brain.delete_file_from_brain(knowledge.file_name)
elif knowledge.url:
brain.delete_file_from_brain(knowledge.url)
return {
"message": f"{file_name} of brain {brain_id} has been deleted by user {current_user.email}."
}
@knowledge_router.get(
"/knowledge/{knowledge_id}/signed_download_url",
dependencies=[Depends(AuthBearer())],
tags=["Knowledge"],
)
async def generate_signed_url_endpoint(
knowledge_id: UUID,
current_user: UserIdentity = Depends(get_current_user),
):
"""
Generate a signed url to download the file from storage.
"""
knowledge = get_knowledge(knowledge_id)
validate_brain_authorization(brain_id=knowledge.brain_id, user_id=current_user.id)
if knowledge.file_name == None:
raise Exception(f"Knowledge {knowledge_id} has no file_name associated with it")
file_path_in_storage = f"{knowledge.brain_id}/{knowledge.file_name}"
file_signed_url = generate_file_signed_url(file_path_in_storage)
return file_signed_url