# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-06-02 23:28
from __future__ import unicode_literals

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
import django.core.validators
from django.db import models, migrations, transaction
from django.db.models import Q
import django.db.models.deletion
import django.utils.timezone
from django.utils import timezone

import archive.models


@transaction.atomic
def check_state_fields_set(apps, schema_editor):
    """
    Asserts that all Runs and RunComponents have their _complete and _successful fields set.
    """
    Run = apps.get_model("archive", "Run")
    RunComponent = apps.get_model("archive", "RunComponent")

    if Run.objects.filter(Q(_complete__isnull=True) | Q(_successful__isnull=True)).exists():
        raise AssertionError("All Runs must have their _complete and _successful flags set before migrating")

    if RunComponent.objects.filter(Q(_complete__isnull=True) | Q(_successful__isnull=True)).exists():
        raise AssertionError("All RunComponents must have their _complete and _successful flags set"
                             "before migrating")


@transaction.atomic
def transition_state_fields(apps, schema_editor):
    """
    Update Runs and RunComponents to use the _runstate and _runcomponentstate fields.
    """
    Run = apps.get_model("archive", "Run")
    RunState = apps.get_model("archive", "RunState")
    RunComponent = apps.get_model("archive", "RunComponent")
    RunComponentState = apps.get_model("archive", "RunComponentState")

    for rc in RunComponent.objects.all():
        if not rc._complete:
            if rc.start_time is None:
                rc._runcomponentstate = RunComponentState.objects.get(name="Pending")
            else:
                rc._runcomponentstate = RunComponentState.objects.get(name="Running")
        else:
            if rc._successful:
                rc._runcomponentstate = RunComponentState.objects.get(name="Successful")
            elif rc.is_cancelled:
                rc._runcomponentstate = RunComponentState.objects.get(name="Cancelled")
            else:
                rc._runcomponentstate = RunComponentState.objects.get(name="Failed")
        rc.save()

    for run in Run.objects.all():
        if not run._complete:
            if run.start_time is None:
                if run.stopped_by is None:
                    run._runstate = RunState.objects.get(name="Cancelling")
                else:
                    run._runstate = RunState.objects.get(name="Pending")
            else:
                if not run._successful:
                    run._runstate = RunState.objects.get(name="Failing")  # Failing takes precedence over Cancelling
                elif run.stopped_by is not None:
                    run._runstate = RunState.objects.get(name="Cancelling")
                else:
                    run._runstate = RunState.objects.get(name="Running")

        else:
            try:
                run.not_enough_CPUs
                exceeds_system_capabilities = True
            except ObjectDoesNotExist:
                exceeds_system_capabilities = False

            if run._successful:
                run._runstate = RunState.objects.get(name="Successful")
            elif run.stopped_by is not None or exceeds_system_capabilities:
                run._runstate = RunState.objects.get(name="Cancelled")
            else:
                run._runstate = RunState.objects.get(name="Failed")
        run.save()


@transaction.atomic
def revert_state_fields(apps, schema_editor):
    """
    Revert Runs/RunComponents to the old state fields.
    """
    Run = apps.get_model("archive", "Run")
    RunComponent = apps.get_model("archive", "RunComponent")

    for rc in RunComponent.objects.all():
        if rc._runcomponentstate.name in ("Pending", "Running"):
            rc._complete = False
            rc._successful = True
            rc.is_cancelled = False
        elif rc._runcomponentstate.name == "Successful":
            rc._complete = True
            rc._successful = True
            rc.is_cancelled = False
        elif rc._runcomponentstate.name == "Cancelled":
            rc._complete = True
            rc._successful = False
            rc.is_cancelled = True
        elif rc._runcomponentstate.name in ("Failed", "Quarantined"):
            rc._complete = True
            rc._successful = False
            rc.is_cancelled = False
        rc.save()

    for run in Run.objects.all():
        if run._runstate.name in ("Pending", "Running"):
            run._complete = False
            run._successful = True
        elif run._runstate.name in ("Cancelling", "Failing"):
            run._complete = False
            run._successful = False
        elif run._runstate.name == "Successful":
            run._complete = True
            run._successful = True
        elif run._runstate.name in ("Cancelled", "Failed", "Quarantined"):
            run._complete = True
            run._successful = False
        run.save()


@transaction.atomic
def create_states(apps, schema_editor):
    """
    Create the built-in RunStates and RunComponentStates.
    """
    RunState = apps.get_model("archive", "RunState")

    pending, _ = RunState.objects.get_or_create(
        name="Pending",
        description="Has not started execution yet"
    )
    assert pending.pk == 1

    running, _ = RunState.objects.get_or_create(
        name="Running",
        description="Executing; no issues reported"
    )
    assert running.pk == 2

    successful, _ = RunState.objects.get_or_create(
        name="Successful",
        description="Execution complete and successful"
    )
    assert successful.pk == 3

    cancelling, _ = RunState.objects.get_or_create(
        name="Cancelling",
        description="Execution cancelling; will finalize when all components are stopped"
    )
    assert cancelling.pk == 4

    cancelled, _ = RunState.objects.get_or_create(
        name="Cancelled",
        description="Execution cancelled and finalized"
    )
    assert cancelled.pk == 5

    failing, _ = RunState.objects.get_or_create(
        name="Failing",
        description="Execution failed; some components running"
    )
    assert failing.pk == 6

    failed, _ = RunState.objects.get_or_create(
        name="Failed",
        description="Execution failed; no components running"
    )
    assert failed.pk == 7

    quarantined, _ = RunState.objects.get_or_create(
        name="Quarantined",
        description="Execution finished successfully but a component is quarantined"
    )
    assert quarantined.pk == 8

    # Create RunComponentStates.
    RunComponentState = apps.get_model("archive", "RunComponentState")

    pending, _ = RunComponentState.objects.get_or_create(
        name="Pending",
        description="Has not started execution yet"
    )
    assert pending.pk == 1

    running, _ = RunComponentState.objects.get_or_create(
        name="Running",
        description="Executing"
    )
    assert running.pk == 2

    successful, _ = RunComponentState.objects.get_or_create(
        name="Successful",
        description="Execution complete and successful"
    )
    assert successful.pk == 3

    cancelled, _ = RunComponentState.objects.get_or_create(
        name="Cancelled",
        description="Execution cancelled"
    )
    assert cancelled.pk == 4

    failed, _ = RunComponentState.objects.get_or_create(
        name="Failed",
        description="Execution finished unsuccessfully"
    )
    assert failed.pk == 5

    quarantined, _ = RunComponentState.objects.get_or_create(
        name="Quarantined",
        description="Execution finished successfully but results have been invalidated"
    )
    assert quarantined.pk == 6


def noop(apps, schema_editor):
    pass


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        # ('pipeline', '__first__'),
        # ('fleet', '0008_rtp_time_queued_default_20151103_1606'),
        # ('librarian', '__first__'),
        # ('librarian', '0004_redacted_20150417_1128'),
        # ('librarian', '0007_transition_SD_to_dataset_20151117_1748'),
        # ('metadata', '0101_squashed'),
        ('auth', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='ExecLog',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('start_time', models.DateTimeField(blank=True, help_text='Starting time', null=True, verbose_name='start time')),
                ('end_time', models.DateTimeField(blank=True, help_text='Ending time', null=True, verbose_name='end time')),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.CreateModel(
            name='MethodOutput',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('return_code', models.IntegerField(null=True, verbose_name='return code')),
                ('output_log', models.FileField(blank=True, help_text='Terminal output of the RunStep Method, i.e. stdout.', null=True, upload_to='Logs', verbose_name='output log')),
                ('error_log', models.FileField(blank=True, help_text='Terminal error output of the RunStep Method, i.e. stderr.', null=True, upload_to='Logs', verbose_name='error log')),
                ('execlog', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='methodoutput', to='archive.ExecLog')),
                ('code_redacted', models.BooleanField(default=False)),
                ('error_redacted', models.BooleanField(default=False)),
                ('output_redacted', models.BooleanField(default=False)),
                ('are_checksums_OK', models.BooleanField(default=True, help_text='Do code checksums match originals?')),
            ],
        ),
        migrations.CreateModel(
            name='Run',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('start_time', models.DateTimeField(blank=True, help_text='Starting time', null=True, verbose_name='start time')),
                ('end_time', models.DateTimeField(blank=True, help_text='Ending time', null=True, verbose_name='end time')),
                ('name', models.CharField(max_length=60, verbose_name='Run name')),
                ('description', models.TextField(blank=True, max_length=1000, verbose_name='Run description')),
                ('user', models.ForeignKey(help_text='User who performed this run', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.CreateModel(
            name='RunComponent',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('start_time', models.DateTimeField(blank=True, help_text='Starting time', null=True, verbose_name='start time')),
                ('end_time', models.DateTimeField(blank=True, help_text='Ending time', null=True, verbose_name='end time')),
                ('reused', models.NullBooleanField(default=None, help_text='Denotes whether this reuses an ExecRecord')),
                ('execrecord', models.IntegerField(db_column='execrecord_id', null=True)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.AddField(
            model_name='execlog',
            name='invoking_record',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invoked_logs', to='archive.RunComponent'),
        ),
        migrations.AddField(
            model_name='execlog',
            name='record',
            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='log', to='archive.RunComponent'),
        ),
        migrations.CreateModel(
            name='RunOutputCable',
            fields=[
                ('runcomponent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archive.RunComponent')),
                ('pipelineoutputcable', models.IntegerField(db_column='pipelineoutputcable_id')),
                ('run', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='runoutputcables', to='archive.Run')),
            ],
            bases=('archive.runcomponent',),
        ),
        migrations.CreateModel(
            name='RunStep',
            fields=[
                ('runcomponent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archive.RunComponent')),
                ('pipelinestep', models.IntegerField(db_column='pipelinestep_id')),
                ('run', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='runsteps', to='archive.Run')),
            ],
            bases=('archive.runcomponent',),
        ),
        migrations.AlterUniqueTogether(
            name='runstep',
            unique_together=set([('run', 'pipelinestep')]),
        ),
        migrations.CreateModel(
            name='RunSIC',
            fields=[
                ('runcomponent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='archive.RunComponent')),
                ('PSIC', models.IntegerField(db_column='PSIC_id')),
                ('dest_runstep', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='RSICs', to='archive.RunStep')),
            ],
            bases=('archive.runcomponent',),
        ),
        migrations.AlterUniqueTogether(
            name='runsic',
            unique_together=set([('dest_runstep', 'PSIC')]),
        ),
        migrations.AlterUniqueTogether(
            name='runoutputcable',
            unique_together=set([('run', 'pipelineoutputcable')]),
        ),
        migrations.AddField(
            model_name='run',
            name='pipeline',
            field=models.IntegerField(db_column='pipeline_id'),
        ),
        migrations.AddField(
            model_name='run',
            name='parent_runstep',
            field=models.OneToOneField(blank=True, help_text='Step of parent run initiating this one as a sub-run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='child_run', to='archive.RunStep'),
        ),
        migrations.AddField(
            model_name='runcomponent',
            name='is_cancelled',
            field=models.BooleanField(default=False, help_text='Denotes whether this has been cancelled'),
        ),
        migrations.AddField(
            model_name='run',
            name='groups_allowed',
            field=models.ManyToManyField(blank=True, help_text='What groups have access?', null=True, related_name='archive_run_has_access_to', to='auth.Group'),
        ),
        migrations.AddField(
            model_name='run',
            name='users_allowed',
            field=models.ManyToManyField(blank=True, help_text='Which users have access?', null=True, related_name='archive_run_has_access_to', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AlterField(
            model_name='run',
            name='user',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
        ),
        migrations.AddField(
            model_name='runcomponent',
            name='_complete',
            field=models.BooleanField(default=False, help_text='Denotes whether this run component has been completed. Private use only'),
        ),
        migrations.AddField(
            model_name='runcomponent',
            name='_successful',
            field=models.BooleanField(default=False, help_text='Denotes whether this has been successful. Private use only!'),
        ),
        migrations.AddField(
            model_name='runcomponent',
            name='_redacted',
            field=models.BooleanField(default=False, help_text='Denotes whether this has been redacted. Private use only!'),
        ),
        migrations.CreateModel(
            name='ExceedsSystemCapabilities',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('threads_requested', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
                ('max_available', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)])),
                ('run', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='not_enough_CPUs', to='archive.Run')),
            ],
        ),
        migrations.CreateModel(
            name='RunInput',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('index', models.PositiveIntegerField()),
                ('run', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='inputs', to='archive.Run')),
                ('dataset', models.IntegerField(db_column='dataset_id')),
            ],
        ),
        migrations.AddField(
            model_name='run',
            name='paused_by',
            field=models.ForeignKey(help_text='User that paused this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pauser', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AddField(
            model_name='run',
            name='purged',
            field=models.BooleanField(default=False),
        ),
        migrations.AddField(
            model_name='run',
            name='sandbox_path',
            field=models.CharField(blank=True, default='', max_length=256),
        ),
        migrations.AddField(
            model_name='run',
            name='stopped_by',
            field=models.ForeignKey(help_text='User that stopped this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='stopper', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AddField(
            model_name='run',
            name='time_queued',
            field=models.DateTimeField(default=django.utils.timezone.now, null=True),
        ),
        migrations.AlterField(
            model_name='run',
            name='name',
            field=models.CharField(blank=True, max_length=60, verbose_name='Run name'),
        ),
        migrations.AlterField(
            model_name='run',
            name='paused_by',
            field=models.ForeignKey(blank=True, help_text='User that paused this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pauser', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AlterField(
            model_name='run',
            name='stopped_by',
            field=models.ForeignKey(blank=True, help_text='User that stopped this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='stopper', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AlterField(
            model_name='run',
            name='paused_by',
            field=models.ForeignKey(blank=True, help_text='User that paused this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='runs_paused', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AlterField(
            model_name='run',
            name='stopped_by',
            field=models.ForeignKey(blank=True, help_text='User that stopped this Run', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='runs_stopped', to=settings.AUTH_USER_MODEL),
        ),
        migrations.AddField(
            model_name='run',
            name='_complete',
            field=models.NullBooleanField(help_text='Denotes whether this run component has been completed. Private use only'),
        ),
        migrations.AddField(
            model_name='run',
            name='_successful',
            field=models.NullBooleanField(help_text='Denotes whether this has been successful. Private use only!'),
        ),
        migrations.AlterField(
            model_name='runcomponent',
            name='_successful',
            field=models.BooleanField(default=True, help_text='Denotes whether this has been successful. Private use only!'),
        ),
        migrations.AlterField(
            model_name='runcomponent',
            name='_complete',
            field=models.NullBooleanField(help_text='Denotes whether this run component has been completed. Private use only'),
        ),
        migrations.AlterField(
            model_name='runcomponent',
            name='_redacted',
            field=models.NullBooleanField(help_text='Denotes whether this has been redacted. Private use only!'),
        ),
        migrations.AlterField(
            model_name='runcomponent',
            name='_successful',
            field=models.NullBooleanField(help_text='Denotes whether this has been successful. Private use only!'),
        ),
        migrations.AlterField(
            model_name='run',
            name='groups_allowed',
            field=models.ManyToManyField(blank=True, help_text='What groups have access?', related_name='archive_run_has_access_to', to='auth.Group'),
        ),
        migrations.AlterField(
            model_name='run',
            name='users_allowed',
            field=models.ManyToManyField(blank=True, help_text='Which users have access?', related_name='archive_run_has_access_to', to=settings.AUTH_USER_MODEL),
        ),
        migrations.CreateModel(
            name='RunComponentState',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=60)),
                ('description', models.TextField()),
            ],
        ),
        migrations.CreateModel(
            name='RunState',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=60)),
                ('description', models.TextField()),
            ],
        ),
        migrations.RunPython(code=create_states, reverse_code=noop),
        migrations.AddField(
            model_name='run',
            name='_runstate',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='archive.RunState'),
        ),
        migrations.AddField(
            model_name='runcomponent',
            name='_runcomponentstate',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='archive.RunComponentState'),
        ),
        migrations.RunPython(code=check_state_fields_set, reverse_code=noop),
        migrations.RunPython(code=transition_state_fields,
                             reverse_code=revert_state_fields),
        migrations.AlterField(
            model_name='run',
            name='_runstate',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='runs', to='archive.RunState'),
        ),
        migrations.AlterField(
            model_name='runcomponent',
            name='_runcomponentstate',
            field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='runcomponents', to='archive.RunComponentState'),
        ),
        migrations.RemoveField(
            model_name='run',
            name='_complete',
        ),
        migrations.RemoveField(
            model_name='run',
            name='_successful',
        ),
        migrations.RemoveField(
            model_name='runcomponent',
            name='_complete',
        ),
        migrations.RemoveField(
            model_name='runcomponent',
            name='_successful',
        ),
        migrations.RemoveField(
            model_name='runcomponent',
            name='is_cancelled',
        ),
    ]
