""" 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('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