<commit_before># -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2018-08-24 15:08
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):

    dependencies = [
        ('desecapi', '0020_user_locked'),
    ]

    operations = [
        migrations.CreateModel(
            name='Token',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
                ('key', models.CharField(db_index=True, max_length=40, unique=True, verbose_name='Key')),
                ('name', models.CharField(max_length=64, verbose_name='Name')),
                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_tokens', to=settings.AUTH_USER_MODEL, verbose_name='User')),
                ('user_specific_id', models.BigIntegerField()),
            ],
            options={
                'verbose_name': 'Token',
                'verbose_name_plural': 'Tokens',
                'abstract': False,
            },
        ),
        migrations.AlterUniqueTogether(
            name='token',
            unique_together=set([('user', 'user_specific_id')]),
        ),
    ]
<commit_msg>fix(api): align migration from 55a8ae23890 with Model<commit_after># -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2018-08-24 15:08
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):

    dependencies = [
        ('desecapi', '0020_user_locked'),
    ]

    operations = [
        migrations.CreateModel(
            name='Token',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
                ('key', models.CharField(db_index=True, max_length=40, unique=True, verbose_name='Key')),
                ('name', models.CharField(default='', max_length=64, verbose_name='Name')),
                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='auth_tokens', to=settings.AUTH_USER_MODEL, verbose_name='User')),
                ('user_specific_id', models.BigIntegerField(verbose_name='User-Specific ID')),
            ],
        ),
        migrations.AlterUniqueTogether(
            name='token',
            unique_together=set([('user', 'user_specific_id')]),
        ),
    ]
<|endoftext|>


<commit_before>import collections
import os
import uuid
from sets import Set

from simphony.core.cuba import CUBA
from simphony.core.data_container import DataContainer
from simphony.cuds.particles import ParticleContainer
from simlammps.io.lammps_data_file_parser import LammpsDataFileParser
from simlammps.io.lammps_simple_data_handler import LammpsSimpleDataHandler
from simlammps.lammps_particle_container import LammpsParticleContainer
from simlammps.config.domain import get_box

# tuple to hold cache of particles and corresponding LammpsParticleContainer
_PC = collections.namedtuple('_PC', ["cache_pc", "lammps_pc"])

class LammpsFileIoDataManager(object):
    """  Class managing Lammps data information using file-io

    The class performs communicating the data to and from lammps using FILE-IO
    communications (i.e. through input and output files). The class manages
    data existing in Lammps (via lammps data file) and allows this data to be
    queried and to be changed.

    Class maintains and provides LammpsParticleContainer (which implements
    the ABCParticleContainer class) so that users can update the particles
    and bonds and maintains a corresponding cache of a cache of the particle
    information.  This information is read from file whenever its queried
    (see _ensure_up_to_date()) and written to the file whenever the flush()
    method is called.

    Parameters
    ----------
    intput_data_filename :
        name of data-file where information is written to (i.e lammps's input).
    output_data_filename :
        name of data-file where information read from (i.e lammps's output).

    """
    def __init__(self, input_data_filename, output_data_filename):
        self._input_data_filename = input_data_filename
        self._output_data_filename = output_data_filename
        self._number_types = 0

        # map from name to unique name
        self._unames = {}

        # map from unique name to names
        self._names = {}

        # dictionary which consists of
        # (1) cache of particles container
        # (2) and corresponding lammps_particle
        # where the the key is the unique name
        self._pcs = {}

        # map from lammps-id to simphony-uid
        self._lammpsid_to_uid = {}

        # flags to keep track of current state of this
        # cache  (e.g. invalid is True when we no longer
        # have up-to-date information)
        self._invalid = False

    def get_name(self, uname):
        """
        Get the name of a particle container

        Parameters
        ----------
        uname : string
            unique name of particle container

        Returns
        -------
        string
            name of particle container

        """
        return self._names[uname]

    def rename(self, uname, new_name):
        """ Rename a particle container

        Parameters
        ---------
        uname :
            unique name of particle container to be renamed
        new_name :
            new name of the particle container

        """
        del self._unames[self._names[uname]]
        self._unames[new_name] = uname
        self._names[uname] = new_name

    def __iter__(self):
        """ Iter over names of particle containers

        """
        for name in self._unames:
            yield name

    def __contains__(self, name):
        """ Checks if particle container with this name exists

        """
        return name in self._unames

    def __getitem__(self, name):
        """ Returns particle container with this name

        """
        return self._pcs[self._unames[name]].lammps_pc

    def __delitem__(self, name):
        """Deletes lammps particle container and associated cache

        """
        del self._pcs[self._unames[name]]
        del self._unames[name]

    def get_data(self, uname):
        """Returns data container associated with particle container

        """
        return self._pcs[uname].cache_pc.data

    def set_data(self, data, uname):
        """Sets data container associated with particle container

        """
        self._pcs[uname].cache_pc.data = data

    def new_particle_container(self, particle_container):
        """Add new particle container to this manager.

        Parameters
        ----------
        particle_container : ABCParticleContainer
            paticle container to be added

        Returns
        -------
        LammpsParticleContainer

        """

        # generate a unique name for this particle container
        # that will not change over the lifetime of the wrapper.
        uname = uuid.uuid4()

        self._unames[particle_container.name] = uname
        self._names[uname] = particle_container.name

        # create empty stand-alone particle container
        # to use as a cache of for input/output to LAMMPS
        pc = ParticleContainer(name="_")
        pc.data = DataContainer(particle_container.data)
        for p in particle_container.iter_particles():
            pc.add_particle(p)
        for p in particle_container.iter_bonds():
            pc.add_bond(p)

        lammps_pc = LammpsParticleContainer(self, uname)
        self._pcs[uname] = _PC(cache_pc=pc, lammps_pc=lammps_pc)
        return lammps_pc

    def get_particle(self, uid, uname):
        """Get particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.get_particle(uid)

    def update_particle(self, particle, uname):
        """Update particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.update_particle(particle)

    def add_particle(self, particle, uname):
        """Add particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        if self._pcs[uname].cache_pc.has_particle(particle.uid):
            raise ValueError(
                "particle with same uid ({}) alread exists".format(
                    particle.uid))
        else:
            return self._pcs[uname].cache_pc.add_particle(particle)

    def remove_particle(self, uid, uname):
        """Remove particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        self._pcs[uname].cache_pc.remove_particle(uid)

    def iter_particles(self, uname, uids=None):
        """Iterate over the particles of a certain type

        Parameters
        ----------
        uids : list of particle uids
            sequence of uids of particles that should be iterated over. If
            ids is None then all particles will be iterated over.

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.iter_particles(uids)

    def flush(self):
        if self._pcs:
            self._write_data_file(self._input_data_filename)
        else:
            raise RuntimeError(
                "No particles.  Lammps cannot run without a particle")
        # TODO handle properly when there are no particle containers
        # or when some of them do not contain any particles
        # (i.e. someone has deleted all the particles)

    def mark_as_invalid(self):
        self._invalid = True

# Private methods #######################################################
    def _ensure_up_to_date(self):
        if self._invalid:
            self._update_from_lammps()
            self._invalid = False

    def _update_from_lammps(self):
        """Read from file and update cache

        """
        assert os.path.isfile(self._output_data_filename)

        handler = LammpsSimpleDataHandler()
        parser = LammpsDataFileParser(handler)
        parser.parse(self._output_data_filename)

        atoms = handler.get_atoms()
        velocities = handler.get_velocities()
        masses = handler.get_masses()

        assert(len(atoms) == len(velocities))

        type_data = {}

        for atom_type, mass in masses.iteritems():
            type_data[atom_type] = DataContainer()
            type_data[atom_type][CUBA.MASS] = mass

        # update each particle container with these
        # material-specific attributes
        for _, pc in self._pcs.iteritems():
            data = type_data[pc.cache_pc.data[CUBA.MATERIAL_TYPE]]
            for key, value in data.iteritems():
                pc.cache_pc.data[key] = value

        for lammpsid, atom in atoms.iteritems():
            uname, uid = self._lammpsid_to_uid[lammpsid]
            cache_pc = self._pcs[uname].cache_pc
            p = cache_pc.get_particle(uid)
            p.coordinates = tuple(atom[1:4])
            cache_pc.update_particle(p)

            # set the pc's material type
            # (current requirement/assumption is that each
            # pc has particle containers of one type)
            atom_type = atom[0]
            cache_pc.data[CUBA.MATERIAL_TYPE] = atom_type

        for lammpsid, velocity in velocities.iteritems():
            uname, uid = self._lammpsid_to_uid[lammpsid]
            cache_pc = self._pcs[uname].cache_pc
            p = cache_pc.get_particle(uid)
            p.data[CUBA.VELOCITY] = tuple(velocity)
            cache_pc.update_particle(p)

    def _write_data_file(self, filename):
        """ Write data file containing current state of simulation

        """
        lines = ["LAMMPS data file via write_data"
                 ", file written by SimPhony\n\n"]

        # recreate (and store) map from lammps-id to simphony-id
        self._lammpsid_to_uid = {}

        # determine the number of particles
        # and collect the different material types
        # in oder to determine the number of types
        num_particles = 0
        types = Set()
        for _, pc in self._pcs.iteritems():
            types.add(pc.cache_pc.data[CUBA.MATERIAL_TYPE])
            num_particles += sum(1 for _ in pc.cache_pc.iter_particles())

        box = get_box([pc.lammps_pc for _, pc in self._pcs.iteritems()])

        lines.append('{} atoms\n'.format(num_particles))
        lines.append('{} atom types\n\n'.format(len(types)))
        lines.append("\n")
        lines.append(box)
        lines.append("\n")
        lines.append("Masses\n\n")

        material_type_to_mass = self._get_mass()
        for material_type in sorted(material_type_to_mass):
            mass = material_type_to_mass[material_type]
            lines.append('{} {}\n'.format(material_type, mass))
        lines.append("\n")

        with open(filename, 'w') as f:
            f.writelines(lines)

            # keep track of what uid(for a specific pc)
            # corresponds to which lammpsid
            uid_to_lammpsid = {}

            f.write("Atoms\n\n")
            f.writelines(self._generate_atoms_lines(uid_to_lammpsid))

            f.write("\nVelocities\n\n")
            f.writelines(self._generate_velocities_lines(uid_to_lammpsid))
            f.write("\n")

    def _generate_atoms_lines(self, uid_to_lammpsid):
        """ Generate atom lines for a data file.

        Parameters
        ----------
        uid_to_lammpsid : dict
            empty dictionary which will be filled with uname and uid
            of each particle as a key and the corresponding lammpsid
            as the value

        Returns
        -------
        generator
            atom line strings for data file

        """
        lammpsid = 0
        for uname, pc in self._pcs.iteritems():
            for p in pc.cache_pc.iter_particles():
                lammpsid += 1
                self._lammpsid_to_uid[lammpsid] = (uname, p.uid)
                uid_to_lammpsid[(uname, p.uid)] = lammpsid
                atom_type = pc.cache_pc.data[CUBA.MATERIAL_TYPE]
                coord = '{0[0]:.16e} {0[1]:.16e} {0[2]:.16e}'.format(
                    p.coordinates)
                yield '{0} {1} {2} 0 0 0\n'.format(
                    lammpsid, atom_type, coord)

    def _generate_velocities_lines(self, uid_to_lammpsid):
        """ Generate velocity lines for a data file.

        Parameters
        ----------
        uid_to_lammpsid : dict
            dictionary filled with uname and uid of each particle as a
            key and the corresponding lammpsid as the value

        Returns
        -------
        generator
            velocities line strings for data file

        """
        for uname, pc in self._pcs.iteritems():
            for p in pc.cache_pc.iter_particles():
                lammpsid = uid_to_lammpsid[(uname, p.uid)]
                vel = '{0[0]:.16e} {0[1]:.16e} {0[2]:.16e}'.format(
                    p.data[CUBA.VELOCITY])
                yield '{0} {1}\n'.format(lammpsid, vel)

    def _get_mass(self):
        """ Get a dictionary from 'material type' to 'mass'.

        Check that fits what LAMMPS can handle as well
        as ensure that it works with the limitations
        of how we are currently handling this info.

        Raises
        ------
        RuntimeError :
            if there are particles' which have the same
            material type (CUBA.MATERIAL_TYPE) but different
            masses (CUBA.MASS)

        """
        mass = {}
        for uname, pc in self._pcs.iteritems():
            data = pc.cache_pc.data
            material_type = data[CUBA.MATERIAL_TYPE]
            if material_type in mass:
                # check that mass is consistent with an matching type
                if data[CUBA.MASS] != mass[material_type]:
                    raise RuntimeError(
                        "Each material type must have the same mass")
            else:
                mass[material_type] = data[CUBA.MASS]
        return mass
<commit_msg>add missing method<commit_after>import collections
import os
import uuid
from sets import Set

from simphony.core.cuba import CUBA
from simphony.core.data_container import DataContainer
from simphony.cuds.particles import ParticleContainer
from simlammps.io.lammps_data_file_parser import LammpsDataFileParser
from simlammps.io.lammps_simple_data_handler import LammpsSimpleDataHandler
from simlammps.lammps_particle_container import LammpsParticleContainer
from simlammps.config.domain import get_box

# tuple to hold cache of particles and corresponding LammpsParticleContainer
_PC = collections.namedtuple('_PC', ["cache_pc", "lammps_pc"])

class LammpsFileIoDataManager(object):
    """  Class managing Lammps data information using file-io

    The class performs communicating the data to and from lammps using FILE-IO
    communications (i.e. through input and output files). The class manages
    data existing in Lammps (via lammps data file) and allows this data to be
    queried and to be changed.

    Class maintains and provides LammpsParticleContainer (which implements
    the ABCParticleContainer class) so that users can update the particles
    and bonds and maintains a corresponding cache of a cache of the particle
    information.  This information is read from file whenever its queried
    (see _ensure_up_to_date()) and written to the file whenever the flush()
    method is called.

    Parameters
    ----------
    intput_data_filename :
        name of data-file where information is written to (i.e lammps's input).
    output_data_filename :
        name of data-file where information read from (i.e lammps's output).

    """
    def __init__(self, input_data_filename, output_data_filename):
        self._input_data_filename = input_data_filename
        self._output_data_filename = output_data_filename
        self._number_types = 0

        # map from name to unique name
        self._unames = {}

        # map from unique name to names
        self._names = {}

        # dictionary which consists of
        # (1) cache of particles container
        # (2) and corresponding lammps_particle
        # where the the key is the unique name
        self._pcs = {}

        # map from lammps-id to simphony-uid
        self._lammpsid_to_uid = {}

        # flags to keep track of current state of this
        # cache  (e.g. invalid is True when we no longer
        # have up-to-date information)
        self._invalid = False

    def get_name(self, uname):
        """
        Get the name of a particle container

        Parameters
        ----------
        uname : string
            unique name of particle container

        Returns
        -------
        string
            name of particle container

        """
        return self._names[uname]

    def rename(self, uname, new_name):
        """ Rename a particle container

        Parameters
        ---------
        uname :
            unique name of particle container to be renamed
        new_name :
            new name of the particle container

        """
        del self._unames[self._names[uname]]
        self._unames[new_name] = uname
        self._names[uname] = new_name

    def __iter__(self):
        """ Iter over names of particle containers

        """
        for name in self._unames:
            yield name

    def __contains__(self, name):
        """ Checks if particle container with this name exists

        """
        return name in self._unames

    def __getitem__(self, name):
        """ Returns particle container with this name

        """
        return self._pcs[self._unames[name]].lammps_pc

    def __delitem__(self, name):
        """Deletes lammps particle container and associated cache

        """
        del self._pcs[self._unames[name]]
        del self._unames[name]

    def get_data(self, uname):
        """Returns data container associated with particle container

        """
        return self._pcs[uname].cache_pc.data

    def set_data(self, data, uname):
        """Sets data container associated with particle container

        """
        self._pcs[uname].cache_pc.data = data

    def new_particle_container(self, particle_container):
        """Add new particle container to this manager.

        Parameters
        ----------
        particle_container : ABCParticleContainer
            paticle container to be added

        Returns
        -------
        LammpsParticleContainer

        """

        # generate a unique name for this particle container
        # that will not change over the lifetime of the wrapper.
        uname = uuid.uuid4()

        self._unames[particle_container.name] = uname
        self._names[uname] = particle_container.name

        # create empty stand-alone particle container
        # to use as a cache of for input/output to LAMMPS
        pc = ParticleContainer(name="_")
        pc.data = DataContainer(particle_container.data)
        for p in particle_container.iter_particles():
            pc.add_particle(p)
        for p in particle_container.iter_bonds():
            pc.add_bond(p)

        lammps_pc = LammpsParticleContainer(self, uname)
        self._pcs[uname] = _PC(cache_pc=pc, lammps_pc=lammps_pc)
        return lammps_pc

    def get_particle(self, uid, uname):
        """Get particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.get_particle(uid)

    def update_particle(self, particle, uname):
        """Update particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.update_particle(particle)

    def add_particle(self, particle, uname):
        """Add particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        if self._pcs[uname].cache_pc.has_particle(particle.uid):
            raise ValueError(
                "particle with same uid ({}) alread exists".format(
                    particle.uid))
        else:
            return self._pcs[uname].cache_pc.add_particle(particle)

    def remove_particle(self, uid, uname):
        """Remove particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        self._ensure_up_to_date()
        self._pcs[uname].cache_pc.remove_particle(uid)

    def has_particle(self, uid, uname):
        """Has particle

        Parameters
        ----------
        uid :
            uid of particle
        uname : string
            name of particle container

        """
        return self._pcs[uname].cache_pc.has_particle(uid)

    def iter_particles(self, uname, uids=None):
        """Iterate over the particles of a certain type

        Parameters
        ----------
        uids : list of particle uids
            sequence of uids of particles that should be iterated over. If
            ids is None then all particles will be iterated over.

        """
        self._ensure_up_to_date()
        return self._pcs[uname].cache_pc.iter_particles(uids)

    def flush(self):
        if self._pcs:
            self._write_data_file(self._input_data_filename)
        else:
            raise RuntimeError(
                "No particles.  Lammps cannot run without a particle")
        # TODO handle properly when there are no particle containers
        # or when some of them do not contain any particles
        # (i.e. someone has deleted all the particles)

    def mark_as_invalid(self):
        self._invalid = True

# Private methods #######################################################
    def _ensure_up_to_date(self):
        if self._invalid:
            self._update_from_lammps()
            self._invalid = False

    def _update_from_lammps(self):
        """Read from file and update cache

        """
        assert os.path.isfile(self._output_data_filename)

        handler = LammpsSimpleDataHandler()
        parser = LammpsDataFileParser(handler)
        parser.parse(self._output_data_filename)

        atoms = handler.get_atoms()
        velocities = handler.get_velocities()
        masses = handler.get_masses()

        assert(len(atoms) == len(velocities))

        type_data = {}

        for atom_type, mass in masses.iteritems():
            type_data[atom_type] = DataContainer()
            type_data[atom_type][CUBA.MASS] = mass

        # update each particle container with these
        # material-specific attributes
        for _, pc in self._pcs.iteritems():
            data = type_data[pc.cache_pc.data[CUBA.MATERIAL_TYPE]]
            for key, value in data.iteritems():
                pc.cache_pc.data[key] = value

        for lammpsid, atom in atoms.iteritems():
            uname, uid = self._lammpsid_to_uid[lammpsid]
            cache_pc = self._pcs[uname].cache_pc
            p = cache_pc.get_particle(uid)
            p.coordinates = tuple(atom[1:4])
            cache_pc.update_particle(p)

            # set the pc's material type
            # (current requirement/assumption is that each
            # pc has particle containers of one type)
            atom_type = atom[0]
            cache_pc.data[CUBA.MATERIAL_TYPE] = atom_type

        for lammpsid, velocity in velocities.iteritems():
            uname, uid = self._lammpsid_to_uid[lammpsid]
            cache_pc = self._pcs[uname].cache_pc
            p = cache_pc.get_particle(uid)
            p.data[CUBA.VELOCITY] = tuple(velocity)
            cache_pc.update_particle(p)

    def _write_data_file(self, filename):
        """ Write data file containing current state of simulation

        """
        lines = ["LAMMPS data file via write_data"
                 ", file written by SimPhony\n\n"]

        # recreate (and store) map from lammps-id to simphony-id
        self._lammpsid_to_uid = {}

        # determine the number of particles
        # and collect the different material types
        # in oder to determine the number of types
        num_particles = 0
        types = Set()
        for _, pc in self._pcs.iteritems():
            types.add(pc.cache_pc.data[CUBA.MATERIAL_TYPE])
            num_particles += sum(1 for _ in pc.cache_pc.iter_particles())

        box = get_box([pc.lammps_pc for _, pc in self._pcs.iteritems()])

        lines.append('{} atoms\n'.format(num_particles))
        lines.append('{} atom types\n\n'.format(len(types)))
        lines.append("\n")
        lines.append(box)
        lines.append("\n")
        lines.append("Masses\n\n")

        material_type_to_mass = self._get_mass()
        for material_type in sorted(material_type_to_mass):
            mass = material_type_to_mass[material_type]
            lines.append('{} {}\n'.format(material_type, mass))
        lines.append("\n")

        with open(filename, 'w') as f:
            f.writelines(lines)

            # keep track of what uid(for a specific pc)
            # corresponds to which lammpsid
            uid_to_lammpsid = {}

            f.write("Atoms\n\n")
            f.writelines(self._generate_atoms_lines(uid_to_lammpsid))

            f.write("\nVelocities\n\n")
            f.writelines(self._generate_velocities_lines(uid_to_lammpsid))
            f.write("\n")

    def _generate_atoms_lines(self, uid_to_lammpsid):
        """ Generate atom lines for a data file.

        Parameters
        ----------
        uid_to_lammpsid : dict
            empty dictionary which will be filled with uname and uid
            of each particle as a key and the corresponding lammpsid
            as the value

        Returns
        -------
        generator
            atom line strings for data file

        """
        lammpsid = 0
        for uname, pc in self._pcs.iteritems():
            for p in pc.cache_pc.iter_particles():
                lammpsid += 1
                self._lammpsid_to_uid[lammpsid] = (uname, p.uid)
                uid_to_lammpsid[(uname, p.uid)] = lammpsid
                atom_type = pc.cache_pc.data[CUBA.MATERIAL_TYPE]
                coord = '{0[0]:.16e} {0[1]:.16e} {0[2]:.16e}'.format(
                    p.coordinates)
                yield '{0} {1} {2} 0 0 0\n'.format(
                    lammpsid, atom_type, coord)

    def _generate_velocities_lines(self, uid_to_lammpsid):
        """ Generate velocity lines for a data file.

        Parameters
        ----------
        uid_to_lammpsid : dict
            dictionary filled with uname and uid of each particle as a
            key and the corresponding lammpsid as the value

        Returns
        -------
        generator
            velocities line strings for data file

        """
        for uname, pc in self._pcs.iteritems():
            for p in pc.cache_pc.iter_particles():
                lammpsid = uid_to_lammpsid[(uname, p.uid)]
                vel = '{0[0]:.16e} {0[1]:.16e} {0[2]:.16e}'.format(
                    p.data[CUBA.VELOCITY])
                yield '{0} {1}\n'.format(lammpsid, vel)

    def _get_mass(self):
        """ Get a dictionary from 'material type' to 'mass'.

        Check that fits what LAMMPS can handle as well
        as ensure that it works with the limitations
        of how we are currently handling this info.

        Raises
        ------
        RuntimeError :
            if there are particles' which have the same
            material type (CUBA.MATERIAL_TYPE) but different
            masses (CUBA.MASS)

        """
        mass = {}
        for uname, pc in self._pcs.iteritems():
            data = pc.cache_pc.data
            material_type = data[CUBA.MATERIAL_TYPE]
            if material_type in mass:
                # check that mass is consistent with an matching type
                if data[CUBA.MASS] != mass[material_type]:
                    raise RuntimeError(
                        "Each material type must have the same mass")
            else:
                mass[material_type] = data[CUBA.MASS]
        return mass
<|endoftext|>


<commit_before>import logging

logger = logging.getLogger(__name__)

def main():
    pass

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    main()<commit_msg>cli built<commit_after><|endoftext|>


<commit_before>"""
Lagrange 2D
Stoecker "Design of Thermal Systems", 2nd ed. page 62.
Bruce Wernick
22 September 2017 5:21:48
"""

def lagrange2d(X, Y, Z):
  """lagrangian interpolation of 2-D table.
  X[0..m-1] and Y[0..n-1] are the independent variables.
  Z[0..n-1,0..m-1] is the dependent variable
  """
  m = len(X)
  n = len(Y)
  if not(len(Z) == n) or not(len(Z[0]) == m):
    raise ValueError('Z dimensions must be the same as X by Y!')
  def f(x, y):
    z = 0.0
    for i in range(m):
      for j in range(n):
        p = 1.0
        for k in range(m):
          if i == k: continue
          p *= (x - X[k]) / (X[i] - X[k])
        for k in range(n):
          if j == k: continue
          p *= (y - Y[k]) / (Y[j] - Y[k])
        z += Z[j][i] * p
    return z
  return f

def polyARI(coeff,x,y):
  a,b,c,d,e,f,g,h,i,j = coeff
  return a+x*(x*(x*g+d)+b)+y*(y*(y*j+f)+c)+x*y*(e+x*h+y*i)

def frange(a, b, n):
  r = []
  for i in range(n):
    r.append(a + (b-a)*float(i)/(n-1.0))
  return r

# ------------------------------------------------------------------------------

if __name__ == '__main__':

  # thermal engineering example
  # te = evap temp [degC]
  # tc = cond temp [degC]
  # qe = cooling duty [kW]
  te = [-5.0, 0.0, 5.0, 6.0, 10.0, 12.5]
  tc = [32.0, 35.0, 45.0, 47.0, 55.0]

  # build some realistic data using ari coefficients
  a = [17.28,8.4e-1,1.09e-1,1.5e-2,-7.0e-3,-5.66e-3,
       1.014e-4,-1.56e-4,2.06e-05,3.72e-05]
  qe = [[polyARI(a, x, y) for x in te] for y in tc]

  # create a lagrangian interpolator
  q = lagrange2d(te,tc,qe)

  # show the differences
  tcRange = frange(tc[0], tc[-1], 7)
  teRange = frange(te[0], te[-1], 9)
  print((' '*8 + 'Difference table'))
  print((' '*6,))
  for x in teRange:
    print(('{:6.2f}'.format(x),))
  print()
  print(('-'*69))
  for y in tcRange:
    print(('{:6.2f}'.format(y),))
    for x in teRange:
      q1 = q(x, y)
      q2 = polyARI(a, x, y)
      print(('{:6.2f}'.format(q2),))
    print()

<commit_msg>Delete lagrange2.py<commit_after><|endoftext|>


<commit_before>"""Provide APP constant for the purposes of manually running the flask app

For example, build the front end, then run the app with environment variables::
        cd smif/app/
        npm run build
        cd ../http_api/
        FLASK_APP=smif.http_api.app FLASK_DEBUG=1 flask run
"""
import os

from smif.data_layer import DatafileInterface
from smif.http_api import create_app

def get_data_interface():
    """Return a data_layer.DataInterface
    """
    return DatafileInterface(
        os.path.join(os.path.dirname(__file__), '..', '..', 'tests', 'fixtures', 'single_run')
    )

APP = create_app(
    static_folder=os.path.join(os.path.dirname(__file__), '..', 'app', 'dist'),
    template_folder=os.path.join(os.path.dirname(__file__), '..', 'app', 'dist'),
    get_data_interface=get_data_interface
)
<commit_msg>Update path sample_project<commit_after>"""Provide APP constant for the purposes of manually running the flask app

For example, build the front end, then run the app with environment variables::
        cd smif/app/
        npm run build
        cd ../http_api/
        FLASK_APP=smif.http_api.app FLASK_DEBUG=1 flask run
"""
import os

from smif.data_layer import DatafileInterface
from smif.http_api import create_app

def get_data_interface():
    """Return a data_layer.DataInterface
    """
    return DatafileInterface(
        os.path.join(os.path.dirname(__file__), '..', '..', 'smif', 'sample_project')
    )

APP = create_app(
    static_folder=os.path.join(os.path.dirname(__file__), '..', 'app', 'dist'),
    template_folder=os.path.join(os.path.dirname(__file__), '..', 'app', 'dist'),
    get_data_interface=get_data_interface
)
<|endoftext|>


<commit_before>"""
Implementation of the functions related to grid-convergence study.
"""

import os

import numpy
from matplotlib import pyplot

from field import Field

def get_exact_solution(simulations, mask, *arguments):
  """
  Gets the exact solution on the finest grid available.
  If no analytical solution is available, the solution on the finest grid is
  considered to be exact.

  Parameters
  ----------
  simulations: dictionary of (string, Simulation object) items
    Solutions on grids with constant refinement ratio.
  mask: string
    Key of the dictionary simulations to define the simulation
    whose grid will be used to compute the analytical solution.
  arguments:
    Arguments for the analytical plug-in
    (arguments of the __init__ method of the class).

  Returns
  -------
  exact: SolutionClass object
    Contains the exact solution.
  """
  if arguments:
    from solutions.dispatcher import dispatcher
    SolutionClass = dispatcher[arguments[0]]
    # compute analytical solution
    exact = SolutionClass(simulations[mask].grid[0],
                          simulations[mask].grid[1],
                          *arguments[1:])
  else:
    # assume finest grid contains exact solution if no analytical solution
    finest = simulations.keys()[-1]
    exact = simulations[finest]
    del simulations[finest]
  return exact

def plot_grid_convergence(simulations, exact,
                          mask=None,
                          field_names=None,
                          norms=None,
                          save_directory=os.path.join(os.getcwd(), 'images'),
                          save_name=None,
                          fmt='png',
                          dpi=100,
                          style=None,
                          show=False):
  """
  Plots the grid-convergence in a log-log figure.

  Parameters
  ----------
  simulations: list of Simulation objects
    List of the cases to include in the figure.
  exact: Solution or Simulation object
    The exact solution;
    either the solution on the finest grid or a Solution object.
  mask: Simulation object, optional
    Simulation whose grids are used to restrict the solution;
    default: None.
  field_names: list of strings, optional
    Names of the fields to include in the figure;
    default: None.
  save_directory: string, optional
    Directory where to save the figure;
    default: <current directory>.
  save_name: string, optional
    Name of the file to save;
    default: None (does not save).
  fmt: string, optional
    Format of the file to save;
    default: 'png'.
  dpi: integer, optional
    Dots per inch (resolution);
    default: 100.
  style: string, optional
    Path of the Matplotlib style-sheet to use;
    default: None.
  show: boolean, optional
    Set 'True' if you want to display the figure;
    default: False.
  """
  print('[info] plotting the grid convergence ...')
  try:
    pyplot.style.use(style)
  except:
    try:
      pyplot.style.use(os.environ['SNAKE'],
                       'snake',
                       'styles',
                       style + '.mplstyle')
    except:
      pass
  fig, ax = pyplot.subplots(figsize=(6, 6))
  ax.grid(True, zorder=0)
  ax.set_xlabel('grid-spacing')
  ax.set_ylabel('differences')
  grid_spacings = [case.get_grid_spacing() for case in simulations]
  norm_labels = {'L2': '$L_2$', 'Linf': '$L_\infty$'}
  for field_name in field_names:
    for norm in norms:
      differences = [case.get_difference(exact, field_name,
                                         mask=mask,
                                         norm=norm)
                     for case in simulations]
      ax.plot(grid_spacings, differences,
              label='{} - {}-norm'.format(field_name, norm_labels[norm]),
              marker='o',
              zorder=10)
  ax.legend()
  ax.set_xlim(0.1 * min(grid_spacings), 10.0 * max(grid_spacings))
  pyplot.xscale('log')
  pyplot.yscale('log')
  ax.axis('equal')
  # save and display
  if save_name:
    print('[info] saving figure ...')
    if not os.path.isdir(save_directory):
      os.makedirs(save_directory)
    time_step = simulations[0].fields[field_names[0]].time_step
    pyplot.savefig(os.path.join(save_directory,
                                '{}{:0>7}.{}'.format(save_name,
                                                     time_step,
                                                     fmt)),
                   bbox_inches='tight',
                   dpi=dpi,
                   format=fmt)
  if show:
    print('[info] displaying figure ...')
    pyplot.show()
  pyplot.close()

def get_observed_orders(simulations, field_names, mask,
                        save_directory=os.getcwd(),
                        save_name='observedOrders'):
  """
  Computes the observed orders of convergence using the solution
  on three grids with constant grid refinement ratio.

  Parameters
  ----------
  simulations: 3-list of Simulation objects
    Contains the simulations.
  field_names: list of strings
    List of field names whose observed order of convergence will be computed.
  mask: Simulation object
    Simulation whose grids are used as a mask to restrict the solutions.
  save_directory: string, optional
    Directory where to save the file;
    default: <current directory>.
  save_name: string, optional
    Prefix of the name of the .dat files to save;
    default: 'observedOrders'.

  Returns
  -------
  alpha: dictionary of (string, float) items
    Contains the observed order of convergence for each variable asked.
  """
  print('[info] computing observed orders of '
        'convergence using the grids {} ...'
        ''.format([case.description for case in simulations]))
  coarse, medium, fine = simulations
  ratio = coarse.get_grid_spacing() / medium.get_grid_spacing()
  alpha = {}  # will contain observed order of convergence
  for name in field_names:
    grid = [mask.fields[name].x, mask.fields[name].y]
    alpha[name] = get_observed_order(coarse.fields[name],
                                     medium.fields[name],
                                     fine.fields[name],
                                     ratio,
                                     grid)
    print('\t{}: {}'.format(name, alpha[name]))
  if save_name:
    print('[info] writing orders into .dat file ...')
    time_step = mask.fields[name].time_step
    if not os.path.isdir(save_directory):
      os.makedirs(save_directory)
    file_path = os.path.join(save_directory,
                             '_'.join([save_name,
                                       coarse.description,
                                       medium.description,
                                       fine.description,
                                       '{:0>7}'.format(time_step)]))
    with open(file_path, 'w') as outfile:
      for name in field_names:
        outfile.write('{}: {}\n'.format(name, alpha[name]))
  return alpha

def get_observed_order(coarse, medium, fine, ratio, grid, order=None):
  """
  Computes the observed order of convergence using the solution on three
  consecutive grids with constant refinement ratio.

  Parameters
  ----------
  coarse, medium, fine: Field objects
    Solutions on three consecutive grids restricted on the coarsest grid.
  ratio: float
    Grid-refinement ratio.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict a solution.
  order: non-zero integer, inf, -inf, 'fro', 'nuc', optional
    Order of the norm;
    default: None (L2-norm).

  Returns
  -------
  p: float
    The observed order of convergence.
  """
  # restrict coarse solution onto grid
  coarse = coarse.restriction(grid)
  # restrict medium solution onto grid
  medium = medium.restriction(grid)
  # restrict fine solution onto grid
  fine = fine.restriction(grid)
  # return observed order of convergence
  return (numpy.log(numpy.linalg.norm(medium.values - coarse.values,
                                      ord=order)
                    / numpy.linalg.norm(fine.values - medium.values,
                                        ord=order))
          / numpy.log(ratio))

def plot_asymptotic_ranges(simulations, orders, mask,
                           save_directory=os.path.join(os.getcwd(), 'images')):
  """
  Computes and plots the asymptotic range fields using the grid convergence
  index and given the observed orders of convergence.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  simulations: 3-list of Simulation objects.
    The three cases to use.
  orders: dictionary of (string, float) items
    Contains the observed order of convergence of each flow variable.
  mask: Simulation  object
    Case whose grids are used to restrict the other (finer) solutions.
  save_directory: string, optional
    Directory where to save the contours;
    default: '<current directory>/images'.
  style: string, optional
    Name of the Matplotlib style-sheet to use.
    The .mplstyle file should be located in 'snake/styles';
    default: 'mesnardo'.
  """
  field_names = orders.keys()
  coarse, medium, fine = simulations
  ratio = coarse.get_grid_spacing() / medium.get_grid_spacing()
  images_directory = os.path.join(save_directory,
                                  '_'.join(['gci',
                                            coarse.description,
                                            medium.description,
                                            fine.description]))
  if not os.path.isdir(images_directory):
    os.makedirs(images_directory)
  for name in field_names:
    grid = [mask.fields[name].x, mask.fields[name].y]
    field = get_asymptotic_range(coarse.fields[name],
                                 medium.fields[name],
                                 fine.fields[name],
                                 orders[name],
                                 ratio,
                                 grid)
    field.plot_contour(field_range=(0.0, 2.0, 101),
                       view=[coarse.grid[0][0], coarse.grid[1][0],
                             coarse.grid[0][-1], coarse.grid[1][-1]],
                       save_directory=images_directory)

def get_asymptotic_range(coarse, medium, fine, order, ratio, grid):
  """
  Computes the asymptotic range field using the grid convergence index.

  The three solutions are in the asymptotic range if the field returned
  contains values that are close to 1.0.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  coarse, medium, fine: Field objects
    Solutions on coarse, medium, and fine grids.
  order: float
    Observed order of convergence.
  ratio: float
    Grid refinement ratio between the two consecutive grids.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict the fields.

  Returns
  -------
  asymptotic_range: Field object
    The asymptotic range as a Field.
  """
  gci_23 = get_grid_convergence_index(coarse, medium, order, ratio, grid,
                                      Fs=1.25)
  gci_12 = get_grid_convergence_index(medium, fine, order, ratio, grid,
                                      Fs=1.25)
  return Field(x=grid[0], y=grid[1],
               values=gci_23.values / (gci_12.values * ratio**order),
               time_step=coarse.time_step,
               label='asymptotic-range-' + coarse.label)

def get_grid_convergence_index(coarse, fine, order, ratio, grid, Fs=1.25):
  """
  Computes the Grid Convergence Index using the solution obtained on two grids,
  coarse and fine, with a constant grid refinement ratio.

  Choose 3.0 as a safety factor, if only two grids were used to estimate the
  observed order of convergence, and 1.25 if three or more grids were used.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  coarse, fine: Field objects
    Solutions on coarse and fine grids.
  order: float
    Observed order of convergence.
  ratio: float
    Grid refinement ratio between the two grids.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict the fields.
  Fs: float, optional
    Safety factor;
    default: 1.25.

  Returns
  -------
  GCI: Field object
    The Grid Convergence Index (in percentage) as a Field.
  """
  coarse = coarse.restriction(grid)
  fine = fine.restriction(grid)
  # remove small field values to avoid large estimations
  # in the relative difference
  tolerance = 1.0E-06
  mask = numpy.logical_or(numpy.absolute(coarse.values) < tolerance,
                          numpy.absolute(fine.values) < tolerance)
  coarse.values[mask], fine.values[mask] = None, None
  # compute relative differences
  relative_differences = numpy.absolute((coarse.values - fine.values)
                                        / fine.values)
  return Field(x=grid[0], y=grid[1],
               values=Fs * relative_differences / (ratio**order - 1.0) * 100.0,
               time_step=coarse.time_step,
               label='GCI-' + coarse.label)
<commit_msg>Provide path of Matplotlib style sheet to use<commit_after>"""
Implementation of the functions related to grid-convergence study.
"""

import os

import numpy
from matplotlib import pyplot

from field import Field

def get_exact_solution(simulations, mask, *arguments):
  """
  Gets the exact solution on the finest grid available.
  If no analytical solution is available, the solution on the finest grid is
  considered to be exact.

  Parameters
  ----------
  simulations: dictionary of (string, Simulation object) items
    Solutions on grids with constant refinement ratio.
  mask: string
    Key of the dictionary simulations to define the simulation
    whose grid will be used to compute the analytical solution.
  arguments:
    Arguments for the analytical plug-in
    (arguments of the __init__ method of the class).

  Returns
  -------
  exact: SolutionClass object
    Contains the exact solution.
  """
  if arguments:
    from solutions.dispatcher import dispatcher
    SolutionClass = dispatcher[arguments[0]]
    # compute analytical solution
    exact = SolutionClass(simulations[mask].grid[0],
                          simulations[mask].grid[1],
                          *arguments[1:])
  else:
    # assume finest grid contains exact solution if no analytical solution
    finest = simulations.keys()[-1]
    exact = simulations[finest]
    del simulations[finest]
  return exact

def plot_grid_convergence(simulations, exact,
                          mask=None,
                          field_names=None,
                          norms=None,
                          save_directory=os.path.join(os.getcwd(), 'images'),
                          save_name=None,
                          fmt='png',
                          dpi=100,
                          style=None,
                          show=False):
  """
  Plots the grid-convergence in a log-log figure.

  Parameters
  ----------
  simulations: list of Simulation objects
    List of the cases to include in the figure.
  exact: Solution or Simulation object
    The exact solution;
    either the solution on the finest grid or a Solution object.
  mask: Simulation object, optional
    Simulation whose grids are used to restrict the solution;
    default: None.
  field_names: list of strings, optional
    Names of the fields to include in the figure;
    default: None.
  save_directory: string, optional
    Directory where to save the figure;
    default: <current directory>.
  save_name: string, optional
    Name of the file to save;
    default: None (does not save).
  fmt: string, optional
    Format of the file to save;
    default: 'png'.
  dpi: integer, optional
    Dots per inch (resolution);
    default: 100.
  style: string, optional
    Path of the Matplotlib style-sheet to use;
    default: None.
  show: boolean, optional
    Set 'True' if you want to display the figure;
    default: False.
  """
  print('[info] plotting the grid convergence ...')
  if style:
    pyplot.style.use(style)
  fig, ax = pyplot.subplots(figsize=(6, 6))
  ax.grid(True, zorder=0)
  ax.set_xlabel('grid-spacing')
  ax.set_ylabel('differences')
  grid_spacings = [case.get_grid_spacing() for case in simulations]
  norm_labels = {'L2': '$L_2$', 'Linf': '$L_\infty$'}
  for field_name in field_names:
    for norm in norms:
      differences = [case.get_difference(exact, field_name,
                                         mask=mask,
                                         norm=norm)
                     for case in simulations]
      ax.plot(grid_spacings, differences,
              label='{} - {}-norm'.format(field_name, norm_labels[norm]),
              marker='o',
              zorder=10)
  ax.legend()
  ax.set_xlim(0.1 * min(grid_spacings), 10.0 * max(grid_spacings))
  pyplot.xscale('log')
  pyplot.yscale('log')
  ax.axis('equal')
  # save and display
  if save_name:
    print('[info] saving figure ...')
    if not os.path.isdir(save_directory):
      os.makedirs(save_directory)
    time_step = simulations[0].fields[field_names[0]].time_step
    pyplot.savefig(os.path.join(save_directory,
                                '{}{:0>7}.{}'.format(save_name,
                                                     time_step,
                                                     fmt)),
                   bbox_inches='tight',
                   dpi=dpi,
                   format=fmt)
  if show:
    print('[info] displaying figure ...')
    pyplot.show()
  pyplot.close()

def get_observed_orders(simulations, field_names, mask,
                        save_directory=os.getcwd(),
                        save_name='observedOrders'):
  """
  Computes the observed orders of convergence using the solution
  on three grids with constant grid refinement ratio.

  Parameters
  ----------
  simulations: 3-list of Simulation objects
    Contains the simulations.
  field_names: list of strings
    List of field names whose observed order of convergence will be computed.
  mask: Simulation object
    Simulation whose grids are used as a mask to restrict the solutions.
  save_directory: string, optional
    Directory where to save the file;
    default: <current directory>.
  save_name: string, optional
    Prefix of the name of the .dat files to save;
    default: 'observedOrders'.

  Returns
  -------
  alpha: dictionary of (string, float) items
    Contains the observed order of convergence for each variable asked.
  """
  print('[info] computing observed orders of '
        'convergence using the grids {} ...'
        ''.format([case.description for case in simulations]))
  coarse, medium, fine = simulations
  ratio = coarse.get_grid_spacing() / medium.get_grid_spacing()
  alpha = {}  # will contain observed order of convergence
  for name in field_names:
    grid = [mask.fields[name].x, mask.fields[name].y]
    alpha[name] = get_observed_order(coarse.fields[name],
                                     medium.fields[name],
                                     fine.fields[name],
                                     ratio,
                                     grid)
    print('\t{}: {}'.format(name, alpha[name]))
  if save_name:
    print('[info] writing orders into .dat file ...')
    time_step = mask.fields[name].time_step
    if not os.path.isdir(save_directory):
      os.makedirs(save_directory)
    file_path = os.path.join(save_directory,
                             '_'.join([save_name,
                                       coarse.description,
                                       medium.description,
                                       fine.description,
                                       '{:0>7}'.format(time_step)]))
    with open(file_path, 'w') as outfile:
      for name in field_names:
        outfile.write('{}: {}\n'.format(name, alpha[name]))
  return alpha

def get_observed_order(coarse, medium, fine, ratio, grid, order=None):
  """
  Computes the observed order of convergence using the solution on three
  consecutive grids with constant refinement ratio.

  Parameters
  ----------
  coarse, medium, fine: Field objects
    Solutions on three consecutive grids restricted on the coarsest grid.
  ratio: float
    Grid-refinement ratio.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict a solution.
  order: non-zero integer, inf, -inf, 'fro', 'nuc', optional
    Order of the norm;
    default: None (L2-norm).

  Returns
  -------
  p: float
    The observed order of convergence.
  """
  # restrict coarse solution onto grid
  coarse = coarse.restriction(grid)
  # restrict medium solution onto grid
  medium = medium.restriction(grid)
  # restrict fine solution onto grid
  fine = fine.restriction(grid)
  # return observed order of convergence
  return (numpy.log(numpy.linalg.norm(medium.values - coarse.values,
                                      ord=order)
                    / numpy.linalg.norm(fine.values - medium.values,
                                        ord=order))
          / numpy.log(ratio))

def plot_asymptotic_ranges(simulations, orders, mask,
                           save_directory=os.path.join(os.getcwd(), 'images')):
  """
  Computes and plots the asymptotic range fields using the grid convergence
  index and given the observed orders of convergence.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  simulations: 3-list of Simulation objects.
    The three cases to use.
  orders: dictionary of (string, float) items
    Contains the observed order of convergence of each flow variable.
  mask: Simulation  object
    Case whose grids are used to restrict the other (finer) solutions.
  save_directory: string, optional
    Directory where to save the contours;
    default: '<current directory>/images'.
  style: string, optional
    Name of the Matplotlib style-sheet to use.
    The .mplstyle file should be located in 'snake/styles';
    default: 'mesnardo'.
  """
  field_names = orders.keys()
  coarse, medium, fine = simulations
  ratio = coarse.get_grid_spacing() / medium.get_grid_spacing()
  images_directory = os.path.join(save_directory,
                                  '_'.join(['gci',
                                            coarse.description,
                                            medium.description,
                                            fine.description]))
  if not os.path.isdir(images_directory):
    os.makedirs(images_directory)
  for name in field_names:
    grid = [mask.fields[name].x, mask.fields[name].y]
    field = get_asymptotic_range(coarse.fields[name],
                                 medium.fields[name],
                                 fine.fields[name],
                                 orders[name],
                                 ratio,
                                 grid)
    field.plot_contour(field_range=(0.0, 2.0, 101),
                       view=[coarse.grid[0][0], coarse.grid[1][0],
                             coarse.grid[0][-1], coarse.grid[1][-1]],
                       save_directory=images_directory)

def get_asymptotic_range(coarse, medium, fine, order, ratio, grid):
  """
  Computes the asymptotic range field using the grid convergence index.

  The three solutions are in the asymptotic range if the field returned
  contains values that are close to 1.0.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  coarse, medium, fine: Field objects
    Solutions on coarse, medium, and fine grids.
  order: float
    Observed order of convergence.
  ratio: float
    Grid refinement ratio between the two consecutive grids.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict the fields.

  Returns
  -------
  asymptotic_range: Field object
    The asymptotic range as a Field.
  """
  gci_23 = get_grid_convergence_index(coarse, medium, order, ratio, grid,
                                      Fs=1.25)
  gci_12 = get_grid_convergence_index(medium, fine, order, ratio, grid,
                                      Fs=1.25)
  return Field(x=grid[0], y=grid[1],
               values=gci_23.values / (gci_12.values * ratio**order),
               time_step=coarse.time_step,
               label='asymptotic-range-' + coarse.label)

def get_grid_convergence_index(coarse, fine, order, ratio, grid, Fs=1.25):
  """
  Computes the Grid Convergence Index using the solution obtained on two grids,
  coarse and fine, with a constant grid refinement ratio.

  Choose 3.0 as a safety factor, if only two grids were used to estimate the
  observed order of convergence, and 1.25 if three or more grids were used.

  References
  ----------
  [1] http://www.grc.nasa.gov/WWW/wind/valid/tutorial/spatconv.html

  Parameters
  ----------
  coarse, fine: Field objects
    Solutions on coarse and fine grids.
  order: float
    Observed order of convergence.
  ratio: float
    Grid refinement ratio between the two grids.
  grid: 2-list of 1D arrays of floats
    Nodal stations in each direction used to restrict the fields.
  Fs: float, optional
    Safety factor;
    default: 1.25.

  Returns
  -------
  GCI: Field object
    The Grid Convergence Index (in percentage) as a Field.
  """
  coarse = coarse.restriction(grid)
  fine = fine.restriction(grid)
  # remove small field values to avoid large estimations
  # in the relative difference
  tolerance = 1.0E-06
  mask = numpy.logical_or(numpy.absolute(coarse.values) < tolerance,
                          numpy.absolute(fine.values) < tolerance)
  coarse.values[mask], fine.values[mask] = None, None
  # compute relative differences
  relative_differences = numpy.absolute((coarse.values - fine.values)
                                        / fine.values)
  return Field(x=grid[0], y=grid[1],
               values=Fs * relative_differences / (ratio**order - 1.0) * 100.0,
               time_step=coarse.time_step,
               label='GCI-' + coarse.label)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from lxml import etree
from openpyxl import Workbook, load_workbook
from collections import OrderedDict
import argparse

source_file = 'messages.xml'
incomplete_translation_file = 'messages_el.xml'
output_file = 'output.xml'
imported_file = 'imported.xml'
exported_file = 'exported.xlsx'

class XmluiParser:
    def __init__(self, from_file, to_file):
        self.from_file = from_file
        self.to_file = to_file

    def processFiles(self):

        # Parse the non completed translation file
        doc = etree.parse(self.from_file)
        di = dict()
        for n in doc.getroot().iterdescendants():
            tmp = n.attrib

            '''
                Empty strings (none) are considered false.

                if string:
                    # String is not empty/has content.
                else:
                    # String is empty/does not have content.
            '''
            if tmp.get('key'):
                di[tmp.get('key')] = n.text
                # print(str(tmp.get('key')) + " --  "  + str(n.text))

        # Parse the latest English translation
        dest = etree.parse(source_file)
        root = dest.getroot()

        for key, val in di.items():
            # print(key, val)
            if key:
                code = root.xpath("/catalogue/message[@key='" + key + "']")
                if code:
                    code[0].text = val

        f = open(self.to_file, 'wb')
        f.write(etree.tostring(root, encoding='UTF-8', pretty_print=True))
        f.close()

class SpreadSheet:
    def __init__(self, from_file, to_file):
        self.from_file = from_file
        self.to_file = to_file

    def exportXLSX(self):
        wb = Workbook()
        ws = wb.active
        # create a dict of the contents of the output XML file

        doc = etree.parse(from_file)
        '''A standard dict does not maintain insertion order thus we need to use collections.OrderedDict. If we do not,
        the generated XLSX will have a mixed ordering of key/value pairs'''
        di = OrderedDict()
        for n in doc.getroot().iterdescendants():
            tmp = n.attrib

            if tmp.get('key'):
                di[tmp.get('key')] = n.text
                # print(str(tmp.get('key')) + " --  "  + str(n.text))

        # print(di)
        for index, (key, val) in enumerate(di.items()):

            index += 1
            colA = "A" + str(index)
            colB = "B" + str(index)

            ws[colA] = key
            ws[colB] = val

        wb.save(to_file)

    def importXLSX(self):

        wb = load_workbook(self.from_file)
        sheet = wb.get_sheet_by_name('Sheet')
        # print(sheet.max_row)

        # Parse the new XML translation
        dest = etree.parse(self.to_file)
        root = dest.getroot()
        num_of_keys = sheet.max_row
        for x in range(1, num_of_keys):
            colA = "A" + str(x)
            colB = "B" + str(x)
            code = root.xpath("/catalogue/message[@key='" + sheet[colA].value + "']")
            if code:
                code[0].text = sheet[colB].value

            #print(sheet[colA].value + "  " + sheet[colB].value)
        f = open(imported_file, 'wb')
        f.write(etree.tostring(root, encoding='UTF-8', pretty_print=True))
        f.close()

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description='This is a script to assist in translating XMLUI.')
    # parser.add_argument('--foo', action='store_true', help='foo help')
    subparsers = parser.add_subparsers(help='sub-command help')

    # create the parser for the "merge" command
    parser_a = subparsers.add_parser('migrate', help='Migrate translation messages from an older version to the latest.')
    parser_a.add_argument('-f', '--from-file', default=incomplete_translation_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_a.add_argument('-t', '--to-file', default=output_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_a.set_defaults(which='migrate')

    # create the parser for the "export" command
    parser_b = subparsers.add_parser('export', help='Convert an XML translation file to XLSX for easier editing.')
    parser_b.add_argument('-f', '--from-file', default=output_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_b.add_argument('-t', '--to-file', default=exported_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_b.set_defaults(which='export')

    # create the parser for the "import" command
    parser_c = subparsers.add_parser('import', help='Convert an XLSX translation file to the native messages_xx.xml format.')
    parser_c.add_argument('-f', '--from-file', default=exported_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_c.add_argument('-t', '--to-file', default=imported_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_c.set_defaults(which='import')

    args = parser.parse_args()
    # print(args)
    # print(args.which)
    try:
        from_file = args.from_file
        to_file = args.to_file
        if args.which == 'migrate':
            # print("Migrate")
            obj = XmluiParser(from_file, to_file)
            obj.processFiles()

        elif args.which == 'export':
            # print("Export")
            obj =  SpreadSheet(from_file, to_file)
            obj.exportXLSX()
        elif args.which == 'import':
            # print("Import")
            obj =  SpreadSheet(from_file, to_file)
            obj.importXLSX()
        else:
            print ("Use -h for instructions.")
    except AttributeError:
        print ("Use -h for instructions.")

<commit_msg>Fixed: Added default filenames for all function calls.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

from lxml import etree
from openpyxl import Workbook, load_workbook
from collections import OrderedDict
import argparse

source_file = 'messages.xml'
incomplete_translation_file = 'messages_el.xml'
output_file = 'output.xml'
imported_file = 'imported.xml'
exported_file = 'exported.xlsx'

class XmluiParser:
    def __init__(self, from_file, to_file, base_file):
        self.from_file = from_file
        self.to_file = to_file
        self.base_file = base_file

    def processFiles(self):

        # Parse the non completed translation file
        doc = etree.parse(self.from_file)
        di = dict()
        for n in doc.getroot().iterdescendants():
            tmp = n.attrib

            '''
                Empty strings (none) are considered false.

                if string:
                    # String is not empty/has content.
                else:
                    # String is empty/does not have content.
            '''
            if tmp.get('key'):
                di[tmp.get('key')] = n.text
                # print(str(tmp.get('key')) + " --  "  + str(n.text))

        # Parse the latest English translation
        dest = etree.parse(self.base_file)
        root = dest.getroot()

        for key, val in di.items():
            # print(key, val)
            if key:
                code = root.xpath("/catalogue/message[@key='" + key + "']")
                if code:
                    code[0].text = val

        f = open(self.to_file, 'wb')
        f.write(etree.tostring(root, encoding='UTF-8', pretty_print=True))
        f.close()

class SpreadSheet:
    def __init__(self, from_file, to_file, base_file=False):
        self.from_file = from_file
        self.to_file = to_file
        if base_file:
            self.base_file = base_file

    def exportXLSX(self):
        wb = Workbook()
        ws = wb.active
        # create a dict of the contents of the output XML file

        doc = etree.parse(from_file)
        '''A standard dict does not maintain insertion order thus we need to use collections.OrderedDict. If we do not,
        the generated XLSX will have a mixed ordering of key/value pairs'''
        di = OrderedDict()
        for n in doc.getroot().iterdescendants():
            tmp = n.attrib

            if tmp.get('key'):
                di[tmp.get('key')] = n.text
                # print(str(tmp.get('key')) + " --  "  + str(n.text))

        # print(di)
        for index, (key, val) in enumerate(di.items()):

            index += 1
            colA = "A" + str(index)
            colB = "B" + str(index)

            ws[colA] = key
            ws[colB] = val

        wb.save(to_file)

    def importXLSX(self):

        wb = load_workbook(self.from_file)
        sheet = wb.get_sheet_by_name('Sheet')
        # print(sheet.max_row)

        # Parse the new XML translation
        dest = etree.parse(self.to_file)
        root = dest.getroot()
        num_of_keys = sheet.max_row
        for x in range(1, num_of_keys):
            colA = "A" + str(x)
            colB = "B" + str(x)
            code = root.xpath("/catalogue/message[@key='" + sheet[colA].value + "']")
            if code:
                code[0].text = sheet[colB].value

            #print(sheet[colA].value + "  " + sheet[colB].value)
        f = open(self.base_file, 'wb')
        f.write(etree.tostring(root, encoding='UTF-8', pretty_print=True))
        f.close()

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description='This is a script to assist in translating XMLUI.')
    # parser.add_argument('--foo', action='store_true', help='foo help')
    subparsers = parser.add_subparsers(help='sub-command help')

    # create the parser for the "merge" command
    parser_a = subparsers.add_parser('migrate', help='Migrate translation messages from an older version to the latest.')
    parser_a.add_argument('-f', '--from-file', default=incomplete_translation_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_a.add_argument('-t', '--to-file', default=output_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_a.add_argument('-b', '--base-file', default=source_file,
                          help='This is usually the messages.xml file released with the current version of DSpace.',
                          required=False)
    parser_a.set_defaults(which='migrate')

    # create the parser for the "export" command
    parser_b = subparsers.add_parser('export', help='Convert an XML translation file to XLSX for easier editing.')
    parser_b.add_argument('-f', '--from-file', default=output_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_b.add_argument('-t', '--to-file', default=exported_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_b.set_defaults(which='export')

    # create the parser for the "import" command
    parser_c = subparsers.add_parser('import', help='Convert an XLSX translation file to the native messages_xx.xml format.')
    parser_c.add_argument('-f', '--from-file', default=exported_file,
                        help='Read translation strings from this file.',
                        required=False)
    parser_c.add_argument('-t', '--to-file', default=source_file,
                        help='Place translations in this file (this usually being the latest version).',
                        required=False)
    parser_c.add_argument('-b', '--base-file', default=source_file,
                          help='This is usually the messages.xml file released with the current version of DSpace.',
                          required=False)
    parser_c.set_defaults(which='import')

    args = parser.parse_args()

    try:
        from_file = args.from_file
        to_file = args.to_file
        if args.which == 'migrate':
            base_file = args.base_file
            print("\nMigrating existing strings from %s to %s. Using the latest %s for generating output.\n" % (from_file, to_file, base_file))
            obj = XmluiParser(from_file, to_file, base_file)
            obj.processFiles()

        elif args.which == 'export':
            print("\nConverting %s to %s. Use a spreadsheet editor to edit your translation.\n" % (
            from_file, to_file))
            obj =  SpreadSheet(from_file, to_file)
            obj.exportXLSX()

        elif args.which == 'import':
            base_file = args.base_file
            print("\nConverting %s to %s. \n" % (
                from_file, to_file))
            obj =  SpreadSheet(from_file, to_file, base_file)
            obj.importXLSX()

        else:
            print ("Use -h for instructions.")

    except AttributeError:
        print ("\nUse -h for instructions.\n")

<|endoftext|>


<commit_before>__author__ = 'davedash'
__version__ = 1.0.0
<commit_msg>Version should be a string<commit_after>__author__ = 'davedash'
__version__ = '1.0.0'
<|endoftext|>


<commit_before>"""Views

Notes:
    * Some views are marked to avoid csrf tocken check because they rely
      on third party providers that (if using POST) won't be sending csrf
      token back.
"""
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib.auth import login, REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt

from social_auth.utils import sanitize_redirect, setting, \
                              backend_setting, clean_partial_pipeline
from social_auth.decorators import dsa_view

DEFAULT_REDIRECT = setting('SOCIAL_AUTH_LOGIN_REDIRECT_URL') or \
                   setting('LOGIN_REDIRECT_URL')
LOGIN_ERROR_URL = setting('LOGIN_ERROR_URL', setting('LOGIN_URL'))
PIPELINE_KEY = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')

@dsa_view(setting('SOCIAL_AUTH_COMPLETE_URL_NAME', 'socialauth_complete'))
def auth(request, backend):
    """Start authentication process"""
    return auth_process(request, backend)

@csrf_exempt
@dsa_view()
def complete(request, backend, *args, **kwargs):
    """Authentication complete view, override this view if transaction
    management doesn't suit your needs."""
    if request.user.is_authenticated():
        return associate_complete(request, backend, *args, **kwargs)
    else:
        return complete_process(request, backend, *args, **kwargs)

@login_required
def associate_complete(request, backend, *args, **kwargs):
    """Authentication complete process"""
    # pop redirect value before the session is trashed on login()
    redirect_value = request.session.get(REDIRECT_FIELD_NAME, '')
    user = auth_complete(request, backend, request.user, *args, **kwargs)

    if not user:
        url = backend_setting(backend, 'LOGIN_ERROR_URL', LOGIN_ERROR_URL)
    elif isinstance(user, HttpResponse):
        return user
    else:
        url = redirect_value or \
              backend_setting(backend,
                              'SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL') or \
              DEFAULT_REDIRECT
    return HttpResponseRedirect(url)

@login_required
@dsa_view()
def disconnect(request, backend, association_id=None):
    """Disconnects given backend from current logged in user."""
    backend.disconnect(request.user, association_id)
    url = request.REQUEST.get(REDIRECT_FIELD_NAME, '') or \
          backend_setting(backend, 'SOCIAL_AUTH_DISCONNECT_REDIRECT_URL') or \
          DEFAULT_REDIRECT
    return HttpResponseRedirect(url)

def auth_process(request, backend):
    """Authenticate using social backend"""
    # Save any defined next value into session
    data = request.POST if request.method == 'POST' else request.GET
    if REDIRECT_FIELD_NAME in data:
        # Check and sanitize a user-defined GET/POST next field value
        redirect = data[REDIRECT_FIELD_NAME]
        if setting('SOCIAL_AUTH_SANITIZE_REDIRECTS', True):
            redirect = sanitize_redirect(request.get_host(), redirect)
        request.session[REDIRECT_FIELD_NAME] = redirect or DEFAULT_REDIRECT

    # Clean any partial pipeline info before starting the process
    clean_partial_pipeline(request)

    if backend.uses_redirect:
        return HttpResponseRedirect(backend.auth_url())
    else:
        return HttpResponse(backend.auth_html(),
                            content_type='text/html;charset=UTF-8')

def complete_process(request, backend, *args, **kwargs):
    """Authentication complete process"""
    # pop redirect value before the session is trashed on login()
    redirect_value = request.session.get(REDIRECT_FIELD_NAME, '')
    user = auth_complete(request, backend, *args, **kwargs)

    if isinstance(user, HttpResponse):
        return user

    if not user and request.user.is_authenticated():
        return HttpResponseRedirect(redirect_value)

    msg = None
    if user:
        if getattr(user, 'is_active', True):
            # catch is_new flag before login() might reset the instance
            is_new = getattr(user, 'is_new', False)
            login(request, user)
            # user.social_user is the used UserSocialAuth instance defined
            # in authenticate process
            social_user = user.social_user
            if redirect_value:
                request.session[REDIRECT_FIELD_NAME] = redirect_value or \
                                                       DEFAULT_REDIRECT

            if setting('SOCIAL_AUTH_SESSION_EXPIRATION', True):
                # Set session expiration date if present and not disabled by
                # setting. Use last social-auth instance for current provider,
                # users can associate several accounts with a same provider.
                expiration = social_user.expiration_datetime()
                if expiration:
                    try:
                        request.session.set_expiry(expiration)
                    except OverflowError:
                        # Handle django time zone overflow, set default expiry.
                        request.session.set_expiry(None)

            # store last login backend name in session
            key = setting('SOCIAL_AUTH_LAST_LOGIN',
                          'social_auth_last_login_backend')
            request.session[key] = social_user.provider

            # Remove possible redirect URL from session, if this is a new
            # account, send him to the new-users-page if defined.
            new_user_redirect = backend_setting(backend,
                                           'SOCIAL_AUTH_NEW_USER_REDIRECT_URL')
            if new_user_redirect and is_new:
                url = new_user_redirect
            else:
                url = redirect_value or \
                      backend_setting(backend,
                                      'SOCIAL_AUTH_LOGIN_REDIRECT_URL') or \
                      DEFAULT_REDIRECT
        else:
            msg = setting('SOCIAL_AUTH_INACTIVE_USER_MESSAGE', None)
            url = backend_setting(backend, 'SOCIAL_AUTH_INACTIVE_USER_URL',
                                  LOGIN_ERROR_URL)
    else:
        msg = setting('LOGIN_ERROR_MESSAGE', None)
        url = backend_setting(backend, 'LOGIN_ERROR_URL', LOGIN_ERROR_URL)
    if msg:
        messages.error(request, msg)
    return HttpResponseRedirect(url)

def auth_complete(request, backend, user=None, *args, **kwargs):
    """Complete auth process. Return authenticated user or None."""
    if user and not user.is_authenticated():
        user = None

    if request.session.get(PIPELINE_KEY):
        data = request.session.pop(PIPELINE_KEY)
        idx, xargs, xkwargs = backend.from_session_dict(data, user=user,
                                                        request=request,
                                                        *args, **kwargs)
        if 'backend' in xkwargs and \
           xkwargs['backend'].name == backend.AUTH_BACKEND.name:
            return backend.continue_pipeline(pipeline_index=idx,
                                             *xargs, **xkwargs)
    return backend.auth_complete(user=user, request=request, *args, **kwargs)
<commit_msg>Removed the use of 'or' because it made impossible the use of reverse_lazy in SOCIAL_AUTH_LOGIN_REDIRECT_URL setting<commit_after>"""Views

Notes:
    * Some views are marked to avoid csrf tocken check because they rely
      on third party providers that (if using POST) won't be sending csrf
      token back.
"""
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib.auth import login, REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt

from social_auth.utils import sanitize_redirect, setting, \
                              backend_setting, clean_partial_pipeline
from social_auth.decorators import dsa_view

DEFAULT_REDIRECT = setting('SOCIAL_AUTH_LOGIN_REDIRECT_URL',
                           setting('LOGIN_REDIRECT_URL'))
LOGIN_ERROR_URL = setting('LOGIN_ERROR_URL', setting('LOGIN_URL'))
PIPELINE_KEY = setting('SOCIAL_AUTH_PARTIAL_PIPELINE_KEY', 'partial_pipeline')

@dsa_view(setting('SOCIAL_AUTH_COMPLETE_URL_NAME', 'socialauth_complete'))
def auth(request, backend):
    """Start authentication process"""
    return auth_process(request, backend)

@csrf_exempt
@dsa_view()
def complete(request, backend, *args, **kwargs):
    """Authentication complete view, override this view if transaction
    management doesn't suit your needs."""
    if request.user.is_authenticated():
        return associate_complete(request, backend, *args, **kwargs)
    else:
        return complete_process(request, backend, *args, **kwargs)

@login_required
def associate_complete(request, backend, *args, **kwargs):
    """Authentication complete process"""
    # pop redirect value before the session is trashed on login()
    redirect_value = request.session.get(REDIRECT_FIELD_NAME, '')
    user = auth_complete(request, backend, request.user, *args, **kwargs)

    if not user:
        url = backend_setting(backend, 'LOGIN_ERROR_URL', LOGIN_ERROR_URL)
    elif isinstance(user, HttpResponse):
        return user
    else:
        url = redirect_value or \
              backend_setting(backend,
                              'SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL') or \
              DEFAULT_REDIRECT
    return HttpResponseRedirect(url)

@login_required
@dsa_view()
def disconnect(request, backend, association_id=None):
    """Disconnects given backend from current logged in user."""
    backend.disconnect(request.user, association_id)
    url = request.REQUEST.get(REDIRECT_FIELD_NAME, '') or \
          backend_setting(backend, 'SOCIAL_AUTH_DISCONNECT_REDIRECT_URL') or \
          DEFAULT_REDIRECT
    return HttpResponseRedirect(url)

def auth_process(request, backend):
    """Authenticate using social backend"""
    # Save any defined next value into session
    data = request.POST if request.method == 'POST' else request.GET
    if REDIRECT_FIELD_NAME in data:
        # Check and sanitize a user-defined GET/POST next field value
        redirect = data[REDIRECT_FIELD_NAME]
        if setting('SOCIAL_AUTH_SANITIZE_REDIRECTS', True):
            redirect = sanitize_redirect(request.get_host(), redirect)
        request.session[REDIRECT_FIELD_NAME] = redirect or DEFAULT_REDIRECT

    # Clean any partial pipeline info before starting the process
    clean_partial_pipeline(request)

    if backend.uses_redirect:
        return HttpResponseRedirect(backend.auth_url())
    else:
        return HttpResponse(backend.auth_html(),
                            content_type='text/html;charset=UTF-8')

def complete_process(request, backend, *args, **kwargs):
    """Authentication complete process"""
    # pop redirect value before the session is trashed on login()
    redirect_value = request.session.get(REDIRECT_FIELD_NAME, '')
    user = auth_complete(request, backend, *args, **kwargs)

    if isinstance(user, HttpResponse):
        return user

    if not user and request.user.is_authenticated():
        return HttpResponseRedirect(redirect_value)

    msg = None
    if user:
        if getattr(user, 'is_active', True):
            # catch is_new flag before login() might reset the instance
            is_new = getattr(user, 'is_new', False)
            login(request, user)
            # user.social_user is the used UserSocialAuth instance defined
            # in authenticate process
            social_user = user.social_user
            if redirect_value:
                request.session[REDIRECT_FIELD_NAME] = redirect_value or \
                                                       DEFAULT_REDIRECT

            if setting('SOCIAL_AUTH_SESSION_EXPIRATION', True):
                # Set session expiration date if present and not disabled by
                # setting. Use last social-auth instance for current provider,
                # users can associate several accounts with a same provider.
                expiration = social_user.expiration_datetime()
                if expiration:
                    try:
                        request.session.set_expiry(expiration)
                    except OverflowError:
                        # Handle django time zone overflow, set default expiry.
                        request.session.set_expiry(None)

            # store last login backend name in session
            key = setting('SOCIAL_AUTH_LAST_LOGIN',
                          'social_auth_last_login_backend')
            request.session[key] = social_user.provider

            # Remove possible redirect URL from session, if this is a new
            # account, send him to the new-users-page if defined.
            new_user_redirect = backend_setting(backend,
                                           'SOCIAL_AUTH_NEW_USER_REDIRECT_URL')
            if new_user_redirect and is_new:
                url = new_user_redirect
            else:
                url = redirect_value or \
                      backend_setting(backend,
                                      'SOCIAL_AUTH_LOGIN_REDIRECT_URL') or \
                      DEFAULT_REDIRECT
        else:
            msg = setting('SOCIAL_AUTH_INACTIVE_USER_MESSAGE', None)
            url = backend_setting(backend, 'SOCIAL_AUTH_INACTIVE_USER_URL',
                                  LOGIN_ERROR_URL)
    else:
        msg = setting('LOGIN_ERROR_MESSAGE', None)
        url = backend_setting(backend, 'LOGIN_ERROR_URL', LOGIN_ERROR_URL)
    if msg:
        messages.error(request, msg)
    return HttpResponseRedirect(url)

def auth_complete(request, backend, user=None, *args, **kwargs):
    """Complete auth process. Return authenticated user or None."""
    if user and not user.is_authenticated():
        user = None

    if request.session.get(PIPELINE_KEY):
        data = request.session.pop(PIPELINE_KEY)
        idx, xargs, xkwargs = backend.from_session_dict(data, user=user,
                                                        request=request,
                                                        *args, **kwargs)
        if 'backend' in xkwargs and \
           xkwargs['backend'].name == backend.AUTH_BACKEND.name:
            return backend.continue_pipeline(pipeline_index=idx,
                                             *xargs, **xkwargs)
    return backend.auth_complete(user=user, request=request, *args, **kwargs)
<|endoftext|>