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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "welearn-database"
version = "1.3.0"
version = "1.4.0.dev0"
description = "All stuff related to relationnal database from the WeLearn project"
authors = [
{name = "Théo",email = "theo.nardin@cri-paris.org"}
Expand Down
32 changes: 32 additions & 0 deletions tests/test_user_related.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
from sqlalchemy.orm import sessionmaker

from tests.helpers import handle_schema_with_sqlite
from welearn_database.data.enumeration import FilterType
from welearn_database.data.models import Base
from welearn_database.data.models.user_related import (
APIKeyManagement,
Bookmark,
ChatMessage,
DataCollectionCampaignManagement,
EndpointRequest,
FilterUsedInQuery,
InferredUser,
ReturnedDocument,
)
Expand Down Expand Up @@ -114,12 +116,14 @@ def test_create_and_read_chat_message(self):
conversation_id=uuid.uuid4(),
role="user",
textual_content="Bonjour",
original_feature_name="chat",
)
self.session.add(chat)
self.session.commit()
result = self.session.query(ChatMessage).filter_by(role="user").first()
self.assertIsNotNone(result)
self.assertEqual(result.textual_content, "Bonjour")
self.assertFalse(result.is_retrieved_by_user)

def test_create_and_read_bookmark(self):
inferred_user = InferredUser(id=uuid.uuid4())
Expand Down Expand Up @@ -149,6 +153,7 @@ def test_create_and_read_returned_document(self):
inferred_user_id=inferred_user.id,
conversation_id=uuid.uuid4(),
role="user",
original_feature_name="chat",
textual_content="test",
)
self.session.add(chat)
Expand Down Expand Up @@ -193,3 +198,30 @@ def test_create_and_read_endpoint_request(self):
)
self.assertIsNotNone(result)
self.assertEqual(result.http_code, 200)

def test_create_and_read_filter_used_in_query(self):
inferred_user = InferredUser(id=uuid.uuid4())
self.session.add(inferred_user)
self.session.commit()
chat = ChatMessage(
id=uuid.uuid4(),
inferred_user_id=inferred_user.id,
conversation_id=uuid.uuid4(),
role="user",
original_feature_name="chat",
textual_content="test",
)
self.session.add(chat)
self.session.commit()
filter_used = FilterUsedInQuery(
id=uuid.uuid4(),
message_id=chat.id,
filter_type=FilterType.SDG.value,
filter_value="13",
)
self.session.add(filter_used)
self.session.commit()
result = (
self.session.query(FilterUsedInQuery).filter_by(filter_value="13").first()
)
self.assertIsNotNone(result)
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""data collection for focus group

Revision ID: 9b4f1da0c1f2
Revises: 2ad4895b2674
Create Date: 2026-02-23 18:11:55.857517

"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = "9b4f1da0c1f2"
down_revision: Union[str, None] = "2ad4895b2674"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column(
"chat_message",
sa.Column(
"is_retrieved_by_user",
sa.Boolean(),
nullable=False,
default=False,
server_default="False",
),
schema="user_related",
)
op.add_column(
"chat_message",
sa.Column("original_feature_name", sa.String(), nullable=True),
schema="user_related",
)
op.create_table(
"filter_used_in_query",
sa.Column(
"id", sa.Uuid(), server_default=sa.func.gen_random_uuid(), nullable=False
),
sa.Column("message_id", sa.Uuid(), nullable=False),
sa.Column(
"filter_type",
postgresql.ENUM(
"sdg",
"source",
name="filter_type",
schema="user_related",
),
nullable=False,
),
sa.Column("filter_value", sa.String(), nullable=False),
sa.ForeignKeyConstraint(
["message_id"],
["user_related.chat_message.id"],
name="filter_used_in_query_message_id_fkey",
),
sa.PrimaryKeyConstraint("id"),
schema="user_related",
)


def downgrade() -> None:
op.drop_column("chat_message", "is_retrieved_by_user", schema="user_related")
op.drop_column("chat_message", "original_feature_name", schema="user_related")
op.drop_constraint(
"filter_used_in_query_message_id_fkey",
"filter_used_in_query",
schema="user_related",
type_="foreignkey",
)
op.drop_table("filter_used_in_query", schema="user_related")
op.execute("DROP TYPE IF EXISTS user_related.filter_type")
5 changes: 5 additions & 0 deletions welearn_database/data/enumeration.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ class ExternalIdType(StrEnum):
HANDLE = auto()
SLUG = auto()
QID = auto()


class FilterType(StrEnum):
SDG = auto()
SOURCE = auto()
33 changes: 31 additions & 2 deletions welearn_database/data/models/user_related.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from uuid import UUID

from sqlalchemy import ForeignKey, func, types
from sqlalchemy.dialects.postgresql import TIMESTAMP
from sqlalchemy.dialects.postgresql import ENUM, TIMESTAMP
from sqlalchemy.orm import Mapped, mapped_column, relationship

from welearn_database.data.enumeration import DbSchemaEnum
from welearn_database.data.enumeration import DbSchemaEnum, FilterType
from welearn_database.data.models.document_related import WeLearnDocument

from . import Base
Expand Down Expand Up @@ -96,6 +96,10 @@ class ChatMessage(Base):
)
role: Mapped[str]
textual_content: Mapped[str]
is_retrieved_by_user: Mapped[bool] = mapped_column(
default=False, server_default="False"
)
original_feature_name: Mapped[str | None]

created_at: Mapped[datetime] = mapped_column(
TIMESTAMP(timezone=False),
Expand Down Expand Up @@ -134,6 +138,7 @@ class ReturnedDocument(Base):
nullable=False,
)
is_clicked: Mapped[bool] = mapped_column(default=False)

welearn_document: Mapped["WeLearnDocument"] = relationship()
Comment on lines 140 to 142
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReturnedDocument.original_feature_name and ReturnedDocument.conversation_id are declared without mapped_column(...), unlike the other columns in this model. This relies on type inference and may lead to inconsistent DDL (e.g., missing types.Uuid configuration for UUIDs) compared to the rest of the schema. Define these explicitly with mapped_column (including types.Uuid for conversation_id) and decide/declare nullable/defaults to match the intended DB constraints.

Copilot uses AI. Check for mistakes.
chat_message: Mapped["ChatMessage"] = relationship()

Expand Down Expand Up @@ -240,3 +245,27 @@ class EndpointRequest(Base):
server_default="NOW()",
)
session = relationship("Session", foreign_keys=[session_id])


class FilterUsedInQuery(Base):
__tablename__ = "filter_used_in_query"
__table_args__ = {"schema": DbSchemaEnum.USER_RELATED.value}

id: Mapped[UUID] = mapped_column(
types.Uuid, primary_key=True, nullable=False, server_default="gen_random_uuid()"
)
message_id = mapped_column(
types.Uuid,
ForeignKey(f"{DbSchemaEnum.USER_RELATED.value}.chat_message.id"),
nullable=False,
)
filter_type: Mapped[str] = mapped_column(
Comment on lines +258 to +262
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FilterUsedInQuery.message_id is missing a Mapped[...] type annotation (it’s currently an untyped attribute assigned to mapped_column). Adding message_id: Mapped[UUID] = mapped_column(...) keeps typing consistent with the rest of the models and helps SQLAlchemy’s annotation-driven configuration.

Copilot uses AI. Check for mistakes.
ENUM(
*(e.value.lower() for e in FilterType),
name="filter_type",
schema=DbSchemaEnum.USER_RELATED.value,
),
)
filter_value: Mapped[str]
Comment on lines +251 to +269
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds new persistent schema elements (ChatMessage.is_retrieved_by_user, ReturnedDocument.original_feature_name/conversation_id, and the new filter_used_in_query table + enum type), but there is no corresponding Alembic migration creating/backfilling these. Please add a migration that creates the filter_type enum, creates filter_used_in_query, and adds the new columns (including handling existing rows if any by using nullable/default/backfill).

Copilot uses AI. Check for mistakes.

chat_message: Mapped["ChatMessage"] = relationship()
Loading