landvex: Fixar och tester klara för alla komponenter

- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
This commit is contained in:
Bernt
2026-07-05 06:41:32 +00:00
parent f4f853d94b
commit aee0f09db8
19583 changed files with 1450867 additions and 1153 deletions
+84
View File
@@ -0,0 +1,84 @@
"""SQLAlchemy-modeller för user management."""
import uuid
from datetime import datetime
from enum import Enum as PyEnum
from sqlalchemy import Column, String, DateTime, Boolean, ForeignKey, Enum, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from app.database import Base
class UserRole(str, PyEnum):
ADMIN = "admin"
MANAGER = "manager"
ANALYST = "analyst"
VIEWER = "viewer"
class UserStatus(str, PyEnum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending" # Invited, not yet set password
class User(Base):
__tablename__ = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String(255), unique=True, nullable=False, index=True)
hashed_password = Column(String(255), nullable=True) # NULL until invitation accepted
first_name = Column(String(100), nullable=False)
last_name = Column(String(100), nullable=False)
role = Column(Enum(UserRole), nullable=False, default=UserRole.VIEWER)
status = Column(Enum(UserStatus), nullable=False, default=UserStatus.PENDING)
is_superuser = Column(Boolean, default=False)
last_login_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
tenant = relationship("Tenant", back_populates="users")
invitations_sent = relationship("Invitation", foreign_keys="Invitation.invited_by_id", back_populates="inviter")
invitations_received = relationship("Invitation", foreign_keys="Invitation.email", primaryjoin="User.email == Invitation.email", viewonly=True)
def __repr__(self):
return f"<User {self.email} ({self.role.value})>"
class Tenant(Base):
__tablename__ = "tenants"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(255), nullable=False)
slug = Column(String(100), unique=True, nullable=False, index=True)
description = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
users = relationship("User", back_populates="tenant")
def __repr__(self):
return f"<Tenant {self.name}>"
class Invitation(Base):
__tablename__ = "invitations"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String(255), nullable=False, index=True)
token = Column(String(255), unique=True, nullable=False, index=True)
role = Column(Enum(UserRole), nullable=False, default=UserRole.VIEWER)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
invited_by_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
expires_at = Column(DateTime(timezone=True), nullable=False)
accepted_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
inviter = relationship("User", foreign_keys=[invited_by_id], back_populates="invitations_sent")
tenant = relationship("Tenant")
def __repr__(self):
return f"<Invitation {self.email} ({self.role.value})>"