from __future__ import unicode_literals

import os
import pickle

from django.utils import six

from django_evolution.builtin_evolutions import BUILTIN_SEQUENCES
from django_evolution.errors import EvolutionException
from django_evolution.models import Evolution, Version
from django_evolution.mutations import RenameModel, SQLMutation
from django_evolution.signature import (has_unique_together_changed,
                                        create_project_sig)
from django_evolution.utils import get_app_label, get_app_name


def get_evolution_sequence(app):
    "Obtain the full evolution sequence for an application"
    app_name = get_app_name(app)

    if app_name in BUILTIN_SEQUENCES:
        return BUILTIN_SEQUENCES[app_name]

    try:
        evolution_module = __import__(app_name + '.evolutions', {}, {}, [''])
        return evolution_module.SEQUENCE
    except:
        return []


def get_unapplied_evolutions(app, database):
    "Obtain the list of unapplied evolutions for an application"
    sequence = get_evolution_sequence(app)
    app_label = get_app_label(app)

    evolutions = Evolution.objects.filter(app_label=app_label).using(database)
    applied = [evo.label for evo in evolutions]

    return [seq for seq in sequence if seq not in applied]


def get_mutations(app, evolution_labels, database):
    """
    Obtain the list of mutations described by the named evolutions.
    """
    # For each item in the evolution sequence. Check each item to see if it is
    # a python file or an sql file.
    try:
        app_name = get_app_name(app)

        if app_name in BUILTIN_SEQUENCES:
            module_name = 'django_evolution.builtin_evolutions'
        else:
            module_name = '%s.evolutions' % app_name

        evolution_module = __import__(module_name, {}, {}, [''])
    except ImportError:
        return []

    mutations = []

    for label in evolution_labels:
        directory_name = os.path.dirname(evolution_module.__file__)

        # The first element is used for compatibility purposes.
        filenames = [
            os.path.join(directory_name, label + '.sql'),
            os.path.join(directory_name, "%s_%s.sql" % (database, label)),
        ]

        found = False

        for filename in filenames:
            if os.path.exists(filename):
                sql_file = open(filename, 'r')
                sql = sql_file.readlines()
                sql_file.close()

                mutations.append(SQLMutation(label, sql))

                found = True
                break

        if not found:
            try:
                module_name = [evolution_module.__name__, label]
                module = __import__('.'.join(module_name),
                                    {}, {}, [module_name])
                mutations.extend(module.MUTATIONS)
            except ImportError:
                raise EvolutionException(
                    'Error: Failed to find an SQL or Python evolution named %s'
                    % label)

    latest_version = Version.objects.current_version(using=database)

    app_label = get_app_label(app)
    old_proj_sig = pickle.loads(str(latest_version.signature))
    proj_sig = create_project_sig(database)

    if app_label in old_proj_sig and app_label in proj_sig:
        # We want to go through now and make sure we're only applying
        # evolutions for models where the signature is different between
        # what's stored and what's current.
        #
        # The reason for this is that we may have just installed a baseline,
        # which would have the up-to-date signature, and we might be trying
        # to apply evolutions on top of that (which would already be applied).
        # These would generate errors. So, try hard to prevent that.
        old_app_sig = old_proj_sig[app_label]
        app_sig = proj_sig[app_label]

        changed_models = set()

        # Find the list of models in the latest signature of this app
        # that aren't in the old signature.
        for model_name, model_sig in six.iteritems(app_sig):
            if (model_name not in old_app_sig or
                old_app_sig[model_name] != model_sig or
                has_unique_together_changed(old_app_sig[model_name],
                                            model_sig)):
                changed_models.add(model_name)

        # Now do the same for models in the old signature, in case the
        # model has been deleted.
        for model_name, model_sig in six.iteritems(old_app_sig):
            if model_name not in app_sig:
                changed_models.add(model_name)

        # We should now have a full list of which models changed. Filter
        # the list of mutations appropriately.
        #
        # Changes affecting a model that was newly-introduced are removed,
        # unless the mutation is a RenameModel, in which case we'll need it
        # during the optimization step (and will remove it if necessary then).
        mutations = [
            mutation
            for mutation in mutations
            if (not hasattr(mutation, 'model_name') or
                mutation.model_name in changed_models or
                isinstance(mutation, RenameModel))
        ]

    return mutations

@@ -57,16 +57,52 @@
 t pickle
+%0Afrom importlib import import_module
 %0A%0Afrom d
@@ -1805,26 +1805,29 @@
 odule = 
-__
 import_
-_
+module
 (module_
@@ -1830,30 +1830,16 @@
 ule_name
-, %7B%7D, %7B%7D, %5B''%5D
 )%0A    ex

Fix a breakage in importing modules in django-evolution.