Files
boc/landvex-admin-backend/app/models.py
T
Bernt aee0f09db8 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
2026-07-05 06:41:32 +00:00

405 lines
15 KiB
Python

"""
Landvex Admin Backend - Unified Models
Includes: Content, quiXzoom, Economy, HR, Aid Intelligence
"""
from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean, Text, JSON, ForeignKey, Enum, Numeric
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
Base = declarative_base()
# ─── USER & AUTH ──────────────────────────────────────────────────────────────
class UserRole(str, enum.Enum):
SUPER_ADMIN = "super_admin"
ADMIN = "admin"
MANAGER = "manager"
EDITOR = "editor"
VIEWER = "viewer"
ANALYST = "analyst"
QUIXZOOM_MANAGER = "quixzoom_manager"
ECONOMY_MANAGER = "economy_manager"
HR_MANAGER = "hr_manager"
AID_MANAGER = "aid_manager"
class UserStatus(str, enum.Enum):
ACTIVE = "active"
INACTIVE = "inactive"
SUSPENDED = "suspended"
class TenantType(str, enum.Enum):
ENTERPRISE = "enterprise"
SMALL_BUSINESS = "small_business"
INDIVIDUAL = "individual"
MUNICIPALITY = "municipality"
GOVERNMENT = "government"
NGO = "ngo"
class TenantStatus(str, enum.Enum):
ACTIVE = "active"
INACTIVE = "inactive"
SUSPENDED = "suspended"
PENDING = "pending"
class Tenant(Base):
__tablename__ = "tenants"
id = Column(Integer, primary_key=True, index=True)
tenant_id = Column(String(50), unique=True, nullable=False)
name = Column(String(255), nullable=False)
tenant_type = Column(Enum(TenantType), default=TenantType.ENTERPRISE)
status = Column(Enum(TenantStatus), default=TenantStatus.ACTIVE)
settings = Column(JSON)
created_at = Column(DateTime, default=datetime.utcnow)
class Invitation(Base):
__tablename__ = "invitations"
id = Column(Integer, primary_key=True, index=True)
invitation_id = Column(String(50), unique=True, nullable=False)
email = Column(String(255), nullable=False)
role = Column(Enum(UserRole), default=UserRole.VIEWER)
tenant_id = Column(Integer, ForeignKey("tenants.id"))
invited_by = Column(Integer, ForeignKey("users.id"))
status = Column(String(50), default="pending") # pending, accepted, expired
expires_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
class AuditAction(str, enum.Enum):
CREATE = "create"
UPDATE = "update"
DELETE = "delete"
LOGIN = "login"
LOGOUT = "logout"
INVITE = "invite"
ACCEPT = "accept"
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, index=True)
action = Column(Enum(AuditAction), nullable=False)
entity_type = Column(String(100)) # user, tenant, invitation, etc.
entity_id = Column(String(100))
user_id = Column(Integer, ForeignKey("users.id"))
tenant_id = Column(Integer, ForeignKey("tenants.id"))
details = Column(JSON)
ip_address = Column(String(50))
user_agent = Column(String(500))
created_at = Column(DateTime, default=datetime.utcnow)
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String(255), unique=True, index=True, nullable=False)
hashed_password = Column(String(255), nullable=False)
full_name = Column(String(255))
role = Column(Enum(UserRole), default=UserRole.VIEWER)
tenant_id = Column(Integer, ForeignKey("tenants.id"))
status = Column(Enum(UserStatus), default=UserStatus.ACTIVE)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
last_login = Column(DateTime)
# ─── CONTENT MODULE ───────────────────────────────────────────────────────────
class ArticleStatus(str, enum.Enum):
DRAFT = "draft"
REVIEW = "review"
PUBLISHED = "published"
ARCHIVED = "archived"
class Article(Base):
__tablename__ = "articles"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(500), nullable=False)
slug = Column(String(500), unique=True, index=True, nullable=False)
content = Column(Text)
excerpt = Column(String(1000))
author_id = Column(Integer, ForeignKey("users.id"))
status = Column(Enum(ArticleStatus), default=ArticleStatus.DRAFT)
seo_title = Column(String(70))
seo_description = Column(String(160))
keywords = Column(JSON)
published_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
author = relationship("User")
class SEOMetadata(Base):
__tablename__ = "seo_metadata"
id = Column(Integer, primary_key=True, index=True)
page_path = Column(String(500), unique=True, nullable=False)
title = Column(String(70))
description = Column(String(160))
canonical_url = Column(String(500))
og_image = Column(String(500))
schema_markup = Column(JSON)
last_audit = Column(DateTime)
audit_score = Column(Integer)
# ─── QUIXZOOM MODULE ──────────────────────────────────────────────────────────
class MissionStatus(str, enum.Enum):
DRAFT = "draft"
PENDING = "pending"
ACTIVE = "active"
PAUSED = "paused"
COMPLETED = "completed"
CANCELLED = "cancelled"
class Mission(Base):
__tablename__ = "missions"
id = Column(Integer, primary_key=True, index=True)
mission_id = Column(String(50), unique=True, nullable=False)
title = Column(String(500), nullable=False)
description = Column(Text)
city = Column(String(255))
country = Column(String(255))
location = Column(JSON)
reward_amount = Column(Numeric(10, 2))
reward_currency = Column(String(3), default="SEK")
status = Column(Enum(MissionStatus), default=MissionStatus.DRAFT)
requirements = Column(JSON)
start_date = Column(DateTime)
end_date = Column(DateTime)
max_submissions = Column(Integer)
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime, default=datetime.utcnow)
class Contributor(Base):
__tablename__ = "contributors"
id = Column(Integer, primary_key=True, index=True)
contributor_id = Column(String(50), unique=True, nullable=False)
email = Column(String(255), unique=True)
full_name = Column(String(255))
phone = Column(String(50))
country = Column(String(255))
city = Column(String(255))
reputation_score = Column(Integer, default=0)
total_earnings = Column(Numeric(10, 2), default=0)
total_submissions = Column(Integer, default=0)
verified = Column(Boolean, default=False)
status = Column(String(50), default="active")
created_at = Column(DateTime, default=datetime.utcnow)
class Submission(Base):
__tablename__ = "submissions"
id = Column(Integer, primary_key=True, index=True)
submission_id = Column(String(50), unique=True, nullable=False)
mission_id = Column(Integer, ForeignKey("missions.id"))
contributor_id = Column(Integer, ForeignKey("contributors.id"))
status = Column(String(50), default="pending")
media_urls = Column(JSON)
location = Column(JSON)
ai_score = Column(Float)
reviewer_notes = Column(Text)
submitted_at = Column(DateTime, default=datetime.utcnow)
reviewed_at = Column(DateTime)
reviewed_by = Column(Integer, ForeignKey("users.id"))
# ─── ECONOMY MODULE ───────────────────────────────────────────────────────────
class AccountType(str, enum.Enum):
ASSET = "asset"
LIABILITY = "liability"
EQUITY = "equity"
REVENUE = "revenue"
EXPENSE = "expense"
class Account(Base):
__tablename__ = "accounts"
id = Column(Integer, primary_key=True, index=True)
account_number = Column(String(20), unique=True, nullable=False)
name = Column(String(255), nullable=False)
account_type = Column(Enum(AccountType), nullable=False)
parent_id = Column(Integer, ForeignKey("accounts.id"))
balance = Column(Numeric(12, 2), default=0)
currency = Column(String(3), default="SEK")
description = Column(Text)
is_active = Column(Boolean, default=True)
parent = relationship("Account", remote_side=[id])
class Transaction(Base):
__tablename__ = "transactions"
id = Column(Integer, primary_key=True, index=True)
transaction_id = Column(String(50), unique=True, nullable=False)
date = Column(DateTime, nullable=False)
description = Column(Text)
reference = Column(String(255))
amount = Column(Numeric(12, 2), nullable=False)
debit_account_id = Column(Integer, ForeignKey("accounts.id"))
credit_account_id = Column(Integer, ForeignKey("accounts.id"))
status = Column(String(50), default="posted")
created_by = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime, default=datetime.utcnow)
class BankAccount(Base):
__tablename__ = "bank_accounts"
id = Column(Integer, primary_key=True, index=True)
bank_name = Column(String(255), nullable=False)
account_name = Column(String(255))
account_number = Column(String(100))
iban = Column(String(100))
bic = Column(String(20))
currency = Column(String(3), default="SEK")
current_balance = Column(Numeric(12, 2), default=0)
account_type = Column(String(50))
is_active = Column(Boolean, default=True)
# ─── HR MODULE ────────────────────────────────────────────────────────────────
class EmployeeStatus(str, enum.Enum):
ACTIVE = "active"
ON_LEAVE = "on_leave"
TERMINATED = "terminated"
CONTRACT = "contract"
class Employee(Base):
__tablename__ = "employees"
id = Column(Integer, primary_key=True, index=True)
employee_id = Column(String(50), unique=True, nullable=False)
first_name = Column(String(255), nullable=False)
last_name = Column(String(255), nullable=False)
email = Column(String(255), unique=True)
phone = Column(String(50))
department = Column(String(255))
position = Column(String(255))
start_date = Column(DateTime)
end_date = Column(DateTime)
salary = Column(Numeric(10, 2))
salary_currency = Column(String(3), default="SEK")
status = Column(Enum(EmployeeStatus), default=EmployeeStatus.ACTIVE)
manager_id = Column(Integer, ForeignKey("employees.id"))
created_at = Column(DateTime, default=datetime.utcnow)
class Contractor(Base):
__tablename__ = "contractors"
id = Column(Integer, primary_key=True, index=True)
contractor_id = Column(String(50), unique=True, nullable=False)
company_name = Column(String(255))
contact_name = Column(String(255))
email = Column(String(255))
phone = Column(String(50))
services = Column(JSON)
hourly_rate = Column(Numeric(10, 2))
currency = Column(String(3), default="SEK")
contract_start = Column(DateTime)
contract_end = Column(DateTime)
status = Column(String(50), default="active")
# ─── AID INTELLIGENCE MODULE ─────────────────────────────────────────────────
class AidProjectType(str, enum.Enum):
SCHOOL = "school"
HOSPITAL = "hospital"
ROAD = "road"
BRIDGE = "bridge"
WATER = "water"
POWER = "power"
AGRICULTURE = "agriculture"
RENEWABLE = "renewable"
HOUSING = "housing"
SANITATION = "sanitation"
DIGITAL = "digital"
ENVIRONMENTAL = "environmental"
EMERGENCY = "emergency"
FOOD = "food"
EDUCATION = "education"
HEALTHCARE = "healthcare"
MICROFINANCE = "microfinance"
WOMEN_EMPOWERMENT = "women_empowerment"
YOUTH_EMPLOYMENT = "youth_employment"
class AidProjectStatus(str, enum.Enum):
PLANNED = "planned"
ACTIVE = "active"
COMPLETED = "completed"
DELAYED = "delayed"
ABANDONED = "abandoned"
UNDER_MAINTENANCE = "under_maintenance"
class AidProject(Base):
__tablename__ = "aid_projects"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(String(50), unique=True, nullable=False)
name = Column(String(500), nullable=False)
description = Column(Text)
country = Column(String(255), nullable=False)
region = Column(String(255))
district = Column(String(255))
project_type = Column(Enum(AidProjectType), nullable=False)
status = Column(Enum(AidProjectStatus), default=AidProjectStatus.PLANNED)
funding_agency = Column(String(255))
implementing_partner = Column(String(255))
budget_usd = Column(Numeric(12, 2))
start_date = Column(DateTime)
expected_completion = Column(DateTime)
actual_completion = Column(DateTime)
location = Column(JSON)
aii_score = Column(Integer)
confidence = Column(Integer)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class AidObservation(Base):
__tablename__ = "aid_observations"
id = Column(Integer, primary_key=True, index=True)
observation_id = Column(String(50), unique=True, nullable=False)
project_id = Column(Integer, ForeignKey("aid_projects.id"))
observation_type = Column(String(50), nullable=False)
date = Column(DateTime, nullable=False)
contributor_id = Column(String(100))
location = Column(JSON)
media_urls = Column(JSON)
ai_analysis = Column(JSON)
confidence = Column(Integer)
verified = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
class AidAlert(Base):
__tablename__ = "aid_alerts"
id = Column(Integer, primary_key=True, index=True)
alert_id = Column(String(50), unique=True, nullable=False)
severity = Column(String(20), nullable=False)
alert_type = Column(String(100), nullable=False)
project_id = Column(Integer, ForeignKey("aid_projects.id"))
description = Column(Text)
detected_at = Column(DateTime, default=datetime.utcnow)
confidence = Column(Integer)
status = Column(String(50), default="open")
resolved_at = Column(DateTime)
resolved_by = Column(Integer, ForeignKey("users.id"))
# ─── SYSTEM HEALTH ────────────────────────────────────────────────────────────
class ServiceHealth(Base):
__tablename__ = "service_health"
id = Column(Integer, primary_key=True, index=True)
service_name = Column(String(255), nullable=False)
service_url = Column(String(500))
status = Column(String(50), default="unknown")
last_check = Column(DateTime, default=datetime.utcnow)
response_time_ms = Column(Integer)
error_rate = Column(Float)
uptime_percentage = Column(Float)