fix(lint): auto-fix ruff violations and tune lint rules
- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,7 @@ def __getattr__(name: str):
|
||||
from app.modules.messaging.definition import messaging_module
|
||||
|
||||
return messaging_module
|
||||
elif name == "get_messaging_module_with_routers":
|
||||
if name == "get_messaging_module_with_routers":
|
||||
from app.modules.messaging.definition import get_messaging_module_with_routers
|
||||
|
||||
return get_messaging_module_with_routers
|
||||
|
||||
@@ -6,7 +6,12 @@ Defines the messaging module including its features, menu items,
|
||||
route configurations, and self-contained module settings.
|
||||
"""
|
||||
|
||||
from app.modules.base import MenuItemDefinition, MenuSectionDefinition, ModuleDefinition, PermissionDefinition
|
||||
from app.modules.base import (
|
||||
MenuItemDefinition,
|
||||
MenuSectionDefinition,
|
||||
ModuleDefinition,
|
||||
PermissionDefinition,
|
||||
)
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
|
||||
@@ -26,7 +31,9 @@ def _get_store_router():
|
||||
|
||||
def _get_feature_provider():
|
||||
"""Lazy import of feature provider to avoid circular imports."""
|
||||
from app.modules.messaging.services.messaging_features import messaging_feature_provider
|
||||
from app.modules.messaging.services.messaging_features import (
|
||||
messaging_feature_provider,
|
||||
)
|
||||
|
||||
return messaging_feature_provider
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ Revision ID: messaging_001
|
||||
Revises: cart_001
|
||||
Create Date: 2026-02-07
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "messaging_001"
|
||||
down_revision = "cart_001"
|
||||
branch_labels = None
|
||||
|
||||
@@ -8,6 +8,13 @@ This module contains the canonical implementations of messaging-related models:
|
||||
- Email templates and settings: Email system
|
||||
"""
|
||||
|
||||
from app.modules.messaging.models.admin_notification import AdminNotification
|
||||
from app.modules.messaging.models.email import (
|
||||
EmailCategory,
|
||||
EmailLog,
|
||||
EmailStatus,
|
||||
EmailTemplate,
|
||||
)
|
||||
from app.modules.messaging.models.message import (
|
||||
Conversation,
|
||||
ConversationParticipant,
|
||||
@@ -16,16 +23,9 @@ from app.modules.messaging.models.message import (
|
||||
MessageAttachment,
|
||||
ParticipantType,
|
||||
)
|
||||
from app.modules.messaging.models.admin_notification import AdminNotification
|
||||
from app.modules.messaging.models.email import (
|
||||
EmailCategory,
|
||||
EmailLog,
|
||||
EmailStatus,
|
||||
EmailTemplate,
|
||||
)
|
||||
from app.modules.messaging.models.store_email_settings import (
|
||||
EmailProvider,
|
||||
PREMIUM_EMAIL_PROVIDERS,
|
||||
EmailProvider,
|
||||
StoreEmailSettings,
|
||||
)
|
||||
from app.modules.messaging.models.store_email_template import StoreEmailTemplate
|
||||
|
||||
@@ -6,12 +6,12 @@ This model handles admin-specific notifications for system alerts and warnings.
|
||||
"""
|
||||
|
||||
from sqlalchemy import (
|
||||
JSON,
|
||||
Boolean,
|
||||
Column,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
JSON,
|
||||
String,
|
||||
Text,
|
||||
)
|
||||
|
||||
@@ -12,7 +12,6 @@ involving customers.
|
||||
"""
|
||||
|
||||
import enum
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
|
||||
@@ -22,13 +22,13 @@ def __getattr__(name: str):
|
||||
if name == "admin_router":
|
||||
from app.modules.messaging.routes.admin import admin_router
|
||||
return admin_router
|
||||
elif name == "admin_notifications_router":
|
||||
if name == "admin_notifications_router":
|
||||
from app.modules.messaging.routes.admin import admin_notifications_router
|
||||
return admin_notifications_router
|
||||
elif name == "store_router":
|
||||
if name == "store_router":
|
||||
from app.modules.messaging.routes.store import store_router
|
||||
return store_router
|
||||
elif name == "store_notifications_router":
|
||||
if name == "store_notifications_router":
|
||||
from app.modules.messaging.routes.store import store_notifications_router
|
||||
return store_notifications_router
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
@@ -17,8 +17,8 @@ Storefront routes:
|
||||
"""
|
||||
|
||||
from app.modules.messaging.routes.api.admin import admin_router
|
||||
from app.modules.messaging.routes.api.storefront import router as storefront_router
|
||||
from app.modules.messaging.routes.api.store import store_router
|
||||
from app.modules.messaging.routes.api.storefront import router as storefront_router
|
||||
|
||||
# Tag for OpenAPI documentation
|
||||
STOREFRONT_TAG = "Messages (Storefront)"
|
||||
|
||||
@@ -13,9 +13,9 @@ from fastapi import APIRouter, Depends
|
||||
from app.api.deps import require_module_access
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
from .admin_email_templates import admin_email_templates_router
|
||||
from .admin_messages import admin_messages_router
|
||||
from .admin_notifications import admin_notifications_router
|
||||
from .admin_email_templates import admin_email_templates_router
|
||||
|
||||
admin_router = APIRouter(
|
||||
dependencies=[Depends(require_module_access("messaging", FrontendType.ADMIN))],
|
||||
|
||||
@@ -239,11 +239,10 @@ def send_test_email(
|
||||
"success": True,
|
||||
"message": f"Test email sent to {test_data.to_email}",
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": email_log.error_message or "Failed to send email",
|
||||
}
|
||||
return {
|
||||
"success": False,
|
||||
"message": email_log.error_message or "Failed to send email",
|
||||
}
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to send test email: {e}")
|
||||
return {
|
||||
|
||||
@@ -25,8 +25,6 @@ from app.modules.messaging.exceptions import (
|
||||
InvalidRecipientTypeException,
|
||||
MessageAttachmentException,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import message_attachment_service
|
||||
from app.modules.messaging.services.messaging_service import messaging_service
|
||||
from app.modules.messaging.models import ConversationType, ParticipantType
|
||||
from app.modules.messaging.schemas import (
|
||||
AdminConversationListResponse,
|
||||
@@ -36,7 +34,6 @@ from app.modules.messaging.schemas import (
|
||||
ConversationCreate,
|
||||
ConversationDetailResponse,
|
||||
MarkReadResponse,
|
||||
MessageCreate,
|
||||
MessageResponse,
|
||||
NotificationPreferencesUpdate,
|
||||
ParticipantInfo,
|
||||
@@ -46,6 +43,10 @@ from app.modules.messaging.schemas import (
|
||||
ReopenConversationResponse,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import (
|
||||
message_attachment_service,
|
||||
)
|
||||
from app.modules.messaging.services.messaging_service import messaging_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
admin_messages_router = APIRouter(prefix="/messages")
|
||||
|
||||
@@ -15,11 +15,15 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_admin_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.messaging.schemas import (
|
||||
AlertStatisticsResponse,
|
||||
MessageResponse,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from app.modules.messaging.services.admin_notification_service import (
|
||||
admin_notification_service,
|
||||
platform_alert_service,
|
||||
)
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.tenancy.schemas.admin import (
|
||||
AdminNotificationCreate,
|
||||
AdminNotificationListResponse,
|
||||
@@ -29,11 +33,7 @@ from app.modules.tenancy.schemas.admin import (
|
||||
PlatformAlertResolve,
|
||||
PlatformAlertResponse,
|
||||
)
|
||||
from app.modules.messaging.schemas import (
|
||||
AlertStatisticsResponse,
|
||||
MessageResponse,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
admin_notifications_router = APIRouter(prefix="/notifications")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,10 +14,10 @@ from fastapi import APIRouter, Depends
|
||||
from app.api.deps import require_module_access
|
||||
from app.modules.enums import FrontendType
|
||||
|
||||
from .store_messages import store_messages_router
|
||||
from .store_notifications import store_notifications_router
|
||||
from .store_email_settings import store_email_settings_router
|
||||
from .store_email_templates import store_email_templates_router
|
||||
from .store_messages import store_messages_router
|
||||
from .store_notifications import store_notifications_router
|
||||
|
||||
store_router = APIRouter(
|
||||
dependencies=[Depends(require_module_access("messaging", FrontendType.STORE))],
|
||||
|
||||
@@ -20,8 +20,10 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_store_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.cms.services.store_email_settings_service import store_email_settings_service
|
||||
from app.modules.billing.services.subscription_service import subscription_service
|
||||
from app.modules.cms.services.store_email_settings_service import (
|
||||
store_email_settings_service,
|
||||
)
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
store_email_settings_router = APIRouter(prefix="/email-settings")
|
||||
|
||||
@@ -236,11 +236,10 @@ def send_test_email(
|
||||
"success": True,
|
||||
"message": f"Test email sent to {test_data.to_email}",
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"success": False,
|
||||
"message": email_log.error_message or "Failed to send email",
|
||||
}
|
||||
return {
|
||||
"success": False,
|
||||
"message": email_log.error_message or "Failed to send email",
|
||||
}
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to send test email: {e}")
|
||||
return {
|
||||
|
||||
@@ -27,8 +27,6 @@ from app.modules.messaging.exceptions import (
|
||||
InvalidRecipientTypeException,
|
||||
MessageAttachmentException,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import message_attachment_service
|
||||
from app.modules.messaging.services.messaging_service import messaging_service
|
||||
from app.modules.messaging.models import ConversationType, ParticipantType
|
||||
from app.modules.messaging.schemas import (
|
||||
AttachmentResponse,
|
||||
@@ -47,6 +45,10 @@ from app.modules.messaging.schemas import (
|
||||
ReopenConversationResponse,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import (
|
||||
message_attachment_service,
|
||||
)
|
||||
from app.modules.messaging.services.messaging_service import messaging_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
store_messages_router = APIRouter(prefix="/messages")
|
||||
|
||||
@@ -13,8 +13,6 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_store_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.tenancy.services.store_service import store_service
|
||||
from models.schema.auth import UserContext
|
||||
from app.modules.messaging.schemas import (
|
||||
MessageResponse,
|
||||
NotificationListResponse,
|
||||
@@ -25,6 +23,8 @@ from app.modules.messaging.schemas import (
|
||||
TestNotificationRequest,
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from app.modules.tenancy.services.store_service import store_service
|
||||
from models.schema.auth import UserContext
|
||||
|
||||
store_notifications_router = APIRouter(prefix="/notifications")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -18,7 +18,6 @@ Customers can only:
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, File, Form, Path, Query, Request, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
@@ -27,13 +26,12 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_customer_api
|
||||
from app.core.database import get_db
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
from app.modules.messaging.exceptions import (
|
||||
AttachmentNotFoundException,
|
||||
ConversationClosedException,
|
||||
ConversationNotFoundException,
|
||||
)
|
||||
from app.modules.tenancy.exceptions import StoreNotFoundException
|
||||
from app.modules.customers.schemas import CustomerContext
|
||||
from app.modules.messaging.models.message import ConversationType, ParticipantType
|
||||
from app.modules.messaging.schemas import (
|
||||
ConversationDetailResponse,
|
||||
@@ -46,6 +44,7 @@ from app.modules.messaging.services import (
|
||||
message_attachment_service,
|
||||
messaging_service,
|
||||
)
|
||||
from app.modules.tenancy.exceptions import StoreNotFoundException
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -73,7 +72,7 @@ def list_conversations(
|
||||
request: Request,
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
status: Optional[str] = Query(None, pattern="^(open|closed)$"),
|
||||
status: str | None = Query(None, pattern="^(open|closed)$"),
|
||||
customer: CustomerContext = Depends(get_current_customer_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -260,7 +259,7 @@ async def send_message(
|
||||
request: Request,
|
||||
conversation_id: int = Path(..., description="Conversation ID", gt=0),
|
||||
content: str = Form(..., min_length=1, max_length=10000),
|
||||
attachments: List[UploadFile] = File(default=[]),
|
||||
attachments: list[UploadFile] = File(default=[]),
|
||||
customer: CustomerContext = Depends(get_current_customer_api),
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
@@ -513,7 +512,7 @@ def _get_sender_name(message) -> str:
|
||||
if customer:
|
||||
return f"{customer.first_name} {customer.last_name}"
|
||||
return "Customer"
|
||||
elif message.sender_type == ParticipantType.STORE:
|
||||
if message.sender_type == ParticipantType.STORE:
|
||||
from app.modules.tenancy.models import User
|
||||
|
||||
user = (
|
||||
@@ -524,6 +523,6 @@ def _get_sender_name(message) -> str:
|
||||
if user:
|
||||
return f"{user.first_name} {user.last_name}"
|
||||
return "Shop Support"
|
||||
elif message.sender_type == ParticipantType.ADMIN:
|
||||
if message.sender_type == ParticipantType.ADMIN:
|
||||
return "Platform Support"
|
||||
return "Unknown"
|
||||
|
||||
@@ -15,9 +15,9 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_db, require_menu_access
|
||||
from app.modules.core.utils.page_context import get_admin_context
|
||||
from app.templates_config import templates
|
||||
from app.modules.enums import FrontendType
|
||||
from app.modules.tenancy.models import User
|
||||
from app.templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import get_current_store_from_cookie_or_header, get_db
|
||||
from app.modules.core.utils.page_context import get_store_context
|
||||
from app.templates_config import templates
|
||||
from app.modules.tenancy.models import User
|
||||
from app.templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -5,58 +5,6 @@ Messaging module Pydantic schemas.
|
||||
This module contains the canonical implementations of messaging-related schemas.
|
||||
"""
|
||||
|
||||
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 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,
|
||||
)
|
||||
|
||||
# Email template schemas
|
||||
from app.modules.messaging.schemas.email import (
|
||||
EmailPreviewRequest,
|
||||
@@ -73,6 +21,60 @@ from app.modules.messaging.schemas.email import (
|
||||
StoreEmailTemplateResponse,
|
||||
StoreEmailTemplateUpdate,
|
||||
)
|
||||
from app.modules.messaging.schemas.message import (
|
||||
AdminConversationListResponse,
|
||||
# Admin schemas
|
||||
AdminConversationSummary,
|
||||
AdminMessageStats,
|
||||
# Attachment schemas
|
||||
AttachmentResponse,
|
||||
# Conversation actions
|
||||
CloseConversationResponse,
|
||||
# Conversation schemas
|
||||
ConversationCreate,
|
||||
ConversationDetailResponse,
|
||||
ConversationListResponse,
|
||||
ConversationResponse,
|
||||
ConversationSummary,
|
||||
MarkReadResponse,
|
||||
# Message schemas
|
||||
MessageCreate,
|
||||
MessageResponse,
|
||||
# Notification preferences
|
||||
NotificationPreferencesUpdate,
|
||||
# Participant schemas
|
||||
ParticipantInfo,
|
||||
ParticipantResponse,
|
||||
RecipientListResponse,
|
||||
# Recipient selection
|
||||
RecipientOption,
|
||||
ReopenConversationResponse,
|
||||
# Unread count
|
||||
UnreadCountResponse,
|
||||
)
|
||||
from app.modules.messaging.schemas.notification import (
|
||||
# Alert statistics
|
||||
AlertStatisticsResponse,
|
||||
NotificationListResponse,
|
||||
# Notification schemas
|
||||
NotificationResponse,
|
||||
# Settings schemas
|
||||
NotificationSettingsResponse,
|
||||
NotificationSettingsUpdate,
|
||||
NotificationTemplateListResponse,
|
||||
# Template schemas
|
||||
NotificationTemplateResponse,
|
||||
NotificationTemplateUpdate,
|
||||
# Test notification
|
||||
TestNotificationRequest,
|
||||
)
|
||||
from app.modules.messaging.schemas.notification import (
|
||||
# Response schemas
|
||||
MessageResponse as NotificationMessageResponse,
|
||||
)
|
||||
from app.modules.messaging.schemas.notification import (
|
||||
UnreadCountResponse as NotificationUnreadCountResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# Attachment schemas
|
||||
|
||||
@@ -14,7 +14,6 @@ from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.modules.messaging.models.message import ConversationType, ParticipantType
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Attachment Schemas
|
||||
# ============================================================================
|
||||
@@ -41,10 +40,9 @@ class AttachmentResponse(BaseModel):
|
||||
"""Human-readable file size."""
|
||||
if self.file_size < 1024:
|
||||
return f"{self.file_size} B"
|
||||
elif self.file_size < 1024 * 1024:
|
||||
if self.file_size < 1024 * 1024:
|
||||
return f"{self.file_size / 1024:.1f} KB"
|
||||
else:
|
||||
return f"{self.file_size / 1024 / 1024:.1f} MB"
|
||||
return f"{self.file_size / 1024 / 1024:.1f} MB"
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
@@ -5,64 +5,64 @@ Messaging module services.
|
||||
This module contains the canonical implementations of messaging-related services.
|
||||
"""
|
||||
|
||||
from app.modules.messaging.services.messaging_service import (
|
||||
messaging_service,
|
||||
MessagingService,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import (
|
||||
message_attachment_service,
|
||||
MessageAttachmentService,
|
||||
)
|
||||
from app.modules.messaging.services.admin_notification_service import (
|
||||
admin_notification_service,
|
||||
AdminNotificationService,
|
||||
platform_alert_service,
|
||||
PlatformAlertService,
|
||||
AlertType,
|
||||
# Constants
|
||||
NotificationType,
|
||||
PlatformAlertService,
|
||||
Priority,
|
||||
AlertType,
|
||||
Severity,
|
||||
admin_notification_service,
|
||||
platform_alert_service,
|
||||
)
|
||||
from app.modules.messaging.services.email_service import (
|
||||
EmailService,
|
||||
EmailProvider,
|
||||
ResolvedTemplate,
|
||||
BrandingContext,
|
||||
send_email,
|
||||
get_provider,
|
||||
get_platform_provider,
|
||||
get_store_provider,
|
||||
get_platform_email_config,
|
||||
# Provider classes
|
||||
SMTPProvider,
|
||||
SendGridProvider,
|
||||
MailgunProvider,
|
||||
SESProvider,
|
||||
DebugProvider,
|
||||
# Configurable provider classes
|
||||
ConfigurableSMTPProvider,
|
||||
ConfigurableSendGridProvider,
|
||||
ConfigurableMailgunProvider,
|
||||
ConfigurableSESProvider,
|
||||
# Store provider classes
|
||||
StoreSMTPProvider,
|
||||
StoreSendGridProvider,
|
||||
StoreMailgunProvider,
|
||||
StoreSESProvider,
|
||||
PLATFORM_DEFAULT_LANGUAGE,
|
||||
# Constants
|
||||
PLATFORM_NAME,
|
||||
PLATFORM_SUPPORT_EMAIL,
|
||||
PLATFORM_DEFAULT_LANGUAGE,
|
||||
SUPPORTED_LANGUAGES,
|
||||
WHITELABEL_TIERS,
|
||||
POWERED_BY_FOOTER_HTML,
|
||||
POWERED_BY_FOOTER_TEXT,
|
||||
SUPPORTED_LANGUAGES,
|
||||
WHITELABEL_TIERS,
|
||||
BrandingContext,
|
||||
ConfigurableMailgunProvider,
|
||||
ConfigurableSendGridProvider,
|
||||
ConfigurableSESProvider,
|
||||
# Configurable provider classes
|
||||
ConfigurableSMTPProvider,
|
||||
DebugProvider,
|
||||
EmailProvider,
|
||||
EmailService,
|
||||
MailgunProvider,
|
||||
ResolvedTemplate,
|
||||
SendGridProvider,
|
||||
SESProvider,
|
||||
# Provider classes
|
||||
SMTPProvider,
|
||||
StoreMailgunProvider,
|
||||
StoreSendGridProvider,
|
||||
StoreSESProvider,
|
||||
# Store provider classes
|
||||
StoreSMTPProvider,
|
||||
get_platform_email_config,
|
||||
get_platform_provider,
|
||||
get_provider,
|
||||
get_store_provider,
|
||||
send_email,
|
||||
)
|
||||
from app.modules.messaging.services.email_template_service import (
|
||||
EmailTemplateService,
|
||||
TemplateData,
|
||||
StoreOverrideData,
|
||||
TemplateData,
|
||||
)
|
||||
from app.modules.messaging.services.message_attachment_service import (
|
||||
MessageAttachmentService,
|
||||
message_attachment_service,
|
||||
)
|
||||
from app.modules.messaging.services.messaging_service import (
|
||||
MessagingService,
|
||||
messaging_service,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -12,12 +12,15 @@ import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import and_, case, func
|
||||
from sqlalchemy import and_, case
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.modules.messaging.models.admin_notification import AdminNotification
|
||||
from app.modules.tenancy.models import PlatformAlert
|
||||
from app.modules.tenancy.schemas.admin import AdminNotificationCreate, PlatformAlertCreate
|
||||
from app.modules.tenancy.schemas.admin import (
|
||||
AdminNotificationCreate,
|
||||
PlatformAlertCreate,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -37,12 +37,16 @@ from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from typing import Any
|
||||
|
||||
from jinja2 import Environment, BaseLoader
|
||||
from jinja2 import BaseLoader, Environment
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.config import settings
|
||||
from app.modules.messaging.models import EmailLog, EmailStatus, EmailTemplate
|
||||
from app.modules.messaging.models import StoreEmailTemplate
|
||||
from app.modules.messaging.models import (
|
||||
EmailLog,
|
||||
EmailStatus,
|
||||
EmailTemplate,
|
||||
StoreEmailTemplate,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -118,7 +122,6 @@ class EmailProvider(ABC):
|
||||
Returns:
|
||||
tuple: (success, provider_message_id, error_message)
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SMTPProvider(EmailProvider):
|
||||
@@ -190,7 +193,7 @@ class SendGridProvider(EmailProvider):
|
||||
) -> tuple[bool, str | None, str | None]:
|
||||
try:
|
||||
from sendgrid import SendGridAPIClient
|
||||
from sendgrid.helpers.mail import Mail, Email, To, Content
|
||||
from sendgrid.helpers.mail import Content, Email, Mail, To
|
||||
|
||||
message = Mail(
|
||||
from_email=Email(from_email, from_name),
|
||||
@@ -211,8 +214,7 @@ class SendGridProvider(EmailProvider):
|
||||
if response.status_code in (200, 201, 202):
|
||||
message_id = response.headers.get("X-Message-Id")
|
||||
return True, message_id, None
|
||||
else:
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
|
||||
except ImportError:
|
||||
return False, None, "SendGrid library not installed. Run: pip install sendgrid"
|
||||
@@ -263,8 +265,7 @@ class MailgunProvider(EmailProvider):
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
return True, result.get("id"), None
|
||||
else:
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Mailgun send error: {e}")
|
||||
@@ -519,7 +520,7 @@ class ConfigurableSendGridProvider(EmailProvider):
|
||||
) -> tuple[bool, str | None, str | None]:
|
||||
try:
|
||||
from sendgrid import SendGridAPIClient
|
||||
from sendgrid.helpers.mail import Mail, Email, To, Content
|
||||
from sendgrid.helpers.mail import Content, Email, Mail, To
|
||||
|
||||
message = Mail(
|
||||
from_email=Email(from_email, from_name),
|
||||
@@ -540,8 +541,7 @@ class ConfigurableSendGridProvider(EmailProvider):
|
||||
if response.status_code in (200, 201, 202):
|
||||
message_id = response.headers.get("X-Message-Id")
|
||||
return True, message_id, None
|
||||
else:
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
|
||||
except ImportError:
|
||||
return False, None, "SendGrid library not installed"
|
||||
@@ -595,8 +595,7 @@ class ConfigurableMailgunProvider(EmailProvider):
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
return True, result.get("id"), None
|
||||
else:
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Configurable Mailgun send error: {e}")
|
||||
@@ -765,7 +764,7 @@ class StoreSendGridProvider(EmailProvider):
|
||||
) -> tuple[bool, str | None, str | None]:
|
||||
try:
|
||||
from sendgrid import SendGridAPIClient
|
||||
from sendgrid.helpers.mail import Mail, Email, To, Content
|
||||
from sendgrid.helpers.mail import Content, Email, Mail, To
|
||||
|
||||
message = Mail(
|
||||
from_email=Email(from_email, from_name),
|
||||
@@ -786,8 +785,7 @@ class StoreSendGridProvider(EmailProvider):
|
||||
if response.status_code in (200, 201, 202):
|
||||
message_id = response.headers.get("X-Message-Id")
|
||||
return True, message_id, None
|
||||
else:
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
return False, None, f"SendGrid error: {response.status_code}"
|
||||
|
||||
except ImportError:
|
||||
return False, None, "SendGrid library not installed"
|
||||
@@ -841,8 +839,7 @@ class StoreMailgunProvider(EmailProvider):
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
return True, result.get("id"), None
|
||||
else:
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
return False, None, f"Mailgun error: {response.status_code} - {response.text}"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Store Mailgun send error: {e}")
|
||||
@@ -1038,7 +1035,9 @@ class EmailService:
|
||||
def _get_store_tier(self, store_id: int) -> str | None:
|
||||
"""Get store's subscription tier with caching."""
|
||||
if store_id not in self._store_tier_cache:
|
||||
from app.modules.billing.services.subscription_service import subscription_service
|
||||
from app.modules.billing.services.subscription_service import (
|
||||
subscription_service,
|
||||
)
|
||||
|
||||
tier = subscription_service.get_current_tier(self.db, store_id)
|
||||
self._store_tier_cache[store_id] = tier.value if tier else None
|
||||
@@ -1169,16 +1168,15 @@ class EmailService:
|
||||
store_logo_url=store.get_logo_url(),
|
||||
is_whitelabel=True,
|
||||
)
|
||||
else:
|
||||
# Standard: Wizamart branding with store details
|
||||
return BrandingContext(
|
||||
platform_name=PLATFORM_NAME,
|
||||
platform_logo_url=None, # Use default platform logo
|
||||
support_email=PLATFORM_SUPPORT_EMAIL,
|
||||
store_name=store.name if store else None,
|
||||
store_logo_url=store.get_logo_url() if store else None,
|
||||
is_whitelabel=False,
|
||||
)
|
||||
# Standard: Wizamart branding with store details
|
||||
return BrandingContext(
|
||||
platform_name=PLATFORM_NAME,
|
||||
platform_logo_url=None, # Use default platform logo
|
||||
support_email=PLATFORM_SUPPORT_EMAIL,
|
||||
store_name=store.name if store else None,
|
||||
store_logo_url=store.get_logo_url() if store else None,
|
||||
is_whitelabel=False,
|
||||
)
|
||||
|
||||
def resolve_template(
|
||||
self,
|
||||
|
||||
@@ -24,8 +24,12 @@ from app.exceptions.base import (
|
||||
ResourceNotFoundException,
|
||||
ValidationException,
|
||||
)
|
||||
from app.modules.messaging.models import EmailCategory, EmailLog, EmailTemplate
|
||||
from app.modules.messaging.models import StoreEmailTemplate
|
||||
from app.modules.messaging.models import (
|
||||
EmailCategory,
|
||||
EmailLog,
|
||||
EmailTemplate,
|
||||
StoreEmailTemplate,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -498,7 +502,7 @@ class EmailTemplateService:
|
||||
"body_html": platform_version.body_html,
|
||||
} if platform_version else None,
|
||||
}
|
||||
elif platform_version:
|
||||
if platform_version:
|
||||
return {
|
||||
"code": code,
|
||||
"language": language,
|
||||
@@ -510,8 +514,7 @@ class EmailTemplateService:
|
||||
"variables": self._parse_required_variables(platform_template.required_variables),
|
||||
"platform_template": None,
|
||||
}
|
||||
else:
|
||||
raise ResourceNotFoundException(f"No template found for language: {language}")
|
||||
raise ResourceNotFoundException(f"No template found for language: {language}")
|
||||
|
||||
def create_or_update_store_override(
|
||||
self,
|
||||
|
||||
@@ -156,9 +156,10 @@ class MessageAttachmentService:
|
||||
def _create_thumbnail(self, content: bytes, original_path: str) -> dict:
|
||||
"""Create thumbnail for image attachments."""
|
||||
try:
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
from PIL import Image
|
||||
|
||||
img = Image.open(io.BytesIO(content))
|
||||
width, height = img.size
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from app.modules.contracts.features import (
|
||||
FeatureDeclaration,
|
||||
FeatureProviderProtocol,
|
||||
FeatureScope,
|
||||
FeatureType,
|
||||
FeatureUsage,
|
||||
|
||||
@@ -17,6 +17,7 @@ from typing import Any
|
||||
from sqlalchemy import and_, func, or_
|
||||
from sqlalchemy.orm import Session, joinedload
|
||||
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from app.modules.messaging.models.message import (
|
||||
Conversation,
|
||||
ConversationParticipant,
|
||||
@@ -25,7 +26,6 @@ from app.modules.messaging.models.message import (
|
||||
MessageAttachment,
|
||||
ParticipantType,
|
||||
)
|
||||
from app.modules.customers.models.customer import Customer
|
||||
from app.modules.tenancy.models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -6,14 +6,18 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.messaging.models import (
|
||||
EmailCategory,
|
||||
EmailLog,
|
||||
EmailStatus,
|
||||
EmailTemplate,
|
||||
)
|
||||
from app.modules.messaging.services.email_service import (
|
||||
DebugProvider,
|
||||
EmailProvider,
|
||||
EmailService,
|
||||
SMTPProvider,
|
||||
get_provider,
|
||||
)
|
||||
from app.modules.messaging.models import EmailCategory, EmailLog, EmailStatus, EmailTemplate
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
||||
@@ -11,7 +11,6 @@ import pytest
|
||||
from fastapi import UploadFile
|
||||
|
||||
from app.modules.messaging.services.message_attachment_service import (
|
||||
ALLOWED_MIME_TYPES,
|
||||
DEFAULT_MAX_FILE_SIZE_MB,
|
||||
IMAGE_MIME_TYPES,
|
||||
MessageAttachmentService,
|
||||
|
||||
@@ -3,14 +3,12 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from app.modules.messaging.services.messaging_service import MessagingService
|
||||
from app.modules.messaging.models import (
|
||||
Conversation,
|
||||
ConversationParticipant,
|
||||
ConversationType,
|
||||
Message,
|
||||
ParticipantType,
|
||||
)
|
||||
from app.modules.messaging.services.messaging_service import MessagingService
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
Reference in New Issue
Block a user