From a827b7f19ea6a1763d73e55f3eaf2917ced66739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20K=C3=B6pf?= Date: Thu, 15 Dec 2022 16:13:36 +0100 Subject: [PATCH] add sqlmodel classes --- .../versions/cd7de470586e_v1_db_structure.py | 16 ++++++---- backend/app/models/__init__.py | 18 ++++++++++- backend/app/models/api_client.py | 21 ++++++++++++ backend/app/models/labeler.py | 3 +- backend/app/models/person.py | 26 +++++++++++++++ backend/app/models/person_stats.py | 28 ++++++++++++++++ backend/app/models/post.py | 32 +++++++++++++++++++ backend/app/models/post_reaction.py | 26 +++++++++++++++ backend/app/models/prompt.py | 3 +- backend/app/models/work_package.py | 27 ++++++++++++++++ 10 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 backend/app/models/api_client.py create mode 100644 backend/app/models/person.py create mode 100644 backend/app/models/person_stats.py create mode 100644 backend/app/models/post.py create mode 100644 backend/app/models/post_reaction.py create mode 100644 backend/app/models/work_package.py diff --git a/backend/alembic/versions/cd7de470586e_v1_db_structure.py b/backend/alembic/versions/cd7de470586e_v1_db_structure.py index 8fab7b94..c394fe4f 100644 --- a/backend/alembic/versions/cd7de470586e_v1_db_structure.py +++ b/backend/alembic/versions/cd7de470586e_v1_db_structure.py @@ -34,7 +34,7 @@ def upgrade() -> None: sa.Column("api_key", sa.String(512), nullable=False), sa.Column("description", sa.String(256), nullable=False), sa.Column("admin_email", sa.String(256), nullable=True), - sa.Column("enabled", sa.Boolean, default=True), + sa.Column("enabled", sa.Boolean, default=True, nullable=False), sa.PrimaryKeyConstraint("id"), ) op.create_index(op.f("ix_api_client_api_key"), "api_client", ["api_key"], unique=True) @@ -44,6 +44,7 @@ def upgrade() -> None: sa.Column("id", UUID(as_uuid=True), default=uuid.uuid4, server_default=sa.text("gen_random_uuid()")), sa.Column("username", sa.String(128), nullable=False), # unique in combination with api_client_id sa.Column("display_name", sa.String(256), nullable=False), # cached last seen display_name + sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), @@ -73,7 +74,7 @@ def upgrade() -> None: sa.Column("expiry_date", sa.DateTime(), nullable=True), sa.Column("person_id", UUID(as_uuid=True), nullable=False), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", JSONB(), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), @@ -90,10 +91,10 @@ def upgrade() -> None: sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.Column("role", sa.String(128), nullable=False), # 'assistant', 'user' or something else - sa.Column("frontend_post_id", sa.String(200)), # unique together with api_client_id + sa.Column("frontend_post_id", sa.String(200), nullable=False), # unique together with api_client_id sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", JSONB(), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.PrimaryKeyConstraint("id"), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), @@ -101,7 +102,7 @@ def upgrade() -> None: op.create_index(op.f("ix_post_frontend_post_id"), "post", ["api_client_id", "frontend_post_id"], unique=True) op.create_index(op.f("ix_post_thread_id"), "post", ["thread_id"], unique=False) op.create_index(op.f("ix_post_workpackage_id"), "post", ["workpackage_id"], unique=False) - op.create_index(op.f("ix_post_person"), "post", ["person_id"], unique=False) + op.create_index(op.f("ix_post_person_id"), "post", ["person_id"], unique=False) op.create_table( "post_reaction", @@ -109,9 +110,10 @@ def upgrade() -> None: sa.Column("person_id", UUID(as_uuid=True), nullable=False), # sender (recipients are part of payload) sa.Column("created_date", sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), sa.Column("payload_type", sa.String(200), nullable=False), # deserialization hint & dbg aid - sa.Column("payload", sa.String(1024), nullable=False), + sa.Column("payload", JSONB(astext_type=sa.Text()), nullable=False), sa.Column("api_client_id", UUID(as_uuid=True), nullable=False), sa.PrimaryKeyConstraint("post_id", "person_id"), + sa.ForeignKeyConstraint(["post_id"], ["post.id"]), sa.ForeignKeyConstraint(["person_id"], ["person.id"]), sa.ForeignKeyConstraint(["api_client_id"], ["api_client.id"]), ) @@ -120,7 +122,7 @@ def upgrade() -> None: def downgrade() -> None: op.drop_table("post_reaction") - op.drop_index("ix_post_person") + op.drop_index("ix_post_person_id") op.drop_index("ix_post_workpackage_id") op.drop_index("ix_post_thread_id") op.drop_index("ix_post_frontend_post_id") diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index f12c41f3..e05e4936 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -1,6 +1,22 @@ # -*- coding: utf-8 -*- +from .api_client import ApiClient from .labeler import Labeler +from .person import Person +from .person_stats import PersonStats +from .post import Post +from .post_reaction import PostReaction from .prompt import Prompt from .service_client import ServiceClient +from .work_package import WorkPackage -__all__ = ["Labeler", "Prompt", "ServiceClient"] +__all__ = [ + "ApiClient", + "Person", + "PersonStats", + "Post", + "PostReaction", + "WorkPackage", + "Labeler", + "Prompt", + "ServiceClient", +] diff --git a/backend/app/models/api_client.py b/backend/app/models/api_client.py new file mode 100644 index 00000000..f0050a90 --- /dev/null +++ b/backend/app/models/api_client.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, SQLModel + + +class ApiClient(SQLModel, table=True): + __tablename__ = "api_client" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + api_key: str = Field(max_length=512, index=True, unique=True) + description: str = Field(max_length=256) + admin_email: Optional[str] = Field(max_length=256, nullable=True) + enabled: bool = Field(default=True) diff --git a/backend/app/models/labeler.py b/backend/app/models/labeler.py index 9dd775b1..9ea4251b 100644 --- a/backend/app/models/labeler.py +++ b/backend/app/models/labeler.py @@ -12,8 +12,7 @@ class Labeler(SQLModel, table=True): display_name: str discord_username: str created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), - nullable=False, + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) is_enabled: bool notes: str diff --git a/backend/app/models/person.py b/backend/app/models/person.py new file mode 100644 index 00000000..57719a4a --- /dev/null +++ b/backend/app/models/person.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, Index, SQLModel + + +class Person(SQLModel, table=True): + __tablename__ = "person" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + username: str = Field(nullable=False, max_length=128) + display_name: str = Field(nullable=False, max_length=256) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + api_client_id: UUID = Field(foreign_key="api_client.id") + + __table_args__ = (Index("ix_person_username", "api_client_id", "username", unique=True),) diff --git a/backend/app/models/person_stats.py b/backend/app/models/person_stats.py new file mode 100644 index 00000000..d05e0047 --- /dev/null +++ b/backend/app/models/person_stats.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from sqlmodel import Field, SQLModel + + +class PersonStats(SQLModel, table=True): + __tablename__ = "person_stats" + + person_id: Optional[UUID] = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("person.id"), primary_key=True) + ) + leader_score: int = 0 + modified_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + + reactions: int = 0 # reactions sent by user + posts: int = 0 # posts sent by user + upvotes: int = 0 # received upvotes (form other users) + downvotes: int = 0 # received downvotes (from other users) + work_reward: int = 0 # reward for workpackage completions + compare_wins: int = 0 # num times user's post won compare tasks + compare_losses: int = 0 # num times users's post lost compare tasks diff --git a/backend/app/models/post.py b/backend/app/models/post.py new file mode 100644 index 00000000..f73fd34b --- /dev/null +++ b/backend/app/models/post.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, Index, SQLModel + + +class Post(SQLModel, table=True): + __tablename__ = "post" + __table_args__ = (Index("ix_post_frontend_post_id", "api_client_id", "frontend_post_id", unique=True),) + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + parent_id: UUID = Field(nullable=True) + thread_id: UUID = Field(nullable=False, index=True) + workpackage_id: UUID = Field(nullable=True, index=True) + person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + role: str = Field(nullable=False, max_length=128) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") + frontend_post_id: str = Field(max_length=200, nullable=False) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) diff --git a/backend/app/models/post_reaction.py b/backend/app/models/post_reaction.py new file mode 100644 index 00000000..7a7c9269 --- /dev/null +++ b/backend/app/models/post_reaction.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, SQLModel + + +class PostReaction(SQLModel, table=True): + __tablename__ = "post_reaction" + + post_id: Optional[UUID] = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("post.id"), nullable=False, primary_key=True) + ) + person_id: UUID = Field( + sa_column=sa.Column(pg.UUID(as_uuid=True), sa.ForeignKey("person.id"), nullable=False, primary_key=True) + ) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) + ) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id") diff --git a/backend/app/models/prompt.py b/backend/app/models/prompt.py index 71ec956a..edd1bde1 100644 --- a/backend/app/models/prompt.py +++ b/backend/app/models/prompt.py @@ -14,6 +14,5 @@ class Prompt(SQLModel, table=True): response: Optional[str] lang: Optional[str] created_date: Optional[datetime] = Field( - sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), - nullable=False, + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()) ) diff --git a/backend/app/models/work_package.py b/backend/app/models/work_package.py new file mode 100644 index 00000000..cfa7dc77 --- /dev/null +++ b/backend/app/models/work_package.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from typing import Optional +from uuid import UUID, uuid4 + +import sqlalchemy as sa +import sqlalchemy.dialects.postgresql as pg +from pydantic import BaseModel +from sqlmodel import Field, SQLModel + + +class WorkPackage(SQLModel, table=True): + __tablename__ = "work_package" + + id: Optional[UUID] = Field( + sa_column=sa.Column( + pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()") + ), + ) + created_date: Optional[datetime] = Field( + sa_column=sa.Column(sa.DateTime(), nullable=False, server_default=sa.func.current_timestamp()), + ) + expiry_date: Optional[datetime] = Field(sa_column=sa.Column(sa.DateTime(), nullable=True)) + person_id: UUID = Field(nullable=False, foreign_key="person.id", index=True) + payload_type: str = Field(nullable=False, max_length=200) + payload: BaseModel = Field(sa_column=sa.Column(pg.JSONB, nullable=False)) + api_client_id: UUID = Field(nullable=False, foreign_key="api_client.id")