feat: add translation billing stats and remove Export/Settings pages
- Add TranslationLog model to track translation API usage per task - Integrate Dify API actual price (total_price) into translation stats - Display translation statistics in admin dashboard with per-task costs - Remove unused Export and Settings pages to simplify frontend - Add GET /api/v2/admin/translation-stats endpoint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ from app.models.user import User
|
||||
from app.models.task import Task, TaskFile, TaskStatus
|
||||
from app.models.session import Session
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.translation_log import TranslationLog
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
@@ -17,4 +18,5 @@ __all__ = [
|
||||
"TaskStatus",
|
||||
"Session",
|
||||
"AuditLog",
|
||||
"TranslationLog",
|
||||
]
|
||||
|
||||
87
backend/app/models/translation_log.py
Normal file
87
backend/app/models/translation_log.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Tool_OCR - Translation Log Model
|
||||
Tracks translation usage statistics for billing and monitoring
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Float, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from datetime import datetime
|
||||
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class TranslationLog(Base):
|
||||
"""
|
||||
Translation log model for tracking API usage and costs.
|
||||
|
||||
Each record represents a single translation job completion,
|
||||
storing token usage and estimated costs for billing purposes.
|
||||
"""
|
||||
|
||||
__tablename__ = "tool_ocr_translation_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||
user_id = Column(Integer, ForeignKey("tool_ocr_users.id", ondelete="CASCADE"),
|
||||
nullable=False, index=True,
|
||||
comment="Foreign key to users table")
|
||||
task_id = Column(String(255), nullable=False, index=True,
|
||||
comment="Task UUID that was translated")
|
||||
target_lang = Column(String(10), nullable=False, index=True,
|
||||
comment="Target language code (e.g., 'en', 'ja', 'zh-TW')")
|
||||
source_lang = Column(String(10), nullable=True,
|
||||
comment="Source language code (or 'auto')")
|
||||
|
||||
# Token usage statistics
|
||||
input_tokens = Column(Integer, default=0, nullable=False,
|
||||
comment="Number of input tokens used")
|
||||
output_tokens = Column(Integer, default=0, nullable=False,
|
||||
comment="Number of output tokens generated")
|
||||
total_tokens = Column(Integer, default=0, nullable=False,
|
||||
comment="Total tokens (input + output)")
|
||||
|
||||
# Translation statistics
|
||||
total_elements = Column(Integer, default=0, nullable=False,
|
||||
comment="Total elements in document")
|
||||
translated_elements = Column(Integer, default=0, nullable=False,
|
||||
comment="Number of elements translated")
|
||||
total_characters = Column(Integer, default=0, nullable=False,
|
||||
comment="Total characters translated")
|
||||
|
||||
# Cost tracking (estimated based on token pricing)
|
||||
estimated_cost = Column(Float, default=0.0, nullable=False,
|
||||
comment="Estimated cost in USD")
|
||||
|
||||
# Processing info
|
||||
processing_time_seconds = Column(Float, default=0.0, nullable=False,
|
||||
comment="Translation processing time")
|
||||
provider = Column(String(50), default="dify", nullable=False,
|
||||
comment="Translation provider (e.g., 'dify')")
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", back_populates="translation_logs")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TranslationLog(id={self.id}, task_id='{self.task_id}', target_lang='{self.target_lang}', tokens={self.total_tokens})>"
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert translation log to dictionary"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"user_id": self.user_id,
|
||||
"task_id": self.task_id,
|
||||
"target_lang": self.target_lang,
|
||||
"source_lang": self.source_lang,
|
||||
"input_tokens": self.input_tokens,
|
||||
"output_tokens": self.output_tokens,
|
||||
"total_tokens": self.total_tokens,
|
||||
"total_elements": self.total_elements,
|
||||
"translated_elements": self.translated_elements,
|
||||
"total_characters": self.total_characters,
|
||||
"estimated_cost": self.estimated_cost,
|
||||
"processing_time_seconds": self.processing_time_seconds,
|
||||
"provider": self.provider,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None
|
||||
}
|
||||
@@ -33,6 +33,7 @@ class User(Base):
|
||||
tasks = relationship("Task", back_populates="user", cascade="all, delete-orphan")
|
||||
sessions = relationship("Session", back_populates="user", cascade="all, delete-orphan")
|
||||
audit_logs = relationship("AuditLog", back_populates="user")
|
||||
translation_logs = relationship("TranslationLog", back_populates="user", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(id={self.id}, email='{self.email}', display_name='{self.display_name}')>"
|
||||
|
||||
Reference in New Issue
Block a user