File size: 8,884 Bytes
594ed40 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
from sqlalchemy import Column, String, DateTime, JSON, Boolean, ForeignKey, Text, Integer, Float
from sqlalchemy.orm import declarative_base, relationship
from datetime import datetime
import uuid
Base = declarative_base()
class User(Base):
"""ユーザーモデル - 複数の認証方法をサポート"""
__tablename__ = "users"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
email = Column(String, unique=True, nullable=True, index=True) # OAuth時はnullable
username = Column(String, unique=True, nullable=True, index=True)
display_name = Column(String, nullable=True)
hashed_password = Column(String, nullable=True) # OAuth時はnullable
# 認証プロバイダー情報
auth_provider = Column(String, default="local") # local, google, github, orcid
google_id = Column(String, unique=True, nullable=True, index=True)
github_id = Column(String, unique=True, nullable=True, index=True)
orcid_id = Column(String, unique=True, nullable=True, index=True)
# 権限とステータス
role = Column(String, default="viewer", nullable=False) # viewer, expert, reviewer, curator, admin
is_expert = Column(Boolean, default=False)
is_guest = Column(Boolean, default=False)
expert_verification_status = Column(String, default="none") # none, pending, approved, rejected
expert_credentials = Column(JSON)
# プロフィール情報
avatar_url = Column(String, nullable=True)
bio = Column(Text, nullable=True)
affiliation = Column(String, nullable=True)
# OAuth tokens (暗号化して保存すべき)
google_access_token = Column(String, nullable=True)
google_refresh_token = Column(String, nullable=True)
github_access_token = Column(String, nullable=True)
orcid_access_token = Column(String, nullable=True)
orcid_refresh_token = Column(String, nullable=True)
# タイムスタンプ
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
last_login_at = Column(DateTime, nullable=True)
# リレーションシップ
workspaces = relationship("Workspace", back_populates="owner", cascade="all, delete-orphan")
workspace_members = relationship("WorkspaceMember", back_populates="user", cascade="all, delete-orphan")
proposals = relationship("Proposal", back_populates="proposer", foreign_keys="[Proposal.proposer_id]")
reviews = relationship("Proposal", back_populates="reviewer", foreign_keys="[Proposal.reviewer_id]")
contributions = relationship("KnowledgeTile", back_populates="contributor", foreign_keys="[KnowledgeTile.contributor_id]")
class KnowledgeTile(Base):
"""知識タイルモデル"""
__tablename__ = "knowledge_tiles"
id = Column(String, primary_key=True, default=lambda: f"ktile_{uuid.uuid4().hex}")
workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False, index=True)
domain_id = Column(String, index=True)
topic = Column(String, nullable=False)
content = Column(Text, nullable=False)
tags = Column(JSON, nullable=True)
# 6次元座標 [x, y, z, c, g, v]
# [x, y, z]: ドメイン固有の3次元空間
# [c, g, v]: メタ空間 (Certainty, Granularity, Verification)
coordinates = Column(JSON, nullable=True) # Example: [0.5, 0.3, 0.8, 0.9, 0.7, 0.85]
# バージョン管理
version = Column(Integer, default=1)
is_latest_version = Column(Boolean, default=True)
based_on_version = Column(Integer, nullable=True)
# 貢献者と検証
contributor_id = Column(String, ForeignKey("users.id"), nullable=True)
confidence_score = Column(Float, default=0.0)
verification_type = Column(String, default="none", index=True) # none, community, expert, multi_expert
verification_count = Column(Integer, default=0)
last_verified_by_id = Column(String, ForeignKey("users.id"), nullable=True)
last_verified_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
workspace = relationship("Workspace")
contributor = relationship("User", back_populates="contributions", foreign_keys=[contributor_id])
last_verified_by = relationship("User", foreign_keys=[last_verified_by_id])
proposals = relationship("Proposal", back_populates="tile")
class Proposal(Base):
"""編集提案モデル"""
__tablename__ = "proposals"
id = Column(String, primary_key=True, default=lambda: f"prop_{uuid.uuid4().hex}")
workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False, index=True)
tile_id = Column(String, ForeignKey("knowledge_tiles.id"), nullable=True) # 新規作成時はnull
proposer_id = Column(String, ForeignKey("users.id"), nullable=False)
status = Column(String, default="pending", index=True) # pending, approved, rejected
proposal_type = Column(String, nullable=False) # create, update, delete
justification = Column(Text)
proposed_content = Column(JSON)
reviewer_id = Column(String, ForeignKey("users.id"), nullable=True)
reviewer_comment = Column(Text)
reviewed_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
workspace = relationship("Workspace")
tile = relationship("KnowledgeTile", back_populates="proposals")
proposer = relationship("User", back_populates="proposals", foreign_keys=[proposer_id])
reviewer = relationship("User", back_populates="reviews", foreign_keys=[reviewer_id])
class Workspace(Base):
"""ワークスペース - ユーザーごとの独立したDB環境"""
__tablename__ = "workspaces"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
name = Column(String, nullable=False)
slug = Column(String, unique=True, nullable=False, index=True) # URLフレンドリーな識別子
description = Column(Text, nullable=True)
# オーナー情報
owner_id = Column(String, ForeignKey("users.id"), nullable=False)
# 設定
is_public = Column(Boolean, default=False) # 公開ワークスペース
allow_guest_edit = Column(Boolean, default=True) # ゲストによる編集を許可
allow_guest_view = Column(Boolean, default=True) # ゲストによる閲覧を許可
# データベース設定
db_type = Column(String, default="sqlite") # sqlite, postgresql
db_path = Column(String, nullable=True) # SQLiteの場合のファイルパス
db_connection_string = Column(String, nullable=True) # PostgreSQLの場合
# 統計情報
tile_count = Column(Integer, default=0)
domain_count = Column(Integer, default=0)
member_count = Column(Integer, default=1)
# タイムスタンプ
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# リレーションシップ
owner = relationship("User", back_populates="workspaces")
members = relationship("WorkspaceMember", back_populates="workspace", cascade="all, delete-orphan")
class WorkspaceMember(Base):
"""ワークスペースメンバー - 共同作業者の管理"""
__tablename__ = "workspace_members"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
workspace_id = Column(String, ForeignKey("workspaces.id"), nullable=False)
user_id = Column(String, ForeignKey("users.id"), nullable=False)
# 権限
role = Column(String, default="viewer") # viewer, editor, admin
can_read = Column(Boolean, default=True)
can_write = Column(Boolean, default=False)
can_delete = Column(Boolean, default=False)
can_invite = Column(Boolean, default=False)
# タイムスタンプ
joined_at = Column(DateTime, default=datetime.utcnow)
# リレーションシップ
workspace = relationship("Workspace", back_populates="members")
user = relationship("User", back_populates="workspace_members")
class OAuthState(Base):
"""OAuth認証の一時的なstate管理"""
__tablename__ = "oauth_states"
id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()))
state = Column(String, unique=True, nullable=False, index=True)
provider = Column(String, nullable=False) # google, orcid
redirect_url = Column(String, nullable=True)
user_id = Column(String, ForeignKey("users.id"), nullable=True) # 既存ユーザーとの連携時
created_at = Column(DateTime, default=datetime.utcnow)
expires_at = Column(DateTime, nullable=False) # 10分で期限切れ
def is_expired(self):
return datetime.utcnow() > self.expires_at
|