Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
268 changes: 238 additions & 30 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ langgraph-checkpoint-postgres = "^2.0.23"
azure-ai-inference = "^1.0.0b9"
azure-identity = "^1.25.0"
psycopg = {extras = ["binary"], version = "^3.2.10"}
welearn-database = "1.4.0"
bs4 = "^0.0.2"
urllib3 = "^2.6.3"
refinedoc = "^1.0.1"
Expand All @@ -51,6 +50,7 @@ langchain-mistralai = "^1.1.2"
langchain-azure-ai = "^1.2.3"
langgraph = "^1.1.10"
mistralai = "^2.4.3"
welearn-database = "^1.4.5"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
Expand Down
15 changes: 15 additions & 0 deletions src/app/services/sql_db/queries_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,18 @@ def add_user_bookmark_sync(user_id: uuid.UUID, document_id: uuid.UUID) -> uuid.U
s.add(new_bookmark)
s.commit()
return document_id


def add_institution_data_to_user_sync(
user_id: uuid.UUID, institution: str, role: str
) -> uuid.UUID:
with session_maker() as s:
user = s.execute(select(InferredUser).where(InferredUser.id == user_id)).first()
if not user:
raise ValueError(f"User {user_id} does not exist")
user = user[0]
user.university_title = institution
user.role = role
s.add(user)
s.commit()
return user_id
64 changes: 64 additions & 0 deletions src/app/tests/api/api_v1/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,67 @@ async def test_add_user_bookmark_user_not_found(
)
resolve_user_and_session_mock.assert_called_once()
run_in_threadpool_mock.assert_not_called()

@mock.patch("src.app.user.api.router.run_in_threadpool")
@mock.patch("src.app.user.api.router.resolve_user_and_session")
async def test_add_user_institution_data_user_not_found(
self, resolve_user_and_session_mock, run_in_threadpool_mock, *mocks
):
"""Ajout d'un bookmark - mocks only what is needed"""
# Mock resolve_user_and_session to return user_id and session_id
user_id = uuid.UUID("cfc8072c-a055-442a-9878-b5a73d9141b2")
session_id = uuid.UUID("bdb62bb2-1fe5-4d14-92fd-60a041355aea")

# mock error for user not found
resolve_user_and_session_mock.side_effect = UserNotFoundError("User not found")
resolve_user_and_session_mock.return_value = (user_id, session_id)

# Mock run_in_threadpool to simulate DB add_user_bookmark_sync
document_id = "ffffffff-ffff-ffff-ffff-ffffffffffff"
run_in_threadpool_mock.return_value = document_id

response = client.post(
f"{settings.API_V1_STR}/user/institution-data",
json={"institution": "Test University", "role": "Student"},
headers={"X-API-Key": "test"},
cookies={"x-session-id": str(session_id)},
)
self.assertEqual(response.status_code, 404)
self.assertEqual(
response.json(), {"detail": "('User not found', 'USER_NOT_FOUND')"}
)
resolve_user_and_session_mock.assert_called_once()
run_in_threadpool_mock.assert_not_called()

@mock.patch("src.app.user.api.router.run_in_threadpool")
@mock.patch("src.app.user.api.router.resolve_user_and_session")
async def test_add_user_institution_data_success(
self, resolve_user_and_session_mock, run_in_threadpool_mock, *mocks
):
"""Ajout d'un institution data - mocks only what is needed"""
# Mock resolve_user_and_session to return user_id and session_id
user_id = uuid.UUID("cfc8072c-a055-442a-9878-b5a73d9141b2")
session_id = uuid.UUID("bdb62bb2-1fe5-4d14-92fd-60a041355aea")
resolve_user_and_session_mock.return_value = (user_id, session_id)

# Mock run_in_threadpool to simulate DB add_institution_data_to_user_sync
document_id = "ffffffff-ffff-ffff-ffff-ffffffffffff"
run_in_threadpool_mock.return_value = document_id

response = client.post(
f"{settings.API_V1_STR}/user/institution-data",
json={"institution": "Test University", "role": "Student"},
headers={"X-API-Key": "test"},
cookies={"x-session-id": str(session_id)},
)
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.json(),
{
"message": "Institution data added to user",
"institution": "Test University",
"role": "Student",
},
)
resolve_user_and_session_mock.assert_called_once()
run_in_threadpool_mock.assert_called_once()
39 changes: 39 additions & 0 deletions src/app/user/api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from fastapi.concurrency import run_in_threadpool

from src.app.services.sql_db.queries_user import (
add_institution_data_to_user_sync,
add_user_bookmark_sync,
delete_user_bookmark_sync,
delete_user_bookmarks_sync,
Expand All @@ -18,6 +19,7 @@
extract_origin_from_request,
extract_session_cookie,
)
from src.app.user.models.models import InstitutionData
from src.app.user.utils.utils import resolve_user_and_session
from src.app.utils.logger import logger as logger_utils

Expand Down Expand Up @@ -199,3 +201,40 @@ async def add_user_bookmark(request: Request, document_id: uuid.UUID):
except Exception as e:
logger.error(f"Error adding bookmark: {e}")
raise HTTPException(status_code=500, detail=str(e))


@router.post(
"/institution-data", summary="Add institution data to user", response_model=dict
)
async def add_institution_data_to_user(request: Request, data: InstitutionData):
session_uuid = extract_session_cookie(request)
if not session_uuid:
raise HTTPException(status_code=401, detail="Session cookie is missing")
host = extract_origin_from_request(request)

try:
user_id, _ = await resolve_user_and_session(
session_uuid=session_uuid,
host=host,
referer=None,
)
except UserNotFoundError as e:
raise HTTPException(status_code=404, detail=str(e))

try:
resp = await run_in_threadpool(
add_institution_data_to_user_sync, user_id, data.institution, data.role
)

print(f"add_institution_data_to_user_sync response: {resp}")
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
logger.error(f"Error adding institution data: {e}")
raise HTTPException(status_code=500, detail=str(e))

return {
"message": "Institution data added to user",
"institution": data.institution,
"role": data.role,
}
Empty file added src/app/user/models/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions src/app/user/models/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from pydantic import BaseModel


class InstitutionData(BaseModel):
institution: str
role: str
Loading