diff --git a/enums.py b/enums.py index 4dbceca..6cacac8 100644 --- a/enums.py +++ b/enums.py @@ -180,6 +180,8 @@ class Tablenames(Enum): TIMED_EXECUTIONS = "timed_executions" CONVERSATION_SHARE = "conversation_share" CONVERSATION_GLOBAL_SHARE = "conversation_global_share" + INBOX_MAIL = "inbox_mail" + INBOX_MAIL_REFERENCE = "inbox_mail_reference" def snake_case_to_pascal_case(self): # the type name (written in PascalCase) of a table is needed to create backrefs @@ -1025,3 +1027,8 @@ class MessageType(Enum): class TimedExecutionKey(Enum): LAST_RESET_USER_MESSAGE_COUNT = "LAST_RESET_USER_MESSAGE_COUNT" + + +class InboxMailReferenceScope(Enum): + SENDER = "SENDER" + RECIPIENT = "RECIPIENT" diff --git a/global_objects/inbox_mail.py b/global_objects/inbox_mail.py new file mode 100644 index 0000000..f455267 --- /dev/null +++ b/global_objects/inbox_mail.py @@ -0,0 +1,131 @@ +from typing import Dict, List, Optional, Any +from submodules.model.util import sql_alchemy_to_dict + +from ..session import session +from sqlalchemy import cast, String, func, desc, asc + +from submodules.model.business_objects import general +from submodules.model.models import InboxMail, InboxMailReference +from sqlalchemy import or_ +from submodules.model.enums import InboxMailReferenceScope + + +def get_by_thread( + org_id: str, + user_id: str, + thread_id: str, +) -> List[InboxMail]: + + inbox_mail_entities = ( + session.query(InboxMail) + .join(InboxMailReference, InboxMail.id == InboxMailReference.inbox_mail_id) + .filter( + InboxMail.organization_id == org_id, + InboxMail.thread_id == thread_id, + InboxMailReference.user_id == user_id, + ) + .order_by(asc(InboxMail.created_at)) + .all() + ) + + return inbox_mail_entities + + +def get_overview_by_threads( + org_id: str, + user_id: str, + page: int = 1, + limit: int = 10, +) -> Dict[str, Any]: + subquery = ( + session.query( + InboxMail.thread_id, + func.max(InboxMail.created_at).label("latest_created_at"), + ) + .join(InboxMailReference, InboxMail.id == InboxMailReference.inbox_mail_id) + .filter( + InboxMail.organization_id == org_id, + InboxMailReference.user_id == user_id, + ) + .group_by(InboxMail.thread_id) + .subquery() + ) + total_threads = session.query(func.count()).select_from(subquery).scalar() + thread_summaries = ( + session.query(InboxMail) + .join( + subquery, + (InboxMail.thread_id == subquery.c.thread_id) + & (InboxMail.created_at == subquery.c.latest_created_at), + ) + .order_by(desc(InboxMail.created_at)) + .offset((page - 1) * limit) + .limit(limit) + .all() + ) + + threads = [ + {"id": str(mail.thread_id), "latest_mail": {**sql_alchemy_to_dict(mail)}} + for mail in thread_summaries + ] + + return { + "totalThreads": total_threads, + "page": page, + "limit": limit, + "threads": threads, + } + + +def create_by_thread( + org_id: str, + sender_id: str, + recipient_ids: List[str], + subject: str, + content: str, + meta_data: Optional[Dict] = None, + parent_id: Optional[str] = None, + thread_id: Optional[str] = None, + is_important: bool = False, + is_admin_notification: bool = False, + with_commit: bool = True, +) -> List[InboxMail]: + inbox_mail_entitiy = InboxMail( + organization_id=org_id, + sender_id=sender_id, + original_recipient_ids=recipient_ids, + subject=subject, + content=content, + meta_data=meta_data or {}, + parent_id=parent_id, + thread_id=thread_id, + is_important=is_important, + is_admin_notification=is_admin_notification, + ) + + general.add(inbox_mail_entitiy) + + inbox_mail_references = [] + + inbox_mail_sender_reference = InboxMailReference( + inbox_mail_id=inbox_mail_entitiy.id, + user_id=sender_id, + scope=InboxMailReferenceScope.SENDER.value, + is_seen=True, + ) + inbox_mail_references.append(inbox_mail_sender_reference) + + for rid in recipient_ids: + inbox_mail_references.append( + InboxMailReference( + inbox_mail_id=inbox_mail_entitiy.id, + user_id=rid, + scope=InboxMailReferenceScope.RECIPIENT.value, + ) + ) + general.add_all(inbox_mail_references) + + if with_commit: + general.commit() + + return inbox_mail_entitiy diff --git a/models.py b/models.py index 0ba844f..7c7f10f 100644 --- a/models.py +++ b/models.py @@ -2561,3 +2561,54 @@ class ConversationGlobalShare(Base): index=True, ) created_at = Column(DateTime, default=sql.func.now()) + + +class InboxMail(Base): + __tablename__ = Tablenames.INBOX_MAIL.value + __table_args__ = {"schema": "global"} + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + organization_id = Column( + UUID(as_uuid=True), + ForeignKey(f"{Tablenames.ORGANIZATION.value}.id", ondelete="CASCADE"), + index=True, + ) + created_at = Column(DateTime, default=sql.func.now()) + sender_id = Column( + UUID(as_uuid=True), + ForeignKey(f"{Tablenames.USER.value}.id", ondelete="SET NULL"), + index=True, + ) + original_recipient_ids = Column(JSON) + thread_id = Column(UUID(as_uuid=True), index=True, default=uuid.uuid4) + parent_id = Column( + UUID(as_uuid=True), + ForeignKey("global.inbox_mail.id", ondelete="SET NULL"), + nullable=True, + index=True, + ) + subject = Column(String) + content = Column(String) + meta_data = Column(JSON) + is_important = Column(Boolean, default=False) + being_working_on = Column(Boolean, default=False) + is_admin_notification = Column(Boolean, default=False) + + +class InboxMailReference(Base): + __tablename__ = Tablenames.INBOX_MAIL_REFERENCE.value + __table_args__ = {"schema": "global"} + id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) + inbox_mail_id = Column( + UUID(as_uuid=True), + ForeignKey(f"global.{Tablenames.INBOX_MAIL.value}.id", ondelete="CASCADE"), + index=True, + nullable=False, + ) + scope = Column(String, nullable=False) # enums.InboxMailReferenceScope + user_id = Column( + UUID(as_uuid=True), + ForeignKey(f"{Tablenames.USER.value}.id", ondelete="CASCADE"), + index=True, + nullable=False, + ) + is_seen = Column(Boolean, default=False)