Files
Task_Reporter/scripts/migrate_orphan_files.py
egg 44822a561a feat: Improve file display, timezone handling, and LOT management
Changes:
- Fix datetime serialization with UTC 'Z' suffix for correct timezone display
- Add PDF upload support with extension fallback for MIME detection
- Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection
- Add file message components (FileMessage, ImageLightbox, UploadPreview)
- Add multi-file upload support with progress tracking
- Link uploaded files to chat messages via message_id
- Include file attachments in AI report generation
- Update specs for file-storage, realtime-messaging, and ai-report-generation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 12:39:15 +08:00

135 lines
4.2 KiB
Python

"""Migration script to create messages for existing files without message_id
This script:
1. Finds all files in tr_room_files that have no message_id
2. Creates an associated message (image_ref or file_ref) for each
3. Updates the file record with the new message_id
Run from project root:
python scripts/migrate_orphan_files.py
"""
import sys
import os
# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import get_settings
from app.modules.file_storage.models import RoomFile
from app.modules.realtime.models import Message, MessageType
from app.modules.file_storage.services.minio_service import generate_presigned_url
settings = get_settings()
def get_db_session():
"""Create database session"""
engine = create_engine(settings.DATABASE_URL)
Session = sessionmaker(bind=engine)
return Session()
def migrate_orphan_files():
"""Migrate orphan files by creating associated messages"""
db = get_db_session()
try:
# Find files without message_id
orphan_files = db.query(RoomFile).filter(
RoomFile.message_id.is_(None),
RoomFile.deleted_at.is_(None)
).all()
if not orphan_files:
print("No orphan files found. Nothing to migrate.")
return
print(f"Found {len(orphan_files)} orphan files to migrate.")
migrated = 0
failed = 0
for file in orphan_files:
try:
# Generate presigned URL for file
download_url = generate_presigned_url(
bucket=file.minio_bucket,
object_path=file.minio_object_path,
expiry_seconds=3600
)
# Determine message type
if file.file_type == "image":
msg_type = MessageType.IMAGE_REF
content = f"[Image] {file.filename}"
else:
msg_type = MessageType.FILE_REF
content = f"[File] {file.filename}"
# Get next sequence number for the room
max_seq = db.query(Message.sequence_number).filter(
Message.room_id == file.room_id
).order_by(Message.sequence_number.desc()).first()
next_seq = (max_seq[0] + 1) if max_seq else 1
# Create metadata
metadata = {
"file_id": file.file_id,
"file_url": download_url,
"filename": file.filename,
"file_type": file.file_type,
"mime_type": file.mime_type,
"file_size": file.file_size,
}
# Add thumbnail_url for images
if file.file_type == "image":
metadata["thumbnail_url"] = download_url
# Create message
message = Message(
room_id=file.room_id,
sender_id=file.uploader_id,
content=content,
message_type=msg_type,
message_metadata=metadata,
created_at=file.uploaded_at, # Use original upload time
sequence_number=next_seq,
)
db.add(message)
db.flush() # Get the message_id
# Update file with message_id
file.message_id = message.message_id
migrated += 1
print(f" Migrated: {file.filename} -> message {message.message_id}")
except Exception as e:
failed += 1
print(f" Failed: {file.filename} - {e}")
# Commit all changes
db.commit()
print(f"\nMigration complete: {migrated} migrated, {failed} failed")
except Exception as e:
db.rollback()
print(f"Migration failed: {e}")
raise
finally:
db.close()
if __name__ == "__main__":
print("Starting orphan files migration...")
print("=" * 50)
migrate_orphan_files()
print("=" * 50)
print("Done.")