|
|
from flask_sqlalchemy import SQLAlchemy |
|
|
from flask_login import UserMixin |
|
|
from datetime import datetime |
|
|
from werkzeug.security import generate_password_hash, check_password_hash |
|
|
|
|
|
db = SQLAlchemy() |
|
|
|
|
|
|
|
|
class User(UserMixin, db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
username = db.Column(db.String(80), unique=True, nullable=False) |
|
|
nickname = db.Column(db.String(80), nullable=True) |
|
|
password_hash = db.Column(db.String(255), nullable=False) |
|
|
is_admin = db.Column(db.Boolean, default=False, nullable=False) |
|
|
is_active = db.Column(db.Boolean, default=True, nullable=False) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
last_login = db.Column(db.DateTime, nullable=True) |
|
|
|
|
|
def set_password(self, password): |
|
|
self.password_hash = generate_password_hash(password) |
|
|
|
|
|
def check_password(self, password): |
|
|
return check_password_hash(self.password_hash, password) |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'username': self.username, |
|
|
'nickname': self.nickname, |
|
|
'is_admin': self.is_admin, |
|
|
'is_active': self.is_active, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'last_login': self.last_login.isoformat() if self.last_login else None |
|
|
} |
|
|
|
|
|
|
|
|
class UploadedFile(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
filename = db.Column(db.String(255), nullable=False) |
|
|
original_filename = db.Column(db.String(255), nullable=False) |
|
|
file_path = db.Column(db.String(500), nullable=False) |
|
|
file_size = db.Column(db.Integer, nullable=False) |
|
|
model_name = db.Column(db.String(100), nullable=True) |
|
|
is_public = db.Column(db.Boolean, default=False, nullable=False) |
|
|
uploaded_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
uploaded_by = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) |
|
|
parent_file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=True) |
|
|
|
|
|
|
|
|
parent_file = db.relationship('UploadedFile', remote_side=[id], backref='child_files') |
|
|
|
|
|
def to_dict(self): |
|
|
|
|
|
chunk_count = len(self.chunks) if hasattr(self, 'chunks') else 0 |
|
|
|
|
|
return { |
|
|
'id': self.id, |
|
|
'filename': self.filename, |
|
|
'original_filename': self.original_filename, |
|
|
'file_size': self.file_size, |
|
|
'model_name': self.model_name, |
|
|
'is_public': self.is_public, |
|
|
'uploaded_at': self.uploaded_at.isoformat() if self.uploaded_at else None, |
|
|
'uploaded_by': self.uploaded_by, |
|
|
'parent_file_id': self.parent_file_id, |
|
|
'chunk_count': chunk_count, |
|
|
'child_count': len(self.child_files) if self.child_files else 0 |
|
|
} |
|
|
|
|
|
|
|
|
class ChatSession(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) |
|
|
title = db.Column(db.String(255), nullable=True) |
|
|
model_name = db.Column(db.String(100), nullable=True) |
|
|
analysis_model = db.Column(db.String(100), nullable=True) |
|
|
answer_model = db.Column(db.String(100), nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
user = db.relationship('User', backref='chat_sessions') |
|
|
messages = db.relationship('ChatMessage', backref='session', lazy=True, cascade='all, delete-orphan', order_by='ChatMessage.created_at') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'user_id': self.user_id, |
|
|
'title': self.title, |
|
|
'model_name': self.model_name, |
|
|
'analysis_model': self.analysis_model, |
|
|
'answer_model': self.answer_model, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None, |
|
|
'message_count': len(self.messages) |
|
|
} |
|
|
|
|
|
|
|
|
class ChatMessage(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
session_id = db.Column(db.Integer, db.ForeignKey('chat_session.id'), nullable=True) |
|
|
role = db.Column(db.String(20), nullable=False) |
|
|
content = db.Column(db.Text, nullable=False) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
|
|
|
input_tokens = db.Column(db.Integer, nullable=True) |
|
|
output_tokens = db.Column(db.Integer, nullable=True) |
|
|
model_name = db.Column(db.String(100), nullable=True) |
|
|
usage_type = db.Column(db.String(20), nullable=True, default='user') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'session_id': self.session_id, |
|
|
'role': self.role, |
|
|
'content': self.content, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'input_tokens': self.input_tokens, |
|
|
'output_tokens': self.output_tokens, |
|
|
'model_name': self.model_name, |
|
|
'usage_type': self.usage_type |
|
|
} |
|
|
|
|
|
|
|
|
class DocumentChunk(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) |
|
|
chunk_index = db.Column(db.Integer, nullable=False) |
|
|
content = db.Column(db.Text, nullable=False) |
|
|
embedding = db.Column(db.Text, nullable=True) |
|
|
chunk_metadata = db.Column(db.Text, nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='chunks') |
|
|
|
|
|
def to_dict(self): |
|
|
import json |
|
|
metadata_dict = None |
|
|
if self.chunk_metadata: |
|
|
try: |
|
|
metadata_dict = json.loads(self.chunk_metadata) |
|
|
except: |
|
|
metadata_dict = None |
|
|
|
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'chunk_index': self.chunk_index, |
|
|
'content': self.content, |
|
|
'metadata': metadata_dict, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None |
|
|
} |
|
|
|
|
|
|
|
|
class ParentChunk(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False, unique=True) |
|
|
world_view = db.Column(db.Text, nullable=True) |
|
|
characters = db.Column(db.Text, nullable=True) |
|
|
story = db.Column(db.Text, nullable=True) |
|
|
episodes = db.Column(db.Text, nullable=True) |
|
|
others = db.Column(db.Text, nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='parent_chunk') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'world_view': self.world_view, |
|
|
'characters': self.characters, |
|
|
'story': self.story, |
|
|
'episodes': self.episodes, |
|
|
'others': self.others, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None |
|
|
} |
|
|
|
|
|
|
|
|
class SystemConfig(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
key = db.Column(db.String(100), unique=True, nullable=False) |
|
|
value = db.Column(db.Text, nullable=True) |
|
|
description = db.Column(db.String(255), nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'key': self.key, |
|
|
'value': self.value, |
|
|
'description': self.description, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None |
|
|
} |
|
|
|
|
|
@staticmethod |
|
|
def get_config(key, default=None): |
|
|
"""์ค์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ""" |
|
|
try: |
|
|
config = SystemConfig.query.filter_by(key=key).first() |
|
|
return config.value if config else default |
|
|
except Exception as e: |
|
|
|
|
|
print(f"[SystemConfig.get_config] ์ค๋ฅ: {e}") |
|
|
return default |
|
|
|
|
|
@staticmethod |
|
|
def set_config(key, value, description=None): |
|
|
"""์ค์ ๊ฐ ์ ์ฅ/์
๋ฐ์ดํธ""" |
|
|
try: |
|
|
|
|
|
from sqlalchemy import inspect |
|
|
inspector = inspect(db.engine) |
|
|
if 'system_config' not in inspector.get_table_names(): |
|
|
print("[SystemConfig.set_config] SystemConfig ํ
์ด๋ธ ์์ฑ ์ค...") |
|
|
db.create_all() |
|
|
|
|
|
config = SystemConfig.query.filter_by(key=key).first() |
|
|
if config: |
|
|
print(f"[SystemConfig.set_config] ๊ธฐ์กด ์ค์ ์
๋ฐ์ดํธ: {key}") |
|
|
config.value = value |
|
|
if description: |
|
|
config.description = description |
|
|
config.updated_at = datetime.utcnow() |
|
|
else: |
|
|
print(f"[SystemConfig.set_config] ์ ์ค์ ์์ฑ: {key}") |
|
|
config = SystemConfig(key=key, value=value, description=description) |
|
|
db.session.add(config) |
|
|
|
|
|
db.session.commit() |
|
|
|
|
|
|
|
|
verify_config = SystemConfig.query.filter_by(key=key).first() |
|
|
if verify_config and verify_config.value == value: |
|
|
print(f"[SystemConfig.set_config] ์ค์ ์ ์ฅ ํ์ธ๋จ: {key} (๊ธธ์ด: {len(str(value))}์)") |
|
|
else: |
|
|
print(f"[SystemConfig.set_config] ๊ฒฝ๊ณ : ์ค์ ์ ์ฅ ํ ํ์ธ ์คํจ: {key}") |
|
|
|
|
|
return config |
|
|
except Exception as e: |
|
|
db.session.rollback() |
|
|
print(f"[SystemConfig.set_config] ์ค๋ฅ: {e}") |
|
|
import traceback |
|
|
traceback.print_exc() |
|
|
raise |
|
|
|
|
|
|
|
|
class EpisodeAnalysis(db.Model): |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) |
|
|
episode_title = db.Column(db.String(100), nullable=False) |
|
|
analysis_content = db.Column(db.Text, nullable=False) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='episode_analyses') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'episode_title': self.episode_title, |
|
|
'analysis_content': self.analysis_content, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None, |
|
|
'updated_at': self.updated_at.isoformat() if self.updated_at else None |
|
|
} |
|
|
|
|
|
|
|
|
class GraphEntity(db.Model): |
|
|
"""Graph Extraction์์ ์ถ์ถ๋ ์ํฐํฐ (์ธ๋ฌผ/์ฅ์)""" |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) |
|
|
episode_title = db.Column(db.String(100), nullable=False) |
|
|
entity_name = db.Column(db.String(200), nullable=False) |
|
|
entity_type = db.Column(db.String(50), nullable=False) |
|
|
description = db.Column(db.Text, nullable=True) |
|
|
role = db.Column(db.String(200), nullable=True) |
|
|
category = db.Column(db.String(200), nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='graph_entities') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'episode_title': self.episode_title, |
|
|
'entity_name': self.entity_name, |
|
|
'entity_type': self.entity_type, |
|
|
'description': self.description, |
|
|
'role': self.role, |
|
|
'category': self.category, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None |
|
|
} |
|
|
|
|
|
class GraphRelationship(db.Model): |
|
|
"""Graph Extraction์์ ์ถ์ถ๋ ๊ด๊ณ""" |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) |
|
|
episode_title = db.Column(db.String(100), nullable=False) |
|
|
source = db.Column(db.String(200), nullable=False) |
|
|
target = db.Column(db.String(200), nullable=False) |
|
|
relationship_type = db.Column(db.String(200), nullable=False) |
|
|
description = db.Column(db.Text, nullable=True) |
|
|
event = db.Column(db.String(500), nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='graph_relationships') |
|
|
|
|
|
def to_dict(self): |
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'episode_title': self.episode_title, |
|
|
'source': self.source, |
|
|
'target': self.target, |
|
|
'relationship_type': self.relationship_type, |
|
|
'description': self.description, |
|
|
'event': self.event, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None |
|
|
} |
|
|
|
|
|
class GraphEvent(db.Model): |
|
|
"""Graph Extraction์์ ์ถ์ถ๋ ์ฌ๊ฑด""" |
|
|
id = db.Column(db.Integer, primary_key=True) |
|
|
file_id = db.Column(db.Integer, db.ForeignKey('uploaded_file.id'), nullable=False) |
|
|
episode_title = db.Column(db.String(100), nullable=False) |
|
|
event_name = db.Column(db.String(500), nullable=False) |
|
|
description = db.Column(db.Text, nullable=False) |
|
|
participants = db.Column(db.Text, nullable=True) |
|
|
location = db.Column(db.String(500), nullable=True) |
|
|
significance = db.Column(db.String(200), nullable=True) |
|
|
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) |
|
|
|
|
|
|
|
|
file = db.relationship('UploadedFile', backref='graph_events') |
|
|
|
|
|
def to_dict(self): |
|
|
import json |
|
|
participants_list = [] |
|
|
if self.participants: |
|
|
try: |
|
|
participants_list = json.loads(self.participants) |
|
|
except: |
|
|
participants_list = [] |
|
|
|
|
|
return { |
|
|
'id': self.id, |
|
|
'file_id': self.file_id, |
|
|
'episode_title': self.episode_title, |
|
|
'event_name': self.event_name, |
|
|
'description': self.description, |
|
|
'participants': participants_list, |
|
|
'location': self.location, |
|
|
'significance': self.significance, |
|
|
'created_at': self.created_at.isoformat() if self.created_at else None |
|
|
} |
|
|
|
|
|
|
|
|
|