refactor: migrate modules from re-exports to canonical implementations
Move actual code implementations into module directories: - orders: 5 services, 4 models, order/invoice schemas - inventory: 3 services, 2 models, 30+ schemas - customers: 3 services, 2 models, customer schemas - messaging: 3 services, 2 models, message/notification schemas - monitoring: background_tasks_service - marketplace: 5+ services including letzshop submodule - dev_tools: code_quality_service, test_runner_service - billing: billing_service - contracts: definition.py Legacy files in app/services/, models/database/, models/schema/ now re-export from canonical module locations for backwards compatibility. Architecture validator passes with 0 errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,308 +1,83 @@
|
||||
# models/schema/message.py
|
||||
"""
|
||||
Pydantic schemas for the messaging system.
|
||||
LEGACY LOCATION - Re-exports from module for backwards compatibility.
|
||||
|
||||
Supports three communication channels:
|
||||
- Admin <-> Vendor
|
||||
- Vendor <-> Customer
|
||||
- Admin <-> Customer
|
||||
The canonical implementation is now in:
|
||||
app/modules/messaging/schemas/message.py
|
||||
|
||||
This file exists to maintain backwards compatibility with code that
|
||||
imports from the old location. All new code should import directly
|
||||
from the module:
|
||||
|
||||
from app.modules.messaging.schemas import message
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from models.database.message import ConversationType, ParticipantType
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Attachment Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class AttachmentResponse(BaseModel):
|
||||
"""Schema for message attachment in responses."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
filename: str
|
||||
original_filename: str
|
||||
file_size: int
|
||||
mime_type: str
|
||||
is_image: bool
|
||||
image_width: int | None = None
|
||||
image_height: int | None = None
|
||||
download_url: str | None = None
|
||||
thumbnail_url: str | None = None
|
||||
|
||||
@property
|
||||
def file_size_display(self) -> str:
|
||||
"""Human-readable file size."""
|
||||
if self.file_size < 1024:
|
||||
return f"{self.file_size} B"
|
||||
elif self.file_size < 1024 * 1024:
|
||||
return f"{self.file_size / 1024:.1f} KB"
|
||||
else:
|
||||
return f"{self.file_size / 1024 / 1024:.1f} MB"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Message Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class MessageCreate(BaseModel):
|
||||
"""Schema for sending a new message."""
|
||||
|
||||
content: str = Field(..., min_length=1, max_length=10000)
|
||||
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
"""Schema for a single message in responses."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
conversation_id: int
|
||||
sender_type: ParticipantType
|
||||
sender_id: int
|
||||
content: str
|
||||
is_system_message: bool
|
||||
is_deleted: bool
|
||||
created_at: datetime
|
||||
|
||||
# Enriched sender info (populated by API)
|
||||
sender_name: str | None = None
|
||||
sender_email: str | None = None
|
||||
|
||||
# Attachments
|
||||
attachments: list[AttachmentResponse] = []
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Participant Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class ParticipantInfo(BaseModel):
|
||||
"""Schema for participant information."""
|
||||
|
||||
id: int
|
||||
type: ParticipantType
|
||||
name: str
|
||||
email: str | None = None
|
||||
avatar_url: str | None = None
|
||||
|
||||
|
||||
class ParticipantResponse(BaseModel):
|
||||
"""Schema for conversation participant in responses."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
participant_type: ParticipantType
|
||||
participant_id: int
|
||||
unread_count: int
|
||||
last_read_at: datetime | None
|
||||
email_notifications: bool
|
||||
muted: bool
|
||||
|
||||
# Enriched info (populated by API)
|
||||
participant_info: ParticipantInfo | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Conversation Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class ConversationCreate(BaseModel):
|
||||
"""Schema for creating a new conversation."""
|
||||
|
||||
conversation_type: ConversationType
|
||||
subject: str = Field(..., min_length=1, max_length=500)
|
||||
recipient_type: ParticipantType
|
||||
recipient_id: int
|
||||
vendor_id: int | None = None
|
||||
initial_message: str | None = Field(None, min_length=1, max_length=10000)
|
||||
|
||||
|
||||
class ConversationSummary(BaseModel):
|
||||
"""Schema for conversation in list views."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
conversation_type: ConversationType
|
||||
subject: str
|
||||
vendor_id: int | None = None
|
||||
is_closed: bool
|
||||
closed_at: datetime | None
|
||||
last_message_at: datetime | None
|
||||
message_count: int
|
||||
created_at: datetime
|
||||
|
||||
# Unread count for current user (from participant)
|
||||
unread_count: int = 0
|
||||
|
||||
# Other participant info (enriched by API)
|
||||
other_participant: ParticipantInfo | None = None
|
||||
|
||||
# Last message preview
|
||||
last_message_preview: str | None = None
|
||||
|
||||
|
||||
class ConversationDetailResponse(BaseModel):
|
||||
"""Schema for full conversation detail with messages."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
conversation_type: ConversationType
|
||||
subject: str
|
||||
vendor_id: int | None = None
|
||||
is_closed: bool
|
||||
closed_at: datetime | None
|
||||
closed_by_type: ParticipantType | None = None
|
||||
closed_by_id: int | None = None
|
||||
last_message_at: datetime | None
|
||||
message_count: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
# Participants with enriched info
|
||||
participants: list[ParticipantResponse] = []
|
||||
|
||||
# Messages ordered by created_at
|
||||
messages: list[MessageResponse] = []
|
||||
|
||||
# Current user's unread count
|
||||
unread_count: int = 0
|
||||
|
||||
# Vendor info if applicable
|
||||
vendor_name: str | None = None
|
||||
|
||||
|
||||
class ConversationListResponse(BaseModel):
|
||||
"""Schema for paginated conversation list."""
|
||||
|
||||
conversations: list[ConversationSummary]
|
||||
total: int
|
||||
total_unread: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Unread Count Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class UnreadCountResponse(BaseModel):
|
||||
"""Schema for unread message count (for header badge)."""
|
||||
|
||||
total_unread: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Notification Preferences Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class NotificationPreferencesUpdate(BaseModel):
|
||||
"""Schema for updating notification preferences."""
|
||||
|
||||
email_notifications: bool | None = None
|
||||
muted: bool | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Conversation Action Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class CloseConversationResponse(BaseModel):
|
||||
"""Response after closing a conversation."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
conversation_id: int
|
||||
|
||||
|
||||
class ReopenConversationResponse(BaseModel):
|
||||
"""Response after reopening a conversation."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
conversation_id: int
|
||||
|
||||
|
||||
class MarkReadResponse(BaseModel):
|
||||
"""Response after marking conversation as read."""
|
||||
|
||||
success: bool
|
||||
conversation_id: int
|
||||
unread_count: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Recipient Selection Schemas (for compose modal)
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class RecipientOption(BaseModel):
|
||||
"""Schema for a selectable recipient in compose modal."""
|
||||
|
||||
id: int
|
||||
type: ParticipantType
|
||||
name: str
|
||||
email: str | None = None
|
||||
vendor_id: int | None = None # For vendor users
|
||||
vendor_name: str | None = None
|
||||
|
||||
|
||||
class RecipientListResponse(BaseModel):
|
||||
"""Schema for list of available recipients."""
|
||||
|
||||
recipients: list[RecipientOption]
|
||||
total: int
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Admin-specific Schemas
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class AdminConversationSummary(ConversationSummary):
|
||||
"""Extended conversation summary with vendor info for admin views."""
|
||||
|
||||
vendor_name: str | None = None
|
||||
vendor_code: str | None = None
|
||||
|
||||
|
||||
class AdminConversationListResponse(BaseModel):
|
||||
"""Schema for admin conversation list with vendor info."""
|
||||
|
||||
conversations: list[AdminConversationSummary]
|
||||
total: int
|
||||
total_unread: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
class AdminMessageStats(BaseModel):
|
||||
"""Messaging statistics for admin dashboard."""
|
||||
|
||||
total_conversations: int = 0
|
||||
open_conversations: int = 0
|
||||
closed_conversations: int = 0
|
||||
total_messages: int = 0
|
||||
|
||||
# By type
|
||||
admin_vendor_conversations: int = 0
|
||||
vendor_customer_conversations: int = 0
|
||||
admin_customer_conversations: int = 0
|
||||
|
||||
# Unread
|
||||
unread_admin: int = 0
|
||||
from app.modules.messaging.schemas.message import (
|
||||
# Attachment schemas
|
||||
AttachmentResponse,
|
||||
# Message schemas
|
||||
MessageCreate,
|
||||
MessageResponse,
|
||||
# Participant schemas
|
||||
ParticipantInfo,
|
||||
ParticipantResponse,
|
||||
# Conversation schemas
|
||||
ConversationCreate,
|
||||
ConversationSummary,
|
||||
ConversationDetailResponse,
|
||||
ConversationListResponse,
|
||||
ConversationResponse,
|
||||
# Unread count
|
||||
UnreadCountResponse,
|
||||
# Notification preferences
|
||||
NotificationPreferencesUpdate,
|
||||
# Conversation actions
|
||||
CloseConversationResponse,
|
||||
ReopenConversationResponse,
|
||||
MarkReadResponse,
|
||||
# Recipient selection
|
||||
RecipientOption,
|
||||
RecipientListResponse,
|
||||
# Admin schemas
|
||||
AdminConversationSummary,
|
||||
AdminConversationListResponse,
|
||||
AdminMessageStats,
|
||||
)
|
||||
|
||||
# Re-export enums from models for backward compatibility
|
||||
from app.modules.messaging.models.message import ConversationType, ParticipantType
|
||||
|
||||
__all__ = [
|
||||
# Attachment schemas
|
||||
"AttachmentResponse",
|
||||
# Message schemas
|
||||
"MessageCreate",
|
||||
"MessageResponse",
|
||||
# Participant schemas
|
||||
"ParticipantInfo",
|
||||
"ParticipantResponse",
|
||||
# Conversation schemas
|
||||
"ConversationCreate",
|
||||
"ConversationSummary",
|
||||
"ConversationDetailResponse",
|
||||
"ConversationListResponse",
|
||||
"ConversationResponse",
|
||||
# Unread count
|
||||
"UnreadCountResponse",
|
||||
# Notification preferences
|
||||
"NotificationPreferencesUpdate",
|
||||
# Conversation actions
|
||||
"CloseConversationResponse",
|
||||
"ReopenConversationResponse",
|
||||
"MarkReadResponse",
|
||||
# Recipient selection
|
||||
"RecipientOption",
|
||||
"RecipientListResponse",
|
||||
# Admin schemas
|
||||
"AdminConversationSummary",
|
||||
"AdminConversationListResponse",
|
||||
"AdminMessageStats",
|
||||
# Enums
|
||||
"ConversationType",
|
||||
"ParticipantType",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user