Initial MVP for QA asset backend
This commit is contained in:
124
hy_qa_asset_backend/backend/app/services/task_runner.py
Normal file
124
hy_qa_asset_backend/backend/app/services/task_runner.py
Normal file
@@ -0,0 +1,124 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app import models
|
||||
from app.services.ai_cleaner import AICleaner
|
||||
from app.services.feishu_client import FeishuClient
|
||||
from app.services.mock_data_service import create_mock_session
|
||||
from app.services.standard_qa_service import refresh_session_counts
|
||||
from app.utils.time_utils import utc_now
|
||||
|
||||
|
||||
class TaskRunner:
|
||||
def __init__(self) -> None:
|
||||
self.feishu = FeishuClient()
|
||||
self.ai = AICleaner()
|
||||
|
||||
async def run(
|
||||
self,
|
||||
db: Session,
|
||||
*,
|
||||
task_type: models.TaskType = models.TaskType.manual_scan,
|
||||
session_id: int | None = None,
|
||||
reprocess: bool = False,
|
||||
) -> models.TaskRun:
|
||||
task = models.TaskRun(task_type=task_type, task_status=models.TaskStatus.running, started_at=utc_now())
|
||||
db.add(task)
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
try:
|
||||
sessions = self._resolve_sessions(db, session_id=session_id)
|
||||
if not sessions and self.feishu.is_mock_mode and session_id is None:
|
||||
sessions = [create_mock_session(db)]
|
||||
task.sessions_scanned = len(sessions)
|
||||
|
||||
for session in sessions:
|
||||
try:
|
||||
created_count = await self._process_session(db, session, reprocess=reprocess)
|
||||
task.sessions_processed += 1
|
||||
task.qa_created += created_count
|
||||
except Exception as exc: # noqa: BLE001
|
||||
task.qa_failed += 1
|
||||
session.process_status = models.ProcessStatus.failed
|
||||
session.failed_reason = str(exc)
|
||||
task.error_message = str(exc)
|
||||
db.add(session)
|
||||
db.commit()
|
||||
self.feishu.update_session_status(session, session.process_status.value)
|
||||
|
||||
task.task_status = (
|
||||
models.TaskStatus.success
|
||||
if task.qa_failed == 0
|
||||
else models.TaskStatus.partial_success
|
||||
if task.sessions_processed > 0
|
||||
else models.TaskStatus.failed
|
||||
)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
task.task_status = models.TaskStatus.failed
|
||||
task.error_message = str(exc)
|
||||
finally:
|
||||
task.ended_at = utc_now()
|
||||
db.add(task)
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
def _resolve_sessions(self, db: Session, session_id: int | None) -> list[models.FeishuSession]:
|
||||
if session_id is not None:
|
||||
session = db.get(models.FeishuSession, session_id)
|
||||
return [session] if session else []
|
||||
return self.feishu.scan_unprocessed_sessions(db)
|
||||
|
||||
async def _process_session(self, db: Session, session: models.FeishuSession, *, reprocess: bool = False) -> int:
|
||||
if reprocess:
|
||||
db.query(models.StandardQAItem).filter(models.StandardQAItem.session_id == session.id).delete()
|
||||
db.query(models.RawQAItem).filter(models.RawQAItem.session_id == session.id).delete()
|
||||
db.commit()
|
||||
|
||||
session.process_status = models.ProcessStatus.processing
|
||||
session.failed_reason = None
|
||||
db.add(session)
|
||||
db.commit()
|
||||
self.feishu.update_session_status(session, session.process_status.value)
|
||||
|
||||
transcript = self.feishu.fetch_transcript(session)
|
||||
session.source_text = transcript
|
||||
ai_items = await self.ai.clean_transcript(session.id, transcript)
|
||||
|
||||
created: list[models.RawQAItem] = []
|
||||
for index, item in enumerate(ai_items, start=1):
|
||||
raw = models.RawQAItem(
|
||||
session_id=session.id,
|
||||
qa_code=f"{session.session_code}-QA-{index:03d}",
|
||||
raw_question=item["raw_question"],
|
||||
normalized_question=item.get("normalized_question"),
|
||||
raw_answer=item["raw_answer"],
|
||||
normalized_answer=item.get("normalized_answer"),
|
||||
suggested_standard_question=item.get("suggested_standard_question"),
|
||||
suggested_standard_answer=item.get("suggested_standard_answer"),
|
||||
answer_person=item.get("answer_person"),
|
||||
primary_topic=item.get("primary_topic", "其他"),
|
||||
problem_tags=item.get("problem_tags", []),
|
||||
course_stage=item.get("course_stage", "不确定"),
|
||||
audience_tags=item.get("audience_tags", []),
|
||||
emotion_intensity=item.get("emotion_intensity"),
|
||||
risk_level=item.get("risk_level", "medium"),
|
||||
risk_types=item.get("risk_types", []),
|
||||
risk_notes=item.get("risk_notes"),
|
||||
need_desensitization=item.get("need_desensitization", False),
|
||||
desensitization_status=item.get("desensitization_status", "unknown"),
|
||||
review_status=models.ReviewStatus.pending,
|
||||
review_notes=item.get("review_notes"),
|
||||
suggested_to_standard_qa=item.get("suggested_to_standard_qa", True),
|
||||
source_timestamp=item.get("source_timestamp"),
|
||||
)
|
||||
db.add(raw)
|
||||
created.append(raw)
|
||||
|
||||
session.process_status = models.ProcessStatus.pending_review
|
||||
db.add(session)
|
||||
db.commit()
|
||||
refresh_session_counts(db, session.id)
|
||||
db.commit()
|
||||
self.feishu.write_raw_qa_items(session, created)
|
||||
self.feishu.update_session_status(session, session.process_status.value)
|
||||
return len(created)
|
||||
Reference in New Issue
Block a user