786 lines
24 KiB
Python
786 lines
24 KiB
Python
"""
|
|
Integration app models for external system integrations.
|
|
"""
|
|
|
|
import uuid
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
from decimal import Decimal
|
|
from django.db import models
|
|
from django.contrib.auth import get_user_model
|
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
|
from django.utils import timezone
|
|
from core.models import Tenant
|
|
from django.conf import settings
|
|
|
|
|
|
|
|
class ExternalSystem(models.Model):
|
|
"""
|
|
External system configuration for integrations.
|
|
"""
|
|
|
|
SYSTEM_TYPES = [
|
|
('ehr', 'Electronic Health Record'),
|
|
('his', 'Hospital Information System'),
|
|
('lis', 'Laboratory Information System'),
|
|
('ris', 'Radiology Information System'),
|
|
('pacs', 'Picture Archiving System'),
|
|
('pharmacy', 'Pharmacy Management System'),
|
|
('billing', 'Billing System'),
|
|
('insurance', 'Insurance System'),
|
|
('government', 'Government System'),
|
|
('vendor', 'Vendor System'),
|
|
('api', 'API Service'),
|
|
('database', 'Database System'),
|
|
('file', 'File System'),
|
|
('ftp', 'FTP Server'),
|
|
('sftp', 'SFTP Server'),
|
|
('cloud', 'Cloud Service'),
|
|
('iot', 'IoT Device'),
|
|
('monitoring', 'Monitoring System'),
|
|
('analytics', 'Analytics Platform'),
|
|
('other', 'Other System')
|
|
]
|
|
|
|
AUTHENTICATION_TYPES = [
|
|
('none', 'No Authentication'),
|
|
('basic', 'Basic Authentication'),
|
|
('bearer', 'Bearer Token'),
|
|
('api_key', 'API Key'),
|
|
('oauth2', 'OAuth 2.0'),
|
|
('certificate', 'Client Certificate'),
|
|
('custom', 'Custom Authentication')
|
|
]
|
|
|
|
system_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
tenant = models.ForeignKey(
|
|
Tenant,
|
|
on_delete=models.CASCADE,
|
|
related_name='external_systems'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
system_type = models.CharField(
|
|
max_length=20,
|
|
choices=SYSTEM_TYPES
|
|
)
|
|
vendor = models.CharField(max_length=200, blank=True)
|
|
version = models.CharField(max_length=100, blank=True)
|
|
|
|
# Connection details
|
|
base_url = models.URLField(blank=True)
|
|
host = models.CharField(max_length=255, blank=True)
|
|
port = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
validators=[MinValueValidator(1), MaxValueValidator(65535)]
|
|
)
|
|
database_name = models.CharField(max_length=200, blank=True)
|
|
|
|
# Authentication
|
|
authentication_type = models.CharField(
|
|
max_length=20,
|
|
choices=AUTHENTICATION_TYPES,
|
|
default='none'
|
|
)
|
|
authentication_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Configuration
|
|
configuration = models.JSONField(default=dict, blank=True)
|
|
timeout_seconds = models.PositiveIntegerField(default=30)
|
|
retry_attempts = models.PositiveIntegerField(default=3)
|
|
retry_delay_seconds = models.PositiveIntegerField(default=5)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
is_healthy = models.BooleanField(default=False)
|
|
last_health_check = models.DateTimeField(null=True, blank=True)
|
|
health_check_interval = models.PositiveIntegerField(
|
|
default=300, # 5 minutes
|
|
help_text="Health check interval in seconds"
|
|
)
|
|
|
|
# Usage tracking
|
|
connection_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_used_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_external_systems'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_external_system'
|
|
indexes = [
|
|
models.Index(fields=['tenant', 'system_type']),
|
|
models.Index(fields=['tenant', 'is_active']),
|
|
models.Index(fields=['tenant', 'is_healthy']),
|
|
models.Index(fields=['last_health_check']),
|
|
]
|
|
unique_together = [['tenant', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.get_system_type_display()})"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.connection_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
@property
|
|
def is_due_for_health_check(self):
|
|
"""Check if system is due for health check."""
|
|
if not self.last_health_check:
|
|
return True
|
|
next_check = self.last_health_check + timedelta(seconds=self.health_check_interval)
|
|
return timezone.now() >= next_check
|
|
|
|
|
|
class IntegrationEndpoint(models.Model):
|
|
"""
|
|
Integration endpoint configuration.
|
|
"""
|
|
|
|
ENDPOINT_TYPES = [
|
|
('rest_api', 'REST API'),
|
|
('soap', 'SOAP Web Service'),
|
|
('hl7', 'HL7 Interface'),
|
|
('dicom', 'DICOM Service'),
|
|
('ftp', 'FTP Transfer'),
|
|
('sftp', 'SFTP Transfer'),
|
|
('database', 'Database Query'),
|
|
('file', 'File Processing'),
|
|
('webhook', 'Webhook'),
|
|
('queue', 'Message Queue'),
|
|
('email', 'Email'),
|
|
('custom', 'Custom Integration')
|
|
]
|
|
|
|
METHODS = [
|
|
('GET', 'GET'),
|
|
('POST', 'POST'),
|
|
('PUT', 'PUT'),
|
|
('PATCH', 'PATCH'),
|
|
('DELETE', 'DELETE'),
|
|
('HEAD', 'HEAD'),
|
|
('OPTIONS', 'OPTIONS')
|
|
]
|
|
|
|
DIRECTIONS = [
|
|
('inbound', 'Inbound'),
|
|
('outbound', 'Outbound'),
|
|
('bidirectional', 'Bidirectional')
|
|
]
|
|
|
|
endpoint_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='endpoints'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
endpoint_type = models.CharField(
|
|
max_length=20,
|
|
choices=ENDPOINT_TYPES
|
|
)
|
|
|
|
# Endpoint details
|
|
path = models.CharField(max_length=500, blank=True)
|
|
method = models.CharField(
|
|
max_length=10,
|
|
choices=METHODS,
|
|
default='GET'
|
|
)
|
|
direction = models.CharField(
|
|
max_length=20,
|
|
choices=DIRECTIONS,
|
|
default='outbound'
|
|
)
|
|
|
|
# Configuration
|
|
headers = models.JSONField(default=dict, blank=True)
|
|
parameters = models.JSONField(default=dict, blank=True)
|
|
request_format = models.CharField(max_length=50, default='json')
|
|
response_format = models.CharField(max_length=50, default='json')
|
|
|
|
# Data mapping
|
|
request_mapping = models.JSONField(default=dict, blank=True)
|
|
response_mapping = models.JSONField(default=dict, blank=True)
|
|
|
|
# Validation
|
|
request_schema = models.JSONField(default=dict, blank=True)
|
|
response_schema = models.JSONField(default=dict, blank=True)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
execution_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_executed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_integration_endpoints'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_endpoint'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'endpoint_type']),
|
|
models.Index(fields=['external_system', 'is_active']),
|
|
models.Index(fields=['direction']),
|
|
models.Index(fields=['last_executed_at']),
|
|
]
|
|
unique_together = [['external_system', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.external_system.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.execution_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
|
|
class DataMapping(models.Model):
|
|
"""
|
|
Data mapping configuration for field transformations.
|
|
"""
|
|
|
|
MAPPING_TYPES = [
|
|
('field', 'Field Mapping'),
|
|
('value', 'Value Mapping'),
|
|
('transformation', 'Data Transformation'),
|
|
('validation', 'Data Validation'),
|
|
('enrichment', 'Data Enrichment'),
|
|
('filtering', 'Data Filtering')
|
|
]
|
|
|
|
TRANSFORMATION_TYPES = [
|
|
('none', 'No Transformation'),
|
|
('format', 'Format Conversion'),
|
|
('calculation', 'Calculation'),
|
|
('lookup', 'Lookup Table'),
|
|
('concatenation', 'Concatenation'),
|
|
('split', 'Split Field'),
|
|
('default', 'Default Value'),
|
|
('conditional', 'Conditional Logic'),
|
|
('custom', 'Custom Function')
|
|
]
|
|
|
|
mapping_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='data_mappings'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
mapping_type = models.CharField(
|
|
max_length=20,
|
|
choices=MAPPING_TYPES
|
|
)
|
|
|
|
# Source configuration
|
|
source_field = models.CharField(max_length=500)
|
|
source_format = models.CharField(max_length=100, blank=True)
|
|
source_validation = models.JSONField(default=dict, blank=True)
|
|
|
|
# Target configuration
|
|
target_field = models.CharField(max_length=500)
|
|
target_format = models.CharField(max_length=100, blank=True)
|
|
target_validation = models.JSONField(default=dict, blank=True)
|
|
|
|
# Transformation
|
|
transformation_type = models.CharField(
|
|
max_length=20,
|
|
choices=TRANSFORMATION_TYPES,
|
|
default='none'
|
|
)
|
|
transformation_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Validation rules
|
|
is_required = models.BooleanField(default=False)
|
|
validation_rules = models.JSONField(default=dict, blank=True)
|
|
default_value = models.TextField(blank=True)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
usage_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_used_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_data_mappings'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_data_mapping'
|
|
indexes = [
|
|
models.Index(fields=['endpoint', 'mapping_type']),
|
|
models.Index(fields=['endpoint', 'is_active']),
|
|
models.Index(fields=['source_field']),
|
|
models.Index(fields=['target_field']),
|
|
]
|
|
unique_together = [['endpoint', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.endpoint.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.usage_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
|
|
class IntegrationExecution(models.Model):
|
|
"""
|
|
Integration execution tracking and logging.
|
|
"""
|
|
|
|
EXECUTION_TYPES = [
|
|
('manual', 'Manual Execution'),
|
|
('scheduled', 'Scheduled Execution'),
|
|
('triggered', 'Event Triggered'),
|
|
('webhook', 'Webhook Triggered'),
|
|
('api', 'API Call'),
|
|
('batch', 'Batch Processing'),
|
|
('real_time', 'Real-time Processing')
|
|
]
|
|
|
|
STATUSES = [
|
|
('pending', 'Pending'),
|
|
('running', 'Running'),
|
|
('completed', 'Completed'),
|
|
('failed', 'Failed'),
|
|
('cancelled', 'Cancelled'),
|
|
('timeout', 'Timeout'),
|
|
('retry', 'Retrying')
|
|
]
|
|
|
|
execution_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='executions'
|
|
)
|
|
execution_type = models.CharField(
|
|
max_length=20,
|
|
choices=EXECUTION_TYPES
|
|
)
|
|
|
|
# Execution details
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=STATUSES,
|
|
default='pending'
|
|
)
|
|
started_at = models.DateTimeField(auto_now_add=True)
|
|
completed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Request/Response
|
|
request_data = models.JSONField(default=dict, blank=True)
|
|
response_data = models.JSONField(default=dict, blank=True)
|
|
request_size_bytes = models.PositiveIntegerField(default=0)
|
|
response_size_bytes = models.PositiveIntegerField(default=0)
|
|
|
|
# Performance metrics
|
|
processing_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
network_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
# Error handling
|
|
error_message = models.TextField(blank=True)
|
|
error_details = models.JSONField(default=dict, blank=True)
|
|
retry_count = models.PositiveIntegerField(default=0)
|
|
|
|
# External tracking
|
|
external_id = models.CharField(max_length=200, blank=True)
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
|
|
# Metadata
|
|
triggered_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='triggered_integrations'
|
|
)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_execution'
|
|
indexes = [
|
|
models.Index(fields=['endpoint', 'status']),
|
|
models.Index(fields=['endpoint', 'started_at']),
|
|
models.Index(fields=['execution_type']),
|
|
models.Index(fields=['correlation_id']),
|
|
models.Index(fields=['external_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.endpoint.name} - {self.execution_id}"
|
|
|
|
@property
|
|
def duration(self):
|
|
"""Calculate execution duration."""
|
|
if self.completed_at and self.started_at:
|
|
return self.completed_at - self.started_at
|
|
return None
|
|
|
|
@property
|
|
def is_successful(self):
|
|
"""Check if execution was successful."""
|
|
return self.status == 'completed'
|
|
|
|
|
|
class WebhookEndpoint(models.Model):
|
|
"""
|
|
Webhook endpoint configuration for receiving external data.
|
|
"""
|
|
|
|
AUTHENTICATION_TYPES = [
|
|
('none', 'No Authentication'),
|
|
('basic', 'Basic Authentication'),
|
|
('bearer', 'Bearer Token'),
|
|
('api_key', 'API Key'),
|
|
('signature', 'Signature Verification'),
|
|
('ip_whitelist', 'IP Whitelist'),
|
|
('custom', 'Custom Authentication')
|
|
]
|
|
|
|
webhook_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='webhooks'
|
|
)
|
|
name = models.CharField(max_length=200)
|
|
description = models.TextField(blank=True)
|
|
|
|
# Webhook configuration
|
|
url_path = models.CharField(
|
|
max_length=500,
|
|
unique=True,
|
|
help_text="URL path for webhook endpoint"
|
|
)
|
|
allowed_methods = models.JSONField(
|
|
default=list,
|
|
help_text="Allowed HTTP methods"
|
|
)
|
|
|
|
# Authentication
|
|
authentication_type = models.CharField(
|
|
max_length=20,
|
|
choices=AUTHENTICATION_TYPES,
|
|
default='none'
|
|
)
|
|
authentication_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Processing configuration
|
|
data_mapping = models.ForeignKey(
|
|
DataMapping,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='webhooks'
|
|
)
|
|
processing_config = models.JSONField(default=dict, blank=True)
|
|
|
|
# Rate limiting
|
|
rate_limit_per_minute = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Maximum requests per minute"
|
|
)
|
|
rate_limit_per_hour = models.PositiveIntegerField(
|
|
null=True,
|
|
blank=True,
|
|
help_text="Maximum requests per hour"
|
|
)
|
|
|
|
# Status
|
|
is_active = models.BooleanField(default=True)
|
|
|
|
# Usage tracking
|
|
request_count = models.PositiveIntegerField(default=0)
|
|
success_count = models.PositiveIntegerField(default=0)
|
|
failure_count = models.PositiveIntegerField(default=0)
|
|
last_request_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
# Metadata
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_webhooks'
|
|
)
|
|
|
|
class Meta:
|
|
db_table = 'integration_webhook_endpoint'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'is_active']),
|
|
models.Index(fields=['url_path']),
|
|
models.Index(fields=['last_request_at']),
|
|
]
|
|
unique_together = [['external_system', 'name']]
|
|
|
|
def __str__(self):
|
|
return f"{self.external_system.name} - {self.name}"
|
|
|
|
@property
|
|
def success_rate(self):
|
|
"""Calculate success rate percentage."""
|
|
total = self.request_count
|
|
if total == 0:
|
|
return 0.0
|
|
return (self.success_count / total) * 100
|
|
|
|
@property
|
|
def full_url(self):
|
|
"""Get full webhook URL."""
|
|
# This would be constructed based on the application's base URL
|
|
return f"/api/webhooks/{self.url_path}"
|
|
|
|
|
|
class WebhookExecution(models.Model):
|
|
"""
|
|
Webhook execution tracking and logging.
|
|
"""
|
|
|
|
STATUSES = [
|
|
('received', 'Received'),
|
|
('processing', 'Processing'),
|
|
('completed', 'Completed'),
|
|
('failed', 'Failed'),
|
|
('rejected', 'Rejected'),
|
|
('timeout', 'Timeout')
|
|
]
|
|
|
|
execution_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
webhook = models.ForeignKey(
|
|
WebhookEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='executions'
|
|
)
|
|
|
|
# Request details
|
|
method = models.CharField(max_length=10)
|
|
headers = models.JSONField(default=dict, blank=True)
|
|
query_params = models.JSONField(default=dict, blank=True)
|
|
payload = models.JSONField(default=dict, blank=True)
|
|
payload_size_bytes = models.PositiveIntegerField(default=0)
|
|
|
|
# Client information
|
|
client_ip = models.GenericIPAddressField(null=True, blank=True)
|
|
user_agent = models.TextField(blank=True)
|
|
|
|
# Processing
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=STATUSES,
|
|
default='received'
|
|
)
|
|
received_at = models.DateTimeField(auto_now_add=True)
|
|
processed_at = models.DateTimeField(null=True, blank=True)
|
|
processing_time_ms = models.PositiveIntegerField(null=True, blank=True)
|
|
|
|
# Response
|
|
response_status = models.PositiveIntegerField(default=200)
|
|
response_data = models.JSONField(default=dict, blank=True)
|
|
|
|
# Error handling
|
|
error_message = models.TextField(blank=True)
|
|
error_details = models.JSONField(default=dict, blank=True)
|
|
|
|
# External tracking
|
|
external_id = models.CharField(max_length=200, blank=True)
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
|
|
# Metadata
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_webhook_execution'
|
|
indexes = [
|
|
models.Index(fields=['webhook', 'status']),
|
|
models.Index(fields=['webhook', 'received_at']),
|
|
models.Index(fields=['client_ip']),
|
|
models.Index(fields=['correlation_id']),
|
|
models.Index(fields=['external_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.webhook.name} - {self.execution_id}"
|
|
|
|
@property
|
|
def duration(self):
|
|
"""Calculate processing duration."""
|
|
if self.processed_at and self.received_at:
|
|
return self.processed_at - self.received_at
|
|
return None
|
|
|
|
@property
|
|
def is_successful(self):
|
|
"""Check if execution was successful."""
|
|
return self.status == 'completed'
|
|
|
|
|
|
class IntegrationLog(models.Model):
|
|
"""
|
|
Integration activity logging for audit and monitoring.
|
|
"""
|
|
|
|
LOG_LEVELS = [
|
|
('debug', 'Debug'),
|
|
('info', 'Info'),
|
|
('warning', 'Warning'),
|
|
('error', 'Error'),
|
|
('critical', 'Critical')
|
|
]
|
|
|
|
CATEGORIES = [
|
|
('connection', 'Connection'),
|
|
('authentication', 'Authentication'),
|
|
('data_transfer', 'Data Transfer'),
|
|
('transformation', 'Data Transformation'),
|
|
('validation', 'Data Validation'),
|
|
('error', 'Error'),
|
|
('performance', 'Performance'),
|
|
('security', 'Security'),
|
|
('audit', 'Audit'),
|
|
('system', 'System')
|
|
]
|
|
|
|
log_id = models.UUIDField(
|
|
primary_key=True,
|
|
default=uuid.uuid4,
|
|
editable=False
|
|
)
|
|
external_system = models.ForeignKey(
|
|
ExternalSystem,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
endpoint = models.ForeignKey(
|
|
IntegrationEndpoint,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
execution = models.ForeignKey(
|
|
IntegrationExecution,
|
|
on_delete=models.CASCADE,
|
|
related_name='logs',
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
# Log details
|
|
level = models.CharField(
|
|
max_length=20,
|
|
choices=LOG_LEVELS
|
|
)
|
|
category = models.CharField(
|
|
max_length=20,
|
|
choices=CATEGORIES
|
|
)
|
|
message = models.TextField()
|
|
details = models.JSONField(default=dict, blank=True)
|
|
|
|
# Context
|
|
correlation_id = models.CharField(max_length=200, blank=True)
|
|
user = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='integration_logs'
|
|
)
|
|
|
|
# Metadata
|
|
timestamp = models.DateTimeField(auto_now_add=True)
|
|
metadata = models.JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'integration_log'
|
|
indexes = [
|
|
models.Index(fields=['external_system', 'level']),
|
|
models.Index(fields=['endpoint', 'level']),
|
|
models.Index(fields=['execution']),
|
|
models.Index(fields=['level', 'timestamp']),
|
|
models.Index(fields=['category', 'timestamp']),
|
|
models.Index(fields=['correlation_id']),
|
|
]
|
|
|
|
def __str__(self):
|
|
system_name = self.external_system.name if self.external_system else "System"
|
|
return f"{system_name} - {self.get_level_display()}: {self.message[:50]}"
|
|
|