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:
@@ -2,27 +2,106 @@
|
||||
"""
|
||||
Messaging module Pydantic schemas.
|
||||
|
||||
Re-exports messaging-related schemas from their source locations.
|
||||
This module contains the canonical implementations of messaging-related schemas.
|
||||
"""
|
||||
|
||||
from models.schema.message import (
|
||||
ConversationResponse,
|
||||
ConversationListResponse,
|
||||
MessageResponse,
|
||||
MessageCreate,
|
||||
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,
|
||||
)
|
||||
from models.schema.notification import (
|
||||
|
||||
from app.modules.messaging.schemas.notification import (
|
||||
# Response schemas
|
||||
MessageResponse as NotificationMessageResponse,
|
||||
UnreadCountResponse as NotificationUnreadCountResponse,
|
||||
# Notification schemas
|
||||
NotificationResponse,
|
||||
NotificationListResponse,
|
||||
# Settings schemas
|
||||
NotificationSettingsResponse,
|
||||
NotificationSettingsUpdate,
|
||||
# Template schemas
|
||||
NotificationTemplateResponse,
|
||||
NotificationTemplateListResponse,
|
||||
NotificationTemplateUpdate,
|
||||
# Test notification
|
||||
TestNotificationRequest,
|
||||
# Alert statistics
|
||||
AlertStatisticsResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ConversationResponse",
|
||||
"ConversationListResponse",
|
||||
"MessageResponse",
|
||||
"MessageCreate",
|
||||
# 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",
|
||||
# Notification response schemas
|
||||
"NotificationMessageResponse",
|
||||
"NotificationUnreadCountResponse",
|
||||
# Notification schemas
|
||||
"NotificationResponse",
|
||||
"NotificationListResponse",
|
||||
# Settings schemas
|
||||
"NotificationSettingsResponse",
|
||||
"NotificationSettingsUpdate",
|
||||
# Template schemas
|
||||
"NotificationTemplateResponse",
|
||||
"NotificationTemplateListResponse",
|
||||
"NotificationTemplateUpdate",
|
||||
# Test notification
|
||||
"TestNotificationRequest",
|
||||
# Alert statistics
|
||||
"AlertStatisticsResponse",
|
||||
]
|
||||
|
||||
312
app/modules/messaging/schemas/message.py
Normal file
312
app/modules/messaging/schemas/message.py
Normal file
@@ -0,0 +1,312 @@
|
||||
# app/modules/messaging/schemas/message.py
|
||||
"""
|
||||
Pydantic schemas for the messaging system.
|
||||
|
||||
Supports three communication channels:
|
||||
- Admin <-> Vendor
|
||||
- Vendor <-> Customer
|
||||
- Admin <-> Customer
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.modules.messaging.models.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
|
||||
|
||||
|
||||
# Backward compatibility alias
|
||||
ConversationResponse = ConversationDetailResponse
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 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
|
||||
152
app/modules/messaging/schemas/notification.py
Normal file
152
app/modules/messaging/schemas/notification.py
Normal file
@@ -0,0 +1,152 @@
|
||||
# app/modules/messaging/schemas/notification.py
|
||||
"""
|
||||
Notification Pydantic schemas for API validation and responses.
|
||||
|
||||
This module provides schemas for:
|
||||
- Vendor notifications (list, read, delete)
|
||||
- Notification settings management
|
||||
- Notification email templates
|
||||
- Unread counts and statistics
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# ============================================================================
|
||||
# SHARED RESPONSE SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
"""Generic message response for simple operations."""
|
||||
|
||||
message: str
|
||||
|
||||
|
||||
class UnreadCountResponse(BaseModel):
|
||||
"""Response for unread notification count."""
|
||||
|
||||
unread_count: int
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# NOTIFICATION SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class NotificationResponse(BaseModel):
|
||||
"""Single notification response."""
|
||||
|
||||
id: int
|
||||
type: str
|
||||
title: str
|
||||
message: str
|
||||
is_read: bool
|
||||
read_at: datetime | None = None
|
||||
priority: str = "normal"
|
||||
action_url: str | None = None
|
||||
metadata: dict[str, Any] | None = None
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class NotificationListResponse(BaseModel):
|
||||
"""Paginated list of notifications."""
|
||||
|
||||
notifications: list[NotificationResponse] = []
|
||||
total: int = 0
|
||||
unread_count: int = 0
|
||||
message: str | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# NOTIFICATION SETTINGS SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class NotificationSettingsResponse(BaseModel):
|
||||
"""Notification preferences response."""
|
||||
|
||||
email_notifications: bool = True
|
||||
in_app_notifications: bool = True
|
||||
notification_types: dict[str, bool] = Field(default_factory=dict)
|
||||
message: str | None = None
|
||||
|
||||
|
||||
class NotificationSettingsUpdate(BaseModel):
|
||||
"""Request model for updating notification settings."""
|
||||
|
||||
email_notifications: bool | None = None
|
||||
in_app_notifications: bool | None = None
|
||||
notification_types: dict[str, bool] | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# NOTIFICATION TEMPLATE SCHEMAS
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class NotificationTemplateResponse(BaseModel):
|
||||
"""Single notification template response."""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
type: str
|
||||
subject: str
|
||||
body_html: str | None = None
|
||||
body_text: str | None = None
|
||||
variables: list[str] = Field(default_factory=list)
|
||||
is_active: bool = True
|
||||
created_at: datetime
|
||||
updated_at: datetime | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class NotificationTemplateListResponse(BaseModel):
|
||||
"""List of notification templates."""
|
||||
|
||||
templates: list[NotificationTemplateResponse] = []
|
||||
message: str | None = None
|
||||
|
||||
|
||||
class NotificationTemplateUpdate(BaseModel):
|
||||
"""Request model for updating notification template."""
|
||||
|
||||
subject: str | None = Field(None, max_length=200)
|
||||
body_html: str | None = None
|
||||
body_text: str | None = None
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# TEST NOTIFICATION SCHEMA
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class TestNotificationRequest(BaseModel):
|
||||
"""Request model for sending test notification."""
|
||||
|
||||
template_id: int | None = Field(None, description="Template to use")
|
||||
email: str | None = Field(None, description="Override recipient email")
|
||||
notification_type: str = Field(
|
||||
default="test", description="Type of notification to send"
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ADMIN ALERT STATISTICS SCHEMA
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class AlertStatisticsResponse(BaseModel):
|
||||
"""Response for alert statistics."""
|
||||
|
||||
total_alerts: int = 0
|
||||
active_alerts: int = 0
|
||||
critical_alerts: int = 0
|
||||
resolved_today: int = 0
|
||||
Reference in New Issue
Block a user