teachingAssistant / src /application /dtos /audio_upload_dto.py
Michael Hu
feat(logging): add detailed logging for audio upload and configuration handling
8023ba2
"""Audio Upload Data Transfer Object"""
from dataclasses import dataclass
from typing import Optional
import mimetypes
import os
@dataclass
class AudioUploadDto:
"""DTO for file upload data
Handles audio file upload information including filename,
content, and content type validation.
"""
filename: str
content: bytes
content_type: str
size: Optional[int] = None
def __post_init__(self):
"""Validate the DTO after initialization"""
# Add logging for debugging mp3 validation issues
import logging
logger = logging.getLogger(__name__)
logger.info(f"Validating AudioUploadDto - Filename: {self.filename}")
logger.info(f"Content-Type: {self.content_type}")
logger.info(f"File size: {len(self.content)} bytes")
# Check file extension and MIME type mapping
_, ext = os.path.splitext(self.filename.lower())
logger.info(f"File extension: {ext}")
content_type_map = {
'.wav': 'audio/wav',
'.mp3': 'audio/mpeg',
'.m4a': 'audio/mp4',
'.flac': 'audio/flac',
'.ogg': 'audio/ogg'
}
expected_content_type = content_type_map.get(ext)
logger.info(f"Expected content type for {ext}: {expected_content_type}")
logger.info(f"Actual content type: {self.content_type}")
# Check mimetypes.guess_type result
guessed_type = mimetypes.guess_type(self.filename)[0]
logger.info(f"mimetypes.guess_type result: {guessed_type}")
self._validate()
if self.size is None:
self.size = len(self.content)
def _validate(self):
"""Validate audio upload data"""
if not self.filename:
raise ValueError("Filename cannot be empty")
if not self.content:
raise ValueError("Audio content cannot be empty")
if not self.content_type:
raise ValueError("Content type cannot be empty")
# Validate file extension
_, ext = os.path.splitext(self.filename.lower())
supported_extensions = ['.wav', '.mp3', '.m4a', '.flac', '.ogg']
if ext not in supported_extensions:
raise ValueError(f"Unsupported file extension: {ext}. Supported: {supported_extensions}")
# Validate content type
expected_content_type = mimetypes.guess_type(self.filename)[0]
if expected_content_type and not self.content_type.startswith('audio/'):
raise ValueError(f"Invalid content type: {self.content_type}. Expected audio/* type")
# Validate file size (max 100MB)
max_size = 100 * 1024 * 1024 # 100MB
if len(self.content) > max_size:
raise ValueError(f"File too large: {len(self.content)} bytes. Maximum: {max_size} bytes")
# Validate minimum file size (at least 1KB)
min_size = 1024 # 1KB
if len(self.content) < min_size:
raise ValueError(f"File too small: {len(self.content)} bytes. Minimum: {min_size} bytes")
@property
def file_extension(self) -> str:
"""Get the file extension"""
return os.path.splitext(self.filename.lower())[1]
@property
def base_filename(self) -> str:
"""Get filename without extension"""
return os.path.splitext(self.filename)[0]
def to_dict(self) -> dict:
"""Convert to dictionary representation"""
return {
'filename': self.filename,
'content_type': self.content_type,
'size': self.size,
'file_extension': self.file_extension
}