feat: Meeting Assistant MVP - Complete implementation
Enterprise Meeting Knowledge Management System with: Backend (FastAPI): - Authentication proxy with JWT (pj-auth-api integration) - MySQL database with 4 tables (users, meetings, conclusions, actions) - Meeting CRUD with system code generation (C-YYYYMMDD-XX, A-YYYYMMDD-XX) - Dify LLM integration for AI summarization - Excel export with openpyxl - 20 unit tests (all passing) Client (Electron): - Login page with company auth - Meeting list with create/delete - Meeting detail with real-time transcription - Editable transcript textarea (single block, easy editing) - AI summarization with conclusions/action items - 5-second segment recording (efficient for long meetings) Sidecar (Python): - faster-whisper medium model with int8 quantization - ONNX Runtime VAD (lightweight, ~20MB vs PyTorch ~2GB) - Chinese punctuation processing - OpenCC for Traditional Chinese conversion - Anti-hallucination parameters - Auto-cleanup of temp audio files OpenSpec: - add-meeting-assistant-mvp (47 tasks, archived) - add-realtime-transcription (29 tasks, archived) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
96
backend/app/database.py
Normal file
96
backend/app/database.py
Normal file
@@ -0,0 +1,96 @@
|
||||
import mysql.connector
|
||||
from mysql.connector import pooling
|
||||
from contextlib import contextmanager
|
||||
from .config import settings
|
||||
|
||||
connection_pool = None
|
||||
|
||||
|
||||
def init_db_pool():
|
||||
global connection_pool
|
||||
connection_pool = pooling.MySQLConnectionPool(
|
||||
pool_name="meeting_pool",
|
||||
pool_size=5,
|
||||
host=settings.DB_HOST,
|
||||
port=settings.DB_PORT,
|
||||
user=settings.DB_USER,
|
||||
password=settings.DB_PASS,
|
||||
database=settings.DB_NAME,
|
||||
)
|
||||
return connection_pool
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_db_connection():
|
||||
conn = connection_pool.get_connection()
|
||||
try:
|
||||
yield conn
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_db_cursor(commit=False):
|
||||
with get_db_connection() as conn:
|
||||
cursor = conn.cursor(dictionary=True)
|
||||
try:
|
||||
yield cursor
|
||||
if commit:
|
||||
conn.commit()
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
|
||||
def init_tables():
|
||||
"""Create all required tables if they don't exist."""
|
||||
create_statements = [
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS meeting_users (
|
||||
user_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
email VARCHAR(100) UNIQUE NOT NULL,
|
||||
display_name VARCHAR(50),
|
||||
role ENUM('admin', 'user') DEFAULT 'user',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS meeting_records (
|
||||
meeting_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
uuid VARCHAR(64) UNIQUE,
|
||||
subject VARCHAR(200) NOT NULL,
|
||||
meeting_time DATETIME NOT NULL,
|
||||
location VARCHAR(100),
|
||||
chairperson VARCHAR(50),
|
||||
recorder VARCHAR(50),
|
||||
attendees TEXT,
|
||||
transcript_blob LONGTEXT,
|
||||
created_by VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS meeting_conclusions (
|
||||
conclusion_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
meeting_id INT,
|
||||
content TEXT,
|
||||
system_code VARCHAR(20),
|
||||
FOREIGN KEY (meeting_id) REFERENCES meeting_records(meeting_id) ON DELETE CASCADE
|
||||
)
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS meeting_action_items (
|
||||
action_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
meeting_id INT,
|
||||
content TEXT,
|
||||
owner VARCHAR(50),
|
||||
due_date DATE,
|
||||
status ENUM('Open', 'In Progress', 'Done', 'Delayed') DEFAULT 'Open',
|
||||
system_code VARCHAR(20),
|
||||
FOREIGN KEY (meeting_id) REFERENCES meeting_records(meeting_id) ON DELETE CASCADE
|
||||
)
|
||||
""",
|
||||
]
|
||||
|
||||
with get_db_cursor(commit=True) as cursor:
|
||||
for statement in create_statements:
|
||||
cursor.execute(statement)
|
||||
Reference in New Issue
Block a user