from datetime import date from enum import Enum from common.extensions import db from flask_security import UserMixin, RoleMixin from sqlalchemy.dialects.postgresql import ARRAY, JSONB import sqlalchemy as sa from common.models.entitlements import License class Tenant(db.Model): """Tenant model""" __bind_key__ = 'public' __table_args__ = {'schema': 'public'} # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) # company Information id = db.Column(db.Integer, primary_key=True) code = db.Column(db.String(50), unique=True, nullable=True) name = db.Column(db.String(80), unique=True, nullable=False) website = db.Column(db.String(255), nullable=True) timezone = db.Column(db.String(50), nullable=True, default='UTC') type = db.Column(db.String(20), nullable=True, server_default='Active') # Entitlements currency = db.Column(db.String(20), nullable=True) storage_dirty = db.Column(db.Boolean, nullable=True, default=False) default_tenant_make_id = db.Column(db.Integer, db.ForeignKey('public.tenant_make.id'), nullable=True) # Relations users = db.relationship('User', backref='tenant') domains = db.relationship('TenantDomain', backref='tenant') licenses = db.relationship('License', back_populates='tenant') license_usages = db.relationship('LicenseUsage', backref='tenant') tenant_makes = db.relationship('TenantMake', backref='tenant', foreign_keys='TenantMake.tenant_id') default_tenant_make = db.relationship('TenantMake', foreign_keys=[default_tenant_make_id], uselist=False) @property def current_license(self): today = date.today() return License.query.filter( License.tenant_id == self.id, License.start_date <= today, (License.end_date.is_(None) | (License.end_date >= today)) ).order_by(License.start_date.desc()).first() def __repr__(self): return f"" def to_dict(self): return { 'id': self.id, 'name': self.name, 'website': self.website, 'timezone': self.timezone, 'type': self.type, 'currency': self.currency, 'default_tenant_make_id': self.default_tenant_make_id, } class Role(db.Model, RoleMixin): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer(), primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) class RolesUsers(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} user_id = db.Column(db.Integer(), db.ForeignKey('public.user.id', ondelete='CASCADE'), primary_key=True) role_id = db.Column(db.Integer(), db.ForeignKey('public.role.id', ondelete='CASCADE'), primary_key=True) class User(db.Model, UserMixin): """User model""" __bind_key__ = 'public' __table_args__ = {'schema': 'public'} # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) # User Information id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) user_name = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(255), unique=True, nullable=False) password = db.Column(db.String(255), nullable=True) first_name = db.Column(db.String(80), nullable=False) last_name = db.Column(db.String(80), nullable=False) active = db.Column(db.Boolean) fs_uniquifier = db.Column(db.String(255), unique=True, nullable=False) confirmed_at = db.Column(db.DateTime, nullable=True) valid_to = db.Column(db.Date, nullable=True) is_primary_contact = db.Column(db.Boolean, nullable=True, default=False) is_financial_contact = db.Column(db.Boolean, nullable=True, default=False) # Security Trackable Information last_login_at = db.Column(db.DateTime, nullable=True) current_login_at = db.Column(db.DateTime, nullable=True) last_login_ip = db.Column(db.String(255), nullable=True) current_login_ip = db.Column(db.String(255), nullable=True) login_count = db.Column(db.Integer, nullable=False, default=0) # Relations roles = db.relationship('Role', secondary=RolesUsers.__table__, backref=db.backref('users', lazy='dynamic')) def __repr__(self): return '' % self.user_name def has_roles(self, *args): return any(role.name in args for role in self.roles) class TenantDomain(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) # Originally, domain was required to be unique. # However, several tenants can run from the same domain (e.g. for demo purposes, # but also internal and external chat clients. domain = db.Column(db.String(255), nullable=False) valid_to = db.Column(db.Date, nullable=True) # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=False) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id')) def __repr__(self): return f"" class TenantProject(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) name = db.Column(db.String(50), nullable=False) description = db.Column(db.Text, nullable=True) services = db.Column(ARRAY(sa.String(50)), nullable=False) encrypted_api_key = db.Column(db.String(500), nullable=True) visual_api_key = db.Column(db.String(20), nullable=True) active = db.Column(db.Boolean, nullable=False, default=True) responsible_email = db.Column(db.String(255), nullable=True) # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id')) # Relations tenant = db.relationship('Tenant', backref='projects') def __repr__(self): return f"" class TenantMake(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) name = db.Column(db.String(50), nullable=False, unique=True) description = db.Column(db.Text, nullable=True) active = db.Column(db.Boolean, nullable=False, default=True) website = db.Column(db.String(255), nullable=True) logo_url = db.Column(db.String(255), nullable=True) default_language = db.Column(db.String(2), nullable=True) allowed_languages = db.Column(ARRAY(sa.String(2)), nullable=True) # Chat customisation options chat_customisation_options = db.Column(JSONB, nullable=True) # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id')) def __repr__(self): return f"" def to_dict(self): return { 'id': self.id, 'name': self.name, 'description': self.description, 'active': self.active, 'website': self.website, 'logo_url': self.logo_url, 'chat_customisation_options': self.chat_customisation_options, 'allowed_languages': self.allowed_languages, 'default_language': self.default_language, } class Partner(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False, unique=True) code = db.Column(db.String(50), unique=True, nullable=False) # Basic information logo_url = db.Column(db.String(255), nullable=True) active = db.Column(db.Boolean, default=True) # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) # Relationships services = db.relationship('PartnerService', back_populates='partner') tenant = db.relationship('Tenant', backref=db.backref('partner', uselist=False)) def to_dict(self): services_info = [] for service in self.services: services_info.append({ 'id': service.id, 'name': service.name, 'description': service.description, 'type': service.type, 'type_version': service.type_version, 'active': service.active, 'configuration': service.configuration, 'permissions': service.permissions, }) return { 'id': self.id, 'tenant_id': self.tenant_id, 'code': self.code, 'logo_url': self.logo_url, 'active': self.active, 'name': self.tenant.name, 'services': services_info } class PartnerService(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) partner_id = db.Column(db.Integer, db.ForeignKey('public.partner.id'), nullable=False) # Basic info name = db.Column(db.String(50), nullable=False) description = db.Column(db.Text, nullable=True) # Service type with versioning (similar to your specialist/retriever pattern) type = db.Column(db.String(50), nullable=False) # REFERRAL, KNOWLEDGE, SPECIALIST, IMPLEMENTATION, WHITE_LABEL type_version = db.Column(db.String(20), nullable=False, default="1.0.0") # Status active = db.Column(db.Boolean, default=True) # Dynamic configuration specific to this service - using JSONB like your other models configuration = db.Column(db.JSON, nullable=True) permissions = db.Column(db.JSON, nullable=True) # For services that need to track shared resources system_metadata = db.Column(db.JSON, nullable=True) user_metadata = db.Column(db.JSON, nullable=True) # Versioning Information created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) # Relationships partner = db.relationship('Partner', back_populates='services') license_tiers = db.relationship('PartnerServiceLicenseTier', back_populates='partner_service') class PartnerTenant(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} partner_service_id = db.Column(db.Integer, db.ForeignKey('public.partner_service.id'), primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), primary_key=True) # JSONB for flexible configuration specific to this relationship configuration = db.Column(db.JSON, nullable=True) # Tracking created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) class TenantConsent(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) partner_id = db.Column(db.Integer, db.ForeignKey('public.partner.id'), nullable=False) partner_service_id = db.Column(db.Integer, db.ForeignKey('public.partner_service.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=False) consent_type = db.Column(db.String(50), nullable=False) consent_date = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) consent_version = db.Column(db.String(20), nullable=False, default="1.0.0") consent_data = db.Column(db.JSON, nullable=False) # Tracking created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) class ConsentVersion(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} id = db.Column(db.Integer, primary_key=True) consent_type = db.Column(db.String(50), nullable=False) consent_version = db.Column(db.String(20), nullable=False) consent_valid_from = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) consent_valid_to = db.Column(db.DateTime, nullable=True) # Tracking created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) class ConsentStatus(Enum): CONSENTED = 'CONSENTED' NOT_CONSENTED = 'NOT_CONSENTED' RENEWAL_REQUIRED = 'RENEWAL_REQUIRED' CONSENT_EXPIRED = 'CONSENT_EXPIRED' UNKNOWN_CONSENT_VERSION = 'UNKNOWN_CONSENT_VERSION' class SpecialistMagicLinkTenant(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} magic_link_code = db.Column(db.String(55), primary_key=True) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) class TranslationCache(db.Model): __bind_key__ = 'public' __table_args__ = {'schema': 'public'} cache_key = db.Column(db.String(16), primary_key=True) source_text = db.Column(db.Text, nullable=False) translated_text = db.Column(db.Text, nullable=False) source_language = db.Column(db.String(2), nullable=True) target_language = db.Column(db.String(2), nullable=False) context = db.Column(db.Text, nullable=True) # Translation cost prompt_tokens = db.Column(db.Integer, nullable=False) completion_tokens = db.Column(db.Integer, nullable=False) # Tracking created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now()) created_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'), nullable=True) last_used_at = db.Column(db.DateTime, nullable=True) class PartnerRAGRetriever(db.Model): __bind_key__ = 'public' __table_args__ = ( db.PrimaryKeyConstraint('tenant_id', 'retriever_id'), db.UniqueConstraint('partner_id', 'tenant_id', 'retriever_id'), {'schema': 'public'}, ) partner_id = db.Column(db.Integer, db.ForeignKey('public.partner.id'), nullable=False) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) retriever_id = db.Column(db.Integer, nullable=False)