# Generated by Django 4.2.30 on 2026-07-23 11:15

from django.db import migrations, models
from django.db.models import F
import django.db.migrations.operations.special
import django.db.models.deletion


def populate_display_id(apps, schema_editor):
    AnalysisRun = apps.get_model('tianxuan_analysis', 'AnalysisRun')
    AnalysisRun.objects.filter(display_id__isnull=True).update(display_id=F('id'))

class Migration(migrations.Migration):

    replaces = [('tianxuan_analysis', '0001_initial'), ('tianxuan_analysis', '0002_analysisrun_progress_msg_analysisrun_progress_pct_and_more'), ('tianxuan_analysis', '0003_analysisrun_sqlite_table'), ('tianxuan_analysis', '0004_tls_ref_data'), ('tianxuan_analysis', '0005_remove_entity_column'), ('tianxuan_analysis', '0006_analysisrun_display_id_analysisrun_llm_thinking_and_more'), ('tianxuan_analysis', '0007_populate_display_id'), ('tianxuan_analysis', '0008_entityprofile_embedding_z_and_more')]

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='AnalysisRun',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('csv_glob', models.CharField(help_text='Glob pattern for CSV files', max_length=1024)),
                ('config', models.JSONField(blank=True, default=dict, help_text='YAML config snapshot')),
                ('status', models.CharField(choices=[('pending', 'Pending'), ('loading', 'Loading Data'), ('profiling', 'Profiling'), ('aggregating', 'Building Entity Profiles'), ('clustering', 'Clustering'), ('extracting', 'Extracting Features'), ('completed', 'Completed'), ('failed', 'Failed')], default='pending', max_length=32)),
                ('created_at', models.DateTimeField(auto_now_add=True)),
                ('updated_at', models.DateTimeField(auto_now=True)),
                ('error_message', models.TextField(blank=True, default='')),
                ('total_flows', models.IntegerField(blank=True, null=True)),
                ('entity_count', models.IntegerField(blank=True, null=True)),
                ('cluster_count', models.IntegerField(blank=True, null=True)),
                ('entity_column', models.CharField(blank=True, default='', help_text='Auto-detected entity column name', max_length=256)),
                ('progress_msg', models.CharField(blank=True, default='', max_length=256)),
                ('progress_pct', models.IntegerField(default=0)),
                ('run_log', models.TextField(blank=True, default='')),
                ('sqlite_table', models.CharField(blank=True, default='', help_text='SQLite table name for persisted raw data', max_length=256)),
            ],
            options={
                'ordering': ['-created_at'],
            },
        ),
        migrations.CreateModel(
            name='ClusterResult',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('cluster_label', models.IntegerField(help_text='Cluster label (-1 for noise)')),
                ('size', models.IntegerField(help_text='Number of entities in this cluster')),
                ('proportion', models.FloatField(blank=True, help_text='Proportion of total entities', null=True)),
                ('noise_ratio', models.FloatField(blank=True, help_text='Noise ratio of the clustering', null=True)),
                ('silhouette_score', models.FloatField(blank=True, help_text='Silhouette score (sampled if large)', null=True)),
                ('run', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='clusters', to='tianxuan_analysis.analysisrun')),
            ],
            options={
                'unique_together': {('run', 'cluster_label')},
            },
        ),
        migrations.CreateModel(
            name='EntityProfile',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('entity_value', models.CharField(help_text='Entity identifier (IP, domain, etc.)', max_length=512)),
                ('cluster_label', models.IntegerField(blank=True, help_text='Assigned cluster (-1=noise)', null=True)),
                ('feature_json', models.JSONField(blank=True, default=dict, help_text='Aggregated entity features as dict')),
                ('embedding_x', models.FloatField(blank=True, help_text='PCA-2D X coordinate', null=True)),
                ('embedding_y', models.FloatField(blank=True, help_text='PCA-2D Y coordinate', null=True)),
                ('cluster', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='entities', to='tianxuan_analysis.clusterresult')),
                ('run', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='entities', to='tianxuan_analysis.analysisrun')),
            ],
            options={
                'unique_together': {('run', 'entity_value')},
            },
        ),
        migrations.CreateModel(
            name='ClusterFeature',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('feature_name', models.CharField(max_length=256)),
                ('mean', models.FloatField(blank=True, null=True)),
                ('std', models.FloatField(blank=True, null=True)),
                ('median', models.FloatField(blank=True, null=True)),
                ('p25', models.FloatField(blank=True, null=True)),
                ('p75', models.FloatField(blank=True, null=True)),
                ('missing_rate', models.FloatField(blank=True, null=True)),
                ('distinguishing_score', models.FloatField(blank=True, help_text='Z-score or ANOVA F-score', null=True)),
                ('distinguishing_method', models.CharField(default='zscore', max_length=16)),
                ('cluster', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='features', to='tianxuan_analysis.clusterresult')),
            ],
            options={
                'unique_together': {('cluster', 'feature_name')},
            },
        ),
        migrations.RunSQL(
            sql="\nCREATE TABLE IF NOT EXISTS tls_ref_data (\n    id          INTEGER PRIMARY KEY AUTOINCREMENT,\n    field_code  TEXT    NOT NULL UNIQUE,\n    meaning     TEXT    NOT NULL DEFAULT '',\n    data_type   TEXT    NOT NULL DEFAULT ''\n);\n",
            reverse_sql='\nDROP TABLE IF EXISTS tls_ref_data;\n',
        ),
        migrations.RemoveField(
            model_name='analysisrun',
            name='entity_column',
        ),
        migrations.AddField(
            model_name='analysisrun',
            name='display_id',
            field=models.IntegerField(blank=True, help_text='User-facing numeric ID (recycles gaps)', null=True, unique=True),
        ),
        migrations.AddField(
            model_name='analysisrun',
            name='llm_thinking',
            field=models.TextField(blank=True, default='', help_text='Accumulated LLM reasoning / thinking text'),
        ),
        migrations.AddField(
            model_name='analysisrun',
            name='run_type',
            field=models.CharField(choices=[('upload', '手动上传'), ('manual', '手动分析'), ('auto', 'LLM自动')], default='upload', max_length=16),
        ),
        migrations.AddField(
            model_name='analysisrun',
            name='tool_calls_json',
            field=models.JSONField(blank=True, default=list, help_text='Structured tool call list: [{step, name, input, output}, ...]'),
        ),
        migrations.RunPython(
            code=populate_display_id,
            reverse_code=django.db.migrations.operations.special.RunPython.noop,
        ),
        migrations.AddField(
            model_name='entityprofile',
            name='embedding_z',
            field=models.FloatField(blank=True, help_text='UMAP-3D Z coordinate', null=True),
        ),
        migrations.AlterField(
            model_name='entityprofile',
            name='embedding_x',
            field=models.FloatField(blank=True, help_text='UMAP-2D X coordinate', null=True),
        ),
        migrations.AlterField(
            model_name='entityprofile',
            name='embedding_y',
            field=models.FloatField(blank=True, help_text='UMAP-2D Y coordinate', null=True),
        ),
    ]
