添加了会议标签
parent
f6e1fd6d94
commit
9fb07bb435
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, UploadFile, File, Form, Depends, BackgroundTasks
|
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.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.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
|
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_transcription_service import AsyncTranscriptionService
|
||||||
from app.services.async_llm_service import async_llm_service
|
from app.services.async_llm_service import async_llm_service
|
||||||
from app.core.auth import get_current_user, get_optional_current_user
|
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 datetime import datetime
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
import os
|
import os
|
||||||
|
|
@ -27,6 +28,24 @@ transcription_service = AsyncTranscriptionService()
|
||||||
class GenerateSummaryRequest(BaseModel):
|
class GenerateSummaryRequest(BaseModel):
|
||||||
user_prompt: Optional[str] = ""
|
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])
|
@router.get("/meetings", response_model=list[Meeting])
|
||||||
def get_meetings(current_user: dict = Depends(get_current_user), user_id: Optional[int] = None):
|
def get_meetings(current_user: dict = Depends(get_current_user), user_id: Optional[int] = None):
|
||||||
with get_db_connection() as connection:
|
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 = '''
|
base_query = '''
|
||||||
SELECT
|
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
|
m.user_id as creator_id, u.caption as creator_username
|
||||||
FROM meetings m
|
FROM meetings m
|
||||||
JOIN users u ON m.user_id = u.user_id
|
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_data = cursor.fetchall()
|
||||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
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_list.append(Meeting(
|
||||||
meeting_id=meeting['meeting_id'],
|
meeting_id=meeting['meeting_id'],
|
||||||
title=meeting['title'],
|
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'],
|
created_at=meeting['created_at'],
|
||||||
attendees=attendees,
|
attendees=attendees,
|
||||||
creator_id=meeting['creator_id'],
|
creator_id=meeting['creator_id'],
|
||||||
creator_username=meeting['creator_username']
|
creator_username=meeting['creator_username'],
|
||||||
|
tags=tags
|
||||||
))
|
))
|
||||||
|
|
||||||
return meeting_list
|
return meeting_list
|
||||||
|
|
@ -87,7 +109,7 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
SELECT
|
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,
|
m.user_id as creator_id, u.caption as creator_username,
|
||||||
af.file_path as audio_file_path
|
af.file_path as audio_file_path
|
||||||
FROM meetings m
|
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_data = cursor.fetchall()
|
||||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||||
|
|
||||||
|
tags = _process_tags(cursor, meeting.get('tags'))
|
||||||
|
|
||||||
# 关闭游标,避免与转录服务的数据库连接冲突
|
# 关闭游标,避免与转录服务的数据库连接冲突
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
|
@ -123,7 +147,8 @@ def get_meeting_details(meeting_id: int, current_user: dict = Depends(get_curren
|
||||||
created_at=meeting['created_at'],
|
created_at=meeting['created_at'],
|
||||||
attendees=attendees,
|
attendees=attendees,
|
||||||
creator_id=meeting['creator_id'],
|
creator_id=meeting['creator_id'],
|
||||||
creator_username=meeting['creator_username']
|
creator_username=meeting['creator_username'],
|
||||||
|
tags=tags
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add audio file path if exists
|
# 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:
|
with get_db_connection() as connection:
|
||||||
cursor = connection.cursor(dictionary=True)
|
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
|
# Create meeting
|
||||||
meeting_query = '''
|
meeting_query = '''
|
||||||
INSERT INTO meetings (user_id, title, meeting_time, summary,created_at)
|
INSERT INTO meetings (user_id, title, meeting_time, summary, tags, created_at)
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
VALUES (%s, %s, %s, %s, %s, %s)
|
||||||
'''
|
'''
|
||||||
cursor.execute(meeting_query, (
|
cursor.execute(meeting_query, (
|
||||||
meeting_request.user_id,
|
meeting_request.user_id,
|
||||||
meeting_request.title,
|
meeting_request.title,
|
||||||
meeting_request.meeting_time,
|
meeting_request.meeting_time,
|
||||||
None,
|
None,
|
||||||
|
meeting_request.tags,
|
||||||
datetime.now().isoformat()
|
datetime.now().isoformat()
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
@ -222,16 +255,24 @@ def update_meeting(meeting_id: int, meeting_request: UpdateMeetingRequest, curre
|
||||||
if meeting['user_id'] != current_user['user_id']:
|
if meeting['user_id'] != current_user['user_id']:
|
||||||
raise HTTPException(status_code=403, detail="Permission denied")
|
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 meeting
|
||||||
update_query = '''
|
update_query = '''
|
||||||
UPDATE meetings
|
UPDATE meetings
|
||||||
SET title = %s, meeting_time = %s, summary = %s
|
SET title = %s, meeting_time = %s, summary = %s, tags = %s
|
||||||
WHERE meeting_id = %s
|
WHERE meeting_id = %s
|
||||||
'''
|
'''
|
||||||
cursor.execute(update_query, (
|
cursor.execute(update_query, (
|
||||||
meeting_request.title,
|
meeting_request.title,
|
||||||
meeting_request.meeting_time,
|
meeting_request.meeting_time,
|
||||||
meeting_request.summary,
|
meeting_request.summary,
|
||||||
|
meeting_request.tags,
|
||||||
meeting_id
|
meeting_id
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
@ -282,7 +323,7 @@ def get_meeting_for_edit(meeting_id: int, current_user: dict = Depends(get_curre
|
||||||
|
|
||||||
query = '''
|
query = '''
|
||||||
SELECT
|
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,
|
m.user_id as creator_id, u.caption as creator_username,
|
||||||
af.file_path as audio_file_path
|
af.file_path as audio_file_path
|
||||||
FROM meetings m
|
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_data = cursor.fetchall()
|
||||||
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
attendees = [{'user_id': row['user_id'], 'caption': row['caption']} for row in attendees_data]
|
||||||
|
|
||||||
|
tags = _process_tags(cursor, meeting.get('tags'))
|
||||||
|
|
||||||
# 关闭游标,避免与转录服务的数据库连接冲突
|
# 关闭游标,避免与转录服务的数据库连接冲突
|
||||||
cursor.close()
|
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'],
|
created_at=meeting['created_at'],
|
||||||
attendees=attendees,
|
attendees=attendees,
|
||||||
creator_id=meeting['creator_id'],
|
creator_id=meeting['creator_id'],
|
||||||
creator_username=meeting['creator_username']
|
creator_username=meeting['creator_username'],
|
||||||
|
tags=tags
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add audio file path if exists
|
# Add audio file path if exists
|
||||||
|
|
|
||||||
|
|
@ -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 pydantic import BaseModel, EmailStr
|
||||||
from typing import Optional, Union, List
|
from typing import Optional, Union, List
|
||||||
import datetime
|
import datetime
|
||||||
|
|
@ -51,6 +50,11 @@ class AttendeeInfo(BaseModel):
|
||||||
user_id: int
|
user_id: int
|
||||||
caption: str
|
caption: str
|
||||||
|
|
||||||
|
class Tag(BaseModel):
|
||||||
|
id: int
|
||||||
|
name: str
|
||||||
|
color: str
|
||||||
|
|
||||||
class TranscriptionTaskStatus(BaseModel):
|
class TranscriptionTaskStatus(BaseModel):
|
||||||
task_id: str
|
task_id: str
|
||||||
status: str # 'pending', 'processing', 'completed', 'failed'
|
status: str # 'pending', 'processing', 'completed', 'failed'
|
||||||
|
|
@ -72,6 +76,7 @@ class Meeting(BaseModel):
|
||||||
creator_username: str
|
creator_username: str
|
||||||
audio_file_path: Optional[str] = None
|
audio_file_path: Optional[str] = None
|
||||||
transcription_status: Optional[TranscriptionTaskStatus] = None
|
transcription_status: Optional[TranscriptionTaskStatus] = None
|
||||||
|
tags: Optional[List[Tag]] = []
|
||||||
|
|
||||||
class TranscriptSegment(BaseModel):
|
class TranscriptSegment(BaseModel):
|
||||||
segment_id: int
|
segment_id: int
|
||||||
|
|
@ -87,12 +92,14 @@ class CreateMeetingRequest(BaseModel):
|
||||||
title: str
|
title: str
|
||||||
meeting_time: Optional[datetime.datetime]
|
meeting_time: Optional[datetime.datetime]
|
||||||
attendee_ids: list[int]
|
attendee_ids: list[int]
|
||||||
|
tags: Optional[str] = None
|
||||||
|
|
||||||
class UpdateMeetingRequest(BaseModel):
|
class UpdateMeetingRequest(BaseModel):
|
||||||
title: str
|
title: str
|
||||||
meeting_time: Optional[datetime.datetime]
|
meeting_time: Optional[datetime.datetime]
|
||||||
summary: Optional[str]
|
summary: Optional[str]
|
||||||
attendee_ids: list[int]
|
attendee_ids: list[int]
|
||||||
|
tags: Optional[str] = None
|
||||||
|
|
||||||
class SpeakerTagUpdateRequest(BaseModel):
|
class SpeakerTagUpdateRequest(BaseModel):
|
||||||
speaker_id: int # 使用原始speaker_id(整数)
|
speaker_id: int # 使用原始speaker_id(整数)
|
||||||
|
|
|
||||||
3
main.py
3
main.py
|
|
@ -2,7 +2,7 @@ import uvicorn
|
||||||
from fastapi import FastAPI, Request, HTTPException
|
from fastapi import FastAPI, Request, HTTPException
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.staticfiles import StaticFiles
|
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.core.config import UPLOAD_DIR, API_CONFIG, MAX_FILE_SIZE
|
||||||
from app.services.async_llm_service import async_llm_service
|
from app.services.async_llm_service import async_llm_service
|
||||||
import os
|
import os
|
||||||
|
|
@ -30,6 +30,7 @@ if UPLOAD_DIR.exists():
|
||||||
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
|
app.include_router(auth.router, prefix="/api", tags=["Authentication"])
|
||||||
app.include_router(users.router, prefix="/api", tags=["Users"])
|
app.include_router(users.router, prefix="/api", tags=["Users"])
|
||||||
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
|
app.include_router(meetings.router, prefix="/api", tags=["Meetings"])
|
||||||
|
app.include_router(tags.router, prefix="/api", tags=["Tags"])
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
def read_root():
|
def read_root():
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue