添加了会议标签
parent
f6e1fd6d94
commit
9fb07bb435
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
from fastapi import APIRouter, HTTPException, UploadFile, File, Form, Depends, BackgroundTasks
|
||||
from app.models.models import Meeting, TranscriptSegment, TranscriptionTaskStatus, CreateMeetingRequest, UpdateMeetingRequest, SpeakerTagUpdateRequest, BatchSpeakerTagUpdateRequest, TranscriptUpdateRequest, BatchTranscriptUpdateRequest
|
||||
from app.models.models import Meeting, TranscriptSegment, TranscriptionTaskStatus, CreateMeetingRequest, UpdateMeetingRequest, SpeakerTagUpdateRequest, BatchSpeakerTagUpdateRequest, TranscriptUpdateRequest, BatchTranscriptUpdateRequest, Tag
|
||||
from app.core.database import get_db_connection
|
||||
from app.core.config import BASE_DIR, UPLOAD_DIR, AUDIO_DIR, MARKDOWN_DIR, ALLOWED_EXTENSIONS, ALLOWED_IMAGE_EXTENSIONS, MAX_FILE_SIZE, MAX_IMAGE_SIZE
|
||||
from app.services.qiniu_service import qiniu_service
|
||||
|
|
@ -7,7 +8,7 @@ from app.services.llm_service import LLMService
|
|||
from app.services.async_transcription_service import AsyncTranscriptionService
|
||||
from app.services.async_llm_service import async_llm_service
|
||||
from app.core.auth import get_current_user, get_optional_current_user
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
|
|
@ -27,6 +28,24 @@ transcription_service = AsyncTranscriptionService()
|
|||
class GenerateSummaryRequest(BaseModel):
|
||||
user_prompt: Optional[str] = ""
|
||||
|
||||
def _process_tags(cursor, tag_string: Optional[str]) -> List[Tag]:
|
||||
if not tag_string:
|
||||
return []
|
||||
|
||||
tag_names = [name.strip() for name in tag_string.split(',') if name.strip()]
|
||||
if not tag_names:
|
||||
return []
|
||||
|
||||
# Ensure all tags exist in the 'tags' table
|
||||
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
|
||||
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
|
||||
|
||||
# Fetch the full tag objects
|
||||
format_strings = ', '.join(['%s'] * len(tag_names))
|
||||
cursor.execute(f"SELECT id, name, color FROM tags WHERE name IN ({format_strings})", tuple(tag_names))
|
||||
tags_data = cursor.fetchall()
|
||||
return [Tag(**tag) for tag in tags_data]
|
||||
|
||||
@router.get("/meetings", response_model=list[Meeting])
|
||||
def get_meetings(current_user: dict = Depends(get_current_user), user_id: Optional[int] = None):
|
||||
with get_db_connection() as connection:
|
||||
|
|
@ -34,7 +53,7 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
|
|||
|
||||
base_query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, m.tags,
|
||||
m.user_id as creator_id, u.caption as creator_username
|
||||
FROM meetings m
|
||||
JOIN users u ON m.user_id = u.user_id
|
||||
|
|
@ -67,6 +86,8 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
|
|||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
tags = _process_tags(cursor, meeting.get('tags'))
|
||||
|
||||
meeting_list.append(Meeting(
|
||||
meeting_id=meeting['meeting_id'],
|
||||
title=meeting['title'],
|
||||
|
|
@ -75,7 +96,8 @@ def get_meetings(current_user: dict = Depends(get_current_user), user_id: Option
|
|||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
creator_username=meeting['creator_username'],
|
||||
tags=tags
|
||||
))
|
||||
|
||||
return meeting_list
|
||||
|
|
@ -87,7 +109,7 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
|
|||
|
||||
query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, m.tags,
|
||||
m.user_id as creator_id, u.caption as creator_username,
|
||||
af.file_path as audio_file_path
|
||||
FROM meetings m
|
||||
|
|
@ -112,6 +134,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
|
|||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
tags = _process_tags(cursor, meeting.get('tags'))
|
||||
|
||||
# 关闭游标,避免与转录服务的数据库连接冲突
|
||||
cursor.close()
|
||||
|
||||
|
|
@ -123,7 +147,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
|
|||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
creator_username=meeting['creator_username'],
|
||||
tags=tags
|
||||
)
|
||||
|
||||
# Add audio file path if exists
|
||||
|
|
@ -182,16 +207,24 @@ def create_meeting(meeting_request: CreateMeetingRequest, current_user: dict = D
|
|||
with get_db_connection() as connection:
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
# Process tags
|
||||
if meeting_request.tags:
|
||||
tag_names = [name.strip() for name in meeting_request.tags.split(',') if name.strip()]
|
||||
if tag_names:
|
||||
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
|
||||
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
|
||||
|
||||
# Create meeting
|
||||
meeting_query = '''
|
||||
INSERT INTO meetings (user_id, title, meeting_time, summary,created_at)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
INSERT INTO meetings (user_id, title, meeting_time, summary, tags, created_at)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
'''
|
||||
cursor.execute(meeting_query, (
|
||||
meeting_request.user_id,
|
||||
meeting_request.title,
|
||||
meeting_request.meeting_time,
|
||||
None,
|
||||
meeting_request.tags,
|
||||
datetime.now().isoformat()
|
||||
))
|
||||
|
||||
|
|
@ -221,17 +254,25 @@ def update_meeting(meeting_id: int, meeting_request: UpdateMeetingRequest, curre
|
|||
|
||||
if meeting['user_id'] != current_user['user_id']:
|
||||
raise HTTPException(status_code=403, detail="Permission denied")
|
||||
|
||||
# Process tags
|
||||
if meeting_request.tags:
|
||||
tag_names = [name.strip() for name in meeting_request.tags.split(',') if name.strip()]
|
||||
if tag_names:
|
||||
insert_ignore_query = "INSERT IGNORE INTO tags (name) VALUES (%s)"
|
||||
cursor.executemany(insert_ignore_query, [(name,) for name in tag_names])
|
||||
|
||||
# Update meeting
|
||||
update_query = '''
|
||||
UPDATE meetings
|
||||
SET title = %s, meeting_time = %s, summary = %s
|
||||
SET title = %s, meeting_time = %s, summary = %s, tags = %s
|
||||
WHERE meeting_id = %s
|
||||
'''
|
||||
cursor.execute(update_query, (
|
||||
meeting_request.title,
|
||||
meeting_request.meeting_time,
|
||||
meeting_request.summary,
|
||||
meeting_request.tags,
|
||||
meeting_id
|
||||
))
|
||||
|
||||
|
|
@ -282,7 +323,7 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
|
|||
|
||||
query = '''
|
||||
SELECT
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at,
|
||||
m.meeting_id, m.title, m.meeting_time, m.summary, m.created_at, m.tags,
|
||||
m.user_id as creator_id, u.caption as creator_username,
|
||||
af.file_path as audio_file_path
|
||||
FROM meetings m
|
||||
|
|
@ -308,6 +349,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
|
|||
attendees_data = cursor.fetchall()
|
||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||
|
||||
tags = _process_tags(cursor, meeting.get('tags'))
|
||||
|
||||
# 关闭游标,避免与转录服务的数据库连接冲突
|
||||
cursor.close()
|
||||
|
||||
|
|
@ -319,7 +362,8 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
|
|||
created_at=meeting['created_at'],
|
||||
attendees=attendees,
|
||||
creator_id=meeting['creator_id'],
|
||||
creator_username=meeting['creator_username']
|
||||
creator_username=meeting['creator_username'],
|
||||
tags=tags
|
||||
)
|
||||
|
||||
# Add audio file path if exists
|
||||
|
|
@ -906,4 +950,4 @@ def get_meeting_llm_tasks(meeting_id: int, current_user: dict = Depends(get_curr
|
|||
# }
|
||||
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=500, detail=f"Failed to get latest LLM task: {str(e)}")
|
||||
# raise HTTPException(status_code=500, detail=f"Failed to get latest LLM task: {str(e)}")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from app.core.database import get_db_connection
|
||||
from app.models.models import Tag
|
||||
from typing import List
|
||||
import mysql.connector
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/tags/", response_model=List[Tag])
|
||||
def get_all_tags():
|
||||
"""_summary_
|
||||
获取所有标签
|
||||
"""
|
||||
query = "SELECT id, name, color FROM tags ORDER BY name"
|
||||
try:
|
||||
with get_db_connection() as connection:
|
||||
with connection.cursor(dictionary=True) as cursor:
|
||||
cursor.execute(query)
|
||||
tags = cursor.fetchall()
|
||||
return tags
|
||||
except mysql.connector.Error as err:
|
||||
print(f"Error: {err}")
|
||||
raise HTTPException(status_code=500, detail="Failed to retrieve tags from database.")
|
||||
|
||||
@router.post("/tags/", response_model=Tag)
|
||||
def create_tag(tag_in: Tag):
|
||||
"""_summary_
|
||||
创建一个新标签
|
||||
"""
|
||||
query = "INSERT INTO tags (name, color) VALUES (%s, %s)"
|
||||
try:
|
||||
with get_db_connection() as connection:
|
||||
with connection.cursor(dictionary=True) as cursor:
|
||||
try:
|
||||
cursor.execute(query, (tag_in.name, tag_in.color))
|
||||
connection.commit()
|
||||
tag_id = cursor.lastrowid
|
||||
return {"id": tag_id, "name": tag_in.name, "color": tag_in.color}
|
||||
except mysql.connector.IntegrityError:
|
||||
connection.rollback()
|
||||
raise HTTPException(status_code=400, detail=f"Tag '{tag_in.name}' already exists.")
|
||||
except mysql.connector.Error as err:
|
||||
print(f"Error: {err}")
|
||||
raise HTTPException(status_code=500, detail="Failed to create tag in database.")
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from typing import Optional, Union, List
|
||||
import datetime
|
||||
|
|
@ -51,6 +50,11 @@ class AttendeeInfo(BaseModel):
|
|||
user_id: int
|
||||
caption: str
|
||||
|
||||
class Tag(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
color: str
|
||||
|
||||
class TranscriptionTaskStatus(BaseModel):
|
||||
task_id: str
|
||||
status: str # 'pending', 'processing', 'completed', 'failed'
|
||||
|
|
@ -72,6 +76,7 @@ class Meeting(BaseModel):
|
|||
creator_username: str
|
||||
audio_file_path: Optional[str] = None
|
||||
transcription_status: Optional[TranscriptionTaskStatus] = None
|
||||
tags: Optional[List[Tag]] = []
|
||||
|
||||
class TranscriptSegment(BaseModel):
|
||||
segment_id: int
|
||||
|
|
@ -87,12 +92,14 @@ class CreateMeetingRequest(BaseModel):
|
|||
title: str
|
||||
meeting_time: Optional[datetime.datetime]
|
||||
attendee_ids: list[int]
|
||||
tags: Optional[str] = None
|
||||
|
||||
class UpdateMeetingRequest(BaseModel):
|
||||
title: str
|
||||
meeting_time: Optional[datetime.datetime]
|
||||
summary: Optional[str]
|
||||
attendee_ids: list[int]
|
||||
tags: Optional[str] = None
|
||||
|
||||
class SpeakerTagUpdateRequest(BaseModel):
|
||||
speaker_id: int # 使用原始speaker_id(整数)
|
||||
|
|
@ -110,4 +117,4 @@ class BatchTranscriptUpdateRequest(BaseModel):
|
|||
|
||||
class PasswordChangeRequest(BaseModel):
|
||||
old_password: str
|
||||
new_password: str
|
||||
new_password: str
|
||||
3
main.py
3
main.py
|
|
@ -2,7 +2,7 @@ import uvicorn
|
|||
from fastapi import FastAPI, Request, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from app.api.endpoints import auth, users, meetings
|
||||
from app.api.endpoints import auth, users, meetings, tags
|
||||
from app.core.config import UPLOAD_DIR, API_CONFIG, MAX_FILE_SIZE
|
||||
from app.services.async_llm_service import async_llm_service
|
||||
import os
|
||||
|
|
@ -30,6 +30,7 @@ if UPLOAD_DIR.exists():
|
|||
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
|
||||
app.include_router(users.router, prefix="/api", tags=["Users"])
|
||||
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
|
||||
app.include_router(tags.router, prefix="/api", tags=["Tags"])
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
|
|
|
|||
Loading…
Reference in New Issue