2025-08-12 13:33:25 +03:00

399 lines
15 KiB
Python

"""
Analytics app forms for CRUD operations.
"""
from django import forms
from django.core.exceptions import ValidationError
from django.utils import timezone
from datetime import datetime, date
from .models import (
Dashboard, DashboardWidget, DataSource, Report,
MetricDefinition
)
class DashboardForm(forms.ModelForm):
"""
Form for creating and updating dashboards.
"""
class Meta:
model = Dashboard
fields = [
'name', 'description', 'dashboard_type', 'is_public',
'layout_config', 'refresh_interval', 'is_active'
]
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter dashboard name'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter dashboard description'
}),
'dashboard_type': forms.Select(attrs={'class': 'form-control'}),
'is_public': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'layout_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 5,
'placeholder': 'Enter JSON layout configuration'
}),
'refresh_interval': forms.NumberInput(attrs={
'class': 'form-control',
'min': 30,
'max': 3600,
'step': 30
}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
help_texts = {
'name': 'Unique name for this dashboard',
'dashboard_type': 'Dashboard type for organization',
'is_public': 'Whether this dashboard is publicly accessible',
'layout_config': 'JSON configuration for dashboard layout',
'refresh_interval': 'Auto-refresh interval in seconds (30-3600)',
}
def clean_name(self):
name = self.cleaned_data['name']
if len(name) < 3:
raise ValidationError('Dashboard name must be at least 3 characters long.')
return name
def clean_refresh_interval(self):
interval = self.cleaned_data['refresh_interval']
if interval and (interval < 30 or interval > 3600):
raise ValidationError('Refresh interval must be between 30 and 3600 seconds.')
return interval
class DashboardWidgetForm(forms.ModelForm):
"""
Form for creating and updating dashboard widgets.
"""
class Meta:
model = DashboardWidget
fields = [
'dashboard', 'name', 'widget_type', 'position_x', 'position_y',
'width', 'height', 'display_config', 'query_config', 'is_active'
]
widgets = {
'dashboard': forms.Select(attrs={'class': 'form-control'}),
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter widget name'
}),
'widget_type': forms.Select(attrs={'class': 'form-control'}),
'position_x': forms.NumberInput(attrs={
'class': 'form-control',
'min': 0,
'max': 12
}),
'position_y': forms.NumberInput(attrs={
'class': 'form-control',
'min': 0
}),
'width': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 12
}),
'height': forms.NumberInput(attrs={
'class': 'form-control',
'min': 1,
'max': 20
}),
'display_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Enter JSON display configuration'
}),
'query_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter query configuration'
}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
help_texts = {
'widget_type': 'Type of widget (chart, table, KPI, etc.)',
'position_x': 'Horizontal position (0-12 grid system)',
'position_y': 'Vertical position',
'width': 'Widget width (1-12 grid columns)',
'height': 'Widget height in grid rows',
'display_config': 'JSON configuration for widget display',
'query_config': 'Query configuration for widget content',
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['dashboard'].queryset = Dashboard.objects.filter(
tenant=user.tenant
).order_by('name')
def clean_position_x(self):
x = self.cleaned_data['position_x']
if x < 0 or x > 12:
raise ValidationError('Position X must be between 0 and 12.')
return x
def clean_width(self):
width = self.cleaned_data['width']
if width < 1 or width > 12:
raise ValidationError('Width must be between 1 and 12.')
return width
class DataSourceForm(forms.ModelForm):
"""
Form for creating and updating data sources.
"""
class Meta:
model = DataSource
fields = [
'name', 'description', 'source_type', 'connection_config',
'authentication_config', 'query_template', 'cache_duration',
'is_active'
]
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter data source name'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter data source description'
}),
'source_type': forms.Select(attrs={'class': 'form-control'}),
'connection_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Enter JSON connection configuration'
}),
'authentication_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter JSON authentication configuration'
}),
'query_template': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Enter query template'
}),
'cache_duration': forms.NumberInput(attrs={
'class': 'form-control',
'min': 0,
'max': 86400,
'value': 300
}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
help_texts = {
'name': 'Unique name for this data source',
'source_type': 'Type of data source (database, API, file, etc.)',
'connection_config': 'JSON configuration for connection settings',
'authentication_config': 'JSON configuration for authentication',
'cache_duration': 'Cache duration in seconds (0-86400)',
}
def clean_name(self):
name = self.cleaned_data['name']
if len(name) < 3:
raise ValidationError('Data source name must be at least 3 characters long.')
return name
def clean_cache_duration(self):
cache_duration = self.cleaned_data['cache_duration']
if cache_duration < 0 or cache_duration > 86400:
raise ValidationError('Cache duration must be between 0 and 86400 seconds.')
return cache_duration
class ReportForm(forms.ModelForm):
"""
Form for creating and updating reports.
"""
class Meta:
model = Report
fields = [
'name', 'description', 'report_type', 'data_source',
'query_config', 'output_format', 'template_config',
'schedule_config', 'is_active'
]
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter report name'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter report description'
}),
'report_type': forms.Select(attrs={'class': 'form-control'}),
'data_source': forms.Select(attrs={'class': 'form-control'}),
'query_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 8,
'placeholder': 'Enter query configuration'
}),
'template_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Enter JSON template configuration'
}),
'output_format': forms.Select(attrs={'class': 'form-control'}),
'schedule_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter JSON schedule configuration'
}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
help_texts = {
'name': 'Unique name for this report',
'report_type': 'Report type for organization',
'query_config': 'Query configuration for report data',
'template_config': 'Template configuration for report formatting',
'output_format': 'Default output format for report',
'schedule_config': 'JSON configuration for automated scheduling',
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['data_source'].queryset = DataSource.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('source_name')
def clean_report_name(self):
name = self.cleaned_data['report_name']
if len(name) < 3:
raise ValidationError('Report name must be at least 3 characters long.')
return name
def clean_query_definition(self):
query = self.cleaned_data['query_definition']
if len(query.strip()) < 10:
raise ValidationError('Query definition must be at least 10 characters long.')
return query
class MetricDefinitionForm(forms.ModelForm):
"""
Form for creating and updating metric definitions.
"""
class Meta:
model = MetricDefinition
fields = [
'name', 'description', 'metric_type', 'data_source',
'calculation_config', 'aggregation_period', 'unit_of_measure',
'target_value', 'warning_threshold', 'critical_threshold',
'aggregation_config', 'is_active'
]
widgets = {
'name': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter metric name'
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter metric description'
}),
'metric_type': forms.Select(attrs={'class': 'form-control'}),
'data_source': forms.Select(attrs={'class': 'form-control'}),
'calculation_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 6,
'placeholder': 'Enter calculation configuration'
}),
'aggregation_period': forms.Select(attrs={'class': 'form-control'}),
'unit_of_measure': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'e.g., %, count, minutes, dollars'
}),
'target_value': forms.NumberInput(attrs={
'class': 'form-control',
'step': 'any',
'placeholder': 'Target value for this metric'
}),
'warning_threshold': forms.NumberInput(attrs={
'class': 'form-control',
'step': 'any',
'placeholder': 'Warning threshold value'
}),
'critical_threshold': forms.NumberInput(attrs={
'class': 'form-control',
'step': 'any',
'placeholder': 'Critical threshold value'
}),
'aggregation_config': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3,
'placeholder': 'Enter aggregation configuration'
}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'})
}
help_texts = {
'name': 'Unique name for this metric',
'metric_type': 'Metric type for organization',
'calculation_config': 'Configuration for metric calculation',
'aggregation_period': 'Period for metric aggregation',
'unit_of_measure': 'Unit of measurement for metric values',
'target_value': 'Target or goal value for this metric',
'warning_threshold': 'Value that triggers warning alerts',
'critical_threshold': 'Value that triggers critical alerts',
'aggregation_config': 'Configuration for metric aggregation',
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['data_source'].queryset = DataSource.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('source_name')
def clean_metric_name(self):
name = self.cleaned_data['metric_name']
if len(name) < 3:
raise ValidationError('Metric name must be at least 3 characters long.')
return name
def clean_query_definition(self):
query = self.cleaned_data['query_definition']
if len(query.strip()) < 5:
raise ValidationError('Query definition must be at least 5 characters long.')
return query
def clean(self):
cleaned_data = super().clean()
target = cleaned_data.get('target_value')
warning = cleaned_data.get('threshold_warning')
critical = cleaned_data.get('threshold_critical')
# Validate threshold relationships
if target and warning and critical:
if warning == critical:
raise ValidationError('Warning and critical thresholds must be different.')
return cleaned_data