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:
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user